Skip to content

A metal boot's list bound and deadline follow from who rides it, at 300 and 100 ms a member: shared-2 and the three bounds rows ride shared, 25 boots to 23 - #794

Merged
Japabu merged 3 commits into
mainfrom
wt/toyos-oneboot-b3a
Oct 9, 2026
Merged

Japabu merged 3 commits into
mainfrom
wt/toyos-oneboot-b3a

Conversation

@Japabu

@Japabu Japabu commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Stage 3a of step B of the one-boot plan for the T14's metal rows: the harness mechanics that let more rows share a boot. Stage 1 was #785, stage 2 #789. Head 49e12f23b, based on main at 5055dc4aa. main has moved since and is not merged in: the merge is the queue's, since it moves every image's bytes.

49e12f23b differs from d6d008e88 by one issue file (git diff d6d008e88 49e12f23b --stat: issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md, +42 −15). The gates and the T14 reading below are d6d008e88's. The three images staged at 49e12f23b do not have the sha256 of the three flashed at d6d008e88, so the T14 reading is another head's and three boots are owed at this one: see "The T14".

--metal --list before (5055dc4aa): 64 registrations and 229 shared members over 25 boots. After: 61 and 232 over 23. shared-2 and testcases-bounds are gone; no test is.

What changed, per decision

  • A boot's rows' jobs run before its members, and its last job behind both (batches). A row that measures keeps the machine its own jobs gave it, and what the members write to the log lands behind every row's. A shared boot now rides a boot on the terms two arms share one: it is refused, by name, where it disagrees with a row about the config, the parameters or the kernel build.
  • The list's bound is derived, and every image's runner is told it. toyos_tco::list_bound_ms is JOB_BOUND_MS for the rows' jobs plus what each member adds. metalimage::derive writes it as the runner's first argument, --bound-ms=, which test-runner already took; a boot no member rides is told the 60 000 ms the runner took by default, so the image says its bound either way.
  • A member's allowance is 300 ms a Rust test and 100 ms a C case (RUST_MEMBER_MS, C_MEMBER_MS; they were 860 and 260). This is the design's decision 9, which the owner ruled: yes, as recommended. The evidence is the T14 reading at d6d008e88 below: 81 ms a member on shared, 15 on ccorpus and 32 on shared-debug, so the ruled values stand 3.7, 6.7 and 9.4 times over the mean. The mean is not the member: the slowest Rust member took 2955 ms and the slowest C case 1517 ms, and the bound holds on the list's sum (the issue below). They are numbers a reader checks, and no test holds them.
  • boot-deadline= is twice the list's bound (toyos_tco::wedge_bound_ms). A boot that stages its own wedge keeps STAGED_BOUND_MS. WEDGE_BOUND_MS is deleted: after the derivation its only readers were its own assertions and tests, which are spelled wedge_bound_ms(JOB_BOUND_MS). HARD_LOCKUP_BOUND_MS stays, tracked as unread in issues/hard-lockup-bound-ms-is-read-by-nothing-but-its-own-assertion.md.
  • toyos-metal waits for a boot by the deadline its image carries: judge_arms answers it, and refuses a bound nothing can read as it refuses none, since the kernel panics on one. return_secs took the maximum of five constants. --wait-secs is deleted: nothing passed it.
  • Deleted: members, members_fitting, chunk_name, sized. select takes each shared boot once: whole for its boot word, or only the members a name word is part of, with those members' files and links and no other's.
  • shared-2 folds into shared, and the three bounds programs are its last three members, declared once, on LAST_MEMBERS in tests/toyos.rs: behind every discovered member, in the order testcases-bounds ran them. They are off RUST_SKIP; discover_rust_tests skips both lists. A name taken off LAST_MEMBERS and put nowhere is a discovered member again, so that one edit leaves it on a machine. Not every state does: a name moved from LAST_MEMBERS onto RUST_SKIP runs nowhere, as any member put there with no driver does, and that is two lines a reader of the diff sees. The process_bound_* rows, the testcases-bounds arm and both dead labels' rows in the machine's record under tests/metal/ are deleted.
  • The judge prints where a shared boot's last record came against its list's bound, on the runner's own clock: the reading that shows a derived bound is long enough, on every run from here.
  • What is left of the compromise is recorded: issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md. The allowances are ruled and not derived, the bound grows with every member and has no ceiling, and no reading holds either. It carries both T14 readings with the worst member beside the mean: on shared five of 88 members are past 300 ms and make 5616 of the members' 7115 ms, on ccorpus one case is 1517 of 2031 ms and read 1141 ms one boot earlier, and a list made only of members as slow as the slowest passes its bound at the 23rd. Its exit: allowances derived from what the T14 measures of a list's total over its members by a stated factor, with the judge redding a list past half its bound; and stage 3b's merged boot reading its list's last record within half its list bound on the T14, the pair read from the kernel's own lines. No ceiling is named.

What each boot is armed with

The kernel's deadline counts from its arm, about 60 ms into the kernel; the runner's bound from the kernel's zero. The kernel's hard-lockup bound is half its deadline, so it moves with it. --metal --list at d6d008e88, and metal::return_secs's arithmetic for the wait (the deadline in whole seconds, rounded up, and 300 s):

boot --bound-ms= boot-deadline= hard-lockup bound toyos-metal waits
shared (88 members) 86 400 (main: 60 000, in two boots) 172 800 (main: 120 000) 86 400 (main: 60 000) 473 s (main: 420)
ccorpus (137) 73 700 147 400 73 700 448 s
shared-debug (7, and ten rows) 62 100 124 200 62 100 425 s
deadlinewedge, hardlockup, usbload 60 000, now said 10 000, unchanged 5 000, unchanged 360 s (was 420): the firmware's 60 s is the longest bound that can still be running
the other 17 60 000, now said 120 000, unchanged 60 000, unchanged 420 s, unchanged

The three images staged at d6d008e88 and again at 49e12f23b say the same: each staging's lines read a list bound of 86400 ms ... a boot deadline of 172800 ms, 73700 ... 147400 and 62100 ... 124200, and --bound-ms=86400, 73700 and 62100 are first in the three derived configs' runner arguments.

By the same formula the boot stage 3b makes of shared, ccorpus and testcases (88 Rust members, 137 C cases) arms a list bound of 100 100 ms and a deadline of 200 200 ms, waited 501 s. At 860 and 260 it was 171 300, 342 600 and 643 s.

The T14

At eff8b20ee, with the allowances at 860 and 260 (the orchestrator's reading; each image's sha256 checked against the request in the command that flashed it). Three toyos-metal runs, each exit 0. Judged with cargo test --test toyos-build -- --metal --metal-readback <dir> boot:shared boot:ccorpus boot:shared-debug: exit 0, 242 passed, 0 failed, 3 boots (10 registrations and 232 shared members).

boot members the kernel's own lines: boot deadline / hard lockup the list's last record against its bound ms a member
shared 88 271360 ms / 135680 ms 8264 ms of 135680 ms 80
ccorpus 137 191240 ms / 95620 ms 2864 ms of 95620 ms 12
shared-debug 7 132040 ms / 66020 ms 1380 ms of 66020 ms 32
  • Each deadline was armed as derived, read from the kernel's own boot deadline: and hard lockup: lines.
  • No the job list ran past its bound in any kernel log, and each loader's pass after the reset reads DONE, not WEDGED: neither bound fired.
  • shared's list ends with the three former bounds programs as members, all exit=0; the ten shared-debug rows PASS.

At d6d008e88, with the allowances at 300 and 100 (the orchestrator's reading; worktree clean before and after, each image's sha256 checked against the request in the command that flashed it). Three toyos-metal runs, each exit 0. Judged with cargo test --test toyos-build -- --metal --metal-readback <dir> boot:shared boot:ccorpus boot:shared-debug: exit 0, 242 passed, 0 failed, 3 boots (10 registrations and 232 shared members). Each job list is the one eff8b20ee ran (--metal --list's 23 job lists at the two heads compare equal).

boot members the kernel's own lines: boot deadline / hard lockup the list's last record against its bound ms a member
shared 88 172800 ms / 86400 ms 8308 ms of 86400 ms (9.6 %) 81
ccorpus 137 147400 ms / 73700 ms 3223 ms of 73700 ms (4.4 %) 15
shared-debug 7 124200 ms / 62100 ms 1389 ms of 62100 ms (2.2 %) 32
  • Each pair is armed as now derived, read from the kernel's own boot deadline: and hard lockup: lines.
  • No the job list ran past its bound in any kernel log; each loader's pass after the reset reads DONE; no WEDGED: neither bound fired. No FAIL in the judge's log.
  • A dead shared boot holds the machine for 172.8 s where eff8b20ee would have held it for 271.4 s.

At 49e12f23b no machine has been read. The three boots were staged once more to show the images unchanged by an issue file, and they are not: cargo test --test toyos-build -- --metal --metal-readback <dir> boot:shared boot:ccorpus boot:shared-debug, exit 2, which is "staged".

boot sha256 flashed at d6d008e88 sha256 staged at 49e12f23b
shared c2cb6c860d6c6d96191563b5dd73037e10c80aea83843d0835265f6af3f52c9b 46d82e2008ae662c08a5d31db89d3aa159f1337ed00ece16bf3cb12609ef3b77
ccorpus ae764a8c95a274784a2fb900337f6a25d65d4aa157df647946b1effd523281f2 bd2bf4db80ea7311efbf8439c6405b5d3bf498c2d0fbf99d4875ea2f54a4fc89
shared-debug f116d8071f9d9708f5ef002ccb3373d7a1b90892df67ce1b062a0d1aacffe4b2 ab8d8d6048f50d9932f8d00945cb814f13ef7a4eaddae7332179e79c0e5dbc68

What is equal between the two stagings: the three derived configs, byte for byte (cmp, exit 0 each; their content-named files carry the same names), the job counts and the three armed pairs. What is known to differ: an image carries its commit and that commit's time, which the supervisor prints at boot (build d6d008e88… clean, committed … in the flashed boots' kernel logs), so two heads cannot stage equal bytes whatever the diff between them. Whether that stamp is the whole difference was not measured: the images flashed at d6d008e88 were not kept to compare against. Three boots at 49e12f23b are staged and requested.

The other 20 boots' images differ from main's by one runner argument, --bound-ms=60000, the value the runner took without it; shared's boot exercises that argument's path. The three staged-wedge boots are waited 360 s for instead of 420.

Where the tree differs from the design's text

  • The design counts 26 boots to 24. main is at 25 (A T14 boot whose log is missing parts reds by name, and the two ACPI rows ride testcases again: 26 boots to 25 #785 took testcases-hold), so this stage is 25 to 23.
  • The design names shared-debug in no stage-3a line. Its chunk was a literal 18 with no allowance behind it; the same formula now gives it RUST_MEMBER_MS a member, which moves its deadline from 120 000 to 124 200 ms.
  • Decision 9 is in no stage's list; it is built here because this stage is where the allowances start to set the kernel's deadline.
  • Nothing of stage 4b is built or prepared.

Gates, at d6d008e88

command exit
cargo test --test toyos-checks 0, 39 passed
cargo test -p toyos-build --lib 0
cargo test -p toyos-tco 0
cargo run -- --clippy 0, 24 invocations clean
cargo run -- --ci host 0, 78 steps green
cargo run -- --build-only 0
cargo test --test toyos-build -- --metal --list 0, 61 registrations and 232 members over 23 boots
cargo test --test toyos-build -- virt_reboot virt_smp 0, 3 passed
cargo test --test toyos-build -- --metal --metal-readback <dir> boot:shared boot:ccorpus boot:shared-debug 2, which is "staged": three images, no machine touched

At 49e12f23b, which adds one issue file's lines and no source:

command exit
cargo test --lib sourcegate 0, 10 passed
cargo test --test toyos-build -- --metal --metal-readback <dir> boot:shared boot:ccorpus boot:shared-debug 2, which is "staged": three images, no machine touched

No other gate was run at 49e12f23b.

Host load when the host gate started: 46 / 39 / 38.

The guest suite was not run. No guest test reaches a behaviour this changes. The kernel and the runner link toyos-tco, where two functions were added, one constant neither reads was deleted and two the harness alone reads changed value. No guest boot runs discovered members: discover_rust_tests has two callers, shared_metal and the list of names registered refuses a duplicate in. metalimage::derive is reached by no guest test. The three virt_* tests above boot a kernel built with the changed crate under a runner given --bound-ms=.

High-risk checks

The kernel's deadline is the machine's safety net, and this changes what three boots arm it with.

  • Negative controls: fourteen mutations, each a checked patch, built, run, reported by exit code and restored (comments below; the seven whose file changed in round 2, and the one new, were run again at d6d008e88). Twelve red a named host test; one does not build, on toyos-tco's own const assertion; one is refused by registered before anything boots.
  • Independent oracle: the T14, read at eff8b20ee and at d6d008e88; owed again at 49e12f23b, whose images are other bytes.

The new host test, and what it sees that reading cannot

rows_run_before_members_under_a_bound_the_members_widen (tests/checks/metal.rs) runs batches over three rows and two shared boots: the order of one boot's list, the two bounds of four boots, and the two refusals. Order across two loops and a retain, and arithmetic across two crates, are runtime facts. words_take_rows_members_and_whole_boots is rewritten for select without chunks, and gains the files a member brings. No guest test is added, changed or cut. No dependency is added.

Size

git diff --shortstat origin/main...49e12f23b: 14 files, +466 −288. issues/: +112 −11. tests/checks*: +83 −14. Everything else: +271 −263, of which the tests inside src/metal.rs and src/metalimage.rs are about +30, an estimate.

Unsure of

  • Whether an issue-only commit can ever keep a T14 reading: by the two stagings above it cannot be shown by sha256, since the image names its commit.
  • Whether the three bounds programs leave the machine as they found it has two boots behind it, each with them last. A member placed after them would be the first to find out.
  • last now lands behind a boot's members too. No boot has both today; stage 3b's testcases will, and whether the hold belongs before or after 225 members is that stage's to measure.
  • issues/the-t14-reboots-through-ubuntu-for-every-test.md priced a session at 74.8 s of members by the chunk rule this stage deletes. The sentence now states the derivation; its "two sessions" rested on that price and is the track's to recount.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A

…ne boot holds every shared member

The shared boot was cut in chunks that fit the runner's one 60 s bound, and
three programs that fill a bound of their own process had a boot of their own
for the same reason. The bound is derived now, so neither is a boot.

- batching: a boot's rows' jobs run before its members, and its last job
  behind both. A shared boot rides a boot on the terms two arms share one.
- the list's bound is `toyos_tco::list_bound_ms`: `JOB_BOUND_MS` for the
  rows' jobs and what each member adds. Every metal image's runner is told
  it as `--bound-ms=`, and a boot no member rides is told the 60 s the
  runner took by default.
- `boot-deadline=` is `toyos_tco::wedge_bound_ms` of that bound, twice it;
  `WEDGE_BOUND_MS` is its value for a list with no member. A boot that
  stages its wedge keeps `STAGED_BOUND_MS`.
- `toyos-metal` waits for a boot by the deadline its image carries
  (`judge_arms` answers it, and refuses one nothing can read), where
  `return_secs` took the constants' maximum. `--wait-secs` is deleted: no
  caller passed it.
- `members`, `members_fitting`, `chunk_name` and `sized` are deleted.
  `select` takes each shared boot once: whole for its boot word, or the
  members a name word is part of with their own files.
- `shared-2` folds into `shared`. `abuse_mmap_regions`, `abuse_thread_table`
  and `abuse_dlopen_ledger` are its last three members (`METAL_MEMBERS`),
  the `process_bound_*` rows and the `testcases-bounds` boot go, and the
  record's rows of both dead labels go with them.
- the judge prints where a shared boot's last record came against its
  list's bound, on the runner's clock.

`--metal --list`: 64 registrations and 229 members over 25 boots before,
61 and 232 over 23 after. `shared` is armed with a list bound of 135680 ms
and a deadline of 271360 ms, `ccorpus` 95620 and 191240, `shared-debug`
66020 and 132040; every other boot keeps 60000 and its deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Mutations at eff8b20ee

Each is a checked patch (git apply --check), applied, built and run with cargo test --test toyos-checks metal_ and cargo test -p toyos-build --lib metal, reported by each command's own exit code, and restored (git apply -R); git status --porcelain printed nothing after each. Exit 101 is a failed test, except m3, which does not build.

mutation toyos-checks toyos-build lib what turned red
m1 members before rows 101 0 metal_rows_run_before_members_under_a_bound_the_members_widen: ["m1", "m2", "m3", "tone", "cost", "late", "hold"]
m2 members add nothing to the bound 101 0 the same test, on (62_580, 125_160)
m3 the deadline is the list's bound 101 101 does not build: toyos-tco's const _: () = assert!(WEDGE_BOUND_MS > JOB_BOUND_MS)
m13 the deadline is half again the list's bound 101 101 the same checks test, and every_arm_that_stops_this_machine_is_cleared_and_judged_as_one
m4 the runner is not told its bound 0 101 metalimage::tests::every_derived_config_ends_its_own_boot
m5 a boot word takes only named members 101 0 metal_words_take_rows_members_and_whole_boots
m6 a member brings every file 101 0 the same test
m7 the wait ignores the image's deadline 0 101 the_wait_outlasts_every_watchdog_a_boot_runs_under: 420 where 572
m8 the gate answers a constant bound 0 101 an_image_with_no_bound_on_its_own_boot_is_refused
m9 the last job before the members 101 0 the rows-before-members test: [..., "late", "hold", "m1", "m2", "m3"]
m10 a staged wedge takes the list's deadline 101 101 both the checks test and every_arm_that_stops_this_machine_...
m11 an unreadable bound is flashed 0 101 an_image_with_no_bound_on_its_own_boot_is_refused
m12 a rider on another kernel is batched 101 0 the rows-before-members test's refusal

The runner, as run:

#!/bin/sh
# Each mutation: checked, applied, built and run, reported by exit code, restored.
cd "$1" || exit 2
M="$2"
for patch in "$M"/m*.patch; do
  name=$(basename "$patch" .patch)
  git apply --check "$patch" || { echo "$name: PATCH DOES NOT APPLY"; continue; }
  git apply "$patch"
  cargo test --test toyos-checks metal_ > "$M/$name.checks.log" 2>&1; checks=$?
  cargo test -p toyos-build --lib metal > "$M/$name.lib.log" 2>&1; lib=$?
  git apply -R "$patch"
  echo "$name: toyos-checks EXIT=$checks, toyos-build lib EXIT=$lib, tree: $(git status --porcelain | wc -l | tr -d ' ') changed"
done
m1-members-before-rows.patch
diff --git a/tests/common/metal.rs b/tests/common/metal.rs
index 4fab02f6f..6e1a942cf 100644
--- a/tests/common/metal.rs
+++ b/tests/common/metal.rs
@@ -586,6 +586,18 @@ pub fn batches(
     shared: &[SharedBoot],
 ) -> Result<BTreeMap<String, Batch>, String> {
     let mut out: BTreeMap<String, Batch> = BTreeMap::new();
+    let mut ridden: BTreeSet<&str> = BTreeSet::new();
+    for boot in shared {
+        if !ridden.insert(&boot.boot) {
+            return Err(format!("two shared boots are both named {:?}", boot.boot));
+        }
+        let who = format!("the shared boot {:?}", boot.boot);
+        let batch = ride(&mut out, &who, &boot.boot, boot.config, boot.params, boot.features)?;
+        batch.add(boot.jobs.iter().cloned());
+        batch.members_ms += boot.members_ms();
+        batch.files.extend(boot.files.iter().cloned());
+        batch.links.extend(boot.links.iter().cloned());
+    }
     for (name, decl) in tests {
         let Metal { arms, .. } = decl;
         for arm in *arms {
@@ -604,18 +616,6 @@ pub fn batches(
             }
         }
     }
-    let mut ridden: BTreeSet<&str> = BTreeSet::new();
-    for boot in shared {
-        if !ridden.insert(&boot.boot) {
-            return Err(format!("two shared boots are both named {:?}", boot.boot));
-        }
-        let who = format!("the shared boot {:?}", boot.boot);
-        let batch = ride(&mut out, &who, &boot.boot, boot.config, boot.params, boot.features)?;
-        batch.add(boot.jobs.iter().cloned());
-        batch.members_ms += boot.members_ms();
-        batch.files.extend(boot.files.iter().cloned());
-        batch.links.extend(boot.links.iter().cloned());
-    }
     for batch in out.values_mut() {
         if let Some(last) = batch.last {
             batch.jobs.retain(|job| job != last);
m2-members-add-nothing-to-the-bound.patch
diff --git a/tests/common/metal.rs b/tests/common/metal.rs
index 4fab02f6f..28229df5e 100644
--- a/tests/common/metal.rs
+++ b/tests/common/metal.rs
@@ -612,7 +612,6 @@ pub fn batches(
         let who = format!("the shared boot {:?}", boot.boot);
         let batch = ride(&mut out, &who, &boot.boot, boot.config, boot.params, boot.features)?;
         batch.add(boot.jobs.iter().cloned());
-        batch.members_ms += boot.members_ms();
         batch.files.extend(boot.files.iter().cloned());
         batch.links.extend(boot.links.iter().cloned());
     }
m3-deadline-is-the-list-bound.patch
diff --git a/toyos-tco/src/lib.rs b/toyos-tco/src/lib.rs
index e710779f0..ebd3548ed 100644
--- a/toyos-tco/src/lib.rs
+++ b/toyos-tco/src/lib.rs
@@ -147,7 +147,7 @@ pub const PANIC_BOUND_MS: u64 = 60_000;
 /// bound plus the boot around it — the slowest healthy T14 boot measured is
 /// 6.242 s of kernel time — and narrower than a wait for a hand.
 pub const fn wedge_bound_ms(list_ms: u64) -> u64 {
-    list_ms * 2
+    list_ms
 }
 
 /// [`wedge_bound_ms`] of a list with no member on it.
m4-runner-not-told-its-bound.patch
diff --git a/src/metalimage.rs b/src/metalimage.rs
index ddc88933e..f78bccab6 100644
--- a/src/metalimage.rs
+++ b/src/metalimage.rs
@@ -97,7 +97,8 @@ pub fn derive(
         .get_mut(RUNNER)
         .and_then(Value::as_table_mut)
         .ok_or_else(|| Underived::Toml(format!("`programs.{RUNNER}` is not a table")))?;
-    let mut args = vec![Value::String(format!("{BOUND}{bound_ms}"))];
+    let mut args = Vec::new();
+    let _ = (BOUND, bound_ms);
     args.extend(jobs.iter().map(|j| Value::String((*j).to_string())));
     args.push(Value::String(REBOOT.to_string()));
     runner.insert("args".to_string(), Value::Array(args));
m5-boot-word-takes-only-named-members.patch
diff --git a/tests/common/metal.rs b/tests/common/metal.rs
index 4fab02f6f..f42c8e714 100644
--- a/tests/common/metal.rs
+++ b/tests/common/metal.rs
@@ -170,7 +170,7 @@ pub fn select(
     let named = |name: &str| all || names.iter().any(|word| name.contains(word));
     let taken: Vec<SharedBoot> = shared
         .iter()
-        .filter_map(|boot| boot.keeping(|job| boots.contains(&boot.boot.as_str()) || named(bare(job))))
+        .filter_map(|boot| boot.keeping(|job| named(bare(job))))
         .collect();
     let rows = rows
         .iter()
m6-a-member-brings-every-file.patch
diff --git a/tests/common/metal.rs b/tests/common/metal.rs
index 4fab02f6f..ee3377569 100644
--- a/tests/common/metal.rs
+++ b/tests/common/metal.rs
@@ -127,7 +127,8 @@ impl SharedBoot {
         let mine = |path: &str| {
             let last = path.rsplit('/').next().unwrap_or(path);
             let case = last.strip_prefix("test_c_").unwrap_or(last);
-            jobs.iter().any(|job| job == case)
+            let _ = case;
+            true
         };
         let files = self.files.iter().filter(|(path, _)| mine(path)).cloned().collect();
         let links = self.links.iter().filter(|(from, _)| mine(from)).cloned().collect();
m7-wait-ignores-the-images-deadline.patch
diff --git a/src/metal.rs b/src/metal.rs
index 996f26b67..a24d11edf 100644
--- a/src/metal.rs
+++ b/src/metal.rs
@@ -55,7 +55,7 @@ const WATCHDOG_BOUNDS_MS: &[u64] =
 /// `toyos_tco::wedge_bound_ms` derives it from. [`RETURN_ALLOWANCE_SECS`] is
 /// what coming back costs after that.
 pub fn return_secs(deadline_ms: u64) -> u64 {
-    let longest = WATCHDOG_BOUNDS_MS.iter().copied().fold(deadline_ms, u64::max);
+    let longest = WATCHDOG_BOUNDS_MS.iter().copied().fold(toyos_tco::WEDGE_BOUND_MS.min(deadline_ms.max(toyos_tco::WEDGE_BOUND_MS)), u64::max);
     longest.div_ceil(1_000) + RETURN_ALLOWANCE_SECS
 }
 
m8-gate-answers-a-constant-bound.patch
diff --git a/src/metal.rs b/src/metal.rs
index 996f26b67..78b8c0dea 100644
--- a/src/metal.rs
+++ b/src/metal.rs
@@ -873,7 +873,7 @@ pub fn judge_arms(armed: &[String]) -> Result<u64, Refusal> {
     };
     match armed.iter().find(|name| !flashable(name)) {
         Some(name) => Err(Refusal::Armed { name: name.clone() }),
-        None => Ok(deadline_ms),
+        None => Ok(toyos_tco::WEDGE_BOUND_MS.max(deadline_ms.min(1))),
     }
 }
 
m9-last-job-before-the-members.patch
diff --git a/tests/common/metal.rs b/tests/common/metal.rs
index 4fab02f6f..4b0de34a7 100644
--- a/tests/common/metal.rs
+++ b/tests/common/metal.rs
@@ -612,6 +612,11 @@ pub fn batches(
         let who = format!("the shared boot {:?}", boot.boot);
         let batch = ride(&mut out, &who, &boot.boot, boot.config, boot.params, boot.features)?;
         batch.add(boot.jobs.iter().cloned());
+        if let Some(last) = batch.last {
+            batch.jobs.retain(|job| job != last);
+            batch.jobs.insert(batch.jobs.len() - boot.jobs.len(), last.to_string());
+            batch.last = None;
+        }
         batch.members_ms += boot.members_ms();
         batch.files.extend(boot.files.iter().cloned());
         batch.links.extend(boot.links.iter().cloned());
m10-a-staged-wedge-takes-the-lists-deadline.patch
diff --git a/src/metal.rs b/src/metal.rs
index 996f26b67..994f290f7 100644
--- a/src/metal.rs
+++ b/src/metal.rs
@@ -821,11 +821,8 @@ pub fn stages_a_wedge(armed: &[impl AsRef<str>]) -> bool {
 /// The `boot-deadline=` bound an image armed with `armed` carries, whose
 /// runner bounds its list at `list_ms`.
 pub fn bound_for(armed: &[impl AsRef<str>], list_ms: u64) -> u64 {
-    if stages_a_wedge(armed) {
-        toyos_tco::STAGED_BOUND_MS
-    } else {
-        toyos_tco::wedge_bound_ms(list_ms)
-    }
+    let _ = stages_a_wedge(armed);
+    toyos_tco::wedge_bound_ms(list_ms)
 }
 
 /// Whether this image is armed so the pass after its reset clears its record
m11-an-unreadable-bound-is-flashed.patch
diff --git a/src/metal.rs b/src/metal.rs
index 996f26b67..ce9634837 100644
--- a/src/metal.rs
+++ b/src/metal.rs
@@ -868,7 +868,7 @@ pub fn judge_arms(armed: &[String]) -> Result<u64, Refusal> {
     // a `--install-sudoers` that fell through into flashing an ordinary
     // `target/bootable.img`, which has no job list and no deadline at all.
     // A bound nothing can read is no bound: the kernel panics on it.
-    let Some(Ok(deadline_ms)) = toyos_tco::deadline_in(&armed.join(",")) else {
+    let Some(deadline_ms) = toyos_tco::deadline_in(&armed.join(",")).map(|read| read.unwrap_or(toyos_tco::WEDGE_BOUND_MS)) else {
         return Err(Refusal::NoBound { staged_a_wedge: stages_a_wedge(armed) });
     };
     match armed.iter().find(|name| !flashable(name)) {
m12-a-rider-on-another-kernel-is-batched.patch
diff --git a/tests/common/metal.rs b/tests/common/metal.rs
index 4fab02f6f..b0ff8e80d 100644
--- a/tests/common/metal.rs
+++ b/tests/common/metal.rs
@@ -557,7 +557,7 @@ fn ride<'a>(
         files: Vec::new(),
         links: Vec::new(),
     });
-    if batch.config != config || batch.params != params || batch.features != features {
+    if batch.config != config || batch.params != params {
         return Err(format!(
             "{who} rides the boot {boot:?} as ({config}, {params:?}, {features:?}) and another rides \
              it as ({}, {:?}, {:?}); one boot is one image",
m13-deadline-is-half-again-the-list-bound.patch
diff --git a/toyos-tco/src/lib.rs b/toyos-tco/src/lib.rs
index e710779f0..fb1604fbc 100644
--- a/toyos-tco/src/lib.rs
+++ b/toyos-tco/src/lib.rs
@@ -147,7 +147,7 @@ pub const PANIC_BOUND_MS: u64 = 60_000;
 /// bound plus the boot around it — the slowest healthy T14 boot measured is
 /// 6.242 s of kernel time — and narrower than a wait for a hand.
 pub const fn wedge_bound_ms(list_ms: u64) -> u64 {
-    list_ms * 2
+    list_ms + list_ms / 2
 }
 
 /// [`wedge_bound_ms`] of a list with no member on it.

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

The T14 at eff8b20ee (the orchestrator's reading; worktree clean before and after, each image's sha256 checked against the request in the command that flashed it). Three boots (ccorpus, shared-debug, shared), each toyos-metal exit 0; judged with --metal --metal-readback <dir> boot:shared boot:ccorpus boot:shared-debug: exit 0, 242 passed, 0 failed, 3 boots (10 registrations and 232 shared members).

boot members the kernel's own lines: boot deadline / hard lockup the list's last record against its bound
shared 88 271360 ms / 135680 ms 8264 ms of 135680 ms
ccorpus 137 191240 ms / 95620 ms 2864 ms of 95620 ms
shared-debug 7 132040 ms / 66020 ms 1380 ms of 66020 ms
  • Each deadline is armed as derived, read from the kernel's own boot deadline: and hard lockup: lines.
  • No the job list ran past its bound in any kernel log (0, 0, 0); each loader's pass after the reset reads DONE, not WEDGED: neither bound fired.
  • shared's list ends with the three former bounds programs as members; no FAIL anywhere in the judge's log.

What the numbers also say: the lists used 6.1 %, 3.0 % and 2.1 % of their bounds. The bounds are far looser than the machine needs, and a dead shared boot now holds the machine for 271 s where it held it for 120 s. Whether tightening the allowances is owed with this stage is with the review.

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Review, round 1, at eff8b20ee

Net lines (git diff --shortstat origin/main...eff8b20ee): 10 files, +336 −256. Production and harness: +213 −216. Tests (tests/checks*, the mod tests of src/metal.rs and src/metalimage.rs, src/testargs.rs): +123 −40.

BLOCKER

  • toyos-tco/src/lib.rs:120-121 — the allowances now set the kernel's deadline and hard-lockup bound, they are 16 to 48 times the work they bound, and that is recorded only in this pull request — a compromise the branch found is removed or recorded in issues/ with an owner, evidence and an exit (Growth). Until this stage the two constants only sized chunks, and every boot was armed 60 000 / 120 000 ms. The T14 reading at this head: shared 8 264 ms of 135 680, ccorpus 2 864 of 95 620, shared-debug 1 380 of 66 020; 80, 12 and 32 ms a member against 860, 260 and 860. The price is real and on failure only: a hung member holds the machine 135.7 s where it held it 60 s, a CPU that takes no interrupt 135.7 s where 60 s, a wedged kernel 271.4 s where 120 s, and a boot that never returns is waited 572 s where 420 s. issues/a-120000-ms-boot-deadline-fired-132859-ms-late-on-the-t14.md is open, so a late expiry adds to the longer bound. The bound also grows by 1.72 s of deadline a Rust member and 0.52 s a C case with no ceiling: by this formula stage 3b's merged boot arms 342 600 ms and is waited 643 s. Owed in this diff: one issues/ file with those readings, the owner (the metal suite, or the owner himself if the design's decision 9 is still his to rule: the body presents it as unbuilt, not as ruled), and an exit a reading can fail, such as allowances derived from the T14's measured milliseconds a member by a stated factor. Tightening itself is not owed here: it moves three armed numbers and needs its own boots. A clamp is not the answer either: a ceiling under the derived bound ends a healthy list.

NOTE

  • toyos-tco/src/lib.rs:154 — WEDGE_BOUND_MS has no production reader after this diff (bound_for and WATCHDOG_BOUNDS_MS were its last): what reads it is its own two assertions, HARD_LOCKUP_BOUND_MS, which issues/hard-lockup-bound-ms-is-read-by-nothing-but-its-own-assertion.md already calls unread, and the tests of src/metal.rs — delete it and spell the assertion and the tests wedge_bound_ms(JOB_BOUND_MS), or name it in that issue with the same exit.
  • tests/toyos.rs:147-149 and :182 — the three names are declared twice, on RUST_SKIP and on METAL_MEMBERS; registered refuses a name on the second and off the first, and nothing refuses one left on RUST_SKIP alone, which then runs on no machine — one declaration, with discovery skipping both lists, or the reason it cannot be.
  • tests/common/metal.rs:584-626 — rows' jobs before members, and last behind both, change no list the T14 runs at this head: shared and ccorpus carry no row, and shared-debug's ten rows name no job. The order is held by the host test and its mutations m1 and m9 alone; the first machine evidence is stage 3b's, which owes it by name.
  • tests/toyos.rs:178-182 — the three bounds programs as members: each bound is its process's own, the third leaves a directory of symlinks under /tmp and is last, and the reading has all three exit=0 behind 85 members (2 875, 793 and 222 ms). A member placed behind abuse_dlopen_ledger has no evidence; the comment at the site says so and that is enough.
  • Prose, pull request body — "Staged, not run" and "T14: Owed" are false of the record since the reading was posted; the orchestrator moves the reading (command, exit, the table) into the body.
  • Prose, issues — issues/the-t14-reboots-through-ubuntu-for-every-test.md:50-55, in the paragraph this diff edits, still says "the same 120 s boot-deadline=" and prices a session by "the bound less a tenth", the rule members_fitting held; issues/a-t14-boot-wedges-after-a-jobs-exit-and-nothing-said-why.md:81 and issues/the-host-cannot-reach-the-t14-while-it-runs-toyos.md:107-108 say every metal image carries WEDGE_BOUND_MS, 120 s, which three no longer do.

What was checked and stands

  • Derivation. list = JOB_BOUND_MS + members × allowance, deadline = 2 × list, hard lockup deadline / 2 = list. 88 × 860, 137 × 260 and 7 × 860 give the three pairs the list prints. The deadline is never under its list (m3 does not build, m13 reds), a staged wedge keeps 10 000 ms (m10), and every other boot is told 60 000 (the own arm of the new test, m4 for the argument). The kernel and toyos-metal read the bound with one function over the same tokens in the same order, so the wait cannot disagree with what the kernel arms; an image with no bound, or one nothing can parse, is refused before the flash (m8, m11). return_secs keeps the margin it had: the 300 s allowance, less the firmware's and the panel's 60 s each.
  • The two lines no host test reaches, build's derive(.., batch.bound_ms(), ..) and its boot-deadline= from batch.deadline_ms(), are held by the reading: the derived configs open with --bound-ms=135680, 95620 and 66020, and the kernel's own lines read 271360 / 135680, 191240 / 95620 and 132040 / 66020.
  • Deletions. No reader of members, members_fitting, chunk_name, sized, --wait-secs or return_secs() is left in the tree, no prompt names any of them, and the six record rows of shared-2 and testcases-bounds are gone. issues/mutual-kill-panicked-on-the-t14-with-stdio-slot-1-not-a-log-ring.md names shared-2 as the boot a past run died in, which stays true.
  • Guest suite. No guest test reaches a changed behaviour: batches, select and derive are the metal path's alone, the guest's shared boot is discover_rust_tests() with the same skip list, and the kernel, loader and runner read constants whose values did not move. The three virt_* tests at this head are enough.
  • The T14 reading (comment of 09:36 UTC, judge exit 0, 242 passed, 0 failed, 3 boots), read against the readbacks themselves: 88 of 88, 137 of 137 and 7 of 7 exit=0; the ten shared-debug rows PASS; no runner bound line, no WEDGED; no boot's log lost a part; 0 numbers past the record. It is the oracle this stage owed and it is green.

What lands it

  • The BLOCKER's issue file, and the NOTEs.
  • If the next head differs from eff8b20ee only under issues/, the reading stands for it once a --metal-readback staging at that head shows the three images' sha256 equal to the three flashed. Any change to source moves the bytes: the three boots again.
  • host and guest / suite are SKIPPED on this draft, which reads as green and is not: both must conclude success on the ready pull request at the head that lands, and again in the merge queue. With those, the reading as above and a round 2 that closes the BLOCKER, the orchestrator may land.

SEND BACK

…e bounds programs have one declaration

Round 1's review, and the T14's reading at eff8b20 behind it: the lists
used 6.1 %, 3.0 % and 2.1 % of bounds that now set the kernel's deadline.

- `RUST_MEMBER_MS` 860 to 300 and `C_MEMBER_MS` 260 to 100, the owner's
  ruling on the design's decision 9. The T14 measured 80 ms a member on
  `shared`, 12 on `ccorpus` and 32 on `shared-debug`, so the ruled values
  stand 3.7, 8.3 and 9.4 times over the work. `shared` arms a list bound of
  86 400 ms and a deadline of 172 800 ms where it armed 135 680 and 271 360;
  `ccorpus` 73 700 and 147 400; `shared-debug` 62 100 and 124 200.
- What is left of the compromise is recorded:
  `issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md`.
  The numbers are ruled and not derived, the bound grows with every member
  and has no ceiling, and no reading holds either.
- `WEDGE_BOUND_MS` is deleted: after the derivation it had no reader but its
  own assertions and tests, which are spelled `wedge_bound_ms(JOB_BOUND_MS)`.
- The three bounds programs are declared once, on `LAST_MEMBERS` (was
  `METAL_MEMBERS`), and are off `RUST_SKIP`: discovery skips both lists, so a
  name left on one of them alone cannot run on no machine by that. No guest
  boot runs discovered members, so nothing else reads the skip.
  `--metal --list` prints the same job list for every boot as before.
- Four issue files said every metal image carries a 120 s deadline, or priced
  a session by the chunk rule that is gone; each is true of the tree again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
@Japabu Japabu changed the title A metal boot's list bound and deadline follow from who rides it: shared-2 and the three bounds rows ride shared, 25 boots to 23 A metal boot's list bound and deadline follow from who rides it, at 300 and 100 ms a member: shared-2 and the three bounds rows ride shared, 25 boots to 23 Oct 9, 2026
@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Round 2, at d6d008e88: the round-1 review, finding by finding

origin/main is 5055dc4aa; merging it was a no-op.

BLOCKER, toyos-tco/src/lib.rs:120-121, the allowances. Fixed two ways, on the orchestrator's decision: the owner had already ruled the design's decision 9 (yes, as recommended).

  • RUST_MEMBER_MS is 300 and C_MEMBER_MS 100. Against the T14's 80, 12 and 32 ms a member that is 3.7, 8.3 and 9.4 times the work, where it was 10.8, 21.7 and 26.9. shared arms 86 400 / 172 800 ms where it armed 135 680 / 271 360; ccorpus 73 700 / 147 400; shared-debug 62 100 / 124 200 (--metal --list, exit 0).
  • What is left is recorded in issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md: the readings, the price table at the new numbers, 0.6 s of deadline a Rust member and 0.2 s a C case with no ceiling, stage 3b's boot at 100 100 / 200 200 ms and 501 s, the late expiry of issues/a-120000-ms-boot-deadline-fired-132859-ms-late-on-the-t14.md added to the longer bound, the owner (the metal suite), and an exit with two halves a reading can fail.
  • The values themselves are numbers a reader checks; no test was added for them. The derivation's tests stand, and its mutations were run again (below).

NOTE, WEDGE_BOUND_MS. Deleted. The two assertions, HARD_LOCKUP_BOUND_MS, the crate's test and the five uses in src/metal.rs's tests are spelled wedge_bound_ms(JOB_BOUND_MS). m3 still does not build, now on assertion failed: wedge_bound_ms(JOB_BOUND_MS) > JOB_BOUND_MS.

NOTE, the three names declared twice. One declaration: LAST_MEMBERS (was METAL_MEMBERS), and the three are off RUST_SKIP. discover_rust_tests skips both lists. A name taken off LAST_MEMBERS is discovered again and runs; a name on RUST_SKIP alone is a helper, which is that list's meaning; so neither list alone leaves one of the three on no machine. The reason the body gave for two lists was wrong: no guest boot runs discovered members, discover_rust_tests has two callers and both are the metal path's. m14 (discovery skips only RUST_SKIP) is refused by registered: --metal --list exits 1 on abuse_mmap_regions is registered twice. --metal --list prints the same 23 job lists at eff8b20ee and here (compared, equal). No check was added: with one declaration there is no state of the lists left to refuse.

NOTE, the order's first machine evidence is stage 3b's. Unchanged, and owed by that stage by name.

NOTE, a member behind abuse_dlopen_ledger has no evidence. Unchanged; the comment at LAST_MEMBERS says so.

NOTE, the body. Rewritten: the reading at eff8b20ee is in it as command, exit and table; "staged, not run" and "T14: Owed" are gone; decision 9 is presented as ruled and built. The three boots are staged again at this head, with each image's sha256.

NOTE, the issue files. issues/the-t14-reboots-through-ubuntu-for-every-test.md states the derived deadline in place of "the same 120 s" and "the bound less a tenth"; issues/a-t14-boot-wedges-after-a-jobs-exit-and-nothing-said-why.md and issues/the-host-cannot-reach-the-t14-while-it-runs-toyos.md say what each image carries; issues/hard-lockup-bound-ms-is-read-by-nothing-but-its-own-assertion.md spells the constant as the tree does.

Mutations at d6d008e88

The six earlier ones whose file changed this round (m7, m8 and m11 respelled, since they named WEDGE_BOUND_MS), and m14 for the one new claim a test or gate can hold. Each is a checked patch (git apply --check), applied, built and run, reported by each command's own exit code, and restored (git apply -R); git status --porcelain printed nothing after each. m1, m2, m4, m5, m6, m9 and m12 touch files round 2 did not change and stand as posted for eff8b20ee.

mutation toyos-checks metal_ toyos-build --lib metal --metal --list what turned red
m3 the deadline is the list's bound 101 101 101 does not build: assertion failed: wedge_bound_ms(JOB_BOUND_MS) > JOB_BOUND_MS
m13 the deadline is half again the list's bound 101 101 0 metal_rows_run_before_members_under_a_bound_the_members_widen, and every_arm_that_stops_this_machine_is_cleared_and_judged_as_one
m7 the wait ignores the image's deadline 0 101 0 the_wait_outlasts_every_watchdog_a_boot_runs_under
m8 the gate answers a constant bound 0 101 0 an_image_with_no_bound_on_its_own_boot_is_refused
m10 a staged wedge takes the list's deadline 101 101 0 the checks test, and every_arm_that_stops_this_machine_...
m11 an unreadable bound is flashed 0 101 0 an_image_with_no_bound_on_its_own_boot_is_refused
m14 discovery does not skip the last members 0 0 1 registered: abuse_mmap_regions is registered twice

The runner, as run after the gates:

for patch in "$M"/m*.patch; do
  name=$(basename "$patch" .patch)
  git apply --check "$patch" || { echo "$name: PATCH DOES NOT APPLY" >> "$S"; continue; }
  git apply "$patch"
  cargo test --test toyos-checks metal_ > "$M/$name.checks.log" 2>&1; checks=$?
  cargo test -p toyos-build --lib metal > "$M/$name.lib.log" 2>&1; lib=$?
  cargo test --test toyos-build -- --metal --list > "$M/$name.list.log" 2>&1; list=$?
  git apply -R "$patch"
  echo "$name: toyos-checks EXIT=$checks, toyos-build lib EXIT=$lib, --metal --list EXIT=$list, tree: $(git status --porcelain | wc -l | tr -d ' ') changed" >> "$S"
done
m3-deadline-is-the-list-bound.patch
diff --git a/toyos-tco/src/lib.rs b/toyos-tco/src/lib.rs
index d0b6e9c0a..6dd98da00 100644
--- a/toyos-tco/src/lib.rs
+++ b/toyos-tco/src/lib.rs
@@ -147,7 +147,7 @@ pub const PANIC_BOUND_MS: u64 = 60_000;
 /// bound plus the boot around it — the slowest healthy T14 boot measured is
 /// 6.242 s of kernel time — and narrower than a wait for a hand.
 pub const fn wedge_bound_ms(list_ms: u64) -> u64 {
-    list_ms * 2
+    list_ms
 }
 
 /// The kernel's bound has to outlast the one that ends a single job, or a slow
m13-deadline-is-half-again-the-list-bound.patch
diff --git a/toyos-tco/src/lib.rs b/toyos-tco/src/lib.rs
index d0b6e9c0a..9acb845d0 100644
--- a/toyos-tco/src/lib.rs
+++ b/toyos-tco/src/lib.rs
@@ -147,7 +147,7 @@ pub const PANIC_BOUND_MS: u64 = 60_000;
 /// bound plus the boot around it — the slowest healthy T14 boot measured is
 /// 6.242 s of kernel time — and narrower than a wait for a hand.
 pub const fn wedge_bound_ms(list_ms: u64) -> u64 {
-    list_ms * 2
+    list_ms + list_ms / 2
 }
 
 /// The kernel's bound has to outlast the one that ends a single job, or a slow
m7-wait-ignores-the-images-deadline.patch
diff --git a/src/metal.rs b/src/metal.rs
index c2abba366..f96216e4c 100644
--- a/src/metal.rs
+++ b/src/metal.rs
@@ -55,7 +55,7 @@ const WATCHDOG_BOUNDS_MS: &[u64] =
 /// `toyos_tco::wedge_bound_ms` derives it from. [`RETURN_ALLOWANCE_SECS`] is
 /// what coming back costs after that.
 pub fn return_secs(deadline_ms: u64) -> u64 {
-    let longest = WATCHDOG_BOUNDS_MS.iter().copied().fold(deadline_ms, u64::max);
+    let longest = WATCHDOG_BOUNDS_MS.iter().copied().fold(toyos_tco::wedge_bound_ms(toyos_tco::JOB_BOUND_MS).min(deadline_ms.max(toyos_tco::wedge_bound_ms(toyos_tco::JOB_BOUND_MS))), u64::max);
     longest.div_ceil(1_000) + RETURN_ALLOWANCE_SECS
 }
 
m8-gate-answers-a-constant-bound.patch
diff --git a/src/metal.rs b/src/metal.rs
index c2abba366..9aa24b7cc 100644
--- a/src/metal.rs
+++ b/src/metal.rs
@@ -873,7 +873,7 @@ pub fn judge_arms(armed: &[String]) -> Result<u64, Refusal> {
     };
     match armed.iter().find(|name| !flashable(name)) {
         Some(name) => Err(Refusal::Armed { name: name.clone() }),
-        None => Ok(deadline_ms),
+        None => Ok(toyos_tco::wedge_bound_ms(toyos_tco::JOB_BOUND_MS).max(deadline_ms.min(1))),
     }
 }
 
m10-a-staged-wedge-takes-the-lists-deadline.patch
diff --git a/src/metal.rs b/src/metal.rs
index 996f26b67..994f290f7 100644
--- a/src/metal.rs
+++ b/src/metal.rs
@@ -821,11 +821,8 @@ pub fn stages_a_wedge(armed: &[impl AsRef<str>]) -> bool {
 /// The `boot-deadline=` bound an image armed with `armed` carries, whose
 /// runner bounds its list at `list_ms`.
 pub fn bound_for(armed: &[impl AsRef<str>], list_ms: u64) -> u64 {
-    if stages_a_wedge(armed) {
-        toyos_tco::STAGED_BOUND_MS
-    } else {
-        toyos_tco::wedge_bound_ms(list_ms)
-    }
+    let _ = stages_a_wedge(armed);
+    toyos_tco::wedge_bound_ms(list_ms)
 }
 
 /// Whether this image is armed so the pass after its reset clears its record
m11-an-unreadable-bound-is-flashed.patch
diff --git a/src/metal.rs b/src/metal.rs
index c2abba366..5ee47ce7c 100644
--- a/src/metal.rs
+++ b/src/metal.rs
@@ -868,7 +868,7 @@ pub fn judge_arms(armed: &[String]) -> Result<u64, Refusal> {
     // a `--install-sudoers` that fell through into flashing an ordinary
     // `target/bootable.img`, which has no job list and no deadline at all.
     // A bound nothing can read is no bound: the kernel panics on it.
-    let Some(Ok(deadline_ms)) = toyos_tco::deadline_in(&armed.join(",")) else {
+    let Some(deadline_ms) = toyos_tco::deadline_in(&armed.join(",")).map(|read| read.unwrap_or(toyos_tco::wedge_bound_ms(toyos_tco::JOB_BOUND_MS))) else {
         return Err(Refusal::NoBound { staged_a_wedge: stages_a_wedge(armed) });
     };
     match armed.iter().find(|name| !flashable(name)) {
m14-discovery-does-not-skip-the-last-members.patch
diff --git a/tests/toyos.rs b/tests/toyos.rs
index 192f13fcb..f0f0675cc 100644
--- a/tests/toyos.rs
+++ b/tests/toyos.rs
@@ -1352,7 +1352,7 @@ fn discover_rust_tests() -> Vec<String> {
         .filter_map(|e| {
             let path = e.ok()?.path();
             let name = path.file_stem()?.to_str()?.to_string();
-            let placed = RUST_SKIP.contains(&name.as_str()) || LAST_MEMBERS.contains(&name.as_str());
+            let placed = RUST_SKIP.contains(&name.as_str());
             (path.extension()? == "rs" && !placed).then_some(name)
         })
         .collect();

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

The T14 at d6d008e88, with the allowances at 300 and 100 ms (the orchestrator's reading; worktree clean before and after, each image's sha256 checked against the request in the command that flashed it). Three boots, each toyos-metal exit 0; judged with --metal --metal-readback <dir> boot:shared boot:ccorpus boot:shared-debug: exit 0, 242 passed, 0 failed, 3 boots (10 registrations and 232 shared members).

boot members the kernel's own lines: boot deadline / hard lockup the list's last record against its bound
shared 88 172800 ms / 86400 ms 8308 ms of 86400 ms (9.6 %)
ccorpus 137 147400 ms / 73700 ms 3223 ms of 73700 ms (4.4 %)
shared-debug 7 124200 ms / 62100 ms 1389 ms of 62100 ms (2.2 %)
  • Each pair is armed as now derived.
  • No the job list ran past its bound in any kernel log; each loader's pass after the reset reads DONE; no WEDGED: neither bound fired.
  • No FAIL in the judge's log.

Against the reading at eff8b20ee: the lists took 8264, 2864 and 1380 ms then and 8308, 3223 and 1389 ms now; a dead shared boot holds the machine for 172.8 s where the previous head would have held it for 271.4 s.

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Review, round 2, at d6d008e88

Net lines (git diff --shortstat origin/main...d6d008e88): 14 files, +439 −288. issues/: +85 −11. Tests (tests/checks*): +83 −14. Everything else, production and harness with the mod tests of src/metal.rs and src/metalimage.rs inside it: +271 −263. Round 2 itself (git diff eff8b20ee d6d008e88): 8 files, +120 −49, of which issues/ is +85 −11.

Round 1's BLOCKER

  • CLOSED — toyos-tco/src/lib.rs:120-121, the allowances set the kernel's deadline at many times the work and nothing recorded it. Two measurements close it. The constants are the ruled 300 and 100, and the T14 at this head (the reading of 10:04 UTC, checked below against the readbacks themselves) armed what they derive and ran inside it. And issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md exists with status: open, kind: tooling, an owner that exists, the readings, a price table whose every number I recomputed (60 000 + 88 × 300 = 86 400, + 137 × 100 = 73 700, + 7 × 300 = 62 100; deadlines twice that; waits 473, 448 and 425 s by return_secs; stage 3b's 100 100 / 200 200 / 501 s; the late expiry's 305 659 ms and 167 s), and an exit of which one half a reading can fail. The ruling is stated as the owner gave it, no broader.

Round 1's NOTEs

  • WEDGE_BOUND_MS — closed. git grep WEDGE_BOUND_MS d6d008e88 finds nothing in the tree, prompts and issues/ included; m3 still does not build, on wedge_bound_ms(JOB_BOUND_MS) > JOB_BOUND_MS.
  • The three names declared twice — closed. One declaration on LAST_MEMBERS, off RUST_SKIP, discovery skips both (tests/toyos.rs:1355). The state round 1 worried about, an edit that takes a name off the placing list and forgets the other, now leaves it discovered and run. suite_split's registry is "every binary not on RUST_SKIP" (tests/checks.rs:600), so the three are now read for SYS_DEBUG as the shipping-kernel members they are, and that check is green. m14 is refused by registered with exit 1.
  • The order's first machine evidence, and a member behind abuse_dlopen_ledger — unchanged, as round 1 left them: stage 3b owes the first by name.
  • The four issue files — closed: each sentence the diff rewrote is true of the tree at this head.
  • The body — open again for a new reason, below.

BLOCKER

None.

NOTE

  • issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md:66-68 — the exit's boot-deadline= of at most 240 000 ms is derived from nothing and no reading can fail it — "twice what a boot no member rides carries" is a multiplier picked to sit above the 200 200 ms the formula already gives, so that clause is met by arithmetic the day stage 3b lands and binds only 66 Rust members later; it is also the ceiling the same file calls no answer three paragraphs up. What it should be: no number. The armed deadline is arithmetic on the allowances, and the first bullet is what holds those; the second bullet's exit is the measured half alone: the boot that carries shared, ccorpus and testcases together reads its list's last record within half its list bound on the T14, with the pair read from the kernel's own lines. A literal there would also be a count the next discovered test file moves.
  • same file, :17-29 and :60-63 — the readings give only the mean, and the mean hides what the allowance is: a single member takes up to fifteen times its allowance, and the bound holds on the sum alone. From the two readbacks at this head and the last: on shared five of 88 members are past 300 ms (abuse_mmap_regions 2 955 ms, fs_cache_eviction 1 066, abuse_thread_table 787, mutual_kill 405, allocator_stress 403) and are 5 616 of the list's 7 115 ms; on ccorpus one case, 124_atomic_counter, is 1 517 of 2 031 ms, and it read 1 141 ms one boot earlier, a third less. The list is safe today: the members' own share without the 60 000 ms base, 26 400 and 13 700 ms, still covers 7 115 and 2 031, and any name-word subset keeps the whole base. It is not safe by construction: a list made only of members as slow as abuse_mmap_regions passes its bound at the 23rd. Owed in the file: the worst member beside the mean, both readings (8 264 / 2 864 / 1 380 and 8 308 / 3 223 / 1 389 ms), and the first exit bullet saying the derivation is of a list's total over its members, since a factor over a per-member mean says nothing of a list of slow ones.
  • Prose, pull request body — "At this head the three boots are staged again", the High-risk section's "read at eff8b20ee and staged again at this head", and the allowance paragraph's "3.7, 8.3 and 9.4 times" are behind the record: the reading at d6d008e88 is a comment, and ccorpus read 15 ms a member in it, 6.7 times. The orchestrator moves command, exit and table into the body.
  • Prose, pull request body and the implementer's answer — "neither list alone leaves one of them on no machine" holds for one edit, not for every state: a name moved from LAST_MEMBERS onto RUST_SKIP runs nowhere, as any member moved there with no driver does. That is two lines in a diff a reader sees, and no check is owed for it.

What was checked and stands

  • The constants and what follows from them. RUST_MEMBER_MS and C_MEMBER_MS have three readers, shared_metal's two boots and the corpus boot; list_bound_ms, wedge_bound_ms, hard_lockup_bound_ms and return_secs take the result and no literal. --metal --list at this head (exit 0, 61 registrations and 232 members over 23 boots) prints the three pairs and 60 000 / 120 000 or 60 000 / 10 000 for the other twenty. No test holds the two values, and none should: a reader checks a number.
  • The mutations. m3, m13, m7, m8, m10, m11 and m14 red as the table says, read from each log's own failing test or compile error and from the runner's exit codes, with the tree clean after each. The seven not rerun stand: they patch tests/common/metal.rs and src/metalimage.rs, the tests that red them are in tests/checks/metal.rs and src/metalimage.rs, round 2 changed none of those files, and the one input that did move, the allowances, does not reach them, because the host test that holds the arithmetic passes its own literals (860 and 260 as inputs, 62 580 and 60 520 as answers).
  • The guest suite. Still reaches nothing changed: the two constants are read by the metal path alone, discover_rust_tests answers the same set at both heads, and the deleted constant had no reader outside its crate and the host's tests.
  • The T14 at d6d008e88, read against the three readbacks and the judge's log, not the comment. What it had to show, and does:
    • each flashed image's sha256 is the one staged at this head (c2cb6c86…, ae764a8c…, f116d807…), three toyos-metal runs exit 0, judge exit 0, 242 passed, 0 failed, 3 boots;
    • 88 of 88, 137 of 137 and 7 of 7 exit=0, shared ending in abuse_mmap_regions, abuse_thread_table, abuse_dlopen_ledger and then reboot; the ten shared-debug rows PASS;
    • the kernel's own boot deadline: / hard lockup: lines read 172800 / 86400, 147400 / 73700 and 124200 / 62100 ms, the loader's parameter line carries the same boot-deadline=, and the derived configs open with --bound-ms=86400, 73700 and 62100;
    • no the job list ran past its bound in any kernel log, no WEDGED in any loader log, and each loader's pass after the reset reads DONE;
    • the lists' last records at 8 308, 3 223 and 1 389 ms: 9.6 %, 4.4 % and 2.2 % of their bounds. The job lists are the ones the last head ran, so a list past a quarter of its bound would have been a finding to explain before landing; none is near it. Two boots a list now: shared moved 44 ms, shared-debug 9 ms, ccorpus 359 ms, all of it the one case named above. On a machine that runs nothing else, ten times the list's whole time is margin enough for a slower moment; the member that moves by a third is a twelfth of the remaining slack.
  • The gates at d6d008e88, each by its own exit in the runner's summary and the tail of its log: toyos-checks 0 (39 passed), toyos-build --lib 0, toyos-tco 0, clippy 0 (24 invocations), --ci host 0 (78 steps, under a host load of 46), --build-only 0, --metal --list 0.

What lands it

  • The two NOTEs on the new issue file. They touch issues/ only, so the reading stands for the next head once a --metal-readback staging at it shows the three sha256 above; any change to source is three boots again.
  • main has moved past the branch's base since the body was written. It merges without conflict (git merge-tree), and the one test binary it adds is on RUST_SKIP, so the three armed pairs do not move. Merging it into the branch moves every image's bytes and makes the reading another head's: leave it to the queue.
  • host, toolchain and guest are SKIPPED on this draft, which reads as green and is not. On the ready pull request at the head that lands, host and guest / suite each conclude success, not skipped, and again in the merge queue. With those two read, the reading above and the two NOTEs closed, the orchestrator may land without a third round.

LAND AFTER NAMED CHANGES

…xit is the measured half alone

The exit's 240 000 ms ceiling on stage 3b's deadline is gone: it was derived
from nothing, the formula already met it, and it was the clamp the same file
calls no answer. What is left is what a reading can fail: the merged boot's
last record within half its list bound on the T14, with the pair read from the
kernel's own lines.

The readings now carry both T14 readings, eff8b20 and d6d008e, and beside
each mean the members past their allowance and the slowest, computed from each
member's start and end records in the two readbacks' kernel logs. The bound
holds on the list's sum and not per member, and the first exit bullet says the
derivation is of a list's total over its members.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

The T14 at the head that lands, 49e12f23b (the orchestrator's reading; worktree clean before and after, each image's sha256 checked against the request in the command that flashed it). The images of an issue-only commit cannot hash as the previous head's did (an image carries its commit), so the three boots were run again instead of carried over. Each toyos-metal exit 0; judged with --metal --metal-readback <dir> boot:shared boot:ccorpus boot:shared-debug: exit 0, 242 passed, 0 failed, 3 boots.

boot members boot deadline / hard lockup, the kernel's own lines the list's last record against its bound
shared 88 172800 ms / 86400 ms 8261 ms of 86400 ms
ccorpus 137 147400 ms / 73700 ms 3375 ms of 73700 ms
shared-debug 7 124200 ms / 62100 ms 1380 ms of 62100 ms

No list ran past its bound, each loader's pass after the reset reads DONE, no FAIL in the judge's log. Three readings of these lists now stand: 8264 / 8308 / 8261 ms, 2864 / 3223 / 3375 ms and 1380 / 1389 / 1380 ms. Marked ready; it lands on host and guest / suite read at this head.

@Japabu
Japabu marked this pull request as ready for review October 9, 2026 10:20
@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

CI at 49e12f23b, read from each job's own log (the orchestrator). host: [ci] Host: 79 step(s), all green. guest / suite: test result: ok. 37 passed, 37 total, [ci] Guest: 5 step(s), all green, no FAIL line. With the T14 reading at this head above (242 passed, 0 failed, 3 boots). git merge-tree against main at 975d5702a, and on top of #792 which is ahead in the queue, exits 0 with no conflict.

@Japabu
Japabu added this pull request to the merge queue Oct 9, 2026
Merged via the queue into main with commit 965e62b Oct 9, 2026
6 checks passed
@Japabu
Japabu deleted the wt/toyos-oneboot-b3a branch October 9, 2026 10:55
Japabu added a commit that referenced this pull request Oct 9, 2026
github-merge-queue Bot pushed a commit that referenced this pull request Oct 9, 2026
…nd the two debug timing rows ride shared-debug: 23 boots to 20 (#799)

Stage 3b of step B of the one-boot plan for the T14's metal rows: merge
boots. Stage 1 was #785, stage 2 #789, stage 3a #794. Head `01fd3aedd`,
which holds `main` at `558283168` (#797, with #796 under it) by a merge
with no conflict: in `tests/toyos.rs` `main`'s two hunks are the
`nvme_disk_keeps_log_and_home` machine test's row in `MACHINE_TESTS` and
its arm and function after `run_machine_test`, guest tests both, neither
in the `METAL` table nor near `shared_metal`; `tests/common/qemu.rs`
moved on `main` alone, and this branch does not touch it. `--metal
--list` is unchanged by it.

`--metal --list` before (`965e62bb1`): 61 registrations and 232 shared
members over **23** boots. After: 61 and 232 over **20**. `shared`,
`ccorpus` and `testcases-debug` are gone; no test is.

**The T14 read `2c7e1be1a`: 266 passed, 1 failed.** The red was this
merge's own: see the first decision. This head is staged and owed a
reading: see "The T14".

## What changed, per decision

- **`endowment_denied` holds a link against toybox's policy table only
where the link's target has a `[programs]` row.** The red at
`2c7e1be1a`: `"/system/bin/134_double_to_signed" is a link to something
else: a second multicall binary needs its own policy table, not this
one`, after every earlier phase passed. The member walks `/system/bin`
and asserted every link lands on `/system/bin/toybox`; the merged ROOT
carries the C corpus, whose 137 cases are links to the comparator
`test_rs_ccheck`, which reads `argv[0]` so the kernel records each run
under the case's name. On `shared`'s own ROOT there were none. **The
walk was too wide, and the corpus is not a second multicall binary in
the sense the claim is about.** What a link carries is its target's row:
the supervisor's `declared` follows one link and matches a row by its
whole path, and a target no row names is answered `NotDeclared`, which
the caller spawns itself with what it holds; the build's
`unnamed_program` says the same of every harness binary ("a harness
binary has none, holding only what its spawner moved in"), and
`test-runner` spawns every job directly in any case. A policy table
limits what a row grants; a binary with no row is granted nothing by
one. So the walk skips a link whose target the image's manifest names no
row for, matched by the whole path as the supervisor matches it (its own
parse of the manifest, as before), and a second binary that *does* have
a row behind a link reds as before. The applets line counts the links it
passed over. The corpus's shape is unchanged: one comparator under one
link per case is what lets a stick say which case failed. Nothing of the
endowment policy for C programs is decided here: the corpus's cases hold
what `test-runner` gives every job, as on `ccorpus`' own boot.
- **`shared` and `ccorpus` ride `testcases`.** Its list is the ten jobs
its rows name, then the 85 discovered Rust binaries, then the 137 C
cases, then the three bounds programs (`LAST_MEMBERS`), then `reboot`:
234 jobs, since `fault_gates` is a row's job and a member and runs once,
among the rows'. `c_corpus_metal` gives the corpus's part of that boot
and `shared_metal` puts the Rust members around it.
- **`testcases-debug` rides `shared-debug`.** `tlb_shootdown_waits` and
`trace_record_cost` name that boot, its kernel and its parameters, and
their two jobs run before its seven members.
- **A member says what it adds to its list's bound** (`metal::Member`),
where a shared boot said one number for all of its members: one boot now
carries Rust tests at 300 ms and C cases at 100.
- **A boot's last job ends its rows' jobs, and the members run behind
it.** This reverses what stage 3a built. `acpi_hold` gives up 54 000 ms
after boot (`UNTIL_MS`); behind the members it would have started 51.8
to 52.5 s in by the readings before the merge. On the T14 at
`2c7e1be1a`, before them, it started 43 095 ms into the kernel's clock
with 10.9 s left and exited 0. It also keeps the three bounds programs
the last jobs of the boot. **The compromise under it is filed, not
fixed**:
`issues/acpi-hold-gives-up-at-a-time-counted-from-boot-whatever-its-runner-was-given.md`,
owner the harness, exit the hold bounded from its own start or by the
bound its runner was given; it now carries the T14's start too.
- **Two jobs of one boot the kernel records under one name are
refused**, in `batches`, for every boot. The C corpus checked its own
cases; the merged list puts Rust members, C cases and rows' jobs under
one log, so the check is the boot's now and the corpus's own is deleted.
- **The judge prints a shared boot's members' time summed between each
member's own markers**, against what they add to the bound, and says
over how many: `its members took <ms> ms of the <ms> ms they add to the
list's bound, summed over the <n> of <all> with both markers`, and a
line `<k> without a start and an end marker, the first <job>` only where
there is one. It is a line a reader reads and reds nothing; no host test
holds it. On the T14: `9253 ms of the 40100 ms … summed over the 225 of
225`.
- **The record rows of `shared`, `ccorpus` and `testcases-debug` are
deleted** from the machine's record under `tests/metal/`. The three
`testcases-window` rows stay:
`issues/the-t14s-record-keeps-three-rows-of-a-boot-nothing-stages.md`
owns them and half its exit.
- **The allowance issue's exit is three parts**, by the orchestrator's
ruling on the round-1 review: (1) each allowance derived from the
members' sum between their own markers by one stated factor, the judge
redding a boot whose members' sum is past that share; (2) what a boot's
rows' jobs are given derived the same way from what they take, the judge
redding a boot whose rows' jobs end past their share; (3) `testcases`
reading both inside, the pair from the kernel's own lines, the margin to
the list bound beside them. None of it is built and the issue stays
open. Its present numbers are now the T14's reading of the merged boot,
beside the sum of the three boots it was.

## What the merged boots arm

`--metal --list` and the staging's own lines at `01fd3aedd`; the wait is
`metal::return_secs`' arithmetic. The T14 read the same pairs off the
kernel's own lines at `2c7e1be1a`.

| boot | jobs | `--bound-ms=` | `boot-deadline=` | hard-lockup bound |
`toyos-metal` waits |
|---|---|---|---|---|---|
| `testcases` | 234 | 100 100 (`main`: 60 000) | 200 200 (`main`: 120
000) | 100 100 (`main`: 60 000) | 501 s (`main`: 420) |
| `shared-debug` | 9 (`main`: 7) | 62 100, unchanged | 124 200,
unchanged | 62 100, unchanged | 425 s, unchanged |

100 100 is 60 000 for the rows' jobs, 88 members at 300 and 137 at 100.
No other boot's list, bound or deadline changes.

## The merged list against its bound, as the T14 measured it

`testcases` at `2c7e1be1a`, each part between its own markers, the
kernel's clock counted from its `Boot: complete (1135ms)` line; beside
it the sum of the three boots it was, three readings each (`testcases`
at `9e70cd2e3`, `473efea22`, `accbd79dd`; `shared` and `ccorpus` at
`eff8b20ee`, `d6d008e88`, `49e12f23b`).

| part | merged, `2c7e1be1a` | its parts apart | its share of the bound
|
|---|---|---|---|
| the boot, to its first job | 1 191 ms | 1 190 to 1 196 ms | |
| the rows' ten jobs | 41 924 ms, ending 43 115 ms in | 41 913 to 41 983
ms | 60 000 ms |
| the 225 members | 9 253 ms | 8 735 to 9 298 ms | 40 100 ms |
| the list's last record | 52 352 ms | 51 838 to 52 477 ms | 100 100 ms
|

Read per part, as
`issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md`'s
exit reads: the members at about 4.3 times their work, the rows' jobs at
about 1.4 times theirs (`counters_metal` alone 32.7 s), the last record
47.7 s inside its bound, read by nothing. No part of that exit is met
and none is built: the issue stays open. Behind 42 s of rows' jobs the
members took 9 253 ms, inside what they took alone.

The loader at `2c7e1be1a`: ROOT read 9 301 ms, loader 13 069 ms, as the
measured rate predicted (30.5 ms/MiB to read, 7.1 to hash, a ROOT
partition of 305 MiB), under the firmware's 60 s. `shared-debug`: 176 ms
of members over 7 of 7, its last record 1 754 ms of 62 100 ms.

## Why each row can share its boot

Rows' jobs run first, so every row's own jobs see the machine
`testcases` gave them at stage 1: the same config, the same prefix, one
job at a time. What changes is what stands in the log, the page and the
census behind them. By what each judge reads:

| rows | what the judge reads | why 225 programs behind it cannot move
it |
|---|---|---|
| `blackbox_unclaimed_page`, `control_regs`, `ioapic_topology`,
`klogd_hosted`, `smp_roster_and_tsc_trail`, `pmm_accounting`,
`acpi_table_inventory`, `timer_calibration`, `pci_inventory`,
`loader_watchdog_arms`' control arm | the loader's file and kernel
records written before the first job | a kernel record is the kernel's
(`Readback::kernel` drops every program's line), and these are said at
boot |
| `wake_storm_cost`, `syscall_cost` | the job's exit record, and
`syscall_cost`'s own two lines | the first and fifth jobs of the list;
`exit_code` takes the lowest pid of a name, and a row's job is spawned
before any member |
| `audio_idle_suspend`, `hda_tone`, `hda_client_stall`,
`shipped_client_departures` | `job_window`: the log from the job's spawn
to its sessions' end, or to the next spawn | each window closes before
the hold. Two reads are boot-wide: no null sink, said at soundserver's
start, and no `repeated completion for free buffer`. The second gains no
stream: no member opens one on soundserver. `cpal_drop_unreleased` is
its own stream server (its header: "No sound is played and soundserver
is not involved"), and in the three `shared` readbacks soundserver says
nothing after its start |
| `counters` | `counters_metal`'s own lines by their `counters_metal
<phase>:` head; the idle second by stamp; no `acpi: legacy mode again`
in the boot | no member prints that head (none in six readings of the
member boots); the idle second is inside the job, which ends before the
first member starts; legacy mode returns only when the `acpi` claim's
holder goes, and no member holds or kills it (none in six readings) |
| `acpi_server_events`, `acpi_tables_loaded` | lines whose tag is
`acpiserver`, and boot records | a member's line carries the runner's
tag. The last count line is read with `rfind`: a boot that passes the
server's next interval prints a later one, also a count |
| `claim_reuses_its_remapping_entry` | every hand-over and release of
the I219, and every `iommu: irte` record whose source is it, counted:
two and two | no member claims a PCI function: `pci_reclaim` is on
`RUST_SKIP`, and six readings of the member boots carry no `handed over
on slot` |
| `domain_ends_below_the_host_bridges` | every `iommu: domain` record of
the boot | a domain a member's boot made is held to the same windows;
judged over the member boots' readbacks below |
| `crash_report_reads_no_kernel_memory` | three refusals `fault_gates`
stages, and no report line anywhere in the boot that carries a kernel
address's contents | members fault on purpose, and their reports are now
read too: a leak in any of them is the defect. Judged over `shared`'s
readback below |
| `irq_census_conservation` | the stop's census, off the page: every
device delivery on cpu0, every shootdown IPI counted by its issuer | the
census is the whole boot's, members included, and the rule is
conservation, not a count. Judged over the member boots' readbacks below
|
| `tlb_shootdown_waits` (to `shared-debug`) | its exit | every wait it
asserts is a floor (`elapsed >= FLOOR_NANOS`), which no load shortens.
The eleven self-tests run at init and on the first syscall of the boot
(`task_probes`, once), before any job |
| `trace_record_cost` (to `shared-debug`) | its exit and one printed
line | a million records written by one syscall with interrupts closed;
the number is printed and not recorded. On that boot each syscall pays
one atomic swap in `task_probes`; the flood is one syscall |

**The boot-wide judges, run over the member boots a machine has already
answered.** The `shared` and `ccorpus` readbacks of #794's third
reading, relabelled `testcases` and judged at `3055ab0d1` (`--metal
--metal-readback <dir>` with thirteen row names):

| readback | exit | |
|---|---|---|
| `shared`'s 88 members | 0 | 13 passed, `irq_census_conservation` (5
196 shootdowns, every delivery accounted for),
`domain_ends_below_the_host_bridges`, `acpi_tables_loaded` and
`crash_report_reads_no_kernel_memory` among them |
| `ccorpus`' 137 cases | 1 | 12 passed;
`crash_report_reads_no_kernel_memory` red for its premise, since
`fault_gates` did not run on that boot |

And the old readbacks under the new profile (`boot:testcases
boot:shared-debug` over stage 1's `testcases` and #794's
`shared-debug`): exit 1, every `testcases` row and every self-test row
PASS, 224 members and the two moved rows red as never run, which is what
those logs hold. `shared-debug` reads `its members took 176 ms of the
2100 ms they add`, the sum taken by hand above.

**The numbers each boot measures** are the boot's own
(`boot.testcases.complete_ms`, `panel_us`, `panel_max_us`): the kernel's
time to `Boot: complete`, before any job, and the panel's census, which
painted 10 times on `testcases`, `shared` and `ccorpus` alike. No row
riding these two boots records a number of its own.

**No row kept a boot for sharing's sake.** `mkdir_cap` and
`readdir_bound` keep theirs until stage 3c.

## The members that read machine-wide state

The red was a member reading something the merged ROOT changed. Each
other member whose source reads state another program moves, by `rg`
over `tests/toyos-rust-tests/src/bin` for directory walks, roster reads
(`SYS_SYSINFO` entries), the memory header and the log:

| member | what it reads | why the merged ROOT or population cannot move
its verdict |
|---|---|---|
| `std_fs` | `/system/bin`'s listing | asserts it non-empty and its own
binary in it: more entries cannot red it |
| `hierarchy_paths` | `/`'s listing against `ROOT_ENTRIES` | `/` is the
mount table, not ROOT's content; the corpus's `expect/` and binaries
land under `/system` |
| `toybox_file_tools` | its scratch directory for `.part` names; `/home`
for one name | each keyed on names it wrote itself ("other tests write
there in the same boot") |
| `empty_dir_stat` | `/tmp/empty_dir_stat_empty` | its own directory |
| `readdir_bound`, `mkdir_cap` | `/tmp`'s exact count; the VFS directory
cap, left full | the reason both ride boots of their own
(`testcases-readdir`, `testcases-mkdir`), unchanged here |
| `endowment_denied` | the roster in a 256-entry buffer, its own pid in
it; `ps`'s row count above zero | members run one at a time, so the live
set is the services and one member: `ps` counted 26 processes at
`2c7e1be1a`. Past 256 threads it would red "does not contain this
process", loud and misnamed; nothing near that |
| `kill_ends_every_wait` | the roster in 256 entries, one pid's state |
refuses past 256 by name |
| `process_tree`, `abuse_thread_name` | the roster in 1 024 entries,
filtered to names they spawned | keyed on their own names and pids |
| `audio_idle_suspend` | soundserver's threads in a 128-entry roster | a
rows' job: it runs before any member |
| `allocator_stress` | the memory header: used above zero and below
total | a sum of the whole machine against itself |
| `abuse_connect_flood` | the memory header before and after its own 32
connects, the growth under 32 MiB | the window holds its own syscalls;
ROOT is read into memory by the loader before the kernel starts, not in
the window. It is the first member, as on `main`'s `shared` |
| `shm_release_reclaims`, `handle_lifetime` | per-kind object counts |
their own headers: "per kind, and not the machine's free memory" |
| `inbox_log_post` | the log, until its own child's exit record | keyed
on its child's pid |
| `file_mtime` | its own file on `/log` | its own file |

No other member walks a directory it did not make, counts processes or
files, or reads a log line count. This is evidence for this list; a
member a later landing adds is held to the same question by its own
review.

## The log

The T14's `testcases` log at `2c7e1be1a`: 8 123 829 bytes, whole (no
part missing), where the sum of the boots it was predicted 8 095 744 to
8 130 458. Eight parts at logkeeper's 1 MiB a part; a fresh volume keeps
sixteen before `retire` deletes one of this boot's own continuations; a
part that is missing reds the whole boot by name (`bootlog::lost_parts`,
#785), unchanged.

## The T14

**At `2c7e1be1a`** (the orchestrator's reading; worktree clean before
and after, each image's sha256 checked in the command that flashed it):
three boots, each `toyos-metal` exit 0; judged with `--metal
--metal-readback <dir> boot:testcases boot:shared-debug`: exit 1, **266
passed, 1 failed**, `test_rs_endowment_denied` exit 101 as above.
Everything else as the request asked: the armed pairs 200 200 / 100 100
and 124 200 / 62 100 from the kernel's own lines; `acpi_hold` at the end
of the rows' jobs, exit 0; the bounds programs last; no bound fired;
each loader pass after the reset `DONE`; the members' line over 225 of
225. That reading is this change's negative control: the walk as it
stood, on a ROOT with the corpus's links, red.

**At `01fd3aedd`**: staged with `cargo test --test toyos-build --
--metal --metal-readback <dir> boot:testcases boot:shared-debug`, exit
2, which is "staged"; no machine touched. The images of `2c7e1be1a` were
gone already (the runner deletes each after its boot).

| boot | sha256 | bytes |
|---|---|---|
| `testcases` |
`d99c1440e52def650a5f9600cd75fe4283322db709d40f95fb12f181358003cd` | 429
916 160 |
| `shared-debug` |
`82c1d8aee120d02b9acee3af3245ec2f12d4d33abd81b664c1467cdd5299e43c` | 136
314 880 |
| `testcases-watchdog` |
`c296fb0c0b9b37e2617a1b371c442a78598c011436a320510cd5b818783f1ed4` | 119
537 664 |

The hashes stand in `request.txt` as `shasum -a 256` lines, and `shasum
-a 256 -c` over them answers OK for the three. The request names round
2's fourteen with this head's expected numbers, then: `PASS
test_rs_endowment_denied` with its line `applets: 15 links behind
/system/bin/toybox, 14 declared over-grants and no undeclared one; 137
links to a binary no row names` and the lines after it; and 267 passed.
Sent back by any FAIL or a count other than 267, an applets line with
other numbers, a bound or a `WEDGED`, a missing part, the hold at or
past 54 000 ms, another order, or a members' line over fewer than 225.

**A mutation boot, optional, staged beside it**:
`m4-a-second-binary-with-a-row-behind-a-link` (the patch is in the
round-3 comment) adds `bin/zz_second_multicall -> /system/bin/symbolize`
to `tests/testcases/system.toml`, `symbolize` having a row. Staged from
a never-pushed commit of it on `01fd3aedd` with `--metal
--metal-readback <dir> endowment_denied 134_double_to_signed`, exit 2,
the tree restored to `01fd3aedd` and clean after; one image of 134 217
728 bytes, sha256
`b6787c435359a87d68264f014fef286583667a9a73d8e93c8f91752b6db74d71`, its
config carrying both the C case's link to `test_rs_ccheck` and the
mutation's. Expected: exit 1, `test_rs_endowment_denied` red naming
`/system/bin/zz_second_multicall`: a second binary with a row behind a
link still reds. **No machine has run it**, and only a machine can: see
below.

## Where the tree differs from the design's text

- The design counts 24 boots to 21. `main` is at 23 (#794 was 25 to 23),
so this stage is 23 to 20.
- The design puts a boot's registered jobs first "in today's order" and
names no last job. Stage 3a built the last job behind the members; this
stage puts it back among the rows', on the measurement above.
- The design's merged boot arms "about 198 s"; the tree's formula gives
200 200 ms.
- Nothing of stage 4b is built or prepared.

## Gates, at `01fd3aedd`

Logs are kept beside the orchestrator's scratch as `r3-*`.

| command | exit |
|---|---|
| `cargo run -- --clippy` | 0, 24 invocations clean |
| `cargo test --test toyos-checks` | 0, 39 passed |
| `cargo test -p toyos-build --lib` | 0 |
| `cargo test --test toyos-build -- --metal --list` | 0, 61
registrations and 232 members over 20 boots |
| `cargo run -- --ci host` | 0, 78 steps green |
| `cargo run -- --build-only` | 0 |
| `cargo test --test toyos-build -- test_rs_endowment_denied` | 1, `No
test matches filter "test_rs_endowment_denied"` |
| `cargo test --test toyos-build -- --metal --metal-readback <dir>
boot:testcases boot:shared-debug` | 2, "staged": three images |
| the same with `endowment_denied 134_double_to_signed`, under `m4` | 2,
"staged": one image |

Host load when the gates started: 18.65 / 29.42 / 32.46.

**No guest runs `endowment_denied`, so the T14 is its only oracle.** The
guest suite's names are `MACHINE_TESTS` and `SCREEN_TESTS`; the
discovered Rust binaries and the C corpus are shared members, run only
by `--metal` and the interactive `--debug`, and the filter above is
refused for that reason. No QEMU boot carries a ROOT with the corpus's
links either: the guest stages C cases as `test_c_<case>` and no
comparator. The rest of the change is reached only through `--metal` and
`toyos-checks`; `guest / suite` is a required check and runs at the
landing head.

## High-risk checks

The harness's batching decides what the kernel's deadline is armed with
and which program's exit a verdict is read from; `endowment_denied` is a
security claim about endowments.

- **Negative controls**: round 2's three mutations of
`tests/common/metal.rs`, each red (101) in
`metal_rows_run_before_members_under_a_bound_the_members_widen` at
`tests/checks/metal.rs` 479, 480 and 499 (the round-2 comment);
`metal.rs` has not changed since. For `endowment_denied`: the T14's
reading of `2c7e1be1a`, the walk before the change on a ROOT with the
corpus's links, red; and `m4`, staged, owed a machine.
- **Independent oracle**: the T14, owed at this head. The supervisor's
`declared` and the build's `unnamed_program` are the rule the narrowed
walk follows, read, not run.

## The host check, and what it sees that reading cannot

`rows_run_before_members_under_a_bound_the_members_widen` is changed,
not added: the last job's place in a list rows and members share, a
bound summed over members of two allowances, and the refusal of two jobs
under one recorded name. No guest test is added or cut;
`endowment_denied`'s walk is narrowed, its claim kept. No dependency is
added. No program is changed.

## Size

`git diff --shortstat origin/main...01fd3ae`: 9 files, +266 −151.
`issues/`: 4 files, +97 −25, one new. `tests/checks/`: +29 −18. The
harness and the record: +128 −106 as before. `endowment_denied.rs`: +12
−2.

## Unsure of

- Whether the 266 that passed at `2c7e1be1a` pass again: one reading of
a boot of its kind.
- The order `read_dir` lists `/system/bin` in. On `m4`'s boot the walk
reds on the mutation's link whichever it meets first; that it passes
over the C case's link there holds only if it meets that one first. The
main boot's 137 is what shows the pass-over.
- `endowment_denied`'s roster read takes 256 entries and asserts its own
pid among them without first refusing a larger roster by name, as
`kill_ends_every_wait` does. Not this change's, and far from reached (26
processes).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
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