Skip to content

Controls batch 11: meters, observation taps, delay lines and sends carry across a plan swap (#1633, #1282, #1327, #1283, #1284, #1381) - #1673

Merged
tamashi095 merged 15 commits into
mainfrom
codex/controls-batch-11
Oct 15, 2026
Merged

tamashi095 merged 15 commits into
mainfrom
codex/controls-batch-11

Conversation

@tamashi095

@tamashi095 tamashi095 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Controls batch 11: the carry chain continues. A plan swap now also carries per-node effect instances (a delay tail rings on), meters and effect observation taps (an acknowledged subscribe stays armed on the successor), PDC compensation lines, strip delay lines and live send ramps (a carried send retargets to its new value), and the browser pairs the carried meter and observation readers in the swap block. An effect lane holds no carry storage until a swap carries it, so the browser's boot budget regains 8 pages of margin. The stored-automation program's 38 filed specs move into ISSUE_SPECS.

Closes #1633
Closes #1282
Closes #1327
Closes #1283
Closes #1284
Closes #1381

What changed

  • Allocate an effect lane's carried slots only when a successor carries #1633 effect_control_lane builds no carried slots; graph::install_effect_carry installs them on the control thread for the successor sites of the carry program only, charged through effect_carry_retained_bytes; can_carry_from refuses a lane without them. Browser boot growth 20,971,520 -> 20,447,232 bytes (8 pages under the 20 MiB pin).
  • Carry per-node effect instances across a plan swap #1282 A live-controlled per-node effect instance that survives with the same layout, control kind, cell and shunt shape moves by pointer: its control lane carries and its processor and shunt are exchanged, so a delay tail rings on through the swap.
  • #1327a The chain's one combined engine edit (crates/engine/src/realtime/plan.rs, accepted on its attempt, with its mod.rs re-export and a doc-only link fix, both root-accepted): ObserverKind, ObserverCarry, observer_carries on the executor and the plan, and HostAttachment::adopt_predecessor called from PreparedRenderPlan::carry_from. A meter whose strip, tap, metrics and config are unchanged carries its observer; fresh meters join the carried window grid; host-core pairs the carried readers allocation-free.
  • #1327b A tap whose effect instance carries (any lane/node form) moves with it after the pending-Observe drain, so a subscribe acknowledged before the swap is armed on the successor and its window continues.
  • Carry compensation lines across a plan swap #1283 Every PDC compensation line whose delayed edge keeps its endpoints carries its pending audio, at the output and inside submixes (equal lengths swap rings; other lengths copy head-aligned); a re-pointed route does not carry.
  • Carry strip delay lines and live send ramps across a plan swap #1284 A strip's unchanged delay lanes carry; a live send with unchanged source and destination carries its ramp, position and mute after the predecessor's pending cell value is applied, and a live difference retargets it (route_record_between serves the classifier and the join). LiveRoute::drain is renamed apply_pending: it reads one latest-target cell and pops nothing (root accepted the rename).
  • #1381b The browser plan attachment's adopt_predecessor pairs the carried meter and observation readers and the host's plan tables follow them, so a browser swap keeps meter delivery and an acknowledged subscription running.
  • Docs. The 38 filed stored-automation specs (Retire the protocol's stored transport position #1635-Record the stored-automation console benchmark baseline #1672) are in .github/ISSUE_SPECS, with every draft reference replaced by its issue number.

Evidence

  • Full CI-equivalent local set on the final code (batch-gates.sh, now also running the bench and realtime policies, the host-core feature matrix with -D warnings and the host-core loom step) plus trace-graph-audit.sh (1,000,000 blocks): all green; SDK headless 528 pass, 0 fail. Browser qualification (run.mjs --check-matrix --self-test-mutations): chromium, firefox and webkit each "all qualification gates passed".
  • Every carry gate asserts zero allocations and frees on the swap block; every new bounded loop or pop in render code is in a realtime-policy region with its bound at the loop.
  • Browser boot growth 20,447,232 bytes at every slice after Allocate an effect lane's carried slots only when a successor carries #1633 (the pin 20,971,520 and the multiplier are unchanged).
  • audit capi pcm_digest moves from 9bc5ec3d8a4c223c to bf7dcedd1d92621f with Carry per-node effect instances across a plan swap #1282. Cause: the audit commits one structural transaction after its first call (adds a muted track), and its session holds a per-node live effect instance that restarted at that swap before Carry per-node effect instances across a plan swap #1282 and now moves by pointer with its state. Checked by building the audit at Carry per-node effect instances across a plan swap #1282's parent 405221f8d (9bc5ec3d8a4c223c) and at 067d145ec (bf7dcedd1d92621f); the later slices leave it unchanged. 0 allocations, 0 frees, 0 render errors.
  • Callgraph pins (scripts/check-web-audioworklet-callgraph.py): enter_block 2 -> 4 indirect sites (#1327a: the attachment hook and the observer_carries read), render_next 0 -> 2 (#1381b: the downcast that hands the paired readers to the host tables), each with its reason.
  • Oracle changes, each checked by the review: the capi #1266 gate-3 test replaces the multiband under a new ID to keep its restart expectation (Carry per-node effect instances across a plan swap #1282), and two capi live tests compare six blocks after the compensation lines flush (Carry compensation lines across a plan swap #1283, widened in the fold); the review measured that each still turns red on the defect it names.

Review

One opus-high review of the batch diff: PASS with findings, no blocker. Folded: both capi oracles compare six blocks after the lines flush; the render-thread pairing helpers are in realtime-policy regions; host-web's tap hand-over uses an explicit "commanded since commit" bit instead of the arm-sample-0 sentinel (new test red on the sentinel); doc fixes. The review confirmed that apply_pending pops nothing and that every changed oracle still tests its claim.

Known defect (pre-existing), fixed by #1584 D6 as the first slice of batch 12: an acknowledged subscribe on an effect owner that restarts (a prepared change) or is removed at a swap is dropped: Observe records left in that owner's predecessor queue are never applied, and its tap starts unarmed on the successor. An ack can precede a drop. #1584 D6 arms the successor's taps at preparation, with this case as a gate.

Issues to file

None new from this batch beyond the review's successor list in the boundary report (C ABI reader pairing when capi gains meter/observation readers; #1584 D6 with the dropped-subscription gate).

Spec changes: decision records and folds on #1633, #1282, #1327, #1283, #1284, #1381; #1635-#1672 moved in; #1626-#1629, #1306, #1567, #1632 references renumbered.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

tamashi095 and others added 15 commits October 15, 2026 03:06
…1672) and replace every draft reference with its issue number

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

effect_control_lane builds no carried slots; graph::install_effect_carry
installs them on the control thread for the successor sites in the carry
program only, charged through effect_carry_retained_bytes, and
can_carry_from refuses a lane without them. Browser boot growth 20,971,520 ->
20,447,232 bytes (8 pages under the 20 MiB pin); the first-compile graph rows
fall 6,048 bytes each at eight lanes; every carry gate unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	crates/capi/tests/resource_lifecycle.rs
A live-controlled per-node effect instance that survives a swap with the same
layout, control kind, cell and shunt shape moves by pointer: its control lane
carries (carried slots, bypass ramp) and its processor and shunt are
exchanged, so a delay tail rings on through the swap; pending Observe records
go through the existing marked drain. Node-to-node moves are no longer refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The one combined engine edit for the carry chain (root ruling, accepted on its
attempt): ObserverKind {Meter, Observation, Spectrum}, ObserverCarry,
observer_carries on the executor and the plan, and
HostAttachment::adopt_predecessor called from PreparedRenderPlan::carry_from;
plus its mod.rs re-export and a doc-only link fix (root accepted both). A meter
whose strip, tap, metrics and config are unchanged carries its observer
through the swap; fresh meters join the carried window grid; host-core pairs
carried meter readers allocation-free. Known red until #1381b in this batch:
host-web structural_apply meter test (recorded in the spec).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A tap whose effect instance carries (any of the four lane/node forms) moves
with it: the tap tables, armed count and publisher swap by pointer after the
pending-Observe drain, so a subscribe acknowledged before the swap is armed on
the successor and its open window continues; host-core pairs the carried
readers (pair_carried_observations), allocation-free on the swap block. A
restarted owner's tap starts fresh (#1584 D6 arms successors at preparation).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…block (#1381b)

The browser plan attachment's adopt_predecessor pairs the readers the core
carried (pair_carried_meters, pair_carried_observations) and the host's plan
tables follow them, so a swap keeps meter delivery and an acknowledged
subscription running instead of restarting; allocation-free in the swap
block. The structural_apply meter test, red since #1327a, now proves the
carry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every PDC compensation line whose delayed edge keeps its endpoints carries its
pending audio into the successor, at the output and inside submixes: equal
lengths swap the rings, other lengths copy head-aligned; a re-pointed route
does not carry. Allocation-free on the swap block; the line table is charged
in the carry program. Two capi live-test oracles compare from the block after
the lines flush.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A strip's delay lane carries when its length is unchanged; a live send whose
source and destination are unchanged carries its ramp, position and mute
after the predecessor's pending cell value is applied; a carried send with a
live difference is retargeted (route_record_between serves the classifier and
the join). Allocation-free on the swap block. LiveRoute::drain is renamed
apply_pending (it reads a latest-target cell and pops nothing).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lpers realtime, host-core builds its tests without features, an explicit tap-command bit, doc fixes (#1283, #1284, #1327, #1381)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… per-node instance now carries across the audit's swap)

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