Write policies
Write your own policy
Create a policy in your own repository step by step: scaffold it, read its manifest and evals, validate, eval, sync, and see a commit refused. Every output is from a recorded run.
A policy is a folder under .agents/policies/<id>/ in your repository: a manifest, an eval suite and, for some kinds, a script. This page makes one with chock new, checks it, compiles it with chock sync, and shows the commit it refuses. Every command and output below is from a recorded run with the engine at the pinned commit.
Before you start
- You have installed chock. See Quickstart: repo path.
- The repository is a git repository. The run below starts in a fresh one.
From the engine docs: docs/custom-policies.md
1. Start from a template
chock new policy <id> --kind {content_regex|guard|script|rule|skill} [--root <folder>]
| Kind | What it does | Files besides manifest.yaml and evals/suite.yaml |
|---|---|---|
content_regex | Refuses an added line matching a pattern, at commit and on the agent's writes | — |
guard | Refuses or asks about a shell command before the agent runs it | implementations/<id>.py |
script | Runs your script on the files a commit or write changes; its exit code decides | implementations/check.py |
rule | Text the agent reads in its context; nothing enforces it | — |
skill | A skill the agent loads when a request matches it | skill/body.md |
Outside a catalog (a folder with no registry.yaml) the id gains the reserved my- prefix, so it never collides with a catalog id. The policy lands in <folder>/.agents/policies/<id>/. Each template validates and passes its own evals as written; edit it, then check it:
chock check --repo <folder> --only validate,evals
A rule, guard or skill puts text in the agent's context, which SEC-5 allows below trust_tier: community only with ambient_override: true and an ambient_override_reason. The templates set the reason to "authored by the installing user".
Write a policy, step by step
A real run, recorded by python -m sitegen.writedoc with the engine at the pinned commit. The steps of data/write-policy.yaml ran in order, offline, in one fresh git repository at /work/demo, made with git init -b main and a git user name and email set, with HOME at /home/demo. Those paths stand for the throwaway folders the run used, and <time> stands for a timestamp. Output is stdout and stderr together.
Step 1, exit code 0. Set up chock in the repository: the git hook dispatchers and the authoring skills.
$ chock init .
Installed pre-commit dispatcher to /work/demo/.git/hooks/pre-commit
No git-pre-commit.sh policies found; pre-commit dispatcher unchanged
Installed pre-merge-commit dispatcher to /work/demo/.git/hooks/pre-merge-commit
No git-pre-commit.sh policies found; pre-merge-commit dispatcher unchanged
Installed pre-push dispatcher to /work/demo/.git/hooks/pre-push
No git-pre-push.sh policies found; pre-push dispatcher unchanged
Installed commit-msg dispatcher to /work/demo/.git/hooks/commit-msg
No git-commit-msg.sh policies found; commit-msg dispatcher unchanged
Registered SessionStart arm hook in .claude/settings.json
INDEX.md: ~257 tokens (chars/4, max 2000)
INDEX.md: ~257 tokens (chars/4, max 2000)
Implementation registered at /work/demo/.git/hooks/pre-commit.d/99-chock-validate
[PASS] All checks passed.
Initialized Chock in /work/demo
Agents: AGENTS.md + claude, copilot, gemini
Skills: eval, optimize, policy-init, validate (refresh with `chock install-skills .`)
Policies: none. This repo enforces nothing yet.
1. copy a policy folder from https://github.com/open-coder-ai/chock-catalog (base/<id>/) into .agents/policies/<id>/
2. chock sync --repo .
Verify anytime with: chock check --only verify
Step 2, exit code 0. Scaffold a policy from the tested content_regex template. Outside a catalog the id gains the my- prefix. Chock prints the two steps to run next.
$ chock new policy block-marker --kind content_regex
INDEX.md: ~336 tokens (chars/4, max 2000)
Created policy 'my-block-marker' at /work/demo/.agents
Next: python -m chock sync --repo .
python -m chock check --repo .
Step 3, exit code 0. The manifest it wrote: the policy's identity and a content_regex gate that refuses an added line matching content_pattern, at commit and on the agent's file writes.
$ cat .agents/policies/my-block-marker/manifest.yaml
id: my-block-marker
name: "Block Marker"
version: "0.1.0"
description: >
Refuses an added line that matches content_pattern, at commit and when the agent
writes a file. The example refuses a DO-NOT-COMMIT marker. Edit content_pattern
and the message; keep the pattern from matching its own text.
artifact: hook
enforcement: block
effects:
- read_only
approval:
required: false
hook:
gate:
kind: content_regex
"on": [commit, tool_use]
action: block
message: >
my-block-marker: this change adds a line with a DO-NOT-COMMIT marker. Remove the marker
or the line before committing.
params:
scan: added_lines
# [ ] matches a space, so this pattern does not match its own text.
content_pattern: 'DO[ ]NOT[ ]COMMIT'
provenance:
author: "me"
created_at: "<time>"
source_repo: "local"
license: "proprietary"
trust_tier: "sandbox"
lifecycle:
status: draft
reviewed_by: []
security:
content_instructions: never-obey
Step 4, exit code 0. The two files it wrote: the manifest and the eval suite. The suite holds four cases, each with an execute block that chock replays against the gate.
$ git status --short --untracked-files=all .agents/policies/my-block-marker
?? .agents/policies/my-block-marker/evals/suite.yaml
?? .agents/policies/my-block-marker/manifest.yaml
Step 5, exit code 0. Validate every policy folder against the engine's schemas and rules. It passes. The warning is about the id's first word.
$ chock validate
[PASS] All checks passed.
Step 6, exit code 0. Run the policy's evals. Each case passes.
$ chock eval my-block-marker
my-block-marker [deterministic]
PASS tc-001 authored my-block-marker: this change adds a line with a DO-NOT-COMMIT marke…
PASS tc-002 authored gate exit 0 [gate derived from manifest; policy not compiled]
PASS tc-003 authored my-block-marker: this change adds a line with a DO-NOT-COMMIT marke…
PASS tc-004 authored gate exit 0 [gate derived from manifest; policy not compiled]
score 1.00
1 policies: 4 pass
Step 7, exit code 0. Compile the policy and wire it: the git hooks, and the hook files of the agents chock wires in this repository. The last lines say how each agent enforces it.
$ chock sync --repo .
Installed pre-commit dispatcher to /work/demo/.git/hooks/pre-commit
Registered 1 pre-commit policy implementation(s)
Installed pre-merge-commit dispatcher to /work/demo/.git/hooks/pre-merge-commit
Registered 1 pre-merge-commit policy implementation(s)
Installed pre-push dispatcher to /work/demo/.git/hooks/pre-push
No git-pre-push.sh policies found; pre-push dispatcher unchanged
Installed commit-msg dispatcher to /work/demo/.git/hooks/commit-msg
No git-commit-msg.sh policies found; commit-msg dispatcher unchanged
Registered 2 PreToolUse/Stop hook(s) in .claude/settings.json
claude_code: pre-tool: block+rewrite, best-effort, fail mode open (agentseam basis live-run, recorded 2026-09-07)
claude_code: Untested: chock has not witnessed its hooks run in this client; the wiring follows agentseam's record. What you see there is wanted as evidence: open-coder-ai/agentseam issues labelled 'evidence wanted'.
Registered 2 hook entr(y/ies) in .gemini/settings.json
gemini_cli: pre-tool: block+rewrite, best-effort, fail mode open (agentseam basis vendor-source, recorded 2026-08-28)
gemini_cli: Untested: chock has not witnessed its hooks run in this client; the wiring follows agentseam's record. What you see there is wanted as evidence: open-coder-ai/agentseam issues labelled 'evidence wanted'.
Registered 1 agent hook(s) in .github/hooks/chock.json
vscode_copilot: pre-tool: block+rewrite, best-effort, fail mode open (agentseam basis live-run-partial, recorded 2026-09-28)
INDEX.md: ~336 tokens (chars/4, max 2000)
Recompiled 1 policies
my-block-marker:
claude: enforced-at-commit + best-effort at tool use (live-run)
copilot: enforced-at-commit + best-effort at tool use (live-run-partial)
gemini: enforced-at-commit + best-effort at tool use (vendor-source)
Step 8, exit code 0. Run every check, ending with one result line.
$ chock check --repo .
== validate
[PASS] All checks passed.
== verify lockfile
verify: all packs match lockfile
== policy evals
my-block-marker [deterministic]
PASS tc-001 authored my-block-marker: this change adds a line with a DO-NOT-COMMIT marke…
PASS tc-002 authored gate exit 0
PASS tc-003 authored my-block-marker: this change adds a line with a DO-NOT-COMMIT marke…
PASS tc-004 authored gate exit 0
score 1.00
5 policies: 4 pass, 44 skipped
44 case(s) have no executable form; they are agent-mode material (tier 3).
== enforcement matrix (skipped: no spec/enforcement-matrix.md in this repo)
== matrix mechanisms (skipped: no spec/enforcement-matrix.md in this repo)
== index freshness
== ambient conflicts
[PASS] All checks passed.
== policy baseline (skipped: no --base given; CI passes the pull request's base branch)
== result: PASS
Step 9, exit code 0. Stage what chock wrote.
$ git add -A
Step 10, exit code 0. Commit. Chock's pre-commit hook runs, and the commit lands.
$ git commit -q -m 'Add my-block-marker'
== validate
[PASS] All checks passed.
== result: PASS
Step 11, exit code 0. Write a file with a line the policy refuses.
$ echo 'x = 1 # DO NOT COMMIT' > notes.py
Step 12, exit code 0. Stage it.
$ git add notes.py
Step 13, exit code 1. Chock refuses the commit with the policy's message, and git exits 1.
$ git commit -q -m 'Add notes'
my-block-marker: this change adds a line with a DO-NOT-COMMIT marker. Remove the marker or the line before committing.
- notes.py: content pattern
Step 14, exit code 0. Remove the marker.
$ echo 'x = 1' > notes.py
Step 15, exit code 0. Stage it again.
$ git add notes.py
Step 16, exit code 0. The commit lands.
$ git commit -q -m 'Add notes'
== validate
[PASS] All checks passed.
== result: PASS
Make it refuse your own text
The template refuses one example marker. To make it yours:
- In
manifest.yaml, sethook.gate.params.content_patternto your regular expression andhook.gate.messageto the line the agent sees. The template's own description says to keep the pattern from matching its own text:.agents/policies/INDEX.mdquotes the message, and a commit also scans that file. - In
evals/suite.yaml, change each case'sexecute.filesorexecute.writesso theblockcases hold text your pattern matches and theallowcases do not. - Run the same three commands again:
chock eval my-<id>
chock sync --repo .
chock check --repo .
Manifest reference and Evals reference list every field. The files chock new copies are the engine's manifest template and eval suite template.
Next
- Make one while you work, from your agent or in the browser: On the fly.
- Offer it to everyone: Add a policy to the catalog.
- Every option of chock new, chock validate, chock eval, chock sync and chock check.
Built from engine 5a4b6287a46e and catalog 1e60d211c1bb.