Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,67 @@ Checked against `228fca26c`.
browser's parity row on the floored latency. D4 and gate 3 still cover the C ABI only here.
- **Anchor.** The `restarted` field of `PlanStateInventory` is `prepare.rs:750` at `228fca26c`
(the batch-15 amendment says `:744`). On `main` at `c5b5c88b2` it is `:757`.

## Decision record: #1285 (batch 15b)

**What shipped.**
- D1: `GraphBuiltinsCompileRequest::input_floors` (`BTreeMap<GraphNodeId, LatencySamples>`,
empty by default). `pdc::timings` takes `max = max(latest incoming arrival, floor)` and
compensates every incoming edge to it. A floor that names no node of the graph is ignored, so the
successor's map can be the predecessor's arrivals as they are ("every node present in both
plans"). `GraphCompileReport::input_arrivals` reports each node's floored `max` where it is
nonzero, sorted by node ID (a missing node has arrival 0, and a 0 floor bounds nothing).
- D2: `PlanStateInventory` holds `input_floors` (from `input_arrivals`, so floored values) and
shows them through `input_floors()`. `retained_bytes` counts them. A preparation with a
`SuccessorBase` passes them as the request's floors. A first plan has none. Across a withdrawn
candidate, `across_withdrawn` clones P0's inventory, so B gets P0's floors (render runs P0).
The discontinuity case (#1323) is not built yet, so every successor floors for now.
- D3: no duck. A restarted strip starts at rest with no duck until #1324 lands, as the batch 15
amendment says.
- D4: `latency_samples` is the floored output arrival, because PDC's output arrival includes the
floor. No extra code was needed.
- D6: a header paragraph "Latency across a replacement (issue 1285)" and
`docs/C_ABI_V1_QUALIFICATION.md` § "Latency floors across a structural transaction (#1285)".
Both say the lower latency comes back with a fresh compile, or at #1323's discontinuity once it
exists. They do not claim a mechanism that is not built.
- Test support: `PlanStateInventory::test_only_floors_only` (feature `test-support`).
- Both hosts run this one core path (control-plane -> host-core). No host adapter changed.

**Departures.**
- Gates 1 and 2 mute `eq0` in A. A's blocks before the swap then equal B's, so the test can
compare "every block". An audible `eq0` in A differs from B before the swap by design.
- Gate 3's reference is the same `RemoveTrack` transaction committed before the first block. Its
successor is compiled with A's floors and adopts at frame 0 with all state at rest, so it is B
compiled with A's floors and rendered from frame 0. A test-support inventory reference needs
`host-core/test-support` in capi's dev-dependencies, and `crates/capi/Cargo.toml` is not an
authorized path.
- Mechanical neutral field (`input_floors: Default::default()`) in the 8 B15-R9 files and in the
authorized graph-compiler tests. `crates/builtins-compiler` is not touched.
- Fixtures: none regenerated. With no floors the compiled output does not change.

**Gates.**
- Gate 1: `host-core` `successor_swap::removing_a_limited_track_keeps_the_output_timing_at_every_rate_and_width`
(4 rates, both widths where built). Red when successors get no floors: latency 0 against 447 at
44.1 kHz.
- Gate 2: `...::removing_a_limited_track_keeps_the_submix_timing_at_every_rate_and_width`. Red
when floors go only to the output: it first differs at the swap block (6).
- Gate 3: `capi` `runtime::tests::removing_a_limited_track_keeps_the_plan_latency`. Red when
successors get no floors: latency 0 against 486.
- Gate 4: `cargo test -p graph-compiler` (fixture corpus) ok; `check-graph-determinism.sh` PASS
100/100; `cargo test -p console-workload` ok; `check-builtins-fixtures.sh` ok (50 files).
- Gate 5: `...::a_second_removal_keeps_the_first_floors`. Red when the inventory records the
natural `max`: C's latency is 0 against 486.
- Supersession (added): `...::a_replacement_across_a_withdrawn_candidate_keeps_the_running_floors`.
A adds two limiters (latency 972). B across withdrawn A must keep P0's latency (486). Red when
the base takes A's floors (972) or drops them (0).
- Unit: `graph-compiler` `tests::timing_compensates_every_incoming_edge_to_a_floor`.
- Gate 6 command: all suites ok. `check-capi-abi.sh` ok, `check-workspace-policy.sh` ok,
clippy (touched crates) ok, rustdoc ok, `realtime-policy` ok.

**Measured.** `audit capi`: allocations 0, deallocations 0, pcm_digest `4f52089e4a8cf83c`
(unchanged). No resource ceiling was re-pinned.

## Amendment (controls coordinator, batch 15b fold, 2026-10-16)

- The note above that the discontinuity case (#1323) is not built yet is superseded: #1323 shipped
it in the same batch (`SessionState::declare_discontinuity`, both hosts).
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ merged); `plan_swap_race` (gate 5, four tests); `resource_lifecycle` (gate 7);
- *Edits outside the original authorized paths.* `hosts/host-web/src/lib.rs` (`promote_spectrum`
only), `hosts/host-web/tests/structural_apply_realtime.rs` (the m-5 cost test),
`hosts/host-web/tests/control_parity.rs` (the guard's parity row),
`.github/ISSUE_SPECS/1698-carry-an-owner-or-source-a-superseding-candidate-keeps-unchanged-from-p0.md` (one line). No edit under
`.github/ISSUE_SPECS/1698-carry-an-owner-a-superseding-candidate-keeps-unchanged-from-p0.md` (one line). No edit under
`crates/engine/src/realtime/`.
- *Parity row's silent adoption block (harness, not engine).* A plain run of this session (no
edit) first outputs a non-zero sample at frame 459 at 48 kHz and 939 at 96 kHz (the plan's
Expand Down Expand Up @@ -607,3 +607,14 @@ merged); `plan_swap_race` (gate 5, four tests); `resource_lifecycle` (gate 7);
- *Task B-A (doc job).* Five private intra-doc links in `crates/host-core/src/source.rs`
(`donate_producers`, `donate_consumers`, `unfed`) are plain code spans;
`RUSTDOCFLAGS='-D warnings' cargo doc --locked --workspace --no-deps` passes.

## Amendment (#1698, batch 15b, coordinator ruling of 2026-10-16 confirmed by root)

D2's base across a withdrawn candidate is now P0's own inventory less the sources A did not carry
from P0 (`PlanStateInventory::across_withdrawn`), so B carries from P0 an owner it keeps unchanged
from P0, as a direct P0 -> B swap does. `ProviderEpoch::carried_base` and its capi epoch term are
gone (reference `capi_retained_bytes` 168,810 -> 167,514). The fold-3 guard
`drops_acked_fresh_source` stays for all four cases, (a) included: a source A added or redeclared
restarted the host's feed against A (`docs/CONTROL_PROTOCOL_SEMANTICS.md:23`), so B starts it
fresh and cannot read A's chunks into P0's continuing source. The guard's capi and parity rows stay.
See #1698's decision record.
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,43 @@ named command exists.

**Realtime-path edits.** None under `crates/engine/src/realtime`. The change is on the source
consumer's render path in `crates/source/src/lib.rs`.

## Decision record: #1318 (batch 15b)

**Shipped.** D1-D4 and D6 in `crates/source/src/lib.rs` and the one header clause.

- `begin_block_play`'s missing-PCM branch reads `held_seek` after the acquire step (the batch 15
precision of D1). Held: the missing frames go to new `held_frames` / `held_blocks` counters and
`underrun_*` stays untouched. Otherwise it is an underrun as before. `next_frame` still advances by
one quantum in both cases, so no rendered bit moves (D2).
- `SourceReadReport` gains `held_frames`, `held`, `cumulative_held_frames`, `cumulative_held_blocks`;
`SourceConsumerTelemetry` gains `held_frames`, `held_blocks` (D3). Held frames use the same
region-bounded count as underrun frames (a held block past the end is neither, unchanged).
- The graph driver needed no change: it folds only `report.underrun_event` and the vacant entry, so a
held block sets nothing (D4). `GraphObservationValidity` and `copy_after_disarm_telemetry` are
untouched (D5). The browser spectrum record and the C ABI read nothing new, so both hosts behave the
same: a held block stops marking spectrum windows as starved on both. The held counters reach hosts
with #1316 (C ABI) and #1537 (browser status), which are the non-goals here.
- Header: "and those blocks count as source underruns" deleted (`miso_engine_v1.h`, lines ~250-253,
rewrapped at 100 columns). #1317 owns the full replacement text.
- D6 facade doc (`crates/host-core/src/source.rs` `seek_at`) left to #1316 as the spec says.

**Departures.** None. The B15-R/B15-Q rulings the task text names are not in this spec file.

**Gates -> tests** (all in `crates/source/src/lib.rs`):

1. `a_wait_for_an_anchor_is_held_and_starvation_after_it_is_an_underrun` (both the delivered and the
undelivered case).
2. `queued_pcm_of_the_playing_generation_plays_while_a_seek_is_held`.
3. `graph_driver_does_not_flag_a_held_block_as_an_underrun` (held source plus a starved second source
in one block).
4. Updated, not deleted: `a_primed_block_waits_for_its_anchor` and
`a_seek_whose_pcm_arrives_inside_the_block_window_keeps_its_pcm` now assert held, not underrun.

**Red evidence** (temporary mutations, restored): classification forced to never-held turns five tests
red (the three new ones plus the two updated); the driver also OR-ing `report.held` turns only gate 3
red; deciding played-vs-missing by `held_seek` alone turns gate 2 and
`prepare_seek_holds_an_anchored_seek` red.

**Measured numbers.** None re-pinned. `TransferBlock` is unchanged; the consumer gains two `u64`
fields, with no resource-report row that counts them.
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,56 @@ audited render, so a defect on that path shows up as an audit violation, not as
- **Batch placement.** "#1310 ... is not in the batch-15/16 list" and "#1344, also open and not in
the list" (Dependencies on GitHub) are void. #1344 and #1310 are in batch 15a (with #1398), which
lands before this slice.

## Decision record: #1319 (batch 15b)

**Shipped.** D1-D4 whole, in the two authorized paths.

- `crates/capi/src/runtime/tests.rs`: `a_held_anchored_seek_crosses_a_plan_swap` (D1),
`a_queued_seek_on_a_persisting_source_survives_supersession` (D2) and
`a_held_seek_carries_from_a_taken_candidate` (D3), with helpers `submit_block_c`, `transact_c`
(returns the response's `EditPath`), `add_source_and_track`, `put_compressor` and
`render_fresh_reference`.
- `tools/audit/src/capi.rs`: right after the structural transaction (outside every render scope)
the audit calls `miso_engine_v1_source_seek_at(session, "fixture-source", 2, 512, 512)` and
submits one generation-2 quantum from frame 512; `submit_source_chunk` takes a generation. The
carried consumer pops the seek at the swap call (call 1), holds it for calls 2-3 and applies it at
call 4. The existing assertions are unchanged.

**Measured.** `audit capi`: allocations 0, all violation counters 0, `replacements == 1`,
`(carried, carry_mismatches) == (1, 0)`. `pcm_digest` moved `bf7dcedd1d92621f` -> `4f52089e4a8cf83c`
(call 4 now plays the generation-2 quantum instead of an underrun's silence; CI checks the digest
for format only). The JSON schema is unchanged; no resource row exists in this audit.

**Gates.** `cargo test --locked -p capi`: 151 lib tests and every integration test pass, among them
the three new tests. `cargo test --locked --release -p audit`: 33 pass. Clippy on `capi` and `audit`
(`--all-targets --all-features -D warnings`), `cargo fmt --all -- --check`, `RUSTDOCFLAGS='-D
warnings' cargo doc --locked --workspace --no-deps` and `scripts/check-workspace-policy.sh` pass.
The C header is untouched.

**Departures from the text.**

- D1 adds a muted track on the fixture source (`add_muted_track`), not a new track and source:
the spec's "keeps the source" is what the test needs, and the reference then compares with exact
zeros from the extra strip.
- D2 and D3 use inserts and a muted track that leave the reference comparable: D2 compares with
the twin, as the spec says (a fresh session's compressor state would differ from a swapped-in
one's); D3's T2 is a muted track, so its reference is a fresh session of T2's document.
- Both D2/D3 assert `EditPath::Rebuild` in each transaction's response (batch 15 check).
- The held counters (#1318) are consumer telemetry that the C ABI tests cannot reach (no
accessor from a plan or session), so the assertions are bit-identity and nonzero-reference, which
an unapplied or dropped seek breaks (the generation-2 blocks are discarded and the source is
silent from the anchor).
- D2 never feeds fresh sources, so the batch 15a guard (`drops_acked_fresh_source`) does not
arise: the fixture source persists from P0 and is never fresh in A. D3 withdraws nothing.

**Red evidence (argued, not run: sprint mode forbids mutation runs).** D1: a carry that resets or
rebuilds `held_seek` leaves the successor with no seek at block 8, so the queued generation-2
block is discarded as unobserved and blocks 8-15 are silent against a playing reference. D2: a
supersession that rebuilds the persisting ring (or clears its command slot) loses the seek or the
queued blocks the same way; the counters pin `(withdrawn, superseded, taken) == (1, 1, 0)`. D3:
preparing B across a taken A makes `s2` fresh in B, so its held seek and queued generation 2 are
lost and `s2` is silent from block 8; the counters pin `(withdrawn, taken) == (0, 0)`.

**Successor work.** Expose the consumer's held counters through a test accessor (or the counter
snapshot) so a C ABI test can assert `held_blocks` directly.
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,88 @@ through `submit_command` and `control_command`, C13's form with no new export. B

- **Batch placement.** "#1310 is open and not in batch 15/16" (Dependencies) is void. #1310, #1344
and #1398 are batch 15a, which lands before this slice (B15-R5).

## Decision record: #1323 (batch 15b)

**What shipped.**
- D1: `miso_engine_v1_declare_discontinuity(session)` in `crates/capi/src/ffi.rs`, a forwarder to
`control_plane::SessionState::declare_discontinuity`. `FEATURE_DECLARE_DISCONTINUITY = 1 << 8`
(the next free bit), and the mask ORs it. Header: the define, the prototype, the session thread
list, and a paragraph "Declared discontinuity (issue 1323)" with the host's duty. The #1285
paragraph now points to it. `scripts/check-capi-abi.sh` lists the symbol (18 frozen).
- B15-R7: `miso_engine_web_v1_declare_discontinuity(handle) -> u32` in `hosts/host-web/src/ffi.rs`,
through `AudioWorkletEngineHost::declare_discontinuity`, which runs the same control-plane
function. The host's `run_command` became `run_control`, so the plan a declaration publishes
keeps its tables as a rebuild's do. The #1293 D7 mirrors are updated and the SDK is regenerated.
- D2/D4 in `crates/control-plane/src/control.rs`. `rebuild` takes `Rebuild::Transaction(token)`
or `Rebuild::Discontinuity(Box<CompiledSession>)`. The discontinuity arm has no protocol commit
and returns no response. Its copy of the committed model is the kept base of the epoch it
displaces (the direct case), or it is dropped when the arm supersedes a candidate, as a commit's
displaced model is. It is always published `Next`, and the test adoption hook is not taken.
Supersession, the donation, the acked-PCM guard, observation forwarding, the `superseded` word
(`r - 1 - last_running`) and republish-on-refusal are the transaction path's own code.
- D3 in `crates/host-core/src/prepare.rs`. `SuccessorBase::discontinuity`, `SuccessorBase::new`
(with a private static neutral `EditRamps`), and every literal site moved to `new`. At
preparation, a discontinuity base swaps its inventory for `at_discontinuity()`: identity, rate,
quantum and source rows only, with no floors. Every join reads the base's inventory, so that one
substitution is the whole rule. The inventory records `raised_arrivals` (each raised node's
natural arrival, beside its floored one in `input_floors`), and
`runs_behind_natural_timing()` is D2's test.

**Rule for the supersession case (the spec is silent).** A transaction E that supersedes a
discontinuity successor D before render adopts it is prepared at the same discontinuity (sticky
`ProviderEpoch::discontinuity`). The combined twin P0 -> E is "declare, then edit" as one
transaction against the running plan, and render never played D. So E has no floors and carries
only sources. A declaration while D waits finds nothing to reset (D has no raised floor) and
publishes nothing. D over a withdrawn A carries A's revision. A's revisions complete at D's
adoption, `EXACT`, with `SUPERSEDED` for those A folded in.

**Departures.**
- `crates/graph-compiler/src/{pdc.rs,compile.rs,lib.rs}` (not authorized): D3 needs natural
arrivals in the inventory, and only PDC knows them. `GraphCompileReport::raised_arrivals` is a
report field only. Program, canonical bytes and fixtures are unchanged, and the unit test is
extended.
- Gate 5 and the supersession gate are in `crates/capi/src/runtime/tests.rs`, not in control-plane
unit tests. Those tests drive `control_plane::SessionState` directly. The control-plane crate has
no render or session-fixture harness, and its `tests/` directory is not authorized.
- Gate 4's error is the replacement-peak cap's `CompileRejected` (set with
`set_replacement_peak_limit`). The C ABI's limits give no per-preparation cap that fails only
the floorless successor.
- Mechanical edits outside the paths: `crates/capi/tests/c/{abi_smoke.c,header_smoke.cpp}` (the
bit inside the mask and the signature, replacing `MASK == 255`), `crates/host-core/tests/strip_meters.rs`
(a `SuccessorBase` site added after the batch check), `ProviderEpoch::test_inventory`, and the capi
test wrappers `test_declare_discontinuity` and `test_plan_latency`.
- `hosts/host-web/tests/control_parity.rs`: a parity row, at the coordinator's request.
- Header: the #1310 paragraph is re-wrapped to 100 columns (one line was 135).
- No browser latency report exists yet (#1688). So the browser gate reads the plan latency through
the host's `queries`, and the parity row compares bits and the C ABI latency.

**Gates.**
- Gate 1: `host-core` `successor_swap::a_declared_discontinuity_resets_the_floors_at_every_rate_and_width`.
Red when the floors are kept (447 against 0 at 44.1 kHz) and when all state is carried (block 0
differs).
- Gate 2: `capi` `runtime::tests::a_declared_discontinuity_reports_the_natural_latency_on_the_c_abi`.
Red when nothing is published. Browser:
`host-web` `tests::structural_apply::a_declared_discontinuity_resets_the_floors_on_the_browser`,
red when the host does not forward. Parity:
`control_parity::a_declared_discontinuity_resets_the_floors_alike_on_both_hosts` (48 and
96 kHz), red at block 8 when the browser does not forward.
- Gate 3: `...::a_declaration_with_nothing_to_reset_publishes_nothing` (revision 0 and after an
added track). Red when the declaration always rebuilds.
- Gate 4: `...::a_refused_declaration_changes_nothing`. Red when the error is swallowed. The retry
under the lifted cap resets the floors.
- Gate 5: `...::a_declared_stop_supersedes_a_pending_warm_successor_alone_and_folded`. Red when
the successor is published `Primed`, and when the pending candidate is left alone.
- Supersession: `...::an_edit_that_supersedes_a_discontinuity_successor_is_prepared_at_it`. Red
without the sticky rule.
- Gate 6: `abi::tests::masks_and_result_codes_are_frozen` (the OR of the symbols) and
`ffi::tests::version_and_capabilities_are_exact`.
- Gate 7 command: all suites pass. `host-web` (test-support) and `graph-compiler` suites pass.
`check-capi-abi.sh` and `--self-test` pass. `check-workspace-policy.sh` passes. `fmt` passes.
Clippy passes on the touched crates. Rustdoc passes. `realtime-policy` passes.
`check-sdk-generated.sh` passes. `check-abi-layout-v1.py` and `--self-test` pass.
`check-web-audioworklet.sh` passes.

**Measured.** `audit capi`: allocations 0, deallocations 0, syscalls 0, pcm_digest
`4f52089e4a8cf83c` (unchanged; `tools/audit` not touched). C ABI gate 2: latency 486 -> 0 at
48 kHz. No resource ceiling was re-pinned.
Loading
Loading