Docs: add Phase Upsilon (temp bank for never-saved projects) to PLAN.md
This commit is contained in:
+149
-2
@@ -18,7 +18,9 @@ seventeen: it came from a direct list of seven defects and refinements (Daniel,
|
||||
2026-08-03), and like Ψ it has **no backing product doc of its own** — the seven are
|
||||
carried verbatim in its phase header below as Ω.1…Ω.7. It does amend two clauses of
|
||||
`docs/product/instrument-control-surface.md`, which are edited there rather than merely
|
||||
contradicted here.
|
||||
contradicted here. **Phase Υ**, a temporary bank for never-saved projects, is also a direct
|
||||
request (Daniel, 2026-09-25). Like Ω it has no backing product doc; its settled design is
|
||||
recorded in its phase section.
|
||||
|
||||
## What this doc is, and how it relates to the others
|
||||
|
||||
@@ -40,7 +42,7 @@ contradicted here.
|
||||
|
||||
**Worktree slug convention:** `p<phase>-w<wave>-t<track>-<slug>`. Greek phase letters
|
||||
transliterate: **Θ → `th`**, **Ξ → `xi`**, **Γ → `g`**, **Ψ → `psi`**, **Ε → `e`**, **Ρ → `r`**,
|
||||
**Λ → `l`**, **Ω → `om`**. So Θ-W1-T1 dispatches into `pth-w1-t1-zone-retirement`, Γ-W1-T1 into
|
||||
**Λ → `l`**, **Ω → `om`**, **Υ → `u`**. So Θ-W1-T1 dispatches into `pth-w1-t1-zone-retirement`, Γ-W1-T1 into
|
||||
`pg-w1-t1-knob-interaction-law`, Ψ-W1-T1 into `ppsi-w1-t1-capture-range-exactness`,
|
||||
Λ-W2-T1 into `pl-w2-t1-linux-compile-blockers` (Phase Λ's two audit tracks already ran
|
||||
under `pl-w1-t1-build-toolchain-audit` and `pl-w1-t2-source-runtime-audit`), and Ω-W1-T1
|
||||
@@ -160,6 +162,11 @@ phase in this plan with unanswered [Daniel]-class questions; the enumeration in
|
||||
predates Ω, so read *"scoped to Θ, Ξ, Γ, Ψ, Ε and Ρ"* as **Θ, Ξ, Γ, Ψ, Ε, Ρ and Ω** — seven
|
||||
phases, all of them fully ruled, and Λ alone excepted.
|
||||
|
||||
**Phase Υ (added 2026-09-25) has eight rulings, Υ-D1…Υ-D8, and all eight are settled**
|
||||
(Daniel, 2026-09-25, every recommendation accepted). It has zero open forks. Its W0
|
||||
experiments are `[verify — DAW]` gates, not [Daniel] questions, so they don't change the Λ
|
||||
carve-out above.
|
||||
|
||||
**Ruling 3 (Daniel, 2026-08-01) — real units at the host boundary.** *"The parameter values
|
||||
exposed to the VST host should be in real units, such that the host automation lanes report
|
||||
usable values."* Satisfied through VST3's **plain-value layer**, not its wire format (which is
|
||||
@@ -329,6 +336,127 @@ touches them. The instrument adds a sixth surface that binds every Θ track:
|
||||
|
||||
---
|
||||
|
||||
## Phase Υ — A temporary bank for never-saved projects
|
||||
|
||||
**Ships:** Capture, ingest, import, export and the resample bake all work in a project that
|
||||
has never been saved. Until first save the bank lives in a per-project temp root under
|
||||
REAPER's default recording path. On first save it is copied into
|
||||
`<.rpp dir>/reasampler_bank/`, and the index doesn't change, because index paths were
|
||||
already relative to the root.
|
||||
|
||||
**Consolidates: none of the seventeen.** Daniel's direct request, 2026-09-25, verbatim:
|
||||
*"capture in a never saved project fails now. instead, can we put a temp bank in the default
|
||||
reaper media folder, which upon initial save (with the copy option checked) will move into
|
||||
the saved directory bank location?"* There is no backing product doc. The design came from a
|
||||
staff-engineer design pass (2026-09-25), and its settled form is recorded here in full.
|
||||
**Zero open forks.** The W0 experiments are `[verify — DAW]` gates, not decisions.
|
||||
|
||||
**The design, in five statements:**
|
||||
|
||||
- **Bank root.** For a saved project, the root is the `.rpp`'s directory. For an unsaved one
|
||||
it is `<GetProjectPathEx(proj)>/ReaSampler unsaved banks/<genGuid>/`. Index paths stay
|
||||
`reasampler_bank/<file>` relative to the root, so the index, the tracking ledger, instrument
|
||||
sample refs and usage records **never change at first save**. The relative-paths-only
|
||||
invariant holds for the index unchanged.
|
||||
- **Lazy temp root.** The root is created by the first act that needs to write (capture,
|
||||
ingest, import, bake landing). Its absolute path goes in ONE new ext-state key,
|
||||
`unsaved_bank_root`, and never in the index, the ledger or any ref. The key is read only
|
||||
when the `.rpp` path is empty, or as a migration breadcrumb. It is cleared, without
|
||||
dirtying the project, after migration.
|
||||
- **Ext-state writes on unsaved projects are allowed.** Today they are refused by the
|
||||
project's own rule (`ext_state_io.cpp`'s `activeSavedProject` / `ensureProjectGuid`), not
|
||||
by an SDK fact. **This is gated on experiment E1.**
|
||||
- **First-save migration**, `migrateUnsavedBank(root, projectDir)`, is idempotent and has
|
||||
two triggers. (1) The existing timer poll sees the first-save transition: path `""` → X on
|
||||
the same project object and GUID. The GUID is minted without dirtying the project.
|
||||
(2) At project load, a saved `.rpp` still carries `unsaved_bank_root`. That covers
|
||||
save-at-close/quit, a crash, and a later session. The migration **copies**, following the
|
||||
existing relocation precedent. It skips an existing file of the same size, refuses to
|
||||
overwrite one that differs and reports it, and counts items that still point into temp.
|
||||
- **The instrument resolves its own project**, not the active tab. It tries candidate roots in
|
||||
order: the project dir, then the breadcrumb root.
|
||||
|
||||
### Rulings — Daniel's, 2026-09-25. Eight, settled (every recommendation accepted).
|
||||
|
||||
**Do not re-litigate these.** There is no product doc, so this table is their only home.
|
||||
|
||||
| Ruling | What it settled | Bound into |
|
||||
|---|---|---|
|
||||
| **Υ-D1** | **Always move the bank on first save, whatever the copy-media checkbox says.** An extension can't observe that checkbox | Υ-W2-T2 |
|
||||
| **Υ-D2** | **Copy, and keep the temp originals.** No second deletion authority beside prune | Υ-W2-T2 |
|
||||
| **Υ-D3** | **Arrange items still pointing at temp files after first save get a console notice only.** No automatic re-point | Υ-W2-T2 |
|
||||
| **Υ-D4** | **The temp root goes under the default recording path** (`GetProjectPathEx` on the unsaved project) | Υ-W2-T1 |
|
||||
| **Υ-D5** | **The breadcrumb stores the absolute temp path** | Υ-W2-T1 |
|
||||
| **Υ-D6** | **Leftover temp roots from abandoned or crashed projects are left alone in v1.** A confirmed, dry-run-first "reclaim unsaved-bank leftovers" action in the prune family comes later | Υ-W3-T2 |
|
||||
| **Υ-D7** | **Drop the Save-As prompts** from offline capture, realtime capture and Design View | Υ-W3-T1 |
|
||||
| **Υ-D8** | **Allow package import and export on unsaved projects** | Υ-W3-T1 |
|
||||
|
||||
### Phase-Υ acceptance criteria
|
||||
|
||||
These bind every track in this phase, on top of the plan-wide set above.
|
||||
|
||||
- **A saved project's behaviour doesn't change.** For a saved project, the root resolves to
|
||||
exactly the directory it resolves to today. Υ-W1 is a pure no-behaviour-change refactor and
|
||||
says so as a criterion.
|
||||
- **The temp path never enters the index, the ledger, instrument refs or usage records.**
|
||||
`unsaved_bank_root` is the only place it is stored.
|
||||
- **Prune is still the only thing that deletes files** (Υ-D2). Migration copies and nothing
|
||||
else. The temp originals survive until Υ-W3-T2's reclaim action, which is itself part of
|
||||
the prune family.
|
||||
- **Migration is idempotent and never overwrites silently.** A second run is a no-op. A file
|
||||
of the same size is skipped. A file that differs is refused and reported, never
|
||||
overwritten. Note that this is stricter than today's `relocateBankFolder` (see A1 below).
|
||||
- **Capture and placement stay separate acts.** Nothing here places an item or re-points
|
||||
one (Υ-D3).
|
||||
- **`bankRootFor` is pure and has its own test target**, the same as every pure module.
|
||||
|
||||
---
|
||||
|
||||
### Υ-W0 — Daniel's REAPER experiments
|
||||
|
||||
The script is `docs/verify-unsaved-bank.md`. **E1 gates Υ-W2**, and E2 shapes how it is
|
||||
built.
|
||||
|
||||
| Exp | Question |
|
||||
|---|---|
|
||||
| **E1** | Does ext state survive this sequence: unsaved → switch tabs → first save? |
|
||||
| **E2** | Does `SetProjExtState` on its own dirty the project? |
|
||||
| **E3** | Is an unsaved project's `GetProjectPathEx` the same across tabs? |
|
||||
| **E4** | Where do items pointing at temp files end up after save, with copy ticked and with it unticked? |
|
||||
| **E5** | Does save-at-close give the extension no tick? (If so, the load-time trigger is the only one that catches that case) |
|
||||
|
||||
### Υ-W1 — The bank-root seam *(IN PROGRESS — dispatched 2026-09-25)*
|
||||
|
||||
| Track | Owns |
|
||||
|---|---|
|
||||
| **T1** `bank-root-seam` | Pure `bankRootFor` plus ONE shell resolver, `bankRootOf(ReaProject*)`. It replaces the extension's ~12 copies of "EnumProjects → `.rpp` dir". **No behaviour change** |
|
||||
| **T2** `instrument-own-project` | The instrument resolves ITS OWN project instead of the active tab (**a real bug fix**: today it reads the active tab), with an ordered list of candidate roots |
|
||||
|
||||
### Υ-W2 — Unsaved ext state, and the migration *(gated on E1; E2 shapes it)*
|
||||
|
||||
| Track | Owns |
|
||||
|---|---|
|
||||
| **T1** `unsaved-ext-state` | Lift the ext-state refusal for unsaved projects; create the temp root lazily; add the `unsaved_bank_root` key; update the ext-state contract text (Υ-D4, Υ-D5) |
|
||||
| **T2** `first-save-migration` | `migrateUnsavedBank` and both of its triggers (timer-poll transition, load-time breadcrumb); the console notice for items still pointing into temp (Υ-D1, Υ-D2, Υ-D3) |
|
||||
|
||||
### Υ-W3 — Lift the refusals, then the leftovers
|
||||
|
||||
| Track | Owns |
|
||||
|---|---|
|
||||
| **T1** `lift-unsaved-refusals` | Every refusal on an unsaved-project surface (Υ-D7, Υ-D8). Closes the `docs/TODO.md` entries "The capture path ignores `saveToActiveProject`'s return at four sites" and "`RunCaptureItemAssign`'s undo point does not follow the pattern its comment claims" |
|
||||
| **T2** `reclaim-unsaved-leftovers` *(later)* | Υ-D6's confirmed, dry-run-first reclaim action in the prune family |
|
||||
|
||||
### Pre-existing issues the design pass found — for awareness, NOT in scope
|
||||
|
||||
| # | Issue |
|
||||
|---|---|
|
||||
| **A1** | `relocateBankFolder` uses `overwrite_existing`, so a Save-As into a folder that holds another project's bank can overwrite files with the same name |
|
||||
| **A3** | Prune doesn't protect bank files that arrange items reference. The usage scan only looks at instrument FX, so a sample that was placed and then removed can be pruned, which takes the item offline |
|
||||
| **A4** | Save-As, including first save, marks the project dirty immediately (GUID re-mint plus `MarkProjectDirty` in `session.cpp`) |
|
||||
| **A5** | REAPER's "Clean current project directory" may offer to delete `reasampler_bank` files `[unverified]` |
|
||||
|
||||
---
|
||||
|
||||
## Phase Ω — Responsiveness, and the control surface's second pass
|
||||
|
||||
**Ships:** A mode switch that draws its result at once instead of two seconds later; a master
|
||||
@@ -1815,4 +1943,23 @@ Phase Lambda — ReaSampler on Linux (none of the seventeen; a direct reque
|
||||
gating a dispatch, W2-T3), L-F3 (copy-only invariant wording — gates nothing),
|
||||
L-F4 (declared support floor — gates W5, not a dispatch).
|
||||
Lambda registers NO VST3 parameter, ever — that table is Gamma-W4-T1's and is frozen.
|
||||
|
||||
Phase Upsilon — Temporary bank for never-saved projects (none of the seventeen; 2026-09-25)
|
||||
W0 Daniel's REAPER experiments (docs/verify-unsaved-bank.md) [E1 GATES W2]
|
||||
W1 The bank-root seam [IN PROGRESS, dispatched 2026-09-25]
|
||||
T1 bank-root-seam .............. pure bankRootFor + ONE shell bankRootOf(ReaProject*)
|
||||
replaces ~12 copies; no behaviour change
|
||||
T2 instrument-own-project ...... own project, not the active tab (a bug fix);
|
||||
ordered candidate roots
|
||||
W2 Unsaved ext state, and the migration [GATED: E1; E2 shapes it]
|
||||
T1 unsaved-ext-state ........... lazy temp root + unsaved_bank_root breadcrumb
|
||||
T2 first-save-migration ........ idempotent COPY; timer-poll + load-time triggers
|
||||
W3 Lift the refusals, then the leftovers
|
||||
T1 lift-unsaved-refusals ....... D7 + D8; closes two TODO.md capture-family entries
|
||||
T2 reclaim-unsaved-leftovers ... D6, later; prune family, dry-run first
|
||||
|
||||
Index paths stay relative to the bank root, so nothing in the index/ledger/refs changes
|
||||
at first save. D1-D8 SETTLED 2026-09-25; ZERO open forks. Awareness only, not in scope:
|
||||
A1 relocate overwrites, A3 prune ignores arrange refs, A4 Save-As dirties at once,
|
||||
A5 Clean-project-dir may offer the bank.
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user