How PROOF works


The one design law

Files are the source of truth. The board is a projection. The AI maintains files, never the board. Nothing in PROOF writes to a plan file. There is no endpoint, no button, no "mark done" action that touches disk from the board side, a status change happens by editing a plan's own frontmatter, the same file the AI (or a human) was already editing. Delete PROOF entirely and the plans are still just readable markdown; the tool is a viewport, never a store.

The honest pitch, stated plainly: roughly 70% human observability, 30% AI efficiency. The AI never looks at the board. Its gain is the file structure itself: a cheaper session start, one ground truth parallel sessions can share, plans forced into small pieces before they turn into a mess. The board is for the human watching.

The schema

A plan is a .md file with a deliberately small frontmatter block, six fields, on purpose:

---
id: 001-interaction-timeline    # = the filename stem, the plan's stable address
status: doing                   # todo | doing | done | blocked
track: A                        # optional; a free grouping label
depends: []                     # ids this plan waits on
touches: [grain/ai/contract.ts] # optional; code areas
owner: ai                       # ai | human
---

Body = the plan's prose, plus a task checklist (- [ ] / - [x]). A big task can become its own child plan file rather than growing one file without bound. The schema stays this small on purpose: a heavier one turns the AI into a bookkeeper instead of a builder.

A malformed plan is never silently dropped. An invalid status or owner falls back to a default and reports the error, so the board still shows the card, flagged, rather than making a plan disappear because of a typo.

The board is four columns

STATUSES = ["todo", "doing", "done", "blocked"] is the closed vocabulary, and the board lays out one column per status, left to right, in that order: To do, Doing, Done, Blocked. Within a column, cards sort by id (the 001-…, 002-… prefix carries the intended order).

A card shows its status badge, an owner mark (AI or Human), the track (if set), a done/total tasks progress count, the last-modified date, and a touches chip list when the plan names code areas. A plan with parse errors gets a visible warning flag on its own card: the board is also the lint's window, an issue is surfaced, not hidden.

Where the dates come from

lastModified reads a plan file's last git commit time (git log -1 --format=%cI), degrading to the filesystem's mtime if the folder isn't a git repo, and to null if neither is available. Git is the timeline: a plan's history is the record of the AI's decisions.

Clicking a card

/plan/:id renders that plan's full body through MILL (renderGrainDocument, the same engine MILL's docs describe), with a body-only layout, since the plan already leads with its own # Title and PROOF renders the frontmatter facts separately above it. The grade guardrail still runs, plan prose is human-authored content, so it stamps clean even though an AI likely wrote both the plan and the code it describes.

Live updates (opt-in)

Passing liveScriptSrc to createProofRoutes appends a client script tag that connects over SSE. A file watcher (watchPlans) debounces a change to plans/ and broadcasts a replace render op targeting the proof-board surface, so every open tab rebuilds without a manual refresh. It's read-only in both directions: the watcher only ever pushes a rebuilt view, it never accepts a message back. Omit liveScriptSrc and the board still works, it's just a normal server-rendered page that needs a reload to see a change.

Refreshes are serialized. Watch startup errors, asynchronous watch errors and rebuild failures are reported through the optional onError callback, or the server error log by default. Shutdown is asynchronous: await watcher.stop(), or the standalone server handle's stop(), before deleting files or releasing resources it may still be reading. Stop closes the watch immediately, suppresses late broadcasts and drains any active rebuild.

The CLI

  • proof check [dir]: a deterministic lint. Schema validity, duplicate plan ids, dangling depends (a plan waiting on an id that doesn't exist), a done plan with unticked tasks left, a doing plan gone stale with no recorded activity for a while. Exits nonzero on a problem, so it's CI-able.
  • proof init [dir]: scaffolds plans/ (a starter plan + a README explaining the schema) plus optional hook scripts (a SessionStart hook that injects the plan index into context, a pre-commit nudge). Non-invasive: it only ever writes new files, and prints the one manual wiring step rather than editing a host's existing settings.json or CLAUDE.md itself.
  • proof serve [dir] [--port N]: the standalone board, for a project with no host app to mount createProofRoutes into.

Verification evidence

The verification command compares plans with Git evidence. An unavailable Git command or invalid comparison ref is a failed verification, not an empty successful diff. A valid comparison with no changes remains a valid empty diff.

What's not built yet

Per proof/PLAN.md: a mindmap view joining plan nodes with memory links and a code graph, a multi-project board (one proof serve over several plans folders), and any human interaction with the board through the client-side door, are all phase 2, not built. The board stays read-only in every direction today.

Non-goals

Not a task manager for humans, humans edit the same files the AI does. Not a store, no database, no board state outside the files and git. Not a second plan system, a repo that already has plan prose in some other form migrates it in, it doesn't run PROOF alongside something else.