DOS in five minutes
July 17, 2026 · View on GitHub
The README tells you what DOS is and why. This tells you what to type — a runnable hello-world you can copy-paste, start to finish, in a throwaway directory. No agents, no fleet, no plan files. Just the truth syscall, working, against a plain git repo.
Every command below was run exactly as written; the output is real, not illustrative.
0. The one-sentence model
DOS answers questions about work it doesn't trust the worker to answer
honestly — did this actually ship? is this run still moving, or just spinning?
may this lane start without colliding with another? Each answer comes from
ground truth (git history, file-tree math, a clock), never from what an agent
says it did. This walkthrough exercises the first and most important one:
verify — did (plan, phase) actually ship?
1. Install
pip install dos-kernel # the distribution name is dos-kernel
# or, from a clone of this repo: pip install -e .
Zero-install trial. If you have uv, you can run the whole tour — including the 60-second caught-lie demo — without installing anything:
uvx --from dos-kernel dos quickstartuv fetches the package into a throwaway environment and runs it; nothing lands on your PATH. When you want the
doscommand for real, use thepip installabove (oruv tool install dos-kernel).
The dist name is
dos-kernel, notdos. A barepip install dospulls an unrelated package (a Flask/OpenAPI helper) that squats thedosname. The import name is stilldos(import dos, thedoscommand) — only the pip name differs. See SECURITY.md "Supply chain".
That puts a dos command on your PATH. Confirm it:
dos --help
The only runtime dependency is PyYAML — the kernel is deliberately near-stdlib.
2. Make a workspace
A workspace is any directory DOS serves. Scaffold one and look at it:
mkdir hello-dos
cd hello-dos
dos init .
wrote .../hello-dos/dos.toml
no source dirs detected — scaffolded a single-writer 'main' lane
DOS workspace initialised. Try: dos doctor --workspace .
dos init writes a single dos.toml — the only file DOS ever asks of your
repo. It holds your policy (lane taxonomy, paths, ship-stamp grammar); the
package holds the mechanism. You can open it now, but the defaults are fine
for this tour.
The lane taxonomy is seeded from your top-level directories — one concurrent lane per source dir, so a real repo gets usable parallel lanes for free (this empty
hello-doshas none, so it scaffolds a singlemainlane). It's a one-time scaffold written as editable data, not a live filesystem binding — reshape it freely. See HACKING.md, "the folders→lanes convention."
Want the workflow skills too? Add
--skillsanddos initALSO copies the generic skill screenplays into.claude/skills/as editable local files — so the adoption path is one command, not a manual copy out of the wheel:dos init --skills . # dos.toml + the core skills (next-up/dispatch/loop/replan) dos init --skill dos-promote . # dos.toml + just a named skill (repeatable) dos init --all . # dos.toml + the full generic skill packThe copies are ordinary files you edit and run (
/dos-dispatch,/dos-promote, …); the package-data is the seed, not a runtime binding. Re-running is idempotent — a diverged local copy is never clobbered without--force.
Already running an agent? Bind the verdict to it in one command. Add
--hooks <runtime>anddos initALSO wires the three DOS hooks into that runtime's own config file — so a refused tool-call is denied before it runs, a stalled stream is re-surfaced, and a stop on an unverified claim is refused. It works on whichever agent you already use, not just Claude Code:dos init --hooks auto . # detects the runtime(s) this repo already uses dos init --hooks claude-code . # writes .claude/settings.json dos init --hooks cursor . # writes .cursor/hooks.json dos init --hooks codex . # writes .codex/config.toml dos init --hooks gemini . # writes .gemini/settings.jsonThe block is merged into any existing config (your other hooks survive), and re-running is idempotent. This is the enforcement path (the host denies on a DOS verdict). The advisory path — the agent calling
dos_verifyitself — is the MCP server, wired separately (dos-mcpin the host config; see the MCP README). Use both: hooks enforce, MCP advises. Full design: docs/221.
dos doctor --workspace .
DOS v0.30.0
workspace root .../hello-dos
execution-state .../hello-dos/dos.state.yaml
plans glob docs/**/*-plan.md
stamp convention generic (any/no dir prefix) [style=grep]
verifiability no commits to read (not a git repo, or empty history)
concurrent lanes (none)
exclusive lanes main
autopick ladder (none)
admission predicates disjointness, self-modify
judges (JUDGE rung) abstain, llm, operator-decision, similarity
evidence sources null, ci_status, citation_resolve, os_acceptance, paste_log (verify: git-only)
enforce handlers observe
overlap policy prefix* (ratio_max=0.333; prefix floor always on)
stall reader REPEATING>=3, STALLED>=5 (ignore_tools: (none))
supervisor target 1 (count_spinning_as_alive=yes, reap_stalled=yes, spin_halt_after=off)
is git workspace no
runtime hooks none wired (run `dos init --hooks auto` to bind)
layout style dos
environment print <hash> (kernel v0.30.0 @ <sha>; py 3.13.7; <os>)
declared tools (none declared)
dos home .../dos (0 project(s) indexed)
The
runtime hooksline shows which agent runtimes have the DOS hooks wired in this workspace — so after you rundos init --hooks cursor .it readsruntime hooks cursor (4), confirming the binding took (a mis-wired hook is otherwise a silent no-op). It's read-only — runningdoctorwrites no config.
Your output may show extra entries — e.g.
admission predicates … budget-guardoroverlap policy prefix*, semantic-groups— if you've pip-installed theexamples/dos_extskeleton; those are your registered plugins showing up live. A plainpip install dos-kernelshows the built-ins above.
doctor is your "what am I actually configured as?" command. A few lines to note.
stamp convention generic is the grammar verify will use to recognize a ship
in your commit messages (we use it in a moment). verifiability no commits to read is DOS being honest up front: this is an empty directory, not a git repo
yet — so there's nothing for the oracle to read (that changes the moment you
commit). The evidence sources … (verify: git-only) line names the extra
witnesses verify could consult (a CI status, a pasted log) and confirms that
out of the box it reads git only — nothing else is trusted until you wire it
in. And the bottom block is DOS describing itself: which enforce handlers,
overlap policy, and stall reader are active, plus the environment print —
the kernel version, commit, Python, and OS a verdict would run under, so a
result is reproducible.
3. Do some work — and ship a "phase"
DOS has no opinion about how you work; it reads your git history as the
record of what happened. The unit it tracks is a phase: a named chunk of work
identified by an id like AUTH1 (a series AUTH, phase 1). You stamp a phase
as shipped by naming it at the start of a commit subject, <PHASE-ID>: <message>:
git init -q
git config user.email you@example.com # if you haven't set a global identity
git config user.name "You"
git config commit.gpgsign false # this throwaway repo has no signing key
# do the work...
echo "def login(): ..." > login.py
# ...then ship it with a phase-id at the front of the subject:
git add -A
git commit -m "AUTH1: ship the login endpoint"
That's the whole convention under the generic stamp grammar: a phase id, a
colon, then your message. The id needs a digit (it names a numbered phase),
which is what separates a ship (AUTH1:) from an ordinary fix: typo commit.
4. The payoff — verify
Now ask the truth syscall whether AUTH1 shipped. You wrote no plan file, no
registry, nothing but the commit — and it still answers, from git history alone:
dos verify --workspace . AUTH AUTH1
SHIPPED AUTH AUTH1 e389e8b (via grep-subject)
That via grep-subject is DOS telling you how it knows: it found the phase
token in a commit subject in the git log — not in any registry, not from
anyone's say-so. (Reading the rung matters: a subject is the cheapest, most
forgeable place to claim a ship, so the verdict names it explicitly.) The exit
code is the verdict (0 = shipped), so a script can branch on it.
Now ask about a phase you haven't shipped:
dos verify --workspace . AUTH AUTH2
NOT_SHIPPED AUTH AUTH2 (via none)
via none means DOS looked everywhere it knows — registry, then git history —
and found nothing. Exit code 1. This is the entire point of DOS in one
contrast: an agent can claim AUTH2 is done all it likes; verify reports
what the artifacts say, which is that it isn't.
What you just proved. Nobody told DOS that
AUTH1shipped — you wrote no plan, no registry, no status file. The only input was a git commit you made, and the verdict was re-derived from git history:you committed: AUTH1: ship the login endpoint ──┐ (git — not self-report) dos verify AUTH AUTH1 ──────────────────────────────┴─► SHIPPED (via grep-subject) dos verify AUTH AUTH2 ───────────────────────────────► NOT_SHIPPED (via none)An agent can narrate "AUTH2 done" all day;
verifyreads the artifacts, not the narration. That gap — claim vs. ground truth — is the entire kernel.
The grammar matters.
verifyrecognizes the glued phase-id form (AUTH1: …, verified asdos verify AUTH AUTH1) out of the box. If your repo stamps ships differently — under a directory (docs/AUTH1: …), or with its own prefixes — declare that once indos.toml's[stamp]table and every surface picks it up. See HACKING.md §"the four data tables". Rundos doctor --checkto be told if your declared grammar doesn't match your own commits.
5. One more syscall — arbitrate
verify distrusts a finished claim. arbitrate is the admission kernel: may
a new unit of work ("lane") start right now without colliding with work already
in flight? It's a pure function — you hand it the request and the live leases, it
hands back a decision. With nothing else running:
dos arbitrate --workspace . --lane main --leases '[]'
{"auto_picked": false, "free_clusters": [], "lane": "main", "lane_kind": "global",
"outcome": "acquire", "pick_count": null,
"reason": "exclusive lane 'main' — no other loop live, admitted.", "tree": ["**/*"]}
outcome: acquire — green light. Note lane_kind: global: the scaffolded main
lane is exclusive (it owns **/*, the whole tree), so the rule here is "it
must run alone" — hand arbitrate a live lease and it would refuse the second
request instead. On a concurrent lane (one per source dir in a real repo), the
refusal trigger is finer-grained: an overlapping live lease, where the file-tree
disjointness rule is what stops two agents editing the same files at once. Either
way the refusal is structured — a named reason you can look up: dos man wedge
lists the whole refusal vocabulary, and
dos man wedge <NAME> prints a generated man page for any one of them.
6. arbitrate decides — lease-lane acquire holds
Run that arbitrate again and it answers acquire again. That is correct, not
a double-booking: arbitrate is the pure decision. It reads the workspace's
lease journal (plus anything you hand it via --leases) but never writes it,
so nothing stays held when it exits. The verb that takes a lane — the same
arbiter, then the grant journaled to the workspace's write-ahead log where every
later caller in any process sees it — is lease-lane acquire:
dos lease-lane acquire --lane main --owner me
{"outcome": "acquire", "journaled": true, "lane": "main", "owner": "me",
"reason": "exclusive lane 'main' — no other loop live, admitted.", "tree": ["**/*"], …}
journaled: true is the difference. Now the hold is real — a second taker is
refused, from another shell, another process, another agent's tab:
dos lease-lane acquire --lane main --owner teammate
{"outcome": "refuse", "journaled": false, "owner": "teammate",
"reason": "lane 'main' is already held by a live loop — …", …}
Inspect or end the hold any time — the journal, not anyone's narration, is the fleet's memory:
dos lease-lane live # the live-lease set, folded from the WAL
dos lease-lane release --lane main --owner me # work landed; free the lane
So: ask with arbitrate (a script gating "may I start?", a what-if against
hypothetical --leases), hold with lease-lane acquire (real concurrent
work). Part two of dos quickstart plays this exact escalation — admit,
redirect, refuse — journaled in a throwaway repo.
Windows / PowerShell. Everything in this tour runs as-is in PowerShell. One caveat for later: an argument with embedded double quotes — e.g. a non-empty
--leases '[{"lane":"api"}]'— is mangled by Windows PowerShell 5.1, which strips the inner quotes beforedossees them (PowerShell 7 passes them through correctly). On 5.1, escape them as\"(--leases '[{\"lane\":\"api\"}]') — or simply omit--leasesand let the verbs read the live set from the workspace journal, which is the default.
Where to go next
You've now used the two load-bearing syscalls. The rest of the surface:
| You want to… | Command |
|---|---|
| See your active config & taxonomy | dos doctor [--json] |
| Check a finished claim | dos verify PLAN PHASE |
| Check an in-flight run is moving, not spinning | dos liveness --run-id … --start-sha … |
| Decide if a lane may start (decision only) | dos arbitrate --lane … --kind … --leases … |
| Take a lane and hold it (journaled) | dos lease-lane acquire --lane … --owner … |
| Watch what's running (lanes/leases/verdicts/commits) | dos top (read-only; --once for one frame) |
| See what's waiting on you (refusals to resolve) | dos decisions |
| Check the plan's claim vs. the ground truth | dos plan [--once] |
| Read the refusal vocabulary | dos man wedge [REASON] |
| Gate an empty work-packet | dos gate PACKET |
Those last three are the read-only live projections — each mutates nothing and
works without extra dependencies (--once / --json on a bare install; the live
redraw is the optional [tui] extra). The when-to-use-each map is
Three live projections.
- The full CLI — every
dosverb, grouped — is in CLI-REFERENCE.md (the CLI reference guide shows the core dozen). - Already running a fleet through LangGraph, CrewAI, AutoGen, or an Agents SDK? Bolt the referee onto the framework you have — one function at its believe-the-agent seam, every recipe executed against the real framework: the fleet-framework cookbook.
- To extend DOS — add your own refusal reasons, lanes, renderers, or safety
predicates without forking the package — read HACKING.md and
copy
examples/dos_ext/. - Why it's shaped this way (and the evidence it pays off across a fleet): the docs index maps the design notes.