docs: archive Phase B (B1-B5, B-cap, R-B) to COMPLETED; reconcile CLAUDE.md
This commit is contained in:
@@ -34,6 +34,7 @@ Key targets (see CMakeLists.txt for the full list):
|
||||
| `mode_switch_tests` | executable | Pure unit tests for `mode_switch` — no REAPER, no DAW. |
|
||||
| `bank_book_tests` | executable | Pure unit tests for `bank_book` — no REAPER, no DAW. |
|
||||
| `wav_trim_tests` | executable | Pure unit tests for `wav_trim` — no REAPER, no DAW. |
|
||||
| `owned_manifest_tests` | executable | Pure unit tests for `owned_manifest` — no REAPER, no DAW. |
|
||||
| `reaper_reasampler` | loadable module | The actual extension binary (`.dll` / `.dylib` / `.so`). |
|
||||
|
||||
### macOS / Linux: SWELL dialog resources
|
||||
@@ -56,17 +57,18 @@ There is no hot-reload. Copy the built binary into REAPER's `UserPlugins/` folde
|
||||
- `view_mode_model` — Design View mode system: mode registry, GUID-keyed membership, folder-tree-aware visibility derivation, snapshot-based park/restore planner, JSON round-trip. Mirror of `bank_model` for the Design View phase.
|
||||
- `view_tree` — pure `I_FOLDERDEPTH`→FolderTree helper for the Design View shell; no REAPER types at the boundary.
|
||||
- `mode_switch` — REAPER-free segment layout + hit-test math for the bank_panel's Design View mode switch; divides a header rectangle into N equal segments and hit-tests a point to a segment. Mirror of `bank_grid`.
|
||||
- `bank_book` — multi-bank registry (Phase B): an ordered set of banks (pool seeded as bank-zero + named banks), each wrapping a `BankIndex`. Owns create/rename/reorder/delete of named banks, pool privileges (un-deletable/un-renamable/un-evacuable, never zero banks) enforced in-model, active-bank id, index-only move/copy of a sample between banks, JSON round-trip + legacy-`bank_index`→pool migration. Wraps `BankIndex` (bank_model untouched; no `bankId` on `Sample`).
|
||||
- `bank_book` — multi-bank registry (Phase B): an ordered set of banks (pool seeded as bank-zero + named banks), each wrapping a `BankIndex`. Owns create/rename/reorder/delete of named banks, pool privileges (un-deletable/un-renamable/un-evacuable, never zero banks) enforced in-model, active-bank id, index-only move/copy/remove of a sample between or from banks, `hashReferencedElsewhere` cross-bank reference query, JSON round-trip + legacy-`bank_index`→pool migration. Wraps `BankIndex` (bank_model untouched; no `bankId` on `Sample`).
|
||||
- `owned_manifest` — owned-file manifest seam (Phase B B-cap): the set of project-relative files the capture path itself created, persisted under the `"owned_files"` ext-state key, so Phase R prune can distinguish the bank system's own orphans from hand-dropped files. Deliberately decoupled from `bank_book` — tracks files created, not index membership. Phase R (R1/R2) consumes it; no prune logic here.
|
||||
- `wav_trim` — 32-bit-float WAV parse + header-aware truncate plan (RIFF/data size rewrite) for the realtime tail's PCM decay-scan trim (T2). Rejects WAVE_FORMAT_EXTENSIBLE with non-float SubFormat GUID. Depends on `peaks` for the `AudioSample` float alias.
|
||||
|
||||
**REAPER-facing shells:**
|
||||
- `capture` — `ICaptureBackend` interface; `OfflineRenderBackend` (deterministic default) and `RealtimeRecordBackend`. Input: `CaptureRequest`. Output: finished file + populated `Sample` handed to `bank_model`.
|
||||
- `insert` — placement via `InsertMedia`; conform-to-project-tempo is an explicit opt-in flag, never silent stretching.
|
||||
- `bank_panel` — docked LICE-drawn grid: thumbnails, audition, multi-select, keyboard navigation.
|
||||
- `persist` — project ext state (`SetProjExtState` / `GetProjExtState`, namespace `"reasampler"`) ↔ `bank_model` JSON (`"bank_index"` key) + `ViewModeModel` JSON (`"view_state"` key) + `TailSetting` JSON (`"tail_setting"` key); project-relative path resolution.
|
||||
- `persist` — project ext state (`SetProjExtState` / `GetProjExtState`, namespace `"reasampler"`) ↔ `BankBook` JSON (`"banks"` key) + `ViewModeModel` JSON (`"view_state"` key) + `TailSetting` JSON (`"tail_setting"` key) + `OwnedManifest` JSON (`"owned_files"` key); project-relative path resolution. A `projectconfig` hook (`BeginLoadProjectState(isUndo)`) triggers a deferred session reload on undo/redo so Ctrl-Z/redo visibly restores book/view/tail/manifest in-session.
|
||||
- `view` — Design View shell: reads the folder tree via `view_tree`, snapshots flag values before parking, drives hide + CPU-park on inactive-mode leaves (`B_SHOWINTCP`/`B_SHOWINMIXER`/`B_MAINSEND`/`I_FXEN` + per-FX offline) and derived visibility on parents; restores from snapshot. Never touches master or `B_MUTE`/`I_SOLO`.
|
||||
- `track_guid` — shared `MediaTrack*` → canonical GUID-string formatter; single source of truth for membership keys used by both the view shell and the actions layer.
|
||||
- `actions` — registers the capture/placement/slot action family and the Design View action family (toggle active mode, activate Arrange/Design, tag/untag selected tracks, show-both); routes each to the modules above via the `command_id`/`gaccel`/`hookcommand` contract.
|
||||
- `actions` — registers the capture/placement/slot action family, the Design View action family (toggle active mode, activate Arrange/Design, tag/untag selected tracks, show-both), and the multi-bank action family (create/rename/reorder/delete bank, evacuate, activate, move/copy/remove selected samples); routes each to the modules above via the `command_id`/`gaccel`/`hookcommand` contract. Every bank index verb wraps its mutation in a batched REAPER undo point (`Undo_BeginBlock2`/`EndBlock2`, `UNDO_STATE_MISCCFG`) so one bank operation is one Ctrl-Z.
|
||||
|
||||
## REAPER extension contract (src/main.cpp)
|
||||
|
||||
|
||||
+229
@@ -654,3 +654,232 @@ items exempt; one undo block per action. ctest 14/14 green.
|
||||
lane-split marker has no natural cheap home in the bank panel; the mode switch
|
||||
already shows the active mode. Preserved as a deferred/backlog note in PLAN.md
|
||||
Phase D2 — not silently dropped.
|
||||
|
||||
---
|
||||
|
||||
# Phase B — Multi-bank (parallel to the M0–M11 capture roadmap and Phase D)
|
||||
|
||||
> **Separate phase namespace.** The M-numbers belong to the capture pillar
|
||||
> (M0–M11); the D-letters belong to Design View. Multi-bank is a third orthogonal
|
||||
> pillar — generalizing the single bank into a pool + named banks — so it takes its
|
||||
> own **lettered** namespace (B1, B2, …). "B" reads for **Banks** and, like Phase D,
|
||||
> keeps the roadmaps from colliding on numbering: Phase B is not "the twelfth
|
||||
> capture step," it is a different pillar. Authoritative spec: **CONTEXT.md
|
||||
> §Multi-bank**. Product framing: `docs/product/multi-bank.md`.
|
||||
|
||||
## B1 — bank_book (pure)
|
||||
**Goal:** REAPER-free bank registry wrapping N `BankIndex` instances: pool seeded +
|
||||
privileged, create/rename/reorder/delete named banks, active-bank id, move/copy a
|
||||
sample between banks, JSON round-trip + legacy-migration. The heart of the phase;
|
||||
mirror of `bank_model` / `view_mode_model`; **`BankIndex` untouched (additive)**.
|
||||
CONTEXT.md §Multi-bank (Module architecture — pure).
|
||||
**Verify:** CTest green. Pool always present, un-deletable, un-renamable,
|
||||
un-evacuable (rules rejected in-model). Active-bank defaults to pool. Move is
|
||||
index-only (source loses entry, destination gains it) and observes destination
|
||||
collapse-by-hash; copy leaves source intact. Delete drops member index entries.
|
||||
Evacuate moves all members to the pool, leaving the bank empty. JSON round-trip
|
||||
lossless across pool-as-bank-zero + named banks + per-bank indices + ordinals +
|
||||
active id. Legacy `bank_index` JSON parses into `{ pool }` with zero named banks.
|
||||
|
||||
- [x] Bank registry: ordered `{ bank id, display name, ordinal, BankIndex }`; pool
|
||||
seeded with fixed id + fixed name; create / rename / reorder / delete named banks
|
||||
(delete drops the bank's member index entries).
|
||||
- [x] Pool-privilege rules enforced in-model: reject delete-pool, reject
|
||||
rename-pool, reject evacuate-pool, never allow zero banks.
|
||||
- [x] Active-bank id (get/set; defaults to pool); resolve active bank's `BankIndex`.
|
||||
- [x] Move sample between banks (index-only; destination collapse-by-hash observed;
|
||||
source entry removed).
|
||||
- [x] Copy sample between banks (index-only; source entry retained; destination
|
||||
collapse-by-hash observed).
|
||||
- [x] Evacuate bank: move every member to the pool (index-only; destination
|
||||
collapse-by-hash observed), leaving the bank empty; pool cannot be evacuated.
|
||||
- [x] JSON round-trip: pool-as-bank-zero inside the blob + named banks + per-bank
|
||||
indices + ordinals + active id.
|
||||
- [x] Legacy migration: a bare `bank_index` JSON promotes to the pool's index with
|
||||
zero named banks (one-way, lossless; blob authoritative thereafter).
|
||||
- [x] Tests: pool privileges (delete/rename/evacuate rejected); move
|
||||
source-loses/dest-gains; copy source-retained; evacuate empties source into pool
|
||||
with dest collapse; cross-bank same-hash coexistence; dest collapse on move into a
|
||||
bank already holding the hash; JSON lossless; legacy migration.
|
||||
|
||||
> **Phase-B-wide undo (fork R-B, settled 2026-07-24 — batched REAPER undo points).**
|
||||
> Every index verb across B1–B5 (create/rename/reorder/delete-bank, move, copy,
|
||||
> evacuate, remove) wraps its bank/index mutation in a **batched REAPER undo point**
|
||||
> (`Undo_BeginBlock` / `Undo_EndBlock`), so one bank operation is one Ctrl-Z. This is
|
||||
> a cross-cutting decision that retro-touches B1–B4, not a B5-local one; the
|
||||
> per-verb points above inherit it. **Must-verify before build:** confirm against
|
||||
> `vendor/reaper-sdk` that `"reasampler"` ext-state mutations participate correctly
|
||||
> in `Undo_BeginBlock`/`Undo_EndBlock` undo blocks — the whole approach depends on
|
||||
> it. See CONTEXT.md §Sample removal (Guardrails) + product notes §Fork R-B.
|
||||
|
||||
**Notes/decisions (R-B — Phase-B-wide undo, landed):**
|
||||
- Every bank index verb — bindable action AND panel gesture (menu/drag/Delete key) —
|
||||
is one batched REAPER undo point (`Undo_BeginBlock2`/`EndBlock2`,
|
||||
`UNDO_STATE_MISCCFG`); ext-state participates in undo via `UNDO_STATE_MISCCFG`
|
||||
("extensions!"), SDK-verified. A `projectconfig` hook
|
||||
(`BeginLoadProjectState(isUndo)`) triggers a deferred session reload so Ctrl-Z/redo
|
||||
visibly restores book/view/tail/manifest in-session. Rejected/no-op ops open no
|
||||
undo point; unsaved-project ops discard the empty block.
|
||||
|
||||
---
|
||||
|
||||
## B-cap — owned-file manifest seam (capture writes; prune consumes in Phase R)
|
||||
**Goal:** Capture writes each file it creates into an **owned-file manifest**
|
||||
persisted in the `"reasampler"` ext-state, so Phase R prune can later distinguish
|
||||
the bank system's own orphans from hand-dropped files. Consumed only in Phase R
|
||||
(R1/R2) — landed early here because reconstructing the manifest retroactively is a
|
||||
backfill cliff (fork R-D, settled 2026-07-24: *defer the feature, design the seam*).
|
||||
CONTEXT.md §Prune (Settled decisions — orphan attribution) + product notes §Fork R-D.
|
||||
**Verify:** every file the capture path creates is recorded in the owned-file
|
||||
manifest; the manifest round-trips through the `"reasampler"` ext-state (Save / Save
|
||||
As / reopen); relative-paths-only preserved. Prune's consumption of it is Phase R.
|
||||
**Depends on:** the capture add-path (M7) + persist blob machinery (M4 / B2).
|
||||
|
||||
- [x] Capture records each created file into an owned-file manifest (the set of
|
||||
files the book has created), persisted in the `"reasampler"` ext-state (sibling
|
||||
`owned_files` key — persistence shape resolved at build time: sibling key, not
|
||||
folded into the `banks` blob).
|
||||
- [x] Manifest round-trips: survives Save / Save As / reopen via the M4 blob
|
||||
machinery; relative-paths-only. (Consumed by Phase R R1/R2 — not consumed here.)
|
||||
|
||||
**Notes/decisions:**
|
||||
- Pure `owned_manifest` module (`src/owned_manifest.{h,cpp}`): relative paths, dedup,
|
||||
JSON round-trip. Deliberately decoupled from `bank_book` — tracks files created, not
|
||||
index membership; sample-remove is not manifest-remove. Unit-tested via new
|
||||
`owned_manifest_tests` CTest target. Persisted under the `"owned_files"` ext-state
|
||||
key. Both capture commit paths (offline + realtime) record created files. Joins the
|
||||
undo-reload set.
|
||||
|
||||
---
|
||||
|
||||
## B2 — persist slice (banks ↔ project ext state)
|
||||
**Goal:** Serialize the book under the `banks` key in `"reasampler"` alongside the
|
||||
existing sections, with the pool folded in as bank-zero; migrate a legacy
|
||||
`bank_index` key into the pool on first load and retire the legacy key; reload-on-open
|
||||
and Save-As survival via the existing M4 machinery. CONTEXT.md §Multi-bank (persist).
|
||||
**Verify (in DAW):** Banks + named banks + active bank + all per-bank samples survive
|
||||
Save / Save As / close+reopen; **relative paths only**; bank travels with the `.rpp`;
|
||||
a project saved before this phase (legacy `bank_index` only) loads as pool + zero
|
||||
named banks with no sample loss, and after save carries `banks` with no `bank_index`
|
||||
written.
|
||||
**Depends on:** B1. (Persistence-key fork settled — fork 1 (a): pool inside the
|
||||
`banks` blob, legacy key retired after one-way migration.)
|
||||
|
||||
- [x] Serialize/deserialize the book under the `banks` key (pool-as-bank-zero inside
|
||||
the blob; distinct section from `view_state`; no `bank_index` key written going
|
||||
forward).
|
||||
- [x] Legacy-migration path on load: absent `banks` + present `bank_index` → promote
|
||||
into pool, mint the blob, treat blob as authoritative (legacy key retired).
|
||||
- [x] Session exposes the book; the active bank's `BankIndex` is the capture add
|
||||
target (route the M7 capture family through it — additive to M7, no M7 rewrite).
|
||||
- [x] Confirm survival across Save / Save As; confirm legacy-project load path.
|
||||
|
||||
---
|
||||
|
||||
## B3 — actions
|
||||
**Goal:** Bindable action set for the multi-bank workflow. CONTEXT.md §Multi-bank
|
||||
(actions). **Verify (in DAW):** Each action registered (bindable in Actions list);
|
||||
bank-activate + move/copy + evacuate MIDI-bindable; create/rename/delete/evacuate
|
||||
drive the B1 model via the B2-persisted session.
|
||||
**Depends on:** B1, B2.
|
||||
|
||||
- [x] Create bank / rename bank / delete bank (delete drops member index entries;
|
||||
confirm-on-non-empty offered at the UI layer in B4).
|
||||
- [x] Evacuate bank → pool (move all members back to the pool; refuses on the pool).
|
||||
- [x] Activate bank (direct-by-id + cycle).
|
||||
- [x] Move selected samples → bank / copy selected samples → bank (move is default).
|
||||
- [x] Pool full-height / banks full-height toggles.
|
||||
- [x] Register each (`command_id`/`gaccel`/`hookcommand`); bank-activate + move/copy
|
||||
+ evacuate MIDI-bindable.
|
||||
|
||||
---
|
||||
|
||||
## B4 — bank_panel vertical split (UI)
|
||||
**Goal:** The vertical-split bank window — pool on top, named-banks tab-page region
|
||||
below, full-height toggles — extending the M5 docked grid. CONTEXT.md §Multi-bank
|
||||
(bank_panel). **Verify (in DAW):** Pool grid renders on top; named-banks tab strip
|
||||
below (empty when no named banks, one tab per named bank); active-bank **unmistakably**
|
||||
indicated; both full-height toggles collapse the split correctly; sample move/copy
|
||||
affordance works; non-empty delete confirms and offers evacuate; the Design View mode
|
||||
switch in the header is unaffected.
|
||||
**Depends on:** B1, B2, B3. (Tab rendering + move-affordance mechanics — fork 5 —
|
||||
settled 2026-07-23: LICE-drawn tabs + both move affordances; see Phase B open questions
|
||||
and product notes → *Fork 5 — settled*.)
|
||||
|
||||
- [x] Vertical split: pool grid region (top) + named-banks tab-page region (bottom).
|
||||
- [x] Named-banks tab strip: **LICE-drawn** (matching the M5 grid + Design View
|
||||
segmented switch, not SWELL-native — fork 5a); one tab per named bank; empty state
|
||||
when none.
|
||||
- [x] Tab-strip overflow/scroll affordance (fork 5a): scroll/chevron overflow shipped
|
||||
with the strip.
|
||||
- [x] Pool full-height / banks full-height toggle affordances wired to B3.
|
||||
- [x] Active-bank indicator — **visually unmistakable** (settled constraint).
|
||||
- [x] Create / rename / delete / activate / evacuate affordances driving B3 actions.
|
||||
- [x] Delete confirms on a non-empty bank, naming the evacuate alternative.
|
||||
- [x] Sample move affordance — **both** (fork 5b): a "move to bank" menu on the current
|
||||
selection (bindable front-end for the B3 move action) **and** drag-between-regions.
|
||||
Copy is the deliberate secondary act, offered on the menu.
|
||||
- [x] Drag mis-drop mitigation (fork 5b): clear drop-target highlighting on the
|
||||
destination region/tab during a drag.
|
||||
|
||||
**Notes/decisions:**
|
||||
- New pure module `src/tab_strip.{h,cpp}`: named-banks tab-strip geometry (B4) —
|
||||
strip rect + N tabs + scroll offset → per-tab rects (overflow-clipped), overflow
|
||||
chevron reservation + maxScroll, and point → tab/chevron hit-test. Unit-tested via
|
||||
`tab_strip_tests` CTest target. Mirror of `mode_switch`.
|
||||
- Active-bank indicator placement (the open polish detail from the Phase B open
|
||||
questions) was resolved at build time in the panel implementation.
|
||||
- Post-landing: m11's console-chatter policy applied to Phase B messages (successes
|
||||
silent, failures kept).
|
||||
|
||||
---
|
||||
|
||||
## B5 — sample-remove (the missing sample-level verb)
|
||||
**Goal:** Drop an individual `Sample`'s index entry from a bank or the pool —
|
||||
the sample-level companion to move/copy/evacuate/delete-bank. Index-only,
|
||||
non-destructive to the file; exposes the `BankIndex::remove` primitive that
|
||||
`bank_model` already has (wires it, does not add it). CONTEXT.md §Sample removal.
|
||||
Product framing + open forks: `docs/product/removal-and-prune.md` §Sample-remove.
|
||||
**Verify (in DAW):** Remove drops the selected sample's entry from the target
|
||||
bank; a same-hash entry in another bank is untouched (no cross-bank dedup);
|
||||
pool *contents* are removable while pool-container privileges hold; removing the
|
||||
last index reference to a file leaves that file on disk (orphaned until prune —
|
||||
never deleted by remove); non-destructive (index + ext-state only, no file, no
|
||||
timeline item).
|
||||
**Depends on:** B1, B2, B3 (action set), B4 (panel affordance).
|
||||
|
||||
- [x] Surface `BankIndex::remove` through `bank_book`: remove a `Sample` from a
|
||||
bank's index; pool contents removable, pool-container privileges unchanged.
|
||||
- [x] "Remove selected sample(s)" action (`command_id`/`gaccel`/`hookcommand`),
|
||||
MIDI-bindable; carries a `scope: this-bank | all-banks` seam (fork R-A, settled
|
||||
2026-07-24: **this-bank** is the default and only surfaced affordance; all-banks
|
||||
stays a latent seam-only parameter, not shipped).
|
||||
- [x] `bank_panel` remove affordance on the current selection (reuse M5 selection
|
||||
model, as move/copy do).
|
||||
- [x] Confirm-on-last-reference guardrail: remove that orphans a file (no other
|
||||
bank references it) confirms, naming the orphaned-until-prune consequence;
|
||||
remove of a still-referenced sample does not confirm.
|
||||
- [x] Tests: remove drops the target entry; same-hash entry in another bank
|
||||
survives; remove-from-pool allowed; last-reference remove leaves an orphan (file
|
||||
untouched); non-destructive (no file/timeline mutation).
|
||||
|
||||
**Notes/decisions (B5 forks — settled 2026-07-24):**
|
||||
- **R-A — remove scope.** Settled: **this-bank**. Removes the entry from the bank in
|
||||
view only; the `scope: this-bank | all-banks` seam stays in the action signature
|
||||
but all-banks is a latent parameter, not a surfaced verb.
|
||||
- `hashReferencedElsewhere` cross-bank reference query added to `bank_book` to
|
||||
support the confirm-on-last-reference guardrail without coupling to the panel.
|
||||
- Both `bank_panel` context menu ("Remove selected sample(s)") and Delete key
|
||||
affordance wired; panel gesture is also one batched undo point (R-B applies).
|
||||
|
||||
---
|
||||
|
||||
## Phase B open questions — all resolved
|
||||
|
||||
All five forks settled by Daniel (2026-07-23): persistence key = fold pool into `banks`,
|
||||
retire legacy key (1a); delete drops members + add evacuate verb (2); move is the
|
||||
default gesture (3); active-bank/shown-tab distinct with an unmistakable indicator (4);
|
||||
LICE-drawn tabs + overflow, and both move affordances with drop-highlighting (5).
|
||||
Active-bank indicator placement (the one residual polish detail) was resolved at
|
||||
build time. B5 forks R-A and R-B settled 2026-07-24 (see B5 and B1 notes above).
|
||||
Both in `docs/product/removal-and-prune.md` §Fork R-A / §Fork R-B.
|
||||
|
||||
@@ -130,206 +130,6 @@ landed milestone.
|
||||
|
||||
---
|
||||
|
||||
# Phase B — Multi-bank (parallel to the M0–M11 capture roadmap and Phase D)
|
||||
|
||||
> **Separate phase namespace.** The M-numbers belong to the capture pillar
|
||||
> (M0–M11); the D-letters belong to Design View. Multi-bank is a third orthogonal
|
||||
> pillar — generalizing the single bank into a pool + named banks — so it takes its
|
||||
> own **lettered** namespace (B1, B2, …). "B" reads for **Banks** and, like Phase D,
|
||||
> keeps the roadmaps from colliding on numbering: Phase B is not "the twelfth
|
||||
> capture step," it is a different pillar. Authoritative spec: **CONTEXT.md
|
||||
> §Multi-bank**. Product framing: `docs/product/multi-bank.md`. When a point lands,
|
||||
> doc-keeper moves it to `COMPLETED.md`.
|
||||
|
||||
## B1 — bank_book (pure)
|
||||
**Goal:** REAPER-free bank registry wrapping N `BankIndex` instances: pool seeded +
|
||||
privileged, create/rename/reorder/delete named banks, active-bank id, move/copy a
|
||||
sample between banks, JSON round-trip + legacy-migration. The heart of the phase;
|
||||
mirror of `bank_model` / `view_mode_model`; **`BankIndex` untouched (additive)**.
|
||||
CONTEXT.md §Multi-bank (Module architecture — pure).
|
||||
**Verify:** CTest green. Pool always present, un-deletable, un-renamable,
|
||||
un-evacuable (rules rejected in-model). Active-bank defaults to pool. Move is
|
||||
index-only (source loses entry, destination gains it) and observes destination
|
||||
collapse-by-hash; copy leaves source intact. Delete drops member index entries.
|
||||
Evacuate moves all members to the pool, leaving the bank empty. JSON round-trip
|
||||
lossless across pool-as-bank-zero + named banks + per-bank indices + ordinals +
|
||||
active id. Legacy `bank_index` JSON parses into `{ pool }` with zero named banks.
|
||||
|
||||
- [ ] Bank registry: ordered `{ bank id, display name, ordinal, BankIndex }`; pool
|
||||
seeded with fixed id + fixed name; create / rename / reorder / delete named banks
|
||||
(delete drops the bank's member index entries).
|
||||
- [ ] Pool-privilege rules enforced in-model: reject delete-pool, reject
|
||||
rename-pool, reject evacuate-pool, never allow zero banks.
|
||||
- [ ] Active-bank id (get/set; defaults to pool); resolve active bank's `BankIndex`.
|
||||
- [ ] Move sample between banks (index-only; destination collapse-by-hash observed;
|
||||
source entry removed).
|
||||
- [ ] Copy sample between banks (index-only; source entry retained; destination
|
||||
collapse-by-hash observed).
|
||||
- [ ] Evacuate bank: move every member to the pool (index-only; destination
|
||||
collapse-by-hash observed), leaving the bank empty; pool cannot be evacuated.
|
||||
- [ ] JSON round-trip: pool-as-bank-zero inside the blob + named banks + per-bank
|
||||
indices + ordinals + active id.
|
||||
- [ ] Legacy migration: a bare `bank_index` JSON promotes to the pool's index with
|
||||
zero named banks (one-way, lossless; blob authoritative thereafter).
|
||||
- [ ] Tests: pool privileges (delete/rename/evacuate rejected); move
|
||||
source-loses/dest-gains; copy source-retained; evacuate empties source into pool
|
||||
with dest collapse; cross-bank same-hash coexistence; dest collapse on move into a
|
||||
bank already holding the hash; JSON lossless; legacy migration.
|
||||
|
||||
> **Phase-B-wide undo (fork R-B, settled 2026-07-24 — batched REAPER undo points).**
|
||||
> Every index verb across B1–B5 (create/rename/reorder/delete-bank, move, copy,
|
||||
> evacuate, remove) wraps its bank/index mutation in a **batched REAPER undo point**
|
||||
> (`Undo_BeginBlock` / `Undo_EndBlock`), so one bank operation is one Ctrl-Z. This is
|
||||
> a cross-cutting decision that retro-touches B1–B4, not a B5-local one; the
|
||||
> per-verb points above inherit it. **Must-verify before build:** confirm against
|
||||
> `vendor/reaper-sdk` that `"reasampler"` ext-state mutations participate correctly
|
||||
> in `Undo_BeginBlock`/`Undo_EndBlock` undo blocks — the whole approach depends on
|
||||
> it. See CONTEXT.md §Sample removal (Guardrails) + product notes §Fork R-B.
|
||||
|
||||
## B-cap — owned-file manifest seam (capture writes; prune consumes in Phase R)
|
||||
**Goal:** Capture writes each file it creates into an **owned-file manifest**
|
||||
persisted in the `"reasampler"` ext-state, so Phase R prune can later distinguish
|
||||
the bank system's own orphans from hand-dropped files. Consumed only in Phase R
|
||||
(R1/R2) — landed early here because reconstructing the manifest retroactively is a
|
||||
backfill cliff (fork R-D, settled 2026-07-24: *defer the feature, design the seam*).
|
||||
CONTEXT.md §Prune (Settled decisions — orphan attribution) + product notes §Fork R-D.
|
||||
**Verify:** every file the capture path creates is recorded in the owned-file
|
||||
manifest; the manifest round-trips through the `"reasampler"` ext-state (Save / Save
|
||||
As / reopen); relative-paths-only preserved. Prune's consumption of it is Phase R.
|
||||
**Depends on:** the capture add-path (M7) + persist blob machinery (M4 / B2).
|
||||
|
||||
- [ ] Capture records each created file into an owned-file manifest (the set of
|
||||
files the book has created), persisted in the `"reasampler"` ext-state (sibling
|
||||
key or folded into the `banks` blob — persistence shape is a small build-time
|
||||
residual, not a fork).
|
||||
- [ ] Manifest round-trips: survives Save / Save As / reopen via the M4 blob
|
||||
machinery; relative-paths-only. (Consumed by Phase R R1/R2 — not consumed here.)
|
||||
|
||||
## B2 — persist slice (banks ↔ project ext state)
|
||||
**Goal:** Serialize the book under the `banks` key in `"reasampler"` alongside the
|
||||
existing sections, with the pool folded in as bank-zero; migrate a legacy
|
||||
`bank_index` key into the pool on first load and retire the legacy key; reload-on-open
|
||||
and Save-As survival via the existing M4 machinery. CONTEXT.md §Multi-bank (persist).
|
||||
**Verify (in DAW):** Banks + named banks + active bank + all per-bank samples survive
|
||||
Save / Save As / close+reopen; **relative paths only**; bank travels with the `.rpp`;
|
||||
a project saved before this phase (legacy `bank_index` only) loads as pool + zero
|
||||
named banks with no sample loss, and after save carries `banks` with no `bank_index`
|
||||
written.
|
||||
**Depends on:** B1. (Persistence-key fork settled — fork 1 (a): pool inside the
|
||||
`banks` blob, legacy key retired after one-way migration.)
|
||||
|
||||
- [ ] Serialize/deserialize the book under the `banks` key (pool-as-bank-zero inside
|
||||
the blob; distinct section from `view_state`; no `bank_index` key written going
|
||||
forward).
|
||||
- [ ] Legacy-migration path on load: absent `banks` + present `bank_index` → promote
|
||||
into pool, mint the blob, treat blob as authoritative (legacy key retired).
|
||||
- [ ] Session exposes the book; the active bank's `BankIndex` is the capture add
|
||||
target (route the M7 capture family through it — additive to M7, no M7 rewrite).
|
||||
- [ ] Confirm survival across Save / Save As; confirm legacy-project load path.
|
||||
|
||||
## B3 — actions
|
||||
**Goal:** Bindable action set for the multi-bank workflow. CONTEXT.md §Multi-bank
|
||||
(actions). **Verify (in DAW):** Each action registered (bindable in Actions list);
|
||||
bank-activate + move/copy + evacuate MIDI-bindable; create/rename/delete/evacuate
|
||||
drive the B1 model via the B2-persisted session.
|
||||
**Depends on:** B1, B2.
|
||||
|
||||
- [ ] Create bank / rename bank / delete bank (delete drops member index entries;
|
||||
confirm-on-non-empty offered at the UI layer in B4).
|
||||
- [ ] Evacuate bank → pool (move all members back to the pool; refuses on the pool).
|
||||
- [ ] Activate bank (direct-by-id + cycle).
|
||||
- [ ] Move selected samples → bank / copy selected samples → bank (move is default).
|
||||
- [ ] Pool full-height / banks full-height toggles.
|
||||
- [ ] Register each (`command_id`/`gaccel`/`hookcommand`); bank-activate + move/copy
|
||||
+ evacuate MIDI-bindable.
|
||||
|
||||
## B4 — bank_panel vertical split (UI)
|
||||
**Goal:** The vertical-split bank window — pool on top, named-banks tab-page region
|
||||
below, full-height toggles — extending the M5 docked grid. CONTEXT.md §Multi-bank
|
||||
(bank_panel). **Verify (in DAW):** Pool grid renders on top; named-banks tab strip
|
||||
below (empty when no named banks, one tab per named bank); active-bank **unmistakably**
|
||||
indicated; both full-height toggles collapse the split correctly; sample move/copy
|
||||
affordance works; non-empty delete confirms and offers evacuate; the Design View mode
|
||||
switch in the header is unaffected.
|
||||
**Depends on:** B1, B2, B3. (Tab rendering + move-affordance mechanics — fork 5 —
|
||||
settled 2026-07-23: LICE-drawn tabs + both move affordances; see Phase B open questions
|
||||
and product notes → *Fork 5 — settled*.)
|
||||
|
||||
- [ ] Vertical split: pool grid region (top) + named-banks tab-page region (bottom).
|
||||
- [ ] Named-banks tab strip: **LICE-drawn** (matching the M5 grid + Design View
|
||||
segmented switch, not SWELL-native — fork 5a); one tab per named bank; empty state
|
||||
when none. **Verify LICE tab draw against the M5 reference before use.**
|
||||
- [ ] Tab-strip overflow/scroll affordance — **in scope from the start** (fork 5a): a
|
||||
naive fixed-width LICE strip breaks down at ~8–12 tabs, so ship scroll/chevron
|
||||
overflow with the strip, do not defer it.
|
||||
- [ ] Pool full-height / banks full-height toggle affordances wired to B3.
|
||||
- [ ] Active-bank indicator — **visually unmistakable** (settled constraint);
|
||||
placement (per-region header / single readout / lit-tab) is the residual polish
|
||||
detail.
|
||||
- [ ] Create / rename / delete / activate / evacuate affordances driving B3 actions.
|
||||
- [ ] Delete confirms on a non-empty bank, naming the evacuate alternative.
|
||||
- [ ] Sample move affordance — **both** (fork 5b): a "move to bank" menu on the current
|
||||
selection (bindable front-end for the B3 move action) **and** drag-between-regions.
|
||||
Copy is the deliberate secondary act, offered on the menu.
|
||||
- [ ] Drag mis-drop mitigation (fork 5b): clear drop-target highlighting on the
|
||||
destination region/tab during a drag; a mis-drop is recoverable by design (move is
|
||||
index-only and reversible). **Verify the drag hit-test doesn't collide with the M5
|
||||
grid's multi-select drag.**
|
||||
|
||||
## B5 — sample-remove (the missing sample-level verb)
|
||||
**Goal:** Drop an individual `Sample`'s index entry from a bank or the pool —
|
||||
the sample-level companion to move/copy/evacuate/delete-bank. Index-only,
|
||||
non-destructive to the file; exposes the `BankIndex::remove` primitive that
|
||||
`bank_model` already has (wires it, does not add it). CONTEXT.md §Sample removal.
|
||||
Product framing + open forks: `docs/product/removal-and-prune.md` §Sample-remove.
|
||||
**Verify (in DAW):** Remove drops the selected sample's entry from the target
|
||||
bank; a same-hash entry in another bank is untouched (no cross-bank dedup);
|
||||
pool *contents* are removable while pool-container privileges hold; removing the
|
||||
last index reference to a file leaves that file on disk (orphaned until prune —
|
||||
never deleted by remove); non-destructive (index + ext-state only, no file, no
|
||||
timeline item).
|
||||
**Depends on:** B1, B2, B3 (action set), B4 (panel affordance).
|
||||
|
||||
- [ ] Surface `BankIndex::remove` through `bank_book`: remove a `Sample` from a
|
||||
bank's index; pool contents removable, pool-container privileges unchanged.
|
||||
- [ ] "Remove selected sample(s)" action (`command_id`/`gaccel`/`hookcommand`),
|
||||
MIDI-bindable; carries a `scope: this-bank | all-banks` seam (fork R-A, settled
|
||||
2026-07-24: **this-bank** is the default and only surfaced affordance; all-banks
|
||||
stays a latent seam-only parameter, not shipped).
|
||||
- [ ] `bank_panel` remove affordance on the current selection (reuse M5 selection
|
||||
model, as move/copy do).
|
||||
- [ ] Confirm-on-last-reference guardrail: remove that orphans a file (no other
|
||||
bank references it) confirms, naming the orphaned-until-prune consequence;
|
||||
remove of a still-referenced sample does not confirm.
|
||||
- [ ] Tests: remove drops the target entry; same-hash entry in another bank
|
||||
survives; remove-from-pool allowed; last-reference remove leaves an orphan (file
|
||||
untouched); non-destructive (no file/timeline mutation).
|
||||
|
||||
## Phase B open questions
|
||||
All five forks settled by Daniel (2026-07-23): persistence key = fold pool into `banks`,
|
||||
retire legacy key (1a); delete drops members + add evacuate verb (2); move is the
|
||||
default gesture (3); active-bank/shown-tab distinct with an unmistakable indicator (4);
|
||||
LICE-drawn tabs + overflow, and both move affordances with drop-highlighting (5).
|
||||
Folded into CONTEXT.md §Multi-bank + the B1–B4 points above. Phase B is fully settled and
|
||||
ready to scope into implementation waves. One polish detail remains:
|
||||
|
||||
- **Active-bank indicator placement** — per-region headers vs. single header readout
|
||||
vs. lit-tab. "Unmistakable" is settled; only placement is open. Polish detail.
|
||||
(touches B4)
|
||||
|
||||
**B5 sample-remove forks — settled 2026-07-24:**
|
||||
- **R-A — remove scope.** Settled: **this-bank**. Removes the entry from the bank in
|
||||
view only; the `scope: this-bank | all-banks` seam stays in the action signature
|
||||
but all-banks is a latent parameter, not a surfaced verb. Folded into the B5 action
|
||||
point above.
|
||||
- **R-B — undo model (Phase-B-wide).** Settled: **batched REAPER undo points**
|
||||
(`Undo_BeginBlock`/`Undo_EndBlock`), one bank op = one Ctrl-Z. Applies across
|
||||
B1–B5 (retro-touches B1–B4) — captured as the cross-cutting note under B1 above,
|
||||
with the ext-state-participation SDK check as a must-verify-before-build.
|
||||
Both in `docs/product/removal-and-prune.md` §Fork R-A / §Fork R-B.
|
||||
|
||||
---
|
||||
|
||||
# Phase R — Reclaim (file lifecycle: the prune path)
|
||||
|
||||
> **New pillar, own lettered namespace.** Prune is the file-lifecycle path the
|
||||
|
||||
Reference in New Issue
Block a user