From 4a36f98de2986bd9b30056c0c77153c64d514979 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 23 Sep 2026 19:52:07 +0000 Subject: [PATCH 1/3] docs: bring the surface and vendor counts up to 0.11.0 The 0.11.0 release added the stop surface and Cursor's write vocabulary, and the figures regenerated, but the hand-written prose around them did not. The README's alt text still described eight surfaces and omitted stop, so the accessible text disagreed with the picture beside it; it now mirrors the figure's own generated description. The stop count read six vendors where stop_vendors returns seven. architecture.md and concepts.md both enumerated eight surfaces without stop, against nine in the Surface enum. enforcement-surfaces.md's callout had both halves wrong: stop wires seven vendors, and the write path wires three now that Cursor records Write. agent-plugins.md listed four plugin formats against five in HOOK_FORMATS, conflating the format count with the four distribution repos. cli-reference.md described a package as a guard and an adapter, which has been incomplete since the gate, its runner and a script gate's implementations began riding along, and it described Cursor as beforeShellExecution alone, with no mention of the write gate or the turn's end. The README also pointed Copilot and VS Code at .github/hooks/agentseam.json. chock writes chock.json there; agentseam.json is agentseam's recorded vendor path, not the file chock owns. Two source comments drifted the same way: surfaces.py still listed cursor among the agents that cannot refuse a finished turn, and both it and in_agent.py miscounted the vendors with no write vocabulary. The cursor line in plugin build's own output named only beforeShellExecution. Co-Authored-By: Claude Opus 5 Signed-off-by: Claude --- README.md | 8 ++++---- docs/agent-plugins.md | 8 ++++---- docs/architecture.md | 8 +++++--- docs/cli-reference.md | 15 ++++++++------- docs/concepts.md | 5 +++-- docs/enforcement-surfaces.md | 7 ++++--- src/chock/compile/emitters/in_agent.py | 2 +- src/chock/compile/surfaces.py | 6 +++--- src/chock/plugin/cli.py | 4 +++- 9 files changed, 35 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 6e200e8..c156d51 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ chock compile block-console-log # emit every surface + the coverage report - One chock policy fans into eight enforcement surfaces. All 15 supported agents get the advisory ambient rule and the two commit-time gates, git hook and CI gate; 9 also get a native pre-tool-use hook and 2 (vscode, copilot) get chock's own agent-hooks file, both enforced live in the agent. Three surfaces -- managed-setting, gateway, mcp-gateway -- are named for honesty even though no agent reaches them yet. + One chock policy compiles into 9 enforcement surfaces. All 15 supported agent names get the advisory ambient rule and the two commit-time gates, git hook and CI gate. 9 also get a native pre-tool-use hook, enforced live in the agent, and 2 (vscode, copilot — one underlying vendor) get chock's own agent-hooks file, also enforced in-agent. 7 get an end-of-turn hook that reads what the turn wrote: a backstop for what a pre-tool hook cannot see, carrying no coverage grade of its own. Three surfaces are named for honesty though no agent reaches them yet: managed-setting is compiled for Claude but not installed, gateway is modelled but not emitted, and mcp-gateway emits but is not yet credited to any agent. Every agent in this table gets the same floor: a git hook, a CI gate and an ambient `AGENTS.md` @@ -170,7 +170,7 @@ a given repo once `chock sync` has actually written the file, which is why the C `chock status` always beats this table for what's true *here*. Four of the nine surfaces are absent from this page entirely because they credit no agent today — `managed-setting` is compiled but not installed, `gateway` is modelled but not yet emitted, `mcp-gateway` credits -nothing until its per-client witness ships, and `stop` installs and refuses on six vendors but is +nothing until its per-client witness ships, and `stop` installs and refuses on seven vendors but is a deliberate backstop for what a pre-tool hook cannot see, so it is worth no grade of its own. Full nine-surface matrix and per-agent caveats: [Enforcement Surfaces](docs/enforcement-surfaces.md). @@ -179,8 +179,8 @@ Full nine-surface matrix and per-agent caveats: | :--- | :--- | :--- | | **Claude Code** | native pre-tool-use hook | `.claude/settings.json` | | **Cursor** | native pre-tool-use hook | `.cursor/hooks.json` | -| **Copilot** | native agent hook | `.github/hooks/agentseam.json` | -| **VS Code** | native agent hook | `.github/hooks/agentseam.json` | +| **Copilot** | native agent hook | `.github/hooks/chock.json` | +| **VS Code** | native agent hook | `.github/hooks/chock.json` | | **Codex** | native pre-tool-use hook | `.codex/hooks.json` | | **Gemini** | native pre-tool-use hook | `.gemini/settings.json` | diff --git a/docs/agent-plugins.md b/docs/agent-plugins.md index 66dd989..b88905f 100644 --- a/docs/agent-plugins.md +++ b/docs/agent-plugins.md @@ -188,10 +188,10 @@ mere presence of two files. ## The hook-carrying vendor formats The Agent Plugins 1.0 standard carries no hooks, so an `agent-plugins` package is advisory -by construction. Enforcement travels in four vendor plugin formats built from the same -policies (`chock plugin build --format claude|copilot|cursor|codex`), each published in its -own generated distribution repo and each **witnessed denying a destructive command on a -real install**: +by construction. Enforcement travels in five vendor plugin formats built from the same +policies (`chock plugin build --format claude|copilot|cursor|codex|devin`). Four of them are +published in a generated distribution repo of their own, and each of those is **witnessed +denying a destructive command on a real install**: | Vendor repo | Client(s) | Deny dialect | | :--- | :--- | :--- | diff --git a/docs/architecture.md b/docs/architecture.md index 6637ea6..02ccbc7 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -19,12 +19,14 @@ policy is the single source of truth for one rule — you never copy it into per ### 2. Compile `chock compile ` reads the policy and **emits the strongest control each target agent -supports**, across up to eight [enforcement surfaces](enforcement-surfaces.md): +supports**, across up to nine [enforcement surfaces](enforcement-surfaces.md): - `git-hook` and `ci-gate` — the universal hard floor (every agent) - `ambient-rule` — the compiled `AGENTS.md` block (advisory) -- `pre-tool-use` (Claude Code, Cursor) and `agent-hooks` (Copilot CLI, VS Code) — - agent-native hard controls; `managed-setting` — compiled, not yet installed +- `pre-tool-use` (nine agents, Claude Code and Cursor among them) and `agent-hooks` + (Copilot CLI, VS Code) — agent-native hard controls +- `stop` — the end-of-turn backstop that reads what the turn wrote (seven agents) +- `managed-setting` — compiled, not yet installed - `gateway` — modeled for budgets/egress (future) It also writes a **coverage report** (`.chock/coverage.json`) mapping every policy × agent onto diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 399d524..e75d2b3 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -202,13 +202,14 @@ Renders each policy as a plugin. The default `agent-plugins` format writes an `manifest.yaml` stays the source of truth, and a packaged policy is `advisory` wherever it is read: v1 defines no enforcement semantics, so packaging changes no value in `coverage.json`. -The five hook formats ship a byte-identical guard and adapter; only the envelope differs. -`claude` (`.claude-plugin/`) is read natively by Claude Code, Copilot CLI, VS Code and Grok -Build; `copilot` is the Agent Plugins 1.0 layout under `com.github.copilot/hooks/`; `cursor` -(`.cursor-plugin/`, `beforeShellExecution`) and `codex` (`.codex-plugin/`, `PreToolUse`) each -reach a hook engine no other package can, failing **open** when `python3` is absent. `devin` -(`.devin-plugin/plugin.json` + `hooks.json`, `PreToolUse`) is best-effort by the vendor's own -design, fail-open, not enforced. They require `--out-dir` (or `--out`); in-place output is +The five hook formats ship a byte-identical guard and adapter; since 0.11.0 the policy's own gate rides along too +(`scripts/gate.json`, the stdlib runner, a script gate's `implementations/` tree), as does its `skill/` folder. Only +the envelope differs. `claude` (`.claude-plugin/`, `PreToolUse` + `Stop`) is read natively by Claude Code, Copilot +CLI, VS Code and Grok Build; `copilot` is the Agent Plugins 1.0 layout under `com.github.copilot/hooks/` (`Stop`); +`cursor` (`.cursor-plugin/`) takes `beforeShellExecution` per guard and, per gate, `preToolUse` on the write plus +`stop`; `codex` (`.codex-plugin/`, `PreToolUse` per guard, `Stop` per gate) reaches a hook engine no other package +can, both failing **open** without `python3`; `devin` (`.devin-plugin/plugin.json` + `hooks.json`, same two events) +is best-effort by the vendor's own design, fail-open, not enforced. They require `--out-dir` (or `--out`); in-place output is refused so a policy folder is never mistaken for a published plugin. `--policies-dir` packages a published directory; `--check` judges without writing. `--policy ID` (repeatable; manifest `id` or directory name, else a named error) narrows the build and skips `--out-dir` diff --git a/docs/concepts.md b/docs/concepts.md index 907936d..88da9a8 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -50,8 +50,9 @@ runner enforces at git-hook time. See [Gate DSL](../spec/gate-dsl.md). ## Enforcement surface -*Where* a compiled control runs. Chock models eight: `ambient-rule`, `git-hook`, -`ci-gate`, `pre-tool-use`, `agent-hooks`, `managed-setting`, `gateway`, `mcp-gateway`. +*Where* a compiled control runs. Chock models nine: `ambient-rule`, `git-hook`, +`ci-gate`, `pre-tool-use`, `stop`, `agent-hooks`, `managed-setting`, `gateway`, +`mcp-gateway`. Different agents support different surfaces, `gateway` is modeled but not emitted yet, and some surfaces need an explicit installer before they enforce anything — see [Enforcement Surfaces](enforcement-surfaces.md). diff --git a/docs/enforcement-surfaces.md b/docs/enforcement-surfaces.md index a225a20..b64df3c 100644 --- a/docs/enforcement-surfaces.md +++ b/docs/enforcement-surfaces.md @@ -22,8 +22,8 @@ each guarantee holds. > `pre-tool-use` structurally *cannot* see: a pre-tool hook is handed the tool call, so it > matches only a recorded write vocabulary, and a heredoc or a redirect carries no file argument > at all. A turn-end hook is handed nothing and reads the worktree, so it sees those bytes -> however they got there -- and it takes no matcher, so it wires **six vendors** where the write -> path wires two. +> however they got there -- and it takes no matcher, so it wires **seven vendors** where the +> write path wires three. > > What it does not buy is coverage, and `coverage_cell` refuses it any (`UNCREDITED_SURFACES`). > Every tool call in the turn has already run by the time it fires, so it cannot prevent a @@ -150,7 +150,8 @@ evidence cap that bounds every grade -- is on its own page: **[Coverage Levels]( 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 (Claude Code and Cursor; Copilot +the `pre-tool-use` surface on agents that support it (nine of them, Claude Code and Cursor +among them; Copilot CLI and VS Code get the same guard via `agent-hooks`). 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 diff --git a/src/chock/compile/emitters/in_agent.py b/src/chock/compile/emitters/in_agent.py index 2686315..0a8a87a 100644 --- a/src/chock/compile/emitters/in_agent.py +++ b/src/chock/compile/emitters/in_agent.py @@ -168,7 +168,7 @@ def _stop_fragments(policy_id: str, spec: dict[str, Any], output_dir: Path) -> l Every vendor `stop_vendors` admits gets one. A turn-end hook carries no tool to match on, so nothing here depends on a write vocabulary -- the reason this surface reaches - seven vendors where the write path reaches two. + seven vendors where the write path reaches three. """ gate = output_dir / GATE_FILE write_generated_json(gate, spec) diff --git a/src/chock/compile/surfaces.py b/src/chock/compile/surfaces.py index 3112b79..550fdab 100644 --- a/src/chock/compile/surfaces.py +++ b/src/chock/compile/surfaces.py @@ -48,9 +48,9 @@ del _agent, _surface #: Derived from the matrix asking about the turn-end event, not inherited from the pre-tool -#: answer: cursor, grok and windsurf can observe a finished turn but not refuse one, and -#: three of the agents here have no write vocabulary recorded, so neither set contains the -#: other. `stop` takes no matcher, so no vendor is held back for want of a tool vocabulary; +#: answer: grok and windsurf can observe a finished turn but not refuse one, and four of the +#: agents here (antigravity, codex_cli, devin, tabnine) have no write vocabulary recorded, so +#: neither set contains the other. `stop` takes no matcher, so no vendor is held back for want of a tool vocabulary; #: vscode_copilot is held back for want of a witnessed key (chock.vendors.stop_vendors). for _agent in STOP_TODAY: if not _stop_can_block(_agent): # pragma: no cover - membership already derives from can_block diff --git a/src/chock/plugin/cli.py b/src/chock/plugin/cli.py index 2673f8f..0b5e3bf 100644 --- a/src/chock/plugin/cli.py +++ b/src/chock/plugin/cli.py @@ -218,7 +218,9 @@ def main(argv: list[str] | None = None) -> int: " root plugin.json + com.github.copilot/hooks/ per guard policy and per gate (Stop); same posture discipline" ) if "cursor" in formats: - print(" .cursor-plugin/plugin.json + hooks/ (beforeShellExecution) per guard policy") + print( + " .cursor-plugin/plugin.json + hooks/ (beforeShellExecution per guard policy; preToolUse on the write and stop per gate)" + ) if "codex" in formats: print(" .codex-plugin/plugin.json + hooks/ (PreToolUse per guard policy; Stop per gate)") if "devin" in formats: From 066f780ee677dd712db7fffa696c17238897f0ca Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 23 Sep 2026 19:53:05 +0000 Subject: [PATCH 2/3] docs: the catalog holds 42 policies, not 39 The ecosystem table's count was last true three policies ago; the catalog's own README badge reads 42. Co-Authored-By: Claude Opus 5 Signed-off-by: Claude --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c156d51..7c11c1f 100644 --- a/README.md +++ b/README.md @@ -239,7 +239,7 @@ is where to start. |---|---| | [agentseam](https://github.com/open-coder-ai/agentseam) | the primitives — one handler API and a verified capability matrix across 16 agents | | [chock](https://github.com/open-coder-ai/chock) | the compiler — one policy into git hooks, CI gates and native pre-tool hooks | -| [chock-catalog](https://github.com/open-coder-ai/chock-catalog) | the policies — 39, each labelled enforced or advisory, with replayed evals | +| [chock-catalog](https://github.com/open-coder-ai/chock-catalog) | the policies — 42, each labelled enforced or advisory, with replayed evals | | [context-report](https://github.com/open-coder-ai/context-report) | the evidence — a signed report of whether an agent artifact actually works | | [chock-threat-intel](https://github.com/open-coder-ai/chock-threat-intel) | the threat ledger the catalog's policies answer to | | chock-{claude,cursor,copilot,codex}-plugins | the catalog, packaged for each agent's plugin format (generated) | From 5ab5033e3644dcdc3840ba1fb2d1ee67754cd8bc Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 23 Sep 2026 20:32:09 +0000 Subject: [PATCH 3/3] docs: correct what chock writes, grades and enforces A full audit of README.md and docs/ against the source, run in scratch repos rather than read off the previous prose. The honesty claim was the least honest passage in the README. It named three grades where COVERAGE_LEVELS has eight, said a level is claimed only with an install witness when a git hook earns its grade from emission alone, and described 'enforced' as what a control that fails closed reaches. Nothing reaches enforced: this repo's own coverage.json records 168 advisory, 72 enforced-at-commit, 41 best-effort, 5 enforceable and zero enforced, and every in-agent hook fails open except Cursor's, which is configurable. The example manifest a reader is told to write would have failed the check shown directly beneath it: version, provenance, lifecycle and the security baseline were all missing. Verified by running chock check against it. The adapter pages named instruction files chock never writes. Seven of those agents read AGENTS.md natively and get no wrapper at all; Claude Code's is the repo-root CLAUDE.md, not .claude/CLAUDE.md; and the files that are written are named agentseam.*, not chock.*. Checked against chock init --agent-agnostic. getting-started promised that protect-main-branch stops an agent before it runs a command. Its gate declares commit and push, so it compiles no pre-tool surface at all. The same page misdescribed what init and new policy leave on disk, and put chock.lock inside .chock/. Also: SEC-7 repointed to what it actually checks, per spec/enforcement-matrix; the rules page moved from AGENTS.md to INDEX.md; the coverage.json example rewritten in the shape the compiler writes; the CI matrix corrected to Linux-only across three Python versions; the action pin moved off v0.1.0; the conflicts and baseline check targets documented; six gate kinds and four trust tiers, not five and three. Co-Authored-By: Claude Opus 5 Signed-off-by: Claude --- README.md | 61 +++++++++++++++++++++++------------ docs/README.md | 3 +- docs/adapters/README.md | 24 ++++++-------- docs/adapters/aider.md | 2 +- docs/adapters/antigravity.md | 4 +-- docs/adapters/claude.md | 5 +-- docs/adapters/codex.md | 5 +-- docs/adapters/cursor.md | 6 ++-- docs/adapters/devin.md | 2 +- docs/adapters/gemini.md | 5 +-- docs/adapters/github.md | 5 +-- docs/adapters/grok.md | 2 +- docs/adapters/kimi.md | 5 +-- docs/adapters/replit.md | 2 +- docs/adapters/tabnine.md | 2 +- docs/adapters/vscode.md | 5 +-- docs/adapters/windsurf.md | 6 ++-- docs/agentic-risk-coverage.md | 2 +- docs/authoring-policies.md | 5 +-- docs/baseline-policies.md | 11 ++++--- docs/cli-reference.md | 18 +++++------ docs/concepts.md | 13 ++++---- docs/coverage-levels.md | 15 +++++---- docs/figures/family-dark.svg | 2 +- docs/figures/family-light.svg | 2 +- docs/figures/make_family.py | 2 +- docs/getting-started.md | 23 +++++++------ docs/hooks/README.md | 3 +- docs/policies/README.md | 2 +- docs/registry-and-lockfile.md | 16 +++++---- docs/reviewer-evidence.md | 2 +- docs/rules/README.md | 12 ++++--- docs/runtime/README.md | 3 +- docs/security/README.md | 3 +- docs/tools/README.md | 3 +- docs/validation.md | 3 +- 36 files changed, 166 insertions(+), 118 deletions(-) diff --git a/README.md b/README.md index 7c11c1f..857f02a 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,8 @@ Your agent is fast, tireless, and occasionally commits an AWS key. Telling it no until the context window fills up, a new session starts, or a different agent joins the repo with no memory of the last conversation. Chock compiles a rule into the strongest control each agent actually supports — a git hook that exits non-zero, a CI gate, a native pre-tool hook in -Claude Code, Cursor, Copilot CLI and VS Code — and labels honestly when all it can do is advise. +nine clients including Claude Code and Cursor, and chock's own agent-hooks file for Copilot CLI +and VS Code — and labels honestly when all it can do is advise. The rules live in your repo as ordinary files, so they travel with every clone, fork and contributor's agent, instead of living in one person's head or one tool's settings pane. @@ -47,20 +48,21 @@ The next commit containing a credential exits non-zero: ```bash echo 'AWS_KEY=AKIAIOSFODNN7EXAMPLE' > config.py && git add config.py # pragma: allowlist secret git commit -m "add config" -# Potential secret detected in this change. Remove credentials and rotate any exposed keys. -# At commit, add '# pragma: allowlist secret' on the same line only for documented test -# fixtures; the pragma is NOT honored at tool-use, where the scanned text is a live tool -# argument an appended token could neutralize. +# Potential secret detected in staged changes. Remove credentials and rotate any exposed +# keys. Add '# pragma: allowlist secret' on the same line only for documented test fixtures. # - config.py: content pattern echo 'AWS_KEY = os.environ["AWS_KEY"]' > config.py && git add config.py git commit -m "read key from env" # passes ``` +That pragma is honoured at commit, push and in CI, and deliberately **not** at tool-use, +where the scanned text is a live tool argument an appended token could neutralize. + `init` deliberately installs no policies of its own — the framework ships mechanism, and which guardrails you turn on is a choice you make, not one chock makes for you. More policies: `chock add protect-main-branch`, `chock add block-destructive-commands`, or browse the -[catalog](https://github.com/open-coder-ai/chock-catalog) — 39 policies, each labelled with what +[catalog](https://github.com/open-coder-ai/chock-catalog) — 42 policies, each labelled with what it actually reaches, from a strict block to a merely-advisory ambient rule. Once installed, a policy is yours: edit the manifest, adjust the message, tighten the pattern, and `chock sync` recompiles every surface from your edited copy, not the upstream original. @@ -70,10 +72,10 @@ recompiles every surface from your edited copy, not the upstream original. Everything Chock installs is a plain file, committed to your repo, so it travels with every clone and fork instead of living in a hosted dashboard only you can see: -- **Author once, enforce everywhere** — one policy compiles to a git hook, a CI gate and native - pre-tool-use hooks across every supported agent, plus an `AGENTS.md` rule that every other - agent reads ambiently; wiring the CI gate (`chock sync --ci`) is what turns a policy's - commit-time surfaces `enforced-at-commit` instead of merely compiled. +- **Author once, enforce everywhere** — one policy compiles to a git hook, a CI gate and a + native in-agent hook wherever the client has one, plus an `AGENTS.md` rule that every + supported agent reads ambiently; a compiled git hook is what earns `enforced-at-commit`, and wiring + the CI gate (`chock sync --ci`) puts the same policy in front of every push as well. - **A real catalog, one command away** — `chock add scan-secrets` (or `protect-main-branch`, `block-destructive-commands`, an OWASP agentic-security pack, and more) pulls a ready-made policy from the [catalog](https://github.com/open-coder-ai/chock-catalog); installed content @@ -82,14 +84,15 @@ clone and fork instead of living in a hosted dashboard only you can see: added with `chock add ` or scaffolded with `chock new policy`; extending the guardrails your team needs ships a manifest and an eval suite, never a change to chock's engine. - **Deterministic, not vibes** — gates are declarative and run through a stdlib-only vendored - runner; guard scripts are plain, reviewable bash. No LLM calls, no network access, at + runner; guard scripts are plain, reviewable shell or Python. No LLM calls, no network access, at enforcement time — a hook either blocks or it doesn't, and it does the same thing twice. -- **Coverage you can prove** — every policy × agent is graded `enforced`, `enforced-at-commit` - or `advisory`, and a level is only claimed once there's an install witness for it: ambient - `AGENTS.md` prose is `advisory`, a compiled git hook or a wired CI gate is `enforced-at-commit`, - and a native in-agent control that fails closed is `enforced`. A grade that is free to say a - surface is behind, and does. -- **One CLI, eight verbs** — `init` · `add` · `remove` · `sync` · `check` · `status` · +- **Coverage you can prove** — every policy × agent is graded, and the grade carries the + evidence that bounds it: ambient `AGENTS.md` prose is `advisory`, a compiled git hook or a + wired CI gate is `enforced-at-commit`, and an in-agent control is graded on what its client + actually honours — `best-effort` for the ten whose in-agent hook fails open, `enforceable` + for Cursor, the one client that can be configured to fail closed. `enforced` exists in the + vocabulary and no agent reaches it today. A grade that is free to say a surface is behind, and does. +- **One CLI, eight everyday verbs** — `init` · `add` · `remove` · `sync` · `check` · `status` · `enable` · `disable`. If you've used `uv` or `poetry`, you already know most of them. A policy is one folder, and the manifest *is* the rule — here is `scan-secrets` in this repo: @@ -127,13 +130,14 @@ write-up, including all nine surfaces and the per-agent matrix: [Architecture](d ## Author your own policy A policy is a small, reviewable manifest — the `hook.gate` block is what enforces, and a -blocking hook with no gate fails validation. There is no separate plugin API to learn: writing +blocking hook with neither a gate nor a script fails validation. There is no separate plugin API to learn: writing a policy means writing this file, plus the eval cases that prove it actually fires. ```yaml # .agents/policies/block-console-log/manifest.yaml id: block-console-log name: "No console.log in committed code" +version: "0.0.1" artifact: hook enforcement: block effects: [read_only] @@ -147,6 +151,22 @@ hook: message: "console.log added -- remove debug output before committing." params: content_pattern: "console\\.log" + +provenance: + author: "you" + author_email: "you@example.com" + created_at: "2026-01-01T00:00:00Z" + updated_at: "2026-01-01T00:00:00Z" + source_repo: "https://example.com" + license: "Apache-2.0" + trust_tier: "sandbox" + +lifecycle: + status: draft + reviewed_by: [] + +security: + content_instructions: never-obey ``` ```bash @@ -209,8 +229,9 @@ large PRs in a weekend; you are still one human reading diffs, and you get no sa they bring — Claude Code today, Cursor tomorrow, something new next month. What you do control is the repo itself, and Chock policies are committed content, so your rules travel with every clone and fork: every contributor's agent reads your rules with zero setup the moment the repo is -cloned, a committed SessionStart hook re-arms Chock's git hooks on a fresh clone (since git -itself never clones hooks), and the CI gate you wire with `chock sync --ci` is yours and depends +cloned, a committed SessionStart hook re-arms Chock's git hooks on a fresh clone in Claude Code +(since git itself never clones hooks; other clients are told to run `chock sync` instead), and +the CI gate you wire with `chock sync --ci` is yours and depends on nothing the contributor does — a policy skipped or bypassed locally is still enforced on the pull request. The rules reach the contributor's agent before the code is written, so what still arrives has already passed your gates: review the policy once, instead of every PR it would have diff --git a/docs/README.md b/docs/README.md index ddc699d..e542a00 100644 --- a/docs/README.md +++ b/docs/README.md @@ -37,7 +37,8 @@ Everything you need to understand, use, and extend Chock. New here? Start with 1. You write a **policy** — a small folder with a manifest, an optional deterministic script, and evals. 2. `chock check` checks it against the **spec**. 3. `chock compile` turns it into the **enforcement surfaces** each agent supports - (git hook, CI gate, Claude PreToolUse / managed-settings, `AGENTS.md` rule) and writes a **coverage report**. + (git hook, CI gate, a native in-agent hook on the nine clients that have one, the turn's end on + seven, and the `AGENTS.md` rule everywhere) and writes a **coverage report**. 4. `chock init` scaffolds the wiring — **no policies**; `chock add` installs each policy you choose from the catalog, and the **registry** and **lockfile** keep it reproducible. ## 🔗 Related references diff --git a/docs/adapters/README.md b/docs/adapters/README.md index dba99e2..38fbf40 100644 --- a/docs/adapters/README.md +++ b/docs/adapters/README.md @@ -1,29 +1,25 @@ -:= chock-context - # Adapters Adapters map the agent-agnostic Chock core to agent-specific surfaces. ## Agent-specific entry points -Each supported agent has a native file that delegates back to `AGENTS.md`: +Most agents read `AGENTS.md` directly and get **no wrapper file at all** — Cursor, Codex, +Copilot, VS Code, Gemini CLI, Windsurf and Kimi Code among them. A wrapper is written only for +an agent that cannot read the shared file. `chock init . --agent-agnostic` writes exactly +these, and nothing else: -- Claude Code: `.claude/CLAUDE.md` -- Cursor: `.cursor/rules/*.mdc` (modern), `.cursorrules` (legacy fallback) +- Claude Code: `CLAUDE.md` (repo root) +- Aider: `CONVENTIONS.md` + `.aider.conf.yml` +- Antigravity CLI: `.agents/rules/agentseam.md` - Devin: `.devin/README.md` -- Windsurf: `.windsurf/rules/*.md` (modern), `.windsurfrules` (legacy fallback) -- Codex: `codex.md` -- GitHub Copilot: `.github/copilot-instructions.md` -- Gemini CLI: `.gemini/GEMINI.md` - Grok Build: `.grok/GROK.md` -- Kimi Code: `.kimi-code/AGENTS.md` -- Aider: `CONVENTIONS.md` + `.aider.conf.yml` -- VS Code: `.github/agents/*.agent.md` +- Junie: `.junie/guidelines.md` - Replit Agent: `replit.md` - Tabnine: `guidelines.md` -- Antigravity CLI: `.agents/rules/*.md` -These files are thin wrappers. The actual rules and skills live in `AGENTS.md`, `.agents/skills/`, and `.agents/policies/`. +These files are thin wrappers. The actual rules and skills live in `AGENTS.md`, +`.agents/policies/INDEX.md`, `.agents/skills/` and `.agents/policies/`. ## Contents diff --git a/docs/adapters/aider.md b/docs/adapters/aider.md index a13411b..d8a4683 100644 --- a/docs/adapters/aider.md +++ b/docs/adapters/aider.md @@ -16,4 +16,4 @@ Aider does not auto-discover project instruction files; load them with `/read CO - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Policies: `.agents/policies/` -- Validator: `the `chock check` CLI (src/chock/validation/)` +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/adapters/antigravity.md b/docs/adapters/antigravity.md index 302a07a..4dc406d 100644 --- a/docs/adapters/antigravity.md +++ b/docs/adapters/antigravity.md @@ -6,7 +6,7 @@ Verified against: https://antigravity.google/docs/rules-workflows (Antigravity R ## Files -- `.agents/rules/chock.md` — workspace rule with `trigger: always_on` that points to `AGENTS.md` +- `.agents/rules/agentseam.md` — workspace rule with `trigger: always_on` that points to `AGENTS.md` Antigravity CLI discovers rules hierarchically in `.agents/rules/*.md`, `GEMINI.md`, and `AGENTS.md`. @@ -17,4 +17,4 @@ Antigravity's current [best-practices documentation](https://antigravity.google/ - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Policies: `.agents/policies/` -- Validator: the `chock check` CLI (src/chock/validation/) +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/adapters/claude.md b/docs/adapters/claude.md index 057ca22..9a73fd2 100644 --- a/docs/adapters/claude.md +++ b/docs/adapters/claude.md @@ -4,7 +4,8 @@ The Claude adapter is a thin pointer to the agent-agnostic Chock core. ## Files -- `.claude/CLAUDE.md` — agent-readable wrapper that points to `AGENTS.md` +- `CLAUDE.md` (repo root) — agent-readable wrapper that points to `AGENTS.md` +- `.claude/settings.json` — hook entries chock installs - `docs/README.md` — human documentation ## Pointers @@ -12,4 +13,4 @@ The Claude adapter is a thin pointer to the agent-agnostic Chock core. - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Wiring: `src/chock/scaffold/adapters.py` -- Validator: `the `chock check` CLI (src/chock/validation/)` +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/adapters/codex.md b/docs/adapters/codex.md index 0c1232d..1a2a650 100644 --- a/docs/adapters/codex.md +++ b/docs/adapters/codex.md @@ -4,7 +4,8 @@ The Codex adapter is a thin pointer to the agent-agnostic Chock core. ## Files -- `codex.md` — agent-readable wrapper that points to `AGENTS.md` +- No wrapper file: this agent reads `AGENTS.md` natively, so `chock init` writes nothing + agent-specific for it. - `docs/README.md` — human documentation ## Pointers @@ -12,4 +13,4 @@ The Codex adapter is a thin pointer to the agent-agnostic Chock core. - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Wiring: `src/chock/scaffold/adapters.py` -- Validator: `the `chock check` CLI (src/chock/validation/)` +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/adapters/cursor.md b/docs/adapters/cursor.md index 4e1551d..6f885e5 100644 --- a/docs/adapters/cursor.md +++ b/docs/adapters/cursor.md @@ -4,8 +4,8 @@ The Cursor adapter is a thin pointer to the agent-agnostic Chock core. ## Files -- `.cursor/rules/chock.mdc` — agent-readable wrapper that points to `AGENTS.md` -- `.cursorrules` — legacy rules file that points to `AGENTS.md` +- No wrapper file: this agent reads `AGENTS.md` natively, so `chock init` writes nothing + agent-specific for it. - `docs/README.md` — human documentation ## Pointers @@ -13,4 +13,4 @@ The Cursor adapter is a thin pointer to the agent-agnostic Chock core. - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Wiring: `src/chock/scaffold/adapters.py` -- Validator: `the `chock check` CLI (src/chock/validation/)` +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/adapters/devin.md b/docs/adapters/devin.md index 29a0465..9891d37 100644 --- a/docs/adapters/devin.md +++ b/docs/adapters/devin.md @@ -12,4 +12,4 @@ The Devin adapter is a thin pointer to the agent-agnostic Chock core. - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Wiring: `src/chock/scaffold/adapters.py` -- Validator: `the `chock check` CLI (src/chock/validation/)` +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/adapters/gemini.md b/docs/adapters/gemini.md index 20ebf8f..ef1979e 100644 --- a/docs/adapters/gemini.md +++ b/docs/adapters/gemini.md @@ -6,7 +6,8 @@ Verified against: https://google-gemini.github.io/gemini-cli/docs/cli/gemini-md. ## Files -- `.gemini/GEMINI.md` — agent-readable wrapper that points to `AGENTS.md` +- No wrapper file: Gemini CLI reads `AGENTS.md` natively, so `chock init` writes nothing + agent-specific for it. Gemini CLI discovers `GEMINI.md` files hierarchically: global `~/.gemini/GEMINI.md`, project root, `.gemini/`, and subdirectories. @@ -15,4 +16,4 @@ Gemini CLI discovers `GEMINI.md` files hierarchically: global `~/.gemini/GEMINI. - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Policies: `.agents/policies/` -- Validator: `the `chock check` CLI (src/chock/validation/)` +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/adapters/github.md b/docs/adapters/github.md index ea4c957..8d9c34f 100644 --- a/docs/adapters/github.md +++ b/docs/adapters/github.md @@ -4,7 +4,8 @@ The GitHub Copilot adapter is a thin pointer to the agent-agnostic Chock core. ## Files -- `.github/copilot-instructions.md` — agent-readable wrapper that points to `AGENTS.md` +- No wrapper file: this agent reads `AGENTS.md` natively, so `chock init` writes nothing + agent-specific for it. - `docs/README.md` — human documentation ## Pointers @@ -12,4 +13,4 @@ The GitHub Copilot adapter is a thin pointer to the agent-agnostic Chock core. - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Wiring: `src/chock/scaffold/adapters.py` -- Validator: `the `chock check` CLI (src/chock/validation/)` +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/adapters/grok.md b/docs/adapters/grok.md index 76970f1..75e87a6 100644 --- a/docs/adapters/grok.md +++ b/docs/adapters/grok.md @@ -15,4 +15,4 @@ Grok Build reads `AGENTS.md` natively, so this file is only a convenience marker - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Policies: `.agents/policies/` -- Validator: `the `chock check` CLI (src/chock/validation/)` +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/adapters/kimi.md b/docs/adapters/kimi.md index 590ad25..6f343f5 100644 --- a/docs/adapters/kimi.md +++ b/docs/adapters/kimi.md @@ -6,7 +6,8 @@ Verified against: https://www.kimi.com/code/docs/en/kimi-code-cli/customization/ ## Files -- `.kimi-code/AGENTS.md` — optional agent-readable wrapper that points to `AGENTS.md` +- No wrapper file: Kimi Code reads `AGENTS.md` natively, so `chock init` writes nothing + agent-specific for it. Kimi Code CLI reads `AGENTS.md` natively from the project root or `.kimi-code/AGENTS.md`, hierarchically. @@ -15,4 +16,4 @@ Kimi Code CLI reads `AGENTS.md` natively from the project root or `.kimi-code/AG - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Policies: `.agents/policies/` -- Validator: `the `chock check` CLI (src/chock/validation/)` +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/adapters/replit.md b/docs/adapters/replit.md index f7bb9a1..1daf56f 100644 --- a/docs/adapters/replit.md +++ b/docs/adapters/replit.md @@ -15,4 +15,4 @@ Replit Agent uses `replit.md` for project-level context. - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Policies: `.agents/policies/` -- Validator: `the `chock check` CLI (src/chock/validation/)` +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/adapters/tabnine.md b/docs/adapters/tabnine.md index bbf732e..4a984f8 100644 --- a/docs/adapters/tabnine.md +++ b/docs/adapters/tabnine.md @@ -15,4 +15,4 @@ Tabnine Agent reads `guidelines.md` for project-specific instructions. - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Policies: `.agents/policies/` -- Validator: `the `chock check` CLI (src/chock/validation/)` +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/adapters/vscode.md b/docs/adapters/vscode.md index 0687440..2632d5c 100644 --- a/docs/adapters/vscode.md +++ b/docs/adapters/vscode.md @@ -6,7 +6,8 @@ Verified against: https://code.visualstudio.com/docs/agent-customization/custom- ## Files -- `.github/agents/chock.agent.md` — VS Code custom agent file +- No wrapper file: VS Code reads `AGENTS.md` natively, so `chock init` writes nothing + agent-specific for it. VS Code custom agents are defined in `.agent.md` files. Workspace-scoped agents live in `.github/agents/` (or `.claude/agents/` in Claude format). @@ -15,4 +16,4 @@ VS Code custom agents are defined in `.agent.md` files. Workspace-scoped agents - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Policies: `.agents/policies/` -- Validator: `the `chock check` CLI (src/chock/validation/)` +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/adapters/windsurf.md b/docs/adapters/windsurf.md index 0679508..9a5955e 100644 --- a/docs/adapters/windsurf.md +++ b/docs/adapters/windsurf.md @@ -4,8 +4,8 @@ The Windsurf adapter is a thin pointer to the agent-agnostic Chock core. ## Files -- `.windsurf/rules/chock.md` — agent-readable wrapper that points to `AGENTS.md` -- `.windsurfrules` — legacy rules file that points to `AGENTS.md` +- No wrapper file: this agent reads `AGENTS.md` natively, so `chock init` writes nothing + agent-specific for it. - `docs/README.md` — human documentation ## Pointers @@ -13,4 +13,4 @@ The Windsurf adapter is a thin pointer to the agent-agnostic Chock core. - Core rules: `AGENTS.md` - Skills: `.agents/skills/` - Wiring: `src/chock/scaffold/adapters.py` -- Validator: `the `chock check` CLI (src/chock/validation/)` +- Validator: the `chock check` CLI (`src/chock/validation/`) diff --git a/docs/agentic-risk-coverage.md b/docs/agentic-risk-coverage.md index 2d91100..a10adf9 100644 --- a/docs/agentic-risk-coverage.md +++ b/docs/agentic-risk-coverage.md @@ -43,7 +43,7 @@ Chock's guard degrades to allow, so no row here earns it. | …an agent poisoning its own long-term memory? | `memory-discipline` | `advisory` — and deliberately so here: write-path memory enforcement is a different system than a repo-scoped framework, and this page does not claim it | | …direct pushes to `main`, `--no-verify`, force-pushes? | `protect-main-branch` gate, `block-no-verify`, `git-safety` | `enforced-at-commit` (gate); the never-bypass-hooks discipline itself: `advisory` backed by the CI gate, which re-runs `chock check` on the PR head regardless of what was skipped locally | | …wildcard permission grants in agent config? | `block-wildcard-agent-permissions` gate | `enforced-at-commit` | -| …a workflow pulling a third-party Action by a tag that can be re-pointed? | `pin-github-actions` gate | `enforced-at-commit`; `best-effort` at the write path on Claude Code and Gemini CLI, bounded to `.github/workflows/*` and `.github/actions/*` | +| …a workflow pulling a third-party Action by a tag that can be re-pointed? | `pin-github-actions` gate | `enforced-at-commit`; `best-effort` at the write path on Claude Code and Gemini CLI and `enforceable` on Cursor, bounded to `.github/workflows/*` and `.github/actions/*` | | …an agent deleting tests or assertions to get green? | `test-integrity` gate, `agent-discipline` (`assertion_deletion: block`) | `enforced-at-commit` (gate) — blocks a deleted test file, a net loss of assertions across the change, or a vacuous assertion (`assert True`, `expect(true)`) added in their place; re-checked in CI on the PR head via the `ci-gate` backstop, so it holds for an inbound contributor whose agent never ran a hook. The pragma `chock: test-removal-reviewed` on the removing line is the reviewed escape hatch. No `pre-tool-use` binding exists, so this is not claimed at the in-agent tier. The ambient rule text remains `advisory` | | …a PR merging with no machine-checkable basis for what it claims to have passed? | `require-review-evidence` (opt-in), `chock review require --base ` | `enforced-in-ci`, and nothing more: it re-derives a contributor's claimed checks against the repository's own `required_checks` (by `command_set_hash`, not by lookup — a shrunk set or a redefined check both fail) and judges outcome separately from validity. It does **not** make review find more bugs, and it is opt-in because it needs curated `required_checks` config; unset, it claims nothing | diff --git a/docs/authoring-policies.md b/docs/authoring-policies.md index 854088f..7b5ebc7 100644 --- a/docs/authoring-policies.md +++ b/docs/authoring-policies.md @@ -180,8 +180,9 @@ which the vendored runner enforces. See [Gate DSL](../spec/gate-dsl.md) for the ### `rule.text` replaces `rule_text` A rule's text lives under `rule.text` in `manifest.yaml`. The top-level `rule_text` key is gone; -`additionalProperties: false` rejects it. The compiled `AGENTS.md` block byte-matches `rule.text` -between the `` markers (SEC-7). +`additionalProperties: false` rejects it. SEC-7 guarantees that the compiled ambient surface is what the +policies produce and nobody hand-edits it: `chock check --only index` regenerates +`.agents/policies/INDEX.md` and the `AGENTS.md` pointer block, and exits non-zero on any diff. ### Skills and workflows carry no `manifest.yaml` (D1) diff --git a/docs/baseline-policies.md b/docs/baseline-policies.md index 8e30df8..72e1ec5 100644 --- a/docs/baseline-policies.md +++ b/docs/baseline-policies.md @@ -90,10 +90,13 @@ own the same way: a folder, a manifest, and (optionally) an ## Cross-platform & tested -`scan-secrets`, `protect-main-branch`, `verify-dependency-exists` and `test-integrity` are **declarative** -(`hook.gate` in `manifest.yaml`); `chock compile` emits the cross-platform git-hook shims and -a self-contained, stdlib-only Python runner. The remaining guards ship bash implementations invoked -through the PreToolUse adapter. `.gitattributes` pins scripts to LF so their hashes — and therefore +Seven policies are **declarative** (`hook.gate` in `manifest.yaml`): `scan-secrets`, +`protect-main-branch`, `verify-dependency-exists`, `test-integrity`, `block-invisible-unicode`, +`block-wildcard-agent-permissions` and `pin-github-actions`. For those, `chock compile` emits the +cross-platform git-hook shims and a self-contained, stdlib-only Python runner. Four ship shell +implementations invoked through the pre-tool adapter: `block-destructive-commands`, +`block-no-verify`, `protect-agent-config` and `protect-commit-privacy`. The `.gitattributes` that `chock init` writes pins `chock.lock`, `.chock/**` and `.agents/**` +to LF so their hashes — and therefore the registry and lockfile — are byte-identical across operating systems. Every catalog policy carries an eval suite, and `chock check --only evals` replays each one's own diff --git a/docs/cli-reference.md b/docs/cli-reference.md index e75d2b3..7eb0d4f 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -22,14 +22,12 @@ which fetches from the catalog you point it at). chock init [repo] [--agents claude cursor copilot] [--agent-agnostic] [--skip-hooks] [--force] ``` -Scaffolds a consumer repo — **wiring only, no policies**: creates `.chock/` (config + -`chock.lock`) and an empty `.agents/policies/`, writes agent wrapper files, installs the +Scaffolds a consumer repo — **wiring only, no policies**: creates `.chock/` (config, coverage, vendored runtime), `chock.lock` at the repo root and an empty `.agents/policies/`, writes agent wrapper files, installs the git hook dispatchers, and runs a validation self-check. Add policies afterwards with `chock add` (or by copying a policy folder in and running `chock sync`). **Idempotent** — safe to re-run; it overwrites derived files but never your own policies. -- `--agents` — space-separated target agents (default: `claude copilot gemini` — the agents that - can't read `AGENTS.md` natively). +- `--agents` — space-separated target agents (default: `claude copilot gemini`). A wrapper file is written only for an agent that cannot read `AGENTS.md` natively — of those three, Claude Code alone. - `--agent-agnostic` — generate wrappers for all supported agents. - `--skip-hooks` — skip git hook installation. - `--force` — overwrite scaffolded files that have local edits (destructive). @@ -111,13 +109,15 @@ Runs every truth check, read-only — `check` never regenerates what it measures | `matrix` | Spec invariants are traceable in the enforcement matrix. Framework-repo homework: auto-skipped (with a note) in repos that have no `spec/enforcement-matrix.md`. | | `mechanisms` | Every enforcement-matrix row naming a `` `function()` `` names a real, invoked, severity-capable one — presence in `matrix` is not the same as being real. Same auto-skip as `matrix`. | | `index` | `INDEX.md` and the `AGENTS.md` pointer are fresh. | +| `conflicts` | No two installed policies contradict each other — one allowing what another refuses. | | `baseline` | The policy set is no weaker than `--base REF`'s: nothing newly in `policies.disabled`, nothing downgraded to advisory, no surfaces dropped. Bare `chock check` skips it with a note; CI runs it against the pull request's base branch. | - `--only` — comma-separated subset, e.g. `--only validate,verify`. - `--mode` — frontier validation profile (e.g. `frontier-claude`), passed to `validate`. - `--event` — hook event context (e.g. `commit`), passed to `validate`; softens pre-existing-drift findings at commit time. -- `eval export --format context-report --out DIR [POLICY_ID ...]` — exports a policy's tier-3 - cases (no `execute` block) as a context-report run/v0.1 directory; see [Evals](evals.md#exporting-tier-3-cases-to-context-report). +(`chock eval export --format context-report --out DIR [POLICY_ID ...]` exports a policy's tier-3 +cases as a context-report run/v0.1 directory — its own command, not a `check` flag. See +[Evals](evals.md#exporting-tier-3-cases-to-context-report).) ### `status` — what is installed, and what happened @@ -152,9 +152,9 @@ with exit 2 if the policy is `mandatory: true`. Both reject unknown ids. chock new {policy|skill|subagent} [--root .] ``` -Creates a valid, empty artifact folder (`manifest.yaml` with a `hook.gate` block, -implementation stub, `evals/suite.yaml`) ready to fill in — by hand or with the -`policy-init` skill in your agent. +Creates a valid, empty artifact folder to fill in by hand or with the `policy-init` skill. `new policy` writes +`manifest.yaml` (the gate is its `hook.gate` block) and `evals/suite.yaml`; `new skill` writes a `SKILL.md`, whose +frontmatter *is* its manifest; `new subagent` writes `subagent.yaml`. ### `compile` — low-level single-policy compile diff --git a/docs/concepts.md b/docs/concepts.md index 88da9a8..968cd5f 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -22,7 +22,7 @@ The manifest's `artifact` field declares what kind of thing the policy is: Those four are the complete set — `manifest.yaml` will not validate any other `artifact` value. **Subagents are declared separately.** A scoped worker an agent can delegate to lives in its own -`subagent.yaml` under `subagents/`, validated by `subagent.schema.json`, which fixes +`subagent.yaml` under `.agents/skills//`, validated by `subagent.schema.json`, which fixes `artifact: subagent`. It is not one of the `manifest.yaml` artifact values above. See [Authoring Policies](authoring-policies.md) for the manifest fields of each. @@ -38,12 +38,13 @@ fields include: - **`approval`** — whether a human must approve before the *action* proceeds (never required for a read-only guard). - **`provenance` / `lifecycle` / `trust_tier`** — authorship, `draft → review → production`, and how - much the artifact is trusted (`sandbox` → `community` → verified). + much the artifact is trusted (`sandbox` → `community` → `verified` → `certified`). ## Gate A hook's `hook.gate` in `manifest.yaml` selects a deterministic **kind** (`content_regex`, `forbidden_ref`, -`dependency_allowlist`, `test_integrity`, or `script` for a check the policy's own program makes), its +`dependency_allowlist`, `test_integrity`, `script` for a check the policy's own program makes, or +`egress_allowlist` at the gateway), its `params`, the events it runs on (`on: [commit|push|tool_use]`), and the message shown on block. `chock compile` flattens it to `gate.json`, which the vendored `.chock/bin/gate.py` runner enforces at git-hook time. See [Gate DSL](../spec/gate-dsl.md). @@ -137,7 +138,7 @@ longer inlines those rules; it holds a managed pointer block that tells the agen read it — while git hooks are the guaranteed enforcement floor. **No in-context mechanism is a guarantee, including inlining.** Agents auto-load their own -client file (`.claude/CLAUDE.md`, `.cursor/rules/*`), which points to `AGENTS.md`, which +client file where they have one (`CLAUDE.md`), or read `AGENTS.md` directly, which points here — every step after the first depends on the agent choosing to follow the pointer. This was verified by observation, not assumed. Inlining rules directly into `AGENTS.md` would not change that, because `AGENTS.md` is not auto-loaded either. @@ -161,5 +162,5 @@ upgrade — which is precisely what makes customisation impossible. See [Policie ## Adapter The per-agent wrapper that points an agent at `AGENTS.md` (the compiled source of truth) in the -format that agent expects — `.claude/CLAUDE.md`, `.cursor/rules/*.mdc`, `.github/copilot-instructions.md`, -and so on. See [Adapters](adapters/README.md). +format that agent expects — root `CLAUDE.md` for Claude Code, `CONVENTIONS.md` for Aider, and so +on. Most agents read `AGENTS.md` natively and get no wrapper at all. See [Adapters](adapters/README.md). diff --git a/docs/coverage-levels.md b/docs/coverage-levels.md index 1f664b7..4ffb946 100644 --- a/docs/coverage-levels.md +++ b/docs/coverage-levels.md @@ -64,7 +64,8 @@ number comparing them would invent a scale that does not exist. > gives the per-path reasoning and the per-client evidence. `DEGRADES_TO_DENY`'s own rule > settles the grade: a control mixing the two is declared at its weakest path, so > `CONTROL_DEGRADES_TO` stays `allow`, chock's `pre-tool-use` and `agent-hooks` stay at -> `best-effort`, and this level still names something we do not earn. That is the intended +> `best-effort` on every client whose hook fails open (Cursor, which can be configured to fail +> closed, reaches `enforceable`), and this level still names something we do not earn. That is the intended > result: the ladder is only worth trusting where it flatters us if it can also report that > we are behind — including when we have genuinely improved and still fall short. @@ -83,15 +84,17 @@ number comparing them would invent a scale that does not exist. > command into the session context when it cannot. See > [Arming a fresh clone](adopting.md#arming-a-fresh-clone). -Example for `protect-main-branch` (targets git-hook + CI + PreToolUse + managed-setting): +Example for `protect-main-branch`, whose gate declares `on: [commit, push]` and so targets +git-hook + CI + ambient-rule + managed-setting, with no pre-tool surface at all. Every agent +reaches the same grade, because the git hook is what earns it: ```json { "protect-main-branch": { - "claude": "best-effort", - "cursor": "enforced-at-commit", - "copilot": "enforced-at-commit", - "aider": "enforced-at-commit" + "claude": { "level": "enforced-at-commit", "basis": null, "witnessed": false }, + "cursor": { "level": "enforced-at-commit", "basis": null, "witnessed": false }, + "copilot": { "level": "enforced-at-commit", "basis": null, "witnessed": false }, + "aider": { "level": "enforced-at-commit", "basis": null, "witnessed": false } } } ``` diff --git a/docs/figures/family-dark.svg b/docs/figures/family-dark.svg index af55724..8c65242 100644 --- a/docs/figures/family-dark.svg +++ b/docs/figures/family-dark.svg @@ -16,7 +16,7 @@ chock-catalog - the policies — 39, each labelled enforced or advisory, with replayed evals + the policies — 42, each labelled enforced or advisory, with replayed evals diff --git a/docs/figures/family-light.svg b/docs/figures/family-light.svg index cb95f50..b8c8a64 100644 --- a/docs/figures/family-light.svg +++ b/docs/figures/family-light.svg @@ -16,7 +16,7 @@ chock-catalog - the policies — 39, each labelled enforced or advisory, with replayed evals + the policies — 42, each labelled enforced or advisory, with replayed evals diff --git a/docs/figures/make_family.py b/docs/figures/make_family.py index 6a70d5e..532d00d 100644 --- a/docs/figures/make_family.py +++ b/docs/figures/make_family.py @@ -22,7 +22,7 @@ "native pre-tool hooks", ], "chock-catalog": [ - "the policies — 39, each labelled enforced or advisory,", + "the policies — 42, each labelled enforced or advisory,", "with replayed evals", ], "context-report": [ diff --git a/docs/getting-started.md b/docs/getting-started.md index 0b2202a..2283826 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -48,10 +48,12 @@ chock init . `init` is fully deterministic — no AI agent required. It: -- creates `.chock/` (config + lockfile + `dependency-allowlist.txt`) and an empty `.agents/policies/`, +- creates `.chock/` (config, `coverage.json`, `dependency-allowlist.txt`, the vendored +runtime under `bin/` and compiled output under `compiled/`) plus `chock.lock` at the repo root and an empty `.agents/policies/`, - writes `AGENTS.md`, a `.gitattributes` pinning generated scripts to LF, a `.gitignore` rule for - the per-machine gate log (`.chock/log/`), the agent wrapper files - (`.claude/CLAUDE.md`, `.cursor/rules/…`, `.github/copilot-instructions.md`, …), and the + the per-machine gate log (`.chock/log/`), a wrapper file for each agent that does **not** read + `AGENTS.md` natively (root `CLAUDE.md` for Claude Code, and by default nothing else — Cursor, + Copilot, Codex, Gemini, VS Code and Windsurf all read `AGENTS.md` directly), and the guardrail pairs `.agents/policies/{AGENTS.md,CLAUDE.md}` and `.agents/skills/{AGENTS.md,CLAUDE.md}`, - installs the four authoring skills (`policy-init`, `validate`, `eval`, `optimize`) into `.agents/skills/`, and @@ -60,8 +62,9 @@ chock init . It installs **no policies**, and tells you so — a freshly initialised repo enforces nothing yet. Policies are content, not framework, and you choose which to adopt. -By default it targets the agents that can't read `AGENTS.md` natively: `claude`, `copilot`, and -`gemini`. Pass `--agents` to change that: +The default target set is `claude copilot gemini`. Of those, only Claude Code gets a wrapper +file: Copilot and Gemini read `AGENTS.md` natively, so naming them changes nothing on disk. +Pass `--agents` to change the set: ```bash chock init . --agents claude cursor copilot codex gemini aider @@ -101,8 +104,10 @@ git checkout -b feature/x git commit -m "on a feature branch" # ✅ allowed ``` -The same rule is also compiled into your agents' native controls, so Claude Code (for example) -is stopped *before* it runs the command — not just at commit time. +This policy's gate declares `on: [commit, push]`, so it compiles to the git hook, the CI gate +and the ambient rule — not to a pre-tool hook. A policy whose gate also declares `tool_use` +(`scan-secrets`, for one) additionally reaches your agent's native controls, stopping it +*before* it writes, rather than only at commit time. ## 5. Author your first policy @@ -112,8 +117,8 @@ Scaffold a new guard: chock new policy block-console-log ``` -This creates `.agents/policies/block-console-log/` with a manifest, a gate, an implementation stub, -and an eval suite. Edit the manifest: +This creates `.agents/policies/block-console-log/` with a `manifest.yaml` — the gate is the +`hook.gate` block inside it, not a separate file — and an `evals/suite.yaml`. Edit the manifest: ```yaml # .agents/policies/block-console-log/manifest.yaml diff --git a/docs/hooks/README.md b/docs/hooks/README.md index 80ad863..62ef461 100644 --- a/docs/hooks/README.md +++ b/docs/hooks/README.md @@ -1,6 +1,7 @@ # Hooks -Hooks are block or verify gates enforced at commit time through git hooks. +Hooks are block or verify gates. They compile to a git hook and a CI gate at commit time, and, +where the gate declares `tool_use`, to a native in-agent hook and the turn's end as well. ## Where hooks live diff --git a/docs/policies/README.md b/docs/policies/README.md index fb163af..b1a6c26 100644 --- a/docs/policies/README.md +++ b/docs/policies/README.md @@ -10,7 +10,7 @@ A policy is the umbrella term for rules, hooks, skills, workflows, and subagents | hook | `.agents/policies//` | Commit-time gate | | skill | `.agents/skills//` | Reusable skill | | workflow | `.agents/skills//` | Multi-step procedure | -| subagent | `subagents//` (via `subagent.yaml`) | Scoped helper agent | +| subagent | `.agents/skills//` (via `subagent.yaml`) | Scoped helper agent | A skill with `artifact: workflow` sequences other skills/subagents via its `SKILL.md` procedure. A skill with `skill.skill_type: code` or `hybrid` may ship committed deterministic scripts under `scripts/` and invoke them from its procedure. diff --git a/docs/registry-and-lockfile.md b/docs/registry-and-lockfile.md index d74b8c2..f60c539 100644 --- a/docs/registry-and-lockfile.md +++ b/docs/registry-and-lockfile.md @@ -30,8 +30,8 @@ This catches the classic mistake of editing a policy without rescanning. Two thi ## The lockfile -`chock.lock` is a pinned record of every installed policy. `init` creates it and -`recompile` keeps it current, so it learns about a policy the moment you copy one in and compile. +`chock.lock` is a pinned record of every installed policy. `init` creates it and `sync` +keeps it current, so it learns about a policy the moment you copy one in and compile. ```json { @@ -50,9 +50,11 @@ This catches the classic mistake of editing a policy without rescanning. Two thi } ``` -- **`managed: false`, `source: local`** — every pack, always. The framework ships no policies, so - there is no framework-owned tree to distinguish from yours: content is installed, and once - installed it is yours. +- **`managed: false`, `source: local`** — every pack after a `sync`. The framework ships no + policies, so there is no framework-owned tree to distinguish from yours: content is installed, + and once installed it is yours. `chock add` records the catalog URL, `source_ref` and + `source_commit` at the moment it fetches; the next `sync` rewrites the lockfile from what is on + disk and that provenance goes back to `local`. - **`sha256`** — a content hash of the whole pack (`.agents/policies/`) — the source you author. - **`artifacts_sha256`** — a content hash of `.chock/compiled/` — the output that actually enforces. Absent until the pack has been compiled; a lockfile written before this field @@ -98,7 +100,7 @@ attestation — worse, because it is the line you would quote as evidence. `arti that: the lockfile now pins both what you wrote and what runs. Two related checks cover the rest of the enforcement path. `chock sync --repo . --check` -proves the compiled tree still matches the manifests it was generated from, and `chock -validate` reports the same drift at commit time through the installed pre-commit hook. Both also +proves the compiled tree still matches the manifests it was generated from, and `chock check +--only verify` reports the same drift at commit time through the installed pre-commit hook. Both also compare the vendored runtimes in `.chock/bin/` — `gate.py` executes every declarative gate, so replacing its `run()` with `return 0` would otherwise disable every policy in the repo at once. diff --git a/docs/reviewer-evidence.md b/docs/reviewer-evidence.md index 27a57cd..3c7d07c 100644 --- a/docs/reviewer-evidence.md +++ b/docs/reviewer-evidence.md @@ -161,7 +161,7 @@ un-forkable.) Add a step using this repository's `action.yml`: - uses: actions/checkout@v4 with: fetch-depth: 0 -- uses: open-coder-ai/chock@v0.1.0 +- uses: open-coder-ai/chock@v0.11.0 with: command: review require --base origin/main ``` diff --git a/docs/rules/README.md b/docs/rules/README.md index 43f8b13..a2c910b 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -4,7 +4,8 @@ Rules are ambient guidance that applies to all work in a repo. ## Where rules live -- `AGENTS.md` — compiled always-on rules +- `.agents/policies/INDEX.md` — the compiled always-on rules +- `AGENTS.md` — a managed pointer block that sends the agent to `INDEX.md` - `.agents/policies//` — source rule folder ## Rule folder contents @@ -15,9 +16,12 @@ Rules are ambient guidance that applies to all work in a repo. ## How to write a rule 1. Keep the rule text to two lines or less. -2. Put examples and rationale in `docs/rules/.md` if needed. +2. Put examples and rationale in the policy's own folder if needed. 3. Validate with the Chock validator. -## Compiling into AGENTS.md +## Compiling into INDEX.md -The framework compiles selected rules into the `AGENTS.md` file. Do not hand-edit the compiled block in `AGENTS.md`; edit the source rule folder and regenerate. +The framework compiles selected rules into `.agents/policies/INDEX.md`, and keeps a short +pointer block in `AGENTS.md` between the `chock:pointer` markers. Do not hand-edit either; +edit the source rule folder and run `chock sync`. `chock check --only index` verifies both +are fresh. diff --git a/docs/runtime/README.md b/docs/runtime/README.md index 66d6273..c91a990 100644 --- a/docs/runtime/README.md +++ b/docs/runtime/README.md @@ -18,7 +18,8 @@ The agent itself performs the invocations; the manifest only declares dependenci Deterministic logic belongs in a committed script under a `code` or `hybrid` skill: - Place the script under `/scripts/`. -- Declare `scripts.entrypoint` in `manifest.yaml`. +- Declare `entry` in the `SKILL.md` frontmatter, which *is* a skill's manifest — a skill has no + separate `manifest.yaml`, and shipping both is rejected. - In `SKILL.md`, instruct the agent to run the script with the required input. - Never generate a script at runtime. - Never ship a standalone script as a deliverable. diff --git a/docs/security/README.md b/docs/security/README.md index f4a3c25..3023d1a 100644 --- a/docs/security/README.md +++ b/docs/security/README.md @@ -10,7 +10,8 @@ Chock treats all external instructions as data, never commands. This folder docu - **SEC-4** — all text surfaces (prompts, docs, eval cases) are scanned for prompt-injection patterns. - **SEC-5** — ambient rules require `trust_tier >= community` or an explicit `ambient_override`. - **SEC-6** — skills that process external content require at least one adversarial eval case. -- **SEC-7** — ambient rule blocks in `AGENTS.md` must match the compiled source `rule.text` exactly. +- **SEC-7** — the compiled ambient surface is what the policies produce: `chock check --only index` + regenerates `.agents/policies/INDEX.md` and the `AGENTS.md` pointer and fails on any diff. ## Verification diff --git a/docs/tools/README.md b/docs/tools/README.md index 616e7ea..100cee1 100644 --- a/docs/tools/README.md +++ b/docs/tools/README.md @@ -19,7 +19,8 @@ Flags: | flag | purpose | |---|---| | `--repo PATH` | repo root to check (default `.`) | -| `--only ` | comma-separated subset of `validate,verify,evals,matrix,index` | +| `--only ` | comma-separated subset of `validate,verify,evals,matrix,mechanisms,index,conflicts,baseline` | +| `--base ` | the base revision the `baseline` target compares against | | `--mode frontier-` | frontier validation profile, passed to `validate` | | `--event ` | hook event context (e.g. `commit`), passed to `validate` | diff --git a/docs/validation.md b/docs/validation.md index 4367b1d..253cfb6 100644 --- a/docs/validation.md +++ b/docs/validation.md @@ -83,7 +83,8 @@ same PR — plus one test that attacks the check and one that feeds it ordinary ## Running it in CI -The reference `.github/workflows/ci.yml` runs, on Ubuntu **and** Windows across Python 3.11/3.12: +The reference `.github/workflows/ci.yml` runs on Ubuntu across Python 3.11, 3.12 and 3.13. +Windows joins the matrix only on a `v*` tag or a manual `full_matrix` dispatch: ```bash ruff check . && ruff format --check .