# Where policies run

Where a compiled policy runs: each hook point as chock's docs describe it, the engine emitter that writes it, and the agents that have it.

A hook point is where a compiled control runs. Chock's docs and code call it a \`surface\`. The first table is chock's own, from its docs. The site reads the second from the engine's compiler at the pinned commit, so it changes when the code does.

> Ask the user before running any command on this page: each one changes how their agent or repository behaves.

## The hook points

Chock's own table of the hook points, from its docs at the pinned commit:

From the engine docs: [docs/enforcement-surfaces.md](https://github.com/open-coder-ai/chock/blob/5a4b6287a46e07f9f856d9bbe10622bd1ff2e021/docs/enforcement-surfaces.md)

| Surface | Determinism | Bypassable? | What it is |
| :--- | :--- | :--- | :--- |
| `git-hook` | Hard, at commit/push | Yes (`--no-verify`) | Pre-commit / pre-merge-commit / pre-push guard |
| `ci-gate` | Hard, un-bypassable | No, **once marked a required status check** -- see [the branch-protection gap](https://github.com/open-coder-ai/chock/blob/5a4b6287a46e07f9f856d9bbe10622bd1ff2e021/docs/adopting.md#the-branch-protection-gap): the workflow file itself is tracked content a PR can delete, so `ci-gate`'s un-bypassable claim rests on a server-side branch-protection setting nothing in this repository can edit | The backstop for a skipped git hook |
| `ambient-rule` | Advisory | Yes | Compiled `AGENTS.md` block the agent is asked to follow |
| `pre-tool-use` | Hard, pre-execution | No | Blocks a command **before** the agent runs it, in each client's own deny dialect — every matrix-blocking vendor with a repo-level config (see the Cursor caveat) |
| `stop` | Hard, **post-execution** | No, but it fires after the tool calls it judges | Reads what the turn left in the worktree and refuses to end the turn. Credited with **no coverage word** -- see the backstop note below |
| `agent-hooks` | Hard, pre-execution | No | The same exit-2 deny for Copilot CLI + VS Code agent mode, from `.github/hooks/chock.json` (witnessed blocking on both, 2026-08-23). A content gate also gets a `PreToolUse` entry over `Edit\|Write` there (a top-level `permissionDecision` deny stopped an `Edit` in VS Code agent mode, 2026-09-28), and its turn-end gate is a `Stop` entry in the same file |
| `managed-setting` | Hard, org-level | No | Admin-deployed allow/ask/deny rules |
| `gateway` | Hard, un-circumventable | No | Budget/egress backstop — *modeled now, emitted later* |
| `mcp-gateway` | Hard, **MCP-routed tools only** | Yes (P3c) | A stdio proxy the client launches instead of the real MCP server; refuses matching `tools/call` payloads. Emitted today; **credits no agent** until the per-client config witness ships |

## Where each hook point comes from in the engine

The site reads this table from the engine at the pinned commit. For each hook point it shows the compiler's emitter (`EMITTERS` in `chock.compile.compiler`), the agents the engine gives it, by the engine's agent names (`SURFACE_AGENTS`), and whether it is left out of every coverage word (`UNCREDITED_SURFACES`). The build fails when the hook points in chock's table and in its code differ.

| Hook point | Emitter | Agents | Held out of coverage |
| --- | --- | --- | --- |
| `ambient-rule` | [src/chock/compile/emitters/ambient.py](https://github.com/open-coder-ai/chock/blob/5a4b6287a46e07f9f856d9bbe10622bd1ff2e021/src/chock/compile/emitters/ambient.py) | `aider`, `antigravity`, `claude`, `cline`, `codex`, `copilot`, `cursor`, `devin`, `factory-droid`, `gemini`, `goose`, `grok`, `junie`, `kimi-code`, `mistral-vibe`, `openhands`, `replit`, `tabnine`, `vscode`, `windsurf` | no |
| `git-hook` | [src/chock/compile/emitters/git_hook.py](https://github.com/open-coder-ai/chock/blob/5a4b6287a46e07f9f856d9bbe10622bd1ff2e021/src/chock/compile/emitters/git_hook.py) | `aider`, `antigravity`, `claude`, `cline`, `codex`, `copilot`, `cursor`, `devin`, `factory-droid`, `gemini`, `goose`, `grok`, `junie`, `kimi-code`, `mistral-vibe`, `openhands`, `replit`, `tabnine`, `vscode`, `windsurf` | no |
| `ci-gate` | [src/chock/compile/emitters/ci.py](https://github.com/open-coder-ai/chock/blob/5a4b6287a46e07f9f856d9bbe10622bd1ff2e021/src/chock/compile/emitters/ci.py) | `aider`, `antigravity`, `claude`, `cline`, `codex`, `copilot`, `cursor`, `devin`, `factory-droid`, `gemini`, `goose`, `grok`, `junie`, `kimi-code`, `mistral-vibe`, `openhands`, `replit`, `tabnine`, `vscode`, `windsurf` | no |
| `pre-tool-use` | [src/chock/compile/emitters/in_agent.py](https://github.com/open-coder-ai/chock/blob/5a4b6287a46e07f9f856d9bbe10622bd1ff2e021/src/chock/compile/emitters/in_agent.py) | `antigravity`, `claude`, `codex`, `cursor`, `devin`, `factory-droid`, `gemini`, `goose`, `grok`, `mistral-vibe`, `openhands`, `tabnine`, `windsurf` | no |
| `stop` | [src/chock/compile/emitters/in_agent.py](https://github.com/open-coder-ai/chock/blob/5a4b6287a46e07f9f856d9bbe10622bd1ff2e021/src/chock/compile/emitters/in_agent.py) | `antigravity`, `claude`, `codex`, `copilot`, `cursor`, `devin`, `factory-droid`, `gemini`, `goose`, `mistral-vibe`, `openhands`, `tabnine`, `vscode` | yes |
| `managed-setting` | [src/chock/compile/emitters/claude_managed.py](https://github.com/open-coder-ai/chock/blob/5a4b6287a46e07f9f856d9bbe10622bd1ff2e021/src/chock/compile/emitters/claude_managed.py) | `claude` | no |
| `gateway` | None: not emitted | none | no |
| `mcp-gateway` | [src/chock/compile/emitters/mcp_gateway.py](https://github.com/open-coder-ai/chock/blob/5a4b6287a46e07f9f856d9bbe10622bd1ff2e021/src/chock/compile/emitters/mcp_gateway.py) | none | no |
| `agent-hooks` | [src/chock/compile/emitters/in_agent.py](https://github.com/open-coder-ai/chock/blob/5a4b6287a46e07f9f856d9bbe10622bd1ff2e021/src/chock/compile/emitters/in_agent.py) | `copilot`, `vscode` | no |

From the engine docs: [docs/enforcement-surfaces.md](https://github.com/open-coder-ai/chock/blob/5a4b6287a46e07f9f856d9bbe10622bd1ff2e021/docs/enforcement-surfaces.md)

## How a policy picks its surfaces

A hook that must stop a command targets `git-hook` + `ci-gate` (the universal floor) and, where available,
`pre-tool-use` + `managed-setting`. A hook whose `on:` includes `tool_use` is compiled to the `pre-tool-use`
surface on agents that support it (thirteen of them, Claude Code and Cursor among them; Copilot CLI and VS Code
get the same guard via `agent-hooks`, and a content gate's write check as a `PreToolUse` entry over `Edit|Write`
there). A hook whose `on:` includes `tool_call` (a `content_regex` or `script` gate with `params.tools`, globs
over tool names such as `mcp__Firecrawl__*` or `WebFetch`) gates PreToolUse for any tool by name, on the nine
agents whose vendor facts record a tool vocabulary (Claude Code, Codex CLI, Cursor, Gemini CLI, Factory Droid,
goose, Mistral Vibe, OpenHands, and Copilot via `agent-hooks`); the others get no `tool_call` entry and are
credited nothing for it. A gate's `outside_repo` globs let its PreToolUse write check see declared files outside
the repository (the turn-end check cannot). A `script` gate on `tool_call` also keeps a local per-session
tool-call log under `.chock/state/` that its script can read; see
[`spec/session-log.md`](https://github.com/open-coder-ai/chock/blob/5a4b6287a46e07f9f856d9bbe10622bd1ff2e021/spec/session-log.md). A best-practice rule with no deterministic check compiles only
to `ambient-rule`. The compiler always pairs a control with the **strongest available backstop** — e.g. a git
hook plus a CI gate, because a git hook alone can be skipped with `--no-verify`.

## Gate runner semantics

At commit time the vendored runner scans the staged files git reports as added, copied,
modified, renamed, or type-changed. It disables `core.quotePath` for its diff calls, so
non-ASCII paths arrive unescaped and are scanned like any other file. `dependency_allowlist`
gates match their watched manifest basenames (e.g. `package.json`) anywhere in the tree, not
only at the repo root. In CI range mode, a base ref that cannot be resolved fails **closed** —
the gate exits 2 rather than passing an unscanned range.
A compiled gate never judges chock's generated tree (`.chock/`) or its own policy's folder
(`.agents/policies/<id>/`): that folder's evals and references show the very content the gate
refuses, so judging them refused the policy's own adoption commit. Every other path, another
policy's folder included, is judged as before.


---

Built from engine [5a4b6287a46e](https://github.com/open-coder-ai/chock/commit/5a4b6287a46e07f9f856d9bbe10622bd1ff2e021) and catalog [1e60d211c1bb](https://github.com/open-coder-ai/chock-catalog/commit/1e60d211c1bb767152c6ab0167932da06ab3a10f).
