Skip to content

Controls batch 12a: subscriptions survive restarted and removed owners; spectrum carries; meter frames follow the strip count (#1584 D6a, #1395, #1618, #1290 d1, #1106) - #1675

Merged
tamashi095 merged 17 commits into
mainfrom
codex/controls-batch-12a
Oct 15, 2026

Conversation

@tamashi095

@tamashi095 tamashi095 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Controls batch 12a: no acknowledged subscription is lost at a swap any more, spectrum captures carry, and the browser keeps meter frames running across a strip-count change. An acknowledged subscribe survives its effect owner's restart, and a removed owner's subscription ends with one OBSERVATION_ENDED event per commit (both hosts). Spectrum captures and collections carry by key and pair at promotion on the control side; a capture that does not carry settles honestly. The browser publishes the successor's correctly shaped meter frame from the first block after a strip-count swap, and the spectrum hop probe that failed CI on four PRs waits on its deadline.

Closes #1395
Closes #1618
Closes #1106

#1584 stays open: this batch is its D6a (core); D6b (browser) is in batch 12b, and the C ABI Observe edit (D1-D5, D7, D8) follows. #1290 stays open: (d1) is here; (d2) is in batch 12b.

What changed

  • Subscribe to an effect's observation tap with a transaction edit #1584 D6a ObservationBindings (controls) is owned by SessionState beside the monitor overlay and travels to every successor in SuccessorBase (root ruling). Each successor arms its taps at preparation with a prepared arm on ObservationLane whose window starts at its first accumulated block, so a restarted owner keeps an acknowledged subscribe, applied or still pending; a carried tap's own state wins. A binding whose target is gone ends with one reliable OBSERVATION_ENDED (0x8011) per commit carrying every ended key (root ruling A), its room reserved beside SESSION_COMMITTED and its size checked at preparation; a lane full of unread events is BACKPRESSURE with nothing changed; an event that cannot fit one control frame refuses the commit as a resource limit. Same on both hosts, with a parity row. The browser admits an Observe inside process(), so push_effect_observe records per-slot state in preallocated storage and the next service() folds it (command() services first).
  • #1395a/#1395b A spectrum capture or collection whose key (target, channels), kind, rate and quantum are unchanged carries its observer through the swap (one-shot state included; a collection's selection follows its entry); the control side pairs the readers at promotion (SpectrumService::adopt_successor), never in the swap block.
  • Settle a one-shot spectrum capture across a plan swap #1618 A capture that does not carry settles at promotion: a one-shot starts the successor's capture Invalid with its generation (one CaptureInvalid read, then a new arm completes), a stream ends, a lost collection selection releases its arm.
  • Replace the running browser session in the Rust host #1290 (d1) The meter frame, its header and a carried-from table travel with the PlanShape; the worklet cuts each set's views in the message handler that commits and switches by reference when the active header reads retired, so process() never cuts a view or allocates (root ruling). WebMeterHeader grows 72 -> 80 bytes (retired, frame and successor pointers); the ABI mirrors follow.
  • Bound the browser spectrum hop probe by its deadline, not a poll count #1106 The browser spectrum hop probe waits for two windows one hop apart on a 10 s deadline with a 5 ms yield and a permanent planted 750 ms slow start; a new qualification row and four self-test mutations hold it. The old 512-poll loop fails the planted start in chromium and webkit.

Evidence

  • Full CI-equivalent local set (batch-gates.sh: fmt, workspace clippy, workspace tests, the doc job, bench and realtime policies, the host-core feature matrix and loom step, fixtures, ABI, protocol parity and allocation audit, cross-targets, the AudioWorklet build/check with the boot budget, SDK headless, expected resources, fuzz) plus trace-graph-audit.sh; browser qualification (--check-matrix --self-test-mutations) in chromium, firefox and webkit: all green on the final head 5f378f426 (SDK headless 528 pass, 0 fail; trace-graph-audit 1,000,000 blocks); each browser prints "all qualification gates passed".
  • Browser boot growth 20,447,232 bytes (unchanged; 8 pages under the 20 MiB pin).
  • audit capi pcm_digest bf7dcedd1d92621f (unchanged), 0 allocations.
  • Re-pins, each recorded with its reason: COMPLETE_SCHEMA_HASH (D6a adds event 0x8011, then the fold makes it one event per commit with repeated keys); two C ABI capabilities vectors; WebMeterHeader ABI mirrors; capi_retained_bytes ceiling 181,696 -> 205,312 (the review fold charges a frame-bounded OBSERVATION_ENDED reserve per cell and the bindings table; measured 186,614 plus 10 %, rounded to 64).

Review

One opus-high review of the batch diff: PASS with findings, no blocker. Root's question answered: the in-process() Observe path allocates nothing (lookups and one enum write into preallocated storage; the allocating fold runs only from the service step and command()). Folded: the meter-header successor-pointer checks were always true on 64-bit native hosts and now compare full-width addresses (red on a missing pointer); the OBSERVATION_ENDED cells are charged by a frame-bounded ceiling, and the bindings table and its after() copy are charged; the observation slots are built with fallible allocation before the commit; a control-plane test shows an over-frame end event refuses as a resource limit with nothing changed; two dead public methods deleted; doc and error-name fixes.

Issues to file

  • Pair carried meter and observation readers on the C ABI #1674 (filed by root) pair-carried-meter-and-observation-readers-on-the-c-abi (placeholder in this branch).
  • Successor (from D6a): cap the observation bindings at admission by the number of keys one control frame holds, so the over-frame refusal cannot be reached.

🤖 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 17 commits October 15, 2026 07:27
…nd the stale ones, and the C ABI pairing placeholder (TBD24)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…TION_ENDED; #1290 (d1) frame with the PlanShape; 12a/12b order)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p-count swap (#1290 d1)

The meter frame, its header and a carried-from table travel with the PlanShape;
the worklet cuts each set's views in the message handler that commits and
switches to the pending set by reference when the active header reads retired,
so process() never cuts a view or allocates. A strip-count change publishes
the successor's correctly shaped frame from the first window after adoption;
windows of carried strips whose handles shift are published, not dropped.
WebMeterHeader grows 72 -> 80 bytes (retired, frame and successor pointers);
the ABI mirrors follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…VATION_ENDED when their target goes (#1584 D6a)

ObservationBindings (controls) is owned by SessionState beside the monitor
overlay and travels to every successor in SuccessorBase; each successor arms
its taps from it at preparation with a prepared arm whose window starts at the
first accumulated block, so a restarted owner keeps an acknowledged subscribe
(applied or still pending). A binding whose target is gone ends with a
reliable OBSERVATION_ENDED event (0x8011, revision and stable key),
room-checked with SESSION_COMMITTED before the commit; no room is
BACKPRESSURE and nothing changes. Same on both hosts; COMPLETE_SCHEMA_HASH and
two C ABI vectors re-pinned; no rendered bit moved.

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

The hop probe waits for two windows one hop apart on a 10 s performance.now()
deadline, yielding 5 ms between pumps, with a permanent planted 750 ms slow
start; a failure names what it saw. A new qualification row and four self-test
mutations hold the wait to its deadline and refuse a spin. The old 512-poll
loop fails the planted start in chromium and webkit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on (#1395a)

A spectrum capture whose target, channels, rate and quantum are unchanged
carries its observer through the swap (one-shot state included); the control
side pairs the readers at promotion from a list built at preparation
(SpectrumService::adopt_successor), never in the swap block, and returns the
displaced captures to drop off the audio thread. Allocation-free in the swap
block and at promotion. Collections are #1395b.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…key (root ruling A)

OBSERVATION_ENDED (0x8011) is now the sequence and one repeated key message per
tap the commit ended, strictly ascending, at least one. attach_observation_ended
reserves a single reliable slot beside SESSION_COMMITTED whatever the number of
keys and returns the event frame length, so BACKPRESSURE means only a lane
holding unread events; an event over one control frame is a resource refusal.
The commit moves the prepared event and allocates nothing. capi_resources charges
the event heap per observation slot and, as D6a missed, the observation slot
tables (40 bytes the capi retained-bytes tests found). COMPLETE_SCHEMA_HASH
re-pinned to 0xcc9a_7bcc_502c_4936 (#1584 D6a fold: one OBSERVATION_ENDED per
commit); the corpus row carries two keys. No rendered bit moved (pcm_digest
bf7dcedd1d92621f).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A collection's entries that keep their key, kind, rate and quantum carry by
key in any order (the selection follows its entry and keeps its epoch);
entries that do not carry start fresh; SpectrumService::adopt_successor pairs
the collection at promotion. Allocation-free in the swap block and at
promotion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
At promotion, a one-shot on a capture that does not carry (no successor at
its key, or a refused hand-over) starts the successor's capture Invalid with
the predecessor's generation, so one read reports CaptureInvalid and a new arm
completes on the successor; a stream on it ends (cadence, smoothing and
analysis cleared); a lost collection selection releases its arm. Carried
captures are untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bounded ended-event charge, fallible slots before commit, an over-frame refusal test, dead API, doc fixes (#1584, #1290, #1395)

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

Labels

None yet

Projects

None yet

1 participant