diff --git a/.claude/agents/implementer.md b/.claude/agents/implementer.md index a4d1bb91c6a..4012e6fe943 100644 --- a/.claude/agents/implementer.md +++ b/.claude/agents/implementer.md @@ -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 @@ -56,8 +60,7 @@ Fork sources live outside this repository: a search for callers must also cover `git commit -F `, 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. No new dependency. +fork's shared config and breaks git in the primary checkout's `rust/`. No new dependency. Push from your branch, never `main`, with `git status --porcelain` empty: `git push -u origin `, and `gh pr create --draft` at the first push. The pull request body is the handoff the reviewer reads, diff --git a/.claude/agents/reviewer.md b/.claude/agents/reviewer.md index c780b0dee34..d4418ca63fb 100644 --- a/.claude/agents/reviewer.md +++ b/.claude/agents/reviewer.md @@ -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. No new + 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. No new dependency or fetch. Nothing outside the brief's fence. Assembly, a naked function and a `core::arch` or `std::arch` path live only in an architecture's own module; `target_arch` only there, in its selector, in `src/arch.rs` and in @@ -68,12 +69,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`, `system.toml` and `hello.c`. -- **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 diff --git a/CLAUDE.md b/CLAUDE.md index 0b910305b39..7a2bf0b0da0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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. Resource management, scheduling, process lifecycle, device arbitration; files: see Capabilities. 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. diff --git a/issues/design-debt/the-abi-still-keeps-retired-syscall-numbers.md b/issues/design-debt/the-abi-still-keeps-retired-syscall-numbers.md new file mode 100644 index 00000000000..59429a59c1b --- /dev/null +++ b/issues/design-debt/the-abi-still-keeps-retired-syscall-numbers.md @@ -0,0 +1,42 @@ +--- +status: open +kind: defect +opened: 2026-10-01 +--- + +# The ABI still keeps retired syscall numbers + +The ABI is completely unstable and a removed syscall's number is free (owner, +2026-10-01): there are no retired numbers and no compatibility shims. The tree +still keeps them: + +- `kernel/src/syscall/dispatch.rs`'s `retired_syscalls!` names deleted calls so + that "an old binary is told which call it was", logging each call of one; + every other unassigned number answers `InvalidArgument` silently. +- `toyos-abi/src/syscall.rs` and `toyos-abi/src/inbox.rs` carry a "formerly …" + or "retired and unused" entry per deleted syscall, `SYS_DEBUG` action and + inbox op, `SYS_INBOX_SETUP`'s doc states the retirement rule, and + `kernel/src/inbox/mod.rs` names op 2 retired. +- `toyos-abi/src/syscall.rs`'s `device_classes!` keeps device classes 3 (`Nic`) + and 4 (`Audio`) "retired rather than reused", and the decode test in + `toyos-abi/src/inventory.rs` refuses them as retired. +- `tests/toyos-rust-tests/src/bin/log_hold.rs`, + `tests/toyos-rust-tests/src/bin/panic_halts_first.rs`, + `tests/common/origin.rs` and `tests/toyos.rs` take syscall 26 as their logged + refusal and read `syscall 26 is retired`. +- Plans still follow the old rule: + `issues/kernel/sys-clock-realtime-is-now-a-format-of-sys-clock-epoch.md`, + `issues/kernel/a-childs-end-is-an-event-and-a-parent-takes-its-children-down.md`, + `issues/kernel/the-kernel-still-parses-what-userland-writes.md`, + `issues/kernel/the-capability-end-state-is-twelve-answers.md`, + `issues/diagnostics/the-kernel-keeps-nothing-it-enumerates.md`, + `issues/diagnostics/no-cyclictest.md`, and + `issues/isolation/the-supervisor-is-host-tested-and-owns-the-stop.md`, whose + "ABI brief" names a gate `.claude/agents/implementer.md` no longer has. + +**Exit**: `retired_syscalls!` and every retirement entry are gone, a deleted +number answers as an unassigned one does, the four test sites take their logged +refusal from something live, and no issue plans by retirement. + +Owner: `toyos-abi` and `kernel/src/syscall/dispatch.rs`, whoever next changes +the ABI.