docs: cascade rulings R2 (band-limiting in scope) and R3 (the Omega snap fence yields)

R2 closes fork F6 IN: complex waveforms must be supported purely, so band-limited
playback enters the near-term scope alongside the loop-grid recalibration. Re-derives
the three post-R1 reliefs it spends — it reaches process(), reopens a parameter door,
and recontends the deck headroom — and mints F9-F12 for the shape it deliberately
does not rule. R3 makes the snap-radius work Phase Omega's own W2-T5 and supersedes
the zero-crossing-snap criterion at its three sites, recording that W1-T5 met it.
This commit is contained in:
2026-08-03 15:37:16 -04:00
parent d5280687f3
commit 5042e2709b
2 changed files with 568 additions and 168 deletions
+103 -35
View File
@@ -220,40 +220,62 @@ in Γ-W4-T1 and changes no wave boundary.
*"supporting smoothness at this micro scale would turn this into a wavetable synth, which
is desirable."* The product framing is `docs/product/single-cycle-and-wavetable.md`.
**It framed EIGHT [Daniel]-class forks. ONE is now ruled and SEVEN remain open**, so it
still gets a pointer here rather than a phase section: `PLAN.md` is the active on-deck
specification list, and a phase whose specification rests on seven unruled forks is
neither briefable nor consistent with the standing claim above that **Λ is the only phase
in this plan with unanswered [Daniel]-class questions**. That claim is unaffected by this
item — there is no phase here to except. When the remaining forks are ruled, the doc
becomes a phase's backing product doc in the ordinary way (the Γ / Ε / Ρ / Λ pattern, not
the doc-less Ψ / Ω one).
**It framed EIGHT [Daniel]-class forks; THREE rulings have since landed against it, and it
now carries TWELVE forks — ONE settled and ELEVEN open.** So it still gets a pointer here
rather than a phase section: `PLAN.md` is the active on-deck specification list, and a
phase whose specification rests on eleven unruled forks is neither briefable nor consistent
with the standing claim above that **Λ is the only phase in this plan with unanswered
[Daniel]-class questions**. That claim is unaffected by this item — there is no phase here
to except. When the remaining forks are ruled, the doc becomes a phase's backing product
doc in the ordinary way (the Γ / Ε / Ρ / Λ pattern, not the doc-less Ψ / Ω one).
**The ruling — Daniel, 2026-08-03:** *"I don't want actual frame-indexed wave tables yet,
that is out of scope. just static captures right now."* It closes fork F5 (the
wavetable-position knob does not ship now), confirms the multi-frame wavetable kind out by
ruling rather than only by the doc's argument, and **narrows the near-term scope to the
doc's W0 — loop-grid recalibration for micro material, which is defect-class work with no
new parameter, no format change and no `process()` involvement.** *"Yet"* is recorded as a
scope ruling, not a rejection; the doc keeps the deferred case whole and names the single
trigger that would revive it. **The ruling does NOT settle band-limiting** — the question
that decides it is still outstanding with Daniel, and nothing in the doc pre-rules it. Per
the doc's §7.1.
**R1 — Daniel, 2026-08-03:** *"I don't want actual frame-indexed wave tables yet, that is
out of scope. just static captures right now."* It closes fork F5 (the wavetable-position
knob does not ship now) and confirms the multi-frame wavetable kind out by ruling rather
than only by the doc's argument. *"Yet"* is recorded as a scope ruling, not a rejection;
the doc keeps the deferred case whole and names the single trigger that would revive it.
**Two things in it are decision-grade for work already in this plan, which is why it is
flagged rather than merely filed:**
**R2 — Daniel, 2026-08-03**, answering the doc's own outstanding question (*is the material
ever anything but sine?*): *"yes, complex waveforms must be supported purely."* **It closes
fork F6 IN — band-limited playback is in scope** — and reads *purely* as *faithfully,
without artifacts the capture does not contain*. Consequences that matter to this plan:
- **The near-term scope is now the doc's W0 AND W2, in that order** — W0 (loop-grid
recalibration; defect-class, no new parameter, no format change, no `process()`
involvement) and then W2, which consumes W0's declared period.
- **W2 is the first item in that doc that reaches `process()`.** The per-sample read binds
a level-indexed buffer rather than a fixed member: no branch, no dispatch, no virtual —
so this plan's `process()` guardrail is satisfied in kind — but `voice.h` is header-inline
with no LTO configured, so it is a **measurement obligation, not an argument**. A W2 track
with no before/after number has an opinion, not an acceptance criterion.
- **It re-opens a one-way door and re-contends the deck headroom.** W2 wants a declared
mode: a permanent VST3 parameter id on Γ-W4-T1's frozen table, and a control against
`instrument-control-surface.md` §1.6's 82 px. The doc's §6.8/§6.9 cost both, including
the caption-slack slot that may make the control free.
- **R2 does NOT rule the shape.** Four new forks (F9 table shape, F10 level selection, F11
interpolator, F12 Preserve coverage) are minted open with a recommended package.
**R3 — Daniel, 2026-08-03**, on the Phase Ω conflict this item used to record: *"call it
part of omega, that was a fence you built not me."* **The conflict is RESOLVED, and the
resolution is recorded in Phase Ω itself** — the phase's zero-crossing-snap criterion and
Ω-W1-T5's two criterion sites are amended in place. The snap-radius work is **Ω-W2-T5**
(branch `omega-w2-t5-snap-radius`, `b7b7e88`); the fence it appeared to break was a scope
constraint on Ω's own tracks, not a product ruling, and Ω-W1-T5's work is not invalidated
by it. R3 also creates a documentation gap — **Ctrl-defeats-snap on a marker drag** is a new
user-visible gesture that `docs/product/instrument-control-surface.md` §4.1 currently
contradicts — flagged for doc-keeper at Ω-W2-T5's note in this file.
**What remains decision-grade for work already in this plan:**
- **The immediate defect it came from is NOT its subject.** `nearestZeroCrossing`
(`src/core/instrument/ui/waveform_view.cpp:181-210`) fans out across the whole buffer
with no radius bound; a snap-radius track is in flight against it. **But Phase Ω's own
acceptance criteria name the zero-crossing snap as a thing that "must come out the other
side identical", and Ω-W1-T5's outline entry repeats it.** A snap-radius change alters
exactly that behaviour. **Whoever owns Ω's criteria must reconcile the two** — this item
records the conflict, it does not resolve it.
- **Sequencing, if any of it is ever dispatched.** Every candidate touches
`waveform_view.{h,cpp}`, `editor_input_waveform.cpp` and `editor_controls.cpp`'s
`waveMarksFor` / `grabbableMarks` — Ω-W1-T5's exact surface. Nothing from that doc
should dispatch until Ω-W1-T5 and the snap-radius track have both landed.
`waveMarksFor` / `grabbableMarks` — Ω-W1-T5's exact surface. **Nothing from that doc
dispatches until Ω-W1-T5 has landed**, and it should build on Ω-W2-T5's radius rule
rather than replacing it.
- **F8 is re-opened by R2, not narrowed.** W0 and W2 now differ in kind — one is
migration-clean defect work, the other a measured DSP build with a frozen parameter and
five unruled forks — so "two phases rather than one" became a serious option again. **If
F9F12 are unruled when W0 is ready, do not let W0 wait on them.**
## Phase-wide acceptance criteria
@@ -353,6 +375,14 @@ statements become untrue the moment this phase lands:
**Do not re-litigate these.** There is no product doc to index them in; this table is their one
home, and a brief for any Ω track is written against it.
**One later ruling binds this phase from OUTSIDE that table, and is named here so the table is
not read as complete: R3 (Daniel, 2026-08-03)** — *"call it part of omega, that was a fence you
built not me"* — which makes the snap-radius work **Ω-W2-T5** and supersedes this phase's
zero-crossing-snap criterion. It is recorded in `docs/product/single-cycle-and-wavetable.md`
§7.1 rather than here, because it arose against that doc; it is deliberately **not** renamed
into the `Ω-F` series, since one ruling with two identifiers is worse than one ruling with a
cross-reference.
| Ruling | What it settled | Bound into |
|---|---|---|
| **Ω-F1** | **Widen the marker-alignment work to the correct scope.** The defect is the overlay↔waveform coordinate mapping as a whole, not the START mark — a class defect with four independent causes, inherited by every overlay | Ω-W1-T5 |
@@ -371,9 +401,16 @@ hope.
These bind every track in this phase, in addition to the plan-wide set above.
- **No behaviour changes outside the named defect.** Every track carries that criterion
explicitly. The Gate deck face, the zero-crossing snap, the ballistics constants, the 500 ms
bank/bake poll body and the audio thread are each named below as things that must come out
the other side identical.
explicitly. The Gate deck face, the ballistics constants, the 500 ms bank/bake poll body and
the audio thread are each named below as things that must come out the other side identical.
**The zero-crossing snap was named here too, and that clause is SUPERSEDED — ruling R3,
Daniel, 2026-08-03:** *"call it part of omega, that was a fence you built not me."* It was a
**scope fence** — written to stop Ω's own tracks perturbing the snap while they fixed the
coordinate mapping — never a finding that `nearestZeroCrossing`'s unbounded fan-out was
correct. Daniel's DAW evidence (a one-cycle 60 Hz capture whose loop start teleports to the
half-cycle) supersedes it. **The superseding track is Ω-W2-T5 `snap-radius`**, which bounds
the snap to `kMarkerGrabWidth` and adds Ctrl-defeats-snap on a marker drag. The fence still
binds every OTHER Ω track: no track but Ω-W2-T5 may change the snap.
- **Nothing is added to any hot path.** No track here touches `process()`, the `peaks` envelope
compute, audition, or the realtime-capture tick. Ω-W1-T3's work is entirely UI-thread paint
cost; Ω-W1-T1's is entirely main-thread REAPER API cost.
@@ -808,11 +845,20 @@ stays 1:1 with the sample's time axis" **is currently false by 2 px.**
- **De-inset the waveform** — `[propose]`, **requires justification before it is taken**: it
touches the shared `draw_kit`, which also serves the docked bank panel and the browse-card
thumbnails, so its blast radius is three surfaces rather than one.
- **The zero-crossing snap must be preserved EXACTLY.** `nearestZeroCrossing`
(`waveform_view.cpp:170-199`) fans out symmetrically, probing `td` before `t+d` so an
- **The zero-crossing snap must be preserved EXACTLY *by this track*.** `nearestZeroCrossing`
(`waveform_view.cpp:181-210`) fans out symmetrically, probing `td` before `t+d` so an
equidistant tie resolves to the lower frame; it runs on the mono cache at drag time and is
exempt for the crossfade handle. **Behaviour-identical after the change** is an acceptance
criterion, not an assumption.
> **Amended, ruling R3 (Daniel, 2026-08-03) — read this as scope, not as a verdict on the
> snap.** This clause bound T5 to change the coordinate mapping WITHOUT perturbing the snap,
> and **T5 met it: the fan-out it received is the fan-out it returned, and its tie-rule
> fixtures pass unmodified.** The snap's behaviour was subsequently changed on purpose by a
> DIFFERENT track — **Ω-W2-T5 `snap-radius`**, which bounds the fan-out to
> `kMarkerGrabWidth` — so a later reader finding a bounded snap in the tree is looking at
> Ω-W2-T5's work, not at a T5 violation. **Bounding which frames are REACHABLE does not
> change how a reachable tie RESOLVES**, which is why T5's fixtures survive it untouched.
- **One question the track must ANSWER, not dodge:** what "sample-accurate" means when
`frames < columns` and one frame spans many pixel columns. A marker must land somewhere
inside its own frame's column span; which end, and whether the inverse `xToFrame`
@@ -828,6 +874,9 @@ stays 1:1 with the sample's time axis" **is currently false by 2 px.**
at the last frame, and at an interior frame, asserted in the pure fixtures.
- `frames < columns` and `frames > columns` are both covered.
- The zero-crossing snap returns the identical frame for the identical input, ties included.
**(Scope criterion on THIS track, discharged. Superseded phase-wide by R3 — the snap's
radius bound is Ω-W2-T5's deliberate change, and this track's tie-rule fixtures pass
unmodified under it.)**
- Trigger shows no loop marks and no loop caption; Gate-with-loop-off shows grey marks that
still drag, and dragging one still turns the enable on.
- `docs/product/instrument-control-surface.md` §6.3(d) and §6.4 agree with the shipped
@@ -848,8 +897,27 @@ fix** (the Λ-W3 precedent).
| **T1** `view-topology-cache` | `src/core/view/` — tree / `parentOf` / handle-map memoization |
| **T2** `content-detect-throttle` | `src/shell/panel/panel_input.cpp` — the 30 Hz detector's cadence |
| **T3** `omega-verification-sweep` | `docs/VERIFICATION.md` — the DAW record for all of Ω-W1 |
| **T5** `snap-radius` | `src/core/instrument/ui/waveform_view.{h,cpp}` + the marker-drag input path — **spec'd nowhere; see below** |
**Shared files in the wave:** none. The three tracks touch three disjoint directories.
**Shared files in the wave:** none. T1T3 touch three disjoint directories, and T5 touches a
fourth (Ω-W1-T5's surface, which lands first).
**Ω-W2-T5 has no spec section here, on purpose.** It arrived from ruling R3 — Daniel,
2026-08-03, recorded in `docs/product/single-cycle-and-wavetable.md` §7.1 and summarized at
"Flagged for awareness" item 4 above — after this phase was written, and is **committed on
branch `omega-w2-t5-snap-radius` (`b7b7e88`)**: it
bounds `nearestZeroCrossing`'s fan-out to `kMarkerGrabWidth` (5 px) and adds
**Ctrl-defeats-snap** on a marker drag. **Numbered T5 rather than T4 because it inherits
Ω-W1-T5's file surface; there is no Ω-W2-T4.** Two things it needs from other owners:
- **Flagged for doc-keeper:** its record belongs in `docs/COMPLETED.md`. This plan names it
only so the phase's own superseded criteria can point at what superseded them.
- **Flagged for doc-keeper — a documentation gap, not a defect.** *Ctrl-defeats-snap on a
marker drag* is a new **user-visible gesture** with no home in
`docs/product/instrument-control-surface.md`, and §4.1's exclusion block there — which
excludes waveform markers from the modifier law because *"overloading Shift there would
collide with a shipped gesture"* — now reads as a claim that markers carry no modifier at
all. That block needs amending and the gesture needs documenting.
#### Ω-W2-T1 — `view-topology-cache` *(measurement-gated on Ω-W1-T1's result)*