Two processes, one contract each way, no shared runtime language.
Keystone OS is a local-first, single-operator agent control terminal. A React 18/Vite/TypeScript HUD (apps/hud/) and a Python/FastAPI runner (runner/) run as two independent processes on 127.0.0.1:3108, joined only by a WebSocket message schema and a shared Obsidian-openable vault filesystem (vault/). An incoming text or voice intent passes through a strict-order router, a 17-pattern regex table first, a single Haiku 4.5 (claude-haiku-4-5-20251001) call only on a miss. The resolved route runs on a serial, timeout-bounded executor through one of six registered skills: four spawn Claude Code processes reading Google Calendar or Gmail read-only, one is a deterministic in-process capture, one is a web-grounded metrics pull. Every run is checked against the file it actually produced, not the model's own report of success.
An opt-in Tier-1 verifier spine (sample, verify, rank, gate, oracle-check) runs beside the executor and has been measured against SWE-bench Verified: 100% resolve rate on gold predictions, 50% on patches a real Sonnet agent spawn produced across 2 instances. The system is built one test-first brief at a time (41 to date) against a 10-point constitution and 3 recorded architecture decisions.
Every count below traces to a command or a file path.
| Metric | Value | Source |
|---|---|---|
| Commit count | 52 | git rev-list --count HEAD |
| First commit | 2026-06-16, 7d89ad8, "Brief 001: repo scaffold, constitution, and live link spine" | git log --reverse |
| Last commit | 2026-07-22, 316e144, "Add plain-language ABOUT.md alongside VISION.md" | git log -1 |
| Tags / releases | 0 | git tag (empty) |
| Branches | 1 (master) plus its remote tracking ref, origin github.com/SingularityAI-Dev/keystone-os | git branch -a; git remote -v |
| Runner source LOC | 4,116 lines across 34 .py files | runner/src |
| Runner test LOC | 7,962 lines across 51 .py files | runner/tests |
| Runner test functions | 371 unparametrised def test_* | grep -rE over runner/tests |
| Runner test items collected | 419 (pytest 9.1.0, collection only, no suite executed) | pytest --collect-only -q |
| HUD source LOC | 3,849 lines across 31 .ts/.tsx files (16 source, 15 test) | apps/hud/src |
| HUD test assertions | 133 it(/test( calls across 15 test files (Vitest, not executed) | grep -rE over *.test.ts* |
| REST + WS endpoints | 10: GET /health, /routes, /schedule, /document, /history, POST /speak, /retry, /intent, /voice, GET /ws | app.py |
| Route catalogue | 7 entries (6 real routes plus the ROUTE_UNKNOWN sentinel) | router/catalog.py |
| Deck trigger phrases | 6 | router/catalog.py |
| Tier-1 regex patterns | 17 | router/classifier.py |
| Skills registered end to end | 6 | executor/skills/__init__.py |
| Constitution non-negotiables | 10 | loop/PROJECT.md |
| Architecture decision records | 3 (ADR-001, ADR-002, ADR-003) | loop/PROJECT.md |
| Build-loop briefs / reports | 41 briefs, 41 matching reports (001 to 041) | loop/briefs/, loop/reports/ |
| SWE-bench gold smoke | 2/2 resolved, resolve_rate 1.0, SWE-bench Verified | runner/state/swebench/gold.smoke-gold.json |
| SWE-bench live agent baseline | 1/2 resolved, resolve_rate 0.5 (model sonnet) | report-1142.json, report-1766.json |
| Published packages | None; root package.json is "private": true | package.json; runner/pyproject.toml |
Two processes, one WebSocket contract, a vault as the second contract.
apps/hud/src/App.tsx renders from parsed WebSocket frames only, never a hardcoded status; apps/hud/src/frames.ts is the discriminated-union parser both sides must agree on. The vault at vault/ (daily-notes/, inbox/, drafts/) is the second contract: the executor writes markdown artifacts into it and the watcher (watch/observer.py, handler.py) reads file events back out as vault_event frames.
text | voice intent
-> router/classifier.py 17-pattern regex table
-> (miss) router/model_tier.py claude-haiku-4-5-20251001
-> (unresolved) ROUTE_UNKNOWN
-> state/queue.jsonl (intent_queue.py)
-> executor/worker.py single serial thread, oldest queued item
-> executor/skills/__init__.py REGISTRY[route_id]
claude -p spawn (executor/spawn.py, per-skill allowed_tools)
or in-process deterministic write (ROUTE_CAPTURE)
-> guard: never-overwrite (existing artifact skips the run)
-> guard: post-run artifact verification (missing file -> artifact_missing)
-> [opt-in] executor/tier1.py sample -> verify -> rank -> gate -> oracle-check
Two mount points, one optional background task set
runner/src/keystone_runner/__main__.py starts the FastAPI app (app.py) plus, if enabled by config flag, the vault watcher, executor worker, scheduler, and vitals sampler as background tasks. apps/hud/src/main.tsx mounts the React tree.
A five-stage DAG that changes nothing by default
With VERIFIER_MODEL set, a best-of-n candidate loop runs with logprob-based reranking: verify_trajectory then rank_candidates then gate_battery then oracle_check (TIER1_DAG). Unset, the default, the executor keeps its original eager first-green behaviour.
A config-driven clock and an on-device voice tier
scheduler/ fires the same enqueue path on a config-driven clock: morning rundown, inbox brief, and plan-today at 06:00, plan-tomorrow at 18:00. voice/ runs on-device STT (faster-whisper), TTS (Kokoro), and wake-word (openwakeword), each opt-in and each swallowing its own errors rather than crashing the runner loop.
Three ADRs, one drift test, one documentation lag caught by the registry.
Two runtimes, one contract, no shared language
loop/PROJECT.md chose a Node/pnpm HUD and a Python/uv runner joined only by the WebSocket schema and the vault file layout, explicitly rejecting a shared runtime language so either half can be rebuilt without touching the other.
The heartbeat schema is frozen at 7 fields
type, ts, runner, link, vaultPath, lastPull, nextPull. New state arrives only as additive frame types (queue_state, exec_status, vault_event, system_vitals, wake_event), never by extending the heartbeat itself.
Routing and execution are split by cost tier
Routing is pinned to the cheapest capable model: regex, then Haiku 4.5, never a local LLM (constitution non-negotiable 4). Execution defaults to Sonnet and is per-skill overridable to Opus.
The spec and the runtime cannot silently diverge
runner/tests/test_harness_drift.py parses the fenced tier1-dag block out of the project's own HARNESS.md orchestration spec and asserts it matches TIER1_DAG in executor/tier1.py verbatim, plus checks that every named budget-knob env var has a live config getter.
The registry is the source of truth, not the README
README.md (lines 89 to 90) and STATUS.md both stated the metrics route was not yet implemented. It is: executor/skills/__init__.py's REGISTRY carries ROUTE_METRICS_PULL as a fully-formed spawn skill, shipped in commit c570b14. app.py derives each route's implemented flag from get_skill(route_id) is not None, and the HUD's CommandDeck.tsx renders every deck button off that live response with no hardcoded disable list.
Per-skill tool allow-lists, never a blanket grant
The plan skills get exactly the two read-only Calendar MCP tools plus Write. The inbox brief gets exactly the two read-only Gmail MCP tools plus Write. No skill's allow-list includes a Calendar or Gmail write, send, or delete tool. Vault-generated content is excluded from git entirely: .gitignore excludes vault/** except the folder scaffold, .gitkeep files, and vault/README.md, because the morning routine writes rundowns, inbox briefs, and calendar-derived daily notes on every run.
Five build phases across 52 commits, five weeks.
2026-06-16 to 2026-06-18
Repo scaffold and constitution, the deterministic regex router, the live vault watcher, the Haiku 4.5 defer tier, and the first end-to-end executor skill (briefs 001 to 004).
2026-06-24 to 2026-06-27
Executor crash recovery, the WebSocket on-connect snapshot, Command Deck wiring, the multi-entry scheduler, the three.js particle sphere, the full on-device voice loop (TTS confirmation, STT intake, push-to-talk, the wake-word listener and its HUD indicator), and runs history with retry (briefs 005 to 031); README, STATUS, and CHANGELOG describe the system as "functionally complete" at this point.
2026-06-27 to 2026-07-01
The runner as a persistent launchd service, hardening the localhost trust boundary, queue retention and bounded logs, the ROUTE_METRICS_PULL skill, first-run and empty states, the unrouted-command hint, and typed command input (briefs 032 to 041).
2026-07-15 to 2026-07-18
The Tier-1 verifier spine encoded into HARNESS.md and logic/verifier.logic.md, then built commit by commit: the gate battery and bounded auto-retry, the oracle-check stop condition, the best-of-n sample_n candidate loop with staged isolation, verify_trajectory plus rank_candidates, the SWE-bench Verified resolve-rate emitter (gold smoke at 1.0), the prediction bridge, and the live baseline measurement of 0.5 over 2 real-agent runs.
2026-07-19 to 2026-07-22
The repo is renamed from agentic-os to Keystone OS (commit 3fdce38), and a plain-language ABOUT.md is added alongside VISION.md (commit 316e144, the current HEAD).
What Keystone OS is not.
Keystone OS today is the single-operator agent terminal described above, not the estate-wide "vault of vaults" aggregation and control layer that VISION.md and the project's one-page PDF describe as the destination; that layer is explicitly unbuilt.
- Not multi-tenant, by design: everything binds to
127.0.0.1with no auth layer (constitution non-negotiable 7). It is a single-operator terminal, not a shared service. - No tagged release, no CI configuration found, no published package on npm or PyPI.
- The
visualiser/folder (271 MB of jsx animation scenes, rendered video, narration audio) anddocs/Keystone-OS.pdf(1 page, 93,260 bytes) are promotional and pitch assets. They are not part of the running system the README's own Architecture section describes (apps/hud/,runner/,vault/,loop/).
Evidence: compiled from the keystone-os repository on 2026-08-03. Every number on this page traces to a file path or command output in the source tree. Last updated: 2026-08-03.