Skip to content

Controls batch 17: stored fader automation renders on both hosts, with VCA offsets and hand moves - #1712

Merged
tamashi095 merged 45 commits into
mainfrom
codex/controls-batch-17
Oct 17, 2026
Merged

tamashi095 merged 45 commits into
mainfrom
codex/controls-batch-17

Conversation

@tamashi095

@tamashi095 tamashi095 commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Controls batch 17: stored fader automation (the R1 group). A session's fader automation now renders on both hosts: it is prepared into one cell program per automated lane, moves sample-exactly in node time through latency and seeks, carries across plan swaps, composes with VCA offsets, and takes a hand move as a stored dB offset (OQ1). Live level values are composed in the canonical floating-point environment on every control-thread entry (#1709). A Reposition swap while a live level move may still ramp is reported as a fallback on the watermark (conservative); the move's exact length across that swap is split to #1711.

Closes #1646
Closes #1647
Closes #1648
Closes #1649
Closes #1650
Closes #1709

What changed

  • Prepare stored fader automation and render it flat #1646 prepare stored fader automation (ba240b6 D6 order; 37150ce preparation; U1 fold 63d27a2, 6c8cf3a, 275ff76; validator b844501; schema 66da5d0).
    • D6 (class B, root F16): the VCA offsets are summed first, then the member is added (session::vca_offsets_sum, vca_compose_db; VcaSum is an offsets accumulator with compose(member)). Every caller follows.
    • Every builtins preparation form compiles one cell program per automated fader lane (tracks and submixes), seeds each cell's event state, holds the lane's S (controls::effective, one author) in a three-word offsets cell, and sets the prepared value inside CanonicalFpEnv. The scalar oracle installs the same program (R17-4).
    • Preparation (builtins body, both graph lowerings, host-core's static composition, and since Compose live level values inside the canonical floating-point environment #1709 the whole of prepare_host_runtime_*) runs inside CanonicalFpEnv; a hostile caller word (FTZ, DAZ, round toward zero) prepares the canonical bits.
    • The session validator refuses an automation sample at or past 2^53 (automation::TIME_LIMIT, numeric.out_of_schema_range), for every entry, as the program builder does; the SDK and the published JSON Schema mirror it. Tests that used u64::MAX as a valid sample use 2^53 - 1.
  • #1650a / #1650b store a hand move on an automated fader as a dB offset (5c8781b, 3cf702b; R17-1, R17-6).
    • Every automation entry carries a required offset { left_db, right_db }; only a driven strip fader_db lane takes a non-zero value, any other lane must hold +0.0 (control.automation.offset). Trim and send offsets moved to Render stored input trim and polarity automation #1658 and Render stored send gain, mute and matrix automation #1654 (R17-6).
    • On a lane a rendered entry drives, SET_CONTROL fader_db reads and writes the entry's offset; S = own offset + reaching VCA offsets. R17-1: the driving entries are owners of the in-place edit, so the in-place and full paths give the same reply and bits on both hosts. A change of the static fallback on an automated lane is a Structure rebuild. The list's automated flag is a descriptor field (ControlEntry field 35; SDK decode).
  • Render moving stored fader automation, seeks and latency #1647 render moving stored fader automation (4667ff6, 1fbb0bc, 1832719; U2 fold 59ca8e3; U2 fold 2 86271e0; attempt 4 9314605).
    • R17-2: the graph compiler installs each builtin bank's floored input arrivals a(n). R17-3: before chain.run the graph sets each builtin slot's block time (timeline span at r + ΣP - a(n), held jump lengths) through builtin_processor_mut; no rack edit. R17-5: the source timeline reader takes a signed first sample (checked, saturating).
    • One event driver per fader stage turns cell events (grid, jump, held, set) into timed fader operations, composed with S; bank, fused fader-matrix bank and scalar oracle share it. Restart Set for a lane a successor adds; seeks set the lane exactly (also inside a node block, behind latency).
    • Gate 8 (the held-ramp obligation): a lane that owes a move when a live unmute ramp ends at E is moved there by a SeekMode::Ramp landing aimed at target_at(E + J) (R17-9); a seek Set exactly at E sets the lane and discharges (R17-8); a live write that frees a held lane releases the cell's hold at the next free block entry, owed or not (R17-10, fold 2). D1.2 removed: unobservable after R17-9 and R17-10.
    • The never-read refusal and stop counters were deleted (root: accepted with a one-line unreachability proof and a safe release path at every debug_assert site).
  • Classify fader automation edits as carried rebuilds #1648 classify and carry fader automation edits (96fb3db, d300cd3; U3 fold 9dcf288; attempt 3 0fac00e; root B17-R13 split 1bd0494, 5fa5dd5, 6cea893; B17-R14 flag d5c85be; B17-R15 0c42f6b).
    • A fader automation add, remove, segment or target edit is a rebuild (LiveRebuild::Automation); the offset is not compared. An automated lane takes no FaderDb record on any path. This closes Hold the automation jump lengths in a plan cell #1639's gap: an edit that needs jump lengths not yet written rebuilds, and the successor reads its own lengths.
    • The carry moves each automated channel's cell state and held-ramp obligation; an unchanged table continues verbatim; a changed one repositions and jumps over J when the composed gain differs; a removed ride is released by the strip join's FaderDb record.
    • The carry reads each lane's offsets cell (U3a), so a live level edit keeps its ramp and the superseded count stays exact at a swap. A Reposition swap holds a free stage's running ramp again, compares the curve where that ramp ends, and lands a moved target over max(J, remaining) (attempt 3). A step of the cell's own at the swap block's first sample is compared there and lands over J, as a fresh plan does (B17-R13 guard).
    • Current difference (split to Keep a live level move's own length across a Reposition swap #1711): a live level move with less than J left at a Reposition swap is stretched to J (heard 24, 26 and 30 blocks: 928, 672 and 160 samples left, J = 960; up to 3.1e-3 from the uninterrupted engine). B17-R14 reports it: the control plane marks such a rebuild CandidateOutcome::TransitionFallback, so the watermark reports OUTCOME_TRANSITION_FALLBACK, never EXACT, for it. The condition is conservative (any transaction rebuild of a session with fader automation while any level move may still ramp; a flagged rebuild keeps the window open over J, B17-R15), so it over-reports; it never under-reports in the gated cases. The flag's doc text (abi.rs, the C header comment, docs/C_ABI_V1_QUALIFICATION.md, both d.ts) says so. Keep a live level move's own length across a Reposition swap #1711 replaces it with the exact behaviour and deletes it.
    • A cell's own J jump still running at a Reposition swap lands over J again toward the new table: that is D2's specified behaviour (no user length; a fresh plan with the new table also jumps), not a deviation.
  • Compose VCA offsets with stored fader automation #1649 compose VCA offsets with stored fader automation (5e82ba2; U3 fold 9dcf288, ae0c160).
    • A live VCA move over an automated lane writes the lane's offsets cell (S as f64 plus the fader-row jump length) in one slot; the banks and the oracle read it at the drain and jump to the composed gain. A missing cell is refused INTERNAL before the stamp (never an ack before a drop).
  • Compose live level values inside the canonical floating-point environment #1709 compose live level values inside CanonicalFpEnv (fdea587; R17-7; U3 fold ae0c160).
    • Guards at SessionState::command, declare_discontinuity_serviced, compile_children (session JSON parse depends on the rounding mode), ControlCatalog::build / stage_live, and the whole of prepare_host_runtime_*. Never on the render thread. Gates on both hosts are red without the guard.
  • Placeholders committed: Index the automation entries per strip once per build #1710 (index automation entries per strip once per build; weekly performance pass); Keep a live level move's own length across a Reposition swap #1711 (keep a live level move's own length across a Reposition swap; deletes the B17-R14 flag).

Evidence

  • audit capi pcm_digest bffc21e857b5e134, unchanged across the batch (the audit session has no automation and no VCA where the D6 order differs). 0 violations, 0 allocations.
  • Re-pins, each measured, with reasons:
    • COMPLETE_SCHEMA_HASH 0x0c71_96f8_6abe_6d57 -> 0x5eb9_ad2e_d137_f3be (#1650a: Automation field 4) -> 0x5cab_2cf3_b4e4_767f (#1650b: ControlEntry field 35). 46 frames.
    • C header: a doc-comment-only addition to the watermark section (the B17-R14 flag text); no symbol, constant or layout changed; check-capi-abi.sh passes with no pin moved.
    • Realtime policy 153 -> 160 marked regions (GraphBlockTime impl, the fader bank's set_block_time, the lane automation impl, the event driver, bank helpers, oracle render, offsets read). No file under crates/engine/src/realtime/ was edited.
    • builtins dependency-boundary policy: automation added to builtins-compiler's list and the mutation fixture (Prepare stored fader automation and render it flat #1646 preparation builds the program; automation is a leaf). session depends on automation (TIME_LIMIT); control-plane on lane; fuzz lock updated.
    • Bytes (x86-64 AVX2): lane slot 224 B at the U2 fold, 304 B after U3a (the carried offsets state); at 224 B one automated lane with one segment charged 2,044 B (program 68 + 64, 8 x 224, offsets cell 120), cross-checked against retained heap with thread-scoped alloc counters; the charge test stays green at the head. Every fader bank +16 B (empty box), every live-controlled strip FaderCells +8 B; an automated controlled strip +80 B writer box. No fixture moved.
  • Gates: all green on codex/controls-batch-17k at 0c42f6b (tree identical to codex/controls-batch-17 at f4da4f2): fmt; bench policy; realtime policy 160 regions; host-core clippy default / no-default / all features and the loom spectrum handoff; workspace clippy -D warnings; no-feature release and debug checks; workspace tests (all features); the doc job; builtins, console and effect-runtime fixtures; graph determinism; effect contract; host-core policy; protocol wasm parity; SDK generated and types; C ABI; protocol allocation audit; builtins and builtins-graph trace audits; test-web-audioworklet; check-cross-targets; web build, check-web-audioworklet, SDK headless; CI path routing, test-support CI, browser expected resources, SDK deletions, command reason and kind vocabularies, script reachability; fuzz check; audit capi pcm_digest bffc21e857b5e134, 0 allocations.
  • Browsers: chromium, firefox and webkit qualification (--check-matrix --self-test-mutations) all pass at 0c42f6b.
  • Boot growth: 19,857,408 bytes, unchanged (pin 20,971,520).
  • capi_retained: 168,650 of 175,296 (168,378 at batch 16a; +248 B for FaderCells and the lane slots through Compose VCA offsets with stored fader automation #1649/Classify fader automation edits as carried rebuilds #1648; +24 B for the B17-R14 Option<LevelMoveWindow> in the control plane's SessionState).

Review

Fresh opus-high adversarial reviewers, one per review unit, then focused fold verifications.

Issues filed for this batch

  • Compose live level values inside the canonical floating-point environment #1709 "Compose live level values inside the canonical floating-point environment" (.github/ISSUE_SPECS/1709-compose-live-level-values-inside-the-canonical-fp-environment.md): closes in this PR (R17-7).
  • Index the automation entries per strip once per build #1710 "Index the automation entries per strip once per build" (.github/ISSUE_SPECS/1710-index-automation-entries-per-strip-once-per-build.md): weekly performance pass, stays open.
  • Keep a live level move's own length across a Reposition swap #1711 "Keep a live level move's own length across a Reposition swap" (.github/ISSUE_SPECS/1711-keep-a-live-level-moves-own-length-across-a-reposition-swap.md): stays open. First item: find out why the stage target moves during a heard move (0.398 at 12 blocks, 0.335 at 24, against 0.3018), a possible defect of its own. Then the deferred jump (D1), deletion of the B17-R14/R15 flag path (D2), and gates G1-G3.
  • Every placeholder mention in the specs and the flag's doc text now carries its number (45f4165).

Bodies to sync after merge

CI fold

  • The first CI run failed on AArch64 (NEON Simd4): rendering_fader_events_allocates_nothing counted in-bank lane indices and assumed one eight-lane bank. c484f2b keys the check on the eight tracks (one Jump per track at its own sample), so it holds at every bank width. No product code changed.

🤖 Generated with Claude Code

tamashi095 and others added 30 commits October 16, 2026 17:23
Amend #1646, #1647, #1648, #1649 and #1650 against 31fa33f: stale anchors,
landed dependencies from batches 10-16, decision 17 rules and the #1058 research.
Remove the specs of nineteen closed issues (batches 15 and 16).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1650 keeps the fader offset only (R17-6); the trim offset moves to #1658 and the
send offset to #1654. #1647 records R17-2, R17-3 and R17-5. #1648 and #1649 note R17-1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
session gains vca_offsets_sum and vca_compose_db; vca_effective_db and VcaSum
are written from them, so every caller takes the new order. Gate 6 and the
host-core oracle state the new order. audit capi pcm_digest unchanged
(bffc21e857b5e134). Records root's rulings R17-2 and R17-4 in #1646.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every automation entry carries a required offset record { left_db, right_db }
(session JSON key 4, wire field 4). Only a driven strip fader_db lane takes a
non-zero offset (R17-6); any other lane must hold +0.0, else
control.automation.offset. SDK, fixtures, skill and docs follow. Nothing renders
the field yet: #1650b, in the same push, is its production reader.
COMPLETE_SCHEMA_HASH 0x0c7196f86abe6d57 -> 0x5eb9ad2ed137f3be (Automation field 4).
audit capi pcm_digest unchanged (bffc21e857b5e134).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every builtins preparation form compiles one cell program per automated fader
lane (tracks and submixes) with the fader row's cell rule from controls, seeds
each cell's event state, holds the lane's VCA sum S (controls::effective) in a
three-word offsets cell, and sets the prepared value vca_compose_db(first value,
S) inside CanonicalFpEnv. The scalar oracle installs the same program (R17-4).
Bytes are charged as measured into builtin_bank_bytes and the graph plan rows.
A flat curve renders the static fader's bits on the C ABI and the browser.
audit capi pcm_digest unchanged (bffc21e857b5e134). Fields whose readers are
#1647, #1648 and #1649 (same push) carry expect(dead_code) until those land.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge fallout: three struct literals in the #1646b gates gain the required
offset record (+0.0 on both lanes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The validator now refuses the same segment samples as automation::ProgramBuilder
(shared automation::TIME_LIMIT; every entry, rendered row or not), with
numeric.out_of_schema_range. Before, a session the validator accepted could be
refused at preparation with host.automation.fader (U1 review MINOR 3, root).
The SDK builder mirrors it. Tests that used u64::MAX as a valid sample use
2^53 - 1. session depends on automation (a leaf); fuzz lockfile updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
R17-2: at the end of compile_with_builtins the graph compiler installs each
builtin bank's floored input arrivals through a defaulted processor method.
R17-3: before chain.run the graph sets each builtin slot's block time (the
timeline span at r + sum P - a(n), the held jump lengths) through
builtin_processor_mut; no rack edit, allocation-free, O(slots).
R17-5: the source timeline reader takes a signed first sample (i128, checked,
saturating at the limits). LiveRamps::from_lengths in controls. Realtime
policy 153 -> 155 regions. audit capi pcm_digest unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MAJOR 1: the builtins preparation body, both graph lowerings and host-core's
static VCA composition run inside CanonicalFpEnv, so the prepared fader bits do
not depend on the caller's floating-point environment; the dead black_box check
is gone. Test: a hostile caller environment (FTZ, DAZ, round toward zero)
prepares the canonical faders. MINOR 1: the scalar owner row charge is tested.
MINOR 2: gate 2 cross-checks the retained heap with thread-scoped alloc
counters (1,212 B per automated lane on x86-64 AVX2). MINOR 3: comments state
the validator refuses every per-entry cause first. NIT 1 recorded as a
departure (+16 B per fader bank). pcm_digest unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1646b gave builtins-compiler a normal automation dependency (preparation builds
the cell program). The doc job's builtins policy pins the dependency list; add
automation to the list and to the mutation fixture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found by the batch 17 U1 fold: preparation now runs canonically, live fader and
VCA compositions and conversions on the control thread do not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
D1: an add, remove, segment or target edit of a rendered fader automation entry
is a rebuild (LiveRebuild::Automation), checked after AutomationTarget and before
changed controls; the offset is not compared. D3: an automated fader lane takes
no FaderDb record on the full path, the fast path or the strip join. Parity row
on both hosts; #1639 gap gate (a fader entry added after an unwritten smoothing
change rebuilds and reads the new lengths). D4 docs. pcm_digest unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One event driver per fader stage turns each automated lane's cell events
(grid, jump, held, set) into timed fader operations at node time
timeline(r + sum P - a(n)), composed with the lane's held S. The bank renders
through process_with only when it has automation; the fused fader-matrix bank
fuses with operations. Restart Set for a lane a successor adds; seeks set the
lane exactly on both hosts. The scalar oracle renders the same events through
the same driver (R17-4), on the ramped fader. Realtime policy 155 -> 159
regions; lane slot 168 -> 192 B (measured, charged). pcm_digest unchanged.
The fader row's docs now say it renders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	crates/host-core/tests/stored_fader_automation.rs
One RAII guard at each control-thread entry that composes or converts level
values: SessionState::command (live classification, staging, rebuild) and
declare_discontinuity_serviced, control-plane compile_children (boot), host-core
ControlCatalog::build and stage_live, and the whole of host-core
prepare_host_runtime_* (route and send gains, effect coefficients, successor
joins). Gates: a live fader and VCA move under a hostile caller word (FTZ, DAZ,
round toward zero) render the canonical bits on the C ABI and the browser; the
preparation test now has non-0 dB routes. The hostile-word helper lives in
lane::fpenv behind test-support, so no test file holds unsafe (fixes the
realtime policy failure the U1 fold introduced). pcm_digest unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	crates/host-core/tests/stored_fader_automation.rs
start_sample and end_sample use a new automation_sample $def (0..=2^53-1), so
the schema refuses what the validator refuses since b844501. Test ties the
bound to automation::TIME_LIMIT (U1 fold verification MINOR 2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gate 8 (the batch 13b held-ramp obligation): a lane that owes a move when a
live unmute ramp ends at E is moved there (RetargetGain to the composed target
over J, hold_until its completion), including a move due at the next block's
entry; the scalar oracle and the fused fader-matrix bank use the same driver.
Lane slot 192 -> 208 B (measured, charged). Two one-line accessors outside the
paths: CellEvents::into_state, BuiltinFaderBank::is_muted. pcm_digest unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	crates/host-core/tests/stored_fader_automation.rs
A live VCA move over an automated fader lane writes the lane's offsets cell
(S as f64 plus the fader-row jump length) instead of a FaderDb record; S has
one author (controls::effective) and both level_records paths compare its bits.
The fader bank (and the fused fader-matrix bank and the scalar oracle) reads
the cell at the drain and jumps to the composed gain. Parity on both hosts,
allocation-free gate, TBD48 hostile-word case. Lane slot 192 -> 224 B,
FaderCells 136 -> 144 B, 80 B writer box per automated controlled strip (all
measured, charged). Realtime policy 159 -> 160. pcm_digest unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FaderBankProcessor::adopt_lane moves each automated channel's cell state and
held-ramp obligation (paired by strip and channel) and clears fresh; the
successor keeps its own offsets cell and S. An unchanged table continues
verbatim; a changed one repositions, re-reports the held ramp and jumps over J
when the composed gain differs (adoption test). A removed ride is released by
the strip join's FaderDb record. Gates 2-4 and 6 on the C ABI, browser parity
leg. Realtime policy 159; pcm_digest unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…_builtins

U1 fold verification MINOR 1: the three canonical-environment tests (host-core,
C ABI, browser) were falsely red under --release because LLVM folded their
vacuity probes; black_box the probe inputs and outputs. NIT 3: the public
prepare_session_builtins composes controls::effective inside CanonicalFpEnv.
NIT 1: comment the scalar oracle's defensive guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a lane a rendered entry drives, SET_CONTROL fader_db reads and writes the
entry's offset (D3); S = own offset + reaching VCA offsets (D6) reaches render
through the offsets cell with the Fader-row length. R17-1: the driving entries
are owners of the in-place edit, so the fast and full paths give the same reply
and bits on both hosts (equivalence gate). Fallback change = Structure. The
list's automated flag is a descriptor field (ControlEntry field 35; SDK decode).
COMPLETE_SCHEMA_HASH 0x5eb9ad2ed137f3be -> 0x5cab2cf3b4e4767f (field 35).
pcm_digest unchanged; realtime policy 160.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…R17-9

R17-10 (render fix): a live write that frees a held lane releases the cell's
hold in the same block (bank, fused bank, scalar oracle). R17-8: a seek Set at
E sets the lane exactly and discharges the move. R17-9: the move at E is a
SeekMode::Ramp landing aimed at target_at(E + J). New gates for constraint 7,
S composition in render, seeks inside a node block and after E, the gained
ride on a carried static fader (#1648b), gate 2 one block late, oracle latency
and the fused path, ends-muted and left-only cases. The never-read refusal and
stop counters are deleted. Lane slot 224 B (one-lane charge 2,044 B).
Realtime policy 160; pcm_digest unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1650b's successor note: list and catalog builds scan the automation table once
per strip. Control-thread cost only; for the weekly performance pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tamashi095 and others added 15 commits October 16, 2026 21:11
#1650b's heap cross-check read PreparedHost::control_catalog, which exists only
with host-core's control-provider feature; the default and no-default clippy
legs did not compile. Without the feature the catalog term is 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MAJOR-A of the U2 fold verification: a live write that frees a lane with no
move owed left the cell's D1.2 hold, so later grid events were only remembered
and the lane went stale. The cell's hold is now released at any free block
entry after a block the stage held through, owed or not (gate: probe red on
6fcd46e). D1.2 removed: unobservable after R17-9 and R17-10 (mutation run:
all gates green without it). One-line unreachability proofs at every
debug_assert site of the fader event driver; release paths hold or skip. Lane
slot 224 B; realtime policy 160; pcm_digest unchanged. Attempt 3 of 5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gates that discriminate: TBD48's automated canonical_live rows use offsets
whose S rounds under the hostile word (red without the command guard); a
session booted under the hostile word is the canonical session (red without
the compile_children guard; JSON parse is a TBD48 reason); wire field 35 is
required on control rows and refused on headers; the in-place canonical bytes
of a hand move equal the full path's (accounting was already right); the SDK
control-list fixture carries an automated row; mask copies a ride's offset;
the D6 order (hand offset first) is pinned; under_hostile_fp_env restores the
caller's word. Docs: a static fader change on an automated lane is Structure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, #1649 U3 fold)

U3 MAJOR 1: the swap carry (apply_pending, also the fused bank) reads each
carried automated lane's offsets cell with ALL, counted; adopt carries the
newest record and the heard S; the successor's first block applies it through
offsets_jump from the heard S unless its own cell has a newer record (then 1
superseded). The strip join writes an offsets-only change into the successor's
cells with the edit's own length. Gates: VCA moves pending at a swap keep their
length and their superseded count; a VCA move inside a rebuild keeps its own
length; heard move then rebuild; offset-only upsert inside a rebuild; obligation
owed at a swap; flat-ride twin with no settle window (ramp word); a second VCA
move. Lane slot 224 -> 304 B (measured, charged). Realtime policy 160;
pcm_digest unchanged. #1648 and #1649 at attempt 2 of 5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	.github/ISSUE_SPECS/1649-compose-vca-offsets-with-stored-fader-automation.md
#	crates/control-plane/tests/vca_automation_realtime.rs
 attempt 4)

F1: an offsets jump applied at the entry that frees the lane clears the
held-through mark, so the release no longer cuts its ramp (gate P2, red on
86271e0's logic). F2: the nothing-owed release gate runs the q37 case with no
grid point between the writes (red under 'release only when owed'). F3: P3 pins
the D1.2 removal against the never-muted twin (red with D1.2 restored); D1.2
was observable and produced a worse output, removed by root decision B17-R11.
#1648 residue: a hand move pending at a swap and one inside a rebuild keep
their own length. B17-R12: the Compare backstop is unreachable from every
shipped host (proof comment + debug_assert; release ramps over J). Attempt 4
of 5. Realtime policy 160; pcm_digest unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A table-only automation edit (any position) repositioned the cell at the swap,
cleared the offsets jump's hold and landed over J, cutting a pending, same-
transaction or in-flight VCA or hand move (watermark EXACT). The repositioned
cell now holds a free stage's running ramp again and compares the curve at the
ramp's end; a move heard before the swap lands over max(J, remaining), so it
ends when it was due. Gates: 16 cases across a table change (red at HEAD); the
superseded count when a carried move is replaced unheard. Test forms gate
Simd8 on avx2 (cross targets). Realtime policy 160; pcm_digest unchanged.
Attempt 3 of 5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-R13)

settle_adoption no longer moves the comparison to the end of a free
stage's running ramp when the cell's own table has a discontinuity at
the block's first sample: the lane jumps over J at the step, as a plan
prepared with that table does. Gate:
a_step_at_a_reposition_swap_jumps_over_the_fader_length (VCA and hand
move, heard 1 and 12, step at the swap +0/+1, against a fresh plan).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…B17-R13)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TBD50 holds the deferred-jump design that keeps a live level move's own
length across a Reposition swap; #1648 records the split and its
attempt count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…7-R14)

A rebuild transaction whose successor automates a fader lane completes
as CandidateOutcome::TransitionFallback while any level move may still
ramp (one conservative window on control: newest revision, longest
ramp; live FaderDb/offsets records and rebuild level changes). Control
cannot see which lanes reposition or how much ramp is left, so the flag
over-reports; TBD50 replaces it with the exact behaviour and deletes it.
No render or realtime edit. Gates:
a_reposition_swap_while_a_level_move_ramps_reports_a_fallback (capi),
a_reposition_swap_while_a_level_move_ramps_reports_the_fallback_alike_on_both_hosts
(host-web).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…B17-R15)

A flagged rebuild may land a stretched live move over J from its first
block, so it folds (its revision, J) into the level-move window: a
second Reposition inside that landing is flagged too. Gate:
a_second_reposition_inside_a_stretched_landing_reports_a_fallback.
TBD50 and the #1648 record gain this case, the wider over-report, the
MINOR 4 setup and the stage-target question to settle first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rendering_fader_events_allocates_nothing counted distinct in-bank lane
indices and expected eight; on NEON (four-lane banks) the eight tracks
form two banks and the set held four (PR #1712, AArch64 job). Each
track's step now ends at 90,000 + 37 t, and the test asserts one jump
at each of those eight samples. Checked locally on x86 at the default
width, Simd8, Simd4 (also between render calls) and Scalar; the old
assertion reproduces the CI failure at Simd4. No product code change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tamashi095
tamashi095 merged commit ad03780 into main Oct 17, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment