Chock.sh Build your plugin

How Chock works

How Chock works

The lifecycle of a policy in a repository, from authoring to a verdict and a log line, with diagrams that cite the engine's files.

A person authors a policy once. chock compile turns it into the control for each hook point, and chock sync wires those into the repository. After that, on agents whose hooks chock sets up, the hooks check each tool call, commit and push. Each check returns a verdict and writes one line to a local decision log. The CI gate runs the same check again on each pull request.

The lifecycle, step by step

These steps follow the main path of the lifecycle diagram. Each step links the engine file that does the work, at the pinned commit:

  • Author a policy: The chock new policy <id> command writes a policy skeleton. A policy is one folder under .agents/policies/<id>/: a manifest, an optional deterministic script, and an eval suite. The policy-init skill writes one from an interview.
  • chock check: Runs the repository's checks in order (validate, verify, evals, matrix, mechanisms, index, conflicts, baseline). The --only flag picks a subset.
  • chock sync: Recompiles every installed policy, then writes each wired agent's hook file, the git hooks and INDEX.md. With --ci it also writes the CI workflow.
  • Agent hook files: One entry per hook, in each agent's own file and format. Every entry runs the same committed launcher command.
  • Launcher: Runs the first of git config chock.python, python3, python and py that runs Python 3.11+. With none it exits 2, never allow.
  • Agent runtime: The file .chock/bin/<agent>.py holds agentseam's bundle plus chock's handler. A --guard call runs the policy's script. A --gate call asks the vendored gate runner.
  • Gate runner: Evaluates a compiled gate, caps its verdict, and concludes with an exit code. It is assembled into .chock/bin/gate.py, the same runner the git hook and CI run.
  • Decision log: One line per hook decision. With repo hooks it is <repo>/.chock/log/gate-events.jsonl. With the plugin it is ~/.chock/log/<agent>/events.jsonl. It never raises, never changes the decision and never logs contents.

Diagrams

Each box cites the engine file that does the work, at a pinned commit. Each diagram has a page with a card per box.

  • Lifecycle in a repository: The repo path from end to end. You author a policy, check it and sync it into hook files. Then each tool call, commit, push and CI run goes through the hook to a verdict and one log line.
  • Emitters and hook points: Which emitter of chock compile writes each hook point, and the file chock sync installs for it.
  • Stop and resume: A person's stop on agents. It shows the files chock stop and chock stop --everywhere write, the hooks that read them, and the log lines a stop leaves.
  • The life of a policy: One policy from its manifest in chock-catalog, through chock compile and an install, to a tool call, its verdict and the decision log.

From the engine docs: docs/architecture.md

The components

ComponentPathRole
CLIsrc/chock/cli.pyOne entry point; dispatches to each activity
Scaffoldsrc/chock/scaffold/init / new / install-skills (deterministic)
Compilersrc/chock/compile/Surfaces + per-surface emitters + coverage
Validationsrc/chock/validation/Spec/schema/budget/security/eval checks
Registrysrc/chock/registry/Artifact index + content hashes
Lockfilesrc/chock/lock.pyReproducible, hash-pinned installs
Hooks installersrc/chock/hooks/Git dispatcher + policy wrappers
Gate buildsrc/chock/gate/build.pyLoads hook.gate from manifest.yaml and bakes gate.json
Vendored runnersrc/chock/gate/runner/Self-contained runtime that chock.gate.assemble builds into one file, .chock/bin/gate.py
CI installersrc/chock/scaffold/install_ci.pyWrites the GitHub Actions workflow that runs ci-gate
Policies.agents/policies/<id>/Installed from a catalog; owned by the adopter, never shipped
Specspec/Neutral format, JSON schemas, enforcement matrix

Pointer model

AGENTS.md holds a single managed pointer to .agents/policies/INDEX.md. chock sync compiles every active rule (full text), every gate, and every skill into that index, capped by index.max_tokens in .chock/config.yaml. INDEX.md is the ambient attention surface for the agent; git hooks remain the guaranteed enforcement floor. The pointer is constant so adapters never have to be rewritten when rules change.

The CLI is a package manager, not a runtime

The deterministic parts (scaffold, compile, validate, install) live in the CLI. The generative parts — authoring a bespoke policy from an interview — live in skills that the CLI installs into your own agent and that your agent runs. Chock ships no LLM of its own.

See Core Concepts for the vocabulary and Enforcement Surfaces for the coverage model in detail.

From the engine docs: docs/decision-log.md

Hash chain

Each line carries seq, its place in its file's chain, and prev, the sha256 of the line before it (that line's exact bytes, without the newline). A log's first line names 64 zeros. After a rotation the new file's first line names the rotated file's last, so the chain runs across both files. Every route writes through the same journal: runtime hooks, git hooks, the MCP gateway, chock stop and chock resume, and overrides.

A writer waits up to one second for the lock. A line it cannot chain (the lock stayed held, or the last line could not be read) is still written, with unchained naming why. A chaining failure never changes a verdict.

chock status --verify-log [--route sync|plugin] [--json]   # exit 0 intact, 1 at a break
chock status --verify-log --head <seq>:<sha256>            # confirm a head you kept is still there

It walks each log (rotated file first) and reports the first break: the file, the line, and the expected and found prev or seq. An edited, deleted, inserted or reordered line breaks the link of the line after it. History that is missing or rotated away is not a break: the walk reports chain starts at <hash> (seq N). Lines written before chaining existed are counted, then the chain starts. A line written after that by a runtime that does not chain yet (until chock sync regenerates it, or the plugin is reinstalled) is a break of kind unchained. The break names the line and says why: a runtime without chaining wrote it, or a writer timed out on the lock. An intact chain needs every runtime regenerated (chock sync, or reinstalling the plugin) once this version is installed; until then, lines from older runtimes break it.

What it shows, and what it does not:

  • It shows a change made after the fact to someone who kept an earlier head (seq and sha256, as --verify-log prints it and chock witness export carries it): --head fails once that record changes, even when every later hash was recomputed.
  • It does not stop anyone who can write the file. A local admin, or any process running as you, can rewrite the whole file and every hash, and the walk then passes. Without a kept head, the chain shows only edits that left the later links alone.
  • Deleting the newest lines, or the oldest lines of the oldest file, leaves no break; only a kept head shows the first.
  • It makes no claim about any regulation or standard.

Built from engine 5a4b6287a46e and catalog 1e60d211c1bb.