Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
7588883
Track: a crate has two consumers or says why it is alone
Japabu Sep 29, 2026
6581610
Merge remote-tracking branch 'origin/main' into wt/toyos-crates
Japabu Sep 29, 2026
b64b39e
Track: code used by one program lives in that program
Japabu Sep 29, 2026
2b2aafc
Place operating lessons in the agent role files and the ARM64 ruling …
Japabu Sep 29, 2026
45f4d61
Merge remote-tracking branch 'origin/main' into wt/toyos-lessons
Japabu Sep 29, 2026
ced37a6
Answer review of #613
Japabu Sep 29, 2026
88dbfed
Fold the kill-by-PID clause into CLAUDE.md and tighten the opening pa…
Japabu Sep 29, 2026
7eb4428
issues: write the owner's rulings of 2026-09-30 where they govern
Japabu Sep 30, 2026
90e2b17
File: two builds of one LLVM key differ in their bytes
Japabu Sep 30, 2026
71b3a57
issues: the microcode defect points at #636, and the review's four cuts
Japabu Sep 30, 2026
cf155a0
Merge remote-tracking branch 'origin/main' into wt/toyos-n2scout
Japabu Oct 1, 2026
865239c
issues: two builds of one LLVM key are a defect M4 waits on, closed b…
Japabu Oct 1, 2026
6b2eef1
issues: a panic is never an accident, decided and enforced in seven c…
Japabu Oct 1, 2026
9cd6f8f
Merge remote-tracking branch 'origin/main' into wt/toyos-nopanic
Japabu Oct 1, 2026
066e1a0
The ABI is free to change, and the kernel keeps only what only a kern…
Japabu Oct 1, 2026
f469e90
issues: the no-panic track names the owner's set and gates a bare #[a…
Japabu Oct 1, 2026
ec166f1
The kernel takes on only what userland cannot, stated once in CLAUDE.md
Japabu Oct 1, 2026
3343680
Merge #666 (wt/toyos-abirule): the ABI is free to change, and the ker…
Japabu Oct 1, 2026
82a28cd
Merge #665 (wt/toyos-nopanic): the no-panic track, and seven stale cr…
Japabu Oct 1, 2026
a179e40
Merge #645 (wt/toyos-rulings): the owner's 2026-09-30 rulings, withou…
Japabu Oct 1, 2026
256913e
Merge #613 (wt/toyos-lessons): operating lessons in the role files an…
Japabu Oct 1, 2026
daa7f21
Merge #604 (wt/toyos-crates): track, code used by one program lives i…
Japabu Oct 1, 2026
456b142
Merge #646 (wt/toyos-n2scout): two builds of one LLVM key differ in t…
Japabu Oct 1, 2026
64e468e
#666: the kernel rule replaces the whole span; the ABI issue's test s…
Japabu Oct 1, 2026
2b301bc
#613 keeps what is still true and not already said; CPU microcode is …
Japabu Oct 1, 2026
dc3f158
#665: every lossy cast, an expect per stop, and stage 3 reaches the k…
Japabu Oct 1, 2026
b250bc2
#604: the crate track's counts and premises, measured on main
Japabu Oct 1, 2026
dc45e23
#646: the LLVM-bytes defect gets a host exit, is told apart, and leav…
Japabu Oct 1, 2026
af9fe18
Merge remote-tracking branch 'origin/main' into wt/toyos-prosebatch
Japabu Oct 1, 2026
77a8f12
Round 2 of #673: microcode is its own case, one expect per site, no k…
Japabu Oct 1, 2026
75bf096
Round 3 of #673: the services pass stage 3's step, and the crate trac…
Japabu Oct 1, 2026
e3013a3
Round 4 of #673: the crate track moves no input boundary, a service's…
Japabu Oct 1, 2026
e07ca9b
Merge origin/main (#674) into wt/toyos-prosebatch
Japabu Oct 1, 2026
42f3fb5
Round 5 of #673: the loader and every mode's boot starts enter the no…
Japabu Oct 1, 2026
d974223
Round 6 of #673: a system service is a program that declares `exempt.…
Japabu Oct 1, 2026
7131598
Round 7 of #673: a system service is a program a shipped mode marks `…
Japabu Oct 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .claude/agents/implementer.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ One brief, one worktree, one branch. What the brief does not name you do not tou
find off your path is filed in `issues/`, never fixed. If something blocks you, stop and say so in
one clause; do not work around it.

Before adding kernel behaviour, ask whether userland can own it. When the clean design changes the
ABI, change the ABI; never pick a lesser design to avoid that. A clean design that reaches past
your fence blocks you.

## Measure, build, test

Where hardware or anything uncertain is involved, take the cheap measurement before you build on a
Expand All @@ -26,6 +30,9 @@ guess. Then build, then test before anyone reviews:
- Long commands run in the background with output to a file under the job scratchpad the brief
names. Stay inside one turn while anything runs: sleep at most two minutes, print a line, check
again. Ten minutes of silence kills you, and ending a turn to announce a wait strands the work.
- Nothing a pull request's evidence rests on, mutation patches and run logs included, lives only in
a temporary directory: `/tmp` is wiped when the CLI restarts. Post mutation patches to the pull
request as a comment.
- A mutation is a measurement only once the mutated tree is shown to build. Apply it as a checked
patch, restore it in the same script, and leave the tree clean.
- Never a flat wait, in code or in a test: wait on the event, bounded by a timeout that fails
Expand Down Expand Up @@ -56,9 +63,9 @@ Fork sources live outside this repository: a search for callers must also cover

`git commit -F <file>`, never `-m`. No `--amend`, no rebase, no force: merge `origin/main`, never
rebase onto it. Never run `git submodule` in a linked worktree: it writes `core.worktree` into the
fork's shared config and breaks git in the primary checkout's `rust/`. Never touch `toyos-abi/src`, `toyos/src` or `userland/libc/src` unless the brief is
an ABI brief. A new dependency is taken where it is the cleanest path: a general, widely used
crate (root `CLAUDE.md`, "Dependencies"), and the pull request says why.
fork's shared config and breaks git in the primary checkout's `rust/`. A new dependency is taken
where it is the cleanest path: a general, widely used crate (root `CLAUDE.md`, "Dependencies"), and
the pull request says why.

Push from your branch, never `main`, with `git status --porcelain` empty: `git push -u origin
<branch>`, and `gh pr create --draft` at the first push. The pull request body is the handoff the reviewer reads,
Expand Down
12 changes: 5 additions & 7 deletions .claude/agents/reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ above; otherwise it is a NOTE.
- **Fit.** Does the tree already do this? Is each new thing where it belongs: a pure decision in a
pure crate, the user/kernel boundary in `toyos-userbound`, a device claim in a userland server?
One declaration read by every reader, refusal by name, authority moved in by the parent. Zero
legacy: no shim, no workaround, no silent default. A new dependency only where it is the
legacy: no shim, no workaround, no silent default. A BLOCKER each: a kernel addition that
userland could own; a design made worse to spare the ABI. A new dependency only where it is the
cleanest path, a general and widely used crate the pull request says why it takes; no new
fetch. Nothing outside the brief's fence.
Assembly, a naked function and a `core::arch` or `std::arch` path live only in an
Expand Down Expand Up @@ -75,12 +76,9 @@ above; otherwise it is a NOTE.
A file added to or deleted from `tests/testcases/tinycc/` moves the count
`tests/testcases/LICENSE` states in the same diff, and `46_grep.c` never comes back. Nothing
else is tracked under `tests/testcases/` but that `LICENSE` and `system.toml`.
- **What no gate reads.** A BLOCKER each: a diff that declares a retired ABI name or reuses a
retired syscall, `SYS_DEBUG` action or inbox op number (the retired numbers are
`kernel/src/syscall/dispatch.rs`'s `retired_syscalls!` and the "formerly …" and "retired and
unused" entries in `toyos-abi/src/syscall.rs` and `toyos-abi/src/inbox.rs`; the retired names
include `SharedToken` and `services::connect`); a workspace member's `Cargo.toml` declaring `[profile]` or `[patch]`, which
cargo ignores with only a warning; a new package without a `description` saying what it is.
- **What no gate reads.** A BLOCKER each: a workspace member's `Cargo.toml` declaring `[profile]`
or `[patch]`, which cargo ignores with only a warning; a new package without a `description`
saying what it is.
A new cargo feature or `cfg` arm of one, and every arm a changed `src/clippy.rs` shape stops building, is shown linted in the pull request body: a `mem::forget` planted in that arm turns `cargo run -- --clippy` red.
- **Growth.** Every line is a responsibility, not an asset. State the branch's net lines
(`git diff --shortstat origin/main...HEAD`), production and tests apart. Production code that grows
Expand Down
10 changes: 5 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ToyOS

An operating system built from scratch in Rust, held to a production-grade engineering bar — the bar is the changes, not yet the product. Modern x86-64 hardware (2020+), UEFI only; ARM64 planned — keep the architecture portable. The quality bar is shipping software: correct, efficient, minimal, zero silent debt. A tracked weakness is still a weakness: the honest answer about current state is "known, tracked, still true" — never "we have an issue for that."
A general-purpose operating system built from scratch in Rust, held to a production-grade engineering bar — the bar is the changes, not yet the product. Modern x86-64 hardware (2020+), UEFI only; ARM64 planned — keep the architecture portable. Its test machines decide its feature set, never its design. The quality bar is shipping software: correct, efficient, minimal, zero silent debt. A tracked weakness is still a weakness: the honest answer about current state is "known, tracked, still true" — never "we have an issue for that."

## Where the rest of this lives

Expand Down Expand Up @@ -38,13 +38,13 @@ A subdirectory `CLAUDE.md` loads when a file in that subtree is `Read`, and not

> A snapshot, deliberately shallow — always read the code.

**Kernel** — minimal; new additions are discussed and justified. Resource management, scheduling, process lifecycle, filesystem, device arbitration. 2 MB pages, demand paging, PIE binaries, full SMP.
**Kernel** — takes on only what userland cannot. 2 MB pages, demand paging, PIE binaries, full SMP.

**Userspace daemons** — compositor, netd, soundd, sshd, logd. Each claims a device or capability from the kernel and serves its function; crash one and the kernel is fine.

**The log is a userland file.** `/system/bin/logd` reads records on a cursor and owns `/log`; the kernel keeps the record ring, the console and the panel, and writes no file. `SYS_FSYNC` reaches the device's cache flush because logd's durability claim rests on it.

**Syscall ABI** — `toyos-abi/`: struct layouts, syscall numbers, typed wrappers; completely unstable, read the code. Never add or change a syscall without discussion; a deleted syscall's number is retired, never reused. `toyos/` builds on it with typed handles, IPC framing, ports, namespaces and `surface` — userland uses `toyos`, the kernel uses `toyos-abi` only.
**Syscall ABI** — `toyos-abi/`: struct layouts, syscall numbers, typed wrappers; completely unstable. The cleanest, most sustainable ABI beats convenience; a removed number is free. `toyos/` builds on it with typed handles, IPC framing, ports, namespaces and `surface` — userland uses `toyos`, the kernel uses `toyos-abi` only.

**Capabilities** — a process holds exactly what its parent moved into it, and among kernel objects there is nothing it can name to get more. No registry, no connect-by-name, no pid-as-authority: `/system/bin/init` builds every program's namespace and device claims from `system.toml` before spawning it, and a handle a process does not hold is a bug in that process — the kernel ends it rather than answering a word it can ignore. **Isolation is non-negotiable, and the filesystem is inside it**: a process names only the paths in the view its parent built for it, the unit of isolation is the program, and a user is the part of the tree a session was handed. Not yet true of files: the kernel still resolves every path against one machine-wide tree until the storage track's per-program views land.

Expand All @@ -60,7 +60,7 @@ A subdirectory `CLAUDE.md` loads when a file in that subtree is `Read`, and not

**Rust** and **QEMU** for development, on any host OS and architecture — the development machine is nothing special. Beside them, where no Rust tool does the job, only C or C++ tools ToyOS can one day build and run (Python, Perl, CMake, make), each declared. No binary for one host OS alone: a macOS binary is a hard no, and "only for tests" does not soften it. ToyOS's own code is Rust; it writes no Python, Perl or shell of its own. Only general and widely used crates — one that does *our* job we write ourselves, and a driver crate never; third-party crates are used as published, and a fork carries a change written to upstream quality and goes when upstream has it. No upstream pull requests are sent for now: ToyOS needs more attention and more contributors before upstream projects take it seriously, and upstreams tend to refuse AI-first projects and their contributions. A third-party source ToyOS cannot build without changing it is carried as an unmodified-source packaging mirror with a byte-identity gate, not as a fork. The north star is **self-hosting**: nothing — build, test, or verification — rests on a host binary. Ask of anything new: could this ever run inside ToyOS? Self-hosting means ToyOS rebuilds itself on ToyOS and reproduces the host's bytes; a bootstrap from source with no binary seed is out of scope.

Vendor firmware a device verifies by its maker's signature may be shipped: pinned by version and hash, redistributable unmodified, recorded in `NOTICE`, and loaded only by that device's own driver through its IOMMU domain; it never executes on the CPU.
Vendor firmware a device or CPU verifies by its maker's signature may be shipped: pinned by version and hash, redistributable unmodified, recorded in `NOTICE`. A device's is loaded only by its own driver through its IOMMU domain and never executes on the CPU; CPU microcode is loaded by the kernel.

The bar is not yet the tree: `.claude/agents/reviewer.md`, "Arrivals", says where every host tool and every standing failure is declared. `NOTICE` names every committed third-party file with its hash, upstream and licence; an image carrying `DOOM1.WAD` may not be sold.

Expand All @@ -74,7 +74,7 @@ The testing rules live where they are enforced: the PR gate and the nightly in `
- `cargo run` builds everything (toolchain, kernel, bootloader, userland, image) and launches QEMU; `--build-only` skips the launch. `cargo test` runs the QEMU harness; `cargo run -- --ci host` runs every host suite, as the PR gate's required `host` check does.
- **Agents never run QEMU.** An agent verifies with host tests and builds the image at most; the orchestrator runs every guest test, one suite at a time.
- **Both produce large output**: run them in the background and read the output file — `[N characters truncated]` means data was lost. A full boot is under a second; incremental builds finish in seconds.
- **Leave the machine as you found it.** The development machine is shared: every agent stops what it started, removes the worktrees and scratch build output it no longer needs, and never leaves an emulator, a build or a watcher running.
- **Leave the machine as you found it.** The development machine is shared: every agent stops what it started, killing only by PID and waiting out a build that holds the global lock, and removes the worktrees and scratch build output it no longer needs.

## Repository layout

Expand Down
9 changes: 3 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,7 @@ opt-level = 2
# `opt-level = 0`. Nothing fail-fast is traded for it — `opt-level` does not
# touch `debug-assertions`, which cargo still passes as `on` (checked with
# `cargo build -p toyos-fat32 -v`), and rustc derives `overflow-checks` from
# that. The pure crates' hostile-input tests keep both knobs that *found* the
# two crafted-ELF kernel panics in `issues/`.
# that.
#
# **One member depends on this line for correctness rather than for speed, and
# its own manifest says so at length — this is the other half of that
Expand All @@ -218,10 +217,8 @@ opt-level = 3

# The one profile every guest binary is built with. Optimised, because an
# unoptimised guest mismeasures everything under TCG;
# debug-assertions and overflow-checks on, because fail-fast beats speed here
# and both crafted-ELF kernel panics in `issues/` were *found* by
# an overflow check. `--release` would turn the last two off silently, so this
# build system no longer has the flag.
# debug-assertions and overflow-checks on, because fail-fast beats speed here.
# `--release` would turn the last two off silently.
#
# It is declared here rather than in `toyos-ld/Cargo.toml` because cargo
# ignores `[profile]` in a workspace member and that is one now. It is the only
Expand Down
6 changes: 2 additions & 4 deletions bootloader/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,8 @@ uefi-services = { version = "0.23.0", features = ["panic_handler", "logger"] }

# The one profile every guest binary is built with. Optimised, because an
# unoptimised guest mismeasures everything under TCG;
# debug-assertions and overflow-checks on, because fail-fast beats speed here
# and both crafted-ELF kernel panics in `issues/` were *found* by
# an overflow check. `--release` would turn the last two off silently, so this
# build system no longer has the flag.
# debug-assertions and overflow-checks on, because fail-fast beats speed here.
# `--release` would turn the last two off silently.
[profile.toyos]
inherits = "dev"
opt-level = 2
Expand Down
67 changes: 67 additions & 0 deletions issues/build/code-used-by-one-program-lives-in-that-program.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
status: open
kind: track
opened: 2026-09-29
---

# Code used by one program lives in that program

A crate exists because two programs share it. What only the kernel uses is the
kernel package's, what only one userland program uses is that program's, and
shared crates with one subject are one crate (owner, 2026-09-29). No gate holds
the layout: step 2 writes it into `.claude/agents/reviewer.md`'s Fit line,
which until then puts a pure decision in a pure crate.

An input boundary is a crate of its own, whoever uses it: the no-panic
track (`issues/kernel/a-panic-is-never-an-accident.md`) forbids its tier 1 per
crate, and a crate that holds a tier-2 stop cannot forbid the set. A crate is
one when its own source decodes a word from outside its trust, or bounds it by
its form: hardware registers, firmware tables, disk bytes, network bytes, or
what another program sent, a syscall's arguments included. A lookup of a key a
program named is neither. No step here merges one; each stays a crate under
the no-panic track.

Every step lands green on `--ci host` and `--build-only`. Test counts are what
`cargo test -p <package> -- --list` lists today.

1. **Delete `toyos-userpin`.** It models the pin invariant and names nothing
the kernel defines; `munmap_reissues_read_window` holds the kernel to it.
Check: `git grep toyos-userpin -- ':!issues/'` is empty.
2. **The kernel's library.** `kernel/pure/` is the `kernel` package's lib, and
its bin is `test = false`. `toyos-pcid`, `toyos-proclife` and `toyos-sched`
move in. `kernel-loom` and `toyos-sched/loom` become `kernel/loom/`, and
`toyos-sched/sim` `kernel/sim/`. The harness dev-depends on the kernel, and
the build system does not depend on it. The library has no `tests/`, since
an integration test builds the binary for the host. `--ci host` tests it
with `sched-check`, the feature scheduler tests need. The Fit line
states this track's rule.
Closes `issues/build/the-pcid-negative-control-runs-nowhere.md`.
Check: the library lists at least 135 tests, `kernel/loom` 79 and
`kernel/sim` 53, and `--clippy` lints the library's tests on the host.
Every moved control, and pcid's `counting-allocator`, reds with its verdict,
and `declared_model_controls` reads the kernel's manifest and every one in
the host workspace, not a list. An `unsafe {}` planted in a module that was
`forbid(unsafe_code)` does not compile. `cargo tree -e normal -p
toyos-build` names no `kernel`.
3. **libc's host test moves to the tests.** `toyos-libc-copies` moves to
`tests/libc-arch/`.
Check: `--ci host` runs there every test the package lists today, and
`--clippy` lints them.
4. **A crate one package uses goes under it, a crate of its own.** A crate of
this tree with exactly one consumer moves under it. Its consumers are the
packages that name it as a dependency of any kind, under any `cfg`, as
`cargo metadata --no-deps` reads every manifest `git ls-files '*Cargo.toml'`
lists, excluded packages and `tests/` included; a crate the images ship as a
program of its own counts as its own consumer. A move under a userland
program lands with `src/userlandhost.rs`'s survey gating a nested crate's
tests, which it lists as escapes today.
Check: `--ci host` runs every test each package lists today, and `--clippy`
lints them.
5. **`toyos-fat32-check` goes under `toyos-fat32/`.** The build and
`toyos-fat32`'s tests both use it, and it moves to `toyos-fat32/check/`,
beside the one subject it judges.
Check: `--ci host` runs every test the package lists today, and `--clippy`
lints them.

**Exit:** no directory this file names as moved or merged still exists, and
step 4's count finds no crate outside its one consumer.
17 changes: 0 additions & 17 deletions issues/build/doom-jpg-shows-ids-art-under-no-recorded-terms.md

This file was deleted.

19 changes: 19 additions & 0 deletions issues/build/the-pcid-negative-control-runs-nowhere.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
status: open
kind: tooling
opened: 2026-09-29
---

# `toyos-pcid`'s negative control runs nowhere

`toyos-pcid/Cargo.toml` declares `counting-allocator`, the control that reverts
`PcidPool` to the counter that reissued a live tag. Nothing runs it:
`src/ci.rs`'s `CONTROLS` has no row for it, and `src/build.rs`'s
`declared_model_controls` does not read `toyos-pcid/Cargo.toml`, so
`every_model_control_is_run` cannot notice. The control still has teeth, run by
hand: `cargo test -p toyos-pcid --features counting-allocator` exits 101 with
`tests::two_live_address_spaces_never_share_a_pcid ... FAILED`.

**Exit:** the control is a `CONTROLS` row demanding that `FAILED` line, and
`declared_model_controls` reads every manifest in the host workspace rather than
a list, so a control declared in a crate the list forgot reds.
Loading
Loading