docs(phase-q): reconcile gate to S+L3 and add naming-consistency dimension

Correct the Phase Q gate (L2 landed; outstanding is Phase S + Phase L L3;
D2 complete, M9 deferred). Add a grep-verified naming audit (code-organization
§2b) and forks Q-7/Q-8/Q-9; FOREVER-STABLE contract strings excluded.
This commit is contained in:
2026-07-27 03:50:20 -04:00
parent a434ef47f8
commit 8c65069fc3
3 changed files with 394 additions and 50 deletions
+65 -10
View File
@@ -1976,6 +1976,15 @@ below). Vital is GPLv3; the borrowed artifact is the **structural pattern**, not
- **Q-5 — god-module split granularity. REC: to the audit's named seams, no finer.** Well-factored,
not atomized.
- **Q-6 — OCP registration-table. REC: in scope, last (most droppable if narrowing).**
- **Q-7 — naming rides the relocation waves, no dedicated naming wave. REC: yes** (forced once
Q-3/Q-4 settle — a rename is near-free during relocation, near-pure-churn standalone).
- **Q-8 — class/module renames beyond the free namespace fix. REC: fix the two that actively
mislead** — `BankIndex`→`BankModel` (the `bank_model.h`/`BankIndex` file↔class word-mismatch)
and the unified JSON parser → `json::Reader`/`json::Writer` (or `json::Parser`) — **leave the
merely-quirky** (`Book`/`Bank`/`Index`, `Sample`/`AudioSample`, `MinMax`, `KitBox`). Daniel's
to call.
- **Q-9 — align the `capture_realtime` (shell) / `realtime_record` (pure) word-order inversion.
REC: yes, during Q-W3** (a free rider — W3 already hoists the realtime lifecycle).
## The directory + namespace map (Q-3 / Q-4)
@@ -2052,6 +2061,35 @@ mirror-unregisters from the same table. Command-id + display strings stay **byte
(FOREVER-STABLE, per-channel — the Phase V V4 contract). Also split residual fat headers
(`capture.h`/`persist.h`) alongside their TU splits (I).
## The naming dimension (Q-7 / Q-8 / Q-9 — grep-verified audit in `docs/product/code-organization.md` §2b)
Alongside giving symbols a directory + namespace *home* (Q-3/Q-4), Phase Q gives
inconsistently/poorly-named symbols a consistent *name* — same Vital bar, orthogonal to the SOLID
focus. The audit (2026-07-27) is grep-verified; the load-bearing findings:
- **Already consistent — preserve verbatim:** the geometry-mirror verb vocabulary
(`compute<Thing>Rects`/`compute<Thing>` + `hitTest<Thing>`, verified across `bank_grid` /
`mode_switch` / `action_buttons` / `action_bar` / `tab_strip` / `prune_button` / `overflow_menu` /
`footer_bar` / `card_drag` / `component_geometry`) and the uniform `<module>_tests` CTest suffix.
- **Collisions — resolved by the Q-4 sub-namespaces for free:** four `class Parser`
(`bank_model.cpp` / `bank_book.cpp` / `owned_manifest.cpp` / `view_mode_model.cpp`) collapse to
one `json::Parser` in Q-W1; the shared pure-UI rect types `FooterRect` / `ButtonRect` (defined in
`prune_button.h`, reused by `footer_bar.h` under an explicit hand-collision "NAME NOTE") get one
`ui::` owner; `Sample` (`model::`) vs `AudioSample` (`audio::`) de-collide by home.
- **Genuine renames (Q-8/Q-9 — Daniel's call):** `BankIndex`→`BankModel` (the `bank_model.h`
file↔class word-mismatch — the worst legibility wart, rec: rename the class so the model family
reads `BankModel`/`BankBook`/`ViewModeModel`); the unified JSON parser named `json::Reader`/
`json::Writer` at W1 mint; align `capture_realtime`(shell)/`realtime_record`(pure) to the house
shell↔core convention (`drag_out`↔`drag_out_win` is the model) during Q-W3.
- **Sequencing (Q-7):** renames ride the wave that already relocates/splits the file — **no
dedicated naming wave.** Rule: *no rename lands on a file the wave isn't otherwise touching.* W1
carries the collision + model-class renames; W2 the `panel_*` names; W3 the realtime word-order
fix. Zero-behavior-change like the rest of Phase Q; verified by the module's own test executable.
- **Out of scope (never renamed):** the FOREVER-STABLE contract strings are not C++ symbols —
`command_id` strings, action display names, ext-state namespace (`"reasampler"`/`"reasampler_beta"`)
and keys, the `reasampler:` lane prefix, the VST3 class UID. Renaming a C++ class is orthogonal
to these literals (audit §2b.5).
## Performance guardrails (HARD CONSTRAINT — Daniel's non-negotiable)
The reorg must cost **zero runtime.** The two hot paths must keep their exact call/inline shape;
@@ -2075,15 +2113,27 @@ hot ones. *A split that would add a hot-path indirection is out of scope — rew
## The GATE (load-bearing — Phase Q is last)
Phase Q is **gated on the tree being otherwise quiescent.** It does not begin until **Phase S has
merged to dev**, **Phase L (L2 + L3) has merged to dev**, any **D2 residuals** are closed, and
**M9** is landed-or-abandoned. *Why:* Phase Q touches **nearly every file in `src/`** (relocate,
re-namespace, split the four largest TUs); every large in-flight branch (Phase S on its worktree,
Phase L's `bank_panel`-touching L2/L3) is diffed against the *current flat layout*, so landing a
rename-and-relocate-everything reorg mid-flight forces every open branch through the worst conflict
class — 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. Re-confirm quiescence against dev before Q-W1.
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 the outstanding work is precisely
**(1) Phase S** merged to dev (the large second-artifact branch, currently on the phase-s worktree)
and **(2) Phase L L3** merged to dev (the VST restyle, itself gated on Phase S). **L1/L2/L4/L5/L6/L7
have already landed** (see `COMPLETED.md`) — the earlier "L2 + L3" wording was stale and is
corrected to **L3 only**. **D2** is functionally complete (D2-W1..W3-B landed; its lone open item,
a per-track lane-split panel indicator, is *explicitly deferred*, not a blocking residual). **M9**
(slots) is *explicitly deferred* (Daniel, 2026-07-26), not scheduled work. D2 and M9 are named here
only so that *reactivating* either re-arms the quiescence condition; neither blocks the gate today.
*Why:* Phase Q touches **nearly every file in `src/`** (relocate, re-namespace, split the four
largest TUs, plus the §2b renames); every large in-flight branch (Phase S on its worktree, L3 once
it lands) is diffed against the *current flat layout*, so landing a rename-and-relocate-everything
reorg mid-flight forces every open branch through the worst conflict class — 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. Re-confirm quiescence against dev before Q-W1.
> **M9 disposition (Daniel-decision note).** "Deferred indefinitely" ≠ "abandoned." Immaterial to
> the gate (both clear it); matters only if M9 is reactivated — *before* Phase Q it lands cheaply on
> the flat layout, *after* it is authored against the reorganized tree. Surfaced, not silently
> resolved (full note: `docs/product/code-organization.md` §4).
## Wave sequencing (each independently landable, CTest-green at every step)
@@ -2093,12 +2143,17 @@ every commit), a property only an *incremental* reorg uses. Risk-ordered:
- **Q-W1** — safe opener: `core/json` extract (delete 4 `Parser`s) + impose the directory/
namespace layout on the 30 clean pure libs + clean shells (pure relocation, no logic change).
All later waves assume this layout.
All later waves assume this layout. **Carries the naming collision fixes + the model-class
renames (Q-8), which are free during this relocation.**
- **Q-W2..Q-W5** — the four god-module splits, one per wave, risk-ordered (`bank_panel` →
`main.cpp` → `actions.cpp` → `persist.cpp`). Q-W4 depends on Q-W2 (`panel_bank_ops` dedupe
target); Q-W5 best after Q-W4 (`prune_action` → `prune_fs` routing); otherwise parallel-safe.
**Q-W2 carries the `panel_*` names; Q-W3 carries the `capture_realtime`/`realtime_record`
word-order fix (Q-9).**
- **Q-W6** — OCP registration-table + residual fat-header (I) splits. Depends on Q-W3 (registration
code isolated first). Sequenced last; most droppable if narrowing.
- **Naming (Q-7): no dedicated wave** — every rename rides the wave already relocating/splitting
its file; a rename that would touch an otherwise-untouched file is deferred.
## Precision / invariant implications (what Phase Q does NOT change)
+69 -20
View File
@@ -473,25 +473,43 @@ feature work runs in parallel, ungated by Phase L.
> `docs/product/code-organization.md`. When a point lands, doc-keeper moves it to
> `COMPLETED.md`.
>
> **THE GATE (load-bearing — state first).** Phase Q is **gated on the tree being otherwise
> quiescent.** It does not begin until **Phase S has merged to dev**, **Phase L (L2 + L3) has
> merged to dev**, any **D2 residuals** are closed, and **M9** is landed-or-abandoned. *Why:*
> Phase Q touches **nearly every file in `src/`** (relocate into subdirectories, re-namespace
> every header, split the four largest TUs). Every large in-flight branch (Phase S on its
> worktree, Phase L's `bank_panel`-touching L2/L3) 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. **Do not begin any Q point until the gate is satisfied.**
> **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 the outstanding work is precisely: **(1)
> Phase S** merged to dev (the large second-artifact branch, currently on the phase-s worktree —
> the dominant gate item); **(2) Phase L L3** merged to dev (the VST restyle, itself gated on
> Phase S landing on dev). **L1/L2/L4/L5/L6/L7 have already landed** (see `COMPLETED.md`) — the
> earlier "L2 + L3" wording was stale and is corrected here to **L3 only**. **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 *explicitly deferred*
> (Daniel, 2026-07-26), not scheduled work. D2 and M9 are named in the gate only so that
> *reactivating* either re-arms the quiescence condition; neither 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. **Do not begin any Q point until the gate is satisfied.**
>
> **M9 disposition (Daniel-decision note).** M9 is recorded "deferred indefinitely," which is not
> the same as "abandoned." Immaterial to the gate (both clear it). It matters only if M9 is ever
> reactivated: *before* Phase Q it lands cheaply on the flat layout; *after*, it is authored
> against the reorganized tree. Surfaced, not silently resolved — no action unless Daniel schedules
> M9. (Full note: `docs/product/code-organization.md` §4.)
>
> **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-6, see
> 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).
> 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
@@ -502,6 +520,20 @@ feature work runs in parallel, ungated by Phase L.
> 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
@@ -536,7 +568,13 @@ unified `Parser` (json) do not collide once sub-namespaced. No REAPER type cross
`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.
`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).
@@ -588,6 +626,10 @@ path gains an `InsertMedia` call). The realtime idle fast-path is still a single
- [ ] 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.
@@ -657,7 +699,8 @@ droppable point if the phase needs narrowing (Q-6).
## Phase Q — sequencing
```
GATE: Phase S + Phase L (L2+L3) + D2 residuals + M9 all merged/closed to dev (tree quiescent)
GATE: Phase S + Phase L L3 merged to dev (D2 complete, M9 deferred) — tree quiescent
("when Phase S and L3 are finished" — L1/L2/L4L7 already landed)
Q-W1 (safe opener: core/json extract + directory/namespace layout on clean modules)
@@ -681,8 +724,14 @@ is independently landable and CTest-green.
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), and any other cross-lib name that collides once flattened into
granular namespaces. Resolve by each symbol's new subsystem home before landing W1. (audit §2.4;
`docs/product/code-organization.md` §6 Q-4.)
- **The GATE** — do not begin any Q point until Phase S + Phase L (L2/L3) + D2 residuals + M9 are
merged/closed and the tree is quiescent. Re-confirm quiescence against dev before W1.
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 deferred; a reactivation of either re-arms the gate).
Re-confirm quiescence against dev before W1.
+260 -20
View File
@@ -14,10 +14,11 @@ Its build roadmap lives in **PLAN.md §Phase Q** and its authoritative spec in
(a grep-verified SOLID audit), the target directory/namespace shape grounded in the Vital
reference, and the numbered fork decisions.
**Status:** framed by product-designer (2026-07-26). Forks Q-1 … Q-6 below are the decision
record; **Q-1 (namespace letter) is settled by this doc**; the remaining forks carry a
leading recommendation and are Daniel's to call. The SOLID audit that grounds every claim
is a **grep-verified** staff-engineer analysis of the actual `src/` tree, reproduced in §2.
**Status:** framed by product-designer (2026-07-26); gate reconciled + naming dimension added
(2026-07-27). Forks Q-1 … Q-9 below are the decision record; **Q-1 (namespace letter) is settled
by this doc**; the remaining forks carry a leading recommendation and are Daniel's to call. Two
grep-verified audits ground every claim: a **SOLID audit** (§2) and a **naming/symbol-consistency
audit** (§2b) — both staff-engineer-rigor analyses of the actual `src/` tree.
---
@@ -28,7 +29,10 @@ is a **grep-verified** staff-engineer analysis of the actual `src/` tree, reprod
the discipline CMake-enforces). What Phase Q fixes is that the *shape* of the code doesn't
yet read the way the architecture actually is: `src/` is one flat 45-file directory, all
37 headers sit in one flat `reasampler` namespace, four modules have grown into
god-modules, and one utility (JSON parsing) is copy-pasted across four models.
god-modules, one utility (JSON parsing) is copy-pasted across four models, and a set of
symbols are named inconsistently or collision-prone (§2b). Phase Q addresses four dimensions:
**structure** (directories), **encapsulation** (namespaces + JSON dedupe), **factoring**
(god-module splits), and **naming** (a consistent component-naming scheme, §2b + Q-7…Q-9).
- **The quality bar is Vital** (§1). Vital groups its ~1,000-file synth by *subsystem*
(`common/` `synthesis/` `interface/` `plugin/`) with nested functional sub-dirs. That is
the aspirational shape: the directory tree *is* the architecture diagram. ReaSampler's
@@ -40,11 +44,19 @@ is a **grep-verified** staff-engineer analysis of the actual `src/` tree, reprod
added virtual dispatch, no header→TU indirection on a hot path.** This is an explicit
acceptance criterion on every point, not a footnote (§3).
- **This phase is GATED on the tree being otherwise quiescent** (§4). A structural reorg that
lands while Phase S (on the phase-s worktree) and Phase L (L2/L3) are mid-flight would
create catastrophic merge conflicts — the reorg touches nearly every file, and every
in-flight branch is diffed against the *old* layout. Phase Q starts only when Phase S,
Phase L, and any D2/M9 residuals have merged to dev and the tree is quiet. Stated
prominently because getting the gate wrong is the one way this phase does real damage.
lands while Phase S (on the phase-s worktree) is mid-flight would create catastrophic merge
conflicts — the reorg touches nearly every file, and every in-flight branch is diffed against
the *old* layout. As of 2026-07-27 the outstanding work is **Phase S** (merged to dev) and
**Phase L L3** (the VST restyle, itself gated on Phase S) — Daniel's plain target: *"when
Phase S and L3 are finished."* (L1/L2/L4L7 have already landed; D2 is functionally complete;
M9 is deferred.) Stated prominently because getting the gate wrong is the one way this phase
does real damage.
- **Beyond SOLID, Phase Q also fixes naming.** The reorg gives every symbol a *directory +
namespace home* (Q-3/Q-4); §2b's grep-verified naming audit adds the orthogonal dimension of
giving poorly/inconsistently-named symbols a *consistent name*, measured against the same
Vital "something I can stand to look at" bar. Renames ride the waves that already relocate the
file (a rename is nearly free when a file is already moving); the scheme + new forks (Q-7…Q-9)
are in §6.
- **Every point is independently landable and CTest-green at every step** (§5). The CMake
targets already draw the module seams; a file move + namespace change keeps
`ctest --test-dir build` green at each point. Green-CTest-at-every-point is an acceptance
@@ -170,6 +182,122 @@ decision-grade evidence; the PLAN points and CONTEXT spec cite back to it.
---
## 2b. The naming audit — grep-verified symbol/module-naming inconsistencies
The SOLID audit (§2) grounds *where responsibilities live*; this section grounds *what things are
called*. Same rigor: **every claim below cites a symbol or file verified by grep/read of the
actual `src/` tree (2026-07-27), not taste asserted in the abstract.** The reorg is the moment to
fix naming because a rename is nearly free when the file is already being relocated or split
(Q-W1Q-W6). Measured against the same bar: *"something I can stand to look at"* means a newcomer
can predict a symbol's name from its role and never meets two unrelated things sharing one name.
### 2b.1 The good news — what is already consistent (leave alone)
Two families are already named on a legible principle; the audit's job there is only to *protect*
them through the reorg, not to change them:
- **The geometry-mirror *verb* vocabulary is consistent.** Every pure layout/hit-test module uses
the same two verbs: `compute<Thing>Rects` / `compute<Thing>` for layout and `hitTest<Thing>` for
hit-testing — verified across `bank_grid` (`computeCellRects`/`hitTestCell`), `mode_switch`
(`computeSegmentRects`/`hitTestSegment`), `action_buttons` (`computeButtonRects`/`hitTestButton`),
`action_bar` (`computeBarSlots`/`hitTestActionBar`), `tab_strip`
(`computeTabRects`/`hitTestTabStrip`), `prune_button` (`computePruneButton`/`hitTestPruneButton`),
`overflow_menu` (`computeMenuButton`/`hitTestMenuButton`), `footer_bar`
(`computeFooterBar`/`hitTestFooterBar`), `card_drag` (`computeSlotRects`/`hitTestSlot`),
`component_geometry` (`computeButtonBox`/`hitTestBox`). This is a real, followed convention —
preserve it verbatim.
- **The `_tests` suffix is uniform.** Every pure module's CTest executable is `<module>_tests`
(30 targets in CLAUDE.md's table, no exceptions). No action.
### 2b.2 Ambiguous / collision-prone symbols (the highest-priority fixes)
These are the naming equivalent of the JSON-`Parser` DRY violation — concrete hazards, not taste:
1. **Four hand-rolled `Parser` classes, one name.** `class Parser` is defined **four times**
`bank_model.cpp:306`, `bank_book.cpp:663`, `owned_manifest.cpp:107`, `view_mode_model.cpp:654`.
Q-W1 already deletes three of them by extracting `core/json`; the naming rule is that the
survivor is **`json::Parser`** (or a more specific `json::Reader`/`json::Writer` pair — see
Q-8), never a bare `Parser` in flat scope.
2. **`FooterRect` and `ButtonRect` are shared across pure UI modules — and the codebase already
*knows* it.** `struct FooterRect` and `struct ButtonRect` are defined in `prune_button.h`
(lines 32, 46) and **reused** by `footer_bar.h`, which carries an explicit in-file "NAME NOTE"
(`footer_bar.h:2734`) documenting that `ButtonRect / FooterRect / SegmentRect / ActionBarRect /
KitBox / KitButtonBox` are "already owned in this namespace" and that new types must carry a
`FooterBar*` prefix to avoid collision. That comment is a smell made visible: the flat
`reasampler::` namespace forces every pure-UI author to hand-check for name collisions before
minting a type. This is the single strongest in-codebase argument for the Q-4 sub-namespaces —
under `reasampler::ui` these shared rect types get one clear owner and the hand-checking stops.
3. **`Sample` (`bank_model.h:69`, the bank metadata struct) vs `AudioSample` (the `peaks` float
alias).** Already flagged in §2.4/Q-4; verified — `Sample` is the model record, `AudioSample`
is a raw PCM float. Under `model::Sample` vs `audio::AudioSample` the collision risk is gone,
but the *names* still read oddly side by side (a `Sample` that is metadata, an `AudioSample`
that is one float). Noted; the namespace split is the required fix, a rename is optional (Q-8).
4. **`Selection` (`bank_grid.h:112`) and `CellRect` (`bank_grid.h:23`) are generic names in a
flat namespace.** `Selection` in particular is the kind of name a newcomer cannot place without
opening the file. `ui::Selection` / `ui::CellRect` resolve it structurally; no rename needed
beyond the namespace.
### 2b.3 Inconsistent module/type *naming families* (the taste-but-grounded tier)
Here the names are legal and non-colliding but do not read on one principle — the "stand to look
at" gap:
1. **The model-family suffixes disagree: `_model` vs `_book` vs `Index`.** Verified: the pure model
modules are `bank_model.{h,cpp}` (owning `class BankIndex`, `bank_model.h:132`), `bank_book.{h,cpp}`
(owning `class BankBook`, `bank_book.h:208`), `view_mode_model.{h,cpp}` (owning `class ViewModeModel`,
`view_mode_model.h:376`), `owned_manifest.{h,cpp}` (owning `class OwnedFileManifest`,
`owned_manifest.h:52`). Four modules, four different file↔class naming relationships:
`bank_model``BankIndex` (file says "model," class says "index"), `bank_book``BankBook`
(file = class), `view_mode_model``ViewModeModel` (file = class), `owned_manifest``OwnedFileManifest`
(file ≈ class, but the class adds "File"). The `bank_model`/`BankIndex` mismatch is the worst:
the file name and its primary class name share no word. This is a genuine legibility wart — the
fix is a *rename decision* (Q-8), not something the directory move alone resolves.
2. **The `bank_book` "wraps `bank_model`" relationship is invisible in the names.** `BankBook`
(`bank_book.h:208`) is a registry of `Bank` (`bank_book.h:147`), each wrapping a `BankIndex`
(`bank_model.h:132`). The names `Book``Bank``Index` do not read as a containment hierarchy;
a reader has to learn it. (Not necessarily worth a rename — "book of banks" is evocative — but
it is the kind of call Q-8 should make deliberately, not by accident.)
3. **`realtime_record.h` (pure) vs `capture_realtime.cpp` (shell) — the word order flips.** Verified:
the pure realtime module is `realtime_record.{h}` (owning `RecordModePlan`/`RecordPhase`/
`RecordTickInputs`, `realtime_record.h:57173`) while its shell is `capture_realtime.cpp`. So the
pure core is `realtime_record` but the shell is `capture_realtime` — the two halves of one feature
are named on inverted word order (`realtime_record` vs `capture_realtime`). Compare the *clean*
shell-pair convention elsewhere: `drag_out` (pure) ↔ `drag_out_win` (shell) — same stem, suffix
marks the platform shell. The realtime pair breaks that pattern. This is the clearest shell↔core
naming-drift instance in the tree (Q-9).
4. **`capture.{h,cpp}` is the *offline* backend shell, but the name claims all of capture.**
Verified: `capture.h` declares `ICaptureBackend`, `OfflineRenderBackend`, **and**
`RealtimeRecordBackend` (`capture.h:112,124,201`), while the realtime *implementation* lives in
`capture_realtime.cpp` and its pure planner in `realtime_record.h`. So `capture` is really
"capture interface + offline backend," a fat header (the §2.3 Interface-Segregation concern) whose
name oversells its scope. Its Q-W3 hoist (`capture_orchestrator`/`scope_resolve`) is the moment
to right-size the name.
### 2b.4 Abbreviations / opacity (low-severity, opportunistic)
Swept for names a newcomer couldn't decode; the tree is mostly clean here (a credit to it). Two
minor notes:
- **`guid_diff` / `GuidBaseline` (`guid_diff.h:40`)** — "GUID diff" is decodable in context (it
diffs the live track/item GUID set between polls) but `GuidBaseline` reads more clearly as "the
previous-poll snapshot" than the module name suggests. Low priority; leave unless its `core/view`
relocation invites it.
- **`MinMax` (`peaks.h:30`), `KitBox` (`component_geometry.h:28`)** — terse but correct and local;
no change. Named here only to record they were swept and cleared.
### 2b.5 What the naming audit does NOT touch (hard boundary)
The FOREVER-STABLE on-the-wire/on-disk contracts are **not** C++ symbol names and are **out of
scope for every rename**: `command_id` strings (`CEREBELLUM_REASAMPLER_*` / `_BETA_`), action
display names (`"ReaSampler: …"`), ext-state namespace (`"reasampler"` / `"reasampler_beta"`) and
its keys (`"banks"`, `"view_state"`, `"tail_setting"`, `"owned_files"`, `"version"`), the
`reasampler:` lane-name prefix, and the Phase S VST3 class UID. Renaming a C++ class is orthogonal
to these strings; the audit's renames touch symbols only, never a shipped contract literal. This
is the same guardrail §7 states for the reorg, restated for the naming dimension because a careless
"tidy the names" pass is exactly how a shipped id gets broken.
---
## 3. Performance is a hard constraint (the guardrail, carried verbatim-in-spirit)
Daniel's stated non-negotiable: reorganize **without sacrificing actual performance.** The
@@ -210,22 +338,49 @@ namespace of every header, splitting the four largest TUs). Meanwhile:
- **Phase S** lives on the **phase-s worktree**, is **not on dev**, and is a large body of
work (a whole second VST3 build artifact + pure sampler core). Its branch is diffed against
the *current* flat layout.
- **Phase L** has **L2** (dock-panel layout redesign, itself gated after M11) and **L3** (VST
restyle, gated on Phase S) still to land — both touching `bank_panel` and the draw/UI
layer, exactly the files Phase Q's god-module split rewrites.
- **D2 residuals / M9** (deferred) could reactivate.
- **Phase L** has **L3** (VST editor + embed-strip restyle, gated on Phase S landing on dev)
still to land — it touches the Phase S draw shells (`reasampler_editor` / `reasampler_embed`),
which arrive on dev with Phase S. (L1/L2/L4/L5/L6/L7 have **already landed** — see
`COMPLETED.md`; the once-listed "L2 pending" is stale and has been corrected here.)
- **D2** is **functionally complete** (D2-W1..W3-B landed; the only open item — a per-track
lane-split panel indicator — is *explicitly deferred*, not a blocking residual). **M9** (slots)
is *explicitly deferred* (Daniel, 2026-07-26), not scheduled work. Neither blocks the gate on
its own; both are named in the gate only so a future reactivation of either re-arms the "tree
must be quiescent" condition.
A structural reorg landing while any of these is mid-flight would force every in-flight
branch through a **rename-and-relocate-everything** merge — the worst possible conflict
class (every hunk moved, every namespace-qualified reference changed). The cost is not linear;
it is a combinatorial re-resolution of every open branch against a moved tree.
**The gate, stated as a rule:** Phase Q does not begin until **Phase S has merged to dev**,
**Phase L (L2 + L3) has merged to dev**, any **D2 residuals** are closed, and **M9** is either
landed or confirmed-abandoned — i.e. the tree is **quiescent**, with no large branch
outstanding. Phase Q is the *last* structural pillar precisely because it reshapes the ground
every other pillar stands on. Landing it early would tax every subsequent phase; landing it
last taxes nothing.
**The gate, stated as a rule (reconciled to reality, product-designer 2026-07-27):** Phase Q
does not begin until the tree is **quiescent**, with no large branch outstanding. As of
2026-07-27 the outstanding work is precisely:
1. **Phase S** — merged to dev (currently on the phase-s worktree; the large second-artifact
branch, the dominant gate item).
2. **Phase L L3** — merged to dev (the VST restyle; itself gated on Phase S, so it lands after
Phase S reaches dev). **L2 is already landed** — the earlier "L2 + L3" wording was stale.
3. **D2** — confirmed complete or its deferred indicator explicitly re-deferred. It is
functionally complete today; this line stays only so that if the deferred panel indicator is
picked up as active work, it re-arms the quiescence condition.
4. **M9** — landed **or** confirmed-abandoned. It is *deferred* today (Daniel, 2026-07-26); the
distinction between "deferred" and "abandoned" is a Daniel call (see the disposition note
below), but either disposition satisfies the gate as long as M9 is not *active in-flight work*
when Q-W1 opens.
Restated as the plain readiness target Daniel named: **"when Phase S and L3 are finished."**
Phase Q is the *last* structural pillar precisely because it reshapes the ground every other
pillar stands on. Landing it early would tax every subsequent phase; landing it last taxes
nothing.
> **Daniel-decision note (M9 disposition).** M9 (MPC-style slots) is recorded as "deferred
> indefinitely / can be picked up later" — which is *not* the same as "abandoned." For the gate
> this is immaterial (deferred and abandoned both clear it). It matters only if M9 is ever
> reactivated as scheduled work: doing so *before* Phase Q means M9 lands on the flat layout and
> is cheap; doing so *after* means M9 is authored against the reorganized `core/`/`shell/` tree.
> No action needed unless Daniel wants M9 scheduled — flagged so the "deferred vs abandoned"
> ambiguity is surfaced, not silently resolved.
*(Sequencing corollary: because the gate is "everything else first," Phase Q's own internal
sequencing —§5— is about risk-ordering the reorg, not about racing other phases.)*
@@ -255,6 +410,28 @@ individually-revertible step:
and any fat-header (I) splits not already resolved. Sequenced last because it depends on
the `main.cpp` split (W3) having already isolated the registration code.
**Where the naming work (§2b) rides — renames follow relocations, no dedicated wave (Q-7).**
A rename is cheapest when the file is already moving or splitting, so naming does **not** get its
own wave; each fix rides the wave that already touches its file:
- **W1 absorbs** the collision fixes (§2b.2): the survivor `Parser` becomes `json::Parser` (or the
Q-8 `Reader`/`Writer` pair) as the four copies collapse; and every clean pure-UI type
(`FooterRect`/`ButtonRect`/`Selection`/`CellRect`) gets its `ui::` (etc.) home as the modules
relocate — retiring the `footer_bar.h` hand-collision "NAME NOTE." W1 already re-namespaces the
clean modules, so the sub-namespace half of every §2b fix lands here for free.
- **W1 also carries** any *pure-model* class rename Q-8 settles (e.g. `BankIndex`→a name matching
`bank_model`), because those modules relocate in W1 and a class rename is a mechanical
find-replace verified by the module's own test executable.
- **W2 absorbs** the `bank_panel`-side names as the god-module splits into `panel_*`.
- **W3 absorbs** the `capture`/`realtime` shell↔core word-order fix (Q-9) — the realtime lifecycle
is *already* being hoisted in W3, so aligning `capture_realtime`/`realtime_record` naming is a
rider on a move that is happening regardless.
The rule (Q-7): **no rename lands on a file that is not otherwise being touched by its wave.** A
rename that would force a file to move *only* to be renamed is deferred — the churn/legibility
trade isn't worth a standalone edit. This keeps the naming dimension inside the same
"green-CTest-at-every-point, minimal-diff-per-wave" discipline as the rest of Phase Q.
**Why incremental beats big-bang here, concretely:** the CMake per-module static-lib + per-
module test-executable structure means a file move + namespace change is *mechanically*
verifiable — `ctest` is green or it isn't, at every point. That property only pays off if the
@@ -366,6 +543,69 @@ no finer.**
because, once W3 has hoisted the registration code, tabling it is a small, high-legibility
finish — but it is the most droppable point if the phase needs narrowing.
**Fork Q-7 — is naming its own wave, or does it ride the relocation waves? RECOMMEND: rides the
waves; no dedicated naming wave.** SETTLED-by-structure once Q-3/Q-4 are settled.
- **Recommendation:** naming fixes ride the wave that already relocates or splits the file (§5),
under the rule *no rename lands on a file the wave isn't otherwise touching.* A rename is nearly
free during a relocation (the file is open, the diff is already large, the module's own test
executable verifies it) and near-pure-churn as a standalone edit. Because Q-3 (directories) and
Q-4 (sub-namespaces) already move and re-namespace every file, the *collision* half of the
naming audit (§2b.2) is resolved by the namespace split with zero extra renames — the sub-
namespace *is* the fix. Only the genuine *class/module renames* (Q-8/Q-9) add symbol churn, and
those are scoped to files already in motion.
- **Alternative considered:** a dedicated final "naming pass" wave (Q-W7). Rejected: it would
re-open files W1W6 just closed, producing exactly the churn-without-relocation the rule forbids,
and a diff that touches everything again defeats the per-wave reviewability property.
- **This makes Q-7 not really a judgment call once Q-3/Q-4 are settled** — it is the forced
consequence of "renames are cheapest during relocation." Recorded as a fork only because Daniel
might still want naming called out as a first-class deliverable rather than folded silently into
the reorg waves; if so, the plan *names* the riders per wave (it does, §5) without adding a wave.
**Fork Q-8 — how far to push *class/module* renames (beyond the free namespace fix)? RECOMMEND:
fix the two that actively mislead; leave the merely-quirky.** Daniel's to call.
- **Recommendation (leading):** rename only where a name *actively misleads* a reader, and stop:
- **`BankIndex` → a name matching `bank_model`** (§2b.3.1) — the file/class word-mismatch is the
worst offender (`bank_model.h` owns `class BankIndex`; the two share no word). Two shapes:
(a) rename the class to `BankModel` (file = class, matches `ViewModeModel`/`BankBook`); or
(b) rename the *file* to `bank_index.{h,cpp}` (class stays `BankIndex`). **Prefer (a)** — it
makes the model family read on one principle (`BankModel`/`BankBook`/`ViewModeModel`, all
`<noun>Model`/`<noun>Book`), and it is a class rename W1 verifies via `bank_model_tests`.
- **The unified JSON `Parser`** (§2b.2.1) — make it a `json::Reader` + `json::Writer` pair (or
keep `json::Parser` if extraction stays parse-only). This is a *new* module's naming, decided
at W1 mint time, so it costs nothing to get right.
- **Leave quirky-but-harmless:** `Book``Bank``Index` containment (§2b.3.2 — evocative, learnable),
`Sample`/`AudioSample` (§2b.2.3 — the namespace split already de-collides them; renaming
`AudioSample``Pcm`/`PcmSample` is optional polish), `guid_diff`/`GuidBaseline`, `MinMax`,
`KitBox`. Renaming these is pure taste with no misleading-a-reader payoff — the "stand to look
at" bar is met by the namespace homes alone.
- **Alternative (more aggressive):** normalize the *entire* model family to one suffix
(`BankModel`/`BankBookModel`/`ViewModeModel`/`OwnedManifestModel`) and rename `AudioSample`
`PcmSample`. Rejected as the lead because it renames things that already read fine, adding symbol
churn (every call site, every test) for marginal legibility — but it is a coherent option if
Daniel wants the model family *rigidly* uniform. Kept on the table as Daniel's call.
- **Alternative (minimal):** do zero class renames; let the sub-namespaces (Q-4) carry the whole
naming win. Defensible — it is the lowest-churn, lowest-risk reading, and the namespace split
genuinely resolves every *collision*. Rejected as the lead only because `bank_model`/`BankIndex`
is a standing "what is this file" cost the reorg is uniquely cheap to fix.
**Fork Q-9 — align the `capture_realtime` / `realtime_record` shell↔core word order? RECOMMEND:
yes, during W3.** Daniel's to call.
- **Recommendation:** align the pair to the house shell↔core convention (`drag_out`
`drag_out_win`: shared stem, suffix marks the shell). The pure planner `realtime_record` and its
shell `capture_realtime` invert word order for one feature — the tree's clearest shell/core drift
(§2b.3.3). W3 is *already* hoisting the realtime lifecycle, so aligning the names is a rider on a
move that happens anyway. Two shapes: (a) core `capture_realtime` / shell `capture_realtime_shell`
(stem = `capture_realtime`, matches the `capture` offline pair); (b) core `realtime_record` /
shell `realtime_record_shell`. **Prefer (a)** — it nests the realtime naming under `capture_*`
alongside the offline path, so the whole capture subsystem reads on one stem.
- **Alternative:** leave it — the inversion is cosmetic and both names are individually clear.
Reasonable if Daniel wants W3 kept strictly to the god-module split with no adjacent renames. The
fix is cheap enough (W3 touches these files regardless) that the lead is to take it, but it is
the most droppable of the three naming forks.
---
## 7. What Phase Q does NOT change (guardrails)