Try it live
This is the actual cogpin.py that ships — no demo reimplementation, no server. It
runs in your browser (CPython compiled to WebAssembly), reading the diff you type as the
same DiffFacts the engine reads from git. Edit the policy on the left and the change on
the right, or hit a preset. The verdict splits into two layers: the agent layer (what
Claude Code denies in real time as the agent works — bypassable) and the change layer
(the un-bypassable pre-push + CI gate).
A/M/D path file · + line added · - line removed · $ cmd command · @ branch · write> path · commit> msg
What you’re looking at
Section titled “What you’re looking at”The left pane is a cogpin.toml policy. The right pane is a tiny stand-in for a diff —
because the engine reads facts, not raw text, you describe the change as facts:
| you type | the fact |
|---|---|
A path / M path / D path |
a file added / modified / deleted |
+ <code> |
an added line in the current file |
- <code> |
a removed line in the current file |
$ <command> |
a command the agent is about to run (agent layer) |
@ <branch> |
the current branch (for forbid_commit_on_branch) |
write> <path> |
a Write/Edit target (for self_protect) |
commit> <message> |
a commit message (require_message_pattern / forbid_in_message) |
Hit run and the engine reports two layers: agent layer denials (what the
PreToolUse hook would block in real time) and change layer findings (what pre-push +
CI would catch).
Things worth trying
Section titled “Things worth trying”strip an assertremoves anassertline undertests/→forbid_removalblocks. The added-line scanners of every other tool are blind to a deletion; this is the-side of the diff.delete the testremoves the whole test file →forbid_deleteblocks. Add a secondA tests/...line and it passes —unless_paired_addreads that as a rename/reorg.commit to mainis denied at the agent layer — change@ mainto@ feature/xand it’s allowed.scope creeptouches a path outside the allowlist →scope_lockblocks. The structural inverse of a denylist: it gates “any file not in the allow-set.”lower coverageflipsfail_under85→75 →numeric_floorblocks. If you only scan the added lines that’s byte-identical to a raise; pairing the number across the removed and added lines is the whole point.edit the gateis awrite> cogpin.toml— denied at the agent layer byself_protect, so the agent can’t loosen its own gate mid-session.an un-blockable ruleloads a policy that tries to hard-block on ajudge(an LLM-judge the agent could sway). The engine refuses to load it —severity="block"requireskind="fact". That error is the moat, enforced at parse time. Flip itsseveritytowarnand it loads.
Everything you can do here, python3 cogpin.py check does in CI — the only difference is
CI reads the facts from real git instead of your textarea.