Repository navigation
The host jobs build their driver with line tables alone, and the seal's red since 2026-10-05 is accounted for - #752
Conversation
…ps already do nightly's `host` has been red at its seal since 2026-10-05: #722 brought gix into the build system, and the cold tree went from 7,605,844,073 B to 8,540,783,725 B, past the 8,000,000,000 B an entry may hold. Diagnostic run 37693139619 listed every file under the cache's paths and replayed two readers over the sealed tree. The steps' targets are read by a reader; the driver's target, 1,757,640,523 B, is built by the workflow's own `cargo run` before `carry` runs, so it alone carried full debuginfo: its 167 libraries that `target/debug` also holds were 738,377,910 B against 322,227,512 B there, and its binary 262,085,512 B against 78,170,512 B, stored under two names. Nothing reads debuginfo past the line tables. So the job's `env:` gives `CARGO_PROFILE_DEV_DEBUG`, which the driver's build and every step inherit, and `carry` no longer sets it. `LIMIT` stays. Filed: a red nightly on main is reported to nobody; a reader on another runner image downloads the entry and discards it. The limit's issue takes the new ratios, the repository's cache total and what the seal over-counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
|
The diagnostic commit commit 14f49933c0a8bf4113f64b0f9ac33c1ed175e593
Author: japabu <japa.busse@gmail.com>
Date: Wed Oct 7 23:58:42 2026 +0200
DIAGNOSTIC, never lands: list the host cache and replay a reader
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index b26634d85..0e54c5c68 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -1,28 +1,18 @@
name: nightly
-# Every job's logic is `cargo run -- --ci <job>` (src/ci.rs); this file says
-# where each one runs.
+# DIAGNOSTIC, never lands: what is under the host cache's paths, and what a
+# reader reads of it.
on:
- schedule:
- - cron: '0 3 * * *'
workflow_dispatch:
-concurrency:
- group: nightly-${{ github.ref }}
- cancel-in-progress: false
-
permissions:
contents: read
jobs:
- # The host cache's writer restores nothing, and `seal` is green only once
- # src/cicache.rs has sealed the tree the save stores.
host:
runs-on: ubuntu-24.04
- timeout-minutes: 90
- # The driver's own target, and no step's: it says this job carries the
- # host cache (src/cicache.rs).
+ timeout-minutes: 120
env:
CARGO_TARGET_DIR: target/ci-driver
steps:
@@ -32,95 +22,46 @@ jobs:
- run: cargo run -- --ci seal
- # The host cache's one writer. Only main's entries are readable from
- # every branch.
- - if: github.ref == 'refs/heads/main'
- uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
- with:
- path: |
- ~/.cargo/registry/index
- ~/.cargo/registry/cache
- ~/.cargo/git/db
- target
- userland/target
- toyos/target
- kernel/target
- bootloader/target
- key: host-sealed-${{ runner.os }}-${{ runner.arch }}-${{ github.run_id }}
-
- toolchain:
- uses: ./.github/workflows/toolchain.yml
-
- # Skipped off main, where the driver refuses it by name.
- release:
- needs: toolchain
- if: github.ref == 'refs/heads/main'
- runs-on: ubuntu-24.04
- timeout-minutes: 30
- permissions:
- contents: write
- steps:
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
-
- - uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
- with:
- path: ${{ needs.toolchain.outputs.sysroot-path }}
- key: ${{ needs.toolchain.outputs.sysroot-key }}
- fail-on-cache-miss: true
-
- - env:
- GH_TOKEN: ${{ github.token }}
- run: cargo run -- --ci release
-
- tcg:
- needs: toolchain
- if: ${{ !cancelled() }}
- uses: ./.github/workflows/guest.yml
- with:
- kvm: false
- sysroot-key: ${{ needs.toolchain.outputs.sysroot-key }}
- sysroot-path: ${{ needs.toolchain.outputs.sysroot-path }}
-
- # `cargo run -- --build-only` from a fresh machine. `sid` as it stands,
- # image and archive both, and no cache — a fresh machine is the premise.
- portability-linux:
- runs-on: ubuntu-24.04
- timeout-minutes: 350
- container:
- image: debian:sid
- steps:
- - name: deps
+ - if: always()
run: |
- for attempt in 1 2 3; do
- apt-get update -qq > /tmp/apt.log 2>&1 \
- && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq git curl ca-certificates \
- build-essential qemu-system-x86 python3 cmake >> /tmp/apt.log 2>&1 \
- && break
- [ "$attempt" = 3 ] && { cat /tmp/apt.log; exit 1; }
- sleep 20
+ cd "$GITHUB_WORKSPACE"
+ E=""
+ for p in "$HOME/.cargo/registry/index" "$HOME/.cargo/registry/cache" "$HOME/.cargo/git/db" target userland/target toyos/target kernel/target bootloader/target; do
+ [ -e "$p" ] && E="$E $p"
done
- git config --global --add safe.directory "$GITHUB_WORKSPACE"
- curl --proto '=https' --tlsv1.2 -sSf -o "$RUNNER_TEMP/rustup-init.sh" https://sh.rustup.rs
- sh "$RUNNER_TEMP/rustup-init.sh" -y --profile minimal --default-toolchain stable
- echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
-
- - &checkout
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
-
- - run: cargo run -- --build-only
-
- portability-macos:
- runs-on: macos-latest
- timeout-minutes: 350
- steps:
- - *checkout
- # Homebrew keeps one QEMU; `cargo run` notes it if it is not the declared one.
- - run: |
- brew install qemu cmake
- - run: |
- curl --proto '=https' --tlsv1.2 -sSf -o "$RUNNER_TEMP/rustup-init.sh" https://sh.rustup.rs
- sh "$RUNNER_TEMP/rustup-init.sh" -y --profile minimal --default-toolchain stable
- echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- - run: cargo run -- --build-only
- # The host suite's macOS arms run here alone: `ci.yml`'s `host` is Linux.
- - run: cargo run -- --ci host
+ echo "DIAG paths:$E"
+ findmnt -T . || true
+ findmnt -T "$HOME" || true
+ df -h . || true
+ echo "DIAG-BEGIN list0"
+ find $E -type f -printf 'L0 %A@ %T@ %s %p\n'
+ echo "DIAG-END list0"
+ echo "DIAG-BEGIN zstd"
+ echo "Z all $(tar -cf - $E 2>/dev/null | zstd -T0 | wc -c)"
+ for d in "$HOME/.cargo/registry/index" "$HOME/.cargo/registry/cache" "$HOME/.cargo/git/db" target/* userland/target/* toyos/target/* target/ci-driver/debug/* target/debug/*; do
+ [ -e "$d" ] && echo "Z $d $(tar -cf - "$d" 2>/dev/null | zstd -T0 | wc -c) $(du -sb "$d" | cut -f1)"
+ done
+ echo "DIAG-END zstd"
+ cp target/ci-sources "$RUNNER_TEMP/ci-sources"
+ find $E -type f -exec touch -a -d '2000-01-01' {} +
+ probe=$(find target/debug/deps -name '*.rlib' | sed -n 1p)
+ head -c 1 "$probe" > /dev/null
+ echo "DIAG atime probe: $(stat -c '%X %n' "$probe")"
+ touch -a -d '2000-01-01' "$probe"
+
+ echo "DIAG reader A (nothing changed) starts $(date -u +%T)"
+ git ls-files -z | xargs -0 touch -c
+ cargo run -- --ci host; echo "DIAG reader A EXIT=$? ends $(date -u +%T)"
+ echo "DIAG-BEGIN list1"
+ find $E -type f -printf 'L1 %A@ %T@ %s %p\n'
+ echo "DIAG-END list1"
+
+ find $E -type f -exec touch -a -d '2000-01-01' {} +
+ cp "$RUNNER_TEMP/ci-sources" target/ci-sources
+ git ls-files -z '*Cargo.toml' ':!rust' ':!tests/testcases' | xargs -0 -n1 sh -c 'echo "# diag" >> "$0"'
+ echo "DIAG reader B (every package changed) starts $(date -u +%T)"
+ git ls-files -z | xargs -0 touch -c
+ cargo run -- --ci host; echo "DIAG reader B EXIT=$? ends $(date -u +%T)"
+ echo "DIAG-BEGIN list2"
+ find $E -type f -printf 'L2 %A@ %T@ %s %p\n'
+ echo "DIAG-END list2"
diff --git a/src/cicache.rs b/src/cicache.rs
index e35039ec7..2c8581a24 100644
--- a/src/cicache.rs
+++ b/src/cicache.rs
@@ -64,7 +64,7 @@ const PATHS: [&str; 8] = [
/// The most the files under [`PATHS`] may hold, uncompressed. The repository's
/// caches are evicted by last access past 10 GB.
-const LIMIT: u64 = 8_000_000_000;
+const LIMIT: u64 = 80_000_000_000;
/// 2001-09-09T01:46:40Z: older than any build, so a file dated so is never
/// newer than one. |
|
Review round 1 of BLOCKERNone. NOTE
Growth
What was askedRoot cause. Measured as stated, by units: between the last green nightly (37190643147, The fix is the cheapest part, not the growth. It removes 944,362,661 B (same image, same 18,712 files) of full debuginfo in What is lost. Nothing a reader or a failing run uses. Every step already built with line tables alone; only the driver's own build changes. A panic's backtrace keeps function, file and line. Variable and type DWARF for a debugger attached to the driver on a hosted runner goes, and nothing attaches one. A developer's build is untouched: the setting exists only in two jobs' The margin. 4.2 % on image 20260927.320.1, measured on a base three landings behind One source of truth or two. Two declarations and one sentence: The two issues. Both have an owner that exists and an exit a reader can check. The reader issue's first exit arm contradicts #746. It must rebase onto this, dispatch What
LAND |
…workspace Git merged every file without a conflict. #752's two `env:` lines (`CARGO_PROFILE_DEV_DEBUG: line-tables-only` in both workflows' `host` jobs) and its shortened `carry()` survive as it wrote them. No manifest and no lock moved, so `Cargo.lock` is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
…nd the SDK resolve together (#746) Stage 3 of `issues/the-tree-says-who-uses-each-thing.md`. The root, `kernel/`, `bootloader/`, `userland/` and `toyos/` were five Cargo resolutions; they are one workspace with one `Cargo.lock`, one `[profile.toyos]`, one `[patch]` table and one tracked `.cargo/config.toml`. No directory moves. Head `dd12c0b32`, on `origin/main` `6f87cdb9c` (#749; none of #757, #759 or #762 had landed when it was merged and measured). It is `9ef866436`, where the CI readings of the fold itself were taken, plus two merges of `main` and the close of the stage's issue. Everything owed at the merged head is in the next section, measured at `dd12c0b32`. ## The merge of #749, measured at `dd12c0b32` #749 wrote its new dependency edges into `kernel/Cargo.lock` and `userland/Cargo.lock`, which this branch deletes. Both modify/delete conflicts are resolved by deleting the file and re-resolving the root lock. Git merged the root `Cargo.lock` without a conflict into a lock that is wrong, as the review found: `cargo metadata --locked` on it exits 101 (`cannot update the lock file … because --locked was passed`). It had `toyos-userbound`'s edges to `toyos-abi` and `toyos-bootmap`, which #749 also wrote into the root lock, and not `acpiserver`'s to `toyos-acpi` and `toyos-aml`, which #749 wrote into userland's alone. `cargo metadata --offline` re-resolved it. `diff` of git's merged lock against the re-resolved one is those two lines under `acpiserver` and nothing else: no package added, no version moved. | Owed | Command | Result | |---|---|---| | The lock resolves as committed | `cargo metadata --locked --format-version 1` | exit 0 at this head by the host suite's step "the licences of what ships", which runs `cargo metadata --locked` for every shipped crate's manifest and is green in `host.log` and in run 37757675374; the hand run's empty stderr (`metadata-locked.err`) predates the merge commit and recorded no exit | | The lock's (name, version) pairs are the union of `main`'s five | `pairs.sh <worktree> 6f87cdb`: the pairs of `Cargo.lock`, `kernel/`, `bootloader/`, `userland/` and `toyos/Cargo.lock` at `6f87cdb9c`, `sort -u`, against the root lock's | 692 against 692, `diff` exit 0 (`pairs.out`) | | The folded kernel and loader are the control's bytes | `prove.sh 6f87cdb dd12c0b …`, the round 3 script unchanged | exit 0; all four `control vs fold` byte rows `cmp` exit 0; every row in "The checks" below (`prove.out`) | | No new reader of a compiled-in path | `git diff -U0 e3bdff8 dd12c0b -- tests src toyos-blackbox toyos-symbols userland/symbolize`, its added lines searched for `\.rs`, `taken at`, `panicked at`, `Location`, `file()`, `PREVIOUS_PANIC`, `strip_prefix`, `src/`, `pure/` | the merge touches five files there, all under `tests/`; 13 hits, of which 4 are diff headers and 9 the field `info.rsdp`; none reads a path. `tests/common/power.rs:429` is still the one reader outside fixtures, and reads `taken at kernel/src/hardlockup/probe.rs` (`readers-merge.diff`, `readers-hits.txt`) | | `cargo run -- --ci host`, once, on the development machine | at `dd12c0b32`, `cargo run -- --ci host > host.log 2>&1; echo EXIT=$?` | exit 0; the log ends `[ci] Host: 77 step(s), all green`; 1-minute load 26.60 when it started (`host.log`) | The logs are in the round's scratch directory (`orch/oneworkspace-r4/`), which a reader of this pull request cannot reach; `prove.out` and `pairs.sh` are in the round 4 comment. ## What changed, per decision - **Members.** `kernel`, `bootloader`, `toyos` and userland's 40 packages join the root `[workspace]`. `userland/Cargo.toml`, four locks, three `rust-toolchain.toml` and three per-directory `.cargo/config.toml` are deleted. The toolchain files chose nothing the build read: every guest `cargo` already runs under `RUSTUP_TOOLCHAIN` naming its sysroot. - **Flags.** `build.target` is gone, since every guest build already passes `--target`. The root config holds one `[target.<triple>]` table per guest triple, six, each with the flags its directory's config gave it. A host build takes none, as before. Two things do change: - The guest crates outside the workspace that are built from their own directory for a ToyOS triple (`tests/toyos-rust-tests` and its `tls-*` crates) now take `-Dwarnings`, because cargo reads the tracked root config from above them. On a checkout without a local config they took no flags. - The one build that sets `RUSTFLAGS` itself (the test binaries linked against a `cdylib`, `src/build.rs`) takes none of the table: the variable replaces it. - **Profiles.** The root's `[profile.dev]` is `opt-level = 2`. The kernel library's host tests, its model controls, the SDK's tests and every surveyed userland crate's host tests used to resolve in their own workspaces and ran at `opt-level = 0`; they now run at 2. - **What stays apart** (the root manifest's `exclude` says why at each entry): `rust/`; `tests/toyos-rust-tests` and its `tls-*` crates and `tests/ssh-client-host`, because `[patch]` is workspace-wide and they patch or refuse what the root patches; and `userland/libc`. libc keeps its own lock because that lock is an input of the sysroot key: as a member it would be resolved by the root lock, and every dependency change of any member would move the key and rebuild every sysroot. The price is a sixth resolution of `toyos`, `toyos-abi`, `toyos-elf`, `toyos-osrelease` and `dlmalloc` that nothing holds to the root's (both carry `dlmalloc` 0.2.13 today). - **The lock** is every `[[package]]` of the five locks, deduplicated and resolved by `cargo metadata`. Nothing was `cargo update`d. Since the lock reviewed at `88bcbf4d3` it has changed by #749's four edges alone (see the section above); `cargo metadata --locked` exits 0 at this head. - **One target directory.** Every guest is built at the root with `-p` into `target/`. A stale sysroot used to `cargo clean` a crate's own target; that would now empty the build system's own, so `Stale::All` removes `target/toyos` and the guest triples' directories instead, and the `cargo clean` path, its member assertion and its test are deleted. - **Kernel and loader build one after the other.** They were built on two threads. In one target directory cargo serialises them anyway (measured in round 1: the second prints `Blocking waiting for file lock on artifact directory`), so the thread scope is deleted. - **The host suite** can no longer be `--workspace`: the kernel binary, the loader and most of userland do not build for a host. `src/hostws.rs` says which members a host tests, and the workspace test and clippy runs `--exclude` the rest by package name. - **Fork clones.** The tracked config includes the gitignored `.cargo/local.toml` when it exists, and `implementer.md` names it. - **The merge of #745.** `src/sysroot.rs` keeps both sides: `SYSROOT_SOURCES` carries `"sdk/std"` and `SYSROOT_MANIFESTS` ends in `".cargo/config.toml"`; its test keeps both loops; `issues/toyos-has-its-own-allocator.md` keeps `sdk/std/sys/alloc.rs` and "from the kernel's graph in `Cargo.lock`". Git merged all three without a conflict. - **The host's own apps build where the userland tests build.** `src/ci.rs`'s apps step passes `--target` only where it checks another host's triple. On `main` the `userland/*` test steps and the host's apps step both named the host triple and shared `userland/target/<host triple>`. The fold took `--target` off the test steps, which no longer need it to keep a guest triple out, and left it on the apps step: the tests filled `target/debug`, the apps `target/<host triple>`, and every dependency was compiled twice. That is what made the sealed tree larger than `main`'s (see CI). - **The merges of `main`.** #747, #748, #750, #751, #753 and #755 merged without a conflict. #749 did not: see the section above. - **The merge of #752.** Git merged it without a conflict: both workflows' `host` jobs keep `CARGO_PROFILE_DEV_DEBUG: line-tables-only` in `env:`, and `carry()` no longer sets it. No manifest and no lock moved in the merge. - **Issues.** `issues/the-tree-resolves-in-five-cargo-locks-not-one.md` is deleted: the one thing it named as left, the T14's run of the metal profile on the folded build, ran green at `db55db96a`, and review round 2 ruled no boot owed for what followed on two conditions, both in the section above. Stages 2 and 3 of `issues/the-tree-says-who-uses-each-thing.md` now read "Landed in #724, #732 and #738" and "Landed in #746". What the file carried that stays true is the root manifest's `exclude`, which says why each excluded directory keeps its own resolution; the deleting commit's message carries the rest (libc's second resolution of five crates, and `miniz_oxide` 0.8.9 beside 0.9.1 until `png` takes 0.9). `issues/cargo-run-inside-kernel-loom-or-kernel-sim-builds-for-a-bare-target.md` is closed on the two in-directory runs at `9ef866436`. `issues/the-sdk-is-linted-by-no-clippy-run.md` is filed and names its owner, the build system. ## The fold changed the kernel's source paths, and the proof did not see it The T14's run of the whole metal profile at `88bcbf4d3` exited 1: 295 passed, 1 failed, 30 boots. The red row was `hard_lockup_ends_a_deaf_cpu`. Its judge looked for `taken at src/hardlockup/probe.rs` in the previous boot's panic record, and the readback's loader log says `taken at kernel/src/hardlockup/probe.rs:145:29`. **What changed in the kernel's strings.** Cargo hands rustc a workspace member's source by its path from the workspace root, and rustc writes that path into every panic and `Location`. The kernel's root was `kernel/`; it is now the repository. So `src/...` became `kernel/src/...`, and a path dependency outside the old root, which the base named by the checkout's absolute path, is now named from the repository root (`toyos-abi/src/...`). Read from the actuator kernel staged at this head: 254 distinct `.rs` paths, 158 under `kernel/`, 38 under a `toyos-*` crate or `bcachefs`, none bare `src/` or `pure/`, none naming the worktree. **Why the proof did not see it.** Both of its oracles were blind to it by construction: - The byte row compared the fold against a control that is the base with its workspace root moved up. The control moved the root too, so it carries the same new paths and the bytes agree. - The `rustc`-lines row compared base against fold after a `sed` that rewrites `(kernel/|bootloader/)?(src|pure)/x.rs` and `ROOT/<crate>/src/lib.rs` to one form. That rewrite is needed, or every path crate's line differs and the row can show nothing else; but it absorbed the change without reporting it. `prove.sh` now reports what that rewrite absorbs: per artifact, how many crates' source arguments were renamed, and a diff of the `.rs` paths the artifact carries, base against fold and control against fold. The script is in the round 3 comment and has run twice since, at `9ef866436` and at this head. **Every reader of a compiled-in path.** I searched the harness, the guest tests, the build system, `toyos-blackbox`, `toyos-symbols`, `userland/symbolize`, the loader and the kernel's panic path for path literals, prefix strips and `Location` readers. One reader matches a compiled-in path by its prefix: `tests/common/power.rs`, the red row's judge, now fixed to the path the kernel records. Everything else is prefix-blind (`panicked at`, a file name with its line) or a synthetic fixture. The kernel's panic slot keeps the last 96 bytes of a path; the longest kernel path is 46, so nothing is cut. Userland's panic sites gain a `userland/` prefix the same way; no test reads one. **The record rows.** The judging asked to record three `boot.testcases-bounds.*` rows. They are not this change's: that boot was already staged on the base and unrecorded, and `main` recorded it in #745. They arrive with the merge and nothing is committed here. ## What the fold changes in what is built The lock row was measured at `dd12c0b32` against `6f87cdb9c`, the `rustc` row by `prove.sh` at the same pair; the two `cargo tree` rows at `88bcbf4d3`, and were not taken again. | Measured | Result | |---|---| | Lock: (name, version) pairs, the fold's against the union of `origin/main`'s five | identical, 692 pairs, `diff` exit 0 | | Lock: sources | registry `getrandom` 0.2.17, 0.3.4, 0.4.2 are gone; the forks at the same versions remain | | Kernel and loader, both arches: every `rustc` command line of `cargo build -v`, base against fold, path and cargo's path-derived hashes taken out | identical, `diff` exit 0: 31 units per kernel, 55 and 37 per loader | | Userland, both triples: `cargo tree -e features` over every program, base against fold | identical, `cmp` exit 0 | | Host members: the same | `diff` exit 1, on `getrandom`'s source alone | So one resolved crate changes: the build system and the other host members compile the ToyOS forks of `getrandom` 0.2.17, 0.3.4 and 0.4.2 instead of the registry's, same versions, same features. And every source path compiled into the kernel and the loader changes, as above. ## The checks (high-risk: build system) Measured at `dd12c0b32` against `origin/main` `6f87cdb9c` by `prove.sh` (the script of the round 3 comment, unchanged), exit 0; its output is in the round 4 comment. Round 3 measured the same rows at `9ef866436` against `b432ed21c`, round 1 at `88bcbf4d3` against `e7010129f`. **Negative control.** The whole change reverted is the base. A second control is the base with only its workspace root moved up, keeping the crate's own base lock and its profile. It is given the fold's root `.cargo/config.toml`, so the control does not hold the flags: the one row that does is base against fold on normalised `rustc` lines. **Oracle.** Bytes and cargo's own command lines. Each cell is its own `cmp` or `diff` exit: | | kernel x86_64 | kernel AArch64 | loader x86_64 | loader AArch64 | |---|---|---|---|---| | control vs fold, bytes | 0 | 0 | 0 | 0 | | fold vs fold rebuilt, bytes | 0 | 0 | 0 | 0 | | base vs fold, bytes | 1 | 1 | 1 | 1 | | base vs fold, `rustc` lines normalised | 0 | 0 | 0 | 0 | | control vs fold, `rustc` lines verbatim | 0 | 0 | 0 | 0 | What the normalisation absorbs, reported by the three `paths` rows: base against fold, cargo hands rustc another source path for 29 of 31 crates of each kernel and for 35 of 50 and 13 of 35 crates of the loaders (`diff` exit 1 each, as expected); the `.rs` paths the x86-64 kernel carries are 250 on both sides, of which the base has 39 under the tree's absolute path and 150 from the crate's own root and the fold none of either (`diff` exit 1); control against fold the artifacts' paths are identical (`diff` exit 0, all four). **Mutations**, each on a fresh copy of the fold: M1 (drop the `[target.x86_64-unknown-uefi]` table) loader build exit 101; M2 (lock `dlmalloc` at 0.2.12) `cmp` exit 1 and lines `diff` exit 1; M3 (select `bcachefs` beside the kernel in one `cargo`) kernel build exit 101. ## Gates The rows of the section "The merge of #749" were read at `dd12c0b32`. Every row below was read at `9ef866436` unless it says otherwise, each once, the narrowest that judges it. `ci.yml` runs on the push of `dd12c0b32`; its result is not in this body. | Gate | Result | |---|---| | `cargo run -- --ci host` | at `dd12c0b32`, development machine: exit 0, `[ci] Host: 77 step(s), all green`. Linux runner at `9ef866436`: `ci.yml` run 37740454881 `host` success; cold inside `--ci seal`, nightly run 37740449787: `[ci] Seal: 80 step(s), all green`; on macOS, the same nightly's `portability-macos`: success | | `cargo test --lib ci::tests` (the changed step's own test) | exit 0, 13 passed | | The images and the guest suite | at `9ef866436`, run 37740454881: `toolchain / build` and `guest / suite` success (KVM); run 37740449787: `toolchain / build` and `tcg / suite` success. At `e3bdff8af`, run 37755369755: `host` and `toolchain / build` success, `guest / suite` still running when read. Not run locally, and not read at `dd12c0b32` | | `prove.sh 6f87cdb dd12c0b …` | exit 0; every row as in the table above | | `cargo test` inside `kernel/loom` | exit 0 | | `cargo test` inside `kernel/sim` | exit 0 | | Cold wall clock, x86-64 kernel and loader (`wall.sh`, one run) | base, two cargos side by side: 24 s, 1-minute load 34.92 before it. Fold, one after the other: 20 s, load 42.23. Other agents' builds were running, so the two are not a controlled pair; the fold was not slower | | Metal profile | every row green at `db55db96a` (comment 6048782042). Since then the branch changed `src/ci.rs` and the root lock's two `acpiserver` edges; the kernel sources that moved are `main`'s own landings (#747, #748, #749), merged in. Review round 2, ruling (3), owes no boot for the merge of #749 on two conditions, both met above | | `cargo test --manifest-path userland/acpiserver/aml/Cargo.toml` at `e3bdff8af` | exit 0 | | `git status --porcelain --ignore-submodules=none` at `dd12c0b32` | empty | `issues/cargo-run-inside-kernel-loom-or-kernel-sim-builds-for-a-bare-target.md`'s close now stands on the two in-directory runs at `9ef866436`. The logs of these rows are files in the scratch directory of the round that took them (`orch/oneworkspace-r3/`), which a reader of this pull request cannot reach; the proof's and the measurements' outputs are in the round 3 comment. ## CI **Why the sealed tree was larger than `main`'s, measured.** A `workflow_dispatch` of `nightly.yml` at `88bcbf4d3` (run 37685714260) sealed `9348536345 B in 20064 files`, red. Units compiled per step, counted from that log and from `main`'s nightly at `b432ed21c` (run 37717000719, sealed `18708 files, 7664839895 B`): | step | `88bcbf4d3` | `main` | |---|---|---| | the driver's own build | 203 | 203 | | the build system | 207 | 207 | | the workspace's host members | 99 | 99 | | clippy, warnings denied | 412 | 417 | | the controls | 56 | 56 | | `userland/*` | 225 | 289 | | the apps for linux | 282 | 47 | | the apps for macos | 248 | 248 | | the apps for windows | 239 | 239 | Of the 256 distinct crates the apps-for-linux step compiled at `88bcbf4d3`, 226 had been compiled by an earlier step of the same run; 30 by none. The cause is the target directory: the test steps built without `--target` into `target/debug`, the apps step with `--target x86_64-unknown-linux-gnu` into `target/x86_64-unknown-linux-gnu`. Profile, features and `RUSTFLAGS` are the same in both. **The fix, measured once on the development machine** (`share.sh` in the round 3 comment; cold, a target of its own, `aarch64-apple-darwin`): after the fourteen test steps' builds (271 units), the ten apps with `--target <host>` compile 270 units and add 616,616 KiB under `target/<host>` and 135,064 KiB under `target/debug`; the same ten without `--target` then compile 47 units and add 107,856 KiB. The 47 are the same crates `main`'s step compiles on the runner. **The seal at `9ef866436`, nightly run 37740449787** (conclusion success: `host`, `portability-linux`, `portability-macos`, `toolchain / build`, `tcg / suite`): ``` the cache entry, read by content: none restored: the run is cold the apps for linux: 10 app(s) pass `cargo build`; … the tree, sealed as the host cache's entry: 17010 files, 7493968284 B of the 8000000000 B an entry may hold; sealed: 2496 sources, built on Linux X64 ubuntu24 20261004.327.1, every target dated as built [ci] Seal: 80 step(s), all green ``` Units per step in its log, against the two columns above: | step | `9ef866436` | `88bcbf4d3` | `main` | |---|---|---|---| | `userland/*` | 225 | 225 | 289 | | the apps for linux | 42 | 282 | 47 | | the apps for macos | 264 | 248 | 248 | | the apps for windows | 240 | 239 | 239 | Every other step compiles what it did at `88bcbf4d3`. I expected 47 for the Linux apps: it is `main`'s 47 less `crc32fast`, `log`, `memchr`, `smallvec` and `toyos-keymap`, which an earlier step had compiled. I did not expect the macOS step's 16 more: all are host-side units (`syn`, `thiserror-impl`, `tokio-macros`, `futures-macro`, `autocfg` and the like), which the Linux step's `--target` build used to compile for the host and which the first `--target` step now compiles instead. The four steps together compile 771 units against 994 at `88bcbf4d3` and 823 on `main`. - Margin: 506,031,716 B under the limit, 6.3 %, on image 20261004.327.1. `main`'s 7,664,839,895 B was sealed on 20260927.320.1. The one pair of figures there is for the two images is `f260e0b98`, built with full debuginfo before #752 cut it to line tables: 8,540,783,725 B on 20260927.320.1 (run 37292450697) against 8,182,940,473 B on 20261004.327.1 (run 37601225884), 357,843,252 B or 4.2 % less on the newer. So this head's figure and `main`'s are not a pair, and this head is unmeasured on the older image. - Against the same branch before the fix and before #752: 9,348,536,345 B at `88bcbf4d3` on 20260927.320.1. **`ci.yml` run 37740454881 at `9ef866436`:** `host`, `toolchain / build` and `guest / suite` success. After this lands every `host` check runs cold until the first nightly on `main` seals and saves: the path list is the cache's version. **Toolchain keys.** The fold moves the sysroot key once: `userland/.cargo/config.toml` was one of its inputs and `.cargo/config.toml` replaces it. From now on a change to any guest triple's flags moves that key. The merges of #747 and #749 moved `toyos-abi` and `toyos`, so the sysroot key moved with `main`; the key at this head was not read here. ## No new gate, test or dependency No guest test is added or changed. No dependency is added. The proof is a one-off script because its subject is this one change against its base. ## Size `git diff --shortstat origin/main...HEAD` at `dd12c0b32`: 53 files, +5454 −7765. Without the locks: 48 files, +446 −704. `src/`, `tests/toyos.rs` and `tests/common/`: 12 files, +237 −360, of which tests are roughly +65 −115 by my reading of the hunks (an estimate, not a count). `issues/`: 16 files, +49 −115. ## What I am unsure of - **The seal on the older runner image.** GitHub serves two; this branch was sealed on the newer one, at `9ef866436`. The only pair of figures for the two is `f260e0b98` with full debuginfo (above): the older image sealed it 357,843,252 B larger. Carried unscaled onto this tree that leaves 148,188,464 B under the limit on the older image; scaled by the pair's ratio, about 178 MB. Both are arithmetic, not a run. - **`dd12c0b32` itself:** `ci.yml` run 37757675374 has `host` and `toolchain / build` success at it; its `guest / suite` is what the landing waits on. #757 (`b6bcb9691`) landed on `main` after this head was merged and measured: ten source files under `kernel/src`, `toyos-abi/src`, `toyos-userbound/src` and `tests/toyos-rust-tests`, no manifest and no lock; `git merge-tree --write-tree dd12c0b b6bcb96` exits 0. Nothing here was measured with it in. It differs from the sealed head by `main`'s #749, #750, #753 and #755 and the root lock's two edges; the seal's byte count at this head is unmeasured. - **Build wall clock.** One run under load; the fold was not slower. - **Clippy reaches further.** The bare-target shapes now also lint the kernel's and the loader's path dependencies for those targets. - **The licence gate reads a superset.** `--all-features` for the kernel now turns on every member's features. It judges more than ships. - **The runner's cargo.** The nightly's `host` at `88bcbf4d3` parsed the optional `include`, so the runner's cargo accepts it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
Head
7f991fbad. Fixes the red onmain: nightly'shosthas failed at its last step, the seal of the host cache, on every scheduled run since 2026-10-05.What stood red, and for how long
d47b383cf)16897 files, 7605844073 B of the 8000000000 B an entry may hold; saved 1,813,819,887 Bf260e0b98)8540783725 B in 18591 files under the cache's paths, above the 8000000000 Bf260e0b98)f260e0b98)8182940473 B in 17350 files under the cache's paths, above the 8000000000 BThree scheduled runs, found on 2026-10-07 by a review of another pull request. Nothing reported it: the scheduled nightly is a check of no pull request and no merge group, and no role's prompt reads it. Filed as
issues/a-red-nightly-on-main-is-reported-to-nobody.md.The brief said every
hostcheck since ran cold because no entry exists. The entry of 2026-10-04 exists and is restored; the readers discard it. Runs 37646740767 and 37687777630 each sayCache restored from key: host-sealed-Linux-X64-37190643147(1,813,819,887 B) and thend47b383cf…'s entry, built on Linux X64 ubuntu24 20260927.320.1, deleted: the run is cold, since they ran on 20261004.327.1. GitHub served both images on 2026-10-07 (the table above, and runs 37685714260 and 37693139619 on the older one hours after 37601225884 on the newer), so a fresh entry does not end this: filed asissues/a-host-reader-on-another-runner-image-downloads-the-entry-and-discards-it.md.Root cause
#722 (
c4ab2b1e1, os-release) addedgixto the build system. Units compiled per step, counted from the logs of 37190643147 and 37601225884:target/ci-driver)target/debug)The build system's dependency closure is built twice, once for the driver and once for the step that tests it, in two targets by design (
src/cicache.rs's header).What is under the paths, and what a reader reads
Diagnostic run 37693139619 (branch commit
14f49933c, since replaced; its patch is in the first comment) liftedLIMIT, sealed, listed every file under the paths with its size, compressed each directory withtar | zstd -T0, then replayed two readers over the sealed tree with every file's access time reset: A with nothing changed, B with everyCargo.tomlchanged so every path crate rebuilt. It ran on image 20260927.320.1.Seal:
18712 files, 8608140775 B. Compressed whole: 2,020,684,970 B.target/debugtarget/ci-driveruserland/target/x86_64-unknown-linux-gnuuserland/target/debuguserland/target/x86_64-pc-windows-msvcuserland/target/aarch64-apple-darwin~/.cargo/registry/cache,index,git/dbregistry/cachenot, because the replay kept~/.cargo/registry/src, which a real reader does not have and unpacks from itReader times in that run: cold steps 16 min 40 s after a 2 min 47 s driver build; reader A 5 min 11 s; reader B 11 min 30 s; the driver's own rebuild 2.07 s in both, from its incremental state. So the driver's target is reused and stays in the entry.
What a reader does not use is in
target/ci-driver. It is built by the workflow'scargo runbeforecarry()runs, so it alone carried full debuginfo:target/debugalso holds once: 738,377,910 B, against 322,227,512 B there with line tables alone;target/debug, and counted under two names (a hard link);The change
ci.ymlandnightly.yml: thehostjob'senv:givesCARGO_PROFILE_DEV_DEBUG: line-tables-only. The driver's build and every step inherit it.src/ci.rs:carry()no longer sets it, so there is one declaration; the test that assertedcarry()set it asserts only whatcarry()still does..claude/agents/reviewer.md, Caches: "the one variable anenv:gives either" becomes the two. The implementer was not briefed to edit a prompt; the orchestrator gave the edit his go at review (rootCLAUDE.md: an agent updates the step its own change renames). Without the edit the prompt is false of the workflows it describes; the orchestrator decides whether it stands.LIMITis unchanged at 8,000,000,000 B. Its reason holds on re-measurement: at the lowest Linux ratio measured (4.01; the two new ones are 4.19 and 4.26) it stores at most 1,994,138,951 B, and two entries beside main's four toolchain layers (912,484,904 B on 2026-10-07) are 4.9 GB of the 10 GB.Rejected: dropping
target/ci-driverfrom the entry (a reader would pay the 2 to 3 minute cold driver build each run);debug = "line-tables-only"in the root[profile.dev](its comment keepsdebugon for LLDB); raisingLIMITalone (the content was not all reused).Outside bounds, as read
actions/toolkitpackages/cache/src/cache.ts: an archive over10 * 1024 * 1024 * 1024B is not saved.gh api repos/ToyOSOrg/ToyOS/actions/cache/usageon 2026-10-07: 10,948,823,786 B in 25 entries, already past 10 GB: one host entry, main's four layers, and twenty sysroot layers of about 420 MB from pull requests and merge groups. Recorded inissues/the-host-caches-limit-reaches-the-10-gb-only-through-one-measured-ratio.md, with the hard links the seal counts twice (521,656,664 B in the diagnostic run) and the files every read rewrites.What #746 must do
Its dispatch (run 37685714260,
88bcbf4d3) sealed9348536345 B in 20064 fileson image 20260927.320.1;mainplus the diagnostic sealed 8,608,140,775 B on the same image the same evening: 740,395,570 B and 1,352 files more. In its log one step grew: "the apps for linux" compiles 282 units against main's 47, and "userland/*" 225 against 285; every other step is within 2 units. That log has no bytes per directory.So #746's merge must: keep
CARGO_PROFILE_DEV_DEBUGin both workflows'env:(review round 1 found the merge withdb55db96awrites no conflict on these lines), and keepcarry()without it; then dispatchnightly.ymlon its branch and show a seal line underLIMIT. This change took 944,362,661 B offmain's tree (below). If it takes the same off #746's, whose driver builds the same 203 units, that tree seals 8,404,173,684 B, 404,173,684 B over: it stays red until the apps-for-linux step shares its units with the userland step again, as it does onmaininuserland/target/x86_64-unknown-linux-gnu. That projection is arithmetic, not a run of #746.Gates
ci.ymlon this head, run 37713267864 (image 20260927.320.1):hostsuccess —Cache restored from key: host-sealed-Linux-X64-37190643147,built from d47b383cf…: 203 of 2501 sources dated as built,[ci] Host: 78 step(s), all green;toolchain / buildandguest / suitesuccess. It iscargo run -- --ci hoston Linux at this head and the first run ofci.yml's changed job.Nothing was built or tested locally: the owner reserved the development machine's compute while this was written, so no
cargocommand ran, and thesrc/ci.rsedit was made by reading. Every result below is run 37699848749's,nightly.ymldispatched on7f991fbad, read fromgh run view 37699848749 --job 113060424069 --log.host,toolchain / build,tcg / suite,portability-linux,portability-macossuccess;releaseskipped offmainhostjob,cargo run -- --ci seal[ci] Seal: 80 step(s), all green; noREDline in the log; the save skipped offmaincicache::tests::*andci::tests::a_step_of_a_job_that_carries_the_cache_builds_in_its_own_target, each... okcargo run -- --ci hostportability-macosran it, successThe seal, on runner image 20260927.320.1 each time:
and
main's latest failing line, run 37601225884 on image 20261004.327.1:8182940473 B in 17350 files under the cache's paths, above the 8000000000 B an entry may hold.f260e0b98sealed 357,843,252 B less on 20261004.327.1 than on 20260927.320.1 (8,182,940,473 against 8,540,783,725), so the logs say that image seals smaller, not larger. This head has not been sealed on it.hostran 20 min 26 s (23:02:01Z to 23:22:27Z), its seal step 20 min 15 s, of which the driver's cold build was 2 min 24 s.main's failinghosttook 20 min 13 s on the same image (run 37292450697) and 13 min 0 s on the newer one (run 37601225884).Unsure of
issues/a-red-nightly-on-main-is-reported-to-nobody.mdis closed.main's alone. The reader's times above are the diagnostic's replay, made before this change.hostreader between this landing and the next nightly loses anything: no entry on either image matches today, and every reader runs cold.🤖 Generated with Claude Code
https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A