Coding-Agent State Protocol

The Machine-Readable Handoff: A Drift Verdict an Agent Can Act On

A coding agent already reads a failing test and fixes the code from its output. The check on the project's recorded state should hand it the same kind of signal — not a sentence to interpret, but the value it expected and the value git actually held.

15 July 2026 · 6 min read · machine-readable verification

In State Drift we named the failure: across many agents, sessions and models, the state you recorded quietly diverges from what git actually contains. In Pre-Push Hooks we wired the fix into the session's boundaries, and in Self-Verifying Agents we drew the line the fix cannot cross: the check on the record has to be deterministic, resolved against git, with no model in the path. This piece is about the other end of that check — its output. Because the first reader of a drift verdict is usually not a person.

The next reader is a program

Think about who consumes the verdict. A human runs casp check at the terminal and reads a colored report. But inside an autonomous loop, the reader is the agent: it runs the check, parses the result, and either proceeds or repairs. The record a session leaves behind — the state file, the last_commit line, the queued next step — is read first by the next session's agent, before it opens a single source file. So the check that guards that record answers to a program at least as often as to a person.

An agent is already fluent in one kind of failure signal. A failing test prints an assertion — expected this, got that — and the agent fixes the code from it directly. That works because the signal is structured: the discrepancy is in the output, not implied by it. The question for a drift verdict is whether it hands back the same kind of signal, or a sentence the agent has to interpret.

What drifted, and what git said instead

A drift finding has always named the claim and the evidence in prose: "last_commit not found in git · state=a1f3c9 does not exist in this repo." Readable, but to act on it a program has to parse the sentence back apart. So the machine-readable report now carries the discrepancy as data — the value the record claimed, and the value git actually held — alongside the same message:

$ casp check --json   # one finding, drift on last_commit
{
  "id": "last_commit.git",
  "rule": "CASP-GIT-001",
  "severity": "fail",
  "label": "last_commit not found in git",
  "detail": "state=a1f3c9 does not exist in this repo",
  "fix": "set state.last_commit to a real SHA (HEAD = 46673a6)",
  "expected": "46673a6",
  "actual": "a1f3c9"
}

expected is what a clean record would hold — here, HEAD. actual is what the record claimed — a hash git has never seen. The pair appears wherever a single before/after is meaningful: the recorded commit versus HEAD, a next_prompt marked shipped where a startable phase should read queued, the migration set on disk versus the set the state names. An agent reads expected and actual the way it reads a failing assertion, and repairs the record without parsing an English clause first.

This changed nothing about the verdict. The report version is the same, the human casp check is byte-for-byte identical, and the fields are simply absent — null — on findings where a single pair says nothing. The check still resolves every claim against git and exits 1 on drift. It gained a clearer way to say what it already found, not a new opinion about it.

The environment, before the state

Structured output helps only once the check runs at all. The failure that greets someone onboarding a repo is more basic: wrong Node, no git, a state file that never got scaffolded, a pre-push hook that was never installed. That is a question about the environment, not the record — a different question than "has the state drifted?" — so it gets its own read:

$ casp doctor

casp:doctor · 7 PASS · 1 WARN · 0 FAIL
  PASS  Node v22.17.1 (>= 20)
  PASS  git version 2.44.0
  PASS  casp/state.json present and valid JSON
  PASS  sessions_dir exists · docs/plan/sessions/
  PASS  core.hooksPath is not set · git uses .git/hooks
  WARN  no pre-push hook installed
        → run `casp install-hook` to run casp check on every push

(exit 0 — a diagnostic never gates)

casp doctor reads the setup and reports PASS / WARN / FAIL per line, with --json for a program. The deliberate part is the last line: it never gates. It always exits 0, even on a FAIL. A diagnostic that blocked a push would just be a second, worse copy of the check — and the whole argument for the check is that there is exactly one deterministic gate. doctor maps what to fix; casp check is the only thing that stops a push.

One call to negotiate the shape

A program parsing the report wants to know what it is parsing before it starts. So a single call reports the binary and the report schema together:

$ casp version --json
{
  "name": "@justethales/casp",
  "version": "0.9.0",
  "node": "v22.17.1",
  "schema_version": 1
}

schema_version is the version of the casp check --json report shape — one lookup to confirm the contract before consuming it, useful when one agent hands the repo to another. The plain casp -V is unchanged for the human at the terminal.

Still one job, still outside the model

None of this widens what CASP does. It does not orchestrate agents, review code, or manage tasks — the loop has those covered. It proves the recorded state matches git, deterministically, and blocks the push when it does not. What changed is the shape of the answer: a verdict the next agent can act on, not just read — and every property that makes it a floor is intact. No model participates, not even advisorily; there is no network call and no account; it reads two local artifacts, the state files and git, so the same floor holds under whatever harness ships next. The report got more legible to the program reading it. The check underneath it did not move an inch.

Start in two minutes

# install
npm i -g @justethales/casp

# in your repo
casp doctor          # is this repo set up to run the gate?
casp check           # prove state == git (exit 0/1)
casp check --json    # the same verdict, machine-readable

@justethales/casp · MIT · git-native · zero telemetry · source on GitHub

The model holds the context. CASP proves the state is true — against git.

Related articles