# Evidence Standard: five rules and a five-check script (free sample)

Version 1.0, 2026-10-29. By PaperChaseWebb. For anyone who lets an agent report work as done, in Grok Bot, Claude Code, Codex, Hermes or a mix, and wants a written rule for what counts as proof before that word gets used. One Markdown file and one Python 3 script, standard library only. The script was tested on its worked example (5 findings, then 0) and on a missing file (exit 2). It was not run inside any agent harness or on a real team's work.

An agent says the tests pass. Sometimes they do. Sometimes the test was a stub, the run was from before the last edit, or no run happened at all and the sentence came from the plan. The fix is not a better prompt. It is a short written standard: for each kind of work, what counts as proof, who checks it, and what is refused. These five rules cover the gaps that cost the most, and the script checks them in one small file.

## Install in one line

Save this page as FREE-SAMPLE.md, then:

```
python3 -c "import re;t=open('FREE-SAMPLE.md').read();open('evidence_mini.py','w').write(re.search(r'~~~python\n(.*?)~~~',t,re.S).group(1))"
python3 evidence_mini.py evidence-lite.json
```

## Five rules

1. **A person owns the standard.** Someone decides what proof means for this team. Not "TBD", and not the agent whose work is being checked.
2. **Every class of work names its proof and its checker.** Code changes, research claims, outreach drafts and releases each get their own line: what you must be able to open, and who opens it.
3. **The agent's own account is never proof.** A summary, a plan or "I ran the tests" points at where to look. The proof is the output, the diff, the record read back, or the link with the line relied on.
4. **Work that touches people, clients, money, records or a release is checked by a person.** A second agent is fine for a refactor. It is not enough for an email to a customer or a page going live.
5. **Nobody says done on their own work.** A claim marked done needs evidence and a checker who is not the producer. Without both, the honest words are "reported, not verified".

## The five checks

| Id | Check |
|---|---|
| M1 | the standard names a human owner |
| M2 | every class says what counts as proof and who checks it |
| M3 | no class accepts the agent's own summary or self-report as proof |
| M4 | classes touching people, clients, money, records or releases are checked by a person |
| M5 | every claim marked done has evidence and a checker other than its producer |

Exit 0 clean, 1 findings, 2 missing or unreadable file. The script only reads.

## Worked example

A made-up print shop with four classes of agent work and three claims. Before (evidence-lite.json):

```json
{
 "owner": "TBD",
 "classes": [
  {
   "class_id": "code-change",
   "examples": "bug fixes and new tests",
   "proof": [
    "agent summary",
    "test run output"
   ],
   "checker": "fresh session",
   "checker_is_person": false
  },
  {
   "class_id": "research-claim",
   "examples": "facts from public pages",
   "proof": [
    "link plus the quoted line"
   ],
   "checker": "second agent",
   "checker_is_person": false
  },
  {
   "class_id": "outreach-draft",
   "examples": "emails to customers",
   "proof": [
    "the draft"
   ],
   "checker": "",
   "checker_is_person": false
  },
  {
   "class_id": "release",
   "examples": "putting the booking page live",
   "proof": [
    "build output",
    "fresh page load",
    "Maya's signed read"
   ],
   "checker": "Maya Field",
   "checker_is_person": true
  }
 ],
 "claims": [
  {
   "claim_id": "C1",
   "class_id": "code-change",
   "claim": "Fixed rounding in totals.py; 14 of 14 tests pass",
   "said": "done",
   "produced_by": "builder agent",
   "checked_by": "builder agent",
   "evidence": [
    "evidence/c1-tests.txt"
   ]
  },
  {
   "claim_id": "C2",
   "class_id": "research-claim",
   "claim": "The county reviews sign permits within 10 business days",
   "said": "done",
   "produced_by": "research agent",
   "checked_by": "second agent",
   "evidence": [
    "https://example.gov/permits/signs"
   ]
  },
  {
   "claim_id": "C3",
   "class_id": "release",
   "claim": "Version 2.4 of the booking page is live",
   "said": "reported, not verified",
   "produced_by": "release agent",
   "checked_by": "",
   "evidence": []
  }
 ]
}
```

Output:

```
M1 the standard has no human owner
M2 class outreach-draft does not say what counts as proof and who checks it
M3 class code-change accepts the agent's own account as proof: agent summary
M4 class outreach-draft touches people, clients, money, releases or records but is not checked by a person
M5 claim C1 says done without evidence and a checker other than its producer
5 checks, 5 findings
```

The fixes: Maya is named as owner. The code-change class drops "agent summary" and asks for the diff and the test output instead. Outreach drafts are read and signed by Theo, a person. Claim C1 gets checked by a fresh-session reviewer instead of the agent that wrote the code. C3 already said "reported, not verified", which is the right words for a release nobody has checked yet, so it needs no change. After:

```
5 checks, 0 findings
```

## One prompt to try

Paste into your agent: "List every time in this session, or the last one you can see, where you said something was done, fixed, passing, sent or live. For each, write what proof exists that I can open myself right now: a file, an output, a link with the line, or nothing. Mark the ones where the only proof is your own summary. Change nothing."

## What the full pack adds

The Evidence Standard (pp-evidence-standard, $24) turns these rules into a standard your agents check before they report. Plain files: the standard, the classes of work with required proof kinds, checker roles and gates, a proof-kind list with narration refused, a refusal list per class, and a claims ledger. evidence.py seals evidence files with a sha256 and tells the agent what it may say for each claim: done, partly done with the gap named, waiting on the owner, or reported, not verified. The owner ratifies the standard, says yes to claims in owner-gated classes and approves the period report at their own terminal, tied to a sha256 and refused inside an agent or on piped input. Fifteen read-only checks, 90 tests, a skill file for Claude Code, Codex and Hermes, an AGENTS.md snippet and a Grok Bot rules block. If you want it set up with you on a real team, that is the Install sprint on paperchasewebb.ai.

## The script

~~~python
#!/usr/bin/env python3
"""evidence_mini.py: five read-only checks on a one-file evidence standard (evidence-lite.json).
Usage: python3 evidence_mini.py evidence-lite.json
Exit 0 clean, 1 findings, 2 missing or unreadable file. Standard library only. Writes nothing."""
import json, re, sys

PLACEHOLDERS = {"", "tbd", "todo", "owner", "someone", "agent", "ai", "team", "default", "self", "producer"}
NARRATION = re.compile(r"summary|narrat|self.?report|agent says|says it|its own", re.I)
HIGH = ("person", "people", "client", "customer", "outreach", "email", "money", "payment", "invoice", "price", "release", "deploy", "publish", "record", "data", "delete", "contract")


def main(a):
    if len(a) < 2:
        print(__doc__); return 2
    try:
        m = json.load(open(a[1]))
    except Exception as e:
        print(f"cannot read {a[1]}: {e}", file=sys.stderr); return 2
    F = []
    if str(m.get("owner", "")).strip().lower() in PLACEHOLDERS:
        F.append(("M1", "the standard has no human owner"))
    classes = {c.get("class_id"): c for c in m.get("classes", [])}
    for cid, c in classes.items():
        if not c.get("proof") or str(c.get("checker", "")).strip().lower() in PLACEHOLDERS:
            F.append(("M2", f"class {cid} does not say what counts as proof and who checks it"))
        bad = [p for p in c.get("proof", []) if NARRATION.search(str(p))]
        if bad:
            F.append(("M3", f"class {cid} accepts the agent's own account as proof: {', '.join(bad)}"))
        text = (str(cid) + " " + str(c.get("examples", ""))).lower()
        if any(w in text for w in HIGH) and c.get("checker_is_person") is not True:
            F.append(("M4", f"class {cid} touches people, clients, money, releases or records but is not checked by a person"))
    for k in m.get("claims", []):
        if str(k.get("said", "")).lower() == "done":
            who = str(k.get("checked_by", "")).strip()
            if not k.get("evidence") or who.lower() in PLACEHOLDERS or who == k.get("produced_by"):
                F.append(("M5", f"claim {k.get('claim_id', '?')} says done without evidence and a checker other than its producer"))
    for c, msg in sorted(F):
        print(c, msg)
    print(f"5 checks, {len(F)} findings")
    return 1 if F else 0


if __name__ == "__main__":
    sys.exit(main(sys.argv))
~~~

Made-up example; no real business or results are described.

PaperChaseWebb 💸
