Mine the rules your agents’ history supports, approve the ones you mean, and have Claude Code check them before every consequential tool call. About fifteen minutes from clone to a guarded session.
git clone https://github.com/cognitive-fab/polyx cd polyx && npm install
A synthetic customer-service corpus and its written policy ship with the package. Mine it, then compare what the agents did with what the policy says.
The report has three parts: tribal knowledge (followed, never written), confirmed, and the compliance gap (written, not followed).
node bin/polyx.mjs mine synthetic node bin/polyx.mjs diff synthetic
--out report.html writes the same report as a page for a reviewer who never opens a terminal.
Claude Code deletes transcripts older than cleanupPeriodDays, which is 30 days unless you set it. Those transcripts are the corpus. Raise the setting first.
Then copy your sessions into the corpus. A re-run only adds: new sessions are copied, sessions that grew are updated, and a session Claude Code has since deleted stays in the copy, which is by then the only one. Re-run it at least once per retention period.
# what it would add; writes nothing
node corpora/link-cc.mjs --dry-run
node corpora/link-cc.mjsOn our own machine, 100 of the first 141 sessions were already gone from Claude Code by the second pass. The corpus still has them.
Each candidate rule arrives with its support, the sessions that satisfy it, the sessions that contradict it, and whose practice it is. Rules are mined per project.
Same corpus, same alphabet, same thresholds: the same rule set, every time. There is no model call anywhere in mining.
node bin/polyx.mjs mine cc
A real candidate from one of our projects. Expect thousands, most of them coincidences. Shortlist the ones that guard something hard to undo, such as a push, a delete or a discard, and that several projects agree on.
The review page shows one rule per screen, in plain language, with the evidence on both sides. Mark it real, not real or narrowed. Nothing is served until someone has said real.
Rules are mined per project. Real everywhere (E) applies one verdict to every project whose own history keeps the rule, and lists the projects where it would only be borrowed.
node bin/polyx.mjs review cc serve
# or from the terminal
node bin/polyx.mjs review cc mark <rule-id> real --everywhereVerdicts persist across re-mining. review cc pace reports the median seconds per verdict, because a reviewer’s time is the real constraint. On 198 of our own sessions, twelve approved rules became 96 project-level rules in service; see the list.
One call, made before a consequential action: here is the episode so far, here is what I am about to do. The answer is one of four verdicts.
warn: a rule says something should have happened first. recommend: a rule suggests an action. clear: every rule on this action holds. abstain: no rule applies, or one does but a fact is unknown, and it names the fact.
node bin/polyx.mjs serve cc --port 7777
Add the hook to .claude/settings.json in the project you want guarded, or ~/.claude/settings.json for all of them. Make the path absolute.
On warn the tool does not run, and the model is told why in the rule’s own words, with its support. If the advisor is down, the hook lets the call through and says so. POLYX_HOOK_STRICT=1 blocks instead.
{ "hooks": { "PreToolUse": [ {
"matcher": "Bash|Edit|Write|MultiEdit|NotebookEdit",
"hooks": [ { "type": "command", "timeout": 5,
"command": "node /path/to/polyx/examples/claude-code-hook/polyx-hook.mjs" } ]
} ] } }What the model saw in one of our sessions. Read-only tools are left out of the matcher: they are never a decision point.
The integration is the same three steps everywhere: type the trajectory so far, post what the agent is considering, act on the verdict. What changes per framework is the alphabet, and for Claude Code that ships.
examples/claude-code-hook/. The test suite runs it exactly as Claude Code does, so the page cannot drift from the code.
| Agent SDK | hooks.PreToolUse |
| LangGraph | interrupt_before |
| OpenAI Agents | tool guardrail |
| Vercel AI SDK | tool middleware |
Jev, TypeSafe’s System One model, answers a declared yes/no question about a text. polyx turns a confident yes into a fact such as obs.states_double_charge, and anything less into unknown, never false.
The walkthrough runs in ten minutes with no key, using a recorded answer file. With a key, the order is: declare, dry-run and read every payload, label about 60 examples, derive the bands, then annotate once.
node bin/polyx.mjs annotate synthetic-observable --import node_modules/@cognitive-fab/polyx-lens/fixtures/synthetic-observable/synthetic-observable@1.jsonl node bin/polyx.mjs mine synthetic-observable --threshold minInstances=3 node bin/polyx.mjs review synthetic-observable mark 2f663a4cec907cc9 real node bin/polyx.mjs coverage synthetic-observable
Full walkthrough: examples/jev-observation. With a key: POLYX_JEV_KEY and annotate <corpus> --dry-run first.
Write your policy as a clause set in YAML, one rule per clause. Clauses polyx cannot state as one action preceding another are marked with the reason and reported, not dropped.
For a worked example on public data, see polyx-bench.
node bin/polyx.mjs diff cc --policy my-policy.yaml --out report.html