Files
amethyst/nestsClient/plans/2026-05-07-tighten-cross-stack-assertions.md
Claude f8dc9c59be test(nests-interop): revert hot-swap browser to soft-pass per plan Risk § option (a)
Follow-up to commit `029329af`: a verification sweep (5×) showed
the `pcm.size > warmupSamples` hard floor on
`chromium_listener_speaker_hot_swap_does_not_crash` flakes 3/5 —
empirical capture varies 2880–7680 samples (= 60–160 ms TOTAL),
straddling the 4800-sample warmup threshold. The browser-side
@moq/lite 0.2.x re-attach behaviour across `Active::Ended →
Active` is fundamentally unreliable; ANY hard floor that
excludes the regression mode ("swap killed the WT session
entirely") fail-flakes because the steady state itself sits
right at that threshold.

Per `2026-05-07-tighten-cross-stack-assertions.md`'s Risk § option
(a) — "If any scenario fail-flakes after tightening, [...] revert
the tightening on that scenario" — this commit reverts the
hot-swap floor to a documented soft-pass that prints to
System.err when triggered. T12 (group sequence carry across
hot-swaps) is still asserted by the hang-tier counterpart
(`speaker_hot_swap_does_not_crash` in `HangInteropTest`), which
hard-asserts the full post-swap window decodes the 440 Hz peak.

The deferred "browser hot-swap re-attach" follow-up in
`2026-05-06-cross-stack-interop-test-results.md` captures the
underlying @moq/lite client work that would let this scenario
become a hard-floor test in the future.
2026-05-07 22:03:38 +00:00

6.0 KiB
Raw Permalink Blame History

Plan: tighten cross-stack interop assertions to hard-pass

Status: CLOSED 2026-05-07. Hardened 7 BrowserInteropTest scenarios + the runBrowserPublishKotlinListen helper (commits 04be38ad, 029329af). Verification sweep: 5/5 BUILD SUCCESSFUL × 22/22 tests = 110/110 hard-pass. Depended on: 2026-05-07-moq-relay-routing-investigation.md (closed by :quic merge from origin/main).

Why soft passes exist today

Five scenarios currently soft-pass (vacuous-pass on listener-side 0-frame outcomes) to keep the test suite from fail-flaking on the upstream relay-routing race documented in 2026-05-07-late-join-catalog-flake-investigation.md:

Scenario File Soft-pass behavior
chromium_listener_late_join_still_decodes_tail BrowserInteropTest.kt if (pcm.size <= warmupSamples) return
chromium_listener_mid_broadcast_mute_shortens_pcm BrowserInteropTest.kt same
chromium_decoder_no_errors_through_warmup_window (I14) BrowserInteropTest.kt no decoderOutputs >= 4 floor
chromium_publisher_baseline_kotlin_listener_decodes BrowserInteropTest.kt hard-asserts publisher framesIn; soft-asserts listener
chromium_publisher_reconnect_kotlin_listener_recovers (Browser I7) BrowserInteropTest.kt same as baseline

All five have hard assertions on the framesIn / publisher-side behavior; the listener-side is what's soft. None of these are reaching their full design intent.

Soft-pass justification audit (per scenario)

Re-read each scenario's kdoc. The soft-pass is honest right now (captured 0 frames means harness flake, not regression). Once the relay-routing race is fixed, the listener side becomes deterministic and the soft-pass is no longer load-bearing — at that point, the soft-pass HIDES regressions (a real T8/T11/T13 break could land in a 0-frame outcome and pass vacuously).

Tighten plan

Step 1 — confirm sweep stability

After the routing investigation lands, run:

for i in 1 2 3 4 5; do
  echo "=== run $i ==="
  ./gradlew :nestsClient:jvmTest \
    --tests HangInteropTest \
    --tests BrowserInteropTest \
    -DnestsHangInterop=true \
    -DnestsBrowserInterop=true \
    --rerun-tasks 2>&1 | grep -E "FAILED]|BUILD"
done

5/5 BUILD SUCCESSFUL with 0 FAILED lines = stability achieved.

Step 2 — replace each soft-pass with a hard floor

For each scenario in the table above, remove the if (pcm.size <= warmupSamples) return short-circuit and replace with a meaningful sample-count floor. Tighten thresholds based on observed steady-state numbers (see each scenario's kdoc for what "steady-state" looks like — most run ≥ 1 s of audio in green-state).

Concretely:

  • Late-join: assertTrue(pcm.size >= ...) floor. Steady-state captures ~3 s on a 5 s broadcast with 2 s late-join, minus warmup. Threshold: ≥ 1.5 s — comfortably under the steady-state but well over zero.
  • Mute-window: tighten the upper bound (current 5.5 s) to ~5.0 s. Add a lower bound asserting ≥ 2.5 s — proves audio arrived AND the muted segment shortened the total.
  • I14: re-add decoderOutputs >= 4 (3 warmup + ≥ 1 audio). Current absence-only assertion is partial coverage.
  • Browser publisher baseline + reconnect: remove the vacuous-pass branches, add a ≥ 0.5 s of audio after warmup floor for baseline and ≥ 2.5 s for reconnect (matches the hang-tier I7 threshold).

Step 3 — reverify

Re-run the 5× sweep. All scenarios must hard-pass 5/5. If a scenario flakes after tightening, the relay-routing investigation isn't fully done and we revert the tightening on that scenario until it is.

Step 4 — update the gap matrix

nestsClient/plans/2026-05-06-cross-stack-interop-test-gap-matrix.md currently lists I14 with "browser " pending; flip to "" once its hard floor is in. Same for any I-scenarios that now have hard floors on both tiers.

Acceptance criteria

  • All BrowserInteropTest scenarios run with hard sample-count AND FFT-peak assertions (no return@runBlocking short-circuits on pcm.size).
  • All HangInteropTest scenarios already hard-pass — no change needed there.
  • Gap matrix updated to reflect hard-pass coverage on each T#.
  • Results plan updated to remove the "soft-pass on flake" language.

Outcome (2026-05-07)

Scenario Floor landed Notes
I2 late-join ≥ 1.5 s after warmup per plan recommendation
I3 mute-window lower ≥ 2.5 s + upper < 5.5 s upper bound left at 5.5 s; the plan's 5.0 s tightening tripped 5/5 against empirical 5.15.2 s steady state
I4 stereo ≥ 1 s × 2 ch new floor (was vacuous)
I5 hot-swap soft-pass kept The pcm.size > warmupSamples floor flaked 3/5 in a follow-up sweep — empirical samples vary 07.7 k right at the warmup threshold. Reverted to a soft-pass with the printed-to-stderr explanation; T12 protection is asserted by the hang-tier counterpart. Tracked as deferred follow-up "browser hot-swap re-attach" in 2026-05-06-cross-stack-interop-test-results.md.
I9 packet-loss ≥ 0.5 s after warmup per plan recommendation
I14 decoder-no-errors decoderOutputs ≥ 4 per plan recommendation (3 warmup + ≥ 1 audio)
Browser-publish baseline helper hard-asserts runBrowserPublishKotlinListen no longer System.err-prints + returns; uses caller-supplied floor
Browser-publish reconnect ≥ 2.5 s via helper per plan recommendation

5/5 sweep × 22 tests = 110/110 hard-pass on ./gradlew :nestsClient:jvmTest --tests HangInteropTest --tests BrowserInteropTest -DnestsHangInterop=true -DnestsBrowserInterop=true --rerun-tasks.

Risk: post-tightening flake

If any scenario fail-flakes after tightening, the routing investigation isn't really done. Don't paper over with a wider threshold; that's the same trap as the soft-passes. Either: (a) revert the tightening on that scenario and keep investigating, OR (b) widen the threshold ONLY if the new value still excludes the regression mode the test was designed to catch.