Files
reasampler/PLAN.md
T
daniel 3b9b78b82c docs(phase-s): settle S-VIEW velocity persistence + draggable nodes, single-pass on L3
F1 -> preview velocity persists via VST3 component state (v5->v6). F2 -> envelope nodes draggable via new pure envelope_edit module. Drop the two-pass L3 restyle framing; build on the merged L3 LnF. F3 remains open.
2026-07-27 12:57:08 -04:00

672 lines
49 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 9 — slots (MPC-style)
> **Abandoned (Daniel, 2026-07-27) — will not be built.**
**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.
## Open questions to resolve during build
Carried from CONTEXT.md §Open questions — keep visible until each is closed by a
landed milestone.
- **`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.
- **Capture send/routing isolation (TODO):** The FX-scope capture neutralizes out-of-scope FX, gain, and pan — but NOT aux **sends**. So a downstream coloring send (e.g. a folder → reverb-track send) still routes and blends the reverb into an item/track capture, past the intended isolation point. A true item-level capture should be taken at the isolated graph point — the target scope's output before out-of-scope track FX/gain/pan **and** before out-of-scope aux/parallel sends. The hard part: distinguish **source routing that must be preserved** (e.g. a MIDI send T1→T2 where T2's synth is where a MIDI item's audio is actually produced — the "item level" for that MIDI item is T2's synth output) from **coloring sends that must be excluded** (folder→reverb). Repro: folder F1; T1 (MIDI) sends MIDI to T2 (synth); T1+T2 → F1; F1 sends to reverb T3; capturing the MIDI item on T1 currently includes the reverb, should be isolated to T2's synth output pre-F1 with the MIDI send preserved and the reverb send excluded. Likely approach: snapshot + mute out-of-scope tracks' aux sends during the render while preserving the main/source signal path — needs a rule for which sends are load-bearing.
---
# Phase D2 — Two-canvas (item-level mode projection; additive to D1)
> **Design View sub-phase.** Extends D1's track-level mode projection to **item
> level** via REAPER 7 fixed lanes: on a track present in both stances, each mode
> owns a fixed lane — the active mode's lane shows and plays, the inactive mode's is
> hidden and silenced — so a Design take and an Arrange take can share the same
> track and time position without colliding on the view. Nothing in D1 changes.
> Runtime floor rises to **REAPER 7** for this sub-phase (no version-gate branch;
> below v7 it is simply unavailable). Authoritative spec: **CONTEXT.md §Two-canvas
> sub-phase (Phase D2 / Phase E)** and the surrounding §Design View — additive phase
> spec. Product framing: `docs/product/design-view.md` §Two-canvas direction. When a
> point lands, doc-keeper moves it to `COMPLETED.md`.
>
> **D2-W1 (pure lane extension), D2-W2 (shell: lane application + new-content
> detection), D2-W3-A (lane minting + item→lane assignment + persist round-trip),
> and D2-W3-B (item-level mode actions + W3-A polish) have all landed** — see
> `COMPLETED.md`. **Phase D2 is functionally complete.**
>
> **Deferred:** panel UI indicator for per-track lane/mode state (a per-track
> lane-split marker). The mode switch already shows the active mode; no natural
> cheap home for a per-track indicator was found in the bank panel. Explicitly
> deferred — not silently dropped. Can be picked up later if wanted.
---
# Phase S — MIDI-playback instrument (native VST3 sampler; a second build artifact)
> **Landed on dev (merged 2026-07-27); DAW verification pending Daniel's smoke test.**
> S1S18 and the product-name/binary-rename work are all on dev. The cross-artifact
> ingest relay (S13 bullet) was explicitly DEGRADED and remains deferred — see below.
> Completed material archived in `COMPLETED.md`. Authoritative spec: **CONTEXT.md
> §MIDI-playback instrument — additive phase spec (Phase S)**. Product framing:
> `docs/product/midi-playback.md`.
>
## S13 — cross-artifact ingest relay (deferred)
> **SPIKE VERDICT (ps-w12, 2026-07-27): DEGRADED — relay deferred.** The instrument's
> REAPER bridge (`reaper_bridge`) is deliberately READ-ONLY; a relay would need a new
> instrument WRITE seam into ext-state and an extension-side timer poller servicing a
> drop-ingest inbox key with a claim/clear nonce — the same cross-process handshake race
> the S17 spec rejected for alternative (A). Both the read-only-instrument boundary and
> the new poller are load-bearing design calls, so the relay is deferred to a future wave.
> The shipped ingest gesture stays drop-onto-docked-panel (S8). The degrade path (editor
> shows a "drop files onto the ReaSampler bank panel to add them" affordance) landed as
> part of Phase S. See `COMPLETED.md` §S13 for full context.
- [ ] Cross-artifact ingest relay: the editor hands the dropped path + this instance's
identity to the extension as a bank-ingest request over an agreed seam. DEFERRED —
relay mechanism proved load-bearing to redesign; drop-onto-panel (S8) is the shipped
ingest path. Requires (a) a new instrument WRITE seam into ext-state and (b) an
extension-side timer poller + claim/clear nonce. A future wave when the design is ready.
## Phase S — held and optional-forever (noted, not specified)
> S3S11 have landed; archived in `COMPLETED.md`. S12 and S13 (partial) have also
> landed; archived in `COMPLETED.md`. The cross-artifact relay (S13) remains deferred — see §S13 above.
> S8 has landed; archived in `COMPLETED.md`.
> S9 has landed; archived in `COMPLETED.md`.
> **S0-DS moved to Phase L (2026-07-26).** The shared LICE drawing kit that stood here is
> now **Phase L point L1** on `dev` — see PLAN.md §Phase L + CONTEXT.md §Phase L and
> `docs/product/visual-design-language.md` (all on `dev`). S10S13 below build with the
> current drawing and **adopt the L1 kit when it lands — not gated on Phase L.**
> S10, S10-Z, and S11 have landed; archived in `COMPLETED.md`.
> S12 has landed; archived in `COMPLETED.md`.
> S13 (drop-to-load) has partially landed; archived in `COMPLETED.md`. Cross-artifact relay remains deferred — see §S13 above.
> S15 has landed; archived in `COMPLETED.md`.
> S16 has landed; archived in `COMPLETED.md`.
> S17 has landed; archived in `COMPLETED.md`.
> S18 has landed; archived in `COMPLETED.md`.
> **S14 moved to Phase L (2026-07-26).** The dock-panel refresh that stood here is now
> **Phase L point L2** on `dev` — and, per Daniel's DS-3 call, expanded from a light re-skin
> into a **thorough dock-panel layout redesign** that lays out the full M11-aware button
> inventory before applying the kit. See PLAN.md §Phase L + CONTEXT.md §Phase L and
> `docs/product/visual-design-language.md` (all on `dev`). The design-system forks DS-1/
> DS-2/DS-3 are all **SETTLED (2026-07-26)** and recorded in the Phase L docs on `dev`.
## Phase S — product name (ReaSampler 9000)
The MIDI-playback instrument's product name is **ReaSampler 9000** (Daniel, 2026-07-26,
on DAW-testing the S1S6 instrument). The extension remains **ReaSampler**; the instrument
is **ReaSampler 9000**. Framing + propagation surfaces:
`docs/product/midi-playback.md` §Product name.
- [ ] Propagate the display name **ReaSampler 9000** across user-visible surfaces: the VST3
class **display name** string (in the factory registration), the `IPlugView` editor title
band (currently "ReaSampler Instrument"), the S6 embed-strip label, and the Phase S docs.
**Do NOT change the VST3 class UID** — instances in already-saved projects key off it; a
UID change orphans every existing instance.
- [ ] **Rename the binary filename too (S-NAME-1 SETTLED, Daniel 2026-07-26):** rename the
built VST3 module (CMake `OUTPUT_NAME` / target artifact — e.g. `reasampler_9000.vst3`)
alongside the display strings, so the on-disk name matches the product name. Record the
full rename surface: **CMake output name** (the second VST3 target's artifact name), the
**factory vendor/name strings**, the **`IPlugView` editor title**, and the **S6 embed
label**. Do NOT touch the **VST3 class UID** (unchanged — the compat anchor).
- [ ] **Compat verification (must-DAW-verify before shipping the rename):** the working
assumption is that REAPER **rebinds a saved instance by its VST3 class UID, not by the
module filename**, so a filename rename with an unchanged UID keeps saved projects working
(existing instances still resolve). **This is not yet confirmed from source** — a web
check surfaced a JUCE/VST3-replace-VST2 case suggesting REAPER's binding is more nuanced
than "UID only" (it can involve an FXID match), so treat UID-rebind as **to-verify, not
asserted fact**. **DAW-verify:** save a project with a ReaSampler 9000 instance under the
old filename, rename the module, reopen — confirm the instance rebinds and restores its
state. If REAPER does key partly on filename, fall back to keeping the current filename
(display-strings-only) and record that as the shipped choice.
- **Tier 2 — "expressive" (HELD).** Velocity layers, round-robin (anti-machine-gun),
full ADSR, per-sample tuning/gain trim, sustain loops. The next depth increment once
Tier 01 proves the instrument belongs — **its points are not drawn up here.**
- **Tier 3 — "instrument polish" (optional-forever).** Filters, filter/pitch
envelopes, LFOs, per-voice pan, choke groups, a modest FX slot. A direction to leave
room for, never a commitment. **Note:** S16 lands the *pitch* envelope + the Varispeed/
Preserve pitch-engine mode early (Daniel's directive) — the Tier-3 "filter/pitch envelopes"
line now means the *filter* envelope + LFOs remainder.
- **Sinc Varispeed-quality upgrade (HELD — WDL_Resampler).** `WDL_Resampler`'s sinc mode
beats the core's 2-point linear interp for **Varispeed** base-repitch quality (see the S16
WDL finding). An optional per-voice quality toggle (linear default / sinc), RT-suitable but
heavier. Held as a Tier-2/3 quality option — not needed for S15/S16, not scheduled. (A
resampler couples duration, so it is a Varispeed-quality option only, **not** a Preserve
engine.)
- **WDL_SimplePitchShifter swap (HELD — fork S16-F2 route a).** `WDL_SimplePitchShifter`
as a drop-in swap for the `pitch_shift` pure module if the hand-rolled OLA onset latency
or warble proves musically unacceptable. Same `PitchEngine::Preserve` contract behind the
seam. WDL excluded from the shipped build by include-chain (windows.h); held as the
quality/latency alternative.
- **Trigger choke-on-note-off (HELD — fork S15-F1).** A future option for Trigger mode to
*cut* (choke) on note-off or on a same-group re-trigger (hi-hat open/closed). Deliberately
out of S15 scope (Trigger ignores note-off entirely there); a Tier-3 choke-group direction.
## Phase S — editor view-model redesign (three views: Sample / Browse / Zone)
> **Additive Phase S sub-phase (S-VIEW; Daniel, 2026-07-27, r9).** Re-partitions the ReaSampler
> 9000 editor from a two-view toggle into a **three-view model where the loaded sample is the
> home** — Sample (default face), Browse (modal picker over Sample), Zone (dedicated keymap
> surface). Adds two performance parameters (key-tracking, preview velocity) and two visual
> components (envelope overlay, real piano-key pattern), and frames two engineering prerequisites
> (drop-to-FX bug, default window size). An **editor** redesign — the S3 voice engine, keymap
> resolution, and read-only-over-bank contract are **unchanged**; component state extends
> additively for key-tracking; VST3 class UID unchanged. Authoritative spec: **CONTEXT.md §Phase
> S — editor view-model redesign (S-VIEW)**. Product framing: `docs/product/midi-playback.md`
> §Addendum r9. When a point lands, doc-keeper moves it to `COMPLETED.md`.
>
> **Depends on Phase S being on dev** (this redesigns Phase S editor shells —
> `reasampler_editor.cpp` + the pure `src/vst/` geometry modules). **Phase L L3 is FINISHED and
> merged** (2026-07-27, commit `c53683e` — `reasampler_editor.cpp` + `reasampler_embed.cpp`
> restyled through the L1 kit). S-VIEW builds the new three-view layout **directly on the current
> L3 look-and-feel as its baseline — one implementation pass, styled correctly from the start.**
> There is **no two-pass "land S-VIEW then restyle through L3"** — L3 already happened; every new
> S-VIEW surface (Sample face, Browse modal, Zone surface, envelope overlay + node handles,
> piano-key strip, preview cluster) is drawn through the L1 kit at build time, inheriting the L3
> palette and component-draw grammar. Restyle-after is not a concern.
**Goal:** A three-view editor that makes "pick a capture, tune it, play it" fast, easy, and fun —
Sample is home, Browse is an easy-to-summon modal picker, Zone is the deliberate keymap surface.
**Verify (in DAW):** open on a 1080p screen → full Sample face (hero waveform + envelope overlay +
fenced root + preview-trigger + control strip) with no scroll; Browse opens as a modal over
Sample, select+confirm loads a new capture; Zone opens on its own button, key-tracking + the
piano-key pattern work; drop-a-capture-onto-FX instantiates a playing instance.
- [ ] **S-VIEW-1 — three-view navigation model.** Retire the flat Browser|Zones toggle; introduce
Sample (home/default), Browse (modal overlay over Sample, select+confirm), Zone (dedicated
surface, own button). Empty state surfaces Browse as the dominant call-to-action. Fresh instance
stays silent (S10 reversal). See CONTEXT.md §S-VIEW navigation contract.
- [ ] **S-VIEW-2 — Sample view (the new main face).** Compose the home face: enlarged **hero
waveform** with the S11 markers (moved from Browse), a **fenced root affordance**, the
**Mono/Stereo toggle** (moved from Browse), and the **"Modes-and-down" control strip** (Mode /
Pitch engine / AHDSR|Trigger / AD pitch env — moved from Zone's param panel, single-capture
one-zone storage per S15-F2). Reference grammar: Simpler / Phase Plant (labelled value-strip
under a hero waveform).
- [ ] **S-VIEW-3 — envelope overlay + DRAGGABLE NODES on the hero waveform (new visual + edit
surface).** Draw the amp envelope (AHDSR for Gate, fade/%-length for Trigger) as a curve over the
Sample waveform at accurate wall-clock time. New pure `envelope_overlay` module (params + frame-
length → polyline; unit-tested); shell traces it in an accent hue. **The breakpoints are draggable
handles** (S-VIEW-F2 SETTLED — in scope, not deferred): X → segment time, Y → level on level-
breakpoint nodes (sustain drags both axes), monotonic-in-time + range-clamped so a drag can never
produce a param the slider couldn't. New pure `envelope_edit` module (node hit-test + pixel-delta→
clamped-param inverse map; mirror of `card_drag`; unit-tested at the clamp/monotonic boundaries).
Sliders stay the precise numeric surface; **both surfaces read/write the same `PerformanceZone`
envelope fields** — one source of truth, kept in sync by re-reading params each paint (no listener
chain). Node-drag commits via the same off-audio-thread path a slider edit uses (no new RT
surface).
- [ ] **S-VIEW-4 — preview-trigger + velocity knob (new, Sample view).** A button firing the
sampler at the loaded capture's root note through the live voice engine (off the audio-thread
commit path — no MIDI controller needed) + an adjacent velocity knob. **Preview velocity PERSISTS**
(S-VIEW-F1 SETTLED): it lands as a new top-level `previewVelocity` field on `ComponentState` via an
**envelope bump to v6** (`src/vst/sample_map.h`), round-tripped through the processor's existing
`getState`/`setState` over `IBStream`**not** the extension's project-scoped `persist` ext-state
(wrong owner + wrong scope; see CONTEXT.md §S-VIEW). Zones payload untouched (independent version
axes); older blobs lift to a mid default. RT discipline: no `process`-thread work.
- [ ] **S-VIEW-5 — Browse reduced to *choosing* (modal over Sample).** Keep search + bank tabs +
captures grid + scroll + selection; add **confirm/cancel** (double-click loads). **Remove** the
large waveform preview, Mono/Stereo toggle, root keyboard-strip (all moved to Sample), and the
loop-point labels + track-root message (cut). Render as a full-window modal overlay (fork
S-VIEW-F3).
- [ ] **S-VIEW-6 — key-tracking parameter (Zone + Sample, 0%200%, default 100%).** A per-
`PerformanceZone` scalar on keyboard pitch tracking around the root (100% = 12-tone-ET, 0% = no
tracking, 200% = double). Additive/version-bumped, defaults 100% (bit-identical). **Key-track
math lives in the pure sampler core** (unit-tested note/root/keyTrack → ratio), applied in both
Varispeed and Preserve repitch. Surfaces as a control on the Zone param panel + Sample control
strip.
- [ ] **S-VIEW-7 — real piano-key pattern on the keyboard strip (new visual).** Overlay the actual
alternating white/black (bright/dark per palette) key pattern over the pastel spectral fill so
the strip reads as a keyboard. Pure `keyboard_strip` gains a natural/accidental predicate (12-
tone, unit-tested); shell draws the two-tone overlay. Shared by the Zone strip + Sample root
affordance.
- [ ] **S-VIEW-8 — Zone view retained + wired.** Keep +Add Zone / Delete, the per-zone keyboard
strip (now with the piano pattern), the Low/High/Root numeric-entry legend, and the per-zone
param panel; add the key-tracking control. Nothing from today's Zones view dropped.
### Phase S editor prerequisites — routed to staff-engineer (bug + default; NOT design forks)
> Framed in CONTEXT.md §S-VIEW; both are functional prerequisites with the SDK swept and no
> platform ceiling — a diagnosis and a one-line default, not product decisions.
- [ ] **S-VIEW-BUG-1 — drop-to-FX diagnosis (staff-engineer).** Dropping a capture onto a track's
FX chain does not instantiate + init ReaSampler 9000 (drops as audio to arrange instead). The
S17 machinery is **SDK-correct and present** (`TrackFX_AddByName` + `vst_chunk` injection, all
verified); this is a **runtime bug**. Investigate in order: (1) FX-name match (`"VST3:" +
vstPluginName()` vs REAPER's scanned name); (2) gesture routing (`decideGesture`
`InstrumentDrop` vs falling to `OsDrag` — the observed symptom); (3) `vst_chunk` write result +
blob-format match with `setState`. **Verify in DAW:** drop → a playing instance on the track,
one Ctrl-Z removes it.
- [ ] **S-VIEW-SIZE-1 — 1080p default window size (staff-engineer).** The editor opens too small
(`ViewRect(0,0,560,400)`). Mechanism **verified** (`getSize`/`setRect`/`checkSizeConstraint`/
`onSize`/`canResize` in `vendor/vst3sdk/public.sdk/source/common/pluginview.h`): set a larger
default `ViewRect` sized for the new three-band Sample face on 1080p (~840×560+ starting point,
tuned to the band heights) + a `checkSizeConstraint` minimum floor. **Verify in DAW:** opens at
the new default showing the full Sample face without scroll; cannot shrink below the floor.
### Phase S editor redesign — forks (Daniel's to call)
*(S-VIEW-F1 and S-VIEW-F2 SETTLED 2026-07-27 — folded into S-VIEW-4 / S-VIEW-3 above. F1: preview
velocity persists via envelope-v6 `ComponentState`. F2: envelope nodes are draggable via the pure
`envelope_edit` module. Only F3 remains open.)*
- **S-VIEW-F3 — Browse modal presentation:** full-window overlay (**lean**) vs. a centered sheet
over a dimmed Sample. Presentation detail.
---
# Phase L — Look-and-feel (system-wide visual design language)
> **New pillar, own lettered namespace, taken up by a parallel team.** Phase L is the
> whole-system look-and-feel effort: a shared LICE drawing kit and the surfaces that
> adopt it, so ReaSampler and ReaSampler 9000 shed the flat "temple os" drawing for a
> modern, sleek 2026 dark synth look. It answers Daniel's post-DAW-test verdict on the
> instrument ("this looks like temple os… the VST is dogshit… scope it for the whole
> system… does Cockos have a toolkit?"). Namespaced **`L` (Look-and-feel)** so it is
> orthogonal to and ungated by the M/D/B/R/V/S pillars — a parallel team owns it while
> Phase S feature work proceeds independently. Authoritative spec: **CONTEXT.md §Phase L
> — visual design language (design-system spec)**. Product framing, the settled decision
> record (DS-1/DS-2/DS-3 all SETTLED 2026-07-26), palette, and the three visual
> directions: `docs/product/visual-design-language.md`. When a point lands, doc-keeper
> moves it to `COMPLETED.md`.
>
> **L1 (shared LICE drawing kit — the foundation), L2 (dock-panel layout redesign), L3
> (VST editor + embed-strip restyle), L4 (dock-panel button layout enhancement), L5
> (dock-panel button refinements), L6 (toolbar polish), and L7 (capture ordering, card
> metadata, and selection styling) have all landed** —
> `theme`/palette module, `component_geometry` geometry/hit-test helpers, `draw_kit` shell, GDI
> `DrawText` retirement in `bank_panel` (L1); `action_bar` pure task-grouped layout module, full
> M11-aware button inventory placed by task cluster, `bank_panel` redesigned through the L1 kit
> (L2); VST editor (`reasampler_editor.cpp`) + embed strip (`reasampler_embed.cpp`) restyled
> through the L1 kit — REAPER-grey neutrals + three pastel accents, pastel spectral keyboard
> strip + zone bars, hover/pressed/drag states, local `kCol*` forest-green palette retired (L3);
> three-zone layout (top capture/placement/maintenance toolbar, bottom Design-View toolbar,
> footer toggle + Tail button + Prune), `footer_bar` pure module, `ActionCluster::Tagging`/
> `Switching` in `action_bar` (L4); top-bar overflow menu (`overflow_menu` pure module),
> custom LICE-kit hover-delay tooltips (`tooltip` pure module), opposite-mode Item/Track tag
> buttons + Show Both, Toggle + Activate-Arrange/Design buttons removed, grouping spacing
> widened (`mode_enable` pure module) (L5); single-row button faces, keybinding in tooltip,
> Cancel RT moved to overflow, top-bar cluster order tidied (L6); per-bank `SlotMap`
> (id→slot) in `bank_book`, sparse-grid rendering, `card_drag` + `card_meta` pure modules,
> `captureTimeSigNum`/`captureTimeSigDenom` on `Sample`, tertiary-border selection (L7). See
> `COMPLETED.md`. **Phase L is complete.**
>
> **This section is self-contained for a team without Phase S context.** Where a point
> touches a Phase S surface (the VST editor, the embed strip, the keyboard strip), the
> gate is stated explicitly so the team does not chase files that are not on dev yet.
>
> **Settled decisions (Daniel, 2026-07-26 — see `docs/product/visual-design-language.md`
> §6):**
> - **DS-1 — toolkit: LICE + WDL free game, no external frameworks.** Draw the modern
> look with LICE directly; reuse any useful WDL/vwnd piece (skin/image helpers, draw
> idioms, a control like the scroll listbox) where it beats re-deriving — "don't
> reinvent the wheel." Reject iPlug2 / JUCE / VSTGUI (external frameworks re-opening the
> settled bare-SDK+LICE build shape). Keep hit-test geometry in pure CTest-covered
> modules — do not import vwnd's retained-mode object model wholesale.
> - **DS-2 — visual direction: Direction B ("Neon Console") + Direction C's spectral
> keyboard strip. SETTLED 2026-07-26, REVISED 2026-07-26 (Daniel) — palette-only.**
> *Neutral surfaces (revised):* the neutral ladder moved **from near-black up into REAPER's
> mid-grey theme family** so the dock reads as part of REAPER, not a black slab — `bg/base`
> ≈ `#2b2b2b`, `bg/panel` ≈ `#333333`, `bg/cell` ≈ `#3a3a3a`, `line/hairline` ≈ `#4a4a4a`,
> `text/primary` ≈ `#dcdcdc`, `text/dim` ≈ `~#a0a0a0`+ (elevation-ladder discipline
> unchanged). *Accent layer (revised):* now a **three-accent pastel system** —
> `accent/primary` pastel lime green (live/active/selected), `accent/secondary` pastel teal
> + `accent/tertiary` pastel purple (categorical distinctions) — replacing the original
> single electric cyan. The spectral keyboard strip is a **pastel** sweep anchored on the
> three accents. *Tight WCAG pairs to re-verify against the grey ladder:* `text/dim`-on-grey
> (mid-grey-on-mid-grey, AA 4.5:1) and the three pastels-as-indicators on `bg/cell` (shrunk
> from ~15:1 to ~6:17:1). **A stylish/bundled-font upgrade was considered and DECLINED
> (Daniel):** no font bundling/redistribution — the kit keeps its current cached-font face,
> no new typeface. The kit palette stays abstract (roles, one constants block — three accent
> roles + grey neutrals), so the direction is a single-file change; final hex is locked
> against the theme WCAG tests within the pastel intent. Detail:
> `docs/product/visual-design-language.md` §2.1/§4/§6 + CONTEXT.md §Phase L.
> - **DS-3 — dock-panel scope: a thorough layout redesign, not a light re-skin.** L2 lays
> out the full button inventory (including M11's action-button additions) intuitively,
> uncluttered, and useful — then applies the kit. Sequenced after M11 merges.
## Phase L — sequencing
```
L1 (shared kit) ──► L2 (dock-panel layout redesign) [LANDED]
├─────────► L3 (VST editor + embed-strip restyle) [LANDED]
└─────────► L4 (dock-panel button layout enhancement) [LANDED]
└────► L5 (dock-panel button refinements; ungated, after L4) [LANDED]
└────► L6 (toolbar polish; ungated, after L5) [LANDED]
└────► L7 (capture ordering + card metadata + selection styling; ungated, after L6) [LANDED]
```
L1, L2, L3, L4, L5, L6, and L7 have all landed. **Phase L is complete.**
## Phase L — must-verify-before-build
- **LICE design-kit surfaces (L1)** — `LICE_GradRect`, `LICE_RoundRect`, AA
`LICE_Line`/`LICE_FLine`/`LICE_ThickFLine`/`LICE_Circle`/`LICE_FillCircle`/
`LICE_DrawCBezier`, `LICE_FillTriangle`/`FillTrapezoid`/`FillConvexPolygon`, and the
`LICE_CachedFont`/`LICE_IFont` font engine (`SetFromHFont`, AA `DrawText`, shadow/outline/
glow FX flags). Verified *present* in `vendor/WDL/WDL/lice/lice.h` + `lice_text.h`
(design-language doc §1.1); **confirm exact signatures + the `LICE_CachedFont``HFONT`
lifecycle at build.**
- **WDL/vwnd reuse assessment (DS-1)** — at build time, evaluate whether a vwnd piece beats
re-deriving it: `virtwnd-slider.cpp` / `vwnd_slider_drawknobstack` as the slider/knob
drawing reference, `virtwnd-listbox.cpp` as a candidate scroll listbox, `virtwnd-controls.h`
for `WDL_STYLE_*` gradient hooks, `virtwnd-skin.h` for image-skin helpers. Reuse where
useful; keep hit-test geometry pure regardless.
- **M11 button inventory (L2)** — resolved at L2 build: inventory taken against dev after
M11 merged; all buttons placed by task cluster in the landed `action_bar` module.
- **L4 re-home surface (L4)** — resolved at L4 build: three-zone layout confirmed against the
post-palette-revision `bank_panel`; `action_bar` extended with `ActionCluster::Tagging` +
`Switching`; new pure `footer_bar` module covers footer layout/hit-test.
- **L5 refinement surface (L5)** — resolved at L5 build: `TrackPopupMenu` overflow menu
confirmed; tooltip mechanism resolved as **custom LICE-kit hover-delay tooltip** (`tooltip`
pure module; sourced from the registered action phrase, prefix stripped at draw time); item-move
and track-tag action ids confirmed; active-mode read confirmed via the same
`view().activeModeId()` the footer toggle uses.
- **L7 model + tempo surface (L7)** — **RESOLVED at build (L7 landed).** Forks F1/F2/F3 all
confirmed: `TimeMap_GetTimeSigAtTime` confirmed at build for the meter stamp; SWELL stock
cursors chosen for drop-result cues (Reorder→IDC_SIZEALL, Move→IDC_HAND, Copy→IDC_UPARROW,
Replace→IDC_SIZEWE); existing pool-privilege guard reused as-is for Alt-replace. Gap navigation
in the grid = skip gaps (arrow keys skip empty slots). See `COMPLETED.md` §L7.
---
# Phase Q — Quality (structural reorganization; zero-runtime-cost)
> **New pillar, own lettered namespace, and — uniquely — the LAST structural pillar.** Phase
> Q is a **pure structural refactor**: it reorganizes `src/` into a healthier shape (more
> encapsulation, granular namespaces, `core/`/`shell/`/`app/` subdirectories) against a stated
> quality bar — *"mtytel Vital is my code reference for quality"* — to bring the codebase
> "into the realm of something I can stand to look at." It ships **no feature and changes no
> behavior**: the test suite passing unchanged is the proof of correctness. Namespaced **`Q`
> (Quality)** — M/D/B/R/V/S/L are all taken; `Q` names the *end* (the quality bar), the reorg
> being the *means*. Authoritative spec: **CONTEXT.md §Phase Q — structural reorganization
> (reorg spec)**. Product framing, the Vital-grounded target shape, the grep-verified SOLID
> audit that is the evidence base, and the settled/recommended fork record (Q-1..Q-6):
> `docs/product/code-organization.md`. When a point lands, doc-keeper moves it to
> `COMPLETED.md`.
>
> **THE GATE (load-bearing — state first; reconciled to reality 2026-07-27).** Phase Q is
> **gated on the tree being otherwise quiescent.** Daniel's plain readiness target: **"when
> Phase S and L3 are finished."** **As of 2026-07-27 both gate conditions are satisfied:**
> Phase S merged to dev (2026-07-27); Phase L L3 (the VST restyle) merged to dev
> (2026-07-27, commit `c53683e`) — **Phase L is complete** (L1/L2/L3/L4/L5/L6/L7 all
> landed, see `COMPLETED.md`). **D2** is functionally complete (D2-W1..W3-B landed; the
> lone open item, a per-track lane-split panel indicator, is *explicitly deferred*, not a
> blocking residual). **M9** (slots) is **abandoned** (Daniel, 2026-07-27) — will not be
> built. D2 is named in the gate only so that *reactivating* its deferred panel indicator
> re-arms the quiescence condition; neither D2 nor M9 blocks the gate today. *Why the gate:* Phase Q
> touches **nearly every file in `src/`** (relocate into subdirectories, re-namespace every
> header, split the four largest TUs, plus the §2b renames). Every large in-flight branch
> (Phase S on its worktree, and L3 once it lands) is diffed against the *current flat
> layout*; landing a rename-and-relocate-everything reorg mid-flight forces every open
> branch through the worst conflict class (every hunk moved, every qualified reference
> changed) — a combinatorial re-resolution, not a linear one. Phase Q is *last* precisely
> because it reshapes the ground every other pillar stands on. Landing it early taxes every
> subsequent phase; landing it last taxes nothing. **The gate is now satisfied — Phase Q
> may begin.**
>
> **M9 disposition — resolved (Daniel, 2026-07-27): abandoned.** M9 is out; it will not be
> reactivated. The gate remains satisfied; no re-arm condition applies.
>
> **Settled (Q-1, this-doc):** the phase is **`Q` (Quality)**; point-id family `Q1..Qn`, wave
> prefixes `Q-W1..Q-W6`. **Recommended, Daniel's to call (Q-2..Q-9, see
> `docs/product/code-organization.md` §6):** Q-2 JSON extraction in scope + first (rec: yes);
> Q-3 directory shape `core/`/`shell/`/`app/` top-split with subsystem dirs beneath (rec: this
> over pure-Vital subsystem-first — it makes the pure/shell invariant *structural*); Q-4
> sub-namespace to match sub-directory (rec: both); Q-5 split god-modules to the audit's named
> seams, no finer (rec: yes); Q-6 OCP registration-table as the final wave (rec: in, last);
> **Q-7 naming rides the relocation waves, no dedicated naming wave (rec: yes — forced once
> Q-3/Q-4 settle); Q-8 class/module renames — fix the two that actively mislead (`BankIndex`→
> `BankModel`; the JSON `Parser`→`json::Reader`/`Writer`), leave the merely-quirky (rec);
> Q-9 align the `capture_realtime`/`realtime_record` shell↔core word order during W3 (rec: yes).**
>
> **HARD CONSTRAINT — performance (see CONTEXT.md §Phase Q, `docs/product/code-organization.md`
> §3).** The reorg must cost **zero runtime.** On the three hot paths — `peaks` envelope
> compute, audition/preview, the realtime-capture tick — **no added virtual dispatch, no
> header→TU indirection, no changed call/inline or branch shape.** `computeEnvelope` stays a
> free function on `const std::vector<float>&`; audition split stays a direct call-through;
> the realtime idle tick stays a single pointer test; `FxBypassGuard` stays stack RAII. This is
> an acceptance criterion on every point: *a split that would add a hot-path indirection is out
> of scope — rework it or drop it.*
>
> **NAMING dimension (added 2026-07-27; grep-verified audit in `docs/product/code-organization.md`
> §2b).** Beyond giving symbols a directory + namespace *home* (Q-3/Q-4), Phase Q also gives
> poorly/inconsistently-named symbols a consistent *name*, against the same Vital bar. The audit
> found: four `class Parser` copies collapsing to one `json::Parser` (Q-W1); shared pure-UI rect
> types (`FooterRect`/`ButtonRect`) that the codebase already hand-checks for collision
> (`footer_bar.h`'s "NAME NOTE") — resolved by the Q-4 sub-namespaces for free; the
> `bank_model`/`BankIndex` file↔class word-mismatch (Q-8); and the `capture_realtime`/
> `realtime_record` shell↔core word-order inversion (Q-9). **Renames ride the wave that already
> relocates/splits the file — no dedicated naming wave (Q-7);** the geometry-mirror
> `compute*`/`hitTest*` verb vocabulary and the `_tests` suffix are already consistent and are
> preserved verbatim. Naming changes are zero-behavior-change like the rest of Phase Q, and the
> FOREVER-STABLE contract strings (command ids, action names, ext-state namespace/keys, VST3 UID)
> are **not** C++ symbols and are never renamed.
>
> **Every point is independently landable and CTest-green at EVERY step.** The CMake
> per-module static-lib + per-module test-executable seams already draw the module boundaries;
> a file move + namespace change is mechanically verifiable — `ctest --test-dir build` is green
> or it isn't. **Green-CTest-at-every-point is an acceptance criterion.** Big-bang is rejected;
> the reorg is risk-ordered waves (W1 safe opener → W2W5 god-module splits → W6 OCP finish).
## Q-W1 — safe opener: extract `core/json` + impose the directory/namespace layout on clean modules
**Goal:** The zero-god-module-risk opener. Two moves: (1) extract a pure **`core/json`** module
(parser + serializer) and **delete the four hand-rolled `Parser`s** in `bank_model` /
`bank_book` / `view_mode_model` / `owned_manifest` (the single largest DRY+SRP violation, and
entirely off the hot paths); (2) impose the settled `core/`/`shell/`/`app/` directory layout +
sub-namespaces (`reasampler::model`/`view`/`capture`/`audio`/`ui`/`reclaim`/`version`/`json`) on
the **30 clean pure libs + the clean shells that need no splitting** — pure relocation, no logic
change. Proves the wave discipline (relocate + encapsulate, CTest-green) before any god-module
surgery. CONTEXT.md §Phase Q (json extraction; directory + namespace map).
**Verify:** CTest green at every commit. The four duplicate `Parser`s are gone, replaced by one
`core/json` consumed by all four models; round-trip serialization is byte-identical to before
(no format change — a *structural* dedupe, not a behavior change). Every relocated clean module
compiles and its test executable passes unmoved. `Sample` (model) vs `AudioSample` (audio) vs
unified `Parser` (json) do not collide once sub-namespaced. No REAPER type crosses into any
`core/` file; the CMake pure/shell enforcement still holds.
**Depends on:** the GATE (tree quiescent). Nothing else in Phase Q.
- [ ] Extract `core/json` (pure parser + serializer: parseString/parseInt/parseKey/skipValue +
escape, plus emit helpers); unify under `reasampler::json`; guard the `Parser` name against
cross-lib collision. Off all hot paths — safe to abstract freely.
- [ ] Rewire `bank_model`, `bank_book`, `view_mode_model`, `owned_manifest` onto `core/json`;
**delete the four duplicate `Parser`s.** Round-trip output byte-identical (dedupe, not
reformat).
- [ ] Relocate the 30 clean pure libs into `core/{model,view,capture,audio,ui,reclaim,version,
json}/` and the clean shells into `shell/{capture,panel,view,persist,actions}/`; move
`main.cpp` to `app/`. Update `CMakeLists.txt` `src/` paths only (no target-graph change).
- [ ] Apply sub-namespaces matching the directories on every relocated *clean* module (the
god-modules re-namespace their own new TUs as they split, W2W5). Resolve `Sample`/
`AudioSample`/`Parser` homes. **This alone resolves the naming *collisions*** (§2b.2): the
shared pure-UI rect types (`FooterRect`/`ButtonRect`/`Selection`/`CellRect`) get one `ui::`
owner — retire the hand-collision "NAME NOTE" in `footer_bar.h`.
- [ ] **Naming riders (Q-8, if settled):** rename the survivor JSON parser to `json::Parser`
(or `json::Reader`/`json::Writer`); if Daniel takes the `BankIndex`→`BankModel` rename, land
it here (mechanical class rename, verified by `bank_model_tests`). No rename on a file this
wave isn't already relocating (Q-7).
- [ ] Confirm CTest green + no hot-path change: `peaks`/audition/realtime-tick untouched by this
wave (pure relocation of clean modules; `peaks` stays a free function).
## Q-W2 — split `bank_panel.cpp` (the biggest god-module, 2424 LOC)
**Goal:** Split the largest god-module (8+ responsibilities) along the audit's named seams:
`panel_render` / `panel_thumbnails` / `panel_audition` / `panel_input` / `panel_bank_ops` /
`panel_window`. Split the fat `bank_panel.h` alongside (Interface Segregation). **Preserve the
audition hot path as a direct call-through, never virtual.** `panel_bank_ops` becomes the single
home for the bank-CRUD verbs that W4 will dedupe `actions.cpp` against. CONTEXT.md §Phase Q
(bank_panel split seams; hot-path audition guardrail). See `docs/product/code-organization.md`
§2.1, §5.
**Verify:** CTest green at every commit. Each seam is its own TU under `shell/panel/`; the panel
draws, thumbnails, auditions, handles input, does bank ops, and manages its window exactly as
before (no behavior change — verify in DAW that the panel is visually and interactively
unchanged). Audition/preview call path stays a **direct call-through** (no virtual dispatch, no
added header→TU indirection on the preview path). The ~20-function public API is now segmented
across the split headers.
**Depends on:** Q-W1 (directory/namespace layout established). Independently landable.
- [ ] Split rendering (`draw*`/`paint*`) → `panel_render`; thumbnail compute+cache →
`panel_thumbnails`.
- [ ] Split the audio audition/preview engine → `panel_audition` — **direct call-through, not
virtual; preview idle path unchanged.**
- [ ] Split input handling (mouse/key/wheel) + new-content detection → `panel_input`; window
lifecycle + OS drag-out/drop-target → `panel_window`.
- [ ] Extract bank-CRUD verbs → `panel_bank_ops` (the future single owner; W4 dedupes
`actions.cpp` against it). Split `bank_panel.h` into per-seam headers (I).
- [ ] Verify in DAW: panel unchanged; CTest green; no hot-path indirection added.
## Q-W3 — split `main.cpp` (hoist orchestration; leave main = pointers + entry + dispatch)
**Goal:** Reduce `main.cpp` (1762 LOC) to its actual job — API pointers + `ReaperPluginEntry` +
dispatch (~the owns-pointers ~120 lines) — by hoisting: `capture_orchestrator` (`RunCapture` /
`captureAndIndexOne` / `renderOffline` / batch/recapture/realtime `Run*`), `scope_resolve`
(`resolveRange`/`resolveRazorRange`/`collectSelectedTracks` + provenance assembly inputs), and
`realtime_lifecycle` (the realtime-capture state machine + globals + selection guards).
**`FxBypassGuard` moves out but stays a stack RAII object (precision-critical); the realtime idle
tick stays a single pointer test.** CONTEXT.md §Phase Q (main split seams; FxBypassGuard +
realtime-tick guardrails). See `docs/product/code-organization.md` §2.1, §3.
**Verify:** CTest green at every commit. Capture (offline + realtime + batch + recapture) behaves
identically in DAW; the null test still nulls, bit-identical repeats still match (the precision
invariants `FxBypassGuard` protects are unchanged); capture ≠ placement holds (no hoisted `Run*`
path gains an `InsertMedia` call). The realtime idle fast-path is still a single pointer test.
`main.cpp` is now pointers + entry + dispatch only.
**Depends on:** Q-W1. Independent of Q-W2.
- [ ] Hoist capture orchestration → `capture_orchestrator` (`shell/capture/`); keep
`FxBypassGuard` a **stack RAII** object as it moves (precision-invariant-critical).
- [ ] Hoist scope/source resolution + provenance assembly inputs → `scope_resolve`.
- [ ] Hoist the realtime-capture lifecycle state machine + globals + the two RAII selection
guards → `realtime_lifecycle`; **idle tick stays a single pointer test.**
- [ ] Leave `main.cpp` = API-pointer ownership + `ReaperPluginEntry` + dispatch; move to `app/`.
- [ ] **Naming rider (Q-9, if settled):** align the `capture_realtime` (shell) / `realtime_record`
(pure) word-order inversion to the house shell↔core convention (rec: stem `capture_realtime`,
shell suffixed) — a free rider since W3 already hoists the realtime lifecycle. No rename on a
file this wave isn't already touching (Q-7).
- [ ] Verify in DAW: null test nulls, bit-identical repeats match, capture≠placement holds;
CTest green; no realtime-tick branch-shape change.
## Q-W4 — split `actions.cpp` + dedupe bank verbs against `panel_bank_ops`
**Goal:** Split the two unrelated command-id families in one TU (981 LOC) into
`design_view_actions` / `bank_actions` / `prune_action`, and **dedupe** `actions.cpp`'s own
`promptText`/`mintBankId` and bank verbs against the `panel_bank_ops` single-owner established in
Q-W2. `prune_action` keeps the `doBankPruneFolder` deletion authority contract intact (routes to
`persist`'s `prune_fs` after W5). CONTEXT.md §Phase Q (actions split seams; bank-verb dedupe).
See `docs/product/code-organization.md` §2.1, §2.4.
**Verify:** CTest green at every commit. Every action fires identically in DAW (Design View
family; multi-bank create/rename/reorder/delete/evacuate/activate/move/copy/remove; prune). The
bank-CRUD verbs have **one** implementation home (no `bank_panel`/`actions` duplication). Each
bank verb still wraps its mutation in one batched undo point; the prune action still writes no
ext state and opens no undo point. Command-id strings are **unchanged** (FOREVER-STABLE
contract — a reorg must not touch a shipped command id).
**Depends on:** Q-W2 (`panel_bank_ops` is the dedupe target). Independent of Q-W3.
- [ ] Split → `design_view_actions` (toggle/activate/tag/untag/showBoth/moveItems),
`bank_actions` (bank CRUD family), `prune_action` (`doBankPruneFolder` — the single
file-deletion action).
- [ ] Dedupe `actions.cpp`'s `promptText`/`mintBankId` + bank verbs against `panel_bank_ops`
(one owner); do **not** change any command-id string.
- [ ] Verify in DAW: all action families fire unchanged; one bank op = one Ctrl-Z; prune still
no-undo/no-ext-state; CTest green.
## Q-W5 — split `persist.cpp` (isolate the single file-deletion authority into `prune_fs`)
**Goal:** Split `persist.cpp` (766 LOC, 5 responsibilities) into `session` (lifecycle+poll,
`BeginLoadProjectState` reload hook), `ext_state_io` (the ext-state ↔ JSON serialization bridge +
GUID minting + folder relocation), and **`prune_fs`** (prune scanning + `deleteOrphanFile` via
`SHFileOperationW`). The split **concentrates** the byte-deleting authority into one obvious
module — it must never spread it. CONTEXT.md §Phase Q (persist split seams; deletion-authority
isolation). See `docs/product/code-organization.md` §2.1, §7.
**Verify:** CTest green at every commit. Session save/load/undo-reload, ext-state round-trip,
folder relocation, and prune deletion all behave identically in DAW. **File deletion lives in
exactly one module (`prune_fs`)** — the single-file-deletion-authority invariant is *improved*
(concentrated), never diluted. Relative-paths-only persistence is unchanged.
**Depends on:** Q-W1. Best after Q-W4 (so `prune_action` routes cleanly to `prune_fs`), but
independently landable.
- [ ] Split → `session` (lifecycle/poll + `projectconfig` reload hook), `ext_state_io`
(serialization bridge + GUID minting + folder relocation).
- [ ] Isolate prune scanning + `deleteOrphanFile` (`SHFileOperationW`) → **`prune_fs`** — the
one file-deletion module; nothing else may delete bytes.
- [ ] Verify in DAW: save/load/undo-reload/relocation/prune unchanged; deletion authority is one
module; relative-paths-only holds; CTest green.
## Q-W6 — OCP registration-table + residual fat-header (I) splits
**Goal:** Close the last SOLID wart: replace the ~350-line hand-written **non-table** action
registration blocks (now isolated in `app/main.cpp` after Q-W3) with a **registration table**, so
adding an action edits one place, not four parallel ones (OCP). Split any remaining fat headers
(`capture.h`/`persist.h`) not already resolved by their TU splits (I). CONTEXT.md §Phase Q (OCP
registration-table). See `docs/product/code-organization.md` §2.3, §6 (Q-6).
**Verify:** CTest green at every commit. Every action still registers, appears in the Actions
list, and fires via `hookcommand` exactly as before; command-id + display strings unchanged
(FOREVER-STABLE, per-channel); unload still mirror-unregisters everything. Adding a hypothetical
new action now touches the table only (demonstrated in review, not shipped). Remaining fat
headers are segmented.
**Depends on:** Q-W3 (registration code must be isolated first). Sequenced last; the most
droppable point if the phase needs narrowing (Q-6).
- [ ] Convert the hand-written `Register("command_id"/"gaccel"/"hookcommand")` blocks to a
data-driven registration table; unload mirror-unregisters from the same table.
- [ ] Split residual fat headers (`capture.h`/`persist.h` and any other) alongside their TUs (I).
- [ ] Verify: all actions register/fire/unregister unchanged; command-id strings untouched; CTest
green.
## Phase Q — sequencing
```
GATE: Phase S + Phase L L3 merged to dev (D2 complete, M9 abandoned) — tree quiescent
("when Phase S and L3 are finished" — L1/L2/L3/L4L7 all landed — GATE SATISFIED)
Q-W1 (safe opener: core/json extract + directory/namespace layout on clean modules)
├─► Q-W2 (split bank_panel) ──► Q-W4 (split actions + dedupe bank verbs vs panel_bank_ops)
├─► Q-W3 (split main.cpp; hoist orchestration) ──► Q-W6 (OCP registration-table + I splits)
└─► Q-W5 (split persist; isolate prune_fs) [best after Q-W4]
```
W1 is the safe, high-leverage opener (all later waves assume the layout it establishes). The four
god-module splits (W2W5) are risk-ordered and mostly parallel-safe; W4 depends on W2's
`panel_bank_ops`, W6 depends on W3's isolated registration code. Big-bang is rejected — every wave
is independently landable and CTest-green.
## Phase Q — must-verify-before-build
- **Hot-path call/inline shape** — before landing each split, confirm no virtual dispatch and no
header→TU indirection was added on `peaks` envelope compute, audition/preview, or the realtime
tick. `computeEnvelope` stays a free function on `const std::vector<float>&`;
audition stays a direct call-through; the idle tick stays a single pointer test. (CONTEXT.md
§Phase Q, `docs/product/code-organization.md` §3.)
- **Command-id + display strings are FOREVER-STABLE** — a reorg must not change a shipped
`command_id` string, action display name, ext-state namespace, or VST3 class UID. Re-namespacing
C++ symbols is orthogonal to these on-the-wire/on-disk contract strings; keep them byte-identical
(per-channel, per the Phase V V4 contract). (CONTEXT.md §Phase Q.)
- **Name-collision sweep on sub-namespacing** — `Sample` (model) vs `AudioSample` (audio) vs the
unified `Parser` (json), plus the shared pure-UI rect types `FooterRect`/`ButtonRect` (already
hand-collision-checked in `footer_bar.h`), and any other cross-lib name that collides once
flattened into granular namespaces. Resolve by each symbol's new subsystem home before landing
W1. (naming audit §2b.2; `docs/product/code-organization.md` §6 Q-4/Q-8.)
- **Naming stays zero-behavior-change and off the wire** — the §2b renames touch C++ symbols only;
no `command_id` string, action display name, ext-state namespace/key, `reasampler:` lane prefix,
or VST3 class UID is renamed (naming audit §2b.5). A rename that would alter a shipped contract
literal is out of scope, full stop.
- **The GATE** — do not begin any Q point until **Phase S + Phase L L3** are merged to dev and the
tree is quiescent (D2 complete, M9 abandoned). Re-confirm quiescence against dev before W1.