Skip to content

Compare stored automation bits across the browser and native hosts #1669

Closed

Description

@tamashi095

Compare stored automation bits across the browser and native hosts

Proposed by Research: render stored session automation in the engine, identically on every
platform
(#1058), answer A5, under decision 15 D15-16
(docs/rulings/live-updates-seamless-swaps-and-one-control-plane-2026-10-05.md). A draft for root
to file. Code anchors verified on 6ee64f484. Batch Q.

Product outcome

One fixture session automates every row the engine renders, with every shape, and is seeked once
while it plays. The shipped browser artifact (wasm simd128) and the native browser host on
x86-64 (AVX2) render it to the same bits, and the C ABI renders the same bits as the browser host.
A second fixture does the same for matrix automation. This is the browser and x86-64 half of the
evidence that stored automation renders identically on every platform; #1670 adds AArch64 and
seek-time invariance on the same fixtures. It adds no new digest owner: it extends the direct
oracle's existing pins. Batch Q.

Context

The existing owners.

  • The G5 corpus is the single owner of the kernels' cross-target digests
    (tools/wasm-gate-corpus/src/lib.rs:1-20), run natively by wasm-gates and under wasmtime.
    Compile stored automation into per-cell events in node time #1637 adds the evaluator's event values to it.
  • The browser direct oracle renders fixed sessions through the shipped simd128 artifact
    (hosts/host-web/tests/browser-v1/direct-oracle.mjs:547-679). Each leg asserts its PCM digest
    equals a native pin before anything prints (the identity leg, :571-583; the command timeline,
    :584-601; the observation timeline, :602-654).
  • Each native pin is written by a host-web test that renders the same session and transcript
    through AudioWorkletEngineHost on the host CPU and reads the pin from expected.json
    (native_identity_session_digest_pins_the_wasm_parity, hosts/host-web/src/tests.rs:1632;
    the command and observation twins at :1751 and :6690; the pin reader serde_pin,
    :2255-2300).
  • CI runs the oracle in the artifact-gates job through scripts/check-web-audioworklet.sh and
    scripts/check-browser-expected-resources.py --artifacts
    (.github/workflows/qualification.yml:331-343).

What the oracle drives today. The command timeline feeds constant PCM and drives live
commands (direct-oracle.mjs:373-454). No leg has stored automation or a session seek.

The C ABI. No test compares a C ABI render with the browser host's today. #1399 adds the
cross-host harness hosts/host-web/tests/strip_cells_cross_host.rs.

Decisions frozen for this slice

  • D1. Two fixtures, beside the existing ones in hosts/host-web/tests/browser-v1/:
    • automation-session.json: two tracks and one submix at 48 kHz, quantum 128. Track 1 automates
      fader, mute, pan, trim, polarity, HPF and LPF on its lanes (one both entry and one
      single-lane entry among them), and has a pre-fader route into the submix with
      follows_mute. Its inserts carry every span-driven launch effect (compressor, delay with its
      time automated, gate/expander, multiband compressor, soft clip, transient shaper, true-peak
      limiter) and a parametric EQ, each with at least one automated Block parameter. Track 2 has
      the same chain with no automation, so a bank holds automated and plain lanes.
    • Across the entries: step, linear and exponential segments (exponential only where a rule
      allows it: Validate builtin automation targets against their rows at preparation #1645's for the input HPF and LPF, Validate builtin automation targets against their rows at preparation #1645's shape rule for an effect frequency, time or
      ratio row), a gap, a step on a sample that is not a grid
      sample, and a segment that starts before the seek target.
    • automation-matrix-session.json: one matrix strip whose four coefficients are automated.
  • D2. One transcript. Both fixtures render 96 blocks of PCM generated by an integer formula
    that is exact in f32 in Rust and in JavaScript, for example
    x[i] = ((i·40503) mod 65536 − 32768) / 65536. At block 40 the host makes one session seek to
    timeline 30,000 (Seek the timeline and every source from the browser module export and the headless SDK #1643's export in the oracle, the same control-plane call natively).
  • D3. The browser leg. direct-oracle.mjs gains runAutomationTimeline for each fixture. It
    asserts each digest equals expected.directOracle.nativeAutomationPcmF32leSha256 and
    nativeAutomationMatrixPcmF32leSha256 before any print, as the existing legs do. The pins are
    written by two new host-web tests beside :1632, which serde_pin extends to read. Compare stored automation bits on AArch64 and across seek times #1670
    reuses these two tests and pins.
  • D4. The C ABI equals the browser host. In Report live_values_superseded in the browser status and prove both hosts drain strip cells alike #1399's cross-host test, both fixtures and the
    transcript render through the C ABI (Seek the timeline and every source in one C ABI call #1642's miso_engine_v1_session_seek) and through
    AudioWorkletEngineHost, and the two outputs are compared bit for bit in the test. No digest.
  • D5. One-ulp evidence (PR evidence, not committed). Plant a one-ulp change in Compile stored automation into per-cell events in node time #1637's
    value_at result. Record that the two native pin tests, the oracle's simd128 legs, the
    cross-host test (when the plant reaches only one host's path, as a check of the comparison) and
    the G5 corpus each turn red, then revert.

Deliverables

  1. D1-D2's fixtures and transcript.
  2. D3: the oracle legs, the two native pin tests and the expected.json pins.
  3. D4's test cases.
  4. D5's record in the PR.

Authorized paths

  • hosts/host-web/tests/browser-v1/ (the two fixtures, direct-oracle.mjs, expected.json).
  • hosts/host-web/src/tests.rs (two pin tests and serde_pin).
  • hosts/host-web/tests/strip_cells_cross_host.rs (D4's cases).

Non-goals

Hazards

  • Pin discipline. A pin is written only from a native render the simd128 leg already equals
    (the oracle asserts before it prints). Never mint a pin from one side.
  • The browser job. Its Playwright legs replay expected.json; a new key must not break
    scripts/check-browser-expected-resources.py's two-class rule (pins exact, resources
    ceilinged).
  • Hot files. hosts/host-web/src/tests.rs and expected.json are stream H's. Root sequences
    the merge.

Objective gates

  1. Browser equals native.
    mkdir -p target/ci/qualification-artifacts target/ci/qualification-named-twin && bash scripts/build-web-audioworklet.sh --named-twin target/ci/qualification-named-twin target/ci/qualification-artifacts,
    then
    bash scripts/check-web-audioworklet.sh --without-metadata-regeneration target/ci/qualification-artifacts target/ci/qualification-named-twin/miso-engine-v1-audio-worklet.simd128.named.wasm
    and python3 -B scripts/check-browser-expected-resources.py --artifacts target/ci/qualification-artifacts
    pass with both automation legs.
  2. Native pins. cargo test --locked -p host-web --features test-support passes, the two new
    pin tests and D4's cross-host cases included.
  3. One ulp turns every leg red (D5, PR evidence).
  4. Workspace. The test-debug-a workspace step of qualification.yml;
    cargo test --locked --release -p lane -p math -p wasm-gates --features math/lane (G5);
    cargo fmt --all -- --check;
    cargo clippy --locked --workspace --all-targets --all-features -- -D warnings.

Test value

  • The automation legs turn red if any row's events, designs or ramps differ between wasm and
    native: a platform transcendental in the evaluator, a fused multiply-add, an FP environment left
    unpinned at preparation (finding F11), or a grid on the render clock.
  • The cross-host test turns red if the C ABI and the browser host prepare or seek a session
    differently, which no test compares today.

Dependencies

Batch Q. Direct dependencies:

#1651, #1642 Seek the timeline and every source in one C ABI call, #1643 Seek the
timeline and every source from the browser module export and the headless SDK
and #1637
Compile stored automation into per-cell events in node time (D5's plant, G5's case) arrive
through those drafts. #1670 Compare stored automation bits on AArch64 and across seek times
depends on this draft.

Amendment (controls coordinator, 2026-10-14)

Checked against 12d4a4f9d.

Amendment (controls coordinator, batch 20, 2026-10-17): narrowed to the delivered rows

Checked against e7722bd19. The owner narrowed this issue on 2026-10-17. This amendment replaces
D1-D5, the authorized paths, the gates, the test value and the dependencies above, and the
amendment of 2026-10-14.

Product outcome (narrowed)

One fixture session automates the rows that the engine renders today: the strip fader, the strip
mute, the VCA offset, the VCA mute, and a following send that tracks its source's mute and its
VCA's mute. It uses every shape that each row accepts, and it is seeked once while it plays. The
shipped simd128 artifact (the direct oracle), the native browser host on x86-64
(AudioWorkletEngineHost) and the C ABI render the same bits. There is no new digest owner. The
oracle leg adds one pin beside the existing pins in expected.json.

What the code accepts (anchors at e7722bd19)

  • Grammar. An entry is {id, target, segments, offset} (crates/session/src/parse.rs:1569-1604).
    The target is {owner, owner_id, section, control, lane} (:1606-1646). A segment is
    {shape, start_sample, end_sample, start_value, end_value, unit} (:1690). Sample times are
    JSON strings.
  • Rows. Strip fader: strip, builtins, control 5. Strip mute: control 6
    (crates/controls/src/builtin_rows.rs:29-30). VCA offset: vca, own, control 1. VCA mute:
    control 2 (crates/controls/src/vca.rs:14-15). Only these four rows compile to cells
    (crates/controls/src/automation.rs:394-408). A route-row entry is kept but inert (Render stored send gain, mute and matrix automation #1654).
  • Shapes. step is always valid. linear needs a continuous row. exponential needs a
    continuous row above 0 that is not in dB (crates/controls/src/automation.rs:255-266). So the
    fader and the VCA offset take step and linear, the two mutes take step only (unit
    linear, values 0 and 1), and exponential is refused on all five rows with
    control.automation.shape.
  • Structure (crates/session/src/validate.rs). Segments are ordered and do not overlap
    (:1023, :1031). Gaps are valid. The jumps of one entry are at least 64 samples apart
    (:1070-1134). One entry per lane, and both claims the two lanes (:1141-1185). The hold
    rule is in docs/SESSION_SCHEMA_V1.md:339-345: a step holds start_value and takes
    end_value at end_sample.
  • Offsets. The stored hand-move offset (Store a hand move on an automated fader as a dB offset #1650) is offset.{left_db,right_db}. A lane that the
    entry drives on the fader or VCA offset row takes a value in [-144, 24] dB. Every other lane
    holds exactly +0.0 (docs/SESSION_SCHEMA_V1.md:363-371).
  • Known-good shapes. fixtures/session/v1/builtins-automation.json (fader :174-197, VCA
    offset :294-318, VCA members :106-118, a following send :129-148). Mute entries:
    hosts/host-web/tests/stored_mute_automation.rs:184-207. VCA mute:
    crates/host-core/tests/stored_vca_automation.rs:161-184.

D1 (replaced). One fixture: hosts/host-web/tests/browser-v1/automation-session.json

Copy the frame of command-session.json (48 kHz, quantum 128, profiles, main-out, default
smoothing). Then:

  • Sources. src-t1 and src-t2, 2 channels, 32f, frames "48000". A seek clamps a
    source to [0, frames] (crates/host-core/src/source.rs:37-39), so the frames must be more
    than 37,168.
  • Tracks. t1 on src-t1 and t2 on src-t2. No console slot, no insert, no input
    filter. Static faders at 0 dB, all hand mutes false.
  • Submix. bus, with the plain builtins of builtins-automation.json:68-118.
  • VCA. vca, members ["t1", "t2"], static fader.left_db -2.0 (the static offset on the
    left lanes), all other static values zero or false.
  • Routes. t1-main and t2-main (post_pan to main-out). t1-bus (pre_fader to bus,
    follows_mute: true). t2-bus (pre_fader to bus, follows_mute: false, the
    non-following contrast). bus-main (post_pan to main-out).
  • Entries (values in dB for the dB rows, 0 or 1 for the mutes):
    • t1-fader, t1 control 5, lane both, offset {left_db: -1.5, right_db: 0.0}:
      linear 256-2304, 0 to -12; a gap; step 3001-3601, -6 then -9 (3001 and 3601 are not grid
      samples); linear 29000-33000, -9 to +3; step 34000-35000, 0 then -3.
    • t1-mute, t1 control 6, lane left: step 0-1000, 0 then 1; step 1600-4200, 0 then 1;
      step 4800-29800, 0 then 1; step 30600-33500, 0 then 1; step 34000-36000, 0 then 0. Muted over
      1000-1600, 4200-4800, 29800-30600 and 33500-34000.
    • t2-fader, t2 control 5, lane right, offset {left_db: 0.0, right_db: 0.0}: linear
      1024-4096, -3 to 0; linear 29900-32000, 0 to -18.
    • vca-offset, vca control 1, lane right, offset {left_db: 0.0, right_db: -0.5}: linear
      512-3584, 0 to -6; step 4096-4352, +2 then 0; linear 29500-31500, -4 to +2; step 33000-35500,
      -3 then -3.
    • vca-mute, vca control 2, lane both: step 0-2000, 0 then 1; step 2400-30200, 0 then 1;
      step 30400-35000, 0 then 1; step 35500-36000, 0 then 0. Muted over 2000-2400, 30200-30400
      and 35000-35500 (review fold 1, M1: the seek target 30,000 and the step at 3001 are outside
      every VCA mute hold, so their landings are heard).
  • What this covers. A both entry and single-lane entries. Lanes driven by their own curve,
    by a VCA curve, by both, and plain lanes. Linear segments, steps on and off the grid, gaps, a
    hold before the first segment and after the last one, and non-zero stored offsets. Each entry
    has a segment that starts before the seek target 30,000 and ends after it, and events after the
    seek. The following send closes for the strip mute (left lane only) and for the VCA mute. The
    non-following send stays open.
  • Dropped. The matrix fixture, effect inserts, and every other row (phase 2, below).

D2 (replaced). One transcript

  • 96 blocks of 128 frames. Blocks 0-39 render timeline 0-5,119. Before block 40 renders, the host
    makes one plain session seek: generation 2, timeline 30,000. Blocks 40-95 render timeline
    30,000-37,167.
  • Before each render, submit exactly one 128-frame chunk per source (the oracle's ring holds one
    quantum, direct-oracle.mjs:96). Block k < 40: generation 1, start frame k*128. Block
    k >= 40: generation 2, start frame 30000 + (k-40)*128. final is always false. Do not
    submit a generation-1 chunk for block 40. A seek discards stale chunks and needs contiguous new
    chunks from the seek frame (crates/source/src/lib.rs:1162-1168, :1355-1381).
  • PCM at source frame f: ((f*m) mod 65536 - 32768) / 65536, with m = 40503 (src-t1 left),
    30011 (src-t1 right), 52153 (src-t2 left), 18749 (src-t2 right). The value is exact in
    f32. In Rust, compute (f as u32).wrapping_mul(m) & 0xffff in integers, then subtract 32768
    and divide by 65536.0 (plain i32 multiplication overflows). In JavaScript,
    (Math.imul(f, m) >>> 0) & 0xffff gives the same integer.
  • The seek: miso_engine_web_v1_session_seek(handle, 2, 30000n) in the oracle
    (hosts/host-web/src/ffi.rs:3351), host.session_seek(None, 2, 30000, None) natively
    (hosts/host-web/src/lib.rs:3756), and miso_engine_v1_session_seek(session, 2, 30000) on the
    C ABI (crates/capi/src/ffi.rs:660). All three reach SessionState::session_seek
    (crates/control-plane/src/control.rs:4327). A plain seek declares a discontinuity and
    publishes a successor plan, on every host alike. Every seek and every submit must return 0.

D3 (replaced). One oracle leg and one pin

  • direct-oracle.mjs gains runAutomationTimeline. It boots automation-session.json, runs D2
    and digests the PCM as the other legs do (pcmSha256, every left block then every right block).
    It records actual.automationTimeline = {blocks: 96, seekBlock: 40, seekTimelineSample: 30000, beforeDisposeStatus, pcmF32leSha256}. Before the print branch, it asserts that the digest
    equals expected.directOracle.nativeAutomationPcmF32leSha256, as the other legs do
    (:493-576).
  • The pin key is directOracle.nativeAutomationPcmF32leSha256. Put automationTimeline and
    the pin after observationTimeline in expected.json.
  • The native pin test is native_automation_timeline_digest_pins_the_wasm_parity in
    hosts/host-web/src/tests.rs, beside its twin at :2016. It copies that twin: include_str!
    of the fixture, AudioWorkletEngineHost::boot with source_ring_frames: QUANTUM,
    submit_source, render_next, the same digest. It asserts against both pins.
  • serde_pin (:2120-2164) gains two fields: the text after "nativeAutomationPcmF32leSha256":,
    and the "pcmF32leSha256": after the anchor "automationTimeline".
  • scripts/check-browser-expected-resources.py adds ("automationTimeline", "pcmF32leSha256")
    to DIGEST_PATHS (:121-125) and says four PCM digests in its docstring (:33-35). The two-class
    rule covers resource rows only, so the new key needs no class. The oracle (:599) and the
    script (:274-283) compare the whole document, so expected.json, the oracle and the script
    change in one commit. No other reader breaks: qualification/qualification.js:1131-1132 and
    run.mjs:204-207 read only the identity pins.

D4 (replaced). One cross-host case in hosts/host-web/tests/control_parity.rs

  • the_automation_fixture_renders_alike_on_both_hosts_across_one_session_seek. The browser side
    boots the fixture bytes through Rig::boot (tests/common/mod.rs:363-385). The C ABI side
    parses the same bytes with parse_session_json and builds CAbi::new
    (control_parity.rs:922-975).
  • It runs D2 with CAbi::session_seek (:4949-4972) and direct submits, as
    a_composed_stored_mute_renders_alike_on_both_hosts does (:3694-3741). Do not use step():
    it hard-codes generation 1 (common/mod.rs:454-475).
  • It compares every block's two planes with bits() (common/mod.rs:287-289) and assert_eq!.
    No digest. The case is under test-support.

D5 (replaced). The plants (PR evidence, not committed)

A plain one-ulp change to the result of value_at alone is not valid. The program builder
compares value_before with value_at to find jumps (crates/automation/src/program.rs:330), so
the change adds jumps and can make preparation refuse the session. Use these three plants, one at
a time, and record each leg's result:

  • P1, the evaluator (every row). Put f32::from_bits(v.to_bits() + 1) on every return of
    value_at (crates/automation/src/evaluate.rs:18-44) and of value_before
    (crates/automation/src/program.rs:338-358). The jumps stay the same. A mute value of 0.0
    becomes the least subnormal, so every open mute span, VCA mute span and follow gate closes
    (value != 0.0: crates/builtins-compiler/src/lib.rs:3252;
    crates/graph/src/runtime.rs:1480-1481, :2196). The FP environment keeps subnormals on every
    target (crates/lane/src/fpenv.rs:518-530). Expected red: the native pin test, the oracle leg,
    and G5 (g5_native_digests_match_pins and the wasm leg; crates/automation/src/corpus.rs:245).
    Expected green: D4 (the path is shared).
  • P2, the dB rows. On a dB row a P1 ulp can disappear (in db_gain, or in
    vca_compose_db). Put next_up on the output of db_gain
    (crates/builtins/src/lib.rs:6472-6479). It reaches every fader and VCA offset lane. Expected
    red: the native pin test and the oracle leg.
  • P3, one host only (does D4 compare?). After state.render returns Ok in
    miso_engine_v1_render_f32_planar (crates/capi/src/ffi.rs:1133), flip the low bit of the
    first left sample when absolute_sample >= 5120. Expected red: D4 at block 40. Expected green:
    the pin test and the oracle leg.

Hand-written tests that compute their expected values with value_at stay green under P1 (for
example crates/host-core/tests/stored_fader_automation.rs:578). Do not count them.

Authorized paths (replaced)

  • hosts/host-web/tests/browser-v1/automation-session.json (new), direct-oracle.mjs,
    expected.json.
  • hosts/host-web/src/tests.rs (one pin test, serde_pin).
  • hosts/host-web/tests/control_parity.rs (D4).
  • scripts/check-browser-expected-resources.py (DIGEST_PATHS and the docstring only).

Objective gates (replaced)

  1. Browser equals native. As on main (.github/workflows/qualification.yml:132, :345-348):
    mkdir -p target/ci/qualification-artifacts target/ci/qualification-named-twin && bash scripts/build-web-audioworklet.sh --named-twin target/ci/qualification-named-twin target/ci/qualification-artifacts,
    then
    bash scripts/check-web-audioworklet.sh --without-metadata-regeneration target/ci/qualification-artifacts target/ci/qualification-named-twin/miso-engine-v1-audio-worklet.simd128.named.wasm
    and python3 -B scripts/check-browser-expected-resources.py --artifacts target/ci/qualification-artifacts
    pass with the automation leg. python3 -B scripts/check-browser-expected-resources.py --self-test
    passes.
  2. Native pin and cross-host. cargo test --locked -p host-web --features test-support passes,
    with the new pin test and the D4 case.
  3. Plants (D5, PR evidence): P1, P2 and P3 each give the expected red legs.
  4. Workspace. The test-debug-a step (qualification.yml:631-638); G5,
    cargo test --locked --release -p lane -p math -p wasm-gates --features math/lane (:705-706);
    cargo fmt --all -- --check;
    cargo clippy --locked --workspace --all-targets --all-features -- -D warnings.

Test value (replaced)

  • The oracle leg and its pin turn red if the simd128 artifact renders an event, a
    composition (fader with VCA offsets, mute OR, the follow gate) or the landing of a plain session
    seek differently from x86-64 native. Today the only artifact-versus-native renders have empty
    automation (session.json:62, command-session.json:141, observation-session.json:159).
  • The D4 case turns red if the C ABI and the browser host prepare the whole fixture or land
    its seek differently. Today the only cross-host case with a seek automates the mute row only
    (control_parity.rs:3571), and the VCA cases do not seek (:6916-7395).

Dependencies (replaced): all on main

Phase-2 extensions (not in this issue)

Decision record: T1+T2 (batch 20)

  • D1 shipped as written. hosts/host-web/tests/browser-v1/automation-session.json has the
    amendment's sources, tracks, submix, VCA, five routes and five entries with no departure. Every
    value was accepted: the "0 then 0" and "-3 then -3" steps, the 64-sample jump spacing and the
    VCA both mute lane all parse, validate and prepare (the native pin test boots the file
    verbatim). Track pan is left -1.0, right 1.0, smoothing_samples 0 and the bus matrix is the
    identity with smoothing_samples 0, as in command-session.json and builtins-automation.json.
    The two content digests are placeholders of the valid form (the engine streams the PCM).
  • D2. One transcript helper, automation_transcript in hosts/host-web/src/tests.rs, and its
    JS twin in runAutomationTimeline: the same integer formula and chunk schedule. Every seek,
    submit and render returns 0 and is asserted on both sides.
  • D3. Pin test native_automation_timeline_digest_pins_the_wasm_parity; serde_pin reads
    nativeAutomationPcmF32leSha256 and the pcmF32leSha256 after "automationTimeline". The test
    also asserts the automation is heard: the same session with automation cleared renders other
    bits before the seek (blocks 0-39) and after it (blocks 40-95).
  • Pin discipline. The native digest and the simd128 oracle digest were read independently
    before any pin was written (the oracle run against a scratch copy of expected.json with a
    probe pin). Both are
    6f82e4668836ea2c7b977f2c322882aea98d73c0dd285caf41e13ccd18186cb6. The leg's
    beforeDisposeStatus.memoryBytes is 2,949,120 (one 64 KiB page above the other legs; the
    seek's successor plan is control work).
  • Script. DIGEST_PATHS gains ("automationTimeline", "pcmF32leSha256"); the docstring and the
    comment above it say four digests.
  • Gates (all green). cargo fmt --all; cargo clippy --locked -p host-web --all-targets --all-features -- -D warnings; cargo check --locked -p host-web --all-targets;
    cargo test --locked -p host-web --features test-support native_ (the four pin tests);
    bash scripts/check-web-audioworklet.sh --without-metadata-regeneration ...;
    python3 -B scripts/check-browser-expected-resources.py --artifacts target/ci/qualification-artifacts
    and --self-test (32 red mutations); node direct-oracle.mjs in compare mode.

T3

  • D4 shipped. the_automation_fixture_renders_alike_on_both_hosts_across_one_session_seek in
    hosts/host-web/tests/control_parity.rs (under test-support). The browser side boots the
    fixture bytes with Rig::boot; the C ABI side builds CAbi::new from parse_session_json of
    the same bytes. Both run the D2 schedule with direct submits and session_seek; every seek,
    submit and render returns OK, and each block's left and right planes are compared with bits().
    Both hosts agree on all 96 blocks.
  • Ring. Rig::boot and limits() both use a ring of two quanta, not one; the transcript
    submits one quantum per block, so the ring size does not change what renders.
  • Probe (not committed). Flipping the low bit of the first C ABI sample at blocks >= 40 in the
    test's read path turns the case red at block 40, left plane.

Review fold 1

  • M1. At timeline 30,000 every member lane was inside a closed mute (VCA mute 29900-30100;
    t1 left 29800-30600). A mute hold only remembers a seek's value (docs/SESSION_SCHEMA_V1.md,
    the fader-row paragraph), so the fader and VCA-offset landings were never heard, and the step
    at 3001 sat inside the VCA unmute hold. The vca-mute segments are now step 0-2000, 2400-30200,
    30400-35000 and 35500-36000 (D1 above is edited in place); t1 left still lands inside a closed
    mute. Re-minted pin, measured on both sides independently and equal:
    a81d883b3f83eaf629bd78db149d1a39825c821c6cb2e9a53506341a97ed1363 (native pin test, and the
    oracle against the unchanged artifact). The pin test now also asserts the landing is heard:
    two copies whose t1-fader holds -6 dB or +20 dB over 30000-30064 only render other bits in
    block 40. It is red on the fixture before this fold and green after it. The expected-resources self-test gains the automationTimeline digest mutation.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions