Skip to content

The shared boots run as guest rows, each member judged by its own exit - #1271

Merged
Japabu merged 8 commits into
mainfrom
shared-boot-guest-rows
Nov 12, 2026
Merged

Japabu merged 8 commits into
mainfrom
shared-boot-guest-rows

Conversation

@Japabu

@Japabu Japabu commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Both metal benches are down indefinitely, and the T14's two shared boots were the only place their members ran: every discovered Rust test binary, the C corpus and LAST_MEMBERS on testcases, and ACTUATOR_TESTS on shared-debug. That is about 93 Rust binaries, 143 corpus cases and 9 actuator members, and several open PRs' central tests are among them. The QEMU shared block left the guest suite on 2026-10-01.

What this does

Two machine tests run those boots on QEMU x86-64:

  • testcases_members_exit_0: the shipping kernel, 236 members.
  • shared_debug_members_exit_0: the test kernel armed with SELFTEST_PARAMS, 9 members.

The two rows are dispatchable like any other MACHINE_TESTS row. The filter members_exit_0 takes both. An unfiltered run includes them, so nightly's KVM and TCG lanes do too.

  • One membership. Each row takes its boot from shared_metal, batches it with metal::batches and stages it with metal::stage, which metal::build now calls too. The two paths therefore stage the same members in the same order, with the same ccheck links, corpus expectations and reachable binaries; the guest rows leave out the bench rows' own jobs on both boots, and their list bound is the bench's ×8. The guest boots the SharedBoot's own kernel build and parameters. No second list exists.
  • Judged by each member's own exit. The guest runs the list to its reboot, which -no-reboot turns into QEMU's exit. The row then reads, for each member, the runner's own ===TEST_END <job> exit=N=== line. Only the runner's head without pid= counts, so a job's line in the runner's ring and a longer name's end are not taken for it. A red row names every failing member, with its own window of the console. Members the list never reached are collapsed into one line. The boot counts as ended only by its list's reboot: the kernel's own Rebooting. record, and QMP's SHUTDOWN naming guest-reset, as power::ended holds. Under -no-reboot, a reset, a power-off and a triple fault all exit with status 0. A kernel death anywhere on the console also reds the row.
  • The in-guest bound. The runner's list bound is the bench's bound times HOST_SCALE_CAP. Under QEMU, the harness's own ceiling, scaled for the host this boot measured, ends a list that does not end itself. In local development under TCG, testcases ended 116 s into its boot. The bench's bound for it is 102 s.
  • Metal is unchanged. Its boots, members and judges are as before. build is split at a function boundary, and the code moves unchanged. The METAL rows that ride testcases and shared-debug stay off the guest rows: each is judged on a bench readback of hardware facts.
  • kernel_of now accepts the test kernel named beside actuators, which shared-debug declares. Any other build named with parameters is still refused.
  • A filter that names a shared member is still refused, and the refusal now says the boot runs whole under its row.
  • The corpus compile path (compile_c, compile_own_c, link_toyos) returns its refusal instead of panicking. The process-wide panic-hook swap and the catch_unwinds are gone, so the shared rows' in-suite corpus compile no longer hides other lanes' panic locations.

Where they run. QEMU x86-64 q35 (Profile::Headless, qemu::SUITE_ARCH): KVM on the guest runner, and TCG on hosts that have no KVM. AArch64 builds no member, by the owner's ruling. No metal machine is added or dropped.

Triage of the first guest run

Guest-rows run 43102627714 at bf8f66e5e on the guest runner, under KVM: green. testcases_members_exit_0 PASS, 236 members exited 0. shared_debug_members_exit_0 PASS, 9 members exited 0. The run reported "liveness ceilings paid at 1.87x" and other host work at 0.04 CPUs, so the host was quiet. No member is red, and none is marked metal-only. The triage needs no code. That head predates the end check (judge_shared); the landing run below exercises it.

Timing, for the liveness bounds only, since QEMU's clock is a hang ceiling and not evidence of speed:

  • The suite took 447.8 s. The testcases row reported 274 s, almost all of it before its guest started: the corpus compile, and 142.6 s waiting for the artifact-staging lock held by the other row's image build.
  • Its image was built at 02:20:33.96 and its verdict came at 02:20:46.86. QEMU's start, the firmware, the boot, all 236 members, the reboot and the judging took 12.9 s of wall clock. shared-debug's took 3.4 s.
  • Against the bounds: the bench's list bound for testcases is 102.2 s, which the host ceiling paid at 1.87x on this runner (about 191 s), and the in-guest ×8 bound is 817.6 s. The KVM list used about 7 % of the host ceiling. The ×8 bound only guards emulated guests, where a development TCG run ended 116 s into its boot. On KVM the host's ceiling is the one that would stop a stuck list.

Earlier dispatches: 43098179083 ended startup_failure in the GitHub Actions outage. 43100012498, 43108299085 and 43112666542 were cancelled while queued, for later heads.

A member fix and a record

readdir_bound made 6,000 empty files in /tmp so that their names alone would exceed a 64 KiB listing reply. On a 4 GiB guest, /tmp's pager serves 3,468 objects: (953,443 managed pages / 8 − 8,192) / 2, then / 16. Anonymous objects are never dropped to make room. The member was refused its 3,465th file and panicked with every file it had made left behind. Every later member that writes /tmp failed behind it: 7 Rust members and 7 corpus cases.

Checks

Head: ba2aacbf3, rebased onto main d1cefbaf9. The rows use #1254's (name, fn) registration, run_machine_test is gone, and #1273's follow-ups are made. At that head:

  • cargo test --test toyos-checks: 55 passed, including suite_split (98 shared binaries on the shipping kernel, 9 on the actuator one, 20 driven elsewhere and declared).
  • cargo test -p toyos-metal: 81 passed.
  • cargo clippy -p toyos-build -p toyos-metal --all-targets with the adopted lints and -D warnings: clean.
  • sourcegate: 9 passed.
  • The harness --list shows both rows and tmp_empty_files as a member, and no tmp_holds_empty_files_by_what_each_costs.

The evidence below was taken before these rebases, at the heads it names.

  • cargo run -- --ci host at edb33219f on a macOS host. It still covers bf8f66e5e: what changed since is a guest binary, which no host suite builds, and an issue record, which sourcegate's tests pass at bf8f66e5e. Result: exit 1, 1 of 114 steps red. The red step is the build system's cargo test --lib, with 386 passed and 1 failed: the_caller_s_environment_reaches_neither_the_build_nor_the_key. That is issues/the-llvm-environment-test-breaks-git-on-macos.md, red on every macOS host, and its file build/llvm.rs is not touched here. Every other step is green, including the harness's own checks and clippy. Log.
  • Round 2 at d0b51ab9a re-ran what its changes can affect. cargo test --test toyos-checks: 47 passed. cargo clippy -p toyos-build --all-targets with the adopted lints and -D warnings: clean. cargo test --lib -- sourcegate: 10 passed. The rest of the --ci host run above is untouched by round 2, which changes only tests/ and issues/.
  • The judge checks a_shared_member_is_judged_by_its_own_end and a_shared_boot_ends_by_its_lists_reboot, each with negative controls. Five mutations each red a check: the speaker filter's source half dropped, its pid= half dropped, the kernel's Rebooting. not required, guest-reset not required, and Rebooting. accepted from any speaker. The restored tree is green (round 2 logs and patches; round 1's single control is here).

Owed on metal

Nothing new. This PR changes no metal boot, member or judge. The build/stage split was verified by inspection only, because staging is suspended while the benches are down.

Unresolved

🤖 Generated with Claude Code

@Japabu

Japabu commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Review of #1271 at bf8f66e5e (base e10b6fd49, current origin/main)

Architecture

The ownership is right. Membership stays in shared_metal and metal::batches. Staging is one function, metal::stage, which metal's build and the guest rows both call. The judge is a host-side reader of the runner's own markers, with a host check beside it. Restoring a guest run is the orchestrator's decision, made under the owner's in-principle yes (issues/toyos-runs-on-arm64.md:74-77). It touches no design in docs/, and limiting it to x86-64 follows the 2026-11-04 ruling. Net change: +351/−49. None of it is shipped code: the harness grows by about 200 lines and the checks by one test (+40), and one guest binary and two records change. The new await_end repeats part of run_test's liveness loop, which is justified: its end is QEMU's exit, not a marker, and await_guest's Liveness would end a list on any member that is quiet for 15 s.

Is metal unchanged? Yes, by diff. stage is main's build body down to the staged files. The only differences are that home and bound_ms are now parameters, and build passes at(dir, label) and batch.bound_ms(). The derive, the per-job binaries, batch.files and reached are byte-for-byte the same. extra and config are now read through Staged.

Checks I ran at bf8f66e5e, macOS host:

  • cargo test --test toyos-checks: exit 0, 46 passed, including a_shared_member_is_judged_by_its_own_end.
  • cargo test -p toyos-metal: exit 0, 73 passed.
  • cargo clippy -p toyos-build --all-targets with ADOPTED and -D warnings: exit 0.

The --ci host evidence at edb33219f still applies, because git diff edb33219f bf8f66e5e touches only readdir_bound.rs and the new issue. The negative-control patch disables both halves of the speaker filter. It goes red on the pid= half.

Answers to the brief

  • Can the judge be fooled?

    • Interleaved or spliced lines can only cause a false red. parse reads the first head, and a lost TEST_END reads as "never ended".
    • A job that prints another member's end line, a longer name's end, or a job's own ring line is filtered out by the pid= rule. The check exercises this.
    • A member that never ends goes red. On KVM the host ceiling fires first; on TCG, at the cap, the in-guest deadline does. A killed job is code() == None, so the runner prints exit=-1, and the members behind it are unreached.
    • A kernel death after the last member goes red when it is written: kernel_death scans the whole console, and ceiling_verdict covers a death that halts. A silent one passes. See B1.
  • Can a member pass vacuously under QEMU? I found none that passes because bench hardware is absent: RUST_SKIP excludes every binary that needs it. Some members do prove less on QEMU, and the rows' green does not stand in for metal on them:

    • The DRIVEN_AND_SHARED members whose "shared run is each bench's verdict": vector_state, std_threads, affinity_pins, user_copy_faults and spawn_returns_the_census. These run on QEMU's CPU. vector_state takes vector::width(), so under TCG's qemu64 it covers SSE state only.
    • On shared-debug, the SELFTEST_PARAMS self-tests are armed and judged by nobody. The bench METAL rows that ride both boots are absent.

    The PR says the second point.

  • Is ×8 only a liveness bound? Yes. No member reads the runner's bound, and no correctness verdict depends on when a member ends. On KVM, the host ceiling (about 102 s, scaled) ends a stuck list long before the 817 s in-guest bound. At the cap under TCG the two bounds coincide, and the in-guest one fires first because it counts from the guest's boot.

  • Does the registration declare where it runs? Yes (tests/toyos.rs:502-511).

  • Does readdir_bound still check its claim? Yes. std lists in 64 KiB batches (sdk/toyos/src/files.rs:399), and the names alone are 128,000 bytes, with a const assertion. That is a wider margin than the old 72,000.

  • Is the record's arithmetic right? Yes. memory_total/8/4096 = 119,180, then (119,180 − 8,192)/2 = 55,494, then /16 = 3,468, and 3,905,302,528/3,468 ≈ 1,126,096. make_room and drop_oldest return early for a memory volume, as the record says. The frontmatter is wrong (B2).

  • Does the unmeasured nightly TCG cost matter? Not for landing. The nightly is frozen, and its exit moves the guest lanes to the guest runner, so resumption runs and measures these rows anyway. The cost is real, though. The TCG lane will build all 180 guest binaries and 143 corpus cases in-suite. A stuck testcases list there can hold a lane for about 14 min before its in-guest bound fires, or about 27 min at the host's backstop, against guest.yml's 60-minute job. See S6.

Findings

BLOCKER B1 — tests/common/qemu.rs:1845-1884 (await_end) and tests/toyos.rs:2302-2320. Any successful QEMU exit is taken as "the list's reboot", but under -no-reboot a reset, a power-off and a triple fault all exit with status 0. The harness already states this contract in tests/common/power.rs:30-32, and power::ended meets it by waiting for the kernel's last word and QMP's shutdown reason.

The consequences:

  • A silent kernel reset after the last member ends is green. A triple fault in the stop path is one example.
  • So is any ending that is not the list's reboot.
  • --- the boot did not end by its list's reboot asserts something the code never checks.

The fix is cheap. Count the end only when the console carries the kernel's Rebooting. (bootlog::REBOOTING, which power.rs:106 already reads on this profile's console). Optionally also open QmpShutdown and require guest-reset, as power::ended does. Add one fixture case to the check.

BLOCKER B2 — issues/tmp-holds-one-file-per-megabyte-of-ram-whatever-the-files-hold.md:1-5 has no severity. issues/README.md says an absent severity means no flag applies, and it defines security as including "one client exhausting a resource other clients need". That is this defect: the PR's own run had one member deny /tmp to 14 later ones. The tracker's analogues all carry severity: [security]: one-program-that-fills-a-servers-table-holds-every-other-client-out.md, netstack-lookup-slots-have-no-per-client-share.md and one-diskserver-session-can-hold-every-device-slot.md. Without the flag, the security query never finds this defect.

BLOCKER B3 (evidence) — Guest run 43102627714 at bf8f66e5e is still queued. The rows' declared primary target, KVM on the guest runner, has no result, and the TCG figures are development runs only. Landing needs that run green, or each red triaged with its independence established. When it lands, its duration also tells whether the 102 s ceiling is reasonable for KVM. The PR body states this honestly.

SUGGESTION S1 — tests/toyos.rs:2324,2336. "Unreached" is a state carried as a sentinel string and recovered by == on the message. A small enum (Passed, Failed(String), Unreached) would make the partition and the check's assertion typed.

SUGGESTION S2 — tests/checks.rs:1148-1167. No fixture line exercises the source != test-runner half of the filter. The kernel exit: … code=0 line could never match ===TEST_END, so dropping only the source condition passes the check. Either give test_rs_never a kernel- or other-program-headed ===TEST_END … exit=0===, or drop "or a kernel record's" from the doc comment.

SUGGESTION S3 — tests/toyos.rs:8538-8553. build_shared_bins now runs inside the 12-wide suite, and compile_c_tests and check_not_run replace the process-wide panic hook for the whole corpus compile. Meanwhile any other lane's panic loses its panicked at file:line line; catching still reports the payload. The mechanism was there before, but only metal and debug mode ran it. A Result-returning compile and link path would remove the hook swap. Fix it, or record it.

SUGGESTION S4 — issues/tmp-holds-…md:

  • :39: the owner is a code site. Name an owner, as a-pager-holds-a-fixed-capacity-…md does (the process-memory track, or the orchestrator).
  • :43-46: the exit prescribes a guest test. The file server's object use already has a Pages trait to substitute in a host module test (system/fileserver/src/pages.rs:41). Name the cheapest tier, and keep a guest test only for whatever the kernel's per-object charge needs.
  • :11: "half of an eighth of RAM, less LOG's and BOOT's pages" reads as RAM/16 − 8,192 pages. The formula two paragraphs down, (RAM/8 − 8,192)/2, is the right one.

SUGGESTION S5 — issues/the-t14-reboots-through-ubuntu-for-every-test.md:11-14 says "which stage the same job lists". The rows stage the same members. They leave out the bench rows' jobs on both boots, and their bound is ×8. The PR body's "one derived config, one job list in the same order" has the same overstatement; the body is the orchestrator's to correct.

SUGGESTION S6 — Add one line to issues/the-nightly-is-frozen-so-nothing-runs-the-guest-matrix.md: the unfiltered suite now carries both shared rows, with an in-suite build of every guest binary and corpus case, and their TCG lane cost is unmeasured. Then resumption measures it on purpose rather than finding it at the 60-minute timeout.

Verdict

SEND BACK. B1 is a one-line judge fix with a fixture case, and B2 is a one-line frontmatter fix. B3 needs no code: it clears when run 43102627714 lands green or its reds are triaged.

@Japabu

Japabu commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Round 2 is at d0b51ab9a, fast-forward from bf8f66e5e:

  • B1, 16f9e6376. judge_shared counts the end only where the kernel's own record says Rebooting. (bootlog::REBOOTING) and QMP's SHUTDOWN names guest-reset. The row opens QmpShutdown as soon as the boot is up. A new check, a_shared_boot_ends_by_its_lists_reboot, stages five reds: a silent reset after the last member, a program's Rebooting., a power-off, a stop QMP never named, and a wait that never saw QEMU exit.
  • B2, d0b51ab9a: severity: [security].
  • B3: run 43102627714 at bf8f66e5e is still queued, kept for the members' KVM triage. One run at the final head follows it.
  • S1: Member::{Passed, Failed, Unreached}.
  • S2: the fixture adds kernel-headed and fileserver-headed ===TEST_END test_rs_never exit=0=== lines.
  • S3, 7a0939181: compile_c, compile_own_c and link_toyos return their refusal, with the same words, so NOT_RUN's quoted stages still match; that was checked by a development run of both rows, whose check_not_run passed. The hook swaps, catch_unwinds and panic_message are gone.
  • S4: the owner is the process-memory track, and the formula sentence is fixed. The exit names a host module test over the Pager trait's fake (system/fileserver/src/pages/tests.rs) as the cheapest proof.
  • S5 and S6: both records are updated.

Negative controls: five mutations, each red, with the restored tree green: https://github.com/ToyOSOrg/evidence/tree/458f382bf9f2f94865a62605bea01d8ccfc3772e/pr-1271/round-2

@Japabu

Japabu commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Round 2 review of #1271 at d0b51ab9a (previous bf8f66e5e, base e10b6fd49)

Architecture

Round 2 keeps round 1's ownership. It also turns the end of a shared boot into a pure verdict. shared_members collects the console and how QEMU stopped, and judge_shared (tests/toyos.rs:2318) decides from those two inputs alone. That is why a host check can stage each ending.

The row applies power::ended's contract: the kernel's last word, plus QMP's reason. It does not copy power::ended's control flow, which waits on a live marker. Reading after the exit suits a list whose end is QEMU's exit.

S3 is a real simplification. A process-wide hook swap, two catch_unwinds and panic_message give way to Result on compile_c, compile_own_c and link_toyos.

Size: +175/−122 since round 1. Of that, tests/ is +149/−109 (harness and checks) and issues/ is +26/−13. The whole branch is +465/−110, still with no shipped code. No design decision changes, so the manual needs nothing.

Checks I ran at d0b51ab9a, macOS host

  • cargo test --test toyos-checks: exit 0, 47 passed, including a_shared_member_is_judged_by_its_own_end and a_shared_boot_ends_by_its_lists_reboot.
  • cargo test -p toyos-metal: exit 0, 73 passed.
  • cargo clippy -p toyos-build --all-targets with ADOPTED and -D warnings: exit 0, no warnings.
  • cargo test -p toyos-build --lib -- sourcegate: exit 0, 10 passed.

The round-2 mutation patches apply at this head's lines. Each log shows exit 101 at the check that names the case. m4's log omits the name of the case that went red. By the order of the red calls, only the power-off or the stop QMP never named can red it, and either shows that guest-reset is required.

Answers to the brief

B1 is closed. I traced every ending other than the list's reboot:

  • A silent reset or triple fault after the last member: QMP says guest-reset, but the kernel never said Rebooting.. Red (fixture).
  • A power-off: guest-shutdown. Red (fixture). A host kill gives host-signal or a failed exit, also red.
  • A program printing Rebooting.: parse reads the speaker only inside the head's bracket, so text after a program's head cannot pass as a kernel record. Red (fixture and m5).
  • QMP never reporting: reason() is None. Red (fixture). A QMP that cannot connect panics into catching, also red. An event lost before the client connected also reads None, so it can only cause a false red.
  • A kernel death after Rebooting.:
    • One that writes a DEATHS word is red through kernel_death. That scans the whole console, which await_end drains until QEMU's stdout closes.
    • One that halts never lets QEMU exit, so the ceiling reds it.
    • The panic-reboot path writes its panic lines and never says Rebooting., so it fails both checks.
    • A watchdog reset says no Rebooting. either.
  • A member that reboots mid-list: the members after it are Unreached. If it is the last member, its own TEST_END is missing. Red either way.
  • Residual, not a finding: a silent triple fault inside the kernel's own reset path, after Rebooting., looks to QEMU exactly like the reset itself: both are guest-reset. x86's reset writes the declared reset register and halts; there is no triple-fault fallback. power::ended accepts the same limit, and a reset that hangs still reds at the ceiling.

The typed members leave the verdicts unchanged. Passed, Failed and Unreached map one-to-one onto the old Ok, Err(message) and UNREACHED sentinel. No failure message could equal the sentinel, so the partition is the same and the reds keep their order. The only text change is the wording of the unreached line.

S3: the refusal path reports everything it did. first_error already returned one line, and the link refusal is joined into one. So the strings that NOT_RUN's contains reads are identical to what panic_message passed on. The corpus expectations read program output, which this change does not touch.

Some panics used to be absorbed by the per-case catches: a clang that cannot run, an unreadable linked file, a scratch directory that cannot be made. Now they leave compile_c_tests through the scoped join, and check_not_run directly. Both paths end where the old code's aggregated panic! and assert! already ended: in the row's catching (tests/toyos.rs:4971), or on the main thread in metal and debug mode. No panic gets further than before. libc_children returns the same text as an Err, and its row reports it as catching reported the panic.

Records.

  • B2 is fixed.
  • S4: the owner matches a-pager-holds-a-fixed-capacity-…md. The formula sentence now matches pager (system/fileserver/src/main.rs:283-291) and the arithmetic. The named fake exists (system/fileserver/src/pages/tests.rs:161) and enforces an object cap (Model::objects), so the host test the exit names can show the fix.
  • S5 and S6 are right.

Findings

BLOCKER B3 (evidence; open, unchanged) — Run 43102627714 at bf8f66e5e is still queued.

  • Neither row has a KVM result yet.
  • Only the development TCG runs have exercised the new end check on a real console, and the PR itself labels those as not evidence.

This clears when the final-head run is green, or when each red is triaged with its independence established.

BLOCKER B4 — tests/common/compile.rs:136-137. The comment gives the reason the link refusal is one line: "the harness reads a failed link for the symbol a declared case stops on, and LLD reports each undefined symbol on a line of its own." That was true while panic_message cut the payload to its first line. 7a09391 removed that cut: check_not_run now runs contains over the whole Err, so a symbol on a later line would match too. The stated reason is stale, and it would mislead whoever next touches the join. Delete it, or give the reason that still holds, if any: one line per case in compile_c_tests's list and in check_not_run's report. The fix is comment-only, so root's rule asks for no new integration run.

SUGGESTION S7 — tests/toyos.rs:2756. check_not_run's bare compile::c_sysroot() existed to run before the hook went in. It now runs on the same thread as the first compile_c, which makes the same call and raises the same panic, so it can go. compile_c_tests's call at :2703 still earns its comment: it keeps a sysroot failure on the row's thread, in its own words, rather than in a worker.

SUGGESTION S8 — tests/toyos.rs:2720. S3 makes w.join().expect("a compile thread") reachable. A clang that cannot run now panics in a worker. The row's reason then reads a compile thread: Any { .. }, and the real message appears only on stderr. .unwrap_or_else(|e| std::panic::resume_unwind(e)) would carry the worker's own payload to catching.

SUGGESTION S9 — tests/common/qemu.rs:1842-1844. await_end's doc still names "a job list's reboot" as what it waits for. After B1, its Ok means only that QEMU exited 0, and judge_shared decides whether that was the reboot. Saying so in the doc would remove the last trace of the conflation B1 fixed.

Verdict

SEND BACK. B1 and B2 are closed, and S1–S6 are addressed. B4 is a comment-only fix that needs no rerun. B3 clears with the final-head guest run, green or triaged. It needs no further review round unless triage changes code.

@Japabu

Japabu commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Round 3 is at 53f739027, fast-forward from d0b51ab9a:

  • B4: the comment on link_toyos' one-line refusal now gives the reason that still holds: one line per case in the corpus compile's list and in NOT_RUN's report.
  • S7: check_not_run's bare c_sysroot() is gone.
  • S8: the compile join uses resume_unwind, so a worker's own panic reaches catching.
  • S9: await_end's doc says Ok is any exit 0, and that whether it was the reboot is the caller's to judge.

cargo test --test toyos-checks: 47 passed. cargo clippy -p toyos-build --all-targets with the adopted lints and -D warnings: clean, re-checked after touching the changed files.

B3 is open. Run 43102627714 at bf8f66e5e is still queued for the members' KVM triage, then one run at the final head.

@Japabu
Japabu force-pushed the shared-boot-guest-rows branch from 53f7390 to a263741 Compare November 11, 2026 22:33
Japabu added a commit that referenced this pull request Nov 12, 2026
The `testcases` boot owed on metal for #1252 now names the four shared-boot
members whose rebased forms have only been compiled: #1261's
`poll_belongs_to_its_handle`, which connects through its connector where it
opened a one-entry namespace, and `messages_are_whole`, `moved_rights` and
`mutual_kill`. No guest row runs them until #1271's shared-boot row lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Japabu and others added 8 commits November 12, 2026 04:32
Both metal benches are down, and the shared boots' members (every
discovered Rust test binary, the C corpus and `LAST_MEMBERS` on
`testcases`, `ACTUATOR_TESTS` on `shared-debug`) ran on nothing else
since the shared block left the guest suite.

Two machine tests, `testcases_members_exit_0` and
`shared_debug_members_exit_0`, run those boots on QEMU x86-64's q35.
Each takes its boot from `shared_metal`, the one membership the metal
profile reads, batches it with `metal::batches` and stages it with
`metal::stage`, which `metal::build` now calls too: the derived config,
its job list in the bench's order, the C corpus's `ccheck` links and
every file the members reach. The kernel and parameters are the
`SharedBoot`'s own. The guest runs the list to its `reboot`, which
`-no-reboot` makes QEMU's exit, and each member is judged by the
runner's own `===TEST_END` line for it, read only under the runner's
head without `pid=`, so a job's line or a longer name's end does not
answer for it. A kernel death anywhere on the console reds the row.

The runner's list bound is the bench's times `HOST_SCALE_CAP`: under
QEMU the harness's ceiling, paid for the host the boot measured, ends a
list that does not end itself. An emulated `testcases` list ended
116 s into its boot in development, past the bench's 102 s.

`kernel_of` takes the test kernel named beside actuators, which
`shared-debug` declares. A filter naming a shared member is still
refused, and now says a shared boot runs whole under its row.

Metal's boots, members and judges are unchanged; the rows riding those
boots on the T14 stay off the guest rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It made 6,000 empty files in `/tmp` so that their names alone would
exceed a 64 KiB listing reply. On a 4 GiB guest the memory volume's
pager serves about 3,468 objects, so the member was refused its 3,465th
file and panicked with every file it had made left behind. On a shared
boot that left `/tmp` full for the rest of the list: every later member
that writes `/tmp` failed with it, seven Rust members and seven corpus
cases.

It now makes 640 files of 200-byte names. The names alone are 128,000
bytes, nearly twice a 64 KiB reply, which a const assertion holds, and
its 641 objects are under the 1,024 any pager serves, whatever the
machine's memory. A refused create removes what it made before it
fails. The cap itself is recorded in
`issues/tmp-holds-one-file-per-megabyte-of-ram-whatever-the-files-hold.md`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The MEMORY role's pager is sized as DATA's is, capacity/16 objects, and
its anonymous objects are never dropped to make room, so the object
count caps `/tmp`'s files outright: 3,468 on a 4 GiB guest, one per
1,126,096 bytes of memory, empty files included. Found by the shared
boot's guest row, where `readdir_bound` was refused its 3,465th empty
file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`compile_c_tests` and `check_not_run` replaced the process-wide panic hook
for the whole corpus compile and caught each case's panic. Now that the
shared guest rows compile the corpus inside the twelve-wide suite, every
other lane's panic lost its location line meanwhile. `compile_c`,
`compile_own_c` and `link_toyos` return the refusal they used to panic
with, word for word, so `NOT_RUN`'s quoted stages compare as before, and
the hook, the `catch_unwind`s and `panic_message` go. A clang that cannot
be run at all still panics: no case is at fault for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…typed

Any successful exit of a `-no-reboot` QEMU was taken as the list's
`reboot`, yet a reset, a power-off and a triple fault all exit with
status 0, so a silent reset after the last member read green.
`judge_shared` now counts the end only where the kernel's own record
says `Rebooting.` (`bootlog::REBOOTING`) and QMP's `SHUTDOWN` event names
`guest-reset`, as `power::ended` holds. The row opens `QmpShutdown` as
soon as the boot is up, since QMP delivers no event from before its
client connected.

The row's verdict over what the boot left is a function of its own,
and the check stages the cases: a silent reset after the last member, a
`Rebooting.` a program says, a power-off, a stop QMP never named and a
wait that never saw QEMU exit are each red. A member is `Passed`,
`Failed` or `Unreached` rather than a sentinel message, and the member
check stages a `===TEST_END` under the kernel's and another program's
head, so the speaker filter's source half is held as its `pid=` half is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… rows

The /tmp cap lets one client take every file other clients need, which
is `severity: [security]` by the tracker's definition. Its owner is the
process-memory track, its formula sentence matches the arithmetic, and
its exit names the cheapest proof: a host module test of the file
server over its `Pager` trait's fake.

The T14 track says the guest rows stage the same members in the same
order, not the same job lists: they leave out the bench rows' jobs. The
frozen nightly's record says the unfiltered suite now carries both
shared rows and an in-suite build of every guest binary and corpus
case, whose TCG cost resumption measures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… holds

A clang that cannot be run panics in a corpus compile worker, and the
join's `expect` replaced that payload with `a compile thread: Any`; the
join now resumes the worker's own panic, so the row's reason carries
it. `check_not_run` no longer makes the C sysroot first: that call
existed to run before a panic hook went in, and its first `compile_c`
makes the same call on the same thread.

`link_toyos`' one-line refusal is one line of the corpus compile's list
and of `NOT_RUN`'s report; nothing cuts it to its first line any more.
`await_end` answers `Ok` for any exit 0, which a reset, a power-off and a
triple fault all are under `-no-reboot`; whether it was the list's
reboot is its caller's to judge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…to main's

#1273 made /tmp hold its files by what each costs and left two
follow-ups for this branch. `testcases_members_exit_0` judges
`tmp_empty_files` on the same 4 GiB guest as the dedicated x86-64 row
`tmp_holds_empty_files_by_what_each_costs` did, so that row and its
function go. Its `DRIVEN_AND_SHARED` entry stays, now naming only the
AArch64 row that still drives the binary.

`issues/tmp-holds-one-file-per-megabyte-of-ram-whatever-the-files-hold.md`
goes: #1273 lifted its count, and what is open of it, one client filling
the server and denying the others, is
`issues/one-client-can-fill-the-tmp-servers-memory-and-deny-every-other-client.md`.
`readdir_bound`'s comment no longer counts its files against a pager's
object count, which a memory volume no longer keeps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Japabu
Japabu force-pushed the shared-boot-guest-rows branch from a263741 to ba2aacb Compare November 12, 2026 02:34
@Japabu

Japabu commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator Author

Orchestrator: landing under the benches-down rule. Review r2 closed B1/B2; B4/S7–S9 fixed. Guest runs: triage 43102627714 at bf8f66e and landing 43112734198 at a263741 — both rows green on KVM both times (testcases 236/236, shared-debug 9/9). Head ba2aacb adds only #1273's follow-ups (the dedicated x86 /tmp row dropped in favour of the shared member; the superseded record deleted) and the registry rebase. From here the T14's shared members run as x86-64 guest rows.

@Japabu
Japabu marked this pull request as ready for review November 12, 2026 02:50
@Japabu
Japabu added this pull request to the merge queue Nov 12, 2026
Merged via the queue into main with commit 3c5c555 Nov 12, 2026
10 checks passed
@Japabu
Japabu deleted the shared-boot-guest-rows branch November 12, 2026 03:10
Japabu added a commit that referenced this pull request Nov 12, 2026
Main 3c5c555 (#1271) makes the testcases list a guest row,
`testcases_members_exit_0`, which judges each member by its own exit;
pagerprobe and the two served-file members this branch adds are members
of that list. No file is changed on both sides.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Japabu added a commit that referenced this pull request Nov 12, 2026
The `testcases` boot owed on metal for #1252 now names the four shared-boot
members whose rebased forms have only been compiled: #1261's
`poll_belongs_to_its_handle`, which connects through its connector where it
opened a one-entry namespace, and `messages_are_whole`, `moved_rights` and
`mutual_kill`. No guest row runs them until #1271's shared-boot row lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Japabu added a commit that referenced this pull request Nov 12, 2026
… and std's word for too big is E2BIG's

#1271 made the testcases shared boot a guest row (testcases_members_exit_0),
which runs every Rust member and the C corpus on QEMU, so the records'
"no guest row runs the corpus" and "the member runs only on metal" were
false. The full-table record now owes metal only the testcases-watchdog
boot, which no guest row runs. The record of the stream state's TCP half
owes stream_state_is_one's run at main to that guest row, since what it
holds is no bench's.

std answers Failure::TooBig ArgumentListTooLong, std's own kind for E2BIG,
which libc answers, rather than InvalidInput.

The MAX_ARGS test's doc says its names table is MAX_NAMES bytes, MAX_NAMES's
doc keeps only its capacity, and the record of a dropped empty argument
names startup::bytes as the writer that skips it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Japabu added a commit that referenced this pull request Nov 12, 2026
… and std's word for too big is E2BIG's

#1271 made the testcases shared boot a guest row (testcases_members_exit_0),
which runs every Rust member and the C corpus on QEMU, so the records'
"no guest row runs the corpus" and "the member runs only on metal" were
false. The full-table record now owes metal only the testcases-watchdog
boot, which no guest row runs. The record of the stream state's TCP half
owes stream_state_is_one's run at main to that guest row, since what it
holds is no bench's.

std answers Failure::TooBig ArgumentListTooLong, std's own kind for E2BIG,
which libc answers, rather than InvalidInput.

The MAX_ARGS test's doc says its names table is MAX_NAMES bytes, MAX_NAMES's
doc keeps only its capacity, and the record of a dropped empty argument
names startup::bytes as the writer that skips it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant