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). - 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;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).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.