---
title: "Three layers, and only one of them enforces anything"
description: "Context advises, the hook denies at the tool call, and CI recomputes the diff, only in our own repo. Each exists because the one above it can fail."
url: https://commitcycle.com/three-layers/
language: en
---

# Each layer is there because the one above it can fail

> Context advises, the hook denies at the tool call, and CI recomputes the diff, only in our own repo. Each exists because the one above it can fail.

Three layers Shipped · proven on Claude Code only

A file can be ignored. A hook can be absent. A pull request cannot be either, but it arrives too late to stop anything. None of the three is sufficient, which is the entire reason there are three.

**Editor:** cycle start writes the task into a gitignored CLAUDE.local.md beside the zone map in AGENTS.md, and cycle submit takes it out again.

**Claude Code:** 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.

**Pull request:** Layer 3 re-checks the diff against what the task declared. It runs in CommitCycle’s own CI today, advisory there; nothing installs it in yours.

## Layer 1 — context, which advises

[The AGENTS.md block](https://commitcycle.com/agents-md/) carries the zone map, the board, the hand-in note it expects and the deploy rule; the task itself — what is open and what is shut — is written at the gate to a gitignored `CLAUDE.local.md`, which Claude Code loads — whether other editors do has never been checked — and removed at hand-in. It is honest in its own text about being advice: _nothing is stopping you, so this one is on you_.

## Layer 2 — the hook, which denies

A pre-tool-call hook answers before the write happens. Ours only narrows: it can deny, and since CC-1142 it never answers allow, so the harness's own permission prompts still apply. What matters more than the mechanism:

- **The hot path never touches the network.** The decision is local, so an outage cannot open a zone — the backend was never in the deny path to begin with.
- **Bash is contained, not classified.** Guessing what a shell command will do is a losing game, so commands run against a git snapshot and changes to protected paths the task does not hold are reverted byte-for-byte; gitignored paths are not seen. Containment closes when the call returns, so what a command leaves running escapes it until the close checks the diff against the zones: a background command (the hook asks first), a foreground one that hits its timeout, a `cmd &` inside one. **25/25** escape vectors contained, **0/20** false positives — on 2026-08-03, against a corpus we chose ourselves; the live false-positive eval has not run.
- **68–83 ms at p95** against a 100 ms budget — measured 2026-08-03 on the first hook build, on an idle machine, and 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.

Worth saying plainly: **this mechanism is commodity**. Claude Code, Codex, Cursor, Augment, Factory and Devin CLI all expose a hook that can return deny, and in the one you are probably using it is free. What is not commodity is [the model it consults](https://commitcycle.com/zones/) — owners, expiry, and permission scoped to a declared task.

## The admission that makes the rest credible

We measured the harness against real sessions on 2026-08-03, and it is **fail-open in all four failure modes**: crash, timeout, malformed output, not installed. That is not a footnote — it is the reason the other two layers exist in the shape they do. A wrapper can close three of those modes by refusing on its own behalf when the hook cannot answer — but only where the repository's failure policy is set to closed. The default, since CC-1143, is open: calls continue and the session is told. The fourth — _never installed at all_ — is invisible from inside the machine, so only CI can see it, and only where the check runs.

## Layer 3 — CI, which recomputes

[The CI check](https://commitcycle.com/ci/) reads the zone map from the pull request's own checkout and recomputes which zones the diff actually touched, against what the task declared — the declaration, not the grant, so approvals and expiry go unchecked. It is the only layer that could see work produced where CommitCycle was never installed.

Its own limits, stated: it runs only in CommitCycle's own repository, and nothing installs it into yours. There, on a private personal free plan, branch protection answers **403**, so it runs advisory, says so, and cannot stop a merge. A board that does not answer turns it into a skip that still passes, and on a same-repository pull request the branch can rewrite the workflow that checks it. An enforcing mode and a board-side check run are built and not switched on.

## Where the arrangement stops

- **Layer 2 is proven on one harness.** The zone model is agent-agnostic; the enforcement point is not. `cycle init` writes hook files for Claude Code and Codex, and for Cursor and Windsurf on request, only where a hook wrapper is already on disk, so on your machine it writes none and the plugin is the hook — in Claude Code only, so Codex, Cursor and Windsurf get none. Only [Claude Code](https://commitcycle.com/claude-code/) has been seen to deny on a real install — the others, where their files are written, are unexercised and are answered in Claude Code's reply format. Layer 1's task half is known to reach Claude Code only, and Layer 3 cannot be installed in your repository.
- **An enterprise can block the hook outright.** Managed settings exist for exactly that, and a policy that locks hook sources locks ours out with everyone else's.
- **None of this arbitrates between agents.** Each checkout is confined to its own task and branch, so two agents in one directory share both; two that disagree still disagree.

How the layers divide the work is in [the docs](https://commitcycle.com/docs/concepts/three-layers/), and the map all three read is [the zone map](https://commitcycle.com/zones/).
