Define it. Run it. Improve it.
One worked example the whole way through: a four-phase incident review, written as three JSON files, dispatched cheap, and then measured. Every command here is one you can run. Every example is invented.
npm install -g @eir-labs/coltraneNode 24+, and the Claude Code CLI logged in — the engine spawns it, and reads no model key of its own.Three files, in the order they depend on each other.
A genome is a directory of JSON. Types first, because agents declare types and chairs bind by type slug — author an agent first and you will spend the afternoon reshaping types underneath it. Then the producers, then the graph that seats them.
coltrane health # gigs, settled spend, ledger integrity
genome/
domain_types/ incident-report.json impact-assessment.json
remediation-plan.json postmortem.json
review-verdict.json
agents/ impact-analyst.json remediation-planner.json
postmortem-writer.json incident-reviewer.json
skills/ timeline-check/ meta.json skill.mjs fixtures/
standards/ incident-review.json{"slug": "impact-assessment","version": 1,"extends": "Interpretation","domain": "incident","status": "active","schema": {"type": "object","properties": {"incident_id": { "type": "string" },"claims": {"type": "array","items": { "type": "string" },"minItems": 1},"confidence": { "type": "number", "minimum": 0, "maximum": 1 },"blast_radius": {"type": "string","enum": ["one-service", "one-path", "site-wide"]}}},"required_fields": ["incident_id", "claims", "confidence"]}
A data shape. It extends one of the six core types, and that choice is what puts a floor under it — an Interpretation has to carry claims or it cannot be written at all. Declare every field a producer will emit here; the type is the contract the seal is checked against. Your own mandatory fields go in top-level required_fields, not inside schema.required — the loader reads the first and ignores the second.
Which core type you extend decides the floor.
Six primitives, six core types, and each core type carries one field that has to be there when the output is sealed. It is not a lint and not a score you can override: the write is refused and the run stops. Pick the primitive that matches what the step actually does, and the floor comes with it.
| primitive | core type | must carry |
|---|---|---|
| SENSE | Signal | source |
| INTERPRET | Interpretation | claims |
| PLAN | Plan | steps |
| CREATE | Artifact | validation_criteria |
| JUDGE | Judgment | criteria |
| VERIFY | Verdict | checks |
The overview has this as a live boundary — take the substance field away and watch the seal refuse.
Load it before you run it.
coltrane validate loads the whole genome and resolves every reference. It is the cheapest feedback in the system: no model call, no run, no wait.
Nothing partially loads. The engine reports what it cannot resolve instead of loading something adjacent and hoping, which is why coltrane validate works as a merge gate: a pull request that breaks a contract fails the job, in seconds, without spending a token.
# .github/workflows/genome.yml
- run: npm install -g @eir-labs/coltrane
- run: coltrane validate # non-zero exit fails the job
env:
COLTRANE_GENOME: ./genome # without this it loads cwd, finds nothing, and exits 0The same engine ships as an MCP stdio server. Point a client at it and the mcp__coltrane__* tools — including the skill and improvement tools in act three — are available in the conversation. The CLI and the MCP surface run the same engine against the same genome.
{
"mcpServers": {
"coltrane": {
"command": "node",
"args": ["./node_modules/@eir-labs/coltrane/dist/src/server_entry.js"],
"env": { "COLTRANE_GENOME": "./genome" }
}
}
}Dispatch it cheap. Only the real run is expensive.
The input is keyed by type slug, because that is how the entry chair finds it — the graph matches on shape, at dispatch as everywhere else.
{"incident-report": {"type": "incident-report","primitive": "SENSE","source": "oncall-pager:INC-4417","observed_at": "2026-03-04T02:11:00Z","symptoms": ["checkout returning 504s","payments client retrying without a cap"]}}
--depth skim tightens the turn caps, so iterating on the shape of the pipeline costs a fraction of the real thing. --budget <usd> is a ceiling in real dollars: before each batch of steps starts, the engine weighs settled spend against it and stops rather than starting the next one. The batch already running is never interrupted, so a run can finish a little past the ceiling. A step that settles without reporting dollars stops the run too — spend it cannot verify is not spend it can afford.
The gig id is the only thing on stdout. Everything else — progress, warnings, the running cost — goes to stderr, which is what makes the id pipeable and the command scriptable.
coltrane dispatch incident-review --input @in.json | xargs coltrane monitor --follow coltrane dispatch incident-review --input @in.json --json | jq -r '.gig_id' # Without --wait, dispatch hands back the gig id and exits 0. It is not reporting # the run's outcome, because the run has not finished. To gate on the outcome: coltrane dispatch incident-review --input @in.json --wait # exit 0 it finished — OR it parked at a chair a human holds, which is not a failure # exit 1 it ran and failed # exit 2 the invocation was malformed
What a sealed output is.
Every completed chair writes one. The payload is the work; the hashes are not metadata attached afterwards but part of the same write, which is why there is no version of the artifact that exists without them.
{"output_id": "o-4417","type": "postmortem","primitive": "CREATE","produced_by": { "agent": "postmortem-writer", "version": 2, "chair": "writer" },"content_sha": "e7c3…40aa","input_shas": ["51ad…9e02", "c084…71bd"],"payload": {"body": "At 02:11 the checkout path began returning 504s …","validation_criteria": ["every claim names the window it came from","the timeline runs from detection to recovery"]}}
And what it consumed.
Walk it up and you land on the signal that started it. Nothing in the middle can be quietly substituted, because substituting it changes a hash — and the postmortem naming both the plan and the assessment is not a claim about what it read, it is the record of what it consumed.
A run that stops is a position, not a mess.
Which is the whole reason to bother with phases. Durable steps and a resume that refuses to splice two versions of the work together are what make it safe to start a run and go and do something else.
Now change one thing and find out whether it helped.
Cost is recorded for you. Quality is not: it comes from a judgment recorded against an output. In this pipeline the review phase already writes one — a scorecard, sealed against the postmortem it judged, carrying the criteria it was judged on rather than a bare number.
{"output_id": "o-5120","type": "review-scorecard","primitive": "JUDGE","input_shas": ["e7c3…40aa"],"payload": {"subject": "o-4417","criteria": [{ "name": "timeline complete", "met": true },{ "name": "every claim carries a window", "met": false },{ "name": "remediation owner named", "met": true }],"score": 14}}
Work produced outside a run can be scored the same way — the judgment is an output like any other, written through output_write, and it counts from the moment it seals.
Then read the two numbers together.
improvement_report buckets a producer’s outputs by version and by model tier and reports mean cost and mean quality for each, plus the delta and a verdict. The pair is the point: cost alone tells you nothing about whether you should have spent it.
| by version | outputs | mean cost | mean quality |
|---|---|---|---|
| v1 | 18 | $0.36 | 12.4 |
| v2 | 22 | $0.31 | 14.1 |
| delta | — | −$0.05 | +1.7 |
| v3 | 4 | $0.29 | null |
| by model tier | outputs | mean cost | mean quality |
|---|---|---|---|
| premium | 14 | $0.40 | 14.1 |
| economy | 12 | $0.05 | 13.7 |
| delta | — | −$0.35 | −0.4 |
Note the null. Four outputs from v3 have cost but no reviews recorded against them, so quality is unmeasured — reported as null, never as zero. An unmeasured thing does not get to look like a bad thing.
Cost keeps the same rule. A completed gig’s ledger row carries the settled spend of the run that completed it as usage, broken down by the model that actually ran. If a chair’s run ended without reporting usage, or never finished, the row counts it in unattributed_invocations and sets partial: the total is a lower bound, not the bill. A gig whose spend was never captured carries no usage at all — absent, not $0.00.
Raise the bar itself.
A producer improves when the definition under it improves, and the sharpest part of the definition is a skill with executable code, because a skill has fixtures and fixtures do not have opinions. Start by looking: skill_browse for what exists, skill_inspect for what one actually does. Then run it against its own fixtures.
The sixth fixture is new — somebody wrote it the day a postmortem claimed a window that started before anyone was paged. The active skill fails it, which is the entire point of writing it down as a fixture rather than as a note. A deterministic skill needs every fixture to promote, so this is now below the bar it would be held to.
That is the loop. Test against the current fixtures, evolve, watch a regression get thrown away, promote what clears the bar — and every fixture you add on the way is a rung the next candidate has to reach. The definition ratchets. It does not drift.
The bar sits outside the model. So a cheaper model is a measurement, not a gamble.
Change model_tier on the producer to economy, dispatch the same standard on the same input, and read the report again. The floors still refuse a substanceless write. The fixtures still fail a skill that regresses. The contracts still have to be met. Nothing about the definition moved, so the only variable is the model — which is the condition under which the answer means anything.
Eight times cheaper for four tenths of a rubric point is a decision somebody can make in a meeting. “It seemed about the same” is not.
The whole command surface, on one screen.
coltrane validate # load the genome; non-zero on load errors or an orphan
coltrane dispatch <standard> --input @in.json [--depth skim] [--budget <usd>]
[--reuse] [--resume <gig>] [--wait]
coltrane monitor <gig> [--follow] # where a run is
coltrane logs <gig> # what it did, phase by phase
coltrane abort <gig> # a real kill signal, not a flag checked later
coltrane trace <output-id> # what an output consumed, all the way down
coltrane simulate <standard> # walk the graph without spending a token
coltrane seal-genome [<dir>] # bulk-seal a pre-sealing genome; byte-unchanged
coltrane health # gigs, settled spend, ledger integrity
coltrane serve # the MCP stdio serverWhich is what makes --json | jq and dispatch | xargs monitor work without any parsing gymnastics.
Identical inputs through the same producer are served from the prior sealed output instead of derived again. Reuse is keyed on content, so it is safe by construction.
Contract mismatches are cheap to find at compose time and expensive to find at minute nine. Simulate walks the graph and tells you which chair is asking for a shape nothing upstream produces.
The loader reports what it cannot resolve rather than loading something adjacent and hoping. That refusal is the same one everything else on this page rests on.