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
@@ -0,0 +1,93 @@
# Close the gate bypass and browser plain-seek test gaps

Owner-approved, relayed by root on 2026-10-18, from the test audit (mutation pilot, stage 1).
Filed by agent G. Test-only: no product code changes. Code anchors at `81a3074fc`.

## Product outcome

Two tests that the mutation pilot found weak now assert what their names claim:

1. The gate/expander bypass test renders an input the gate acts on, so a bypass that does not
reach the kernel turns it red.
2. A host-web test checks the host's bookkeeping of the successor a plain session seek publishes
(root ruling B16-R9), which audio alone cannot show.

## Context

- Gap 1 (pilot rows s1o-m1, s1o-m2). `contract::bypass_is_exact_identity_at_all_rates`
(`crates/gate-expander/tests/contract.rs`) rendered 3 samples at default values. Preparation
seeds every lane open with the full hold (100 ms at defaults), so the gain stays 0 dB and the
output is dry through the `gain_db == 0` branch whatever the bypass word says. Mutants: the
kernel ignores the bypass word (`kernel.rs:255`, s1o-m1) and the prepared gate drops the bypass
flag (`lib.rs:473`, s1o-m2) left it green. s1o-m3 (bypass mutes) was already red.
- Gap 2 (pilot row s1e-m2). `hosts/host-web/src/lib.rs:1721`: the plain form of
`ReadyHost::session_seek` runs through `run_control`. A mutant that sends it straight to the
control plane leaves the successor with no `PendingShape`, does not count it in
`blocking_rebuilds`, skips the table-room check and does not name the successor's meter header.
The first command submission before the swap then fails the host
(`web.internal.pending_shape`). Audio and latency are unchanged, so no host-web test saw it
(`control_parity::a_plain_session_seek_declares_and_an_anchored_one_does_not_alike_on_both_hosts`
checks result verdicts, bits and latency, not the host's table bookkeeping).

## Decisions

- D1. Gap 1 rewrites the old test in place (it supersedes it): the active values (`T = -20`,
ratio 20, range 48 dB, hold 0 ms) over a 4,800-frame -60 dBFS plateau with a signed zero at
frame 0, both channels. It asserts the unbypassed render is not dry (no vacuous pass) and the
bypassed render is bit-exact dry, at 44.1, 48, 88.2 and 96 kHz.
- D2. Gap 2 adds `tests::structural_apply::a_plain_session_seek_keeps_and_counts_its_successor_on_the_browser`
beside `a_declared_discontinuity_resets_the_floors_on_the_browser`, from the same #1285 floors.
It mirrors the C ABI's `runtime::session_seek_declares::an_accepted_plain_session_seek_publishes_one_successor_and_applies`
(`crates/capi/src/runtime/tests.rs`): both the swapped and the still-waiting removal.
Assertions: one successor epoch; `blocking_rebuilds` up by one; exactly one unadopted
`PendingShape`, at the successor's epoch (no superseded candidate's left); the rendering plan
names that entry's meter header; `submit_commands(0)` is `RESULT_OK` and the host stays ready;
the next block adopts the successor.
- D3. The table-room backpressure branch is not tested. `PENDING_SHAPES` is 4, `run_control`
reclaims adopted entries first and drops superseded ones, and the core holds at most one
waiting candidate, so no public call sequence fills the table before a plain seek. The branch
is defensive; no test is added for it.
- D4. host-core has no host-side table bookkeeping to mirror. The C ABI has the equivalent
publication test (D2); it keeps no tables, so it needs nothing more.

## Authorized paths

- `crates/gate-expander/tests/contract.rs`
- `hosts/host-web/src/tests.rs`
- this spec

## Objective gates

- `cargo fmt --all --check`
- `cargo clippy --locked -p gate-expander --all-targets --all-features -- -D warnings`, and the same
for `-p host-web`
- `cargo test --locked --all-features -p gate-expander`, and the same for `-p host-web`
- `scripts/check-workspace-policy.sh`

## Evidence (agent G, 2026-10-18)

Mutants are the pilot's exact old/new texts, applied one at a time with a revert-on-exit driver;
the tree was clean after each.

| Test | Branch | s1o-m1 | s1o-m2 | s1o-m3 | s1e-m2 |
|---|---|---|---|---|---|
| gate-expander `contract::bypass_is_exact_identity_at_all_rates` | green | red | red | red | - |
| host-web `tests::structural_apply::a_plain_session_seek_keeps_and_counts_its_successor_on_the_browser` | green | - | - | - | red |

- Gap 1: all three mutants fail at the bypassed-left bit comparison.
- Gap 2: the mutant fails first at the `blocking_rebuilds` assertion. With that assertion and the
table checks removed in a scratch probe, the mutant fails at the `submit_commands(0)`
assertion, so the submission check is red on its own.

## Test value

- Gap 1: red if bypass does not reach the gate kernel (the kernel ignores the bypass word, or
preparation drops the flag) while the gate is acting, which no other gate-expander test caught
in the pilot (only rack's `bypass_shunt_identity` tests caught the dropped flag).
- Gap 2: red if the browser's plain session seek publishes its successor without the host's plan
bookkeeping, so the successor is uncounted, unnamed and has no tables and the next submission
before its swap fails the host, which no host-web test catches because the audio is unchanged.

## Dependencies

None.
58 changes: 51 additions & 7 deletions crates/gate-expander/tests/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,19 +217,63 @@ fn missing_and_misordered_initial_records_return_exact_errors() {
assert_eq!(error.code, "effect.parameter.initial");
}

/// Bypass is exact dry identity on an input that the gate acts on.
///
/// At the active values (`T = -20`, `rho = 20`, range 48 dB, hold 0 ms) a -60 dBFS plateau that
/// lasts 4,800 frames closes the gate onto the range floor, so the unbypassed output is not dry.
/// The same input, bypassed, must come back bit-exact dry (signed zero included) at every launch
/// rate. The unbypassed render is asserted not dry, so the bypass check cannot pass vacuously:
/// at default values the prepared lane is open with the full hold and every sample is dry
/// whatever the bypass word says.
///
/// Red mutations: the kernel ignores the bypass word (`kernel.rs`, the identity mask), or the
/// prepared gate drops the bypass flag (`lib.rs`, `bypass: L::splat(0.0)`).
#[test]
fn bypass_is_exact_identity_at_all_rates() {
const FRAMES: usize = 4_800;
const QUIET: f32 = 1.0 / 1_024.0;
let mut left_input: Vec<f32> = (0..FRAMES)
.map(|frame| if (frame / 8) % 2 == 0 { QUIET } else { -QUIET })
.collect();
left_input[0] = f32::from_bits(0x8000_0000);
let mut right_input: Vec<f32> = left_input.iter().map(|value| -value).collect();
right_input[0] = f32::from_bits(0x8000_0000);
let bits = |samples: &[f32]| {
samples
.iter()
.map(|value| value.to_bits())
.collect::<Vec<_>>()
};
for rate in [44_100, 48_000, 88_200, 96_000] {
let values = initial_values();
let values = support::active_values();

let mut effect = prepare(request_at_rate(&values, rate));
let mut left = left_input.clone();
let mut right = right_input.clone();
render_scalar(&mut effect, &mut left, &mut right, 128);
assert_ne!(
bits(&left),
bits(&left_input),
"unbypassed left must act at {rate} Hz"
);
assert_ne!(
bits(&right),
bits(&right_input),
"unbypassed right must act at {rate} Hz"
);

let mut request = request_at_rate(&values, rate);
request.bypass = true;
let mut effect = GateExpanderFactory.prepare(request).expect("prepare");
let mut left = [f32::from_bits(0x8000_0000), 0.25, -0.5];
let mut right = [-0.0, -0.25, 0.5];
let expected = (left, right);
render_scalar(&mut effect, &mut left, &mut right, 3);
assert_eq!(left, expected.0);
assert_eq!(right, expected.1);
let mut left = left_input.clone();
let mut right = right_input.clone();
render_scalar(&mut effect, &mut left, &mut right, 128);
assert_eq!(bits(&left), bits(&left_input), "bypassed left at {rate} Hz");
assert_eq!(
bits(&right),
bits(&right_input),
"bypassed right at {rate} Hz"
);
assert_eq!(effect.metadata.latency, LatencySamples(0));
}
}
Expand Down
131 changes: 131 additions & 0 deletions hosts/host-web/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7602,6 +7602,137 @@ mod structural_apply {
assert_eq!(rig.host.service(None), RESULT_OK);
}

/// Root ruling B16-R9's bookkeeping on the browser: a plain session seek declares the
/// discontinuity in the same call, so the host keeps and counts the successor it publishes as
/// it keeps a declaration's. From #1285's floors (a muted track behind a true-peak limiter
/// removed), swapped or still waiting for its block, the plain seek publishes one successor;
/// the host holds that plan's tables for its adoption block, counts one blocking rebuild,
/// names the successor's meter header from the rendering plan, and admits a command
/// submission against it before the swap. The next block adopts it and the host stays ready.
/// Mirrors the C ABI's `an_accepted_plain_session_seek_publishes_one_successor_and_applies`.
///
/// Test value: red if the browser's plain seek reaches the control plane without the host's
/// plan bookkeeping (`run_control`): the audio and the latency are the same, but the
/// successor has no tables, is not counted and is not named, and the first submission before
/// its swap fails the host (`web.internal.pending_shape`).
#[test]
fn a_plain_session_seek_keeps_and_counts_its_successor_on_the_browser() {
for swap in [true, false] {
let mut model = gate_model();
let limited = &mut model.tracks[0];
limited.fader.left_mute = true;
limited.fader.right_mute = true;
limited.inserts.effects.push(session::Effect {
id: StableId::parse("lim").expect("insert ID"),
identity: session::EffectIdentity::Native {
effect_id: StableId::parse("miso.true-peak-limiter").expect("limiter id"),
},
quality: session::EffectQuality::Normal,
bypass: false,
link_mode: session::LinkMode::Maximum,
params: Vec::new(),
sidechain: session::SidechainDeclaration::None,
});
let edits = [
SessionEdit::RemoveRoute {
route_id: model.routes[0].id.clone(),
},
SessionEdit::RemoveTrack {
track_id: model.tracks[0].id.clone(),
},
];
let mut rig = Rig::boot(&model, options(), None);
for _ in 0..APPLY_BEFORE {
rig.step();
}
assert!(matches!(
rig.apply(&edits, 1),
Outcome::Applied {
path: EditPath::Rebuild,
..
}
));
if swap {
for _ in 0..4 {
rig.step();
}
assert!(!rig.pending(), "swap {swap}: the removal was adopted");
} else {
assert!(
rig.pending(),
"swap {swap}: the removal waits for its block"
);
}
let counted = rig.host.blocking_rebuilds();
let newest = |rig: &Rig| {
rig.host
.ready
.as_ref()
.expect("ready")
.control
.newest_plan_epoch()
};
let before = newest(&rig);

const FRAME: u64 = 1_280;
assert_eq!(rig.host.session_seek(None, 2, FRAME, None), RESULT_OK);
let successor = newest(&rig);
assert_eq!(successor, before + 1, "swap {swap}: one successor");
assert_eq!(
rig.host.blocking_rebuilds(),
counted + 1,
"swap {swap}: the successor is counted"
);
{
let ready = rig.host.ready.as_ref().expect("ready");
let waiting: Vec<_> = ready
.pending_shapes
.iter()
.filter(|entry| !entry.adopted)
.collect();
assert_eq!(
waiting.len(),
1,
"swap {swap}: the successor's tables, and no superseded candidate's"
);
assert_eq!(waiting[0].epoch, successor, "swap {swap}");
assert_eq!(
rig.host.meter_successor_address(),
core::ptr::from_ref(&waiting[0].shape.meter_header).addr(),
"swap {swap}: the rendering plan names the successor's header"
);
}
assert_eq!(
rig.host.submit_commands(0),
RESULT_OK,
"swap {swap}: a submission before the swap is admitted on the successor"
);
assert_eq!(rig.host.status().state, STATE_READY, "swap {swap}");

let quantum = u64::from(QUANTUM);
let lane = |lane: u64| {
(FRAME..FRAME + quantum)
.map(|frame| strip_sample(0, lane, frame))
.collect::<Vec<f32>>()
};
let planes = [lane(0), lane(1)];
let views: [&[f32]; 2] = [&planes[0], &planes[1]];
assert_eq!(
rig.host
.submit_source(SOURCE, 2, FRAME, 48_000, &views, QUANTUM, false),
RESULT_OK
);
assert_eq!(rig.host.render_next(), RESULT_OK, "swap {swap}");
assert_eq!(
rig.epoch(),
successor,
"swap {swap}: the next block adopts it"
);
assert_eq!(rig.host.status().state, STATE_READY, "swap {swap}");
assert_eq!(rig.host.service(None), RESULT_OK);
}
}

/// #1310 on the browser path, which runs the same control plane: a structural edit while a
/// candidate waits unadopted commits and supersedes it. The first candidate never renders; the
/// replacement carries both transactions and is adopted at the next block.
Expand Down
Loading