Bernstein - Multi-agent orchestration
Bernstein is a multi-agent orchestration server for launching audited runs, managing a claimable task backlog, and verifying results.
Start orchestration runs or empty administrative containers (
bernstein_run) and poll verifiable run status (bernstein_run_status).Create bare backlog tasks and atomically claim work as an agent (
bernstein_create_task,bernstein_claim).Report completion, approve pending results, or cancel a task and its subtasks (
bernstein_complete,bernstein_approve,bernstein_cancel).Post task mailbox messages and versioned artifacts such as reports, tables, links, and SARIF findings (
bernstein_post_message,bernstein_post_artifact).Read liveness, task counts, and cost (
bernstein_status), and wait for role notifications over SSE (bernstein_subscribe).Inspect a task capsule with local journal and audit evidence (
bernstein_task_capsule).List or load skills, references, and script files (
load_skill).Shut down the entire project orchestrator (
bernstein_shutdown_orchestrator).
Provides GitHub App integration for connecting with GitHub repositories, enabling automated workflows and repository management within the orchestration ecosystem.
Provides Grafana dashboards for observability and monitoring, allowing visualization of orchestration metrics, task execution status, and cost tracking data.
Orchestrates Ollama CLI agent for running local AI models offline, enabling parallel task execution with local models without cloud dependencies.
Exposes Prometheus /metrics endpoint for monitoring orchestration performance, agent health, task metrics, and cost tracking with built-in metrics export.
"To achieve great things, two things are needed: a plan and not quite enough time." - attributed to Leonard Bernstein
the open-source governance layer for AI agents
website · docs · deepwiki · install · first run · glossary · limitations · name policy · discord
简体中文 · 繁體中文 · 日本語 · 한국어 · हिन्दी · বাংলা · Русский · Español · Português · Deutsch · Français · Italiano · Nederlands · Polski · Svenska · Suomi · Українська · Türkçe · العربية · עברית · Bahasa Indonesia · Tiếng Việt · ไทย
Status: beta. Solo-maintained, under active development. The version number counts releases, not maturity - minor versions may change interfaces. Pin the version for anything you depend on; regressions get fixed fast, file them.
Bernstein is the open-source governance layer for AI agents. It runs on policy as code: you write the policy - who may do what, what needs approval, what must be recorded - and Bernstein enforces it and produces the verifiable record. A deterministic scheduler - no model in the coordination loop - runs agents in parallel, gates what they produce, and records every step, so a run can be verified after the fact, offline, from the artifacts alone. CLI coding agents work out of the box (Claude Code, Codex, Gemini CLI, and 50+ more), and the same layer governs any agent workload: the deliverable can be a diff, a research report, a dataset, or an audit evidence pack. Air-gap install profile included. Apache-2.0.
at a glance
Five 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.
Hardware key evidence. Offline PIV custody and FIDO2 approval verifiers share the receipt protocol, with pinned trust and explicit time. Token custody does not raise TRACE trust levels. See hardware key evidence.
Checkable after the fact. The replay journal records every run, and the always-on lineage spine records every lineage-bearing step; the opt-in HMAC-chained audit log (
BERNSTEIN_AUDIT=1) 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) and completes on a signed lineage receipt rather than a git commit.Isolated by construction. Each coding task gets its own git worktree behind merge gates; artifact-mode tasks get a working directory under
.sdd/workspaces/. Agents share no mutable workspace by default; the only shared state is the task backlog, which is claimed atomically. Stricter filesystem enforcement is opt-in, from the sandbox backends. Disable worktrees and every task runs in the shared checkout.Broad and local. More than 50 selectable 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.
what a run looks like
One YAML file declares the run: phases, roles, dependencies, and the conditions under which a node runs at all. The scheduler executes it as plain Python - nothing in the file is a prompt, and no model decides what happens next. This graph produces an audit evidence pack; the full file ships at .bernstein/workflows/audit-evidence-pack.yaml.
name: audit-evidence-pack
version: "1.0.0"
phases:
- name: scope
allowed_roles: [manager, architect]
- name: collect
- name: validate
allowed_roles: [qa, security]
- name: deliver
allowed_roles: [security, manager]
nodes:
define-control-inventory:
phase: scope
role: architect
collect-audit-logs:
phase: collect
role: security
depends_on: [define-control-inventory]
# three more evidence streams collect in parallel:
# collect-sboms-and-attestations, collect-runbooks-and-policies,
# collect-eval-results
assemble-pack:
phase: validate
role: docs
depends_on:
- collect-audit-logs
- collect-sboms-and-attestations
- collect-runbooks-and-policies
- collect-eval-results
mock-auditor-pass:
phase: validate
role: qa
depends_on: [assemble-pack]
remediate-findings:
phase: collect
role: docs
depends_on:
- source: mock-auditor-pass
condition: "status == 'failed'"
retry:
max_attempts: 3
until: "status == 'done'"
sign-and-deliver:
phase: deliver
role: security
depends_on:
- source: mock-auditor-pass
condition: "status == 'done'"flowchart LR
inv[define-control-inventory] --> logs[collect-audit-logs]
inv --> sbom[collect-sboms-and-attestations]
inv --> rb[collect-runbooks-and-policies]
inv --> ev[collect-eval-results]
logs --> pack[assemble-pack]
sbom --> pack
rb --> pack
ev --> pack
pack --> gate{mock-auditor-pass}
gate -->|failed| fix["remediate-findings (retry x3)"]
gate -->|done| sign[sign-and-deliver]Each node is claimed by an agent whose role the phase allows; role fences and approval gates hold no matter what the agent does inside the task. A coding node completes behind merge gates in its own git worktree. The nodes above complete differently: an artifact contract names the deliverable (report, dataset, scan, action log), and the node finishes on a signed lineage receipt instead of a commit. Same scheduler, same journal, same offline verification - whether the graph ships code, research, an ops change, or a mix of all three. Ready-made graphs for software, research, docs, enterprise, and contributor workflows live in .bernstein/scenarios/.
install in 30 seconds
uv tool install bernstein # or: pipx install bernstein
bernstein init
bernstein doctor # checks a CLI agent is installed and authenticated
bernstein -g "fix the failing test in tests/test_foo.py"bernstein doctor --json also exposes audit-contract findings, identical to
bernstein govern audit --only INS --json for the same installation observations.
The normal doctor report keeps its existing display. See the
doctor operations guide for stable check IDs.
pipx, pip, brew, dnf, npm, and Docker are covered in the install guide; the air-gapped wheelhouse has its own air-gap guide.
bernstein worktrees sweep previews worktrees and sandbox sessions left behind
by finished tasks, with age and disk usage. Only worktrees can be removed, using
--apply and a separate removal approval. Sandboxes are report-only (removal:
#6571); doctor warns about aged leaks. See the
lane cleanup workflow.
Skill initialization failures are quarantined with a persisted reason and audit event. bernstein status shows the count; doctor and govern audit report the disabled declarations. After fixing the cause, explicitly release the marker with bernstein quarantine re-enable DECLARATION_ID.
GHCR · GitHub Codespaces
The recording above is a real run, and it ships with its own proof. The cast, the signed run receipt derived from that run's journal, and the public key that pins it all live 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.pemThe committed receipt can be re-checked at any time with the command above, and a tampered copy fails the same check. 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. In bernstein live, the left and right columns scroll independently as whole panes, so widgets below the fold remain reachable in shorter terminals.
|
|
|
|
prove a run
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 is written on every run; the lineage spine is always on and gains an entry for each lineage-bearing step, so a short run can finish with a valid, empty spine. 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.
One run receipt binds the journal head, the lineage-spine head when the run wrote spine entries, and, opt-in, an audit-chain range, under a single Ed25519-signed subject with the public key embedded. A reviewer holding that file and the operator's public key can confirm the embedded actions and chains were not changed: no HMAC key, no live .sdd/, and exit 2 naming the first divergent step on tamper. That receipt identifies the journal state it embeds; proving that state is the complete finished journal additionally requires an independent head/count seal. With the file alone and 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, then reports a pass^k floor (all k attempts must pass) alongside the pass@1 ceiling. That result is sealed in a signed receipt which bernstein bench reliability-verify recomputes offline, so a fabricated floor fails verification. Details: pass^k reliability floor.
Use bernstein govern audit --format json or --format sarif for machine-readable findings and --strict --quiet for a CI gate. See govern audit.
bernstein delegation verify RUN compares the receipt chain with the final
head and hop count sealed in the run manifest with the audit-key HMAC, detecting
tail deletion, substitution and post-close growth, including keyless rewrites of
the declaration. Removing the declaration leaves the run incomplete (exit 3),
never valid. This does not detect replay of an older authentic snapshot or protect
against a writer holding the audit key. See the
delegation verification workflow.
how it works
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.
everyday commands
cd your-project
bernstein init # creates .sdd/ workspace, bernstein.yaml + templates/
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.
bernstein workflow runs declarative YAML DAGs of agent, command, and loop nodes - with resume support for interrupted runs:
bernstein workflow run idea-to-pr -g "Add JWT auth" # prints run_id
bernstein workflow resume <run_id> # picks up at the first non-completed nodeRun state checkpoints to .sdd/runs/<run_id>/ on every node. Resume validates the manifest digest at run start, so a spec change is refused rather than silently executing a different manifest. See workflow manifests.
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.
supported agents
Claude Code, Codex CLI, Gemini CLI, GitHub Copilot CLI, Cursor, Aider, Goose, Muse Code, 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 30 of them. bernstein integrations list enumerates all 56 wired-in integrations from src/bernstein/adapters/registry.py, the single source of truth for what resolves. 54 of them are selectable agent adapters; the other two rows are the mock test stub and the self-hosted-endpoints endpoint profile. 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.
volunteer compute
A project can mark issues as open to volunteers, and anyone can run one on their own machine without an account or a coordinator. The project declares what a task is allowed to do in a volunteer.json manifest - sandbox backend, network allowlist, wall-clock and memory ceilings - and a donor's own limits can only narrow that, never widen it. The receipt a finished task produces binds the result to the containment decision it ran under, so a maintainer can check months later what the work was actually permitted to touch.
bernstein volunteer verify .
bernstein volunteer browse --budget 60The donor guide covers running a worker and the budget you set, the project guide covers declaring a manifest, and the threat model states what each boundary does and does not protect. The one-command runner is not shipped yet: verify, browse, budget, verify-bundle and hub are the working subcommands today. autopilot is registered but refuses to run until a TaskSource is implemented.
beyond the front page
Everything deep lives on the docs site:
page | what it covers |
the full capability list: MCP server mode, signed agent cards, sandbox backends, artifact sinks, regulatory mappings | |
| |
local stdio server, plus a hosted read-only endpoint at | |
where the value lands, and where Bernstein is the wrong tool | |
declarative YAML DAGs of agent / command / loop nodes | |
browser dashboard on the same API the TUI uses | |
experimental: run agents on Cloudflare Workers with R2 workspace sync against your own account. The hosted | |
read-only query receipts, plus a query driver that binds each result to the schema snapshot it was derived against | |
point roles at agent definitions outside the built-in templates - a generic YAML/SKILL.md directory, or a Claude Code plugin-layout tree | |
what Bernstein bridges to, by the name you call it: SCIM, OIDC, Vault, OPA/Cedar, SPIFFE, OpenTelemetry, MCP, A2A - with what is shipped, what is wired, and what is not planned | |
scorecard, fuzzing, hardening | |
how it works under the hood |
why the name?
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.
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.
mentioned in
Listed in vinta/awesome-python, covered in Augment Code's open-source agent orchestrators roundup, and listed in Python Weekly #742. We also wrote up the approach as the deterministic zero-LLM orchestration pattern in awesome-agentic-patterns.
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.
Administrative runs
Use bernstein_run(template="administrative", goal="Weekly sprint") to create an
empty, audited container without a planner or agent process. Add manual work with
bernstein_create_task(title="Review brief", description="...", run_id="admin-...")
and poll bernstein_run_status(run_id="admin-..."). Members are claimable through
the usual claim API and excluded from automatic orchestration. A nonempty run
completes when every member has status done, closed, or cancelled; an empty run
stays open. See the operator workflow.
contributing, support, license
PRs welcome; CONTRIBUTING.md has setup and code style. Security reports go through SECURITY.md. 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 · task notifications
Available Tools
14 toolsbernstein_approveA
Sign off a finished result only when the task is in pending_approval; every other status is refused. This completes the task on the Bernstein server. Host effects: makes network requests.
Inputs: task_id (required), note. Returns: result
bernstein_cancelA
Cancel one task and its subtask tree; the orchestrator keeps running. Cancellable statuses: open, claimed, in_progress, blocked, waiting_for_subtasks, planned. An already-terminal task is reported, not treated as an error. This changes task state on the Bernstein server. Host effects: makes network requests.
Inputs: task_id (required), reason. Returns: result
bernstein_claimA
Claim the next eligible task for an agent. This changes task assignment state on the Bernstein server. Host effects: makes network requests.
Inputs: claimer_id (required), role, project, capability, +3 more. Returns: result
bernstein_completeA
Report a task result only from a worker-held state: open, claimed, or in_progress; every other status is refused. This completes the task on the Bernstein server. Host effects: makes network requests.
Inputs: task_id (required), result_summary (required). Returns: result
bernstein_create_taskA
Create a bare task without triggering orchestration or spawning agents. The task enters the backlog and is immediately claimable via bernstein_claim by any session matching its role. Host effects: writes files; makes network requests.
Inputs: title (required), description (required), role, model, +5 more. Returns: result
bernstein_post_artifactC
Post a versioned artifact to a task on the Bernstein server. Host effects: makes network requests.
Inputs: key (required), poster (required), task_id (required), artifact_type (required), +11 more. Returns: result
bernstein_post_messageB
Post a progress message to a task mailbox on the Bernstein server. Host effects: makes network requests.
Inputs: body (required), sender (required), task_id (required), kind, +1 more. Returns: result
bernstein_runA
Start an orchestration run. A run does real work and takes minutes to hours; the call returns once the run is queued, not when it finishes. Do not re-issue it while waiting, that starts a second run. Follow the run with bernstein_run_status, passing either the returned task_id or the returned run_id, after waiting the returned poll_after_ms. Pass parent_task_id to create the run as a subtask of an existing task. The queued orchestration writes project state and starts agent work. Host effects: writes files; spawns agent processes; makes network requests. Set template=administrative to create an empty audited manual container without a planner or agent processes; goal may be omitted for this template.
Inputs: goal, role, scope, priority, +4 more. Returns: _meter, result
bernstein_run_statusA
Poll a verifiable handle for a run started with bernstein_run. Accepts either identifier that call returned: the task_id or the run_id. Reads the local run journal and audit evidence without changing them. Host effects: reads files.
Inputs: run_id (required), workdir. Returns: _meter, result
bernstein_shutdown_orchestratorA
Shut down the ENTIRE Bernstein orchestrator for this project, including every run and worker; use bernstein_cancel to stop one task while the orchestrator keeps running. Writes the local SHUTDOWN signal file. Host effects: writes files.
Inputs: workdir. Returns: result
bernstein_statusA
Liveness, task counts, and cost in one read. Pass status to include the matching tasks; pass detail=true for full per-role and per-task rows. Retrieves data from the Bernstein server without changing it. Host effects: makes network requests.
Inputs: detail, status. Returns: _meter, result
bernstein_subscribeB
Wait for one future role task notification over authenticated SSE, then unsubscribe. Host effects: makes network requests.
Inputs: role (required), timeout_seconds. Returns: _meter, result
bernstein_task_capsuleA
Read a task capsule together with its local journal and audit evidence. With verify=true, verification may create the install audit key if it is absent. Host effects: reads files; writes files.
Inputs: task_id (required), verify, workdir. Returns: result
load_skillA
List available skills when name is omitted, or load a named skill body, reference, or script file contents. Returns file contents as text; executes nothing. Host effects: reads files.
Inputs: name, script, reference. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 14 tools
Each tool targets a distinct action+state, and the descriptions explicitly cross-reference the confusing neighbors (e.g. bernstein_run vs bernstein_run_status vs bernstein_status; bernstein_cancel vs bernstein_shutdown_orchestrator; bernstein_complete vs bernstein_approve gated by task status). Genuinely ambiguous pairs are pre-empted in the text, so an agent can reliably select. The only overlap is status-read surfaces (run_status, status, task_capsule), and those are separated by what they read.
Most tools use a bernstein_ prefix with a verb or verb_noun form (create_task, post_message, post_artifact, cancel, claim, approve, complete), which is predictable. Deviations exist: bernstein_task_capsule is noun-only, load_skill drops the server prefix, and bare verbs sit beside verb_noun forms. Still readable and largely systematic.
14 tools for an orchestration server that must cover run lifecycle, task lifecycle, messaging, and artifacts is well-scoped. No tool looks redundant enough to be dropped, and nothing essential is obviously missing from the count.
The surface covers the main lifecycle: queue a run, create/claim/complete/approve/cancel tasks, post messages and artifacts, poll status, shut down, and inspect capsules. Gaps remain around reading existing mailbox messages (post_message and subscribe exist but no explicit mailbox fetch) and releasing/reassigning a claimed task. These are workable but could force agent workarounds.
Maintenance
Related MCP Connectors
The AI orchestration agent for modern software teams.
AI work orchestration for plans, tasks, teams, and coding-agent dispatch.
Command center for developers coordinating AI coding agents across projects.
Coordination hub for AI coding agents: message teammates, ask humans, audit every event.
Related MCP Servers
- -licenseBqualityNot gradedmaintenanceEnables AI-driven orchestration of GitHub development workflows including automated issue analysis, code generation, code review, and PR creation through multiple specialized agents. Integrates with GitHub Actions to automate the complete development process from issue to pull request.7-
- FlicenseNot gradedqualityNot gradedmaintenanceA multi-agent runtime that coordinates six specialized agents through a typed artifact pipeline with 41 RPC methods. It features dynamic autonomy levels and context sufficiency scoring that adjust agent behavior based on the operator's state and task requirements.-
- AlicenseAqualityCmaintenanceMulti-agent orchestration server that enables parallel task delegation, sequential pipelines, cron scheduling, and cross-model peer review via CLI providers like Codex, Antigravity, OpenCode, and Claude Code.4240 npm6MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI coding providers to run structured multi-agent workflows with document drafting, task tracking, handoffs, and health-gated completion.29 npmMIT

