Local-first orchestration for terminal-based AI coding agents
Project description
striatum
A local workflow runner for terminal-based AI coding agents.
AI coding agents are powerful in isolation and surprisingly fragile in combination. Without a coordinator, two agents reviewing the same draft will agree with each other whether the work is good or not. Without provenance, a crashed session leaves no reliable record of what it touched. Without explicit workflow structure, recovery means poking at files and guessing.
Striatum is the coordinator that was missing. It gives your AI agents — Codex, Claude Code, Gemini CLI, or anything that runs in a terminal — a structured multi-lane workflow with leases, verdicts, audit chains, and durable repo artifacts. No hosted service. No telemetry. No vendor SDK inside the runner.
What Striatum Does
- Coordinates multi-agent work across parallel implement → review → repair → synthesize loops, with deterministic state tracked in a local daemon-owned Postgres database
- Eliminates reviewer co-blindness by making lane assignment first-class: a
codeximplementer can be reviewed byclaudeand synthesized bygemini; aneeds_revisionverdict is recorded, not papered over - Chains every event with a SHA-256 anchor linked to its predecessor — per-repository for events, daemon-global for the RPC audit log;
daemon doctorverifies both chains - Keeps providers portable by wrapping any model whose runtime is a terminal command; core scheduling never imports a model vendor or parses terminal output
- Produces replayable evidence via
corpus export: a redacted JSONL bundle with stable hashes for sharing provenance without sharing live state
The authoritative mutation surface is the daemon MCP. The same CLI verbs that the AI operator uses are available to the human principal — the distinction is not interface, it is role.
At a Glance
flowchart LR
subgraph human["Human principal (escalation only)"]
H[Operator / on-call]
end
subgraph operator["AI operator (default driver)"]
O[Codex / Claude Code / Gemini CLI session]
end
subgraph striatum["Striatum runner"]
CLI["striatum CLI"]
D[("striatumd daemon")]
PG[(Postgres<br/>striatumd schema)]
Scratch[".striatum/<br/>(scratch, FIFOs)"]
CLI -- "Unix socket RPC" --> D
D -- "SELECT / INSERT" --> PG
CLI -- "supervised lanes" --> Scratch
end
subgraph repo["Target repository"]
Source[("src/, docs/, …")]
Artifacts[("artifacts: prompts, findings,<br/>syntheses, decisions, handoffs")]
end
H -. "escalation only" .-> CLI
O -- "claim / publish / review" --> CLI
CLI -- "read / write" --> Source
CLI -- "publish provenance" --> Artifacts
The daemon owns live state. The target repository owns durable provenance.
.striatum/ next to each target repo is operational scratch (supervised
wrapper FIFOs, pidfiles, and transient supervisor scratch). The daemon runtime
token lives under the daemon runtime directory as client-token.
Architecture
┌────────────────────────────────────────────────────────────────┐
│ AI operator · human principal · web UI │
│ (Codex / Claude Code / Gemini CLI / browser) │
├────────────────────────────────────────────────────────────────┤
│ striatum CLI · daemon MCP · local HTTP serve │
│ same verb surface; role distinguishes access │
├────────────────────────────────────────────────────────────────┤
│ Daemon RPC envelope (v1) │
│ capability checks · audit-chain append · method registry │
├────────────────────────────────────────────────────────────────┤
│ Python PG handlers · Go PG handlers (RFC 0039) │
│ runs · sessions · jobs · leases · verdicts │
│ artifacts · blockers · events · audit_log │
├────────────────────────────────────────────────────────────────┤
│ Postgres striatumd schema (schema v6) │
│ append-only events + artifacts · hash-chained audit rows │
│ serialized audit head · per-repo event chain heads │
└────────────────────────────────────────────────────────────────┘
▲
target repo: durable Markdown artifacts
.striatum/: operational scratch (never live state)
Every state transition is a short serializable Postgres transaction that emits a structured event. Events and artifact records are append-only — UPDATE/DELETE are revoked from the daemon read-write role. The hash chain serializes concurrent appenders on the chain-head row so forks are impossible.
Workflow Shapes
Striatum does not pick a default workflow. Every run starts from an explicit workflow.json you choose or generate. These are the canonical shapes — pick the one that matches your desired outcome.
Minimal bounded job
One scoped task, one artifact, no review gate. Good for generating a small report, producing a migration note, or creating a first draft that will be reviewed outside Striatum.
flowchart TD
A["source context"] --> B["single job"]
B --> C["published artifact"]
striatum workflow init --style minimal workflows/my-task
Review and synthesis
The safest first-contact shape. Exercises the core runner model without asking an agent to touch broad source areas. Good for RFC review, product proposals, TODO-to-plan conversion, and documentation review.
flowchart TD
A["proposal or draft"] --> B["draft artifact"]
B --> C["fresh review"]
C --> D["synthesis or apply artifact"]
striatum workflow init --style review workflows/my-review
Code change with bounded revision
Make a repository change and give the reviewer one explicit route to send it back. Good for small code changes, doc fixes, and focused bug fixes.
flowchart TD
A["change request"] --> B["draft change"]
B --> C["review change"]
C --> D["apply accepted change"]
C -. "needs_revision, max 1" .-> B
striatum workflow init --style code-change workflows/my-change
Human checkpoint
Stop and wait for an owner decision before proceeding. The pause is explicit live state, not a comment in an artifact. Good for accept/reject decisions, choosing between designs, or approving a risky write scope.
flowchart TD
A["analysis"] --> B["review"]
B --> C["human checkpoint"]
C --> D["continue path"]
C --> E["cancel path"]
Start from examples/human-checkpoint-flow/.
Evidence-backed artifact
Output that makes claims auditable from curated evidence, not from a hidden transcript. Good for support-heavy technical recommendations, decisions that cite file paths or commands, and claims another reviewer must verify independently.
flowchart TD
A["produce artifact"] --> B["write support ledger"]
B --> C["evidence audit"]
C --> D["final review"]
Start from examples/support-ledger-flow/.
Multi-review synthesis
Disagreement across reviewers is the point. Parallel independent reviews feed a findings ledger or synthesis job; a final review checks the combined recommendation. Good for RFCs, architecture decisions, adversarial posture coverage, and high-risk implementation plans.
flowchart TD
A["source proposal"] --> B["review A"]
A --> C["review B"]
A --> D["review C"]
B --> E["findings ledger"]
C --> E
D --> E
E --> F["synthesis"]
F --> G["final review"]
Start from examples/rfc-ledger-cleanup/.
The Two Roles
Striatum runs with two named roles (RFC 0053):
- AI operator — the default driver. Claims work, publishes artifacts, advances state through
striatumCLI verbs. Same surface that humans have; bounded by function, not by interface. - Human principal — escalation only. Resolves blockers the AI judges itself stuck on (
escalationartifacts). Routine work belongs to the operator.
The day-zero usage guide walks new arrivals through both roles, prerequisites, first run, and the principal's escalation surface.
Why Striatum
Reviewer co-blindness. If the same model both implements and reviews, it will accept work the operator wouldn't. Striatum makes lane assignment first-class (RFC 0018) so a codex implementer can be reviewed by claude and synthesized by gemini, and a verdict reaching needs_revision is recorded — not papered over. The dogfood ledger under docs/dogfood/ shows where this caught real divergence between drafts.
Audit-quality provenance. Many workflows lose state when a session crashes, a process exits nonzero, or a serve restarts. Striatum's authoritative live state is the daemon-owned Postgres; every event carries a previous_hash / row_hash anchor (schema v6, migration 0006); every RPC request lands a row in striatumd.audit_log with a chain head locked FOR UPDATE so concurrent appenders serialize. corpus export produces a verifying manifest with replay-stable SHA-256s.
Provider portability. The runner has no model dependency. Add a lane to a workflow JSON, install a skill bundle for that provider's harness, and the same CLI verbs work. The product boundary in docs/SPEC.md explicitly forbids the runner from importing any vendor SDK.
Quick Start
pip install striatum-orchestrator
# Check/provision the daemon's Postgres substrate.
striatum daemon doctor --apply-migrations
# Start the Go daemon in a separate terminal and keep it running.
striatum daemon start
# Adopt/register a target repo and install the operator skill bundle.
TARGET_REPO=/path/to/your/repo
striatum --repo "$TARGET_REPO" adopt --profile claude_code --json
# Drive a workflow. The operator AI does the rest.
WORKFLOW=examples/code-change-flow/workflow.json
striatum --repo "$TARGET_REPO" workflow validate "$WORKFLOW" --json
striatum --repo "$TARGET_REPO" run prepare --workflow "$WORKFLOW" --json
striatum --repo "$TARGET_REPO" run start --run-id <run_id> --json
striatum --repo "$TARGET_REPO" dashboard --run-id <run_id> --once
Full walkthrough: docs/USING_STRIATUM.md. AI-operator playbook: docs/HOW_TO_AGENT.md. Human-principal escalation guide: docs/HOW_TO_HUMAN.md.
Install the agent skill bundle
# Claude Code (recommended)
striatum skills install --profile claude_code
# Codex
striatum skills install --profile codex
# Gemini CLI
striatum skills install --profile gemini
# All at once
striatum skills install --profile all
The skill bundle teaches a Striatum-aware agent how to drive the runner without reading the source repo. Generated files are version-stamped; striatum doctor flags outdated bundles and emits the exact skills install invocation to fix them.
Use the local web UI
striatum --repo "$TARGET_REPO" serve --web --allow-mutations
Server-rendered Jinja2 UI with a live SVG dependency graph, state-colored job nodes, artifact browsing, verdict recording, and recovery actions. Loopback-only; non-loopback bind is refused at startup.
Project Status
| Area | Status |
|---|---|
| Version | v1.55.0 (see CHANGELOG.md) |
| Platforms | Linux + macOS · Python 3.11+ · Postgres 14+ |
| PyPI | striatum-orchestrator |
| License | Apache-2.0 |
| CI | 1254 passed / 7 skipped / 0 failures on main as of v1.55.0 |
| Daemon substrate | Postgres-native (RFC 0048 complete through all three phases) |
| Schema | v6 — dedicated previous_hash/row_hash columns, serialized chain-head writes |
| Go daemon | Phase 1 landed (RFC 0039): read-only method registry + PG/audit layer; mutating verbs and distribution artifacts are Phase 2 |
| Active RFCs | RFC 0050 ergonomics polish; RFC 0051 auto-finalize; RFC 0039 Phase 2 (Go core CLI) |
| Engram integration | Corpus Contract V2 RFC in design (RFC 0057); corpus export (RFC 0044 V1) ships |
Install from Source
git clone https://github.com/halbritt/striatum.git
cd striatum
make install
.venv/bin/striatum --help
Run tests:
make lint typecheck test
For development without installing the console script:
PYTHONPATH=src python3 -m striatum.cli --help
Documentation
| File | When to read |
|---|---|
docs/USING_STRIATUM.md |
The day-zero usage guide — operator + principal in one pass |
docs/HOW_TO_HUMAN.md |
Human-principal escalation playbook; retains manual operator reference for debugging and demos |
docs/HOW_TO_AGENT.md |
Long-form companion to the RFC 0015 agent skill bundle |
docs/POSTGRES_TRANSITION.md |
Operator runbook for the D094 / RFC 0043 PostgreSQL cutover, retired SQLite handling, and repo registration |
docs/WORKFLOW_TYPES.md |
Workflow shapes and lane sets; starters, examples, defaults |
docs/WRITING_WORKFLOWS.md |
How to author your own workflow.json |
docs/CLI_REFERENCE.md |
Flat list of every CLI verb and stable exit codes |
docs/SPEC.md |
The implementation contract; source of truth when this page disagrees with the runner |
docs/CONSUMER_REPO_LAYOUT.md |
Recommended target-repo layout (RFC 0056) |
docs/ROADMAP.md |
Operator kickoff doc: active runway, queue, blocked items |
docs/INDEX.md |
Every doc in docs/ with a one-line summary |
docs/rfcs/README.md |
Accepted and proposed RFCs (0001 → current) |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file striatum_orchestrator-1.56.0.tar.gz.
File metadata
- Download URL: striatum_orchestrator-1.56.0.tar.gz
- Upload date:
- Size: 38.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b3851c5665f30cec7d5df7725a91d632c9fa1b37526652eedc55bde9da26296
|
|
| MD5 |
1e216db1861a6cfe7a8c9dc697089fdd
|
|
| BLAKE2b-256 |
fb2ede8cb239d7893f16498105fcf65d8a5ddae668114f8597b9c4e8d2bf271b
|
Provenance
The following attestation bundles were made for striatum_orchestrator-1.56.0.tar.gz:
Publisher:
release.yml on halbritt/striatum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
striatum_orchestrator-1.56.0.tar.gz -
Subject digest:
9b3851c5665f30cec7d5df7725a91d632c9fa1b37526652eedc55bde9da26296 - Sigstore transparency entry: 1573378405
- Sigstore integration time:
-
Permalink:
halbritt/striatum@0d2f37faf89fd1a74e2592521b52ce3e1c663c13 -
Branch / Tag:
refs/tags/v1.56.0 - Owner: https://github.com/halbritt
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0d2f37faf89fd1a74e2592521b52ce3e1c663c13 -
Trigger Event:
push
-
Statement type:
File details
Details for the file striatum_orchestrator-1.56.0-py3-none-any.whl.
File metadata
- Download URL: striatum_orchestrator-1.56.0-py3-none-any.whl
- Upload date:
- Size: 38.7 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b3f60cb0213c6198f352c9d48647765d41cade7a1d96876d58d79b271e4b20b
|
|
| MD5 |
7bed4e7e7afe73d53c8262b95a8ba26e
|
|
| BLAKE2b-256 |
ba73e90f1c074c155626caee5db181f477d65d844813fd400fe47d81c38bc48b
|
Provenance
The following attestation bundles were made for striatum_orchestrator-1.56.0-py3-none-any.whl:
Publisher:
release.yml on halbritt/striatum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
striatum_orchestrator-1.56.0-py3-none-any.whl -
Subject digest:
8b3f60cb0213c6198f352c9d48647765d41cade7a1d96876d58d79b271e4b20b - Sigstore transparency entry: 1573378432
- Sigstore integration time:
-
Permalink:
halbritt/striatum@0d2f37faf89fd1a74e2592521b52ce3e1c663c13 -
Branch / Tag:
refs/tags/v1.56.0 - Owner: https://github.com/halbritt
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0d2f37faf89fd1a74e2592521b52ce3e1c663c13 -
Trigger Event:
push
-
Statement type: