Appearance
A task declares filesExpected upfront, a ledger records every edit, and a Stop hook mechanically checks disk before completion is accepted — so an agent can't claim work it didn't do.
Pattern
Hook-Enforced Contract
The shape
Declared upfront, recorded during, checked at completion.
1. Declareat plan time
task.filesExpected
[
{
path: "src/auth.py",
op: "modify",
required:
[validate_token]
},
{
path: "src/old.py",
op: "delete"
}
]
→
in_progress
2. Recordduring execution
task-{id}.ledger.jsonl
append on every
Write / Edit:
{ ts, tool,
path, op,
taskId, plan }
distinguishes
"touched this
session" from
"already existed"
→
completed
3. VerifyStop hook
stop-verify.sh
for each expected:
create → exists?
modify → exists
+ ledger hit?
delete → gone?
symbols → grep -q
exit 0 → accepted
exit 1 → block
Each stage is load-bearing. Strip one and the guarantee breaks.
Why all three
Remove any one and completion becomes self-certified again.
Declaration
filesExpected on the task — what will be touched, which op, what symbols.
Remove it →
The Stop hook has nothing to check. Completion reverts to self-report.
Recording
Ledger appended on every Write/Edit in the session.
Remove it →
Can't verify modify — file may already have existed without being touched this run.
Verification
Stop hook reads expected + ledger, checks disk, exits 0 or 1.
Remove it →
Contract is declared and recorded but never enforced. Agent walks past.
The three components are a tripod — kicking any leg collapses the whole.
What this kills
The phantom-edit failure mode, step by step.
Phantom edit — anti-patternthe pattern exists to eliminate this
1
Builder writes "STATUS: COMPLETE — deleted X, modified Y"
(no files actually touched)
2
Validator runs a narrow grep on the agent's description — sees nothing suspicious — passes
3
Phase finalizer reads the real git diff — files were never touched
4
Regression discovered in production.
Step 1 is impossible under the contract. The Stop hook sees filesExpected, checks disk, finds the missing edit, exits 1 — before the validator ever runs. The error surfaces at the builder's own retry ladder, not three steps downstream in production.
What happens when it fails
The Tier 1 / 2 / 3 retry ladder.
Tier 1
Direct fix
Error names an exact path and symbol. Fix precisely what the error says — no re-investigation.
immediate retry
Tier 2
Investigate
Error is unclear. Re-read the task description and the TDD section, apply a corrected approach.
second attempt
Tier 3
Block
After two failed retries, call task_update --status blocked with a specific reason. Orchestrator creates a correction task — no infinite loops.
hand off
Two strikes and the task escalates. The orchestrator owns the fix — the agent never loops forever on a contract it can't satisfy.
The discipline
Self-reported completion is a claim. Disk state is proof.
The hook doesn't trust the agent's summary — it opens the files. Declared upfront, recorded during, checked at exit. If the contract doesn't match the disk, the agent doesn't get to say it's done.