From f8b947995804255dfae1bdd44558bfde4cf1889d Mon Sep 17 00:00:00 2001 From: BL Date: Tue, 20 Oct 2026 10:17:16 +0000 Subject: [PATCH 1/8] TA5: delete 131 measured-redundant tests of the batch 2 sample Batch 2 (B2) measured 162 of 200 sampled KEEP tests as mutation-redundant at 81a3074fc. Each was read against the AGENTS.md test-value question; 31 are kept by reading (named edge cases, recorded sole catchers, ABI and wire rules, allocation, count and ceiling claims, a randomized differential), 131 are deleted. Helpers deleted only after clippy reported them unused; two test files left with no test are deleted. MUTATIONS.md rows and open specs that name a deleted test say so in place. Co-Authored-By: Claude Opus 5.5 --- ...1339-give-the-delay-a-live-bypass-shunt.md | 4 +- ...ultiband-compressor-a-live-bypass-shunt.md | 4 +- ...with-one-protocol-command-on-every-host.md | 2 +- ...moves-exactly-into-a-six-word-successor.md | 2 +- crates/automation/src/events.rs | 31 - crates/builtins/tests/MUTATIONS.md | 10 +- crates/builtins/tests/contract.rs | 76 -- crates/builtins/tests/fader_ramp.rs | 67 -- crates/builtins/tests/filter_liveness.rs | 58 -- crates/builtins/tests/stage.rs | 127 ---- crates/builtins/tests/tail_contract.rs | 83 --- crates/capi/src/runtime/live_tests.rs | 478 ------------- crates/capi/src/runtime/supersede_tests.rs | 105 --- crates/capi/src/runtime/tests.rs | 56 -- crates/compressor/tests/MUTATIONS.md | 14 +- crates/compressor/tests/knee_overflow.rs | 34 +- crates/compressor/tests/mono_collapse.rs | 88 --- crates/compressor/tests/padding.rs | 135 ---- crates/controls/tests/live_lanes.rs | 45 +- crates/controls/tests/monitor.rs | 74 +- crates/controls/tests/row_descriptor.rs | 11 - crates/delay/tests/randomized.rs | 13 - crates/dsp-reference/src/svf.rs | 17 - crates/effect-compiler/src/prepare.rs | 11 - .../tests/parameter_lattice.rs | 29 - crates/effect-contract/src/symmetry.rs | 29 - crates/effect-contract/tests/lattice.rs | 19 - crates/effect-runtime/tests/MUTATIONS.md | 7 + crates/effect-runtime/tests/dynamics.rs | 18 - crates/effect-runtime/tests/partition.rs | 5 - crates/effect-runtime/tests/state_payload.rs | 64 +- .../effect-runtime/tests/stationary_hoist.rs | 19 - crates/engine/src/realtime/latest_cell.rs | 41 -- crates/engine/src/realtime/mod.rs | 26 - crates/engine/src/realtime/spsc.rs | 115 +-- crates/graph-compiler/src/lib.rs | 309 +------- crates/graph-compiler/tests/bypass_cohorts.rs | 52 -- .../tests/bypass_shunt_identity.rs | 240 ------- crates/graph/src/program/tests.rs | 27 - crates/graph/src/runtime.rs | 676 +----------------- crates/graph/tests/MUTATIONS.md | 15 +- crates/host-core/src/control_provider.rs | 109 --- crates/host-core/src/prepare.rs | 65 -- crates/host-core/src/spectrum.rs | 51 -- crates/host-core/tests/MUTATIONS.md | 11 +- crates/host-core/tests/browser_successor.rs | 40 -- crates/host-core/tests/collapse_arming.rs | 9 - .../tests/input_liveness_live_controls.rs | 121 ---- crates/host-core/tests/live_delta.rs | 531 -------------- crates/host-core/tests/route_mute.rs | 22 - crates/host-core/tests/spectrum.rs | 80 --- .../tests/stored_fader_automation.rs | 66 -- crates/host-core/tests/strip_handles.rs | 151 ---- crates/host-core/tests/successor_swap.rs | 180 ----- crates/host-core/tests/symmetry_witness.rs | 42 +- crates/host-core/tests/timeline.rs | 25 - crates/host-core/tests/withdrawn_successor.rs | 48 -- crates/lane/tests/MUTATIONS.md | 8 +- crates/lane/tests/f64_lane.rs | 90 --- crates/lane/tests/fader_matrix.rs | 214 +----- crates/lane/tests/fp_env.rs | 32 - crates/lane/tests/input_chain_arming.rs | 46 -- crates/math/tests/f1_fast_db_bounds.rs | 18 - crates/math/tests/m2_lane_identity.rs | 44 -- crates/multiband-compressor/src/shim.rs | 17 - .../multiband-compressor/tests/MUTATIONS.md | 6 + .../multiband-compressor/tests/crossover.rs | 110 --- crates/multiband-compressor/tests/product.rs | 53 -- crates/parametric-eq/src/lib.rs | 158 ---- crates/parametric-eq/tests/MUTATIONS.md | 11 +- crates/parametric-eq/tests/carry.rs | 101 --- crates/parametric-eq/tests/contract.rs | 63 -- crates/parametric-eq/tests/exact_rest.rs | 77 -- crates/parametric-eq/tests/response.rs | 87 --- crates/protocol/src/btlv.rs | 45 -- crates/protocol/src/controller/tests.rs | 149 ---- crates/protocol/src/model.rs | 46 +- .../protocol/tests/console_session_edits.rs | 91 +-- .../tests/session_edit_builtins_rack.rs | 23 - crates/rack/src/lib.rs | 288 -------- crates/rack/tests/MUTATIONS.md | 10 +- crates/rack/tests/live_control_bank.rs | 84 +-- crates/session/tests/control_smoothing.rs | 24 +- crates/session/tests/json_grammar.rs | 14 - crates/soft-clip/src/padding_tests.rs | 61 -- crates/soft-clip/tests/contract.rs | 69 +- crates/source/src/lib.rs | 118 --- crates/transient-shaper/tests/MUTATIONS.md | 7 + crates/transient-shaper/tests/bank.rs | 64 +- crates/transient-shaper/tests/contract.rs | 45 -- crates/transient-shaper/tests/partition.rs | 127 ---- crates/true-peak-limiter/src/lib.rs | 73 +- crates/true-peak-limiter/tests/MUTATIONS.md | 15 +- hosts/host-web/src/ffi.rs | 12 - hosts/host-web/src/tests.rs | 189 ----- hosts/host-web/tests/control_parity.rs | 150 +--- tools/bench-support/src/json.rs | 6 - tools/console-workload/tests/automation.rs | 83 +-- tools/console-workload/tests/paired_spans.rs | 5 - tools/realtime-policy/tests/policy/calls.rs | 40 -- .../tests/policy/drain/forms.rs | 35 +- .../tests/policy/drain/twins.rs | 19 - tools/realtime-policy/tests/policy/markers.rs | 23 - .../tests/policy/source_reads.rs | 23 - .../realtime-policy/tests/policy/unmarked.rs | 10 - .../realtime-policy/tests/policy/web_host.rs | 35 - .../tests/policy/whole_plan.rs | 7 - 107 files changed, 129 insertions(+), 7783 deletions(-) delete mode 100644 crates/host-core/tests/strip_handles.rs delete mode 100644 crates/transient-shaper/tests/partition.rs diff --git a/.github/ISSUE_SPECS/1339-give-the-delay-a-live-bypass-shunt.md b/.github/ISSUE_SPECS/1339-give-the-delay-a-live-bypass-shunt.md index 862ee986f..ba0b1b2b2 100644 --- a/.github/ISSUE_SPECS/1339-give-the-delay-a-live-bypass-shunt.md +++ b/.github/ISSUE_SPECS/1339-give-the-delay-a-live-bypass-shunt.md @@ -37,7 +37,7 @@ the lift is heard with the tail already in place. carrying non-finite or runaway values downstream. A bypassed delay's output is that dry block, which is already in range. Zeroing it is a dropout of a valid signal, caused by wet state nobody hears. The test `the_delay_keeps_its_prepared_bypass_because_a_shunt_would_move_a_bit` - (`crates/graph-compiler/tests/bypass_shunt_identity.rs:798-858`) pins exactly that dropout: a + (`crates/graph-compiler/tests/bypass_shunt_identity.rs:798-858`; deleted by TA5) pinned exactly that dropout: a 1 ms delay at `0.95` feedback fed `9e29` trips the ring check, and the prepared bypass zeroes the block. So this correctness reason does not hold, and decision 15 D15-13 E4 gives the delay a live shunt. @@ -173,7 +173,7 @@ the lift is heard with the tail already in place. block equals a reference that runs A and lifts the delay's bypass live at block 7. #1282's gate-3 delay leg is deleted, and its multiband leg stays. 7. **Deleted, superseded:** `the_delay_keeps_its_prepared_bypass_because_a_shunt_would_move_a_bit` - (`bypass_shunt_identity.rs:798-858`), whose claim gate 1 now reverses. Update the red-mutation + (`bypass_shunt_identity.rs:798-858`; already deleted by TA5), whose claim gate 1 now reverses. Update the red-mutation doc of `the_never_banked_list_is_exactly_the_launch_effects_that_decline_a_bank` (`:754-760`). 8. **Click against the mute baseline** (root, 2026-10-05, from #1055). The bypass crossfade's click is measured against #1055's mute baseline, at the session's `muteMs` default, diff --git a/.github/ISSUE_SPECS/1340-give-the-multiband-compressor-a-live-bypass-shunt.md b/.github/ISSUE_SPECS/1340-give-the-multiband-compressor-a-live-bypass-shunt.md index 13a9a075f..1aef1ae24 100644 --- a/.github/ISSUE_SPECS/1340-give-the-multiband-compressor-a-live-bypass-shunt.md +++ b/.github/ISSUE_SPECS/1340-give-the-multiband-compressor-a-live-bypass-shunt.md @@ -44,7 +44,7 @@ bits. This closes decision 14's F4 for the multiband compressor. - **Tests that pin today's behaviour.** - `crates/graph-compiler/tests/bypass_cohorts.rs:716` (`a_mixed_bypass_multiband_cohort_keeps_its_prepared_bypass`) and `:794` (`a_hot_bypassed_multiband_lane_leaves_its_neighbours_bits_unchanged`, whose - positive control is the whole-bank recovery under #1087's lowering). + positive control is the whole-bank recovery under #1087's lowering; deleted by TA5). - `crates/effect-compiler/tests/native_session.rs:516-528` asserts the multiband's prepared bypass and the list. - `crates/multiband-compressor/tests/nonfinite.rs:168` (`the_boundary_is_the_shared_limit_and_a_bank_shares_its_reset`) @@ -163,7 +163,7 @@ bits. This closes decision 14's F4 for the multiband compressor. `TRACKS / lanes()` banks; the render is bit-identical to today's prepared-bypass lowering (`todays_lowering`) and to `Backend::Scalar` at music levels. 3. **A hot bypassed lane in a bound bank.** `a_hot_bypassed_multiband_lane_leaves_its_neighbours_bits_unchanged` - keeps its name and assertion (zero neighbour taps moved), now with the hot lane inside a bound + (deleted by TA5, so this gate writes it again) keeps its name and assertion (zero neighbour taps moved), now with the hot lane inside a bound mixed bank (assert the bank binds). Its in-test positive control under #1087's lowering is removed, because the lowering is now the production path; D1's red mutation replaces it. 4. **Lowering.** `native_session.rs` asserts that the bypassed multiband entry is lowered diff --git a/.github/ISSUE_SPECS/1586-read-observation-windows-with-one-protocol-command-on-every-host.md b/.github/ISSUE_SPECS/1586-read-observation-windows-with-one-protocol-command-on-every-host.md index 705824d3b..e25cd1aea 100644 --- a/.github/ISSUE_SPECS/1586-read-observation-windows-with-one-protocol-command-on-every-host.md +++ b/.github/ISSUE_SPECS/1586-read-observation-windows-with-one-protocol-command-on-every-host.md @@ -134,7 +134,7 @@ Commands: `cargo test --locked -p protocol --all-features`, `cargo test --locked no row; 257 selections are refused by the codec. 4. **Browser bytes unchanged.** `selected_observation_reads_are_bounded_stable_and_non_consuming` (`hosts/host-web/src/tests.rs:6318`), `selected_observation_reads_keep_same_track_owner_windows_and_sequences` - (`:6460`) and `selected_observation_numeric_read_refuses_failed_host_without_touching_output` + (`:6460`, deleted by TA5) and `selected_observation_numeric_read_refuses_failed_host_without_touching_output` (`:6550`) pass unchanged. 5. **Same rows on both hosts.** A native test boots the same session on both hosts, arms the same tap (C ABI by `Observe`, browser by kind 7), renders the same PCM, and gets equal status, diff --git a/.github/ISSUE_SPECS/1713-carry-a-following-routes-column-moves-exactly-into-a-six-word-successor.md b/.github/ISSUE_SPECS/1713-carry-a-following-routes-column-moves-exactly-into-a-six-word-successor.md index d50d5102d..8602e6d22 100644 --- a/.github/ISSUE_SPECS/1713-carry-a-following-routes-column-moves-exactly-into-a-six-word-successor.md +++ b/.github/ISSUE_SPECS/1713-carry-a-following-routes-column-moves-exactly-into-a-six-word-successor.md @@ -54,7 +54,7 @@ six-word. Costs and limits: The conservative flag: `PlanStateInventory::carries_a_following_route_into_a_six_word_route`, its field and its join line, the control plane's `follow_into_six_word` term, and the gates -`a_rebuild_that_drops_a_following_route_to_six_words_reports_a_fallback` (capi), +`a_rebuild_that_drops_a_following_route_to_six_words_reports_a_fallback` (capi; deleted by TA5), `a_rebuild_that_drops_a_following_route_to_six_words_reports_the_fallback_alike` (host-web) and `the_join_flags_a_following_route_carried_into_a_six_word_one` (host-core), replaced by gate 3. diff --git a/crates/automation/src/events.rs b/crates/automation/src/events.rs index fafa384cc..f9e600f6a 100644 --- a/crates/automation/src/events.rs +++ b/crates/automation/src/events.rs @@ -897,35 +897,4 @@ mod tests { .map(|event| (event.offset, event.kind)) .collect() } - - /// #1646 D2: a cell seeded with its prepared value emits nothing on a flat curve at that - /// value, while a fresh cell retargets at its first grid point; a seed at another value still - /// retargets. - /// - /// Red if `seed_target` does not set the target: the first grid event of every prepared lane - /// would then retarget the lane to the value it already holds. - #[test] - fn a_seeded_target_suppresses_the_first_retarget_to_the_same_bits() { - let program = flat(-7.25); - assert_eq!( - first_block(&program, &mut CellState::new()), - [(0, EventKind::Grid)] - ); - - let mut seeded = CellState::new(); - seeded.seed_target(-7.25); - assert_eq!( - seeded.target().map(f32::to_bits), - Some((-7.25_f32).to_bits()) - ); - assert_eq!(first_block(&program, &mut seeded), []); - - let mut other = CellState::new(); - other.seed_target(-7.0); - assert_eq!(first_block(&program, &mut other), [(0, EventKind::Grid)]); - assert_eq!( - other.target().map(f32::to_bits), - Some((-7.25_f32).to_bits()) - ); - } } diff --git a/crates/builtins/tests/MUTATIONS.md b/crates/builtins/tests/MUTATIONS.md index 5ce51eaa5..84ec793dd 100644 --- a/crates/builtins/tests/MUTATIONS.md +++ b/crates/builtins/tests/MUTATIONS.md @@ -12,7 +12,7 @@ Command form: `cargo test -p builtins --test -- --exact`. | M1 | `let denominator = 1.0 + t1;` → `1.0 - t1` | `builtins/src/lib.rs` (`SvfSection::design`) | T1 `prepared_sections_match_reference_coefficients` | FAILED, `rate=44100, cutoff=10, high_pass=true` | | M2 | drop the D7 flush of `s1`, joint arm included: `self.s1 = if rest { 0.0 } else { flush(n1) }` → `self.s1 = n1` (#1328; before it, `self.s1 = flush(n1)` → `n1`, red at `index=3670`). Dropping only the per-word arm (`else { n1 }`) stays green: this signal never leaves `n1` below `FLUSH_EPS` beside an `n2` at or above `REST_EPS` | `dsp-reference/src/tpt.rs` | T2 `scalar_stage_is_bit_identical_to_reference_recurrence` | FAILED, `rate=44100, signal=1, index=2645` (re-run for #1328) | | M2b | drop only the per-word arm of the D7 flush (#1328): `self.s1 = if rest { 0.0 } else { flush(n1) }` → `else { n1 }`, and separately the same for `s2` | `dsp-reference/src/tpt.rs` | T2b `scalar_stage_is_the_reference_recurrence_where_the_per_word_flush_fires` (T2 stays green on both: its signals reach rest through the joint arm) | FAILED, `rate=44100, index=257` (`s1`) and `rate=44100, index=276` (`s2`) | -| M3a | every bank lane takes lane 0's coefficients (`zip(sections)` → `sections[0]`) | `builtins/src/lib.rs` (`svf_coef`) | T3 `bank_is_bit_identical_to_scalar_stage_at_every_width` | FAILED, `width=4, members=3, lane=1, frame=0` | +| M3a | every bank lane takes lane 0's coefficients (`zip(sections)` → `sections[0]`) | `builtins/src/lib.rs` (`svf_coef`) | T3 `bank_is_bit_identical_to_scalar_stage_at_every_width` (deleted by TA5) | FAILED, `width=4, members=3, lane=1, frame=0` | | M3c | report counters sum every lane, not only the members (`take(self.members)` → `take(L::WIDTH)`) | `builtins/src/lib.rs` (`InputStage::members_sum`) | T3, padding-lane arm | FAILED, `width=4, members=1` | | M4 | high-pass mix `m1 = -k` → `-k * 1.001` | `builtins/src/lib.rs` (`SvfSection::design`) | T1, T2 **and** the determinism corpus | all three FAILED | | M5 | the fixture oracle drops the identity-section `-0.0` mapping | `tools/audit/src/fixture_builtins.rs` | `issue064_checked_corpus_is_read_only_complete_and_has_no_authoring_reachability` | FAILED at the PCM semantics check | @@ -200,7 +200,7 @@ what the rows below record. | # | mutation | site | suite | verdict | |---|---|---|---|---| | C-1 | the retarget writer `set_trim_signed` drops its refresh | `builtins/src/lib.rs` | `input_liveness_mono` (3 tests) | RED — `the cached channel-symmetry flag is stale for lane 0`, on `an_asymmetric_retarget_declines_the_lane_on_the_admitting_block`, `re_equalising_the_words_restores_the_designed_term_and_nothing_more` and `a_retarget_between_a_collapsed_block_and_the_disengage_survives_the_copy`. This is the drain's own invalidation, and it is the one a live-words cache exists to have | -| C-2 | the **dual** ramping arm of `process` drops its refresh | `builtins/src/lib.rs` | `host-core::input_liveness_live_controls` (2 tests) | RED — `stale for lane 2`, on `re_equalising_after_a_disengaging_drain_holds_the_never_collapsed_bits` and `the_collapse_census_follows_the_commands`. A ramp advances the compared words on every block it runs, with no record drained: a drain-only invalidation is **not** sufficient, and this row is the proof | +| C-2 | the **dual** ramping arm of `process` drops its refresh | `builtins/src/lib.rs` | `host-core::input_liveness_live_controls` (2 tests) | RED — `stale for lane 2`, on `re_equalising_after_a_disengaging_drain_holds_the_never_collapsed_bits` (deleted by TA5) and `the_collapse_census_follows_the_commands`. A ramp advances the compared words on every block it runs, with no record drained: a drain-only invalidation is **not** sufficient, and this row is the proof | | C-3 | the **collapsed** ramping arm of `process_mono` drops its refresh (after `mirror_trim_ramp`) | `builtins/src/lib.rs` | — (green across `builtins`, `rack`, `host-core`, `console-workload`, `parametric-eq`, `builtins-compiler`, `graph`) | **EQUIVALENT, and kept anyway.** `mirror_trim_ramp` assigns channel `1`'s whole record from channel `0`'s, so every compared ramp word is equal when it returns -- and it was equal on entry too, which `debug_assert!(self.trim_ramp_channels_agree())` at the top of `process_mono` states. The comparison therefore lands where it already was. The refresh is kept because the equivalence rests on the mirror being *total*: a mirror that ever became partial or conditional would make this row red, and the cost is one walk on a ramping collapsed block | ### Row count @@ -374,3 +374,9 @@ The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) delete - `settled_matrix_takes_the_select_free_arm_only_without_an_identity_lane`: no plausible same-crate KEEP catcher recorded - `fused_fader_matrix_takes_the_select_free_arm_only_without_an_identity_lane`: no plausible same-crate KEEP catcher recorded + +## TA5 deletions + +The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. + +- `bank_is_bit_identical_to_scalar_stage_at_every_width`: builtins `an_uncommanded_live_fader_is_bit_identical_to_the_prepared_one`, `identity_chain_renders_audit_clean_deterministic_and_positive_zero` diff --git a/crates/builtins/tests/contract.rs b/crates/builtins/tests/contract.rs index 321dcee55..82e2cbe18 100644 --- a/crates/builtins/tests/contract.rs +++ b/crates/builtins/tests/contract.rs @@ -216,82 +216,6 @@ fn parameter_descriptors_have_complete_stable_contracts() { ); } -#[test] -fn descriptor_domains_are_exhaustive_at_launch_rates() { - for rate in [44_100, 48_000, 88_200, 96_000] { - for descriptor in BUILTIN_PARAMETER_DESCRIPTORS { - assert!(descriptor.domain.contains(descriptor.default, rate)); - assert!(!descriptor.domain.contains(f32::NAN, rate)); - assert!(!descriptor.domain.contains(f32::INFINITY, rate)); - assert!(!descriptor.domain.contains(f32::NEG_INFINITY, rate)); - } - for descriptor in [ - BUILTIN_PARAMETER_DESCRIPTORS[2], - BUILTIN_PARAMETER_DESCRIPTORS[3], - ] { - let maximum = builtin_filter_cutoff_maximum_hz(rate) - .expect("launch rate has an exact cutoff maximum"); - let successor = f32::from_bits(maximum.to_bits() + 1); - let nyquist = rate as f32 / 2.0; - let just_below_maximum = f32::from_bits(maximum.to_bits() - 1); - assert!(descriptor.domain.contains(0.0, rate)); - assert!(!descriptor.domain.contains(-0.0, rate)); - assert!(!descriptor.domain.contains(9.999, rate)); - assert!(descriptor.domain.contains(10.0, rate)); - assert!(descriptor.domain.contains(just_below_maximum, rate)); - assert!(descriptor.domain.contains(maximum, rate)); - assert!(!descriptor.domain.contains(successor, rate)); - assert!(!descriptor.domain.contains(nyquist, rate)); - } - } - for boolean in [ - BUILTIN_PARAMETER_DESCRIPTORS[0], - BUILTIN_PARAMETER_DESCRIPTORS[5], - ] { - assert!(boolean.domain.contains(0.0, 48_000)); - assert!(boolean.domain.contains(1.0, 48_000)); - assert!(!boolean.domain.contains(-0.0, 48_000)); - assert!(!boolean.domain.contains(0.5, 48_000)); - } - for decibels in [ - BUILTIN_PARAMETER_DESCRIPTORS[1], - BUILTIN_PARAMETER_DESCRIPTORS[4], - ] { - assert!(decibels.domain.contains(-144.0, 48_000)); - assert!(decibels.domain.contains(24.0, 48_000)); - assert!(!decibels.domain.contains(-144.001, 48_000)); - assert!(!decibels.domain.contains(24.001, 48_000)); - } - // Bounded, not open-ended: issue #210 phase 2 appended `delay_samples` after the matrix rows, - // and an open `[6..]` would have quietly swept it into the matrix contract. - for matrix in &BUILTIN_PARAMETER_DESCRIPTORS[6..10] { - assert!(matrix.domain.contains(-1.0, 48_000)); - assert!(matrix.domain.contains(1.0, 48_000)); - assert!(!matrix.domain.contains(-1.001, 48_000)); - assert!(!matrix.domain.contains(1.001, 48_000)); - } - let delay = BUILTIN_PARAMETER_DESCRIPTORS[10]; - assert_eq!(delay.name, "delay_samples"); - for rate in [44_100, 48_000, 88_200, 96_000] { - // A flat integer range: rate-independent, unlike the two cutoff rows above. - assert!(delay.domain.contains(0.0, rate)); - assert!(delay.domain.contains(1.0, rate)); - assert!(delay.domain.contains(48_000.0, rate)); - assert!(!delay.domain.contains(48_001.0, rate)); - assert!(!delay.domain.contains(-1.0, rate)); - assert!(!delay.domain.contains(f32::NAN, rate)); - assert!(!delay.domain.contains(f32::INFINITY, rate)); - } - let pan = BUILTIN_PARAMETER_DESCRIPTORS[11]; - assert_eq!(pan.name, "pan"); - assert!(pan.domain.contains(-1.0, 48_000)); - assert!(pan.domain.contains(1.0, 48_000)); - assert!(!pan.domain.contains(1.01, 48_000)); - // The descriptor maximum and the session schema maximum being *one* number rather than two - // that agree today is gated in `builtins-compiler`, which is the crate that can see - // both without this one taking a session dependency it has no other reason to have. -} - /// Owner ruling R5 (#1036): the former extended research rates (176.4-384 kHz) have no cutoff /// domain and no fallback; they refuse exactly like any other non-launch rate. #[test] diff --git a/crates/builtins/tests/fader_ramp.rs b/crates/builtins/tests/fader_ramp.rs index 274cec22d..9f80c81ef 100644 --- a/crates/builtins/tests/fader_ramp.rs +++ b/crates/builtins/tests/fader_ramp.rs @@ -1099,73 +1099,6 @@ fn a_repeated_mute_leaves_the_stage_as_it_was() { } } -/// D2: the held flag follows the mute ramp. It is set by a windowed mute change, cleared the -/// frame the countdown ends, cleared by a live retarget during an unmute ramp, and carried -/// across a plan swap's lane export and import. -#[test] -fn the_held_flag_follows_the_mute_ramp() { - let mut bank = match Subject::bank(Backend::Simd4) { - Subject::Bank(bank) => bank, - Subject::Scalar(_) => unreachable!(), - }; - let block = |bank: &mut BuiltinFaderBank, frames: u32| { - let mut left = vec![0.5_f32; frames as usize * 4]; - let mut right = left.clone(); - bank.process(&mut left, &mut right, frames); - }; - // A mute that repeats the lane's state starts no ramp (batch 13b), so mute it first. - bank.set_mute(0, BuiltinLaneSelector::Left, true, 0) - .expect("lane"); - bank.set_mute(0, BuiltinLaneSelector::Left, false, 40) - .expect("lane"); - assert_eq!( - bank.gain_held(0, 0), - Some(true), - "an unmute ramp holds the gain" - ); - assert_eq!( - bank.gain_held(0, 1), - Some(false), - "the right channel is free" - ); - block(&mut bank, 39); - assert_eq!(bank.ramp_remaining(0, 0), Some(1)); - assert_eq!(bank.gain_held(0, 0), Some(true), "one frame left"); - - // A plan swap carries the flag. - let state = bank.export_lane(0).expect("member"); - let mut successor = match Subject::bank(Backend::Simd4) { - Subject::Bank(bank) => bank, - Subject::Scalar(_) => unreachable!(), - }; - successor.import_lane(1, &state).expect("member"); - assert_eq!(successor.gain_held(1, 0), Some(true), "carried by the swap"); - - block(&mut bank, 1); - assert_eq!( - bank.gain_held(0, 0), - Some(false), - "cleared as the countdown ends" - ); - - bank.set_mute(0, BuiltinLaneSelector::Left, true, 0) - .expect("lane"); - bank.set_mute(0, BuiltinLaneSelector::Left, false, 40) - .expect("lane"); - bank.set_fader_db(0, BuiltinLaneSelector::Left, -6.0, 8) - .expect("domain"); - assert_eq!( - bank.gain_held(0, 0), - Some(false), - "a live retarget ends an unmute ramp" - ); - bank.set_mute(0, BuiltinLaneSelector::Left, true, 0) - .expect("lane"); - assert_eq!(bank.gain_held(0, 0), Some(true), "muted"); - assert_eq!(bank.gain_held(3, 0), None, "a padding lane"); - assert_eq!(bank.gain_held(0, 2), None, "no third channel"); -} - /// The scalar fader's lane queries answer `None` for a lane above `1`, as the bank's do for a /// channel above `1`. /// diff --git a/crates/builtins/tests/filter_liveness.rs b/crates/builtins/tests/filter_liveness.rs index 1a6f59be0..823866a0c 100644 --- a/crates/builtins/tests/filter_liveness.rs +++ b/crates/builtins/tests/filter_liveness.rs @@ -547,64 +547,6 @@ fn a_disable_in_the_enables_drain_still_completes_and_elides() { } } -/// Gate 3: an enable from a settled disabled section jumps the recursion and crossfades the -/// mix, so its first sample is the dry input; restored integrators on an identity section take -/// the six-word ramp instead. -#[test] -fn an_enable_from_rest_jumps_the_recursion_only_when_the_integrators_are_zero() { - for rate in LAUNCH_RATES { - for section in 0..2 { - let target = design(rate, section, if section == 0 { 200.0 } else { 3_000.0 }); - let words = target.coefficients; - let mut input = disabled_at(rate); - input.apply_prepared_filter(target).expect("enable"); - let output = render_frame(&mut input, 0, 0.625); - assert_eq!(output[0].to_bits(), 0.625_f32.to_bits()); - assert_eq!(output[1].to_bits(), (-0.625_f32).to_bits()); - // The kernel's frame-0 word, as it computes it: the start plus one step. - let after_one = |start: f32, target: f32| start + (target - start) * (1.0 / 64.0); - let identity_mix = [1.0_f32, 0.0, 0.0]; - let expected = [ - words[0], - words[1], - words[2], - after_one(identity_mix[0], words[3]), - after_one(identity_mix[1], words[4]), - after_one(identity_mix[2], words[5]), - ]; - for channel in 0..2 { - assert_eq!( - current_words(&input, channel, section), - bits6(expected), - "rate {rate} section {section} channel {channel}" - ); - } - - // Restored non-zero integrators on the identity section: rule 4. - let mut restored = disabled_at(rate); - let mut state = [0_u32; 8]; - for channel in 0..2 { - state[channel * 4 + section * 2] = 0.25_f32.to_bits(); - state[channel * 4 + section * 2 + 1] = (-0.125_f32).to_bits(); - } - builtins::test_support::set_input_state_words(&mut restored, state); - restored.apply_prepared_filter(target).expect("enable"); - render_frame(&mut restored, 0, 0.625); - let ramped: [f32; 6] = core::array::from_fn(|index| { - let start = [0.0, 0.0, 0.0, 1.0, 0.0, 0.0][index]; - after_one(start, words[index]) - }); - for channel in 0..2 { - assert_eq!( - current_words(&restored, channel, section), - bits6(ramped), - "restored rate {rate} section {section} channel {channel}" - ); - } - } - } -} - /// `||M||_V` in `f64` for a 2x2 matrix `M`: the operator norm induced by /// `||x||_V^2 = x1^2 + sqrt(2) x1 x2 + x2^2`, the shared eigenbasis norm of every `k = sqrt(2)` /// design. With `R = [[1, r], [0, r]]`, `r = 1/sqrt(2)` (so `||x||_V = ||R x||_2`), it is the diff --git a/crates/builtins/tests/stage.rs b/crates/builtins/tests/stage.rs index bc67aac7b..4aa862d22 100644 --- a/crates/builtins/tests/stage.rs +++ b/crates/builtins/tests/stage.rs @@ -311,133 +311,6 @@ fn scalar_stage_is_the_reference_recurrence_where_the_per_word_flush_fires() { } } -/// T3: a bank is the scalar stage at another width, and a padding lane changes nothing. -#[test] -fn bank_is_bit_identical_to_scalar_stage_at_every_width() { - for (backend, width) in banks() { - let lanes = width.lanes() as usize; - for members in [1, lanes - 1, lanes] { - let mut scalar: Vec = (0..members) - .map(|index| prepared_input(48_000, parameters_for(index))) - .collect(); - let bank_inputs: Vec = (0..members) - .map(|index| prepared_input(48_000, parameters_for(index))) - .collect(); - let mut bank = BuiltinInputBank::new(backend, width, bank_inputs).expect("bank"); - assert_eq!(bank.active_lanes(), members); - - const FRAMES: usize = 257; - let mut rng = Rng(0xB00B_5EED ^ members as u64 ^ lanes as u64); - let planar: Vec> = (0..members) - .map(|_| (0..FRAMES * 2).map(|_| rng.next_sample()).collect()) - .collect(); - - // Padding lanes are pre-filled with the bit patterns that would poison a recurrence. - const POISON: [u32; 4] = [0x7FC0_0000, 0x7F80_0000, 0x8000_0000, 0x0000_0001]; - let mut left = vec![0.0_f32; FRAMES * lanes]; - let mut right = vec![0.0_f32; FRAMES * lanes]; - for frame in 0..FRAMES { - for lane in 0..lanes { - let (l, r) = if lane < members { - (planar[lane][frame], planar[lane][FRAMES + frame]) - } else { - let poison = f32::from_bits(POISON[(frame + lane) % POISON.len()]); - (poison, poison) - }; - left[frame * lanes + lane] = l; - right[frame * lanes + lane] = r; - } - } - let bank_report = bank.process(&mut left, &mut right, FRAMES as u32); - - let mut scalar_report = BuiltinProcessReport::default(); - for (lane, input) in scalar.iter_mut().enumerate() { - let mut scalar_left = planar[lane][..FRAMES].to_vec(); - let mut scalar_right = planar[lane][FRAMES..].to_vec(); - let report = input.process( - DualMonoBlock::new(&mut scalar_left, &mut scalar_right, 0).expect("block"), - ); - scalar_report.sanitized_input += report.sanitized_input; - scalar_report.recovered_left_state += report.recovered_left_state; - scalar_report.recovered_right_state += report.recovered_right_state; - for frame in 0..FRAMES { - assert_eq!( - left[frame * lanes + lane].to_bits(), - scalar_left[frame].to_bits(), - "width={lanes}, members={members}, lane={lane}, frame={frame}, left" - ); - assert_eq!( - right[frame * lanes + lane].to_bits(), - scalar_right[frame].to_bits(), - "width={lanes}, members={members}, lane={lane}, frame={frame}, right" - ); - } - assert_eq!( - test_support::bank_lane_state_words(&bank, lane), - test_support::input_state_words(input), - "width={lanes}, members={members}, lane={lane}, state" - ); - } - assert_eq!( - bank_report, scalar_report, - "width={lanes}, members={members}" - ); - - // Padding lanes stay exactly `+0.0` in state, whatever was left in the scratch. - for lane in members..lanes { - assert_eq!( - test_support::bank_lane_state_words(&bank, lane), - [0; 8], - "width={lanes}, members={members}, padding lane={lane}" - ); - } - - // A padding lane whose *state* is non-finite is excluded from the boundary check and - // from every counter. Sanitisation already makes a padding lane's samples inert, so - // this is the only way its exclusion is load-bearing -- and it is: without the - // `active` mask, one padding lane would zero the whole block's member output. - if members < lanes { - let fresh = || { - BuiltinInputBank::new( - backend, - width, - (0..members) - .map(|index| prepared_input(48_000, parameters_for(index))) - .collect(), - ) - .expect("fresh bank") - }; - let mut bank = fresh(); - let mut control = fresh(); - let mut poisoned = test_support::bank_lane_state_words(&bank, members); - poisoned[0] = f32::NAN.to_bits(); - test_support::set_bank_lane_state_words(&mut bank, members, poisoned); - let mut poisoned_left = left.clone(); - let mut poisoned_right = right.clone(); - let mut control_left = left.clone(); - let mut control_right = right.clone(); - let poisoned_report = - bank.process(&mut poisoned_left, &mut poisoned_right, FRAMES as u32); - let control_report = - control.process(&mut control_left, &mut control_right, FRAMES as u32); - assert_eq!( - poisoned_report, control_report, - "width={lanes}, members={members}, padding-lane counters" - ); - for frame in 0..FRAMES { - for lane in 0..members { - assert_eq!( - poisoned_left[frame * lanes + lane].to_bits(), - control_left[frame * lanes + lane].to_bits(), - "width={lanes}, members={members}, lane={lane}, frame={frame}" - ); - } - } - } - } - } -} - /// T6: the block boundary check is per lane and per block, and it never crosses lanes. #[test] fn boundary_check_is_lane_local_per_block() { diff --git a/crates/builtins/tests/tail_contract.rs b/crates/builtins/tests/tail_contract.rs index cbb598a9d..892c34ed4 100644 --- a/crates/builtins/tests/tail_contract.rs +++ b/crates/builtins/tests/tail_contract.rs @@ -2744,89 +2744,6 @@ fn fixed_design_stall_is_the_module_stall_rounded_up_and_covers_the_flush_floor( } } -/// #1465 F2(c), and the both-disabled branch of preparation (root's named exception, 2026-10-08). -/// A design with both filters disabled states `D = 0`, `G_p = G_t = ceil_mB(|trim|)` for a -/// power-of-two trim (0 dB, `|trim| = 2`) and `ceil_mB(|trim| (1 + u))` otherwise (+6 dB, whose -/// word lies below `10^(6/20)`, so the two differ), and one underflow as both stalls, through both -/// `input_section_bound` and `input_section_bounds`; with the channels' trims different, the larger -/// gain; and one disabled channel beside an enabled one still leaves the design stated. -#[test] -fn a_design_with_filters_disabled_states_its_trim_gain() { - let rate = 48_000; - // `+6.0206 dB`: the `f32` gain word is exactly 2. - let two_db = 6.020_6_f32; - let underflow = ceil_mb(f64::from(f32::MIN_POSITIVE)); - for (trim_db, exact) in [(0.0_f32, true), (two_db, true), (6.0, false), (-3.0, false)] { - let strip = parameters(0.0, 0.0, trim_db, false); - let kernel = input(rate, 0.0, 0.0, trim_db, false); - let trim = trim_gain(&kernel); - let expected = if exact { - ceil_mb(trim) - } else { - ceil_mb(trim * (1.0 + U)) - }; - if trim_db == two_db { - assert_eq!(trim, 2.0, "the +6.0206 dB word"); - } - if !exact { - assert_ne!( - expected, - ceil_mb(trim), - "{trim_db} dB: the rounding moves the value" - ); - } - let single = input_section_bound(rate, strip).expect("bound"); - let session = input_section_bounds(rate, [strip]).expect("bounds")[0]; - for (path, value) in [ - ("input_section_bound", single), - ("input_section_bounds", session), - ] { - let what = format!("{path} at {trim_db} dB"); - assert_eq!(value.tail, TailSamples::Finite(0), "{what}"); - assert_eq!(value.rest, RestBound::Bounded(RestSamples::ZERO), "{what}"); - assert_eq!( - composition_values(value, &what), - (0, expected, expected, underflow, underflow), - "{what}" - ); - } - } - // Different trims, both disabled: the larger gain, on both paths. - let channel = |trim_db: f32, lpf_hz: f32| ChannelParameters { - trim_db, - lpf_hz, - ..ChannelParameters::default() - }; - let strip = BuiltinParameters { - left: channel(0.0, 0.0), - right: channel(12.0, 0.0), - ..BuiltinParameters::default() - }; - let louder = ceil_mb(f64::from(math::pow(10.0, 12.0 / 20.0) as f32) * (1.0 + U)); - let single = input_section_bound(rate, strip).expect("bound"); - let session = input_section_bounds(rate, [strip]).expect("bounds")[0]; - for (path, value) in [ - ("input_section_bound", single), - ("input_section_bounds", session), - ] { - let (_, peak, tail, ..) = composition_values(value, path); - assert_eq!( - (peak, tail), - (louder, louder), - "{path}: the louder channel's gain" - ); - } - // One channel disabled, the other enabled: stated, at least the disabled channel's gain. - let mixed = BuiltinParameters { - left: channel(24.0, 0.0), - right: channel(0.0, 1_000.0), - ..BuiltinParameters::default() - }; - let (_, peak, ..) = - composition_values(input_section_bound(rate, mixed).expect("bound"), "mixed"); - assert!(peak >= ceil_mb(f64::from(math::pow(10.0, 24.0 / 20.0) as f32))); -} - // ---- #1466: the live input section's decay, peak gain and stalls (gate 1, L4; L3 above) ----- /// The live composition values at `rate`, as the gates read them: `math::tail`'s accessor at the diff --git a/crates/capi/src/runtime/live_tests.rs b/crates/capi/src/runtime/live_tests.rs index 1042951ad..a44dad4a0 100644 --- a/crates/capi/src/runtime/live_tests.rs +++ b/crates/capi/src/runtime/live_tests.rs @@ -1502,73 +1502,6 @@ fn watermark(rig: &Rig) -> (u64, u64, u64, u64) { ) } -/// #1314 gate 2: a rebuild (r1) and then a live fader edit (r2), committed before any render, -/// both complete at the swap block and not one block earlier: the live edit went to the pending -/// candidate's revision gate, so the running plan never reports either revision. -#[test] -fn a_live_edit_on_a_pending_candidate_completes_at_the_swap() { - let document = long_session(1, 48_000); - let model = parse_session_json(&document).expect("model"); - let mut rig = Rig::new(&document); - let initial = rig.summary().0; - for _ in 0..3 { - rig.step(); - assert_eq!(watermark(&rig), (initial, 0, crate::OUTCOME_EXACT, 0)); - } - assert_eq!(rig.apply(&[content_edit(&model)]), crate::RESULT_OK); - let r1 = rig.summary().0; - assert_eq!(rig.summary().3, 1, "a candidate is pending"); - let edit = fader_edit( - "eq0", - DualMonoFader { - left_db: -6.0, - right_db: -6.0, - left_mute: false, - right_mute: false, - }, - ); - assert_eq!(rig.apply(&edit), crate::RESULT_OK); - let r2 = rig.summary().0; - assert_eq!((r1, r2), (initial + 1, initial + 2)); - assert_eq!( - rig.summary().3, - 1, - "the live edit went to the one candidate" - ); - assert_eq!(watermark(&rig), (initial, 0, crate::OUTCOME_EXACT, 0)); - - let k = rig.block; - rig.render(); - assert_eq!( - watermark(&rig), - (r2, k * rig.quantum as u64, crate::OUTCOME_EXACT, 2) - ); - - // A rebuild alone carries its own revision to its swap. - rig.render(); - let source = &model.sources[0]; - let edit = SessionEdit::SetSourceContent { - source_id: source.id.clone(), - content: format!("blake3:{}", "cd".repeat(32)), - channels: source.channels, - bit_depth: source.bit_depth, - frames: source.frames, - }; - assert_eq!(rig.apply(&[edit]), crate::RESULT_OK); - let r3 = rig.summary().0; - assert_eq!(rig.summary().3, 1, "a second candidate is pending"); - assert_eq!( - watermark(&rig), - (r2, k * rig.quantum as u64, crate::OUTCOME_EXACT, 2) - ); - let swap = rig.block; - rig.render(); - assert_eq!( - watermark(&rig), - (r3, swap * rig.quantum as u64, crate::OUTCOME_EXACT, 3) - ); -} - /// #1314 gate 3: on one thread, a live edit committed between blocks 3 and 4 completes in block 4, /// from its first sample, and block 5 publishes nothing new. #[test] @@ -5331,134 +5264,6 @@ mod set_control { blocks } - /// One rig of one document takes `edits`: one revision, the plan epoch unchanged, no candidate, - /// and the next blocks are heard to differ from an untouched second rig. The effect rows' - /// render bits are pinned against the direct host-core lane by - /// `live_effect_parameter_edits_render_like_the_browsers_lane` and - /// `live_effect_bypass_edits_render_like_a_rebuild`. - fn live_alone(document: &str, edits: &[SessionEdit], label: &str) { - let mut rig = Rig::new(document); - let mut untouched = Rig::new(document); - for block in 0..window(rig.latency(), 0, rig.quantum) { - assert_eq!( - bits(&rig.step()), - bits(&untouched.step()), - "{label}: warm-up {block}" - ); - } - let (revision, _, epoch, _) = rig.summary(); - assert_eq!(rig.apply(edits), crate::RESULT_OK, "{label}"); - let (after, _, after_epoch, pending) = rig.summary(); - assert_eq!( - (after, after_epoch, pending), - (revision + 1, epoch, 0), - "{label}: one revision, the same plan, no candidate" - ); - let heard = (0..8).any(|_| bits(&rig.step()) != bits(&untouched.step())); - assert!(heard, "{label}: the edit changes the output"); - } - - /// Gate 1: each value the classifier makes live is live through `SET_CONTROL`, and a write of - /// its one row renders the bits of the same change written as the whole struct that holds it. - /// - /// Red if a row write touches another field, lane or value than the one it names (the left - /// lane for a both-lane write, the mute in place of the fader, a matrix coefficient written - /// without its mode switch), or reaches the plan through a path the whole-struct write does - /// not. - #[test] - fn a_set_control_on_a_live_value_is_live_and_renders_like_the_whole_struct_write() { - let document = long_session(10, 48_000); - let model = parse_session_json(&document).expect("model"); - let track = model.tracks[0].clone(); - let name = track.id.as_str(); - let fader = |left_db, right_db, mute: bool| DualMonoFader { - left_db, - right_db, - left_mute: mute, - right_mute: mute, - }; - - live_twin( - &document, - &[set(builtin(name, FADER_DB, Lane::Left), -6.0, None)], - &fader_edit( - name, - DualMonoFader { - left_db: -6.0, - ..track.fader.clone() - }, - ), - "fader, left lane", - ); - live_twin( - &document, - &[set(builtin(name, FADER_DB, Lane::Both), -3.0, None)], - &fader_edit(name, fader(-3.0, -3.0, false)), - "fader, both lanes", - ); - live_twin( - &document, - &[set(builtin(name, MUTE, Lane::Both), 1.0, None)], - &fader_edit( - name, - DualMonoFader { - left_mute: true, - right_mute: true, - ..track.fader.clone() - }, - ), - "mute", - ); - - let pan_edit = set(builtin(name, PAN, Lane::Both), 0.5, None); - let panned = applied(&document, core::slice::from_ref(&pan_edit)).tracks[0] - .matrix_or_pan - .clone(); - live_twin( - &document, - &[pan_edit], - &matrix_edit(&id(name), panned), - "pan", - ); - - // A matrix coefficient follows its mode switch in the same transaction. - let matrix = [ - set(own(name, MATRIX_MODE, Lane::Both), 1.0, None), - set(builtin(name, MATRIX_LL, Lane::Both), 0.5, None), - ]; - let switched = applied(&document, &matrix).tracks[0].matrix_or_pan.clone(); - assert!(matches!(switched, MatrixOrPan::Matrix { ll, .. } if ll == 0.5)); - live_twin( - &document, - &matrix, - &matrix_edit(&id(name), switched), - "matrix after a mode switch", - ); - - // An effect parameter and an effect bypass: an insert's and a console slot's. - let document = effect_session(10, 48_000); - let model = parse_session_json(&document).expect("model"); - let first = model.tracks[0].id.as_str(); - let last = model.tracks[model.tracks.len() - 1].id.as_str(); - live_alone( - &document, - &[ - set(insert(first, "comp", 1, Lane::Left), -30.0, None), - set(insert(first, "comp", 6, Lane::Both), 6.0, None), - set(console(last, "clip", 1, Lane::Both), 12.0, None), - ], - "effect parameters", - ); - live_alone( - &document, - &[ - set(insert(first, "comp", 0, Lane::Both), 1.0, None), - set(console(last, "clip", 0, Lane::Both), 1.0, None), - ], - "effect bypass", - ); - } - /// Gate 2: a value the classifier leaves to a rebuild is a rebuild through `SET_CONTROL`. /// /// Red if a rebuild-only value is acked as live (a candidate not prepared, a cell written). @@ -6413,50 +6218,6 @@ mod set_control { ); } - /// Gate 4 (#1557): a transaction of a valid send gain and an out-of-domain matrix value is - /// refused whole: no send cell is written and the revision does not move. - /// - /// Red if the gain is written before the matrix value is refused. - #[test] - fn a_send_transaction_with_an_out_of_domain_value_writes_no_cell() { - let document = stateless_bus_document(); - let mut rig = Rig::new(&document); - for _ in 0..4 { - rig.step(); - } - let (revision, _, epoch, pending) = rig.summary(); - let (status, diagnostics) = refusal( - &mut rig, - &[ - set(route("eq0-bus", ROUTE_GAIN_DB), -6.0, None), - set(route("eq0-bus", ROUTE_MATRIX_LR), 100.0, None), - ], - ); - assert_ne!( - status, - protocol::StatusCode::Ok, - "the out-of-domain value is refused" - ); - assert_eq!(diagnostics.len(), 1); - assert_eq!(diagnostics[0].1, 1, "the matrix edit, the second"); - assert_eq!( - (rig.summary().0, rig.summary().2, rig.summary().3), - (revision, epoch, pending), - "no revision, no candidate" - ); - assert_eq!( - rig.current_route_cells(), - vec![0], - "no send cell was written" - ); - assert_eq!( - rig.apply(&[set(route("eq0-bus", ROUTE_GAIN_DB), -6.0, None)]), - crate::RESULT_OK, - "the valid half applies alone" - ); - assert_eq!(rig.current_route_cells(), vec![1]); - } - /// Gate 1, effect family on a submix strip: a console slot's parameter, an insert's /// parameter and an insert's bypass commit live and render the bits of the direct host-core lane /// (host-core records pushed to the bus's own producers at the same block). @@ -8469,47 +8230,6 @@ mod set_control { } } - /// U3 fold, MAJOR 1 (reviewer C's case). A VCA move in the same transaction as a - /// structural edit (the transaction rebuilds) reaches the carried automated lane as the - /// join's offsets record, with the edit's own length: every block equals the same move - /// sent alone (`live`), with its own 4,096 samples and with the session's length. - /// - /// Test value: red if the join writes no offsets record (the successor's `S` reaches the - /// lane by the adoption rule, over the fader length, from the block's curve), or the - /// carried lane composes its first block with the new `S` as if it had heard it. - #[test] - fn a_vca_move_inside_a_rebuild_keeps_its_own_length() { - bench_support::alloc::assert_installed(); - for ((ride, document), (length, ramp)) in - [("moving ride", riding()), ("flat ride", flat())] - .into_iter() - .flat_map(|document| { - [ - ("its own 4,096 samples", Some(4_096)), - ("the session's length", None), - ] - .map(|length| (document.clone(), length)) - }) - { - let label = &format!("{ride}, {length}"); - let model = parse_session_json(&document).expect("model"); - let mut rigs = warm(&document); - let mut edits = vec![vca_move(-6.0, ramp)]; - edits.extend(add_muted_track(&model, "added")); - send(&mut rigs[0], &edits, protocol::EditPath::Rebuild, label); - send( - &mut rigs[1], - &[vca_move(-6.0, ramp)], - protocol::EditPath::Live, - label, - ); - let (first, heard) = lockstep(&mut rigs, 44); - assert_eq!(rigs[0].summary().3, 0, "{label}: the successor is adopted"); - assert_eq!(first, None, "{label}: first difference (block, sample)"); - assert!(heard, "{label}: the move is heard"); - } - } - /// A hand move (#1650b) of `document`'s first track's fader to `db` on both lanes over /// `ramp`: on an automated fader it is stored as the ride's offset and moves `S`. fn hand_move(document: &str, db: f32, ramp: Option) -> SessionEdit { @@ -9026,23 +8746,6 @@ mod set_control { } } - /// #1711 R19-1 on the C ABI. A VCA or hand move heard 1 and 12 blocks (3,872 and 2,464 - /// samples left, both at least `J` = 960) whose target a table-only rebuild moves: the - /// move's ramp runs to its end as the uninterrupted engine runs it, then the cell jumps - /// over `J`; every sample is bit-equal to the fresh plan with that jump at the move's - /// first free sample. - /// - /// Test value: red if the adoption retargets the move at the swap rather than deferring - /// the jump to the ramp's end. - #[test] - fn a_moved_target_lets_a_live_move_longer_than_the_jump_end_when_it_was_due() { - for hand in [false, true] { - for heard in [1, 12] { - moved_target_case(hand, heard, &[], 0); - } - } - } - /// #1711 U1 fold 1 (MAJOR-1) on the C ABI. A VCA or hand move heard 1 and 4 blocks on /// the ride flat at -3 dB, then a table-only rebuild to -5 dB up to 2,000, a linear -8 to /// -14 dB over 2,000 to 8,000, then -14 dB: the move's target moves, so its jump is @@ -10304,43 +10007,6 @@ fn an_unchanged_fader_ride_carries_with_no_jump() { } } -/// #1648 gate 4. Removing the ride rebuilds; the released lane ramps from the curve's last target -/// to its static value over the fader jump length, and from the end of that release every sample -/// equals a fresh plan of the new session (a static fader), session-seeked to the adoption block. -/// -/// Test value: red if a removed entry leaves the lane on its last automated value, or steps it. -#[test] -fn a_removed_fader_ride_releases_to_the_static_fader() { - let (old, new) = (ride_document(Some(-12.0)), ride_document(None)); - let mut rig = Rig::new(&old); - for _ in 0..8 { - rig.step(); - } - let edit = SessionEdit::RemoveAutomation { - automation_id: StableId::parse("fader-ride").expect("automation ID"), - }; - let (adoption, live) = rebuild_and_adopt(&mut rig, &[edit], ADOPTED_BLOCKS); - let reference = seeked_reference(&new, adoption, ADOPTED_BLOCKS); - let jump = 960_usize; - let quantum = rig.quantum; - for plane in 0..2 { - let (live, reference) = ( - plane_bits(&live, plane, quantum), - plane_bits(&reference, plane, quantum), - ); - assert_ne!( - live[jump - quantum..jump - 1], - reference[jump - quantum..jump - 1], - "plane {plane}: the lane is still in its release" - ); - assert_eq!( - live[jump - 1..], - reference[jump - 1..], - "plane {plane}: the static fader's bits from the release's last sample" - ); - } -} - /// #1652 gate 1: track `eq0` sends `pre_fader` (its right lane, `rr` only) with `follows_mute` /// into bus `b`, and its direct route takes only its left lane to the output, so the output's /// left plane is the strip and its right plane the send. With `entry`, `eq0` has a mute entry on @@ -10416,150 +10082,6 @@ fn follow_document(entry: bool, muted: bool) -> String { session::canonical_session_json(&model).expect("canonical follow session") } -/// #1652 U2a fold 2 (MAJOR-A, conservative; #1713 deletes the flag). A rebuild whose send join -/// carries a following route into a six-word one (it removes `eq0`'s mute entry while `eq0-b` -/// follows it) completes as the transition fallback: the C ABI watermark that covers it reads -/// `OUTCOME_TRANSITION_FALLBACK`, count 1. The removal is sent at block 8 (sample 1,024), while the -/// send's columns ramp toward the mute the curve stepped on at 1,000 (480 samples). A rebuild that -/// keeps the entry (the following route stays following, seven -> seven) and a rebuild of a -/// session with no following route (no mute entry) complete as `OUTCOME_EXACT`, count 0. A second -/// rebuild that automates `eq0`'s fader in the block after the first one's adoption (inside `J`) -/// completes `OUTCOME_EXACT` too, count still 1: the flag marks its own candidate only. -/// -/// Test value: red if the control plane does not mark a seven -> six rebuild (the six-word -/// successor's re-timed column move then completes exact), or if it marks a rebuild whose -/// following routes stay following, or one with no following route (the flag must stay narrow), -/// or if the flag outlives its own candidate (the fader rebuild after it then reports the -/// fallback). -#[test] -fn a_rebuild_that_drops_a_following_route_to_six_words_reports_a_fallback() { - let remove = SessionEdit::RemoveAutomation { - automation_id: StableId::parse("eq0-mute").expect("automation ID"), - }; - let fallback = crate::OUTCOME_TRANSITION_FALLBACK; - let exact = crate::OUTCOME_EXACT; - for (label, entry, remove_entry, expected) in [ - ("seven -> six, a column mid-move", true, true, fallback), - ("seven -> seven", true, false, exact), - ("no following route", false, false, exact), - ] { - let document = follow_document(entry, false); - let model = parse_session_json(&document).expect("model"); - let edits = if remove_entry { - vec![remove.clone()] - } else { - // A muted copy of `eq0` with its own route to the output: a rebuild that leaves - // `eq0-b` and `eq0`'s mute entry as they are. - let mut track = model.tracks[0].clone(); - track.id = StableId::parse("added").expect("track ID"); - (track.fader.left_mute, track.fader.right_mute) = (true, true); - let mut route = model - .routes - .iter() - .find(|route| route.id.as_str() == "eq0-main") - .expect("the direct route") - .clone(); - route.id = StableId::parse("added-main").expect("route ID"); - route.source = RouteSource::Track { - track_id: track.id.clone(), - tap: session::SendTap::PostPan, - }; - vec![ - SessionEdit::UpsertTrack { track }, - SessionEdit::UpsertRoute { route }, - ] - }; - let mut rig = Rig::new(&document); - for _ in 0..8 { - rig.step(); - } - let bytes = rig.transaction(&edits); - let (result, response) = rig.send(&bytes); - assert_eq!(result, crate::RESULT_OK, "{label}"); - assert_eq!( - edit_path_of(&response), - protocol::EditPath::Rebuild, - "{label}" - ); - let revision = rig.summary().0; - for _ in 0..4 { - rig.step(); - } - assert_eq!(rig.summary().3, 0, "{label}: the successor is adopted"); - let (result, watermark) = crate::ffi::test_plan_watermark(rig.plan); - assert_eq!(result, crate::RESULT_OK); - assert_eq!( - ( - watermark.revision, - watermark.outcome_flags, - watermark.transition_fallback_count - ), - (revision, expected, u64::from(expected == fallback)), - "{label}: the rebuild's revision, outcome flags and transition-fallback count" - ); - } - // Fold-2 NIT-1: the flag marks its own candidate only. The seven -> six rebuild above is - // flagged, and a second rebuild that automates a fader in the block after its adoption - // (inside `J` = 240 samples of it) completes exact; the count stays 1. - let document = follow_document(true, false); - let mut rig = Rig::new(&document); - for _ in 0..8 { - rig.step(); - } - let bytes = rig.transaction(&[remove]); - let (result, response) = rig.send(&bytes); - assert_eq!(result, crate::RESULT_OK); - assert_eq!(edit_path_of(&response), protocol::EditPath::Rebuild); - let first = rig.summary().0; - rig.step(); - let (result, watermark) = crate::ffi::test_plan_watermark(rig.plan); - assert_eq!(result, crate::RESULT_OK); - assert_eq!( - (watermark.revision, watermark.first_sample), - (first, 8 * rig.quantum as u64), - "the flagged successor is adopted at the next block" - ); - let ride = session::Automation { - id: StableId::parse("eq0-ride").expect("automation ID"), - target: session::ControlAddress { - owner: session::Owner::Strip(StableId::parse("eq0").expect("strip ID")), - section: session::Section::Builtins, - // The fader row (builtin parameter 5). - control: 5, - lane: session::ParameterChannel::Both, - }, - segments: vec![session::AutomationSegment { - shape: session::AutomationShape::Linear, - start_sample: 0, - end_sample: SOURCE_FRAMES, - start_value: 0.0, - end_value: -12.0, - unit: session::ParameterUnit::Db, - }], - offset: session::AutomationOffset::default(), - }; - let bytes = rig.transaction(&[SessionEdit::UpsertAutomation { automation: ride }]); - let (result, response) = rig.send(&bytes); - assert_eq!(result, crate::RESULT_OK); - assert_eq!(edit_path_of(&response), protocol::EditPath::Rebuild); - let revision = rig.summary().0; - for _ in 0..4 { - rig.step(); - } - assert_eq!(rig.summary().3, 0, "the second successor is adopted"); - let (result, watermark) = crate::ffi::test_plan_watermark(rig.plan); - assert_eq!(result, crate::RESULT_OK); - assert_eq!( - ( - watermark.revision, - watermark.outcome_flags, - watermark.transition_fallback_count - ), - (revision, crate::OUTCOME_EXACT, 1), - "a fader rebuild inside a flagged follow rebuild's landing finds no level move" - ); -} - /// #1653: sends `edits` on `rig`, which must answer `live`, and renders `blocks` blocks from the /// next block on. Returns that block's first sample `T` (the first block that begins after the /// submit returns, which applies the transaction's records) and the blocks, `T`'s first. diff --git a/crates/capi/src/runtime/supersede_tests.rs b/crates/capi/src/runtime/supersede_tests.rs index bd92805b6..0f27d753b 100644 --- a/crates/capi/src/runtime/supersede_tests.rs +++ b/crates/capi/src/runtime/supersede_tests.rs @@ -137,111 +137,6 @@ fn revision(children: &CompiledChildren) -> u64 { children.session.test_controller().session().revision().0 } -/// Gate 1: three structural transactions submitted without a render between them each commit, -/// and the plan the last one publishes renders exactly what one transaction of all three edits -/// renders; the watermark completes the first two as superseded. -/// -/// P0 has tracks `t1`-`t4` on the fixture source, `t2` and `t3` with a compressor insert. T1 adds -/// source `s` with its track and a compressor on `t1`, and moves `t4`'s fader; T2 replaces `t2`'s -/// compressor; T3 removes `t3`'s. `s` is fed between T1 and T2, so its ring is the withdrawn -/// candidate's and moves by the donation; `t4` carries from P0 with its fader retargeted at T1's -/// own ramp length. -/// -/// Test value: red if any of T1-T3 is refused, if the replacement carries from the withdrawn -/// candidate's identity while P0 runs (the carry finds no predecessor and the fixture source and -/// `t2` restart), if it is prepared against the current model instead of P0's (`t4`'s fader is -/// then no change, and `t4` keeps P0's), if the retarget forgets T1's ramp length, if `s`'s acked -/// PCM is lost with the withdrawn candidate, or if the superseded count is off. -#[test] -fn a_superseding_replacement_renders_as_one_combined_transaction() { - let mut model = stateless_session(48_000); - model.tracks[1].inserts.effects.push(compressor()); - model.tracks[2].inserts.effects.push(compressor()); - // `t4` is untouched but for a fader change T1 carries: it carries from P0 in every plan. - let mut t4 = model.tracks[0].clone(); - t4.id = id("t4"); - let mut route = model.routes[0].clone(); - route.id = id("t4-main"); - let RouteSource::Track { track_id, .. } = &mut route.source else { - panic!("track route") - }; - *track_id = t4.id.clone(); - model.tracks.push(t4); - model.routes.push(route); - let fader = session::DualMonoFader { - left_db: -6.0, - right_db: -9.0, - left_mute: false, - right_mute: false, - }; - // The fader edits carry their own ramp length (#1394), which the replacement's base keeps - // across the withdrawn candidate (root ruling B15-Q3). - let mut fader_edits = protocol::control_core::fader_edits(&id("t4"), &fader); - for edit in &mut fader_edits[..2] { - let SessionEdit::SetControl { ramp_samples, .. } = edit else { - panic!("a control edit") - }; - *ramp_samples = Some(1); - } - let first: Vec = add_source_track(&model) - .into_iter() - .chain([put(&model, 0, compressor())]) - .chain(fader_edits) - .collect(); - let second = [put(&model, 1, gate())]; - let third = [remove(&model, 2)]; - let combined: Vec = first.iter().chain(&second).chain(&third).cloned().collect(); - - let run = |superseding: bool| { - let mut children = compile(&model, limits()); - submit_direct(&mut children, &[FIXTURE], 0); - let mut blocks = vec![render_direct(&mut children, 0)]; - let before = children.plan.queries().watermark().expect("watermark"); - if superseding { - for (request, (base, edits)) in [(42, &first[..]), (43, &second[..]), (44, &third[..])] - .into_iter() - .enumerate() - { - commit(&mut children, request as u64 + 1, base, edits); - if request == 0 { - // `s` is fed while T1's candidate waits, into the candidate's own ring. - submit_direct(&mut children, &[ADDED], 0); - submit_direct(&mut children, &[ADDED], 1); - } - } - assert_eq!(revision(&children), 45); - } else { - commit(&mut children, 1, 42, &combined); - submit_direct(&mut children, &[ADDED], 0); - submit_direct(&mut children, &[ADDED], 1); - } - submit_direct(&mut children, &[FIXTURE], 1); - blocks.push(render_direct(&mut children, 1)); - let adopted = children.plan.queries().watermark().expect("watermark"); - for block in 2..9 { - submit_direct(&mut children, &[FIXTURE, ADDED], block); - blocks.push(render_direct(&mut children, block)); - } - (blocks, before, adopted) - }; - let (superseded, before, adopted) = run(true); - let (twin, _, _) = run(false); - for (block, (actual, expected)) in superseded.iter().zip(&twin).enumerate() { - assert!( - expected.iter().any(|sample| *sample != 0.0), - "block {block}: the twin plays" - ); - assert_eq!(bits(actual), bits(expected), "block {block}"); - } - assert_eq!(adopted.revision, 45); - assert_eq!( - adopted.outcome_flags, - engine::realtime::OUTCOME_EXACT | engine::realtime::OUTCOME_SUPERSEDED - ); - assert_eq!(adopted.superseded, before.superseded + 2); - assert_eq!(adopted.exact, before.exact + 1); -} - /// The lifecycle counters every constructed owner and every reservation are balanced in. fn assert_balanced(label: &str, counters: TestOwnerCounters) { assert_eq!( diff --git a/crates/capi/src/runtime/tests.rs b/crates/capi/src/runtime/tests.rs index b5db761c7..50da17278 100644 --- a/crates/capi/src/runtime/tests.rs +++ b/crates/capi/src/runtime/tests.rs @@ -5361,62 +5361,6 @@ fn configuring_an_unserved_counter_is_refused_and_changes_nothing() { crate::ffi::test_session_destroy(c_session); } -/// Issue #1351 gate 4 (D1, D6): the provider serves every counter of its fixed set at 0 from -/// construction, `TRANSITION_REPREPARE_REFUSALS` among them, and a configuration may name it. -/// -/// Test value: red if the new counter is missing from the registry decoder or the served set, so -/// that a host's read of it is `NOT_FOUND` or its configuration is refused. -#[test] -fn every_served_counter_reads_zero_on_a_fresh_session() { - let (c_session, c_plan) = boxed_c_children(SESSION); - let all = counters_get_c(c_session, 1, &[]).expect("all served counters"); - assert_eq!( - all.iter().map(|value| value.id).collect::>(), - vec![ - protocol::CounterId::TelemetryCoalesced, - protocol::CounterId::TelemetryDropped, - protocol::CounterId::LiveValuesSuperseded, - protocol::CounterId::TransitionReprepareRefusals, - ] - ); - assert!(all.iter().all(|value| value.value == 0)); - assert_eq!( - counters_get_c( - c_session, - 2, - &[protocol::CounterId::TransitionReprepareRefusals] - ), - Ok(vec![protocol::CounterValue { - id: protocol::CounterId::TransitionReprepareRefusals, - value: 0, - }]) - ); - let configure = counter_telemetry( - 3, - vec![protocol::CounterId::TransitionReprepareRefusals], - 1, - Vec::new(), - ); - let (result, response) = command_c(c_session, &configure); - assert_eq!( - (result, response_status(&response)), - (crate::RESULT_OK, StatusCode::Ok) - ); - render_c(c_plan, 0); - assert_eq!(crate::ffi::test_service(c_session), crate::RESULT_OK); - let snapshots = lossy_counter_snapshots(c_session); - assert_eq!(snapshots.len(), 1); - assert_eq!( - snapshots[0].values, - vec![protocol::CounterValue { - id: protocol::CounterId::TransitionReprepareRefusals, - value: 0, - }] - ); - crate::ffi::test_plan_destroy(c_plan); - crate::ffi::test_session_destroy(c_session); -} - /// Issue #1348 gate 4 (D7): a published candidate scheduled `NoEarlierThan` 64 blocks ahead /// survives 1,000 service steps after a rendered block: render still adopts it, exactly at the /// block that starts at its sample, its provider stays pending until then, no plan or provider is diff --git a/crates/compressor/tests/MUTATIONS.md b/crates/compressor/tests/MUTATIONS.md index 0b5b71fbd..937030f77 100644 --- a/crates/compressor/tests/MUTATIONS.md +++ b/crates/compressor/tests/MUTATIONS.md @@ -169,7 +169,7 @@ tree restored between rows. | M2-C7 | `Channel::copy_state_from` drops `main` | `compressor/src/kernel.rs` | `a_desymmetrized_bank_is_a_never_collapsed_bank` | RED — the delay ring the output is read out of | | M2-C8 | `Channel::copy_state_from` drops `detector` | `compressor/src/kernel.rs` | `a_desymmetrized_bank_is_a_never_collapsed_bank` | RED — the detector ring the lookahead tap gathers from | -`a_statically_bypassed_bank_collapses_to_the_dual_bits` carries no mutation of its own: it is +`a_statically_bypassed_bank_collapses_to_the_dual_bits` (deleted by TA5) carries no mutation of its own: it is coverage for the one place a **prepared** bypass and a **live** one differ. A live bypass clears the witness' `UNBYPASSED` term and declines the collapse; a prepared bypass on a live-control-free bank does not, because `EffectBankStage::lane_symmetry` is the designed-word comparison alone. The effect @@ -230,7 +230,7 @@ differentials see dispatch as well as bits. | # | mutation | red | |---|---|---| -| 982-M1 | the arm ignores `bypass` | `identity::bypass_preserves_exact_dry_bits_at_sample_zero` (deleted by TA4), gate 1 (`the_all_wet_arm_is_the_base_body_on_all_wet_tables`, the three differentials), gate 3, `conformance`, `contract::every_launch_rate_processes_scalar_and_supported_bank_at_zero_latency`, `mono_collapse::a_statically_bypassed_bank_collapses_to_the_dual_bits` (9 red) | +| 982-M1 | the arm ignores `bypass` | `identity::bypass_preserves_exact_dry_bits_at_sample_zero` (deleted by TA4), gate 1 (`the_all_wet_arm_is_the_base_body_on_all_wet_tables`, the three differentials), gate 3, `conformance`, `contract::every_launch_rate_processes_scalar_and_supported_bank_at_zero_latency`, `mono_collapse::a_statically_bypassed_bank_collapses_to_the_dual_bits` (deleted by TA5) (9 red) | | 982-M2 | the arm is taken when any lane is wet (`mask_any`) | gate 1 on the mixed corpus banks (`the_settled_body_is_the_base_body_on_the_corpus_table`, `randomized_differential_simd4`/`_simd8`), gate 3, `scenario_981_*`, `cross_target`, `lane_identity::every_width_produces_the_same_words` (7 red) | | 982-M3 | the arm drops `+ makeup` | gate 1, `scenario_982_all_wet_render_is_pinned`, `cross_target` (its `f32` lane 7 has mix 1 and makeup 6), `lane_identity::every_width_produces_the_same_words` (7 red). `oracle` stays green, as the verification found | | 982-M4 | the arm is never taken | gate 3 (`the_all_wet_arm_is_taken_exactly_when_every_lane_is_wet`) and gate 1's dispatch assertion (5 red). No bit-exactness test can see it: it is a performance-only regression | @@ -268,7 +268,7 @@ same witness-keyed "both NaN" rule as the dual half, and the grid runs a third p |---|---|---| | 985-M2 | the short last chunk is skipped (`chunks_exact_mut` on the outer loop), the verification's replacement for "the body reads the right plane", which cannot be written | gate 1 (its 31-, 33- and 97-frame blocks: the grid on every table and the three differentials), `scenario_985_collapsed_render_is_pinned` (7 red) | | 985-M3 | the recursive word's write-back is dropped | gate 1, `scenario_985_*`, and four `mono_collapse` tests including `the_collapsed_body_renders_the_dual_bodys_left_plane` (deleted by TA4; KEEP catcher compressor `mono_collapse::mono_reopen_drives_the_next_render_from_the_copied_state`) (11 red) | -| 985-M4 | the collapsed wet arm ignores `bypass` | `mono_collapse::a_statically_bypassed_bank_collapses_to_the_dual_bits`, gate 1 (7 red) | +| 985-M4 | the collapsed wet arm ignores `bypass` | `mono_collapse::a_statically_bypassed_bank_collapses_to_the_dual_bits` (deleted by TA5), gate 1 (7 red) | | # | mutation | why it survives | |---|---|---| @@ -368,7 +368,7 @@ file was restored byte for byte from a saved copy. | # | mutation | test | result | |---|---|---|---| -| 994-C1 | `knee_coefficients` without its `is_finite` test (the pre-#994 design) | `knee_overflow` | RED, 4 of 5: the prepare, automation, restore and bank tests. First failure `prepare: W 3e-45 (0x00000002) R 4 frame 0: 0.11473124 vs f64 reference 1` — the NaN target clamped to -100 dB and a 0.1 ms attack duck the very first full-scale sample by 18.8 dB. The automation test is red although both of its endpoints (0 and `1e-38`) are outside the overflow band: the `Linear 64` ramp passes through it | +| 994-C1 | `knee_coefficients` without its `is_finite` test (the pre-#994 design) | `knee_overflow` | RED, 4 of 5: the prepare, automation, restore (`a_restored_overflowing_knee_does_not_duck`, deleted by TA5) and bank tests. First failure `prepare: W 3e-45 (0x00000002) R 4 frame 0: 0.11473124 vs f64 reference 1` — the NaN target clamped to -100 dB and a 0.1 ms attack duck the very first full-scale sample by 18.8 dB. The automation test is red although both of its endpoints (0 and `1e-38`) are outside the overflow band: the `Linear 64` ramp passes through it | | 994-C2 | the rule as a constant bound one ulp too low: `knee_db >= f32::from_bits(0x0010_0000)` | `knee_overflow` | RED, 2 of 5: the prepare and bank tests, the two whose widths include `2^-129` itself | `the_narrowest_soft_knees_do_not_duck_a_sample_at_the_threshold` stays green under both: it is a @@ -382,3 +382,9 @@ The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) delete - `bank_matches_scalar_per_lane_bits`: no plausible same-crate KEEP catcher recorded - `block_partitions_are_invariant`: no plausible same-crate KEEP catcher recorded - `bypass_preserves_exact_dry_bits_at_sample_zero`: no plausible same-crate KEEP catcher recorded + +## TA5 deletions + +The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. + +- `a_statically_bypassed_bank_collapses_to_the_dual_bits`: compressor `mono_reopen_drives_the_next_render_from_the_copied_state` diff --git a/crates/compressor/tests/knee_overflow.rs b/crates/compressor/tests/knee_overflow.rs index 912481222..75e5644c1 100644 --- a/crates/compressor/tests/knee_overflow.rs +++ b/crates/compressor/tests/knee_overflow.rs @@ -24,7 +24,7 @@ use effect_contract::{InitialParameterValue, ParameterChannel, PreparedAutomatio use support::{ PARAMETER_COUNT, bind_bank, native_bank_width, prepare, render_bank, render_scalar, request, - restore, snapshot, values_with, + values_with, }; /// Positive widths whose `f32` reciprocal `1 / (2 W)` overflows: the smallest subnormal, the @@ -200,38 +200,6 @@ fn an_automated_knee_ramp_through_the_overflow_band_does_not_duck() { } } -/// Restoring a state payload whose knee is 2.8e-45 into an instance prepared at the default 6 dB -/// knee renders exactly as the hard knee. -/// -/// Red mutation (MUTATIONS.md row 994-C1): drop the `is_finite` test in `knee_coefficients`. -#[test] -fn a_restored_overflowing_knee_does_not_duck() { - let knee = f32::from_bits(0x0000_0002); - for ratio in [4.0_f32, 20.0] { - let hard = render_full_scale(&values(0.0, ratio), &[]); - let donor = prepare(request(&values(knee, ratio))); - let (left_payload, right_payload) = snapshot(donor.as_ref()); - let receiver_values = values(6.0, ratio); - let mut receiver = prepare(request(&receiver_values)); - restore( - receiver.as_mut(), - support::STATE_VERSION, - &left_payload, - &right_payload, - ) - .expect("a valid payload restores"); - let mut left = vec![1.0_f32; FRAMES]; - let mut right = vec![1.0_f32; FRAMES]; - let report = render_scalar(receiver.as_mut(), &mut left, &mut right, 128, 128, &[]); - assert_eq!( - report.nonfinite_left_blocks + report.nonfinite_right_blocks, - 0 - ); - assert_undocked(&left, &hard, knee, ratio, "restore"); - assert_undocked(&right, &hard, knee, ratio, "restore"); - } -} - /// A homogeneous bank at this build's SIMD width, one knee per lane — every overflowing width, /// the hard knee and the narrowest soft knees side by side — renders every lane exactly as the /// scalar hard knee. diff --git a/crates/compressor/tests/mono_collapse.rs b/crates/compressor/tests/mono_collapse.rs index 93ba00a5b..a1d25b607 100644 --- a/crates/compressor/tests/mono_collapse.rs +++ b/crates/compressor/tests/mono_collapse.rs @@ -370,91 +370,3 @@ fn mono_reopen_drives_the_next_render_from_the_copied_state() { ); } } - -/// A **statically bypassed** bank collapses to the dual bypassed bits, plane and state. -/// -/// # The seam this closes -/// -/// A *live* bypass clears the channel-symmetry witness' `UNBYPASSED` term, so a live-control-driven -/// bypassed lane declines the collapse and never reaches a one-plane body. A **prepared** bypass on -/// a live-control-free bank does not: `EffectBankStage::lane_symmetry` is the effect's -/// designed-word comparison alone, and it leaves `UNBYPASSED` unconditionally true. That asymmetry -/// is written down in `effect_contract::symmetry` as "a seam the collapse must close", and this -/// closes it by measurement rather than by the argument that the bypass is per plane inside the -/// kernel. -/// -/// The second assertion is the one that makes it a *chain*-level statement: on symmetric input a -/// dual bypassed block must publish the same words on both planes, because the collapse's seam -/// publishes the left plane on both. If a bypassed kernel could make its two channels differ on -/// identical input, collapsing a bypassed cohort would be unsound however faithful the one-plane -/// body was. -#[test] -fn a_statically_bypassed_bank_collapses_to_the_dual_bits() { - let Some((_, width)) = support::native_bank_width() else { - return; - }; - let lanes = width.lanes() as usize; - let values = support::values_with(&[(0, -30.0), (1, 4.0)]); - let requests: Vec<_> = (0..lanes) - .map(|_| request_configured(&values, LinkMode::Average, true)) - .collect(); - let mut dual = support::bind_bank(&requests).expect("dual bank"); - let mut collapsed = support::bind_bank(&requests).expect("collapsed bank"); - let idle = offsets(lanes); - - for step in 0..BLOCKS { - let mut dual_left = block(step * FRAMES, lanes); - let mut dual_right = dual_left.clone(); - let mut mono_left = dual_left.clone(); - let mut stale = vec![f32::from_bits(0x7F7F_FFFF); FRAMES * lanes]; - let first = (step * FRAMES) as u64; - run_block( - dual.as_mut(), - &mut dual_left, - &mut dual_right, - width, - first, - &[], - &idle, - false, - ); - run_block( - collapsed.as_mut(), - &mut mono_left, - &mut stale, - width, - first, - &[], - &idle, - true, - ); - for (word, (collapsed_word, dual_word)) in - mono_left.iter().zip(dual_left.iter()).enumerate() - { - assert_eq!( - collapsed_word.to_bits(), - dual_word.to_bits(), - "block {step} word {word}: bypassed collapsed left plane" - ); - } - for (word, (dual_right_word, dual_left_word)) in - dual_right.iter().zip(dual_left.iter()).enumerate() - { - assert_eq!( - dual_right_word.to_bits(), - dual_left_word.to_bits(), - "block {step} word {word}: a dual bypassed block on symmetric input must publish \ - the same words on both planes, or a collapsed bypassed cohort is unsound" - ); - } - } - - collapsed.desymmetrize_channels(); - for track in 0..lanes as u32 { - assert_eq!( - support::snapshot_track(collapsed.as_ref(), track), - support::snapshot_track(dual.as_ref(), track), - "track {track}: bypassed state after the disengage copy" - ); - } -} diff --git a/crates/compressor/tests/padding.rs b/crates/compressor/tests/padding.rs index b16817eee..ff9a48dc2 100644 --- a/crates/compressor/tests/padding.rs +++ b/crates/compressor/tests/padding.rs @@ -318,141 +318,6 @@ fn the_fixtures_render_through_a_padded_bank_as_they_do_per_node() { } } -/// Gate 4, the P1 verdict's M2: a bypassed lane fed a tripping value moves no bank-mate's bit. -/// -/// A session bypass keeps its track in the bank (#1087): the effect runs the lane with prepared -/// `bypass = false`, and the rack's `BypassShunt` copies the dry words back over that lane's -/// output, so the bypassed lane's wet path still runs and still meets D7. Here the bypassed lane -/// carries a legal gain of +24 dB (ratio 1, makeup +24) and is fed `1e29` on some blocks, a legal -/// sample below the `1e30` bound that its wet block pushes past it. The test drives the real -/// `BypassShunt` the same way `rack::LiveControlEffectBankStage` does: capture the dry block, run -/// the bank, restore the bypassed lane. -/// -/// For every active count `2..=W` (full banks too) and the first and last member bypassed: -/// * every enabled member renders the bits and reports of the render in which the bypassed lane -/// was fed ordinary noise instead, and of its own per-node instance; -/// * the bypassed lane's output is its dry input, bit for bit; -/// * its wet block really was rejected (the report is the non-vacuity check), and no other lane -/// reports anything. -/// -/// Red for the pre-#1090 whole-bank recovery: every enabled member of the bank is silenced for as -/// long as the bypassed lane stays hot (Sol's probe measured 43,008 of 43,008 words). -#[test] -fn a_bypassed_lane_fed_a_tripping_value_moves_no_bank_mates_bit() { - let Some((backend, width)) = native_bank_width() else { - println!("scalar-only build: no bank width"); - return; - }; - let lanes = width.lanes() as usize; - const BLOCKS: usize = 8; - const TRIPPING_BLOCKS: [usize; 3] = [2, 3, 5]; - let partitions = vec![QUANTUM as usize; BLOCKS]; - let frames = QUANTUM as usize * BLOCKS; - let hot = values_with(&[(0, 0.0), (1, 1.0), (2, 0.0), (5, 24.0), (6, 1.0)]); - let cool: Vec = (0..lanes).map(console_values).collect(); - for members in 2..=lanes { - for bypassed in [0, members - 1] { - let values: Vec<&Values> = (0..members) - .map(|member| { - if member == bypassed { - &hot - } else { - &cool[member] - } - }) - .collect(); - let requests: Vec<_> = values.iter().map(|values| request(*values)).collect(); - let noise = |member: usize, channel: u64| { - support::noise(frames, 0x1090_0000 + member as u64 * 2 + channel, 0.5) - }; - let benign: Vec = (0..members) - .map(|member| Planes { - left: noise(member, 0), - right: noise(member, 1), - }) - .collect(); - let mut tripping: Vec = (0..members) - .map(|member| Planes { - left: noise(member, 0), - right: noise(member, 1), - }) - .collect(); - for block in TRIPPING_BLOCKS { - let range = block * QUANTUM as usize..(block + 1) * QUANTUM as usize; - tripping[bypassed].left[range.clone()].fill(1.0e29); - tripping[bypassed].right[range].fill(-1.0e29); - } - let shunted = |inputs: &[Planes]| { - let mut bank = bind( - backend, - width, - &lane_requests(&requests, lanes), - &members_first(members, lanes), - ); - banked( - bank.as_mut(), - width, - inputs, - &partitions, - |shunt, left, right, frames| { - // `LiveControlEffectBankStage`'s restore: the bypassed lane's dry words, by - // copy. - let (dry_left, dry_right) = shunt.dry(); - for frame in 0..frames { - let word = frame * lanes + bypassed; - left[word] = dry_left[word]; - right[word] = dry_right[word]; - } - }, - ) - }; - let hot_render = shunted(&tripping); - let cool_render = shunted(&benign); - let oracle = per_node(&requests, &tripping, &partitions); - let context = format!("{members} of {lanes}, lane {bypassed} bypassed"); - for member in 0..members { - if member == bypassed { - let dry: Vec = tripping[member] - .left - .iter() - .chain(&tripping[member].right) - .map(|sample| sample.to_bits()) - .collect(); - assert_eq!( - hot_render[member].0, dry, - "{context}: the bypassed lane is dry" - ); - let rejected: u64 = hot_render[member] - .1 - .iter() - .map(|report| report.nonfinite_left_blocks + report.nonfinite_right_blocks) - .sum(); - assert!( - rejected >= 2 * TRIPPING_BLOCKS.len() as u64, - "{context}: the bypassed lane's wet block was not rejected ({rejected})" - ); - continue; - } - assert_eq!( - hot_render[member], cool_render[member], - "{context}: enabled member {member} moved with the bypassed lane's input" - ); - assert_eq!( - hot_render[member], oracle[member], - "{context}: enabled member {member} is not its per-node render" - ); - assert!( - hot_render[member] - .1 - .iter() - .all(|report| *report == ProcessReport::default()), - "{context}: enabled member {member} was reported" - ); - } - } - } -} - /// A padded lane is not a track: a state payload call that names one is `effect.state.track`, /// exactly as a lane past the width is, while every member's payload round-trips. /// diff --git a/crates/controls/tests/live_lanes.rs b/crates/controls/tests/live_lanes.rs index 724569d77..446f04935 100644 --- a/crates/controls/tests/live_lanes.rs +++ b/crates/controls/tests/live_lanes.rs @@ -2,54 +2,13 @@ mod common; -use common::{LAUNCH_EFFECTS, RATE, effects_fixture, fixture, insert_id}; -use controls::{ControlClass, ControlRow, Family, LiveLanes, MonitorOverlay, list, live_lanes}; -use session::{Effect, EffectIdentity, EffectQuality, LinkMode, SidechainDeclaration}; +use common::{RATE, effects_fixture, fixture}; +use controls::{ControlClass, ControlRow, Family, LiveLanes, MonitorOverlay, list}; fn rows(model: &session::SessionModel) -> Vec { list(model, &MonitorOverlay::default(), RATE).collect() } -/// Red if a live row's lane is not attached (a live edit would find no cell), or if a lane is -/// attached for a row that is not live (a session with no effect would pay for effect cells). -#[test] -fn the_effect_lane_follows_the_effect_rows() { - // No effect, no effect row, no lane. With today's classes the input rows (#1556) and the - // fixture's one send into a submix (#1557) are live. - let mut model = fixture(); - let input_only = LiveLanes { - strip_input: true, - routes: true, - ..LiveLanes::default() - }; - assert_eq!(live_lanes(&model), input_only); - - // One insert on one track gives the effect lane and no other. - let effect = LAUNCH_EFFECTS[1]; - model.tracks[0].inserts.effects.push(Effect { - id: insert_id(effect), - identity: EffectIdentity::Native { - effect_id: common::id(effect), - }, - quality: EffectQuality::Normal, - bypass: false, - link_mode: LinkMode::DualMono, - params: Vec::new(), - sidechain: SidechainDeclaration::None, - }); - assert_eq!( - live_lanes(&model), - LiveLanes { - effects: true, - ..input_only - } - ); - - // A console slot is an effect instance too. - let model = effects_fixture(); - assert!(live_lanes(&model).effects); -} - /// Red if the input or route lane ignores its family, or if a row that is not live asks for a /// lane: the test table flips every route row to derived, then every input row to derived. #[test] diff --git a/crates/controls/tests/monitor.rs b/crates/controls/tests/monitor.rs index a615b89ff..65c87e746 100644 --- a/crates/controls/tests/monitor.rs +++ b/crates/controls/tests/monitor.rs @@ -3,14 +3,13 @@ mod common; -use common::{RATE, fixture, id, strip_builtin, strip_own}; +use common::{RATE, fixture, id, strip_own}; use controls::{ ControlError, Lane, MonitorOverlay, MonitorRow, MonitorWrite, Owner, Section, check_monitor, - effective, set, + effective, }; const SOLO: u32 = 2; -const FADER_DB: u32 = 5; fn write(strip: &str, soloed: bool, ramp: Option) -> MonitorWrite { MonitorWrite { @@ -21,75 +20,6 @@ fn write(strip: &str, soloed: bool, ramp: Option) -> MonitorWrite { } } -/// A solo is checked like any write, by its row: a track's row takes exactly 0 or 1 on `both` -/// with an optional ramp, a submix has no solo row, and a model row is not a monitor write. -/// -/// Red if a submix becomes soloable, a malformed solo is accepted, a model row is taken for a -/// monitor write, or the write drops its ramp. -#[test] -fn a_solo_write_is_checked_by_its_row() { - let model = fixture(); - let check_solo = |strip: &str, lane, value, ramp| { - check_monitor(&model, &strip_own(strip, SOLO, lane), value, ramp, RATE) - }; - assert_eq!( - check_solo("vocal", Lane::Both, 1.0, Some(480)), - Ok(Some(write("vocal", true, Some(480)))) - ); - assert_eq!( - check_solo("guitar", Lane::Both, 0.0, None), - Ok(Some(write("guitar", false, None))) - ); - // A step is a length, not an absent one. - assert_eq!( - check_solo("vocal", Lane::Both, 1.0, Some(0)), - Ok(Some(write("vocal", true, Some(0)))) - ); - assert_eq!( - check_solo("bus", Lane::Both, 1.0, None), - Err(ControlError::AddressUnknown), - "a submix is solo-safe: it has no solo row" - ); - assert_eq!( - check_solo("nobody", Lane::Both, 1.0, None), - Err(ControlError::AddressUnknown) - ); - assert_eq!( - check_solo("vocal", Lane::Left, 1.0, None), - Err(ControlError::Lane) - ); - for value in [0.5, -0.0, 2.0, f32::NAN] { - assert_eq!( - check_solo("vocal", Lane::Both, value, None), - Err(ControlError::Domain), - "{value}" - ); - } - // A model row is for `set`: the writer says so without judging its value. - assert_eq!( - check_monitor( - &model, - &strip_builtin("vocal", FADER_DB, Lane::Both), - 99.0, - None, - RATE - ), - Ok(None) - ); - // And `set` refuses the monitor row, whatever transport reaches it. - let mut model = model; - assert_eq!( - set( - &mut model, - &strip_own("vocal", SOLO, Lane::Both), - 1.0, - None, - RATE - ), - Err(ControlError::Monitor) - ); -} - /// The overlay after a transaction's writes: in order, the last write of a strip wins, a strip the /// model no longer has is dropped, and the ramp is that of the last write that changed a strip. /// diff --git a/crates/controls/tests/row_descriptor.rs b/crates/controls/tests/row_descriptor.rs index 5c897a9b0..0f7cb573d 100644 --- a/crates/controls/tests/row_descriptor.rs +++ b/crates/controls/tests/row_descriptor.rs @@ -89,14 +89,3 @@ fn effect_sections_and_unknown_ids_have_no_descriptor() { ); assert_eq!(Section::Own.kind(), SectionKind::Own); } - -/// A rate-keyed row is resolved at the rate given. -#[test] -fn a_rate_keyed_row_follows_the_rate() { - let at = |rate| { - row_descriptor(OwnerKind::Strip, SectionKind::Builtins, 3, rate) - .expect("hpf_hz") - .maximum - }; - assert!(at(96_000) > at(44_100)); -} diff --git a/crates/delay/tests/randomized.rs b/crates/delay/tests/randomized.rs index 11bd8fa5e..f5b85e2b2 100644 --- a/crates/delay/tests/randomized.rs +++ b/crates/delay/tests/randomized.rs @@ -15,16 +15,3 @@ conformance::randomized_effect_test!( banks_natively: false, witness: true, ); - -/// #1278 D2a: the plan-swap carry restores every lane it carries, so a restore must accept every -/// state the effect itself reaches -- here each sampled state of a feedback, mix or cross-feedback -/// ramp to a domain edge, at every launch rate. -/// Since #1409 every ramp word stays between its start and its target, so the strict restore -/// (#1411) admits each snapshot. Red when the render site whose word the snapshot holds leaves a -/// ramp word outside its endpoints at render (a missing or reverted #1409 clamp), or when the -/// restore refuses a valid in-range snapshot. The fix is that clamp or that validation, never a -/// restore slack. -#[test] -fn the_effects_own_edge_ramp_snapshots_restore() { - conformance::EffectDifferential::assert_edge_ramps_restore(Box::new(delay::DelayFactory)); -} diff --git a/crates/dsp-reference/src/svf.rs b/crates/dsp-reference/src/svf.rs index 2bf0696df..ec42bb64f 100644 --- a/crates/dsp-reference/src/svf.rs +++ b/crates/dsp-reference/src/svf.rs @@ -444,7 +444,6 @@ mod tests { const DECIBELS_PER_NEPER: f64 = 8.685_889_638_065_035; /// Flat gate for every row whose design frequency is at or above 1 kHz. const FLAT_DECIBEL_TOLERANCE: f64 = 1e-9; - const IMPULSE_FRAMES: usize = 4_096; /// One RBJ cookbook evaluation plus the terms of its own conditioning bound. #[derive(Clone, Copy, Debug)] @@ -730,22 +729,6 @@ mod tests { ); } - #[test] - fn svf_recurrence_matches_state_space_impulse() { - let mut worst = 0.0_f64; - for row in rows() { - let coefficients = row.coefficients(); - let mut section = ReferenceSvf::new(coefficients); - let modelled = coefficients.state_space().impulse_response(IMPULSE_FRAMES); - for (frame, expected) in modelled.into_iter().enumerate() { - let input = if frame == 0 { 1.0 } else { 0.0 }; - worst = worst.max((section.process(input) - expected).abs()); - } - } - eprintln!("E2 worst_abs={worst:e}"); - assert!(worst <= 1e-12, "recurrence vs state space worst {worst:e}"); - } - #[test] fn zero_gain_bell_and_shelves_are_exact_identity() { for kind in [ diff --git a/crates/effect-compiler/src/prepare.rs b/crates/effect-compiler/src/prepare.rs index e0befd3c0..986b29a30 100644 --- a/crates/effect-compiler/src/prepare.rs +++ b/crates/effect-compiler/src/prepare.rs @@ -1545,17 +1545,6 @@ mod owner_tests { )); } - #[test] - fn invalid_edit_poison_survives_valid_overwrite_until_discard() { - let mut rig = OwnerRig::new(); - rig.owner.begin(0).expect("base revision"); - assert!(rig.owner.edit(2, ParameterChannel::Left, f32::NAN).is_err()); - assert!(rig.owner.edit(2, ParameterChannel::Left, 100.0).is_err()); - assert!(rig.owner.prepare_targets_into(&mut []).is_err()); - rig.owner.discard().expect("discard poisoned candidate"); - rig.owner.begin(0).expect("restart at unchanged revision"); - } - /// A target set wants no room: any number of transactions publish before a block reads, each /// is accepted, and the block applies the newest write of each cell. /// diff --git a/crates/effect-compiler/tests/parameter_lattice.rs b/crates/effect-compiler/tests/parameter_lattice.rs index cf897cb5d..295a36078 100644 --- a/crates/effect-compiler/tests/parameter_lattice.rs +++ b/crates/effect-compiler/tests/parameter_lattice.rs @@ -218,35 +218,6 @@ fn every_ladder_is_integer_multiples_of_the_step_and_stays_on_the_lattice() { } } -#[test] -fn every_builtin_row_declares_a_lawful_lattice_at_every_launch_rate() { - // The builtin table is the other half of the shipped surface, including the appended `pan` - // row and the two rate-keyed cutoffs whose ceiling moves with the prepared rate. - for rate in [44_100_u32, 48_000, 88_200, 96_000] { - for descriptor in &builtins::BUILTIN_PARAMETER_DESCRIPTORS { - let where_ = format!("builtin {} @ {rate}", descriptor.name); - let lattice = builtins::builtin_parameter_lattice_points(descriptor, rate) - .unwrap_or_else(|error| panic!("{where_}: unlawful: {error:?}")); - assert!(!lattice.points.is_empty(), "{where_}: empty"); - for point in &lattice.points { - assert!( - decimal_to_f32(&point.canonical).is_some(), - "{where_}: {} does not convert", - point.canonical - ); - } - // A declared disabled sentinel is an admitted extra value outside the ordered set, - // never a point that stepping can wander onto. - if let Some(disabled) = &lattice.disabled { - assert!( - lattice_index_for_decimal(&lattice.points, disabled).is_err(), - "{where_}: the disabled sentinel must sit outside the ordered lattice" - ); - } - } - } -} - #[test] fn the_appended_pan_row_is_the_persisted_pan_authority() { // #239 ruling 5461507633 B4: pan persists as pan. The row is stable-ID 12, per-lane, and diff --git a/crates/effect-contract/src/symmetry.rs b/crates/effect-contract/src/symmetry.rs index c6840227c..699b41c72 100644 --- a/crates/effect-contract/src/symmetry.rs +++ b/crates/effect-contract/src/symmetry.rs @@ -427,35 +427,6 @@ mod tests { ); } - #[test] - fn a_left_channel_parameter_desymmetrises_and_a_both_channel_one_does_not() { - let left = EffectControlRecord::Parameter { - parameter_index: 3, - channel: ParameterChannel::Left, - value: 0.5, - }; - let right = EffectControlRecord::Parameter { - parameter_index: 3, - channel: ParameterChannel::Right, - value: 0.5, - }; - let both = EffectControlRecord::Parameter { - parameter_index: 3, - channel: ParameterChannel::Both, - value: 0.5, - }; - assert_eq!(left.symmetry_event(), SymmetryEvent::Desymmetrize); - assert_eq!(right.symmetry_event(), SymmetryEvent::Desymmetrize); - assert_eq!(both.symmetry_event(), SymmetryEvent::Preserve); - - let mut witness = ChannelSymmetryWitness::SYMMETRIC; - witness.admit(&both); - assert!(witness.eligible(), "a both-channel command must preserve"); - witness.admit(&left); - assert!(!witness.eligible()); - assert_eq!(witness.declined(), ChannelSymmetryWitness::LIVE); - } - #[test] fn a_subscription_never_moves_the_witness() { let mut witness = ChannelSymmetryWitness::SYMMETRIC; diff --git a/crates/effect-contract/tests/lattice.rs b/crates/effect-contract/tests/lattice.rs index 9dfa90242..463e2721b 100644 --- a/crates/effect-contract/tests/lattice.rs +++ b/crates/effect-contract/tests/lattice.rs @@ -65,25 +65,6 @@ fn contains(points: &[LatticePoint], canonical: &str) -> bool { // Endpoints and defaults are lattice members by declaration (ruling B2/B3). // --------------------------------------------------------------------------- -#[test] -fn declared_bounds_and_default_are_always_members() { - // The parametric EQ's frequency row: 20 kHz is the round user-facing extreme, and a pure - // greatest-generated-point-below-max rule would have made it unreachable. - let points = hertz(10.0, 20_000.0, 80.0, 20.0); - assert_eq!(points.first().expect("minimum").canonical, "10.000"); - assert_eq!(points.last().expect("maximum").canonical, "20000.000"); - assert!(points.first().expect("minimum").intrinsic); - assert!(points.last().expect("maximum").intrinsic); - assert!( - points - .iter() - .any(|point| point.canonical == "80.000" && point.intrinsic) - ); - // 20 kHz is exactly spellable, which is the whole point of the amendment. - assert!(lattice_index_for_decimal(&points, "20000.0").is_ok()); - assert!(lattice_index_for_decimal(&points, "20000").is_ok()); -} - #[test] fn geometric_ratio_row_keeps_its_round_maximum_and_meaningful_default() { // The compressor ratio row: 20:1 is the declared maximum, so `set ratio 20` is legal. diff --git a/crates/effect-runtime/tests/MUTATIONS.md b/crates/effect-runtime/tests/MUTATIONS.md index 320faf369..a443898b2 100644 --- a/crates/effect-runtime/tests/MUTATIONS.md +++ b/crates/effect-runtime/tests/MUTATIONS.md @@ -257,3 +257,10 @@ The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) delete - `the_corpus_is_width_independent`: no plausible same-crate KEEP catcher recorded - `a_knee_whose_reciprocal_overflows_is_a_hard_knee`: effect-runtime `dynamics::knee_words_follow_the_finite_reciprocal_rule` - `the_narrowest_soft_knees_are_finite_at_the_threshold`: effect-runtime `dynamics::knee_words_follow_the_finite_reciprocal_rule` + +## TA5 deletions + +The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. + +- `words_are_little_endian`: effect-runtime `float_words_round_trip_by_bits` +- `snapshot_then_restore_returns_every_word`: effect-runtime `float_words_round_trip_by_bits` diff --git a/crates/effect-runtime/tests/dynamics.rs b/crates/effect-runtime/tests/dynamics.rs index 1bc770b36..23231cb2e 100644 --- a/crates/effect-runtime/tests/dynamics.rs +++ b/crates/effect-runtime/tests/dynamics.rs @@ -132,24 +132,6 @@ fn below_the_knee_is_the_exact_identity() { } } -/// A ratio of 1 is a no-op at every level, and a ratio below 1 expands upward. -#[test] -fn the_ratio_sets_the_slope() { - let unity = GainComputerCoef::::new(-18.0, 1.0, 6.0); - for level in [-160.0f32, -18.0, 0.0, 24.0] { - assert_eq!( - gain_delta_db::(level, &unity).to_bits(), - 0.0f32.to_bits(), - "ratio 1 must be an identity at {level} dB" - ); - } - let expander = GainComputerCoef::::new(-40.0, 0.5, 0.0); - assert!( - gain_delta_db::(-20.0, &expander) > 0.0, - "a ratio below 1 expands upward" - ); -} - /// The two dB conversions are exact at unity and are inverses to within a rounding. #[test] fn the_db_conversions_agree() { diff --git a/crates/effect-runtime/tests/partition.rs b/crates/effect-runtime/tests/partition.rs index debd96693..abf6078a8 100644 --- a/crates/effect-runtime/tests/partition.rs +++ b/crates/effect-runtime/tests/partition.rs @@ -124,11 +124,6 @@ fn assert_partition_invariant(width: &str) { } } -#[test] -fn the_composition_is_partition_invariant_at_width_one() { - assert_partition_invariant::("W=1"); -} - #[test] fn the_composition_is_partition_invariant_at_width_four() { assert_partition_invariant::("W=4"); diff --git a/crates/effect-runtime/tests/state_payload.rs b/crates/effect-runtime/tests/state_payload.rs index ae3f1a501..745f2ed86 100644 --- a/crates/effect-runtime/tests/state_payload.rs +++ b/crates/effect-runtime/tests/state_payload.rs @@ -2,8 +2,8 @@ use effect_runtime::state_payload::{ HEADER_WORDS, STATE_LENGTH_CODE, STATE_VERSION_CODE, StateLayout, StatePayloadInput, - StatePayloadOutput, StateWords, StateWordsMut, expected_sizes, read_f32, read_header, read_u32, - restore, snapshot, validate_lengths, write_f32, write_header, write_u32, + StatePayloadOutput, StateWords, StateWordsMut, expected_sizes, read_f32, read_header, restore, + snapshot, validate_lengths, write_f32, write_header, }; const LAYOUT: StateLayout = StateLayout { @@ -31,20 +31,6 @@ fn expected_sizes_account_for_the_header() { assert_eq!(LAYOUT.data_words(), 2 + 2 * 5); } -/// Words are little-endian on every target. -/// -/// Red mutation: `write_u32` uses `to_be_bytes`, or `read_u32` uses `from_be_bytes`. -#[test] -fn words_are_little_endian() { - let mut bytes = [0u8; 8]; - write_u32(&mut bytes, 0, 0x1122_3344); - assert_eq!(&bytes[0..4], &[0x44, 0x33, 0x22, 0x11]); - write_u32(&mut bytes, 1, 0x0000_00ff); - assert_eq!(&bytes[4..8], &[0xff, 0x00, 0x00, 0x00]); - assert_eq!(read_u32(&bytes, 0), 0x1122_3344); - assert_eq!(read_u32(&bytes, 1), 0x0000_00ff); -} - /// `f32` words round-trip by bit pattern, including the signed zeros and a subnormal. #[test] fn float_words_round_trip_by_bits() { @@ -71,52 +57,6 @@ fn float_words_round_trip_by_bits() { } } -#[test] -fn snapshot_then_restore_returns_every_word() { - let (mut common, mut left, mut right) = buffers(); - let source_common = [0xdead_beef_u32, 7]; - let source_left = [1_u32, 2, 3, 4, 5]; - let source_right = [0xffff_ffff_u32, 0, 0x8000_0000, 42, 0x7f80_0000]; - snapshot( - &LAYOUT, - &StateWords { - common: &source_common, - left: &source_left, - right: &source_right, - }, - &mut StatePayloadOutput { - common: &mut common, - left: &mut left, - right: &mut right, - }, - ) - .expect("snapshot into correctly sized buffers"); - - assert_eq!(read_u32(&common, 0), LAYOUT.version, "version word"); - assert_eq!(read_u32(&common, 1), LAYOUT.data_words(), "word count"); - - let mut out_common = [0u32; 2]; - let mut out_left = [0u32; 5]; - let mut out_right = [0u32; 5]; - restore( - &LAYOUT, - &StatePayloadInput { - common: &common, - left: &left, - right: &right, - }, - &mut StateWordsMut { - common: &mut out_common, - left: &mut out_left, - right: &mut out_right, - }, - ) - .expect("restore from a payload this codec wrote"); - assert_eq!(out_common, source_common); - assert_eq!(out_left, source_left); - assert_eq!(out_right, source_right); -} - /// A payload that is one byte short, or one byte long, is rejected — both, and with the same code. /// /// Red mutation: `validate_lengths` compares with `<` instead of `!=`, which accepts a payload diff --git a/crates/effect-runtime/tests/stationary_hoist.rs b/crates/effect-runtime/tests/stationary_hoist.rs index 9d2d4b2ea..291671695 100644 --- a/crates/effect-runtime/tests/stationary_hoist.rs +++ b/crates/effect-runtime/tests/stationary_hoist.rs @@ -22,25 +22,6 @@ fn assert_constant(mut ramp: LinearRamp, expected: f32, count: usize) { assert_eq!(ramp, LinearRamp::fixed(expected)); } -/// Redundant finite retargets settle immediately and keep every rendered word. -#[test] -fn a_redundant_retarget_is_constant_and_settled() { - for value in [ - 0.0_f32, - 1.0, - -1.0, - 0.5, - -24.0, - 1.0e-7, - f32::MIN_POSITIVE, - f32::MAX, - ] { - let mut ramp = LinearRamp::fixed(value); - ramp.set_target(value, 64); - assert_constant(ramp, value, 80); - } -} - /// Negative zero arms: intermediate additions produce +0.0 and the final snap restores -0.0. #[test] fn negative_zero_remains_armed_and_snaps_back_to_its_sign() { diff --git a/crates/engine/src/realtime/latest_cell.rs b/crates/engine/src/realtime/latest_cell.rs index debb67193..81cc593fe 100644 --- a/crates/engine/src/realtime/latest_cell.rs +++ b/crates/engine/src/realtime/latest_cell.rs @@ -1260,47 +1260,6 @@ mod tests { let _ = RevisionGate::pair(GATE_REVISION_MAX + 1); } - /// Root ruling n3 (#1432 D1): the stamp's revision bound is on in every build and a refusal - /// changes nothing. In a release build an unchecked stamp at the published revision would - /// make the in-place rule rewrite the slot render may be reading. - #[test] - fn a_stamp_at_or_below_the_published_revision_or_above_the_ceiling_is_refused_and_changes_nothing() - { - let (gate, mut pin) = RevisionGate::pair(5); - let (cell, dirty) = (LatestCell::<2>::new(), DirtyWord::new()); - let (mut writer, mut reader, mut stage) = ( - CellWriter::<2>::new(), - CellReader::new(), - DirtyReader::new(), - ); - for (revision, reason) in [ - (5, StampRefusal::NotAbovePublished), - (3, StampRefusal::NotAbovePublished), - (GATE_REVISION_MAX + 1, StampRefusal::AboveCeiling), - (u64::MAX, StampRefusal::AboveCeiling), - ] { - assert_eq!( - pin.stamp(revision).map(|_| ()), - Err(StampRefused { - revision, - last_published: 5, - reason - }) - ); - } - assert_eq!(pin.last_published(), 5); - // In a debug build this stamp would panic if a refusal had set `open`. - let stamp = pin.stamp(7).expect("a revision above the published one"); - writer.write(&cell, &dirty, 0, stamp, value_words(7, 0)); - pin.publish(&gate, 7); - let snapshot = gate.snapshot(); - assert_eq!(stage.pending(&dirty), 1); - let CellRead::Applied { words, .. } = reader.read(&cell, snapshot, &mut stage, 0) else { - panic!("the value applies in the first block"); - }; - assert_eq!(words, value_words(7, 0)); - } - #[cfg(debug_assertions)] #[test] #[should_panic(expected = "is not above the last published")] diff --git a/crates/engine/src/realtime/mod.rs b/crates/engine/src/realtime/mod.rs index d641a3603..457b9c88d 100644 --- a/crates/engine/src/realtime/mod.rs +++ b/crates/engine/src/realtime/mod.rs @@ -2174,32 +2174,6 @@ mod tests { (blocks, reports, owner, hooks.load(Ordering::Relaxed)) } - #[test] - fn successor_continues_the_predecessor_state_gap_free() { - let reference = reference(); - let (blocks, reports, owner, hooks) = swap_at_block_five(true); - assert_eq!(blocks, reference); - assert_eq!(hooks, 1); - for (block, report) in reports.iter().enumerate() { - let (swap, carry) = if block == 5 { - (SwapOutcome::Applied, CarryOutcome::Carried) - } else { - (SwapOutcome::None, CarryOutcome::NotRequested) - }; - assert_eq!((report.swap, report.carry), (swap, carry), "block {block}"); - } - assert_eq!( - (owner.carried_count(), owner.carry_mismatch_count()), - (1, 0) - ); - - let (blocks, reports, owner, _) = swap_at_block_five(false); - assert_eq!(blocks[..5], reference[..5]); - assert_ne!(blocks[5], reference[5]); - assert_eq!(reports[5].carry, CarryOutcome::NotRequested); - assert_eq!(owner.carried_count(), 0); - } - #[test] fn a_mismatched_predecessor_is_reported_not_carried() { let hooks = Arc::new(AtomicUsize::new(0)); diff --git a/crates/engine/src/realtime/spsc.rs b/crates/engine/src/realtime/spsc.rs index 0ff69130f..7438d639a 100644 --- a/crates/engine/src/realtime/spsc.rs +++ b/crates/engine/src/realtime/spsc.rs @@ -1512,8 +1512,7 @@ mod tests { use super::{QueueGeneration, bounded_spsc, bounded_spsc_internal}; use core::num::NonZeroUsize; use std::sync::Arc; - use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; - use std::time::{Duration, Instant}; + use std::sync::atomic::{AtomicUsize, Ordering}; struct DropProbe(Arc); @@ -1540,118 +1539,6 @@ mod tests { assert_eq!(drops.load(Ordering::Relaxed), 2); } - /// Sets its flag when dropped, so a peer learns that this thread left its loop however it left - /// it. `engine` cannot use `bench_support::producer::StopOnDrop`: `bench-support` depends on - /// `engine` (#1251 D3). - struct EndedOnDrop(Arc); - - impl Drop for EndedOnDrop { - fn drop(&mut self) { - self.0.store(true, Ordering::Release); - } - } - - /// How long one side of the stress waits on a full or empty queue before it fails. The peer - /// moves the queue within microseconds whenever it is running. - const STALL_DEADLINE: Duration = Duration::from_secs(10); - - /// Fails the side that waits on the queue once its peer has ended, or after - /// [`STALL_DEADLINE`] without progress. `peer_ended` must have been read before the failed - /// queue operation: a peer that ended published its last operation first, so a queue still - /// full or empty after it ended stays that way. - fn fail_if_stalled( - peer_ended: bool, - stalled_since: &mut Option, - side: &str, - peer: &str, - position: u64, - ) { - assert!( - !peer_ended, - "spsc stress: the {peer} ended while the {side} waited at item {position}" - ); - let since = *stalled_since.get_or_insert_with(Instant::now); - assert!( - since.elapsed() < STALL_DEADLINE, - "spsc stress: the {side} made no progress at item {position} within {STALL_DEADLINE:?}" - ); - } - - #[test] - fn concurrent_spsc_stress() { - const ITEMS: u64 = 1_000_000; - let (mut producer, mut consumer) = bounded_spsc( - NonZeroUsize::new(127).expect("capacity"), - QueueGeneration(7), - ) - .expect("queue"); - // Each thread marks its end however it leaves its loop, so a failed assertion on one side - // fails the other's wait instead of leaving it spinning on a full or empty queue (#1251). - let producer_ended = Arc::new(AtomicBool::new(false)); - let consumer_ended = Arc::new(AtomicBool::new(false)); - let (producer_end, consumer_peer) = - (Arc::clone(&producer_ended), Arc::clone(&consumer_ended)); - let producer_thread = std::thread::spawn(move || { - let _ended = EndedOnDrop(producer_end); - let mut next = 0; - let mut stalled_since = None; - while next < ITEMS { - let peer_ended = consumer_peer.load(Ordering::Acquire); - match producer.try_push(next) { - Ok(()) => { - next += 1; - stalled_since = None; - } - Err(full) => { - assert_eq!(full.value, next); - fail_if_stalled( - peer_ended, - &mut stalled_since, - "producer", - "consumer", - next, - ); - std::thread::yield_now(); - } - } - } - producer - }); - let consumer_thread = std::thread::spawn(move || { - let _ended = EndedOnDrop(consumer_ended); - let mut expected = 0; - let mut checksum = 0_u128; - let mut stalled_since = None; - while expected < ITEMS { - let peer_ended = producer_ended.load(Ordering::Acquire); - match consumer.try_pop() { - Ok(value) => { - assert_eq!(value, expected); - checksum = checksum.wrapping_add(u128::from(value)); - expected += 1; - stalled_since = None; - } - Err(_) => { - fail_if_stalled( - peer_ended, - &mut stalled_since, - "consumer", - "producer", - expected, - ); - std::thread::yield_now(); - } - } - } - (consumer, checksum) - }); - let producer = producer_thread.join().expect("producer thread"); - let (consumer, checksum) = consumer_thread.join().expect("consumer thread"); - assert_eq!(producer.success_count(), ITEMS); - assert_eq!(consumer.success_count(), ITEMS); - assert_eq!(checksum, u128::from(ITEMS) * u128::from(ITEMS - 1) / 2); - } - /// The queue layout retains cursor cache-line alignment without pinning resource bytes. #[cfg(target_pointer_width = "64")] #[test] diff --git a/crates/graph-compiler/src/lib.rs b/crates/graph-compiler/src/lib.rs index ade54cdfb..e0fcea04d 100644 --- a/crates/graph-compiler/src/lib.rs +++ b/crates/graph-compiler/src/lib.rs @@ -1947,83 +1947,6 @@ mod tests { } } - #[test] - fn indexed_topo_explicit_controls_preserve_contract() { - let nodes = vec![ - graph_node("z", 0, TailSamples::Finite(0)), - graph_node("a", 0, TailSamples::Finite(0)), - graph_node("m", 0, TailSamples::Finite(0)), - graph_node("b", 0, TailSamples::Finite(0)), - graph_node("q", 0, TailSamples::Finite(0)), - ]; - let edges = vec![ - edge("a-z-1", "a", "z"), - edge("m-z", "m", "z"), - edge("a-z-2", "a", "z"), - edge("b-m", "b", "m"), - ]; - let expected_names: &[(&[&str], u64)] = &[ - (&["submix:a", "submix:b", "submix:q"], 0), - (&["submix:m"], 1), - (&["submix:z"], 2), - ]; - let oracle_accepts = |candidate: &[DependencyLevel]| { - candidate.len() == expected_names.len() - && candidate - .iter() - .zip(expected_names) - .all(|(level, (names, number))| { - level.level == *number - && level.nodes.iter().map(node_text).collect::>() - == names - .iter() - .map(|name| (*name).to_owned()) - .collect::>() - }) - }; - - let actual = topo(&nodes, &edges).expect("explicit DAG is acyclic"); - assert!( - oracle_accepts(&actual), - "independent level oracle rejected topo output" - ); - let mut wrong_result = actual.clone(); - wrong_result[2].level = 1; - assert!( - !oracle_accepts(&wrong_result), - "independent level oracle accepted an incorrect level" - ); - - let mut reversed_nodes = nodes.clone(); - reversed_nodes.reverse(); - let mut reversed_edges = edges.clone(); - reversed_edges.reverse(); - assert_eq!(topo(&reversed_nodes, &reversed_edges), Some(actual.clone())); - assert_eq!(topo(&[], &[]), Some(Vec::new())); - - let cycle = [edge("cycle-a-b", "a", "b"), edge("cycle-b-a", "b", "a")]; - assert_eq!(topo(&nodes, &cycle), None); - assert_eq!(topo(&nodes, &[edge("self-loop", "a", "a")]), None); - assert_eq!( - topo(&nodes, &[edge("dangling-source", "missing", "a")]), - None - ); - assert_eq!( - topo(&nodes, &[edge("dangling-destination", "a", "missing")]), - None - ); - assert_eq!( - topo( - &[ - graph_node("a", 0, TailSamples::Finite(0)), - graph_node("a", 0, TailSamples::Finite(0)), - ], - &[], - ), - None - ); - } - fn caps(maximum_finite_tail_samples: u64) -> GraphCompileCaps { GraphCompileCaps { maximum_nodes: 100, @@ -7506,68 +7429,6 @@ mod tests { ); } - /// A send taken from an elided rack boundary declines the merge, through the second-reader - /// clause rather than the observer one. - /// - /// Issue #181 declined on the *presence* of a `program::Tap`, which made this case and the - /// metered one indistinguishable. They are not the same: a tap is a name, and an edge out of - /// the aliased stage resolves through it to the compressor, so it is counted by - /// `runtime::op_dataflow` as an ordinary second reader of the compressor's output. That clause - /// is what refuses here, and it has to, because a merged chain would send pre-compressor audio. - #[test] - fn a_send_from_a_rack_boundary_declines_the_merge() { - const BLOCKS: u64 = 12; - let Some(width) = BankWidth::for_backend(host_dispatch()) else { - return; - }; - let cohorts = 64 / width.lanes() as u64; - let mut sent = parse_session_json(CONSOLE_SIXTY_FOUR_TRACK_INTENDED_FIXTURE) - .expect("intended fixture"); - let template = sent.routes[0].clone(); - let mut route = template.clone(); - route.id = StableId::parse("ch00-simd1-send").expect("route id"); - route.source = RouteSource::Track { - track_id: StableId::parse("ch00").expect("track id"), - tap: session::SendTap::InsertSend, - }; - sent.routes.push(route); - sent.routes.sort_by(|left, right| left.id.cmp(&right.id)); - - let registry = launch_native_effect_registry().expect("launch registry"); - let artifact = compile_console_model_with_builtins(&sent, 2_040, &[], registry); - let (pcm, transposes, chains, slots, _, redirects, _) = - render_console_builtins_blocks(artifact, BLOCKS, Vec::new()); - assert_eq!( - slots, - STRIP_SLOTS_PER_COHORT * cohorts, - "the send changes no bank's membership" - ); - assert_eq!( - chains, - cohorts + 1, - "ch00's cohort splits at the sent boundary; every other cohort stays one chain" - ); - assert_eq!(transposes, BLOCKS * chains, "G5 on the declining plan"); - let scalar_artifact = compile_console_oracle(&sent, 2_041, &[]); - let (scalar_pcm, ..) = render_console_builtins_blocks(scalar_artifact, BLOCKS, Vec::new()); - assert_pcm_bits_equal(&pcm, &scalar_pcm, "64-track strip with a post-simd1 send"); - assert!( - pcm.iter().flatten().any(|sample| *sample != 0.0), - "the sent strip rendered audio" - ); - // The redirect count is zero for every lane on this fixture since issue #212 -- see - // `the_intended_strip_fuses_the_whole_signal_path_into_one_chain_per_cohort` for why the - // strip's chain now ends in a buffer its consumer already reads in place. What this test - // still says is that a send taken from `PostSimd1` changes nothing at the *far end* of the - // strip: it splits ch00's cohort at the observed boundary, and the scatter accounting on - // every lane, ch00's included, is exactly what it is without the send. - assert_eq!( - redirects, 0, - "a send taken from `PostSimd1` is upstream of the limiter, so it declines the chain \ - merge without touching the scatter accounting at the far end of the strip" - ); - } - /// Mono-collapse M1: the two bank planners classify every track alike, and the proof is the /// thing the merge actually needs -- their banks cover the same lanes in the same order. /// @@ -9223,100 +9084,6 @@ mod tests { ); } - /// A meter on the pre-fader alias splits its cohort's chain there, redirects nothing, and reads - /// the limiter's output. - /// - /// `PostSimd2PreFader` is elided into a `program::Tap` on the limiter's op, so a meter leased - /// there reads the limiter's own buffer. This test was written (issue #202 rec 3) for a - /// `scatter_target` clause that declined a redirect when the last slot's alias was observed, - /// back when the limiter *was* the last slot. Issue #212 made the fader and matrix slots of the - /// same chain, so the alias now sits *inside* it, and the clause that answers is - /// `chains_into`'s observed-alias clause, keyed on the **alias node** -- keying it on the - /// producing node would miss it: nobody observes the limiter, the observer is bound to - /// `ch00/PostSimd2PreFader`. Issue #886 then removed the redirect's observer clauses outright - /// (an observer of a redirected last slot reads the redirected buffer), so no redirect is - /// declined here at all: the split chain's consumer is the fader bank, which the gather-side - /// redirect serves, and every other chain still ends in a buffer its consumer reads in place. - #[test] - fn a_pre_fader_meter_splits_its_cohorts_chain_and_reads_the_limiter() { - const BLOCKS: u64 = 12; - let Some(width) = BankWidth::for_backend(host_dispatch()) else { - return; - }; - let cohorts = 64 / width.lanes() as u64; - let intended = parse_session_json(CONSOLE_SIXTY_FOUR_TRACK_INTENDED_FIXTURE) - .expect("intended fixture"); - let meters = vec![MeterRequest { - handle: MeterHandle(NonZeroU64::new(1).expect("constant")), - track_id: "ch00".to_owned(), - tap: MeterTap::PostSimd2PreFader, - config: MeterConfig { - period_frames: NonZeroU32::new(128).expect("constant"), - peak_hold_frames: 0, - peak_decay_db_per_second: 0.0, - queue_capacity: NonZeroUsize::new(64).expect("constant"), - reset_generation: 0, - }, - }]; - let registry = launch_native_effect_registry().expect("launch registry"); - let artifact = compile_console_model_with_builtins(&intended, 2_060, &meters, registry); - let (pcm, transposes, chains, slots, frames, redirects, _) = - render_console_builtins_blocks(artifact, BLOCKS, Vec::new()); - assert_eq!( - slots, - STRIP_SLOTS_PER_COHORT * cohorts, - "the meter changes no bank's membership" - ); - // Issue #212 moved this boundary from the end of the chain to the middle of it. When the - // chain was `builtins -> EQ -> compressor -> limiter`, `PostSimd2PreFader` aliased the - // *last* slot's output and was downstream of every merge, so an observer there declined - // nothing. The fader and the matrix are now slots of the same chain, so that alias sits - // between two of them -- and an observer on it declines the limiter -> fader merge, exactly - // as `chains_into`'s observed-alias clause says it must. - // - // This is the documented cliff, and it is a cost paid only when the observer is there: - // leasing a stage meter at `PostSimd2PreFader` costs that track's cohort one extra - // planar/AoSoA round-trip per block, because its chain can no longer span the stage the - // meter reads. The meter must see pre-fader audio and a merged chain would hand it the - // chain's input, so this is the intended trade -- and both halves of it are pinned here. - assert_eq!( - chains, - cohorts + 1, - "the meter sits on an alias *inside* the chain, so ch00's cohort splits there" - ); - assert_eq!(transposes, BLOCKS * chains); - assert_eq!( - redirects, 0, - "the split chain feeds a banked fader and every other chain ends in a buffer its \ - consumer reads in place, so nothing redirects, metered or not" - ); - - // And the meter reads the limiter's output. The oracle is the same session with every - // effect on the per-node scalar path, where no chain -- and so no redirect -- exists. - let scalar_artifact = compile_console_oracle(&intended, 2_061, &meters); - let (scalar_pcm, _, _, _, scalar_frames, _, _) = - render_console_builtins_blocks(scalar_artifact, BLOCKS, Vec::new()); - assert_pcm_bits_equal( - &pcm, - &scalar_pcm, - "64-track strip with a pre-fader stage meter", - ); - assert!( - !frames.is_empty(), - "the leased meter must publish windows, or this test proves nothing about it" - ); - assert_eq!( - frames, scalar_frames, - "the metered boundary must read the limiter's output, not the previous block's words" - ); - assert!( - frames - .iter() - .any(|frame| frame.left.sample_peak != 0.0 || frame.right.sample_peak != 0.0), - "the metered windows must carry signal" - ); - } - /// A second reader of the last slot's output declines that lane's scatter redirect. /// /// The sole-readership clause, reached the way a session reaches it: a send taken from @@ -9351,8 +9118,8 @@ mod tests { "the send changes no bank's membership" ); // The same boundary move as - // `a_pre_fader_meter_splits_its_cohorts_chain_and_reads_the_limiter`, through the other - // clause. A send taken from `PostSimd2PreFader` gives ch00's limiter output a second + // `a_pre_fader_meter_splits_its_cohorts_chain_and_reads_the_limiter` (deleted by TA5), + // through the other clause. A send taken from `PostSimd2PreFader` gives ch00's limiter output a second // reader, and since #212 that alias sits *inside* the chain rather than at its end -- so // `chains_into`'s sole-readership clause declines the limiter -> fader merge and ch00's // cohort splits there. A send from a stage the chain does not span still costs nothing. @@ -9375,78 +9142,6 @@ mod tests { ); } - /// Issue #218: the intended strip folds every route and the whole master reduction into its - /// cohorts' epilogues, and the bits do not move. - /// - /// # Two oracles - /// - /// The standing one is the test-only `Scalar` backend (#1059), where nothing banks, effect or - /// builtin, so it forms no chain and folds no route. Until #1098 it was a registry that put - /// every *effect* on the per-node path while the builtins, fader and matrix still banked and - /// folded all sixty-four routes: an oracle for the effects and not for the fold. A console slot - /// no longer renders per node on a vector backend, so that arm is gone, and its fold count is - /// asserted here as zero rather than assumed. - /// - /// The fold's own oracle is the same session bound with the fold **declined** - /// (`graph::test_only_set_route_fold_declined`). That arm renders the route ops and the D9 - /// reduction the fold replaced, and nothing else about the plan moves, so the two arms differ in - /// exactly the thing under test. Until issue #885 the decline came from a post-matrix meter on - /// ch00; a post-matrix meter now keeps the fold armed - /// (`a_meter_on_the_matrix_keeps_the_route_fold_and_still_meters`), so the decline is explicit. - #[test] - fn the_intended_strip_folds_every_route_into_its_cohorts_epilogue() { - const BLOCKS: u64 = 12; - let Some(width) = BankWidth::for_backend(host_dispatch()) else { - return; - }; - let cohorts = 64 / width.lanes() as u64; - let intended = parse_session_json(CONSOLE_SIXTY_FOUR_TRACK_INTENDED_FIXTURE) - .expect("intended fixture"); - let registry = launch_native_effect_registry().expect("launch registry"); - let artifact = compile_console_model_with_builtins(&intended, 2_180, &[], registry); - let (pcm, transposes, chains, slots, _, _, folds) = - render_console_builtins_blocks(artifact, BLOCKS, Vec::new()); - assert_eq!( - chains, cohorts, - "the whole strip is still one chain per cohort" - ); - assert_eq!(slots, STRIP_SLOTS_PER_COHORT * cohorts); - assert_eq!(transposes, BLOCKS * chains, "G5 is unmoved by the fold"); - assert_eq!( - folds, 64, - "every track's route folds into its cohort's epilogue" - ); - let scalar_artifact = compile_console_oracle(&intended, 2_181, &[]); - let (scalar_pcm, _, _, _, _, _, scalar_folds) = - render_console_builtins_blocks(scalar_artifact, BLOCKS, Vec::new()); - assert_eq!( - scalar_folds, 0, - "the Scalar oracle banks nothing, so it folds nothing" - ); - assert_pcm_bits_equal(&pcm, &scalar_pcm, "64-track strip with the route fold"); - - // The fold's own oracle: the same session, bound with the fold declined. - let unfolded_artifact = - compile_console_model_with_builtins(&intended, 2_188, &[], registry); - graph::test_only_set_route_fold_declined(true); - let (unfolded_pcm, _, _, _, _, _, unfolded_folds) = - render_console_builtins_blocks(unfolded_artifact, BLOCKS, Vec::new()); - graph::test_only_set_route_fold_declined(false); - assert_eq!( - unfolded_folds, 0, - "the declined arm must not fold, or it is not an oracle for it" - ); - assert_pcm_bits_equal( - &pcm, - &unfolded_pcm, - "the folded master against the reduction", - ); - assert!( - pcm.iter().flatten().any(|sample| *sample != 0.0), - "the folded strip rendered audio" - ); - } - /// A second reader of a track's matrix declines the route fold. /// /// The sole-readership clause, reached the way a session reaches it: ch00 is routed to the diff --git a/crates/graph-compiler/tests/bypass_cohorts.rs b/crates/graph-compiler/tests/bypass_cohorts.rs index 4073c8f29..9739c5bd5 100644 --- a/crates/graph-compiler/tests/bypass_cohorts.rs +++ b/crates/graph-compiler/tests/bypass_cohorts.rs @@ -782,55 +782,3 @@ fn a_mixed_bypass_multiband_cohort_keeps_its_prepared_bypass() { "the render is audible" ); } - -/// Issue #1100, gate 3 (#1087 verdict, M2): one bypassed multiband lane fed `6e29` leaves its -/// enabled neighbours' bits unchanged. -/// -/// `6e29` is a legal sample: the input stage passes anything finite below `1e30`. At the -/// multiband's defaults it trips the block-boundary check (D7) of any bank it runs in, and that -/// check zeroes the whole bank. The positive control is #1087's lowering, rebuilt here: there the -/// hot bypassed lane shares a bank with enabled ones and silences them. With the prepared bypass -/// kept, the bypassed instance never shares a bank with an enabled one, and every enabled track -/// renders the same words whatever the bypassed track is fed. -/// -/// Red mutation: drop the multiband from `PREPARED_BYPASS_EFFECTS` -> the enabled neighbours of -/// the hot lane are zeroed. -#[test] -fn a_hot_bypassed_multiband_lane_leaves_its_neighbours_bits_unchanged() { - const TRACKS: usize = 8; - const HOT: u32 = 3; - let model = multiband_console(TRACKS, 1 << HOT); - let neighbours = |rendered: &Rendered| -> BTreeMap<(String, String), Vec> { - rendered - .nodes - .iter() - .filter(|((track, _), _)| track != &format!("ch{HOT:02}")) - .map(|(key, words)| (key.clone(), words.clone())) - .collect() - }; - for lowering in [Lowering::Session, Lowering::P1] { - let quiet = render_with(&model, Backend::current(), lowering, &|_| Feed::Music); - let hot = render_with(&model, Backend::current(), lowering, &|track| { - if track == HOT { Feed::Hot } else { Feed::Music } - }); - let moved = neighbours(&quiet) - .iter() - .zip(neighbours(&hot).iter()) - .filter(|((key, quiet), (_, hot))| { - assert_eq!(quiet.len(), hot.len(), "{key:?}: length"); - folded(quiet) != folded(hot) - }) - .count(); - match lowering { - Lowering::Session => assert_eq!( - moved, 0, - "a hot bypassed multiband lane moved {moved} of its enabled neighbours' taps" - ), - _ => assert!( - moved > 0, - "the positive control: under #1087's lowering the hot lane must reach its \ - bank-mates, or this proves nothing" - ), - } - } -} diff --git a/crates/graph-compiler/tests/bypass_shunt_identity.rs b/crates/graph-compiler/tests/bypass_shunt_identity.rs index 3a46b113c..821ccb826 100644 --- a/crates/graph-compiler/tests/bypass_shunt_identity.rs +++ b/crates/graph-compiler/tests/bypass_shunt_identity.rs @@ -608,181 +608,6 @@ fn a_shunt_bypassed_per_node_instance_is_bit_identical_to_prepared_bypass() { } } -/// Gate 3 with real effects: a live toggle on a session-bypassed banked lane. -/// -/// With live controls attached, the session bypass seeds the lane's live channel -/// (`attach_effect_live_controls` reads `initial_bypass`), so the lane starts bypassed and a live -/// `Bypass(false)` returns the *current* wet signal: its wet path ran all along. The reference is -/// the same lane rendered per node from a prepared-enabled instance and a shunt switched on the -/// same blocks, which is the per-node live path a session-enabled instance has always taken. The -/// limiter carries the longest latency line, so the dry signal after the re-bypass is the delayed -/// one on its first block. -/// -/// Red mutation: the drain leaves `bypass` alone on an `EffectControlRecord::Bypass` record -> -/// lane 1 stays bypassed after block 5 and renders dry where the reference is wet. (That -/// `attach_effect_live_controls` seeds the live lane from the session bypass is pinned in -/// `effect-compiler`'s `native_session` tests.) -#[test] -fn a_live_toggle_on_a_session_bypassed_bank_lane_matches_the_per_node_live_path() { - let registry = launch_native_effect_registry().expect("launch registry"); - let width = width(); - let lanes = width.lanes() as usize; - for effect_id in [ - "miso.true-peak-limiter", - "miso.parametric-eq", - "miso.soft-clip", - ] { - let factory = registry.get_shared_ascii(effect_id).expect("launch effect"); - let descriptor = factory.descriptor(); - let case = cases(descriptor)[0]; - let mut rng = Rng::new(0x1087); - let values: Vec> = (0..lanes) - .map(|_| initial_values(descriptor, &mut rng)) - .collect(); - let requests: Vec> = values - .iter() - .map(|values| request(descriptor, case, false, values)) - .collect(); - let bank = factory - .bind_homogeneous_bank(PrepareEffectBankRequest { - backend: Backend::current(), - width, - requests: &requests, - active_mask: width.full_mask(), - }) - .expect("a well-formed bank request") - .expect("a bankable effect binds at the host width"); - let latency = usize::try_from(bank.metadata.program_key.latency.0).expect("latency fits"); - // Lane 1 is session-bypassed with a live channel; lane 2 is session-bypassed without one. - let (mut producer, live_lane) = effect_control_lane( - EffectLaneSpec { - // A bypass cell alone: this lane's only live value is the shunt switch. - shape: EffectLaneShape::from_live_parameters([], 0), - bypass: true, - observation_depth: 0, - }, - &LiveValuesSuperseded::new(), - ) - .expect("the live lane"); - let (gate, mut pin) = RevisionGate::pair(0); - let mut live_lane = Some(live_lane); - let controls: Vec> = (0..lanes) - .map(|lane| match lane { - 1 => Some(live_lane.take().expect("one live lane")), - 2 => Some(EffectControlLane::without_channel(true)), - _ => None, - }) - .collect(); - let stage = LiveControlEffectBankStage::new( - bank, - width, - QUANTUM, - controls, - (0..lanes).map(|_| None).collect(), - latency, - ) - .expect("stage"); - let mut chain = chain(stage); - // Lane 1's live reference: a prepared-enabled instance and a shunt switched on the same - // blocks. Every other lane's reference is its scalar instance with its session bypass as - // the prepared flag. - let mut live_wet = factory - .prepare(request(descriptor, case, false, &values[1])) - .expect("live reference") - .processor; - let mut live_shunt = effect_contract::BypassShunt::new(QUANTUM as usize, latency); - let mut oracles: Vec> = values - .iter() - .enumerate() - .map(|(lane, values)| { - factory - .prepare(request(descriptor, case, lane == 2, values)) - .expect("scalar oracle") - .processor - }) - .collect(); - let mut signal = Rng::new(0x1087 ^ 0x5eed); - let mut live_bypassed = true; - for block in 0..BLOCKS { - // Un-bypass at block 5 and re-bypass at block 10, each applied at the first sample of - // the block that drains it. - let toggle = match block { - 5 => Some(false), - 10 => Some(true), - _ => None, - }; - if let Some(value) = toggle { - let revision = pin.last_published() + 1; - let stamp = pin.stamp(revision).expect("a legal revision"); - producer - .write( - stamp, - EffectControlRecord::Bypass(effect_contract::BypassSwitch::step(value)), - ) - .expect("a bypass cell"); - pin.publish(&gate, revision); - live_bypassed = value; - } - let mut members = Planes { - left: Vec::new(), - right: Vec::new(), - }; - for lane in 0..lanes { - members - .left - .push(input_block(lane, 0, block, Level::Loud, &mut signal)); - members - .right - .push(input_block(lane, 1, block, Level::Loud, &mut signal)); - } - let mut expected_left = members.left.clone(); - let mut expected_right = members.right.clone(); - chain - .run( - &mut members, - QUANTUM, - block * u64::from(QUANTUM), - gate.snapshot(), - ) - .expect("bank render"); - for lane in 0..lanes { - if lane == 1 { - if live_bypassed || live_shunt.feeds_line() { - live_shunt.capture(&expected_left[lane], &expected_right[lane]); - } - process_scalar( - live_wet.as_mut(), - &mut expected_left[lane], - &mut expected_right[lane], - block, - ); - if live_bypassed { - live_shunt.apply(&mut expected_left[lane], &mut expected_right[lane]); - } - } else { - process_scalar( - oracles[lane].as_mut(), - &mut expected_left[lane], - &mut expected_right[lane], - block, - ); - } - let what = format!("{effect_id} lane {lane} block {block}"); - assert_bits( - &format!("{what} left"), - &members.left[lane], - &expected_left[lane], - ); - assert_bits( - &format!("{what} right"), - &members.right[lane], - &expected_right[lane], - ); - } - } - } -} - /// `NEVER_BANKED_EFFECTS` is exactly the launch effects whose factory declines a well-formed bank /// of default members at the host's width, and every launch effect is in the registry this file /// walks. @@ -827,71 +652,6 @@ fn the_never_banked_list_is_exactly_the_launch_effects_that_decline_a_bank() { assert_eq!(registered, walked, "this file walks every launch effect"); } -/// Why the delay keeps its prepared bypass: a shunt would move a bit. -/// -/// The delay's D7 check reads its feedback ring as well as its output. A 1 ms delay at -/// `0.95` feedback fed `9e29` -- inside the range an effect can receive in a compiled plan -- -/// grows a ring cell past `1e30` within one block, and the check zeroes the block. Under a -/// prepared bypass the zeroed block is the output; a shunt would copy the dry block over it. If -/// this ever renders the same bits, the delay's check no longer reads its state and it may take the -/// shunt like every other effect. -#[test] -fn the_delay_keeps_its_prepared_bypass_because_a_shunt_would_move_a_bit() { - let registry = launch_native_effect_registry().expect("launch registry"); - let factory = registry.get_shared_ascii("miso.delay").expect("the delay"); - let descriptor = factory.descriptor(); - let case = cases(descriptor)[0]; - let by_name = |name: &str| { - descriptor - .parameters - .iter() - .position(|parameter| parameter.display_name == name) - .unwrap_or_else(|| panic!("delay parameter {name}")) as u32 - }; - let mut values = default_values(descriptor); - for (name, value) in [("delay time", 1.0), ("feedback", 0.95), ("damping", 0.0)] { - let index = by_name(name); - for item in values - .iter_mut() - .filter(|item| item.parameter_index == index) - { - item.value = value; - } - } - let mut prepared = factory - .prepare(request(descriptor, case, true, &values)) - .expect("prepared bypass") - .processor; - let mut wet = factory - .prepare(request(descriptor, case, false, &values)) - .expect("prepared enabled") - .processor; - let mut shunt = effect_contract::BypassShunt::new(QUANTUM as usize, 0); - let mut moved = false; - for block in 0..4 { - let mut today = [ - vec![9.0e29_f32; QUANTUM as usize], - vec![9.0e29_f32; QUANTUM as usize], - ]; - let mut shunted = today.clone(); - let [left, right] = &mut today; - process_scalar(prepared.as_mut(), left, right, block); - let [left, right] = &mut shunted; - shunt.capture(left, right); - process_scalar(wet.as_mut(), left, right, block); - shunt.apply(left, right); - moved |= today - .iter() - .flatten() - .zip(shunted.iter().flatten()) - .any(|(a, b)| folded(*a) != folded(*b)); - } - assert!( - moved, - "the delay's prepared bypass and a shunt now agree; it may leave NEVER_BANKED_EFFECTS" - ); -} - // ---- #1341: the bypass crossfade over the session ramp, per node ---------------------------------- /// What a per-node live instance does with its bypass: nothing (W, never bypassed), a session diff --git a/crates/graph/src/program/tests.rs b/crates/graph/src/program/tests.rs index 44189eff2..96c4e5b86 100644 --- a/crates/graph/src/program/tests.rs +++ b/crates/graph/src/program/tests.rs @@ -306,33 +306,6 @@ fn delayed_edge_gets_staging_buffer_and_blocks_in_place() { assert!(delay.staging.0 < program.buffers); } -/// An elided stage is exactly a stage with one undelayed, un-sidechained input: give the same -/// stage a PDC edge and it keeps its op. -#[test] -fn a_delayed_stage_boundary_is_not_elided() { - let (nodes, edges) = plain_track("t", &["r"]); - let (spec, schedule, levels) = build(nodes, edges); - let delayed = InsertedDelay { - node: stage_node("t", TrackStage::PostSimd1), - edge_id: GraphEdgeId::TrackMain { - target: stage_node("t", TrackStage::PostSimd1), - }, - samples: LatencySamples(8), - }; - let program = lower( - &spec, - &schedule, - &levels, - std::slice::from_ref(&delayed), - &[], - ) - .expect("lowers"); - assert_eq!(program.taps.len(), 2); - assert_eq!(program.ops.len(), 7); - let index = node_index(&spec, &stage_node("t", TrackStage::PostSimd1)).expect("node"); - assert!(program.node_op[index as usize].is_some()); -} - /// A dynamic-rack effect is not bank-eligible, so the two stage boundaries after it are pure /// aliases of *its* buffer and the fader downstream still reads that buffer in place. /// diff --git a/crates/graph/src/runtime.rs b/crates/graph/src/runtime.rs index 01b034941..ca1cf5274 100644 --- a/crates/graph/src/runtime.rs +++ b/crates/graph/src/runtime.rs @@ -8278,20 +8278,6 @@ thread_local! { static HOST_MASTER_DECLINED: std::cell::Cell = const { std::cell::Cell::new(false) }; } -/// Decline (`true`) or restore (`false`) the host-plane master for every later bind on this -/// thread: issue #916's arena oracle, on the pattern of [`test_only_set_route_fold_declined`]. -/// -/// A plan bound declined has no Output unit, every folded master is [`FoldTarget::Arena`], and a -/// redirect into the Output op is not withheld. So its Output op and its folds write the Output's -/// arena buffer, as every plan did before the issue, and nothing writes the host's planes. The -/// test copies that buffer out itself, which is the executor's old end-of-block copy. The switch -/// is read once per bind, in `preflight_sequential`. Callers restore `false` after the bind they -/// meant to decline. -#[cfg(test)] -pub(crate) fn test_only_set_host_master_declined(declined: bool) { - HOST_MASTER_DECLINED.with(|slot| slot.set(declined)); -} - #[cfg(any(test, feature = "test-support"))] thread_local! { /// Issue #918's copy oracle. Bind-time only; render never reads it. @@ -10532,7 +10518,7 @@ fn foldable_lane( /// /// * **the association order** -> `route_ids_ordered_against_the_cohorts_decline_the_route_fold`, /// plus `a_leased_stage_meter_declines_the_merge_and_still_meters` and -/// `a_pre_fader_meter_splits_its_cohorts_chain_and_reads_the_limiter` (named +/// `a_pre_fader_meter_splits_its_cohorts_chain_and_reads_the_limiter` (deleted by TA5; named /// `an_observed_alias_on_the_last_slot_declines_that_lanes_scatter_redirect` until issue #886, /// the name `crates/graph/tests/MUTATIONS.md` row 218-3 still records). Keeping the length check /// and dropping the element-wise comparison is the unsound direction, and it is the one measured. @@ -12712,82 +12698,6 @@ mod tests { ); } - /// RT-1: real graph arena members gather one buffer set and scatter directly into another. - #[test] - fn arena_members_direct_scatter_preserves_redirected_identity_bits() { - struct Identity; - impl BankStage for Identity { - fn process(&mut self, _block: BankBlock<'_>) -> Result<(), RenderError> { - Ok(()) - } - } - - const FRAMES: usize = 11; - let mut arena = stereo_arena(FRAMES, 8); - let inputs = [1, 2, 3, 4]; - let outputs = [5, 6, 7, 8]; - let mut expected = Vec::new(); - for (lane, input) in inputs.iter().copied().enumerate() { - let left: Vec = (0..FRAMES) - .map(|frame| f32::from_bits(0x7fc0_0000 | ((lane * FRAMES + frame) as u32 + 1))) - .collect(); - let right: Vec = left - .iter() - .map(|word| f32::from_bits(word.to_bits() ^ 0x8000_3990)) - .collect(); - arena.write(0, input).copy_from_slice(&left); - arena.write(1, input).copy_from_slice(&right); - expected.push((left, right)); - } - for output in outputs { - arena.write(0, output).fill(f32::from_bits(0x7f80_3991)); - arena.write(1, output).fill(f32::from_bits(0x7f80_3992)); - } - - let active = vec![true; 4].into_boxed_slice(); - let mut chain = BankChain::new( - AoSoaScratch::new(BankWidth::Four, FRAMES as u32).expect("scratch"), - active.clone(), - vec![BankSlot { - stage: Box::new(Identity), - active_lanes: active, - }], - ) - .expect("chain"); - let mut members = ArenaMembers { - arena: &mut arena, - inputs: &inputs, - outputs: &outputs, - fold: &[], - master: MasterPlanes::Arena(0), - sources: SourceGather::NONE, - }; - chain - .run(&mut members, FRAMES as u32, 0, LiveSnapshot::ALL) - .expect("direct graph run"); - - for (lane, output) in outputs.iter().copied().enumerate() { - let (left, right) = arena.read_stereo(output); - assert_eq!( - left.iter().map(|word| word.to_bits()).collect::>(), - expected[lane] - .0 - .iter() - .map(|word| word.to_bits()) - .collect::>() - ); - assert_eq!( - right.iter().map(|word| word.to_bits()).collect::>(), - expected[lane] - .1 - .iter() - .map(|word| word.to_bits()) - .collect::>() - ); - } - assert_eq!(chain.transposes(), 1); - } - /// The folded epilogue is the route op followed by the D9 reduction, word for word. /// /// The oracle is the shape this replaces, built out of the very kernels the route op and @@ -14031,86 +13941,6 @@ mod tests { assert_ne!(right, twin_right); } - /// E10. The route is two multiplies plus one add per output word, with the gain - /// folded into the coefficients at bind (D3). The oracle is - /// `softfma::unfused_multiply_add_via_f64`, which computes the same multiply-add through `f64` with - /// no dependence on `mix2x2_block`'s vector body; the exact product and the innocuous double - /// rounding of the sum make it bit-identical to the `f32` expression (issue #163 phase 2). - /// - /// Red mutation (`tests/MUTATIONS.md`): compute `gain * (ll * l + lr * r)` instead. - #[test] - fn route_applies_folded_gain_with_frozen_op_order() { - const FRAMES: usize = 4_096; - let transform = (0.707_9f32, 0.9f32, -0.1f32, 0.2f32, 0.8f32); - let folded = [ - transform.0 * transform.1, - transform.0 * transform.2, - transform.0 * transform.3, - transform.0 * transform.4, - ]; - let mut state = 0x1234_5678u32; - let left_in: Vec = (0..FRAMES).map(|_| lcg(&mut state)).collect(); - let right_in: Vec = (0..FRAMES).map(|_| lcg(&mut state)).collect(); - let mut left = left_in.clone(); - let mut right = right_in.clone(); - mix2x2_block::(&mut left, &mut right, folded); - for frame in 0..FRAMES { - let (l, r) = (left_in[frame], right_in[frame]); - let expected_left = - lane::softfma::unfused_multiply_add_via_f64(folded[1], r, folded[0] * l); - let expected_right = - lane::softfma::unfused_multiply_add_via_f64(folded[3], r, folded[2] * l); - assert_eq!( - left[frame].to_bits(), - expected_left.to_bits(), - "frame {frame} left" - ); - assert_eq!( - right[frame].to_bits(), - expected_right.to_bits(), - "frame {frame} right" - ); - } - // Block lengths that are not a multiple of the lane width, so the scalar tail is walked - // and has to agree with the vector body word for word. - for block in [1usize, 3, 7, 63, 65, 129, 511] { - let mut tail_left = left_in[..block].to_vec(); - let mut tail_right = right_in[..block].to_vec(); - mix2x2_block::(&mut tail_left, &mut tail_right, folded); - assert_eq!( - ( - tail_left.iter().map(|s| s.to_bits()).collect::>(), - tail_right.iter().map(|s| s.to_bits()).collect::>() - ), - ( - left[..block] - .iter() - .map(|s| s.to_bits()) - .collect::>(), - right[..block] - .iter() - .map(|s| s.to_bits()) - .collect::>() - ), - "block {block}: the scalar tail must match the vector body" - ); - } - // The fold is not the unfolded form: this is the bit change #98 F4 declares. - let unfolded: Vec = (0..FRAMES) - .map(|frame| { - (transform.0 * (transform.1 * left_in[frame] + transform.2 * right_in[frame])) - .to_bits() - }) - .collect(); - assert_ne!( - left.iter() - .map(|sample| sample.to_bits()) - .collect::>(), - unfolded, - "the folded route is a distinct rounding, not a coincidence" - ); - } - // ----------------------------------------------------------------------------------------- // Issue #885: the route fold stays armed when every track carries a post-matrix meter. // ----------------------------------------------------------------------------------------- @@ -15259,184 +15089,8 @@ mod tests { (executor, program) } - /// `Input -> PostInputBuiltins (a one-lane W4 builtin bank) -> Output`, one track, no route - /// (issue #916). The bank's last slot is dedicated storage whose sole reader is the Output op, - /// so `scatter_target` offers a redirect *into the Output op*, which `build_sequential` - /// withholds. The arena oracle, bound with the host master declined, takes it. - fn direct_bank_parts( - output_meters: Option<&Published>, - ) -> (crate::PreparedGraphPlan, crate::GraphRuntimeBindings) { - const FRAMES: u32 = 13; - let id = |text: &str| crate::StableGraphId::parse(text).expect("stable id"); - let input = GraphNodeId::TrackStage { - track_id: id("track00"), - stage: TrackStage::Input, - }; - let member = GraphNodeId::TrackStage { - track_id: id("track00"), - stage: TrackStage::PostInputBuiltins, - }; - let output = GraphNodeId::Output { - output_id: id("main"), - }; - let port = |node: &GraphNodeId, kind| crate::GraphPortId { - node: node.clone(), - kind, - effect_port: None, - }; - let edge = |id, source: &GraphNodeId, destination: &GraphNodeId| crate::GraphEdge { - id, - source: port(source, crate::GraphPortKind::MainOutput), - destination: port(destination, crate::GraphPortKind::MainInput), - path: "$.issue916".to_owned(), - }; - let mut edges = vec![ - edge( - GraphEdgeId::TrackMain { - target: member.clone(), - }, - &input, - &member, - ), - edge( - GraphEdgeId::RouteSource { - route_id: id("route00"), - }, - &member, - &output, - ), - ]; - edges.sort_by(|left, right| left.id.cmp(&right.id)); - let levels = [ - vec![input.clone()], - vec![member.clone()], - vec![output.clone()], - ]; - let schedule: Vec<_> = levels.iter().flatten().cloned().collect(); - let mut nodes: Vec<_> = schedule - .iter() - .cloned() - .map(|id| crate::GraphNode { - id, - latency: effect_contract::LatencySamples(0), - tail: effect_contract::TailSamples::Finite(0), - }) - .collect(); - nodes.sort_by(|left, right| left.id.cmp(&right.id)); - let envelope = engine::realtime::RenderEnvelope { - sample_rate: engine::SampleRateHz(48_000), - quantum: engine::QuantumFrames(FRAMES), - output_channels: NonZeroUsize::new(2).expect("stereo"), - }; - let plan = crate::PreparedGraphPlan::new(crate::PreparedGraphPlanParts { - plan_id: 916, - spec: GraphSpec { - nodes, - ports: Vec::new(), - edges, - }, - sequential_schedule: schedule, - dependency_levels: levels - .iter() - .enumerate() - .map(|(level, nodes)| crate::DependencyLevel { - level: level as u64, - nodes: nodes.clone(), - }) - .collect(), - route_timings: Vec::new(), - inserted_delays: Vec::new(), - buffer_assignments: Vec::new(), - estimate: crate::GraphResourceEstimate { - logical_nodes: 0, - materialized_nodes: 0, - edges: 0, - schedule_items: 0, - dependency_levels: 0, - reductions: 0, - routes: 0, - effects: 0, - audio_buffer_samples: 0, - total_delay_samples: 0, - delay_bytes: 0, - graph_metadata_bytes: 0, - declared_effect_bytes: 0, - effect_bank_count: 0, - effect_bank_scratch_bytes: 0, - effect_bank_runtime_buffer_bytes: 0, - effect_bank_metadata_bytes: 0, - builtin_bank_bytes: 0, - builtin_bank_scratch_bytes: 0, - builtin_bank_count: 0, - largest_allocation_bytes: 0, - incremental_plan_bytes: 0, - session_plus_plan_bytes: 0, - }, - envelope, - required_bindings: vec![input.clone(), member.clone(), output.clone()], - routes: Vec::new(), - track_delays: Vec::new(), - effects: Vec::new(), - effect_controls: Vec::new(), - effect_observations: Vec::new(), - banks: Vec::new(), - builtin_banks: vec![GraphPreparedBuiltinBank { - backend: lane::Backend::Simd4, - members: vec![member].into_boxed_slice(), - processor: Box::new(CrossTilt), - scratch: AoSoaScratch::new(BankWidth::Four, FRAMES).expect("scratch"), - }], - observers: Vec::new(), - }); - let mut observers = Vec::new(); - if let Some(published) = output_meters { - for handle in [OUTPUT_METER_HANDLE, OUTPUT_METER_HANDLE + 1] { - observers.push(GraphNodeObserverBinding::new( - output.clone(), - handle, - Box::new(WordMeter { - handle, - accepts_resident: false, - published: Arc::clone(published), - }), - )); - } - } - ( - plan, - crate::GraphRuntimeBindings { - envelope, - nodes: vec![ - GraphNodeBinding::new(input, Box::new(NoiseInput(7))), - GraphNodeBinding::identity(output), - ], - observers, - }, - ) - } - /// NaN padding with a recognisable payload: the words a host plane holds before a render. const HOST_PAD: u32 = 0x7fc0_0916; - /// The pattern the arena buffer of the session Output is filled with before every block. - const OUTPUT_SLOT_FILL: [u32; 2] = [0x7fc1_0916, 0x7fc2_0916]; - - fn stereo_bits((left, right): (&[f32], &[f32])) -> Vec { - left.iter() - .chain(right) - .map(|word| word.to_bits()) - .collect() - } - - /// The session output's arena buffer of a bound executor. - fn output_slot(program: &ExecutionProgram) -> u32 { - program.output.0 + ARENA_BASE - } - - fn fill_output_slot(executor: &mut crate::GraphExecutor, program: &ExecutionProgram) { - let (left, right) = executor.runtime.buffer_mut(output_slot(program)); - left.fill(f32::from_bits(OUTPUT_SLOT_FILL[0])); - right.fill(f32::from_bits(OUTPUT_SLOT_FILL[1])); - } /// One host-plane render through `GraphExecutor::render`, the executor half of /// `PreparedRenderPlan::render`. @@ -15458,334 +15112,6 @@ mod tests { ) } - /// Issue #916's arena oracle: the render loop `GraphExecutor::render` ran before the issue, - /// over a runtime bound with the host master declined, then its end-of-block copy of the - /// Output's arena buffer into `output`. - /// - /// Returns that buffer's words as they stood just before the first unit that writes the - /// master into it. That unit is the Output op's own, unless the Output op's reduction was - /// neutralised to its own slot. Then the master was written earlier, by the chains that fold - /// into it or by a chain whose scatter was redirected into it. A host-plane render must leave - /// the buffer holding exactly those words, because only the master's writers touch it after - /// that point, and they now write the host's planes. - fn render_arena_oracle( - executor: &mut crate::GraphExecutor, - program: &ExecutionProgram, - mut output: engine::realtime::PlanarBufferMut<'_>, - first_sample: u64, - ) -> Vec { - let slot = output_slot(program); - let runtime = &mut executor.runtime; - assert_eq!( - runtime.output_unit, None, - "the oracle binds with no Output unit" - ); - let frames = runtime.arena.frames(); - let untouched = f32::from_bits(HOST_PAD); - let (mut left, mut right) = (vec![untouched; frames], vec![untouched; frames]); - let mut host = HostMaster::new(&mut left, &mut right, frames).expect("oracle host"); - // The Output op is the last op writing its slot: dedicated storage is never recycled. - let output_unit = (0..runtime.units.len()) - .rev() - .find(|unit| matches!(&runtime.units[*unit], RuntimeUnit::Op(op) if op.output == slot)) - .expect("the Output op's unit"); - let neutralised = matches!( - &runtime.units[output_unit], - RuntimeUnit::Op(op) if *op.inputs == [slot] - ); - let writes_slot = |unit: &RuntimeUnit| match unit { - RuntimeUnit::Bank { - members, - lanes, - fold, - master, - .. - } => { - (!fold.is_empty() && *master == FoldTarget::Arena(slot)) - || members[members.len() - lanes..] - .iter() - .any(|member| member.output == slot) - } - RuntimeUnit::Op(_) => false, - }; - let validity = GraphObservationValidity::CLEAR; - let mut before_master = None; - for unit in 0..runtime.units.len() { - let writes_master = - unit == output_unit || (neutralised && writes_slot(&runtime.units[unit])); - if writes_master && before_master.is_none() { - before_master = Some(stereo_bits(runtime.buffer(slot))); - } - runtime - .execute( - unit, - first_sample, - LiveSnapshot::ALL, - host.reborrow(), - None, - &crate::GraphBlockTime::new( - Some(first_sample), - u32::try_from(frames).expect("oracle frames"), - None, - None, - ), - ) - .expect("oracle unit"); - runtime - .observe_unit(unit, first_sample, validity, &host) - .expect("oracle observer"); - } - // The executor's end-of-block copy, exactly as it stood before issue #916. - let (arena_left, arena_right) = runtime.buffer(slot); - output - .plane_mut(0) - .expect("left") - .copy_from_slice(arena_left); - output - .plane_mut(1) - .expect("right") - .copy_from_slice(arena_right); - assert!( - left.iter() - .chain(&right) - .all(|word| word.to_bits() == HOST_PAD), - "an arena-oracle unit wrote the host planes" - ); - before_master.expect("a master writer ran") - } - - /// Which issue #916 gate-1 plan to bind. - #[derive(Clone, Copy, Debug)] - enum HostShape { - /// 64 tracks in eight W8 cohorts, every route folded into the Output: the console shape. - /// `frames`. - Folded(u32), - /// The same plan with the fold declined: 64 route ops and a 64-input reduction. - Unfolded, - /// The bus plan with its three pads: the tracks fold into the bus, the bus redirects its - /// scatter into its fader, and the Output's one input is the bus route. The Output's slot - /// is a pad's. - Submix, - /// The bus plan with no pads: nothing folds, and the Output's slot is a track's input - /// slot, which the tracks' bank gathers and whose route runs in place on it. - SubmixOnATrackSlot, - /// One track, its route folded into the Output: a fan-in-one master that only stores. - OneRouteFolded, - /// The same with the fold declined: the Output op's fan-in-one copy. - OneRouteUnfolded, - /// [`direct_bank_parts`]: a bank's last slot straight into the Output op. - BankIntoOutput, - } - - impl HostShape { - const ALL: [Self; 8] = [ - Self::Folded(13), - Self::Folded(128), - Self::Unfolded, - Self::Submix, - Self::SubmixOnATrackSlot, - Self::OneRouteFolded, - Self::OneRouteUnfolded, - Self::BankIntoOutput, - ]; - - const fn frames(self) -> u32 { - match self { - Self::Folded(frames) => frames, - _ => 13, - } - } - - /// `[bank_route_folds, bank_scatter_redirects]` of the host-plane plan. - const fn shape(self) -> [u64; 2] { - match self { - Self::Folded(..) => [64, 0], - Self::Unfolded | Self::OneRouteUnfolded | Self::BankIntoOutput => [0, 0], - Self::Submix => [4, 1], - Self::SubmixOnATrackSlot => [0, 1], - Self::OneRouteFolded => [1, 0], - } - } - - /// Bind the host-plane plan, or with `oracle` the arena oracle of the same plan. - fn bind( - self, - oracle: bool, - published: &Published, - ) -> (crate::GraphExecutor, ExecutionProgram) { - let console = |tracks, width, frames| { - let fixture = FoldFixture { - output_meters: true, - ..FoldFixture::metered(width, tracks, frames) - }; - let (plan, bindings, _) = fold_fixture_parts(fixture, published); - (plan, bindings) - }; - let (plan, bindings) = match self { - Self::Folded(frames) => console(64, CONSOLE_WIDTH, frames), - Self::Unfolded => console(64, CONSOLE_WIDTH, 13), - Self::OneRouteFolded | Self::OneRouteUnfolded => console(1, BankWidth::Four, 13), - Self::Submix => folded_bus_parts(3, Some(published)), - Self::SubmixOnATrackSlot => folded_bus_parts(0, Some(published)), - Self::BankIntoOutput => direct_bank_parts(Some(published)), - }; - let declined = matches!(self, Self::Unfolded | Self::OneRouteUnfolded); - test_only_set_route_fold_declined(declined); - test_only_set_host_master_declined(oracle); - let bound = bind_executor(plan, bindings, None); - test_only_set_route_fold_declined(false); - test_only_set_host_master_declined(false); - bound - } - } - - /// Gate 1 of issue #916: the host's planes after a render are the arena oracle's master bit - /// for bit, at a stride equal to the block and seven words wider, with every padding word - /// untouched; every observer window, the two Output meters' included, is the oracle's; and - /// the session output's arena buffer is left holding exactly what it held before the - /// master's first writer ran, so neither the Output op nor a folded Output master writes it. - /// - /// The oracle is [`render_arena_oracle`]: the same plan bound with the host master declined, - /// driven through the render loop that preceded the issue, then copied out of the arena as - /// the executor's end-of-block copy did. The shapes cover the three the brief names -- 64 - /// folded routes, 64 unfolded routes, and one submix into the Output -- plus a submix whose - /// Output slot is a gathered track input, a folded and an unfolded fan-in-one route, and a - /// bank straight into the Output op (whose scatter redirect is withheld). - /// - /// Red mutations (`crates/graph/tests/MUTATIONS.md`, rows 916-1 to 916-6, 916-10, 916-11 and - /// 916-15). They restore the arena path with the end-of-block copy, which only the - /// output-slot check sees. They write the Output op's master to the arena and skip the host, - /// install every folded Output master as an arena master, and read the fan-in-one input from - /// the other plane. They identify the Output op by buffer index, stop withholding the redirect - /// into the Output op, and fill the planes on success. They undedicate the Output, and point - /// its observers at the arena. - #[test] - fn the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride() { - const BLOCKS: u64 = 4; - for shape in HostShape::ALL { - let frames = shape.frames() as usize; - for stride in [frames, frames + 7] { - let (oracle_published, published) = (Published::default(), Published::default()); - let (mut oracle, oracle_program) = shape.bind(true, &oracle_published); - let (mut candidate, program) = shape.bind(false, &published); - assert_eq!(program, oracle_program, "{shape:?}: one lowered program"); - let redirects = match shape { - // The oracle takes the redirect into the Output op; the host-plane plan - // withholds it and changes nothing else. - HostShape::BankIntoOutput => 1, - _ => shape.shape()[1], - }; - assert_eq!( - [ - candidate.runtime.route_folds(), - candidate.runtime.scatter_redirects() - ], - shape.shape(), - "{shape:?}: [route folds, scatter redirects]" - ); - assert_eq!( - [ - oracle.runtime.route_folds(), - oracle.runtime.scatter_redirects() - ], - [shape.shape()[0], redirects], - "{shape:?}: the oracle's [route folds, scatter redirects]" - ); - { - use engine::realtime::PreparedPlanExecutor as _; - assert_eq!(candidate.bank_shape(), oracle.bank_shape(), "{shape:?}"); - } - let unit = candidate.runtime.output_unit.expect("an Output unit"); - assert!( - matches!(&candidate.runtime.units[unit], RuntimeUnit::Op(op) if op.output == output_slot(&program)), - "{shape:?}: the Output unit runs the op that owns the output slot" - ); - let mut masters = Vec::new(); - for block in 0..BLOCKS { - let first_sample = block * frames as u64; - fill_output_slot(&mut oracle, &oracle_program); - fill_output_slot(&mut candidate, &program); - let mut expected = vec![f32::from_bits(HOST_PAD); 2 * stride]; - let before_master = render_arena_oracle( - &mut oracle, - &oracle_program, - engine::realtime::PlanarBufferMut::try_new( - &mut expected, - 2, - frames, - stride, - ) - .expect("oracle output"), - first_sample, - ); - let mut actual = vec![f32::from_bits(HOST_PAD); 2 * stride]; - render_host( - &mut candidate, - engine::realtime::PlanarBufferMut::try_new(&mut actual, 2, frames, stride) - .expect("host output"), - first_sample, - ) - .expect("host-plane render"); - let bits = - |words: &[f32]| words.iter().map(|word| word.to_bits()).collect::>(); - assert_eq!( - bits(&actual), - bits(&expected), - "{shape:?}, stride {stride}, block {block}: the host planes are the oracle's" - ); - for (index, word) in actual.iter().enumerate() { - if index % stride >= frames { - assert_eq!( - word.to_bits(), - HOST_PAD, - "{shape:?}, stride {stride}, block {block}: padding word {index}" - ); - } - } - assert_eq!( - stereo_bits(candidate.runtime.buffer(output_slot(&program))), - before_master, - "{shape:?}, stride {stride}, block {block}: the output slot was written" - ); - let master: Vec = actual[..frames] - .iter() - .chain(&actual[stride..stride + frames]) - .map(|word| word.to_bits()) - .collect(); - assert!( - master.iter().any(|word| *word != 0 && *word != HOST_PAD), - "{shape:?}: the block carries audio" - ); - masters.push(master); - } - let frames_of = |published: &Published| published.lock().unwrap().clone(); - let (oracle_frames, frames_seen) = - (frames_of(&oracle_published), frames_of(&published)); - assert_eq!( - frames_seen, oracle_frames, - "{shape:?}, stride {stride}: every observer window is the oracle's" - ); - let output_windows: Vec<&MeterFrame> = frames_seen - .iter() - .filter(|frame| frame.handle >= OUTPUT_METER_HANDLE) - .collect(); - assert_eq!( - output_windows.len(), - 2 * BLOCKS as usize, - "{shape:?}: both Output meters saw every block" - ); - for window in output_windows { - let block = (window.first_sample / frames as u64) as usize; - let seen: Vec = window.left.iter().chain(&window.right).copied().collect(); - assert_eq!( - seen, masters[block], - "{shape:?}: an Output meter reads the planes the host receives" - ); - } - } - } - } - /// `t00 Input -> Output`, and with `read_output` also `Output -> t01 PostFader`: a hand-built /// plan with a consumer of the session output, which the graph compiler never emits. fn output_reader_parts( diff --git a/crates/graph/tests/MUTATIONS.md b/crates/graph/tests/MUTATIONS.md index eb8b2c944..369cd9f9d 100644 --- a/crates/graph/tests/MUTATIONS.md +++ b/crates/graph/tests/MUTATIONS.md @@ -22,8 +22,8 @@ cargo test --locked -p --lib | M15 | fan-in 0 leaves the output buffer untouched instead of zeroing it | `src/runtime.rs` `reduce_plane` | `reduction_is_left_to_right_bit_identical_to_scalar_reference` | RED | | M3 | the slice PDC keeps the block's entry cursor instead of advancing it | `src/runtime.rs` `CompensationDelay::process` | `compensation_delay_is_partition_invariant_and_matches_per_sample_reference` | RED | | M12 | the slice PDC exchanges its two segments in the wrong order | `lane` `kernels::pdc_delay_block` | `compensation_delay_is_partition_invariant_and_matches_per_sample_reference` | RED | -| M4 | the route unfolds the gain and drops the fusion (`ll*l + lr*r`) | `lane` `kernels::mix2x2_block` | `route_applies_folded_gain_with_frozen_op_order` | RED | -| M9 | `mix2x2_block`'s scalar tail uses a different coefficient order than its vector body | `lane` `kernels::mix2x2_block` | `route_applies_folded_gain_with_frozen_op_order` | RED | +| M4 | the route unfolds the gain and drops the fusion (`ll*l + lr*r`) | `lane` `kernels::mix2x2_block` | `route_applies_folded_gain_with_frozen_op_order` (deleted by TA5) | RED | +| M9 | `mix2x2_block`'s scalar tail uses a different coefficient order than its vector body | `lane` `kernels::mix2x2_block` | `route_applies_folded_gain_with_frozen_op_order` (deleted by TA5) | RED | | M5 | the native executor stages an edge from the wrong producer | `src/runtime.rs` `build_native` | `fifty_random_dag_sessions_render_bit_identically_in_both_executors` | RED | | M6 | the sequential executor reduces before staging its delayed edges | `src/runtime.rs` `execute_op` | `fifty_random_dag_sessions_render_bit_identically_in_both_executors` | RED | | M8 | a bank member's inputs are gathered without their reduction | `src/runtime.rs` `Runtime::execute` | `level_major_w4_builtin_bank_is_analytic_for_three_blocks_in_both_executors` | RED | @@ -208,7 +208,7 @@ before the next row. Each mutation is an exact-text replacement, so an unmatched been reported rather than silently skipped. Host: `x86_64`, workspace `.cargo/config.toml` pin `-C target-feature=+avx2,+fma`, debug profile. The new tests these rows name: -- "Gate 1" is `runtime::tests::the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride`. +- "Gate 1" is `runtime::tests::the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride` (deleted by TA5). - "Gate 3" is `runtime::tests::a_failed_render_silences_the_host_planes_and_a_rejected_one_leaves_them_alone`. - "The kernel test" is `runtime::tests::a_host_plane_reduction_is_the_arena_reduction_bit_for_bit`. - "Metered" is `runtime::tests::a_folded_metered_plan_is_the_unfolded_plans_master_and_meters_bit_for_bit`. @@ -344,7 +344,7 @@ result column gives that second run's failure. For 926-12, the second run also d | 926-12 | drop the master's delayed-input clause (920-9) | `graph/src/runtime.rs` `output_route_fold` | gate 1 | RED on the constructor's `debug_assert` (`a folded route table belongs to the Output op's identity reduction`: the Output op stages). With the counts and that clause deleted: RED, `DelayedEdge, fan-in 64, 1 frames, block 0: the host planes`, `-0.0` (`2147483648`) where the oracle has `+0.0`: the line's `+0.0` warm-up words are mixed through the delayed route's negative 2x2. | | 926-13 | a non-route producer folds with an identity 2x2 (`plain_route_gains(..).unwrap_or([1.0, 0.0, 0.0, 1.0])`) (920-10) | `graph/src/runtime.rs` `output_route_fold` | gate 1 | RED on the count (`SubmixContributor ... folds`, 65 against 0). With the counts deleted: RED, `SubmixContributor, fan-in 64, 1 frames, block 0: the host planes`, `+0.0` where the oracle keeps `-0.0`: the pass-through's `(-0.0, +0.0)` left plane mixes to `+0.0`. | | 926-14 | drop the in-between `op_names_buffer` scan (920-11) | `graph/src/runtime.rs` `output_route_fold` | the graph suite; the console gate | GREEN (104 of 104, and the console gate). No test can make the scan fire: a write between the route and the Output would need the live buffer's slot, which the colouring owns through the Output op, and a read is a second reader, which 926-11's clause declines. The scan is the belt to that brace, as `route_fold`'s is. | -| 926-15 | drop the no-bank clause (920-12) | `graph/src/runtime.rs` `output_route_fold` | the graph suite; the console gate | RED on five graph tests at bind, `graph.route_fold.master`: `a_banked_source_gathers_the_played_block_bit_for_bit_with_the_copy`, `a_failed_render_silences_the_host_planes_and_a_rejected_one_leaves_them_alone`, `a_folded_metered_plan_is_the_unfolded_plans_master_and_meters_bit_for_bit`, `a_post_matrix_meter_on_every_track_of_a_full_bank_keeps_the_fold_armed`, `the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride` (#920 saw four; #918's gate 1 is the fifth). RED on the console gate's other-rows sweep, at bind (`nine_track_baseline: console graph bindings`). On a banked plan whose chain fold is admitted, both folds claim the same routes and `validate_fold_installation` refuses the bind. | +| 926-15 | drop the no-bank clause (920-12) | `graph/src/runtime.rs` `output_route_fold` | the graph suite; the console gate | RED on five graph tests at bind, `graph.route_fold.master`: `a_banked_source_gathers_the_played_block_bit_for_bit_with_the_copy`, `a_failed_render_silences_the_host_planes_and_a_rejected_one_leaves_them_alone`, `a_folded_metered_plan_is_the_unfolded_plans_master_and_meters_bit_for_bit`, `a_post_matrix_meter_on_every_track_of_a_full_bank_keeps_the_fold_armed`, `the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride` (deleted by TA5) (#920 saw four; #918's gate 1 is the fifth). RED on the console gate's other-rows sweep, at bind (`nine_track_baseline: console graph bindings`). On a banked plan whose chain fold is admitted, both folds claim the same routes and `validate_fold_installation` refuses the bind. | | 926-16 | the Output fold's routes are not retired: they keep their units and run, and the table still applies (920-13) | `graph/src/runtime.rs` `build_sequential`, `validate_fold_installation` | gate 1; the console gate | RED on the unit count (`Plain ... each folded route is one unit fewer`, 193 against 129) and on the console gate's census (`the plumbing row must retire one route op per track`, 0 against 64). With the counts deleted: RED, `Plain, fan-in 64, 1 frames, block 0: the host planes`: every route is mixed twice. | | 926-17 | the routes are retired but the table is not installed (`output_routes` empty) (920-14) | `graph/src/runtime.rs` `build_sequential` | gate 1; the console gate | RED on the count (0 against 64). With the counts deleted: RED, `Plain, fan-in 64, 1 frames, block 0: the host planes`: the Output sums the unmixed inputs. GREEN on the console gate: its routes are the identity 2x2 at 0 dB, so the unmixed sum is the mixed one there and the census still drops by 64. | | 926-18 | skip an odd fan-in's lone last input (the `G = 1` arm returns `true`) | `graph/src/runtime.rs` `route_reduce` | the kernel test, gate 1, the console gate | RED, RED, GREEN. Kernel: `width 1, 1 frames, fan-in 3, -0.0 false: left`. Gate 1: `Plain, fan-in 9, 1 frames, block 0: the host planes`, the gate's odd fan-in. The console row's fan-in is sixty-four, all pairs. | @@ -766,3 +766,10 @@ The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) delete - `the_driver_fed_gain_pan_row_dispatches_every_unit_but_its_inputs`: no plausible same-crate KEEP catcher recorded - `a_muted_route_seals_one_route_mute_row_after_its_transform`: no plausible same-crate KEEP catcher recorded + +## TA5 deletions + +The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. + +- `route_applies_folded_gain_with_frozen_op_order`: graph `tests::fifty_random_dag_sessions_render_deterministic_nonsilent_pcm` +- `the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride`: graph `the_block_entry_holds_the_newest_published_lengths`, `direct_bank_graph_render_is_allocation_free_and_bit_exact` diff --git a/crates/host-core/src/control_provider.rs b/crates/host-core/src/control_provider.rs index ad200c9a5..c8b8be76c 100644 --- a/crates/host-core/src/control_provider.rs +++ b/crates/host-core/src/control_provider.rs @@ -3091,115 +3091,6 @@ mod tests { ); } - /// #1650b gate 5 (the catalog). A left-lane fader entry on `eq0` with a `+2.5` dB hand - /// offset lists `eq0`'s left `fader_db` row `automated` with the offset as its value, and the - /// right row (no entry drives it) not `automated`, at its static value. A hand move (the offset - /// moved) keeps the epoch; adding the entry and removing it each advance it, and a client's - /// old epoch is then stale. - /// - /// Test value: red if a client cannot tell that its write on an automated lane is an offset - /// (no flag, or the static value listed), or if the flag can change under a held epoch. - #[test] - fn an_automated_fader_lane_lists_its_offset_and_the_flag_is_a_descriptor() { - let plain = nine_track(); - let (mut provider, _clock) = provider_of(&plain); - let left = fader_address(&plain, "eq0"); - let mut automated = plain.clone(); - automated.automation.push(session::Automation { - id: session::StableId::parse("eq0-ride").expect("ID"), - target: left.clone(), - segments: vec![session::AutomationSegment { - shape: session::AutomationShape::Linear, - start_sample: 0, - end_sample: 4_800, - start_value: 0.0, - end_value: -6.0, - unit: session::ParameterUnit::Db, - }], - offset: session::AutomationOffset { - left_db: 2.5, - right_db: 0.0, - }, - }); - let rows = |provider: &mut SessionControlProvider| { - whole_list(provider, 256) - .into_iter() - .filter(|entry| { - entry.address.owner_id.as_deref() == Some("eq0") - && entry.address.section == ControlSection::Builtins - && entry.address.control == Some(left.control) - }) - .map(|entry| match entry.body { - ControlBody::Control(row) => (entry.address.lane, row.value, row.automated), - _ => unreachable!("a control row"), - }) - .collect::>() - }; - let static_db = [ - plain.tracks[0].fader.left_db, - plain.tracks[0].fader.right_db, - ]; - assert_eq!( - rows(&mut provider), - vec![ - (Some(ParameterChannel::Left), static_db[0], false), - (Some(ParameterChannel::Right), static_db[1], false), - ] - ); - provider.replace_session_catalog( - ControlCatalog::build(&automated, &MonitorOverlay::default(), RATE).expect("catalog"), - 2, - ); - assert_eq!( - provider.structure_epoch(), - 2, - "the flag is a descriptor field" - ); - assert_eq!( - rows(&mut provider), - vec![ - (Some(ParameterChannel::Left), 2.5, true), - (Some(ParameterChannel::Right), static_db[1], false), - ] - ); - // A hand move writes the offset, a value: the epoch holds. - controls::set(&mut automated, &left, -1.5, None, RATE).expect("a hand move"); - assert_eq!( - automated.automation[0].offset.left_db.to_bits(), - (-1.5_f32).to_bits() - ); - assert_eq!( - automated.tracks[0].fader.left_db.to_bits(), - static_db[0].to_bits(), - "the static fader is the lane's fallback" - ); - provider.replace_session_catalog( - ControlCatalog::build(&automated, &MonitorOverlay::default(), RATE).expect("catalog"), - 3, - ); - assert_eq!(provider.structure_epoch(), 2, "a hand move is a value"); - assert_eq!( - rows(&mut provider)[0], - (Some(ParameterChannel::Left), -1.5, true) - ); - provider.replace_session_catalog( - ControlCatalog::build(&plain, &MonitorOverlay::default(), RATE).expect("catalog"), - 4, - ); - assert_eq!(provider.structure_epoch(), 3, "the entry removed"); - assert_eq!( - provider - .control_list(&ControlListRequest { - structure_epoch: 2, - after_handle: 0, - limit: 1, - owner: None, - }) - .err(), - Some(ParameterProviderError::Stale) - ); - } - #[test] fn a_commit_refreshes_values_and_only_a_changed_address_sequence_advances_the_epoch() { let mut model = nine_track(); diff --git a/crates/host-core/src/prepare.rs b/crates/host-core/src/prepare.rs index 23d145d32..b71c080ea 100644 --- a/crates/host-core/src/prepare.rs +++ b/crates/host-core/src/prepare.rs @@ -7455,25 +7455,6 @@ mod tests { ); } - fn instance(effect_id: &str, bypass: bool, threshold: f32) -> session::Effect { - session::Effect { - id: session::StableId::parse("fx").expect("stable id"), - identity: session::EffectIdentity::Native { - effect_id: session::StableId::parse(effect_id).expect("effect id"), - }, - quality: session::EffectQuality::Normal, - bypass, - link_mode: session::LinkMode::DualMono, - params: vec![session::EffectParam { - parameter_id: 1, - channel: session::ParameterChannel::Both, - unit: session::ParameterUnit::Db, - value: threshold, - }], - sidechain: session::SidechainDeclaration::None, - } - } - /// The carry program is built only when some owner may carry, and the effect carry refuses a /// plan without one (`NoEffectCarryProgram`). A strip whose only non-prepared owner is a /// surviving insert must therefore say it may carry; a join that looked at the console slots @@ -7498,52 +7479,6 @@ mod tests { assert!(!chain(super::FamilyJoin::Prepared).may_carry()); } - /// #1280 D1: the one per-instance function says which difference is live. A compressor's - /// bypass and `Block`-rate threshold are live records (the bypass first, with the session's - /// ramp); the delay's bypass is a rebuild-only row, so the join must count it prepared. A join - /// that took any bypass for live would retarget a prepared bypass into a cell the lane lacks. - #[test] - fn the_per_instance_function_separates_live_from_prepared() { - let ramps = controls::LiveRamps::for_session( - &parse_session_json(include_str!("../../../fixtures/session/v1/canonical.json")) - .expect("fixture"), - ); - let records = |before: &session::Effect, after: &session::Effect| { - super::effect_instance_records(before, after, 48_000, ramps) - }; - let compressor = |bypass, threshold| instance("miso.compressor", bypass, threshold); - assert_eq!( - records(&compressor(false, -10.0), &compressor(false, -10.0)), - Ok(None) - ); - let live = records(&compressor(false, -10.0), &compressor(true, -20.0)) - .expect("live") - .expect("records"); - assert_eq!( - live.records.len(), - 3, - "the bypass, then the threshold on both lanes" - ); - assert_eq!( - live.records[0], - effect_contract::EffectControlRecord::Bypass(effect_contract::BypassSwitch { - bypassed: true, - ramp_samples: ramps.for_row(super::LiveRampRow::BypassCrossfade), - }) - ); - assert!( - records( - &instance("miso.delay", false, 0.0), - &instance("miso.delay", true, 0.0) - ) - .is_err(), - "the delay's bypass is prepared" - ); - let mut other = compressor(false, -10.0); - other.quality = session::EffectQuality::High; - assert!(records(&compressor(false, -10.0), &other).is_err()); - } - #[test] fn typed_diagnostic_adapters_preserve_projection_and_bound() { let effects = EffectDiagnosticSet(vec![ diff --git a/crates/host-core/src/spectrum.rs b/crates/host-core/src/spectrum.rs index 81579d24c..afa7be0eb 100644 --- a/crates/host-core/src/spectrum.rs +++ b/crates/host-core/src/spectrum.rs @@ -3274,39 +3274,6 @@ mod tests { (observer, capture) } - /// #1395 D3: only a named pair of captures of one key exchange places; the successor's capture - /// then holds the state the predecessor's held, and nothing else swaps. - #[test] - fn pair_carried_swaps_only_a_named_pair_of_one_key() { - let pair = [super::SpectrumPair { - successor: 0, - predecessor: 0, - }]; - let started = |channels| { - let (_observer, mut capture) = continuous_pair(channels); - capture.start_continuous(48_000, 128).expect("start"); - capture - }; - // A named pair of one key: the successor's fresh capture takes the running stream. - let (_observer, mut next) = continuous_pair(SpectrumChannels::Stereo); - let mut before = started(SpectrumChannels::Stereo); - assert!(next.pair_carried(&mut before, &pair)); - assert_eq!(next.stream_epoch(), Some(1)); - assert_eq!(before.stream_epoch(), None); - // An empty list names nothing. - let (_observer, mut next) = continuous_pair(SpectrumChannels::Stereo); - let mut before = started(SpectrumChannels::Stereo); - assert!(!next.pair_carried(&mut before, &[])); - assert_eq!(next.stream_epoch(), None); - assert_eq!(before.stream_epoch(), Some(1)); - // A list that names captures of another channel mask is a fault, never a pairing. - let (_observer, mut next) = continuous_pair(SpectrumChannels::Left); - let mut before = started(SpectrumChannels::Stereo); - assert!(!next.pair_carried(&mut before, &pair)); - assert_eq!(before.channels(), SpectrumChannels::Stereo); - assert_eq!(before.stream_epoch(), Some(1)); - } - /// Three captures of distinct keys with their observers, as a collection holds them. fn collection_of( keys: &[(&str, SpectrumChannels)], @@ -3572,24 +3539,6 @@ mod tests { assert_eq!(left[0], SPECTRUM_FLOOR_DB); } - #[test] - fn analyzer_calibrates_a_half_amplitude_at_minus_six_dbfs() { - let analyzer = SpectrumAnalyzer::new(); - let mut frequencies = [0.0; SPECTRUM_BIN_COUNT]; - let mut left = [0.0; SPECTRUM_BIN_COUNT]; - let mut right = [0.0; SPECTRUM_BIN_COUNT]; - let mut output = super::SpectrumOutput { - frequencies_hz: &mut frequencies, - left_dbfs: Some(&mut left), - right_dbfs: Some(&mut right), - }; - analyzer - .analyze(&window(0.5), 48_000, &mut output) - .expect("half-amplitude window analyzes"); - assert!((left[0] + 6.0206).abs() < 0.01, "DC dBFS: {}", left[0]); - assert!((right[0] + 6.0206).abs() < 0.01, "DC dBFS: {}", right[0]); - } - #[test] fn analyzer_matches_independent_dft_at_selected_bins_for_launch_rates() { let analyzer = SpectrumAnalyzer::new(); diff --git a/crates/host-core/tests/MUTATIONS.md b/crates/host-core/tests/MUTATIONS.md index 1de7892fb..6dbaeb43c 100644 --- a/crates/host-core/tests/MUTATIONS.md +++ b/crates/host-core/tests/MUTATIONS.md @@ -354,12 +354,13 @@ the stereo arm does not, and uncommanded they agree. ## Issue #1211 — one mute owner and live-control producers per strip -Driver: `cargo test -p host-core --test strip_controls --test strip_handles` and `cargo test -p host-core --test randomized`, one mutation at a time, tree +Driver: `cargo test -p host-core --test strip_controls --test strip_handles` (the `strip_handles` +binary deleted by TA5) and `cargo test -p host-core --test randomized`, one mutation at a time, tree restored between rows. Delivery host: x86_64 AVX2+FMA. | Row | Mutation | Red | | --- | --- | --- | -| 1211-M5 | build the control requests from `live_control_tracks` (the track prefix) again | `a_bus_fader_record_renders_as_the_session_with_that_fader` (three producers) and `handles_list_tracks_then_submixes_and_file_bus_effects` (`left: ["t0", "t1", "t2"]`) | +| 1211-M5 | build the control requests from `live_control_tracks` (the track prefix) again | `a_bus_fader_record_renders_as_the_session_with_that_fader` (three producers) and `handles_list_tracks_then_submixes_and_file_bus_effects` (deleted by TA5) (`left: ["t0", "t1", "t2"]`) | | 1211-M6 | sort `strip_controls` by ID instead of by `canonical_index` | `a_bus_fader_record_renders_as_the_session_with_that_fader` (the bus producer is no longer at strip index 3) | | 1211-M7 | `live_records` draws over `handles.track_count` instead of `strip_controls.len()` | `randomized_consoles_render_the_same_bits_armed_dual_and_serialized` (`bus_live_records: 0`) | @@ -402,3 +403,9 @@ Gate 4's base behaviour was replayed by removing the attach call entirely: the t |---|---|---| | 1246-H1 | `longest_vca_id_bytes` measures the first VCA, not the longest | `the_session_shape_measures_the_longest_vca_id`: 1, not 46 | | 1246-H2 | `longest_vca_id_bytes` measures the track IDs | the same test: 2, not 46 | + +## TA5 deletions + +The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. + +- `handles_list_tracks_then_submixes_and_file_bus_effects`: no same-crate KEEP catcher recorded diff --git a/crates/host-core/tests/browser_successor.rs b/crates/host-core/tests/browser_successor.rs index 09dbb11a5..b056811d9 100644 --- a/crates/host-core/tests/browser_successor.rs +++ b/crates/host-core/tests/browser_successor.rs @@ -107,14 +107,6 @@ fn session_b() -> SessionModel { model } -/// A without track `eq1` and its route. -fn session_without_eq1() -> SessionModel { - let mut model = session_a(); - model.tracks.retain(|track| track.id.as_str() != "eq1"); - model.routes.retain(|route| route.id.as_str() != "eq1-main"); - model -} - #[derive(Clone, Copy, Debug)] enum Branch { Single, @@ -506,38 +498,6 @@ fn the_concurrent_selected_meter_entry_forms_no_fused_bank_and_renders_the_same_ ); } -/// Gate 4. A successor whose capture target the new session removed is refused with the typed -/// `host.spectrum.target` diagnostic, on both branches, rather than prepared without its capture. -/// -/// Red if the successor path stops checking the capture target against the new session (the -/// preparation succeeds and a capture handle with no strip is handed to the host), or reports it -/// under another code. The base is borrowed (`&inventory`), so a refusal cannot move the -/// predecessor's state; this test claims nothing about the predecessor. -#[test] -fn a_successor_without_its_spectrum_target_is_refused_with_the_typed_diagnostic() { - let a = Session::compile(session_a()); - let b = Session::compile(session_without_eq1()); - for branch in [Branch::Single, Branch::Collection] { - // The capture targets track `eq1`, which B removed. - let (predecessor, _kept) = prepare(branch, &a, "eq1", None) - .unwrap_or_else(|failure| panic!("{branch:?} predecessor: {failure:?}")); - let edit_ramps = EditRamps::new(); - let refused = prepare( - branch, - &b, - "eq1", - Some(base(&predecessor.inventory, &a.model, &edit_ramps)), - ) - .err() - .unwrap_or_else(|| panic!("{branch:?}: the missing target was accepted")); - assert!( - String::from_utf8_lossy(refused.as_bytes()).contains("host.spectrum.target"), - "{branch:?}: {}", - String::from_utf8_lossy(refused.as_bytes()) - ); - } -} - /// Gate 5. Red if the swap block of a spectrum plan allocates or frees on render. #[test] fn the_spectrum_swap_block_allocates_and_frees_nothing() { diff --git a/crates/host-core/tests/collapse_arming.rs b/crates/host-core/tests/collapse_arming.rs index 32e1edef2..f946d2c29 100644 --- a/crates/host-core/tests/collapse_arming.rs +++ b/crates/host-core/tests/collapse_arming.rs @@ -554,15 +554,6 @@ fn a_live_left_only_polarity_write_on_misaligned_cohorts_renders_the_dual_bits() }); } -#[test] -fn a_live_left_only_trim_write_on_misaligned_cohorts_renders_the_dual_bits() { - assert_live_left_only_write(TrackInputRecord::TrimDb { - lanes: BuiltinLaneSelector::Left, - db: -6.0, - smoothing_samples: 0, - }); -} - // --------------------------------------------------------------------------------------------- // The controls: they pass under both rules, and they keep the fix from disarming too much. // --------------------------------------------------------------------------------------------- diff --git a/crates/host-core/tests/input_liveness_live_controls.rs b/crates/host-core/tests/input_liveness_live_controls.rs index 30b69c166..4cf9cb212 100644 --- a/crates/host-core/tests/input_liveness_live_controls.rs +++ b/crates/host-core/tests/input_liveness_live_controls.rs @@ -334,60 +334,6 @@ fn a_command_moves_exactly_the_addressed_track() { } } -/// Two tracks commanded to different values do not swap. -/// -/// The sharpest form of the lane-index test: a transposing drain renders the same *mix* either -/// way, so the two arms are compared per track. -#[test] -fn two_commands_are_not_interchangeable() { - let session = mono_session(); - let build = |first: usize, second: usize| { - let mut host = prepare_with_live_controls(&session); - push( - &mut host, - first, - TrackInputRecord::TrimDb { - lanes: BuiltinLaneSelector::Both, - db: -144.0, - smoothing_samples: 0, - }, - ); - push( - &mut host, - second, - TrackInputRecord::PolarityInvert { - lanes: BuiltinLaneSelector::Both, - inverted: true, - smoothing_samples: 0, - }, - ); - let _ = render(&mut host, 4); - peaks(&host).clone() - }; - let forward = build(0, 5); - let reversed = build(5, 0); - let names: Vec = forward.keys().cloned().collect(); - assert_ne!( - forward[&names[0]], reversed[&names[0]], - "silencing track {} is not the same as flipping it", - names[0] - ); - assert_ne!( - forward[&names[5]], reversed[&names[5]], - "and the same holds for track {}", - names[5] - ); - // The six tracks nobody addressed are identical in both arms, which is what says the two - // commands went where they were addressed rather than somewhere else consistently. - for index in [1_usize, 2, 3, 4, 6, 7] { - assert_eq!( - forward[&names[index]], reversed[&names[index]], - "track {} was addressed by neither arm", - names[index] - ); - } -} - /// A command takes effect on the **first sample of the block that drains it**. /// /// The drain contract every live-control gate in the tree rests on. With a zero-length window the @@ -727,73 +673,6 @@ fn a_per_lane_record_drained_on_the_disengaging_block_reaches_one_channel() { } } -/// The same window, then put back: re-equalising the two channels neither re-engages the collapse -/// nor moves the bits away from the never-collapsed oracle. -/// -/// The `LIVE` latch and the disengage window interact here, and the interaction is the one that -/// makes the fix's narrower restore rule safe: after the disengage the right channel's ramp record -/// is whatever live controls addressed to it, which is *nothing yet*, and the second record is what -/// puts it where the first put the left one. A copy at the boundary would have made the second -/// record a no-op and hidden the whole episode. -#[test] -fn re_equalising_after_a_disengaging_drain_holds_the_never_collapsed_bits() { - let mut mono = prepare_with_live_controls(&mono_session()); - let mut stereo = prepare_with_live_controls(SESSION); - let _ = render(&mut mono, 2); - let _ = render(&mut stereo, 2); - // One cohort at the eight-lane launch width, two on a four-lane (AArch64 NEON) build (#1017): - // every cohort collapses on both blocks. - let cohorts = (TRACKS / lane::Backend::current().width()) as u64; - let engaged = collapses(&mono)[0]; - assert_eq!( - engaged, - 2 * cohorts, - "every cohort collapses before the ride" - ); - - for host in [&mut mono, &mut stereo] { - push( - host, - 2, - TrackInputRecord::TrimDb { - lanes: BuiltinLaneSelector::Left, - db: -12.0, - smoothing_samples: 64, - }, - ); - } - assert_eq!( - render(&mut mono, 2), - render(&mut stereo, 2), - "the asymmetric episode diverged" - ); - - for host in [&mut mono, &mut stereo] { - push( - host, - 2, - TrackInputRecord::TrimDb { - lanes: BuiltinLaneSelector::Right, - db: -12.0, - smoothing_samples: 64, - }, - ); - } - assert_eq!( - render(&mut mono, 6), - render(&mut stereo, 6), - "the re-equalised episode diverged" - ); - assert_eq!(peaks(&mono), peaks(&stereo), "per-track meters diverged"); - // Only the cohorts track 2 is not in keep collapsing, on each of the eight blocks since. - assert_eq!( - collapses(&mono)[0], - engaged + (cohorts - 1) * 8, - "the ridden chain must not have collapsed again: `LIVE` is a latch, and re-equal words \ - alone do not re-arm it" - ); -} - /// A **symmetric** record drained on a collapsed block does not disengage, and still renders the /// never-collapsed bits. /// diff --git a/crates/host-core/tests/live_delta.rs b/crates/host-core/tests/live_delta.rs index 1cbe967c9..fbbaf8087 100644 --- a/crates/host-core/tests/live_delta.rs +++ b/crates/host-core/tests/live_delta.rs @@ -425,44 +425,6 @@ fn a_sign_of_zero_delay_edit_is_a_rebuild_by_its_row() { ); } -/// #1554 gate 1, D5. Red if a submix strip is left structural: its fader, mute and pan are live -/// records addressed to the submix's ID, as a track's are, and a track's records are not -/// addressed to it. -#[test] -fn a_submix_strip_fader_mute_and_pan_are_live() { - let current = with_bus(false); - assert_eq!( - classify(¤t, |next| next.submixes[0].fader.left_db = -6.0), - Ok(vec![( - "bus".to_owned(), - vec![fader_db(BuiltinLaneSelector::Left, -6.0, 0)], - None - )]) - ); - assert_eq!( - classify(¤t, |next| { - next.submixes[0].fader.left_mute = true; - next.submixes[0].fader.right_mute = true; - }), - Ok(vec![( - "bus".to_owned(), - vec![mute(BuiltinLaneSelector::Both, true, 0)], - None - )]) - ); - let panned = classify(¤t, |next| { - next.submixes[0].matrix_or_pan = MatrixOrPan::Pan { - left: 0.5, - right: 0.5, - smoothing_samples: 0, - }; - }) - .expect("a submix pan is live"); - assert_eq!(panned.len(), 1); - assert_eq!(panned[0].0, "bus"); - assert!(panned[0].1.is_empty() && panned[0].2.is_some()); -} - /// A delta's strip records and its route records, owned. type Both = (Vec, Vec); @@ -853,112 +815,6 @@ fn records_follow_effective_values_not_raw_ones() { assert_eq!(routes.len(), 1); } -/// #1558 gate 5, D7. Red if a record takes the wrong row's length: a VCA offset edit's own length -/// moves the member, the session's `fader_ms` length applies without one, a membership change takes -/// the `VcaOffset` row's session length, the strip's own `fader_db` edit wins over the VCA's on a -/// lane it moved, and a gate change from the source's mute takes the source's `Mute` length. -#[test] -fn vca_and_follow_records_take_the_ramp_of_the_edit_that_moved_them() { - use BuiltinLaneSelector::{Both, Left}; - let session_ramps = LiveRamps { - fader_samples: 100, - mute_samples: 300, - pan_samples: 200, - link_samples: 0, - }; - let current = with_vca(0.0, 0.0, [false; 2]); - let vca_target = EditRampTarget::Vca(current.vcas[0].id.clone()); - let first = track_id(¤t, 0); - let strips = |current: &SessionModel, - edit: fn(&mut SessionModel), - ramps: &EditRamps| - -> Vec { - let (strips, _) = classify_both(current, edit, session_ramps, ramps).expect("live"); - assert_eq!(strips.len(), 1); - assert_eq!(strips[0].0, first); - strips[0].1.clone() - }; - let mut ramp = EditRamps::new(); - ramp.set(&vca_target, EditRampRow::VcaFader, Some(480)); - assert_eq!( - strips(¤t, |next| next.vcas[0].fader.left_db = -6.0, &ramp), - vec![fader_db(Left, -6.0, 480)] - ); - // No edit length: `fader_ms`. - assert_eq!( - strips( - ¤t, - |next| next.vcas[0].fader.left_db = -6.0, - &EditRamps::new() - ), - vec![fader_db(Left, -6.0, 100)] - ); - // A mute takes the VCA mute's edit, else `mute_ms`. - let mut mute_ramp = EditRamps::new(); - mute_ramp.set(&vca_target, EditRampRow::VcaMute, Some(48)); - assert_eq!( - strips( - ¤t, - |next| next.vcas[0].fader.left_mute = true, - &mute_ramp - ), - vec![mute(Left, true, 48)] - ); - assert_eq!( - strips( - ¤t, - |next| next.vcas[0].fader.left_mute = true, - &EditRamps::new() - ), - vec![mute(Left, true, 300)] - ); - // The strip's own edit first: own length 0 steps the lane the strip moved, and the VCA's 480 - // applies to the lane only it moved; the two lanes are two records. - let mut both = EditRamps::new(); - both.set(&vca_target, EditRampRow::VcaFader, Some(480)); - both.set( - &EditRampTarget::Strip(current.tracks[0].id.clone()), - EditRampRow::Fader, - Some(0), - ); - assert_eq!( - strips( - ¤t, - |next| { - next.tracks[0].fader.left_db = -2.0; - next.vcas[0].fader.right_db = -6.0; - }, - &both - ), - vec![ - fader_db(Left, -2.0, 0), - fader_db(BuiltinLaneSelector::Right, -6.0, 480) - ] - ); - // One value, one length: both lanes merge. - assert_eq!( - strips( - ¤t, - |next| { - next.vcas[0].fader.left_db = -6.0; - next.vcas[0].fader.right_db = -6.0; - }, - &ramp - ), - vec![fader_db(Both, -6.0, 480)] - ); - // A membership change has no edit length: the `VcaOffset` row's session length. - let loud = with_vca(-6.0, -6.0, [false; 2]); - let (changed, _) = classify_both( - &loud, - |next| next.vcas[0].members.clear(), - session_ramps, - &ramp, - ) - .expect("live"); - assert_eq!(changed[0].1, vec![fader_db(Both, 0.0, 100)]); -} - /// #1558 gate 5. Red if a gate change from the source's mute ramps on its own: the send takes the /// length of the source strip's `Mute` record of the same transaction, so a strip and the sends that /// follow it ramp together; a gate change that the send's own `follows_mute` or `mute` edit caused @@ -1979,64 +1835,6 @@ fn ramps_of_a_move_with( [fader, muted, matrix.smoothing_samples] } -/// #1365 gate 5. Red if the classifier treats `control_smoothing` as structure, or reads the -/// pre-commit lengths for the records of the same transaction: setting, changing and clearing the -/// key are each live, and a fader move beside them carries the lengths of `next`. -#[test] -fn a_control_smoothing_edit_is_live_and_its_lengths_govern_the_same_transactions_records() { - use BuiltinLaneSelector::Left; - let slow = ControlSmoothing { - mute_ms: 5.0, - fader_ms: 15.0, - pan_ms: 25.0, - }; - let fast = ControlSmoothing { - mute_ms: 0.0, - fader_ms: 1.0, - pan_ms: 2.0, - }; - let base = two_tracks(); - // (before, after, the FaderDb length of `after` at 48 kHz). - for (before, after, fader_samples) in [ - (None, Some(slow), 720), - (Some(slow), Some(fast), 48), - (Some(slow), None, 960), - (None, Some(fast), 48), - ] { - let current = with_smoothing(&base, before); - let alone = edited(¤t, |next| next.control_smoothing = after); - let delta = classify_live_delta( - ¤t, - &alone, - LiveRamps::for_session(&alone), - &EditRamps::new(), - ) - .expect("live"); - assert!(delta.strips.is_empty(), "{before:?} -> {after:?}"); - - let moved = edited(¤t, |next| { - next.control_smoothing = after; - next.tracks[0].fader.left_db = -6.0; - }); - let delta = classify_live_delta( - ¤t, - &moved, - LiveRamps::for_session(&moved), - &EditRamps::new(), - ) - .expect("live"); - assert_eq!( - flatten(&delta), - vec![( - track_id(¤t, 0), - vec![fader_db(Left, -6.0, fader_samples)], - None - )], - "{before:?} -> {after:?}" - ); - } -} - /// Gate 3. Red if the classifier keeps a pan window of 0, ignores the session for one row, or maps /// a row to the wrong key: the session's `control_smoothing` (or the default table) is what the /// fader, mute and matrix records carry at 48 kHz, and a pan's own non-zero window still wins. @@ -2867,37 +2665,6 @@ fn filter_records_are_the_preparers_targets_for_the_changed_values() { assert_eq!(case(steady, steady), Ok(Vec::new())); } -/// #1556 D4. Red if a cutoff the filter domain refuses is acked live: above Nyquist, non-finite, -/// below the rate-keyed minimum, or an HPF at or above the LPF. -#[test] -fn filter_values_the_domain_refuses_need_a_rebuild() { - let current = with_filters((0.0, 0.0), (0.0, 0.0)); - let nyquist = current.sample_rate_hz as f32 / 2.0; - for (hpf, lpf) in [ - (nyquist, 0.0), - (f32::NAN, 0.0), - (0.0, f32::INFINITY), - (5.0, 0.0), - (-1.0, 0.0), - (1_000.0, 1_000.0), - (2_000.0, 1_000.0), - ] { - assert_eq!( - classify_input(¤t, |next| set_filters(next, (hpf, lpf), (0.0, 0.0))), - Err(LiveRebuild::Domain), - "{hpf} {lpf}" - ); - } - assert!( - classify_input(¤t, |next| set_filters( - next, - (80.0, 8_000.0), - (10.0, 0.0) - )) - .is_ok() - ); -} - /// #1556 D2. Red if the records of one strip are not trims, then polarities, then filter targets, /// each left before right, or if a track's input values reach another strip. #[test] @@ -3432,26 +3199,6 @@ fn an_eq_hpf_enable_change_carries_targets() { assert_eq!(targets, Some(expected)); } -/// #1265 gate 1(d). Red if the classifier admits an EQ value preparation refuses (a Q above its -/// domain), so that a live commit would leave a committed model its own rebuild refuses. -#[test] -fn an_out_of_domain_eq_q_needs_a_rebuild() { - let current = with_shaped_eq(); - assert_eq!( - eq_entry(¤t, |next| { - inserts(next, 2).push(param(5, ParameterChannel::Left, ParameterUnit::Ratio, 18.5)); - }), - Err(LiveRebuild::Domain) - ); - // The domain's own bound is live. - assert!( - eq_entry(¤t, |next| { - inserts(next, 2).push(param(5, ParameterChannel::Left, ParameterUnit::Ratio, 18.0)); - }) - .is_ok() - ); -} - // Issue #1266: effect bypass through the latency-preserving shunt. /// `with_effects` with the fixture's parametric EQ as a `pre_insert` console slot (`console(0)`, @@ -3547,96 +3294,6 @@ fn a_live_bypass_flip_is_one_record_ahead_of_its_parameters() { ); } -/// #1341 D1. Red if a bypass record carries a ramp other than the session's `mute_ms` length (the -/// fader's or pan's, or a per-edit mute length: the bypass is the one live row with no per-edit -/// length), if a flip with `mute_ms = 0` is not a step, or if a parameter-only edit carries a ramp. -#[test] -fn a_bypass_flip_carries_the_sessions_mute_ramp_and_nothing_else() { - let current = with_bypassable(); - let session_ramps = LiveRamps { - fader_samples: 100, - mute_samples: 300, - pan_samples: 200, - link_samples: 0, - }; - let ramp_of = |ramps: LiveRamps, edit_ramps: &EditRamps, flip: bool| { - let next = edited(¤t, |next| { - if flip { - next.tracks[0].inserts.effects[0].bypass = true; - } else { - inserts(next, 0).push(param(1, ParameterChannel::Left, ParameterUnit::Db, -30.0)); - } - }); - let delta = classify_live_delta(¤t, &next, ramps, edit_ramps).expect("live"); - assert_eq!(delta.effects.len(), 1); - delta.effects[0] - .records - .iter() - .find_map(|record| match record { - EffectControlRecord::Bypass(switch) => Some(switch.ramp_samples), - _ => None, - }) - .unwrap_or(0) - }; - assert_eq!(ramp_of(session_ramps, &EditRamps::new(), true), 300); - assert_eq!(ramp_of(STEP, &EditRamps::new(), true), 0); - assert_eq!(ramp_of(session_ramps, &EditRamps::new(), false), 0); - let mut edit_ramps = EditRamps::new(); - edit_ramps.set( - &EditRampTarget::Strip(current.tracks[0].id.clone()), - EditRampRow::Mute, - Some(50), - ); - assert_eq!( - ramp_of(session_ramps, &edit_ramps, true), - 300, - "no per-edit length" - ); - // A ramp the indexed law cannot spell is a domain refusal, never a record. - let too_long = LiveRamps { - mute_samples: lane::kernels::INDEXED_RAMP_LENGTH_MAXIMUM + 1, - ..session_ramps - }; - let next = edited(¤t, |next| { - next.tracks[0].inserts.effects[0].bypass = true - }); - assert_eq!( - classify_live_delta(¤t, &next, too_long, &EditRamps::new()).map(|_| ()), - Err(LiveRebuild::Domain) - ); -} - -/// #1266 D1 on the EQ. Red if an EQ's bypass record reaches its target designer (which refuses -/// it, so the edit would rebuild) or displaces its parameter edits: the bypass goes first and the -/// targets are the parameter edits' alone. -#[test] -fn an_eq_bypass_beside_a_gain_change_rides_ahead_of_its_targets() { - let current = with_shaped_eq(); - let seeds = prepared_eq_seeds(¤t); - let (records, targets) = eq_entry(¤t, |next| { - next.tracks[0].inserts.effects[2].bypass = true; - inserts(next, 2)[3].value = 5.0; - }) - .expect("live"); - assert_eq!( - records, - vec![ - EffectControlRecord::Bypass(effect_contract::BypassSwitch::step(true)), - record(3, Lane::Left, 5.0) - ] - ); - let expected = designed_targets( - current.sample_rate_hz, - &seeds, - &[EqTargetEdit { - parameter_id: 4, - channel: Lane::Left, - value: 5.0, - }], - ); - assert_eq!(targets, Some(expected)); -} - /// #1266 gate 1, decision 14 F4. Red if a prepared bypass -- the delay's or the multiband /// compressor's -- is classified live in either direction: the record would be acked and never /// heard, because the running plan holds that bypass in its prepared state. @@ -3700,98 +3357,6 @@ fn a_submix_effect_bypass_is_live() { ); } -/// `with_effects` with the first track routed into submix `bus`, whose strip carries the console -/// soft-clip slot's entry and a compressor insert of its own. -fn with_effect_bus() -> SessionModel { - let fixture = parse_session_json(FIXTURE).expect("fixture parses"); - let mut compressor = fixture.lower_track(&fixture.tracks[0]).pre_insert[0].clone(); - compressor.id = id("bcomp"); - compressor.identity = EffectIdentity::Native { - effect_id: id("miso.compressor"), - }; - compressor.params.clear(); - compressor.bypass = false; - let mut model = with_effects(); - let first = model.tracks[0].id.clone(); - let bus = id("bus"); - let route = model - .routes - .iter_mut() - .find(|route| matches!(&route.source, RouteSource::Track { track_id, .. } if *track_id == first)) - .expect("first track's route"); - let output = core::mem::replace( - &mut route.destination, - RouteDestination::SubmixInput { - submix_id: bus.clone(), - }, - ); - let mut bus_main = route.clone(); - bus_main.id = id("bus-main"); - bus_main.follows_mute = false; - bus_main.source = RouteSource::Submix { - submix_id: bus.clone(), - tap: SendTap::PostPan, - }; - bus_main.destination = output; - model.routes.push(bus_main); - let mut submix = Submix::unity(bus, &model.console); - submix.inserts.effects.push(compressor); - model.submixes.push(submix); - normalized(&model) -} - -/// #1554 D5 on effect parameters. Red if a submix strip's console entry or insert parameter is -/// left structural, or is addressed to a track: each is a record at its own strip's ID and -/// instance address. -#[test] -fn submix_effect_params_are_live() { - let current = with_effect_bus(); - assert_eq!(current.submixes.len(), 1); - assert_eq!( - current.submixes[0].console.len(), - 1, - "bus carries the clip slot" - ); - assert_eq!( - classify_effects(¤t, |next| { - next.submixes[0].console[0].params.push(param( - 1, - ParameterChannel::Both, - ParameterUnit::Db, - 6.0, - )); - }), - Ok(( - Vec::new(), - vec![( - "bus".to_owned(), - LiveEffectAddress::console(0), - vec![record(0, Lane::Left, 6.0), record(0, Lane::Right, 6.0)] - )] - )), - "submix console entry" - ); - assert_eq!( - classify_effects(¤t, |next| { - next.submixes[0].inserts.effects[0].params.push(param( - 1, - ParameterChannel::Left, - ParameterUnit::Db, - -30.0, - )); - }), - Ok(( - Vec::new(), - vec![( - "bus".to_owned(), - LiveEffectAddress::insert(0), - vec![record(0, Lane::Left, -30.0)] - )] - )), - "submix insert" - ); -} - /// An automation on `with_effects`' EQ insert (`insert(2)` of the first track). fn eq_automation(model: &SessionModel, parameter_id: u32, unit: ParameterUnit) -> Automation { Automation { @@ -3933,44 +3498,6 @@ fn a_rebuild_only_row_is_a_rebuild_with_its_reason() { ); } -/// Gate 3. Red if a setting produces a record or a rebuild: the pan window alone and the session's -/// ramp lengths alone change what later edits do, and nothing in the running plan. -#[test] -fn a_setting_change_alone_is_live_with_no_record() { - let current = two_tracks(); - assert_eq!( - classify(¤t, |next| { - let MatrixOrPan::Pan { - smoothing_samples, .. - } = &mut next.tracks[0].matrix_or_pan - else { - panic!("the settled tracks pan") - }; - *smoothing_samples = 32; - }), - Ok(Vec::new()) - ); - assert_eq!( - classify(¤t, |next| { - let mut lengths = session::CONTROL_SMOOTHING_DEFAULT; - lengths.fader_ms = 25.0; - next.control_smoothing = Some(lengths); - }), - Ok(Vec::new()) - ); - // The window is read by the records of the same edit: a move beside it uses the new window. - let moved = classify(¤t, |next| { - next.tracks[0].matrix_or_pan = MatrixOrPan::Pan { - left: 0.5, - right: 0.5, - smoothing_samples: 32, - }; - }) - .expect("a pan move is live"); - assert_eq!(moved.len(), 1); - assert_eq!(moved[0].2.expect("a matrix record").smoothing_samples, 32); -} - /// Gate 4. Red if the mask compares resolved values instead of representations, or the reverse: /// a `both` entry rewritten as equal `left` and `right` entries is a different field with the same /// value, so it is live with no record; a different value is a record. @@ -4147,64 +3674,6 @@ fn folded(gain_db: f32, matrix: [f32; 4], mute: bool, source_lane_muted: [bool; .expect("in the route domain") } -/// Gate 1. Red if a send record carries other bits than the coefficients preparation folds from the -/// post-commit values, or is emitted for a send whose held target does not change: a gain edit on a -/// muted send (the silenced `+0.0` target stays as it was) or a sign-of-zero rewrite. The unmute -/// then carries the gain the muted edit set. -#[test] -fn send_records_are_the_folded_target_of_the_post_commit_values() { - let current = with_bus(false); - let send = send_index(¤t); - let id = current.routes[send].id.as_str().to_owned(); - let matrix = matrix_of(¤t.routes[send]); - let gain = current.routes[send].gain_db; - - assert_eq!(classify_routes(¤t, |_| {}), Ok(Vec::new())); - assert_eq!( - classify_routes(¤t, |next| next.routes[send].gain_db = -6.0), - Ok(vec![( - id.clone(), - folded(-6.0, matrix, false, [false; 2]), - false, - 0 - )]) - ); - let swapped = [0.0, 1.0, 1.0, 0.0]; - assert_eq!( - classify_routes(¤t, |next| { - let m = &mut next.routes[send].channel_matrix; - (m.ll, m.lr, m.rl, m.rr) = (0.0, 1.0, 1.0, 0.0); - }), - Ok(vec![( - id.clone(), - folded(gain, swapped, false, [false; 2]), - false, - 0 - )]) - ); - assert_eq!( - classify_routes(¤t, |next| next.routes[send].mute = true), - Ok(vec![(id.clone(), [0.0; 4], true, 0)]) - ); - - // A gain edit on a muted send gives no record, and the later unmute carries the new gain. - let muted = edited(¤t, |model| model.routes[send].mute = true); - assert_eq!( - classify_routes(&muted, |next| next.routes[send].gain_db = -6.0), - Ok(Vec::new()) - ); - let edited_while_muted = edited(&muted, |model| model.routes[send].gain_db = -6.0); - assert_eq!( - classify_routes(&edited_while_muted, |next| next.routes[send].mute = false), - Ok(vec![( - id, - folded(-6.0, matrix, false, [false; 2]), - false, - 0 - )]) - ); -} - /// Gate 1. Red if a following send's record ignores the source lane mutes (the rebuilt plan would /// zero a column the record leaves), or zeroes them for a send that does not follow. #[test] diff --git a/crates/host-core/tests/route_mute.rs b/crates/host-core/tests/route_mute.rs index 4a358527a..94cf5add0 100644 --- a/crates/host-core/tests/route_mute.rs +++ b/crates/host-core/tests/route_mute.rs @@ -1386,28 +1386,6 @@ fn a_fully_follow_muted_undelayed_send_is_inactive() { } } -/// #1218 gate 2, delayed. #1217 gate 3's bus with `d-e` open but following `d`, whose two lanes -/// are muted: `d-e`'s edge into `e` carries the limiter's 486-sample compensation (asserted from -/// the compiled plan), so it stays active, mixes `[+0.0; 4]` every block, and the bus renders that -/// gate's oracle. -/// -/// Red if a fully follow-muted delayed send is skipped (its counter stops, and its consumer would -/// stage a buffer this block never wrote) or still mixes its open coefficients. -#[test] -fn a_fully_follow_muted_delayed_send_stays_active() { - let mut model = delayed_bus(true); - assert_eq!(model.tracks[0].id.as_str(), "d"); - let fader = &mut model.tracks[0].fader; - (fader.left_mute, fader.right_mute) = (true, true); - let follow = model - .routes - .iter_mut() - .find(|route| route.id.as_str() == "d-e") - .expect("d-e is declared"); - (follow.mute, follow.follows_mute) = (false, true); - assert_delayed_route_stays_active(&delayed_session_checked(model)); -} - // ---- #1652: a following send of a stored mute --------------------------------------------- /// #1652 (batch 18, #1652c): a send into a submix whose source strip automates its mute holds the diff --git a/crates/host-core/tests/spectrum.rs b/crates/host-core/tests/spectrum.rs index 2641be9ed..e15df8eb8 100644 --- a/crates/host-core/tests/spectrum.rs +++ b/crates/host-core/tests/spectrum.rs @@ -40,86 +40,6 @@ fn caps() -> HostPrepareCaps { } } -fn render_window( - host: host_core::PreparedHost, - mut capture: host_core::SpectrumCapture, -) -> host_core::SpectrumWindow { - capture.arm().expect("first spectrum arm"); - assert_eq!( - capture.try_read().expect_err("window is initially pending"), - SpectrumCaptureReadError::Pending - ); - assert!(capture.arm().is_err(), "one outstanding capture"); - let (mut session, mut sources, _) = host.start_render_session().expect("start render"); - for block in 0..(SPECTRUM_WINDOW_FRAMES / QUANTUM) { - let left = [0.25_f32; QUANTUM]; - let right = [-0.5_f32; QUANTUM]; - sources - .submit( - b"fixture-source", - SourceSubmission { - generation: 1, - start_frame: (block * QUANTUM) as u64, - sample_rate_hz: 48_000, - planes: &[&left, &right], - frames: QUANTUM as u32, - end_of_region: false, - }, - ) - .expect("source block"); - let mut output = [0.0_f32; QUANTUM * 2]; - session - .render_planar(&mut output, 2, QUANTUM, QUANTUM, (block * QUANTUM) as u64) - .expect("render block"); - } - let window = capture.try_read().expect("complete spectrum window"); - assert_eq!(window.first_sample, 0); - assert_eq!(window.end_sample(), Some(SPECTRUM_WINDOW_FRAMES as u64)); - window -} - -#[test] -fn prepared_graph_captures_all_three_real_boundaries_and_analyzes() { - let compiled = compile_host_session(SESSION, &caps()).expect("compiled fixture"); - let targets = [ - SpectrumTarget::TrackPostInputBuiltins("eq0".into()), - SpectrumTarget::TrackPostMatrix("eq0".into()), - SpectrumTarget::Output("main-out".into()), - ]; - for target in targets { - let capture_bytes = spectrum_capture_resources_for(&target).retained_bytes; - let (host, capture) = prepare_host_runtime_with_spectrum( - &compiled, - &caps(), - &SpectrumCaptureRequest { - target, - channels: SpectrumChannels::Stereo, - maximum_capture_bytes: capture_bytes, - }, - ) - .expect("selected target prepares"); - let window = render_window(host, capture); - assert!(window.left.iter().any(|value| *value != 0.0)); - assert!(window.right.iter().any(|value| *value != 0.0)); - - let analyzer = SpectrumAnalyzer::new(); - let mut frequencies = [0.0; SPECTRUM_BIN_COUNT]; - let mut left_dbfs = [0.0; SPECTRUM_BIN_COUNT]; - let mut right_dbfs = [0.0; SPECTRUM_BIN_COUNT]; - let mut output = host_core::SpectrumOutput { - frequencies_hz: &mut frequencies, - left_dbfs: Some(&mut left_dbfs), - right_dbfs: Some(&mut right_dbfs), - }; - analyzer - .analyze(&window, 48_000, &mut output) - .expect("captured window analyzes"); - assert_eq!(frequencies[0], 0.0); - assert!(left_dbfs.iter().all(|value| value.is_finite())); - assert!(right_dbfs.iter().all(|value| value.is_finite())); - } -} - #[test] fn spectrum_capture_refuses_unknown_target_and_small_budget_without_partial_state() { let compiled = compile_host_session(SESSION, &caps()).expect("compiled fixture"); diff --git a/crates/host-core/tests/stored_fader_automation.rs b/crates/host-core/tests/stored_fader_automation.rs index 6934cff6d..4cb31a7a1 100644 --- a/crates/host-core/tests/stored_fader_automation.rs +++ b/crates/host-core/tests/stored_fader_automation.rs @@ -2612,72 +2612,6 @@ fn a_session_seek_sets_a_latent_fader_lane_exactly_where_it_reaches_the_node() { } } -/// #1647 gate 8 with a seek step after `E` in `E`'s block (MAJOR-4 of the U2 review): behind a -/// latent insert, the unmute of 1,000 samples ends at `E` = 1,000, and a session seek (to 8,000) -/// at a block entry `r` reaches the fader at `r + a(n)`, after `E` in the same block (quantum 100: -/// `E` at the block's first sample; quantum 37: inside it). The lane moves at `E`, and from the -/// seek's step it is set exactly: the probe shows its `Set` there, and the output equals the plan -/// whose fader is static at the curve's value at 8,000, on the bank and on the oracle. -/// -/// Test value: red if the cut at `E` drops a seek step that lies after it in the block. -#[test] -fn a_seek_step_after_the_unmute_ramps_end_in_its_block_still_lands() { - const FRAMES: u64 = 2_048; - const T: u64 = 8_000; - let (model, entry) = muted_ride(fade_from(-30.0, 48_000), 10.0); - let (model, arrival) = latent(model); - let e = u64::from(UNMUTE); - for quantum in [100_u32, 37] { - let q = u64::from(quantum); - let block_end = (e / q + 1) * q; - let r = (0..=e / q) - .map(|k| k * q) - .find(|&r| r + arrival > e && r + arrival < block_end) - .expect("a block entry whose seek reaches the fader after E in E's block"); - let step = r + arrival; - let script = move |rig: &mut Rig| { - if rig.rendered == 0 { - live_mute(rig, builtins::BuiltinLaneSelector::Both, false, UNMUTE); - } - if rig.rendered == r { - rig.seek(T); - } - }; - let out = scripted_across(&model, &[quantum], FRAMES, &script); - let value = curve(&entry, T as i64).to_bits(); - let mut at_quantum = model.clone(); - at_quantum.quantum_frames = quantum; - assert_eq!( - sets_in_block_of(&at_quantum, &script, step), - [(0, step, value), (1, step, value)], - "quantum {quantum}: a Set per channel at the step" - ); - let mut reference_model = static_at(&model, curve(&entry, T as i64)); - reference_model.quantum_frames = quantum; - let reference = scripted(Rig::new(&reference_model), FRAMES, &|rig| { - if rig.rendered == r { - rig.seek(T); - } - }); - let step = step as usize; - for plane in 0..2 { - assert_ne!( - out[plane][step - 1].to_bits(), - reference[plane][step - 1].to_bits(), - "quantum {quantum}: the move ramps before the step" - ); - assert_eq!( - first_difference( - &out[plane][step..step + 64], - &reference[plane][step..step + 64] - ), - None, - "quantum {quantum}, plane {plane}: exact from the step" - ); - } - } -} - /// The fader gain of `db` composed with the VCA offset sum `offsets` (#1646 D1). fn composed(db: f32, offsets: f64) -> f32 { builtins::checked_fader_gain(session::vca_compose_db(db, offsets)).expect("fader domain") diff --git a/crates/host-core/tests/strip_handles.rs b/crates/host-core/tests/strip_handles.rs deleted file mode 100644 index 6873e5939..000000000 --- a/crates/host-core/tests/strip_handles.rs +++ /dev/null @@ -1,151 +0,0 @@ -//! Issue #1207 gate 2: the live-control handles list every strip, tracks first. -//! -//! `HostLiveControlHandles::strips` is the addressing authority: the tracks in canonical ID order, -//! then the submixes in canonical ID order. The builtin control producers (`strip_controls`) are -//! parallel to every strip (issue #1211 D1), the default meters stay parallel to the track prefix, -//! and a submix's effects get a live channel and an observation handle exactly as a track's do (D5 -//! removed the K1 interim). - -use core::num::{NonZeroU32, NonZeroUsize}; - -use builtins::MeterTap; -use host_core::{ - HostLiveControlRequest, HostPrepareCaps, HostShapePolicy, - prepare_host_session_with_live_controls, -}; -use session::{ - RouteDestination, RouteSource, SendTap, StableId, Submix, canonical_session_json, - parse_session_json, -}; - -const FIXTURE: &str = include_str!("../../../fixtures/session/v1/observation-frame-shape.json"); -const QUANTUM: u32 = 128; - -fn caps() -> HostPrepareCaps { - HostPrepareCaps { - shape: HostShapePolicy::AnyLaunchRate, - source_ring_frames: 1_024, - maximum_automation_spans_per_block: 128, - maximum_tracks: 100, - maximum_submixes: 100, - maximum_vcas: 100, - maximum_vca_reach_pairs: u64::MAX, - maximum_sources: 100, - maximum_routes: 100, - maximum_effects: 100, - maximum_graph_session_plus_plan_bytes: 100_000_000, - maximum_source_total_bytes: 10_000_000, - maximum_source_overhead_bytes: 10_000_000, - maximum_effect_state_bytes: 100_000_000, - maximum_effect_scratch_bytes: 100_000_000, - maximum_builtin_retained_bytes: 100_000_000, - maximum_named_allocation_bytes: 100_000_000, - maximum_meter_streams: 64, - maximum_meter_items: 1 << 16, - maximum_meter_bytes: 1 << 24, - } -} - -/// The three-track observation fixture (`t0` compressor, `t1` EQ, `t2` gate) plus two submixes, -/// declared out of order: `zzz-bus` sorts after every track and `aaa-bus` before every one, so -/// a strip list that merged the two segments by sorting would put `aaa-bus` first. Each bus -/// carries a compressor insert; `t0` feeds `zzz-bus`, `t1` feeds `aaa-bus`, and both buses feed -/// the output. -fn document() -> String { - let mut model = parse_session_json(FIXTURE).expect("observation fixture parses"); - let compressor = model.tracks[0].inserts.effects[0].clone(); - for (bus, feeder) in [("zzz-bus", 0_usize), ("aaa-bus", 1)] { - let id = StableId::parse(bus).expect("bus id"); - let mut submix = Submix::unity(id.clone(), &model.console); - let mut insert = compressor.clone(); - insert.id = StableId::parse("bus-comp").expect("insert id"); - submix.inserts.effects.push(insert); - model.submixes.push(submix); - let mut out = model.routes[feeder].clone(); - model.routes[feeder].destination = RouteDestination::SubmixInput { - submix_id: id.clone(), - }; - out.id = StableId::parse(&format!("{bus}-main")).expect("route id"); - out.source = RouteSource::Submix { - submix_id: id, - tap: SendTap::PostPan, - }; - model.routes.push(out); - } - canonical_session_json(&model).expect("canonical bus session") -} - -/// Red if a submix lands inside the track prefix (or the strips stop being tracks-then-submixes), -/// if the builtin controls stop being parallel to the strips (issue #1211 D1), if the default -/// meters start to cover buses, or if a bus effect stays without a live channel or an observation -/// handle. -#[test] -fn handles_list_tracks_then_submixes_and_file_bus_effects() { - let (_, _, handles) = prepare_host_session_with_live_controls( - &document(), - &caps(), - &HostLiveControlRequest { - meter_period_frames: Some(NonZeroU32::new(QUANTUM).expect("period")), - meter_queue_depth: NonZeroUsize::new(16).expect("meter depth"), - meter_tap: MeterTap::PostMatrix, - observation_taps: 1, - master_track: None, - }, - ) - .unwrap_or_else(|failure| panic!("prepare: {}", String::from_utf8_lossy(failure.as_bytes()))); - - let strips: Vec<&str> = handles.strips.iter().map(AsRef::as_ref).collect(); - assert_eq!(strips, ["t0", "t1", "t2", "aaa-bus", "zzz-bus"]); - assert_eq!(handles.track_count, 3); - - let tracks = &strips[..handles.track_count]; - let controls: Vec<&str> = handles - .strip_controls - .iter() - .map(|control| control.track_id.as_ref()) - .collect(); - assert_eq!( - controls, strips, - "one builtin channel per strip, in strip order" - ); - let meters: Vec<&str> = handles - .meters - .iter() - .map(|meter| meter.track_id.as_ref()) - .collect(); - assert_eq!(meters, tracks, "one default meter per track, none per bus"); - - let mut effects: Vec<(&str, &str)> = handles - .effect_controls - .iter() - .map(|producer| (producer.track_id.as_ref(), producer.effect_id.as_ref())) - .collect(); - effects.sort_unstable(); - assert_eq!( - effects, - [ - ("aaa-bus", "bus-comp"), - ("t0", "comp"), - ("t1", "eq"), - ("t2", "gate"), - ("zzz-bus", "bus-comp"), - ], - "every prepared effect, the buses' included, has a live channel" - ); - let mut observed: Vec<(&str, &str)> = handles - .effect_observations - .iter() - .map(|handle| (handle.track_id.as_ref(), handle.effect_id.as_ref())) - .collect(); - observed.sort_unstable(); - assert_eq!( - observed, - [ - ("aaa-bus", "bus-comp"), - ("t0", "comp"), - ("t2", "gate"), - ("zzz-bus", "bus-comp"), - ], - "every tapped effect, the buses' included, has an observation handle (the EQ has no tap)" - ); -} diff --git a/crates/host-core/tests/successor_swap.rs b/crates/host-core/tests/successor_swap.rs index ba83bd3c3..471c12608 100644 --- a/crates/host-core/tests/successor_swap.rs +++ b/crates/host-core/tests/successor_swap.rs @@ -740,30 +740,6 @@ fn a_future_anchor_starts_the_added_stem_on_its_block_at_four_lanes() { future_anchor_starts_the_added_stem_on_its_block(Backend::Simd4); } -/// Gate 2 of #1274: the anchor is two blocks before the swap block, so the render observes the -/// seek late and starts the stem at the swap block's own frame. Red if the lateness is measured -/// on any clock but the plan's absolute render sample, or not added at all. -#[test] -fn a_past_anchor_starts_the_added_stem_in_time() { - const ANCHOR_BLOCK: usize = SWAP_BLOCK - 2; - let kept = Feed::new(SOURCE, 1); - let added = Feed::new(ADDED_SOURCE, 2); - let b = Session::compile(added_source_session()); - let reference_added = spliced(ADDED_SOURCE, &added, |block| { - (block >= SWAP_BLOCK).then_some(block) - }); - for backend in backends() { - let reference = reference_run(&b, &[&kept, &reference_added], backend); - let run = anchored_swap_run(&kept, &added, ANCHOR_BLOCK, SWAP_BLOCK, backend); - assert_eq!( - first_difference(&run, &reference), - None, - "{backend:?}: the stem is in time from the swap block on" - ); - assert!(reference[SWAP_BLOCK].bits.iter().any(|bits| *bits != 0)); - } -} - /// Gate 3 of #1274: two playing sources, each given `seek_at` to one anchor before a different /// render, both jump in the anchor block. Each keeps playing its queued old generation until then. /// Red if a seek applies when observed, if a held seek drops the old generation's queued PCM, or @@ -1569,14 +1545,6 @@ fn a_high_pass_ramp_in_flight_finishes_after_the_swap(backend: Backend) { ); } -/// Gate 2, ramp case, at eight lanes. Red if the coefficient target, step or countdown is not -/// carried: the successor's lane would jump to its prepared coefficients or freeze mid-ramp. -#[cfg(target_feature = "avx2")] -#[test] -fn a_high_pass_ramp_in_flight_finishes_after_the_swap_at_eight_lanes() { - a_high_pass_ramp_in_flight_finishes_after_the_swap(Backend::Simd8); -} - /// Gate 2, ramp case, at four lanes. #[test] fn a_high_pass_ramp_in_flight_finishes_after_the_swap_at_four_lanes() { @@ -6125,12 +6093,6 @@ fn a_changed_control_kind_carries_no_effect_lane(backend: Backend) { ); } -#[cfg(target_feature = "avx2")] -#[test] -fn a_changed_control_kind_carries_no_effect_lane_at_eight_lanes() { - a_changed_control_kind_carries_no_effect_lane(Backend::Simd8); -} - #[test] #[cfg_attr( not(any(target_feature = "neon", target_feature = "simd128")), @@ -8959,72 +8921,6 @@ fn compensated_submix_lines_keep_their_audio_at_every_rate_and_width() { } } -/// Two submixes, each with a limited and an unlimited track: `eq0` (limited) and `eq1` into -/// `bus1`, `eq3` (limited) and `eq2` into `bus2`. Both unlimited routes carry a line. -fn two_bus_session(rate: u32, silent: Option<&str>) -> SessionModel { - let mut model = pdc_session(rate, &["eq0", "eq3"]); - add_bus(&mut model, "bus1"); - add_bus(&mut model, "bus2"); - send_route_to(&mut model, "eq0", "bus1"); - send_route_to(&mut model, "eq1", "bus1"); - send_route_to(&mut model, "eq2", "bus2"); - send_route_to(&mut model, "eq3", "bus2"); - if let Some(track) = silent { - model = with_mute(model, track, true); - } - model -} - -/// Gate 4: B re-points `eq1`'s route from `bus1` to `bus2`. The route keeps its ID and gets -/// another destination, so its line does not carry and its strip is not restarted. -/// -/// With `eq1` muted in both sessions the re-pointed line holds only zeros, so the run equals B -/// from frame 0 exactly when every other line carried: `eq2`'s line into `bus2` is the one that -/// does. With `eq1` playing, the new route's line starts at rest and the swap block misses the -/// audio B's line holds: red if the line followed the route ID into the other bus, where it -/// would play the old pending audio and match B. -fn a_repointed_route_does_not_carry_its_line(backend: Backend, rate: u32) { - let what = format!("{backend:?} at {rate} Hz"); - let feed = Feed::new(SOURCE, 1); - for silent in [Some("eq1"), None] { - let a_model = two_bus_session(rate, silent); - let mut b_model = a_model.clone(); - send_route_to(&mut b_model, "eq1", "bus2"); - let (a, b) = (Session::compile(a_model), Session::compile(b_model)); - let (run, reference) = pdc_runs(&a, &b, &feed, backend, &what); - assert_eq!( - run.restarted, - Vec::>::new(), - "{what}: the re-pointed route's strip keeps its state" - ); - // Before the swap A renders, and A routes `eq1` through the other bus. - let difference = run.blocks[SWAP_BLOCK..] - .iter() - .zip(&reference.blocks[SWAP_BLOCK..]) - .position(|(swapped, fresh)| swapped != fresh); - if silent.is_some() { - assert_eq!( - difference, None, - "{what}: every other line carried, and the re-pointed one held silence" - ); - } else { - assert!( - difference.is_some(), - "{what}: the new route's line starts at rest, so B's audio is missing after the swap" - ); - } - } -} - -#[test] -fn a_repointed_route_does_not_carry_its_line_at_every_rate_and_width() { - for backend in backends() { - for rate in PDC_RATES { - a_repointed_route_does_not_carry_its_line(backend, rate); - } - } -} - // Issue #1285 (slice 15 of #1269): latency floors. A successor is compiled with every node's // input arrival in its predecessor as a floor, so removing a latent effect, or the track that // holds it, moves no surviving path in time: every compensation line keeps its length and carries. @@ -10299,82 +10195,6 @@ mod spectrum_carry { ); } - /// Gate 1: a continuous capture keeps reading, window for window, across the swap. - #[test] - fn a_continuous_capture_carries_and_its_reader_keeps_receiving() { - let (a, b) = sessions(); - let request = stereo_post_matrix(); - let expected = reference(&b, &request, Drive::Stream); - let run = swapped( - &a, - &request, - &b, - &request, - Drive::Stream, - Promote::AsReported, - ); - assert_eq!(run.blocks[SWAP].carry, CarryOutcome::Carried); - assert_eq!(run.blocks[SWAP].swap, SwapOutcome::Applied); - assert_eq!( - run.pairing, - vec![SpectrumPair { - successor: 0, - predecessor: 0 - }] - ); - assert_eq!( - run.record, - vec![ObserverCarry { - kind: ObserverKind::Spectrum, - successor: 0, - predecessor: 0 - }], - "the swap block moved exactly the observers the control side's list pairs" - ); - - let reads = &run.driver.reads; - assert_eq!(reads.len(), TOTAL); - let first_ready = reads - .iter() - .position(|read| read.status == SpectrumStreamStatus::Ready) - .expect("the stream produces before the swap"); - assert!(first_ready < SWAP, "a window is complete before the swap"); - let mut next_sequence = 0; - let mut spanning = false; - let mut after_swap = 0; - for (block, (got, want)) in reads.iter().zip(&expected.reads).enumerate() { - assert_eq!(got.status, want.status, "block {block}: status"); - if block > first_ready { - assert!( - matches!( - got.status, - SpectrumStreamStatus::Ready | SpectrumStreamStatus::Pending - ), - "block {block}: no Failed, Gap or Warming after the stream first produced, got {:?}", - got.status - ); - } - match (&got.window, &want.window) { - (Some(got), Some(want)) => { - assert_same_window(got, want, &format!("block {block}")); - assert_eq!(got.sequence, next_sequence, "block {block}: no gap"); - assert_eq!(got.stream_epoch, 1, "block {block}: the epoch never moved"); - next_sequence += 1; - spanning |= got.first_sample < SWAP_SAMPLE - && got.end_sample().expect("end") > SWAP_SAMPLE; - after_swap += usize::from(block >= SWAP); - } - (None, None) => {} - _ => panic!("block {block}: a window on one side only"), - } - } - assert!( - spanning, - "a window spans the swap, or the carry is not tested" - ); - assert!(after_swap >= 3, "the reader keeps receiving after the swap"); - } - /// Without the pairing the host reads the predecessor's capture, whose observer sits in the /// displaced plan: the oracle above can fail. #[test] diff --git a/crates/host-core/tests/symmetry_witness.rs b/crates/host-core/tests/symmetry_witness.rs index fb8e70286..0dc05144f 100644 --- a/crates/host-core/tests/symmetry_witness.rs +++ b/crates/host-core/tests/symmetry_witness.rs @@ -610,46 +610,6 @@ fn a_left_channel_command_declines_exactly_one_lane() { ); } -/// Two per-lane EQ **target** writes that leave the channels agreeing, applied in one block, keep -/// the lane; written apart, or to different words, they decline it. -/// -/// # The contract this pins, after decision 15's cells (issue #1345 D3) -/// -/// The EQ is a prepared-target owner: every edit reaches the drain as designed targets in cells, -/// one per `(slot, channel)`. A `Left` edit and a `Right` edit to the same value are two owner -/// transactions that write the two channel cells with equal designed words. The lane folds its -/// witness from the cells' final contents: two cells applied in one block with equal words are one -/// both-channel write (and are applied once, as a `Both` target), so the lane keeps `LIVE`. This is -/// the structural form of #1004's pairing rule. It was not the rule while targets were a FIFO: a -/// FIFO has no last-wins staging, so equal last targets per channel did not mean equal state, and -/// the pair declined. A cell holds the last write, so equal words mean equal state, given that -/// `LIVE` held before (the channels were doing identical work). -/// -/// What still declines: a pair split across two blocks (each block sees a lone write), words that -/// differ, and `[Left A, Both C, Right A]` (the cells end at `C` and `A`). -/// -/// Red mutations: fold each applied target cell as a lone write (never pairing) -> the first test -/// fails; pair any two applied target cells whatever their words -> the other two fail. -#[test] -fn two_per_lane_target_writes_that_agree_in_one_block_keep_the_lane() { - let (_session, mut console) = prepare(&mono_session()); - let [before, lanes] = census(&console); - assert_eq!(before, lanes, "the fixture starts fully eligible"); - - push(&mut console, "eq2", parameter(ParameterChannel::Left, -6.0)); - push( - &mut console, - "eq2", - parameter(ParameterChannel::Right, -6.0), - ); - render(&mut console, 8); - assert_eq!( - census(&console), - [before, lanes], - "two one-channel EQ targets with equal words, applied in one block, are one both-channel write" - ); -} - #[test] fn two_per_lane_target_writes_split_across_blocks_or_unequal_decline_the_lane() { let (_session, mut console) = prepare(&mono_session()); @@ -692,7 +652,7 @@ const COMPRESSOR_SESSION: &str = /// Twin `Parameter` spans keep the lane; a lone half, or a twin one ulp apart, declines it. /// /// The span half of the contract `two_per_lane_target_writes_that_agree_in_one_block_keep_the_lane` -/// states for targets (issue #1004, issue #1012). A `Left` record and a `Right` record of the same +/// (deleted by TA5) stated for targets (issue #1004, issue #1012). A `Left` record and a `Right` record of the same /// parameter and the same value, drained in one block, stage as adjacent twin spans; the /// compressor validates and applies `pending[0][p]` and `pending[1][p]` by the same code from the /// same value, so the two channels stay in bit-equal state and `LIVE` is kept. Any one-channel diff --git a/crates/host-core/tests/timeline.rs b/crates/host-core/tests/timeline.rs index 024e8aee3..624e19323 100644 --- a/crates/host-core/tests/timeline.rs +++ b/crates/host-core/tests/timeline.rs @@ -532,31 +532,6 @@ fn a_seek_queued_at_the_swap_block_lands_in_the_successors_first_block() { ); } -/// The D3 guard (batch 16a fold MINOR 3): a successor prepared by hand at another rate or quantum -/// gets a fresh timeline with a producer of its own, not a vacant one whose carry the swap block -/// would refuse. -/// -/// Test value: red if the guard is dropped (every successor's timeline vacant): this successor -/// would hold no timeline producer until a carry that cannot happen. -#[test] -fn a_successor_at_another_rate_or_quantum_starts_a_fresh_timeline() { - let a = Session::compile(one_track()); - let predecessor = a.prepare(Backend::current()); - for (rate, quantum) in [(44_100, QUANTUM as u32), (48_000, 2 * QUANTUM as u32)] { - let mut model = one_track(); - model.sample_rate_hz = rate; - model.quantum_frames = quantum; - let b = Session::compile(model); - let mut successor = successor_of(&b, &a, &predecessor.inventory, false); - let fresh = producer(&mut successor.sources); - assert_eq!( - fresh.active_generation(), - SourceGeneration(1), - "{rate} Hz, {quantum} frames" - ); - } -} - /// Gate 2, a successor with a larger `A_max`: its history is its own, of its own `K`. /// /// Test value: red if the successor sizes its history from the predecessor's `K` (a seek its diff --git a/crates/host-core/tests/withdrawn_successor.rs b/crates/host-core/tests/withdrawn_successor.rs index 500d40cbf..d135bdca0 100644 --- a/crates/host-core/tests/withdrawn_successor.rs +++ b/crates/host-core/tests/withdrawn_successor.rs @@ -1104,51 +1104,3 @@ fn an_owner_a_restarts_or_removes_and_b_reverts_carries_from_p0() { reverted_owner_carries_from_p0(removed(), backend); } } - -/// #1698, sources stay under the host contract: a source declared differently in the newest -/// committed session restarts at generation 1, frame 0 (`CONTROL_PROTOCOL_SEMANTICS.md`). A -/// redeclares or removes P0's `b`, and B declares it as P0 does: `b` is fresh in B, a ring of its -/// own that takes generation 1 from frame 0, while `a`, which A carries from P0, still carries. -/// -/// Test value: red if B's base carries a source A did not carry from P0 (P0's full inventory -/// without the source filter), which would refuse the host's restarted feed of `b`. -#[test] -fn a_source_b_reverts_is_fresh_and_a_source_a_carries_still_carries() { - let backend = Backend::current(); - let p0 = Session::compile(p0_model()); - let signal = signal(); - for (label, a_model) in [ - ("b redeclared", with_b_frames(40_000)), - ("b removed", without(p0_model(), "b", "tb")), - ] { - let a = Session::compile(a_model); - let mut running = running_p0(&p0, backend, &signal); - let mut withdrawn = successor_of(&a, &running.inventory, &p0.model, backend); - withdrawn.sources.adopt_persisting(&mut running.sources); - let base = base_across(&running, &withdrawn); - let mut replacement = successor_of(&p0, &base, &p0.model, backend); - let index = |source: &str| { - replacement - .inventory - .source(source) - .expect("B declares it") - .index as usize - }; - assert!( - replacement.plan.can_prepare_source_seek(index("b")), - "{label}: B's b owns a fresh ring" - ); - assert!( - !replacement.plan.can_prepare_source_seek(index("a")), - "{label}: B's a is vacant, carried from P0" - ); - submit(&mut replacement.sources, "b", &signal[1], 1, 0).unwrap_or_else(|error| { - panic!("{label}: B's b takes generation 1 at frame 0: {error:?}") - }); - assert_eq!( - replacement.sources.adopt_persisting(&mut withdrawn.sources), - 1, - "{label}: a's producer moves from A" - ); - } -} diff --git a/crates/lane/tests/MUTATIONS.md b/crates/lane/tests/MUTATIONS.md index ce13f92f7..f6262921a 100644 --- a/crates/lane/tests/MUTATIONS.md +++ b/crates/lane/tests/MUTATIONS.md @@ -372,7 +372,7 @@ it; the count exists beside the digests for the same reason `minmax_lowering_mis ## Issue #954 — the select-free fused fader/matrix kernel `fader_matrix_block_without_identity` is `fader_matrix_block`'s second arm with no `L::select` in -the body. Gate 1, `select_free_fused_fader_matrix_matches_both_oracles_when_no_lane_is_identity` +the body. Gate 1, `select_free_fused_fader_matrix_matches_both_oracles_when_no_lane_is_identity` (deleted by TA5) (`tests/fader_matrix.rs`), holds it equal to two oracles with no identity lane: oracle A is `fader_matrix_block` with an all-false mask, oracle B is `gain_mute_block` on each plane followed by `matrix2x2_block_without_identity`. It covers five input families (the four hostile ones and an @@ -508,3 +508,9 @@ browser's render-closure trap gate. Host AMD EPYC 7313P, rustc 1.97.1, `x86-64-v The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - `g1_select_max_is_order_free_only_on_the_sanitized_domain`: no plausible same-crate KEEP catcher recorded + +## TA5 deletions + +The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. + +- `select_free_fused_fader_matrix_matches_both_oracles_when_no_lane_is_identity`: no same-crate KEEP catcher recorded diff --git a/crates/lane/tests/f64_lane.rs b/crates/lane/tests/f64_lane.rs index 1057c6ca6..03e4524bd 100644 --- a/crates/lane/tests/f64_lane.rs +++ b/crates/lane/tests/f64_lane.rs @@ -404,93 +404,3 @@ fn gate2_add_and_mul_are_exact_on_seeded_random_pairs() { ); }); } - -/// Gate 3's oracle: `x * x` for a finite `x = ±M * 2^E`, as `(M * M) * 2^(2E)`. -/// -/// `M * M` is below `2^48`, so its conversion is exact, and `2E` lies in `[-298, 208]`, so the power -/// of two is a normal `f64` and the product is exact. -fn oracle_square_bits(x: f32) -> u64 { - let bits = x.to_bits(); - let exponent = ((bits >> 23) & 0xFF) as i32; - let mantissa = u64::from(bits & 0x7F_FFFF); - let (significand, scale) = if exponent == 0 { - (mantissa, -149) - } else { - (mantissa | 0x80_0000, exponent - 150) - }; - let power = f64::from_bits(((2 * scale + 1023) as u64) << 52); - ((significand * significand) as f64 * power).to_bits() -} - -/// Gate 3's directed inputs: `±MAX`, `±MIN_POSITIVE`, both subnormal extremes, `1 ± ulp`, both -/// zeros, and `±2^k` for `k` in `-34..=5`. -fn square_pool() -> Vec { - let mut pool: Vec = [ - 0x7F7F_FFFF_u32, - 0xFF7F_FFFF, - 0x0080_0000, - 0x8080_0000, - 0x0000_0001, - 0x8000_0001, - 0x007F_FFFF, - 0x807F_FFFF, - 0x3F80_0001, - 0x3F7F_FFFF, - 0x0000_0000, - 0x8000_0000, - ] - .into_iter() - .map(f32::from_bits) - .collect(); - for k in -34_i32..=5 { - let power = f32::from_bits(((127 + k) as u32) << 23); - pool.push(power); - pool.push(-power); - } - pool -} - -/// `widen(x).mul(widen(x))` at `L::WIDTH` against the integer oracle, lane by lane. -fn square_mismatches(inputs: &[f32]) -> u64 { - let width = L::WIDTH; - let mut src = [0.0f32; MAX_WIDTH]; - let mut mismatches = 0; - let mut index = 0; - while index < inputs.len() { - for (lane, value) in src.iter_mut().enumerate().take(width) { - *value = inputs[(index + lane) % inputs.len()]; - } - let widened = L::load(&src[..width]).widen(); - let square = black_box(widened.mul(widened)); - let mut out = [0.0f64; MAX_WIDTH]; - square.store(&mut out[..width]); - let out = black_box(out); - for lane in 0..width { - mismatches += u64::from(out[lane].to_bits() != oracle_square_bits(src[lane])); - } - index += width; - } - mismatches -} - -#[test] -fn gate3_the_square_of_a_widened_f32_is_exact() { - let mut inputs = square_pool(); - let mut rng = Rng(0x0949_5A0A_2E00_0003); - let mut finite = 0; - while finite < 100_000 { - let x = f32::from_bits(rng.next_u64() as u32); - if x.is_finite() { - inputs.push(x); - finite += 1; - } - } - lane::each_lane!(|L| { - let name = core::any::type_name::(); - assert_eq!( - square_mismatches::(&inputs), - 0, - "gate 3: square witness at {name}" - ); - }); -} diff --git a/crates/lane/tests/fader_matrix.rs b/crates/lane/tests/fader_matrix.rs index d5aaf1f02..474446c15 100644 --- a/crates/lane/tests/fader_matrix.rs +++ b/crates/lane/tests/fader_matrix.rs @@ -3,8 +3,8 @@ use lane::{ CanonicalFpEnv, Lane, kernels::builtins::{ - Matrix2x2Coef, fader_matrix_block, fader_matrix_block_without_identity, gain_mute_block, - mask_from_flags, matrix2x2_block, matrix2x2_block_without_identity, no_lanes, + Matrix2x2Coef, fader_matrix_block, gain_mute_block, mask_from_flags, matrix2x2_block, + matrix2x2_block_without_identity, no_lanes, }, }; @@ -216,213 +216,3 @@ fn select_free_matrix_matches_the_select_form_when_no_lane_is_identity() { let _canonical = CanonicalFpEnv::enter(); lane::each_lane!(|L| select_free_width::()); } - -/// The fused gate's input families: the four hostile families, then overflow. -/// -/// Family 4 is finite, but its words times a gain above one overflow to an infinity, so the -/// matrix then meets `inf * 0.0` and `inf - inf` and produces NaNs of its own. -fn fused_family_source(family: usize) -> [f32; WIDTH] { - if family < 4 { - family_source(family) - } else { - [ - f32::MAX, - -f32::MAX, - 3.0e38, - -2.5e38, - 1.0, - -0.0, - f32::MAX * 0.5, - 1.0e38, - ] - } -} - -/// The fused gate's fader gains, `[set][channel][lane]`: mixed, unity and zero, and large (up to -/// the `+24 dB` ceiling `15.85`, which overflows family 4). -const FUSED_GAINS: [[[f32; WIDTH]; 2]; 3] = [ - [ - [1.25, 0.75, 1.0, 0.5, 1.1, 0.9, 0.8, 1.2], - [0.625, 1.5, 0.25, 1.0, 0.7, 1.3, 0.5, 0.875], - ], - [ - [1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0], - [0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0], - ], - [ - [3.98, 15.85, 1.0, 0.0, 3.98, 0.5, 15.85, 2.0], - [15.85, 3.98, 0.0, 1.0, 2.0, 15.85, 0.25, 3.98], - ], -]; - -/// The fused gate's mute flags, `[set][channel][lane]`: none, mixed, and all. -const FUSED_MUTES: [[[f32; WIDTH]; 2]; 3] = [ - [[0.0; WIDTH], [0.0; WIDTH]], - [ - [0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0], - [1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0], - ], - [[1.0; WIDTH], [1.0; WIDTH]], -]; - -/// Words the select-free fused kernel changed against one oracle over one case. -#[derive(Default)] -struct FusedDifferences { - /// The first word that fails [`same_word`], if any. - first: Option, - /// Words that are NaN in both runs with different payloads (allowed by the class statement), - /// against oracle A and oracle B. - nan_payloads: [usize; 2], -} - -impl FusedDifferences { - fn record(&mut self, oracle: usize, planes: [(&str, &[f32], &[f32]); 2]) { - for (plane, new, old) in planes { - for (index, (new, old)) in new.iter().zip(old.iter()).enumerate() { - if !same_word(*new, *old) { - self.first.get_or_insert_with(|| { - format!( - "oracle {} {plane}[{index}] new={:08x} old={:08x}", - ["A", "B"][oracle], - new.to_bits(), - old.to_bits() - ) - }); - } else if new.to_bits() != old.to_bits() { - self.nan_payloads[oracle] += 1; - } - } - } - } -} - -/// Runs the select-free fused kernel over one case against both oracles. -/// -/// Oracle A is [`fader_matrix_block`] over `identity`; oracle B is [`gain_mute_block`] on each -/// plane followed by [`matrix2x2_block_without_identity`]. The select-free kernel sees the same -/// coefficients with no identity lane, which is its precondition. Guard words at both ends are -/// compared too. -fn fused_select_free_differences( - frames: usize, - identity: [f32; WIDTH], - family: usize, - gains: usize, - mutes: usize, -) -> FusedDifferences { - let samples = frames * L::WIDTH; - let source = fused_family_source(family); - let mut left = vec![f32::from_bits(0x42f6_e979); samples + 2]; - let mut right = vec![f32::from_bits(0xc2f6_e979); samples + 2]; - for index in 0..samples { - left[index + 1] = source[index % WIDTH]; - right[index + 1] = source[(index * 3 + 1) % WIDTH]; - } - let gain_left = L::load(&FUSED_GAINS[gains][0][..L::WIDTH]); - let gain_right = L::load(&FUSED_GAINS[gains][1][..L::WIDTH]); - let mute_left = mask_from_flags::(&FUSED_MUTES[mutes][0][..L::WIDTH]); - let mute_right = mask_from_flags::(&FUSED_MUTES[mutes][1][..L::WIDTH]); - let select_form = hostile_matrix::(identity); - let select_free = Matrix2x2Coef { - identity: no_lanes::(), - ..select_form - }; - let (mut a_left, mut a_right) = (left.clone(), right.clone()); - fader_matrix_block( - &mut a_left[1..=samples], - &mut a_right[1..=samples], - frames, - gain_left, - mute_left, - gain_right, - mute_right, - &select_form, - ); - let (mut b_left, mut b_right) = (left.clone(), right.clone()); - gain_mute_block(&mut b_left[1..=samples], frames, gain_left, mute_left); - gain_mute_block(&mut b_right[1..=samples], frames, gain_right, mute_right); - matrix2x2_block_without_identity( - &mut b_left[1..=samples], - &mut b_right[1..=samples], - frames, - &select_free, - ); - fader_matrix_block_without_identity( - &mut left[1..=samples], - &mut right[1..=samples], - frames, - gain_left, - mute_left, - gain_right, - mute_right, - &select_free, - ); - let mut differences = FusedDifferences::default(); - differences.record(0, [("L", &left, &a_left), ("R", &right, &a_right)]); - differences.record(1, [("L", &left, &b_left), ("R", &right, &b_right)]); - differences -} - -const FUSED_FRAMES: [usize; 5] = [1, 3, 8, 9, 128]; - -fn fused_select_free_width() -> [usize; 2] { - let mut nan_payloads = [0; 2]; - for frames in FUSED_FRAMES { - for family in 0..5 { - for gains in 0..FUSED_GAINS.len() { - for mutes in 0..FUSED_MUTES.len() { - let differences = fused_select_free_differences::( - frames, - [0.0; WIDTH], - family, - gains, - mutes, - ); - if let Some(word) = differences.first { - panic!( - "width={} frames={frames} family={family} gains={gains} \ - mutes={mutes}: {word}", - L::WIDTH - ); - } - nan_payloads[0] += differences.nan_payloads[0]; - nan_payloads[1] += differences.nan_payloads[1]; - } - } - } - } - // Negative control: with identity lanes the select form is a different function, which is why - // the arm is keyed on the mask. Unmuted identity lanes pass their scaled inputs through, while - // the select-free arm mixes them. Asserted over the union of frame counts and families. - let mixed = [1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0]; - assert!( - FUSED_FRAMES.iter().any(|&frames| (0..5).any(|family| { - fused_select_free_differences::(frames, mixed, family, 0, 0) - .first - .is_some() - })), - "width={}: the select-free fused kernel must differ from the select form on identity lanes", - L::WIDTH - ); - nan_payloads -} - -/// Issue #954 gate 1: with no identity lane, the select-free fused kernel is `fader_matrix_block`, -/// and it is `gain_mute_block` on each plane followed by `matrix2x2_block_without_identity`. -/// -/// Five input families (the four hostile ones and overflow), three gain sets (mixed, unity and -/// zero, large), three mute sets (none, mixed, all), frame counts `[1, 3, 8, 9, 128]`, guard words -/// included, at `f32`, `Simd4` and `Simd8`. NaN words compare as "both NaN" (see [`same_word`]); -/// the count of NaN-payload differences, which the class statement allows, is printed. Runs in -/// dev and in release, which is the build CI tests `lane` in. -/// -/// Red mutation (issue #954 M3, `crates/lane/tests/MUTATIONS.md`): swap `matrix.lr` and -/// `matrix.rl` in `fader_matrix_block_without_identity`. -#[test] -fn select_free_fused_fader_matrix_matches_both_oracles_when_no_lane_is_identity() { - let _canonical = CanonicalFpEnv::enter(); - let mut counts = Vec::new(); - lane::each_lane!(|L| counts.push(fused_select_free_width::())); - println!( - "NaN-payload differences [oracle A, oracle B] at each width, narrowest first: {counts:?}" - ); -} diff --git a/crates/lane/tests/fp_env.rs b/crates/lane/tests/fp_env.rs index a3cd976e4..ad1bc8143 100644 --- a/crates/lane/tests/fp_env.rs +++ b/crates/lane/tests/fp_env.rs @@ -77,38 +77,6 @@ mod x86 { ); } - /// #1709 (U3 fold, batch 17): the test helper `under_hostile_fp_env` installs the hostile - /// word inside its body and writes the caller's exact word back after it, also when the body - /// panics. The caller's word is itself non-canonical (round up, a sticky flag), so a helper - /// that wrote the canonical word back would be seen. - /// - /// Test value: red if the helper leaves the hostile word on the caller's thread (on return or - /// while unwinding), restores another word than the caller's, or does not install the hostile - /// word, so every #1709 gate built on it would run in the wrong environment. - #[cfg(feature = "test-support")] - #[test] - fn under_hostile_fp_env_restores_the_callers_word() { - let saved = read_mxcsr(); - let _restore = Restore(saved); - let caller = hostile_word(saved); - // SAFETY: `hostile_word` sets FTZ, DAZ, RC and a status flag on a word read from this - // thread, all below bit 16; `_restore` writes `saved` back. - unsafe { write_mxcsr(caller) }; - let inside = fpenv::under_hostile_fp_env(read_mxcsr); - assert_eq!(inside, fpenv::hostile_fp_control_word(caller)); - assert_ne!(inside, caller, "the helper changes the word"); - assert_eq!(read_mxcsr(), caller, "the caller's word comes back"); - let outcome = std::panic::catch_unwind(|| { - fpenv::under_hostile_fp_env(|| panic!("a body that panics")); - }); - assert!(outcome.is_err(), "the body panicked"); - assert_eq!( - read_mxcsr(), - caller, - "the caller's word comes back while unwinding" - ); - } - #[test] fn the_caller_word_survives_an_unwind_through_the_guard() { let saved = read_mxcsr(); diff --git a/crates/lane/tests/input_chain_arming.rs b/crates/lane/tests/input_chain_arming.rs index e242409b9..e340d3716 100644 --- a/crates/lane/tests/input_chain_arming.rs +++ b/crates/lane/tests/input_chain_arming.rs @@ -240,52 +240,6 @@ fn the_right_channel_arms_its_joint_flush_alone() { lane::each_lane!(|L| right_channel_alone_arms::(core::any::type_name::())); } -/// Every state word: one word of one section of the silent channel in the band, every other word -/// `+0.0`. The block must arm and clear it, on both channels and in the collapsed bodies. -fn every_state_word_arms(width: &str) { - for entry in ENTRIES { - for mono in [false, true] { - for channel in 0..if mono { 1 } else { 2 } { - let sections = if entry == Entry::Mixed { 1..2 } else { 0..2 }; - for section in sections { - for word in 0..2 { - let mut c = chain::(entry, WINDOW); - let mut s = InputChainState::::default(); - let target = &mut s.section[channel][section]; - if word == 0 { - target.ic1 = L::splat(BAND[0]); - } else { - target.ic2 = L::splat(BAND[1]); - } - s.silence[channel] = L::splat(WINDOW - 1.0); - let mut planes = [live(), live()]; - planes[channel] = vec![0.0; FRAMES * 8]; - let [left, right] = &mut planes; - run( - entry, - mono, - &mut left[..FRAMES * L::WIDTH], - &mut right[..FRAMES * L::WIDTH], - &mut c, - &mut s, - ); - assert!( - state_bits(&s, channel).iter().all(|bits| *bits == 0), - "{width}, {entry:?}, mono {mono}: channel {channel}, section \ - {section}, word {word} in the band arms the block" - ); - } - } - } - } - } -} - -#[test] -fn any_state_word_in_the_band_arms_the_block() { - lane::each_lane!(|L| every_state_word_arms::(core::any::type_name::())); -} - /// A block longer than the window: the channel starts at rest with a fresh counter, takes one tiny /// sample on frame 0 and zeros after it. Its counter reaches the window on frame `window`, inside /// the block, with the tail's state in the band: the block must arm though no lane held state at diff --git a/crates/math/tests/f1_fast_db_bounds.rs b/crates/math/tests/f1_fast_db_bounds.rs index 0174b3666..f18963971 100644 --- a/crates/math/tests/f1_fast_db_bounds.rs +++ b/crates/math/tests/f1_fast_db_bounds.rs @@ -394,24 +394,6 @@ fn f1_domain_and_clamping() { // Subsampled bounds — these run in the default `cargo test`. // --------------------------------------------------------------------------------------------- -#[test] -fn f1_gain_from_db_subsample() { - let (lo, hi) = gain_domain(); - assert_sweep( - "fast_gain_from_db negative subsample", - sweep(lo, hi, 4099, gain_error_db), - GAIN_MAX_DB, - 200_000, - ); - let (lo, hi) = gain_domain_positive(); - assert_sweep( - "fast_gain_from_db positive subsample", - sweep(lo, hi, 4099, gain_error_db), - GAIN_MAX_DB, - 200_000, - ); -} - #[test] fn f1_level_db_subsample() { let (lo, hi) = level_domain(); diff --git a/crates/math/tests/m2_lane_identity.rs b/crates/math/tests/m2_lane_identity.rs index eaa71df56..b951cf229 100644 --- a/crates/math/tests/m2_lane_identity.rs +++ b/crates/math/tests/m2_lane_identity.rs @@ -173,14 +173,6 @@ impl LaneFn for Log2 { } } -/// `math::fast_db::fast_gain_from_db`. -struct FastGain; -impl LaneFn for FastGain { - fn at(x: L) -> L { - math::fast_db::fast_gain_from_db(x) - } -} - /// Compare every vector width against the scalar oracle, bit for bit: width 4, then width 8 where /// `avx2` is enabled (a 4-lane build has no eight-lane type, issue #1112). fn compare_widths(name: &str, input: &[f32]) -> Vec { @@ -219,42 +211,6 @@ fn check(name: &str, input: &[f32], pinned: &[u8; 32]) { ); } -/// Inputs whose clamp treatment is the load-bearing premise of LANE-4. -fn exp2_caller_edges() -> Vec { - let mut values = vec![ - f32::from_bits(0x7fc0_0000), - f32::from_bits(0x7fc0_0001), - f32::from_bits(0xffc0_0000), - f32::INFINITY, - f32::NEG_INFINITY, - 0.0, - -0.0, - f32::from_bits(0x0000_0001), - f32::from_bits(0x8000_0001), - f32::from_bits(0x007f_ffff), - f32::from_bits(0x807f_ffff), - f32::MIN_POSITIVE, - -f32::MIN_POSITIVE, - ]; - for boundary in [-127.0_f32, -126.0, 126.0, 127.0] { - let bits = boundary.to_bits(); - values.push(f32::from_bits(bits - 1)); - values.push(boundary); - values.push(f32::from_bits(bits + 1)); - } - while !values.len().is_multiple_of(8) { - values.push(0.0); - } - values -} - -#[test] -fn m2_exp2_callers_directed_edges_are_lane_identical() { - let input = exp2_caller_edges(); - compare_widths::("fast_gain_from_db directed edges", &input); - compare_widths::("exp2_lane directed edges", &input); -} - #[test] fn m2_exp2_lane_identity() { let input = corpus(); diff --git a/crates/multiband-compressor/src/shim.rs b/crates/multiband-compressor/src/shim.rs index e6d9a0c8e..d10813070 100644 --- a/crates/multiband-compressor/src/shim.rs +++ b/crates/multiband-compressor/src/shim.rs @@ -123,23 +123,6 @@ mod tests { ); } - /// Both ends are exact: a zero coefficient arrives, a unit coefficient freezes. - #[test] - fn the_smoother_has_exact_ends() { - for target in [-42.5f32, 0.0, -0.0, -100.0] { - assert_eq!( - branching_smooth::(-7.5, target, 0.0, 0.0).to_bits(), - target.to_bits(), - "a zero coefficient must arrive exactly" - ); - assert_eq!( - branching_smooth::(-7.5, target, 1.0, 1.0), - -7.5, - "a unit coefficient must freeze" - ); - } - } - /// The three link modes, including the `±0.0`, overflow and NaN corners. /// /// Red mutations: maximum becomes minimum; the `abs` is dropped; the average halves the sum. diff --git a/crates/multiband-compressor/tests/MUTATIONS.md b/crates/multiband-compressor/tests/MUTATIONS.md index d85046dfa..592cfae29 100644 --- a/crates/multiband-compressor/tests/MUTATIONS.md +++ b/crates/multiband-compressor/tests/MUTATIONS.md @@ -219,3 +219,9 @@ mutated file was restored byte for byte from a saved copy. | 994-M1 | `knee_coefficients` without its `is_finite` test (the pre-#994 design) | `effect-runtime/src/dynamics.rs` | `knee_tests::a_band_level_at_the_threshold_never_takes_a_nan_target` | RED: `W 0x00000002 T 0e0 R 1 x 0e0: -100 is a duck at the threshold` — at ratio 1, which compresses nothing, the NaN target still became a -100 dB target | | 994-M2 | `BAND_KNEE = knee_coefficients(2.0 * KNEE_DB)` | `src/lib.rs` | `knee_tests::the_fixed_knee_words_are_unchanged` | RED | | 994-M3 | the rule as a constant bound one ulp too low: `knee_db >= f32::from_bits(0x0010_0000)` | `effect-runtime/src/dynamics.rs` | `knee_tests::a_band_level_at_the_threshold_never_takes_a_nan_target` | RED | + +## TA5 deletions + +The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. + +- `the_two_stage_split_matches_the_reference_and_recombines_flat`: no same-crate KEEP catcher recorded diff --git a/crates/multiband-compressor/tests/crossover.rs b/crates/multiband-compressor/tests/crossover.rs index 475ef23de..227635b93 100644 --- a/crates/multiband-compressor/tests/crossover.rs +++ b/crates/multiband-compressor/tests/crossover.rs @@ -6,120 +6,10 @@ //! `tests/lr4_two_section_mapping_f64.rs`. This gate is the `f32` production form: the same //! `lr4_step` the render path runs, at `WIDTH = 1`. -use dsp_reference::ReferenceLr4Crossover; use lane::Lane; use multiband_compressor::{Lr4State, lr4_coefficients, lr4_step}; const RATES: [u32; 4] = [44_100, 48_000, 88_200, 96_000]; -const CROSSOVERS: [f32; 3] = [80.0, 1_000.0, 8_000.0]; - -/// Complex gain of `output` relative to `input`, by demodulation over a whole number of periods. -fn complex_gain(input: &[f32], output: &[f32], sample_rate: f64, frequency: f64) -> (f64, f64) { - let step = core::f64::consts::TAU * frequency / sample_rate; - let (mut xr, mut xi, mut yr, mut yi) = (0.0, 0.0, 0.0, 0.0); - for (index, (x, y)) in input.iter().zip(output.iter()).enumerate() { - let (sin, cos) = (step * index as f64).sin_cos(); - xr += f64::from(*x) * cos; - xi -= f64::from(*x) * sin; - yr += f64::from(*y) * cos; - yi -= f64::from(*y) * sin; - } - let norm = xr * xr + xi * xi; - ((yr * xr + yi * xi) / norm, (yi * xr - yr * xi) / norm) -} - -fn rms(values: &[f32]) -> f64 { - (values - .iter() - .map(|value| f64::from(*value) * f64::from(*value)) - .sum::() - / values.len() as f64) - .sqrt() -} - -/// E2. Per-sample agreement with the four-section `f64` oracle, the half-power crossing, and a -/// flat band sum on the frozen one-sixth-octave grid. -/// -/// The per-sample bound is version 1's `2e-5`, kept and not loosened. The flatness bound is new -/// and an order of magnitude tighter than version 1's 0.05 dB RMS check, because `high = ap - low` -/// makes the sum the all-pass by construction rather than by cancellation. -/// -/// Red mutation: swap `a2` and `a3` in the design; drop the `nk2` doubling. -#[test] -fn the_two_stage_split_matches_the_reference_and_recombines_flat() { - let mut worst_band = 0.0f64; - let mut worst_flatness = 0.0f64; - for rate in RATES { - for crossover in CROSSOVERS { - let coefficients = lr4_coefficients::(rate, crossover).expect("design"); - let mut state = Lr4State::::default(); - // The split's input arms its joint flush (issue #1328, amendment A9). - let mut run = 0.0f32; - let mut reference = - ReferenceLr4Crossover::new(f64::from(rate), f64::from(crossover)).expect("oracle"); - let (mut input, mut low_band, mut sum) = (Vec::new(), Vec::new(), Vec::new()); - for index in 0..8_192 { - let sample = - (core::f32::consts::TAU * crossover * index as f32 / rate as f32).sin(); - let rest = lane::silence_step(sample, &mut run, lane::silence_frames(rate) as f32); - let (low, high) = lr4_step(sample, rest, &coefficients, &mut state); - let (expected_low, expected_high) = reference.process_sample(f64::from(sample)); - worst_band = worst_band - .max(f64::from((low - expected_low as f32).abs())) - .max(f64::from((high - expected_high as f32).abs())); - assert!( - (low - expected_low as f32).abs() < 2.0e-5 - && (high - expected_high as f32).abs() < 2.0e-5, - "rate={rate} crossover={crossover} index={index}" - ); - if index >= 4_096 { - input.push(sample); - low_band.push(low); - sum.push(low + high); - } - } - let crossing = 20.0 * (rms(&low_band) / rms(&input)).log10(); - assert!( - (crossing + 6.020_599_913).abs() <= 0.02, - "rate={rate} crossover={crossover} crossing={crossing}" - ); - for index in 0..=60 { - let probe = (20.0 * 2.0f64.powf(f64::from(index) / 6.0)).round(); - if probe > 0.45 * f64::from(rate) { - break; - } - let frames = rate as usize; - let mut state = Lr4State::::default(); - let mut run = 0.0f32; - let step = core::f64::consts::TAU * probe / f64::from(rate); - for frame in 0..frames { - let sample = (step * frame as f64).sin() as f32; - let rest = - lane::silence_step(sample, &mut run, lane::silence_frames(rate) as f32); - let _ = lr4_step(sample, rest, &coefficients, &mut state); - } - let mut input = Vec::with_capacity(frames); - let mut summed = Vec::with_capacity(frames); - for frame in frames..2 * frames { - let sample = (step * frame as f64).sin() as f32; - let rest = - lane::silence_step(sample, &mut run, lane::silence_frames(rate) as f32); - let (low, high) = lr4_step(sample, rest, &coefficients, &mut state); - input.push(sample); - summed.push(low + high); - } - let (real, imaginary) = complex_gain(&input, &summed, f64::from(rate), probe); - let flatness = 20.0 * real.hypot(imaginary).log10(); - worst_flatness = worst_flatness.max(flatness.abs()); - assert!( - flatness.abs() <= 0.01, - "rate={rate} crossover={crossover} probe={probe} flatness={flatness:e} dB" - ); - } - } - } - eprintln!("E2 worst_band_abs={worst_band:e} worst_flatness_db={worst_flatness:e}"); -} /// The design guards its domain and its own half-power self-check. #[test] diff --git a/crates/multiband-compressor/tests/product.rs b/crates/multiband-compressor/tests/product.rs index 3348affe3..ce2985bc0 100644 --- a/crates/multiband-compressor/tests/product.rs +++ b/crates/multiband-compressor/tests/product.rs @@ -1338,59 +1338,6 @@ fn the_silence_counter_is_carried_and_validated() { } } -/// Issue #1328 amendment A9: each channel's silence counter counts that channel's own input. -/// -/// One channel silent for ten blocks and the other live: the silent channel's counter reads the -/// 1,280 frames and the live one's reads `+0.0`, in both orders. A counter stepped from the other -/// channel's input would arm the live channel's joint flush on the silent one's run. -#[test] -fn each_channel_counts_its_own_input() { - const COUNTER_WORD: usize = 47; - let counter = |section: &[u8]| { - f32::from_le_bytes( - section[COUNTER_WORD * 4..COUNTER_WORD * 4 + 4] - .try_into() - .expect("word"), - ) - }; - for silent_left in [true, false] { - let prepared = MultibandCompressorFactory - .prepare(request(&values())) - .expect("prepare"); - let sizes = prepared.metadata.state_sizes; - let mut effect = prepared.processor; - for block in 0..10_u64 { - let live = support::signal(128, 0x1328_0100 + block); - let silent = vec![0.0_f32; 128]; - let (mut left, mut right) = if silent_left { - (silent, live) - } else { - (live, silent) - }; - process( - effect.as_mut(), - &mut left, - &mut right, - block * 128, - &[], - 128, - ); - } - let saved = snapshot(effect.as_ref(), sizes); - let (left, right) = (counter(&saved.1), counter(&saved.2)); - let (expected_left, expected_right) = if silent_left { - (1_280.0, 0.0) - } else { - (0.0, 1_280.0) - }; - assert_eq!( - (left.to_bits(), right.to_bits()), - (f32::to_bits(expected_left), f32::to_bits(expected_right)), - "silent left {silent_left}: the counters are ({left}, {right})" - ); - } -} - /// Issue #1328 amendment A9: the crossover's joint flush arms on the frame its channel's input has /// been silent for `N_SILENCE` frames (4,096 at 48 kHz), and not before. /// diff --git a/crates/parametric-eq/src/lib.rs b/crates/parametric-eq/src/lib.rs index 67b46dea6..1a6622e17 100644 --- a/crates/parametric-eq/src/lib.rs +++ b/crates/parametric-eq/src/lib.rs @@ -8501,164 +8501,6 @@ mod padded_banks { } } - /// Gate 3 (P2a verdict, L4): a padded lane fed `+0.0` writes exactly `+0.0` -- never `-0.0`, - /// never a subnormal -- and keeps every integrator exactly `+0.0`, finite and at rest, block - /// after block, whatever design it clones and whatever schedule its bank takes. - /// - /// Every band family, at both gain extremes and both Q extremes, on all four bands, with both - /// cuts on and the right channel detuned, cloned into every padded lane of a one-member bank at - /// both widths. The member drives the bank through each schedule it has: stationary and elided - /// (quiet music), refused (a `-0.0` word, then a non-finite one), ramping (a prepared target on - /// the member mid-run, so every lane runs the ramped kernel and the padded ones with a `+0.0` - /// increment) and faulting (a `9.5e29` sine, D7). `Channel::recover_failed_lanes` states why - /// the frame body writes `+0.0` from `+0.0` for every designed word; this is that claim, - /// measured. - /// - /// Red if a padded lane is not at rest when bound, or if any path writes it a signed zero or a - /// word other than `+0.0` (a padded lane's output feeds the next slot of a chain and its own - /// next block, so a `-0.0` there would travel). - #[test] - fn a_padded_lane_fed_positive_zero_writes_positive_zero_at_rest() { - let rate = 48_000; - let parameters = PARAMETRIC_EQ_DESCRIPTOR.parameters.len(); - let mut blocks = 0_u64; - for lanes in lane_counts() { - for kind in 1..=6_u32 { - for (gain, q) in [(-24.0, 0.1), (24.0, 18.0), (-24.0, 18.0), (24.0, 0.1)] { - let mut values = Vec::with_capacity(parameters * 2); - for index in 0..parameters { - for (side, channel) in [ParameterChannel::Left, ParameterChannel::Right] - .into_iter() - .enumerate() - { - let detune = 1.0 + 0.1 * side as f32; - let value = match (index < 24, index % 6, index) { - (true, 0, _) => 1.0, - (true, 1, _) => kind as f32, - (true, 2, _) => 200.0 * (1 + index / 6) as f32 * detune, - (true, 3, _) => gain, - (true, 4, _) => q, - (true, _, _) => 0.5, - (false, _, 24 | 27) => 1.0, - (false, _, 25) => 20.0 * detune, - (false, _, 28) => 18_000.0 / detune, - (false, _, _) => q, - }; - values.push(InitialParameterValue { - parameter_index: index as u32, - channel, - value, - }); - } - } - let initial = [values]; - let member_of = vec![0; lanes]; - let mask: Vec = (0..lanes).map(|lane| lane == 0).collect(); - let mut bank = bind(lanes, &requests(&initial, &member_of, rate), &mask); - let at_bind: Vec = (1..lanes) - .map(|lane| lane_payload(bank.as_ref(), lane)) - .collect(); - let context = format!("{lanes} lanes, kind {kind}, gain {gain}, Q {q}"); - let mut planes = [vec![0.0; QUANTUM * lanes], vec![0.0; QUANTUM * lanes]]; - let offsets = vec![0_u32; lanes + 1]; - for block in 0..12_u64 { - let first = block * QUANTUM as u64; - if block == 4 { - // The member's first band retuned: a 64-sample ramp on lane 0 only. - let mut candidate = initial[0].clone(); - let mut changed = vec![false; parameters * 2]; - for side in 0..2 { - candidate[2 * 2 + side].value = 3_000.0; - changed[2 * 2 + side] = true; - } - let mut targets = [PreparedEffectTarget { - slot: 0, - channel: ParameterChannel::Left, - words: [0; effect_contract::PREPARED_EFFECT_TARGET_WORDS], - }; EQ_SECTION_COUNT * 2]; - let count = ParametricEqFactory - .prepare_targets( - EffectTargetRequest { - sample_rate: rate, - values: &candidate, - changed: &changed, - }, - &mut targets, - ) - .expect("a legal target"); - for target in &targets[..count] { - bank.apply_prepared_target_lane(0, target) - .expect("a target on the member"); - } - } - let mut input = [ - sine(1.0e-3, first, QUANTUM, rate, 0.0), - sine(1.0e-3, first, QUANTUM, rate, 1.9), - ]; - match block { - 2 => input[0][17] = -0.0, - 3 => input[1][40] = f32::NAN, - 6 | 7 => { - input = [ - sine(9.5e29, first, QUANTUM, rate, 0.0), - sine(9.5e29, first, QUANTUM, rate, 1.9), - ]; - } - _ => {} - } - gather(&mut planes, lanes, &[0], &[input]); - render( - bank.as_mut(), - &mut planes, - lanes, - QUANTUM, - first, - false, - &[], - &offsets, - ); - for lane in 1..lanes { - for (channel, plane) in planes.iter().enumerate() { - if let Some(word) = column(plane, lanes, lane, QUANTUM) - .into_iter() - .find(|word| word.to_bits() != 0) - { - panic!( - "{context}, block {block}: padded lane {lane}, channel \ - {channel} wrote {:#010x}", - word.to_bits() - ); - } - } - let payload = lane_payload(bank.as_ref(), lane); - for (side, words) in payload.iter().enumerate().skip(1) { - for section in 0..EQ_SECTION_COUNT { - for integrator in 0..2 { - let word = - (section * STATE_WORDS_PER_BAND + integrator) * 4; - assert_eq!( - &words[word..word + 4], - &[0; 4], - "{context}, block {block}: padded lane {lane}, \ - channel {side}, section {section}: an integrator \ - left +0.0" - ); - } - } - } - assert!( - without_silence(&payload) == without_silence(&at_bind[lane - 1]), - "{context}, block {block}: padded lane {lane} moved a word" - ); - } - blocks += 1; - } - } - } - } - assert!(blocks > 0); - } - /// Gate 4, the planted state: a non-finite integrator planted in one member recovers that /// member alone and reports it alone, at every member count and on either channel. /// diff --git a/crates/parametric-eq/tests/MUTATIONS.md b/crates/parametric-eq/tests/MUTATIONS.md index f144fdd3d..cb299f2da 100644 --- a/crates/parametric-eq/tests/MUTATIONS.md +++ b/crates/parametric-eq/tests/MUTATIONS.md @@ -446,7 +446,7 @@ run, and the edit reverted (#1328 attempt 4; the attempt records are in the issu | # | mutation | gate that goes red | result | |---|---|---|---| -| 1328-M1 | `lane::flush_pair` without its input term (attempt 3's law: `rest = abs(n1) < REST_EPS && abs(n2) < REST_EPS`) | `exact_rest::a_tiny_input_through_four_boosting_shelves_gets_every_boost` (`10 Hz, input 2.7939677e-11 … misses the ordinary run by 1.5091782e3`), dev and release; `the_low_shelf_fixed_point_reaches_exact_rest` stays green | RED | +| 1328-M1 | `lane::flush_pair` without its input term (attempt 3's law: `rest = abs(n1) < REST_EPS && abs(n2) < REST_EPS`) | `exact_rest::a_tiny_input_through_four_boosting_shelves_gets_every_boost` (`10 Hz, input 2.7939677e-11 … misses the ordinary run by 1.5091782e3`), dev and release; `the_low_shelf_fixed_point_reaches_exact_rest` (deleted by TA5) stays green | RED | | 1328-M2 | `self.right.identity = self.left.identity;` dropped from `desymmetrize` | `mono_collapse::a_desymmetrized_bank_carries_the_collapsed_channels_identity_flags_and_dry_masks`: dev through `identity_flags_agree` (`lib.rs`), release on the right plane at block 27 after the left-only retarget | RED | | 1328-M3 | `self.right.dry = self.left.dry;` dropped from `desymmetrize` | the same test, dev only (`identity_flags_agree`); release renders the same bits (a stale dry lane runs the identity section wet, which moves at most `-0.0` to `+0.0`, and this input moves none) | RED (dev) | @@ -459,7 +459,7 @@ it names, the named tests run, and the source restored. |---|---|---|---| | 1328-M4 | the rest threshold always armed (attempt 3's law: `silence_step` returns `REST_EPS`, `silence_armable` always true) | `exact_rest::a_sparse_input_through_four_boosting_shelves_gets_every_boost_at_every_rate` (`44100 Hz, 10 Hz shelves, period 2 … misses the ordinary run by 1.102885e3`), `a_tiny_input_through_four_boosting_shelves_gets_every_boost`, `an_exact_zero_inside_the_cascade_moves_nothing_while_the_input_is_live` | RED | | 1328-M5 | A8's law: armed by a zero *section* input (thresholds from `v0 == 0` in `svf_step_when`, every block armable) | the sparse gate (`… misses the ordinary run by 1.0375549e3`) and the cancellation gate (`DC at 1e-6 … misses the ordinary run by 6.996817e-5 … (peak 7.826055e3)`); the chain gate stays green | RED | -| 1328-M6 | `Channel::commit_track` drops the restored silence word | `carry::a_restore_mid_silence_arms_the_joint_flush_on_the_continuing_lanes_frame`, `carry::a_malformed_silence_word_is_refused` | RED | +| 1328-M6 | `Channel::commit_track` drops the restored silence word | `carry::a_restore_mid_silence_arms_the_joint_flush_on_the_continuing_lanes_frame` (deleted by TA5), `carry::a_malformed_silence_word_is_refused` | RED | | 1328-M7 | `desymmetrize` drops `self.right.silence = self.left.silence` | `mono_collapse::a_desymmetrized_bank_carries_the_collapsed_channels_silence_counter` | RED | | 1328-M8 | the silent fast path earned without every lane armed (`quiet && (armed \|\| true)`) | `exact_rest::a_fixed_point_inside_the_joint_band_is_cleared_once_the_flush_arms` | RED | | 1328-M9 | the silent fast path skips without `silence_advance` | the same test (`the silence counter after 68 silent blocks`) | RED | @@ -476,3 +476,10 @@ The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) delete - `disabled_cuts_preserve_all_high_pass_signed_zero_against_four_section_oracle`: parametric-eq `elision::a_discontinuity_reset_mid_ramp_refreshes_the_identity_flag` - `a_tiny_restored_disabled_cut_state_refuses_elision_but_preserves_old_bands`: parametric-eq `elision::a_discontinuity_reset_mid_ramp_refreshes_the_identity_flag` - `a_mid_ramp_restore_continues_bit_for_bit_at_every_sample`: no plausible same-crate KEEP catcher recorded + +## TA5 deletions + +The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. + +- `the_low_shelf_fixed_point_reaches_exact_rest`: parametric-eq `a_restore_refused_on_one_channel_moves_neither`, `the_collapsed_body_arms_its_joint_flush` +- `a_restore_mid_silence_arms_the_joint_flush_on_the_continuing_lanes_frame`: parametric-eq `a_restore_refused_on_one_channel_moves_neither`, `the_collapsed_body_arms_its_joint_flush` diff --git a/crates/parametric-eq/tests/carry.rs b/crates/parametric-eq/tests/carry.rs index 3eca03f71..3e87cf071 100644 --- a/crates/parametric-eq/tests/carry.rs +++ b/crates/parametric-eq/tests/carry.rs @@ -124,10 +124,6 @@ fn continuing(ramp: &Ramp, elapsed: usize) -> PreparedEffect { effect } -fn same_bits(a: &[f32], b: &[f32]) -> bool { - a.iter().zip(b).all(|(x, y)| x.to_bits() == y.to_bits()) -} - /// A restore validates both channels before it commits either: a payload whose left section is a /// mid-ramp snapshot and whose right section is refused leaves the receiver as it was, scalar and /// bank lane alike. @@ -197,103 +193,6 @@ fn bank_lane_snapshot(bank: &PreparedEffectBank, lane: usize) -> Payload { out } -/// Issue #1328, amendment A9: the payload carries the input's silence counter, so a lane restored -/// mid-silence arms its joint flush on the frame the lane it was taken from does. -/// -/// A 10 Hz +24 dB low shelf at 48 kHz takes a `1e-11` impulse; its state then sits inside the joint -/// band (both words below `REST_EPS`, one above `FLUSH_EPS`) for longer than `N_SILENCE` (4,096 -/// frames), so the frame the counter arms is the frame the band's words reach `+0.0`. The payload -/// is taken after eight blocks (counter 1,023) and restored into a fresh instance that has rendered -/// nothing; both render to -/// past the arming frame. Asserted: the band's words are in the joint band before the arming frame -/// and `+0.0` from it on, and the restored instance renders the continuing one's bits on every block. -/// A restore that dropped or reset the counter would arm 1,023 frames late. -#[test] -fn a_restore_mid_silence_arms_the_joint_flush_on_the_continuing_lanes_frame() { - const FRAMES: usize = 128; - const ARMED_AT: usize = 4_096; - let values = support::single_section_values( - parametric_eq::EqBandKind::LowShelf, - 10.0, - 24.0, - core::f32::consts::FRAC_1_SQRT_2, - 1.0, - ); - let mut continuing = ParametricEqFactory - .prepare(request(&values, false)) - .expect("prepare"); - let mut restored = ParametricEqFactory - .prepare(request(&values, false)) - .expect("prepare"); - for block in 0..ARMED_AT / FRAMES + 8 { - let mut left = [0.0_f32; FRAMES]; - if block == 0 { - left[0] = 1.0e-11; - } - let mut right = left; - let (mut restored_left, mut restored_right) = (left, right); - if block == 8 { - let payload = snapshot(&continuing); - assert_eq!( - support::word(&payload.1, support::SILENCE_WORD), - 1_023.0_f32.to_bits(), - "the snapshot carries the left counter" - ); - let sizes = restored.metadata.state_sizes; - restored - .processor - .restore_state_payload( - 1, - StatePayloadInput::new(&payload.0, &payload.1, &payload.2, sizes) - .expect("input"), - ) - .expect("a mid-silence payload restores"); - } - let first = (block * FRAMES) as u64; - continuing.processor.process( - EffectProcessBlock::new(&mut left, &mut right, None, first, &[], 128).expect("block"), - ); - // The restored instance renders nothing before the restore: everything it holds then is - // what the payload wrote. - if block >= 8 { - restored.processor.process( - EffectProcessBlock::new( - &mut restored_left, - &mut restored_right, - None, - first, - &[], - 128, - ) - .expect("block"), - ); - } - if block >= 8 { - assert!( - same_bits(&left, &restored_left) && same_bits(&right, &restored_right), - "block {block}: the restored instance must render the continuing one's bits" - ); - } - let payload = snapshot(&continuing); - let band = [0, 1].map(|word| f32::from_bits(support::band_word(&payload.1, 0, word))); - let end = (block + 1) * FRAMES; - if end == ARMED_AT { - assert!( - band.iter().all(|w| w.abs() < lane::REST_EPS) - && band.iter().any(|w| w.abs() >= lane::FLUSH_EPS), - "the band must sit in the joint band before the counter arms: {band:?}" - ); - } - if end > ARMED_AT { - assert_eq!( - band.map(f32::to_bits), - [0, 0], - "block {block}: the joint flush must fire once the counter arms" - ); - } - } -} - /// The silence word is admitted only as the counter writes it: `+0.0`, or an integer in /// `[1, 2^24]`. Each malformed word is refused and changes nothing (issue #1328, amendment A9). #[test] diff --git a/crates/parametric-eq/tests/contract.rs b/crates/parametric-eq/tests/contract.rs index 96331925a..92008e8ac 100644 --- a/crates/parametric-eq/tests/contract.rs +++ b/crates/parametric-eq/tests/contract.rs @@ -615,69 +615,6 @@ fn a_payload_with_a_stale_header_is_rejected_on_its_own_evidence() { ); } -/// A payload whose words are out of domain or inconsistent with its parameters is rejected whole. -#[test] -fn a_malformed_payload_is_rejected_without_touching_either_channel() { - let values = bell_values(); - let mut effect = ParametricEqFactory - .prepare(request(&values, false)) - .expect("prepare"); - process_zeros(&mut effect, 0, 8, &[]); - let before = snapshot(&effect); - for (word, bits) in [ - (0_usize, f32::NAN.to_bits()), - (2, f32::NAN.to_bits()), - (8, f32::INFINITY.to_bits()), - (14, 65_u32), - (15, 5.0_f32.to_bits()), - (17, 0.0_f32.to_bits()), - ] { - let (common, mut left, right) = before; - left[word * 4..word * 4 + 4].copy_from_slice(&bits.to_le_bytes()); - assert_eq!( - effect.processor.restore_state_payload( - 1, - StatePayloadInput::new(&common, &left, &right, effect.metadata.state_sizes) - .expect("input"), - ), - Err(StatePayloadError { - code: "effect.state.payload" - }), - "word {word}" - ); - assert_eq!(snapshot(&effect), before, "word {word} left a trace"); - } - // A settled band whose stored words disagree with its stored parameters is a forgery. - let (common, mut left, right) = before; - let poisoned = f32::from_bits(band_word(&left, 0, 2)) + 1.0e-3; - left[8..12].copy_from_slice(&poisoned.to_bits().to_le_bytes()); - assert_eq!( - effect.processor.restore_state_payload( - 1, - StatePayloadInput::new(&common, &left, &right, effect.metadata.state_sizes) - .expect("input"), - ), - Err(StatePayloadError { - code: "effect.state.payload" - }) - ); - - // A settled lane must carry exact +0.0 increments. A bank section can still run the ramp - // kernel because a peer lane is moving, so a finite forged step would otherwise drift. - let (common, mut left, right) = before; - left[8 * 4..9 * 4].copy_from_slice(&1.0_f32.to_bits().to_le_bytes()); - assert_eq!( - effect.processor.restore_state_payload( - 1, - StatePayloadInput::new(&common, &left, &right, effect.metadata.state_sizes) - .expect("input"), - ), - Err(StatePayloadError { - code: "effect.state.payload" - }) - ); -} - /// A short or over-long snapshot buffer is refused before a byte is written. #[test] fn snapshot_rejects_bad_output_without_touching_either_lane() { diff --git a/crates/parametric-eq/tests/exact_rest.rs b/crates/parametric-eq/tests/exact_rest.rs index 60f0f224f..63d623a93 100644 --- a/crates/parametric-eq/tests/exact_rest.rs +++ b/crates/parametric-eq/tests/exact_rest.rs @@ -19,89 +19,12 @@ use std::f32::consts::FRAC_1_SQRT_2; use effect_contract::{ EffectProcessBlock, InitialParameterValue, NativeEffectFactory, ParameterChannel, - PreparedEffect, }; use parametric_eq::{EqBandKind, ParametricEqFactory}; use support::{band_word, request_at_rate, set_initial, single_section_values, snapshot, values}; const RATE: u32 = 96_000; const FRAMES: usize = 128; -/// The bound the issue states: rest within 9,600,000 samples (100 s) of the impulse. -const REST_BOUND_SAMPLES: usize = 9_600_000; -/// Blocks rendered after rest that must stay at rest. -const HELD_BLOCKS: usize = 4; - -fn at_rest(effect: &PreparedEffect, left: &[f32], right: &[f32]) -> bool { - let (_, left_state, right_state) = snapshot(effect); - [&left_state[..], &right_state[..]] - .iter() - .all(|payload| band_word(payload, 0, 0) == 0 && band_word(payload, 0, 1) == 0) - && left.iter().chain(right).all(|sample| sample.to_bits() == 0) -} - -#[test] -fn the_low_shelf_fixed_point_reaches_exact_rest() { - let configured = single_section_values(EqBandKind::LowShelf, 10.0, 24.0, FRAC_1_SQRT_2, 0.1); - let mut effect = ParametricEqFactory - .prepare(request_at_rate(&configured, false, RATE)) - .expect("the shelf prepares"); - - let mut left = [0.0_f32; FRAMES]; - let mut right = [0.0_f32; FRAMES]; - left[0] = 1.0; - right[0] = 1.0; - let mut rest_at = None; - let mut block = 0_usize; - while block * FRAMES < REST_BOUND_SAMPLES { - let first_sample = (block * FRAMES) as u64; - effect.processor.process( - EffectProcessBlock::new( - &mut left, - &mut right, - None, - first_sample, - &[], - FRAMES as u32, - ) - .expect("block"), - ); - if block > 0 && at_rest(&effect, &left, &right) { - rest_at = Some(block); - break; - } - left.fill(0.0); - right.fill(0.0); - block += 1; - } - let rest_at = rest_at.unwrap_or_else(|| { - let (_, left_state, _) = snapshot(&effect); - panic!( - "no exact rest within {REST_BOUND_SAMPLES} samples: band 0 left ic1 {:e}, ic2 {:e}", - f32::from_bits(band_word(&left_state, 0, 0)), - f32::from_bits(band_word(&left_state, 0, 1)), - ) - }); - for held in 1..=HELD_BLOCKS { - left.fill(0.0); - right.fill(0.0); - let first_sample = ((rest_at + held) * FRAMES) as u64; - effect.processor.process( - EffectProcessBlock::new( - &mut left, - &mut right, - None, - first_sample, - &[], - FRAMES as u32, - ) - .expect("block"), - ); - assert!( - at_rest(&effect, &left, &right), - "rest reached at block {rest_at} must hold, broke {held} blocks later" - ); - } -} /// Parameters per EQ band in the descriptor: enabled, kind, frequency, gain, Q, slope. const BAND_PARAMETERS: usize = 6; diff --git a/crates/parametric-eq/tests/response.rs b/crates/parametric-eq/tests/response.rs index 8ec0235ef..50809c84c 100644 --- a/crates/parametric-eq/tests/response.rs +++ b/crates/parametric-eq/tests/response.rs @@ -1161,93 +1161,6 @@ fn malformed_requests_preserve_output_sentinels_and_opaque_ids() { assert_eq!(summary.configuration_id, u64::MAX); } -#[test] -fn preparation_validates_full_request_and_owns_source_words() { - let mut source = single_section_values(EqBandKind::Bell, 1_000.0, 6.0, 0.7, 1.0); - let configuration = prepared(&source, false, 48_000); - let frequencies = [0.0_f32, 1_000.0, 12_000.0]; - let mut before_left = [f32::NAN; 3]; - let mut before_right = [f32::NAN; 3]; - query_response_into( - EqResponseRequest { - configuration_id: 1, - configuration: &configuration, - frequencies_hz: &frequencies, - maximum_points: frequencies.len(), - }, - EqResponseOutput { - total_left_db: &mut before_left, - total_right_db: &mut before_right, - sections_left_db: None, - sections_right_db: None, - }, - ) - .expect("source-owned baseline query"); - set_initial(&mut source, 3, ParameterChannel::Left, -24.0); - set_initial(&mut source, 3, ParameterChannel::Right, 24.0); - let mut after_left = [f32::NAN; 3]; - let mut after_right = [f32::NAN; 3]; - query_response_into( - EqResponseRequest { - configuration_id: 2, - configuration: &configuration, - frequencies_hz: &frequencies, - maximum_points: frequencies.len(), - }, - EqResponseOutput { - total_left_db: &mut after_left, - total_right_db: &mut after_right, - sections_left_db: None, - sections_right_db: None, - }, - ) - .expect("source-owned query after mutation"); - assert_eq!(before_left, after_left); - assert_eq!(before_right, after_right); - - let short = &source[..source.len() - 1]; - assert!(matches!( - EqResponseConfiguration::prepare(request_at_rate(short, false, 48_000)), - Err(EqResponseError::Configuration(_)) - )); - - let mut reordered = source.clone(); - reordered.swap(0, 1); - assert!(matches!( - EqResponseConfiguration::prepare(request_at_rate(&reordered, false, 48_000)), - Err(EqResponseError::Configuration(_)) - )); - - let mut wrong_channel = source.clone(); - wrong_channel[0].channel = ParameterChannel::Both; - assert!(matches!( - EqResponseConfiguration::prepare(request_at_rate(&wrong_channel, false, 48_000)), - Err(EqResponseError::Configuration(_)) - )); - - let mut zero_capacity = request_at_rate(&source, false, 48_000); - zero_capacity.limits.maximum_total_state_bytes = 0; - assert!(matches!( - EqResponseConfiguration::prepare(zero_capacity), - Err(EqResponseError::Configuration(_)) - )); - - let mut disabled_nan = values(); - set_initial(&mut disabled_nan, 2, ParameterChannel::Left, f32::NAN); - assert!(matches!( - EqResponseConfiguration::prepare(request_at_rate(&disabled_nan, false, 48_000)), - Err(EqResponseError::Configuration(_)) - )); - - for rate in [44_100, 48_000, 88_200, 96_000] { - assert!(EqResponseConfiguration::prepare(request_at_rate(&source, false, rate)).is_ok()); - } - assert!(matches!( - EqResponseConfiguration::prepare(request_at_rate(&source, false, 176_400)), - Err(EqResponseError::Configuration(_)) - )); -} - #[test] fn deep_cut_plus_gain_composes_before_public_flooring() { let rate = 48_000; diff --git a/crates/protocol/src/btlv.rs b/crates/protocol/src/btlv.rs index 1e15dfba6..c8942c923 100644 --- a/crates/protocol/src/btlv.rs +++ b/crates/protocol/src/btlv.rs @@ -1067,51 +1067,6 @@ mod tests { ); } - #[test] - fn nested_count_mismatch_restores_parent_counter() { - let limits = ProtocolLimits::default(); - let nested = FieldSpec::msg(1, true, false, &crate::schema::enum_choice::SPEC); - let child_count = crate::schema::enum_choice::SPEC - .field_count(&[]) - .expect("schema count"); - - let mut sizing = CountSink::new(limits); - sizing.check_field_count(1).expect("parent count"); - assert_eq!( - sizing.nested_spec(nested, &mut |sink| { - sink.message_header(child_count)?; - sink.field_spec(crate::schema::enum_choice::VALUE, &1.0_f32.to_le_bytes()) - }), - Err(EncodeError::LimitExceeded) - ); - assert_eq!(sizing.fields.actual, 0); - sizing - .nested_spec(nested, &mut |sink| { - sink.message_header(child_count)?; - sink.field_spec(crate::schema::enum_choice::VALUE, &1.0_f32.to_le_bytes())?; - sink.field_spec(crate::schema::enum_choice::LABEL, b"one") - }) - .expect("parent counter restored"); - sizing.finish_message().expect("parent count matches"); - - let mut bytes = [0_u8; 96]; - let mut writer = SliceSink::new(&mut bytes, limits); - writer.check_field_count(1).expect("parent count"); - assert_eq!( - writer.nested_spec(nested, &mut |sink| sink.message_header(child_count)), - Err(EncodeError::LimitExceeded) - ); - assert_eq!(writer.fields.actual, 0); - writer - .nested_spec(nested, &mut |sink| { - sink.message_header(child_count)?; - sink.field_spec(crate::schema::enum_choice::VALUE, &1.0_f32.to_le_bytes())?; - sink.field_spec(crate::schema::enum_choice::LABEL, b"one") - }) - .expect("parent counter restored"); - writer.finish_message().expect("parent count matches"); - } - #[test] fn nested_body_runs_once_per_sink_and_patches_length() { let limits = ProtocolLimits::default(); diff --git a/crates/protocol/src/controller/tests.rs b/crates/protocol/src/controller/tests.rs index 55ecf6c9e..47155d884 100644 --- a/crates/protocol/src/controller/tests.rs +++ b/crates/protocol/src/controller/tests.rs @@ -2948,155 +2948,6 @@ fn an_event_frame_under_the_retired_cancellation_id_is_refused() { ); } -#[test] -fn b3b1_btlv_telemetry_echo_and_counters_are_typed_and_nondestructive() { - fn frame( - message_id: MessageId, - request_id: u64, - revision: ExpectedRevision, - payload: &[u8], - count: u32, - ) -> Vec { - let codec = ProtocolCodec::default(); - let mut bytes = vec![0_u8; crate::OUTER_HEADER_BYTES + payload.len()]; - let (wire_revision, flags) = match revision { - ExpectedRevision::Any => (0, 1), - ExpectedRevision::Exact(revision) => (revision.0, 0), - }; - codec - .write_outer_header( - &mut bytes, - crate::FrameKind::Command, - message_id, - StatusCode::Ok, - request_id, - wire_revision, - flags, - payload.len() as u32, - count, - ) - .expect("header"); - bytes[crate::OUTER_HEADER_BYTES..].copy_from_slice(payload); - bytes - } - let codec = ProtocolCodec::default(); - let configuration = TelemetryConfiguration { - meter_handles: vec![1], - meter_period_blocks: 4, - counter_ids: vec![crate::CounterId::ControlCommandBackpressure], - counter_period_blocks: 8, - diagnostics_enabled: false, - minimum_diagnostic_severity: crate::DiagnosticSeverity::Warning, - }; - let mut config_payload = vec![ - 0; - codec - .encoded_telemetry_configuration_len(&configuration) - .expect("config length") - ]; - codec - .encode_telemetry_configuration(&configuration, &mut config_payload) - .expect("config"); - let mut controller = controller(8); - let first = controller - .process_b1b_btlv( - &frame( - MessageId::TelemetryConfigure, - 1, - ExpectedRevision::Exact(SessionRevision(7)), - &config_payload, - 6, - ), - &mut DecodeScratch::new(&mut [0_u16; 6]), - ) - .expect("configure"); - assert_eq!(first.status, StatusCode::Ok); - assert_eq!( - codec.decode_telemetry_configuration(first.payload(), 6), - Ok(configuration.clone()) - ); - assert_eq!(controller.provider().telemetry_configuration, configuration); - let second = controller - .process_b1b_btlv( - &frame( - MessageId::TelemetryConfigure, - 2, - ExpectedRevision::Exact(SessionRevision(7)), - &config_payload, - 6, - ), - &mut DecodeScratch::new(&mut [0_u16; 6]), - ) - .expect("idempotent configure"); - assert_eq!(second.payload(), first.payload()); - - let request = CountersRequest { - all: false, - ids: vec![1, 5], - }; - let mut request_payload = [0_u8; 32]; - codec - .encode_counters_request(&request, &mut request_payload) - .expect("counter request"); - let first = controller - .process_b1b_btlv( - &frame( - MessageId::CountersGet, - 3, - ExpectedRevision::Any, - &request_payload, - 2, - ), - &mut DecodeScratch::new(&mut [0_u16; 2]), - ) - .expect("counter read"); - let second = controller - .process_b1b_btlv( - &frame( - MessageId::CountersGet, - 4, - ExpectedRevision::Any, - &request_payload, - 2, - ), - &mut DecodeScratch::new(&mut [0_u16; 2]), - ) - .expect("nonreset read"); - assert_eq!(first.payload(), second.payload()); - assert_eq!( - codec - .decode_counter_snapshot(first.payload(), 3) - .expect("snapshot") - .values - .len(), - 2 - ); - let unknown = CountersRequest { - all: false, - ids: vec![99], - }; - let mut unknown_payload = [0_u8; 32]; - codec - .encode_counters_request(&unknown, &mut unknown_payload) - .expect("unknown selector"); - assert_eq!( - controller - .process_b1b_btlv( - &frame( - MessageId::CountersGet, - 5, - ExpectedRevision::Any, - &unknown_payload, - 2 - ), - &mut DecodeScratch::new(&mut [0_u16; 2]), - ) - .expect("unknown response") - .status, - StatusCode::NotFound - ); -} - #[test] fn b3b2_diagnostics_btlv_pages_are_typed_nondestructive_and_expire_old_cursors() { fn frame(request_id: u64, request: DiagnosticsRequest) -> Vec { diff --git a/crates/protocol/src/model.rs b/crates/protocol/src/model.rs index 81926c033..a17469f7e 100644 --- a/crates/protocol/src/model.rs +++ b/crates/protocol/src/model.rs @@ -1703,7 +1703,7 @@ mod tests { use super::*; use crate::control_rows::row_address; use controls::{Lane, Owner, Section}; - use session::{ControlSmoothing, ParameterChannel, canonical_session_json, parse_session_json}; + use session::{ControlSmoothing, canonical_session_json, parse_session_json}; const EXAMPLE: &str = include_str!("../../../fixtures/session/v1/canonical.json"); @@ -2279,50 +2279,6 @@ mod tests { assert_eq!(store.compiled().normalized_model().sample_rate_hz, 96_000); } - #[test] - fn effects_and_parameters_follow_exact_key_rules() { - let mut store = store(); - let edit = SessionEdit::set_control( - controls::ControlAddress { - owner: Owner::Strip(id("vocal")), - section: Section::Inserts(id("eq")), - control: 1, - lane: Lane::Both, - }, - 1.0, - None, - ); - store - .apply_transaction( - ExpectedRevision::Exact(store.revision()), - &[ - // The fixture's effect id is not a launch effect, so name one the control - // core can address. - SessionEdit::SetEffectIdentity { - track_id: id("vocal"), - rack_name: RackName::Inserts, - effect_id: id("eq"), - identity: EffectIdentity::Native { - effect_id: id("miso.parametric-eq"), - }, - }, - edit.clone(), - edit, - ], - ) - .expect("same compound key replaces"); - let effect = &store.compiled().normalized_model().tracks[0] - .inserts - .effects[0]; - let written: Vec<_> = effect - .params - .iter() - .filter(|param| param.parameter_id == 1 && param.channel == ParameterChannel::Both) - .collect(); - assert_eq!(written.len(), 1); - assert_eq!(written[0].value, 1.0); - } - fn store_at(revision: u64) -> SessionStore { let mut model = parse_session_json(EXAMPLE).expect("fixture"); model.revision = revision; diff --git a/crates/protocol/tests/console_session_edits.rs b/crates/protocol/tests/console_session_edits.rs index ce1705d26..909c9eb2d 100644 --- a/crates/protocol/tests/console_session_edits.rs +++ b/crates/protocol/tests/console_session_edits.rs @@ -14,9 +14,7 @@ //! The base session has two tracks and one `pre_insert` slot, `eq`. Track `a` also carries an //! insert whose ID is `eq`, so an edit routed to the wrong place changes a value these tests read. -use protocol::control_core::{ - BYPASS_CONTROL, ControlAddress, ControlError, Lane, Owner, Section, row_address, -}; +use protocol::control_core::{BYPASS_CONTROL, ControlAddress, ControlError, Lane, Owner, Section}; use protocol::{ DecodeScratch, ExpectedRevision, ProtocolCodec, RequestId, SessionEdit, SessionEditError, SessionRevision, SessionStore, SessionStoreError, SessionTransactionFrame, apply_session_edit, @@ -841,93 +839,6 @@ fn a_track_cannot_change_the_slot_set_through_its_entries() { } } -/// #1218 gates 6 and 7 (D1), as a `SET_CONTROL` of a route's `follows_mute` row (decision 17, -/// #1561 retired `SetRouteFollowsMute`), through the wire: -/// -/// - `true` on route `a-out`, whose destination is the output, is refused at its own edit with -/// `control.inactive` (the row does not apply to an output route); the revision, model and -/// snapshot stay; -/// - the same edit after re-pointing `a-out` into submix `bus` commits, and the committed snapshot -/// is the canonical JSON of the hand-edited model; -/// - `false` then commits back. -/// -/// Red if an output route can follow (a strip's main route would stay silent after a live -/// unmute, VERIFY-2 N1), if the re-pointed route cannot, or if the row writes the wrong route or -/// ignores the value. -#[test] -fn set_route_follows_mute_is_legal_only_on_a_route_into_a_submix() { - let mut start = base(); - start - .submixes - .push(Submix::unity(id("bus"), &start.console)); - let mut store = SessionStore::new(start, caps()).expect("a unity bus compiles"); - let revision = store.revision(); - let snapshot = store.canonical_snapshot().to_owned(); - let model = store.compiled().normalized_model().clone(); - assert_eq!(model.routes[0].id.as_str(), "a-out"); - let follow = |follows_mute| { - SessionEdit::set_control( - row_address( - Owner::Route(id("a-out")), - Section::Own, - "follows_mute", - Lane::Both, - ) - .expect("the follows_mute row"), - f32::from(u8::from(follows_mute)), - None, - ) - }; - - let refused = through_the_wire(&[follow(true)]); - let error = store - .apply_transaction(ExpectedRevision::Exact(revision), &refused) - .expect_err("an output route cannot follow"); - assert_eq!( - error, - SessionStoreError::Edit { - operation_index: 0, - error: SessionEditError::Control(ControlError::Inactive), - } - ); - assert_eq!(store.revision(), revision); - assert_eq!(store.canonical_snapshot(), snapshot); - assert_eq!(store.compiled().normalized_model(), &model); - - let repointed = through_the_wire(&[ - SessionEdit::SetRouteDestination { - route_id: id("a-out"), - destination: session::RouteDestination::SubmixInput { - submix_id: id("bus"), - }, - }, - follow(true), - ]); - let commit = store - .apply_transaction(ExpectedRevision::Exact(revision), &repointed) - .expect("a follow on a route re-pointed into a submix commits"); - assert_eq!(commit.revision.0, revision.0 + 1); - let mut expected = model.clone(); - expected.revision = revision.0 + 1; - expected.routes[0].follows_mute = true; - expected.routes[0].destination = session::RouteDestination::SubmixInput { - submix_id: id("bus"), - }; - assert_eq!( - store.canonical_snapshot(), - canonical_session_json(&expected).expect("canonical") - ); - assert!(!store.compiled().normalized_model().routes[1].follows_mute); - - store - .apply_transaction( - ExpectedRevision::Exact(SessionRevision(revision.0 + 1)), - &through_the_wire(&[follow(false)]), - ) - .expect("a follow switched off commits"); - assert!(!store.compiled().normalized_model().routes[0].follows_mute); -} - /// #1561 gate 2 (D3). `SetTrackConsole` is structural: a transaction that carries one with no /// `SetConsole` before it is refused at that edit with `session.edit.value_op_retired`, and nothing /// commits -- not even an earlier edit of the transaction. Setting the same knob with `SET_CONTROL` diff --git a/crates/protocol/tests/session_edit_builtins_rack.rs b/crates/protocol/tests/session_edit_builtins_rack.rs index c93b568a4..146d4294b 100644 --- a/crates/protocol/tests/session_edit_builtins_rack.rs +++ b/crates/protocol/tests/session_edit_builtins_rack.rs @@ -89,29 +89,6 @@ fn rack_addressed_edits_refuse_the_builtins_token() { } } -/// The same edits against a real rack are applied, so the refusal above is about the token and not -/// about the edits. -#[test] -fn the_same_edits_against_a_real_rack_are_applied() { - let rack = session::Rack { - effects: Vec::new(), - }; - let mut model = session(); - assert_ne!(model.tracks[0].inserts, rack); - assert_eq!( - apply_session_edit( - &mut model, - &SessionEdit::SetTrackRack { - track_id: track_id(), - rack_name: RackName::Inserts, - rack: rack.clone(), - }, - ), - Ok(()), - ); - assert_eq!(model.tracks[0].inserts, rack); -} - /// And the strip is edited through `SET_CONTROL` on its builtin rows, which the new token does not /// touch. #[test] diff --git a/crates/rack/src/lib.rs b/crates/rack/src/lib.rs index d404f7601..d27d66e3d 100644 --- a/crates/rack/src/lib.rs +++ b/crates/rack/src/lib.rs @@ -4276,42 +4276,6 @@ mod tests { } } - #[cfg(target_feature = "avx2")] - struct WrongWidthPlanes { - planes: Planes, - per_lane_writes: usize, - } - #[cfg(target_feature = "avx2")] - impl BankMembers for WrongWidthPlanes { - fn plane(&self, lane: usize) -> (&[f32], &[f32]) { - self.planes.plane(lane) - } - fn plane_mut(&mut self, lane: usize) -> (&mut [f32], &mut [f32]) { - self.per_lane_writes += 1; - self.planes.plane_mut(lane) - } - fn distinct_planes_mut( - &mut self, - _lanes: usize, - frames: usize, - ) -> Option> { - let mut left = self.planes.left.iter_mut().take(4); - let mut right = self.planes.right.iter_mut().take(4); - let pairs: [BankPlanePair<'_>; 4] = core::array::from_fn(|_| { - ( - left.next() - .expect("four malicious left lanes") - .as_mut_slice(), - right - .next() - .expect("four malicious right lanes") - .as_mut_slice(), - ) - }); - BankPlaneViews::from_four(pairs, frames) - } - } - struct ShortClaimPlanes { planes: Planes, per_lane_writes: usize, @@ -5261,63 +5225,6 @@ mod tests { assert_planes_bit_equal(&members.planes, &expected, "direct call sentinel"); } - /// An other-width probe: the provider offers a four-lane view to an eight-lane chain, so it - /// exists where eight-lane banks do (issue #1112). - #[test] - #[cfg(target_feature = "avx2")] - fn wrong_width_provider_is_dropped_before_complete_staged_fallback() { - let mut state = 0x3994_3995_3996_3997; - let source = hostile_planes(8, 11, &mut state); - let expected = Planes { - left: source.left.clone(), - right: source.right.clone(), - }; - let mut malformed = WrongWidthPlanes { - planes: Planes { - left: source - .left - .into_iter() - .chain([vec![f32::from_bits(0x7fc0_3998); 11]]) - .collect(), - right: source - .right - .into_iter() - .chain([vec![f32::from_bits(0x7fc0_3999); 11]]) - .collect(), - }, - per_lane_writes: 0, - }; - let active = vec![true; 8].into_boxed_slice(); - let mut chain = BankChain::new( - AoSoaScratch::new(BankWidth::Eight, 11).expect("scratch"), - active.clone(), - vec![slot(active.to_vec(), Box::new(PassThrough))], - ) - .expect("chain"); - chain - .run(&mut malformed, 11, 0, LiveSnapshot::ALL) - .expect("staged fallback"); - assert_planes_bit_equal( - &Planes { - left: malformed.planes.left[..8].to_vec(), - right: malformed.planes.right[..8].to_vec(), - }, - &expected, - "wrong-width staged fallback", - ); - assert_eq!(malformed.per_lane_writes, 8); - assert!( - malformed.planes.left[8] - .iter() - .all(|word| word.to_bits() == 0x7fc0_3998) - ); - assert!( - malformed.planes.right[8] - .iter() - .all(|word| word.to_bits() == 0x7fc0_3999) - ); - } - #[test] fn inadequate_claimed_capacity_takes_complete_staged_fallback() { let mut state = 0x399a_399b_399c_399d; @@ -5923,116 +5830,6 @@ mod tests { } } - #[test] - fn all_active_folded_masks_use_one_cohort_with_physical_lane_ids() { - let frames = 13_u32; - // A full four-lane bank; a full, a scattered and a one-lane eight-lane bank, where a - // build has eight lanes (issue #1112). - let masks = BankWidth::ALL.iter().flat_map(|&width| { - if width.lanes() == 4 { - vec![(width, vec![true, true, true, true])] - } else { - vec![ - (width, vec![true; 8]), - ( - width, - vec![true, false, true, false, false, true, false, false], - ), - ( - width, - vec![false, false, true, false, false, false, false, false], - ), - ] - } - }); - for (width, active) in masks { - let lanes = width.lanes() as usize; - let build = || PlanesWithFold { - planes: Planes { - left: (0..lanes) - .map(|lane| vec![lane as f32 + 1.0; frames as usize]) - .collect(), - right: (0..lanes) - .map(|lane| vec![-(lane as f32) - 1.0; frames as usize]) - .collect(), - }, - gains: vec![1.0; lanes], - bus_left: vec![0.0; frames as usize], - bus_right: vec![0.0; frames as usize], - taken: Vec::new(), - trace: Vec::new(), - cohorts: Vec::new(), - }; - let chain = || { - BankChain::new( - AoSoaScratch::new(width, frames).expect("scratch"), - active.clone().into_boxed_slice(), - vec![slot(active.clone(), Box::new(ScaleByLane))], - ) - .expect("chain") - }; - let mut members = build(); - let mut default = DefaultFoldProvider(build()); - let mut optimized_chain = chain(); - let mut default_chain = chain(); - optimized_chain - .arm_fold(active.clone().into_boxed_slice()) - .expect("fold"); - default_chain - .arm_fold(active.clone().into_boxed_slice()) - .expect("fold"); - for block in 0..2 { - optimized_chain - .run( - &mut members, - frames, - block * u64::from(frames), - LiveSnapshot::ALL, - ) - .expect("run"); - default_chain - .run( - &mut default, - frames, - block * u64::from(frames), - LiveSnapshot::ALL, - ) - .expect("default run"); - } - let ids: Vec = (0..lanes).filter(|lane| active[*lane]).collect(); - let expected_cohorts = if ids.is_empty() { - Vec::new() - } else { - vec![ids.clone(), ids] - }; - assert_eq!(members.cohorts, expected_cohorts); - assert!(members.trace.iter().all(|(kind, _)| *kind == 'f')); - assert!( - default.0.cohorts.is_empty(), - "trait default delegates lane by lane" - ); - assert_eq!(members.taken, default.0.taken); - assert_eq!(members.bus_left, default.0.bus_left); - assert_eq!(members.bus_right, default.0.bus_right); - assert_eq!(members.planes.left, default.0.planes.left); - assert_eq!(members.planes.right, default.0.planes.right); - for (lane, is_active) in active.iter().copied().enumerate() { - if !is_active { - assert!( - members.planes.left[lane] - .iter() - .all(|x| x.to_bits() == (lane as f32 + 1.0).to_bits()) - ); - assert!( - members.planes.right[lane] - .iter() - .all(|x| x.to_bits() == (-(lane as f32) - 1.0).to_bits()) - ); - } - } - } - } - /// One recorded [`BankMembers::fold_resident`] offer: `(lanes, frames, lane ids, left bits, /// right bits)`. type ResidentOffer = (usize, usize, Vec, Vec, Vec); @@ -6529,39 +6326,6 @@ mod tests { ); } - #[test] - fn arming_an_auxiliary_destination_refuses_an_unrenderable_mask() { - let frames = 32_u32; - // The build's widest bank, its last lane not rendered (#1112). - let lanes = WIDEST.lanes() as usize; - let mut active = vec![true; lanes]; - active[lanes - 1] = false; - let scratch = AoSoaScratch::new(WIDEST, frames).expect("scratch"); - let mut chain = BankChain::new( - scratch, - active.clone().into_boxed_slice(), - vec![slot(active.clone(), Box::new(PassThrough))], - ) - .expect("chain"); - assert_eq!( - chain.arm_aux(vec![true; lanes - 1].into_boxed_slice()), - Err(RackError::Shape), - "a mask that is not the chain's lane count is refused" - ); - assert_eq!( - chain.arm_aux(vec![true; lanes].into_boxed_slice()), - Err(RackError::Shape), - "arming the last lane, which this chain does not render, is refused" - ); - assert!( - chain.aux_lanes().is_empty(), - "a refused arm leaves the chain on the zero-cost path" - ); - chain - .arm_aux(active.into_boxed_slice()) - .expect("arming exactly the rendered lanes is admitted"); - } - #[test] fn gather_scatter_round_trip_is_bit_exact() { let frames = 128_u32; @@ -6691,58 +6455,6 @@ mod tests { ); } - /// T3: a slot that is an identity on every lane is never executed, and the chain still counts - /// exactly one transpose per block. - #[test] - fn identity_everywhere_slot_is_not_executed() { - struct Counting { - slot: usize, - calls: u64, - } - impl BankStage for Counting { - fn process(&mut self, _block: BankBlock<'_>) -> Result<(), RenderError> { - self.calls += 1; - Ok(()) - } - fn qualification_counters(&self) -> [u64; 2] { - let mut counters = [0, 0]; - counters[self.slot] = self.calls; - counters - } - } - let frames = 16_u32; - let mut planes = Planes { - left: (0..4).map(|_| vec![0.0; frames as usize]).collect(), - right: (0..4).map(|_| vec![0.0; frames as usize]).collect(), - }; - let mut chain = BankChain::new( - AoSoaScratch::new(BankWidth::Four, frames).expect("scratch"), - vec![true; 4].into_boxed_slice(), - vec![ - slot(vec![true; 4], Box::new(Counting { slot: 0, calls: 0 })), - slot(vec![false; 4], Box::new(Counting { slot: 1, calls: 0 })), - slot(vec![true; 4], Box::new(Counting { slot: 0, calls: 0 })), - ], - ) - .expect("chain"); - for block in 1..=5_u64 { - chain - .run( - &mut planes, - frames, - block * u64::from(frames), - LiveSnapshot::ALL, - ) - .expect("run"); - assert_eq!(chain.transposes(), block); - } - assert_eq!( - chain.qualification_counters(), - [10, 0], - "both live slots ran once per block; the identity-everywhere slot never ran" - ); - } - /// T4: `BankChain::new` rejects every mask-shape violation, including a slot active on a lane /// the chain never gathers. #[test] diff --git a/crates/rack/tests/MUTATIONS.md b/crates/rack/tests/MUTATIONS.md index 1efefc84a..7c3fecc5b 100644 --- a/crates/rack/tests/MUTATIONS.md +++ b/crates/rack/tests/MUTATIONS.md @@ -23,8 +23,8 @@ cargo test --locked -p rack -p rack-compiler -p graph-compiler |---|---|---|---|---|---| | M1 | `scatter_lane` reads the wrong lane: `*sample = chunk[lane]` becomes `chunk[lane ^ 1]` | `rack/src/lib.rs` | `gather_scatter_round_trip_is_bit_exact` | RED | ``assertion `left == right` failed: left lane=0 frame=0`` | | M2 | `gather_lane` chunks by frames instead of lanes: `chunks_exact_mut(lanes)` becomes `chunks_exact_mut(frames as usize)` | `rack/src/lib.rs` | `stage_sees_frame_major_layout` | RED | ``assertion `left == right` failed: lane=0 frame=1`` | -| M3 | `BankChain::run` drops the identity-slot guard: `if slot.active_lanes.iter().any(..)` becomes `if true` | `rack/src/lib.rs` | `identity_everywhere_slot_is_not_executed` | RED | ``assertion `left == right` failed: both live slots ran once per block; the identity-everywhere slot never ran`` | -| M3b | the transpose counter moves inside the slot loop (master plan §4.5: it is *per chain*, not per slot) | `rack/src/lib.rs` | `identity_everywhere_slot_is_not_executed` | RED | ``assertion `left == right` failed`` (2 live slots ⇒ 10 counted round-trips for 5 blocks) | +| M3 | `BankChain::run` drops the identity-slot guard: `if slot.active_lanes.iter().any(..)` becomes `if true` | `rack/src/lib.rs` | `identity_everywhere_slot_is_not_executed` (deleted by TA5) | RED | ``assertion `left == right` failed: both live slots ran once per block; the identity-everywhere slot never ran`` | +| M3b | the transpose counter moves inside the slot loop (master plan §4.5: it is *per chain*, not per slot) | `rack/src/lib.rs` | `identity_everywhere_slot_is_not_executed` (deleted by TA5) | RED | ``assertion `left == right` failed`` (2 live slots ⇒ 10 counted round-trips for 5 blocks) | | M4 | `BankChain::new` stops enforcing "a slot may only be active on an active lane" | `rack/src/lib.rs` | `chain_new_rejects_mask_shape_and_lane_implication` | RED | ``assertion `left == right` failed: a slot may not be active on a lane the chain never gathers`` | | M5 | `AoSoaScratch::new` re-adds a plane: `vec![0.0; length]` becomes `vec![0.0; length * 2]` for the left plane | `rack/src/lib.rs` | `scratch_allocates_exactly_two_planes` | RED | ``assertion `left == right` failed`` | | M6 | `gather_lane` drops the first frame of every block: `.zip(left)` becomes `.zip(&left[1..])` | `rack/src/lib.rs` | `chain_run_is_partition_invariant` | RED | ``assertion `left == right` failed: partition=1 lane=0 frame=0`` | @@ -305,3 +305,9 @@ the graph suites of `graph/tests/MUTATIONS.md` issue #915, and console-workload Row 915-R1 is the reason the rack half of gate 1 exists. Whether the staging block is written is invisible to every digest, so it is pinned where it can be seen. + +## TA5 deletions + +The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. + +- `identity_everywhere_slot_is_not_executed`: rack `tests::full_bank_gather_scatter_round_trip_is_bit_exact`, `tests::prepared_slot_activity_preserves_dispatch_trace_and_errors` diff --git a/crates/rack/tests/live_control_bank.rs b/crates/rack/tests/live_control_bank.rs index 7d5639d8d..c8f02ced2 100644 --- a/crates/rack/tests/live_control_bank.rs +++ b/crates/rack/tests/live_control_bank.rs @@ -6,8 +6,6 @@ //! packed span array correctly, that a lane never sees another lane's command, and that a bypassed //! lane gets its own latency-matched dry signal while its neighbours keep the wet one. -use std::sync::{Arc, Mutex}; - use effect_contract::{ BankProcessReport, BankWidth, EffectBankProcessBlock, EffectCellWriter, EffectControlLane, EffectControlRecord, EffectId, EffectLaneShape, EffectLaneSpec, EffectProgramKey, @@ -16,11 +14,8 @@ use effect_contract::{ PreparedSidechainPort, ResetKind, StatePayloadError, StatePayloadInput, StatePayloadOutput, StatePayloadSizes, TailSamples, effect_control_lane, }; -use engine::realtime::{GatePin, LiveSnapshot, LiveValuesSuperseded, RenderError, RevisionGate}; -use rack::{ - AoSoaScratch, BankBlock, BankChain, BankMembers, BankSlot, BankStage, - LiveControlEffectBankStage, -}; +use engine::realtime::{GatePin, LiveSnapshot, LiveValuesSuperseded, RevisionGate}; +use rack::{AoSoaScratch, BankChain, BankMembers, BankSlot, BankStage, LiveControlEffectBankStage}; const LANES: usize = 4; const CAPACITY: u32 = 4; @@ -492,81 +487,6 @@ fn a_live_control_stage_reports_the_lanes_that_hold_a_channel() { assert_eq!(stage_of([true; LANES]).live_lanes(), 0b1111); } -/// #1504: `run` hands the block's live snapshot to every slot that has an active lane, once in -/// every block, silent blocks included, and to no slot that has none. -/// -/// This lives here and not in `src/lib.rs`'s inline module because a `BankStage` is `Send`, so a -/// test reads what the chain's stages recorded through an `Arc>`, and -/// `scripts/check-rack-policy.sh` forbids `std::sync` under `crates/rack/src` (render code). -/// -/// Test value: a chain that passes a fixed or stale snapshot to a stage, or skips a stage's -/// drain when the block is silent. Under `S` that makes the watermark early. -#[test] -fn run_hands_the_blocks_snapshot_to_every_active_slot_in_every_block() { - type Drains = Arc>>; - struct Recording { - slot: usize, - drains: Drains, - } - impl BankStage for Recording { - fn process(&mut self, _block: BankBlock<'_>) -> Result<(), RenderError> { - Ok(()) - } - fn begin_block( - &mut self, - first_sample: u64, - snapshot: LiveSnapshot, - ) -> Result<(), RenderError> { - self.drains.lock().expect("drains").push(( - self.slot, - first_sample, - snapshot.revision(), - )); - Ok(()) - } - } - let frames = 8_u32; - // Every block is silent: the planes stay zero. - let mut planes = Planes { - left: (0..LANES).map(|_| vec![0.0; frames as usize]).collect(), - right: (0..LANES).map(|_| vec![0.0; frames as usize]).collect(), - }; - let drains = Drains::default(); - let slot = |slot: usize, active_lanes: [bool; LANES]| BankSlot { - stage: Box::new(Recording { - slot, - drains: Arc::clone(&drains), - }), - active_lanes: active_lanes.to_vec().into_boxed_slice(), - }; - let mut chain = BankChain::new( - AoSoaScratch::new(BankWidth::Four, frames).expect("scratch"), - vec![true; LANES].into_boxed_slice(), - vec![ - slot(0, [true; LANES]), - // An identity on every lane: it runs nothing, so it has no drain to run. - slot(1, [false; LANES]), - slot(2, [true, true, false, false]), - ], - ) - .expect("chain"); - let (gate, mut pin) = RevisionGate::pair(0); - let revisions = [0_u64, 3, 3, 9]; - let mut expected = Vec::new(); - for (block, revision) in revisions.into_iter().enumerate() { - if revision > pin.last_published() { - pin.publish(&gate, revision); - } - let first_sample = block as u64 * u64::from(frames); - chain - .run(&mut planes, frames, first_sample, gate.snapshot()) - .expect("run"); - expected.push((0, first_sample, revision)); - expected.push((2, first_sample, revision)); - } - assert_eq!(*drains.lock().expect("drains"), expected); -} - /// Shape is validated once, off the render thread. #[test] fn stage_construction_rejects_a_lane_count_or_quantum_mismatch() { diff --git a/crates/session/tests/control_smoothing.rs b/crates/session/tests/control_smoothing.rs index 1b3b467ae..32e7eb156 100644 --- a/crates/session/tests/control_smoothing.rs +++ b/crates/session/tests/control_smoothing.rs @@ -4,7 +4,7 @@ use serde_json::{Map, Value, json}; use session::{ CONTROL_SMOOTHING_DEFAULT, ControlSmoothing, ControlSmoothingSamples, DiagnosticCode, - DiagnosticSet, SessionModel, canonical_session_json, parse_session_json, + DiagnosticSet, canonical_session_json, parse_session_json, }; const EXAMPLE: &str = include_str!("../../../fixtures/session/v1/canonical.json"); @@ -148,28 +148,6 @@ fn both_kinds_round_trip_through_canonical_json_to_identical_bytes() { ); } -/// A model built rather than parsed is held to the parser's domain by the validator. -#[test] -fn a_built_model_with_a_length_outside_the_domain_is_refused() { - let mut model: SessionModel = parse_session_json(EXAMPLE).expect("fixture"); - for bad in [-1.0, 1000.5, f32::NAN, f32::INFINITY] { - model.control_smoothing = Some(ControlSmoothing { - mute_ms: 10.0, - fader_ms: 20.0, - pan_ms: bad, - }); - let error = canonical_session_json(&model).expect_err("out of domain"); - assert_eq!( - diagnostics(&error), - [( - DiagnosticCode::NumericOutOfSchemaRange, - "$.control_smoothing.pan_ms".to_owned() - )], - "{bad}" - ); - } -} - fn samples_at(rate: u32, control_smoothing: Option) -> ControlSmoothingSamples { let mut model = parse_session_json(EXAMPLE).expect("fixture"); model.sample_rate_hz = rate; diff --git a/crates/session/tests/json_grammar.rs b/crates/session/tests/json_grammar.rs index db8f94903..f77e489b2 100644 --- a/crates/session/tests/json_grammar.rs +++ b/crates/session/tests/json_grammar.rs @@ -21,20 +21,6 @@ fn only<'a>( diagnostic } -#[test] -fn insignificant_whitespace_and_shuffled_keys_canonicalize() { - let mut value: serde_json::Value = serde_json::from_str(CANONICAL).expect("fixture JSON"); - let object = value.as_object_mut().expect("root object"); - let schema = object.remove("schema_version").expect("schema"); - object.insert("schema_version".to_owned(), schema); - let source = serde_json::to_string(&value).expect("noncanonical JSON"); - let model = parse_session_json(&source).expect("valid noncanonical JSON"); - assert_eq!( - canonical_session_json(&model).expect("canonical"), - CANONICAL - ); -} - #[test] fn decoded_duplicate_path_and_second_key_span_are_exact() { let source = r#"{"outer":{"id":"first","\u0069d":"second","ignored":{"x":1}}}"#; diff --git a/crates/soft-clip/src/padding_tests.rs b/crates/soft-clip/src/padding_tests.rs index d58284f37..8c55227c9 100644 --- a/crates/soft-clip/src/padding_tests.rs +++ b/crates/soft-clip/src/padding_tests.rs @@ -690,67 +690,6 @@ fn padded_banks_render_the_fixtures_per_node() { // Gate 3: the clone source does not reach an active lane // --------------------------------------------------------------------------------------------- -/// Gate 3: an active lane's bits are the same whichever member the padded lanes clone. -/// -/// Every render also matches the members' scalar instances and checks each padded lane (gate 1's -/// checks), and the renders of one mask must agree word for word across every clone source, -/// including a member at every parameter's minimum and one at every maximum. Red if a factory reads -/// its members from anywhere but lanes `0..members` -- from the tail of the requests, say, as if -/// padding came first -- so that an active lane runs a clone; or if a whole-bank decision reads a -/// padded lane's parameters. -#[test] -fn active_lanes_do_not_depend_on_the_clone_source() { - for &width in WIDTHS { - let lanes = width.lanes() as usize; - let masks: Vec> = [1, 2, lanes / 2 + 1, lanes - 1] - .into_iter() - .map(|members| prefix_mask(width, members)) - .collect(); - for (index, mask) in masks.iter().enumerate() { - let members = mask.iter().filter(|active| **active).count(); - let mut draw = Draw::new(0x1092_0300 + (lanes * 10 + index) as u64); - // Members that differ as much as the domains allow: one at every minimum and one at - // every maximum, then drawn ones. - let mut values: Vec = (0..members).map(|_| draw.values()).collect(); - values[0] = values_from(SOFT_CLIP_PARAMETERS.map(|p| { - let minimum = p.minimum.expect("bounded"); - (minimum, minimum) - })); - if members > 1 { - values[members - 1] = values_from(SOFT_CLIP_PARAMETERS.map(|p| { - let maximum = p.maximum.expect("bounded"); - (maximum, maximum) - })); - } - let blocks = random_blocks(&mut draw, members, 8); - let link = LINKS[index % LINKS.len()]; - let mut reference: Option>> = None; - for clone_of in 0..members { - let outputs = render( - &Padded { - width, - mask, - members: &values, - clone_of, - rate: 48_000, - link, - }, - &blocks, - &format!("{width:?} mask {mask:?} clone of member {clone_of}"), - ); - match &reference { - None => reference = Some(outputs), - Some(first) => assert_eq!( - &outputs, first, - "{width:?} mask {mask:?}: the active lanes moved when the padding cloned \ - member {clone_of} instead of member 0" - ), - } - } - } - } -} - // --------------------------------------------------------------------------------------------- // Gate 4: D7 attributes active lanes only // --------------------------------------------------------------------------------------------- diff --git a/crates/soft-clip/tests/contract.rs b/crates/soft-clip/tests/contract.rs index ee02fd6d6..9b8792e39 100644 --- a/crates/soft-clip/tests/contract.rs +++ b/crates/soft-clip/tests/contract.rs @@ -15,7 +15,7 @@ use effect_contract::{ }; use lane::kernels::halfband::{HALFBAND63_CENTER, HALFBAND63_EVEN}; use soft_clip::{SOFT_CLIP_DESCRIPTOR, SoftClipFactory}; -use support::{PARAMETERS, bank_available, initial_values, prepare, process, request, values_from}; +use support::{PARAMETERS, initial_values, prepare, process, request, values_from}; /// E11 — resources, latency, tail and the tap table against the independent `f64` design. #[test] @@ -220,73 +220,6 @@ fn identity_and_bypass_emit_the_delayed_dry_signal_bit_for_bit() { } } -/// Bank binding validates before it declines, and declines rather than failing where the width is -/// not this artifact's. Run at the build's own width: eight lanes in the 8-lane (AVX2) build, four -/// in a 4-lane (NEON/simd128) build (#1112). -#[test] -fn bank_binding_validates_before_declining_an_unavailable_width() { - let width = BankWidth::for_backend(lane::Backend::current()).expect("a vector build"); - let lanes = width.lanes() as usize; - let values = initial_values(); - let requests = vec![request(&values); lanes]; - // Wrong request count for the width. - assert!(matches!( - SoftClipFactory.bind_homogeneous_bank(PrepareEffectBankRequest { - backend: support::backend(width), - width, - requests: &requests[..lanes - 1], - active_mask: width.full_mask(), - }), - Err(EffectPrepareError { - code: "effect.bank.requests" - }) - )); - // Backend and width disagree: every other vector backend this build has, so none in a 4-lane - // build. - for &other in lane::Backend::VECTOR { - if other == support::backend(width) { - continue; - } - assert!(matches!( - SoftClipFactory.bind_homogeneous_bank(PrepareEffectBankRequest { - backend: other, - width, - requests: &requests, - active_mask: width.full_mask(), - }), - Err(EffectPrepareError { - code: "effect.bank.requests" - }) - )); - } - // A mixed cohort is declined, not rejected. - let other = values_from([(6.0, 6.0), (0.0, 0.0), (1.0, 1.0)]); - let mut mixed = vec![request(&values); lanes]; - mixed[lanes / 2] = request(&other); - mixed[lanes / 2].quantum = 64; - assert!( - SoftClipFactory - .bind_homogeneous_bank(PrepareEffectBankRequest { - backend: support::backend(width), - width, - requests: &mixed, - active_mask: width.full_mask(), - }) - .expect("mixed cohort declines") - .is_none() - ); - // The host's own width binds. - let bound = SoftClipFactory - .bind_homogeneous_bank(PrepareEffectBankRequest { - backend: support::backend(width), - width, - requests: &requests, - active_mask: width.full_mask(), - }) - .expect("bind"); - assert_eq!(bound.is_some(), bank_available(width)); -} - /// Automation validation is unchanged: canonical ordered points only. #[test] fn automation_rejects_everything_but_canonical_ordered_points() { diff --git a/crates/source/src/lib.rs b/crates/source/src/lib.rs index 6346f4fb8..7f7ca1f99 100644 --- a/crates/source/src/lib.rs +++ b/crates/source/src/lib.rs @@ -4284,39 +4284,6 @@ mod tests { } } - #[test] - fn graph_driver_forwards_underrun_and_seek_generation_facts() { - let (producer, consumer, _) = session_ring(config(1, 4, 8)).expect("ring"); - let mut host = producer.into_host_chunk_provider(RATE); - let mut driver = test_driver(consumer, vec![test_mapping(0)]); - - driver.begin_block(0, 4).expect("first block"); - assert_eq!( - driver.observation_validity(), - GraphObservationValidity { - source_underrun: true, - source_generation_changed: false, - } - ); - let samples = [1.0; 4]; - host.try_seek(SourceCommand::Seek { - generation: SourceGeneration(2), - frame: SourceFrame(100), - }) - .expect("seek"); - assert!(driver.prepare_source_seek(0, 2, 100)); - host.submit(chunk(2, 100, &[&samples], 4, false)) - .expect("fresh block"); - driver.begin_block(4, 4).expect("second block"); - assert_eq!( - driver.observation_validity(), - GraphObservationValidity { - source_underrun: false, - source_generation_changed: true, - } - ); - } - /// #1318 gate 3. Red if the graph source driver still folds a held block into the graph-wide /// `source_underrun` flag (a planned wait would mark every spectrum window as starved), or if /// the fix swallows a true underrun on another source in the same block. @@ -5655,44 +5622,6 @@ mod tests { assert!(left.iter().chain(&right).all(|word| word.to_bits() == 0)); } - /// Follow-up MINOR 4. Red if a successor with an installed program reports - /// `NotRequested` (or `Carried`) when its predecessor is not a graph plan: it asked for - /// state and got none, so the swap must count a mismatch. - #[test] - fn a_program_facing_a_plain_predecessor_reports_a_mismatch() { - struct Silent; - impl engine::realtime::PreparedPlanExecutor for Silent { - fn render( - &mut self, - _arena: &mut engine::realtime::BufferArena, - mut output: PlanarBufferMut<'_>, - _time: engine::realtime::RenderTime, - ) -> Result<(), engine::realtime::RenderError> { - for channel in 0..output.channels() { - output.plane_mut(channel)?.fill(0.0); - } - Ok(()) - } - } - let validity = Arc::new(AtomicU8::new(0)); - let mut predecessor = PreparedRenderPlan::prepare_with_executor( - engine::realtime::PrepareRenderPlan { - plan_id: 1, - envelope: envelope(), - scratch: &[], - }, - Box::new(Silent), - ) - .expect("plain plan"); - let mut successor = bind(SourceGraphSource::vacant(2), &validity); - graph::install_carry_program(&mut successor, carry_source_zero(1)).expect("install"); - assert_eq!( - successor.adopt_predecessor_plan(&mut predecessor), - CarryOutcome::PredecessorMismatch - ); - assert_eq!(plan_block(&mut successor), silence()); - } - /// Follow-up MINOR 7. Red if the swap block stops comparing channel counts (a one-channel /// consumer would land under two-channel mappings and fail render) or quantum sizes. #[test] @@ -6660,53 +6589,6 @@ mod tests { assert_eq!(consumer.active_generation(), SourceGeneration(3)); } - #[test] - fn the_producer_refuses_without_changing_anything() { - let (mut producer, mut consumer) = timeline(4); - for (command, error) in [ - (seek(0, 5), SourceSeekError::GenerationZero), - ( - seek(1, 5), - SourceSeekError::GenerationNotStrictlyIncreasing { - active: SourceGeneration(1), - requested: SourceGeneration(1), - }, - ), - ( - SourceCommand::SeekAt { - generation: SourceGeneration(2), - frame: SourceFrame(5), - anchor_sample: QU + 1, - }, - SourceSeekError::AnchorUnaligned, - ), - ( - seek(2, TIMELINE_SAMPLE_LIMIT + 1), - SourceSeekError::FrameOutOfRange, - ), - ] { - assert_eq!(producer.check_seek(command), Err(error)); - assert_eq!(producer.try_seek(command), Err(error)); - assert_eq!(producer.active_generation(), SourceGeneration(1)); - } - producer.try_seek(seek(2, TIMELINE_SAMPLE_LIMIT)).unwrap(); - // The one slot is taken: a second seek is backpressure, checked or pushed, and the - // first one still applies. - assert!(matches!( - producer.check_seek(seek(3, 9)), - Err(SourceSeekError::Backpressure { .. }) - )); - assert!(matches!( - producer.try_seek(seek(3, 9)), - Err(SourceSeekError::Backpressure { .. }) - )); - assert_eq!(producer.active_generation(), SourceGeneration(2)); - consumer.begin_block_at(0); - assert_eq!(consumer.position(), TIMELINE_SAMPLE_LIMIT + QU); - assert_eq!(consumer.active_generation(), SourceGeneration(2)); - assert_eq!(producer.check_seek(seek(3, 9)), Ok(())); - } - #[test] fn the_history_reads_both_sides_of_a_seek_and_extrapolates_below_its_oldest_entry() { let (mut producer, mut consumer) = timeline(4); diff --git a/crates/transient-shaper/tests/MUTATIONS.md b/crates/transient-shaper/tests/MUTATIONS.md index 49ddbc030..679296bc2 100644 --- a/crates/transient-shaper/tests/MUTATIONS.md +++ b/crates/transient-shaper/tests/MUTATIONS.md @@ -190,3 +190,10 @@ dropped. The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - `the_scalar_product_is_partition_invariant`: no plausible same-crate KEEP catcher recorded + +## TA5 deletions + +The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. + +- `link_modes_drive_the_detector_as_specified`: no same-crate KEEP catcher recorded +- `the_bank_is_partition_invariant`: transient-shaper `bank_snapshot_restore_and_resets_are_track_local` diff --git a/crates/transient-shaper/tests/bank.rs b/crates/transient-shaper/tests/bank.rs index 1afadc63f..c84e6e8a8 100644 --- a/crates/transient-shaper/tests/bank.rs +++ b/crates/transient-shaper/tests/bank.rs @@ -8,10 +8,8 @@ mod common; use common::*; use effect_contract::{ - BankWidth, EffectBankProcessBlock, EffectProcessBlock, LinkMode, NativeEffectFactory, - PrepareEffectBankRequest, ResetKind, StatePayloadInput, + EffectBankProcessBlock, EffectProcessBlock, LinkMode, ResetKind, StatePayloadInput, }; -use transient_shaper::TransientShaperFactory; /// Per-track parameter sets for state ownership: every lane a different program point. fn track_values(lanes: usize) -> Vec<[effect_contract::InitialParameterValue; 6]> { @@ -128,63 +126,3 @@ fn bank_snapshot_restore_and_resets_are_track_local() { .is_err() ); } - -/// Issue #1092 (console strip P2e): the transient shaper has opted into padding, so the public -/// factory binds a padded bank request of every active count at this build's width, and it still -/// refuses one whose member -- active or padded -- is malformed, with `prepare`'s own code. -/// -/// Red if the #1088 guard comes back (a padded request is declined), or if the padded lanes are no -/// longer validated (a malformed clone binds). `src/padding_tests.rs` holds what the bound bank -/// renders to the padding contract. -#[test] -fn a_padded_request_binds_and_still_validates_every_lane() { - let backend = lane::Backend::current(); - let width = BankWidth::for_backend(backend).expect("every product target banks"); - let lanes = width.lanes() as usize; - let values = initial_values(); - let requests = vec![request(&values); lanes]; - let bind = |requests: &[effect_contract::PrepareEffectRequest<'_>], mask: &[bool]| { - TransientShaperFactory.bind_homogeneous_bank(PrepareEffectBankRequest { - backend, - width, - requests, - active_mask: mask, - }) - }; - assert!( - bind(&requests, width.full_mask()) - .expect("a full bank") - .is_some(), - "the control: the same members bind as a full bank" - ); - for members in 1..lanes { - let mask: Vec = (0..lanes).map(|lane| lane < members).collect(); - let bank = bind(&requests, &mask) - .expect("a padded request is well formed") - .unwrap_or_else(|| panic!("{members} of {lanes} lanes active: the bank binds")); - assert_eq!(bank.metadata.width, width); - } - // Malform a member other than the first, so that a check of the first request alone -- or a - // decision taken above the member loop -- goes red. - let mut malformed = requests.clone(); - malformed[1].limits.maximum_total_state_bytes = 0; - let refusal = TransientShaperFactory - .prepare(malformed[1]) - .err() - .expect("a malformed member") - .code; - let mask: Vec = (0..lanes).map(|lane| lane < 2).collect(); - assert_eq!( - bind(&malformed, &mask).err().map(|error| error.code), - Some(refusal), - "a padded request still validates its members" - ); - let mut malformed = requests.clone(); - malformed[lanes - 1].limits.maximum_total_state_bytes = 0; - let mask: Vec = (0..lanes).map(|lane| lane == 0).collect(); - assert_eq!( - bind(&malformed, &mask).err().map(|error| error.code), - Some(refusal), - "and its padded lanes' clones" - ); -} diff --git a/crates/transient-shaper/tests/contract.rs b/crates/transient-shaper/tests/contract.rs index 520fde858..3815f6f58 100644 --- a/crates/transient-shaper/tests/contract.rs +++ b/crates/transient-shaper/tests/contract.rs @@ -351,51 +351,6 @@ fn identity_rules_are_bit_exact_and_the_followers_still_warm() { } } -/// The three link modes, read straight off the envelope state after one frame. -/// -/// From a zeroed envelope the first rising sample gives `e = attack * 0 + (1 - attack) * u`, which -/// is exactly `(1 - attack) * u` in `f32`, so the detector each channel saw is readable from the -/// state word to the last bit. That pins the linking law itself — including `Average`'s frozen -/// `0.5 * |l| + 0.5 * |r|` operation order — instead of a downstream consequence of it. -/// -/// Red mutation: dispatch `LinkMode::Maximum` to `LINK_AVERAGE`. The two half-scaled products -/// remain the frozen Average law; this row does not require alternative parenthesization to differ. -#[test] -fn link_modes_drive_the_detector_as_specified() { - let values = values_of(1.0, 0.0, 1.0); - let one_minus_attack = 1.0_f32 - f32::from_bits(TRANSIENT_SHAPER_COEFFICIENT_BITS[1][0]); - let half = 0.5_f32; - let expected = [ - (LinkMode::DualMono, 1.0_f32, 0.25_f32), - (LinkMode::Maximum, 1.0, 1.0), - ( - LinkMode::Average, - half * 1.0 + half * 0.25, - half * 1.0 + half * 0.25, - ), - ]; - for (link_mode, left_detector, right_detector) in expected { - let mut effect = prepare_with(&values, 48_000, false, link_mode); - let mut left = [-1.0_f32]; - let mut right = [0.25_f32]; - effect.process( - effect_contract::EffectProcessBlock::new(&mut left, &mut right, None, 0, &[], 128) - .expect("link block"), - ); - let state = snapshot(effect.as_ref()); - assert_eq!( - state_f32(&state.0, 0).to_bits(), - (one_minus_attack * left_detector).to_bits(), - "{link_mode:?} left detector" - ); - assert_eq!( - state_f32(&state.1, 0).to_bits(), - (one_minus_attack * right_detector).to_bits(), - "{link_mode:?} right detector" - ); - } -} - /// A restore is atomic and every word is validated. /// /// Red mutation: accept `remaining > RAMP_SAMPLES`, or drop the `valid_envelope` check. diff --git a/crates/transient-shaper/tests/partition.rs b/crates/transient-shaper/tests/partition.rs deleted file mode 100644 index 10031f7eb..000000000 --- a/crates/transient-shaper/tests/partition.rs +++ /dev/null @@ -1,127 +0,0 @@ -//! Partition invariance (master plan §10 P1): a block boundary must not be observable. -//! -//! The two places a boundary could show are the D11 ramp — whose iterated additions and exact snap -//! `LinearRamp::next_value` performs identically however the frames are grouped — and the ramp -//! prefix, which is recomputed per block from the ramps' own `remaining`. - -mod common; - -use common::*; -use effect_contract::{EffectBankProcessBlock, LinkMode, ParameterChannel}; - -/// Deterministic, integer-built noise with impulses; no platform math anywhere in the signal. -fn signal(seed: u64, frames: usize) -> Vec { - let mut state = seed | 1; - (0..frames) - .map(|index| { - state ^= state >> 12; - state ^= state << 25; - state ^= state >> 27; - let draw = state.wrapping_mul(0x2545_f491_4f6c_dd1d); - if index % 128 == 0 { - return 0.95; - } - ((draw >> 40) as i32 - 8_388_608 / 2) as f32 / 16_777_216.0 - }) - .collect() -} - -const PARTITIONS: [usize; 5] = [1, 7, 64, 128, 512]; - -/// One bank render's result: the two interleaved output blocks and one payload pair per track. -type RenderedBank = (Vec, Vec, Vec<(Vec, Vec)>); - -/// The same property at the build's bank width, where the ramp prefix is the maximum over lanes. -#[test] -fn the_bank_is_partition_invariant() { - let Some((_, width)) = native_bank() else { - println!("no bank width on this build; skipping"); - return; - }; - let lanes = width.lanes() as usize; - let frames = 512; - let planar: Vec<(Vec, Vec)> = (0..lanes) - .map(|track| { - ( - signal(0x1000 + track as u64 * 17, frames), - signal(0x2000 + track as u64 * 23, frames), - ) - }) - .collect(); - let values: Vec<_> = (0..lanes) - .map(|track| values_of(-0.5 + track as f32 * 0.1, 0.75 - track as f32 * 0.1, 1.0)) - .collect(); - let spans: Vec<_> = (0..lanes) - .map(|track| point(ParameterChannel::Left, 0, 0, -0.875 + track as f32 * 0.125)) - .collect(); - let offsets: Vec = (0..=lanes as u32).collect(); - - let render = |block: usize| -> RenderedBank { - let mut bank = bind_native_bank_quantum(&values, LinkMode::Maximum, 512).expect("bank"); - let mut left = vec![0.0_f32; frames * lanes]; - let mut right = vec![0.0_f32; frames * lanes]; - for frame in 0..frames { - for track in 0..lanes { - left[frame * lanes + track] = planar[track].0[frame]; - right[frame * lanes + track] = planar[track].1[frame]; - } - } - let mut start = 0; - while start < frames { - let end = (start + block).min(frames); - let (spans, offsets): (&[_], &[u32]) = if start == 0 { - (&spans, &offsets) - } else { - (&[], &[0; 9][..lanes + 1]) - }; - bank.process_bank( - EffectBankProcessBlock::new( - &mut left[start * lanes..end * lanes], - &mut right[start * lanes..end * lanes], - None, - (end - start) as u32, - width, - start as u64, - spans, - offsets, - 512, - ) - .expect("bank partition block"), - ); - start = end; - } - let sizes = transient_shaper::TRANSIENT_SHAPER_DESCRIPTOR.qualities[1].maximum_state; - let states = (0..lanes) - .map(|track| bank_snapshot(bank.as_ref(), track as u32, sizes)) - .collect(); - (left, right, states) - }; - - let reference = render(frames); - let changed = (0..frames * lanes) - .filter(|index| { - reference.0[*index].to_bits() != planar[index % lanes].0[index / lanes].to_bits() - }) - .count(); - assert!( - changed > frames * lanes / 2, - "only {changed} of {} bank samples moved", - frames * lanes - ); - let reference_left = reference.0.iter().map(|x| x.to_bits()).collect::>(); - let reference_right = reference.1.iter().map(|x| x.to_bits()).collect::>(); - for block in PARTITIONS { - let candidate = render(block); - assert_eq!( - candidate.0.iter().map(|x| x.to_bits()).collect::>(), - reference_left, - "bank left PCM at block size {block}" - ); - assert_eq!( - candidate.1.iter().map(|x| x.to_bits()).collect::>(), - reference_right, - "bank right PCM at block size {block}" - ); - assert_eq!(candidate.2, reference.2, "bank state at block size {block}"); - } -} diff --git a/crates/true-peak-limiter/src/lib.rs b/crates/true-peak-limiter/src/lib.rs index 3a816898a..846ef0307 100644 --- a/crates/true-peak-limiter/src/lib.rs +++ b/crates/true-peak-limiter/src/lib.rs @@ -6642,10 +6642,10 @@ mod tests { /// A core of `L::WIDTH` identically prepared lanes, driven directly through `process_block`. /// - /// The same construction `a_nonfinite_block_is_zeroed_reset_and_counted` uses, and for the same - /// reason: `process_block` *is* the shipped path — `process` and `process_bank` both end in it - /// — and reaching it directly is what lets the control arm suppress the fast path without - /// having to change the signal or the parameters to do it. + /// The same construction `a_nonfinite_block_is_zeroed_reset_and_counted` (deleted by TA5) + /// used, and for the same reason: `process_block` *is* the shipped path — `process` and + /// `process_bank` both end in it — and reaching it directly is what lets the control arm + /// suppress the fast path without having to change the signal or the parameters to do it. fn silent_core(ceiling: f32, release: f32, lookahead: f32) -> LimiterCore { let values = values_with(ceiling, release, lookahead); let mut preparation = request(&values); @@ -7064,37 +7064,6 @@ mod tests { } } - /// **A `-0.0` input block is not silence, and is not skipped.** - /// - /// Issue #182 S2, the input side of the signed-zero rule. Masking the sign bit in - /// `block_is_positive_zero` makes a `-0.0` block count as silence, and a standing claim then - /// engages on it. Every other test in this family stays green under that mutation. - /// - /// This crate's exposure is its delay line, as the compressor's is. A `-0.0` block the kernel - /// actually renders is written into `main_ring` and emerges `B = 486` samples — a little under - /// four blocks — later, with its sign intact: `select(bypass, delayed, delayed * gain)` gives - /// `-0.0 * 1.0 = -0.0` on the limiting arm and `-0.0` on the bypass arm. A fast path that - /// skipped the block never writes it, so the sample that should have emerged is `+0.0`. - /// - /// The `any(.. == 0x8000_0000)` is the anti-vacuity: it asserts a `-0.0` really did come out - /// the far end, so the comparison had the divergence available to it. - #[test] - fn a_negative_zero_input_block_is_not_treated_as_silence() { - let mut plan = vec![None]; - plan.extend(vec![Some(0.0_f32); 40]); - plan.push(Some(-0.0_f32)); - plan.extend(vec![Some(0.0_f32); 16]); - - // The anti-vacuity at the build's own width (issue #1112). - let arm = compare_silence_arms::("native", &plan, -6.0, 100.0, 0.05); - assert!( - arm.rendered.contains(&0x8000_0000), - "no -0.0 ever reached the output, so the comparison had nothing to catch" - ); - compare_silence_arms::("scalar", &plan, -6.0, 100.0, 0.05); - compare_silence_arms::("W4", &plan, -6.0, 100.0, 0.05); - } - /// **A block carrying automation is rendered, not skipped, and the resident tap keeps up.** /// /// Issue #182 S2 at the contract boundary rather than the kernel one: `process` and @@ -7538,40 +7507,6 @@ mod tests { ); } - /// E10: the D7 boundary check replaces every per-value recovery path. - #[test] - fn a_nonfinite_block_is_zeroed_reset_and_counted() { - let values = values_with(-6.0, 100.0, 5.0); - let mut preparation = request(&values); - preparation.link_mode = LinkMode::DualMono; - let metadata = expected_prepared_metadata(&TRUE_PEAK_LIMITER_DESCRIPTOR, preparation) - .expect("metadata"); - let (left_defaults, right_defaults) = initial_defaults(&values).expect("defaults"); - let mut core = LimiterCore::::new( - metadata, - vec![left_defaults].into_boxed_slice(), - vec![right_defaults].into_boxed_slice(), - ) - .expect("core"); - let mut left = vec![0.5_f32; 128]; - let mut right = vec![0.5_f32; 128]; - core.process_block(&mut left, &mut right, 128); - assert_eq!(core.nonfinite_report().nonfinite_blocks, 0); - - core.left.reduction[0] = f32::NAN; - let mut left = vec![0.5_f32; 128]; - let mut right = vec![0.5_f32; 128]; - core.process_block(&mut left, &mut right, 128); - assert!(left.iter().all(|sample| sample.to_bits() == 0)); - assert!(right.iter().all(|sample| sample.to_bits() == 0)); - assert_eq!(core.nonfinite_report().nonfinite_blocks, 1); - assert_eq!(core.nonfinite_report().nonfinite_lanes, 1); - assert_eq!(core.cursors, Cursors::default()); - assert_eq!(core.left.reduction[0].to_bits(), 0); - assert!(core.left.required_ring.iter().all(|value| *value == 1.0)); - assert_eq!(core.left.box_sum[0], core.left.lane[0].window as f32); - } - /// E11: the current layout round-trips, and every corruption is rejected without mutating the peer. #[test] fn state_round_trips_and_rejects_corruption() { diff --git a/crates/true-peak-limiter/tests/MUTATIONS.md b/crates/true-peak-limiter/tests/MUTATIONS.md index f82d9df10..9f5ebe3d7 100644 --- a/crates/true-peak-limiter/tests/MUTATIONS.md +++ b/crates/true-peak-limiter/tests/MUTATIONS.md @@ -27,7 +27,7 @@ cargo test -p true-peak-limiter --lib phase_outputs | 6 | `flush(target.max(released))` → `target.max(released)` | `src/lib.rs` `channel_frame` | `silence_restores_exact_identity_including_signed_zero` | | 7 | van Herk suffix pass guarded by `complete && width < 2`, so it never runs at W4/W8 | `src/lib.rs` `sliding_minimum` | `lane_identity_holds_across_widths` | | 8 | `left.prefix.fill(1.0)` / `right.prefix.fill(1.0)` at block entry (min-filter prefix not carried across blocks) | `src/lib.rs` `limiter_block` | `partition_invariance_holds_over_block_sizes` (deleted by TA4) | -| 9 | `return;` inserted after `limiter_block` so the §4.4 boundary check never runs | `src/lib.rs` `LimiterCore::process_block` | `a_nonfinite_block_is_zeroed_reset_and_counted` | +| 9 | `return;` inserted after `limiter_block` so the §4.4 boundary check never runs | `src/lib.rs` `LimiterCore::process_block` | `a_nonfinite_block_is_zeroed_reset_and_counted` (deleted by TA5) | | 10 | the `recomputed == box_sum` check in restore disabled | `src/lib.rs` `read_lane` | `state_round_trips_and_rejects_corruption` | | 11 | `peak = history[6].abs()` → `peak = L::zero()` (sample term dropped from `P`) | `src/lib.rs` `detector_peak` | `fixed_latency_guarded_ceiling_and_bypass_bits_hold` | | 12 | `box_sum.div(window)` → `box_sum.mul(1.0.div(window))` (reciprocal instead of divide) | `src/lib.rs` `channel_frame` | `silence_restores_exact_identity_including_signed_zero` | @@ -146,7 +146,7 @@ Every row below is a **bit** test. The claims here are about *when* a state word | # | mutation | file | test that turned red | |---|---|---|---| | round2-1 | the block-end write-back of `prefix` dropped | `src/lib.rs` `limiter_block_uniform` | `a_mixed_lookahead_cohort_falls_back_bit_identically`, `a_restore_that_desyncs_the_phase_falls_back`, `fixed_latency_guarded_ceiling_and_bypass_bits_hold`, `lane_identity_holds_across_widths`, `partition_invariance_holds_over_block_sizes` (deleted by TA4) | -| round2-2 | the block-end write-back of `phase` dropped, so the cohort restarts each block at the phase it entered the previous one with | `src/lib.rs` `limiter_block_uniform` | `a_de_zipper_window_open_across_a_block_boundary_refuses_the_claim`, `a_mixed_lookahead_cohort_falls_back_bit_identically`, `a_negative_zero_input_block_is_not_treated_as_silence`, `a_restore_that_desyncs_the_phase_falls_back`, `a_restore_withdraws_the_silence_claim`, `a_settled_silent_limiter_renders_exactly_the_never_fast_path` | +| round2-2 | the block-end write-back of `phase` dropped, so the cohort restarts each block at the phase it entered the previous one with | `src/lib.rs` `limiter_block_uniform` | `a_de_zipper_window_open_across_a_block_boundary_refuses_the_claim`, `a_mixed_lookahead_cohort_falls_back_bit_identically`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5), `a_restore_that_desyncs_the_phase_falls_back`, `a_restore_withdraws_the_silence_claim`, `a_settled_silent_limiter_renders_exactly_the_never_fast_path` | | round2-3 | `.min(ring - left_end)` dropped from the segment run | `src/lib.rs` `segment` | `the_segment_walk_visits_the_slots_a_frame_at_a_time_walk_visits` — **and nothing else** | | round2-4 | `.min(ring - left_expiring)` dropped from the segment run | `src/lib.rs` `segment` | `the_segment_walk_visits_the_slots_a_frame_at_a_time_walk_visits` — **and nothing else** | | round2-5 | `.min(ring - start)` dropped from the segment run | `src/lib.rs` `segment` | six render tests, including `a_settled_silent_limiter_renders_exactly_the_never_fast_path` and `a_restore_that_desyncs_the_phase_falls_back` | @@ -240,9 +240,9 @@ the recursive word, the cursors, the phases and all four ramps — are compared, | # | mutation | file | test that turned red | |---|---|---|---| -| 182-6 | input `block_is_positive_zero` legs dropped from the admission test | `src/lib.rs` `LimiterCore::process_block` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path`, `a_negative_zero_input_block_is_not_treated_as_silence`, `a_stale_detector_history_refuses_the_claim` | +| 182-6 | input `block_is_positive_zero` legs dropped from the admission test | `src/lib.rs` `LimiterCore::process_block` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5), `a_stale_detector_history_refuses_the_claim` | | 182-7 | `block_is_positive_zero(&self.reduction)` dropped from `is_at_silent_rest` | `src/lib.rs` `ChannelState::is_at_silent_rest` | `a_limiter_still_releasing_through_the_silence_is_never_frozen` | -| 182-8 | `block_is_positive_zero(&self.main_ring)` dropped | `src/lib.rs` `ChannelState::is_at_silent_rest` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path`, `a_negative_zero_input_block_is_not_treated_as_silence` | +| 182-8 | `block_is_positive_zero(&self.main_ring)` dropped | `src/lib.rs` `ChannelState::is_at_silent_rest` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5) | | 182-9 | `block_is_positive_zero(&self.history)` dropped | `src/lib.rs` `ChannelState::is_at_silent_rest` | `a_stale_detector_history_refuses_the_claim` — **and nothing else** | | 182-10 | output `block_is_positive_zero` legs dropped from the claim | `src/lib.rs` `LimiterCore::process_block` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path` | | 182-11 | `self.cursors.advance(frames, &self.shape)` deleted from the fast path | `src/lib.rs` `LimiterCore::process_block` | `a_settled_silent_limiter_...`, `a_negative_zero_input_...`, `a_stale_detector_history_...` (via the state comparison, not the sample comparison) | @@ -647,3 +647,10 @@ lane 7; the session test's `ch48` row still owns E0 and EL at `Simd8`. The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - `partition_invariance_holds_over_block_sizes`: no plausible same-crate KEEP catcher recorded + +## TA5 deletions + +The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. + +- `a_nonfinite_block_is_zeroed_reset_and_counted`: no same-crate KEEP catcher recorded +- `a_negative_zero_input_block_is_not_treated_as_silence`: true-peak-limiter `the_bank_renders_its_scalar_instances_under_random_state` diff --git a/hosts/host-web/src/ffi.rs b/hosts/host-web/src/ffi.rs index 5df036851..c87571d0b 100644 --- a/hosts/host-web/src/ffi.rs +++ b/hosts/host-web/src/ffi.rs @@ -4421,18 +4421,6 @@ pub(crate) fn test_buffer_address(handle: u32, kind: u32) -> usize { with_host_mut(handle, 0, |host| buffer_pointer(host, kind).addr()) } -/// The address `miso_engine_web_v1_source_seek_report_ptr` returns before its `u32` cast (a -/// native address does not fit it), and the record's own address. -#[cfg(test)] -pub(crate) fn test_seek_report_addresses(handle: u32) -> (usize, usize) { - with_host(handle, (0, 0), |host| { - ( - seek_report_pointer(host).addr(), - ptr::from_ref(host.seek_report()).addr(), - ) - }) -} - #[cfg(test)] pub(crate) fn test_status(handle: u32) -> Option { with_host(handle, None, |host| Some(*host.status())) diff --git a/hosts/host-web/src/tests.rs b/hosts/host-web/src/tests.rs index 68561b56a..85f66811b 100644 --- a/hosts/host-web/src/tests.rs +++ b/hosts/host-web/src/tests.rs @@ -1423,84 +1423,6 @@ fn the_anchored_seek_exports_play_at_the_anchor_and_report_where_it_landed() { ); } -/// Issue #1293 gate 5: on a host that is not ready (no control half), `source_seek_at` and -/// `source_seek_report` refuse with `RESULT_REFUSED_LIFECYCLE`, the report record keeps its last -/// words, and a wrong handle or an ID count past the staging is `RESULT_INVALID_ARGUMENT`. -/// -/// Test value: red if either export runs on a host with no control half (it would reach for -/// control state the instance does not hold) or overwrites the record on a refusal, if the -/// pointer export names anything but the record while ready or anything at all once not ready, -/// or if the report export records a staged-ID refusal as the last result (it records none). -#[test] -fn the_seek_exports_refuse_a_host_with_no_control_half_and_change_nothing() { - let rig = SeekExports::boot(); - assert_eq!(rig.seek_at(2, 10_000, 256), RESULT_OK); - rig.submit(2, 10_000); - let _ = rig.render(); - let (code, before) = rig.report(); - assert_eq!(code, RESULT_OK); - // A ready host's pointer export names the record. - let (pointer, record) = crate::ffi::test_seek_report_addresses(rig.handle); - assert!(record != 0 && pointer == record); - - // Argument checks on a live host. - assert_eq!( - miso_engine_web_v1_source_seek_at(0, 14, 3, 0, 512), - RESULT_INVALID_ARGUMENT - ); - assert_eq!( - miso_engine_web_v1_source_seek_report(0, 14), - RESULT_INVALID_ARGUMENT - ); - assert_eq!( - miso_engine_web_v1_source_seek_at(rig.handle, u32::MAX, 3, 0, 512), - RESULT_INVALID_ARGUMENT - ); - // The anchored seek records its refusal; the report leaves the last result alone. - assert_eq!( - crate::ffi::test_status(rig.handle) - .expect("status") - .last_result, - RESULT_INVALID_ARGUMENT - ); - assert_eq!(rig.seek_at(3, 0, 1024), RESULT_OK); - assert_eq!( - miso_engine_web_v1_source_seek_report(rig.handle, u32::MAX), - RESULT_INVALID_ARGUMENT - ); - assert_eq!( - crate::ffi::test_status(rig.handle) - .expect("status") - .last_result, - RESULT_OK - ); - assert_eq!( - crate::ffi::native_staging::seek_report(rig.handle), - Some(before) - ); - - // An output-shape mismatch makes the host sticky-failed: no control half to run on. - assert_eq!( - miso_engine_web_v1_render(rig.handle, 0), - RESULT_REPREPARE_REQUIRED - ); - assert_eq!( - crate::ffi::test_status(rig.handle).expect("status").state, - STATE_FAILED - ); - assert_eq!(rig.seek_at(3, 0, 1024), RESULT_REFUSED_LIFECYCLE); - assert_eq!(rig.report().0, RESULT_REFUSED_LIFECYCLE); - assert_eq!( - crate::ffi::native_staging::seek_report(rig.handle), - Some(before), - "a refusal leaves the record alone" - ); - // A host that is not ready has no record address to give. - let (pointer, record) = crate::ffi::test_seek_report_addresses(rig.handle); - assert!(record != 0 && pointer == 0); - assert_eq!(miso_engine_web_v1_source_seek_report_ptr(0), 0); -} - #[test] fn output_mismatch_and_disposal_are_sticky_and_idempotent() { let mut host = prepared_host(64); @@ -3347,29 +3269,6 @@ fn meter_spans_cover_all_launch_rates_and_a_nine_track_tail() { } } -#[test] -fn source_seek_keeps_the_meter_epoch_and_absolute_peak_clock() { - let mut host = live_control_host(128, 1); - assert_eq!(host.set_meter_lease(true), RESULT_OK); - feed_and_render(&mut host, 1, 0, 0.4); - assert_eq!(host.poll_meters(), 1); - let meter_generation = host.meter_header().reserved[0]; - - assert_eq!(host.seek_source(b"fixture-source", 2, 0), RESULT_OK); - let plane = vec![0.7_f32; 128]; - let planes: [&[f32]; 2] = [&plane, &plane]; - assert_eq!( - host.submit_source(b"fixture-source", 2, 0, 48_000, &planes, 128, false), - RESULT_OK - ); - assert_eq!(host.render_next(), RESULT_OK); - assert_eq!(host.poll_meters(), 1); - assert_eq!(host.meter_header().reserved[0], meter_generation); - assert_eq!(host.meter_header().first_sample, 128); - assert_eq!(host.meter_header().end_sample, 256); - assert_eq!(&host.meter_frame()[..4], &[0.7, 0.7, 0.7, 0.7]); -} - #[test] fn meter_lease_reacquisition_waits_for_a_clean_boundary() { let mut host = live_control_host(128, 2); @@ -4067,94 +3966,6 @@ fn selected_observation_reads_are_bounded_stable_and_non_consuming() { ); } -#[test] -fn selected_observation_reads_keep_same_track_owner_windows_and_sequences() { - const QUANTUM: u32 = 128; - let mut host = same_track_observation_host(QUANTUM); - let compressor = ObservationSelection { - track_id: "t0", - rack: LiveEffectRack::Inserts, - effect_slot_id: "comp", - tap_id: 1, - channels: ObservationReadChannels::Both, - }; - let gate = ObservationSelection { - track_id: "t0", - rack: LiveEffectRack::Inserts, - effect_slot_id: "gate", - tap_id: 1, - channels: ObservationReadChannels::Both, - }; - assert_eq!(observe(&mut host, 0, 1, 0, 1, 2, true), RESULT_OK); - assert_eq!(observe(&mut host, 0, 1, 1, 1, 3, true), RESULT_OK); - for block in 0..3 { - feed_and_render_channels(&mut host, block, 0.75, 0.125); - } - - let (compressor_window, compressor_consumed) = { - let ready = host.ready.as_ref().expect("ready ownership"); - let (effect, tap) = resolve_observation(ready, &compressor).expect("compressor address"); - let reader = &host - .test_observation_handle(effect) - .expect("compressor observation handle") - .readers[tap]; - ( - reader.read().expect("compressor published window"), - reader.consumed_sequence(), - ) - }; - let (gate_window, gate_consumed) = { - let ready = host.ready.as_ref().expect("ready ownership"); - let (effect, tap) = resolve_observation(ready, &gate).expect("gate address"); - let reader = &host - .test_observation_handle(effect) - .expect("gate observation handle") - .readers[tap]; - ( - reader.read().expect("gate published window"), - reader.consumed_sequence(), - ) - }; - assert_eq!(compressor_window.blocks, 2); - assert_eq!(gate_window.blocks, 3); - assert_ne!(compressor_window.left, compressor_window.right); - assert_ne!(gate_window.left, gate_window.right); - assert_eq!(compressor_consumed, 0); - assert_eq!(gate_consumed, 0); - - let rows = host - .read_observations(&[compressor, gate]) - .expect("same-track two-effect selected read"); - assert_eq!(rows.len(), 2); - assert_eq!(rows[0].effect_slot_id.as_ref(), "comp"); - assert_eq!(rows[1].effect_slot_id.as_ref(), "gate"); - assert_eq!(rows[0].window, Some(compressor_window)); - assert_eq!(rows[1].window, Some(gate_window)); - assert_eq!(rows[0].left, Some(compressor_window.left)); - assert_eq!(rows[0].right, Some(compressor_window.right)); - assert_eq!(rows[1].left, Some(gate_window.left)); - assert_eq!(rows[1].right, Some(gate_window.right)); - - let (compressor_after, gate_after) = { - let ready = host.ready.as_ref().expect("ready ownership"); - let (compressor_effect, compressor_tap) = - resolve_observation(ready, &compressor).expect("compressor address"); - let (gate_effect, gate_tap) = resolve_observation(ready, &gate).expect("gate address"); - ( - host.test_observation_handle(compressor_effect) - .expect("compressor observation handle") - .readers[compressor_tap] - .consumed_sequence(), - host.test_observation_handle(gate_effect) - .expect("gate observation handle") - .readers[gate_tap] - .consumed_sequence(), - ) - }; - assert_eq!(compressor_after, compressor_consumed); - assert_eq!(gate_after, gate_consumed); -} - #[test] fn selected_observation_numeric_read_refuses_failed_host_without_touching_output() { const QUANTUM: u32 = 128; diff --git a/hosts/host-web/tests/control_parity.rs b/hosts/host-web/tests/control_parity.rs index 53fb7e85e..9eca1ba0d 100644 --- a/hosts/host-web/tests/control_parity.rs +++ b/hosts/host-web/tests/control_parity.rs @@ -2112,133 +2112,6 @@ fn a_stale_effect_row_edit_is_refused_alike_on_both_hosts() { } } -/// #1623 (root ruling, thick core): a `RESPONSE_GET` frame whose value the Rust encoder refuses -/// -- a NaN or infinite axis endpoint, a NaN override value, a bypass value between 0 and 1 -- is -/// decoded as sent and refused by the core on both hosts, with one result, the same reply bytes -/// and the same typed code (`response.grid`, `response.override.value`, -/// `response.override.bypass`), and changes nothing: the control rows and the document are the -/// bytes they were on each host. -/// -/// Test value: red if either host's codec refuses the value again (the code becomes the generic -/// `protocol.failure`), if one host checks a value the other does not, or if a refusal changes -/// state. -#[test] -fn a_response_request_with_a_bad_value_is_refused_alike_on_both_hosts() { - let request = protocol::ResponseGetRequest { - target: protocol::ResponseTarget::Configuration(protocol::ResponseConfiguration::Effect { - effect: "miso.parametric-eq".to_owned(), - quality: session::EffectQuality::Normal, - link_mode: session::LinkMode::DualMono, - }), - grid: protocol::ResponseGrid::Logarithmic, - points: 64, - minimum_hz: 20.0, - maximum_hz: 20_000.0, - channels: protocol::ParameterChannel::Both, - sections: false, - overrides: vec![ - protocol::ResponseOverride { - control: protocol::RESPONSE_BYPASS_CONTROL, - lane: protocol::ParameterChannel::Both, - value: 1.0, - }, - protocol::ResponseOverride { - control: 4, - lane: protocol::ParameterChannel::Both, - value: 3.25, - }, - ], - configuration_id: None, - sample_rate_hz: None, - quantum_frames: None, - maximum_prepared_bytes: None, - maximum_state_bytes: None, - maximum_scratch_bytes: None, - maximum_automation_spans: None, - maximum_reply_bytes: None, - }; - let model = model_at(48_000); - let mut browser = Rig::new(&model); - let mut c_abi = CAbi::new(&model); - // One frame, sent to both hosts, with a request ID past both legs' counters (the C ABI leg - // pages its control dumps in more frames), which the C ABI leg's counter then takes: each host - // sees ascending IDs, and both see the same frame. - let next_frame = |browser: &mut Rig, c_abi: &mut CAbi| loop { - let frame = browser.encode(CommandPayload::ResponseGet(&request), ExpectedRevision::Any); - let id = u64::from_le_bytes(frame[24..32].try_into().expect("request ID")); - if id > c_abi.request { - c_abi.request = id; - break frame; - } - }; - let frame = next_frame(&mut browser, &mut c_abi); - // The unpatched request is answered alike. - let (b, c) = (browser.send_frame(&frame), c_abi.send_raw(&frame)); - assert_eq!(b, c, "one answer to the unpatched request"); - assert_eq!(b.0, C_RESULT_OK); - let before_b = dump(|payload, expected| browser.command(payload, expected)); - let before_c = dump(|payload, expected| c_abi.send(payload, expected)); - assert_eq!(before_b, before_c); - let patched = |frame: Vec, from: f32, to: f32| { - let from = from.to_le_bytes(); - let at: Vec = frame - .windows(4) - .enumerate() - .filter(|(_, window)| *window == from) - .map(|(at, _)| at) - .collect(); - assert_eq!(at.len(), 1, "the value is in the frame exactly once"); - let mut bytes = frame; - bytes[at[0]..at[0] + 4].copy_from_slice(&to.to_le_bytes()); - bytes - }; - for (name, from, to, code) in [ - ("a NaN minimum", 20.0, f32::NAN, "response.grid"), - ( - "an infinite maximum", - 20_000.0, - f32::INFINITY, - "response.grid", - ), - ( - "a NaN override value", - 3.25, - f32::NAN, - "response.override.value", - ), - ("a bypass of one half", 1.0, 0.5, "response.override.bypass"), - ] { - let bytes = patched(next_frame(&mut browser, &mut c_abi), from, to); - let (b, c) = (browser.send_frame(&bytes), c_abi.send_raw(&bytes)); - assert_eq!(b, c, "{name}: one answer on both hosts"); - assert_eq!(b.0, C_RESULT_OK, "{name}: a refusal is a non-OK frame"); - let mut fields = [0_u16; 1024]; - let DecodedTypedResponseFrame::NonOk { header, payload } = ProtocolCodec::default() - .decode_typed_response(&b.1, &mut DecodeScratch::new(&mut fields)) - .expect("a typed response") - else { - panic!("{name}: expected a refusal"); - }; - assert_eq!(header.status, protocol::StatusCode::InvalidField, "{name}"); - let codes: Vec<_> = payload - .diagnostics - .iter() - .map(|d| d.code.as_str()) - .collect(); - assert_eq!(codes, [code], "{name}"); - assert_eq!( - dump(|payload, expected| browser.command(payload, expected)), - before_b, - "{name}: the browser host is unchanged" - ); - assert_eq!( - dump(|payload, expected| c_abi.send(payload, expected)), - before_c, - "{name}: the C ABI is unchanged" - ); - } -} - /// Block the bypass of `a_live_bypass_crossfades_alike_on_both_hosts` is submitted before. const BYPASS_BLOCK: u64 = 6; @@ -3176,25 +3049,8 @@ fn a_moved_target_defers_its_jump_alike_on_both_hosts() { } } -/// #1711 U1 fold 1 (MAJOR-1) across hosts: on a fader ride flat at -3 dB, a hand move with its -/// own 4,000 samples heard 3 blocks, then a table-only rebuild to -5 dB up to 2,000, a linear -8 -/// to -14 dB over 2,000 to 8,000, then -14 dB. The moved target defers the cell's jump to the -/// move's first free sample (4,512), and the table's step at 2,000, inside the move's ramp, -/// replaces the ramp the deferral waits for. Both hosts give one verdict and reply per edit, the -/// same bits in every block and the same watermark (`EXACT`, no transition fallback), and every -/// block equals the pair of rigs prepared with -3 dB before 2,000 (the same table after it), -/// given the same move and, at the same block, a table-only rebuild that moves nothing (a split -/// at 950,000), at the four rates. Both rebuilds keep the hand move's stored offset. -/// -/// Test value: red if a discontinuity jump on a restartable cell leaves the pending deferral in -/// place (its grid hold freezes the lane on the step's target until 4,512), on either host. -#[test] -fn a_table_step_inside_a_deferred_moves_ramp_lands_alike_on_both_hosts() { - table_step_inside_a_deferred_move(model_at); -} - /// #1655 U3 fold 1 (MAJOR-1) across hosts: the composed twin of -/// [`a_table_step_inside_a_deferred_moves_ramp_lands_alike_on_both_hosts`]. `t1` is also in VCA +/// `a_table_step_inside_a_deferred_moves_ramp_lands_alike_on_both_hosts` (deleted by TA5). `t1` is also in VCA /// `v`, whose `offset_db` rides linearly from 0 to -6 dB over 48,000 samples in every plan, so /// `t1`'s fader channels are composed lanes over its own ride and the VCA's. The adoption holds /// and defers every cursor; the own table's step at 2,000 replaces the move's ramp on the whole @@ -3223,8 +3079,8 @@ fn a_table_step_inside_a_deferred_moves_ramp_releases_a_composed_lane_alike_on_b }); } -/// The body of [`a_table_step_inside_a_deferred_moves_ramp_lands_alike_on_both_hosts`] over the -/// session `model_of(rate)`. +/// The table step inside a deferred move's ramp (#1711 U1 fold 1), over the session +/// `model_of(rate)`; the composed twin above is its one caller since TA5 deleted the plain case. fn table_step_inside_a_deferred_move(model_of: fn(u32) -> SessionModel) { let segment = |shape, start, end, from, to| session::AutomationSegment { shape, diff --git a/tools/bench-support/src/json.rs b/tools/bench-support/src/json.rs index 56ccedb67..54f68dca7 100644 --- a/tools/bench-support/src/json.rs +++ b/tools/bench-support/src/json.rs @@ -84,12 +84,6 @@ mod tests { ); } - #[test] - fn serializes_empty_and_empty_element_arrays() { - assert_eq!(json_string_array(&[]), "[]"); - assert_eq!(json_string_array(&[String::new()]), "[\"\"]"); - } - #[test] fn serializes_ordered_duplicate_elements() { let values = ["first", "first", "second"] diff --git a/tools/console-workload/tests/automation.rs b/tools/console-workload/tests/automation.rs index e3704142a..df911198f 100644 --- a/tools/console-workload/tests/automation.rs +++ b/tools/console-workload/tests/automation.rs @@ -180,9 +180,8 @@ mod mixing { use console_workload::SessionRuntime; use console_workload::mixing_automation::{ - self, Arm, CONTROLS, Effect, Lowering, MixingArm, MixingAutomation, ResolveError, + self, Arm, CONTROLS, Lowering, MixingAutomation, ResolveError, }; - use effect_contract::ParameterChannel; use lane::Backend; /// Every control resolves by `(track_id, effect contract id)` to its own channel, and names @@ -312,84 +311,4 @@ mod mixing { let restated = preflight.collapse(Arm::Restated, None); assert!(restated[0] <= quiet[0]); } - - /// The EQ is pushed as the SDK pushes it: a Left edit then a Right edit, as two - /// `push_parameter` calls. Both landing before a block reads them keep the collapse, because - /// the target cells hold the latest word per channel and the block sees two equal words - /// (VERIFY-AUTOMATION F2, issue #1345). A block that runs between the two edits reads one - /// channel changed and retires the three EQ cohorts, and they stay retired for the whole - /// preroll: the lane's words differ from then on, so no block of it runs collapsed. - #[test] - fn the_eq_lowering_keeps_the_collapse_when_a_pair_lands_in_one_block() { - let backend = Backend::current(); - let eq_both = MixingArm::prepare(Arm::Restated, Some(Effect::Eq), backend); - let quiet = MixingArm::prepare(Arm::Quiet, None, backend); - assert_eq!( - eq_both.runtime.bank_collapse_counters(), - quiet.runtime.bank_collapse_counters(), - "a Both EQ restatement keeps every cohort collapsed" - ); - let [kept, cohorts] = quiet.runtime.bank_collapse_counters(); - let mut together = SessionRuntime::build_with_dispatch( - mixing_automation::WORKLOAD, - mixing_automation::CONFIG, - backend, - ); - let mut apart = SessionRuntime::build_with_dispatch( - mixing_automation::WORKLOAD, - mixing_automation::CONFIG, - backend, - ); - let automation = MixingAutomation::resolve(&together).expect("controls"); - let eq_controls: Vec<_> = automation - .controls() - .iter() - .filter(|control| control.control.effect == Effect::Eq) - .collect(); - for control in &eq_controls { - for side in [ParameterChannel::Left, ParameterChannel::Right] { - assert!(together.push_parameter( - control.channel, - control.control.parameter_index, - side, - control.base - )); - } - } - for block in 0..mixing_automation::PREROLL_BLOCKS { - together.render(block).expect("render"); - } - let [together_kept, _] = together.bank_collapse_counters(); - assert_eq!( - together_kept, kept, - "a Left and a Right edit before one block keep all {cohorts} cohorts collapsed" - ); - for control in &eq_controls { - assert!(apart.push_parameter( - control.channel, - control.control.parameter_index, - ParameterChannel::Left, - control.base - )); - } - apart.render(0).expect("render"); - for control in &eq_controls { - assert!(apart.push_parameter( - control.channel, - control.control.parameter_index, - ParameterChannel::Right, - control.base - )); - } - for block in 1..mixing_automation::PREROLL_BLOCKS { - apart.render(block).expect("render"); - } - let [apart_kept, _] = apart.bank_collapse_counters(); - assert_eq!( - apart_kept, - kept - 3 * mixing_automation::PREROLL_BLOCKS, - "a block between the edits retires the three EQ cohorts of the {cohorts} for the whole \ - preroll" - ); - } } diff --git a/tools/console-workload/tests/paired_spans.rs b/tools/console-workload/tests/paired_spans.rs index 707710ed2..eaf603a3a 100644 --- a/tools/console-workload/tests/paired_spans.rs +++ b/tools/console-workload/tests/paired_spans.rs @@ -1410,11 +1410,6 @@ fn the_limiter_bank_renders_the_dual_bits_in_every_scenario() { run_all(&[Fx::Limiter]); } -#[test] -fn the_chained_strip_renders_the_dual_bits_in_every_scenario() { - run_all(&[Fx::Eq, Fx::Compressor, Fx::Limiter]); -} - /// Every order of a mixed drain on the chained strip: one-channel, `Both` and paired writes in /// one block, across the EQ's target cells and the compressor's and the limiter's parameter /// cells. diff --git a/tools/realtime-policy/tests/policy/calls.rs b/tools/realtime-policy/tests/policy/calls.rs index 14355d49f..003d6fb63 100644 --- a/tools/realtime-policy/tests/policy/calls.rs +++ b/tools/realtime-policy/tests/policy/calls.rs @@ -253,46 +253,6 @@ fn an_iterator_that_pops_is_refused() { refuses("calls-iterator-count", &with_count, &[ITERATOR]); } -#[test] -fn every_iterator_trait_and_every_rename_of_one_is_refused() { - for (name, header) in [ - ("double-ended", "impl DoubleEndedIterator for Lane"), - ("into", "impl IntoIterator for Lane"), - ("path", "impl core::iter::Iterator for Lane"), - ("raw", "impl r#Iterator for Lane"), - ] { - let text = marked(&format!( - "{header} {{\n fn next_back(&mut self) -> Option {{\n self.control.try_pop().ok()\n }}\n}}\n" - )); - refuses(&format!("calls-trait-{name}"), &text, &[ITERATOR]); - } - // A `use` renames the trait: the impl names the new spelling, in another file. - let tree = super::common::Tree::base("calls-trait-renamed"); - tree.apply(&[ - Edit::Create { - path: "crates/pops/src/lib.rs", - text: "mod alias;\nmod model;\n", - }, - Edit::Create { - path: "crates/pops/src/alias.rs", - text: "use core::iter::Iterator as Walk;\n", - }, - Edit::Create { - path: "crates/pops/src/model.rs", - text: &marked( - "impl Walk for Lane {\n fn next(&mut self) -> Option {\n self.control.try_pop().ok()\n }\n}\n", - ), - }, - ]); - let report = tree.check(); - assert_eq!( - report.classes(), - [ITERATOR].into_iter().collect(), - "{:#?}", - report.findings - ); -} - #[test] fn an_iterator_that_does_not_pop_and_a_pop_in_another_trait_are_accepted() { let quiet = "impl Iterator for Lane {\n type Item = Record;\n fn next(&mut self) -> Option {\n self.items.pop()\n }\n}\n"; diff --git a/tools/realtime-policy/tests/policy/drain/forms.rs b/tools/realtime-policy/tests/policy/drain/forms.rs index 5476730d5..d0f01068f 100644 --- a/tools/realtime-policy/tests/policy/drain/forms.rs +++ b/tools/realtime-policy/tests/policy/drain/forms.rs @@ -2,12 +2,10 @@ //! region (D1), the scope that resolves a count (D2), the syntax it visits (D4) and what counts as //! a pop (D5). -use realtime_policy::Class; - use super::super::common::{ BASE_FILES, BASE_REGIONS, BEGIN, END, Edit, expect_failure, expect_valid, }; -use super::{ANCHOR, BUILTINS_COMPILER, DRAIN, accepts, each_row, refuses}; +use super::{DRAIN, accepts, each_row, refuses}; const BUFFER: &str = "crates/engine/src/realtime/buffer.rs"; @@ -237,22 +235,6 @@ fn a_pop_below_a_node_outside_the_allow_list_is_refused() { each_row(rows, |name, text| refuses(name, &text)); } -/// D4: `Expr::Async` is not on the list. The `async` keyword is forbidden too (A2), so both -/// classes report. -#[test] -fn a_counted_drain_in_an_async_block_is_refused_twice() { - let text = "fn drain(control: &mut Consumer) {\n let _ = async {\n let available = control.available_at_entry();\n for _ in 0..available {\n let Ok(record) = control.try_pop() else {\n break;\n };\n apply(record);\n }\n };\n}"; - expect_failure( - "drain-in-async-block", - &[Edit::ReplaceLine { - path: BUILTINS_COMPILER, - old: ANCHOR, - new: text, - }], - &[Class::ForbiddenBody, DRAIN], - ); -} - /// D5: a raw-identifier method name is the same method. #[test] fn a_raw_identifier_pop_is_a_pop() { @@ -262,21 +244,6 @@ fn a_raw_identifier_pop_is_a_pop() { ); } -/// D3: a loop outside a nested `fn` counts as around a pop inside it. -#[test] -fn a_pop_in_a_nested_function_is_judged_by_the_loops_outside_it() { - // The loop also calls the nested function, which pops: the call rule (#1444) refuses that too. - expect_failure( - "pop-in-a-nested-function-in-an-unbounded-loop", - &[Edit::ReplaceLine { - path: super::BUILTINS_COMPILER, - old: super::ANCHOR, - new: "fn drain(control: &mut Consumer) {\n loop {\n fn helper(control: &mut Consumer) {\n let _ = control.try_pop();\n }\n helper(control);\n break;\n }\n}", - }], - &[DRAIN, Class::PoppingCall], - ); -} - /// D1: every node the rule reads lies in the pop's region. The same drain passes when its count /// binding is inside the region. #[test] diff --git a/tools/realtime-policy/tests/policy/drain/twins.rs b/tools/realtime-policy/tests/policy/drain/twins.rs index 06281e6af..622fd4205 100644 --- a/tools/realtime-policy/tests/policy/drain/twins.rs +++ b/tools/realtime-policy/tests/policy/drain/twins.rs @@ -13,15 +13,6 @@ fn a_single_pop_in_a_method_chain_passes() { ); } -/// `acquire_current_block`: two counted phases with a re-observe between them. -#[test] -fn the_two_phase_acquire_passes() { - accepts( - "twin-two-phase-acquire", - "impl Consumer {\n fn acquire(&mut self, clock: Clock) {\n if self.current.is_some() {\n return;\n }\n let available = self\n .data_consumer\n .available_at_entry()\n .min(self.transfer_block_count);\n let mut popped = 0_usize;\n let mut unobserved = None;\n for _ in 0..available {\n let Ok(block) = self.data_consumer.try_pop() else {\n break;\n };\n popped += 1;\n if self.is_unobserved(block.generation) {\n unobserved = Some(block);\n break;\n }\n if self.settle(block) {\n return;\n }\n }\n let Some(block) = unobserved else {\n return;\n };\n self.observe(clock);\n if self.settle(block) {\n return;\n }\n let unpopped = available.saturating_sub(popped);\n let rest = self.data_consumer.available_at_entry().min(unpopped);\n for _ in 0..rest {\n let Ok(block) = self.data_consumer.try_pop() else {\n break;\n };\n if self.settle(block) {\n return;\n }\n }\n }\n}", - ); -} - /// The spectrum capture's drains: a named cap, a `break` on an empty queue, and a read that matches /// the pop and returns after the loop. #[test] @@ -31,13 +22,3 @@ fn the_spectrum_drains_pass() { "impl Capture {\n fn cancel(&mut self) {\n let available = self.consumer.available_at_entry().min(RESULT_SLOTS);\n for _ in 0..available {\n if self.consumer.try_pop().is_err() {\n break;\n }\n }\n self.reset();\n }\n\n fn read_record(&mut self) -> Result {\n match self.state.load() {\n COMPLETE => match self.consumer.try_pop() {\n Ok(record) => Ok(record),\n Err(_) => Err(ReadError::Pending),\n },\n INVALID => {\n let _ = self.consumer.try_pop();\n Err(ReadError::Invalid)\n }\n _ => Err(ReadError::Invalid),\n }\n }\n\n fn read_continuous(&mut self) -> Result {\n let available = self.consumer.available_at_entry().min(RESULT_SLOTS);\n for _ in 0..available {\n match self.consumer.try_pop() {\n Ok(record) if self.stale(&record) => {}\n Ok(record) => {\n return Ok(record);\n }\n Err(_) => return Err(ReadError::Pending),\n }\n }\n Err(ReadError::Warming)\n }\n}", ); } - -/// The collection's `cancel_except`: a drain loop over the captures that names `capture` only in -/// its count and its pop, then a reset loop of the same header with a pop-free call. -#[test] -fn the_collection_cancel_except_passes() { - accepts( - "twin-cancel-except", - "impl Collection {\n fn cancel_except(&mut self, keep: Option) {\n for (index, capture) in self.captures.iter_mut().enumerate() {\n if keep == Some(index) {\n continue;\n }\n let available = capture.consumer.available_at_entry().min(RESULT_SLOTS);\n for _ in 0..available {\n if capture.consumer.try_pop().is_err() {\n break;\n }\n }\n }\n for (index, capture) in self.captures.iter_mut().enumerate() {\n if keep == Some(index) {\n continue;\n }\n capture.reset();\n }\n }\n}", - ); -} diff --git a/tools/realtime-policy/tests/policy/markers.rs b/tools/realtime-policy/tests/policy/markers.rs index d4445f599..3339545e4 100644 --- a/tools/realtime-policy/tests/policy/markers.rs +++ b/tools/realtime-policy/tests/policy/markers.rs @@ -19,19 +19,6 @@ fn rewrite<'a>(path: &'a str, text: &'a str) -> [Edit<'a>; 2] { [Edit::Delete { path }, Edit::Create { path, text }] } -#[test] -fn a_marker_written_in_a_string_literal_is_malformed() { - // One line: the marker word inside a literal. - let one_line = format!("const S: &str = \"// {BEGIN}\";\n"); - expect_failure("marker-in-string", &append(&one_line), &[MALFORMED]); - // Across lines: the middle line is a valid-looking marker line, and only the literal that - // spans it says otherwise. - let spanning = format!("const S: &str = \"\n// {BEGIN}\n\";\n"); - expect_failure("marker-line-in-string", &append(&spanning), &[MALFORMED]); - let raw = format!("const S: &str = r#\"\n// {END}\n\"#;\n"); - expect_failure("marker-line-in-raw-string", &append(&raw), &[MALFORMED]); -} - #[test] fn a_marker_written_in_a_doc_comment_is_malformed() { let line_doc = format!("/// {BEGIN}\nfn documented() {{}}\n"); @@ -189,16 +176,6 @@ fn unmatched_markers_name_the_file_and_drop_it_from_the_floors() { assert_eq!(unmatched.path.as_deref(), Some("hosts/host-web/src/lib.rs")); } -#[test] -fn the_file_floor_fires_when_a_marked_file_loses_its_markers() { - // Removing every marker line of one file drops the file and its regions; its code stays, - // including the file's unmarked `try_pop` drains. - let tree = Tree::base("marked-file-count-floor"); - strip_marker_lines(&tree, "crates/builtins/src/lib.rs"); - // The file's drains are pops outside every region now (#1443 D2). - super::common::assert_classes(&tree, &[Class::UnmarkedTryPop, FILE_FLOOR, REGION_FLOOR]); -} - #[test] fn the_floors_fire_when_no_file_is_marked() { let tree = Tree::base("no-marked-files"); diff --git a/tools/realtime-policy/tests/policy/source_reads.rs b/tools/realtime-policy/tests/policy/source_reads.rs index 316525e27..d3bb48e95 100644 --- a/tools/realtime-policy/tests/policy/source_reads.rs +++ b/tools/realtime-policy/tests/policy/source_reads.rs @@ -162,29 +162,6 @@ fn a_path_that_is_not_a_string_literal_is_a_finding() { } } -/// rustc reads `r#path` as `path` and `r#include` as `include`. -#[test] -fn raw_identifiers_do_not_hide_a_path_or_an_include() { - expect_failure( - "reads-raw-path", - &[create(READS, "#[r#path = \"shadow.txt\"]\nmod m;\n")], - &[PATH], - ); - expect_failure( - "reads-raw-include", - &[ - create(READS, "const X: u8 = r#include!(\"hidden.in\");\n"), - create("crates/lane/src/hidden.in", LOOPING_TRAIT), - ], - &[INCLUDE], - ); - expect_failure( - "reads-raw-include-import", - &[create(READS, "use std::r#include as inc;\n")], - &[INCLUDE_IMPORT], - ); -} - #[test] fn an_include_of_source_is_a_finding() { expect_failure( diff --git a/tools/realtime-policy/tests/policy/unmarked.rs b/tools/realtime-policy/tests/policy/unmarked.rs index 77c32d509..4270eadb8 100644 --- a/tools/realtime-policy/tests/policy/unmarked.rs +++ b/tools/realtime-policy/tests/policy/unmarked.rs @@ -200,16 +200,6 @@ fn every_declaration_must_be_test_code() { const QUEUES: &str = "struct Queues;\nimpl Queues {\n fn dequeue(&mut self, c: &mut Consumer) {\n while let Ok(r) = c.try_pop() {\n apply(r);\n }\n }\n}\n"; -#[test] -fn an_allowlisted_function_admits_its_pops() { - entries( - "allowed", - &[create(LIB, QUEUES)], - vec![entry(LIB, "Queues::dequeue", 1)], - &[], - ); -} - #[test] fn an_entry_admits_one_function_only() { let text = format!( diff --git a/tools/realtime-policy/tests/policy/web_host.rs b/tools/realtime-policy/tests/policy/web_host.rs index 12742693a..d36a2aa51 100644 --- a/tools/realtime-policy/tests/policy/web_host.rs +++ b/tools/realtime-policy/tests/policy/web_host.rs @@ -209,41 +209,6 @@ fn a_path_on_a_guarded_module_or_an_inline_module_is_refused() { ); } -/// Rule (d): a glob `use` among the top-level items of `ffi.rs` or `render_lock.rs`, plain, -/// public or grouped. A glob in a child module is not refused. -#[test] -fn a_top_level_glob_use_is_refused_and_a_child_module_glob_is_not() { - let globs = [ - "use crate::fake::*;", - "pub use crate::fake::*;", - "use crate::{fake::*, other};", - "use crate::fake::{self, *};", - ]; - for (index, glob) in globs.iter().enumerate() { - for path in [FFI, RENDER_LOCK] { - let report = expect_failure( - &format!("web-glob-{index}-{}", path.len()), - &[append(path, &format!("{glob}\n"))], - &[Class::GlobInWrapModule], - ); - assert_eq!(report.findings.len(), 1, "{glob} in {path}"); - assert_eq!(report.findings[0].path.as_deref(), Some(path)); - } - } - // `use super::*;` in a child module that holds no listed export (the shape of the five test - // modules of the real tree) cannot change how an export or the import resolves. - expect_valid( - "web-glob-child-module", - &[ - append( - FFI, - "#[cfg(test)]\nmod child_tests {\n use super::*;\n}\npub(crate) mod live_tests {\n pub use super::*;\n}\n", - ), - append(RENDER_LOCK, "mod more {\n use super::*;\n}\n"), - ], - ); -} - /// A listed export must be a top-level item of `ffi.rs`: moved into a child module (where a glob /// is not refused) it is missing, not accepted. #[test] diff --git a/tools/realtime-policy/tests/policy/whole_plan.rs b/tools/realtime-policy/tests/policy/whole_plan.rs index 9ff0c02ef..28fb32e2b 100644 --- a/tools/realtime-policy/tests/policy/whole_plan.rs +++ b/tools/realtime-policy/tests/policy/whole_plan.rs @@ -107,13 +107,6 @@ fn a_missing_file_or_a_render_entry_only_in_tests_is_a_finding() { ); } -/// The test module no longer ends the scan: items after it are production code. -#[test] -fn a_whole_plan_borrow_after_the_test_module_is_a_finding() { - let after = format!("fn after_tests(plan: *mut Plan) {{ let _ = {UNSAFE} {{ &*plan }}; }}\n"); - expect_failure("whole-plan-after-tests", &[append(&after)], &[WHOLE_PLAN]); -} - /// Only a module named `tests` under exactly `#[cfg(test)]` is test code. #[test] fn only_a_cfg_test_module_named_tests_is_skipped() { From f2f6e6aa10beefb027a7063692ddb7d69369c1ad Mon Sep 17 00:00:00 2001 From: BL Date: Tue, 20 Oct 2026 10:17:28 +0000 Subject: [PATCH 2/8] qualification: give test-debug-a 22 minutes (#1728) The slowest recent green run of the job took 14:41 against a 15-minute timeout. 22 minutes is 1.5 times that run. Only this job's timeout moves; #1728 stays open. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/qualification.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/qualification.yml b/.github/workflows/qualification.yml index fe2649270..5f7c56072 100644 --- a/.github/workflows/qualification.yml +++ b/.github/workflows/qualification.yml @@ -606,7 +606,8 @@ jobs: needs: route if: needs.route.outputs.route == 'full' runs-on: ubuntu-24.04 - timeout-minutes: 15 + # 1.5 times the slowest recent green run (14:41), #1728. + timeout-minutes: 22 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 From a7f6a2c7b0d9e1f39fe6ba0be3e4c8b600e174fb Mon Sep 17 00:00:00 2001 From: BL Date: Tue, 20 Oct 2026 10:21:04 +0000 Subject: [PATCH 3/8] TA5 spec and its successor leads The TA5 spec records the owner approval, the batch 2 method and result (162/200, 81%, Wilson 75-86%), the blind spot and how reading handled it, the per-package counts, the 31 tests kept by reading with their reasons, the deleted helpers and the #1728 timeout change. The per-test list goes to issue comments. The successor spec records two leads TA5 did not fix. Co-Authored-By: Claude Opus 5.5 --- ...-TA5-delete-the-batch-2-redundant-tests.md | 213 ++++++++++++++++++ .../TBD-TA5-successor-audit-leads.md | 28 +++ 2 files changed, 241 insertions(+) create mode 100644 .github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md create mode 100644 .github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md diff --git a/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md b/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md new file mode 100644 index 000000000..86882298a --- /dev/null +++ b/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md @@ -0,0 +1,213 @@ +# Delete the redundant tests of the batch 2 sample (test audit TA5) + +Owner approval: option A, relayed by root on 2026-10-20: delete what batch 1 measured redundant +(TA4, #1731), sample batch 2, and end the audit with one PR (TA5) from that sample. This is that +PR, the last of the 2026-10 test audit. Branch `codex/batch-test-policy-4`, from `main` at +`ad5c7ae86` (TA1 to TA4 merged). + +## Method (batch 2) + +Batch 2 is agent B2's 2026-10-20 run at base `81a3074fc` (8.3 h; method and data kept with the +audit's working notes, `b2/README.md`). Pool: the 3,516 batch 1 KEEP tests minus the 9 TA3 +deletions and the 239 never-delete tests (`candidates.tsv` flags plus a name rule: `alloc`, `abi`, +`layout`, `golden`, `wire`, `g5`, `regression`/`reproducer`, `fuzz`), 3,268 tests. Draw: +`random.Random(20261020).sample(sorted(ids), 200)`. Mutants: cargo-mutants 27.1.0, from functions +each sampled test covers, up to 4 catches or 12 viable tries per test (median 12); each mutant ran +every sampled test that covers its function, then the covering KEEP tests (fail-fast, first red +test recorded). 414 mutants, 321 viable, 12 survivors. + +## Result + +- MEASURED-REDUNDANT (every caught mutant also red in a KEEP test): **162 of 200, 81.0%, 95% + Wilson 75.0% to 85.8%**. The other 38: 11 CLAIM-KEPT, 5 HELD-RANDOMIZED, 6 UNIQUE, 14 THIN, 2 + WEAK. +- The projection onto the pool (about 2,647 tests) is an upper bound per test, not a sum: deleting + many pool tests at once removes catchers of each other. The 162 are safe together because no + sampled test was a catcher of another. + +## The blind spot, and how it was handled + +Mutation cannot see allocation counts, exact count claims or ceilings, and a sample of about 12 +random mutants per test can miss a claim. B2 read the 178 mutation-redundant bodies (regex triage +for allocation, counts, ceilings, bytes, reproducers, randomness) and held 11 as CLAIM-KEPT and 5 as +HELD-RANDOMIZED; those are not in the 162. This PR then read each of the 162 and answered the +AGENTS.md question, "which plausible defect turns this red that no remaining test catches?". A +test is kept when the answer names a real edge case that the measured mutants could not reach and +no remaining test asserts (the remaining tests checked are named per test); otherwise it is +deleted. Reading found what the triage missed: a randomized differential named `run_seeds`, an +allocation audit inside a macro, three tests that a `MUTATIONS.md` records as the only red test of +a row, and one test that `scripts/lib/aarch64-known-defects.py` lists as a known AArch64 failure. + +## What this change does + +1. Deletes 131 of the 162 tests and keeps 31 by reading (list below). None of the 162 had moved + or gone since `81a3074fc`; nothing was skipped. +2. Deletes 48 helper items (984 lines) only after `cargo clippy --all-targets --all-features` + reported them unused, looped until it reported nothing; no `#[allow(dead_code)]` was added. + Deletes two test files left with no test: `crates/host-core/tests/strip_handles.rs` and + `crates/transient-shaper/tests/partition.rs`. +3. Updates the references: each `MUTATIONS.md` row or doc line that names a deleted test now says + "(deleted by TA5)", and each such file ends with a "TA5 deletions" list that names a KEEP + catcher only where batch 2 recorded it red on that test's sampled mutants in the same crate. + Code comments and open specs that name a deleted test say so in place (#1339, #1340, #1586, + #1713). Recorded log output and dated records (`docs/handoffs`, `docs/audits`, `artifacts/`) + are not edited. +4. Gives the `qualification` job `test-debug-a` ("workspace debug tests (minus DSP crates and + evidence tools)") `timeout-minutes: 22` in place of 15: 1.5 times the slowest recent green run + (14:41). This refers to #1728 and does not close it; #1728's gate (ten consecutive green runs + with at least 25% margin) stays open. No other timeout moves. + +## Per package + +| package | deleted | kept by reading | deleted test lines | +|---|---|---|---| +| audit | 0 | 1 | 0 | +| automation | 1 | 0 | 30 | +| bench-support | 1 | 0 | 5 | +| builtins | 5 | 2 | 406 | +| builtins-compiler | 0 | 1 | 0 | +| capi | 9 | 1 | 603 | +| compressor | 3 | 0 | 252 | +| console-workload | 2 | 0 | 83 | +| control-plane | 0 | 1 | 0 | +| controls | 3 | 0 | 117 | +| delay | 1 | 0 | 12 | +| dsp-reference | 1 | 1 | 15 | +| effect-compiler | 2 | 1 | 38 | +| effect-contract | 2 | 1 | 46 | +| effect-runtime | 5 | 1 | 97 | +| engine | 3 | 0 | 139 | +| gate-expander | 0 | 2 | 0 | +| graph | 4 | 0 | 326 | +| graph-compiler | 7 | 0 | 590 | +| host-core | 30 | 3 | 1,270 | +| host-web | 5 | 3 | 328 | +| lane | 4 | 1 | 76 | +| math | 2 | 1 | 23 | +| multiband-compressor | 3 | 0 | 151 | +| parametric-eq | 5 | 2 | 464 | +| protocol | 5 | 2 | 343 | +| rack | 5 | 1 | 322 | +| realtime-policy | 11 | 2 | 177 | +| session | 2 | 1 | 34 | +| soft-clip | 2 | 0 | 126 | +| source | 3 | 0 | 115 | +| transient-shaper | 3 | 1 | 197 | +| true-peak-limiter | 2 | 2 | 63 | +| **total** | **131** | **31** | **6,448** | + +Lines are the deleted test functions with their doc comments, at `ad5c7ae86`. Shortstat of the +deletion commit: 107 files changed, 129 insertions, 7,783 deletions (tests, helpers, imports, the +two files, and the reference updates). + +## Kept by reading (31) + +- audit `fixture_builtins::tests::check_rejects_owned_jsonl_tuple_mutations`: a field or record + change in an owned JSONL after the manifest is regenerated; the format test only deletes, + appends a byte or empties. +- builtins `a_long_matrix_move_stays_inside_its_endpoints`: the #1408 reproducer at the matrix + call site; the D11 reference test never overshoots and the lane test checks the law only. +- builtins `a_windowed_move_is_monotone_and_lands_exactly_on_its_target`: the D11 exact-assignment + leg; the endpoint and partition siblings pass without it. +- builtins-compiler `composite_drain_applies_the_other_stages_value_when_one_stage_is_refused`: the + acked-batch question on the fused fader+matrix drain; the sibling covers the input drain only. +- capi `capi_retained_bytes_charge_every_byte_the_compile_retains`: a resource-charge claim over + four sessions, and the only caller of `assert_host_owners_are_charged`. +- control-plane `no_room_for_the_end_refuses_the_removal_and_changes_nothing`: the acked-batch + question in the control plane (commit and drop the end event); the only one there. +- dsp-reference `gate_expander::tests::a_level_inside_the_hysteresis_band_keeps_reloading_the_hold`: + the only red test of gate-expander `MUTATIONS.md` row 3 (the in-band reload). +- effect-compiler `a_session_bypass_lowers_to_an_enabled_effect_and_a_bypassed_lane`: the only pin + of the constants `NEVER_BANKED_EFFECTS`, `PREPARED_BYPASS_EFFECTS` and the lowered set; no + mutant edits a constant. +- effect-contract `a_drain_emits_the_contract_canonical_span_order`: the only red test of + effect-contract `MUTATIONS.md` 140-1; the block validator is not on the render path. +- effect-runtime `subnormal_targets_are_hoisted_without_flushing`: the denormal rule; the siblings + have no subnormal word. +- gate-expander `passes_effect_contract_conformance`: the conformance macro's `process.allocation` + audit, the crate's only render allocation check. +- gate-expander `a_moving_ramp_word_past_its_domain_is_refused`: the one-ulp domain edge with + `effect.state.parameter`; no other gate-expander test refuses with that code. +- host-core `a_bus_renders_the_bits_of_a_track_fed_its_sum`: a randomized differential (32 seeds of + drawn strips, effects and routes); the only bus-equals-track-fed-its-sum check. +- host-core `a_hand_move_on_an_automated_fader_writes_the_lanes_offsets_cell`: the D6 summation + order (hand offset first, then the VCAs by ID); a reorder is not a mutant. +- host-core `the_session_shape_measures_the_longest_vca_id`: the only assertion of + `longest_vca_id_bytes`, a staging-size ceiling. +- host-web `ffi::live_response_ffi_tests::live_response_owners_name_console_slots_inserts_and_input_filters`: + the browser ABI's rack codes 0, 2 and 4 and the refusal of the retired 1 and 3. +- host-web `tests::each_boot_option_rule_has_its_own_typed_refusal`: the `struct_size` and + `reserved0` ABI refusals; other tests cover `abi_version` and the sizes only. +- host-web `tests::the_meter_frame_carries_a_peak_pair_and_a_gain_word_per_submix`: the meter + frame's `3(T + S) + 3` word layout with submixes, which the worklet reads. +- lane `g4_silence_skip_block_saturates_a_long_trailing_count`: the 2^24 clamp, reachable only + with a 2^24 + 3 frame block. +- math `m2_exp2_lane_identity`: a known AArch64 release failure (LANE-3, #1019) named in + `scripts/lib/aarch64-known-defects.py`; x86 mutants cannot reach it. +- parametric-eq `a_non_finite_input_block_is_zeroed_counted_once_and_leaves_the_next_block_clean`: + the count claim (one block on one channel, not one per sample). +- parametric-eq `svf_words_match_the_independent_oracle_on_the_complete_grid`: the only gate on the + A1 storage (parametric-eq `MUTATIONS.md`, recorded failures 1 and 1b); its 10 Hz, Q = 18, + 88.2 kHz row is on no other grid. +- protocol `controller::tests::canonical_json_snapshots_reparse_before_and_after_commit_across_utf8_split_pages`: + snapshot pages are byte ranges that may split a UTF-8 sequence; the only such test. +- protocol `controller::tests::the_end_needs_room_with_the_commit_and_a_dropped_token_releases_it`: + the acked-batch question for the end event's reservation, release and `Unencodable`. +- rack `re_equal_words_after_a_desymmetrised_episode_do_not_re_engage`: the only negative of + `collapse_channels_agree`; the siblings assert re-engagement. +- realtime-policy `drain::outer::an_item_that_declares_the_collection_root_refuses_the_drain`: the + reproducers of verdict rows r01 and b06. +- realtime-policy `render_wrap::an_unwrapped_entry_outside_the_named_exceptions_is_a_finding`: the + only test that produces `UnwrappedNotPermitted`. +- session `route_mute_is_a_required_boolean`: a schema rule (a missing `mute` must not parse as + on); the only test of the key. +- transient-shaper `a_moving_ramp_word_past_its_domain_is_refused`: the one-ulp edge; the sibling + refuses gross values only. +- true-peak-limiter `every_case_is_finite_and_not_vacuous`: the guard of the limiter's G5 corpus, + which a digest pin cannot give (NaN payloads are canonicalised on wasm). +- true-peak-limiter `production_tracks_the_f64_oracle`: the only independent `f64` oracle of the + limiter gain law (E5); E4 bounds the peak only. + +## Deleted helpers (compiler-reported) + +- `crates/capi/src/runtime/live_tests.rs`: `live_alone` +- `crates/controls/tests/monitor.rs`: `FADER_DB` +- `crates/dsp-reference/src/svf.rs`: `IMPULSE_FRAMES` +- `crates/engine/src/realtime/spsc.rs`: `fail_if_stalled`, `STALL_DEADLINE`, `EndedOnDrop` and its + `Drop` +- `crates/graph/src/runtime.rs`: `HostShape` and its impl, `render_arena_oracle`, + `fill_output_slot`, `output_slot`, `stereo_bits`, `OUTPUT_SLOT_FILL`, `direct_bank_parts`, + `test_only_set_host_master_declined` +- `crates/host-core/src/prepare.rs`: `instance`; `tests/browser_successor.rs`: + `session_without_eq1`; `tests/live_delta.rs`: `with_effect_bus`; `tests/spectrum.rs`: + `render_window`; `tests/successor_swap.rs`: `a_repointed_route_does_not_carry_its_line`, + `two_bus_session` +- `crates/lane/tests/f64_lane.rs`: `square_mismatches`, `square_pool`, `oracle_square_bits`; + `tests/fader_matrix.rs`: `fused_select_free_width`, `FUSED_FRAMES`, + `fused_select_free_differences`, `FusedDifferences` and its `record`, `FUSED_MUTES`, + `FUSED_GAINS`, `fused_family_source`; `tests/input_chain_arming.rs`: `every_state_word_arms` +- `crates/math/tests/m2_lane_identity.rs`: `exp2_caller_edges`, `FastGain` and its `LaneFn` +- `crates/multiband-compressor/tests/crossover.rs`: `rms`, `complex_gain`, `CROSSOVERS` +- `crates/parametric-eq/tests/carry.rs`: `same_bits`; `tests/exact_rest.rs`: `at_rest`, + `HELD_BLOCKS`, `REST_BOUND_SAMPLES` +- `crates/rack/src/lib.rs`: `WrongWidthPlanes` and its `BankMembers` +- `hosts/host-web/src/ffi.rs`: `test_seek_report_addresses` + +Leads the compiler does not report are in the successor spec +`TBD-TA5-successor-audit-leads.md`. + +## Objective gates + +Every `run:` step of the qualification `lint` job (fmt, workspace clippy `-D warnings`, +`RUSTDOCFLAGS='-D warnings' cargo doc --locked --workspace --no-deps`, the hermetic policy gates), +and `cargo test --locked --all-features -p ` for every touched package. + +## Evidence + +The per-test list (package, test, location, the one-line answer, and the KEEP catchers batch 2 +recorded) is too long for an issue body: per-test list with catchers: issue comments ("TA5 +per-test list, part 1/3" to "part 3/3"). The PR diff is the list of what went. + +## Survivor gaps + +- (to be filled) diff --git a/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md b/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md new file mode 100644 index 000000000..6f22fd2ec --- /dev/null +++ b/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md @@ -0,0 +1,28 @@ +# Record the leads the last test-audit PR found and did not fix (TA5 successor) + +TA5 (`TBD-TA5-delete-the-batch-2-redundant-tests.md`) deleted 131 tests of the batch 2 sample and +did not add scope. It found two leads. Neither blocks TA5. + +## Leads + +1. **An orphaned test seam in `graph`.** TA5 deleted `test_only_set_host_master_declined` + (`crates/graph/src/runtime.rs`; compiler-reported unused once its only caller, the #916 gate + `the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride`, went). The + `#[cfg(test)]` thread-local `HOST_MASTER_DECLINED` it wrote stays: `preflight_sequential` still + reads it, nothing sets it, so the clause is always false. The compiler does not report a read + of a never-written cell. Delete the thread-local and its read, or record why it stays. +2. **The redundancy measurement over-reports on its own.** Of batch 2's 162 MEASURED-REDUNDANT + tests, reading kept 31. Five of them carry a claim that another record states and the name + triage did not read: three are the only red test of a `MUTATIONS.md` row (gate-expander row 3, + effect-contract 140-1, parametric-eq recorded failures 1 and 1b), one is a known AArch64 + release failure in `scripts/lib/aarch64-known-defects.py` (`m2_exp2_lane_identity`, #1019), + and one is a randomized differential named `run_seeds` (host-core + `a_bus_renders_the_bits_of_a_track_fed_its_sum`). B2's projection of about 2,647 deletable pool + tests therefore does not stand without that cross-check. If a pool-wide cut is ever reopened, + its brief must first match every candidate against the `MUTATIONS.md` sole-catcher rows, the + known-defect lists and the seeded-differential helpers, and must still read each body. + +## Gate + +Lead 1: the seam is gone or documented, with workspace clippy and the graph tests green. Lead 2: +no work unless a pool-wide cut is reopened; then the reopening brief cites this item. From 3d0c5b3cea202c6a4ae3f76b63e902ce036ee010 Mon Sep 17 00:00:00 2001 From: BL Date: Tue, 20 Oct 2026 10:39:28 +0000 Subject: [PATCH 4/8] Fold the TA5 review: drop the deletion ledgers, shorten the row notes, add the gap spec Remove the "TA5 deletions" appendices from the MUTATIONS.md files (a list of deleted tests is a ledger row). Each row that named a deleted test now names a remaining same-crate catcher or says "no catcher recorded". Notes in specs and comments stay only where the name would be a stale reference. Add T5S's survivor-gap spec and fill the TA5 spec's "Survivor gaps" section. Co-Authored-By: Claude Opus 5.5 --- ...1339-give-the-delay-a-live-bypass-shunt.md | 2 +- ...ultiband-compressor-a-live-bypass-shunt.md | 2 +- ...-TA5-delete-the-batch-2-redundant-tests.md | 17 ++++++----- ...lose-the-batch-2-mutation-survivor-gaps.md | 30 +++++++++++++++++++ crates/builtins/tests/MUTATIONS.md | 10 ++----- crates/compressor/tests/MUTATIONS.md | 14 +++------ crates/effect-runtime/tests/MUTATIONS.md | 7 ----- crates/graph/tests/MUTATIONS.md | 15 +++------- crates/host-core/tests/MUTATIONS.md | 11 ++----- crates/lane/tests/MUTATIONS.md | 8 +---- .../multiband-compressor/tests/MUTATIONS.md | 6 ---- crates/parametric-eq/tests/MUTATIONS.md | 11 ++----- crates/rack/tests/MUTATIONS.md | 10 ++----- crates/transient-shaper/tests/MUTATIONS.md | 7 ----- crates/true-peak-limiter/tests/MUTATIONS.md | 15 +++------- hosts/host-web/tests/control_parity.rs | 2 +- 16 files changed, 64 insertions(+), 103 deletions(-) create mode 100644 .github/ISSUE_SPECS/TBD-TA5-gap-close-the-batch-2-mutation-survivor-gaps.md diff --git a/.github/ISSUE_SPECS/1339-give-the-delay-a-live-bypass-shunt.md b/.github/ISSUE_SPECS/1339-give-the-delay-a-live-bypass-shunt.md index ba0b1b2b2..34a313b7b 100644 --- a/.github/ISSUE_SPECS/1339-give-the-delay-a-live-bypass-shunt.md +++ b/.github/ISSUE_SPECS/1339-give-the-delay-a-live-bypass-shunt.md @@ -173,7 +173,7 @@ the lift is heard with the tail already in place. block equals a reference that runs A and lifts the delay's bypass live at block 7. #1282's gate-3 delay leg is deleted, and its multiband leg stays. 7. **Deleted, superseded:** `the_delay_keeps_its_prepared_bypass_because_a_shunt_would_move_a_bit` - (`bypass_shunt_identity.rs:798-858`; already deleted by TA5), whose claim gate 1 now reverses. Update the red-mutation + (`bypass_shunt_identity.rs:798-858`; deleted by TA5), whose claim gate 1 now reverses. Update the red-mutation doc of `the_never_banked_list_is_exactly_the_launch_effects_that_decline_a_bank` (`:754-760`). 8. **Click against the mute baseline** (root, 2026-10-05, from #1055). The bypass crossfade's click is measured against #1055's mute baseline, at the session's `muteMs` default, diff --git a/.github/ISSUE_SPECS/1340-give-the-multiband-compressor-a-live-bypass-shunt.md b/.github/ISSUE_SPECS/1340-give-the-multiband-compressor-a-live-bypass-shunt.md index 1aef1ae24..7a35d8258 100644 --- a/.github/ISSUE_SPECS/1340-give-the-multiband-compressor-a-live-bypass-shunt.md +++ b/.github/ISSUE_SPECS/1340-give-the-multiband-compressor-a-live-bypass-shunt.md @@ -163,7 +163,7 @@ bits. This closes decision 14's F4 for the multiband compressor. `TRACKS / lanes()` banks; the render is bit-identical to today's prepared-bypass lowering (`todays_lowering`) and to `Backend::Scalar` at music levels. 3. **A hot bypassed lane in a bound bank.** `a_hot_bypassed_multiband_lane_leaves_its_neighbours_bits_unchanged` - (deleted by TA5, so this gate writes it again) keeps its name and assertion (zero neighbour taps moved), now with the hot lane inside a bound + (deleted by TA5) keeps its name and assertion (zero neighbour taps moved), now with the hot lane inside a bound mixed bank (assert the bank binds). Its in-test positive control under #1087's lowering is removed, because the lowering is now the production path; D1's red mutation replaces it. 4. **Lowering.** `native_session.rs` asserts that the bypassed multiband entry is lowered diff --git a/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md b/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md index 86882298a..8c800dab6 100644 --- a/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md +++ b/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md @@ -46,12 +46,12 @@ a row, and one test that `scripts/lib/aarch64-known-defects.py` lists as a known reported them unused, looped until it reported nothing; no `#[allow(dead_code)]` was added. Deletes two test files left with no test: `crates/host-core/tests/strip_handles.rs` and `crates/transient-shaper/tests/partition.rs`. -3. Updates the references: each `MUTATIONS.md` row or doc line that names a deleted test now says - "(deleted by TA5)", and each such file ends with a "TA5 deletions" list that names a KEEP - catcher only where batch 2 recorded it red on that test's sampled mutants in the same crate. - Code comments and open specs that name a deleted test say so in place (#1339, #1340, #1586, - #1713). Recorded log output and dated records (`docs/handoffs`, `docs/audits`, `artifacts/`) - are not edited. +3. Updates the references: each `MUTATIONS.md` row that names a deleted test names a remaining + same-crate catcher that batch 2 recorded red on that test's sampled mutants, or says "(deleted + by TA5; no catcher recorded)". No list of deleted tests is added to a tracked file. Code + comments and open specs (#1339, #1340, #1586, #1713) note a deleted name only where it would + otherwise be a stale reference. Recorded log output and dated records (`docs/handoffs`, + `docs/audits`, `artifacts/`) are not edited. 4. Gives the `qualification` job `test-debug-a` ("workspace debug tests (minus DSP crates and evidence tools)") `timeout-minutes: 22` in place of 15: 1.5 times the slowest recent green run (14:41). This refers to #1728 and does not close it; #1728's gate (ten consecutive green runs @@ -210,4 +210,7 @@ per-test list, part 1/3" to "part 3/3"). The PR diff is the list of what went. ## Survivor gaps -- (to be filled) +Batch 2 left 12 mutants that no sampled and no covering KEEP test turned red. Agent T5S read each +at `ad5c7ae86`: 3 equivalent, 1 unreachable, 2 dead code, 6 real gaps. The six real gaps (one +small test each) and the decision on the dead contract are the successor spec +`TBD-TA5-gap-close-the-batch-2-mutation-survivor-gaps.md`. diff --git a/.github/ISSUE_SPECS/TBD-TA5-gap-close-the-batch-2-mutation-survivor-gaps.md b/.github/ISSUE_SPECS/TBD-TA5-gap-close-the-batch-2-mutation-survivor-gaps.md new file mode 100644 index 000000000..fcca24453 --- /dev/null +++ b/.github/ISSUE_SPECS/TBD-TA5-gap-close-the-batch-2-mutation-survivor-gaps.md @@ -0,0 +1,30 @@ +# Close the batch 2 mutation-survivor gaps (TBD-TA5-gap) + +## Mission + +The batch 2 mutation sample (measured at 81a3074fc, read at ad5c7ae86) found six product or gate behaviours that change under a mutant while no test turns red. It also found one engine contract that has no engine caller. This issue adds one small test per gap and decides the dead contract. Each test has to name the defect it catches, as the "Test value" rule in AGENTS.md requires. + +## Scope + +One row per item. Lines are at ad5c7ae86. + +1. **Section-header names on the control list.** `SectionRecord::name`, crates/host-core/src/control_provider.rs:225. Mutant: the body returns `""`. Unchecked behaviour: every `ControlSectionHeader.name` on the wire is `own`, `builtins` or the console-slot/insert ID. Existing catalog walks match `SectionHeader(_)` and never read `name`. Smallest test: in the existing host-core catalog walk (control_provider.rs, near :2802), assert each section header's `name` against `own`/`builtins`/`address.slot_id`. This is one `assert_eq!` per arm on a session that has one console slot and one insert. +2. **Effect-count cap.** `count_effects`, crates/host-core/src/prepare.rs:6765. Mutant: `Ok(0)`. Unchecked behaviour: `check_counts` refuses a session whose console-plus-insert effect count is above `caps.maximum_effects` with `host.resource.count`. `PreparedReport.effect_count` and `HostShape.effect_count` also report the real count. Every caps test covers tracks, submixes, VCAs or reach pairs only. Smallest test: in crates/host-core/tests (next to submix_caps.rs), one track with two inserts. It prepares at `maximum_effects: 2` and is refused with `host.resource.count\t$\n` at `maximum_effects: 1`. +3. **Effect-order edit refusal.** `apply_session_edit` (`SetTrackEffectOrder`), crates/protocol/src/model.rs:799. Mutant: `||` -> `&&`. Unchecked behaviour: an order list that is a strict subset of the rack's effects is refused with `InvalidEffectOrder`. Under the mutant it succeeds and silently drops the unlisted effect. No test triggers `InvalidEffectOrder` at all. Smallest test: in crates/protocol/tests, a rack `[a, b]` with `SetTrackEffectOrder [b]`. Assert `Err(InvalidEffectOrder)` and that the rack is still `[a, b]`. Add one same-length case with a duplicated ID (`[a, a]`), also refused. +4. **Peak-heap window measures something.** `record_peak_window`, tools/bench-support/src/alloc.rs:159. Mutant: the body is a no-op. Unchecked behaviour: `current_thread_peak()` tracks live and peak bytes. Its two readers (parameter-metadata/tests/control_command_heap.rs, control-plane/tests/observation_bindings.rs) assert only ceilings, which a constant 0 passes, so the #1615 heap gates cannot fail. Smallest test: in bench-support, call `current_thread_peak_reset()`, allocate a `Vec` of 4096 bytes, and assert `peak_bytes >= 4096`. Then drop it and assert `live_bytes == 0` and `peak_bytes >= 4096`. +5. **Lowercase hex rule in the fixture audit.** `json_object_hex`, tools/audit/src/fixture_builtins.rs:2934. Mutant: `&&` -> `||`. Unchecked behaviour: a JSONL hex field (`start`, `handle`, `reset_generation`, `sequence`) in uppercase is refused. Under the mutant it is accepted. Smallest test: a unit test in fixture_builtins.rs that calls `json_object_hex` on a one-key object. `"00000000000000ab"` gives Ok(0xab), and `"00000000000000AB"` gives the lowercase-hex error. +6. **PCM mismatch index (low value; the implementer may decline it with a reason).** `verify_pcm_words`, tools/audit/src/fixture_builtins.rs:2627. Mutant: `!=` -> `==` in `position`. Unchecked behaviour: the error names the first word that differs, not the first word that is equal. Ok/Err is not affected. Smallest test: write a temp file with words `[1, 2, 3]`, expect `[1, 9, 3]`, and assert the error contains `word=1`. +7. **Dead contract: document-decimal lattice matching.** `ExactDecimal::parse` and `lattice_index_for_decimal`, crates/effect-contract/src/step.rs:731 and :873 (mutants at :774 and :775, `||` -> `&&`, which accept `.5` and non-digit text). No crate, host or C ABI path calls `lattice_index_for_decimal`. Its only non-test caller is the `lattice_oracle` SDK cross-check tool, which passes canonical renderings only. The module comment says persisted values are matched on document text, but no engine path does that. Decision required: either (a) delete `ExactDecimal`, `lattice_index_for_decimal` and their tests, and have `lattice_oracle` use an exact match on `points[i].canonical` instead, or (b) if a product path must match document text, record that as its own issue. Do not add parse tests to code that has no caller. + +## Non-goals + +- No test for the equivalent or unreachable survivors: `scheduling_barrier` (lane/src/fpenv.rs:504, no instructions, codegen-only), `distinct_planes_mut` (graph/src/runtime.rs:3646, the staged fallback gives the same bits), the `BankStage::begin_block` default (rack/src/lib.rs:502, same body), and `is_strictly_jury_stable` (dsp-reference/src/svf.rs:186, every caller stays at or below 0.45 fs). +- No rewrite of the heap-gate ceilings, the caps model or the fixture corpus. +- No new benchmark or harness. + +## Gates + +- Each new test is red on its listed mutant and green on the tree. Record each red run in the PR body, not in a committed file. +- Every test has a one-sentence test-value answer naming the defect. +- Item 7: the decision is in the PR body. If (a), the crate and tool still build, and `lattice_oracle` output stays byte-identical to its pre-change output (one-time PR evidence, not a committed test). +- The qualification `lint` job steps pass on the branch. diff --git a/crates/builtins/tests/MUTATIONS.md b/crates/builtins/tests/MUTATIONS.md index 84ec793dd..2ab45a67a 100644 --- a/crates/builtins/tests/MUTATIONS.md +++ b/crates/builtins/tests/MUTATIONS.md @@ -12,7 +12,7 @@ Command form: `cargo test -p builtins --test -- --exact`. | M1 | `let denominator = 1.0 + t1;` → `1.0 - t1` | `builtins/src/lib.rs` (`SvfSection::design`) | T1 `prepared_sections_match_reference_coefficients` | FAILED, `rate=44100, cutoff=10, high_pass=true` | | M2 | drop the D7 flush of `s1`, joint arm included: `self.s1 = if rest { 0.0 } else { flush(n1) }` → `self.s1 = n1` (#1328; before it, `self.s1 = flush(n1)` → `n1`, red at `index=3670`). Dropping only the per-word arm (`else { n1 }`) stays green: this signal never leaves `n1` below `FLUSH_EPS` beside an `n2` at or above `REST_EPS` | `dsp-reference/src/tpt.rs` | T2 `scalar_stage_is_bit_identical_to_reference_recurrence` | FAILED, `rate=44100, signal=1, index=2645` (re-run for #1328) | | M2b | drop only the per-word arm of the D7 flush (#1328): `self.s1 = if rest { 0.0 } else { flush(n1) }` → `else { n1 }`, and separately the same for `s2` | `dsp-reference/src/tpt.rs` | T2b `scalar_stage_is_the_reference_recurrence_where_the_per_word_flush_fires` (T2 stays green on both: its signals reach rest through the joint arm) | FAILED, `rate=44100, index=257` (`s1`) and `rate=44100, index=276` (`s2`) | -| M3a | every bank lane takes lane 0's coefficients (`zip(sections)` → `sections[0]`) | `builtins/src/lib.rs` (`svf_coef`) | T3 `bank_is_bit_identical_to_scalar_stage_at_every_width` (deleted by TA5) | FAILED, `width=4, members=3, lane=1, frame=0` | +| M3a | every bank lane takes lane 0's coefficients (`zip(sections)` → `sections[0]`) | `builtins/src/lib.rs` (`svf_coef`) | T3 `bank_is_bit_identical_to_scalar_stage_at_every_width` (deleted by TA5; catcher `identity_chain_renders_audit_clean_deterministic_and_positive_zero`) | FAILED, `width=4, members=3, lane=1, frame=0` | | M3c | report counters sum every lane, not only the members (`take(self.members)` → `take(L::WIDTH)`) | `builtins/src/lib.rs` (`InputStage::members_sum`) | T3, padding-lane arm | FAILED, `width=4, members=1` | | M4 | high-pass mix `m1 = -k` → `-k * 1.001` | `builtins/src/lib.rs` (`SvfSection::design`) | T1, T2 **and** the determinism corpus | all three FAILED | | M5 | the fixture oracle drops the identity-section `-0.0` mapping | `tools/audit/src/fixture_builtins.rs` | `issue064_checked_corpus_is_read_only_complete_and_has_no_authoring_reachability` | FAILED at the PCM semantics check | @@ -200,7 +200,7 @@ what the rows below record. | # | mutation | site | suite | verdict | |---|---|---|---|---| | C-1 | the retarget writer `set_trim_signed` drops its refresh | `builtins/src/lib.rs` | `input_liveness_mono` (3 tests) | RED — `the cached channel-symmetry flag is stale for lane 0`, on `an_asymmetric_retarget_declines_the_lane_on_the_admitting_block`, `re_equalising_the_words_restores_the_designed_term_and_nothing_more` and `a_retarget_between_a_collapsed_block_and_the_disengage_survives_the_copy`. This is the drain's own invalidation, and it is the one a live-words cache exists to have | -| C-2 | the **dual** ramping arm of `process` drops its refresh | `builtins/src/lib.rs` | `host-core::input_liveness_live_controls` (2 tests) | RED — `stale for lane 2`, on `re_equalising_after_a_disengaging_drain_holds_the_never_collapsed_bits` (deleted by TA5) and `the_collapse_census_follows_the_commands`. A ramp advances the compared words on every block it runs, with no record drained: a drain-only invalidation is **not** sufficient, and this row is the proof | +| C-2 | the **dual** ramping arm of `process` drops its refresh | `builtins/src/lib.rs` | `host-core::input_liveness_live_controls` (2 tests) | RED — `stale for lane 2`, on `re_equalising_after_a_disengaging_drain_holds_the_never_collapsed_bits` (deleted by TA5; no catcher recorded) and `the_collapse_census_follows_the_commands`. A ramp advances the compared words on every block it runs, with no record drained: a drain-only invalidation is **not** sufficient, and this row is the proof | | C-3 | the **collapsed** ramping arm of `process_mono` drops its refresh (after `mirror_trim_ramp`) | `builtins/src/lib.rs` | — (green across `builtins`, `rack`, `host-core`, `console-workload`, `parametric-eq`, `builtins-compiler`, `graph`) | **EQUIVALENT, and kept anyway.** `mirror_trim_ramp` assigns channel `1`'s whole record from channel `0`'s, so every compared ramp word is equal when it returns -- and it was equal on entry too, which `debug_assert!(self.trim_ramp_channels_agree())` at the top of `process_mono` states. The comparison therefore lands where it already was. The refresh is kept because the equivalence rests on the mirror being *total*: a mirror that ever became partial or conditional would make this row red, and the cost is one walk on a ramping collapsed block | ### Row count @@ -374,9 +374,3 @@ The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) delete - `settled_matrix_takes_the_select_free_arm_only_without_an_identity_lane`: no plausible same-crate KEEP catcher recorded - `fused_fader_matrix_takes_the_select_free_arm_only_without_an_identity_lane`: no plausible same-crate KEEP catcher recorded - -## TA5 deletions - -The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. - -- `bank_is_bit_identical_to_scalar_stage_at_every_width`: builtins `an_uncommanded_live_fader_is_bit_identical_to_the_prepared_one`, `identity_chain_renders_audit_clean_deterministic_and_positive_zero` diff --git a/crates/compressor/tests/MUTATIONS.md b/crates/compressor/tests/MUTATIONS.md index 937030f77..51e882b4d 100644 --- a/crates/compressor/tests/MUTATIONS.md +++ b/crates/compressor/tests/MUTATIONS.md @@ -169,7 +169,7 @@ tree restored between rows. | M2-C7 | `Channel::copy_state_from` drops `main` | `compressor/src/kernel.rs` | `a_desymmetrized_bank_is_a_never_collapsed_bank` | RED — the delay ring the output is read out of | | M2-C8 | `Channel::copy_state_from` drops `detector` | `compressor/src/kernel.rs` | `a_desymmetrized_bank_is_a_never_collapsed_bank` | RED — the detector ring the lookahead tap gathers from | -`a_statically_bypassed_bank_collapses_to_the_dual_bits` (deleted by TA5) carries no mutation of its own: it is +`a_statically_bypassed_bank_collapses_to_the_dual_bits` (deleted by TA5; catcher `mono_reopen_drives_the_next_render_from_the_copied_state`) carries no mutation of its own: it is coverage for the one place a **prepared** bypass and a **live** one differ. A live bypass clears the witness' `UNBYPASSED` term and declines the collapse; a prepared bypass on a live-control-free bank does not, because `EffectBankStage::lane_symmetry` is the designed-word comparison alone. The effect @@ -230,7 +230,7 @@ differentials see dispatch as well as bits. | # | mutation | red | |---|---|---| -| 982-M1 | the arm ignores `bypass` | `identity::bypass_preserves_exact_dry_bits_at_sample_zero` (deleted by TA4), gate 1 (`the_all_wet_arm_is_the_base_body_on_all_wet_tables`, the three differentials), gate 3, `conformance`, `contract::every_launch_rate_processes_scalar_and_supported_bank_at_zero_latency`, `mono_collapse::a_statically_bypassed_bank_collapses_to_the_dual_bits` (deleted by TA5) (9 red) | +| 982-M1 | the arm ignores `bypass` | `identity::bypass_preserves_exact_dry_bits_at_sample_zero` (deleted by TA4), gate 1 (`the_all_wet_arm_is_the_base_body_on_all_wet_tables`, the three differentials), gate 3, `conformance`, `contract::every_launch_rate_processes_scalar_and_supported_bank_at_zero_latency`, `mono_collapse::a_statically_bypassed_bank_collapses_to_the_dual_bits` (deleted by TA5; catcher `mono_reopen_drives_the_next_render_from_the_copied_state`) (9 red) | | 982-M2 | the arm is taken when any lane is wet (`mask_any`) | gate 1 on the mixed corpus banks (`the_settled_body_is_the_base_body_on_the_corpus_table`, `randomized_differential_simd4`/`_simd8`), gate 3, `scenario_981_*`, `cross_target`, `lane_identity::every_width_produces_the_same_words` (7 red) | | 982-M3 | the arm drops `+ makeup` | gate 1, `scenario_982_all_wet_render_is_pinned`, `cross_target` (its `f32` lane 7 has mix 1 and makeup 6), `lane_identity::every_width_produces_the_same_words` (7 red). `oracle` stays green, as the verification found | | 982-M4 | the arm is never taken | gate 3 (`the_all_wet_arm_is_taken_exactly_when_every_lane_is_wet`) and gate 1's dispatch assertion (5 red). No bit-exactness test can see it: it is a performance-only regression | @@ -268,7 +268,7 @@ same witness-keyed "both NaN" rule as the dual half, and the grid runs a third p |---|---|---| | 985-M2 | the short last chunk is skipped (`chunks_exact_mut` on the outer loop), the verification's replacement for "the body reads the right plane", which cannot be written | gate 1 (its 31-, 33- and 97-frame blocks: the grid on every table and the three differentials), `scenario_985_collapsed_render_is_pinned` (7 red) | | 985-M3 | the recursive word's write-back is dropped | gate 1, `scenario_985_*`, and four `mono_collapse` tests including `the_collapsed_body_renders_the_dual_bodys_left_plane` (deleted by TA4; KEEP catcher compressor `mono_collapse::mono_reopen_drives_the_next_render_from_the_copied_state`) (11 red) | -| 985-M4 | the collapsed wet arm ignores `bypass` | `mono_collapse::a_statically_bypassed_bank_collapses_to_the_dual_bits` (deleted by TA5), gate 1 (7 red) | +| 985-M4 | the collapsed wet arm ignores `bypass` | `mono_collapse::a_statically_bypassed_bank_collapses_to_the_dual_bits` (deleted by TA5; catcher `mono_reopen_drives_the_next_render_from_the_copied_state`), gate 1 (7 red) | | # | mutation | why it survives | |---|---|---| @@ -368,7 +368,7 @@ file was restored byte for byte from a saved copy. | # | mutation | test | result | |---|---|---|---| -| 994-C1 | `knee_coefficients` without its `is_finite` test (the pre-#994 design) | `knee_overflow` | RED, 4 of 5: the prepare, automation, restore (`a_restored_overflowing_knee_does_not_duck`, deleted by TA5) and bank tests. First failure `prepare: W 3e-45 (0x00000002) R 4 frame 0: 0.11473124 vs f64 reference 1` — the NaN target clamped to -100 dB and a 0.1 ms attack duck the very first full-scale sample by 18.8 dB. The automation test is red although both of its endpoints (0 and `1e-38`) are outside the overflow band: the `Linear 64` ramp passes through it | +| 994-C1 | `knee_coefficients` without its `is_finite` test (the pre-#994 design) | `knee_overflow` | RED, 4 of 5: the prepare, automation, restore (`a_restored_overflowing_knee_does_not_duck`, deleted by TA5; no catcher recorded) and bank tests. First failure `prepare: W 3e-45 (0x00000002) R 4 frame 0: 0.11473124 vs f64 reference 1` — the NaN target clamped to -100 dB and a 0.1 ms attack duck the very first full-scale sample by 18.8 dB. The automation test is red although both of its endpoints (0 and `1e-38`) are outside the overflow band: the `Linear 64` ramp passes through it | | 994-C2 | the rule as a constant bound one ulp too low: `knee_db >= f32::from_bits(0x0010_0000)` | `knee_overflow` | RED, 2 of 5: the prepare and bank tests, the two whose widths include `2^-129` itself | `the_narrowest_soft_knees_do_not_duck_a_sample_at_the_threshold` stays green under both: it is a @@ -382,9 +382,3 @@ The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) delete - `bank_matches_scalar_per_lane_bits`: no plausible same-crate KEEP catcher recorded - `block_partitions_are_invariant`: no plausible same-crate KEEP catcher recorded - `bypass_preserves_exact_dry_bits_at_sample_zero`: no plausible same-crate KEEP catcher recorded - -## TA5 deletions - -The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. - -- `a_statically_bypassed_bank_collapses_to_the_dual_bits`: compressor `mono_reopen_drives_the_next_render_from_the_copied_state` diff --git a/crates/effect-runtime/tests/MUTATIONS.md b/crates/effect-runtime/tests/MUTATIONS.md index a443898b2..320faf369 100644 --- a/crates/effect-runtime/tests/MUTATIONS.md +++ b/crates/effect-runtime/tests/MUTATIONS.md @@ -257,10 +257,3 @@ The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) delete - `the_corpus_is_width_independent`: no plausible same-crate KEEP catcher recorded - `a_knee_whose_reciprocal_overflows_is_a_hard_knee`: effect-runtime `dynamics::knee_words_follow_the_finite_reciprocal_rule` - `the_narrowest_soft_knees_are_finite_at_the_threshold`: effect-runtime `dynamics::knee_words_follow_the_finite_reciprocal_rule` - -## TA5 deletions - -The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. - -- `words_are_little_endian`: effect-runtime `float_words_round_trip_by_bits` -- `snapshot_then_restore_returns_every_word`: effect-runtime `float_words_round_trip_by_bits` diff --git a/crates/graph/tests/MUTATIONS.md b/crates/graph/tests/MUTATIONS.md index 369cd9f9d..be9bf0253 100644 --- a/crates/graph/tests/MUTATIONS.md +++ b/crates/graph/tests/MUTATIONS.md @@ -22,8 +22,8 @@ cargo test --locked -p --lib | M15 | fan-in 0 leaves the output buffer untouched instead of zeroing it | `src/runtime.rs` `reduce_plane` | `reduction_is_left_to_right_bit_identical_to_scalar_reference` | RED | | M3 | the slice PDC keeps the block's entry cursor instead of advancing it | `src/runtime.rs` `CompensationDelay::process` | `compensation_delay_is_partition_invariant_and_matches_per_sample_reference` | RED | | M12 | the slice PDC exchanges its two segments in the wrong order | `lane` `kernels::pdc_delay_block` | `compensation_delay_is_partition_invariant_and_matches_per_sample_reference` | RED | -| M4 | the route unfolds the gain and drops the fusion (`ll*l + lr*r`) | `lane` `kernels::mix2x2_block` | `route_applies_folded_gain_with_frozen_op_order` (deleted by TA5) | RED | -| M9 | `mix2x2_block`'s scalar tail uses a different coefficient order than its vector body | `lane` `kernels::mix2x2_block` | `route_applies_folded_gain_with_frozen_op_order` (deleted by TA5) | RED | +| M4 | the route unfolds the gain and drops the fusion (`ll*l + lr*r`) | `lane` `kernels::mix2x2_block` | `route_applies_folded_gain_with_frozen_op_order` (deleted by TA5; catcher `fifty_random_dag_sessions_render_deterministic_nonsilent_pcm`) | RED | +| M9 | `mix2x2_block`'s scalar tail uses a different coefficient order than its vector body | `lane` `kernels::mix2x2_block` | `route_applies_folded_gain_with_frozen_op_order` (deleted by TA5; catcher `fifty_random_dag_sessions_render_deterministic_nonsilent_pcm`) | RED | | M5 | the native executor stages an edge from the wrong producer | `src/runtime.rs` `build_native` | `fifty_random_dag_sessions_render_bit_identically_in_both_executors` | RED | | M6 | the sequential executor reduces before staging its delayed edges | `src/runtime.rs` `execute_op` | `fifty_random_dag_sessions_render_bit_identically_in_both_executors` | RED | | M8 | a bank member's inputs are gathered without their reduction | `src/runtime.rs` `Runtime::execute` | `level_major_w4_builtin_bank_is_analytic_for_three_blocks_in_both_executors` | RED | @@ -208,7 +208,7 @@ before the next row. Each mutation is an exact-text replacement, so an unmatched been reported rather than silently skipped. Host: `x86_64`, workspace `.cargo/config.toml` pin `-C target-feature=+avx2,+fma`, debug profile. The new tests these rows name: -- "Gate 1" is `runtime::tests::the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride` (deleted by TA5). +- "Gate 1" is `runtime::tests::the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride` (deleted by TA5; catcher `the_block_entry_holds_the_newest_published_lengths`). - "Gate 3" is `runtime::tests::a_failed_render_silences_the_host_planes_and_a_rejected_one_leaves_them_alone`. - "The kernel test" is `runtime::tests::a_host_plane_reduction_is_the_arena_reduction_bit_for_bit`. - "Metered" is `runtime::tests::a_folded_metered_plan_is_the_unfolded_plans_master_and_meters_bit_for_bit`. @@ -344,7 +344,7 @@ result column gives that second run's failure. For 926-12, the second run also d | 926-12 | drop the master's delayed-input clause (920-9) | `graph/src/runtime.rs` `output_route_fold` | gate 1 | RED on the constructor's `debug_assert` (`a folded route table belongs to the Output op's identity reduction`: the Output op stages). With the counts and that clause deleted: RED, `DelayedEdge, fan-in 64, 1 frames, block 0: the host planes`, `-0.0` (`2147483648`) where the oracle has `+0.0`: the line's `+0.0` warm-up words are mixed through the delayed route's negative 2x2. | | 926-13 | a non-route producer folds with an identity 2x2 (`plain_route_gains(..).unwrap_or([1.0, 0.0, 0.0, 1.0])`) (920-10) | `graph/src/runtime.rs` `output_route_fold` | gate 1 | RED on the count (`SubmixContributor ... folds`, 65 against 0). With the counts deleted: RED, `SubmixContributor, fan-in 64, 1 frames, block 0: the host planes`, `+0.0` where the oracle keeps `-0.0`: the pass-through's `(-0.0, +0.0)` left plane mixes to `+0.0`. | | 926-14 | drop the in-between `op_names_buffer` scan (920-11) | `graph/src/runtime.rs` `output_route_fold` | the graph suite; the console gate | GREEN (104 of 104, and the console gate). No test can make the scan fire: a write between the route and the Output would need the live buffer's slot, which the colouring owns through the Output op, and a read is a second reader, which 926-11's clause declines. The scan is the belt to that brace, as `route_fold`'s is. | -| 926-15 | drop the no-bank clause (920-12) | `graph/src/runtime.rs` `output_route_fold` | the graph suite; the console gate | RED on five graph tests at bind, `graph.route_fold.master`: `a_banked_source_gathers_the_played_block_bit_for_bit_with_the_copy`, `a_failed_render_silences_the_host_planes_and_a_rejected_one_leaves_them_alone`, `a_folded_metered_plan_is_the_unfolded_plans_master_and_meters_bit_for_bit`, `a_post_matrix_meter_on_every_track_of_a_full_bank_keeps_the_fold_armed`, `the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride` (deleted by TA5) (#920 saw four; #918's gate 1 is the fifth). RED on the console gate's other-rows sweep, at bind (`nine_track_baseline: console graph bindings`). On a banked plan whose chain fold is admitted, both folds claim the same routes and `validate_fold_installation` refuses the bind. | +| 926-15 | drop the no-bank clause (920-12) | `graph/src/runtime.rs` `output_route_fold` | the graph suite; the console gate | RED on five graph tests at bind, `graph.route_fold.master`: `a_banked_source_gathers_the_played_block_bit_for_bit_with_the_copy`, `a_failed_render_silences_the_host_planes_and_a_rejected_one_leaves_them_alone`, `a_folded_metered_plan_is_the_unfolded_plans_master_and_meters_bit_for_bit`, `a_post_matrix_meter_on_every_track_of_a_full_bank_keeps_the_fold_armed`, `the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride` (deleted by TA5; catcher `the_block_entry_holds_the_newest_published_lengths`) (#920 saw four; #918's gate 1 is the fifth). RED on the console gate's other-rows sweep, at bind (`nine_track_baseline: console graph bindings`). On a banked plan whose chain fold is admitted, both folds claim the same routes and `validate_fold_installation` refuses the bind. | | 926-16 | the Output fold's routes are not retired: they keep their units and run, and the table still applies (920-13) | `graph/src/runtime.rs` `build_sequential`, `validate_fold_installation` | gate 1; the console gate | RED on the unit count (`Plain ... each folded route is one unit fewer`, 193 against 129) and on the console gate's census (`the plumbing row must retire one route op per track`, 0 against 64). With the counts deleted: RED, `Plain, fan-in 64, 1 frames, block 0: the host planes`: every route is mixed twice. | | 926-17 | the routes are retired but the table is not installed (`output_routes` empty) (920-14) | `graph/src/runtime.rs` `build_sequential` | gate 1; the console gate | RED on the count (0 against 64). With the counts deleted: RED, `Plain, fan-in 64, 1 frames, block 0: the host planes`: the Output sums the unmixed inputs. GREEN on the console gate: its routes are the identity 2x2 at 0 dB, so the unmixed sum is the mixed one there and the census still drops by 64. | | 926-18 | skip an odd fan-in's lone last input (the `G = 1` arm returns `true`) | `graph/src/runtime.rs` `route_reduce` | the kernel test, gate 1, the console gate | RED, RED, GREEN. Kernel: `width 1, 1 frames, fan-in 3, -0.0 false: left`. Gate 1: `Plain, fan-in 9, 1 frames, block 0: the host planes`, the gate's odd fan-in. The console row's fan-in is sixty-four, all pairs. | @@ -766,10 +766,3 @@ The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) delete - `the_driver_fed_gain_pan_row_dispatches_every_unit_but_its_inputs`: no plausible same-crate KEEP catcher recorded - `a_muted_route_seals_one_route_mute_row_after_its_transform`: no plausible same-crate KEEP catcher recorded - -## TA5 deletions - -The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. - -- `route_applies_folded_gain_with_frozen_op_order`: graph `tests::fifty_random_dag_sessions_render_deterministic_nonsilent_pcm` -- `the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride`: graph `the_block_entry_holds_the_newest_published_lengths`, `direct_bank_graph_render_is_allocation_free_and_bit_exact` diff --git a/crates/host-core/tests/MUTATIONS.md b/crates/host-core/tests/MUTATIONS.md index 6dbaeb43c..336c6d1e8 100644 --- a/crates/host-core/tests/MUTATIONS.md +++ b/crates/host-core/tests/MUTATIONS.md @@ -354,13 +354,12 @@ the stereo arm does not, and uncommanded they agree. ## Issue #1211 — one mute owner and live-control producers per strip -Driver: `cargo test -p host-core --test strip_controls --test strip_handles` (the `strip_handles` -binary deleted by TA5) and `cargo test -p host-core --test randomized`, one mutation at a time, tree +Driver: `cargo test -p host-core --test strip_controls --test strip_handles` (deleted by TA5) and `cargo test -p host-core --test randomized`, one mutation at a time, tree restored between rows. Delivery host: x86_64 AVX2+FMA. | Row | Mutation | Red | | --- | --- | --- | -| 1211-M5 | build the control requests from `live_control_tracks` (the track prefix) again | `a_bus_fader_record_renders_as_the_session_with_that_fader` (three producers) and `handles_list_tracks_then_submixes_and_file_bus_effects` (deleted by TA5) (`left: ["t0", "t1", "t2"]`) | +| 1211-M5 | build the control requests from `live_control_tracks` (the track prefix) again | `a_bus_fader_record_renders_as_the_session_with_that_fader` (three producers) and `handles_list_tracks_then_submixes_and_file_bus_effects` (deleted by TA5; no catcher recorded) (`left: ["t0", "t1", "t2"]`) | | 1211-M6 | sort `strip_controls` by ID instead of by `canonical_index` | `a_bus_fader_record_renders_as_the_session_with_that_fader` (the bus producer is no longer at strip index 3) | | 1211-M7 | `live_records` draws over `handles.track_count` instead of `strip_controls.len()` | `randomized_consoles_render_the_same_bits_armed_dual_and_serialized` (`bus_live_records: 0`) | @@ -403,9 +402,3 @@ Gate 4's base behaviour was replayed by removing the attach call entirely: the t |---|---|---| | 1246-H1 | `longest_vca_id_bytes` measures the first VCA, not the longest | `the_session_shape_measures_the_longest_vca_id`: 1, not 46 | | 1246-H2 | `longest_vca_id_bytes` measures the track IDs | the same test: 2, not 46 | - -## TA5 deletions - -The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. - -- `handles_list_tracks_then_submixes_and_file_bus_effects`: no same-crate KEEP catcher recorded diff --git a/crates/lane/tests/MUTATIONS.md b/crates/lane/tests/MUTATIONS.md index f6262921a..53978d704 100644 --- a/crates/lane/tests/MUTATIONS.md +++ b/crates/lane/tests/MUTATIONS.md @@ -372,7 +372,7 @@ it; the count exists beside the digests for the same reason `minmax_lowering_mis ## Issue #954 — the select-free fused fader/matrix kernel `fader_matrix_block_without_identity` is `fader_matrix_block`'s second arm with no `L::select` in -the body. Gate 1, `select_free_fused_fader_matrix_matches_both_oracles_when_no_lane_is_identity` (deleted by TA5) +the body. Gate 1, `select_free_fused_fader_matrix_matches_both_oracles_when_no_lane_is_identity` (deleted by TA5; no catcher recorded) (`tests/fader_matrix.rs`), holds it equal to two oracles with no identity lane: oracle A is `fader_matrix_block` with an all-false mask, oracle B is `gain_mute_block` on each plane followed by `matrix2x2_block_without_identity`. It covers five input families (the four hostile ones and an @@ -508,9 +508,3 @@ browser's render-closure trap gate. Host AMD EPYC 7313P, rustc 1.97.1, `x86-64-v The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - `g1_select_max_is_order_free_only_on_the_sanitized_domain`: no plausible same-crate KEEP catcher recorded - -## TA5 deletions - -The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. - -- `select_free_fused_fader_matrix_matches_both_oracles_when_no_lane_is_identity`: no same-crate KEEP catcher recorded diff --git a/crates/multiband-compressor/tests/MUTATIONS.md b/crates/multiband-compressor/tests/MUTATIONS.md index 592cfae29..d85046dfa 100644 --- a/crates/multiband-compressor/tests/MUTATIONS.md +++ b/crates/multiband-compressor/tests/MUTATIONS.md @@ -219,9 +219,3 @@ mutated file was restored byte for byte from a saved copy. | 994-M1 | `knee_coefficients` without its `is_finite` test (the pre-#994 design) | `effect-runtime/src/dynamics.rs` | `knee_tests::a_band_level_at_the_threshold_never_takes_a_nan_target` | RED: `W 0x00000002 T 0e0 R 1 x 0e0: -100 is a duck at the threshold` — at ratio 1, which compresses nothing, the NaN target still became a -100 dB target | | 994-M2 | `BAND_KNEE = knee_coefficients(2.0 * KNEE_DB)` | `src/lib.rs` | `knee_tests::the_fixed_knee_words_are_unchanged` | RED | | 994-M3 | the rule as a constant bound one ulp too low: `knee_db >= f32::from_bits(0x0010_0000)` | `effect-runtime/src/dynamics.rs` | `knee_tests::a_band_level_at_the_threshold_never_takes_a_nan_target` | RED | - -## TA5 deletions - -The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. - -- `the_two_stage_split_matches_the_reference_and_recombines_flat`: no same-crate KEEP catcher recorded diff --git a/crates/parametric-eq/tests/MUTATIONS.md b/crates/parametric-eq/tests/MUTATIONS.md index cb299f2da..6b9f70a00 100644 --- a/crates/parametric-eq/tests/MUTATIONS.md +++ b/crates/parametric-eq/tests/MUTATIONS.md @@ -446,7 +446,7 @@ run, and the edit reverted (#1328 attempt 4; the attempt records are in the issu | # | mutation | gate that goes red | result | |---|---|---|---| -| 1328-M1 | `lane::flush_pair` without its input term (attempt 3's law: `rest = abs(n1) < REST_EPS && abs(n2) < REST_EPS`) | `exact_rest::a_tiny_input_through_four_boosting_shelves_gets_every_boost` (`10 Hz, input 2.7939677e-11 … misses the ordinary run by 1.5091782e3`), dev and release; `the_low_shelf_fixed_point_reaches_exact_rest` (deleted by TA5) stays green | RED | +| 1328-M1 | `lane::flush_pair` without its input term (attempt 3's law: `rest = abs(n1) < REST_EPS && abs(n2) < REST_EPS`) | `exact_rest::a_tiny_input_through_four_boosting_shelves_gets_every_boost` (`10 Hz, input 2.7939677e-11 … misses the ordinary run by 1.5091782e3`), dev and release; `the_low_shelf_fixed_point_reaches_exact_rest` (deleted by TA5; no catcher recorded) stays green | RED | | 1328-M2 | `self.right.identity = self.left.identity;` dropped from `desymmetrize` | `mono_collapse::a_desymmetrized_bank_carries_the_collapsed_channels_identity_flags_and_dry_masks`: dev through `identity_flags_agree` (`lib.rs`), release on the right plane at block 27 after the left-only retarget | RED | | 1328-M3 | `self.right.dry = self.left.dry;` dropped from `desymmetrize` | the same test, dev only (`identity_flags_agree`); release renders the same bits (a stale dry lane runs the identity section wet, which moves at most `-0.0` to `+0.0`, and this input moves none) | RED (dev) | @@ -459,7 +459,7 @@ it names, the named tests run, and the source restored. |---|---|---|---| | 1328-M4 | the rest threshold always armed (attempt 3's law: `silence_step` returns `REST_EPS`, `silence_armable` always true) | `exact_rest::a_sparse_input_through_four_boosting_shelves_gets_every_boost_at_every_rate` (`44100 Hz, 10 Hz shelves, period 2 … misses the ordinary run by 1.102885e3`), `a_tiny_input_through_four_boosting_shelves_gets_every_boost`, `an_exact_zero_inside_the_cascade_moves_nothing_while_the_input_is_live` | RED | | 1328-M5 | A8's law: armed by a zero *section* input (thresholds from `v0 == 0` in `svf_step_when`, every block armable) | the sparse gate (`… misses the ordinary run by 1.0375549e3`) and the cancellation gate (`DC at 1e-6 … misses the ordinary run by 6.996817e-5 … (peak 7.826055e3)`); the chain gate stays green | RED | -| 1328-M6 | `Channel::commit_track` drops the restored silence word | `carry::a_restore_mid_silence_arms_the_joint_flush_on_the_continuing_lanes_frame` (deleted by TA5), `carry::a_malformed_silence_word_is_refused` | RED | +| 1328-M6 | `Channel::commit_track` drops the restored silence word | `carry::a_restore_mid_silence_arms_the_joint_flush_on_the_continuing_lanes_frame` (deleted by TA5; catcher `a_restore_refused_on_one_channel_moves_neither`), `carry::a_malformed_silence_word_is_refused` | RED | | 1328-M7 | `desymmetrize` drops `self.right.silence = self.left.silence` | `mono_collapse::a_desymmetrized_bank_carries_the_collapsed_channels_silence_counter` | RED | | 1328-M8 | the silent fast path earned without every lane armed (`quiet && (armed \|\| true)`) | `exact_rest::a_fixed_point_inside_the_joint_band_is_cleared_once_the_flush_arms` | RED | | 1328-M9 | the silent fast path skips without `silence_advance` | the same test (`the silence counter after 68 silent blocks`) | RED | @@ -476,10 +476,3 @@ The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) delete - `disabled_cuts_preserve_all_high_pass_signed_zero_against_four_section_oracle`: parametric-eq `elision::a_discontinuity_reset_mid_ramp_refreshes_the_identity_flag` - `a_tiny_restored_disabled_cut_state_refuses_elision_but_preserves_old_bands`: parametric-eq `elision::a_discontinuity_reset_mid_ramp_refreshes_the_identity_flag` - `a_mid_ramp_restore_continues_bit_for_bit_at_every_sample`: no plausible same-crate KEEP catcher recorded - -## TA5 deletions - -The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. - -- `the_low_shelf_fixed_point_reaches_exact_rest`: parametric-eq `a_restore_refused_on_one_channel_moves_neither`, `the_collapsed_body_arms_its_joint_flush` -- `a_restore_mid_silence_arms_the_joint_flush_on_the_continuing_lanes_frame`: parametric-eq `a_restore_refused_on_one_channel_moves_neither`, `the_collapsed_body_arms_its_joint_flush` diff --git a/crates/rack/tests/MUTATIONS.md b/crates/rack/tests/MUTATIONS.md index 7c3fecc5b..5e37dc1b2 100644 --- a/crates/rack/tests/MUTATIONS.md +++ b/crates/rack/tests/MUTATIONS.md @@ -23,8 +23,8 @@ cargo test --locked -p rack -p rack-compiler -p graph-compiler |---|---|---|---|---|---| | M1 | `scatter_lane` reads the wrong lane: `*sample = chunk[lane]` becomes `chunk[lane ^ 1]` | `rack/src/lib.rs` | `gather_scatter_round_trip_is_bit_exact` | RED | ``assertion `left == right` failed: left lane=0 frame=0`` | | M2 | `gather_lane` chunks by frames instead of lanes: `chunks_exact_mut(lanes)` becomes `chunks_exact_mut(frames as usize)` | `rack/src/lib.rs` | `stage_sees_frame_major_layout` | RED | ``assertion `left == right` failed: lane=0 frame=1`` | -| M3 | `BankChain::run` drops the identity-slot guard: `if slot.active_lanes.iter().any(..)` becomes `if true` | `rack/src/lib.rs` | `identity_everywhere_slot_is_not_executed` (deleted by TA5) | RED | ``assertion `left == right` failed: both live slots ran once per block; the identity-everywhere slot never ran`` | -| M3b | the transpose counter moves inside the slot loop (master plan §4.5: it is *per chain*, not per slot) | `rack/src/lib.rs` | `identity_everywhere_slot_is_not_executed` (deleted by TA5) | RED | ``assertion `left == right` failed`` (2 live slots ⇒ 10 counted round-trips for 5 blocks) | +| M3 | `BankChain::run` drops the identity-slot guard: `if slot.active_lanes.iter().any(..)` becomes `if true` | `rack/src/lib.rs` | `identity_everywhere_slot_is_not_executed` (deleted by TA5; catcher `prepared_slot_activity_preserves_dispatch_trace_and_errors`) | RED | ``assertion `left == right` failed: both live slots ran once per block; the identity-everywhere slot never ran`` | +| M3b | the transpose counter moves inside the slot loop (master plan §4.5: it is *per chain*, not per slot) | `rack/src/lib.rs` | `identity_everywhere_slot_is_not_executed` (deleted by TA5; catcher `prepared_slot_activity_preserves_dispatch_trace_and_errors`) | RED | ``assertion `left == right` failed`` (2 live slots ⇒ 10 counted round-trips for 5 blocks) | | M4 | `BankChain::new` stops enforcing "a slot may only be active on an active lane" | `rack/src/lib.rs` | `chain_new_rejects_mask_shape_and_lane_implication` | RED | ``assertion `left == right` failed: a slot may not be active on a lane the chain never gathers`` | | M5 | `AoSoaScratch::new` re-adds a plane: `vec![0.0; length]` becomes `vec![0.0; length * 2]` for the left plane | `rack/src/lib.rs` | `scratch_allocates_exactly_two_planes` | RED | ``assertion `left == right` failed`` | | M6 | `gather_lane` drops the first frame of every block: `.zip(left)` becomes `.zip(&left[1..])` | `rack/src/lib.rs` | `chain_run_is_partition_invariant` | RED | ``assertion `left == right` failed: partition=1 lane=0 frame=0`` | @@ -305,9 +305,3 @@ the graph suites of `graph/tests/MUTATIONS.md` issue #915, and console-workload Row 915-R1 is the reason the rack half of gate 1 exists. Whether the staging block is written is invisible to every digest, so it is pinned where it can be seen. - -## TA5 deletions - -The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. - -- `identity_everywhere_slot_is_not_executed`: rack `tests::full_bank_gather_scatter_round_trip_is_bit_exact`, `tests::prepared_slot_activity_preserves_dispatch_trace_and_errors` diff --git a/crates/transient-shaper/tests/MUTATIONS.md b/crates/transient-shaper/tests/MUTATIONS.md index 679296bc2..49ddbc030 100644 --- a/crates/transient-shaper/tests/MUTATIONS.md +++ b/crates/transient-shaper/tests/MUTATIONS.md @@ -190,10 +190,3 @@ dropped. The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - `the_scalar_product_is_partition_invariant`: no plausible same-crate KEEP catcher recorded - -## TA5 deletions - -The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. - -- `link_modes_drive_the_detector_as_specified`: no same-crate KEEP catcher recorded -- `the_bank_is_partition_invariant`: transient-shaper `bank_snapshot_restore_and_resets_are_track_local` diff --git a/crates/true-peak-limiter/tests/MUTATIONS.md b/crates/true-peak-limiter/tests/MUTATIONS.md index 9f5ebe3d7..49451d9d1 100644 --- a/crates/true-peak-limiter/tests/MUTATIONS.md +++ b/crates/true-peak-limiter/tests/MUTATIONS.md @@ -27,7 +27,7 @@ cargo test -p true-peak-limiter --lib phase_outputs | 6 | `flush(target.max(released))` → `target.max(released)` | `src/lib.rs` `channel_frame` | `silence_restores_exact_identity_including_signed_zero` | | 7 | van Herk suffix pass guarded by `complete && width < 2`, so it never runs at W4/W8 | `src/lib.rs` `sliding_minimum` | `lane_identity_holds_across_widths` | | 8 | `left.prefix.fill(1.0)` / `right.prefix.fill(1.0)` at block entry (min-filter prefix not carried across blocks) | `src/lib.rs` `limiter_block` | `partition_invariance_holds_over_block_sizes` (deleted by TA4) | -| 9 | `return;` inserted after `limiter_block` so the §4.4 boundary check never runs | `src/lib.rs` `LimiterCore::process_block` | `a_nonfinite_block_is_zeroed_reset_and_counted` (deleted by TA5) | +| 9 | `return;` inserted after `limiter_block` so the §4.4 boundary check never runs | `src/lib.rs` `LimiterCore::process_block` | `a_nonfinite_block_is_zeroed_reset_and_counted` (deleted by TA5; no catcher recorded) | | 10 | the `recomputed == box_sum` check in restore disabled | `src/lib.rs` `read_lane` | `state_round_trips_and_rejects_corruption` | | 11 | `peak = history[6].abs()` → `peak = L::zero()` (sample term dropped from `P`) | `src/lib.rs` `detector_peak` | `fixed_latency_guarded_ceiling_and_bypass_bits_hold` | | 12 | `box_sum.div(window)` → `box_sum.mul(1.0.div(window))` (reciprocal instead of divide) | `src/lib.rs` `channel_frame` | `silence_restores_exact_identity_including_signed_zero` | @@ -146,7 +146,7 @@ Every row below is a **bit** test. The claims here are about *when* a state word | # | mutation | file | test that turned red | |---|---|---|---| | round2-1 | the block-end write-back of `prefix` dropped | `src/lib.rs` `limiter_block_uniform` | `a_mixed_lookahead_cohort_falls_back_bit_identically`, `a_restore_that_desyncs_the_phase_falls_back`, `fixed_latency_guarded_ceiling_and_bypass_bits_hold`, `lane_identity_holds_across_widths`, `partition_invariance_holds_over_block_sizes` (deleted by TA4) | -| round2-2 | the block-end write-back of `phase` dropped, so the cohort restarts each block at the phase it entered the previous one with | `src/lib.rs` `limiter_block_uniform` | `a_de_zipper_window_open_across_a_block_boundary_refuses_the_claim`, `a_mixed_lookahead_cohort_falls_back_bit_identically`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5), `a_restore_that_desyncs_the_phase_falls_back`, `a_restore_withdraws_the_silence_claim`, `a_settled_silent_limiter_renders_exactly_the_never_fast_path` | +| round2-2 | the block-end write-back of `phase` dropped, so the cohort restarts each block at the phase it entered the previous one with | `src/lib.rs` `limiter_block_uniform` | `a_de_zipper_window_open_across_a_block_boundary_refuses_the_claim`, `a_mixed_lookahead_cohort_falls_back_bit_identically`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5; catcher `the_bank_renders_its_scalar_instances_under_random_state`), `a_restore_that_desyncs_the_phase_falls_back`, `a_restore_withdraws_the_silence_claim`, `a_settled_silent_limiter_renders_exactly_the_never_fast_path` | | round2-3 | `.min(ring - left_end)` dropped from the segment run | `src/lib.rs` `segment` | `the_segment_walk_visits_the_slots_a_frame_at_a_time_walk_visits` — **and nothing else** | | round2-4 | `.min(ring - left_expiring)` dropped from the segment run | `src/lib.rs` `segment` | `the_segment_walk_visits_the_slots_a_frame_at_a_time_walk_visits` — **and nothing else** | | round2-5 | `.min(ring - start)` dropped from the segment run | `src/lib.rs` `segment` | six render tests, including `a_settled_silent_limiter_renders_exactly_the_never_fast_path` and `a_restore_that_desyncs_the_phase_falls_back` | @@ -240,9 +240,9 @@ the recursive word, the cursors, the phases and all four ramps — are compared, | # | mutation | file | test that turned red | |---|---|---|---| -| 182-6 | input `block_is_positive_zero` legs dropped from the admission test | `src/lib.rs` `LimiterCore::process_block` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5), `a_stale_detector_history_refuses_the_claim` | +| 182-6 | input `block_is_positive_zero` legs dropped from the admission test | `src/lib.rs` `LimiterCore::process_block` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5; catcher `the_bank_renders_its_scalar_instances_under_random_state`), `a_stale_detector_history_refuses_the_claim` | | 182-7 | `block_is_positive_zero(&self.reduction)` dropped from `is_at_silent_rest` | `src/lib.rs` `ChannelState::is_at_silent_rest` | `a_limiter_still_releasing_through_the_silence_is_never_frozen` | -| 182-8 | `block_is_positive_zero(&self.main_ring)` dropped | `src/lib.rs` `ChannelState::is_at_silent_rest` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5) | +| 182-8 | `block_is_positive_zero(&self.main_ring)` dropped | `src/lib.rs` `ChannelState::is_at_silent_rest` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5; catcher `the_bank_renders_its_scalar_instances_under_random_state`) | | 182-9 | `block_is_positive_zero(&self.history)` dropped | `src/lib.rs` `ChannelState::is_at_silent_rest` | `a_stale_detector_history_refuses_the_claim` — **and nothing else** | | 182-10 | output `block_is_positive_zero` legs dropped from the claim | `src/lib.rs` `LimiterCore::process_block` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path` | | 182-11 | `self.cursors.advance(frames, &self.shape)` deleted from the fast path | `src/lib.rs` `LimiterCore::process_block` | `a_settled_silent_limiter_...`, `a_negative_zero_input_...`, `a_stale_detector_history_...` (via the state comparison, not the sample comparison) | @@ -647,10 +647,3 @@ lane 7; the session test's `ch48` row still owns E0 and EL at `Simd8`. The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - `partition_invariance_holds_over_block_sizes`: no plausible same-crate KEEP catcher recorded - -## TA5 deletions - -The 2026-10 test audit (TA5, the batch 2 sample measured at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 2 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 2 recorded no same-crate catcher. - -- `a_nonfinite_block_is_zeroed_reset_and_counted`: no same-crate KEEP catcher recorded -- `a_negative_zero_input_block_is_not_treated_as_silence`: true-peak-limiter `the_bank_renders_its_scalar_instances_under_random_state` diff --git a/hosts/host-web/tests/control_parity.rs b/hosts/host-web/tests/control_parity.rs index 9eca1ba0d..23ddfe631 100644 --- a/hosts/host-web/tests/control_parity.rs +++ b/hosts/host-web/tests/control_parity.rs @@ -3080,7 +3080,7 @@ fn a_table_step_inside_a_deferred_moves_ramp_releases_a_composed_lane_alike_on_b } /// The table step inside a deferred move's ramp (#1711 U1 fold 1), over the session -/// `model_of(rate)`; the composed twin above is its one caller since TA5 deleted the plain case. +/// `model_of(rate)`. fn table_step_inside_a_deferred_move(model_of: fn(u32) -> SessionModel) { let segment = |shape, start, end, from, to| session::AutomationSegment { shape, From 7b0ea9d060257d74a22c08b0f2bf284149000a0b Mon Sep 17 00:00:00 2001 From: BL Date: Tue, 20 Oct 2026 11:21:38 +0000 Subject: [PATCH 5/8] Fold the TA5 verifier: restore 33 tests that no remaining test defends The verifier found five deleted tests that were the only defenders of their claims. Re-reading the 73 DELETE answers that named siblings only by file kept 28 more where no remaining test, by full name, asserts the same claim. 64 of the 162 are now kept and 98 deleted; every DELETE answer names the remaining test. MUTATIONS rows name a catcher that can see the row's mutant or say none is recorded; the 1340 spec's gate 3 now asks for a new test. Co-Authored-By: Claude Opus 5.5 --- ...ultiband-compressor-a-live-bypass-shunt.md | 13 +- ...with-one-protocol-command-on-every-host.md | 2 +- ...-TA5-delete-the-batch-2-redundant-tests.md | 111 ++-- .../TBD-TA5-successor-audit-leads.md | 39 +- crates/builtins/tests/MUTATIONS.md | 4 +- crates/builtins/tests/filter_liveness.rs | 58 ++ crates/capi/src/runtime/live_tests.rs | 104 ++++ crates/compressor/tests/MUTATIONS.md | 6 +- crates/compressor/tests/mono_collapse.rs | 88 +++ crates/controls/tests/live_lanes.rs | 45 +- crates/effect-compiler/src/prepare.rs | 11 + .../tests/parameter_lattice.rs | 29 + crates/effect-runtime/tests/dynamics.rs | 18 + crates/graph/src/runtime.rs | 518 ++++++++++++++++++ crates/graph/tests/MUTATIONS.md | 12 +- crates/host-core/tests/MUTATIONS.md | 4 +- crates/host-core/tests/live_delta.rs | 294 ++++++++++ .../tests/stored_fader_automation.rs | 66 +++ crates/host-core/tests/strip_handles.rs | 151 +++++ crates/host-core/tests/successor_swap.rs | 142 +++++ crates/host-core/tests/symmetry_witness.rs | 42 +- crates/host-core/tests/timeline.rs | 25 + crates/lane/tests/MUTATIONS.md | 2 +- crates/lane/tests/input_chain_arming.rs | 46 ++ crates/math/tests/f1_fast_db_bounds.rs | 18 + crates/multiband-compressor/src/shim.rs | 17 + crates/multiband-compressor/tests/product.rs | 53 ++ crates/parametric-eq/src/lib.rs | 158 ++++++ crates/parametric-eq/tests/MUTATIONS.md | 2 +- crates/protocol/src/controller/tests.rs | 149 +++++ crates/protocol/src/model.rs | 46 +- crates/rack/tests/MUTATIONS.md | 2 +- crates/rack/tests/live_control_bank.rs | 84 ++- crates/session/tests/json_grammar.rs | 14 + crates/transient-shaper/tests/bank.rs | 64 ++- crates/transient-shaper/tests/partition.rs | 127 +++++ crates/true-peak-limiter/tests/MUTATIONS.md | 6 +- hosts/host-web/src/ffi.rs | 12 + hosts/host-web/src/tests.rs | 189 +++++++ 39 files changed, 2675 insertions(+), 96 deletions(-) create mode 100644 crates/host-core/tests/strip_handles.rs create mode 100644 crates/transient-shaper/tests/partition.rs diff --git a/.github/ISSUE_SPECS/1340-give-the-multiband-compressor-a-live-bypass-shunt.md b/.github/ISSUE_SPECS/1340-give-the-multiband-compressor-a-live-bypass-shunt.md index 7a35d8258..5440a9c2f 100644 --- a/.github/ISSUE_SPECS/1340-give-the-multiband-compressor-a-live-bypass-shunt.md +++ b/.github/ISSUE_SPECS/1340-give-the-multiband-compressor-a-live-bypass-shunt.md @@ -43,8 +43,8 @@ bits. This closes decision 14's F4 for the multiband compressor. issue does not change that. - **Tests that pin today's behaviour.** - `crates/graph-compiler/tests/bypass_cohorts.rs:716` (`a_mixed_bypass_multiband_cohort_keeps_its_prepared_bypass`) - and `:794` (`a_hot_bypassed_multiband_lane_leaves_its_neighbours_bits_unchanged`, whose - positive control is the whole-bank recovery under #1087's lowering; deleted by TA5). + and, until TA5 deleted it, `:794` (`a_hot_bypassed_multiband_lane_leaves_its_neighbours_bits_unchanged`, + whose positive control was the whole-bank recovery under #1087's lowering). - `crates/effect-compiler/tests/native_session.rs:516-528` asserts the multiband's prepared bypass and the list. - `crates/multiband-compressor/tests/nonfinite.rs:168` (`the_boundary_is_the_shared_limit_and_a_bank_shares_its_reset`) @@ -162,10 +162,11 @@ bits. This closes decision 14's F4 for the multiband compressor. `initial_bypass == true`, `metadata.bypass == false` and a lane; the cohort binds `TRACKS / lanes()` banks; the render is bit-identical to today's prepared-bypass lowering (`todays_lowering`) and to `Backend::Scalar` at music levels. -3. **A hot bypassed lane in a bound bank.** `a_hot_bypassed_multiband_lane_leaves_its_neighbours_bits_unchanged` - (deleted by TA5) keeps its name and assertion (zero neighbour taps moved), now with the hot lane inside a bound - mixed bank (assert the bank binds). Its in-test positive control under #1087's lowering is - removed, because the lowering is now the production path; D1's red mutation replaces it. +3. **A hot bypassed lane in a bound bank.** A new test (TA5 deleted the old + `a_hot_bypassed_multiband_lane_leaves_its_neighbours_bits_unchanged`) asserts that zero neighbour + taps move with the hot lane inside a bound mixed bank (assert the bank binds). It needs no + positive control under #1087's lowering, because that lowering is now the production path; + D1's red mutation replaces it. 4. **Lowering.** `native_session.rs` asserts that the bypassed multiband entry is lowered (`initial_bypass`, `!metadata.bypass`, a lane), and that every launch descriptor lowers. 5. **A live lift is heard, bit-exactly.** In `crates/host-core/tests/live_delta.rs`: the classifier diff --git a/.github/ISSUE_SPECS/1586-read-observation-windows-with-one-protocol-command-on-every-host.md b/.github/ISSUE_SPECS/1586-read-observation-windows-with-one-protocol-command-on-every-host.md index e25cd1aea..705824d3b 100644 --- a/.github/ISSUE_SPECS/1586-read-observation-windows-with-one-protocol-command-on-every-host.md +++ b/.github/ISSUE_SPECS/1586-read-observation-windows-with-one-protocol-command-on-every-host.md @@ -134,7 +134,7 @@ Commands: `cargo test --locked -p protocol --all-features`, `cargo test --locked no row; 257 selections are refused by the codec. 4. **Browser bytes unchanged.** `selected_observation_reads_are_bounded_stable_and_non_consuming` (`hosts/host-web/src/tests.rs:6318`), `selected_observation_reads_keep_same_track_owner_windows_and_sequences` - (`:6460`, deleted by TA5) and `selected_observation_numeric_read_refuses_failed_host_without_touching_output` + (`:6460`) and `selected_observation_numeric_read_refuses_failed_host_without_touching_output` (`:6550`) pass unchanged. 5. **Same rows on both hosts.** A native test boots the same session on both hosts, arms the same tap (C ABI by `Observe`, browser by kind 7), renders the same PCM, and gets equal status, diff --git a/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md b/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md index 8c800dab6..165d9c71f 100644 --- a/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md +++ b/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md @@ -33,23 +33,24 @@ for allocation, counts, ceilings, bytes, reproducers, randomness) and held 11 as HELD-RANDOMIZED; those are not in the 162. This PR then read each of the 162 and answered the AGENTS.md question, "which plausible defect turns this red that no remaining test catches?". A test is kept when the answer names a real edge case that the measured mutants could not reach and -no remaining test asserts (the remaining tests checked are named per test); otherwise it is -deleted. Reading found what the triage missed: a randomized differential named `run_seeds`, an -allocation audit inside a macro, three tests that a `MUTATIONS.md` records as the only red test of -a row, and one test that `scripts/lib/aarch64-known-defects.py` lists as a known AArch64 failure. +no remaining test asserts. Every DELETE answer names the claim and the remaining test, by its full +name, that asserts it; every KEEP answer names the remaining tests checked. The first pass named +siblings only by file for 73 DELETE answers; the TA5 verifier found that 5 of them were the only +defenders of their claims, so all 73 were read again and kept wherever no remaining test could be +named. Reading also found what the triage missed: a randomized differential named `run_seeds`, an +allocation audit inside a macro, tests that a `MUTATIONS.md` records as the only red test of a +row, and one test that `scripts/lib/aarch64-known-defects.py` lists as a known AArch64 failure. ## What this change does -1. Deletes 131 of the 162 tests and keeps 31 by reading (list below). None of the 162 had moved +1. Deletes 98 of the 162 tests and keeps 64 by reading (list below). None of the 162 had moved or gone since `81a3074fc`; nothing was skipped. -2. Deletes 48 helper items (984 lines) only after `cargo clippy --all-targets --all-features` +2. Deletes 34 helper items (476 lines) only after `cargo clippy --all-targets --all-features` reported them unused, looped until it reported nothing; no `#[allow(dead_code)]` was added. - Deletes two test files left with no test: `crates/host-core/tests/strip_handles.rs` and - `crates/transient-shaper/tests/partition.rs`. + No test file is left empty. 3. Updates the references: each `MUTATIONS.md` row that names a deleted test names a remaining - same-crate catcher that batch 2 recorded red on that test's sampled mutants, or says "(deleted - by TA5; no catcher recorded)". No list of deleted tests is added to a tracked file. Code - comments and open specs (#1339, #1340, #1586, #1713) note a deleted name only where it would + test that can see the row's mutation, or says "(deleted by TA5; no catcher recorded)". No list of deleted tests is added to a tracked file. Code + comments and open specs (#1339, #1340, #1713) note a deleted name only where it would otherwise be a stale reference. Recorded log output and dated records (`docs/handoffs`, `docs/audits`, `artifacts/`) are not edited. 4. Gives the `qualification` job `test-debug-a` ("workspace debug tests (minus DSP crates and @@ -64,43 +65,41 @@ a row, and one test that `scripts/lib/aarch64-known-defects.py` lists as a known | audit | 0 | 1 | 0 | | automation | 1 | 0 | 30 | | bench-support | 1 | 0 | 5 | -| builtins | 5 | 2 | 406 | +| builtins | 4 | 3 | 349 | | builtins-compiler | 0 | 1 | 0 | -| capi | 9 | 1 | 603 | -| compressor | 3 | 0 | 252 | +| capi | 7 | 3 | 501 | +| compressor | 2 | 1 | 165 | | console-workload | 2 | 0 | 83 | | control-plane | 0 | 1 | 0 | -| controls | 3 | 0 | 117 | +| controls | 2 | 1 | 78 | | delay | 1 | 0 | 12 | | dsp-reference | 1 | 1 | 15 | -| effect-compiler | 2 | 1 | 38 | +| effect-compiler | 0 | 3 | 0 | | effect-contract | 2 | 1 | 46 | -| effect-runtime | 5 | 1 | 97 | +| effect-runtime | 4 | 2 | 80 | | engine | 3 | 0 | 139 | | gate-expander | 0 | 2 | 0 | -| graph | 4 | 0 | 326 | +| graph | 3 | 1 | 180 | | graph-compiler | 7 | 0 | 590 | -| host-core | 30 | 3 | 1,270 | -| host-web | 5 | 3 | 328 | -| lane | 4 | 1 | 76 | -| math | 2 | 1 | 23 | -| multiband-compressor | 3 | 0 | 151 | -| parametric-eq | 5 | 2 | 464 | -| protocol | 5 | 2 | 343 | -| rack | 5 | 1 | 322 | +| host-core | 20 | 13 | 735 | +| host-web | 2 | 6 | 142 | +| lane | 3 | 2 | 72 | +| math | 1 | 2 | 6 | +| multiband-compressor | 1 | 2 | 83 | +| parametric-eq | 4 | 3 | 307 | +| protocol | 3 | 4 | 152 | +| rack | 4 | 2 | 248 | | realtime-policy | 11 | 2 | 177 | -| session | 2 | 1 | 34 | +| session | 1 | 2 | 21 | | soft-clip | 2 | 0 | 126 | | source | 3 | 0 | 115 | -| transient-shaper | 3 | 1 | 197 | +| transient-shaper | 1 | 3 | 44 | | true-peak-limiter | 2 | 2 | 63 | -| **total** | **131** | **31** | **6,448** | +| **total** | **98** | **64** | **4,564** | -Lines are the deleted test functions with their doc comments, at `ad5c7ae86`. Shortstat of the -deletion commit: 107 files changed, 129 insertions, 7,783 deletions (tests, helpers, imports, the -two files, and the reference updates). +Lines are the deleted test functions with their doc comments, at `ad5c7ae86`. Shortstat against `ad5c7ae86`: 83 files changed, 349 insertions(+), 5,221 deletions(-). -## Kept by reading (31) +## Kept by reading (64) - audit `fixture_builtins::tests::check_rejects_owned_jsonl_tuple_mutations`: a field or record change in an owned JSONL after the manifest is regenerated; the format test only deletes, @@ -168,6 +167,42 @@ two files, and the reference updates). - true-peak-limiter `production_tracks_the_f64_oracle`: the only independent `f64` oracle of the limiter gain law (E5); E4 bounds the peak only. +Added by the re-reading and the review fold: + +- builtins `an_enable_from_rest_jumps_the_recursion_only_when_the_integrators_are_zero`: an enable from rest jumps the recursion, restored integrators ramp; no remaining test enables from rest. +- capi `runtime::live_tests::a_live_edit_on_a_pending_candidate_completes_at_the_swap`: a live edit on a pending candidate completes at the swap; the remaining candidate test checks lanes and bits, not the watermark. +- capi `runtime::live_tests::a_removed_fader_ride_releases_to_the_static_fader`: a removed ride releases to a fresh static-fader plan; the remaining removal tests compare host with host. +- compressor `a_statically_bypassed_bank_collapses_to_the_dual_bits`: a prepared-bypass bank collapses to the dual bits; the only one (compressor MUTATIONS 985-M4). +- controls `the_effect_lane_follows_the_effect_rows`: an insert or console slot attaches the effect lane; the remaining lane test covers route and input rows. +- effect-compiler `every_builtin_row_declares_a_lawful_lattice_at_every_launch_rate`: every builtin row has a lawful lattice at every launch rate; the others check three rows. +- effect-compiler `prepare::owner_tests::invalid_edit_poison_survives_valid_overwrite_until_discard`: a valid write does not clear a poisoned candidate; the sibling writes nothing after the poison. +- effect-runtime `the_ratio_sets_the_slope`: ratio 1 is a bit-exact identity and a ratio below 1 expands; no other test uses a ratio below 1. +- graph `runtime::tests::the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride`: the only red test of graph MUTATIONS rows 916-1, 916-1b, 916-6 and 916-15 (restored after review). +- host-core `a_repointed_route_does_not_carry_its_line_at_every_rate_and_width`: a repointed route does not carry its PDC line; the sibling covers the send value only. +- host-core `a_seek_step_after_the_unmute_ramps_end_in_its_block_still_lands`: a seek step after the unmute ramp's end in the same block lands (#1647 MAJOR-4). +- host-core `a_submix_strip_fader_mute_and_pan_are_live`: a submix pan is a live record at the bus ID; fader and mute are covered elsewhere, pan is not. +- host-core `a_successor_at_another_rate_or_quantum_starts_a_fresh_timeline`: a successor at another rate or quantum gets a fresh timeline producer (the D3 guard). +- host-core `handles_list_tracks_then_submixes_and_file_bus_effects`: strip handle order, per-track meters and bus effect channels and observation handles (host-core MUTATIONS 1211-M5). +- host-core `send_records_are_the_folded_target_of_the_post_commit_values`: a gain edit on a muted send gives no record and the unmute carries it; no other test edits a muted send. +- host-core `spectrum_carry::a_continuous_capture_carries_and_its_reader_keeps_receiving`: a continuous spectrum capture reads across a swap with no gap; the siblings cover one-shot captures. +- host-core `submix_effect_params_are_live`: a submix console-slot parameter is a record at the console address; the sibling covers an insert bypass. +- host-core `two_per_lane_target_writes_that_agree_in_one_block_keep_the_lane`: #1345 D3 positive arm, in host-core (restored after review; console-workload's twin stays deleted). +- host-core `vca_and_follow_records_take_the_ramp_of_the_edit_that_moved_them`: VCA, membership and strip-own records take their edit's ramp on non-automated lanes. +- host-web `tests::selected_observation_reads_keep_same_track_owner_windows_and_sequences`: two effects of one track keep their own observation windows and sequences. +- host-web `tests::source_seek_keeps_the_meter_epoch_and_absolute_peak_clock`: a source seek keeps the meter epoch and the absolute peak clock. +- host-web `tests::the_seek_exports_refuse_a_host_with_no_control_half_and_change_nothing`: the seek exports' lifecycle refusals and `source_seek_report_ptr` returning 0 when not ready, an ABI rule (restored after review). +- lane `any_state_word_in_the_band_arms_the_block`: any state word of any section in the band arms the block; the siblings plant one word. +- math `f1_gain_from_db_subsample`: the only per-PR check of the GAIN_MAX_DB bound; the full sweep is nightly (restored after review). +- multiband-compressor `each_channel_counts_its_own_input`: #1328 A9 exact per-channel silence counts (restored after review). +- multiband-compressor `shim::tests::the_smoother_has_exact_ends`: the smoother's exact ends (coefficient 0 arrives, 1 freezes); the sibling uses fixed coefficients. +- parametric-eq `padded_banks::a_padded_lane_fed_positive_zero_writes_positive_zero_at_rest`: a padded EQ lane fed +0.0 writes exactly +0.0 in every schedule. +- protocol `controller::tests::b3b1_btlv_telemetry_echo_and_counters_are_typed_and_nondestructive`: BTLV telemetry configure echo, idempotence, non-resetting counter reads, NotFound selector. +- protocol `model::tests::effects_and_parameters_follow_exact_key_rules`: a repeated (parameter, channel) key leaves one entry with the last value. +- rack `run_hands_the_blocks_snapshot_to_every_active_slot_in_every_block`: each block's snapshot reaches every active slot, silent blocks too (the watermark must not run early). +- session `insignificant_whitespace_and_shuffled_keys_canonicalize`: shuffled keys and whitespace canonicalize to the canonical bytes (canonical JSON rule). +- transient-shaper `a_padded_request_binds_and_still_validates_every_lane`: a padded transient-shaper bank request validates its clones; the conformance probes use full masks. +- transient-shaper `the_bank_is_partition_invariant`: the transient-shaper bank partition invariance, the crate's only one. + ## Deleted helpers (compiler-reported) - `crates/capi/src/runtime/live_tests.rs`: `live_alone` @@ -175,23 +210,17 @@ two files, and the reference updates). - `crates/dsp-reference/src/svf.rs`: `IMPULSE_FRAMES` - `crates/engine/src/realtime/spsc.rs`: `fail_if_stalled`, `STALL_DEADLINE`, `EndedOnDrop` and its `Drop` -- `crates/graph/src/runtime.rs`: `HostShape` and its impl, `render_arena_oracle`, - `fill_output_slot`, `output_slot`, `stereo_bits`, `OUTPUT_SLOT_FILL`, `direct_bank_parts`, - `test_only_set_host_master_declined` - `crates/host-core/src/prepare.rs`: `instance`; `tests/browser_successor.rs`: - `session_without_eq1`; `tests/live_delta.rs`: `with_effect_bus`; `tests/spectrum.rs`: - `render_window`; `tests/successor_swap.rs`: `a_repointed_route_does_not_carry_its_line`, - `two_bus_session` + `session_without_eq1`; `tests/spectrum.rs`: `render_window` - `crates/lane/tests/f64_lane.rs`: `square_mismatches`, `square_pool`, `oracle_square_bits`; `tests/fader_matrix.rs`: `fused_select_free_width`, `FUSED_FRAMES`, `fused_select_free_differences`, `FusedDifferences` and its `record`, `FUSED_MUTES`, - `FUSED_GAINS`, `fused_family_source`; `tests/input_chain_arming.rs`: `every_state_word_arms` + `FUSED_GAINS`, `fused_family_source` - `crates/math/tests/m2_lane_identity.rs`: `exp2_caller_edges`, `FastGain` and its `LaneFn` - `crates/multiband-compressor/tests/crossover.rs`: `rms`, `complex_gain`, `CROSSOVERS` - `crates/parametric-eq/tests/carry.rs`: `same_bits`; `tests/exact_rest.rs`: `at_rest`, `HELD_BLOCKS`, `REST_BOUND_SAMPLES` - `crates/rack/src/lib.rs`: `WrongWidthPlanes` and its `BankMembers` -- `hosts/host-web/src/ffi.rs`: `test_seek_report_addresses` Leads the compiler does not report are in the successor spec `TBD-TA5-successor-audit-leads.md`. diff --git a/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md b/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md index 6f22fd2ec..f688a6491 100644 --- a/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md +++ b/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md @@ -1,28 +1,23 @@ -# Record the leads the last test-audit PR found and did not fix (TA5 successor) +# Record the lead the last test-audit PR found and did not fix (TA5 successor) -TA5 (`TBD-TA5-delete-the-batch-2-redundant-tests.md`) deleted 131 tests of the batch 2 sample and -did not add scope. It found two leads. Neither blocks TA5. +TA5 (`TBD-TA5-delete-the-batch-2-redundant-tests.md`) deleted 98 tests of the batch 2 sample and +did not add scope. It found one lead. It does not block TA5. -## Leads +## Lead -1. **An orphaned test seam in `graph`.** TA5 deleted `test_only_set_host_master_declined` - (`crates/graph/src/runtime.rs`; compiler-reported unused once its only caller, the #916 gate - `the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride`, went). The - `#[cfg(test)]` thread-local `HOST_MASTER_DECLINED` it wrote stays: `preflight_sequential` still - reads it, nothing sets it, so the clause is always false. The compiler does not report a read - of a never-written cell. Delete the thread-local and its read, or record why it stays. -2. **The redundancy measurement over-reports on its own.** Of batch 2's 162 MEASURED-REDUNDANT - tests, reading kept 31. Five of them carry a claim that another record states and the name - triage did not read: three are the only red test of a `MUTATIONS.md` row (gate-expander row 3, - effect-contract 140-1, parametric-eq recorded failures 1 and 1b), one is a known AArch64 - release failure in `scripts/lib/aarch64-known-defects.py` (`m2_exp2_lane_identity`, #1019), - and one is a randomized differential named `run_seeds` (host-core - `a_bus_renders_the_bits_of_a_track_fed_its_sum`). B2's projection of about 2,647 deletable pool - tests therefore does not stand without that cross-check. If a pool-wide cut is ever reopened, - its brief must first match every candidate against the `MUTATIONS.md` sole-catcher rows, the - known-defect lists and the seeded-differential helpers, and must still read each body. +**The redundancy measurement over-reports on its own.** Of batch 2's 162 MEASURED-REDUNDANT +tests, reading kept 64. Some carry a claim that another record states and the name triage did not +read: the only red test of a `MUTATIONS.md` row (gate-expander row 3, effect-contract 140-1, +parametric-eq recorded failures 1 and 1b, graph 916-1 to 916-15), a known AArch64 release failure +in `scripts/lib/aarch64-known-defects.py` (`m2_exp2_lane_identity`, #1019), and a randomized +differential named `run_seeds` (host-core `a_bus_renders_the_bits_of_a_track_fed_its_sum`). +Others were the only test of a claim that only reading against named siblings shows; a first pass +that cited siblings only by file deleted five of those, and review restored them. B2's +projection of about 2,647 deletable pool tests therefore does not stand. If a pool-wide cut is +ever reopened, its brief must first match every candidate against the `MUTATIONS.md` +sole-catcher rows, the known-defect lists and the seeded-differential helpers, and every DELETE +answer must name, by its full name, the remaining test that asserts the same claim. ## Gate -Lead 1: the seam is gone or documented, with workspace clippy and the graph tests green. Lead 2: -no work unless a pool-wide cut is reopened; then the reopening brief cites this item. +No work unless a pool-wide cut is reopened; then the reopening brief cites this item. diff --git a/crates/builtins/tests/MUTATIONS.md b/crates/builtins/tests/MUTATIONS.md index 2ab45a67a..c641f1754 100644 --- a/crates/builtins/tests/MUTATIONS.md +++ b/crates/builtins/tests/MUTATIONS.md @@ -12,7 +12,7 @@ Command form: `cargo test -p builtins --test -- --exact`. | M1 | `let denominator = 1.0 + t1;` → `1.0 - t1` | `builtins/src/lib.rs` (`SvfSection::design`) | T1 `prepared_sections_match_reference_coefficients` | FAILED, `rate=44100, cutoff=10, high_pass=true` | | M2 | drop the D7 flush of `s1`, joint arm included: `self.s1 = if rest { 0.0 } else { flush(n1) }` → `self.s1 = n1` (#1328; before it, `self.s1 = flush(n1)` → `n1`, red at `index=3670`). Dropping only the per-word arm (`else { n1 }`) stays green: this signal never leaves `n1` below `FLUSH_EPS` beside an `n2` at or above `REST_EPS` | `dsp-reference/src/tpt.rs` | T2 `scalar_stage_is_bit_identical_to_reference_recurrence` | FAILED, `rate=44100, signal=1, index=2645` (re-run for #1328) | | M2b | drop only the per-word arm of the D7 flush (#1328): `self.s1 = if rest { 0.0 } else { flush(n1) }` → `else { n1 }`, and separately the same for `s2` | `dsp-reference/src/tpt.rs` | T2b `scalar_stage_is_the_reference_recurrence_where_the_per_word_flush_fires` (T2 stays green on both: its signals reach rest through the joint arm) | FAILED, `rate=44100, index=257` (`s1`) and `rate=44100, index=276` (`s2`) | -| M3a | every bank lane takes lane 0's coefficients (`zip(sections)` → `sections[0]`) | `builtins/src/lib.rs` (`svf_coef`) | T3 `bank_is_bit_identical_to_scalar_stage_at_every_width` (deleted by TA5; catcher `identity_chain_renders_audit_clean_deterministic_and_positive_zero`) | FAILED, `width=4, members=3, lane=1, frame=0` | +| M3a | every bank lane takes lane 0's coefficients (`zip(sections)` → `sections[0]`) | `builtins/src/lib.rs` (`svf_coef`) | T3 `bank_is_bit_identical_to_scalar_stage_at_every_width` (deleted by TA5; catcher `randomized::the_banks_render_their_scalar_sections_under_random_retargets`) | FAILED, `width=4, members=3, lane=1, frame=0` | | M3c | report counters sum every lane, not only the members (`take(self.members)` → `take(L::WIDTH)`) | `builtins/src/lib.rs` (`InputStage::members_sum`) | T3, padding-lane arm | FAILED, `width=4, members=1` | | M4 | high-pass mix `m1 = -k` → `-k * 1.001` | `builtins/src/lib.rs` (`SvfSection::design`) | T1, T2 **and** the determinism corpus | all three FAILED | | M5 | the fixture oracle drops the identity-section `-0.0` mapping | `tools/audit/src/fixture_builtins.rs` | `issue064_checked_corpus_is_read_only_complete_and_has_no_authoring_reachability` | FAILED at the PCM semantics check | @@ -200,7 +200,7 @@ what the rows below record. | # | mutation | site | suite | verdict | |---|---|---|---|---| | C-1 | the retarget writer `set_trim_signed` drops its refresh | `builtins/src/lib.rs` | `input_liveness_mono` (3 tests) | RED — `the cached channel-symmetry flag is stale for lane 0`, on `an_asymmetric_retarget_declines_the_lane_on_the_admitting_block`, `re_equalising_the_words_restores_the_designed_term_and_nothing_more` and `a_retarget_between_a_collapsed_block_and_the_disengage_survives_the_copy`. This is the drain's own invalidation, and it is the one a live-words cache exists to have | -| C-2 | the **dual** ramping arm of `process` drops its refresh | `builtins/src/lib.rs` | `host-core::input_liveness_live_controls` (2 tests) | RED — `stale for lane 2`, on `re_equalising_after_a_disengaging_drain_holds_the_never_collapsed_bits` (deleted by TA5; no catcher recorded) and `the_collapse_census_follows_the_commands`. A ramp advances the compared words on every block it runs, with no record drained: a drain-only invalidation is **not** sufficient, and this row is the proof | +| C-2 | the **dual** ramping arm of `process` drops its refresh | `builtins/src/lib.rs` | `host-core::input_liveness_live_controls` (2 tests) | RED — `stale for lane 2`, on `re_equalising_after_a_disengaging_drain_holds_the_never_collapsed_bits` (deleted by TA5; catcher `the_collapse_census_follows_the_commands`) and `the_collapse_census_follows_the_commands`. A ramp advances the compared words on every block it runs, with no record drained: a drain-only invalidation is **not** sufficient, and this row is the proof | | C-3 | the **collapsed** ramping arm of `process_mono` drops its refresh (after `mirror_trim_ramp`) | `builtins/src/lib.rs` | — (green across `builtins`, `rack`, `host-core`, `console-workload`, `parametric-eq`, `builtins-compiler`, `graph`) | **EQUIVALENT, and kept anyway.** `mirror_trim_ramp` assigns channel `1`'s whole record from channel `0`'s, so every compared ramp word is equal when it returns -- and it was equal on entry too, which `debug_assert!(self.trim_ramp_channels_agree())` at the top of `process_mono` states. The comparison therefore lands where it already was. The refresh is kept because the equivalence rests on the mirror being *total*: a mirror that ever became partial or conditional would make this row red, and the cost is one walk on a ramping collapsed block | ### Row count diff --git a/crates/builtins/tests/filter_liveness.rs b/crates/builtins/tests/filter_liveness.rs index 823866a0c..1a6f59be0 100644 --- a/crates/builtins/tests/filter_liveness.rs +++ b/crates/builtins/tests/filter_liveness.rs @@ -547,6 +547,64 @@ fn a_disable_in_the_enables_drain_still_completes_and_elides() { } } +/// Gate 3: an enable from a settled disabled section jumps the recursion and crossfades the +/// mix, so its first sample is the dry input; restored integrators on an identity section take +/// the six-word ramp instead. +#[test] +fn an_enable_from_rest_jumps_the_recursion_only_when_the_integrators_are_zero() { + for rate in LAUNCH_RATES { + for section in 0..2 { + let target = design(rate, section, if section == 0 { 200.0 } else { 3_000.0 }); + let words = target.coefficients; + let mut input = disabled_at(rate); + input.apply_prepared_filter(target).expect("enable"); + let output = render_frame(&mut input, 0, 0.625); + assert_eq!(output[0].to_bits(), 0.625_f32.to_bits()); + assert_eq!(output[1].to_bits(), (-0.625_f32).to_bits()); + // The kernel's frame-0 word, as it computes it: the start plus one step. + let after_one = |start: f32, target: f32| start + (target - start) * (1.0 / 64.0); + let identity_mix = [1.0_f32, 0.0, 0.0]; + let expected = [ + words[0], + words[1], + words[2], + after_one(identity_mix[0], words[3]), + after_one(identity_mix[1], words[4]), + after_one(identity_mix[2], words[5]), + ]; + for channel in 0..2 { + assert_eq!( + current_words(&input, channel, section), + bits6(expected), + "rate {rate} section {section} channel {channel}" + ); + } + + // Restored non-zero integrators on the identity section: rule 4. + let mut restored = disabled_at(rate); + let mut state = [0_u32; 8]; + for channel in 0..2 { + state[channel * 4 + section * 2] = 0.25_f32.to_bits(); + state[channel * 4 + section * 2 + 1] = (-0.125_f32).to_bits(); + } + builtins::test_support::set_input_state_words(&mut restored, state); + restored.apply_prepared_filter(target).expect("enable"); + render_frame(&mut restored, 0, 0.625); + let ramped: [f32; 6] = core::array::from_fn(|index| { + let start = [0.0, 0.0, 0.0, 1.0, 0.0, 0.0][index]; + after_one(start, words[index]) + }); + for channel in 0..2 { + assert_eq!( + current_words(&restored, channel, section), + bits6(ramped), + "restored rate {rate} section {section} channel {channel}" + ); + } + } + } +} + /// `||M||_V` in `f64` for a 2x2 matrix `M`: the operator norm induced by /// `||x||_V^2 = x1^2 + sqrt(2) x1 x2 + x2^2`, the shared eigenbasis norm of every `k = sqrt(2)` /// design. With `R = [[1, r], [0, r]]`, `r = 1/sqrt(2)` (so `||x||_V = ||R x||_2`), it is the diff --git a/crates/capi/src/runtime/live_tests.rs b/crates/capi/src/runtime/live_tests.rs index a44dad4a0..656f33064 100644 --- a/crates/capi/src/runtime/live_tests.rs +++ b/crates/capi/src/runtime/live_tests.rs @@ -1502,6 +1502,73 @@ fn watermark(rig: &Rig) -> (u64, u64, u64, u64) { ) } +/// #1314 gate 2: a rebuild (r1) and then a live fader edit (r2), committed before any render, +/// both complete at the swap block and not one block earlier: the live edit went to the pending +/// candidate's revision gate, so the running plan never reports either revision. +#[test] +fn a_live_edit_on_a_pending_candidate_completes_at_the_swap() { + let document = long_session(1, 48_000); + let model = parse_session_json(&document).expect("model"); + let mut rig = Rig::new(&document); + let initial = rig.summary().0; + for _ in 0..3 { + rig.step(); + assert_eq!(watermark(&rig), (initial, 0, crate::OUTCOME_EXACT, 0)); + } + assert_eq!(rig.apply(&[content_edit(&model)]), crate::RESULT_OK); + let r1 = rig.summary().0; + assert_eq!(rig.summary().3, 1, "a candidate is pending"); + let edit = fader_edit( + "eq0", + DualMonoFader { + left_db: -6.0, + right_db: -6.0, + left_mute: false, + right_mute: false, + }, + ); + assert_eq!(rig.apply(&edit), crate::RESULT_OK); + let r2 = rig.summary().0; + assert_eq!((r1, r2), (initial + 1, initial + 2)); + assert_eq!( + rig.summary().3, + 1, + "the live edit went to the one candidate" + ); + assert_eq!(watermark(&rig), (initial, 0, crate::OUTCOME_EXACT, 0)); + + let k = rig.block; + rig.render(); + assert_eq!( + watermark(&rig), + (r2, k * rig.quantum as u64, crate::OUTCOME_EXACT, 2) + ); + + // A rebuild alone carries its own revision to its swap. + rig.render(); + let source = &model.sources[0]; + let edit = SessionEdit::SetSourceContent { + source_id: source.id.clone(), + content: format!("blake3:{}", "cd".repeat(32)), + channels: source.channels, + bit_depth: source.bit_depth, + frames: source.frames, + }; + assert_eq!(rig.apply(&[edit]), crate::RESULT_OK); + let r3 = rig.summary().0; + assert_eq!(rig.summary().3, 1, "a second candidate is pending"); + assert_eq!( + watermark(&rig), + (r2, k * rig.quantum as u64, crate::OUTCOME_EXACT, 2) + ); + let swap = rig.block; + rig.render(); + assert_eq!( + watermark(&rig), + (r3, swap * rig.quantum as u64, crate::OUTCOME_EXACT, 3) + ); +} + /// #1314 gate 3: on one thread, a live edit committed between blocks 3 and 4 completes in block 4, /// from its first sample, and block 5 publishes nothing new. #[test] @@ -10007,6 +10074,43 @@ fn an_unchanged_fader_ride_carries_with_no_jump() { } } +/// #1648 gate 4. Removing the ride rebuilds; the released lane ramps from the curve's last target +/// to its static value over the fader jump length, and from the end of that release every sample +/// equals a fresh plan of the new session (a static fader), session-seeked to the adoption block. +/// +/// Test value: red if a removed entry leaves the lane on its last automated value, or steps it. +#[test] +fn a_removed_fader_ride_releases_to_the_static_fader() { + let (old, new) = (ride_document(Some(-12.0)), ride_document(None)); + let mut rig = Rig::new(&old); + for _ in 0..8 { + rig.step(); + } + let edit = SessionEdit::RemoveAutomation { + automation_id: StableId::parse("fader-ride").expect("automation ID"), + }; + let (adoption, live) = rebuild_and_adopt(&mut rig, &[edit], ADOPTED_BLOCKS); + let reference = seeked_reference(&new, adoption, ADOPTED_BLOCKS); + let jump = 960_usize; + let quantum = rig.quantum; + for plane in 0..2 { + let (live, reference) = ( + plane_bits(&live, plane, quantum), + plane_bits(&reference, plane, quantum), + ); + assert_ne!( + live[jump - quantum..jump - 1], + reference[jump - quantum..jump - 1], + "plane {plane}: the lane is still in its release" + ); + assert_eq!( + live[jump - 1..], + reference[jump - 1..], + "plane {plane}: the static fader's bits from the release's last sample" + ); + } +} + /// #1652 gate 1: track `eq0` sends `pre_fader` (its right lane, `rr` only) with `follows_mute` /// into bus `b`, and its direct route takes only its left lane to the output, so the output's /// left plane is the strip and its right plane the send. With `entry`, `eq0` has a mute entry on diff --git a/crates/compressor/tests/MUTATIONS.md b/crates/compressor/tests/MUTATIONS.md index 51e882b4d..c6a8288a0 100644 --- a/crates/compressor/tests/MUTATIONS.md +++ b/crates/compressor/tests/MUTATIONS.md @@ -169,7 +169,7 @@ tree restored between rows. | M2-C7 | `Channel::copy_state_from` drops `main` | `compressor/src/kernel.rs` | `a_desymmetrized_bank_is_a_never_collapsed_bank` | RED — the delay ring the output is read out of | | M2-C8 | `Channel::copy_state_from` drops `detector` | `compressor/src/kernel.rs` | `a_desymmetrized_bank_is_a_never_collapsed_bank` | RED — the detector ring the lookahead tap gathers from | -`a_statically_bypassed_bank_collapses_to_the_dual_bits` (deleted by TA5; catcher `mono_reopen_drives_the_next_render_from_the_copied_state`) carries no mutation of its own: it is +`a_statically_bypassed_bank_collapses_to_the_dual_bits` carries no mutation of its own: it is coverage for the one place a **prepared** bypass and a **live** one differ. A live bypass clears the witness' `UNBYPASSED` term and declines the collapse; a prepared bypass on a live-control-free bank does not, because `EffectBankStage::lane_symmetry` is the designed-word comparison alone. The effect @@ -230,7 +230,7 @@ differentials see dispatch as well as bits. | # | mutation | red | |---|---|---| -| 982-M1 | the arm ignores `bypass` | `identity::bypass_preserves_exact_dry_bits_at_sample_zero` (deleted by TA4), gate 1 (`the_all_wet_arm_is_the_base_body_on_all_wet_tables`, the three differentials), gate 3, `conformance`, `contract::every_launch_rate_processes_scalar_and_supported_bank_at_zero_latency`, `mono_collapse::a_statically_bypassed_bank_collapses_to_the_dual_bits` (deleted by TA5; catcher `mono_reopen_drives_the_next_render_from_the_copied_state`) (9 red) | +| 982-M1 | the arm ignores `bypass` | `identity::bypass_preserves_exact_dry_bits_at_sample_zero` (deleted by TA4), gate 1 (`the_all_wet_arm_is_the_base_body_on_all_wet_tables`, the three differentials), gate 3, `conformance`, `contract::every_launch_rate_processes_scalar_and_supported_bank_at_zero_latency`, `mono_collapse::a_statically_bypassed_bank_collapses_to_the_dual_bits` (9 red) | | 982-M2 | the arm is taken when any lane is wet (`mask_any`) | gate 1 on the mixed corpus banks (`the_settled_body_is_the_base_body_on_the_corpus_table`, `randomized_differential_simd4`/`_simd8`), gate 3, `scenario_981_*`, `cross_target`, `lane_identity::every_width_produces_the_same_words` (7 red) | | 982-M3 | the arm drops `+ makeup` | gate 1, `scenario_982_all_wet_render_is_pinned`, `cross_target` (its `f32` lane 7 has mix 1 and makeup 6), `lane_identity::every_width_produces_the_same_words` (7 red). `oracle` stays green, as the verification found | | 982-M4 | the arm is never taken | gate 3 (`the_all_wet_arm_is_taken_exactly_when_every_lane_is_wet`) and gate 1's dispatch assertion (5 red). No bit-exactness test can see it: it is a performance-only regression | @@ -268,7 +268,7 @@ same witness-keyed "both NaN" rule as the dual half, and the grid runs a third p |---|---|---| | 985-M2 | the short last chunk is skipped (`chunks_exact_mut` on the outer loop), the verification's replacement for "the body reads the right plane", which cannot be written | gate 1 (its 31-, 33- and 97-frame blocks: the grid on every table and the three differentials), `scenario_985_collapsed_render_is_pinned` (7 red) | | 985-M3 | the recursive word's write-back is dropped | gate 1, `scenario_985_*`, and four `mono_collapse` tests including `the_collapsed_body_renders_the_dual_bodys_left_plane` (deleted by TA4; KEEP catcher compressor `mono_collapse::mono_reopen_drives_the_next_render_from_the_copied_state`) (11 red) | -| 985-M4 | the collapsed wet arm ignores `bypass` | `mono_collapse::a_statically_bypassed_bank_collapses_to_the_dual_bits` (deleted by TA5; catcher `mono_reopen_drives_the_next_render_from_the_copied_state`), gate 1 (7 red) | +| 985-M4 | the collapsed wet arm ignores `bypass` | `mono_collapse::a_statically_bypassed_bank_collapses_to_the_dual_bits`, gate 1 (7 red) | | # | mutation | why it survives | |---|---|---| diff --git a/crates/compressor/tests/mono_collapse.rs b/crates/compressor/tests/mono_collapse.rs index a1d25b607..93ba00a5b 100644 --- a/crates/compressor/tests/mono_collapse.rs +++ b/crates/compressor/tests/mono_collapse.rs @@ -370,3 +370,91 @@ fn mono_reopen_drives_the_next_render_from_the_copied_state() { ); } } + +/// A **statically bypassed** bank collapses to the dual bypassed bits, plane and state. +/// +/// # The seam this closes +/// +/// A *live* bypass clears the channel-symmetry witness' `UNBYPASSED` term, so a live-control-driven +/// bypassed lane declines the collapse and never reaches a one-plane body. A **prepared** bypass on +/// a live-control-free bank does not: `EffectBankStage::lane_symmetry` is the effect's +/// designed-word comparison alone, and it leaves `UNBYPASSED` unconditionally true. That asymmetry +/// is written down in `effect_contract::symmetry` as "a seam the collapse must close", and this +/// closes it by measurement rather than by the argument that the bypass is per plane inside the +/// kernel. +/// +/// The second assertion is the one that makes it a *chain*-level statement: on symmetric input a +/// dual bypassed block must publish the same words on both planes, because the collapse's seam +/// publishes the left plane on both. If a bypassed kernel could make its two channels differ on +/// identical input, collapsing a bypassed cohort would be unsound however faithful the one-plane +/// body was. +#[test] +fn a_statically_bypassed_bank_collapses_to_the_dual_bits() { + let Some((_, width)) = support::native_bank_width() else { + return; + }; + let lanes = width.lanes() as usize; + let values = support::values_with(&[(0, -30.0), (1, 4.0)]); + let requests: Vec<_> = (0..lanes) + .map(|_| request_configured(&values, LinkMode::Average, true)) + .collect(); + let mut dual = support::bind_bank(&requests).expect("dual bank"); + let mut collapsed = support::bind_bank(&requests).expect("collapsed bank"); + let idle = offsets(lanes); + + for step in 0..BLOCKS { + let mut dual_left = block(step * FRAMES, lanes); + let mut dual_right = dual_left.clone(); + let mut mono_left = dual_left.clone(); + let mut stale = vec![f32::from_bits(0x7F7F_FFFF); FRAMES * lanes]; + let first = (step * FRAMES) as u64; + run_block( + dual.as_mut(), + &mut dual_left, + &mut dual_right, + width, + first, + &[], + &idle, + false, + ); + run_block( + collapsed.as_mut(), + &mut mono_left, + &mut stale, + width, + first, + &[], + &idle, + true, + ); + for (word, (collapsed_word, dual_word)) in + mono_left.iter().zip(dual_left.iter()).enumerate() + { + assert_eq!( + collapsed_word.to_bits(), + dual_word.to_bits(), + "block {step} word {word}: bypassed collapsed left plane" + ); + } + for (word, (dual_right_word, dual_left_word)) in + dual_right.iter().zip(dual_left.iter()).enumerate() + { + assert_eq!( + dual_right_word.to_bits(), + dual_left_word.to_bits(), + "block {step} word {word}: a dual bypassed block on symmetric input must publish \ + the same words on both planes, or a collapsed bypassed cohort is unsound" + ); + } + } + + collapsed.desymmetrize_channels(); + for track in 0..lanes as u32 { + assert_eq!( + support::snapshot_track(collapsed.as_ref(), track), + support::snapshot_track(dual.as_ref(), track), + "track {track}: bypassed state after the disengage copy" + ); + } +} diff --git a/crates/controls/tests/live_lanes.rs b/crates/controls/tests/live_lanes.rs index 446f04935..724569d77 100644 --- a/crates/controls/tests/live_lanes.rs +++ b/crates/controls/tests/live_lanes.rs @@ -2,13 +2,54 @@ mod common; -use common::{RATE, effects_fixture, fixture}; -use controls::{ControlClass, ControlRow, Family, LiveLanes, MonitorOverlay, list}; +use common::{LAUNCH_EFFECTS, RATE, effects_fixture, fixture, insert_id}; +use controls::{ControlClass, ControlRow, Family, LiveLanes, MonitorOverlay, list, live_lanes}; +use session::{Effect, EffectIdentity, EffectQuality, LinkMode, SidechainDeclaration}; fn rows(model: &session::SessionModel) -> Vec { list(model, &MonitorOverlay::default(), RATE).collect() } +/// Red if a live row's lane is not attached (a live edit would find no cell), or if a lane is +/// attached for a row that is not live (a session with no effect would pay for effect cells). +#[test] +fn the_effect_lane_follows_the_effect_rows() { + // No effect, no effect row, no lane. With today's classes the input rows (#1556) and the + // fixture's one send into a submix (#1557) are live. + let mut model = fixture(); + let input_only = LiveLanes { + strip_input: true, + routes: true, + ..LiveLanes::default() + }; + assert_eq!(live_lanes(&model), input_only); + + // One insert on one track gives the effect lane and no other. + let effect = LAUNCH_EFFECTS[1]; + model.tracks[0].inserts.effects.push(Effect { + id: insert_id(effect), + identity: EffectIdentity::Native { + effect_id: common::id(effect), + }, + quality: EffectQuality::Normal, + bypass: false, + link_mode: LinkMode::DualMono, + params: Vec::new(), + sidechain: SidechainDeclaration::None, + }); + assert_eq!( + live_lanes(&model), + LiveLanes { + effects: true, + ..input_only + } + ); + + // A console slot is an effect instance too. + let model = effects_fixture(); + assert!(live_lanes(&model).effects); +} + /// Red if the input or route lane ignores its family, or if a row that is not live asks for a /// lane: the test table flips every route row to derived, then every input row to derived. #[test] diff --git a/crates/effect-compiler/src/prepare.rs b/crates/effect-compiler/src/prepare.rs index 986b29a30..e0befd3c0 100644 --- a/crates/effect-compiler/src/prepare.rs +++ b/crates/effect-compiler/src/prepare.rs @@ -1545,6 +1545,17 @@ mod owner_tests { )); } + #[test] + fn invalid_edit_poison_survives_valid_overwrite_until_discard() { + let mut rig = OwnerRig::new(); + rig.owner.begin(0).expect("base revision"); + assert!(rig.owner.edit(2, ParameterChannel::Left, f32::NAN).is_err()); + assert!(rig.owner.edit(2, ParameterChannel::Left, 100.0).is_err()); + assert!(rig.owner.prepare_targets_into(&mut []).is_err()); + rig.owner.discard().expect("discard poisoned candidate"); + rig.owner.begin(0).expect("restart at unchanged revision"); + } + /// A target set wants no room: any number of transactions publish before a block reads, each /// is accepted, and the block applies the newest write of each cell. /// diff --git a/crates/effect-compiler/tests/parameter_lattice.rs b/crates/effect-compiler/tests/parameter_lattice.rs index 295a36078..cf897cb5d 100644 --- a/crates/effect-compiler/tests/parameter_lattice.rs +++ b/crates/effect-compiler/tests/parameter_lattice.rs @@ -218,6 +218,35 @@ fn every_ladder_is_integer_multiples_of_the_step_and_stays_on_the_lattice() { } } +#[test] +fn every_builtin_row_declares_a_lawful_lattice_at_every_launch_rate() { + // The builtin table is the other half of the shipped surface, including the appended `pan` + // row and the two rate-keyed cutoffs whose ceiling moves with the prepared rate. + for rate in [44_100_u32, 48_000, 88_200, 96_000] { + for descriptor in &builtins::BUILTIN_PARAMETER_DESCRIPTORS { + let where_ = format!("builtin {} @ {rate}", descriptor.name); + let lattice = builtins::builtin_parameter_lattice_points(descriptor, rate) + .unwrap_or_else(|error| panic!("{where_}: unlawful: {error:?}")); + assert!(!lattice.points.is_empty(), "{where_}: empty"); + for point in &lattice.points { + assert!( + decimal_to_f32(&point.canonical).is_some(), + "{where_}: {} does not convert", + point.canonical + ); + } + // A declared disabled sentinel is an admitted extra value outside the ordered set, + // never a point that stepping can wander onto. + if let Some(disabled) = &lattice.disabled { + assert!( + lattice_index_for_decimal(&lattice.points, disabled).is_err(), + "{where_}: the disabled sentinel must sit outside the ordered lattice" + ); + } + } + } +} + #[test] fn the_appended_pan_row_is_the_persisted_pan_authority() { // #239 ruling 5461507633 B4: pan persists as pan. The row is stable-ID 12, per-lane, and diff --git a/crates/effect-runtime/tests/dynamics.rs b/crates/effect-runtime/tests/dynamics.rs index 23231cb2e..1bc770b36 100644 --- a/crates/effect-runtime/tests/dynamics.rs +++ b/crates/effect-runtime/tests/dynamics.rs @@ -132,6 +132,24 @@ fn below_the_knee_is_the_exact_identity() { } } +/// A ratio of 1 is a no-op at every level, and a ratio below 1 expands upward. +#[test] +fn the_ratio_sets_the_slope() { + let unity = GainComputerCoef::::new(-18.0, 1.0, 6.0); + for level in [-160.0f32, -18.0, 0.0, 24.0] { + assert_eq!( + gain_delta_db::(level, &unity).to_bits(), + 0.0f32.to_bits(), + "ratio 1 must be an identity at {level} dB" + ); + } + let expander = GainComputerCoef::::new(-40.0, 0.5, 0.0); + assert!( + gain_delta_db::(-20.0, &expander) > 0.0, + "a ratio below 1 expands upward" + ); +} + /// The two dB conversions are exact at unity and are inverses to within a rounding. #[test] fn the_db_conversions_agree() { diff --git a/crates/graph/src/runtime.rs b/crates/graph/src/runtime.rs index ca1cf5274..e230c2575 100644 --- a/crates/graph/src/runtime.rs +++ b/crates/graph/src/runtime.rs @@ -8278,6 +8278,20 @@ thread_local! { static HOST_MASTER_DECLINED: std::cell::Cell = const { std::cell::Cell::new(false) }; } +/// Decline (`true`) or restore (`false`) the host-plane master for every later bind on this +/// thread: issue #916's arena oracle, on the pattern of [`test_only_set_route_fold_declined`]. +/// +/// A plan bound declined has no Output unit, every folded master is [`FoldTarget::Arena`], and a +/// redirect into the Output op is not withheld. So its Output op and its folds write the Output's +/// arena buffer, as every plan did before the issue, and nothing writes the host's planes. The +/// test copies that buffer out itself, which is the executor's old end-of-block copy. The switch +/// is read once per bind, in `preflight_sequential`. Callers restore `false` after the bind they +/// meant to decline. +#[cfg(test)] +pub(crate) fn test_only_set_host_master_declined(declined: bool) { + HOST_MASTER_DECLINED.with(|slot| slot.set(declined)); +} + #[cfg(any(test, feature = "test-support"))] thread_local! { /// Issue #918's copy oracle. Bind-time only; render never reads it. @@ -15089,8 +15103,184 @@ mod tests { (executor, program) } + /// `Input -> PostInputBuiltins (a one-lane W4 builtin bank) -> Output`, one track, no route + /// (issue #916). The bank's last slot is dedicated storage whose sole reader is the Output op, + /// so `scatter_target` offers a redirect *into the Output op*, which `build_sequential` + /// withholds. The arena oracle, bound with the host master declined, takes it. + fn direct_bank_parts( + output_meters: Option<&Published>, + ) -> (crate::PreparedGraphPlan, crate::GraphRuntimeBindings) { + const FRAMES: u32 = 13; + let id = |text: &str| crate::StableGraphId::parse(text).expect("stable id"); + let input = GraphNodeId::TrackStage { + track_id: id("track00"), + stage: TrackStage::Input, + }; + let member = GraphNodeId::TrackStage { + track_id: id("track00"), + stage: TrackStage::PostInputBuiltins, + }; + let output = GraphNodeId::Output { + output_id: id("main"), + }; + let port = |node: &GraphNodeId, kind| crate::GraphPortId { + node: node.clone(), + kind, + effect_port: None, + }; + let edge = |id, source: &GraphNodeId, destination: &GraphNodeId| crate::GraphEdge { + id, + source: port(source, crate::GraphPortKind::MainOutput), + destination: port(destination, crate::GraphPortKind::MainInput), + path: "$.issue916".to_owned(), + }; + let mut edges = vec![ + edge( + GraphEdgeId::TrackMain { + target: member.clone(), + }, + &input, + &member, + ), + edge( + GraphEdgeId::RouteSource { + route_id: id("route00"), + }, + &member, + &output, + ), + ]; + edges.sort_by(|left, right| left.id.cmp(&right.id)); + let levels = [ + vec![input.clone()], + vec![member.clone()], + vec![output.clone()], + ]; + let schedule: Vec<_> = levels.iter().flatten().cloned().collect(); + let mut nodes: Vec<_> = schedule + .iter() + .cloned() + .map(|id| crate::GraphNode { + id, + latency: effect_contract::LatencySamples(0), + tail: effect_contract::TailSamples::Finite(0), + }) + .collect(); + nodes.sort_by(|left, right| left.id.cmp(&right.id)); + let envelope = engine::realtime::RenderEnvelope { + sample_rate: engine::SampleRateHz(48_000), + quantum: engine::QuantumFrames(FRAMES), + output_channels: NonZeroUsize::new(2).expect("stereo"), + }; + let plan = crate::PreparedGraphPlan::new(crate::PreparedGraphPlanParts { + plan_id: 916, + spec: GraphSpec { + nodes, + ports: Vec::new(), + edges, + }, + sequential_schedule: schedule, + dependency_levels: levels + .iter() + .enumerate() + .map(|(level, nodes)| crate::DependencyLevel { + level: level as u64, + nodes: nodes.clone(), + }) + .collect(), + route_timings: Vec::new(), + inserted_delays: Vec::new(), + buffer_assignments: Vec::new(), + estimate: crate::GraphResourceEstimate { + logical_nodes: 0, + materialized_nodes: 0, + edges: 0, + schedule_items: 0, + dependency_levels: 0, + reductions: 0, + routes: 0, + effects: 0, + audio_buffer_samples: 0, + total_delay_samples: 0, + delay_bytes: 0, + graph_metadata_bytes: 0, + declared_effect_bytes: 0, + effect_bank_count: 0, + effect_bank_scratch_bytes: 0, + effect_bank_runtime_buffer_bytes: 0, + effect_bank_metadata_bytes: 0, + builtin_bank_bytes: 0, + builtin_bank_scratch_bytes: 0, + builtin_bank_count: 0, + largest_allocation_bytes: 0, + incremental_plan_bytes: 0, + session_plus_plan_bytes: 0, + }, + envelope, + required_bindings: vec![input.clone(), member.clone(), output.clone()], + routes: Vec::new(), + track_delays: Vec::new(), + effects: Vec::new(), + effect_controls: Vec::new(), + effect_observations: Vec::new(), + banks: Vec::new(), + builtin_banks: vec![GraphPreparedBuiltinBank { + backend: lane::Backend::Simd4, + members: vec![member].into_boxed_slice(), + processor: Box::new(CrossTilt), + scratch: AoSoaScratch::new(BankWidth::Four, FRAMES).expect("scratch"), + }], + observers: Vec::new(), + }); + let mut observers = Vec::new(); + if let Some(published) = output_meters { + for handle in [OUTPUT_METER_HANDLE, OUTPUT_METER_HANDLE + 1] { + observers.push(GraphNodeObserverBinding::new( + output.clone(), + handle, + Box::new(WordMeter { + handle, + accepts_resident: false, + published: Arc::clone(published), + }), + )); + } + } + ( + plan, + crate::GraphRuntimeBindings { + envelope, + nodes: vec![ + GraphNodeBinding::new(input, Box::new(NoiseInput(7))), + GraphNodeBinding::identity(output), + ], + observers, + }, + ) + } + /// NaN padding with a recognisable payload: the words a host plane holds before a render. const HOST_PAD: u32 = 0x7fc0_0916; + /// The pattern the arena buffer of the session Output is filled with before every block. + const OUTPUT_SLOT_FILL: [u32; 2] = [0x7fc1_0916, 0x7fc2_0916]; + + fn stereo_bits((left, right): (&[f32], &[f32])) -> Vec { + left.iter() + .chain(right) + .map(|word| word.to_bits()) + .collect() + } + + /// The session output's arena buffer of a bound executor. + fn output_slot(program: &ExecutionProgram) -> u32 { + program.output.0 + ARENA_BASE + } + + fn fill_output_slot(executor: &mut crate::GraphExecutor, program: &ExecutionProgram) { + let (left, right) = executor.runtime.buffer_mut(output_slot(program)); + left.fill(f32::from_bits(OUTPUT_SLOT_FILL[0])); + right.fill(f32::from_bits(OUTPUT_SLOT_FILL[1])); + } /// One host-plane render through `GraphExecutor::render`, the executor half of /// `PreparedRenderPlan::render`. @@ -15112,6 +15302,334 @@ mod tests { ) } + /// Issue #916's arena oracle: the render loop `GraphExecutor::render` ran before the issue, + /// over a runtime bound with the host master declined, then its end-of-block copy of the + /// Output's arena buffer into `output`. + /// + /// Returns that buffer's words as they stood just before the first unit that writes the + /// master into it. That unit is the Output op's own, unless the Output op's reduction was + /// neutralised to its own slot. Then the master was written earlier, by the chains that fold + /// into it or by a chain whose scatter was redirected into it. A host-plane render must leave + /// the buffer holding exactly those words, because only the master's writers touch it after + /// that point, and they now write the host's planes. + fn render_arena_oracle( + executor: &mut crate::GraphExecutor, + program: &ExecutionProgram, + mut output: engine::realtime::PlanarBufferMut<'_>, + first_sample: u64, + ) -> Vec { + let slot = output_slot(program); + let runtime = &mut executor.runtime; + assert_eq!( + runtime.output_unit, None, + "the oracle binds with no Output unit" + ); + let frames = runtime.arena.frames(); + let untouched = f32::from_bits(HOST_PAD); + let (mut left, mut right) = (vec![untouched; frames], vec![untouched; frames]); + let mut host = HostMaster::new(&mut left, &mut right, frames).expect("oracle host"); + // The Output op is the last op writing its slot: dedicated storage is never recycled. + let output_unit = (0..runtime.units.len()) + .rev() + .find(|unit| matches!(&runtime.units[*unit], RuntimeUnit::Op(op) if op.output == slot)) + .expect("the Output op's unit"); + let neutralised = matches!( + &runtime.units[output_unit], + RuntimeUnit::Op(op) if *op.inputs == [slot] + ); + let writes_slot = |unit: &RuntimeUnit| match unit { + RuntimeUnit::Bank { + members, + lanes, + fold, + master, + .. + } => { + (!fold.is_empty() && *master == FoldTarget::Arena(slot)) + || members[members.len() - lanes..] + .iter() + .any(|member| member.output == slot) + } + RuntimeUnit::Op(_) => false, + }; + let validity = GraphObservationValidity::CLEAR; + let mut before_master = None; + for unit in 0..runtime.units.len() { + let writes_master = + unit == output_unit || (neutralised && writes_slot(&runtime.units[unit])); + if writes_master && before_master.is_none() { + before_master = Some(stereo_bits(runtime.buffer(slot))); + } + runtime + .execute( + unit, + first_sample, + LiveSnapshot::ALL, + host.reborrow(), + None, + &crate::GraphBlockTime::new( + Some(first_sample), + u32::try_from(frames).expect("oracle frames"), + None, + None, + ), + ) + .expect("oracle unit"); + runtime + .observe_unit(unit, first_sample, validity, &host) + .expect("oracle observer"); + } + // The executor's end-of-block copy, exactly as it stood before issue #916. + let (arena_left, arena_right) = runtime.buffer(slot); + output + .plane_mut(0) + .expect("left") + .copy_from_slice(arena_left); + output + .plane_mut(1) + .expect("right") + .copy_from_slice(arena_right); + assert!( + left.iter() + .chain(&right) + .all(|word| word.to_bits() == HOST_PAD), + "an arena-oracle unit wrote the host planes" + ); + before_master.expect("a master writer ran") + } + + /// Which issue #916 gate-1 plan to bind. + #[derive(Clone, Copy, Debug)] + enum HostShape { + /// 64 tracks in eight W8 cohorts, every route folded into the Output: the console shape. + /// `frames`. + Folded(u32), + /// The same plan with the fold declined: 64 route ops and a 64-input reduction. + Unfolded, + /// The bus plan with its three pads: the tracks fold into the bus, the bus redirects its + /// scatter into its fader, and the Output's one input is the bus route. The Output's slot + /// is a pad's. + Submix, + /// The bus plan with no pads: nothing folds, and the Output's slot is a track's input + /// slot, which the tracks' bank gathers and whose route runs in place on it. + SubmixOnATrackSlot, + /// One track, its route folded into the Output: a fan-in-one master that only stores. + OneRouteFolded, + /// The same with the fold declined: the Output op's fan-in-one copy. + OneRouteUnfolded, + /// [`direct_bank_parts`]: a bank's last slot straight into the Output op. + BankIntoOutput, + } + + impl HostShape { + const ALL: [Self; 8] = [ + Self::Folded(13), + Self::Folded(128), + Self::Unfolded, + Self::Submix, + Self::SubmixOnATrackSlot, + Self::OneRouteFolded, + Self::OneRouteUnfolded, + Self::BankIntoOutput, + ]; + + const fn frames(self) -> u32 { + match self { + Self::Folded(frames) => frames, + _ => 13, + } + } + + /// `[bank_route_folds, bank_scatter_redirects]` of the host-plane plan. + const fn shape(self) -> [u64; 2] { + match self { + Self::Folded(..) => [64, 0], + Self::Unfolded | Self::OneRouteUnfolded | Self::BankIntoOutput => [0, 0], + Self::Submix => [4, 1], + Self::SubmixOnATrackSlot => [0, 1], + Self::OneRouteFolded => [1, 0], + } + } + + /// Bind the host-plane plan, or with `oracle` the arena oracle of the same plan. + fn bind( + self, + oracle: bool, + published: &Published, + ) -> (crate::GraphExecutor, ExecutionProgram) { + let console = |tracks, width, frames| { + let fixture = FoldFixture { + output_meters: true, + ..FoldFixture::metered(width, tracks, frames) + }; + let (plan, bindings, _) = fold_fixture_parts(fixture, published); + (plan, bindings) + }; + let (plan, bindings) = match self { + Self::Folded(frames) => console(64, CONSOLE_WIDTH, frames), + Self::Unfolded => console(64, CONSOLE_WIDTH, 13), + Self::OneRouteFolded | Self::OneRouteUnfolded => console(1, BankWidth::Four, 13), + Self::Submix => folded_bus_parts(3, Some(published)), + Self::SubmixOnATrackSlot => folded_bus_parts(0, Some(published)), + Self::BankIntoOutput => direct_bank_parts(Some(published)), + }; + let declined = matches!(self, Self::Unfolded | Self::OneRouteUnfolded); + test_only_set_route_fold_declined(declined); + test_only_set_host_master_declined(oracle); + let bound = bind_executor(plan, bindings, None); + test_only_set_route_fold_declined(false); + test_only_set_host_master_declined(false); + bound + } + } + + /// Gate 1 of issue #916: the host's planes after a render are the arena oracle's master bit + /// for bit, at a stride equal to the block and seven words wider, with every padding word + /// untouched; every observer window, the two Output meters' included, is the oracle's; and + /// the session output's arena buffer is left holding exactly what it held before the + /// master's first writer ran, so neither the Output op nor a folded Output master writes it. + /// + /// The oracle is [`render_arena_oracle`]: the same plan bound with the host master declined, + /// driven through the render loop that preceded the issue, then copied out of the arena as + /// the executor's end-of-block copy did. The shapes cover the three the brief names -- 64 + /// folded routes, 64 unfolded routes, and one submix into the Output -- plus a submix whose + /// Output slot is a gathered track input, a folded and an unfolded fan-in-one route, and a + /// bank straight into the Output op (whose scatter redirect is withheld). + /// + /// Red mutations (`crates/graph/tests/MUTATIONS.md`, rows 916-1 to 916-6, 916-10, 916-11 and + /// 916-15). They restore the arena path with the end-of-block copy, which only the + /// output-slot check sees. They write the Output op's master to the arena and skip the host, + /// install every folded Output master as an arena master, and read the fan-in-one input from + /// the other plane. They identify the Output op by buffer index, stop withholding the redirect + /// into the Output op, and fill the planes on success. They undedicate the Output, and point + /// its observers at the arena. + #[test] + fn the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride() { + const BLOCKS: u64 = 4; + for shape in HostShape::ALL { + let frames = shape.frames() as usize; + for stride in [frames, frames + 7] { + let (oracle_published, published) = (Published::default(), Published::default()); + let (mut oracle, oracle_program) = shape.bind(true, &oracle_published); + let (mut candidate, program) = shape.bind(false, &published); + assert_eq!(program, oracle_program, "{shape:?}: one lowered program"); + let redirects = match shape { + // The oracle takes the redirect into the Output op; the host-plane plan + // withholds it and changes nothing else. + HostShape::BankIntoOutput => 1, + _ => shape.shape()[1], + }; + assert_eq!( + [ + candidate.runtime.route_folds(), + candidate.runtime.scatter_redirects() + ], + shape.shape(), + "{shape:?}: [route folds, scatter redirects]" + ); + assert_eq!( + [ + oracle.runtime.route_folds(), + oracle.runtime.scatter_redirects() + ], + [shape.shape()[0], redirects], + "{shape:?}: the oracle's [route folds, scatter redirects]" + ); + { + use engine::realtime::PreparedPlanExecutor as _; + assert_eq!(candidate.bank_shape(), oracle.bank_shape(), "{shape:?}"); + } + let unit = candidate.runtime.output_unit.expect("an Output unit"); + assert!( + matches!(&candidate.runtime.units[unit], RuntimeUnit::Op(op) if op.output == output_slot(&program)), + "{shape:?}: the Output unit runs the op that owns the output slot" + ); + let mut masters = Vec::new(); + for block in 0..BLOCKS { + let first_sample = block * frames as u64; + fill_output_slot(&mut oracle, &oracle_program); + fill_output_slot(&mut candidate, &program); + let mut expected = vec![f32::from_bits(HOST_PAD); 2 * stride]; + let before_master = render_arena_oracle( + &mut oracle, + &oracle_program, + engine::realtime::PlanarBufferMut::try_new( + &mut expected, + 2, + frames, + stride, + ) + .expect("oracle output"), + first_sample, + ); + let mut actual = vec![f32::from_bits(HOST_PAD); 2 * stride]; + render_host( + &mut candidate, + engine::realtime::PlanarBufferMut::try_new(&mut actual, 2, frames, stride) + .expect("host output"), + first_sample, + ) + .expect("host-plane render"); + let bits = + |words: &[f32]| words.iter().map(|word| word.to_bits()).collect::>(); + assert_eq!( + bits(&actual), + bits(&expected), + "{shape:?}, stride {stride}, block {block}: the host planes are the oracle's" + ); + for (index, word) in actual.iter().enumerate() { + if index % stride >= frames { + assert_eq!( + word.to_bits(), + HOST_PAD, + "{shape:?}, stride {stride}, block {block}: padding word {index}" + ); + } + } + assert_eq!( + stereo_bits(candidate.runtime.buffer(output_slot(&program))), + before_master, + "{shape:?}, stride {stride}, block {block}: the output slot was written" + ); + let master: Vec = actual[..frames] + .iter() + .chain(&actual[stride..stride + frames]) + .map(|word| word.to_bits()) + .collect(); + assert!( + master.iter().any(|word| *word != 0 && *word != HOST_PAD), + "{shape:?}: the block carries audio" + ); + masters.push(master); + } + let frames_of = |published: &Published| published.lock().unwrap().clone(); + let (oracle_frames, frames_seen) = + (frames_of(&oracle_published), frames_of(&published)); + assert_eq!( + frames_seen, oracle_frames, + "{shape:?}, stride {stride}: every observer window is the oracle's" + ); + let output_windows: Vec<&MeterFrame> = frames_seen + .iter() + .filter(|frame| frame.handle >= OUTPUT_METER_HANDLE) + .collect(); + assert_eq!( + output_windows.len(), + 2 * BLOCKS as usize, + "{shape:?}: both Output meters saw every block" + ); + for window in output_windows { + let block = (window.first_sample / frames as u64) as usize; + let seen: Vec = window.left.iter().chain(&window.right).copied().collect(); + assert_eq!( + seen, masters[block], + "{shape:?}: an Output meter reads the planes the host receives" + ); + } + } + } + } + /// `t00 Input -> Output`, and with `read_output` also `Output -> t01 PostFader`: a hand-built /// plan with a consumer of the session output, which the graph compiler never emits. fn output_reader_parts( diff --git a/crates/graph/tests/MUTATIONS.md b/crates/graph/tests/MUTATIONS.md index be9bf0253..cbf2f8b4d 100644 --- a/crates/graph/tests/MUTATIONS.md +++ b/crates/graph/tests/MUTATIONS.md @@ -22,8 +22,8 @@ cargo test --locked -p --lib | M15 | fan-in 0 leaves the output buffer untouched instead of zeroing it | `src/runtime.rs` `reduce_plane` | `reduction_is_left_to_right_bit_identical_to_scalar_reference` | RED | | M3 | the slice PDC keeps the block's entry cursor instead of advancing it | `src/runtime.rs` `CompensationDelay::process` | `compensation_delay_is_partition_invariant_and_matches_per_sample_reference` | RED | | M12 | the slice PDC exchanges its two segments in the wrong order | `lane` `kernels::pdc_delay_block` | `compensation_delay_is_partition_invariant_and_matches_per_sample_reference` | RED | -| M4 | the route unfolds the gain and drops the fusion (`ll*l + lr*r`) | `lane` `kernels::mix2x2_block` | `route_applies_folded_gain_with_frozen_op_order` (deleted by TA5; catcher `fifty_random_dag_sessions_render_deterministic_nonsilent_pcm`) | RED | -| M9 | `mix2x2_block`'s scalar tail uses a different coefficient order than its vector body | `lane` `kernels::mix2x2_block` | `route_applies_folded_gain_with_frozen_op_order` (deleted by TA5; catcher `fifty_random_dag_sessions_render_deterministic_nonsilent_pcm`) | RED | +| M4 | the route unfolds the gain and drops the fusion (`ll*l + lr*r`) | `lane` `kernels::mix2x2_block` | `route_applies_folded_gain_with_frozen_op_order` (deleted by TA5; no catcher recorded) | RED | +| M9 | `mix2x2_block`'s scalar tail uses a different coefficient order than its vector body | `lane` `kernels::mix2x2_block` | `route_applies_folded_gain_with_frozen_op_order` (deleted by TA5; no catcher recorded) | RED | | M5 | the native executor stages an edge from the wrong producer | `src/runtime.rs` `build_native` | `fifty_random_dag_sessions_render_bit_identically_in_both_executors` | RED | | M6 | the sequential executor reduces before staging its delayed edges | `src/runtime.rs` `execute_op` | `fifty_random_dag_sessions_render_bit_identically_in_both_executors` | RED | | M8 | a bank member's inputs are gathered without their reduction | `src/runtime.rs` `Runtime::execute` | `level_major_w4_builtin_bank_is_analytic_for_three_blocks_in_both_executors` | RED | @@ -100,7 +100,7 @@ mutation at a time over `cargo test -p graph -p graph-compiler |---|---|---|---|---| | 218-1 | the first contributor accumulates instead of storing (`copy_from_slice` becomes `sum_into_block`) | `graph/src/runtime.rs` `ArenaMembers::fold_plane` | `runtime::tests::the_first_contributor_stores_so_a_negative_zero_master_keeps_its_sign` | RED (`+0.0` where `-0.0` is required; bits 0 against 0x8000_0000) | | 218-2 | the fan-in-zero fill is skipped for *every* kind, not only a bound source | `graph/src/runtime.rs` `execute_op` | `runtime::tests::the_fan_in_zero_fill_is_dead_only_under_a_bound_source` | RED (an identity node with no contributors renders the previous block instead of silence) | -| 218-3 | the association proof keeps its length check and drops the element-wise comparison | `graph/src/runtime.rs` `route_fold` | `route_ids_ordered_against_the_cohorts_decline_the_route_fold` | RED, plus `a_leased_stage_meter_declines_the_merge_and_still_meters` and `a_pre_fader_meter_splits_its_cohorts_chain_and_reads_the_limiter` | +| 218-3 | the association proof keeps its length check and drops the element-wise comparison | `graph/src/runtime.rs` `route_fold` | `route_ids_ordered_against_the_cohorts_decline_the_route_fold` | RED, plus `a_leased_stage_meter_declines_the_merge_and_still_meters` and `a_pre_fader_meter_splits_its_cohorts_chain_and_reads_the_limiter` (deleted by TA5; no catcher recorded) | | 218-4 | the observer clause is dropped from `foldable_lane` | `graph/src/runtime.rs` | `runtime::tests::a_post_matrix_meter_on_every_track_of_a_full_bank_keeps_the_fold_armed` | RED (a meter at `PostFader` no longer declines the fold). Re-run for #885: the original catcher, `the_folded_master_is_the_reductions_own_bits`, relied on a post-matrix meter declining the fold, a premise #885 removed; it now stays GREEN under this mutation. | | 218-5 | the opening chain's own ops are no longer excluded from the in-between master scan | `graph/src/runtime.rs` `route_fold` | `every_standing_workload_folds_one_route_per_track` | RED (nothing folds at all: the session output's colour is track zero's input slot) | | 218-6 | sole readership of a chain's last slot is dropped (`len() != 1` becomes `is_empty()`) | `graph/src/runtime.rs` `foldable_lane` | — | **GREEN.** Reported rather than dressed up: the clause is real but shadowed. A second route from the same tap adds a summand the master's input list carries, so the association proof declines on length first; a sidechain from that tap is read by an op scheduled before the route, so `readers[producer][0]` is not a route and the plain-route clause declines instead. | @@ -208,7 +208,7 @@ before the next row. Each mutation is an exact-text replacement, so an unmatched been reported rather than silently skipped. Host: `x86_64`, workspace `.cargo/config.toml` pin `-C target-feature=+avx2,+fma`, debug profile. The new tests these rows name: -- "Gate 1" is `runtime::tests::the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride` (deleted by TA5; catcher `the_block_entry_holds_the_newest_published_lengths`). +- "Gate 1" is `runtime::tests::the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride`. - "Gate 3" is `runtime::tests::a_failed_render_silences_the_host_planes_and_a_rejected_one_leaves_them_alone`. - "The kernel test" is `runtime::tests::a_host_plane_reduction_is_the_arena_reduction_bit_for_bit`. - "Metered" is `runtime::tests::a_folded_metered_plan_is_the_unfolded_plans_master_and_meters_bit_for_bit`. @@ -344,7 +344,7 @@ result column gives that second run's failure. For 926-12, the second run also d | 926-12 | drop the master's delayed-input clause (920-9) | `graph/src/runtime.rs` `output_route_fold` | gate 1 | RED on the constructor's `debug_assert` (`a folded route table belongs to the Output op's identity reduction`: the Output op stages). With the counts and that clause deleted: RED, `DelayedEdge, fan-in 64, 1 frames, block 0: the host planes`, `-0.0` (`2147483648`) where the oracle has `+0.0`: the line's `+0.0` warm-up words are mixed through the delayed route's negative 2x2. | | 926-13 | a non-route producer folds with an identity 2x2 (`plain_route_gains(..).unwrap_or([1.0, 0.0, 0.0, 1.0])`) (920-10) | `graph/src/runtime.rs` `output_route_fold` | gate 1 | RED on the count (`SubmixContributor ... folds`, 65 against 0). With the counts deleted: RED, `SubmixContributor, fan-in 64, 1 frames, block 0: the host planes`, `+0.0` where the oracle keeps `-0.0`: the pass-through's `(-0.0, +0.0)` left plane mixes to `+0.0`. | | 926-14 | drop the in-between `op_names_buffer` scan (920-11) | `graph/src/runtime.rs` `output_route_fold` | the graph suite; the console gate | GREEN (104 of 104, and the console gate). No test can make the scan fire: a write between the route and the Output would need the live buffer's slot, which the colouring owns through the Output op, and a read is a second reader, which 926-11's clause declines. The scan is the belt to that brace, as `route_fold`'s is. | -| 926-15 | drop the no-bank clause (920-12) | `graph/src/runtime.rs` `output_route_fold` | the graph suite; the console gate | RED on five graph tests at bind, `graph.route_fold.master`: `a_banked_source_gathers_the_played_block_bit_for_bit_with_the_copy`, `a_failed_render_silences_the_host_planes_and_a_rejected_one_leaves_them_alone`, `a_folded_metered_plan_is_the_unfolded_plans_master_and_meters_bit_for_bit`, `a_post_matrix_meter_on_every_track_of_a_full_bank_keeps_the_fold_armed`, `the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride` (deleted by TA5; catcher `the_block_entry_holds_the_newest_published_lengths`) (#920 saw four; #918's gate 1 is the fifth). RED on the console gate's other-rows sweep, at bind (`nine_track_baseline: console graph bindings`). On a banked plan whose chain fold is admitted, both folds claim the same routes and `validate_fold_installation` refuses the bind. | +| 926-15 | drop the no-bank clause (920-12) | `graph/src/runtime.rs` `output_route_fold` | the graph suite; the console gate | RED on five graph tests at bind, `graph.route_fold.master`: `a_banked_source_gathers_the_played_block_bit_for_bit_with_the_copy`, `a_failed_render_silences_the_host_planes_and_a_rejected_one_leaves_them_alone`, `a_folded_metered_plan_is_the_unfolded_plans_master_and_meters_bit_for_bit`, `a_post_matrix_meter_on_every_track_of_a_full_bank_keeps_the_fold_armed`, `the_host_planes_are_the_arena_oracles_master_bit_for_bit_at_every_stride` (#920 saw four; #918's gate 1 is the fifth). RED on the console gate's other-rows sweep, at bind (`nine_track_baseline: console graph bindings`). On a banked plan whose chain fold is admitted, both folds claim the same routes and `validate_fold_installation` refuses the bind. | | 926-16 | the Output fold's routes are not retired: they keep their units and run, and the table still applies (920-13) | `graph/src/runtime.rs` `build_sequential`, `validate_fold_installation` | gate 1; the console gate | RED on the unit count (`Plain ... each folded route is one unit fewer`, 193 against 129) and on the console gate's census (`the plumbing row must retire one route op per track`, 0 against 64). With the counts deleted: RED, `Plain, fan-in 64, 1 frames, block 0: the host planes`: every route is mixed twice. | | 926-17 | the routes are retired but the table is not installed (`output_routes` empty) (920-14) | `graph/src/runtime.rs` `build_sequential` | gate 1; the console gate | RED on the count (0 against 64). With the counts deleted: RED, `Plain, fan-in 64, 1 frames, block 0: the host planes`: the Output sums the unmixed inputs. GREEN on the console gate: its routes are the identity 2x2 at 0 dB, so the unmixed sum is the mixed one there and the census still drops by 64. | | 926-18 | skip an odd fan-in's lone last input (the `G = 1` arm returns `true`) | `graph/src/runtime.rs` `route_reduce` | the kernel test, gate 1, the console gate | RED, RED, GREEN. Kernel: `width 1, 1 frames, fan-in 3, -0.0 false: left`. Gate 1: `Plain, fan-in 9, 1 frames, block 0: the host planes`, the gate's odd fan-in. The console row's fan-in is sixty-four, all pairs. | @@ -702,7 +702,7 @@ host-core/test-support,graph/test-support --test route_mute`); gate 4 is | 1217-4 | an inactive sole input leaves the destination's in-place buffer alone (the `+0.0` store only when `count > 1`) | `graph/src/runtime.rs` `route_segments` | gate 1, in place | RED, 1 of 7: `the muted sole route's bus: plane 0 frame 0: -0.15693776, expected 0.0` -- the raw `post_pan` tap. | | 1217-5 | an active opening run is fill-`+0.0`-then-add instead of today's reduction | `graph/src/runtime.rs` `reduce_gated` | gates 1 and 2 | RED: `r0 open at -0.0, r1 and r2 muted: plane 0 frame 0: 0.0, expected -0.0`; `seed 2, Bus, r1 muted, plane 0: sample 0: 0.0 != -0.0`. | | 1217-6 | the host-master form's later run stores instead of adding | `graph/src/runtime.rs` `reduce_gated_into` | gate 1 | RED, 1 of 7: `seed 0, Output, r0 muted, plane 0: sample 0: -0.0 != 0.0`. | -| 1217-7 | a delayed muted route goes inactive (`&& input.delay.is_none()` dropped) | `graph/src/runtime.rs` `route_activity` | gate 3 | Attempt 1: RED, 1 of 7, by the counter only (`a_muted_delayed_route_stays_active`, 0 mixes against 8). Attempt 2: also RED **on audio**, `a_muted_delayed_routes_line_carries_its_zero_mix` (`plane 0: e's input, d-e's delayed zero mix alone: sample 486: 0.0 != -0.0`), and #1218's `a_fully_follow_muted_delayed_send_stays_active` by its counter. | +| 1217-7 | a delayed muted route goes inactive (`&& input.delay.is_none()` dropped) | `graph/src/runtime.rs` `route_activity` | gate 3 | Attempt 1: RED, 1 of 7, by the counter only (`a_muted_delayed_route_stays_active`, 0 mixes against 8). Attempt 2: also RED **on audio**, `a_muted_delayed_routes_line_carries_its_zero_mix` (`plane 0: e's input, d-e's delayed zero mix alone: sample 486: 0.0 != -0.0`), and #1218's `a_fully_follow_muted_delayed_send_stays_active` (deleted by TA5; no catcher recorded) by its counter. | | 1217-8 | a route destination copies its route inputs to a `Vec` per block | `graph/src/runtime.rs` `execute_op` | gate 5 | RED: `route_activity_renders_without_allocating` aborts (`SIGABRT`) on the armed render audit. | | 1217-9 | bind builds the table whatever the gates | `graph/src/runtime.rs` `build_sequential`, `route_activity` | gate 4 | RED: `a plan without a silencing route builds no route-activity table`. | | 1217-10 | the estimate charges no table | `graph-compiler/src/estimate.rs` `resource_estimate` | gate 4 | RED: `graph_metadata_bytes grew by 0, the bound plan by 237`. | diff --git a/crates/host-core/tests/MUTATIONS.md b/crates/host-core/tests/MUTATIONS.md index 336c6d1e8..1de7892fb 100644 --- a/crates/host-core/tests/MUTATIONS.md +++ b/crates/host-core/tests/MUTATIONS.md @@ -354,12 +354,12 @@ the stereo arm does not, and uncommanded they agree. ## Issue #1211 — one mute owner and live-control producers per strip -Driver: `cargo test -p host-core --test strip_controls --test strip_handles` (deleted by TA5) and `cargo test -p host-core --test randomized`, one mutation at a time, tree +Driver: `cargo test -p host-core --test strip_controls --test strip_handles` and `cargo test -p host-core --test randomized`, one mutation at a time, tree restored between rows. Delivery host: x86_64 AVX2+FMA. | Row | Mutation | Red | | --- | --- | --- | -| 1211-M5 | build the control requests from `live_control_tracks` (the track prefix) again | `a_bus_fader_record_renders_as_the_session_with_that_fader` (three producers) and `handles_list_tracks_then_submixes_and_file_bus_effects` (deleted by TA5; no catcher recorded) (`left: ["t0", "t1", "t2"]`) | +| 1211-M5 | build the control requests from `live_control_tracks` (the track prefix) again | `a_bus_fader_record_renders_as_the_session_with_that_fader` (three producers) and `handles_list_tracks_then_submixes_and_file_bus_effects` (`left: ["t0", "t1", "t2"]`) | | 1211-M6 | sort `strip_controls` by ID instead of by `canonical_index` | `a_bus_fader_record_renders_as_the_session_with_that_fader` (the bus producer is no longer at strip index 3) | | 1211-M7 | `live_records` draws over `handles.track_count` instead of `strip_controls.len()` | `randomized_consoles_render_the_same_bits_armed_dual_and_serialized` (`bus_live_records: 0`) | diff --git a/crates/host-core/tests/live_delta.rs b/crates/host-core/tests/live_delta.rs index fbbaf8087..5bb40c3f0 100644 --- a/crates/host-core/tests/live_delta.rs +++ b/crates/host-core/tests/live_delta.rs @@ -425,6 +425,44 @@ fn a_sign_of_zero_delay_edit_is_a_rebuild_by_its_row() { ); } +/// #1554 gate 1, D5. Red if a submix strip is left structural: its fader, mute and pan are live +/// records addressed to the submix's ID, as a track's are, and a track's records are not +/// addressed to it. +#[test] +fn a_submix_strip_fader_mute_and_pan_are_live() { + let current = with_bus(false); + assert_eq!( + classify(¤t, |next| next.submixes[0].fader.left_db = -6.0), + Ok(vec![( + "bus".to_owned(), + vec![fader_db(BuiltinLaneSelector::Left, -6.0, 0)], + None + )]) + ); + assert_eq!( + classify(¤t, |next| { + next.submixes[0].fader.left_mute = true; + next.submixes[0].fader.right_mute = true; + }), + Ok(vec![( + "bus".to_owned(), + vec![mute(BuiltinLaneSelector::Both, true, 0)], + None + )]) + ); + let panned = classify(¤t, |next| { + next.submixes[0].matrix_or_pan = MatrixOrPan::Pan { + left: 0.5, + right: 0.5, + smoothing_samples: 0, + }; + }) + .expect("a submix pan is live"); + assert_eq!(panned.len(), 1); + assert_eq!(panned[0].0, "bus"); + assert!(panned[0].1.is_empty() && panned[0].2.is_some()); +} + /// A delta's strip records and its route records, owned. type Both = (Vec, Vec); @@ -815,6 +853,112 @@ fn records_follow_effective_values_not_raw_ones() { assert_eq!(routes.len(), 1); } +/// #1558 gate 5, D7. Red if a record takes the wrong row's length: a VCA offset edit's own length +/// moves the member, the session's `fader_ms` length applies without one, a membership change takes +/// the `VcaOffset` row's session length, the strip's own `fader_db` edit wins over the VCA's on a +/// lane it moved, and a gate change from the source's mute takes the source's `Mute` length. +#[test] +fn vca_and_follow_records_take_the_ramp_of_the_edit_that_moved_them() { + use BuiltinLaneSelector::{Both, Left}; + let session_ramps = LiveRamps { + fader_samples: 100, + mute_samples: 300, + pan_samples: 200, + link_samples: 0, + }; + let current = with_vca(0.0, 0.0, [false; 2]); + let vca_target = EditRampTarget::Vca(current.vcas[0].id.clone()); + let first = track_id(¤t, 0); + let strips = |current: &SessionModel, + edit: fn(&mut SessionModel), + ramps: &EditRamps| + -> Vec { + let (strips, _) = classify_both(current, edit, session_ramps, ramps).expect("live"); + assert_eq!(strips.len(), 1); + assert_eq!(strips[0].0, first); + strips[0].1.clone() + }; + let mut ramp = EditRamps::new(); + ramp.set(&vca_target, EditRampRow::VcaFader, Some(480)); + assert_eq!( + strips(¤t, |next| next.vcas[0].fader.left_db = -6.0, &ramp), + vec![fader_db(Left, -6.0, 480)] + ); + // No edit length: `fader_ms`. + assert_eq!( + strips( + ¤t, + |next| next.vcas[0].fader.left_db = -6.0, + &EditRamps::new() + ), + vec![fader_db(Left, -6.0, 100)] + ); + // A mute takes the VCA mute's edit, else `mute_ms`. + let mut mute_ramp = EditRamps::new(); + mute_ramp.set(&vca_target, EditRampRow::VcaMute, Some(48)); + assert_eq!( + strips( + ¤t, + |next| next.vcas[0].fader.left_mute = true, + &mute_ramp + ), + vec![mute(Left, true, 48)] + ); + assert_eq!( + strips( + ¤t, + |next| next.vcas[0].fader.left_mute = true, + &EditRamps::new() + ), + vec![mute(Left, true, 300)] + ); + // The strip's own edit first: own length 0 steps the lane the strip moved, and the VCA's 480 + // applies to the lane only it moved; the two lanes are two records. + let mut both = EditRamps::new(); + both.set(&vca_target, EditRampRow::VcaFader, Some(480)); + both.set( + &EditRampTarget::Strip(current.tracks[0].id.clone()), + EditRampRow::Fader, + Some(0), + ); + assert_eq!( + strips( + ¤t, + |next| { + next.tracks[0].fader.left_db = -2.0; + next.vcas[0].fader.right_db = -6.0; + }, + &both + ), + vec![ + fader_db(Left, -2.0, 0), + fader_db(BuiltinLaneSelector::Right, -6.0, 480) + ] + ); + // One value, one length: both lanes merge. + assert_eq!( + strips( + ¤t, + |next| { + next.vcas[0].fader.left_db = -6.0; + next.vcas[0].fader.right_db = -6.0; + }, + &ramp + ), + vec![fader_db(Both, -6.0, 480)] + ); + // A membership change has no edit length: the `VcaOffset` row's session length. + let loud = with_vca(-6.0, -6.0, [false; 2]); + let (changed, _) = classify_both( + &loud, + |next| next.vcas[0].members.clear(), + session_ramps, + &ramp, + ) + .expect("live"); + assert_eq!(changed[0].1, vec![fader_db(Both, 0.0, 100)]); +} + /// #1558 gate 5. Red if a gate change from the source's mute ramps on its own: the send takes the /// length of the source strip's `Mute` record of the same transaction, so a strip and the sends that /// follow it ramp together; a gate change that the send's own `follows_mute` or `mute` edit caused @@ -3357,6 +3501,98 @@ fn a_submix_effect_bypass_is_live() { ); } +/// `with_effects` with the first track routed into submix `bus`, whose strip carries the console +/// soft-clip slot's entry and a compressor insert of its own. +fn with_effect_bus() -> SessionModel { + let fixture = parse_session_json(FIXTURE).expect("fixture parses"); + let mut compressor = fixture.lower_track(&fixture.tracks[0]).pre_insert[0].clone(); + compressor.id = id("bcomp"); + compressor.identity = EffectIdentity::Native { + effect_id: id("miso.compressor"), + }; + compressor.params.clear(); + compressor.bypass = false; + let mut model = with_effects(); + let first = model.tracks[0].id.clone(); + let bus = id("bus"); + let route = model + .routes + .iter_mut() + .find(|route| matches!(&route.source, RouteSource::Track { track_id, .. } if *track_id == first)) + .expect("first track's route"); + let output = core::mem::replace( + &mut route.destination, + RouteDestination::SubmixInput { + submix_id: bus.clone(), + }, + ); + let mut bus_main = route.clone(); + bus_main.id = id("bus-main"); + bus_main.follows_mute = false; + bus_main.source = RouteSource::Submix { + submix_id: bus.clone(), + tap: SendTap::PostPan, + }; + bus_main.destination = output; + model.routes.push(bus_main); + let mut submix = Submix::unity(bus, &model.console); + submix.inserts.effects.push(compressor); + model.submixes.push(submix); + normalized(&model) +} + +/// #1554 D5 on effect parameters. Red if a submix strip's console entry or insert parameter is +/// left structural, or is addressed to a track: each is a record at its own strip's ID and +/// instance address. +#[test] +fn submix_effect_params_are_live() { + let current = with_effect_bus(); + assert_eq!(current.submixes.len(), 1); + assert_eq!( + current.submixes[0].console.len(), + 1, + "bus carries the clip slot" + ); + assert_eq!( + classify_effects(¤t, |next| { + next.submixes[0].console[0].params.push(param( + 1, + ParameterChannel::Both, + ParameterUnit::Db, + 6.0, + )); + }), + Ok(( + Vec::new(), + vec![( + "bus".to_owned(), + LiveEffectAddress::console(0), + vec![record(0, Lane::Left, 6.0), record(0, Lane::Right, 6.0)] + )] + )), + "submix console entry" + ); + assert_eq!( + classify_effects(¤t, |next| { + next.submixes[0].inserts.effects[0].params.push(param( + 1, + ParameterChannel::Left, + ParameterUnit::Db, + -30.0, + )); + }), + Ok(( + Vec::new(), + vec![( + "bus".to_owned(), + LiveEffectAddress::insert(0), + vec![record(0, Lane::Left, -30.0)] + )] + )), + "submix insert" + ); +} + /// An automation on `with_effects`' EQ insert (`insert(2)` of the first track). fn eq_automation(model: &SessionModel, parameter_id: u32, unit: ParameterUnit) -> Automation { Automation { @@ -3674,6 +3910,64 @@ fn folded(gain_db: f32, matrix: [f32; 4], mute: bool, source_lane_muted: [bool; .expect("in the route domain") } +/// Gate 1. Red if a send record carries other bits than the coefficients preparation folds from the +/// post-commit values, or is emitted for a send whose held target does not change: a gain edit on a +/// muted send (the silenced `+0.0` target stays as it was) or a sign-of-zero rewrite. The unmute +/// then carries the gain the muted edit set. +#[test] +fn send_records_are_the_folded_target_of_the_post_commit_values() { + let current = with_bus(false); + let send = send_index(¤t); + let id = current.routes[send].id.as_str().to_owned(); + let matrix = matrix_of(¤t.routes[send]); + let gain = current.routes[send].gain_db; + + assert_eq!(classify_routes(¤t, |_| {}), Ok(Vec::new())); + assert_eq!( + classify_routes(¤t, |next| next.routes[send].gain_db = -6.0), + Ok(vec![( + id.clone(), + folded(-6.0, matrix, false, [false; 2]), + false, + 0 + )]) + ); + let swapped = [0.0, 1.0, 1.0, 0.0]; + assert_eq!( + classify_routes(¤t, |next| { + let m = &mut next.routes[send].channel_matrix; + (m.ll, m.lr, m.rl, m.rr) = (0.0, 1.0, 1.0, 0.0); + }), + Ok(vec![( + id.clone(), + folded(gain, swapped, false, [false; 2]), + false, + 0 + )]) + ); + assert_eq!( + classify_routes(¤t, |next| next.routes[send].mute = true), + Ok(vec![(id.clone(), [0.0; 4], true, 0)]) + ); + + // A gain edit on a muted send gives no record, and the later unmute carries the new gain. + let muted = edited(¤t, |model| model.routes[send].mute = true); + assert_eq!( + classify_routes(&muted, |next| next.routes[send].gain_db = -6.0), + Ok(Vec::new()) + ); + let edited_while_muted = edited(&muted, |model| model.routes[send].gain_db = -6.0); + assert_eq!( + classify_routes(&edited_while_muted, |next| next.routes[send].mute = false), + Ok(vec![( + id, + folded(-6.0, matrix, false, [false; 2]), + false, + 0 + )]) + ); +} + /// Gate 1. Red if a following send's record ignores the source lane mutes (the rebuilt plan would /// zero a column the record leaves), or zeroes them for a send that does not follow. #[test] diff --git a/crates/host-core/tests/stored_fader_automation.rs b/crates/host-core/tests/stored_fader_automation.rs index 4cb31a7a1..6934cff6d 100644 --- a/crates/host-core/tests/stored_fader_automation.rs +++ b/crates/host-core/tests/stored_fader_automation.rs @@ -2612,6 +2612,72 @@ fn a_session_seek_sets_a_latent_fader_lane_exactly_where_it_reaches_the_node() { } } +/// #1647 gate 8 with a seek step after `E` in `E`'s block (MAJOR-4 of the U2 review): behind a +/// latent insert, the unmute of 1,000 samples ends at `E` = 1,000, and a session seek (to 8,000) +/// at a block entry `r` reaches the fader at `r + a(n)`, after `E` in the same block (quantum 100: +/// `E` at the block's first sample; quantum 37: inside it). The lane moves at `E`, and from the +/// seek's step it is set exactly: the probe shows its `Set` there, and the output equals the plan +/// whose fader is static at the curve's value at 8,000, on the bank and on the oracle. +/// +/// Test value: red if the cut at `E` drops a seek step that lies after it in the block. +#[test] +fn a_seek_step_after_the_unmute_ramps_end_in_its_block_still_lands() { + const FRAMES: u64 = 2_048; + const T: u64 = 8_000; + let (model, entry) = muted_ride(fade_from(-30.0, 48_000), 10.0); + let (model, arrival) = latent(model); + let e = u64::from(UNMUTE); + for quantum in [100_u32, 37] { + let q = u64::from(quantum); + let block_end = (e / q + 1) * q; + let r = (0..=e / q) + .map(|k| k * q) + .find(|&r| r + arrival > e && r + arrival < block_end) + .expect("a block entry whose seek reaches the fader after E in E's block"); + let step = r + arrival; + let script = move |rig: &mut Rig| { + if rig.rendered == 0 { + live_mute(rig, builtins::BuiltinLaneSelector::Both, false, UNMUTE); + } + if rig.rendered == r { + rig.seek(T); + } + }; + let out = scripted_across(&model, &[quantum], FRAMES, &script); + let value = curve(&entry, T as i64).to_bits(); + let mut at_quantum = model.clone(); + at_quantum.quantum_frames = quantum; + assert_eq!( + sets_in_block_of(&at_quantum, &script, step), + [(0, step, value), (1, step, value)], + "quantum {quantum}: a Set per channel at the step" + ); + let mut reference_model = static_at(&model, curve(&entry, T as i64)); + reference_model.quantum_frames = quantum; + let reference = scripted(Rig::new(&reference_model), FRAMES, &|rig| { + if rig.rendered == r { + rig.seek(T); + } + }); + let step = step as usize; + for plane in 0..2 { + assert_ne!( + out[plane][step - 1].to_bits(), + reference[plane][step - 1].to_bits(), + "quantum {quantum}: the move ramps before the step" + ); + assert_eq!( + first_difference( + &out[plane][step..step + 64], + &reference[plane][step..step + 64] + ), + None, + "quantum {quantum}, plane {plane}: exact from the step" + ); + } + } +} + /// The fader gain of `db` composed with the VCA offset sum `offsets` (#1646 D1). fn composed(db: f32, offsets: f64) -> f32 { builtins::checked_fader_gain(session::vca_compose_db(db, offsets)).expect("fader domain") diff --git a/crates/host-core/tests/strip_handles.rs b/crates/host-core/tests/strip_handles.rs new file mode 100644 index 000000000..6873e5939 --- /dev/null +++ b/crates/host-core/tests/strip_handles.rs @@ -0,0 +1,151 @@ +//! Issue #1207 gate 2: the live-control handles list every strip, tracks first. +//! +//! `HostLiveControlHandles::strips` is the addressing authority: the tracks in canonical ID order, +//! then the submixes in canonical ID order. The builtin control producers (`strip_controls`) are +//! parallel to every strip (issue #1211 D1), the default meters stay parallel to the track prefix, +//! and a submix's effects get a live channel and an observation handle exactly as a track's do (D5 +//! removed the K1 interim). + +use core::num::{NonZeroU32, NonZeroUsize}; + +use builtins::MeterTap; +use host_core::{ + HostLiveControlRequest, HostPrepareCaps, HostShapePolicy, + prepare_host_session_with_live_controls, +}; +use session::{ + RouteDestination, RouteSource, SendTap, StableId, Submix, canonical_session_json, + parse_session_json, +}; + +const FIXTURE: &str = include_str!("../../../fixtures/session/v1/observation-frame-shape.json"); +const QUANTUM: u32 = 128; + +fn caps() -> HostPrepareCaps { + HostPrepareCaps { + shape: HostShapePolicy::AnyLaunchRate, + source_ring_frames: 1_024, + maximum_automation_spans_per_block: 128, + maximum_tracks: 100, + maximum_submixes: 100, + maximum_vcas: 100, + maximum_vca_reach_pairs: u64::MAX, + maximum_sources: 100, + maximum_routes: 100, + maximum_effects: 100, + maximum_graph_session_plus_plan_bytes: 100_000_000, + maximum_source_total_bytes: 10_000_000, + maximum_source_overhead_bytes: 10_000_000, + maximum_effect_state_bytes: 100_000_000, + maximum_effect_scratch_bytes: 100_000_000, + maximum_builtin_retained_bytes: 100_000_000, + maximum_named_allocation_bytes: 100_000_000, + maximum_meter_streams: 64, + maximum_meter_items: 1 << 16, + maximum_meter_bytes: 1 << 24, + } +} + +/// The three-track observation fixture (`t0` compressor, `t1` EQ, `t2` gate) plus two submixes, +/// declared out of order: `zzz-bus` sorts after every track and `aaa-bus` before every one, so +/// a strip list that merged the two segments by sorting would put `aaa-bus` first. Each bus +/// carries a compressor insert; `t0` feeds `zzz-bus`, `t1` feeds `aaa-bus`, and both buses feed +/// the output. +fn document() -> String { + let mut model = parse_session_json(FIXTURE).expect("observation fixture parses"); + let compressor = model.tracks[0].inserts.effects[0].clone(); + for (bus, feeder) in [("zzz-bus", 0_usize), ("aaa-bus", 1)] { + let id = StableId::parse(bus).expect("bus id"); + let mut submix = Submix::unity(id.clone(), &model.console); + let mut insert = compressor.clone(); + insert.id = StableId::parse("bus-comp").expect("insert id"); + submix.inserts.effects.push(insert); + model.submixes.push(submix); + let mut out = model.routes[feeder].clone(); + model.routes[feeder].destination = RouteDestination::SubmixInput { + submix_id: id.clone(), + }; + out.id = StableId::parse(&format!("{bus}-main")).expect("route id"); + out.source = RouteSource::Submix { + submix_id: id, + tap: SendTap::PostPan, + }; + model.routes.push(out); + } + canonical_session_json(&model).expect("canonical bus session") +} + +/// Red if a submix lands inside the track prefix (or the strips stop being tracks-then-submixes), +/// if the builtin controls stop being parallel to the strips (issue #1211 D1), if the default +/// meters start to cover buses, or if a bus effect stays without a live channel or an observation +/// handle. +#[test] +fn handles_list_tracks_then_submixes_and_file_bus_effects() { + let (_, _, handles) = prepare_host_session_with_live_controls( + &document(), + &caps(), + &HostLiveControlRequest { + meter_period_frames: Some(NonZeroU32::new(QUANTUM).expect("period")), + meter_queue_depth: NonZeroUsize::new(16).expect("meter depth"), + meter_tap: MeterTap::PostMatrix, + observation_taps: 1, + master_track: None, + }, + ) + .unwrap_or_else(|failure| panic!("prepare: {}", String::from_utf8_lossy(failure.as_bytes()))); + + let strips: Vec<&str> = handles.strips.iter().map(AsRef::as_ref).collect(); + assert_eq!(strips, ["t0", "t1", "t2", "aaa-bus", "zzz-bus"]); + assert_eq!(handles.track_count, 3); + + let tracks = &strips[..handles.track_count]; + let controls: Vec<&str> = handles + .strip_controls + .iter() + .map(|control| control.track_id.as_ref()) + .collect(); + assert_eq!( + controls, strips, + "one builtin channel per strip, in strip order" + ); + let meters: Vec<&str> = handles + .meters + .iter() + .map(|meter| meter.track_id.as_ref()) + .collect(); + assert_eq!(meters, tracks, "one default meter per track, none per bus"); + + let mut effects: Vec<(&str, &str)> = handles + .effect_controls + .iter() + .map(|producer| (producer.track_id.as_ref(), producer.effect_id.as_ref())) + .collect(); + effects.sort_unstable(); + assert_eq!( + effects, + [ + ("aaa-bus", "bus-comp"), + ("t0", "comp"), + ("t1", "eq"), + ("t2", "gate"), + ("zzz-bus", "bus-comp"), + ], + "every prepared effect, the buses' included, has a live channel" + ); + let mut observed: Vec<(&str, &str)> = handles + .effect_observations + .iter() + .map(|handle| (handle.track_id.as_ref(), handle.effect_id.as_ref())) + .collect(); + observed.sort_unstable(); + assert_eq!( + observed, + [ + ("aaa-bus", "bus-comp"), + ("t0", "comp"), + ("t2", "gate"), + ("zzz-bus", "bus-comp"), + ], + "every tapped effect, the buses' included, has an observation handle (the EQ has no tap)" + ); +} diff --git a/crates/host-core/tests/successor_swap.rs b/crates/host-core/tests/successor_swap.rs index 471c12608..b1799ad7e 100644 --- a/crates/host-core/tests/successor_swap.rs +++ b/crates/host-core/tests/successor_swap.rs @@ -8921,6 +8921,72 @@ fn compensated_submix_lines_keep_their_audio_at_every_rate_and_width() { } } +/// Two submixes, each with a limited and an unlimited track: `eq0` (limited) and `eq1` into +/// `bus1`, `eq3` (limited) and `eq2` into `bus2`. Both unlimited routes carry a line. +fn two_bus_session(rate: u32, silent: Option<&str>) -> SessionModel { + let mut model = pdc_session(rate, &["eq0", "eq3"]); + add_bus(&mut model, "bus1"); + add_bus(&mut model, "bus2"); + send_route_to(&mut model, "eq0", "bus1"); + send_route_to(&mut model, "eq1", "bus1"); + send_route_to(&mut model, "eq2", "bus2"); + send_route_to(&mut model, "eq3", "bus2"); + if let Some(track) = silent { + model = with_mute(model, track, true); + } + model +} + +/// Gate 4: B re-points `eq1`'s route from `bus1` to `bus2`. The route keeps its ID and gets +/// another destination, so its line does not carry and its strip is not restarted. +/// +/// With `eq1` muted in both sessions the re-pointed line holds only zeros, so the run equals B +/// from frame 0 exactly when every other line carried: `eq2`'s line into `bus2` is the one that +/// does. With `eq1` playing, the new route's line starts at rest and the swap block misses the +/// audio B's line holds: red if the line followed the route ID into the other bus, where it +/// would play the old pending audio and match B. +fn a_repointed_route_does_not_carry_its_line(backend: Backend, rate: u32) { + let what = format!("{backend:?} at {rate} Hz"); + let feed = Feed::new(SOURCE, 1); + for silent in [Some("eq1"), None] { + let a_model = two_bus_session(rate, silent); + let mut b_model = a_model.clone(); + send_route_to(&mut b_model, "eq1", "bus2"); + let (a, b) = (Session::compile(a_model), Session::compile(b_model)); + let (run, reference) = pdc_runs(&a, &b, &feed, backend, &what); + assert_eq!( + run.restarted, + Vec::>::new(), + "{what}: the re-pointed route's strip keeps its state" + ); + // Before the swap A renders, and A routes `eq1` through the other bus. + let difference = run.blocks[SWAP_BLOCK..] + .iter() + .zip(&reference.blocks[SWAP_BLOCK..]) + .position(|(swapped, fresh)| swapped != fresh); + if silent.is_some() { + assert_eq!( + difference, None, + "{what}: every other line carried, and the re-pointed one held silence" + ); + } else { + assert!( + difference.is_some(), + "{what}: the new route's line starts at rest, so B's audio is missing after the swap" + ); + } + } +} + +#[test] +fn a_repointed_route_does_not_carry_its_line_at_every_rate_and_width() { + for backend in backends() { + for rate in PDC_RATES { + a_repointed_route_does_not_carry_its_line(backend, rate); + } + } +} + // Issue #1285 (slice 15 of #1269): latency floors. A successor is compiled with every node's // input arrival in its predecessor as a floor, so removing a latent effect, or the track that // holds it, moves no surviving path in time: every compensation line keeps its length and carries. @@ -10195,6 +10261,82 @@ mod spectrum_carry { ); } + /// Gate 1: a continuous capture keeps reading, window for window, across the swap. + #[test] + fn a_continuous_capture_carries_and_its_reader_keeps_receiving() { + let (a, b) = sessions(); + let request = stereo_post_matrix(); + let expected = reference(&b, &request, Drive::Stream); + let run = swapped( + &a, + &request, + &b, + &request, + Drive::Stream, + Promote::AsReported, + ); + assert_eq!(run.blocks[SWAP].carry, CarryOutcome::Carried); + assert_eq!(run.blocks[SWAP].swap, SwapOutcome::Applied); + assert_eq!( + run.pairing, + vec![SpectrumPair { + successor: 0, + predecessor: 0 + }] + ); + assert_eq!( + run.record, + vec![ObserverCarry { + kind: ObserverKind::Spectrum, + successor: 0, + predecessor: 0 + }], + "the swap block moved exactly the observers the control side's list pairs" + ); + + let reads = &run.driver.reads; + assert_eq!(reads.len(), TOTAL); + let first_ready = reads + .iter() + .position(|read| read.status == SpectrumStreamStatus::Ready) + .expect("the stream produces before the swap"); + assert!(first_ready < SWAP, "a window is complete before the swap"); + let mut next_sequence = 0; + let mut spanning = false; + let mut after_swap = 0; + for (block, (got, want)) in reads.iter().zip(&expected.reads).enumerate() { + assert_eq!(got.status, want.status, "block {block}: status"); + if block > first_ready { + assert!( + matches!( + got.status, + SpectrumStreamStatus::Ready | SpectrumStreamStatus::Pending + ), + "block {block}: no Failed, Gap or Warming after the stream first produced, got {:?}", + got.status + ); + } + match (&got.window, &want.window) { + (Some(got), Some(want)) => { + assert_same_window(got, want, &format!("block {block}")); + assert_eq!(got.sequence, next_sequence, "block {block}: no gap"); + assert_eq!(got.stream_epoch, 1, "block {block}: the epoch never moved"); + next_sequence += 1; + spanning |= got.first_sample < SWAP_SAMPLE + && got.end_sample().expect("end") > SWAP_SAMPLE; + after_swap += usize::from(block >= SWAP); + } + (None, None) => {} + _ => panic!("block {block}: a window on one side only"), + } + } + assert!( + spanning, + "a window spans the swap, or the carry is not tested" + ); + assert!(after_swap >= 3, "the reader keeps receiving after the swap"); + } + /// Without the pairing the host reads the predecessor's capture, whose observer sits in the /// displaced plan: the oracle above can fail. #[test] diff --git a/crates/host-core/tests/symmetry_witness.rs b/crates/host-core/tests/symmetry_witness.rs index 0dc05144f..fb8e70286 100644 --- a/crates/host-core/tests/symmetry_witness.rs +++ b/crates/host-core/tests/symmetry_witness.rs @@ -610,6 +610,46 @@ fn a_left_channel_command_declines_exactly_one_lane() { ); } +/// Two per-lane EQ **target** writes that leave the channels agreeing, applied in one block, keep +/// the lane; written apart, or to different words, they decline it. +/// +/// # The contract this pins, after decision 15's cells (issue #1345 D3) +/// +/// The EQ is a prepared-target owner: every edit reaches the drain as designed targets in cells, +/// one per `(slot, channel)`. A `Left` edit and a `Right` edit to the same value are two owner +/// transactions that write the two channel cells with equal designed words. The lane folds its +/// witness from the cells' final contents: two cells applied in one block with equal words are one +/// both-channel write (and are applied once, as a `Both` target), so the lane keeps `LIVE`. This is +/// the structural form of #1004's pairing rule. It was not the rule while targets were a FIFO: a +/// FIFO has no last-wins staging, so equal last targets per channel did not mean equal state, and +/// the pair declined. A cell holds the last write, so equal words mean equal state, given that +/// `LIVE` held before (the channels were doing identical work). +/// +/// What still declines: a pair split across two blocks (each block sees a lone write), words that +/// differ, and `[Left A, Both C, Right A]` (the cells end at `C` and `A`). +/// +/// Red mutations: fold each applied target cell as a lone write (never pairing) -> the first test +/// fails; pair any two applied target cells whatever their words -> the other two fail. +#[test] +fn two_per_lane_target_writes_that_agree_in_one_block_keep_the_lane() { + let (_session, mut console) = prepare(&mono_session()); + let [before, lanes] = census(&console); + assert_eq!(before, lanes, "the fixture starts fully eligible"); + + push(&mut console, "eq2", parameter(ParameterChannel::Left, -6.0)); + push( + &mut console, + "eq2", + parameter(ParameterChannel::Right, -6.0), + ); + render(&mut console, 8); + assert_eq!( + census(&console), + [before, lanes], + "two one-channel EQ targets with equal words, applied in one block, are one both-channel write" + ); +} + #[test] fn two_per_lane_target_writes_split_across_blocks_or_unequal_decline_the_lane() { let (_session, mut console) = prepare(&mono_session()); @@ -652,7 +692,7 @@ const COMPRESSOR_SESSION: &str = /// Twin `Parameter` spans keep the lane; a lone half, or a twin one ulp apart, declines it. /// /// The span half of the contract `two_per_lane_target_writes_that_agree_in_one_block_keep_the_lane` -/// (deleted by TA5) stated for targets (issue #1004, issue #1012). A `Left` record and a `Right` record of the same +/// states for targets (issue #1004, issue #1012). A `Left` record and a `Right` record of the same /// parameter and the same value, drained in one block, stage as adjacent twin spans; the /// compressor validates and applies `pending[0][p]` and `pending[1][p]` by the same code from the /// same value, so the two channels stay in bit-equal state and `LIVE` is kept. Any one-channel diff --git a/crates/host-core/tests/timeline.rs b/crates/host-core/tests/timeline.rs index 624e19323..024e8aee3 100644 --- a/crates/host-core/tests/timeline.rs +++ b/crates/host-core/tests/timeline.rs @@ -532,6 +532,31 @@ fn a_seek_queued_at_the_swap_block_lands_in_the_successors_first_block() { ); } +/// The D3 guard (batch 16a fold MINOR 3): a successor prepared by hand at another rate or quantum +/// gets a fresh timeline with a producer of its own, not a vacant one whose carry the swap block +/// would refuse. +/// +/// Test value: red if the guard is dropped (every successor's timeline vacant): this successor +/// would hold no timeline producer until a carry that cannot happen. +#[test] +fn a_successor_at_another_rate_or_quantum_starts_a_fresh_timeline() { + let a = Session::compile(one_track()); + let predecessor = a.prepare(Backend::current()); + for (rate, quantum) in [(44_100, QUANTUM as u32), (48_000, 2 * QUANTUM as u32)] { + let mut model = one_track(); + model.sample_rate_hz = rate; + model.quantum_frames = quantum; + let b = Session::compile(model); + let mut successor = successor_of(&b, &a, &predecessor.inventory, false); + let fresh = producer(&mut successor.sources); + assert_eq!( + fresh.active_generation(), + SourceGeneration(1), + "{rate} Hz, {quantum} frames" + ); + } +} + /// Gate 2, a successor with a larger `A_max`: its history is its own, of its own `K`. /// /// Test value: red if the successor sizes its history from the predecessor's `K` (a seek its diff --git a/crates/lane/tests/MUTATIONS.md b/crates/lane/tests/MUTATIONS.md index 53978d704..d22967bde 100644 --- a/crates/lane/tests/MUTATIONS.md +++ b/crates/lane/tests/MUTATIONS.md @@ -372,7 +372,7 @@ it; the count exists beside the digests for the same reason `minmax_lowering_mis ## Issue #954 — the select-free fused fader/matrix kernel `fader_matrix_block_without_identity` is `fader_matrix_block`'s second arm with no `L::select` in -the body. Gate 1, `select_free_fused_fader_matrix_matches_both_oracles_when_no_lane_is_identity` (deleted by TA5; no catcher recorded) +the body. Gate 1, `select_free_fused_fader_matrix_matches_both_oracles_when_no_lane_is_identity` (deleted by TA5; catcher builtins `fused_fader_matrix_shapes_match_the_separate_stages`) (`tests/fader_matrix.rs`), holds it equal to two oracles with no identity lane: oracle A is `fader_matrix_block` with an all-false mask, oracle B is `gain_mute_block` on each plane followed by `matrix2x2_block_without_identity`. It covers five input families (the four hostile ones and an diff --git a/crates/lane/tests/input_chain_arming.rs b/crates/lane/tests/input_chain_arming.rs index e340d3716..e242409b9 100644 --- a/crates/lane/tests/input_chain_arming.rs +++ b/crates/lane/tests/input_chain_arming.rs @@ -240,6 +240,52 @@ fn the_right_channel_arms_its_joint_flush_alone() { lane::each_lane!(|L| right_channel_alone_arms::(core::any::type_name::())); } +/// Every state word: one word of one section of the silent channel in the band, every other word +/// `+0.0`. The block must arm and clear it, on both channels and in the collapsed bodies. +fn every_state_word_arms(width: &str) { + for entry in ENTRIES { + for mono in [false, true] { + for channel in 0..if mono { 1 } else { 2 } { + let sections = if entry == Entry::Mixed { 1..2 } else { 0..2 }; + for section in sections { + for word in 0..2 { + let mut c = chain::(entry, WINDOW); + let mut s = InputChainState::::default(); + let target = &mut s.section[channel][section]; + if word == 0 { + target.ic1 = L::splat(BAND[0]); + } else { + target.ic2 = L::splat(BAND[1]); + } + s.silence[channel] = L::splat(WINDOW - 1.0); + let mut planes = [live(), live()]; + planes[channel] = vec![0.0; FRAMES * 8]; + let [left, right] = &mut planes; + run( + entry, + mono, + &mut left[..FRAMES * L::WIDTH], + &mut right[..FRAMES * L::WIDTH], + &mut c, + &mut s, + ); + assert!( + state_bits(&s, channel).iter().all(|bits| *bits == 0), + "{width}, {entry:?}, mono {mono}: channel {channel}, section \ + {section}, word {word} in the band arms the block" + ); + } + } + } + } + } +} + +#[test] +fn any_state_word_in_the_band_arms_the_block() { + lane::each_lane!(|L| every_state_word_arms::(core::any::type_name::())); +} + /// A block longer than the window: the channel starts at rest with a fresh counter, takes one tiny /// sample on frame 0 and zeros after it. Its counter reaches the window on frame `window`, inside /// the block, with the tail's state in the band: the block must arm though no lane held state at diff --git a/crates/math/tests/f1_fast_db_bounds.rs b/crates/math/tests/f1_fast_db_bounds.rs index f18963971..0174b3666 100644 --- a/crates/math/tests/f1_fast_db_bounds.rs +++ b/crates/math/tests/f1_fast_db_bounds.rs @@ -394,6 +394,24 @@ fn f1_domain_and_clamping() { // Subsampled bounds — these run in the default `cargo test`. // --------------------------------------------------------------------------------------------- +#[test] +fn f1_gain_from_db_subsample() { + let (lo, hi) = gain_domain(); + assert_sweep( + "fast_gain_from_db negative subsample", + sweep(lo, hi, 4099, gain_error_db), + GAIN_MAX_DB, + 200_000, + ); + let (lo, hi) = gain_domain_positive(); + assert_sweep( + "fast_gain_from_db positive subsample", + sweep(lo, hi, 4099, gain_error_db), + GAIN_MAX_DB, + 200_000, + ); +} + #[test] fn f1_level_db_subsample() { let (lo, hi) = level_domain(); diff --git a/crates/multiband-compressor/src/shim.rs b/crates/multiband-compressor/src/shim.rs index d10813070..e6d9a0c8e 100644 --- a/crates/multiband-compressor/src/shim.rs +++ b/crates/multiband-compressor/src/shim.rs @@ -123,6 +123,23 @@ mod tests { ); } + /// Both ends are exact: a zero coefficient arrives, a unit coefficient freezes. + #[test] + fn the_smoother_has_exact_ends() { + for target in [-42.5f32, 0.0, -0.0, -100.0] { + assert_eq!( + branching_smooth::(-7.5, target, 0.0, 0.0).to_bits(), + target.to_bits(), + "a zero coefficient must arrive exactly" + ); + assert_eq!( + branching_smooth::(-7.5, target, 1.0, 1.0), + -7.5, + "a unit coefficient must freeze" + ); + } + } + /// The three link modes, including the `±0.0`, overflow and NaN corners. /// /// Red mutations: maximum becomes minimum; the `abs` is dropped; the average halves the sum. diff --git a/crates/multiband-compressor/tests/product.rs b/crates/multiband-compressor/tests/product.rs index ce2985bc0..3348affe3 100644 --- a/crates/multiband-compressor/tests/product.rs +++ b/crates/multiband-compressor/tests/product.rs @@ -1338,6 +1338,59 @@ fn the_silence_counter_is_carried_and_validated() { } } +/// Issue #1328 amendment A9: each channel's silence counter counts that channel's own input. +/// +/// One channel silent for ten blocks and the other live: the silent channel's counter reads the +/// 1,280 frames and the live one's reads `+0.0`, in both orders. A counter stepped from the other +/// channel's input would arm the live channel's joint flush on the silent one's run. +#[test] +fn each_channel_counts_its_own_input() { + const COUNTER_WORD: usize = 47; + let counter = |section: &[u8]| { + f32::from_le_bytes( + section[COUNTER_WORD * 4..COUNTER_WORD * 4 + 4] + .try_into() + .expect("word"), + ) + }; + for silent_left in [true, false] { + let prepared = MultibandCompressorFactory + .prepare(request(&values())) + .expect("prepare"); + let sizes = prepared.metadata.state_sizes; + let mut effect = prepared.processor; + for block in 0..10_u64 { + let live = support::signal(128, 0x1328_0100 + block); + let silent = vec![0.0_f32; 128]; + let (mut left, mut right) = if silent_left { + (silent, live) + } else { + (live, silent) + }; + process( + effect.as_mut(), + &mut left, + &mut right, + block * 128, + &[], + 128, + ); + } + let saved = snapshot(effect.as_ref(), sizes); + let (left, right) = (counter(&saved.1), counter(&saved.2)); + let (expected_left, expected_right) = if silent_left { + (1_280.0, 0.0) + } else { + (0.0, 1_280.0) + }; + assert_eq!( + (left.to_bits(), right.to_bits()), + (f32::to_bits(expected_left), f32::to_bits(expected_right)), + "silent left {silent_left}: the counters are ({left}, {right})" + ); + } +} + /// Issue #1328 amendment A9: the crossover's joint flush arms on the frame its channel's input has /// been silent for `N_SILENCE` frames (4,096 at 48 kHz), and not before. /// diff --git a/crates/parametric-eq/src/lib.rs b/crates/parametric-eq/src/lib.rs index 1a6622e17..67b46dea6 100644 --- a/crates/parametric-eq/src/lib.rs +++ b/crates/parametric-eq/src/lib.rs @@ -8501,6 +8501,164 @@ mod padded_banks { } } + /// Gate 3 (P2a verdict, L4): a padded lane fed `+0.0` writes exactly `+0.0` -- never `-0.0`, + /// never a subnormal -- and keeps every integrator exactly `+0.0`, finite and at rest, block + /// after block, whatever design it clones and whatever schedule its bank takes. + /// + /// Every band family, at both gain extremes and both Q extremes, on all four bands, with both + /// cuts on and the right channel detuned, cloned into every padded lane of a one-member bank at + /// both widths. The member drives the bank through each schedule it has: stationary and elided + /// (quiet music), refused (a `-0.0` word, then a non-finite one), ramping (a prepared target on + /// the member mid-run, so every lane runs the ramped kernel and the padded ones with a `+0.0` + /// increment) and faulting (a `9.5e29` sine, D7). `Channel::recover_failed_lanes` states why + /// the frame body writes `+0.0` from `+0.0` for every designed word; this is that claim, + /// measured. + /// + /// Red if a padded lane is not at rest when bound, or if any path writes it a signed zero or a + /// word other than `+0.0` (a padded lane's output feeds the next slot of a chain and its own + /// next block, so a `-0.0` there would travel). + #[test] + fn a_padded_lane_fed_positive_zero_writes_positive_zero_at_rest() { + let rate = 48_000; + let parameters = PARAMETRIC_EQ_DESCRIPTOR.parameters.len(); + let mut blocks = 0_u64; + for lanes in lane_counts() { + for kind in 1..=6_u32 { + for (gain, q) in [(-24.0, 0.1), (24.0, 18.0), (-24.0, 18.0), (24.0, 0.1)] { + let mut values = Vec::with_capacity(parameters * 2); + for index in 0..parameters { + for (side, channel) in [ParameterChannel::Left, ParameterChannel::Right] + .into_iter() + .enumerate() + { + let detune = 1.0 + 0.1 * side as f32; + let value = match (index < 24, index % 6, index) { + (true, 0, _) => 1.0, + (true, 1, _) => kind as f32, + (true, 2, _) => 200.0 * (1 + index / 6) as f32 * detune, + (true, 3, _) => gain, + (true, 4, _) => q, + (true, _, _) => 0.5, + (false, _, 24 | 27) => 1.0, + (false, _, 25) => 20.0 * detune, + (false, _, 28) => 18_000.0 / detune, + (false, _, _) => q, + }; + values.push(InitialParameterValue { + parameter_index: index as u32, + channel, + value, + }); + } + } + let initial = [values]; + let member_of = vec![0; lanes]; + let mask: Vec = (0..lanes).map(|lane| lane == 0).collect(); + let mut bank = bind(lanes, &requests(&initial, &member_of, rate), &mask); + let at_bind: Vec = (1..lanes) + .map(|lane| lane_payload(bank.as_ref(), lane)) + .collect(); + let context = format!("{lanes} lanes, kind {kind}, gain {gain}, Q {q}"); + let mut planes = [vec![0.0; QUANTUM * lanes], vec![0.0; QUANTUM * lanes]]; + let offsets = vec![0_u32; lanes + 1]; + for block in 0..12_u64 { + let first = block * QUANTUM as u64; + if block == 4 { + // The member's first band retuned: a 64-sample ramp on lane 0 only. + let mut candidate = initial[0].clone(); + let mut changed = vec![false; parameters * 2]; + for side in 0..2 { + candidate[2 * 2 + side].value = 3_000.0; + changed[2 * 2 + side] = true; + } + let mut targets = [PreparedEffectTarget { + slot: 0, + channel: ParameterChannel::Left, + words: [0; effect_contract::PREPARED_EFFECT_TARGET_WORDS], + }; EQ_SECTION_COUNT * 2]; + let count = ParametricEqFactory + .prepare_targets( + EffectTargetRequest { + sample_rate: rate, + values: &candidate, + changed: &changed, + }, + &mut targets, + ) + .expect("a legal target"); + for target in &targets[..count] { + bank.apply_prepared_target_lane(0, target) + .expect("a target on the member"); + } + } + let mut input = [ + sine(1.0e-3, first, QUANTUM, rate, 0.0), + sine(1.0e-3, first, QUANTUM, rate, 1.9), + ]; + match block { + 2 => input[0][17] = -0.0, + 3 => input[1][40] = f32::NAN, + 6 | 7 => { + input = [ + sine(9.5e29, first, QUANTUM, rate, 0.0), + sine(9.5e29, first, QUANTUM, rate, 1.9), + ]; + } + _ => {} + } + gather(&mut planes, lanes, &[0], &[input]); + render( + bank.as_mut(), + &mut planes, + lanes, + QUANTUM, + first, + false, + &[], + &offsets, + ); + for lane in 1..lanes { + for (channel, plane) in planes.iter().enumerate() { + if let Some(word) = column(plane, lanes, lane, QUANTUM) + .into_iter() + .find(|word| word.to_bits() != 0) + { + panic!( + "{context}, block {block}: padded lane {lane}, channel \ + {channel} wrote {:#010x}", + word.to_bits() + ); + } + } + let payload = lane_payload(bank.as_ref(), lane); + for (side, words) in payload.iter().enumerate().skip(1) { + for section in 0..EQ_SECTION_COUNT { + for integrator in 0..2 { + let word = + (section * STATE_WORDS_PER_BAND + integrator) * 4; + assert_eq!( + &words[word..word + 4], + &[0; 4], + "{context}, block {block}: padded lane {lane}, \ + channel {side}, section {section}: an integrator \ + left +0.0" + ); + } + } + } + assert!( + without_silence(&payload) == without_silence(&at_bind[lane - 1]), + "{context}, block {block}: padded lane {lane} moved a word" + ); + } + blocks += 1; + } + } + } + } + assert!(blocks > 0); + } + /// Gate 4, the planted state: a non-finite integrator planted in one member recovers that /// member alone and reports it alone, at every member count and on either channel. /// diff --git a/crates/parametric-eq/tests/MUTATIONS.md b/crates/parametric-eq/tests/MUTATIONS.md index 6b9f70a00..cacb559ee 100644 --- a/crates/parametric-eq/tests/MUTATIONS.md +++ b/crates/parametric-eq/tests/MUTATIONS.md @@ -459,7 +459,7 @@ it names, the named tests run, and the source restored. |---|---|---|---| | 1328-M4 | the rest threshold always armed (attempt 3's law: `silence_step` returns `REST_EPS`, `silence_armable` always true) | `exact_rest::a_sparse_input_through_four_boosting_shelves_gets_every_boost_at_every_rate` (`44100 Hz, 10 Hz shelves, period 2 … misses the ordinary run by 1.102885e3`), `a_tiny_input_through_four_boosting_shelves_gets_every_boost`, `an_exact_zero_inside_the_cascade_moves_nothing_while_the_input_is_live` | RED | | 1328-M5 | A8's law: armed by a zero *section* input (thresholds from `v0 == 0` in `svf_step_when`, every block armable) | the sparse gate (`… misses the ordinary run by 1.0375549e3`) and the cancellation gate (`DC at 1e-6 … misses the ordinary run by 6.996817e-5 … (peak 7.826055e3)`); the chain gate stays green | RED | -| 1328-M6 | `Channel::commit_track` drops the restored silence word | `carry::a_restore_mid_silence_arms_the_joint_flush_on_the_continuing_lanes_frame` (deleted by TA5; catcher `a_restore_refused_on_one_channel_moves_neither`), `carry::a_malformed_silence_word_is_refused` | RED | +| 1328-M6 | `Channel::commit_track` drops the restored silence word | `carry::a_restore_mid_silence_arms_the_joint_flush_on_the_continuing_lanes_frame` (deleted by TA5; catcher `a_malformed_silence_word_is_refused`), `carry::a_malformed_silence_word_is_refused` | RED | | 1328-M7 | `desymmetrize` drops `self.right.silence = self.left.silence` | `mono_collapse::a_desymmetrized_bank_carries_the_collapsed_channels_silence_counter` | RED | | 1328-M8 | the silent fast path earned without every lane armed (`quiet && (armed \|\| true)`) | `exact_rest::a_fixed_point_inside_the_joint_band_is_cleared_once_the_flush_arms` | RED | | 1328-M9 | the silent fast path skips without `silence_advance` | the same test (`the silence counter after 68 silent blocks`) | RED | diff --git a/crates/protocol/src/controller/tests.rs b/crates/protocol/src/controller/tests.rs index 47155d884..55ecf6c9e 100644 --- a/crates/protocol/src/controller/tests.rs +++ b/crates/protocol/src/controller/tests.rs @@ -2948,6 +2948,155 @@ fn an_event_frame_under_the_retired_cancellation_id_is_refused() { ); } +#[test] +fn b3b1_btlv_telemetry_echo_and_counters_are_typed_and_nondestructive() { + fn frame( + message_id: MessageId, + request_id: u64, + revision: ExpectedRevision, + payload: &[u8], + count: u32, + ) -> Vec { + let codec = ProtocolCodec::default(); + let mut bytes = vec![0_u8; crate::OUTER_HEADER_BYTES + payload.len()]; + let (wire_revision, flags) = match revision { + ExpectedRevision::Any => (0, 1), + ExpectedRevision::Exact(revision) => (revision.0, 0), + }; + codec + .write_outer_header( + &mut bytes, + crate::FrameKind::Command, + message_id, + StatusCode::Ok, + request_id, + wire_revision, + flags, + payload.len() as u32, + count, + ) + .expect("header"); + bytes[crate::OUTER_HEADER_BYTES..].copy_from_slice(payload); + bytes + } + let codec = ProtocolCodec::default(); + let configuration = TelemetryConfiguration { + meter_handles: vec![1], + meter_period_blocks: 4, + counter_ids: vec![crate::CounterId::ControlCommandBackpressure], + counter_period_blocks: 8, + diagnostics_enabled: false, + minimum_diagnostic_severity: crate::DiagnosticSeverity::Warning, + }; + let mut config_payload = vec![ + 0; + codec + .encoded_telemetry_configuration_len(&configuration) + .expect("config length") + ]; + codec + .encode_telemetry_configuration(&configuration, &mut config_payload) + .expect("config"); + let mut controller = controller(8); + let first = controller + .process_b1b_btlv( + &frame( + MessageId::TelemetryConfigure, + 1, + ExpectedRevision::Exact(SessionRevision(7)), + &config_payload, + 6, + ), + &mut DecodeScratch::new(&mut [0_u16; 6]), + ) + .expect("configure"); + assert_eq!(first.status, StatusCode::Ok); + assert_eq!( + codec.decode_telemetry_configuration(first.payload(), 6), + Ok(configuration.clone()) + ); + assert_eq!(controller.provider().telemetry_configuration, configuration); + let second = controller + .process_b1b_btlv( + &frame( + MessageId::TelemetryConfigure, + 2, + ExpectedRevision::Exact(SessionRevision(7)), + &config_payload, + 6, + ), + &mut DecodeScratch::new(&mut [0_u16; 6]), + ) + .expect("idempotent configure"); + assert_eq!(second.payload(), first.payload()); + + let request = CountersRequest { + all: false, + ids: vec![1, 5], + }; + let mut request_payload = [0_u8; 32]; + codec + .encode_counters_request(&request, &mut request_payload) + .expect("counter request"); + let first = controller + .process_b1b_btlv( + &frame( + MessageId::CountersGet, + 3, + ExpectedRevision::Any, + &request_payload, + 2, + ), + &mut DecodeScratch::new(&mut [0_u16; 2]), + ) + .expect("counter read"); + let second = controller + .process_b1b_btlv( + &frame( + MessageId::CountersGet, + 4, + ExpectedRevision::Any, + &request_payload, + 2, + ), + &mut DecodeScratch::new(&mut [0_u16; 2]), + ) + .expect("nonreset read"); + assert_eq!(first.payload(), second.payload()); + assert_eq!( + codec + .decode_counter_snapshot(first.payload(), 3) + .expect("snapshot") + .values + .len(), + 2 + ); + let unknown = CountersRequest { + all: false, + ids: vec![99], + }; + let mut unknown_payload = [0_u8; 32]; + codec + .encode_counters_request(&unknown, &mut unknown_payload) + .expect("unknown selector"); + assert_eq!( + controller + .process_b1b_btlv( + &frame( + MessageId::CountersGet, + 5, + ExpectedRevision::Any, + &unknown_payload, + 2 + ), + &mut DecodeScratch::new(&mut [0_u16; 2]), + ) + .expect("unknown response") + .status, + StatusCode::NotFound + ); +} + #[test] fn b3b2_diagnostics_btlv_pages_are_typed_nondestructive_and_expire_old_cursors() { fn frame(request_id: u64, request: DiagnosticsRequest) -> Vec { diff --git a/crates/protocol/src/model.rs b/crates/protocol/src/model.rs index a17469f7e..81926c033 100644 --- a/crates/protocol/src/model.rs +++ b/crates/protocol/src/model.rs @@ -1703,7 +1703,7 @@ mod tests { use super::*; use crate::control_rows::row_address; use controls::{Lane, Owner, Section}; - use session::{ControlSmoothing, canonical_session_json, parse_session_json}; + use session::{ControlSmoothing, ParameterChannel, canonical_session_json, parse_session_json}; const EXAMPLE: &str = include_str!("../../../fixtures/session/v1/canonical.json"); @@ -2279,6 +2279,50 @@ mod tests { assert_eq!(store.compiled().normalized_model().sample_rate_hz, 96_000); } + #[test] + fn effects_and_parameters_follow_exact_key_rules() { + let mut store = store(); + let edit = SessionEdit::set_control( + controls::ControlAddress { + owner: Owner::Strip(id("vocal")), + section: Section::Inserts(id("eq")), + control: 1, + lane: Lane::Both, + }, + 1.0, + None, + ); + store + .apply_transaction( + ExpectedRevision::Exact(store.revision()), + &[ + // The fixture's effect id is not a launch effect, so name one the control + // core can address. + SessionEdit::SetEffectIdentity { + track_id: id("vocal"), + rack_name: RackName::Inserts, + effect_id: id("eq"), + identity: EffectIdentity::Native { + effect_id: id("miso.parametric-eq"), + }, + }, + edit.clone(), + edit, + ], + ) + .expect("same compound key replaces"); + let effect = &store.compiled().normalized_model().tracks[0] + .inserts + .effects[0]; + let written: Vec<_> = effect + .params + .iter() + .filter(|param| param.parameter_id == 1 && param.channel == ParameterChannel::Both) + .collect(); + assert_eq!(written.len(), 1); + assert_eq!(written[0].value, 1.0); + } + fn store_at(revision: u64) -> SessionStore { let mut model = parse_session_json(EXAMPLE).expect("fixture"); model.revision = revision; diff --git a/crates/rack/tests/MUTATIONS.md b/crates/rack/tests/MUTATIONS.md index 5e37dc1b2..47af72b89 100644 --- a/crates/rack/tests/MUTATIONS.md +++ b/crates/rack/tests/MUTATIONS.md @@ -24,7 +24,7 @@ cargo test --locked -p rack -p rack-compiler -p graph-compiler | M1 | `scatter_lane` reads the wrong lane: `*sample = chunk[lane]` becomes `chunk[lane ^ 1]` | `rack/src/lib.rs` | `gather_scatter_round_trip_is_bit_exact` | RED | ``assertion `left == right` failed: left lane=0 frame=0`` | | M2 | `gather_lane` chunks by frames instead of lanes: `chunks_exact_mut(lanes)` becomes `chunks_exact_mut(frames as usize)` | `rack/src/lib.rs` | `stage_sees_frame_major_layout` | RED | ``assertion `left == right` failed: lane=0 frame=1`` | | M3 | `BankChain::run` drops the identity-slot guard: `if slot.active_lanes.iter().any(..)` becomes `if true` | `rack/src/lib.rs` | `identity_everywhere_slot_is_not_executed` (deleted by TA5; catcher `prepared_slot_activity_preserves_dispatch_trace_and_errors`) | RED | ``assertion `left == right` failed: both live slots ran once per block; the identity-everywhere slot never ran`` | -| M3b | the transpose counter moves inside the slot loop (master plan §4.5: it is *per chain*, not per slot) | `rack/src/lib.rs` | `identity_everywhere_slot_is_not_executed` (deleted by TA5; catcher `prepared_slot_activity_preserves_dispatch_trace_and_errors`) | RED | ``assertion `left == right` failed`` (2 live slots ⇒ 10 counted round-trips for 5 blocks) | +| M3b | the transpose counter moves inside the slot loop (master plan §4.5: it is *per chain*, not per slot) | `rack/src/lib.rs` | `identity_everywhere_slot_is_not_executed` (deleted by TA5; catcher console-workload `the_ragged_tail_banks_like_any_other_cohort_and_pays_one_chain_for_it`) | RED | ``assertion `left == right` failed`` (2 live slots ⇒ 10 counted round-trips for 5 blocks) | | M4 | `BankChain::new` stops enforcing "a slot may only be active on an active lane" | `rack/src/lib.rs` | `chain_new_rejects_mask_shape_and_lane_implication` | RED | ``assertion `left == right` failed: a slot may not be active on a lane the chain never gathers`` | | M5 | `AoSoaScratch::new` re-adds a plane: `vec![0.0; length]` becomes `vec![0.0; length * 2]` for the left plane | `rack/src/lib.rs` | `scratch_allocates_exactly_two_planes` | RED | ``assertion `left == right` failed`` | | M6 | `gather_lane` drops the first frame of every block: `.zip(left)` becomes `.zip(&left[1..])` | `rack/src/lib.rs` | `chain_run_is_partition_invariant` | RED | ``assertion `left == right` failed: partition=1 lane=0 frame=0`` | diff --git a/crates/rack/tests/live_control_bank.rs b/crates/rack/tests/live_control_bank.rs index c8f02ced2..7d5639d8d 100644 --- a/crates/rack/tests/live_control_bank.rs +++ b/crates/rack/tests/live_control_bank.rs @@ -6,6 +6,8 @@ //! packed span array correctly, that a lane never sees another lane's command, and that a bypassed //! lane gets its own latency-matched dry signal while its neighbours keep the wet one. +use std::sync::{Arc, Mutex}; + use effect_contract::{ BankProcessReport, BankWidth, EffectBankProcessBlock, EffectCellWriter, EffectControlLane, EffectControlRecord, EffectId, EffectLaneShape, EffectLaneSpec, EffectProgramKey, @@ -14,8 +16,11 @@ use effect_contract::{ PreparedSidechainPort, ResetKind, StatePayloadError, StatePayloadInput, StatePayloadOutput, StatePayloadSizes, TailSamples, effect_control_lane, }; -use engine::realtime::{GatePin, LiveSnapshot, LiveValuesSuperseded, RevisionGate}; -use rack::{AoSoaScratch, BankChain, BankMembers, BankSlot, BankStage, LiveControlEffectBankStage}; +use engine::realtime::{GatePin, LiveSnapshot, LiveValuesSuperseded, RenderError, RevisionGate}; +use rack::{ + AoSoaScratch, BankBlock, BankChain, BankMembers, BankSlot, BankStage, + LiveControlEffectBankStage, +}; const LANES: usize = 4; const CAPACITY: u32 = 4; @@ -487,6 +492,81 @@ fn a_live_control_stage_reports_the_lanes_that_hold_a_channel() { assert_eq!(stage_of([true; LANES]).live_lanes(), 0b1111); } +/// #1504: `run` hands the block's live snapshot to every slot that has an active lane, once in +/// every block, silent blocks included, and to no slot that has none. +/// +/// This lives here and not in `src/lib.rs`'s inline module because a `BankStage` is `Send`, so a +/// test reads what the chain's stages recorded through an `Arc>`, and +/// `scripts/check-rack-policy.sh` forbids `std::sync` under `crates/rack/src` (render code). +/// +/// Test value: a chain that passes a fixed or stale snapshot to a stage, or skips a stage's +/// drain when the block is silent. Under `S` that makes the watermark early. +#[test] +fn run_hands_the_blocks_snapshot_to_every_active_slot_in_every_block() { + type Drains = Arc>>; + struct Recording { + slot: usize, + drains: Drains, + } + impl BankStage for Recording { + fn process(&mut self, _block: BankBlock<'_>) -> Result<(), RenderError> { + Ok(()) + } + fn begin_block( + &mut self, + first_sample: u64, + snapshot: LiveSnapshot, + ) -> Result<(), RenderError> { + self.drains.lock().expect("drains").push(( + self.slot, + first_sample, + snapshot.revision(), + )); + Ok(()) + } + } + let frames = 8_u32; + // Every block is silent: the planes stay zero. + let mut planes = Planes { + left: (0..LANES).map(|_| vec![0.0; frames as usize]).collect(), + right: (0..LANES).map(|_| vec![0.0; frames as usize]).collect(), + }; + let drains = Drains::default(); + let slot = |slot: usize, active_lanes: [bool; LANES]| BankSlot { + stage: Box::new(Recording { + slot, + drains: Arc::clone(&drains), + }), + active_lanes: active_lanes.to_vec().into_boxed_slice(), + }; + let mut chain = BankChain::new( + AoSoaScratch::new(BankWidth::Four, frames).expect("scratch"), + vec![true; LANES].into_boxed_slice(), + vec![ + slot(0, [true; LANES]), + // An identity on every lane: it runs nothing, so it has no drain to run. + slot(1, [false; LANES]), + slot(2, [true, true, false, false]), + ], + ) + .expect("chain"); + let (gate, mut pin) = RevisionGate::pair(0); + let revisions = [0_u64, 3, 3, 9]; + let mut expected = Vec::new(); + for (block, revision) in revisions.into_iter().enumerate() { + if revision > pin.last_published() { + pin.publish(&gate, revision); + } + let first_sample = block as u64 * u64::from(frames); + chain + .run(&mut planes, frames, first_sample, gate.snapshot()) + .expect("run"); + expected.push((0, first_sample, revision)); + expected.push((2, first_sample, revision)); + } + assert_eq!(*drains.lock().expect("drains"), expected); +} + /// Shape is validated once, off the render thread. #[test] fn stage_construction_rejects_a_lane_count_or_quantum_mismatch() { diff --git a/crates/session/tests/json_grammar.rs b/crates/session/tests/json_grammar.rs index f77e489b2..db8f94903 100644 --- a/crates/session/tests/json_grammar.rs +++ b/crates/session/tests/json_grammar.rs @@ -21,6 +21,20 @@ fn only<'a>( diagnostic } +#[test] +fn insignificant_whitespace_and_shuffled_keys_canonicalize() { + let mut value: serde_json::Value = serde_json::from_str(CANONICAL).expect("fixture JSON"); + let object = value.as_object_mut().expect("root object"); + let schema = object.remove("schema_version").expect("schema"); + object.insert("schema_version".to_owned(), schema); + let source = serde_json::to_string(&value).expect("noncanonical JSON"); + let model = parse_session_json(&source).expect("valid noncanonical JSON"); + assert_eq!( + canonical_session_json(&model).expect("canonical"), + CANONICAL + ); +} + #[test] fn decoded_duplicate_path_and_second_key_span_are_exact() { let source = r#"{"outer":{"id":"first","\u0069d":"second","ignored":{"x":1}}}"#; diff --git a/crates/transient-shaper/tests/bank.rs b/crates/transient-shaper/tests/bank.rs index c84e6e8a8..1afadc63f 100644 --- a/crates/transient-shaper/tests/bank.rs +++ b/crates/transient-shaper/tests/bank.rs @@ -8,8 +8,10 @@ mod common; use common::*; use effect_contract::{ - EffectBankProcessBlock, EffectProcessBlock, LinkMode, ResetKind, StatePayloadInput, + BankWidth, EffectBankProcessBlock, EffectProcessBlock, LinkMode, NativeEffectFactory, + PrepareEffectBankRequest, ResetKind, StatePayloadInput, }; +use transient_shaper::TransientShaperFactory; /// Per-track parameter sets for state ownership: every lane a different program point. fn track_values(lanes: usize) -> Vec<[effect_contract::InitialParameterValue; 6]> { @@ -126,3 +128,63 @@ fn bank_snapshot_restore_and_resets_are_track_local() { .is_err() ); } + +/// Issue #1092 (console strip P2e): the transient shaper has opted into padding, so the public +/// factory binds a padded bank request of every active count at this build's width, and it still +/// refuses one whose member -- active or padded -- is malformed, with `prepare`'s own code. +/// +/// Red if the #1088 guard comes back (a padded request is declined), or if the padded lanes are no +/// longer validated (a malformed clone binds). `src/padding_tests.rs` holds what the bound bank +/// renders to the padding contract. +#[test] +fn a_padded_request_binds_and_still_validates_every_lane() { + let backend = lane::Backend::current(); + let width = BankWidth::for_backend(backend).expect("every product target banks"); + let lanes = width.lanes() as usize; + let values = initial_values(); + let requests = vec![request(&values); lanes]; + let bind = |requests: &[effect_contract::PrepareEffectRequest<'_>], mask: &[bool]| { + TransientShaperFactory.bind_homogeneous_bank(PrepareEffectBankRequest { + backend, + width, + requests, + active_mask: mask, + }) + }; + assert!( + bind(&requests, width.full_mask()) + .expect("a full bank") + .is_some(), + "the control: the same members bind as a full bank" + ); + for members in 1..lanes { + let mask: Vec = (0..lanes).map(|lane| lane < members).collect(); + let bank = bind(&requests, &mask) + .expect("a padded request is well formed") + .unwrap_or_else(|| panic!("{members} of {lanes} lanes active: the bank binds")); + assert_eq!(bank.metadata.width, width); + } + // Malform a member other than the first, so that a check of the first request alone -- or a + // decision taken above the member loop -- goes red. + let mut malformed = requests.clone(); + malformed[1].limits.maximum_total_state_bytes = 0; + let refusal = TransientShaperFactory + .prepare(malformed[1]) + .err() + .expect("a malformed member") + .code; + let mask: Vec = (0..lanes).map(|lane| lane < 2).collect(); + assert_eq!( + bind(&malformed, &mask).err().map(|error| error.code), + Some(refusal), + "a padded request still validates its members" + ); + let mut malformed = requests.clone(); + malformed[lanes - 1].limits.maximum_total_state_bytes = 0; + let mask: Vec = (0..lanes).map(|lane| lane == 0).collect(); + assert_eq!( + bind(&malformed, &mask).err().map(|error| error.code), + Some(refusal), + "and its padded lanes' clones" + ); +} diff --git a/crates/transient-shaper/tests/partition.rs b/crates/transient-shaper/tests/partition.rs new file mode 100644 index 000000000..10031f7eb --- /dev/null +++ b/crates/transient-shaper/tests/partition.rs @@ -0,0 +1,127 @@ +//! Partition invariance (master plan §10 P1): a block boundary must not be observable. +//! +//! The two places a boundary could show are the D11 ramp — whose iterated additions and exact snap +//! `LinearRamp::next_value` performs identically however the frames are grouped — and the ramp +//! prefix, which is recomputed per block from the ramps' own `remaining`. + +mod common; + +use common::*; +use effect_contract::{EffectBankProcessBlock, LinkMode, ParameterChannel}; + +/// Deterministic, integer-built noise with impulses; no platform math anywhere in the signal. +fn signal(seed: u64, frames: usize) -> Vec { + let mut state = seed | 1; + (0..frames) + .map(|index| { + state ^= state >> 12; + state ^= state << 25; + state ^= state >> 27; + let draw = state.wrapping_mul(0x2545_f491_4f6c_dd1d); + if index % 128 == 0 { + return 0.95; + } + ((draw >> 40) as i32 - 8_388_608 / 2) as f32 / 16_777_216.0 + }) + .collect() +} + +const PARTITIONS: [usize; 5] = [1, 7, 64, 128, 512]; + +/// One bank render's result: the two interleaved output blocks and one payload pair per track. +type RenderedBank = (Vec, Vec, Vec<(Vec, Vec)>); + +/// The same property at the build's bank width, where the ramp prefix is the maximum over lanes. +#[test] +fn the_bank_is_partition_invariant() { + let Some((_, width)) = native_bank() else { + println!("no bank width on this build; skipping"); + return; + }; + let lanes = width.lanes() as usize; + let frames = 512; + let planar: Vec<(Vec, Vec)> = (0..lanes) + .map(|track| { + ( + signal(0x1000 + track as u64 * 17, frames), + signal(0x2000 + track as u64 * 23, frames), + ) + }) + .collect(); + let values: Vec<_> = (0..lanes) + .map(|track| values_of(-0.5 + track as f32 * 0.1, 0.75 - track as f32 * 0.1, 1.0)) + .collect(); + let spans: Vec<_> = (0..lanes) + .map(|track| point(ParameterChannel::Left, 0, 0, -0.875 + track as f32 * 0.125)) + .collect(); + let offsets: Vec = (0..=lanes as u32).collect(); + + let render = |block: usize| -> RenderedBank { + let mut bank = bind_native_bank_quantum(&values, LinkMode::Maximum, 512).expect("bank"); + let mut left = vec![0.0_f32; frames * lanes]; + let mut right = vec![0.0_f32; frames * lanes]; + for frame in 0..frames { + for track in 0..lanes { + left[frame * lanes + track] = planar[track].0[frame]; + right[frame * lanes + track] = planar[track].1[frame]; + } + } + let mut start = 0; + while start < frames { + let end = (start + block).min(frames); + let (spans, offsets): (&[_], &[u32]) = if start == 0 { + (&spans, &offsets) + } else { + (&[], &[0; 9][..lanes + 1]) + }; + bank.process_bank( + EffectBankProcessBlock::new( + &mut left[start * lanes..end * lanes], + &mut right[start * lanes..end * lanes], + None, + (end - start) as u32, + width, + start as u64, + spans, + offsets, + 512, + ) + .expect("bank partition block"), + ); + start = end; + } + let sizes = transient_shaper::TRANSIENT_SHAPER_DESCRIPTOR.qualities[1].maximum_state; + let states = (0..lanes) + .map(|track| bank_snapshot(bank.as_ref(), track as u32, sizes)) + .collect(); + (left, right, states) + }; + + let reference = render(frames); + let changed = (0..frames * lanes) + .filter(|index| { + reference.0[*index].to_bits() != planar[index % lanes].0[index / lanes].to_bits() + }) + .count(); + assert!( + changed > frames * lanes / 2, + "only {changed} of {} bank samples moved", + frames * lanes + ); + let reference_left = reference.0.iter().map(|x| x.to_bits()).collect::>(); + let reference_right = reference.1.iter().map(|x| x.to_bits()).collect::>(); + for block in PARTITIONS { + let candidate = render(block); + assert_eq!( + candidate.0.iter().map(|x| x.to_bits()).collect::>(), + reference_left, + "bank left PCM at block size {block}" + ); + assert_eq!( + candidate.1.iter().map(|x| x.to_bits()).collect::>(), + reference_right, + "bank right PCM at block size {block}" + ); + assert_eq!(candidate.2, reference.2, "bank state at block size {block}"); + } +} diff --git a/crates/true-peak-limiter/tests/MUTATIONS.md b/crates/true-peak-limiter/tests/MUTATIONS.md index 49451d9d1..29e96db46 100644 --- a/crates/true-peak-limiter/tests/MUTATIONS.md +++ b/crates/true-peak-limiter/tests/MUTATIONS.md @@ -146,7 +146,7 @@ Every row below is a **bit** test. The claims here are about *when* a state word | # | mutation | file | test that turned red | |---|---|---|---| | round2-1 | the block-end write-back of `prefix` dropped | `src/lib.rs` `limiter_block_uniform` | `a_mixed_lookahead_cohort_falls_back_bit_identically`, `a_restore_that_desyncs_the_phase_falls_back`, `fixed_latency_guarded_ceiling_and_bypass_bits_hold`, `lane_identity_holds_across_widths`, `partition_invariance_holds_over_block_sizes` (deleted by TA4) | -| round2-2 | the block-end write-back of `phase` dropped, so the cohort restarts each block at the phase it entered the previous one with | `src/lib.rs` `limiter_block_uniform` | `a_de_zipper_window_open_across_a_block_boundary_refuses_the_claim`, `a_mixed_lookahead_cohort_falls_back_bit_identically`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5; catcher `the_bank_renders_its_scalar_instances_under_random_state`), `a_restore_that_desyncs_the_phase_falls_back`, `a_restore_withdraws_the_silence_claim`, `a_settled_silent_limiter_renders_exactly_the_never_fast_path` | +| round2-2 | the block-end write-back of `phase` dropped, so the cohort restarts each block at the phase it entered the previous one with | `src/lib.rs` `limiter_block_uniform` | `a_de_zipper_window_open_across_a_block_boundary_refuses_the_claim`, `a_mixed_lookahead_cohort_falls_back_bit_identically`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5; no catcher recorded), `a_restore_that_desyncs_the_phase_falls_back`, `a_restore_withdraws_the_silence_claim`, `a_settled_silent_limiter_renders_exactly_the_never_fast_path` | | round2-3 | `.min(ring - left_end)` dropped from the segment run | `src/lib.rs` `segment` | `the_segment_walk_visits_the_slots_a_frame_at_a_time_walk_visits` — **and nothing else** | | round2-4 | `.min(ring - left_expiring)` dropped from the segment run | `src/lib.rs` `segment` | `the_segment_walk_visits_the_slots_a_frame_at_a_time_walk_visits` — **and nothing else** | | round2-5 | `.min(ring - start)` dropped from the segment run | `src/lib.rs` `segment` | six render tests, including `a_settled_silent_limiter_renders_exactly_the_never_fast_path` and `a_restore_that_desyncs_the_phase_falls_back` | @@ -240,9 +240,9 @@ the recursive word, the cursors, the phases and all four ramps — are compared, | # | mutation | file | test that turned red | |---|---|---|---| -| 182-6 | input `block_is_positive_zero` legs dropped from the admission test | `src/lib.rs` `LimiterCore::process_block` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5; catcher `the_bank_renders_its_scalar_instances_under_random_state`), `a_stale_detector_history_refuses_the_claim` | +| 182-6 | input `block_is_positive_zero` legs dropped from the admission test | `src/lib.rs` `LimiterCore::process_block` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5; no catcher recorded), `a_stale_detector_history_refuses_the_claim` | | 182-7 | `block_is_positive_zero(&self.reduction)` dropped from `is_at_silent_rest` | `src/lib.rs` `ChannelState::is_at_silent_rest` | `a_limiter_still_releasing_through_the_silence_is_never_frozen` | -| 182-8 | `block_is_positive_zero(&self.main_ring)` dropped | `src/lib.rs` `ChannelState::is_at_silent_rest` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5; catcher `the_bank_renders_its_scalar_instances_under_random_state`) | +| 182-8 | `block_is_positive_zero(&self.main_ring)` dropped | `src/lib.rs` `ChannelState::is_at_silent_rest` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path`, `a_negative_zero_input_block_is_not_treated_as_silence` (deleted by TA5; no catcher recorded) | | 182-9 | `block_is_positive_zero(&self.history)` dropped | `src/lib.rs` `ChannelState::is_at_silent_rest` | `a_stale_detector_history_refuses_the_claim` — **and nothing else** | | 182-10 | output `block_is_positive_zero` legs dropped from the claim | `src/lib.rs` `LimiterCore::process_block` | `a_settled_silent_limiter_renders_exactly_the_never_fast_path` | | 182-11 | `self.cursors.advance(frames, &self.shape)` deleted from the fast path | `src/lib.rs` `LimiterCore::process_block` | `a_settled_silent_limiter_...`, `a_negative_zero_input_...`, `a_stale_detector_history_...` (via the state comparison, not the sample comparison) | diff --git a/hosts/host-web/src/ffi.rs b/hosts/host-web/src/ffi.rs index c87571d0b..5df036851 100644 --- a/hosts/host-web/src/ffi.rs +++ b/hosts/host-web/src/ffi.rs @@ -4421,6 +4421,18 @@ pub(crate) fn test_buffer_address(handle: u32, kind: u32) -> usize { with_host_mut(handle, 0, |host| buffer_pointer(host, kind).addr()) } +/// The address `miso_engine_web_v1_source_seek_report_ptr` returns before its `u32` cast (a +/// native address does not fit it), and the record's own address. +#[cfg(test)] +pub(crate) fn test_seek_report_addresses(handle: u32) -> (usize, usize) { + with_host(handle, (0, 0), |host| { + ( + seek_report_pointer(host).addr(), + ptr::from_ref(host.seek_report()).addr(), + ) + }) +} + #[cfg(test)] pub(crate) fn test_status(handle: u32) -> Option { with_host(handle, None, |host| Some(*host.status())) diff --git a/hosts/host-web/src/tests.rs b/hosts/host-web/src/tests.rs index 85f66811b..68561b56a 100644 --- a/hosts/host-web/src/tests.rs +++ b/hosts/host-web/src/tests.rs @@ -1423,6 +1423,84 @@ fn the_anchored_seek_exports_play_at_the_anchor_and_report_where_it_landed() { ); } +/// Issue #1293 gate 5: on a host that is not ready (no control half), `source_seek_at` and +/// `source_seek_report` refuse with `RESULT_REFUSED_LIFECYCLE`, the report record keeps its last +/// words, and a wrong handle or an ID count past the staging is `RESULT_INVALID_ARGUMENT`. +/// +/// Test value: red if either export runs on a host with no control half (it would reach for +/// control state the instance does not hold) or overwrites the record on a refusal, if the +/// pointer export names anything but the record while ready or anything at all once not ready, +/// or if the report export records a staged-ID refusal as the last result (it records none). +#[test] +fn the_seek_exports_refuse_a_host_with_no_control_half_and_change_nothing() { + let rig = SeekExports::boot(); + assert_eq!(rig.seek_at(2, 10_000, 256), RESULT_OK); + rig.submit(2, 10_000); + let _ = rig.render(); + let (code, before) = rig.report(); + assert_eq!(code, RESULT_OK); + // A ready host's pointer export names the record. + let (pointer, record) = crate::ffi::test_seek_report_addresses(rig.handle); + assert!(record != 0 && pointer == record); + + // Argument checks on a live host. + assert_eq!( + miso_engine_web_v1_source_seek_at(0, 14, 3, 0, 512), + RESULT_INVALID_ARGUMENT + ); + assert_eq!( + miso_engine_web_v1_source_seek_report(0, 14), + RESULT_INVALID_ARGUMENT + ); + assert_eq!( + miso_engine_web_v1_source_seek_at(rig.handle, u32::MAX, 3, 0, 512), + RESULT_INVALID_ARGUMENT + ); + // The anchored seek records its refusal; the report leaves the last result alone. + assert_eq!( + crate::ffi::test_status(rig.handle) + .expect("status") + .last_result, + RESULT_INVALID_ARGUMENT + ); + assert_eq!(rig.seek_at(3, 0, 1024), RESULT_OK); + assert_eq!( + miso_engine_web_v1_source_seek_report(rig.handle, u32::MAX), + RESULT_INVALID_ARGUMENT + ); + assert_eq!( + crate::ffi::test_status(rig.handle) + .expect("status") + .last_result, + RESULT_OK + ); + assert_eq!( + crate::ffi::native_staging::seek_report(rig.handle), + Some(before) + ); + + // An output-shape mismatch makes the host sticky-failed: no control half to run on. + assert_eq!( + miso_engine_web_v1_render(rig.handle, 0), + RESULT_REPREPARE_REQUIRED + ); + assert_eq!( + crate::ffi::test_status(rig.handle).expect("status").state, + STATE_FAILED + ); + assert_eq!(rig.seek_at(3, 0, 1024), RESULT_REFUSED_LIFECYCLE); + assert_eq!(rig.report().0, RESULT_REFUSED_LIFECYCLE); + assert_eq!( + crate::ffi::native_staging::seek_report(rig.handle), + Some(before), + "a refusal leaves the record alone" + ); + // A host that is not ready has no record address to give. + let (pointer, record) = crate::ffi::test_seek_report_addresses(rig.handle); + assert!(record != 0 && pointer == 0); + assert_eq!(miso_engine_web_v1_source_seek_report_ptr(0), 0); +} + #[test] fn output_mismatch_and_disposal_are_sticky_and_idempotent() { let mut host = prepared_host(64); @@ -3269,6 +3347,29 @@ fn meter_spans_cover_all_launch_rates_and_a_nine_track_tail() { } } +#[test] +fn source_seek_keeps_the_meter_epoch_and_absolute_peak_clock() { + let mut host = live_control_host(128, 1); + assert_eq!(host.set_meter_lease(true), RESULT_OK); + feed_and_render(&mut host, 1, 0, 0.4); + assert_eq!(host.poll_meters(), 1); + let meter_generation = host.meter_header().reserved[0]; + + assert_eq!(host.seek_source(b"fixture-source", 2, 0), RESULT_OK); + let plane = vec![0.7_f32; 128]; + let planes: [&[f32]; 2] = [&plane, &plane]; + assert_eq!( + host.submit_source(b"fixture-source", 2, 0, 48_000, &planes, 128, false), + RESULT_OK + ); + assert_eq!(host.render_next(), RESULT_OK); + assert_eq!(host.poll_meters(), 1); + assert_eq!(host.meter_header().reserved[0], meter_generation); + assert_eq!(host.meter_header().first_sample, 128); + assert_eq!(host.meter_header().end_sample, 256); + assert_eq!(&host.meter_frame()[..4], &[0.7, 0.7, 0.7, 0.7]); +} + #[test] fn meter_lease_reacquisition_waits_for_a_clean_boundary() { let mut host = live_control_host(128, 2); @@ -3966,6 +4067,94 @@ fn selected_observation_reads_are_bounded_stable_and_non_consuming() { ); } +#[test] +fn selected_observation_reads_keep_same_track_owner_windows_and_sequences() { + const QUANTUM: u32 = 128; + let mut host = same_track_observation_host(QUANTUM); + let compressor = ObservationSelection { + track_id: "t0", + rack: LiveEffectRack::Inserts, + effect_slot_id: "comp", + tap_id: 1, + channels: ObservationReadChannels::Both, + }; + let gate = ObservationSelection { + track_id: "t0", + rack: LiveEffectRack::Inserts, + effect_slot_id: "gate", + tap_id: 1, + channels: ObservationReadChannels::Both, + }; + assert_eq!(observe(&mut host, 0, 1, 0, 1, 2, true), RESULT_OK); + assert_eq!(observe(&mut host, 0, 1, 1, 1, 3, true), RESULT_OK); + for block in 0..3 { + feed_and_render_channels(&mut host, block, 0.75, 0.125); + } + + let (compressor_window, compressor_consumed) = { + let ready = host.ready.as_ref().expect("ready ownership"); + let (effect, tap) = resolve_observation(ready, &compressor).expect("compressor address"); + let reader = &host + .test_observation_handle(effect) + .expect("compressor observation handle") + .readers[tap]; + ( + reader.read().expect("compressor published window"), + reader.consumed_sequence(), + ) + }; + let (gate_window, gate_consumed) = { + let ready = host.ready.as_ref().expect("ready ownership"); + let (effect, tap) = resolve_observation(ready, &gate).expect("gate address"); + let reader = &host + .test_observation_handle(effect) + .expect("gate observation handle") + .readers[tap]; + ( + reader.read().expect("gate published window"), + reader.consumed_sequence(), + ) + }; + assert_eq!(compressor_window.blocks, 2); + assert_eq!(gate_window.blocks, 3); + assert_ne!(compressor_window.left, compressor_window.right); + assert_ne!(gate_window.left, gate_window.right); + assert_eq!(compressor_consumed, 0); + assert_eq!(gate_consumed, 0); + + let rows = host + .read_observations(&[compressor, gate]) + .expect("same-track two-effect selected read"); + assert_eq!(rows.len(), 2); + assert_eq!(rows[0].effect_slot_id.as_ref(), "comp"); + assert_eq!(rows[1].effect_slot_id.as_ref(), "gate"); + assert_eq!(rows[0].window, Some(compressor_window)); + assert_eq!(rows[1].window, Some(gate_window)); + assert_eq!(rows[0].left, Some(compressor_window.left)); + assert_eq!(rows[0].right, Some(compressor_window.right)); + assert_eq!(rows[1].left, Some(gate_window.left)); + assert_eq!(rows[1].right, Some(gate_window.right)); + + let (compressor_after, gate_after) = { + let ready = host.ready.as_ref().expect("ready ownership"); + let (compressor_effect, compressor_tap) = + resolve_observation(ready, &compressor).expect("compressor address"); + let (gate_effect, gate_tap) = resolve_observation(ready, &gate).expect("gate address"); + ( + host.test_observation_handle(compressor_effect) + .expect("compressor observation handle") + .readers[compressor_tap] + .consumed_sequence(), + host.test_observation_handle(gate_effect) + .expect("gate observation handle") + .readers[gate_tap] + .consumed_sequence(), + ) + }; + assert_eq!(compressor_after, compressor_consumed); + assert_eq!(gate_after, gate_consumed); +} + #[test] fn selected_observation_numeric_read_refuses_failed_host_without_touching_output() { const QUANTUM: u32 = 128; From 2ab077720277f0c8841c61fac118ed69057d7628 Mon Sep 17 00:00:00 2001 From: BL Date: Tue, 20 Oct 2026 11:25:32 +0000 Subject: [PATCH 6/8] TA5 re-check: restore the #1328 gate 2 low-shelf reproducer the_low_shelf_fixed_point_reaches_exact_rest reproduces the 10 Hz slope-0.1 shelf that stuck at 6.01e-20 and holds a ceiling (exact rest within REST_BOUND_SAMPLES); the named defender does not reproduce the shelf. Restored with its helpers; MUTATIONS 1328-M1 is as at ad5c7ae86. Counts: 97 deleted, 65 kept. Co-Authored-By: Claude Opus 5.5 --- ...-TA5-delete-the-batch-2-redundant-tests.md | 20 ++--- .../TBD-TA5-successor-audit-leads.md | 4 +- crates/parametric-eq/tests/MUTATIONS.md | 2 +- crates/parametric-eq/tests/exact_rest.rs | 77 +++++++++++++++++++ 4 files changed, 91 insertions(+), 12 deletions(-) diff --git a/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md b/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md index 165d9c71f..4147a3b6c 100644 --- a/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md +++ b/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md @@ -36,16 +36,16 @@ test is kept when the answer names a real edge case that the measured mutants co no remaining test asserts. Every DELETE answer names the claim and the remaining test, by its full name, that asserts it; every KEEP answer names the remaining tests checked. The first pass named siblings only by file for 73 DELETE answers; the TA5 verifier found that 5 of them were the only -defenders of their claims, so all 73 were read again and kept wherever no remaining test could be +defenders of their claims, so all 73 were read again (and one more was restored on re-check) and kept wherever no remaining test could be named. Reading also found what the triage missed: a randomized differential named `run_seeds`, an allocation audit inside a macro, tests that a `MUTATIONS.md` records as the only red test of a row, and one test that `scripts/lib/aarch64-known-defects.py` lists as a known AArch64 failure. ## What this change does -1. Deletes 98 of the 162 tests and keeps 64 by reading (list below). None of the 162 had moved +1. Deletes 97 of the 162 tests and keeps 65 by reading (list below). None of the 162 had moved or gone since `81a3074fc`; nothing was skipped. -2. Deletes 34 helper items (476 lines) only after `cargo clippy --all-targets --all-features` +2. Deletes 31 helper items (465 lines) only after `cargo clippy --all-targets --all-features` reported them unused, looped until it reported nothing; no `#[allow(dead_code)]` was added. No test file is left empty. 3. Updates the references: each `MUTATIONS.md` row that names a deleted test names a remaining @@ -86,7 +86,7 @@ row, and one test that `scripts/lib/aarch64-known-defects.py` lists as a known A | lane | 3 | 2 | 72 | | math | 1 | 2 | 6 | | multiband-compressor | 1 | 2 | 83 | -| parametric-eq | 4 | 3 | 307 | +| parametric-eq | 3 | 4 | 244 | | protocol | 3 | 4 | 152 | | rack | 4 | 2 | 248 | | realtime-policy | 11 | 2 | 177 | @@ -95,11 +95,11 @@ row, and one test that `scripts/lib/aarch64-known-defects.py` lists as a known A | source | 3 | 0 | 115 | | transient-shaper | 1 | 3 | 44 | | true-peak-limiter | 2 | 2 | 63 | -| **total** | **98** | **64** | **4,564** | +| **total** | **97** | **65** | **4,501** | -Lines are the deleted test functions with their doc comments, at `ad5c7ae86`. Shortstat against `ad5c7ae86`: 83 files changed, 349 insertions(+), 5,221 deletions(-). +Lines are the deleted test functions with their doc comments, at `ad5c7ae86`. Shortstat against `ad5c7ae86`: 82 files changed, 350 insertions(+), 5,143 deletions(-). -## Kept by reading (64) +## Kept by reading (65) - audit `fixture_builtins::tests::check_rejects_owned_jsonl_tuple_mutations`: a field or record change in an owned JSONL after the manifest is regenerated; the format test only deletes, @@ -143,6 +143,9 @@ Lines are the deleted test functions with their doc comments, at `ad5c7ae86`. Sh with a 2^24 + 3 frame block. - math `m2_exp2_lane_identity`: a known AArch64 release failure (LANE-3, #1019) named in `scripts/lib/aarch64-known-defects.py`; x86 mutants cannot reach it. +- parametric-eq `the_low_shelf_fixed_point_reaches_exact_rest`: #1328 gate 2, the reproducer of the 10 Hz + slope-0.1 shelf stuck at 6.01e-20, and a ceiling claim (rest within `REST_BOUND_SAMPLES`); the + named defender plants +-1e-15 with every band disabled (restored on re-check). - parametric-eq `a_non_finite_input_block_is_zeroed_counted_once_and_leaves_the_next_block_clean`: the count claim (one block on one channel, not one per sample). - parametric-eq `svf_words_match_the_independent_oracle_on_the_complete_grid`: the only gate on the @@ -218,8 +221,7 @@ Added by the re-reading and the review fold: `FUSED_GAINS`, `fused_family_source` - `crates/math/tests/m2_lane_identity.rs`: `exp2_caller_edges`, `FastGain` and its `LaneFn` - `crates/multiband-compressor/tests/crossover.rs`: `rms`, `complex_gain`, `CROSSOVERS` -- `crates/parametric-eq/tests/carry.rs`: `same_bits`; `tests/exact_rest.rs`: `at_rest`, - `HELD_BLOCKS`, `REST_BOUND_SAMPLES` +- `crates/parametric-eq/tests/carry.rs`: `same_bits` - `crates/rack/src/lib.rs`: `WrongWidthPlanes` and its `BankMembers` Leads the compiler does not report are in the successor spec diff --git a/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md b/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md index f688a6491..276edea6f 100644 --- a/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md +++ b/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md @@ -1,12 +1,12 @@ # Record the lead the last test-audit PR found and did not fix (TA5 successor) -TA5 (`TBD-TA5-delete-the-batch-2-redundant-tests.md`) deleted 98 tests of the batch 2 sample and +TA5 (`TBD-TA5-delete-the-batch-2-redundant-tests.md`) deleted 97 tests of the batch 2 sample and did not add scope. It found one lead. It does not block TA5. ## Lead **The redundancy measurement over-reports on its own.** Of batch 2's 162 MEASURED-REDUNDANT -tests, reading kept 64. Some carry a claim that another record states and the name triage did not +tests, reading kept 65. Some carry a claim that another record states and the name triage did not read: the only red test of a `MUTATIONS.md` row (gate-expander row 3, effect-contract 140-1, parametric-eq recorded failures 1 and 1b, graph 916-1 to 916-15), a known AArch64 release failure in `scripts/lib/aarch64-known-defects.py` (`m2_exp2_lane_identity`, #1019), and a randomized diff --git a/crates/parametric-eq/tests/MUTATIONS.md b/crates/parametric-eq/tests/MUTATIONS.md index cacb559ee..7ffe1a189 100644 --- a/crates/parametric-eq/tests/MUTATIONS.md +++ b/crates/parametric-eq/tests/MUTATIONS.md @@ -446,7 +446,7 @@ run, and the edit reverted (#1328 attempt 4; the attempt records are in the issu | # | mutation | gate that goes red | result | |---|---|---|---| -| 1328-M1 | `lane::flush_pair` without its input term (attempt 3's law: `rest = abs(n1) < REST_EPS && abs(n2) < REST_EPS`) | `exact_rest::a_tiny_input_through_four_boosting_shelves_gets_every_boost` (`10 Hz, input 2.7939677e-11 … misses the ordinary run by 1.5091782e3`), dev and release; `the_low_shelf_fixed_point_reaches_exact_rest` (deleted by TA5; no catcher recorded) stays green | RED | +| 1328-M1 | `lane::flush_pair` without its input term (attempt 3's law: `rest = abs(n1) < REST_EPS && abs(n2) < REST_EPS`) | `exact_rest::a_tiny_input_through_four_boosting_shelves_gets_every_boost` (`10 Hz, input 2.7939677e-11 … misses the ordinary run by 1.5091782e3`), dev and release; `the_low_shelf_fixed_point_reaches_exact_rest` stays green | RED | | 1328-M2 | `self.right.identity = self.left.identity;` dropped from `desymmetrize` | `mono_collapse::a_desymmetrized_bank_carries_the_collapsed_channels_identity_flags_and_dry_masks`: dev through `identity_flags_agree` (`lib.rs`), release on the right plane at block 27 after the left-only retarget | RED | | 1328-M3 | `self.right.dry = self.left.dry;` dropped from `desymmetrize` | the same test, dev only (`identity_flags_agree`); release renders the same bits (a stale dry lane runs the identity section wet, which moves at most `-0.0` to `+0.0`, and this input moves none) | RED (dev) | diff --git a/crates/parametric-eq/tests/exact_rest.rs b/crates/parametric-eq/tests/exact_rest.rs index 63d623a93..60f0f224f 100644 --- a/crates/parametric-eq/tests/exact_rest.rs +++ b/crates/parametric-eq/tests/exact_rest.rs @@ -19,12 +19,89 @@ use std::f32::consts::FRAC_1_SQRT_2; use effect_contract::{ EffectProcessBlock, InitialParameterValue, NativeEffectFactory, ParameterChannel, + PreparedEffect, }; use parametric_eq::{EqBandKind, ParametricEqFactory}; use support::{band_word, request_at_rate, set_initial, single_section_values, snapshot, values}; const RATE: u32 = 96_000; const FRAMES: usize = 128; +/// The bound the issue states: rest within 9,600,000 samples (100 s) of the impulse. +const REST_BOUND_SAMPLES: usize = 9_600_000; +/// Blocks rendered after rest that must stay at rest. +const HELD_BLOCKS: usize = 4; + +fn at_rest(effect: &PreparedEffect, left: &[f32], right: &[f32]) -> bool { + let (_, left_state, right_state) = snapshot(effect); + [&left_state[..], &right_state[..]] + .iter() + .all(|payload| band_word(payload, 0, 0) == 0 && band_word(payload, 0, 1) == 0) + && left.iter().chain(right).all(|sample| sample.to_bits() == 0) +} + +#[test] +fn the_low_shelf_fixed_point_reaches_exact_rest() { + let configured = single_section_values(EqBandKind::LowShelf, 10.0, 24.0, FRAC_1_SQRT_2, 0.1); + let mut effect = ParametricEqFactory + .prepare(request_at_rate(&configured, false, RATE)) + .expect("the shelf prepares"); + + let mut left = [0.0_f32; FRAMES]; + let mut right = [0.0_f32; FRAMES]; + left[0] = 1.0; + right[0] = 1.0; + let mut rest_at = None; + let mut block = 0_usize; + while block * FRAMES < REST_BOUND_SAMPLES { + let first_sample = (block * FRAMES) as u64; + effect.processor.process( + EffectProcessBlock::new( + &mut left, + &mut right, + None, + first_sample, + &[], + FRAMES as u32, + ) + .expect("block"), + ); + if block > 0 && at_rest(&effect, &left, &right) { + rest_at = Some(block); + break; + } + left.fill(0.0); + right.fill(0.0); + block += 1; + } + let rest_at = rest_at.unwrap_or_else(|| { + let (_, left_state, _) = snapshot(&effect); + panic!( + "no exact rest within {REST_BOUND_SAMPLES} samples: band 0 left ic1 {:e}, ic2 {:e}", + f32::from_bits(band_word(&left_state, 0, 0)), + f32::from_bits(band_word(&left_state, 0, 1)), + ) + }); + for held in 1..=HELD_BLOCKS { + left.fill(0.0); + right.fill(0.0); + let first_sample = ((rest_at + held) * FRAMES) as u64; + effect.processor.process( + EffectProcessBlock::new( + &mut left, + &mut right, + None, + first_sample, + &[], + FRAMES as u32, + ) + .expect("block"), + ); + assert!( + at_rest(&effect, &left, &right), + "rest reached at block {rest_at} must hold, broke {held} blocks later" + ); + } +} /// Parameters per EQ band in the descriptor: enabled, kind, frequency, gain, Q, slope. const BAND_PARAMETERS: usize = 6; From 0053c057085aa08e1837115b3e609595446e3231 Mon Sep 17 00:00:00 2001 From: BL Date: Tue, 20 Oct 2026 11:30:42 +0000 Subject: [PATCH 7/8] Drop the TA4 deletion ledgers from the MUTATIONS.md files Root ruling: a list of deleted tests in a tracked file is a ledger row. Git history and the comments on #1731 keep the list. Co-Authored-By: Claude Opus 5.5 --- crates/builtins/tests/MUTATIONS.md | 7 ------- crates/compressor/tests/MUTATIONS.md | 9 --------- crates/delay/tests/MUTATIONS.md | 6 ------ crates/effect-runtime/tests/MUTATIONS.md | 10 ---------- crates/graph-compiler/tests/MUTATIONS.md | 9 --------- crates/graph/tests/MUTATIONS.md | 7 ------- crates/lane/tests/MUTATIONS.md | 6 ------ crates/parametric-eq/tests/MUTATIONS.md | 11 ----------- crates/protocol/tests/MUTATIONS.md | 6 ------ crates/soft-clip/tests/MUTATIONS.md | 6 ------ crates/transient-shaper/tests/MUTATIONS.md | 6 ------ crates/true-peak-limiter/tests/MUTATIONS.md | 6 ------ hosts/host-web/MUTATIONS.md | 6 ------ 13 files changed, 95 deletions(-) diff --git a/crates/builtins/tests/MUTATIONS.md b/crates/builtins/tests/MUTATIONS.md index c641f1754..b3e079c5d 100644 --- a/crates/builtins/tests/MUTATIONS.md +++ b/crates/builtins/tests/MUTATIONS.md @@ -367,10 +367,3 @@ restored. Before attempt 2 both S-1 rows were green, because the safety net moved both seeds at once and either half refused the commit. - -## TA4 deletions - -The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - -- `settled_matrix_takes_the_select_free_arm_only_without_an_identity_lane`: no plausible same-crate KEEP catcher recorded -- `fused_fader_matrix_takes_the_select_free_arm_only_without_an_identity_lane`: no plausible same-crate KEEP catcher recorded diff --git a/crates/compressor/tests/MUTATIONS.md b/crates/compressor/tests/MUTATIONS.md index c6a8288a0..27966e7de 100644 --- a/crates/compressor/tests/MUTATIONS.md +++ b/crates/compressor/tests/MUTATIONS.md @@ -373,12 +373,3 @@ file was restored byte for byte from a saved copy. `the_narrowest_soft_knees_do_not_duck_a_sample_at_the_threshold` stays green under both: it is a preservation gate for widths the rule must not move, not a red gate. - -## TA4 deletions - -The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - -- `the_collapsed_body_renders_the_dual_bodys_left_plane`: compressor `mono_collapse::mono_reopen_drives_the_next_render_from_the_copied_state` -- `bank_matches_scalar_per_lane_bits`: no plausible same-crate KEEP catcher recorded -- `block_partitions_are_invariant`: no plausible same-crate KEEP catcher recorded -- `bypass_preserves_exact_dry_bits_at_sample_zero`: no plausible same-crate KEEP catcher recorded diff --git a/crates/delay/tests/MUTATIONS.md b/crates/delay/tests/MUTATIONS.md index 2d3dad0f1..61e62c4e8 100644 --- a/crates/delay/tests/MUTATIONS.md +++ b/crates/delay/tests/MUTATIONS.md @@ -56,9 +56,3 @@ left `M7c`, `M9` and `M10` alive, and each survivor was a real gap rather than a **Two mutations fail by panicking rather than by asserting.** `M7b` underflows `transition_remaining` and `M15` reaches the test's own `panic!`. Both are unambiguous reds, and both are recorded as what they are rather than dressed up as assertion failures. - -## TA4 deletions - -The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - -- `a_mid_ramp_restore_continues_bit_identically`: no plausible same-crate KEEP catcher recorded diff --git a/crates/effect-runtime/tests/MUTATIONS.md b/crates/effect-runtime/tests/MUTATIONS.md index 320faf369..6d7e234ea 100644 --- a/crates/effect-runtime/tests/MUTATIONS.md +++ b/crates/effect-runtime/tests/MUTATIONS.md @@ -247,13 +247,3 @@ The smallest width, `0x0000_0001`, is not by itself a witness: `0.5 * 2^-149` ro its half-width is zero and the knee interval `(-W/2, W/2]` is empty even without the rule. Every list of overflowing widths in these tests therefore also carries `0x0000_0002` (the verification's 2.8e-45), `0x000F_FFFF` and `0x0010_0000`, where the half-width is positive. - -## TA4 deletions - -The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - -- `the_step_is_computed_once`: effect-runtime `contract_ramp_identity::linear_smoother_is_bit_identical_to_the_linear_ramp` -- `the_curve_matches_the_paper`: effect-runtime `dynamics::knee_words_follow_the_finite_reciprocal_rule` -- `the_corpus_is_width_independent`: no plausible same-crate KEEP catcher recorded -- `a_knee_whose_reciprocal_overflows_is_a_hard_knee`: effect-runtime `dynamics::knee_words_follow_the_finite_reciprocal_rule` -- `the_narrowest_soft_knees_are_finite_at_the_threshold`: effect-runtime `dynamics::knee_words_follow_the_finite_reciprocal_rule` diff --git a/crates/graph-compiler/tests/MUTATIONS.md b/crates/graph-compiler/tests/MUTATIONS.md index e73e3a687..a25420c2b 100644 --- a/crates/graph-compiler/tests/MUTATIONS.md +++ b/crates/graph-compiler/tests/MUTATIONS.md @@ -366,12 +366,3 @@ deleted or trimmed some tests that rows above name. The rows stay as history; th the owner bytes from `size_of`. It asserts that the three deltas agree, are zero exactly when no instance is controlled, and that the exact caps accept and one byte below refuses. A partial under-charge such as this row's is left to the allocator ceiling in `tests/bypass_resources.rs`. - -## TA4 deletions - -The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - -- `direct_graph_report_exposes_zero_output_latency_and_tail_without_identity_change`: no plausible same-crate KEEP catcher recorded -- `dynamic_rack_compressors_bank_and_render_bit_identically_to_the_per_node_path`: no plausible same-crate KEEP catcher recorded -- `console_sixty_four_track_fixture_banks_its_dynamic_compressor_bit_identically`: graph-compiler `tests::add_a_track_keeps_existing_track_bits_and_one_transpose_per_chain` -- `the_estimate_charges_each_lane_its_own_ring`: graph-compiler `track_delay::a_ring_is_a_named_allocation` diff --git a/crates/graph/tests/MUTATIONS.md b/crates/graph/tests/MUTATIONS.md index cbf2f8b4d..1d35fc554 100644 --- a/crates/graph/tests/MUTATIONS.md +++ b/crates/graph/tests/MUTATIONS.md @@ -759,10 +759,3 @@ deleted the last three builtins-compiler scalar-oracle pairing tests. The rows s - 916-16 and 916-17: `actual_scalar_nonadjacent_failed_render_materializes_post_fader_before_error` and `actual_scalar_nonadjacent_ramp_retarget_and_failed_retry_stay_at_original_boundaries` are deleted. The allocation_tracker tests these rows name remain. - -## TA4 deletions - -The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - -- `the_driver_fed_gain_pan_row_dispatches_every_unit_but_its_inputs`: no plausible same-crate KEEP catcher recorded -- `a_muted_route_seals_one_route_mute_row_after_its_transform`: no plausible same-crate KEEP catcher recorded diff --git a/crates/lane/tests/MUTATIONS.md b/crates/lane/tests/MUTATIONS.md index d22967bde..3ec59d814 100644 --- a/crates/lane/tests/MUTATIONS.md +++ b/crates/lane/tests/MUTATIONS.md @@ -502,9 +502,3 @@ browser's render-closure trap gate. Host AMD EPYC 7313P, rustc 1.97.1, `x86-64-v | 1219-M11 | the settled frames re-implemented as a private loop, and `mix2x2_block` then flushing its outputs (two edits: a static mix that drifts from the ramp's settled path) | gate 3 alone (`case 2, length 3830 ... left frame 3`, `-0.0` against `mix2x2_block`'s `+0.0`) | RED | | 1219-M12 | attempt 2: `route_mix_ramp_block` back to `let count = left.len(); let right = &mut right[..count];` | `--callgraph probe_ramp` on the probe: `FAIL probe_ramp: unexpected trap owner in the render closure: ...route_mix_ramp_block...`, `closure=5 traps=3`, entry `slice_index_fail` (unmutated: `closure=4 traps=0 trap_owners=[]`). Native gates cannot see it: bits do not move. | RED (probe), GREEN (native) | | 1219-M13 | attempt 2: `route_mix_settled_tail` passes its planes uncut (`mix2x2_block::(left, right, c)`) | `--callgraph probe_ramp`: `FAIL ... unexpected trap owner ...: route_mix_settled_tail`, `closure=5 traps=3` | RED (probe), GREEN (native) | - -## TA4 deletions - -The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - -- `g1_select_max_is_order_free_only_on_the_sanitized_domain`: no plausible same-crate KEEP catcher recorded diff --git a/crates/parametric-eq/tests/MUTATIONS.md b/crates/parametric-eq/tests/MUTATIONS.md index 7ffe1a189..2d1eb63a1 100644 --- a/crates/parametric-eq/tests/MUTATIONS.md +++ b/crates/parametric-eq/tests/MUTATIONS.md @@ -465,14 +465,3 @@ it names, the named tests run, and the source restored. | 1328-M9 | the silent fast path skips without `silence_advance` | the same test (`the silence counter after 68 silent blocks`) | RED | | 1328-M10 | `rest_plane`'s unarmed branch returns the last written plane instead of the unarmed one | the sparse gate (its train starts after armed silence) | RED | | 1328-M11 | `lane_is_inert` without its pair term | `elision::a_non_inert_state_in_a_dead_section_refuses_elision`, `exact_rest::a_fixed_point_inside_the_joint_band_is_cleared_once_the_flush_arms` | RED | - -## TA4 deletions - -The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - -- `automation_is_partition_invariant`: no plausible same-crate KEEP catcher recorded -- `every_width_matches_the_scalar_instantiation`: no plausible same-crate KEEP catcher recorded -- `the_collapsed_body_renders_the_dual_bodys_left_plane`: no plausible same-crate KEEP catcher recorded -- `disabled_cuts_preserve_all_high_pass_signed_zero_against_four_section_oracle`: parametric-eq `elision::a_discontinuity_reset_mid_ramp_refreshes_the_identity_flag` -- `a_tiny_restored_disabled_cut_state_refuses_elision_but_preserves_old_bands`: parametric-eq `elision::a_discontinuity_reset_mid_ramp_refreshes_the_identity_flag` -- `a_mid_ramp_restore_continues_bit_for_bit_at_every_sample`: no plausible same-crate KEEP catcher recorded diff --git a/crates/protocol/tests/MUTATIONS.md b/crates/protocol/tests/MUTATIONS.md index df37d9bb0..30353e2ea 100644 --- a/crates/protocol/tests/MUTATIONS.md +++ b/crates/protocol/tests/MUTATIONS.md @@ -46,9 +46,3 @@ Driver: a scratch script (PR evidence, not committed), one mutation at a time on | M7 | the console decoder reads `pre_insert` from the `post_insert` field | `session_wire.rs` | `console_declaration_and_track_entry_opcodes_round_trip_canonically` and the three above | | M8 | `ConsoleSlotFixed` reported as `session.edit.not_found` | `controller.rs` | `retired_and_console_refused_codes_meet_their_conformance_rows` | | M9 | `SetTrackConsole` writes the first track's entries | `model.rs` | `every_console_edit_round_trips_...`, `a_slot_set_change_that_skips_a_track_refuses_whole` | - -## TA4 deletions - -The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - -- `a_slot_set_change_that_skips_a_track_is_never_acknowledged`: protocol `console_session_edits::a_slot_set_change_that_skips_a_track_refuses_whole` diff --git a/crates/soft-clip/tests/MUTATIONS.md b/crates/soft-clip/tests/MUTATIONS.md index c57e8f925..9bfb6dbc9 100644 --- a/crates/soft-clip/tests/MUTATIONS.md +++ b/crates/soft-clip/tests/MUTATIONS.md @@ -109,9 +109,3 @@ mutation and the defect are the same edit. Both reset kinds are now inside the m * The wasm leg of the corpus (`tools/wasm-gates`) is exercised by `scripts/run-wasm-gates.sh`; its own mutations live in that crate's `MUTATIONS.md`. - -## TA4 deletions - -The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - -- `partition_invariance` (the test binary; its one test, `every_partition_renders_the_one_shot_bits_and_state`): no plausible same-crate KEEP catcher recorded diff --git a/crates/transient-shaper/tests/MUTATIONS.md b/crates/transient-shaper/tests/MUTATIONS.md index 49ddbc030..4ad5bb788 100644 --- a/crates/transient-shaper/tests/MUTATIONS.md +++ b/crates/transient-shaper/tests/MUTATIONS.md @@ -184,9 +184,3 @@ dropped. Bit-identical for the reason given under row 8. It is a performance regression — it puts three `LinearRamp::next_value` calls per lane per channel back into the steady-state loop, which is audit finding F6 — and is rejected on that ground, not on a numeric one. - -## TA4 deletions - -The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - -- `the_scalar_product_is_partition_invariant`: no plausible same-crate KEEP catcher recorded diff --git a/crates/true-peak-limiter/tests/MUTATIONS.md b/crates/true-peak-limiter/tests/MUTATIONS.md index 29e96db46..a7813f483 100644 --- a/crates/true-peak-limiter/tests/MUTATIONS.md +++ b/crates/true-peak-limiter/tests/MUTATIONS.md @@ -641,9 +641,3 @@ NL green is the case's reference: its pin is the words of the pre-#990 behaviour here; the session test's "equal" check (`ch16`) still owns it at every width. E0 is green at `Simd8` because lanes 0 and 7 unlink in the same block, so the eight-lane bank unlinks through lane 7; the session test's `ch48` row still owns E0 and EL at `Simd8`. - -## TA4 deletions - -The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - -- `partition_invariance_holds_over_block_sizes`: no plausible same-crate KEEP catcher recorded diff --git a/hosts/host-web/MUTATIONS.md b/hosts/host-web/MUTATIONS.md index cd710ca5d..09bc84391 100644 --- a/hosts/host-web/MUTATIONS.md +++ b/hosts/host-web/MUTATIONS.md @@ -565,9 +565,3 @@ qualification of decision 15's heavy gates" (#1605) or the first CI run that app | same (S3), Chromium, Firefox, WebKit | in `#submit` (`sdk/src/core/observation-subscriptions.ts`), drop `if (!rendered)` so the typed refusal is thrown whatever `next` returns | gate 3: the render instance's close ends in `backpressure` (`resolved: false`, log `[ok, bp]`) | | same (S4) | `current.renderedQuanta >= since` in `next` | gate 1: early, and more than one close send in the log | | same (S5) | the browser engine passes no `renderWait` | gates 1 and 3 | - -## TA4 deletions - -The 2026-10 test audit (TA4, batch 1 mutation measurement at `81a3074fc`) deleted the tests named below. Where a KEEP test is named, batch 1 recorded it red on that test's sampled mutants in this crate; the mutation of each row above was not re-run. Where none is named, batch 1 recorded no same-crate catcher that could plausibly see the row's mutation. - -- `default_ring_covers_stall_tolerance`: no plausible same-crate KEEP catcher recorded From 1a4e139453f0ddc0ea967ba13e6a8c76bae7f5fd Mon Sep 17 00:00:00 2001 From: BL Date: Tue, 20 Oct 2026 11:31:33 +0000 Subject: [PATCH 8/8] Number the TA5 specs: #1733, #1734 The successor-leads draft held no work, so its lesson moves into the TA5 spec as its last section. Co-Authored-By: Claude Opus 5.5 --- ...ose-the-batch-2-mutation-survivor-gaps.md} | 2 +- ...734-delete-the-batch-2-redundant-tests.md} | 20 +++++++++++++--- .../TBD-TA5-successor-audit-leads.md | 23 ------------------- 3 files changed, 18 insertions(+), 27 deletions(-) rename .github/ISSUE_SPECS/{TBD-TA5-gap-close-the-batch-2-mutation-survivor-gaps.md => 1733-close-the-batch-2-mutation-survivor-gaps.md} (98%) rename .github/ISSUE_SPECS/{TBD-TA5-delete-the-batch-2-redundant-tests.md => 1734-delete-the-batch-2-redundant-tests.md} (93%) delete mode 100644 .github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md diff --git a/.github/ISSUE_SPECS/TBD-TA5-gap-close-the-batch-2-mutation-survivor-gaps.md b/.github/ISSUE_SPECS/1733-close-the-batch-2-mutation-survivor-gaps.md similarity index 98% rename from .github/ISSUE_SPECS/TBD-TA5-gap-close-the-batch-2-mutation-survivor-gaps.md rename to .github/ISSUE_SPECS/1733-close-the-batch-2-mutation-survivor-gaps.md index fcca24453..b5087c041 100644 --- a/.github/ISSUE_SPECS/TBD-TA5-gap-close-the-batch-2-mutation-survivor-gaps.md +++ b/.github/ISSUE_SPECS/1733-close-the-batch-2-mutation-survivor-gaps.md @@ -1,4 +1,4 @@ -# Close the batch 2 mutation-survivor gaps (TBD-TA5-gap) +# Close the batch 2 mutation-survivor gaps ## Mission diff --git a/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md b/.github/ISSUE_SPECS/1734-delete-the-batch-2-redundant-tests.md similarity index 93% rename from .github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md rename to .github/ISSUE_SPECS/1734-delete-the-batch-2-redundant-tests.md index 4147a3b6c..d71e51e63 100644 --- a/.github/ISSUE_SPECS/TBD-TA5-delete-the-batch-2-redundant-tests.md +++ b/.github/ISSUE_SPECS/1734-delete-the-batch-2-redundant-tests.md @@ -224,8 +224,7 @@ Added by the re-reading and the review fold: - `crates/parametric-eq/tests/carry.rs`: `same_bits` - `crates/rack/src/lib.rs`: `WrongWidthPlanes` and its `BankMembers` -Leads the compiler does not report are in the successor spec -`TBD-TA5-successor-audit-leads.md`. +The lesson for any later pool-wide cut is the last section of this spec. ## Objective gates @@ -244,4 +243,19 @@ per-test list, part 1/3" to "part 3/3"). The PR diff is the list of what went. Batch 2 left 12 mutants that no sampled and no covering KEEP test turned red. Agent T5S read each at `ad5c7ae86`: 3 equivalent, 1 unreachable, 2 dead code, 6 real gaps. The six real gaps (one small test each) and the decision on the dead contract are the successor spec -`TBD-TA5-gap-close-the-batch-2-mutation-survivor-gaps.md`. +`1733-close-the-batch-2-mutation-survivor-gaps.md`. + +## Lesson for any later pool-wide cut + +**The redundancy measurement over-reports on its own.** Of batch 2's 162 MEASURED-REDUNDANT +tests, reading kept 65. Some carry a claim that another record states and the name triage did not +read: the only red test of a `MUTATIONS.md` row (gate-expander row 3, effect-contract 140-1, +parametric-eq recorded failures 1 and 1b, graph 916-1 to 916-15), a known AArch64 release failure +in `scripts/lib/aarch64-known-defects.py` (`m2_exp2_lane_identity`, #1019), and a randomized +differential named `run_seeds` (host-core `a_bus_renders_the_bits_of_a_track_fed_its_sum`). +Others were the only test of a claim that only reading against named siblings shows; a first pass +that cited siblings only by file deleted five of those, and review restored them. B2's +projection of about 2,647 deletable pool tests therefore does not stand. If a pool-wide cut is +ever reopened, its brief must first match every candidate against the `MUTATIONS.md` +sole-catcher rows, the known-defect lists and the seeded-differential helpers, and every DELETE +answer must name, by its full name, the remaining test that asserts the same claim. diff --git a/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md b/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md deleted file mode 100644 index 276edea6f..000000000 --- a/.github/ISSUE_SPECS/TBD-TA5-successor-audit-leads.md +++ /dev/null @@ -1,23 +0,0 @@ -# Record the lead the last test-audit PR found and did not fix (TA5 successor) - -TA5 (`TBD-TA5-delete-the-batch-2-redundant-tests.md`) deleted 97 tests of the batch 2 sample and -did not add scope. It found one lead. It does not block TA5. - -## Lead - -**The redundancy measurement over-reports on its own.** Of batch 2's 162 MEASURED-REDUNDANT -tests, reading kept 65. Some carry a claim that another record states and the name triage did not -read: the only red test of a `MUTATIONS.md` row (gate-expander row 3, effect-contract 140-1, -parametric-eq recorded failures 1 and 1b, graph 916-1 to 916-15), a known AArch64 release failure -in `scripts/lib/aarch64-known-defects.py` (`m2_exp2_lane_identity`, #1019), and a randomized -differential named `run_seeds` (host-core `a_bus_renders_the_bits_of_a_track_fed_its_sum`). -Others were the only test of a claim that only reading against named siblings shows; a first pass -that cited siblings only by file deleted five of those, and review restored them. B2's -projection of about 2,647 deletable pool tests therefore does not stand. If a pool-wide cut is -ever reopened, its brief must first match every candidate against the `MUTATIONS.md` -sole-catcher rows, the known-defect lists and the seeded-differential helpers, and every DELETE -answer must name, by its full name, the remaining test that asserts the same claim. - -## Gate - -No work unless a pool-wide cut is reopened; then the reopening brief cites this item.