ToyOS is a general-purpose operating system written from scratch in Rust. Its changes are held to a production-grade bar: correct, efficient, minimal. The product is still developing rapidly; a recorded weakness remains a weakness. Self-hosting is the north star: ToyOS must eventually rebuild itself and reproduce the host's bytes.
Modern x86-64 (2020+) and AArch64 are equal targets, UEFI only. Metal is first-class, and QEMU is supported. The machines available for testing determine what we can verify today, not the architecture. Development ergonomics and iteration speed matter throughout.
- Simplify the architecture. Prefer changes in ownership or representation that eliminate responsibilities, states and compensating mechanisms. Preserve useful capabilities. An overarching simplification is worth proposing even when it crosses components; do not substitute an isolated workaround because it fits a smaller assignment.
- Migrate directly. ToyOS has no users or compatibility obligations. Optimize for the clean final architecture and total implementation effort. Change components together, do the prerequisites and delete superseded paths. Temporary loss of functionality or broken builds are acceptable during a migration; preserving intermediate functionality must not motivate bridges, duplicate paths or extra mechanisms. Preserve it where doing so costs nothing. Report temporary gaps and verify the completed migration.
- Code is liability. Delete dead code and unnecessary machinery. Prefer direct, readable code; an abstraction earns its place through real uses or a boundary it makes clearer and safer. Do not build for imagined callers. A rule visible in review belongs in instructions rather than another custom gate.
- Zero legacy. No backwards compatibility, BIOS or 32-bit support. Do not conceal failures with fallbacks or preserve obsolete paths. Selecting the best supported mechanism for each machine is ordinary hardware support: QEMU must never reduce what metal gets.
- Fail clearly. Internal invariant violations fail loudly. External inputs are validated and refused at their boundary; a userland bug never crashes the kernel. Expected absence and explicitly reported operation failures are not invariant violations. Contain failure at the component that can handle it without hiding it.
- Rust is first-class. Prefer compile-time safety to runtime checks, and runtime checks to tests. Validate external representations once before constructing typed values; validate each later message at its own boundary. Nothing a type makes impossible needs a runtime test. A kernel object type and an application's protocol type are separate contracts.
- Userland first. The kernel owns only what userland cannot. It creates no kernel thread except each CPU's idle loop. Kernel work is bounded, runs on the thread or interrupt that caused it and is charged to it; long-running unowned work belongs in a userland service.
- Authority is explicit. A process receives capabilities through authorized creation or transfer; names and process IDs do not grant authority. Rights can be narrowed, never widened by a holder. Filesystem access belongs inside the same isolation model.
- Existing Rust just works. Programs build and run unchanged through proper platform support in ecosystem forks, not ToyOS-specific replacement crates. Apps use the same source on ToyOS, Linux under Wayland, macOS and Windows. A ToyOS-only program must do a ToyOS-only job; its manifest's
exemptexplains it (build/userlandhost.rs). X11 is legacy. - No silent debt. Remove a discovered compromise or record its evidence, owner and exit in
issues/. Filing it does not make it acceptable or complete. Never weaken audible or visual quality without the owner's explicit agreement.
Never use a fixed delay where an event can be awaited. A wait has a failure timeout. A hardware-mandated delay with no notification is the exception, with the requirement cited at the operation.
Read the code for current behavior. Cargo.toml accounts for packages; images/system.toml declares the system programs; issues/ holds defects and staged work. Do not duplicate their inventories here.
toyos-abi (sdk/abi/) defines only the kernel/program boundary. Its ABI is unstable and may change for a cleaner design. The kernel depends on it; sdk/toyos/ provides the Rust API for userland. Userland protocols own their wire types and errors in their own libraries. They do not use SyscallError or other kernel ABI definitions as the protocol between programs; SDK internals translate kernel failures at the boundary.
The supervisor constructs process namespaces and endowments. The kernel enforces object authority; each userland protocol validates its own inputs and grants. The target is per-program filesystem views; inspect the storage track and code for the remaining shared kernel paths.
ToyOS trusts only what it cannot avoid trusting: the CPU and chipset vendor with its signed microcode, the platform firmware (UEFI, AML, SMM), which already controls the whole machine, and the signed image with its pinned toolchain and crates. Everything else is isolated: programs, users and sessions, the network, file contents, and external or hot-plug devices, whose DMA stays behind the IOMMU. The single exception is a privilege beyond a normal device claim, such as a reset line, DMA without an IOMMU or a firmware register window: it is granted only in the signed image's system.toml, to a program that image ships, and a package installed later never gets one, whoever signed it. Inside the trusted base a check is a bug fence, kept where it is cheap and catches our own mistakes, such as a write landing in RAM, the IOMMU, the interrupt controllers or another driver's device; it never withholds a feature for want of a vendor document.
The loader owns every UEFI call, before ExitBootServices. CPU control registers come from one declaration, applied and asserted on every CPU. Input translation belongs in userland, per surface. The kernel's logging responsibility is its record ring and console; logkeeper owns the log file. POSIX compatibility belongs in sdk/libc, never in the kernel's native interface.
Read the relevant subtree's CLAUDE.md: kernel/, system/, tests/, build/, apps/ or sdk/window/. Module comments should state only contracts needed to use the code. Issue format lives in issues/README.md; agent responsibilities live in .claude/agents/.
Choose designs on their merits for modern hardware. Use vendor documentation, published specifications and research. Take a fact first from the vendor's document or the governing published specification, then from the vendor's own code under any licence, including its engineers' code for another OS, then from anyone else's code, and last from a measurement on our machines, which also checks every fact above it that a bench can reach. Measurements test assumptions and expose regressions; a missing gain on one bench does not alone reject a sound general design. Label proposals and inferences honestly. Creative architectural exploration may precede measurement; uncertain implementation assumptions must be investigated before relying on them.
Read another implementation's code, whoever wrote it, only clean-room: one agent derives facts and keeps the provenance of each in its evidence, never in the repository; another implements from those facts without seeing the code. The derived specification goes in the implementation brief, not the repository. A vendor's document is still cited at the site. Editing an upstream fork is governed by the implementer's fork rules. No upstream pull request is sent for now.
Use general, widely used crates as published when they are the cleanest solution. Write ToyOS-specific functionality ourselves, including drivers; minimize kernel dependencies and keep libc out of the kernel. A security boundary may justify our own well-tested crate. A fork carries upstream-quality source changes and goes when upstream supplies them; packaging alone uses unmodified source pinned by hash.
Rust and QEMU are the development prerequisites. Other host tools must be buildable from C or C++ for eventual self-hosting, justified where Rust tools do not suffice, and declared in issues/the-build-runs-host-tools-outside-rust-and-qemu.md. No dependency on a binary for only one host OS. ToyOS's own maintained code is Rust, not Python, Perl or shell. Bootstrapping all the way from source without a binary seed is out of scope.
NOTICE records committed third-party files, hashes, origins and licences. Vendor-signed firmware may ship pinned by version and hash when redistribution permits it. Device firmware is loaded by its owning driver through its IOMMU domain; CPU microcode loading is privileged kernel work. Loading responsibility does not require embedding firmware payloads in the kernel binary.
- Choose the cheapest sufficient test. First make bugs unrepresentable, then test logic and failure handling on the host, including hardware models. Prefer metal for actual hardware behavior; QEMU is last. Receiving malformed input is a unit or module test, even when the real input arrives from another process. Crossing a component boundary does not itself justify an integration test.
- Keep tests focused. Userland is tested as units and modules, with at most a cheap smoke test. Avoid scenarios that install software, launch applications and drive unrelated steps. Kernel integration and hardware tests must name the behavior host tests cannot establish. Each test has a clear claim, controlled inputs and a meaningful failure; automated tests do not depend on the public internet.
- Declare where integration tests run. Record supported metal machines and QEMU configurations with the test registration or harness metadata. Before opening a code PR, run the relevant integration tests locally on those targets. The broad integration matrix runs nightly, not for every PR or merge-queue entry. Host checks remain routine. Inspect the actual harness and workflows; these instructions do not themselves change their scheduling.
- Amortize boots. Batch compatible metal tests in a shared boot. Separate boots need a reason, such as a boot-only property, destructive test or incompatible image. Plenty of useful metal tests are welcome; unnecessary reboots are not.
- Use real hardware for timing and audio. QEMU establishes order, completion, contents and counts; its clock is only a hang ceiling. Benchmarks and exploratory comparisons are evidence, not automatically regression tests. A timing test has a justified bound for its declared machine.
- Run what can change. Use
cargo run -- --ci hostfor host checks,cargo run -- --build-onlyfor an image, and filteredcargo testinvocations for relevant guest behavior. Repeat checks only when a change, failure or unresolved concern could invalidate their result. A new commit, comment deletion or rename alone does not require another integration run; compile where necessary. - Investigate reds. Do not rerun a failure merely to get green. Fix a defect introduced by the change. A real unrelated defect may be recorded without blocking the PR once its independence is established. Repair or delete a defective or redundant test according to its value, preserving evidence of any real bug it exposed. Host load invalidates timing claims in guest tests, not evidence of an actual race or lost progress.
- Show evidence. Record commands, exit codes, logs, revisions and machines. Identify earlier evidence reused and why later edits do not invalidate it. Use an independent oracle where available and a negative control where a defect would otherwise land unseen; the whole change reverted onto the measured base is the control for the whole change. Another agent is not an independent oracle. Do not add a test for what a type or straightforward inspection already establishes.
Agents may run their own selected guest tests. Metal runs are coordinated and executed by the orchestrator; implementers prepare them with --metal-readback, which touches no machine.
One agent, one worktree, one branch. The primary checkout owns main and rust/; work in a separate worktree. Never clone ToyOS to make one or run git submodule there: the latter can corrupt the shared fork's worktree configuration. Do not disturb another agent's files, branches, locks or processes. Stop only processes you started, by PID, and remove your scratch output when it is no longer needed.
main moves only through reviewed pull requests and its required merge queue: no direct push, force-push, deletion or bypass. Commit with git commit -F <file>. You own your unpushed commits and your own remote ToyOS branch; fork branches are append-only. Keep ABI consumers, fork pins and their implementation coherent in one landing. Account for every changed hunk in a modify/delete conflict.
Implement the agreed outcome across the components it requires; a file boundary is not an approval boundary. Ask the owner when a discovery changes an owner-approved architectural decision, or when the architectural choice is uncertain. Routine implementation choices belong to the agents. Record unrelated discoveries without letting them consume the task.
Keep full command output in logs when it is long; truncation loses evidence. Report observed results, and label estimates and unverified claims. Never put personal data, machine or network identifiers into network requests, commits or published evidence. The build user-agent is toyos-build (https://github.com/ToyOSOrg/ToyOS); build/sourcegate.rs defines identifying shapes to exclude.
Comments are a maintenance cost. Prefer expressive code and names. Delete false, stale, redundant or unnecessarily long comments rather than polishing them. Keep concise boundary contracts, non-obvious invariants, unsafe preconditions and hardware requirements that code cannot express, such as why an MSR needs a particular value. A source citation is useful when it explains that constraint. History, investigations and measurements belong in commits or evidence.
A module header is not an essay or an inventory. Keep only what a reader needs to use or safely change the module. Durable rules belong at their relevant boundary; do not build a second specification in comments or private agent memory.
Change instruction files when the owner briefs it or a change removes or renames something they instruct agents to use. Replace conflicting rules instead of appending exceptions. Keep these files short, give each rule one home, and prefer judgment over a procedure for every incident. Update issue records directly affected by the change so they remain accurate. Record unrelated discoveries without expanding the assignment into their implementation or a general tracker cleanup.