From eecd6003db213486f89d609c5c18b863625eaa95 Mon Sep 17 00:00:00 2001 From: japabu Date: Sun, 11 Oct 2026 20:48:41 +0200 Subject: [PATCH 1/3] CI runs once per pull request, in the merge queue The owner's ruling. A pull request's run did the merge queue's work a second time: `gh pr ready` started it, auto-merge waited for it, and the queue then ran the same tree again. Over the last 89 ci.yml runs the pull request runs took 1651 job-minutes against the queue's 2164. On `pull_request` every check now reports skipped, which GitHub reads as success for a required check, so a pull request still enters the queue. The skip sits on the job that names each check: `host` in ci.yml, `build` in toolchain.yml and `suite` in guest.yml. A skipped caller of a reusable workflow reports its own name (`toolchain`, `guest`), never the callee's `toolchain / build` or `guest / suite`; a draft's run on this repository shows exactly that, so a caller-level skip would leave the required `guest / suite` waiting forever. The `types:` list goes: no run does work on a pull request, so a draft marked ready needs no fresh one. Caches: a pull request's entries were scoped to its merge ref and served only that pull request's re-runs; the queue's ref is its own branch, read by no other. Main's entries are written by publish.yml's toolchain on every push to main and by the nightly, as before, and every queue run reads them. Root CLAUDE.md, reviewer.md's two workflow rules and one issue's text named the pull request's run; they say what now holds. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C --- .claude/agents/reviewer.md | 4 ++-- .github/workflows/ci.yml | 10 ++++++---- .github/workflows/guest.yml | 2 ++ .github/workflows/toolchain.yml | 2 ++ CLAUDE.md | 2 +- issues/ci-runs-the-whole-guest-suite.md | 6 +++--- 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.claude/agents/reviewer.md b/.claude/agents/reviewer.md index 44a3af67491..b4333fddca3 100644 --- a/.claude/agents/reviewer.md +++ b/.claude/agents/reviewer.md @@ -96,7 +96,7 @@ if it meets the bar above; otherwise it is a NOTE. `cargo run -- --ci `, `seal` in the writer and `host` in the reader, and nothing else; the reader restores before that step, and the writer saves after it. The save's guard, `github.ref == 'refs/heads/main'`, is the only step-level `if:` in those jobs; ci.yml's `host` - skips only a draft, and nightly's `host` has no `if:` of its own, a skipped job being a green + skips only a `pull_request` run, and nightly's `host` has no `if:` of its own, a skipped job being a green check. No `continue-on-error`, `shell:`, `defaults:` or cargo `runner` reaches them. nightly.yml's `on:` is one daily `schedule` and `workflow_dispatch`. - **Workflows.** A BLOCKER each: @@ -104,7 +104,7 @@ if it meets the bar above; otherwise it is a NOTE. - `nightly.yml`'s `release` is the only job granted `contents: write`. - No workflow runs on `pull_request_target`, `workflow_run`, `issue_comment` or any other trigger that runs code other than main's on main's ref. - No workflow or job declares `cache-mode: write` or `write-only`. - - `guest / suite` has no job-level `if:`, and a job that calls it runs whatever `toolchain` concluded: a skipped required check reads as green. + - `guest / suite`'s one job-level `if:` skips a `pull_request` run, and a job that calls it runs whatever `toolchain` concluded: a skipped required check reads as green. - A job that saves a cache entry runs only `cargo run -- --ci `. - **Growth.** Every line is a responsibility, not an asset. State the branch's net lines (`git diff --shortstat origin/main...`), production and tests apart. Production code that diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7521a0b6afb..aadcd6c76a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,12 @@ name: ci +# CI runs once per pull request, in the merge queue. On `pull_request` every +# check reports skipped, which a required check reads as green; the skip sits +# in the called workflow's job, because a skipped caller reports its own name +# and not `toolchain / build` or `guest / suite`. on: pull_request: branches: [main] - types: [opened, synchronize, reopened, ready_for_review] merge_group: concurrency: @@ -16,7 +19,7 @@ permissions: jobs: host: - if: github.event_name == 'merge_group' || github.event.pull_request.draft == false + if: github.event_name != 'pull_request' runs-on: ubuntu-24.04 timeout-minutes: 45 env: @@ -43,14 +46,13 @@ jobs: - run: cargo run -- --ci host toolchain: - if: github.event_name == 'merge_group' || github.event.pull_request.draft == false uses: ./.github/workflows/toolchain.yml # A required check that is skipped reads as green, so this runs whatever # `toolchain` concluded. guest: needs: toolchain - if: ${{ !cancelled() && (github.event_name == 'merge_group' || github.event.pull_request.draft == false) }} + if: ${{ !cancelled() }} uses: ./.github/workflows/guest.yml with: kvm: true diff --git a/.github/workflows/guest.yml b/.github/workflows/guest.yml index 712965682a4..2879f7e9b4c 100644 --- a/.github/workflows/guest.yml +++ b/.github/workflows/guest.yml @@ -17,6 +17,8 @@ on: jobs: suite: + # ci.yml's pull request run reports this check skipped. + if: github.event_name != 'pull_request' runs-on: ubuntu-24.04 # A wedge guard, not a budget. timeout-minutes: 60 diff --git a/.github/workflows/toolchain.yml b/.github/workflows/toolchain.yml index 22ba02bfbd9..bd8c3a105b5 100644 --- a/.github/workflows/toolchain.yml +++ b/.github/workflows/toolchain.yml @@ -10,6 +10,8 @@ on: jobs: build: + # ci.yml's pull request run reports this check skipped. + if: github.event_name != 'pull_request' # Bare, not a container: its glibc is a build's floor. runs-on: ubuntu-24.04 timeout-minutes: 350 diff --git a/CLAUDE.md b/CLAUDE.md index 3b0691e139c..a51d2762b21 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -63,7 +63,7 @@ Vendor firmware a device or CPU verifies by its maker's signature may be shipped ## Build & test -- `cargo run` builds everything (toolchain, kernel, bootloader, userland, image) and launches QEMU; `--build-only` skips the launch. `cargo test` runs the QEMU harness, and `cargo test --test toyos-build -- --metal` the T14's. `cargo run -- --ci host` runs every host suite: it is the `host` check a ready pull request and the merge queue run. The guest suite is their `guest / suite` check, under KVM; the nightly runs it under TCG. +- `cargo run` builds everything (toolchain, kernel, bootloader, userland, image) and launches QEMU; `--build-only` skips the launch. `cargo test` runs the QEMU harness, and `cargo test --test toyos-build -- --metal` the T14's. `cargo run -- --ci host` runs every host suite: it is the merge queue's `host` check, which a pull request's own run reports skipped. The guest suite is its `guest / suite` check, under KVM; the nightly runs it under TCG. - **A behaviour is tested on the cheapest tier that reaches it**: a type that makes the bug unrepresentable, then a host test, then a metal row on the T14, and a QEMU guest test last. - **Agents run their own guest tests**, the whole suite or a filter, side by side. **Every metal bench is the orchestrator's alone: no other agent runs `--metal` without `--metal-readback`, which touches no machine.** - **Timing and audio verdicts come only from metal.** A QEMU test asserts order, completion, content and counts, never how long something took, and plays no audio; its only clock is a hang ceiling. diff --git a/issues/ci-runs-the-whole-guest-suite.md b/issues/ci-runs-the-whole-guest-suite.md index 2bcab2c021b..a9903b33e58 100644 --- a/issues/ci-runs-the-whole-guest-suite.md +++ b/issues/ci-runs-the-whole-guest-suite.md @@ -8,15 +8,15 @@ opened: 2026-10-10 The owner's ruling: guest tests run on demand, and CI's small tier holds only the tests that must break when something important breaks. `guest.yml` runs `cargo run -- --ci guest` (`build/ci.rs`, -`guest`), which is every registration in `tests/toyos.rs`, on every ready pull request, in the -merge queue (`ci.yml`) and nightly (`nightly.yml`); no tier is declared, so no review can keep a +`guest`), which is every registration in `tests/toyos.rs`, in the merge queue (`ci.yml`) and +nightly (`nightly.yml`); no tier is declared, so no review can keep a new guest test out of it. The question: which tests belong in the tier. Once the owner names them, the orchestrator dispatches the reshape. **Exit condition**: `--ci guest` runs a declared list in `tests/toyos.rs` and nothing else, the -rest of the suite runs on demand, and root `CLAUDE.md`'s "The guest suite is their `guest / +rest of the suite runs on demand, and root `CLAUDE.md`'s "The guest suite is its `guest / suite` check" and `reviewer.md`'s **Guest tests** say so in the same diff. The same diff deletes `ON_DEMAND` in `tests/toyos.rs`, the stopgap that keeps a test out of an unfiltered run until then: once `--ci guest` runs a declared list, everything outside it is on demand. From 5c0bee982a9e8f40f95b1070661a2dabdf28a3e3 Mon Sep 17 00:00:00 2001 From: japabu Date: Sun, 11 Oct 2026 20:48:42 +0200 Subject: [PATCH 2/3] issues: general crates are published and ToyOS consumes them The owner's request: a filed track, added unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C --- ...s-are-published-and-toyos-consumes-them.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 issues/general-crates-are-published-and-toyos-consumes-them.md diff --git a/issues/general-crates-are-published-and-toyos-consumes-them.md b/issues/general-crates-are-published-and-toyos-consumes-them.md new file mode 100644 index 00000000000..26deaa1f477 --- /dev/null +++ b/issues/general-crates-are-published-and-toyos-consumes-them.md @@ -0,0 +1,33 @@ +--- +status: open +kind: track +opened: 2026-10-11 +--- + +# General-purpose crates are published, and ToyOS consumes them + +**The owner's direction:** +- Crates that are general-purpose get published on crates.io with an owner of their own (an AI owner). +- ToyOS becomes one consumer of each. It may request changes, but the crate stays general. +- Not during the current rapid-development era: nothing is built for this until the owner says go. + +**Candidates** (scan of the restructured tree, 2026-10-11), best first: +1. `toyos-fat32-check`: FAT32 checker written from Microsoft's fatgen103. No ToyOS dependencies, `no_std`. +2. `toyos-fat32`: FAT32 read/write hardened against hostile volumes. It needs `toyos-wallclock` folded in, and its tests made self-contained. +3. `toyos-cpuvuln`: Linux's CPU vulnerability verdicts from CPUID/MSR facts. Its parity is pinned to one Linux tag. +4. The bcachefs `upstream` reader, split out of `lib/bcachefs`. +5. `toyos-microcode` and `toyos-usbhid`. +6. Later: the TCP stack (once `wire`'s grab-bag is factored out), the SSH server (once its track and defect issues close), and the AML interpreter (once it is decoupled from `toyos-acpi`). + +**Not candidates:** sha2/sha2-hw, random, crc, wallclock and tmpdir. Widely used crates already do their jobs. + +**What exists:** `build/sdkversion.rs`'s `PUBLISHED` already publishes six SDK crates from the monorepo, with crates.io trusted publishing. + +**Open design:** +- Publish from the tree, or move out to a repository of its own? Publishing from the tree keeps atomic changes and self-hosting. A repository of its own gives the crate a real owner and its own history. +- How an owning agent is run. +- How ToyOS pins and requests. + +**Exit:** a crate ToyOS used to build from its tree is published under its own owner, and ToyOS builds against the published version. + +**Owner:** the owner (Jan) decides when it starts. From 259b712980d8ce05afb2c8409c03b64c49cc7a6b Mon Sep 17 00:00:00 2001 From: japabu Date: Sun, 11 Oct 2026 21:07:03 +0200 Subject: [PATCH 3/3] ci keeps ready_for_review, drops its dead concurrency group; the guest rule names its if: exactly A draft opened under the old workflow carries only the callers' names (`host`, `toolchain`, `guest`), never `guest / suite`; without a run on `ready_for_review` its required check would stay expected and auto-merge would never enqueue it. The run costs about a second. The concurrency group governed only one-second skip runs and merge-group refs that are unique per group, so it cancelled nothing. reviewer.md's guest rule now names the one `if:` it admits, so an `if:` that would skip merge_group stays a BLOCKER. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C --- .claude/agents/reviewer.md | 2 +- .github/workflows/ci.yml | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.claude/agents/reviewer.md b/.claude/agents/reviewer.md index b4333fddca3..28eedce959d 100644 --- a/.claude/agents/reviewer.md +++ b/.claude/agents/reviewer.md @@ -104,7 +104,7 @@ if it meets the bar above; otherwise it is a NOTE. - `nightly.yml`'s `release` is the only job granted `contents: write`. - No workflow runs on `pull_request_target`, `workflow_run`, `issue_comment` or any other trigger that runs code other than main's on main's ref. - No workflow or job declares `cache-mode: write` or `write-only`. - - `guest / suite`'s one job-level `if:` skips a `pull_request` run, and a job that calls it runs whatever `toolchain` concluded: a skipped required check reads as green. + - `guest / suite`'s only job-level `if:` is `github.event_name != 'pull_request'`, and a job that calls it runs whatever `toolchain` concluded: a skipped required check reads as green. - A job that saves a cache entry runs only `cargo run -- --ci `. - **Growth.** Every line is a responsibility, not an asset. State the branch's net lines (`git diff --shortstat origin/main...`), production and tests apart. Production code that diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aadcd6c76a2..d23d5844aad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,12 +7,9 @@ name: ci on: pull_request: branches: [main] + types: [opened, synchronize, reopened, ready_for_review] merge_group: -concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: true - # No job here writes: a toolchain release is main's publisher's alone. permissions: contents: read