docs: collapse Phase Gamma Wave 3 to its landed record, open bake-prints-limiter

This commit is contained in:
2026-08-02 13:34:55 -04:00
parent e87d044042
commit c7afa3a80f
+108 -197
View File
@@ -1005,213 +1005,117 @@ running DAW; both are asserted in CTest only** — 99/99, Debug config, on one m
### Γ-W3 — The reflow, and the bake correction
**Depends on Γ-W2 for:** the PITCH/RATE descriptor (W2-T1) — the reflow measures the real
three-cell group, and laying it out against a forecast of that group means re-measuring
afterward. **This is the whole reason the arrangement is late**, and it is why the canvas was
split out of it into W1-T4. **T2 depends on the same wave for a different reason:** rate and
pitch offset must exist before the bake's reset list can name them.
**Depended on Γ-W2** for the PITCH/RATE descriptor (W2-T1) — the reflow measured the real
three-cell group rather than a forecast of it, which is the whole reason the arrangement was
scheduled late. **Depended on Γ-W1** for W1-T2's published meter/GR/clip state, which MASTER's
deck draws, and W1-T4's floor, budget constants and row predicate, which T1 consumed rather
than re-derived. **T2 depended on Phase Ξ**`Ξ-W2-T1 (resample-bake-chain)` landing on `dev`
first, the phase's only external gate — see `docs/COMPLETED.md` for the full narrative of each
landed track.
**Depends on Γ-W1 for:** W1-T2's published meter/GR/clip state, which MASTER's deck draws
(drawing against a stub would mean building the meter twice), and W1-T4's floor, budget
constants and row predicate, which T1 **consumes rather than re-derives**. **T2 depends on
W1-T2 for the limiter enable flag**, the third of the three values it must add.
**Two tracks have landed** — Γ-W3-T1 (`deck-reflow`) and Γ-W3-T2 (`bake-reset-amendment`) — see
`docs/COMPLETED.md` for the full narrative of each. **A third track is now open and has not
run:** Γ-W3-T3 (`bake-prints-limiter`), added below on Daniel's ruling of 2026-08-02.
**Depends on Phase Ξ for T2 — the phase's only EXTERNAL gate.** `Ξ-W2-T1
(resample-bake-chain)` must have landed on `dev` before T2 dispatches. T2 amends what that
track shipped; it cannot amend a branch.
**Two tracks, disjoint by surface — but T2's disjointness is CONDITIONAL and must be
confirmed, not assumed.** T1 owns `ui/knob_deck`, `ui/deck_groups` (row-predicate
consumption, FILTER's caption move, MASTER's inventory) and `shell/instrument/editor_paint_deck`.
T2 owns the bake's reset step wherever Ξ-W2-T1 put it. **T2's first act is to read what
actually shipped and confirm its reset surface touches none of T1's three modules.** If the
shipped reset enumerates controls through `deck_groups` or `deck_values`, the two are not
disjoint and **T2 serializes behind T1 inside the wave** — a named contingency, taken openly,
not discovered at merge. That risk is real precisely because this plan cannot predict the
shipped shape; predicting it is what put the phase in this position.
**Why T1 is one track.** The row law, the group inventory and the double-height deck are one
geometry decision spread over `knob_deck`, `deck_groups` and the deck painter. Splitting it
would put two tracks in the same pure modules. **The window floor is no longer part of it**
W1-T4 set it two waves ago, and this track must not move it.
**Neither track takes a payload rung.** T1 is layout only; T2 changes a reset list, not a
format.
**None of the three tracks takes a payload rung.** T1 was layout only; T2 changed a reset list,
not a format; T3 changes what the render's audio contains, not what is stored.
#### Γ-W3-T1 — `deck-reflow`
**Goal.** Two categorical rows plus a double-height MASTER bus deck, inside a 1280 × 720
ceiling, returning 112 px to the waveform.
**Landed** — see `docs/COMPLETED.md` for the full narrative. The knob deck's row law stops
being a wrap outcome and becomes a property of the group descriptor: two categorical rows
(Sound — PITCH/RATE, FILTER, VELOCITY, VOICE; Contour — PITCH ENV, FILTER ENV, AMP ENVELOPE)
plus a double-height, right-anchored MASTER bus deck outside both, carrying the limiter enable
toggle, one reserved cell, the output meter column and a passive gain-reduction lamp.
`DeckRow`/`deckRowFor` (`ui/deck_groups`) is an exhaustive switch over every `DeckGroupId`, so a
group added later without a row assignment is a compile error; the greedy whole-group wrap this
replaces is gone entirely. FILTER's `Band|Notch` moves into its caption's previously-unused
second toggle slot, taking the group from 524 to 432 px; VOICE deliberately keeps its
`Retrig|Legato` row toggle, since moving it to the caption would make VOICE wider, not narrower.
**Spec:** `docs/product/instrument-control-surface.md` §1 (the whole section, incl. the §1.2
measured table **and §1.6, the headroom ledger**) and §3.23.3 (what MASTER draws). **§7 lists
the invariants this track invalidates or widens — read it before touching `knob_deck.h`.**
**The row block widened 1020 → 1028 px and the editor floor moved 1190 → 1198 px (Daniel's
ruling, 2026-08-02).** The originally specified 1020 could not simultaneously deliver the
filter tie-line (both rows' FILTER/FILTER ENV right edges at one x) and equal,
no-narrower-than-12px gutters on both rows; at 1028 all three hold, with 82 px of headroom left
against the 1280 px ceiling.
**Surface boundary — owns:** `core/instrument/ui/knob_deck` (the row law, the double-height
group, the justification — **consuming** W1-T4's budget constants, not restating them),
`core/instrument/ui/deck_groups` (consumption of W1-T4's row predicate, FILTER's `Band|Notch`
caption move, MASTER's inventory), and `shell/instrument/editor_paint_deck` (the MASTER
meter/limiter/bubble draw). **Does not own** `kEditorMinWidth` or any budget constant — those
are W1-T4's and are **read**, never moved — nor any parameter, the limiter DSP, or the
waveform band.
The MASTER meter's per-block state moved from a plain overwriting store to an accumulated one —
at 48 kHz/512-frame blocks roughly 47 blocks elapse between two 500 ms UI ticks, and the
overwriting store had been displaying one block in ~47 and dropping the rest. The processor now
folds a per-channel peak max and a limiter min-gain across the whole interval, drained by
`masterBusMeter()`. **The instrument reload was decoupled from VST3 activation as part of this
track** — `setActive(false)` now parks the decoded `SampleData` and destroys only the voice
state, `setActive(true)` rebuilds the voices around the parked sample — discharging the
`docs/TODO.md` follow-up already recorded in full there. The limiter toggle's commit is split so
the audible state commits inline on the click and only the host's
`restartComponent(kLatencyChanged)` notification is deferred to the editor's existing 500 ms
sync tick.
**Behavior.**
- **Row 1 (sound), one row, non-negotiable:** PITCH/RATE 192 · FILTER 432 · VELOCITY 192 ·
VOICE 164 = **980** natural.
- **Row 2 (contour):** PITCH ENV 252 · FILTER ENV 312 · AMP ENVELOPE 312 = **876** natural.
- **MASTER is double-height (216 px) and right-anchored**, outside both rows, 142 px wide.
- **FILTER's `Band|Notch` moves from its row-toggle position to the caption corner**, taking
the group 524 → **432** (92 px). It occupies FILTER's currently-unused `captionToggle2`
slot — **no new geometry is required**.
- **VOICE keeps its `Retrig|Legato` row toggle.** Moving it to the caption makes VOICE
*wider* (226, not narrower), because its caption row is the binding side. Verified; do not
"fix" it.
- **Justification law, applied to BOTH rows:** space-between within the row block; slack
divided equally among the row's (n1) gutters, integer residue to the leftmost;
**no gutter narrower than `kDeckGroupGap` (12)**. **Decks are never stretched.** MASTER is
not part of either row's justification.
- **Row block = 1028 px at the floor** (widened from the originally specified 1020 — Daniel,
2026-08-02), giving row 1 gutters 16/16/16 and row 2 gutters 76/76, at which width
**FILTER's right edge and FILTER ENV's right edge both land on x = 640**. The tie-line and
both rows' equal gutters hold at 1028 because each row's slack divides by its gutter count
with no residue. **This is why the floor is 1198 and not 1190.** The originally specified
1020 delivered NEITHER the tie-line (638 vs 636) nor the claimed exactly-`kDeckGroupGap`
smallest gutter (13); the three properties were never simultaneously satisfiable, and 12 is
a floor rather than a target — spec §1.3 records all three deviations. Above the floor the
tie-line drifts and that is accepted (spec §1.3).
- **The bands are already 216 / 358 and the floor was already 1190 × 680** — Γ-W1-T4 landed all
four in wave 1, and the greedy wrap happened to reach two rows at that width. **This track
changes only the row block and the floor (see above); the rest it makes true by construction rather than by coincidence.**
Row 1's natural width fits the block **only after this track's `Band|Notch` move**: 1030
today, +42 from W2-T1's PITCH/RATE, 92 here, = **980**. That is this track's fit assertion
and W1-T4 deliberately left it open.
- **The 82 px of remaining headroom is the budget for the life of this layout**, and one deck
cell is 60 px. **This is why MASTER's reserved slot is ONE cell** (Γ-F5, ruled): two would
spend 60 of the 82 up front on a control nobody has named, leaving 22 — which would freeze
row 1 forever, since any later row-1 addition needs 60. Widening MASTER later costs the same
60 it would cost now, and by then the trade is against a real control instead of a guess.
**State this ledger where a future reader will hit it** — spec §1.6 is its home, and a
reader proposing a new knob needs to see it before they propose.
- **MASTER's interior** (spec §1.4, exact to the pixel): caption row with the limiter toggle
and a **round** 12 px `warn` GR bubble in the far corner (non-interactive — the same slot the
envelope decks' radio uses; round so it reads as a lamp, not a control); **gain knob in the
upper-left cell at box-relative y = 26** and a **reserved empty slot at y = 138** — i.e. the
two cells land on row 1's and row 2's knob baselines exactly, which is what stitches the
spanning deck to both rows; **meter column 62 px wide × 186 px tall** on the right.
- **Three rules not to generalise wrongly:** MASTER's left column uses **fixed cell slots at
the two baselines, NOT the horizontal run-division law** (that law would stretch one knob
over 186 px); the reserved slot **draws nothing** (blank reads as breathing room, a dashed
placeholder reads as unfinished); the meter is **one rect spanning both baselines**, not two
per-row meters.
- **The meter draws W1-T2's published state**, with the ballistics run on the UI timer.
**Bar count follows the same `LaneSplit` decision `waveformSurface` already folds** (channel
mode ∧ source channel count) — one wide bar when the waveform draws one lane, two skinnier
bars when it draws two. Not a second rule: a mono source in stereo mode is dual-mono, and
two identical bars would be a lie.
- **Meter appearance:** bar in `accent/primary`; peak-hold tick 2 px in `text/primary`; clip
cap in `warn`, latched, click-to-clear; scale linear in dB over 60…+6 with ticks every
6 dB and numerals at 0/12/24/36/48/60, the 0 dB tick heavier. **No green/yellow/red
segmentation** — `warn` stays reserved for clip states.
**Acceptance criteria.**
- At the floor width the deck lays out in **exactly two rows plus the spanning MASTER**,
**by construction** — asserted against the group inventory, not observed as a wrap outcome.
- Every group's width matches the §1.2 table exactly, **in both Gate and Trigger** (row 2's
natural width is mode-stable at 876 because the reserve slots hold FILTER ENV and AMP at
312 in both modes — assert it).
- Row 1 and row 2 are **flush left and flush right**; at the floor width the filter tie-line
is exact (both edges at x = 640) and BOTH rows' gutters are equal.
- **Row 1's natural width is 980 and fits the 1028 block** — the fit Γ-W1-T4 could not yet
assert, closed here by the `Band|Notch` move.
- **`kEditorMinWidth` is 1198 and the floor is still ≤ 1280 × 720** — moved 1190 → 1198 by this
track, verified against Γ-W1-T4's derived test rather than a second copy of it.
- The waveform band is **358 px at the floor**, and the deck band is 216 — **unchanged from the
interim, now reached by construction**: `deckRowCount` at and above the floor is 2 because the
row predicate says so, not because a wrap landed there. Assert against the group inventory.
- MASTER's gain knob shares a knob baseline with FILTER's knobs; its reserved slot shares one
with AMP ENVELOPE's.
- The meter reads correctly in mono and stereo, the peak-hold tick holds 1.5 s, the clip cap
latches and clears, and the GR bubble lights only while the limiter reduces gain.
- **With the limiter engaged the clip cap never latches** on material the limiter is catching;
if it does, that is a defect report against W1-T2, not a user error.
- `knob_deck`'s and `sample_bands`' tests are updated to the new law, and the invalidated
notes in `knob_deck.h` (the fourteen-pixel headroom figure; the cells-and-floor pairing) are
**re-derived, not deleted** — spec §7.1, §7.4.
**Open questions.**
- **[propose at review]** Whether the greedy whole-group wrap survives at all as a sub-floor
degrade, or is replaced outright by explicit row assignment. What is **not** optional: at
and above the floor width the layout is the specified arrangement, reached by construction.
`DeckLayout::rowCount`/`::height` change meaning either way (spec §7.3).
- **No [Daniel] questions.** Forks Γ-F5 (**one cell**) and Γ-F1 (**680 stays**) are both
ruled; they are stated in Behavior above, not carried here as options.
- **[verify]** `deck_groups.cpp`'s `kEnvModeSegW = 23` ceiling rises to **47** once PITCH ENV
is on row 2 (AMP binds at 55). No change is required; the comment stating the old ceiling
stops being true and must be corrected (spec §7.2).
**Not verified in a running DAW — CTest-asserted only:** the meter at its 500 ms UI cadence, the
GR lamp under real limiter action, the limiter toggle's latency renegotiation, the clip cap's
click-to-clear, and the recapture-while-editor-closed path.
#### Γ-W3-T2 — `bake-reset-amendment`
**Goal.** Complete the resample bake's reset list against the control surface that now
exists — the correction Phase Γ owes Phase Ξ because Ξ-W2-T1 shipped ahead of the sequencing
this plan asserted.
**Landed** — see `docs/COMPLETED.md` for the full narrative. The correction Phase Γ owed Phase
Ξ: Ξ-W2-T1's bake shipped ahead of the sequencing this plan asserted, so its reset list predated
rate, pitch offset, the limiter enable, and the loop enable. **`resetAfterBake` needed no code
change** — all four already reset by construction, since the function defaults everything and
copies back only survivors, and none of the four was ever named a survivor. The track shipped
field-by-field assertions over two independently-dialled fixtures, never struct equality, plus a
mutation-verified spot-check sweep confirming both fixtures actually moved every asserted field
off its default.
**Consolidates:** nothing from the seventeen. It is a **correction obligation**, not a
feature (see "Flagged for awareness" item 2).
**One invariant correction:** `bake/CLAUDE.md` had claimed the whole signal chain prints, master
gain included. It doesn't — the render's gain multiply is the only master-stage value it
prints; the limiter runs in the processor's block, off the bake path entirely.
**Spec:** `docs/product/instrument-control-surface.md` §3.4, and Ξ-W2-T1's own "Reset scope"
block above — **which is the ratified rule this track applies, not a rule it may reinterpret.**
**Outstanding, not closed by this track.** A capture baked with the limiter engaged comes back
unlimited — a real audible gap, and Daniel has ruled that a future track will change the bake to
print the limiter. **That track is now Γ-W3-T3, below.**
**Surface boundary — owns:** the bake's parameter-reset step, wherever Ξ-W2-T1 landed it, and
its tests. **Does not own** the bake chain, the crossing architecture, the replace-vs-add
decision, the capture path, any deck module, any painter, any parameter, or any
`ComponentState` version. **It changes what a shipped list contains — nothing else.**
**Neither track has been verified in a running DAW; both are asserted in CTest only.**
**Behavior.**
- **Three values join the reset list**, all classified against Ξ-W2's own ratified rule
("reset what the bake baked in"), all **reset**, none of them a new Daniel decision:
**rate**, **pitch offset**, and **limiter enabled**. The limiter's reasoning is worth
carrying rather than re-deriving: master gain is already on the reset list, so the bake
includes the master stage, so the limiter's effect is in the audio.
- **Verified against what shipped, not against what was predicted.** This plan named three
values before either the bake or the controls existed. **Read Ξ-W2-T1's landed reset list
first** and reconcile: if it already anticipated any of the three, say so and drop it; if
it classified something differently from `docs/product/instrument-control-surface.md` §3.4,
**the landed code is the fact and this plan is the prediction** — escalate the difference,
do not silently overwrite either.
- **Re-run Ξ-W2-T1's own "genuinely new parameter" check over everything Phase Γ added**, not
just the three named. Γ also ships the loop enable (W2-T2) and raises the stage-time
ceiling (W1-T1). Classify each **against the rule**: the loop enable is a loop fact whose
effect is in the rendered audio (**reset**, with the loop points it travels with); the
ceiling is not a parameter at all. State each disposition; silence is not one.
- **Root note still survives.** The bake's most load-bearing exception is untouched by this
track — capturing at root is what makes root survivable, and resetting it would detune
every subsequent iteration.
#### Γ-W3-T3 — `bake-prints-limiter`
**Acceptance criteria.**
- After a bake, **rate reads 100 %, pitch offset 0 st, and the limiter reads bypassed** — and
the root note, key-tracking and the VOICE group are still untouched.
- **The bake stays audible and faithful with the new controls dialled in**: dial rate, pitch
offset and the limiter, bake, and the neutral instrument playing the programmed note sounds
as the dialled one did — the criterion Ξ-W2-T1 already carries, now actually exercised over
Γ's controls.
- **A reconciliation note in the track's review** stating, per value, whether the landed code
already covered it, and recording any difference between what shipped and what §3.4
predicted.
- **No format change, no new field, no version bump, no change to the crossing architecture
or the replace-vs-add decision.** A regression baseline proves the bake's audio is
otherwise unchanged.
**Not started. Opened by Daniel's ruling, 2026-08-02.**
**Open questions.**
- **No [Daniel] questions.** The rule is ratified and §3.4's classification is derived from
it.
- **[verify, FIRST]** the disjointness contingency in the wave header: read the shipped reset
step and confirm it touches none of Γ-W3-T1's modules. If it does, serialize behind T1 and
say so.
- **Explicitly NOT this track's:** the two consequences automation adds to the bake — the
reset having to notify the host, and a host lane re-imposing its curve onto baked audio.
Both are **Γ-W4-T1's**, because that track creates them. Doing this correction once, before
automation, and letting Γ-W4-T1 add its own obligation on top is deliberate: the
alternative is an amendment to an amendment.
**Goal.** Print the limiter through the bake's master stage, so a capture baked with the
limiter engaged returns limited audio rather than unlimited audio.
**Why this exists.** Γ-W3-T2's own finding disproved the premise
`docs/product/instrument-control-surface.md` §3.4's reset classification rested on:
`renderBake` (`core/instrument/bake/bake_render.cpp`) prints only a flat master-gain multiply,
and the limiter (`core/instrument/engine/limiter`) runs in the processor's `process()` block,
off the bake path entirely. Until this track lands, this is a recorded, known limitation — see
`docs/COMPLETED.md`'s Γ-W3-T2 entry — not an oversight.
**Consolidates:** nothing from the seventeen. A correction, on the same footing as Γ-W3-T2 (see
"Work in this plan that is not one of the seventeen").
**Spec:** none yet written. This ruling postdates §3.4 and has no product-doc section of its
own; §3.4 is superseded on this one point, which a future scoping pass of this track should
correct there as well as here.
**Surface boundary — likely, not yet confirmed against a full scoping pass:** owns
`core/instrument/bake/bake_render` (the gain-multiply step, extended to also run the signal
through a limiter), consuming `core/instrument/engine/limiter` — not owned, not modified. Does
not own the processor's live block, the limiter DSP itself, the parameter surface, or
`bake_reset` (the limiter-enable reset classification is already Γ-W3-T2's, landed).
**Open questions — none of this is ruled yet, only the goal is:**
- **[propose at review]** Whether the bake instantiates its own `Limiter` — mirroring
`renderBake`'s existing bake-only `VoiceEngine`, off the audio thread, never linked into
`reaper_reasampler` — or reaches the limiter's settled behavior some other way. The
bake-only-engine precedent (`bake/CLAUDE.md`) argues for the former.
- **[propose at review]** Whether the limiter's lookahead needs any accommodation in an
offline, non-realtime render — the processor's `getLatencySamples()` PDC report exists for
the live block, and a bake is not on that clock, so this may be a non-issue; it has not been
checked.
- **[propose at review]** Whether this track also corrects `bake/CLAUDE.md`'s invariant text
("the limiter is not [printed]") alongside the code, once scoped in full.
- **No [Daniel] question on the goal itself** — the ruling above is the goal; what is open is
the mechanism, not whether to do it.
---
@@ -3520,9 +3424,9 @@ proof it exists to give.
from `TODO-1.0.md`, and not a track this plan originally scoped. The second such track
in this plan today; if others appear, they belong on this list rather than in the
table.
- **All of Phase Γ** (`pg-*`). **Twelve tracks across four waves** (W1 seven, W2 two, W3 two,
W4 one), from a direct interview with Daniel (2026-08-01) and his four later rulings the
same day, not from `TODO-1.0.md`. Listed
- **All of Phase Γ** (`pg-*`). **Thirteen tracks across four waves** (W1 seven, W2 two, W3
three, W4 one), from a direct interview with Daniel (2026-08-01) and his four later rulings
the same day, not from `TODO-1.0.md`. Listed
here as a block rather than per track, because the whole phase is outside the source doc;
the product reasoning lives in `docs/product/instrument-control-surface.md` and the
parameter system's in `docs/product/parameter-automation.md` §§610. **Two `docs/TODO.md`
@@ -3542,6 +3446,11 @@ proof it exists to give.
plan's sequencing claim. If more corrections of this shape appear, they belong here rather
than in the table — the table is a completeness proof over `TODO-1.0.md`, and a correction
has no source row to point at.
- **Γ-W3-T3 `bake-prints-limiter` is also a CORRECTION, not a feature**, and for the same
reason as T2: it exists only because T2's own finding disproved the premise §3.4's reset
classification rested on. Unlike the twelve above, it does not come from the 2026-08-01
interview or that day's four rulings — it is a separate ruling, one day later (2026-08-02),
opened after T2 landed and found the gap.
- **All of Phase Ε** (`pe-*`). **Six tracks across three waves**, from a direct request
(Daniel, 2026-08-02), not from `TODO-1.0.md`. Listed here as a block, like Γ and Ψ; the
product reasoning lives in `docs/product/bank-package.md`. It **supersedes nothing** —
@@ -3652,9 +3561,11 @@ Phase Γ — The instrument's control surface (none of the seventeen; ends
W2 New controls, and the overlay's marks — landed [2 tracks]
T1 pitch-rate-deck ............ Rate + Pitch, Varisp/Presrv compounding [rung 2]
T2 loop-crossfade-ux .......... four-mark grammar; fade painted where it is heard
W3 The reflow, and the bake correction [2 tracks]
W3 The reflow, and the bake correction [3 tracks; T1/T2 landed, T3 open]
T1 deck-reflow ................ two rows + double-height MASTER, by construction
T2 bake-reset-amendment ....... the Xi correction Gamma owns [needs Xi-W2-T1 on dev]
T2 bake-reset-amendment ....... the Xi correction Gamma owns
T3 bake-prints-limiter ........ NOT STARTED — bake to print the limiter
through the master stage [Daniel, 2026-08-02]
W4 VST3 parameters [1 track]
T1 vst3-parameter-set ......... 44 derived params, frozen id table [Ruling 1]
[rung 3 RESERVED, spent only if verify says so]
@@ -3669,8 +3580,8 @@ Phase Γ — The instrument's control surface (none of the seventeen; ends
Γ-W4-T1's storage verification forces it.
Shared files, named: engine/CMakeLists.txt (W1-T2 | W1-T5), ui/CMakeLists.txt
(W1-T1 | W1-T3), editor_session.cpp (W2-T1 | W2-T2) — all textual adjacency, not
semantic contention. W3-T2's disjointness from W3-T1 is CONDITIONAL: confirm it against
what Xi-W2-T1 shipped, and serialize behind T1 if it does not hold.
semantic contention. W3-T2's disjointness from W3-T1, once a CONDITIONAL risk to confirm
against what Xi-W2-T1 shipped, is resolved: both tracks landed — see docs/COMPLETED.md.
Phase Psi — The extension trust pass (none of the seventeen; a direct list of seven)
W1 Exact bounds, disciplined switches, reachable actions, resolved drops [4 tracks]