Zones

Name the paths that would hurt. Everything else stays open.

Most of a repository is safe for an agent to change. A few parts are not — the schema, the contracts, whatever your incident history says. A zone is how you say which is which, in a file you review like code — and in governance mode, the only paths CommitCycle walls off. The default, memory mode, has no zones and enforces one thing: an edit needs an active task.

dash.commitcycle.com/boardCC

✓ gate: four fields answered, no high-risk zone to approve

REQ-7 · schema · write · CC-42

Reason: the board query needs an index

Instead: hand the DDL to the owner

1 hour4 hoursUntil the task closes

✓ Approved — schema is open to CC-42 for 4 hours

In the console a task crosses the gate into In Progress, and a zone’s owner answers an access request — for one hour, four, or until the task closes.

Claude Code — ~/acme-webplugin · commitcycle

> add an index on tasks.updated_at — the board query is slow

● Write(apps/api/migrations/0042_tasks_updated_at.sql)

⎿ PreToolUse:Write — ✗ Denied by CC

zone schema · risk high

owner ana@acme.dev

task CC-42 declared no protected zones

reason path is in a zone the task never asked for

● The schema zone is closed to CC-42. Asking its owner rather than looking for another way in.

● Bash(cycle request-access --zone schema --mode write --reason "the board query needs an index" --alternative "hand the DDL to the owner")

⎿ REQ-7 is with the zone owner

approve or deny: https://dash.commitcycle.com/requests

The hook refuses the write before it lands, names the zone and its owner, and the agent asks for access instead of looking for another way in. Claude Code is where this block has been seen working.

Four things, and one of them is a person

Path patterns say where. A risk level says how much care. A default policy says who may read it when nothing has been granted — writing always takes a grant. And an owner says who to ask — which is the attribute that turns a wall into a door.

  • Path patterns — the same glob shapes GitHub rulesets use, so nothing new to learn
  • Risk level — high, medium, low. High means the owner is asked before the work starts
  • Default policy — deny closes reading too; read-only and open leave it readable. Either way, a write needs a grant
  • Owner — a real person, in a file, not in tribal memory
.zones/zones.yml · committed
# CommitCycle's own map, trimmed. Four zones in the real file, not thirty,
# all readable since 2026-10-10 — writing any of them still takes a grant.
zones:
  - id: schema
    risk: high
    owner: paulo@commitcycle.com
    paths:
      - "apps/api/migrations/**"
      - "apps/api/src/db/schema/**"
    default_policy: open

  - id: enforcement
    risk: high
    owner: paulo@commitcycle.com
    paths:
      - "packages/hook/**"
      - "packages/action/**"
    default_policy: open

  - id: brand
    risk: medium
    default_policy: open

# Everything not listed here is unprotected, on purpose.

Access arrives as a grant, and leaves on its own

Moving a task to In Progress is what mints it. Besides that gate, only an owner answering an access request opens a zone, and cycle renew only re-issues the same scope — there is no open-zone command, and nobody has to remember to close anything.

Bound to one task

It opens the zones that task declared, and nothing adjacent. The tempting neighbouring change is the one non-goals exist to name.

Bound to one branch

The guarantee is per checkout: two agents in one directory share a branch, and so a task and a grant. This is the failure this product first hit itself (D-43, still proposed); the answer so far is one worktree per agent.

Expires within eight hours

The gate issues it for eight hours at most. An owner who approved a high-risk zone for one hour or four brings the whole grant's expiry forward to match (CC-1327).

Blocked14:02 · task CC-42

Write apps/api/migrations/0007_add_org_index.sql

Zone
schema · risk high
Task declared
api, web
Owner
paulo@commitcycle.com

The agent is told why in the same response that denies it, so it asks for access instead of trying a different way in. A block that only says no gets routed around.

Access requestwaiting · 2 min

CC-42 wants schema

Reason
The org index needs a migration
Scope
write · apps/api/migrations/**
Expires
4 hours after approval
Approve for 4hDeny

Just-in-time: the grant expires on its own. Nobody has to remember to close it, which is the step every access process actually fails at.

The blocking is commodity. The model underneath is not.

Worth saying before you find it out yourself: denying a tool call in flight ships in Claude Code, Codex, Cursor, Augment, Factory and Devin CLI, and it is free in whichever of those you are already using. If that were the product, you would not need this page.

What none of them has is the model the denial consults. Their rules are per-machine and permanent. There is no owner to ask, no approval moment, no expiry, and no notion of a task that permission could be scoped to.

Everyone's rules

A path list. Set once, true forever, the same for every piece of work, with nobody attached to it.

A zone plus a grant

Path × owner × risk × this task × this branch × eight hours at most, and a record of who opened it and why.

Against CODEOWNERS and branch rulesets the difference is when: same vocabulary, asked after the change exists, enforced on the host. A zone is checked at the write, on your laptop, offline — and the CI check that recomputes the diff afterwards runs only in CommitCycle's own repository.

What was measured, and what it does not prove

Every figure here carries its limit in the same breath.

  • 25/25 Bash escape vectors contained — shell writes run against a git snapshot and changes to protected paths the task does not hold are reverted byte-for-byte. Containment closes when the call returns, so what a command leaves running — a background command (the hook asks first), a foreground one that hits its timeout, a cmd & inside one — escapes it until the close checks the diff against the zones (CC-1379). Measured 2026-08-03 (CC-3). Gitignored paths are not seen, so a .env inside a zone gets no containment. The corpus was chosen by the author. The live false-positive eval has not run.
  • 0/20 false positives on that same guard. Same self-chosen corpus. Twenty commands is not twenty real tasks.
  • 8/20 false positives on the conservative allowlist — the approach we refused, and the reason containment won instead. Same self-chosen corpus. What was measured is the 40% rate; that it would cost the install is our reading of it, not a measurement.
  • 68–83 ms at p95 for the decision, against a 100 ms budget. Measured 2026-08-03 on the first hook build, on an idle machine. p99 touched 102 ms in one case. CC-1377 re-timed it in October 2026 on two cloud VMs: on the slower one wrapper and core reached 211–228 ms at p95 (load 0.74–1.04), and the hook's cost over a shell-launched node process was 84–111 ms there against 44–48 ms on the faster one, for the same code. Two hosts, not a survey.

Where zones stop

  • They confine each agent; they do not referee between them. Two agents that disagree still disagree — each checkout is simply held to its own task and branch.
  • Unprotected is the default, and should be. An over-eager gate costs more than a missing one, so anything you do not declare is simply open.
  • Topics never gate. Zones enforce paths; the disciplines a task declares only advise. The two never merge.
  • The record outlives us. The map is a file in your repository and, by default, so are the task records and the audit record a close from the CLI writes; the board keeps a copy of each audit record, and the only copy of one a close from the console or a merge wrote. Grants are short-lived local state that expire with the work. Uninstall CommitCycle and you keep the files, and cycle export --audits prints the board's history, records included, as Markdown.

Questions this page answers

How do I stop an AI coding agent from editing certain files?

Declare those paths as a zone with an owner and a risk level. The agent gets access only to the zones its task declared, on the branch that task is bound to, and the access expires on its own. Everything you did not declare stays unprotected, which is the right default for most of a repo.

How is this different from CODEOWNERS?

CODEOWNERS is the same vocabulary applied after the fact, on the host: it asks for a review once the change already exists. A zone is checked when the write is attempted, on your machine, and it carries an expiry and a task scope that CODEOWNERS has no concept of.