Integrations¶
rulewarden's core is harness-agnostic: a diff or command goes in, a report and an exit code come out. Each adapter
maps that onto one harness's hook contract. The contracts were verified on 2026-10-14 and are recorded in
research/harness-apis.md. Defence in depth is intentional:
| Layer | When it runs | Can the agent bypass it? | Role |
|---|---|---|---|
| Agent hook (Claude Code, OpenCode, Codex, Gemini) | before each tool call | only through a misconfigured harness | immediate feedback, denies before actuation |
git hooks (pre-commit, commit-msg, pre-push) |
on commit and push | --no-verify, which the agent hook denies |
works with any agent, or none |
CI (rulewarden hook ci) |
on every pull request | no | final gate |
Setup order (read this first)¶
Agent hooks are fail-closed. Without a valid rules.lock they deny every tool call, which freezes the agent
session (this happened while building rulewarden; see DECISIONS.md ADR-010 and ADR-013). So:
rulewarden initvendors the starter packs, compiles your instruction file intorules/compiled-*.yaml, installs the git hooks, and does not install agent hooks.- A human reviews
rules/, runsrulewarden test-rules, and edits until every fixture passes. - A human runs
rulewarden init --hooks-only --lock. This locks, installs the agent hooks, and locks again so the lock also covers the hook files, which are protected paths. If the first lock fails, no agent hook is written. rulewarden doctorchecks the wiring, the lock, the audit chain and determinism. It reports aFAILif agent hooks are installed without a valid lock.
Every later change to rules/, .rulewarden/, .claude/ or the other protected paths needs a human lock.
Claude Code¶
init --hooks-only --lock merges these hooks into .claude/settings.json, keeping existing hooks:
| Event | What rulewarden does |
|---|---|
SessionStart |
records the baseline (a snapshot of the tree plus untracked-file hashes) |
PreToolUse on Bash |
checks the command against command-scope rules (--no-verify, force-push, history rewrite, rm -rf outside the repo, human-only commands) and denies it |
PreToolUse on Edit\|Write\|NotebookEdit |
computes the proposed file content, checks it as a diff, and denies the edit before anything is written |
PostToolUse |
checks the working tree against the session baseline; blocking findings go back to the agent next to the tool result |
Stop |
warning gate (ADR-011): blocks only on findings introduced in this session that the agent may fix; never blocks twice in a row; leftovers are recorded in NEEDS_HUMAN.md and the audit log |
PreToolUse and PostToolUse use onFailure: "block", so a crash or timeout of rulewarden blocks the call instead
of letting it through. Exit code 1 is fail-open in Claude Code, so the adapter always answers with exit 0 plus a JSON
decision, or exit 2.
Observed behaviour: hooks written to .claude/settings.json during a running session took effect in that session
(Claude Code 2.1.299). The docs do not promise this, which is the reason for the setup order above.
git hooks and the pre-commit framework¶
rulewarden init writes .git/hooks/pre-commit (staged diff), commit-msg (message prose), and pre-push (the
pushed range, including new branches). Each one is a two-line script that runs rulewarden hook git <name>.
With the pre-commit framework, use a local hook in .pre-commit-config.yaml:
repos:
- repo: local
hooks:
- id: rulewarden
name: rulewarden (staged changes)
entry: rulewarden hook git pre-commit
language: system
pass_filenames: false
always_run: true
stages: [pre-commit]
- id: rulewarden-commit-msg
name: rulewarden (commit message)
entry: rulewarden hook git commit-msg
language: system
stages: [commit-msg]
A .pre-commit-hooks.yaml manifest, which lets other repositories use repo: https://github.com/…/rulewarden, is
waiting on a human scope change (NEEDS_HUMAN.md H5).
CI (GitHub Actions)¶
name: rulewarden
on: pull_request
permissions:
contents: read
jobs:
gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install rulewarden==0.1.1
- run: rulewarden lock --check
- run: rulewarden hook ci "origin/${{ github.base_ref }}"
hook ci checks BASE...HEAD, prints GitHub ::error annotations, and exits 1 on blocking findings. Protect the
workflow file and rules.lock with CODEOWNERS, because the lock cannot vouch for itself without a mediating harness.
OpenCode¶
init --hooks-only --lock --opencode writes .opencode/plugins/rulewarden.js. Its tool.execute.before hook
pipes the tool call to rulewarden hook opencode and throws on a deny, which blocks the call. If the hook process
crashes or prints something that is not JSON, the plugin also throws (fail-closed). Only pre-tool checks
are mapped; the git hooks and CI cover the end of the turn.
Codex CLI and Gemini CLI¶
rulewarden hook codex and rulewarden hook gemini translate those harnesses' pre-tool JSON (Codex apply_patch
and shell; Gemini write_file, replace, run_shell_command) to the same handler. Configure them by hand, following
research/harness-apis.md. Both harnesses treat hook crashes and timeouts as fail-open, and Codex project hooks load
only for trusted projects. Treat these adapters as early feedback, and rely on the git hooks and CI as the gates.