From 11ada4ea576452fa2d2329d2fa515371df1ef6f7 Mon Sep 17 00:00:00 2001 From: japabu Date: Mon, 28 Sep 2026 17:58:51 +0200 Subject: [PATCH 1/4] Disable the flaky netd_refused_accept: it hung 2341s waiting on a wake MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Orchestrator Fast tier at #562's head 2a9c77ee timed the test out at "timed out after 2341s, with the guest still talking 8s ago", stuck on the wake for the connection an accept refused for room left; #562 touches nothing this test runs. A flaky test is disabled at once behind its filed issue (issues/kernel/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md), which also records the unconfirmed reading that the host's dial ended on a reset rather than a graceful close, returning netd's listener to Listen with no wake owed — not established without a kept console. A second, unrelated sighting from the same run: the hang cost the full 39-minute ceiling backstop rather than the much smaller GUEST_QUIET silence window, because the guest's periodic console lines never let it go quiet. tests/CLAUDE.md already documents this as intended, so it is filed as a tooling note on the cost rather than a defect (issues/build/a-stuck-but-chatty-guest-costs-the-full-ceiling-backstop-before-the-harness-ends-it.md). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01W6rME2DoqwjcYFStYHHY4j --- ...ing-backstop-before-the-harness-ends-it.md | 33 +++++++++++++++++++ ...hung-waiting-for-a-wake-that-never-came.md | 31 +++++++++++++++++ src/redlist.rs | 4 +++ 3 files changed, 68 insertions(+) create mode 100644 issues/build/a-stuck-but-chatty-guest-costs-the-full-ceiling-backstop-before-the-harness-ends-it.md create mode 100644 issues/kernel/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md diff --git a/issues/build/a-stuck-but-chatty-guest-costs-the-full-ceiling-backstop-before-the-harness-ends-it.md b/issues/build/a-stuck-but-chatty-guest-costs-the-full-ceiling-backstop-before-the-harness-ends-it.md new file mode 100644 index 00000000000..4024d5a54b7 --- /dev/null +++ b/issues/build/a-stuck-but-chatty-guest-costs-the-full-ceiling-backstop-before-the-harness-ends-it.md @@ -0,0 +1,33 @@ +--- +status: open +kind: tooling +opened: 2026-09-28 +--- + +# A stuck-but-chatty guest costs the full ceiling backstop before the harness ends it + +`ceiling_verdict` (`tests/common/qemu.rs:672-707`) ends a test's wait early +only when the guest has both passed its own budget *and* gone silent for +`GUEST_QUIET` (15 s, `tests/common/qemu.rs:509`); a guest still printing +anything — including the kernel's own periodic idle-loop line on a 10 s +cadence, named at `tests/common/qemu.rs:505-508` as one of the periodic +speakers that keeps a guest "talking" with no live test progress behind it — +never trips that arm and runs on to the absolute backstop, +`ceiling.max(GUEST_WEDGED)` (`tests/common/qemu.rs:702`, `GUEST_WEDGED` = +300 s, `tests/common/qemu.rs:530`), scaled by the per-test timeout, the +phase's `WIDTH` and `host_scale`/oversubscription (`budget`, +`tests/common/qemu.rs:244-245`; `budget_smp`, `tests/common/qemu.rs:375-377`). + +Sighting: `netd_refused_accept` (base timeout 120 s, +`tests/toyos.rs:10111`) hung on `2a9c77ee` and was reported only at +`FAIL netd_refused_accept: timed out after 2341s, with the guest still +talking 8s ago` (`orch-runs/562r5-fast.log` lines ~1160-1169) — 39 minutes of +wall clock before the harness ended it, because the guest's periodic +console lines never let `quiet >= GUEST_QUIET` hold. + +`tests/CLAUDE.md` already states this as the intended design (a talking +guest is judged slow, not stuck, until the backstop). Nothing here says the +design is wrong; recorded because a real stuck guest, if only "chatty" for a +reason unrelated to progress, is priced at the full backstop rather than the +much smaller `GUEST_QUIET` silence window — a wall-clock cost a CI run pays +whenever this shape recurs. diff --git a/issues/kernel/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md b/issues/kernel/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md new file mode 100644 index 00000000000..aede48cd9d6 --- /dev/null +++ b/issues/kernel/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md @@ -0,0 +1,31 @@ +--- +status: expected-red +kind: defect +opened: 2026-09-28 +--- + +# `netd_refused_accept` hung waiting for a wake that never came + +Orchestrator Fast tier at PR #562's head `2a9c77ee`, log +`orch-runs/562r5-fast.log` lines ~1160-1169: `FAIL netd_refused_accept: timed +out after 2341s, with the guest still talking 8s ago`. The guest's last line +was `netd_refused_accept: waiting for a wake for the connection an accept +refused for room left, once room returned` — the `wake()` call at +`tests/toyos-rust-tests/src/bin/netd_refused_accept.rs:63`, blocked reading +`listener.notify`. #562 changes nothing this test runs: the guest binary, +netd, the netcase config and the harness function that drives it are +identical to main. + +Unconfirmed reading, not established from a kept console: the same log's +`the host's connections ended [Ok(585728), Ok(0), Ok(0), ...]` shows the +host's dial ending `Ok(585728)` rather than with the harness's 120 s +write-stall error, so it likely ended on a reset rather than a graceful +close. A reset would return the listener's socket to `Listen` +(`settle` in `userland/netd/src/listen.rs:73-85`, the `tcp::State::Closed` +arm re-`listen`s without producing an accept), after which netd owes the +test's `end(held.pop()...)` connection no wake. The guest console for this +run was not kept, so which path the reset actually took is not established. + +Exit condition: the mechanism established from a kept console, fixed, and +`netd_refused_accept` green. Owner: netd's accept/wake path +(`userland/netd/src/listen.rs`); held by the orchestrator. diff --git a/src/redlist.rs b/src/redlist.rs index 884f0760969..81c357749ee 100644 --- a/src/redlist.rs +++ b/src/redlist.rs @@ -60,6 +60,10 @@ pub const DISABLED: &[Disabled] = &[ test: "log_ring_keeps_the_owners_slots", issue: "issues/kernel/a-log-rings-owner-is-named-only-when-logd-reads-its-registration.md", }, + Disabled { + test: "netd_refused_accept", + issue: "issues/kernel/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md", + }, Disabled { test: "partition_claim_departure", issue: "issues/boot-media/partition-claim-departure-exits-clean-with-none-of-its-refusals-said.md", From 9ac43db59f1d0c30dd027604b9122eec95138546 Mon Sep 17 00:00:00 2001 From: japabu Date: Mon, 28 Sep 2026 18:16:54 +0200 Subject: [PATCH 2/4] Round 1 review fixes: the real backstop contradiction, the netd issue's area and owner, and two removed claims MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The deleted tooling issue restated tests/CLAUDE.md's own design and owed nothing. In its place: GUEST_WEDGED's own doc says the wedged-but-chatty backstop is "Not budget-scaled", but ceiling_verdict's backstop is ceiling.max(GUEST_WEDGED) and ceiling is already width/host-scaled, so once a scaled ceiling exceeds 300s the backstop is that scaled ceiling, not the number the doc names — exactly the 2341s netd_refused_accept paid. The netd issue moves to issues/hardware/ beside its listener siblings, takes their owner line, and its exit condition now names the instrument it needs: netcase_against_host drops result.serial on its error path, so no kept console can exist until that path keeps it. Also removed: the "likely ended on a reset" claim (Ok cannot tell a reset from a FIN), "held by the orchestrator" on an expected-red entry, and both files' scratchpad-path citations, keeping the quoted FAIL line as the evidence. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01W6rME2DoqwjcYFStYHHY4j --- ...ing-backstop-before-the-harness-ends-it.md | 33 ----------------- ...-broken-by-its-own-pairing-with-ceiling.md | 35 +++++++++++++++++++ ...hung-waiting-for-a-wake-that-never-came.md | 20 ++++++----- src/redlist.rs | 2 +- 4 files changed, 47 insertions(+), 43 deletions(-) delete mode 100644 issues/build/a-stuck-but-chatty-guest-costs-the-full-ceiling-backstop-before-the-harness-ends-it.md create mode 100644 issues/build/guest-wedgeds-not-budget-scaled-contract-is-broken-by-its-own-pairing-with-ceiling.md rename issues/{kernel => hardware}/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md (61%) diff --git a/issues/build/a-stuck-but-chatty-guest-costs-the-full-ceiling-backstop-before-the-harness-ends-it.md b/issues/build/a-stuck-but-chatty-guest-costs-the-full-ceiling-backstop-before-the-harness-ends-it.md deleted file mode 100644 index 4024d5a54b7..00000000000 --- a/issues/build/a-stuck-but-chatty-guest-costs-the-full-ceiling-backstop-before-the-harness-ends-it.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -status: open -kind: tooling -opened: 2026-09-28 ---- - -# A stuck-but-chatty guest costs the full ceiling backstop before the harness ends it - -`ceiling_verdict` (`tests/common/qemu.rs:672-707`) ends a test's wait early -only when the guest has both passed its own budget *and* gone silent for -`GUEST_QUIET` (15 s, `tests/common/qemu.rs:509`); a guest still printing -anything — including the kernel's own periodic idle-loop line on a 10 s -cadence, named at `tests/common/qemu.rs:505-508` as one of the periodic -speakers that keeps a guest "talking" with no live test progress behind it — -never trips that arm and runs on to the absolute backstop, -`ceiling.max(GUEST_WEDGED)` (`tests/common/qemu.rs:702`, `GUEST_WEDGED` = -300 s, `tests/common/qemu.rs:530`), scaled by the per-test timeout, the -phase's `WIDTH` and `host_scale`/oversubscription (`budget`, -`tests/common/qemu.rs:244-245`; `budget_smp`, `tests/common/qemu.rs:375-377`). - -Sighting: `netd_refused_accept` (base timeout 120 s, -`tests/toyos.rs:10111`) hung on `2a9c77ee` and was reported only at -`FAIL netd_refused_accept: timed out after 2341s, with the guest still -talking 8s ago` (`orch-runs/562r5-fast.log` lines ~1160-1169) — 39 minutes of -wall clock before the harness ended it, because the guest's periodic -console lines never let `quiet >= GUEST_QUIET` hold. - -`tests/CLAUDE.md` already states this as the intended design (a talking -guest is judged slow, not stuck, until the backstop). Nothing here says the -design is wrong; recorded because a real stuck guest, if only "chatty" for a -reason unrelated to progress, is priced at the full backstop rather than the -much smaller `GUEST_QUIET` silence window — a wall-clock cost a CI run pays -whenever this shape recurs. diff --git a/issues/build/guest-wedgeds-not-budget-scaled-contract-is-broken-by-its-own-pairing-with-ceiling.md b/issues/build/guest-wedgeds-not-budget-scaled-contract-is-broken-by-its-own-pairing-with-ceiling.md new file mode 100644 index 00000000000..82b3e6ae4c4 --- /dev/null +++ b/issues/build/guest-wedgeds-not-budget-scaled-contract-is-broken-by-its-own-pairing-with-ceiling.md @@ -0,0 +1,35 @@ +--- +status: open +kind: tooling +opened: 2026-09-28 +--- + +# `GUEST_WEDGED`'s "not budget-scaled" contract is broken by its own pairing with `ceiling` + +`ceiling_verdict`'s absolute backstop (`tests/common/qemu.rs:702`) is +`ceiling.max(GUEST_WEDGED)`, and `ceiling` is what every caller passes in +already `budget`/`budget_smp`-scaled by phase width and host speed — e.g. +`screendump_while_rendering`'s `budget_smp(timeout, self.smp)` +(`tests/common/qemu.rs:3314`). `GUEST_WEDGED`'s own doc +(`tests/common/qemu.rs:516-522`) says the backstop is "Not budget-scaled, and +that is the point of the pair … scaling it would only make that state cost an +hour at width 12." The `.max()` contradicts that claim directly: once a +scaled `ceiling` exceeds the unscaled 300 s `GUEST_WEDGED`, the backstop *is* +that scaled `ceiling`, not the 300 s the doc names. + +Sighting: `netd_refused_accept` (base timeout 120 s, `tests/toyos.rs:10111`) +paid the scaled ceiling, not 300 s: `FAIL netd_refused_accept: timed out +after 2341s, with the guest still talking 8s ago`. + +The code's other callers of `GUEST_WEDGED` all want the unscaled number: it +is used raw at `tests/common/update.rs:193` and `:264` and at +`tests/toyos.rs:3368`, and `qemu.rs:527-528` cites a measured 302 s wedge on +a shared boot, not a width-scaled one. `ceiling_verdict`'s `.max(ceiling)` is +the one place that number stops being 300 s. + +Exit condition: the code and the doc agree — a wedged-but-talking guest is +ended at the unscaled `GUEST_WEDGED` backstop, matching every other caller — +or the doc at `tests/common/qemu.rs:516-522` is deleted if scaling the +backstop through `ceiling` is actually intended. + +Owner: whoever next touches `ceiling_verdict` in `tests/common/qemu.rs`. diff --git a/issues/kernel/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md b/issues/hardware/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md similarity index 61% rename from issues/kernel/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md rename to issues/hardware/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md index aede48cd9d6..3de761f5ddc 100644 --- a/issues/kernel/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md +++ b/issues/hardware/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md @@ -6,26 +6,28 @@ opened: 2026-09-28 # `netd_refused_accept` hung waiting for a wake that never came -Orchestrator Fast tier at PR #562's head `2a9c77ee`, log -`orch-runs/562r5-fast.log` lines ~1160-1169: `FAIL netd_refused_accept: timed -out after 2341s, with the guest still talking 8s ago`. The guest's last line -was `netd_refused_accept: waiting for a wake for the connection an accept +Orchestrator Fast tier at PR #562's head `2a9c77ee`: `FAIL netd_refused_accept: +timed out after 2341s, with the guest still talking 8s ago`. The guest's last +line was `netd_refused_accept: waiting for a wake for the connection an accept refused for room left, once room returned` — the `wake()` call at `tests/toyos-rust-tests/src/bin/netd_refused_accept.rs:63`, blocked reading `listener.notify`. #562 changes nothing this test runs: the guest binary, netd, the netcase config and the harness function that drives it are identical to main. -Unconfirmed reading, not established from a kept console: the same log's +Unconfirmed reading, not established from a kept console: the same run's `the host's connections ended [Ok(585728), Ok(0), Ok(0), ...]` shows the host's dial ending `Ok(585728)` rather than with the harness's 120 s -write-stall error, so it likely ended on a reset rather than a graceful -close. A reset would return the listener's socket to `Listen` +write-stall error. A reset would return the listener's socket to `Listen` (`settle` in `userland/netd/src/listen.rs:73-85`, the `tcp::State::Closed` arm re-`listen`s without producing an accept), after which netd owes the test's `end(held.pop()...)` connection no wake. The guest console for this run was not kept, so which path the reset actually took is not established. Exit condition: the mechanism established from a kept console, fixed, and -`netd_refused_accept` green. Owner: netd's accept/wake path -(`userland/netd/src/listen.rs`); held by the orchestrator. +`netd_refused_accept` green — which needs `netcase_against_host` +(`tests/toyos.rs:10113-10121`) to keep `result.serial` on its error path +instead of dropping it, since nothing else in the tree keeps this test's +console. + +**Owner**: whoever holds `issues/design-debt/toyos-has-its-own-network-stack.md`. diff --git a/src/redlist.rs b/src/redlist.rs index 81c357749ee..ee43b7d537d 100644 --- a/src/redlist.rs +++ b/src/redlist.rs @@ -62,7 +62,7 @@ pub const DISABLED: &[Disabled] = &[ }, Disabled { test: "netd_refused_accept", - issue: "issues/kernel/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md", + issue: "issues/hardware/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md", }, Disabled { test: "partition_claim_departure", From 5a38b521396f8a61da14b166157b3caa4f4e2a0f Mon Sep 17 00:00:00 2001 From: japabu Date: Mon, 28 Sep 2026 18:55:05 +0200 Subject: [PATCH 3/4] Round 2 review fixes: drop the harness issue, fix qemu.rs's own false prose, and stop the netd issue asserting a reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The harness issue's exit condition could close while the contradiction it named stood, and both its branches were already ruled out by qemu.rs's own comment on `ceiling_verdict`'s backstop (:696-701): the pairing with `ceiling` is intended, so the backstop already is the scaled ceiling once that ceiling exceeds GUEST_WEDGED — exactly what the 2341s netd_refused_accept paid. So the issue is deleted, and the prose that called that backstop "Not budget-scaled" (qemu.rs:517, :522, :525-528), or its destination "the GUEST_WEDGED backstop" (:662-663), or its ceiling-below-backstop shape "every real test has" (:818-819), is deleted rather than rewritten: it was false of the tree, not the tree's own guard. The netd issue's last sentence claimed "which path the reset actually took" was left unestablished — but Ok(585728) cannot tell a reset from a graceful close, so no reset was ever established to have a path. Deleted along with the rest of that clause. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01W6rME2DoqwjcYFStYHHY4j --- ...-broken-by-its-own-pairing-with-ceiling.md | 35 ------------------- ...hung-waiting-for-a-wake-that-never-came.md | 2 +- tests/common/qemu.rs | 25 +++++-------- 3 files changed, 10 insertions(+), 52 deletions(-) delete mode 100644 issues/build/guest-wedgeds-not-budget-scaled-contract-is-broken-by-its-own-pairing-with-ceiling.md diff --git a/issues/build/guest-wedgeds-not-budget-scaled-contract-is-broken-by-its-own-pairing-with-ceiling.md b/issues/build/guest-wedgeds-not-budget-scaled-contract-is-broken-by-its-own-pairing-with-ceiling.md deleted file mode 100644 index 82b3e6ae4c4..00000000000 --- a/issues/build/guest-wedgeds-not-budget-scaled-contract-is-broken-by-its-own-pairing-with-ceiling.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -status: open -kind: tooling -opened: 2026-09-28 ---- - -# `GUEST_WEDGED`'s "not budget-scaled" contract is broken by its own pairing with `ceiling` - -`ceiling_verdict`'s absolute backstop (`tests/common/qemu.rs:702`) is -`ceiling.max(GUEST_WEDGED)`, and `ceiling` is what every caller passes in -already `budget`/`budget_smp`-scaled by phase width and host speed — e.g. -`screendump_while_rendering`'s `budget_smp(timeout, self.smp)` -(`tests/common/qemu.rs:3314`). `GUEST_WEDGED`'s own doc -(`tests/common/qemu.rs:516-522`) says the backstop is "Not budget-scaled, and -that is the point of the pair … scaling it would only make that state cost an -hour at width 12." The `.max()` contradicts that claim directly: once a -scaled `ceiling` exceeds the unscaled 300 s `GUEST_WEDGED`, the backstop *is* -that scaled `ceiling`, not the 300 s the doc names. - -Sighting: `netd_refused_accept` (base timeout 120 s, `tests/toyos.rs:10111`) -paid the scaled ceiling, not 300 s: `FAIL netd_refused_accept: timed out -after 2341s, with the guest still talking 8s ago`. - -The code's other callers of `GUEST_WEDGED` all want the unscaled number: it -is used raw at `tests/common/update.rs:193` and `:264` and at -`tests/toyos.rs:3368`, and `qemu.rs:527-528` cites a measured 302 s wedge on -a shared boot, not a width-scaled one. `ceiling_verdict`'s `.max(ceiling)` is -the one place that number stops being 300 s. - -Exit condition: the code and the doc agree — a wedged-but-talking guest is -ended at the unscaled `GUEST_WEDGED` backstop, matching every other caller — -or the doc at `tests/common/qemu.rs:516-522` is deleted if scaling the -backstop through `ceiling` is actually intended. - -Owner: whoever next touches `ceiling_verdict` in `tests/common/qemu.rs`. diff --git a/issues/hardware/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md b/issues/hardware/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md index 3de761f5ddc..4e389c509b5 100644 --- a/issues/hardware/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md +++ b/issues/hardware/netd-refused-accept-hung-waiting-for-a-wake-that-never-came.md @@ -22,7 +22,7 @@ write-stall error. A reset would return the listener's socket to `Listen` (`settle` in `userland/netd/src/listen.rs:73-85`, the `tcp::State::Closed` arm re-`listen`s without producing an accept), after which netd owes the test's `end(held.pop()...)` connection no wake. The guest console for this -run was not kept, so which path the reset actually took is not established. +run was not kept. Exit condition: the mechanism established from a kept console, fixed, and `netd_refused_accept` green — which needs `netcase_against_host` diff --git a/tests/common/qemu.rs b/tests/common/qemu.rs index 09e7bf0eeb9..9b573dd8bba 100644 --- a/tests/common/qemu.rs +++ b/tests/common/qemu.rs @@ -514,19 +514,14 @@ pub const GUEST_QUIET: Duration = Duration::from_secs(15); /// silence alone cannot end a desktop wait, and a suite that never ends is /// worse than one that reds. /// -/// **Not [`budget`]-scaled, and that is the point of the pair.** Width is what a -/// ceiling on a *slow* guest has to be corrected for, and the silence bound -/// above is what a slow guest is now judged by — it keeps talking, so it is -/// never judged by this at all. What is left for this number to catch is a guest -/// that is stuck *and* chatty, which is a state the width does not produce; -/// scaling it would only make that state cost an hour at width 12. The longest -/// guest action any caller waits on is eight seconds of audio. +/// Width is what a ceiling on a *slow* guest has to be corrected for, and the +/// silence bound above is what a slow guest is now judged by — it keeps talking, +/// so it is never judged by this at all. What is left for this number to catch +/// is a guest that is stuck *and* chatty, which is a state the width does not +/// produce. The longest guest action any caller waits on is eight seconds of +/// audio. /// -/// It is also the real ceiling of any failing wait on a shared boot, because -/// the kernel's own 10 s cadence keeps the quiet clock above reset: a settle -/// predicate that could never come true was measured ending here at 302 s, -/// not at 15 (PR #96's verification). Price a new waiting check against this -/// number, not the one above. +/// Price a new waiting check against this number, not the one above. pub const GUEST_WEDGED: Duration = Duration::from_secs(300); pub fn guest_liveness() -> Liveness { @@ -659,8 +654,7 @@ impl std::fmt::Display for WaitVerdict { /// loaded `smp:2` runner its `vcpus/cores` factor clamps to 1, a guest making /// steady progress called wedged by a clock. So a guest still *talking* is now /// never ended by `ceiling`: the per-test budget bites only a guest that has -/// *also* gone quiet for [`GUEST_QUIET`], and a talking one runs to the -/// [`GUEST_WEDGED`] backstop below. +/// *also* gone quiet for [`GUEST_QUIET`]. /// /// **`elapsed > ceiling` stays a necessary condition, and that is what keeps /// this safe.** Silence alone is not a wedge on this suite's boots: a healthy @@ -815,8 +809,7 @@ pub fn ceiling_self_check() -> Result<(), String> { // all four directions. A talking guest past its budget is slow, not // wedged; a silent one within its budget is idle, not wedged; the wedge // guard still fires, and fast; and the backstop still catches a guest - // that talks forever. Staged with a ceiling below [`GUEST_WEDGED`] so the - // backstop is a distinct, higher number — the shape every real test has. + // that talks forever. const TIGHT: Duration = Duration::from_secs(153); let bstop = TIGHT.max(GUEST_WEDGED); assert!(TIGHT < bstop, "the case needs a ceiling below the backstop"); From 13709f80046fd563edf253e2ed2dd983a13dd203 Mon Sep 17 00:00:00 2001 From: japabu Date: Mon, 28 Sep 2026 19:19:27 +0200 Subject: [PATCH 4/4] Round 3 review fixes: four more false qemu.rs sentences removed, and ceiling_self_check gets the case that would have caught the round-2 NOTE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each was false whenever a ceiling exceeds GUEST_WEDGED, the same falsehood already deleted elsewhere in round 2 or contradicted by ceiling_verdict's own backstop computation (ceiling.max(GUEST_WEDGED)): a talking guest is not always spared by `ceiling`, GUEST_WEDGED does not judge every talking guest, a new check's price is not this constant alone, and screendump_while_rendering's backstop is not GUEST_WEDGED but ceiling.max(GUEST_WEDGED). ceiling_self_check gained the case the NOTE named: a ceiling above GUEST_WEDGED with a guest talking past GUEST_WEDGED but still short of that ceiling must not be ended at GUEST_WEDGED. Mutating qemu.rs:685's `let backstop = ceiling.max(GUEST_WEDGED);` to `let backstop = GUEST_WEDGED;` fails the new case with "a guest talking past GUEST_WEDGED but short of a higher ceiling was ended anyway — the backstop did not follow a ceiling above GUEST_WEDGED"; restoring the line turns it green again. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01W6rME2DoqwjcYFStYHHY4j --- tests/common/qemu.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/common/qemu.rs b/tests/common/qemu.rs index 9b573dd8bba..e3c4ea6aeae 100644 --- a/tests/common/qemu.rs +++ b/tests/common/qemu.rs @@ -513,15 +513,6 @@ pub const GUEST_QUIET: Duration = Duration::from_secs(15); /// The compositor prints its interval line whatever else has stopped, so /// silence alone cannot end a desktop wait, and a suite that never ends is /// worse than one that reds. -/// -/// Width is what a ceiling on a *slow* guest has to be corrected for, and the -/// silence bound above is what a slow guest is now judged by — it keeps talking, -/// so it is never judged by this at all. What is left for this number to catch -/// is a guest that is stuck *and* chatty, which is a state the width does not -/// produce. The longest guest action any caller waits on is eight seconds of -/// audio. -/// -/// Price a new waiting check against this number, not the one above. pub const GUEST_WEDGED: Duration = Duration::from_secs(300); pub fn guest_liveness() -> Liveness { @@ -652,9 +643,7 @@ impl std::fmt::Display for WaitVerdict { /// the instant it passed — so a merely-slow guest reported exactly what a wedged /// one did. `launcher_refusals` was killed at `192s "still talking 1s ago"` on a /// loaded `smp:2` runner its `vcpus/cores` factor clamps to 1, a guest making -/// steady progress called wedged by a clock. So a guest still *talking* is now -/// never ended by `ceiling`: the per-test budget bites only a guest that has -/// *also* gone quiet for [`GUEST_QUIET`]. +/// steady progress called wedged by a clock. /// /// **`elapsed > ceiling` stays a necessary condition, and that is what keeps /// this safe.** Silence alone is not a wedge on this suite's boots: a healthy @@ -860,6 +849,19 @@ pub fn ceiling_self_check() -> Result<(), String> { )); } + // 3c. **The other side of `ceiling.max(GUEST_WEDGED)`**: a ceiling *above* + // `GUEST_WEDGED` must itself be the backstop, not get clamped down to + // the floor. `CEILING` (380 s, from case 1) is such a ceiling; a guest + // talking past `GUEST_WEDGED` (300 s) but still short of `CEILING` is + // not yet at its backstop and must run on. + if ceiling_verdict(None, GUEST_WEDGED + Duration::from_secs(50), CEILING, talking, 40).is_some() + { + return Err(String::from( + "a guest talking past GUEST_WEDGED but short of a higher ceiling was ended anyway — \ + the backstop did not follow a ceiling above GUEST_WEDGED", + )); + } + // 4. **What the verdict carries, which is the half that was missing.** Every // arm above names a death in one sentence; until 2026-08-18 that sentence // was the whole of what a failure arm had, and a `DOUBLE FAULT on CPU 1` @@ -3283,8 +3285,8 @@ impl QemuInstance { /// is the case whose paint "never arrived in the window" while the guest was /// alive — the budget-scaled deadline undercounts a later moment in the run /// exactly as the serial ceiling did. Only a screen *frozen* for - /// [`GUEST_QUIET`] past the deadline, or the [`GUEST_WEDGED`] backstop, ends - /// the wait; `done` firing ends it at once, so a passing caller is untouched + /// [`GUEST_QUIET`] past the deadline ends the wait; `done` firing ends it at + /// once, so a passing caller is untouched /// and a real bug (the paint that should not be there, and stays) still fires /// its assertion, a frozen-screen `GUEST_QUIET` later. ///