# A JSON receipt for AI handoffs, and a small check for "done"

> Source: <https://dev.to/akiohirayama/a-json-receipt-for-ai-handoffs-and-a-small-check-for-done-2fp2>
> Published: 2026-10-29 16:13:46+00:00

An AI saying "done" is a claim. Work is complete only when something outside that AI has checked it.

I run my personal website, [akiohirayama.com](https://akiohirayama.com/), in nine languages with ChatGPT/Codex and Claude. Several AIs touch the same work: one drafts, another reviews, a third checks how pages actually render. The weak point is the handoff. The next AI usually receives a summary it has to believe.

This post shows the fix I use: a small, structured receipt that travels with the work, and a check that reads it.

The [Multi-AI Handoff & Approval Card](https://github.com/akiohirayama/multi-ai-handoff-card) has eight fields. If any of them is empty, the work has only been reported. It is not done yet.

```
{
  "task": "what was asked, in one sentence",
  "claimed_result": "what the AI says it did",
  "risk_tier": "auto | auto-then-verify | human-approval",
  "risk_reason": "why this tier",
  "evidence": [
    { "check": "what was checked", "checked_by": "script / fresh-context run / human (not the same AI run)", "result": "pass | fail | unclear" }
  ],
  "changed": ["pages, files or systems touched"],
  "rollback": { "how": "how to undo it", "who": "who can undo it" },
  "open_items": ["anything unverified, ambiguous or skipped"],
  "approved_by": "a human, or the rule that allowed auto-approval"
}
```

Three fields do most of the work:

Not every step needs a human. Approving everything trains people to click "yes" without reading.

| Tier | Examples | Default | 
|---|---|---|
| auto | Draft a translation, generate a design option | Run automatically, log it | 
| auto-then-verify | Change a live page, update shared text | Run, then verify before it counts as done | 
| human-approval | Publish, delete, spend, touch credentials, message anyone | Stop and ask a human, with a one-screen summary | 

A receipt is only useful if something reads it. This script returns `DONE` only when every field is filled, every piece of evidence passed and has a named checker, the rollback says both how and who, and no open items remain.

``` python
import json
import sys

REQUIRED = ["task", "claimed_result", "risk_tier", "risk_reason", "evidence",
            "changed", "rollback", "open_items", "approved_by"]
TIERS = {"auto", "auto-then-verify", "human-approval"}

def check(card):
    problems = []
    for key in REQUIRED:
        if key not in card:
            problems.append(f"missing field: {key}")
    for key in ["task", "claimed_result", "risk_reason", "approved_by"]:
        if key in card and not str(card[key]).strip():
            problems.append(f"empty field: {key}")
    if card.get("risk_tier") not in TIERS:
        problems.append("risk_tier must be auto, auto-then-verify or human-approval")
    evidence = card.get("evidence") or []
    if not evidence:
        problems.append("no evidence: the work is reported, not done")
    for item in evidence:
        if item.get("result") != "pass":
            problems.append(f"evidence not passing: {item.get('check', '?')}")
        if not str(item.get("checked_by", "")).strip():
            problems.append(f"evidence without a checker: {item.get('check', '?')}")
    rollback = card.get("rollback") or {}
    if not rollback.get("how") or not rollback.get("who"):
        problems.append("rollback needs both how and who")
    if card.get("open_items"):
        problems.append(f"{len(card['open_items'])} open item(s) still listed")
    return problems

if __name__ == "__main__":
    with open(sys.argv[1], encoding="utf-8") as f:
        card = json.load(f)
    found = check(card)
    for p in found:
        print("-", p)
    print("DONE" if not found else "NOT DONE")
    sys.exit(0 if not found else 1)
```

Here is a card that fails:

```
{"task":"Publish the article","claimed_result":"Published","risk_tier":"human-approval","risk_reason":"public","evidence":[{"check":"links work","checked_by":"","result":"unclear"}],"changed":["note.com"],"rollback":{"how":"unpublish"},"open_items":["image credits"],"approved_by":""}
- empty field: approved_by
- evidence not passing: links work
- evidence without a checker: links work
- rollback needs both how and who
- 1 open item(s) still listed
NOT DONE
```

The exit code is non-zero, so the same check can gate a pipeline step or a handoff to the next agent.

The card, `card.json` and a longer field guide are free under CC BY 4.0:

I am also hiring an AI Engineer, at a monthly salary of USD 30,000, to build this kind of control layer across multiple AIs: [role details](https://akiohirayamaaiengineer.wordpress.com/).

— AKIO HIRAYAMA ([akiohirayama.com](https://akiohirayama.com/))
