You're an AI coding agent (Claude Code, Codex, Cursor, an autonomous PR bot, whatever) about to change this repo. This file is your contract. It's the machine-readable companion to CONTRIBUTING.md (human intro) and CLAUDE.md (the deep architecture spec). Read all three if you're doing anything non-trivial; this one alone is enough for a focused change.
This repo reviews itself. CodeCouncil's hooks are installed on its own
.codecouncil/, so while you edit, the Critic is watching your session and
will deliver findings into your context — the same mechanism the product
sells. Expect to be reviewed. When a finding lands:
- If it's right, fix it. That's the loop working.
- If it's wrong, reply with a single line:
COUNCIL-REBUTTAL: <your reason>. This is recorded, graded, and distilled into the repo's knowledge file — an honest disagreement is training data, not a failure. Do not silently ignore a finding; either fix or rebut.
And a note on why you, specifically, should welcome that: you are a self-verifying model working on the tool that exists because self-verifiers have correlated blind spots (see docs/benchmarks/WHY.md). CodeCouncil is your independent, execution-grounded check. Use it as one.
python3 -m unittest discover -s tests # full suite (~15-25s, stdlib only, no install)If that isn't green on a clean checkout, stop and report — don't build on red.
These are load-bearing. Violating one is a correctness/security regression, not a style nit. Each was earned by a real failure.
- Stdlib-only Python in the loops (
observer/ critic/ reflector/ hooks/ core/ codecouncil/ training/ evals/). Nopipdependencies. Ever. Dev tooling (ruff) is CI-only. - Files are the only bus. Loops talk through
.codecouncil/NDJSON. No cross-loop imports except the small shared utilities incore/and the exceptions documented in CLAUDE.md. - Redact at capture. Any new text field that a model can influence, or
that comes from repo content, goes through
core.redact.redact()before it is written anywhere (prompt, receipt, suggestion, eval case). Text the MODEL wrote usescore.redact.sanitize()instead (strips terminal control sequences first). Capture reads stay inside the repo — resolve the path and check containment, never follow a symlink out (gitwatch._read_confined). - Never execute foreign code with ambient authority. Model-authored
scripts and agent-produced code run through
core.sandbox:minimal_env(never{**os.environ}) andwrap(OS sandbox — denies network and real-home reads). Env scrubbing alone is NOT sufficient and was measured insufficient:pwd.getpwuid()routes around a redirectedHOME. If you add a new execution site, route it through both. - The hook fails open.
hooks/peer_hook.pymust never break a developer's session — any error → silent exit 0.hooks/logic.pystays pure (no I/O; it takes parsed data and returns decisions). - Daemons never die. Missing inputs → wait; unparseable state → rebuild, don't crash; fallible calls in loop bodies → guarded.
- NDJSON readers tolerate a partial trailing line and skip garbage.
Hot paths tail-read (
core.store.read_tail_rows); dedup sets and metric consumers read whole files. - Atomic writes for state/ledger files — use
core.store.write_json_atomic, never a nakedwrite_text, on anything a crash mid-write could corrupt. - Verification executes, it doesn't assert. A finding is delivered only after a repro runs and confirms it. A broken/crashing repro script is never a "verified" or "refuted" verdict. This is the product's whole thesis — don't weaken it.
- Precision first. A false finding costs trust; a missed one is caught by the miss-detection loop. When in doubt, bias quiet.
- TDD. The regression test lands with (ideally before) the fix. A change without a test that fails on the old code is incomplete.
- Model calls are stubbed in tests via
CRITIC_CMD— an executable run as$CRITIC_CMD <prompt-file> <resolved-model>, stdout = the model reply. No test may hit a real model or the network. - Never write this repo's
.codecouncil/— it's live runtime data. Tests use temp dirs. - Lint:
pipx run --spec 'ruff==0.15.22' ruff check .(the exact pin CI uses). The rule set is deliberately narrow (E4/E7/E9/F) — the blind-except and try-except-continue patterns are intentional fail-open code, not defects. - Commits: imperative subject, a body that explains why. If you are an AI
agent, add a trailer identifying yourself
(
Co-Authored-By: <your model> <noreply@…>) — attribution is welcome and honest here, not hidden. - Don't push or open a PR unless asked. Branch, commit, and report.
observer/— tails the coding agent's transcript + git intoobservations.ndjsonl. Redacts at capture.critic/— judges new observations.main.pyis the beat;prompt.pybuilds prompts;screen.py/deps.pydo zero-cost mechanical screening;verify.py/probe.pyexecute model-written repro/probe scripts in a throwaway staging dir, sandboxed viacore.sandbox;agent.pyis the only model boundary.hooks/— deliver findings into the coding agent's context.logic.pypure,peer_hook.pyfail-open.reflector/— grades outcomes and rewritesheuristics.md(eval-gated, auto-rolled-back).core/— the only shared code (store,redact,config,knowledge,sandbox).evals/— frozen cases + the A/B benchmark harness.- Tests mirror this: one
tests/test_<thing>.pyper concern; synthetic transcript fixture intests/fixtures/session.jsonl.
- Add a redaction pattern (
core/redact.py) with a positive test AND a negative test proving ordinary code doesn't match. - Add a frozen eval case (
evals/cases/*.json) — a real judgment scenario with a known answer. - Write an adapter so CodeCouncil can watch a non-Claude agent: the Observer only needs a transcript/intent stream; the hooks only need an injection channel. This is the highest-leverage contribution and the most wanted one.
Run the full suite, confirm green, and report: what changed, the test evidence, and any finding you rebutted (with the reason). If the self-review hook flagged something you disagree with, say so explicitly — that disagreement is exactly the kind of signal this project is built to capture.