feat(guardian): tell the skeptic when no checker ran, and which verdict that takes (#407) - #410
Conversation
…ct that takes (#407) _evidence_section(None) returned the empty string, and the emptiness was the defect. Measuring #401 needed a control arm judging the same 24 findings with no checker output, and in it 6 of 24 rationales cited mypy or ruff anyway — every one to confirm a false claim, on a conditional the model could not check: "and if mypy --strict is configured to disallow Any explicitly, it would be flagged." Told nothing about checkers, it supplied its own. Two prompts were measured on that arm, 24 skeptic calls each, no finder calls. A prohibition failed. "Do not rest a verdict on what a checker would report" left 5 of 6 appeals standing — inside the 1-to-4 noise floor of two draws of the original — and the language got worse: the conditional became a flat claim about "the project's mandatory mypy --strict gate", which does not exist over that file, since make type-check runs mypy src and pyproject excludes scripts. Told what not to do, the model complied in form and confirmed anyway, with more confidence than before. A directive worked. Naming the verdict such a claim takes moved five of the six to 'uncertain', all on cited-checker rows, and finding 0 now states its position instead of inventing a gate: "Without access to the project's mypy configuration or output, it is not possible to confirm." The recall guard held — finding 4, the one substantive finding of the 24, stayed confirmed with the correct reasoning. Unregistered: findings 21 and 22, the two the RFC called beyond any tool because they were wrong about intent rather than behaviour, were refuted as "a deliberate guardrail... intended behavior, not a defect." 'uncertain' rather than 'refuted' because it is the true state: the checker neither agreed nor disagreed, because it never ran. Refuting an unadjudicable claim is the recall regression this component exists to avoid. What this does not buy, stated in the data README rather than implied: uncertain findings are kept, discounted x0.9 as a ranking signal, and render_review_body takes threshold=0 — so all five are still posted and only their order changes. This is an epistemic fix, not a noise fix. Suppression is a separate knob and needs its own evidence. skeptic.py is inside the review-fingerprint closure, so this re-mints every reviewer identity, by design (#375). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a directive to the skeptic prompt when no checker output is available, instructing the model to mark findings that rely entirely on checker output as 'uncertain' rather than 'confirmed' or 'refuted'. This addresses an issue where the model would assume checker configurations when none were provided. Feedback on the pull request includes improving the reproducibility of the command in the README by using a repository-root-relative path, and making the test assertion for the prompt's text more robust by normalizing whitespace.
The reproduction command in the README did not run. It invoked the script from the repository root while giving --recording a path relative to the experiment directory, so the one section whose entire value is that it works raised FileNotFoundError. Verified both ways after fixing: the root-relative path now gets as far as the missing API key, the old one still cannot find the file. The prompt assertion accepted two spellings with an `or`, hedging against wherever the line wrap happened to fall. That is a test passing because it was given two chances rather than because it knows the answer. Whitespace is normalised instead, and the neighbouring assertions now match whole clauses rather than fragments that could survive a rewrite of the sentence around them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request implements a directive instructing the skeptic model to mark findings that rely entirely on checker reports as 'uncertain' when no checker output is available. This change is documented in a new experiment README, supported by JSONL data files, and verified with updated unit tests. As there are no review comments, I have no feedback to provide.



Closes #407.
The defect
_evidence_section(None)returned the empty string, and the emptiness was the defect. Measuring #401 needed a control arm judging the same 24 findings with no checker output at all — and in it 6 of 24 rationales citedmypyorruffanyway, every one to confirm a false claim, on a conditional the model could not check:Told nothing about checkers, the skeptic supplied its own — the same unverified move the RFC faults the finder for, one level up.
Two prompts, both measured
The replay control arm is this configuration, so each attempt costs 24 skeptic calls and no finder call. Registered before running: primary — how many of the six checker-appeals stop being confirmed on that basis; recall guard — finding 4, the one substantive finding of the 24, must stay confirmed; noise floor — two draws of the original prompt moved 4 and 1 verdicts, so anything smaller is not a result.
Attempt 1: a prohibition. Failed.
Citations 6/24 → 5/24, five of six still confirmed. Inside the noise floor, and the text got worse — the conditional became a flat assertion:
There is no such gate over that file:
make type-checkrunsmypy srcandpyproject.tomlexcludesscripts. Told what not to do, the model complied in form and confirmed anyway, with more confidence than before.Attempt 2: a directive. Worked.
sys.path"violates type safety"Five of six moved, all one direction, all on cited-checker rows — outside the noise floor and mechanistically legible. Finding 0 now states its position instead of inventing a gate:
The recall guard held with the correct reasoning:
CalledProcessErroris raised and not caught, which is the defect actually fixed in #399.Unregistered gain: findings 21 and 22 were refuted. Those are the two the RFC singled out as beyond any tool — wrong about intent, not behaviour. The model got there unaided: "a deliberate guardrail … its failure due to a diminished corpus is intended behavior, not a defect." One draw, not predicted; recorded as an observation.
uncertainrather thanrefutedbecause it is the true state: the checker neither agreed nor disagreed, because it never ran. Refuting an unadjudicable claim is the recall regression this component exists to avoid.What this does not buy
uncertainis kept.apply_judgementsmultiplies confidence by 0.9 as a ranking signal and nothing more, andrender_review_bodytakesthreshold: int = 0. All five findings are still posted; only their order changes. This is an epistemic fix, not a noise fix — suppression is a separate knob (impact_threshold, which #246 §3.5 records as shipping inert) and deserves its own evidence rather than being folded in here.One draw per arm; recall denominator of one. The primary effect is well outside the noise floor, nothing else here is.
Note
skeptic.pyis inside the review-fingerprint closure (43 modules), so this re-mints every reviewer identity — by design (#375).Data, including the failed first attempt, under
benchmarks/guardian/experiments/407-no-checker-appeal/.🤖 Generated with Claude Code