From e87d044042e2e53a1cdcca4c8be52a733429626e Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Sun, 2 Aug 2026 13:24:57 -0400 Subject: [PATCH] docs: record Phase Gamma Wave 3's two landed tracks in COMPLETED --- docs/COMPLETED.md | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/docs/COMPLETED.md b/docs/COMPLETED.md index a3f8f6e..c34c465 100644 --- a/docs/COMPLETED.md +++ b/docs/COMPLETED.md @@ -1172,3 +1172,79 @@ judging it visually in the DAW and has not yet ruled.** **Neither track has been verified in a running DAW; both are asserted in CTest only.** The full test suite passes on the merged result — **99/99, Debug config, on one machine** — not a general cross-platform or Release-config claim. + +### Γ-W3-T1 — deck-reflow + +The knob deck's row law stops being a wrap outcome and becomes a property of the group +descriptor, by construction: two categorical rows — Sound (PITCH/RATE, FILTER, VELOCITY, +VOICE) and 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 { +Sound, Contour, Spanning }` and `deckRowFor` (`ui/deck_groups`) are 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, not merely unreached at +this width. + +FILTER's `Band|Notch` toggle moves from the knob row into its own caption's previously +unused second toggle slot, taking the group from 524 to 432 px (−92) — the reduction that +lets row 1 (980 px natural) fit inside the row block. VOICE deliberately keeps its +`Retrig|Legato` row toggle rather than following suit: moving it to the caption would make +VOICE *wider* (226 px vs. 164), since its caption row is already the binding side. + +**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 landing at the same +x) and equal, no-narrower-than-12px gutters on both rows — the three properties were never +jointly satisfiable at that width. At 1028 all hold: row 1's three gutters land at +16/16/16, row 2's two at 76/76, and both FILTER and FILTER ENV land their right edge at x += 640. Ceiling headroom against the 1280 px cap is now 82 px. + +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, so +the overwriting store had displayed one block in ~47 and dropped the rest. The processor +now folds a per-channel peak max and a limiter min-gain across the whole interval, and the +consuming `masterBusMeter()` read clears the accumulators as it drains them. + +**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, so a host-driven +activation cycle costs no disk read and no WAV decode. This discharges the `docs/TODO.md` +follow-up already recorded in full detail at the top of this file ("Decouple the +instrument reload from VST3 activation") — not restated here. + +The limiter toggle's commit is split so that cheaper cycle stays off the mouse handler: +the audible state — the parameter, the audio-thread mirror, the latency reader — commits +inline on the click; only the host's `restartComponent(kLatencyChanged)` notification is +deferred, drained by the editor's existing 500 ms sync tick. + +**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 +(the bank fold and its predicate are unit-covered; the activation that drives them is +not). + +### Γ-W3-T2 — bake-reset-amendment + +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. The finding, on reading what actually shipped: **`resetAfterBake` needed no +code change.** All four already reset by construction — none was ever added to the +survivor copy-back list, and the function's shape is "default everything, copy back only +survivors," so anything never named a survivor already resets. The track shipped +field-by-field assertions over two independently-dialled fixtures (never struct equality, +which would pass while silently letting a survivor slip through undetected) plus a +spot-check sweep confirming both fixtures actually moved every asserted field off its +default, so the coverage is mutation-verified rather than merely present. + +**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. +The claim is now scoped to gain alone, with an explicit note that "the bake prints the +gain" does not generalize to the rest of the master stage. + +**Outstanding, not closed by this track.** The limiter's exclusion from the printed master +stage is a real audible gap — a capture baked with the limiter engaged comes back +unlimited — and Daniel has ruled that a future track will change the bake to print the +limiter. Until that lands this is a recorded, known limitation, not an oversight. + +**Neither track has been verified in a running DAW; both are asserted in CTest only.**