Repository navigation
Controls batch 17: stored fader automation renders on both hosts, with VCA offsets and hand moves - #1712
Merged
Conversation
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>
#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>
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
session::vca_offsets_sum,vca_compose_db;VcaSumis an offsets accumulator withcompose(member)). Every caller follows.controls::effective, one author) in a three-word offsets cell, and sets the prepared value insideCanonicalFpEnv. The scalar oracle installs the same program (R17-4).prepare_host_runtime_*) runs insideCanonicalFpEnv; a hostile caller word (FTZ, DAZ, round toward zero) prepares the canonical bits.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 usedu64::MAXas a valid sample use 2^53 - 1.offset { left_db, right_db }; only a driven stripfader_dblane 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).SET_CONTROL fader_dbreads 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'sautomatedflag is a descriptor field (ControlEntryfield 35; SDK decode).chain.runthe graph sets each builtin slot's block time (timeline span at r + ΣP - a(n), held jump lengths) throughbuiltin_processor_mut; no rack edit. R17-5: the source timeline reader takes a signed first sample (checked, saturating).SeekMode::Ramplanding 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.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.max(J, remaining)(attempt 3). A step of the cell's own at the swap block's first sample is compared there and lands overJ, as a fresh plan does (B17-R13 guard).Jleft at a Reposition swap is stretched toJ(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 rebuildCandidateOutcome::TransitionFallback, so the watermark reportsOUTCOME_TRANSITION_FALLBACK, neverEXACT, 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 overJ, 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.Jjump still running at a Reposition swap lands overJagain 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.SessionState::command,declare_discontinuity_serviced,compile_children(session JSON parse depends on the rounding mode),ControlCatalog::build/stage_live, and the whole ofprepare_host_runtime_*. Never on the render thread. Gates on both hosts are red without the guard.Evidence
audit capipcm_digestbffc21e857b5e134, unchanged across the batch (the audit session has no automation and no VCA where the D6 order differs). 0 violations, 0 allocations.COMPLETE_SCHEMA_HASH0x0c71_96f8_6abe_6d57->0x5eb9_ad2e_d137_f3be(#1650a:Automationfield 4) ->0x5cab_2cf3_b4e4_767f(#1650b:ControlEntryfield 35). 46 frames.check-capi-abi.shpasses with no pin moved.crates/engine/src/realtime/was edited.automationadded 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).sessiondepends onautomation(TIME_LIMIT);control-planeonlane; fuzz lock updated.FaderCells+8 B; an automated controlled strip +80 B writer box. No fixture moved.audit capipcm_digestbffc21e857b5e134, 0 allocations.--check-matrix --self-test-mutations) all pass at 0c42f6b.Option<LevelMoveWindow>in the control plane'sSessionState).Review
Fresh opus-high adversarial reviewers, one per review unit, then focused fold verifications.
CanonicalFpEnv) -> fold 63d27a2 + policy 6c8cf3a + Compose live level values inside the canonical floating-point environment #1709 placeholder -> fold verification PASS; leftovers 275ff76, schema bound 66da5d0.J-> Classify fader automation edits as carried rebuilds #1648 attempt 3 0fac00e -> verification FAIL (remaining < J stretched toJwith EXACT; a step at the swap's first sample skipped). Root B17-R13: split (step guard + Keep a live level move's own length across a Reposition swap #1711), B17-R14: conservative fallback flag -> focused verification FAIL (one MAJOR: a second Reposition inside the first one's stretched landing read EXACT) -> B17-R15 one-line fix 0c42f6b -> re-check PASS (ramps 200/500/4000, the second rebuild 1-7 blocks later: all report the fallback; the fix is red-checked bya_second_reposition_inside_a_stretched_landing_reports_a_fallback). Classify fader automation edits as carried rebuilds #1648: 3 of 5 attempts plus the split landing; no further attempt.set_outcomeremoved: EXACT), the rebuild-path fold, and the J fold.Issues filed for this batch
.github/ISSUE_SPECS/1709-compose-live-level-values-inside-the-canonical-fp-environment.md): closes in this PR (R17-7)..github/ISSUE_SPECS/1710-index-automation-entries-per-strip-once-per-build.md): weekly performance pass, stays open..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.Bodies to sync after merge
CI fold
rendering_fader_events_allocates_nothingcounted 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