Repository navigation
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…found Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he tree cca_gate_core.py holds the required-layer table, ordering, staleness and legacy rules once, stdlib only, so the commit guard, the stop hook and pipeline_state can all load it by path. The tree fingerprint copies the index with copy2: a plain copy defeated git's racy-clean check and missed same-tick edits on a loaded Windows machine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… incomplete run pipeline_state is rebuilt on cca_gate_core: record() refuses out of order layers, tool layers recorded by hand and counts that disagree with the run, and close() refuses an incomplete run. Also fixes the core fingerprint: when .claude/ is gitignored and exists, git add exits 1 if the exclude pathspec names the ignored audits path, so the fingerprint returned None after the first record. The exclude is now passed only when check-ignore says the path is not ignored; every other add failure still fails closed. tests/conftest.py tree_of follows the same rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ever .gitignore says The previous probe asked check-ignore about run-state.json, but git's exit 1 on an exclude pathspec depends on the directory. With a .gitignore of *.json the state file reads as ignored, the exclude was dropped, and the ledger, the log and the snapshots entered the tree. The fingerprint now runs git add -A and then git rm --cached -r --ignore-unmatch on .claude/audits in the same temp index, which is independent of every ignore pattern. tree_of in conftest mirrors it. One parametrized test covers four .gitignore contents. Also removes the unused _COUNT_KEYS from pipeline_state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The guard no longer embeds its own layer tables. It loads cca_gate_core.py from beside itself by path, fingerprints the tree for schema 2 runs, and refuses (exit 2) when the core is missing while a run is open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…erprint the whole repo FINGERPRINT_TIMEOUT_S now bounds the whole fingerprint (each git call gets what is left; past it the answer is None), because a hook that times out fails open. A git found anywhere inside the audited tree, or by a relative path, is refused, not only one at the run root. A run started in a subdirectory now fingerprints the whole repository and excludes its own .claude/audits by relative path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…stall hint is real main() exited 1 on an exception after a run was found, which Claude Code treats as non blocking, so the commit proceeded. It now refuses (exit 2) on any exception, keeping the specific messages for a missing core and unreadable state. fingerprint() returns None on ValueError (os.path.relpath across drives), the audits exclusion is a :(literal) pathspec, and the hint names the console script `cca-audit install`, since `python -m cca_checks.plugin.cli` has no __main__. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The commit guard only fires on git commit, so a no-fix run, or a run that stops to summarise after L5, never met a gate. The Stop hook blocks the end of the turn while a run is incomplete or still open, at most three times per run, then logs STOPPED_INCOMPLETE once and lets go. It allows on background tasks, a legacy run, an L6 BLOCKED, a missing core, and any internal failure, the opposite direction from the commit guard on purpose. The settings snippet now arms both hooks with a 60 s timeout, above the 30 s fingerprint bound, and the install warning requires both guards to be named. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uards register separately The stop guard blocked every stop on a corrupt run-state.json until Claude Code's own override. It now blocks once with the abort command and allows when stop_hook_active is set. The install check judged the pair by one settings file, so a commit guard in the user file plus a stop guard in the project file warned falsely. Each guard is now looked for across all candidate files, and the warning names whichever is missing. The --print-hook help says it arms both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ayers cca_scorecard.py --record writes L2.6 and cca_tautology_check.py verify --record writes L5.6, both with source=tool, the only source the run state accepts for those two layers. Red proofs are recorded in a follow-up commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Contract table gains the 0.4 row and the count flags for L2, L2.5 and L3. L2.6 and L5.6 are recorded by their tools, never by hand. The ledger run_id is the one printed by pipeline start. A no-fix run closes after recording L2.5, and a hunt opens its run at Step 0.4. The Codex skill checks pipeline status --require-complete before it reports. The changelog records the gated steps and the Stop hook. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The no-fix argument now names L2.6 and the close. An empty Step 2.5 list records L2.5 with zero counts instead of skipping it, and Step 5.5 says the gate owes it only when the tree changed. A hunt is told to skip the second pipeline start at Step 0.6, the Step 0.4 comment says the five gates above, and the Output Summary lists 0.4, 2.6 and 5.6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Step 2.5 heading no longer limits the layer to STANDARD and DEEP, and the opening paragraph says FAST verifies P1 only with a single fp-check and still records the layer. The adversarial panel and Step 2.5b keep their own tier labels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…only ImportError An uncaught exception exits 1, which Claude Code treats as non blocking, so a core raising RuntimeError at import let a commit through while a run was open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ing the open run
start() overwrote the state file, which re-baselined base_tree over the run's
edits (a run with no fixes recorded stopped owing L4 onward), dropped L0.4 when
--mode was omitted, reset stop_refusals, and left no trail. It now raises
RecordRefused while a state file exists, readable or not, naming `pipeline abort`.
The CLI prints {"refused": "start"} and exits 2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d, REVISE owes L5.6 Step 0.4 and the 0.4 Contract row opened a `hunt ... no-fix` run as a fixing run because Step 0.6, where --no-fix is passed, is skipped for a hunt. The Step 0.6 note now says a second start is refused. The REVISE loop re-verifies L5, L5.5 and L5.6, and Step 4 says the gate detects change from the working tree while --fixes is recorded for the report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…is still open With every layer recorded, outstanding was empty, so the check exited 0 and Codex could report while the run stayed open and kept blocking later commits. It now exits 2 with a single outstanding_to_close reason telling it to run `pipeline close`; the Codex skill says to do that and check again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t stales L5 _run_one runs pytest on the reverted pre-fix source, which wrote .pyc files; the restore put the fixed source back but left them, so the tree fingerprint after verify differed from the one L5 recorded in a repository that does not ignore __pycache__. The subprocess now runs with PYTHONDONTWRITEBYTECODE=1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 of 4 tasks
Nonbehavioural typing fix: no runtime path changes, so the red-proof rule does not apply. Each site gets an explicit None check that pyright can see. The importlib spec and loader raise ImportError with the path, close() returns None when the state cannot be loaded (as it already does when the file is absent), the gate core keeps its isinstance test on the layers mapping but binds the value first, and the two tests assert before use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
In 0.10.x the commit guard enforced four layers of
/audit-fix(L1, L2, L2.5, L6). The steps it did not check were the ones agents skipped, and one skip was worse than it looked: the fix layers were owed only whenrecord L4 --fixes Nwas called, so an agent that edited code and never recorded L4 switched off L5, L5.5 and L5.6 at once. This PR makes every step enforceable.cca_checks/plugin/hooks/cca_gate_core.py(stdlib only) holds the fingerprint, the required layer table, the order table and the completeness check.pipeline_state, the commit guard and a new Stop hook all load it by path, so the rules exist once..claude/auditsremoved whatever.gitignoresays. Every verdict from L5 onward stores the tree it judged; an edit after a verdict makes it stale.recordrefuses an out of order layer (exit 2, naming the section to re-read). L2, L2.5 and L3 take counts that must agree with each other and with the disposition ledger. L2.6 and L5.6 are recorded by their tools (--record), never by hand.--status n/ano longer satisfies a required layer,pipeline closerefuses an incomplete run, a secondpipeline startis refused, andno-fixruns now close instead of blocking the next commit.cca_stop_guard.py) refuses to end the turn while a run is open: three times per run, then it logsSTOPPED_INCOMPLETE. It allows while background subagents run, when L6 is BLOCKED, and on any internal error, so it never traps a session.cca-audit install --print-hookprints both hooks.pipeline status --require-completebefore reporting.Spec:
docs/superpowers/specs/2026-10-10-gate-every-step-design.md. Plan:docs/superpowers/plans/2026-10-10-gate-every-step.md.Checklist
pytest -qpasses, apart from the 11 semgrep tests that also fail at the based2317deon my workstation (Smart App Control blocks semgrep, WinError 4551). Here: 11 failed, 786 passed, 1 skipped of 798. Base: 11 failed, 689 passed, 1 skipped of 701. Identical failing set. CI on Ubuntu is the green run that counts.ruff check cca_checks testsis cleantests/test_gate_holes.pybuild the run state by hand with APIs that exist atd2317deand fail there in their assertion; the rest are proved by named mutations. Each receipt is in the test docstring.Known follow ups (deliberately not in this PR)
record()fingerprints between load and write, so parallel L5.5 and L5.6 records can lose an update (fails closed).python; exit 127 on a host without it is non blocking.🤖 Generated with Claude Code