# PLAN.md — ReaSampler milestone roadmap Living milestone roadmap for ReaSampler. Derived from CONTEXT.md's 11-step build order; CONTEXT.md remains the authoritative spec — this file is the tickable checklist, not a re-statement of the spec. When a point lands, doc-keeper removes it here and appends it to `COMPLETED.md`. **Conventions** - One checkbox `- [ ]` = one discrete, independently-landable point. - Each milestone opens with a **Goal** (one line) and a **Verify** criterion (the acceptance gate; precision invariants pulled in where one applies). - Verify-in-DAW points require a manual REAPER run; pure points are gated by CTest. - "See CONTEXT.md §…" points at the authoritative detail — do not duplicate it here. --- ## Milestone 8 — RealtimeRecordBackend **Goal:** Realtime record behind the same `ICaptureBackend`, producing identical bank entries. CONTEXT.md §capture (realtime), §Precision invariants. **Verify (in DAW):** Hidden temp track resamples wet output; recorded file moves into the bank; **non-destructive** — temp track removed cleanly, source routing and user monitoring restored unchanged. **Note (from M3):** The realtime backend captures during playback and does NOT invoke the offline-render path, so it is inherently dialog-free (no render-progress window) — a secondary benefit beyond hardware/performed-FX capture. - [ ] Hidden-track resample recipe (`I_RECMODE`/`I_RECINPUT`/`I_RECARM`, `CSurf_OnRecord`/`CSurf_OnStop`); verify record-mode values against SDK. - [ ] Resolve wet-master routing that does not alter user monitoring (open question). - [ ] Move recorded source into bank; populate identical `Sample`; clean teardown. ## Milestone 9 — slots (MPC-style) **Goal:** "Capture to slot N" / "insert slot N", MIDI-bindable. CONTEXT.md Build order 9. **Verify (in DAW):** Slot capture and slot insert fire from MIDI bindings; slot state persists via the index. - [ ] Slot model + slot↔sample assignment. - [ ] "Capture to slot N" / "insert slot N" actions, MIDI-bindable. ## Milestone 10 — provenance + null-test verify action **Goal:** Provenance (parent sample id + FX-chain snapshot) and "re-capture from source"; ship the null-test verification action. CONTEXT.md §Precision invariants, Build order 10. **Verify (in DAW):** **Null test** — a dry offline capture of a range, re-inserted at its source position, nulls to silence against the source. This action is the tool's trust anchor and must pass. - [ ] Provenance fields populated on resample-from-sample (parent id + FX-chain snapshot string). - [ ] "Re-capture from source" action. - [ ] Null-test verification action (capture → re-insert at source pos → assert silence sum). **Note (from M7):** The null test requires a TRUE pre-FX dry capture, which REAPER offline render cannot produce via RENDER_SETTINGS (there is no pre-FX bit). True dry must be obtained by bypassing the source FX around an offline render (snapshot→bypass→render→restore) OR via the M8 realtime pre-FX path — so the dry-capture mechanism should be designed as part of the M10 null-test work. ## Milestone 11 — polish **Goal:** Batch capture (per selected item / per razor area), resample-and-mute-source, conform-on-insert, native OS drag-out. CONTEXT.md Build order 11, §Non-goals (drag-out deferred to last). **Verify (in DAW):** Each polish action works without regressing the precision invariants; drag-out places a valid file in the OS target. - [ ] Batch capture: per selected item / per razor area. - [ ] Resample-and-mute-source. - [ ] Conform-on-insert (explicit). - [ ] Native OS drag-out (deferred final; `InsertMedia` path must already work). --- ## Open questions to resolve during build Carried from CONTEXT.md §Open questions — keep visible until each is closed by a landed milestone. - **Realtime wet-master routing** that captures master output without altering the user's monitoring. (blocks M8) - **`parseInt` narrowing hardening:** `src/bank_model.cpp` `parseInt` casts `int64_t → int` via `static_cast` without a range check; integers that fit in int64 but exceed `INT_MAX` are implementation-defined. Hardening candidate — add bounds check before the cast when integer-field validation is in scope. --- # 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 (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. JSON round-trip lossless across 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. - [ ] Pool-privilege rules enforced in-model: reject delete-pool, reject rename-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). - [ ] JSON round-trip: 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). - [ ] Tests: pool privileges (delete/rename rejected); move source-loses/dest-gains; copy source-retained; cross-bank same-hash coexistence; dest collapse on move into a bank already holding the hash; JSON lossless; legacy migration. ## B2 — persist slice (banks ↔ project ext state) **Goal:** Serialize the book under the `banks` key in `"reasampler"` alongside the existing sections; migrate a legacy `bank_index` key into the pool on first load; 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. **Depends on:** B1. Resolve the `bank_index` retirement-vs-retention fork first (see Phase B open questions). - [ ] Serialize/deserialize the book under the `banks` key (shared blob, distinct section from `bank_index` / `view_state`). - [ ] Legacy-migration path on load: absent `banks` + present `bank_index` → pool. - [ ] 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 MIDI-bindable; create/rename/delete drive the B1 model via the B2-persisted session. **Depends on:** B1, B2. - [ ] Create bank / rename bank / delete bank (delete honors the resolved member-disposition rule — see open questions). - [ ] Activate bank (direct-by-id + cycle). - [ ] Move selected samples → bank / copy selected samples → bank. - [ ] Pool full-height / banks full-height toggles. - [ ] Register each (`command_id`/`gaccel`/`hookcommand`); bank-activate + move/copy 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 indicated; both full-height toggles collapse the split correctly; sample move/copy affordance works (drag and/or menu); the Design View mode switch in the header is unaffected. **Depends on:** B1, B2, B3. - [ ] Vertical split: pool grid region (top) + named-banks tab-page region (bottom). - [ ] Named-banks tab strip: one tab per named bank; empty state when none. - [ ] Pool full-height / banks full-height toggle affordances wired to B3. - [ ] Active-bank indicator. - [ ] Create / rename / delete / activate affordances driving B3 actions. - [ ] Sample move/copy affordance (drag between regions and/or "send to bank" menu). ## Phase B open questions - **`bank_index` key retirement vs. retention** — (a) fold the pool into the `banks` blob and retire the legacy key, vs (b) keep `bank_index` as the pool's canonical slot and store only named banks under `banks`. Leaning (b) for additive-minimalism against in-flight M7/M8 persist work. Resolve before B2. (blocks B2) - **Named-bank delete → member disposition** — (i) reabsorb into pool, (ii) orphan-check by file reference, (iii) forbid non-empty delete. Leaning (i). (touches B1/B3) - **Move affordance + tab rendering** — drag vs. menu as primary; SWELL-native vs. LICE-drawn tabs. Verify SWELL tab-control availability against the M5 reference. (touches B4) - **Active-bank indicator placement** — per-region headers vs. single header readout. (touches B4)