From 8c65069fc31982f5dc06c7822c02fb69f7a33793 Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Mon, 27 Jul 2026 03:50:20 -0400 Subject: [PATCH] docs(phase-q): reconcile gate to S+L3 and add naming-consistency dimension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- CONTEXT.md | 75 ++++++-- PLAN.md | 89 +++++++--- docs/product/code-organization.md | 280 +++++++++++++++++++++++++++--- 3 files changed, 394 insertions(+), 50 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 3da3e09..66d1a57 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -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 + (`computeRects`/`compute` + `hitTest`, 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 `_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) diff --git a/PLAN.md b/PLAN.md index bd0421f..e68b835 100644 --- a/PLAN.md +++ b/PLAN.md @@ -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, W2–W5). 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/L4–L7 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. diff --git a/docs/product/code-organization.md b/docs/product/code-organization.md index 33d73da..7cdb8f8 100644 --- a/docs/product/code-organization.md +++ b/docs/product/code-organization.md @@ -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/L4–L7 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-W1–Q-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: `computeRects` / `compute` for layout and `hitTest` 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 `_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:27–34`) 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:57–173`) 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 W1–W6 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 + `Model`/`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)