---
title: "The .zones directory: what appears, and when | CommitCycle"
description: "What lives under .zones/, what creates each part, and why a fresh setup is three files rather than seven folders."
url: https://commitcycle.com/docs/reference/zones-directory/
language: en
---

# The .zones directory

> What lives under .zones/, what creates each part, and why a fresh setup is three files rather than seven folders.

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

```plaintext
.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.

## What appears, and when

| 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.

### `tasks/` — one file per task

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.

### `state/` — local, and never committed

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.

### `audit/` — one file per close

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](https://commitcycle.com/docs/operations/closing-gate/) describes the closes a person runs.

### Keeping the records on the board instead

`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`:

```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.

### `playbooks/` — what the work taught

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.

## If you are on the connector only

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.

## Why AGENTS.md is not in here

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

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.

## Related

- [Zones](https://commitcycle.com/docs/concepts/zones/) — what belongs in a zone map
- [AGENTS.md](https://commitcycle.com/docs/concepts/agents-md/) — what the block says, and what it deliberately does not claim
- [The audit record](https://commitcycle.com/docs/operations/audit-record/) — what a close writes
