docs(phase-q): fold Q-W0 sign-off into PLAN/CONTEXT — all 59 dispositions settled; Q-W2v wave, T4-18 VST placement, ~600 ceiling + structural heuristics, W3 riders (wav_codec, ICaptureBackend, Q-9)
This commit is contained in:
+183
-40
@@ -2301,7 +2301,10 @@ shell maps that pure result to a cursor. No cue logic in the shell; the shell on
|
||||
> findings report — a functional-correctness/algorithm-quality complement to the SOLID/naming audit
|
||||
> below. **Q-W1 is gated on Q-W0's triage being complete and Daniel signing off on each finding's
|
||||
> disposition** (fix-now vs. document-and-defer). Spec: §"The pre-restructure audit wave (Q-W0)"
|
||||
> below.
|
||||
> below. **STATUS (2026-07-28): Q-W0 COMPLETE and SIGNED OFF — all 59 dispositions approved; the
|
||||
> Q-W1 sub-gate is satisfied once the six approved fix-now remediations land (in flight on
|
||||
> `pq-w0-fixes`). The audit's plan reshape is folded into this spec: Q-W2 6→8 seams, NEW wave
|
||||
> Q-W2v (parallel with Q-W2), Q-W3 3→4 hoists + riders, Q-W5 + the ext-state-loop dedupe.**
|
||||
|
||||
## What it is
|
||||
|
||||
@@ -2330,6 +2333,21 @@ reinvented wheel or a numerically-fragile DSP path should be eliminated or consc
|
||||
tree. Bringing the code "into the realm of something I can stand to look at" is not only a matter of
|
||||
shape; it is also a matter of the code being *functionally sound*.
|
||||
|
||||
**STATUS (2026-07-28): Q-W0 is COMPLETE — audit run, triage done, sign-off given.** Four parallel
|
||||
tracks (T1 DSP, T2 architecture, T3 env-coupled constants, T4 sizing + placement), **59
|
||||
findings**; report: `docs/product/code-quality-audit.md`, appendices:
|
||||
`docs/product/audit-notes/q-w0-t{1..4}-*.md`. Daniel approved **every disposition as proposed** on
|
||||
2026-07-28. Six findings are fix-now *in Q-W0* — **T1-01** (linked-lag stereo splice alignment),
|
||||
**T1-03** (Preserve prime bound + immediate `freezeTail()`), **T1-09** (`declickR_` dead-state
|
||||
removal, riding the `sampler_core` edits), **T2-01(a)** (provenance wire-cursor hardening
|
||||
backport), **T3-01** (gain-ramp seconds), **T3-03** (fade-ceiling seconds) — in flight on branch
|
||||
`pq-w0-fixes`; **the Q-W1 sub-gate is satisfied once they land** (each with its module's CTest
|
||||
target green; the audible DSP fixes with a stated before/after listening check). All other
|
||||
fix-nows are assigned to the wave that already opens their file and are recorded in the wave specs
|
||||
below. **The Q-11 question is answered:** the correlation-aligned SOLA pitch engine is **sound —
|
||||
no technique replacement (phase-vocoder / WSOLA) warranted**; every pitch finding is a bounded
|
||||
in-technique fix or a documented operating limit.
|
||||
|
||||
**Audit scope — the named surfaces:**
|
||||
|
||||
1. **DSP / audio, close eye on pitch.** Assess *algorithm quality* — correctness, artifacts,
|
||||
@@ -2395,10 +2413,11 @@ the subsystem map off the folders. ReaSampler adopts the *pattern* (directory =
|
||||
adapted to its own most load-bearing invariant — the pure/shell split — as the top level (see
|
||||
below). Vital is GPLv3; the borrowed artifact is the **structural pattern**, not code.
|
||||
|
||||
## Settled decisions (Q-1 settled; Q-2..Q-6 recommended — see `docs/product/code-organization.md` §6)
|
||||
## Settled decisions (Q-1/Q-10/Q-11 settled 2026-07-27; Q-5/Q-6/Q-8/Q-9 + the audit's §4 forks settled 2026-07-28 — REC history kept; see `docs/product/code-organization.md` §6 and `docs/product/code-quality-audit.md` §4)
|
||||
|
||||
- **Q-1 — namespace letter. SETTLED: `Q` (Quality).** Point-id family `Q1..Qn`, wave prefixes
|
||||
`Q-W0` (the pre-restructure audit) then `Q-W1..Q-W6` (the structural reorg). `O` (Organization)
|
||||
`Q-W0` (the pre-restructure audit) then `Q-W1..Q-W6` (the structural reorg; + **`Q-W2v`**, the
|
||||
VST god-module wave added at the Q-W0 sign-off, 2026-07-28). `O` (Organization)
|
||||
was set aside: the glyph reads ambiguously against zero in
|
||||
point ids, and "Organization" undersells a phase measured against a *quality* bar.
|
||||
- **Q-2 — JSON extraction in scope + first. REC: yes.** The 4× duplicated `Parser` is the largest
|
||||
@@ -2413,17 +2432,43 @@ below). Vital is GPLv3; the borrowed artifact is the **structural pattern**, not
|
||||
`audio`/`ui`/`reclaim`/`version`/`json`. Directory and namespace agree; a symbol's home is
|
||||
unambiguous from either.
|
||||
- **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).**
|
||||
not atomized. **SETTLED (Daniel, 2026-07-28): seams-by-responsibility with the Q-W0 T4 seam
|
||||
lists adopted** — `bank_panel` 6→8 seams (+ `panel_layout`, `panel_drag`; T4-01),
|
||||
`capture_orchestrator` further split with `capture_batch` (T4-02) — **and the ~600-line file
|
||||
ceiling is an acceptance criterion on every split wave**: seams are the method, the ceiling is
|
||||
the bar; arbitrary bisection to hit the number is rejected. One documented exception:
|
||||
`sampler_core.cpp` stays whole (T4-14/T4-27).
|
||||
- **Q-6 — OCP registration-table. REC: in scope, last (most droppable if narrowing).** **SETTLED
|
||||
(Daniel, 2026-07-28): in scope, last wave, as planned.**
|
||||
- **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.
|
||||
to call. **SETTLED (Daniel, 2026-07-28): both renames** — `BankIndex`→`BankModel` (W1) and the
|
||||
JSON parser minted as `json::Reader`/`json::Writer` (W1). From the audit, additionally:
|
||||
**`ICaptureBackend` is deleted in Q-W3** (T4-26 — one deriver, zero polymorphic call sites; the
|
||||
CLAUDE.md/CONTEXT "two backends behind one interface" correction **rides Q-W3's own commit**,
|
||||
not earlier).
|
||||
- **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).
|
||||
REC: yes, during Q-W3** (a free rider — W3 already hoists the realtime lifecycle). **SETTLED
|
||||
(Daniel, 2026-07-28): yes** — the pure module takes the stem `capture_realtime`, the shell
|
||||
takes the suffix (the `drag_out`↔`drag_out_win` model), during W3.
|
||||
- **Audit §4a — VST placement. SETTLED (Daniel, 2026-07-28): T4-18** — `src/vst/` integrates into
|
||||
the single `core/`/`shell/` top split as `core/instrument/{engine,map,ui}` +
|
||||
`shell/instrument/`. One rule, no special case: a file's directory says whether it may touch a
|
||||
*host* type (REAPER **or** VST3 SDK); the artifact boundary is a link-graph fact the sources
|
||||
already straddle. The T4-19 artifact-first subtree was set aside. Directory map below updated.
|
||||
- **Audit §4e — WAV/RIFF consolidation moment. SETTLED (Daniel, 2026-07-28): a named rider on
|
||||
Q-W3** — one pure **`wav_codec`** owner (chunk walker + layout + build + patch), absorbing the
|
||||
T4-10 ingest extraction (T2-08 / T4-23 / T4-10). The dedup-by-hash / null-test maintenance
|
||||
surface gets exactly one implementation.
|
||||
- **Audit §4f — Q-W2v scheduling. SETTLED (Daniel, 2026-07-28): parallel with Q-W2** (different
|
||||
artifact, zero file overlap); the serial "Q-W7" alternative was set aside.
|
||||
- **Audit §4b/§4c/§4d — the Q-W0 fix-now set. SETTLED (Daniel, 2026-07-28): all approved
|
||||
fix-now** — T1-01 + T1-03 (with T1-09 riding), T2-01(a), T3-01 + T3-03; in flight on
|
||||
`pq-w0-fixes`; landing them closes Q-W0 and opens Q-W1.
|
||||
|
||||
## The directory + namespace map (Q-3 / Q-4)
|
||||
|
||||
@@ -2434,24 +2479,42 @@ directories.
|
||||
- `core/model/` (`::model`) — `bank_model`, `bank_book`, `owned_manifest`, `provenance`
|
||||
- `core/view/` (`::view`) — `view_mode_model`, `view_tree`, `lane_keys`, `mode_switch`
|
||||
- `core/capture/` (`::capture`) — `render_settings`, `batch_capture`, `tail_control`,
|
||||
`capture_paths`, `wav_trim`, `insert_plan`
|
||||
`capture_paths`, `wav_trim`, `insert_plan`; **post-Q-W3** `wav_codec` (the one pure WAV/RIFF
|
||||
owner — audit §4e rider)
|
||||
- `core/audio/` (`::audio`) — `peaks`
|
||||
- `core/ui/` (`::ui`) — `theme`, `component_geometry`, `bank_grid`, `tab_strip`, `action_buttons`,
|
||||
`prune_button`
|
||||
- `core/reclaim/` (`::reclaim`) — `prune_reconcile`
|
||||
- `core/version/` (`::version`) — `app_version`
|
||||
- `core/json/` (`::json`) — **NEW** — extracted parser/serializer (replaces the 4 duplicate
|
||||
`Parser`s)
|
||||
- `core/json/` (`::json`) — **NEW** — extracted parser/serializer, minted as
|
||||
`json::Reader`/`json::Writer` (replaces the 5 duplicate hand-rolled decoders — the four
|
||||
`Parser`s + `tail_control`'s, T2-02)
|
||||
- `core/instrument/` — **T4-18 SETTLED (Daniel, 2026-07-28)** — the VST artifact's pure side
|
||||
joins the one top split (directory = "may it touch a host type", REAPER *or* VST3 SDK):
|
||||
- `core/instrument/engine/` — `sampler_core`, `pitch_shift`, `velocity_curve`, `master_gain`
|
||||
- `core/instrument/map/` — `sample_map` (+ `component_state_io` post-Q-W2v), `bank_sync`,
|
||||
`bridge_marshal`, `note_entry`, `trigger_seam`
|
||||
- `core/instrument/ui/` — `editor_geometry`, `keyboard_strip`, `waveform_view`,
|
||||
`capture_browser`, `browser_scroll`, `param_slider`, `knob_deck`, `curve_popup`,
|
||||
`envelope_overlay`, `envelope_edit`, `embed_strip`
|
||||
|
||||
**`shell/` (REAPER-facing — subdir by subsystem, namespace as house style prefers):**
|
||||
- `shell/capture/` — `capture`, `capture_realtime`, `provenance_shell`, `track_guid`, `item_read`;
|
||||
**post-Q-W3** `capture_orchestrator`, `scope_resolve`, `realtime_lifecycle`
|
||||
**post-Q-W3** `capture_orchestrator`, `capture_batch`, `scope_resolve`, `realtime_lifecycle`,
|
||||
`capture_realtime_finalize` (with the Q-9 stem/suffix rename)
|
||||
- `shell/panel/` — `draw_kit`; **post-Q-W2** `panel_render`, `panel_thumbnails`,
|
||||
`panel_audition`, `panel_input`, `panel_bank_ops`, `panel_window` (from `bank_panel`)
|
||||
`panel_audition`, `panel_input`, `panel_layout`, `panel_drag`, `panel_bank_ops`,
|
||||
`panel_window` (from `bank_panel` — eight seams, T4-01)
|
||||
- `shell/view/` — `view`
|
||||
- `shell/persist/` — **post-Q-W5** `session`, `ext_state_io`, `prune_fs` (from `persist`)
|
||||
- `shell/actions/` — `drag_out_win`; **post-Q-W4** `design_view_actions`, `bank_actions`,
|
||||
`prune_action` (from `actions`)
|
||||
- `shell/instrument/` — `reaper_bridge`, `reasampler_embed`, `vst_entry`, `reasampler_vst.h` /
|
||||
`reasampler_uid.h`; **post-Q-W2v** the editor TUs (`editor_session` / `editor_controls` /
|
||||
`editor_paint_sample` / `editor_paint_browse_zone` / `editor_input_sample` /
|
||||
`editor_input_browse_zone` / `editor_platform` — `editor_layout` hoists *pure* →
|
||||
`core/instrument/ui/`, discharging T2-06) and the processor TUs (`processor_state` /
|
||||
`processor_reload` / lifecycle+`process()`)
|
||||
|
||||
**`app/`:** `main.cpp` (post-Q-W3: API-pointer ownership + `ReaperPluginEntry` + dispatch only).
|
||||
|
||||
@@ -2459,37 +2522,83 @@ directories.
|
||||
unified `Parser` (`::json`) must not collide once flattened into granular namespaces; resolve by
|
||||
subsystem home.
|
||||
|
||||
## The god-module split seams (Q-5)
|
||||
## The god-module split seams (Q-5 — SETTLED 2026-07-28: T4 seam lists adopted; ~600 ceiling is the bar)
|
||||
|
||||
Split each to the audit-validated seams, no finer:
|
||||
Split each to the audit-validated seams, no finer — **and every shipped TU lands under the
|
||||
~600-line ceiling** (the Q-5 settlement: seams are the method, the ceiling is the bar; arbitrary
|
||||
bisection to hit the number is rejected; the one documented exception is `sampler_core.cpp`).
|
||||
LOC figures updated to the Q-W0 T4 census (2026-07-28):
|
||||
|
||||
- **`bank_panel.cpp` (2424 LOC → `shell/panel/`, Q-W2):** `panel_render` (draw/paint) /
|
||||
`panel_thumbnails` (compute+cache) / `panel_audition` (preview engine — **hot path, direct
|
||||
call-through**) / `panel_input` (mouse/key/wheel + new-content detection) / `panel_bank_ops`
|
||||
(bank CRUD — the single owner W4 dedupes against) / `panel_window` (lifecycle + OS
|
||||
drag-out/drop-target). Split the fat `bank_panel.h` per seam (I).
|
||||
- **`main.cpp` (1762 LOC → hoist to `shell/capture/`, Q-W3):** `capture_orchestrator`
|
||||
(`RunCapture`/`captureAndIndexOne`/`renderOffline`/batch/recapture/realtime `Run*`) /
|
||||
`scope_resolve` (range/razor/track resolution + provenance assembly inputs) /
|
||||
`realtime_lifecycle` (state machine + globals + selection guards). `FxBypassGuard` moves out
|
||||
but **stays stack RAII** (precision-critical). `main.cpp` → `app/`, reduced to pointers + entry
|
||||
+ dispatch.
|
||||
- **`actions.cpp` (981 LOC → `shell/actions/`, Q-W4):** `design_view_actions` /
|
||||
- **`bank_panel.cpp` (3459 LOC → `shell/panel/`, Q-W2 — eight seams, T4-01):** `panel_render`
|
||||
(draw/paint) / `panel_thumbnails` (compute+cache) / `panel_audition` (preview engine — **hot
|
||||
path, direct call-through**) / `panel_input` (mouse/key/wheel + new-content detection) /
|
||||
**`panel_layout`** (toolbar/footer/menu rects + row/cluster builders + region geometry — new
|
||||
seam) / **`panel_drag`** (the card-drag/hover state machine, pure mirror `card_drag` — new
|
||||
seam) / `panel_bank_ops` (bank CRUD — the single owner W4 dedupes against) / `panel_window`
|
||||
(lifecycle + OS drag-out/drop-target). Without the two new seams, `panel_render` (~700) and
|
||||
`panel_input` (~800) would ship over the ceiling. Split the fat `bank_panel.h` per seam (I).
|
||||
- **`main.cpp` (1897 LOC → hoist to `shell/capture/`, Q-W3 — four hoists, T4-02):**
|
||||
`capture_orchestrator` (`RunCapture`/`captureAndIndexOne`/`renderOffline`/single-capture +
|
||||
realtime/insert action bodies — lands ~450) / **`capture_batch`** (batch family +
|
||||
`RunRecaptureFromSource` + the two RAII selection guards — new hoist; recapture is
|
||||
planner-driven like batch and shares the guard machinery) / `scope_resolve` (range/razor/track
|
||||
resolution + provenance assembly inputs) / `realtime_lifecycle` (state machine + globals).
|
||||
`FxBypassGuard` moves out but **stays stack RAII** (precision-critical). `main.cpp` → `app/`,
|
||||
reduced to pointers + entry + dispatch. **Wave riders (SETTLED 2026-07-28):** delete
|
||||
`ICaptureBackend` + correct the CLAUDE.md/CONTEXT description in the same commit (T4-26);
|
||||
shared `stampCaptureSample` epilogue dedupe (T2-09); `capture_realtime_finalize` split with
|
||||
the Q-9 rename (T4-08); `makeUniqueTag` per-session monotonic counter (T1-11); the pure
|
||||
`wav_codec` consolidation (audit §4e).
|
||||
- **`actions.cpp` (1016 LOC → `shell/actions/`, Q-W4 — T4-03: seams unchanged, still
|
||||
sub-600):** `design_view_actions` /
|
||||
`bank_actions` / `prune_action` (`doBankPruneFolder` — the single file-deletion action). Dedupe
|
||||
`promptText`/`mintBankId` + bank verbs against `panel_bank_ops`.
|
||||
- **`persist.cpp` (766 LOC → `shell/persist/`, Q-W5):** `session` (lifecycle/poll +
|
||||
- **`persist.cpp` (852 LOC → `shell/persist/`, Q-W5 — T4-04: seams unchanged; the pS-usage
|
||||
growth landed exactly where this wave isolates it):** `session` (lifecycle/poll +
|
||||
`BeginLoadProjectState` reload hook) / `ext_state_io` (serialization bridge + GUID minting +
|
||||
folder relocation) / **`prune_fs`** (prune scan + `deleteOrphanFile` via `SHFileOperationW` —
|
||||
the isolated single file-deletion authority).
|
||||
the isolated single file-deletion authority). **Wave rider (T2-04):** the `GetProjExtState`
|
||||
grow-loop ×3 dedupes onto `bridge_marshal`'s generalized retry policy — `usage_scan`'s
|
||||
prune-safety-adjacent copy included.
|
||||
- **`reasampler_editor.cpp` (3065 LOC — the largest file in the repo → `shell/instrument/`,
|
||||
Q-W2v — eight TUs, T4-11; split axis = the Sample/Browse/Zone face structure):**
|
||||
`editor_session` / `editor_controls` / `editor_layout` (**pure-candidate hoist** →
|
||||
`core/instrument/ui/`, `editor_geometry` the named owner — discharges T2-06's
|
||||
stranded-layout-math finding) / `editor_paint_sample` / `editor_paint_browse_zone` /
|
||||
`editor_input_sample` / `editor_input_browse_zone` / `editor_platform`. Rider: adopt the pure
|
||||
`ThumbnailKey` on the VST side while the editor is open (T2-10).
|
||||
- **`reasampler_processor.cpp` (1164 LOC → `shell/instrument/`, Q-W2v — three TUs, T4-12):**
|
||||
`processor_state` / `processor_reload` / lifecycle+`process()` kept whole. **No virtual seam
|
||||
on the atomic-pointer-swap reload pattern (T4-29).**
|
||||
- **`sample_map` (970 LOC + 708-line header → Q-W2v, T4-13 ≡ T2-07):** resolution core vs the
|
||||
**`component_state_io`** binary ComponentState codec (+ matching header split) — the codec
|
||||
grows every envelope bump (v6→v11 in one quarter); the extension stops linking the whole voice
|
||||
engine to serialize one preset blob. The `core/wire` LE byte-codec template
|
||||
(`putLE`/`readLE`, T4-20) lands with it.
|
||||
- **`sampler_core.cpp` (968 LOC + 762-line header — Q-W2v, T4-14/T4-27): the TU stays WHOLE —
|
||||
the documented hot-path exception to the ~600 ceiling.** Envelope `tick()`s run
|
||||
per-voice-per-sample; same-TU definition is what lets the compiler inline the stack (no LTO in
|
||||
the build); a by-class split is the exact heuristic-(3) dispatch blowout. The header splits
|
||||
into `zone_params.h` + `sampler_core.h`. Recorded here so nobody "fixes" it later.
|
||||
|
||||
## The JSON extraction (Q-2 / Q-W1)
|
||||
|
||||
Extract one pure **`core/json`** (`::json`): parser (`parseString`/`parseInt`/`parseKey`/
|
||||
Extract one pure **`core/json`** (`::json` — minted as **`json::Reader`/`json::Writer`**, Q-8
|
||||
SETTLED 2026-07-28): parser (`parseString`/`parseInt`/`parseKey`/
|
||||
`skipValue` + escape) + serialize/emit helpers. Rewire `bank_model`, `bank_book`,
|
||||
`view_mode_model`, `owned_manifest` onto it and **delete their four hand-rolled `Parser`s**.
|
||||
`view_mode_model`, `owned_manifest`, **and `tail_control` (T2-02 — the fifth hand-rolled decoder
|
||||
the §2 audit undercounted)** onto it and **delete all five hand-rolled decoders**.
|
||||
Round-trip output must be **byte-identical** to before — this is a structural dedupe, not a format
|
||||
change. Off all hot paths (serialization runs at save/load, never per frame) — safe to abstract
|
||||
freely.
|
||||
freely. **W1 siblings (from the Q-W0 sign-off):** the shared `readFileBytes` pure helper
|
||||
(T2-03); the length-prefixed wire-`Cursor` collapse into one shared wire codec beside
|
||||
`core/json`, consumed by `provenance` / `assignment_request` / `sample_usage` /
|
||||
`parseBankGeneration` (T2-01(b) — the structural half; the hardening backport lands in Q-W0);
|
||||
the **concrete** `ui::Rect` unification + `contains()` + per-role aliases, retiring the
|
||||
XYWH-vs-LTRB fork — NOT a template (T2-05 ≡ T4-21), with the `clamp01` rider (T4-24) and the
|
||||
`hitIndex` template only as an opportunistic follow-on (T4-22); the `slot_map` extraction
|
||||
(T4-05) and optional `view_mode_model` planner split (T4-06) riding files W1 already opens; the
|
||||
relocation scope grows to the ~20 clean VST pure libs under `core/instrument/` (T4-18).
|
||||
|
||||
## The OCP registration-table (Q-6 / Q-W6)
|
||||
|
||||
@@ -2515,7 +2624,7 @@ focus. The audit (2026-07-27) is grep-verified; the load-bearing findings:
|
||||
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`
|
||||
- **Genuine renames (Q-8/Q-9 — SETTLED, Daniel 2026-07-28: all land):** `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
|
||||
@@ -2550,6 +2659,25 @@ the following are acceptance criteria on every point:
|
||||
**Net:** every recommended split falls on a cold path or preserves call/inline shape on the two
|
||||
hot ones. *A split that would add a hot-path indirection is out of scope — rework it or drop it.*
|
||||
|
||||
## Structural heuristics (Daniel, 2026-07-28 — acceptance criteria phase-wide)
|
||||
|
||||
Three heuristics postdate the original framing and bind every wave. They **generalize** the
|
||||
three-hot-path performance guardrail above — they do not replace it:
|
||||
|
||||
1. **More directories is a must; more files is good; ~600-line file ceiling.** SRP applies to
|
||||
namespaces, encapsulation, and file organization alike. The ceiling is the *bar*, the audit's
|
||||
named seams are the *method*: a file landing over ~600 needs a responsibility seam, not an
|
||||
arbitrary bisection (bisection-to-hit-the-number is rejected). A documented hot-path
|
||||
exception (`sampler_core.cpp`, T4-14/T4-27) is legitimate; silent overshoot is not.
|
||||
2. **Templates are the right tool for compile-time dedup — use them where earned.** The LE
|
||||
byte-codec `putLE`/`readLE` (T4-20) is earned: compile-time dispatch, zero runtime cost, off
|
||||
the hot paths. The rect family is NOT (T4-21): the types differ in name only, so one
|
||||
**concrete** `ui::Rect` — a template there would model nothing.
|
||||
3. **SOLID is great, but saved CPU is better.** No dispatch-stack blowouts *anywhere* — not just
|
||||
the three named hot paths; prefer static polymorphism where the types are compile-time-known.
|
||||
The T4-27 warning is the canonical case: a by-class `sampler_core` split would put virtual
|
||||
envelope `tick()`s on the per-voice-per-sample path — exactly the blowout this forbids.
|
||||
|
||||
## The GATE (load-bearing — Phase Q is last)
|
||||
|
||||
Phase Q is **gated on the tree being otherwise quiescent.** Daniel's plain readiness target:
|
||||
@@ -2583,16 +2711,27 @@ every commit), a property only an *incremental* reorg uses. Risk-ordered:
|
||||
pre-restructure audit wave (Q-W0)"). Produces a written, triaged findings report; runs **first**
|
||||
and **gates Q-W1** — no structural point begins until the triage closes and Daniel signs off on
|
||||
every disposition. Fix-now findings are remediated here or folded into the wave that opens the
|
||||
file; the report may add/reshape downstream Q-W1..Q-W6 points before they start.
|
||||
- **Q-W1** — safe opener: `core/json` extract (delete 4 `Parser`s) + impose the directory/
|
||||
file; the report may add/reshape downstream Q-W1..Q-W6 points before they start. **COMPLETE
|
||||
and signed off 2026-07-28; the sub-gate closes when the six fix-now remediations land
|
||||
(`pq-w0-fixes`).**
|
||||
- **Q-W1** — safe opener: `core/json` extract (delete the 5 hand-rolled decoders — T2-02 adds
|
||||
`tail_control`) + impose the directory/
|
||||
namespace layout on the 30 clean pure libs + clean shells (pure relocation, no logic change).
|
||||
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.
|
||||
renames (Q-8), which are free during this relocation.** **Scope grown by Q-W0 (2026-07-28):**
|
||||
+ `readFileBytes` (T2-03), the wire-`Cursor` codec (T2-01(b)), the concrete `ui::Rect`
|
||||
unification (T2-05 ≡ T4-21) + `clamp01` (T4-24), the `slot_map`/planner riders (T4-05/T4-06),
|
||||
and the ~20 clean VST pure libs under `core/instrument/` (T4-18).
|
||||
- **Q-W2..Q-W5 (+ Q-W2v)** — the god-module splits, risk-ordered (`bank_panel` →
|
||||
`main.cpp` → `actions.cpp` → `persist.cpp`), plus **Q-W2v** (NEW, added at the Q-W0 sign-off:
|
||||
the VST god-modules — editor eight TUs / processor three TUs / `component_state_io`;
|
||||
`sampler_core` TU whole, the documented exception), which **runs parallel with Q-W2**
|
||||
(different artifact, zero file overlap — audit §4f SETTLED). 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).**
|
||||
word-order fix (Q-9) + the settled riders (`ICaptureBackend` deletion + doc correction,
|
||||
`wav_codec`, `stampCaptureSample` dedupe, T1-11, `capture_realtime_finalize`).**
|
||||
- **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
|
||||
@@ -2624,7 +2763,9 @@ every commit), a property only an *incremental* reorg uses. Risk-ordered:
|
||||
realtime-tick — ever (a hard acceptance bar, not advice).
|
||||
- **No design-level (D-letter SOLID) rework.** `main`/`bank_panel` depending on concrete capture
|
||||
backends is a low-priority Dependency-Inversion concern — **out of scope** (a design change, not
|
||||
a reorg). Phase Q reorganizes; it does not re-architect interfaces.
|
||||
a reorg). Phase Q reorganizes; it does not re-architect interfaces. (Deleting the *dead*
|
||||
`ICaptureBackend` abstraction in Q-W3 is the opposite move — removing a false interface with
|
||||
one deriver and zero polymorphic call sites, T4-26 — and is in scope.)
|
||||
- **No `peaks` data-ownership change.** `peaks` forcing a whole-file `std::vector<float>` copy on
|
||||
the thumbnail path is noted but **not touched** — reworking it risks the hot path.
|
||||
- **No big-bang commit.** Every wave is independently landable and CTest-green; reject a change set
|
||||
@@ -2632,5 +2773,7 @@ every commit), a property only an *incremental* reorg uses. Risk-ordered:
|
||||
- **Do not begin before the GATE.** Re-confirm the tree is quiescent (Phase S + L + D2 merged/closed;
|
||||
M9 abandoned) before any Q point. **And do not begin any structural point (Q-W1+) before the Q-W0
|
||||
sub-gate:** the audit's triage is complete and Daniel has signed off on every finding's disposition.
|
||||
(Sign-off given 2026-07-28; the sub-gate now closes when the six fix-now remediations land on
|
||||
`pq-w0-fixes`.)
|
||||
- **Verify** the CMake `src/` path updates and the SWELL/LICE surfaces still resolve after
|
||||
relocation, as the existing build already requires.
|
||||
|
||||
@@ -220,8 +220,21 @@ panel adopts knob deck + curve popup, `param_slider` slider rows retired on that
|
||||
> downstream Q-W1..Q-W6 points; fixes that Q-W0 classifies fix-now are remediated in Q-W0 (or folded
|
||||
> into the wave that already touches the file), **not** deferred silently into the structural waves.
|
||||
>
|
||||
> **Q-W0 SIGN-OFF: COMPLETE (Daniel, 2026-07-28).** The audit ran as four parallel tracks (T1 DSP,
|
||||
> T2 architecture, T3 env-coupled constants, T4 sizing/placement — **59 findings**; report
|
||||
> `docs/product/code-quality-audit.md`, appendices `docs/product/audit-notes/`), and **all 59
|
||||
> findings' dispositions are approved as proposed.** The Q-W1 sub-gate is satisfied **once the six
|
||||
> approved fix-now remediations land** (in flight on branch `pq-w0-fixes`, Q-W0-scoped): T1-01,
|
||||
> T1-03, T1-09, T2-01(a), T3-01, T3-03. The audit's §3 plan reshape is **folded into the waves
|
||||
> below** (Q-W2 6→8 seams; NEW wave **Q-W2v** parallel with Q-W2; Q-W3 3→4 hoists + riders; Q-W5
|
||||
> + the ext-state-loop dedupe), and its §4 decision list is settled — see the settlement block
|
||||
> below. The Q-11 question is answered by the audit: the SOLA pitch engine is **sound — no
|
||||
> technique replacement warranted**; every pitch finding is a bounded in-technique fix or a
|
||||
> documented operating limit.
|
||||
>
|
||||
> **Settled (Q-1, this-doc):** the phase is **`Q` (Quality)**; point-id family `Q1..Qn`, wave
|
||||
> prefixes `Q-W0` (the pre-restructure audit) then `Q-W1..Q-W6` (the structural reorg).
|
||||
> prefixes `Q-W0` (the pre-restructure audit) then `Q-W1..Q-W6` (the structural reorg; **+
|
||||
> `Q-W2v`**, the VST god-module wave added at the Q-W0 sign-off, 2026-07-28).
|
||||
> **Settled (Q-10/Q-11, Daniel 2026-07-27):** Q-10 audit-report home = a **committed doc**
|
||||
> (`docs/product/code-quality-audit.md`, not a tracked issue list); Q-11 pitch-remediation depth =
|
||||
> **defer to findings** (default document-and-defer; weigh a bounded OLA fix before a technique
|
||||
@@ -238,6 +251,25 @@ panel adopts knob deck + curve popup, `param_slider` slider rows retired on that
|
||||
> `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).**
|
||||
>
|
||||
> **SETTLED (Daniel, 2026-07-28 — with the Q-W0 sign-off; the REC record above kept as history):**
|
||||
> **Q-5 SETTLED** — split to **seams-by-responsibility with the T4 seam lists adopted**
|
||||
> (`bank_panel` 6→8 seams adding `panel_layout` + `panel_drag`, T4-01; `capture_orchestrator`
|
||||
> further split with `capture_batch`, T4-02), and the **~600-line file ceiling is an acceptance
|
||||
> criterion on every split wave** — seams are the method, the ceiling is the bar; arbitrary
|
||||
> bisection to hit the number is rejected. **Q-6 SETTLED: in scope, last wave, as planned.**
|
||||
> **Q-8 SETTLED: both renames** — `BankIndex`→`BankModel` (W1) and the JSON parser minted as
|
||||
> `json::Reader`/`json::Writer` (W1); additionally from the audit, **`ICaptureBackend` is deleted
|
||||
> in Q-W3** (T4-26 — one deriver, zero polymorphic call sites; the CLAUDE.md/CONTEXT "two
|
||||
> backends behind one interface" correction **rides Q-W3 itself**, recorded as a rider — the docs
|
||||
> are not edited before that wave). **Q-9 SETTLED: yes** — align to stem `capture_realtime`,
|
||||
> shell suffixed, during W3. **VST placement (audit §4a) SETTLED: T4-18** — `src/vst/` integrates
|
||||
> into the single `core/`/`shell/` top split as `core/instrument/{engine,map,ui}` +
|
||||
> `shell/instrument/` (Q-3 directory map updated in CONTEXT.md §Phase Q). **WAV/RIFF
|
||||
> consolidation (audit §4e) SETTLED:** a named rider on **Q-W3** — one pure **`wav_codec`** owner
|
||||
> (walker + layout + build + patch), absorbing the T4-10 ingest extraction. **Q-W2v scheduling
|
||||
> (audit §4f) SETTLED: parallel with Q-W2** (different artifact, zero file overlap; the serial
|
||||
> "Q-W7" alternative set aside).
|
||||
>
|
||||
> **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
|
||||
@@ -247,6 +279,21 @@ panel adopts knob deck + curve popup, `param_slider` slider rows retired on that
|
||||
> an acceptance criterion on every point: *a split that would add a hot-path indirection is out
|
||||
> of scope — rework it or drop it.*
|
||||
>
|
||||
> **STRUCTURAL HEURISTICS (Daniel, 2026-07-28 — acceptance criteria on every wave; these
|
||||
> *generalize* the three-hot-path guardrail above, they do not replace it):**
|
||||
> (1) **More directories is a must, more files is good, ~600-line file ceiling** — SRP applies to
|
||||
> namespaces, encapsulation, and file organization alike. The ceiling is the *bar*, the audit's
|
||||
> named seams are the *method*: a file landing over ~600 needs a responsibility seam, not an
|
||||
> arbitrary bisection; a documented hot-path exception (`sampler_core.cpp`, T4-14/T4-27) is
|
||||
> legitimate, silent overshoot is not.
|
||||
> (2) **Templates are the right tool for compile-time dedup — use them where earned** (the LE
|
||||
> byte codec `putLE`/`readLE`, T4-20), not for name-only unification (the rect family is one
|
||||
> **concrete** `ui::Rect`, NOT a template — T4-21's ruling).
|
||||
> (3) **SOLID is great but saved CPU is better** — no dispatch-stack blowouts *anywhere*, not
|
||||
> just the three named hot paths; prefer static polymorphism where types are compile-time-known
|
||||
> (T4-27's warning is the canonical case: a by-class `sampler_core` split would put virtual
|
||||
> envelope `tick()`s on the per-voice-per-sample path).
|
||||
>
|
||||
> **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
|
||||
@@ -265,9 +312,14 @@ panel adopts knob deck + curve popup, `param_slider` slider rows retired on that
|
||||
> 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 → W2–W5 god-module splits → W6 OCP finish).
|
||||
> the reorg is risk-ordered waves (W1 safe opener → W2/W2v–W5 god-module splits → W6 OCP finish).
|
||||
|
||||
## Q-W0 — pre-restructure functional + DSP quality audit (runs FIRST; gates Q-W1)
|
||||
**STATUS (2026-07-28): audit COMPLETE, triage COMPLETE, sign-off COMPLETE.** The findings report
|
||||
is committed (`docs/product/code-quality-audit.md`; track appendices in
|
||||
`docs/product/audit-notes/` — T1 DSP, T2 architecture, T3 env-constants, T4 sizing/placement; 59
|
||||
findings). Daniel approved every disposition 2026-07-28. **The Q-W1 sub-gate is satisfied once
|
||||
the six approved fix-now remediations land** (final point below; in flight on `pq-w0-fixes`).
|
||||
**Goal:** Before a single structural point moves, perform a **thorough static/functional audit** of
|
||||
the codebase and produce a **written, triaged findings report**. This is the *functional-correctness
|
||||
and algorithm-quality* complement to the grep-verified SOLID/naming audit that already grounds
|
||||
@@ -322,19 +374,33 @@ before/after listening or null check. **The gate to Q-W1 is: triage complete + D
|
||||
are a decision, not an omission.
|
||||
- [ ] **Sign-off gate.** Daniel reviews the triage and signs off on each disposition. Q-W1 does not
|
||||
begin until this is done; fold any new/reshaped downstream points the audit surfaces into
|
||||
Q-W1..Q-W6 before starting them.
|
||||
Q-W1..Q-W6 before starting them. **DONE (Daniel, 2026-07-28): all 59 dispositions approved as
|
||||
proposed; the §3 plan reshape and §4 decisions are folded into Q-W1..Q-W6 + Q-W2v below.**
|
||||
- [ ] **Fix-now remediations (approved 2026-07-28; Q-W0-scoped; in flight on branch
|
||||
`pq-w0-fixes`):** T1-01 linked-lag stereo splice alignment, T1-03 Preserve prime bound +
|
||||
immediate `freezeTail()`, T1-09 `declickR_` dead-state removal (rider on the `sampler_core`
|
||||
edits), T2-01(a) provenance wire-cursor hardening backport, T3-01 gain-ramp seconds, T3-03
|
||||
fade-ceiling seconds. Each lands with its module's CTest target green; the audible DSP fixes
|
||||
with a stated before/after listening check. **Landing these closes Q-W0 and opens Q-W1.**
|
||||
|
||||
## 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
|
||||
(parser + serializer) and **delete the five hand-rolled JSON decoders** — the four `Parser`s in
|
||||
`bank_model` / `bank_book` / `view_mode_model` / `owned_manifest` **plus `tail_control`'s fifth
|
||||
decoder** (T2-02, Q-W0's undercount fix — the wave's one-JSON-path goal is not met without it) —
|
||||
the single largest DRY+SRP violation, 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` + the `instrument`
|
||||
family) on the **30 clean pure libs + the clean shells that need no splitting + the ~20 clean
|
||||
VST pure libs** (T4 §1.5, under the settled T4-18 `core/instrument/{engine,map,ui}` +
|
||||
`shell/instrument/` shape) — pure relocation, no logic change. **Q-W0 additions (SETTLED
|
||||
2026-07-28):** the shared `readFileBytes` pure helper (T2-03); the wire-`Cursor` collapse into
|
||||
one shared codec beside `core/json` (T2-01(b)); the rect unification — one **concrete**
|
||||
`ui::Rect` + `contains()` + per-role aliases, NOT a template (T2-05 ≡ T4-21). 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 five duplicate JSON decoders are gone, replaced by
|
||||
one `core/json` consumed by all five consumers; 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
|
||||
@@ -345,81 +411,174 @@ any fix-now findings the audit assigned to Q-W1 folded in). First structural wav
|
||||
- [ ] 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).
|
||||
- [ ] Rewire `bank_model`, `bank_book`, `view_mode_model`, `owned_manifest`, **and
|
||||
`tail_control` (T2-02)** onto `core/json`; **delete the five duplicate decoders.** Round-trip
|
||||
output byte-identical (dedupe, not reformat).
|
||||
- [ ] Add the shared `readFileBytes` pure helper to the `core/` utility home; both artifacts
|
||||
link it (T2-03).
|
||||
- [ ] Collapse the length-prefixed wire-`Cursor` family into one shared wire codec beside
|
||||
`core/json`, consumed by `provenance` / `assignment_request` / `sample_usage` /
|
||||
`parseBankGeneration` (T2-01(b) — the structural half; the hardening backport lands in Q-W0).
|
||||
- [ ] Riders on files this wave already opens: `slot_map` extraction from `bank_book` (T4-05);
|
||||
`view_mode_model` planner split (T4-06 — optional if the wave wants to stay minimal);
|
||||
`view_lanes` split only if relocation touches `view.cpp` anyway (T4-09).
|
||||
- [ ] 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).
|
||||
json}/` **+ the ~20 clean VST pure libs into `core/instrument/{engine,map,ui}/` (T4-18
|
||||
SETTLED, Daniel 2026-07-28)**, and the clean shells into
|
||||
`shell/{capture,panel,view,persist,actions,instrument}/`; 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, W2–W5). Resolve `Sample`/
|
||||
god-modules re-namespace their own new TUs as they split, W2/W2v–W5). 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).
|
||||
owner — one **concrete** `ui::Rect` + `contains()` + per-role aliases, retiring the
|
||||
XYWH-vs-LTRB fork and folding in `editor_geometry`'s `Rect` (T2-05 ≡ T4-21 — explicitly NOT a
|
||||
template); retire the hand-collision "NAME NOTE" in `footer_bar.h`. Riders: `clamp01` dedup
|
||||
(T4-24); the `hitIndex` hit-test template only as an opportunistic follow-on once the rect
|
||||
unification lands (T4-22).
|
||||
- [ ] **Naming riders (Q-8 — SETTLED, Daniel 2026-07-28: both renames):** the survivor JSON
|
||||
parser is minted as **`json::Reader`/`json::Writer`**; land **`BankIndex`→`BankModel`** 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:
|
||||
## Q-W2 — split `bank_panel.cpp` (the biggest extension god-module — 3459 LOC at the Q-W0 census)
|
||||
**Goal:** Split the largest extension god-module (8+ responsibilities) along the audit's named
|
||||
seams — **eight TUs (Q-5 SETTLED with the T4-01 reshape, Daniel 2026-07-28)**:
|
||||
`panel_render` / `panel_thumbnails` / `panel_audition` / `panel_input` / `panel_bank_ops` /
|
||||
`panel_window`. Split the fat `bank_panel.h` alongside (Interface Segregation). **Preserve the
|
||||
`panel_window` **+ `panel_layout` (toolbar/footer/menu rects + row/cluster builders + region
|
||||
geometry glue) + `panel_drag` (the card-drag/hover state machine — it already has a pure mirror,
|
||||
`card_drag`)** — without the two new seams, `panel_render` (~700) and `panel_input` (~800) would
|
||||
ship over the ~600 ceiling on day one. 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
|
||||
**Verify:** CTest green at every commit. Each seam is its own TU under `shell/panel/` and
|
||||
**every TU lands under the ~600-line ceiling** (the Q-5 acceptance bar); 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.
|
||||
**Parallel-safe with Q-W2v** (different artifact, zero file overlap — §4f SETTLED).
|
||||
|
||||
- [ ] Split rendering (`draw*`/`paint*`) → `panel_render`; thumbnail compute+cache →
|
||||
`panel_thumbnails`.
|
||||
`panel_thumbnails`; toolbar/footer/menu rect + row/cluster builders + region geometry →
|
||||
**`panel_layout`** (new seam, T4-01).
|
||||
- [ ] 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`.
|
||||
lifecycle + OS drag-out/drop-target → `panel_window`; the card-drag/hover state machine →
|
||||
**`panel_drag`** (new seam, T4-01). Per-mouse-move work stays plain free-function calls
|
||||
(T4-28).
|
||||
- [ ] 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.
|
||||
- [ ] Verify in DAW: panel unchanged; CTest green; no hot-path indirection added; all eight TUs
|
||||
under the ~600 ceiling.
|
||||
|
||||
## Q-W2v — split the VST god-modules (NEW wave — Q-W0 T4 §1.5; runs parallel with Q-W2)
|
||||
**Goal:** Close the audit's structural scope gap: the VST artifact's god-modules had no owning
|
||||
wave, and `reasampler_editor.cpp` (3065 LOC) is the largest file in the repo. Split the editor
|
||||
into **eight TUs along the Sample/Browse/Zone face axis** (T4-11): `editor_session` /
|
||||
`editor_controls` / `editor_layout` (**pure-candidate hoist** into the existing pure homes —
|
||||
`editor_geometry` is the named owner; this discharges T2-06's stranded-layout-math finding) /
|
||||
`editor_paint_sample` / `editor_paint_browse_zone` / `editor_input_sample` /
|
||||
`editor_input_browse_zone` / `editor_platform`. Split `reasampler_processor.cpp` (1164 LOC) into
|
||||
**three TUs** (T4-12): `processor_state` / `processor_reload` / lifecycle+`process()` kept
|
||||
whole. Split `sample_map` into resolution core vs the **`component_state_io`** binary codec +
|
||||
matching header split (T4-13 ≡ T2-07 — the codec grows every envelope bump; the extension stops
|
||||
linking the whole voice engine to serialize one preset blob). **`sampler_core.cpp` stays whole
|
||||
(968 LOC) — a DOCUMENTED hot-path exception to the ~600 ceiling** (T4-14/T4-27: envelope
|
||||
`tick()`s run per-voice-per-sample; same-TU definition is what lets the compiler inline the
|
||||
stack, no LTO in the build; a by-class split is the exact heuristic-(3) dispatch blowout); its
|
||||
header splits into `zone_params.h` + `sampler_core.h`. The `core/wire` LE byte-codec template
|
||||
(`putLE`/`readLE`, T4-20) lands here with its biggest consumer. CONTEXT.md §Phase Q (VST split
|
||||
seams; `sampler_core` exception).
|
||||
**Verify:** CTest green at every commit. Editor and processor behave identically in DAW (visual
|
||||
+ interactive parity across all three faces; `process()` audio unchanged). Every new TU lands
|
||||
under the ~600-line ceiling **except the one documented `sampler_core.cpp` exception**.
|
||||
`process()` + its per-block helpers stay one TU; the atomic-pointer-swap reload pattern gains
|
||||
**no virtual seam** (T4-29); no dispatch-stack blowout anywhere (heuristic 3).
|
||||
**Depends on:** Q-W1 (layout + the T4-18 `instrument/` placement established). **Parallel-safe
|
||||
with Q-W2** (different artifact, zero file overlap — §4f SETTLED, Daniel 2026-07-28; the serial
|
||||
"Q-W7" alternative was set aside).
|
||||
|
||||
- [ ] Split `reasampler_editor.cpp` → the eight face-axis TUs; hoist `editor_layout`'s pure
|
||||
geometry into the existing pure homes (`editor_geometry` — discharges T2-06).
|
||||
- [ ] Split `reasampler_processor.cpp` → `processor_state` / `processor_reload` /
|
||||
lifecycle+`process()` whole; **no virtual seam on the atomic-swap pattern** (T4-29).
|
||||
- [ ] Split `sample_map` → resolution core + `component_state_io` codec (+ header split); the
|
||||
extension's preset-blob path stops linking the voice engine (T4-13 ≡ T2-07).
|
||||
- [ ] `sampler_core`: split `zone_params.h` out of the header; **TU stays whole — documented
|
||||
exception** (T4-14/T4-27), recorded in the wave brief so nobody "fixes" it later.
|
||||
- [ ] Land the `core/wire` LE byte-codec template (`putLE`/`readLE`, T4-20) with
|
||||
`component_state_io`; other consumers rewire opportunistically.
|
||||
- [ ] Rider: adopt the pure `ThumbnailKey` on the VST editor side (T2-10).
|
||||
- [ ] Verify in DAW: editor + processor unchanged; CTest green; ceiling met (one documented
|
||||
exception); no added dispatch.
|
||||
|
||||
## 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`
|
||||
**Goal:** Reduce `main.cpp` (1897 LOC at the Q-W0 census) to its actual job — API pointers +
|
||||
`ReaperPluginEntry` + dispatch — by hoisting **four** TUs (T4-02 reshape, SETTLED with Q-5,
|
||||
Daniel 2026-07-28 — the planned three left `capture_orchestrator` at ~885, over the ceiling):
|
||||
`capture_orchestrator` (`RunCapture` / `captureAndIndexOne` / `renderOffline` / single-capture +
|
||||
realtime/insert action bodies — lands ~450), **`capture_batch`** (the batch family +
|
||||
`RunRecaptureFromSource` + the two RAII selection guards — recapture is planner-driven like
|
||||
batch and shares the guard machinery), `scope_resolve`
|
||||
(`resolveRange`/`resolveRazorRange`/`collectSelectedTracks` + provenance assembly inputs), and
|
||||
`realtime_lifecycle` (the realtime-capture state machine + globals + selection guards).
|
||||
`realtime_lifecycle` (the realtime-capture state machine + globals).
|
||||
**`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 +
|
||||
tick stays a single pointer test.** **Q-W0 riders owned by this wave (all SETTLED 2026-07-28):**
|
||||
delete `ICaptureBackend` (T4-26 — one deriver, zero polymorphic call sites; `OfflineRenderBackend`
|
||||
becomes concrete; the CLAUDE.md/CONTEXT "two backends behind one interface" correction **rides
|
||||
this wave's own commit**, not earlier); the shared `stampCaptureSample` capture-epilogue dedupe
|
||||
(T2-09); the `capture_realtime_finalize` split riding the Q-9 rename (T4-08); the `makeUniqueTag`
|
||||
per-session monotonic-counter fix (T1-11); and the **WAV/RIFF consolidation (audit §4e)** — one
|
||||
pure **`wav_codec`** owner (walker + layout + build + patch), absorbing `ingest.cpp`'s pure WAV
|
||||
build helpers (T2-08 / T4-23 / T4-10). 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.
|
||||
`main.cpp` is now pointers + entry + dispatch only. The four hoisted TUs + `wav_codec` land
|
||||
under the ~600 ceiling; the WAV/RIFF layout has **one** pure owner (the dedup-by-hash and
|
||||
null-test invariants now rest on one implementation); `ICaptureBackend` is gone with no behavior
|
||||
change and the CLAUDE.md/CONTEXT description is corrected in the same commit.
|
||||
**Depends on:** Q-W1. Independent of Q-W2/Q-W2v.
|
||||
|
||||
- [ ] Hoist capture orchestration → `capture_orchestrator` (`shell/capture/`); keep
|
||||
`FxBypassGuard` a **stack RAII** object as it moves (precision-invariant-critical).
|
||||
- [ ] Hoist the batch family + `RunRecaptureFromSource` + the two RAII selection guards →
|
||||
**`capture_batch`** (fourth hoist, T4-02) so `capture_orchestrator` lands ~450.
|
||||
- [ ] 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.**
|
||||
- [ ] Hoist the realtime-capture lifecycle state machine + globals → `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).
|
||||
- [ ] **Naming rider (Q-9 — SETTLED, Daniel 2026-07-28: yes):** align the `capture_realtime`
|
||||
(shell) / `realtime_record` (pure) word-order inversion to the house shell↔core convention —
|
||||
the pure module takes the stem `capture_realtime`, the shell takes the suffix
|
||||
(`drag_out`↔`drag_out_win` is the model). Split `capture_realtime_finalize` (async lifecycle
|
||||
vs file-side finalize) in the same surgery (T4-08). No rename on a file this wave isn't
|
||||
already touching (Q-7).
|
||||
- [ ] Delete `ICaptureBackend` (T4-26): `OfflineRenderBackend` becomes concrete; correct the
|
||||
CLAUDE.md/CONTEXT "two backends behind one interface" description **in the same commit**.
|
||||
- [ ] Dedupe the capture-stamp epilogue → shared `stampCaptureSample` (T2-09 — the divergent
|
||||
bits stay in the realtime caller); fix `makeUniqueTag` with a per-session monotonic counter,
|
||||
both call sites (T1-11 — same-second batch captures currently collide silently).
|
||||
- [ ] **WAV/RIFF consolidation rider (audit §4e — SETTLED, Daniel 2026-07-28):** one pure
|
||||
`wav_codec` owner (chunk walker + layout + build + patch), absorbing `ingest.cpp`'s pure
|
||||
WAV/PCM build (T4-10 — the ingest shell drops to ~500 and the WAV build gains a test target).
|
||||
- [ ] 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
|
||||
**Goal:** Split the two unrelated command-id families in one TU (1016 LOC at the Q-W0 census —
|
||||
T4-03: the planned seams still land sub-600, no reshape) 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
|
||||
@@ -442,12 +601,16 @@ contract — a reorg must not touch a shipped command id).
|
||||
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.
|
||||
**Goal:** Split `persist.cpp` (852 LOC at the Q-W0 census — T4-04: seams unchanged; the
|
||||
pS-usage growth landed exactly where this wave isolates it; 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. **Q-W0 rider (T2-04, SETTLED
|
||||
2026-07-28):** generalize the `GetProjExtState` grow-loop retry policy into `bridge_marshal`'s
|
||||
pure decode home (or its `core/` successor) and rewire all three hand-rolled copies —
|
||||
`usage_scan`'s prune-safety-adjacent copy included. 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*
|
||||
@@ -459,6 +622,9 @@ independently landable.
|
||||
(serialization bridge + GUID minting + folder relocation).
|
||||
- [ ] Isolate prune scanning + `deleteOrphanFile` (`SHFileOperationW`) → **`prune_fs`** — the
|
||||
one file-deletion module; nothing else may delete bytes.
|
||||
- [ ] Dedupe the `GetProjExtState` grow-loop ×3 (T2-04): one retry policy generalized from
|
||||
`bridge_marshal`; rewire `usage_scan`'s prune-safety-adjacent copy with `sample_usage_tests`
|
||||
green.
|
||||
- [ ] Verify in DAW: save/load/undo-reload/relocation/prune unchanged; deletion authority is one
|
||||
module; relative-paths-only holds; CTest green.
|
||||
|
||||
@@ -466,7 +632,9 @@ independently landable.
|
||||
**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
|
||||
(`capture.h`/`persist.h`) not already resolved by their TU splits (I). (Q-W0: no reshape —
|
||||
T4-02 notes the ~385-line registration residue left in `app/main.cpp` after Q-W3 shrinks
|
||||
further under the table.) 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
|
||||
@@ -488,27 +656,42 @@ GATE: Phase S + Phase L L3 merged to dev (D2 complete, M9 abandoned) — tree qu
|
||||
("when Phase S and L3 are finished" — L1/L2/L3/L4–L7 all landed — GATE SATISFIED)
|
||||
│
|
||||
▼
|
||||
Q-W0 (pre-restructure functional + DSP quality audit — findings report + triage)
|
||||
│ ── SUB-GATE: triage complete + Daniel signed off on every disposition ──
|
||||
▼ (fix-now findings remediated/assigned; downstream Q-W1..W6 reshaped as needed)
|
||||
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]
|
||||
Q-W0 (audit + triage + report — COMPLETE; all 59 dispositions signed off 2026-07-28)
|
||||
│ ── SUB-GATE: satisfied once the six approved fix-now remediations land ──
|
||||
▼ (T1-01 T1-03 T1-09 T2-01a T3-01 T3-03 — in flight on pq-w0-fixes)
|
||||
Q-W1 (safe opener: core/json ×5 + wire codec + rect unification + relocation incl. ~20 VST
|
||||
│ pure libs under core/instrument/{engine,map,ui} + riders)
|
||||
├─► Q-W2 (split bank_panel — 8 seams) ──► Q-W4 (split actions + dedupe vs panel_bank_ops)
|
||||
├─► Q-W2v (NEW: VST god-modules — editor 8 TUs / processor 3 TUs / component_state_io;
|
||||
│ sampler_core TU whole — documented exception) [parallel with Q-W2: zero overlap]
|
||||
├─► Q-W3 (split main — 4 hoists incl. capture_batch; + wav_codec, ICaptureBackend deletion,
|
||||
│ stamp dedupe, T1-11, capture_realtime_finalize) ──► Q-W6 (OCP registration-table)
|
||||
└─► Q-W5 (split persist; + ext-state-loop dedupe) [best after Q-W4]
|
||||
```
|
||||
Q-W0 is the **entry point** — the functional/DSP audit runs FIRST and gates Q-W1 (no structural
|
||||
point begins until its triage closes and Daniel signs off). W1 is then the safe, high-leverage
|
||||
structural opener (all later waves assume the layout it establishes). The four god-module splits
|
||||
(W2–W5) 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.
|
||||
Q-W0 has run and is signed off (2026-07-28); its sub-gate closes when the six fix-now
|
||||
remediations land. W1 is the safe, high-leverage structural opener (all later waves assume the
|
||||
layout — including the T4-18 `instrument/` placement — it establishes). The god-module splits
|
||||
(W2, W2v, W3, W5) are risk-ordered and mostly parallel-safe; **Q-W2v runs parallel with Q-W2**
|
||||
(different artifact, zero file overlap — audit §4f SETTLED); 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
|
||||
- **Q-W0 closed before any structural point** — the functional/DSP audit's findings report exists,
|
||||
every finding is triaged (fix-now vs. document-and-defer, each with rationale), fix-now findings
|
||||
are remediated or assigned to the wave that opens their file, and **Daniel has signed off on every
|
||||
disposition.** Q-W1 does not begin otherwise. (CONTEXT.md §Phase Q Q-W0; naming/DSP smell
|
||||
categories §2c of `docs/product/code-organization.md`.)
|
||||
categories §2c of `docs/product/code-organization.md`.) **Status 2026-07-28: triage + sign-off
|
||||
COMPLETE (all 59 dispositions); the sub-gate closes when the six fix-now remediations land
|
||||
(`pq-w0-fixes`).**
|
||||
- **~600-line ceiling on every split wave** — every TU a split wave ships lands under ~600 LOC,
|
||||
with `sampler_core.cpp` the single documented exception (T4-14/T4-27). Seams are the method,
|
||||
the ceiling is the bar; arbitrary bisection to hit the number is rejected (Q-5 settlement,
|
||||
2026-07-28).
|
||||
- **No dispatch-stack blowouts anywhere** — heuristic (3) generalizes the hot-path guardrail
|
||||
beyond the three named paths: prefer static polymorphism where types are compile-time-known;
|
||||
templates only where earned for compile-time dedup (T4-20 yes; T4-21's rect NO-template
|
||||
ruling).
|
||||
- **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>&`;
|
||||
|
||||
Reference in New Issue
Block a user