Skip to content

The audit record

Every task that reaches Done leaves one audit record. It is written by the machine at close, not composed afterwards by whoever remembers the week, and it is the artifact you hand a skeptic. The board keeps a copy of every one; the CLI also writes it to .zones/audit/<task>.md for you to commit, unless the repository keeps its records on the board ("records": "board"), where cycle export <task> prints it (keeping the records on the board).

The unit is the task, and that is the whole distinction. Durable provenance for agent-written code already exists and is good — there are tools that record, per line, which model wrote it and from what prompt, surviving rebase and squash. That answers who typed this character. This record answers a governance question instead: for one unit of work, what did it declare it would touch, what did it actually touch, who approved the difference, and what was overridden and why. Same repository, different question — and a per-line log cannot answer this one no matter how complete it is.

The frontmatter is the machine-readable half. This is CC-39’s, as committed in .zones/audit/CC-39.md, with the escaped quotes in its title left out:

task: CC-39
title: "Approve and close is offered by the console but can never succeed"
owner: paulo@commitcycle.com
mode: standard
branch: task/CC-39-approve-and-close-is-offered-by-the-cons
quality_gate: regression suite
zones_declared: []
zones_touched: []
declaration_matches: true
exceptions: 2
blocks: 0
mutations_observed: 0
evidence:
- type: capture
location: .zones/evidence/CC-39/verification.md
by: agent
at: 2026-08-04
diff:
files: 13
base: HEAD~1
head: d8a07c8a
opened: 2026-08-04
closed: 2026-08-04
closed_by: agent

On a board with accounts, closed_by, and the name on an override or a guard change, come from the credential that made the close — a signed-in person, or whoever paired the machine — not from a name the caller typed (CC-1308, CC-1344). Only the deployment’s shared token still carries the name it was sent with, because there is no person behind it to name instead.

zones_declared against zones_touched is the line that matters: it is the difference between what the task said it would do and what the diff shows it did. declaration_matches is that comparison, computed.

A fourth line appears only when it has something to say. If the work hit a wall mid-task, asked for a zone and an owner approved it, that approval amends the declaration — and the record splits the two, so you can tell a task that scoped a risky area up front from one that was let into it by a person:

zones_declared: [web:write]
zones_amended: [schema:write]
zones_touched: [web, schema]
declaration_matches: true

declaration_matches is measured against both together, because both are what the closing gate was checked against. The approval itself — who answered, when, for how long, and what the agent offered to do instead — is in the Access asked for table further down.

The body carries what was asked, what it was explicitly not allowed to do, how anyone would know it worked, the logged exceptions, and the file-by-file diff.

An exception is not a failure. It is something the close noticed and refused to swallow silently — recorded with an owner and a date, so it is a question someone can answer later rather than a detail nobody was told:

2026-08-04 — at close, 12 changed file(s) are in no zone: .zones/tasks/CC-39.md, AGENTS.md, apps/dashboard/package.json and 9 more. Should any be protected? cycle protect <glob> --zone <id> if yes, cycle dismiss <glob> to stop being asked.

Most repos have a long tail of paths nobody has decided about. Asking once, at close, when the evidence is in front of you, is cheaper than asking at declaration time when it is hypothetical.

The most useful entry any of these records carries is the one about what could not be seen:

2026-08-04 — at close, Nothing observed this work: 12 changed file(s) and no mutation events at all. That is what a hand-edited task looks like — but it is also what a task run with the hook switched off looks like, and from here they are the same.

CC cannot tell those two apart from the record alone. A task edited by a human at their keyboard and a task run with Layer 2 uninstalled produce the same silence.

This is the same finding that makes Layer 3 necessary — a hook is fail-open unless something outside the laptop checks — and it is why closes on this repository had been refused twice over it rather than overridden, by 2026-08-06 when this page was first written (CC-99). A record that quietly rounded that silence up to “verified” would be worth less than no record.