Handover: a machine-checkable protocol for AI agent handoffs
Plus a trap-based benchmark, and an honest n=1 that started out confounded - by Harshvardhan Pandey
Coding agents lose context at every session boundary. The outgoing session writes a summary, the next session reads it and starts working. Two things nobody checks: whether those claims are still true against the current repo, and whether the summary actually stops the next agent from repeating a failure the last one already hit.
I watched an agent audit a branch 288 commits behind trunk. Nearly everything it flagged was already fixed upstream. It wasn’t malfunctioning. It trusted an unverified handoff, and there was no mechanism to verify one.
That’s the gap. Everyone is doing context engineering right now: CLAUDE.md and AGENTS.md files, session summaries, handoff notes between Claude Code or Cursor sessions. None of it is checkable. A summary that was accurate on Tuesday reads exactly the same on Friday after forty commits land.
A Handover document is a YAML header plus prose.
---
protocol_version: 1
handoff: guest-checkout
true_at_sha: a1b2c3d4e5f6
first_action: Run the pending migration on the preview database, then point the /api/guest-order route at the new table.
verify_cmd: npm test -- guest-checkout && npm run typecheck
status: in_progress
---
true_at_sha is the anchor. Everything below it was true at that commit, and drift from it is measurable rather than assumed. verify_cmd is the way out of belief: you cannot set status: done without one, and the linter will run it.
The linter splits two questions that normally get conflated. Structure (is this document shaped right, scored 0-100) is judgeable from the text alone. Truth is not, so the repo-aware checks are a separate opt-in layer. --repo resolves the anchor commit and measures how far the codebase has moved. --verify runs the verify_cmd. --claims runs each individual claim’s own check and reports which specific ones went stale.
No LLM and no network anywhere in the enforcement path. It’s a linter, not an agent.
The benchmark
Three repository scenarios, each instrumented with planted failure states a cold agent would plausibly walk into: a reverted change that invites reintroduction, an approach already tried and abandoned, and a next step ambiguous enough to be guessed wrong. The question is whether a handoff artifact steers the successor away from them.
The first agent-in-the-loop run came back perfect. Every condition avoided every trap, including the control that got no document at all. That is not a result, that is a broken fixture. I had written the negative knowledge into the fixture’s own source comments (”do not add this back, see the handover doc”), so every agent could read the answer regardless of what I handed it. I measured nothing.
Fixed the fixture, reran it. One trap out of three discriminated: the compliant handoff was the only condition that avoided reintroducing the reverted client-side pricing bug. The misleading summary and the empty control both walked straight into it. Traps 2 and 3 showed no separation at this scale, and the paper reports that instead of dropping them.
One scenario, one trial per condition. That is a pilot, not a study, and the paper uses those words.
Here’s how you can replicate it in two minutes
In Claude Code:
/plugin marketplace add geekidharsh/handover
/plugin install handover@handover
Restart (hooks load at session start), then /handover:write scaffolds a compliant handoff from live git state and /handover:lint scores it. Or plain Node, any repo, any harness:
node bin/handover-scaffold.js > HANDOVER_myfeature.md
node bin/handover-lint.js HANDOVER_myfeature.md --repo --verify --claims
That last command is the point: it verifies the document against the actual repository and reports which specific claims no longer hold.
Coding agents lose context at every session boundary. The outgoing session writes a summary, the next session reads it and starts working. Two things nobody checks: whether those claims are still true against the current repo, and whether the summary actually stops the next agent from repeating a failure the last one already hit.
I watched an agent audit a branch 288 commits behind trunk. Nearly everything it flagged was already fixed upstream. It wasn’t malfunctioning. It trusted an unverified handoff, and there was no mechanism to verify one.
That’s the gap. Everyone is doing context engineering right now: CLAUDE.md and AGENTS.md files, session summaries, handoff notes between Claude Code or Cursor sessions. None of it is checkable. A summary that was accurate on Tuesday reads exactly the same on Friday after forty commits land.
A Handover document is a YAML header plus prose. The header holds the things that must never be ambiguous:
---
protocol_version: 1
handoff: guest-checkout
true_at_sha: a1b2c3d4e5f6
first_action: Run the pending migration on the preview database, then point the /api/guest-order route at the new table.
verify_cmd: npm test -- guest-checkout && npm run typecheck
status: in_progress
---
true_at_sha is the anchor.
Everything below it was true at that commit, and drift from it is measurable rather than assumed. verify_cmd is the way out of belief: you cannot set status: done without one, and the linter will run it.
The linter splits two questions that normally get conflated. Structure (is this document shaped right, scored 0-100) is judgeable from the text alone. Truth is not, so the repo-aware checks are a separate opt-in layer. --repo resolves the anchor commit and measures how far the codebase has moved. --verify runs the verify_cmd. --claims runs each individual claim’s own check and reports which specific ones went stale.
No LLM and no network anywhere in the enforcement path. It’s a linter, not an agent.
The benchmark
Three repository scenarios, each instrumented with planted failure states a cold agent would plausibly walk into: a reverted change that invites reintroduction, an approach already tried and abandoned, and a next step ambiguous enough to be guessed wrong. The question is whether a handoff artifact steers the successor away from them.
The first agent-in-the-loop run came back perfect. Every condition avoided every trap, including the control that got no document at all. That is not a result, that is a broken fixture. I had written the negative knowledge into the fixture’s own source comments (”do not add this back, see the handover doc”), so every agent could read the answer regardless of what I handed it. I measured nothing.
Fixed the fixture, reran it. One trap out of three discriminated: the compliant handoff was the only condition that avoided reintroducing the reverted client-side pricing bug. The misleading summary and the empty control both walked straight into it. Traps 2 and 3 showed no separation at this scale, and the paper reports that instead of dropping them.
One scenario, one trial per condition. That is a pilot, not a study, and the paper uses those words.
Replicate it in two minutes
In Claude Code:
/plugin marketplace add geekidharsh/handover
/plugin install handover@handover
Restart (hooks load at session start), then
/handover:write
scaffolds a compliant handoff from live git state and /handover:lint scores it.
Or plain Node, any repo, any harness:
node bin/handover-scaffold.js > HANDOVER_myfeature.md
node bin/handover-lint.js HANDOVER_myfeature.md --repo --verify --claims
That last command is the point: it verifies the document against the actual repository and reports which specific claims no longer hold.
What I want from readers
Falsification, mostly. If a document scores 100/100 and still loses something that mattered in a real handoff, then the protocol is wrong and I want the issue filed. If the bench’s traps are gameable, show me how. And if you run the pilot on another model tier and traps 2 and 3 discriminate where they didn’t at n=1, that is exactly the open question §9 leaves sitting there.
Source:
Apache-2.0: github.com/geekidharsh/handover


