Skip to content

graphs

The graphs a run executes, and the harness that runs them. This folder is the harness, runner and graphs packages inside the coxswain repository; they ship as part of the one coxswain package. To install Coxswain, see the root README.

Four nouns, one seam each:

Noun Owns Lives in
Harness consequences: side effects, policy, the gate, the ledger harness/
Graph sequence — what runs, in what order, at what tier. Writes nothing graphs/
Cartridge who a run works for: role → skill, where writes land ../cartridges
Runner execution: canned dicts in tests, the model live runner/
harness/   the runtime. resolve, discover, run, gate, apply, record
graphs/    the programs. pure functions of (args, runner)
  delivery/   work that produces work: initiative-decompose, lifecycle-propose, phase-validate,
              review-diff, rescue-review
  ops/        keeping a running system honest: coxswain, epic-reconcile, retro-propose,
              triage-propose, triage, sweep
runner/    node execution: the protocol, a scripted runner, the live one
shell.py   the harness entry point: `python shell.py <graph> ...`
docs/      the contract every graph must satisfy, and one generated page per graph
fixtures/  canned runs and a small work store for offline runs and tests
tests/     this folder's suite, including the portability check

A graph is run(args, runner) -> dict. Nodes ask for a role and a tier, never a skill or a model: the cartridge maps role to skill, the provider profile maps tier to model. Nothing in a graph writes; the harness applies a proposal in a worktree it created, after the gate approved it. See docs/GRAPH-CONTRACT.md for the full contract.

Tests

From this folder:

cd src/graphs && uv run pytest -q

The suite runs with no network and no key: ScriptedRunner replays canned nodes.

Running one offline

From this folder, against the cartridges beside it:

scratch="$(mktemp -d)"
uv run python shell.py triage --team local \
  --cartridges-dir ../cartridges/cartridges \
  --skills-root ../cartridges/skills-plugins \
  --alerts fixtures/alerts.json \
  --scripted fixtures/triage-run.json --assume r \
  --runs-dir "$scratch" --ledger "$scratch/ledger.jsonl"

--scripted replays canned nodes, so no key is needed. --assume r (or a) answers the gate without asking. Pass --ledger as above for a throwaway run: without it the gate decisions are appended to the default ledger, $XDG_STATE_HOME/agent-graphs/ledger.jsonl, or ~/.local/state/agent-graphs/ledger.jsonl when XDG_STATE_HOME is unset. Drop --scripted to run against the real model; the provider profile decides which model each tier means. A host's cox route launch starts the harness the same way, with harness_dir set to <checkout>/src/graphs.

The graphs

The registry is every module under graphs/ that declares a SPEC; harness/registry.py discovers them. shell.py takes the subcommand.

Graph Subcommand Namespace Shape
initiative-decompose decompose delivery decompose → adversary-on-the-edges → emit. An idea into phases and a task DAG
lifecycle-propose lifecycle delivery scope → plan → [alternative plan → arbitrate plans] → [attack the plan] → build (worktree) → handoff → review → adversary → arbitrate → emit
phase-validate validate delivery validate_chunk per task → validate_phase against the phase's ORIGINAL goal. Invoked by the epic driver
review-diff review-diff delivery review_charter → review_adversary → verify_evidence → [arbitrate]. Verdict and findings out, nothing written or pushed
rescue-review rescue-review delivery handoff → review_charter → review_adversary → [arbitrate] → emit. A patch that already exists, reviewed with the harness's own check results
coxswain cos ops one dispatch node over a driver-assembled docket. Selects; the harness invokes
epic-reconcile reconcile ops compare (set arithmetic) → emit. Declared state vs actual
retro-propose retro ops stats (pure arithmetic over ledger rows) → retro → emit. Proposes only what it can cite
triage-propose triage ops fetch → classify → verify → emit. Zero writes; proposes corrections to the runbook it just used
triage triage-quarantine ops facts (pure arithmetic over an item's attempts) → triage → emit. Classifies a stranded item at the attempt cap and emits by class, or escalates a repeated (class, diagnosis) pair
sweep sweep ops sweep_plan → sweep_apply → sweep_verify → [review pair]. One rule applied everywhere it matches; sweep_apply calls a model only when the rule can't be scripted, one build call, and sweep_verify runs no model at all

shell.py also takes phase, epic and rescue. These are drivers, not graphs: they invoke graphs, block on them and own side effects, so they live in harness/ and declare no SPEC. epic is the epic driver (harness/epic.py, specified in graphs/delivery/epic-swarm.md), which cox route launch epic starts.

Each graph's diagram is generated from its own docstring by python -m harness.graphdoc; the committed pages live under docs/graphs/.

License

MIT. See LICENSE.