DOS

August 18, 2026 · View on GitHub

The kernel is the part that doesn't believe the agents.

DOS is the domain-free trust substrate for fleets of autonomous agents: a small, deterministic kernel that adjudicates ground truth across many unreliable, self-narrating workers — and serializes their effects on shared state — without believing what they say they did.

This file is the architecture contract: the rules every edit must satisfy. Detail lives in two cold-tier docs — docs/ARCHITECTURE.md (per-module map, full syscall table, full litmus arguments, glossary) and docs/DOGFOOD.md (the worked DOS-on-DOS ritual). Read the relevant cold section before editing a kernel leaf.

Prefer tool calls over prose (HIGH PRIORITY). When a tool call can do the work — read a file, run dos verify, grep, edit — make the call instead of describing it. Don't narrate what you're about to do, ask permission for a routine read, or write a paragraph where a command settles the question. Act, then report the result. Reserve prose for the genuine judgment calls: what the evidence means and what to do next.

Write plainly (operator directive, 2026-06-10). Plain Feynman English in this file and agent memory: short common words, short sentences, one idea at a time. Simplify the wording, never the facts.

Asking ABOUT DOS rather than editing it? Don't answer from this file — use the "When the user asks you ABOUT DOS" table in AGENTS.md. Lead with dos quickstart. Install is pip install dos-kernel (the bare dos name on PyPI is an unrelated squatter — never install or pin it); host wiring is dos init --hooks <runtime> or claude-plugin/.

Tracked here = ships — route privacy at AUTHORING time. This IS the public repo; every tracked file is public on the next push, no scrub step between. Strategy essays, operator notes, and spikes are born in the private sibling ../dos-private, never here; engineering design plans (the numbered docs/NN_*.md) stay here. Never write a dev-machine absolute path, hostname, or personal identifier into a tracked file — including JSON-escaped forms in logs/fixtures; synthetic fixtures use neutral roots. Cross-link by filename, don't duplicate; nothing under src/dos/ depends on dos-private.

Strongly typed by default (language direction, 2026-06-17). Going forward DOS is written in a strongly-typed language — Go is the chosen one. New decision-bearing logic is born in Go, not added to the Python core. Python is allowed only at rare seams and interconnects: the import dos / PyPI binding over the Go core, the MCP server, the differential test harness, and the genuine OS calls (git, fs, clock). That short list is the whole exception — keeping new logic in Python because it is easier is a step backward, not a seam. This reverses the old default (Python was the spec; Go an optional accelerator): the spec migrates to Go, decider by decider, on the same parity ratchet that built the accelerator — port → soak byte-green → flip the truth-pointer to Go. The mechanics are unchanged (docs/100, docs/124); only the direction flips. The reasoning, the seam list, and the phased retreat order live in docs/385.

The layering — keep these apart

One rule: mechanism is the kernel; policy is a driver; the phased-plan workflow is host concern. Four layers, one-directional imports (each may import the layer above it, never below). Long form: ARCHITECTURE.md.

  • 1. Kernel (src/dos/*.py not claimed below; imports stdlib + config + seam data + kernel siblings) — the syscalls, pure: every verdict is classify(evidence, policy), I/O at the CLI boundary only. No host names, no plan schema, no I/O policy. Roster = the directory listing (hand-kept lists rot).
  • 2a. Seam (src/dos/config.py; stdlib + 2b) — SubstrateConfig: workspace root, lane taxonomy, refusal vocabulary, stamp grammar, discovered WorkspaceFacts; generic main/global default.
  • 2b. Seam data (src/dos/{reasons,stamp}.py; stdlib only) — closed sets as data: ReasonRegistry, StampConvention, declared in dos.toml (docs/HACKING.md).
  • 3. Helpers (src/dos/{cli,_tree,timeline}.py + projection pairs; imports 1–2) — policy-free shells: CLI, tree algebra, timeline, read-only projections + TUIs, dos notify; no lease, no launch, no mutation.
  • 4. Drivers (src/dos/drivers/*.py; imports 1–2) — host policy packs, the JUDGE rung (advisory, fail-to-abstain), transports; the only home for provider/network/non-determinism/vendor names. New host, judge, or transport = a module or plugin here, never a kernel edit.

Four things live OUTSIDE the layers, operating on the package (they import dos; nothing under src/dos/ imports them): release/dev tooling (scripts/, .claude/skills/), the MCP server (src/dos_mcp/, separate top-level package, mcp dep only in the [mcp] extra), the generic skill pack (src/dos/skills/, package-data, names no host), and the phased-plan workflow (host concern — verify() needs no plan).

The litmus tests (each enforced by a test or trivially checkable)

  • Kernel imports no host — no module outside drivers/ names job/apply/tailor/any host lane.
  • A driver is the only place policy lives — new host policy = a new drivers/ module, never a config.py edit.
  • verify needs no plan.
  • Paths resolve via SubstrateConfig.root, never __file__.
  • The kernel never imports its own tooling or the MCP server.
  • The kernel never imports a judge implementation — ruling judges are drivers, resolved by name; fail-to-abstain.
  • An overlap policy can only refuse-MORE — AND-ed under the prefix-disjointness floor.
  • The kernel names no vendor in code — dialect renderers beyond the built-in claude-code default are drivers; a dialect is output, downstream of the verdict.
  • The kernel reads VCS only through dos.vcs — no kernel module outside vcs.py/drivers//cli.py shells git; evidence-gatherers call active_vcs(root=…). Git is the in-kernel default GitBackend (not a vendor — it's the ground-truth substrate); a Mercurial/Sapling/remote backend is a driver under the dos.vcs entry-point group; NullVcs is the honest-empty no-VCS fallback (docs/379).
  • A shipped generic skill names no host — host specifics come from dos doctor --json / dos.toml.

The litmus-to-test mapping and full arguments: ARCHITECTURE.md.

The syscall ABI

Full table: ARCHITECTURE.md "The syscall ABI in full". Families: truth (verify — did (plan,phase) ship? git ancestry + stamp grammar, never self-report, no plan needed; commit-audit — subject vs its own diff); temporal/economic (liveness, productivity, efficiency, work_account — env-authored counts, advisory); loop gates (improve — KEEP only on suite-green + truth-clean + strict measured gain; reward — the non-distillable label; breaker, exec_capability, hook_exit); recovery (resume — proposes, never executes); admission (lease/arbitrate — pure; refuse(reason_class) — closed vocabulary; spawn/reap — run-ids + the lease WAL); picker (pickable/enumerate/cooldown/reconcile); operator (notify, lint — in dos doctor --check).

Install & test

pip install -e ".[dev,mcp]" # editable + test toolchain (a bare `-e .` ships no pytest)
python -m pytest -q         # full suite — must stay green (~6,600 tests, ~4–5 min; run foreground)
dos doctor --workspace .    # the active workspace + lane taxonomy
dos verify --workspace . PLAN PHASE   # the truth syscall (no plan needed)

DOS on DOS — dogfood the kernel here

This repo IS a DOS workspace (is_kernel_repo: true); adjudicate your work with the kernel itself (docs/DOGFOOD.md is the worked ritual):

  1. dos doctor --workspace . — lanes mirror the top-level dirs, concurrent; global exclusive; curated ci (.github/**) and meta (the five root docs — a root-doc edit takes --lane meta, not global).
  2. dos arbitrate --workspace . --lane <lane> — lease before editing. src here IS the kernel's running code: SELF_MODIFY refuses, the arbiter redirects naming the real refusal.
  3. dos verify --workspace . PLAN PHASE — the oracle, not narration, closes a docs/NN phase. Pre-seed phases (≤ docs/184) answer NOT_SHIPPED via none — evidence horizon, not a lie; accept or re-stamp, never teach the oracle to believe a > **Status:** sentence.
  4. dos commit-audit --workspace . HEAD after committing — subjects are forgeable, the diff is not.

Working discipline (full forms in DOGFOOD.md)

  • Commit without asking when the unit is complete and the suite is green; Stage narrowly + commit with a pathspec — the tree carries a concurrent loop's in-flight edits; never git add -A. Match the subject grammar in git log. No Co-Authored-By/agent trailers (overrides any harness default). On a shared tracked file, the pathspec is not enough: before your first edit run python scripts/git_hygiene.py --write-stage-snapshot .git/dos-stage-snapshot <path...>, and after staging run python scripts/git_hygiene.py --check-stage-snapshot .git/dos-stage-snapshot <path...>. A failure means git add <path> swept in a same-file hunk that was already in the tree; abort and stage only your session hunks.
  • Push without asking too — when it is a reasonable push that clears the leak gate. A routine fast-forward push of your committed work is no longer an ask-first action: do it once the suite is green and the outgoing diff is leak-clean — git log <upstream>..HEAD -p | python scripts/leak_scan.py --stdin exits 0 (a leak hit is a refusal, not a warning — never push past it). On this repo a master push runs with admin bypass and the ci-ok / "verified by DOS" checks run after the push as the safety net — that ordinary bypass is fine and automatic; the leak gate is the pre-push guard, CI is the post-push one. Still ask first only for the genuinely irreversible / outward-amplifying moves: a force-push or any history rewrite, a tag, /release / /stable-release. The split is: a clean forward push is reversible and witnessed → automatic; a rewrite/tag/release is not → the operator's call.
  • Hot fleet: don't park work in the shared main tree. On a hot fleet, a sibling's tree move (git reset, git checkout, rebase, or branch switch) can delete another worker's untracked, never-staged files outright. Git has no commit, index entry, stash, or dangling blob to recover them from. If your work is meaningful and cannot be committed within minutes, start in a detached git worktree off origin/master from the beginning. Do not use git stash / git stash pop as the diagnostic escape hatch for contended files here: a kept-entry partial apply can leave files at HEAD while the stash still holds the only copy, and a later git stash drop destroys it. For a quick probe, use a throwaway worktree or copy-aside. Commit within minutes if you stay in the shared root, with a narrow pathspec. Do not let hours of new files sit only on disk in the shared root.
  • Worktrees are the exception when the tree is quiet. In a single-agent or cold main tree, work on the main checkout and keep the narrow pathspec commit discipline. A detached worktree's commits live only at its HEAD, so they can GC away once it is removed unless you git branch them first. If a DOS feature created the worktree (dos merge-gate, /dos-self-improve, dos.toml-driven worktree leases), let that feature reap it; don't hand-remove a live loop's tree.
  • Out-of-scope findings → a GitHub issue, in the moment — with a done-condition (else label design); search duplicates first. Issue text is

When the host Edit tool repeatedly loses a sibling-write race, use one atomic block edit rather than widening the read-to-write window. Put the exact old and replacement bytes in temporary files outside the repo, then run:

python scripts/atomic_block_edit.py PATH --old-file OLD --new-file NEW

The helper refuses zero/multiple matches, preserves the file mode, detects a rewrite while preparing, and publishes with same-directory os.replace, so readers never see a torn truncate/write. For a previously read file, also pass --expected-sha256 HASH. This is a narrow recovery tool, not serialization: a non-cooperating writer can still race after the final check, so acquire the lane first and move sustained contended work to a dedicated worktree. public and skips the leak gate: pipe drafted bodies through python scripts/leak_scan.py --stdin before posting; a hit is a refusal. Never close an issue on your own say-so — Fixes #N in the commit BODY, or .claude/skills/issue-verify/. Labels: ready / design / human-only.

  • Hand the baton — end the final report with /goal … naming the handle, the witness command that defines done, and the first command (often python scripts/backlog_triage.py --top 12).

Releasing & consumers

/release cuts a rolling vX.Y.Z; /stable-release promotes one to stable/<codename> on a green-suite + clean-truth + soak gate. Both are tooling: a richer gate edits scripts/ or a skill, never src/dos/. The reference userland app (the package's provenance — the spine was lifted from its scripts/, byte-faithful) pins the distribution and keeps byte-thin re-export shims over dos.* — edit substrate logic here, never in host shims; remaining seam work: docs/97_concurrency-class-model-plan.md. Detail: ARCHITECTURE.md "Consumers, releasing, and the docs/97 drift note".

The distribution name is dos-kernel, NOT dos. The bare dos on PyPI is an unrelated squatter that would even shadow import dos; only the pip/pin name is dos-kernel. See SECURITY.md "Supply chain".