Claustrum 0.4.0
dotnet tool install --global Claustrum --version 0.4.0
dotnet new tool-manifest
dotnet tool install --local Claustrum --version 0.4.0
#tool dotnet:?package=Claustrum&version=0.4.0
nuke :add-package Claustrum --version 0.4.0
Claustrum
██████╗██╗ █████╗ ██╗ ██╗███████╗████████╗██████╗ ██╗ ██╗███╗ ███╗
██╔════╝██║ ██╔══██╗██║ ██║██╔════╝╚══██╔══╝██╔══██╗██║ ██║████╗ ████║
██║ ██║ ███████║██║ ██║███████╗ ██║ ██████╔╝██║ ██║██╔████╔██║
██║ ██║ ██╔══██║██║ ██║╚════██║ ██║ ██╔══██╗██║ ██║██║╚██╔╝██║
╚██████╗███████╗██║ ██║╚██████╔╝███████║ ██║ ██║ ██║╚██████╔╝██║ ╚═╝ ██║
╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
one core · two doors · any harness
Harness-neutral coding-agent delegation. An "architect" in any host — Claude desktop app, Claude Code, VS Code, Cursor, opencode, GitHub Copilot CLI — delegates builds, reviews and test runs to agents running on any other harness and model (Claude, DeepSeek via opencode + OpenRouter, Cursor, Copilot, or a bare API call), and gets back one normalized receipt.
The story behind it, with the numbers, is newsletter issue 008: Claustrum — un direttore, qualunque sia il podio · English edition.
Status: alpha. M0–M3 have shipped — the five backends, casts, the
claustrum mcpserver, parallel builders each on their own git worktree, a budget ledger kept across the whole job tree, andbackends doctor --probe. M4 is in progress: the spawned architect (claustrum coordinate) and the release pipeline. The measured numbers behind all of it are in NOTES.md.
Why
A team split across four harnesses had one pipeline worth keeping — architect → builders → blind
reviewer ‖ UI reviewer → tester — and it only existed inside Claude Code, because delegation was
Claude's Agent tool and the roles were Claude-format files. A discipline that belongs to one
person is not a discipline.
At the same time the cost sheet said to stop choosing models by vendor. Per solved task, cheap tokens stop being cheap once the human cleanup after a failed run costs more than about eighteen minutes; and error propagates asymmetrically by role — an architect's mistake is inherited by every builder, a builder's mistake is caught downstream. So: Opus for the architect and the blind reviewer, something cheap for builders and the tester, and never a reviewer weaker than the builder it reviews. Routing by role needs one conductor who can talk to every model, from whatever podium they happen to be on.
Two doors, one core
Every host on the list gives an in-chat agent exactly two universal hooks: a shell tool and MCP. So Claustrum has two doors and nothing else:
claustrum run builder --brief-file order.md --json # door 1: a command, blocks, prints one JSON
claustrum mcp # door 2: stdio MCP server — delegate, job_status, cast_questions, doctor …
Whichever door you use, the core does the same five things: prepare the delegated agent's instructions (who it is, what it may touch, how it must report), photograph the repository with git, launch the agent on the chosen harness and wait, photograph again — the difference is what it really changed — and read the final report.
The work order and the receipt
You hand in a brief with four fixed sections: ## Task (the requester's words, verbatim),
## Scope, ## Must still work, ## Diff (how a reviewer obtains it). Non-blind roles may add
## Context; for a blind role the runner refuses a brief that carries context, plan, rationale,
or a pasted report — the blind review is enforced, not requested.
You get back one RunResult regardless of who did the work:
outcome ........... success
who ............... builder on DeepSeek V4 Flash, via opencode
files changed ..... 2 ← git says so, not the agent
report
what I did ...... added the opencode backend
to be tested .... reading the JSONL events
decided alone ... nothing, the brief was enough
shaky ........... the event names are unconfirmed
cost .............. $0.012 · 41 s
files_changed and diff come from the before/after git snapshots, so they are right even for a
harness whose own output does not list edits. The report is the fenced claustrum-report block
every role must end with — the four lines of the builder's honest report, and shaky is the one
the coordinator reads first.
The cast
Who plays which role is decided once and stored as a file, .claustrum/casts/<name>.json,
committable and shared:
{ "name": "default", "library": "1.1.0",
"architect": { "mode": "host", "model": null, "tier": null, "resolved": null, "fallback": null, "resolved_fallback": null },
"roles": {
"builder": { "model": "opencode:openrouter/deepseek/deepseek-v4-pro", "backend": null, "tier": null, "max_parallel": 3, "resolved": null, "fallback": "claude:sonnet", "resolved_fallback": null },
"code-reviewer": { "model": "claude:opus", "backend": null, "tier": "xhigh", "max_parallel": null, "resolved": null, "fallback": null, "resolved_fallback": null },
"demo-author": null,
"tester": { "model": "opencode:openrouter/deepseek/deepseek-v4-flash", "backend": null, "tier": null, "max_parallel": null, "resolved": null, "fallback": null, "resolved_fallback": null },
"ui-reviewer": null },
"budget_usd": 10 }
claustrum cast create writes this shape; tier and backend are hand edits it leaves null. A
document without roles or architect is refused on load — don't hand-write a flat one. A role set
to null is "not needed". A role's optional fallback names the model that plays it instead when its
provider refuses the first one before the role did anything (out of credits, a rejected key, an unknown
model, no connection): the run is retried once there, and the receipt says so (docs/MANUAL.md §8).
/claustrum in any chat asks one question per library role — architect (host or spawned), builder,
reviewers, demo-author and tester (or "not needed") — then the builders' parallel cap and a budget
(or "no cap"). The tool owns the questionnaire (claustrum cast questions --json) so the questions
and their live options are identical in every host. budget_usd: null disables the cap; cast show
prints it as "budget_usd": null.
Roles: one source, every harness
Roles live once, in roles/<role>/ROLE.md + role.json, and name model classes
(frontier-coding, cheap-coding, fast) that each developer maps to real models in
claustrum.json. claustrum sync renders them into .claude/agents/, .opencode/agent/,
.cursor/agents/, .github/agents/, plus a /claustrum skill and the MCP registration for each
host. Generated files carry a marker; sync --check flags hand edits in CI; unmarked files are
never overwritten.
Backends
| Backend | Role injection | Permissions (edit + shell, never push) |
|---|---|---|
claude |
--append-system-prompt-file |
--permission-mode acceptEdits --disallowedTools "Bash(git push),Bash(git push *)" |
opencode |
inline agent in OPENCODE_CONFIG_CONTENT |
permission: {"edit":"allow","bash":{"git push":"deny","git push *":"deny"}} |
cursor |
role prefixed to the prompt, fed on stdin | --mode plan for read-only rungs, else -f; deny by prompt rule |
copilot |
per-job agent file | --allow-all-tools --deny-tool "shell(git push)" |
api |
system message; no tools | reasoning-only roles |
acp-opencode |
opt-in: the same OPENCODE_CONFIG_CONTENT agent, driven over the Agent Client Protocol (opencode acp) |
same as opencode |
ACP backends are opt-in (--backend acp-opencode, or a model alias acp-opencode:<model>); the
native backends stay the defaults, and a new ACP harness is one IAcpHarness row plus one recorded
transcript.
Adding a backend is one class implementing IBackend (detect, build argv, parse output) and one
registry line. Claustrum launches backends on the OS it runs on — the Windows binary talks to
Windows harnesses, the Linux binary to Linux ones; it never translates paths. Under WSL, where both
worlds are visible at once, backends doctor --probe warns when the two disagree.
Enforced, not requested
- Blind review — the runner rejects a brief for a
blindrole that carries rationale. - Parallel builders —
max_parallelis a cap at every value, a semaphore shared by CLI and MCP; above 1 each job gets its owngit worktreeon aclaustrum/<job>branch, where the runner commits its work; integration is by rebase. - Budget — accounted over the whole job tree (
CLAUSTRUM_PARENT_JOBnames the tree); a child that would exceed what is left is refused;claustrum jobs budget <tree>shows the ledger.
Two ways to conduct
architect: host — the agent you are chatting with adopts the role and issues delegations.
claustrum coordinate --cast <name> --issues 12,13 — Claustrum spawns the architect too, headless,
with the cast injected and claustrum on its allowed tools; the pipeline is the architect's prompt,
not a state machine.
Build
.NET 10, NativeAOT single binary (Windows, macOS, Linux; ~3 MB).
dotnet build
dotnet test
dotnet publish src/Claustrum/Claustrum.csproj -c Release -r linux-x64 -p:PublishAot=true
Contribution rules — style, comment ceiling, linear history, issues on the
project board — are in AGENTS.md;
design rationale in NOTES.md, with one standing design note of its own:
docs/metaconcert-on-claustrum.html — why the
metaconcert protocol would run on Claustrum, and what the
mc-* roles on docs/PLAN.md's not in v1 list would cost.
Install
A release binary per OS, the .NET global tool, or a build from source — docs/INSTALL.md has all three.
dotnet tool install -g Claustrum
Then docs/TEST-DRIVE.md walks the first run on a real task, step by step, and docs/MANUAL.md is the reference for every verb, file, field and rule. Upgrading? docs/UPGRADING.md says what each release asks of you.
Licence
MIT. Named after the thin sheet of neurons under the cortex that Crick and Koch (2005) proposed as the conductor binding the brain's specialised regions into one act.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.