Skip to content

The .zones directory

A repository that has just been set up has this in .zones/ and beside it:

.zones/
zones.yml the zone map — what is protected, why, and who owns it
board.json which board this repository answers to
AGENTS.md the block your agent reads (see below)

cycle init also leaves a few small files that other tools read: a CLAUDE.md that imports AGENTS.md, .gitignore entries for .zones/state/ and CLAUDE.local.md, a .gitattributes line for the AGENTS.md merge driver, a hook file for each harness you chose where a hook wrapper is on disk, and a .mcp.json where a CommitCycle binary is installed in the project.

That is complete. Everything else in this page appears on its own, the first time you do the thing that produces it. If you have compared your .zones/ against a repository that has been running for months and concluded that yours is missing something, it is not — you have just not closed a task yet.

The directories are consequences, not prerequisites. Creating them empty at setup would be the documentation project this product exists to argue against, and an empty audit/ says nothing that its absence does not say better.

Path Created by The first time you…
zones.yml cycle init, or cc_setup over the connector set the repository up
board.json cycle pair or cycle connect, or cc_setup over the connector connect the repository to a board
AGENTS.md cycle init, or cc_setup over the connector set the repository up
tasks/ cycle start, or cc_intake / cc_scope over the connector file or start a task
state/grants/ cycle sync, or cc_start over the connector hold a grant
audit/ cycle verify --close, or cc_close over the connector close a task
playbooks/ cycle seed / cycle feed, or cc_close’s learned answer keep what a close taught
decisions/ † cycle challenge accept or reject a proposal
state/events/ ‡ the hook, and only the hook have a write observed

† needs the CLI installed — the challenge flow has no connector form yet. ‡ needs the hook, and no server can substitute for it. Everything else in this table is a decision — the board makes it, and either side can write it down. An event is an observation, and an observation without a witness is an invention. Decisions travel as files; observations need a witness.

Written when the task is filed or started from your side, so the record sits beside the code. It holds what the task declared: its goal, its non-goals, the zones it asked for, and the state changes it went through. A task filed elsewhere — in the console, say — has no file until something writes it: cycle sync writes one for any task past Triage, cycle pull --write-missing does it on demand, and cycle doctor lists the ones missing. A Triage task filed in the console has none, which is what cycle show is for.

cycle start writes it when it files the task. Over the connector, cc_intake hands the same file back the moment the task is filed — still in Triage — and cc_scope, cc_start, cc_submit and cc_close each return it regenerated, so the record on disk follows the board. The connector’s version says there is no branch and no grant until the gate issues them, because on that path there cannot be.

Committed, deliberately. The record travels with the branch it describes.

Grants and event logs. cycle init adds .zones/state/ to your .gitignore, and it stays out for two reasons: a grant is scoped to one machine and one branch, and the event log is a record of what happened here rather than of what the code is.

The two halves are different animals. A grant is a decision — the gate issues it, and it reaches this directory through cycle sync, or as a file cc_start hands back over the connector. An event is an observation: the hook writes events/, only the hook ever can, and its shape is not a contract.

The CLI keeps a few more things here as it needs them, all of them local: the policies cycle sync brings down (failure-policy, pr-policy), the agent’s last replies (replies/), the context hooks’ caches (context/), what cycle sync last sent the board — a marker per task’s event log and the records’ hashes — so it sends only what is new (sync/, CC-1383), the guard’s snapshots (guard/), and setup-writes.json, which lets the first task after cycle init claim the files the setup wrote (CC-750). No tool call can write anywhere under state/ — it is the hook’s own memory, and no grant opens it.

The record the closing gate produces — through cycle verify --close, or handed back as a file by cc_close over the connector: what the task declared against what the diff touched, plus whatever evidence the quality gate asked for. Committed, because it is the answer to “who changed this, under what permission, and who agreed” long after everyone has forgotten. Those two are the only closes that hand you a file. A close from the console’s Review and close, or the one the board makes when GitHub reports the pull request merged, keeps its record on the board alone; it reaches the repository only if you export it (cycle export --audits, below).

A connector close is on the honour system, and the record shows it rather than saying it: base and head read reported instead of commit SHAs, and mutations_observed is zero, because no hook was running and nothing independent saw the writes. The sentence spelling that out is in cc_close’s reply; the record carries the evidence of it.

An empty or partial audit/ on a repository with closed tasks is therefore normal where closes happen on the board: in the console, on merge, or every close once the repository keeps its records there (below). cycle export --audits hands every record the board kept over as Markdown. The closing gate describes the closes a person runs.

tasks/ and audit/ are a copy of what the board already holds, and a copy nobody maintains drifts: every cycle sync catches stale records up and writes missing ones, and that churn lands in whatever task ran it. If you would rather not commit it, say so once in board.json:

{ "api_url": "…", "tenant": "…", "repo": "…", "records": "board" }

Then cycle start writes no file under tasks/, cycle sync and cycle pull stop writing records, and cycle verify --close writes nothing under audit/: the board keeps every close’s audit record itself. Nothing is deleted, and records already committed stay readable. cycle export prints the board — tasks, goals, history, the files each task touched, and your playbooks — as one Markdown file whenever you want a copy in git, and cycle export <task> prints one task with its audit records in full. The board’s Export button downloads the same file.

The default is "repo", which is everything above this section. A close never loses its receipt to the setting: if the board does not confirm it kept the audit record, the CLI writes the file as before.

One file per declared topic — a taxonomy that grows by the birth rule, plus always-on playbooks pointed at from AGENTS.md that load whatever a task declares — growing by append and never by rewrite. cycle seed drafts them from your codebase with citations; cycle feed adds a bullet after a close — and over the connector, cc_close asks what the work taught and hands back the same append for your agent to write. A topic with nothing to cite is not a topic and gets no file, and a lesson with a citation is cited content, which is how a missing playbook may be born from one.

You will end up with everything except two things: zones.yml, board.json, AGENTS.md, a growing tasks/, grants under state/grants/ while work runs, an audit/ file for each close made through cc_close (a close in the console or on merge keeps its record on the board), and playbooks/ whenever a close taught something. That is not a partial installation — it is the whole record, kept on the honour system.

The two absences are the honest ones. decisions/ waits for the challenge flow to reach the connector. And state/events/ never appears, because it is the hook watching writes happen — the one file no board can hand you. A close on this path reports its file list rather than proving it, and installing the hook is what turns reported into observed.

It is the one file CommitCycle does not own.

.zones/ is ours — our tasks, our grants, our record. Nothing else reads it and nothing else has to. AGENTS.md is the opposite: it belongs to your agent. Codex and Cursor look for it at the repository root, and Claude Code reads it there through the CLAUDE.md import that cycle init writes. Inside .zones/ nobody would ever open it, and Layer 1 would quietly stop existing.

That is also why it is the only file with <!-- cc:begin --> and <!-- cc:end --> markers around our part. Inside .zones/ we can rewrite whatever we like. In AGENTS.md we are a guest: everything between the markers is regenerated on every sync, and every byte outside them is yours and is never touched. Write your own house rules above or below the block and they will survive.

What you will see in CommitCycle’s own repository, and should not copy

Section titled “What you will see in CommitCycle’s own repository, and should not copy”

CC is built under CC, so its .zones/ is the oldest one that exists — and it carries three directories that are not part of the product:

  • evidence/ — files attached by hand at closes whose quality gate asked for proof. Nothing creates it; the gate accepts evidence as a URL or a path, and these happen to be paths.
  • exceptions/ — written arguments for cases where a rule did not fit, kept so the reasoning survives the decision. A convention of this repository, not a feature.
  • salvage/ — residue of one recovery operation, kept for history.

They are all defensible where they are and none of them is something your repository is missing. Anyone imitating this layout would be copying our history and calling it architecture.

  • Zones — what belongs in a zone map
  • AGENTS.md — what the block says, and what it deliberately does not claim
  • The audit record — what a close writes