"To achieve great things, two things are needed: a plan and not quite enough time." - Leonard Bernstein
website · docs · install · first run · glossary · limitations · name policy · sponsor
Bernstein is a deterministic orchestrator for CLI coding agents (Claude Code, Codex, Gemini CLI, and 40+ more). Scheduling is plain Python - no LLM in the coordination loop - so runs are reproducible end to end. Every coding task runs in its own git worktree behind lint/type/test gates; artifact-mode tasks, which complete on a signed lineage receipt instead of a commit, get a plain working directory instead. Results stay checkable after the fact: an always-on lineage spine and replay journal, plus an opt-in HMAC-chained audit log (BERNSTEIN_AUDIT=1) with receipts you can verify offline. Air-gap install profile included. Apache-2.0.
Four things set it apart; everything after is detail.
- No LLM in the coordination loop. Scheduling is plain Python, so a run is reproducible end to end. Replay yesterday's plan and get yesterday's task graph.
- Checkable after the fact. The lineage spine and replay journal record every run; the opt-in audit chain adds receipts you verify offline. Non-determinism surfaces as a hash mismatch at the exact step, not a flaky re-run. Non-code deliverables get the same treatment: a task can declare an artifact contract (report, dataset, action log, ops result) on a plan step, a backlog entry, or the task CLI and complete on a signed lineage receipt instead of a git commit.
- Isolated by construction. Each coding task gets its own git worktree behind merge gates; artifact-mode tasks get working-directory separation under
.sdd/workspaces/. Under this default isolation there is no shared mutable state between agents; filesystem enforcement beyond that separation is opt-in, from the sandbox backends (disabling worktrees runs every task in the shared checkout). - Broad and local. 40+ CLI agent adapters plus a generic
--promptwrapper, file-based state, no SaaS hop, no third-party data plane.
The full list is on the capabilities page; the feature matrix is the exhaustive index.
pipx install bernstein
bernstein init
bernstein -g "fix the failing test in tests/test_foo.py"pip, uv, brew, dnf, npm, Docker, and the air-gapped wheelhouse are covered in the install guide.
The recording above is a real run, and it ships with its own proof: the cast, the signed run receipt that exact run produced, and the public key that pins it live together in docs/assets/demo-run/. Verify the run you just watched, offline:
bernstein verify receipt docs/assets/demo-run/run-receipt.json \
--public-key docs/assets/demo-run/run-receipt.pub.pemCI re-verifies the committed receipt on every push — and proves a tampered copy fails — so the published evidence cannot rot into a decorative file. scripts/record_demo.sh regenerates the recording, receipt, and key from a fresh real run; nothing inside the terminal is synthesised.
A run in flight is watchable from either operator surface. Both read the same task API, so neither is a lagging mirror of the other.
![]() |
![]() |
|---|---|
bernstein live — the terminal dashboard |
bernstein gui serve — the same run in a browser |
Determinism here is something you check, not something you take on faith. Run once with audit enabled, then verify what was recorded:
BERNSTEIN_AUDIT=1 bernstein -g "fix the failing test in tests/test_foo.py"
bernstein replay list # run ids recorded on disk
bernstein replay latest --verify # recompute the journal head, name the first divergent step
bernstein lineage verify <run_id> # recompute the always-on lineage spine
bernstein audit verify # HMAC chain + Merkle seal (written because audit was enabled)
bernstein audit diagnose <run_id> --signal gate --sign-key KEY
# name the exact step a failure entered the run, as a signed receipt
bernstein verify run <run_id> --signing-key-path key.pem # sign one portable run receipt
bernstein verify receipt .sdd/runs/<run_id>/run-receipt.json # verify it offline: file onlyThe journal and the lineage spine are written on every run. bernstein audit verify only has a chain to check when the run was started with BERNSTEIN_AUDIT=1, a compliance preset, or bernstein run --audit. The --audit flag belongs to bernstein run; on the bernstein -g form above, set the environment variable.
The run receipt binds the journal head and the lineage-spine head (plus, opt-in, an audit-chain range) under one Ed25519-signed subject with the public key embedded, so a reviewer holding the file and the operator's public key can confirm the recorded actions are exactly what executed - no HMAC key, no live .sdd/, exit 2 naming the first divergent step on tamper. With the file alone (no --public-key pin) the check is integrity-only: it proves the receipt is internally consistent, not who signed it, and the verdict says so. Details in deterministic replay.
The same checkability applies to evaluation numbers: bernstein bench run <suite> --reliability k (also spelled bernstein eval --reliability k) runs every task k times under fixed coordination and reports a pass^k floor (all k attempts must pass) alongside the pass@1 ceiling, sealed in a signed receipt that bernstein bench reliability-verify recomputes offline — a fabricated floor fails verification. Details: pass^k reliability floor.
Each goal moves through four stages:
- Decompose. The manager breaks your goal into tasks with roles, owned files, and completion signals. One LLM call, then plain Python from there.
- Spawn. Agents start in isolated git worktrees, one per coding task; an artifact-mode task gets a plain working directory instead. Main branch stays clean.
- Verify. The janitor checks concrete signals: tests pass, files exist, lint clean, types correct.
- Merge. Verified work lands in main. Failed tasks get retried or routed to a different model.
Why the scheduler is plain Python, and what that trades away: why deterministic.
cd your-project
bernstein init # creates .sdd/ workspace + bernstein.yaml
bernstein -g "Add rate limiting" # agents spawn, work in parallel, verify, exit
bernstein live # watch progress in the TUI dashboard
bernstein run plan.yaml # multi-stage plan: skip LLM planning, execute directly
bernstein stop # graceful shutdown with drainThe full operator surface (PR automation, schedules, chat bridges, the autofix daemon) is in operator commands.
Repository hygiene gates: bernstein readme-l10n verify fails a PR whose translated READMEs drifted from the English source (naming the stale section), bernstein readme-l10n sync rebinds them after an English edit. See readme-l10n.
Claude Code, Codex CLI, Gemini CLI, GitHub Copilot CLI, Cursor, Aider, Goose, OpenAI Agents SDK, Amp, Cody, Continue, Devin Terminal, Junie, Kilo, Kiro, AWS Q Developer, Ollama, OpenCode, OpenHands, Open Interpreter, gptme, Plandex, AIChat, Letta Code, Qwen, and more. The adapter index carries install commands for 29 of them; bernstein integrations list enumerates all 48 wired-in adapters from the registry in src/bernstein/adapters/registry.py, which is the single source of truth for what resolves; src/bernstein/adapters/use_cases.py carries the end-user copy for each one. Anything else with a --prompt flag works through the generic wrapper.
Mix agents in the same run: cheap local models for boilerplate, heavier cloud models for architecture. bernstein integrations list --installed shows what is available on your machine.
Everything deep lives on the docs site:
| capabilities | the full capability list: MCP server mode, signed agent cards, sandbox backends, artifact sinks, regulatory mappings |
| who this is for | where the value lands, and where Bernstein is the wrong tool |
| workflows | declarative YAML DAGs of agent / command / loop nodes |
| web UI | browser dashboard on the same API the TUI uses |
| cloud execution | experimental: run agents on Cloudflare Workers with R2 workspace sync against your own account. The hosted api.bernstein.run service is not yet available |
| datasources | read-only query receipts, plus a query driver that binds each result to the schema snapshot it was derived against |
| security | scorecard, fuzzing, hardening |
| architecture | how it works under the hood |
Bernstein is named after Leonard Bernstein, the American conductor and composer. The project orchestrates a crew of CLI coding agents the way Bernstein conducted the New York Philharmonic: every player on cue, the score deterministic, the conductor accountable for the result. He is the original orchestrator the project takes its name from.
i wrote bernstein because i was paying $400/month in claude bills running three coding agents in parallel and getting nondeterministic merges. Apache 2.0, solo maintained. Live stats: bernstein.run.
Listed in vinta/awesome-python, covered in Augment Code's open-source agent orchestrators roundup, cited by awesome-agentic-patterns as the production implementation of deterministic zero-LLM orchestration, featured in Python Weekly #742, and ranked as the orchestration layer in a ten-repo Claude Code agent-system breakdown.
All coverage: 20+ awesome lists, directories, newsletters, and peer citations
The full tracked list, including every awesome-list entry, catalog listing, prior-art citation, and newsletter mention, lives in docs/mentions.md. Entries are added as they appear; corrections welcome by issue or PR.
PRs welcome; CONTRIBUTING.md has setup and code style. Security reports go through SECURITY.md. If Bernstein saves you time: GitHub Sponsors. Contact: forte@bernstein.run.
Citation metadata lives in CITATION.cff. License: Apache-2.0; the project name is covered separately in TRADEMARKS.md.
Alex Chernysh · GitHub · X · bernstein.run


