fix(trace): a gate verdict from ANY commit counted as run-status evidence (#1413) - #1414
Merged
Merged
Conversation
…ence (#1413) `trace.py` now compares the stored verdict's `commit` against HEAD, degrading to the same "unverified" NOTE a toolchain mismatch already produces. Two probes added to `evidence-self-test`. `_gate_log`'s own comment states the rule — "A verdict is attributable to (tree, HEAD, TOOLCHAIN)" — and the code checked TWO of the three: INVALID (tree/HEAD moved DURING the run) and a toolchain mismatch. The commit was never compared. FOUND BY RELYING ON IT. #1412 added an enforced requirement with a fresh `// VERIFIES:` binding, and `scripts/trace.sh check` returned PASS on a verdict taken at an earlier commit — one that predated the binding. Sabotage-measured before the fix, with a control proving the machinery fires: control : verdict commit == HEAD -> TRACE: PASS rc=0 sabotage : verdict commit := deadbeef... -> TRACE: PASS rc=0 <-- unchecked control 2: verdict toolchain := bogus -> NOTE "...run a full gate" SCOPE, NARROWLY. Inside gate.sh this was never wrong: gate.sh:203 exports GATE_LOG, so the live log is used and is attributable by construction. In CI the traceability job has no target/ and already reports run-status unverified. The hole was the STANDALONE `trace.sh check` path — the one used while AUTHORING a binding, which is when the evidence claim is first made. Sharper failure it permitted: rename a test, bind the old name, and the stale log still records that name passing, so the binding validates against a test that no longer exists. DEGRADED, NOT FATAL, deliberately: the toolchain case returns (None, reason) and the caller reports a NOTE. Matching it gives "not attributable" one behaviour and keeps an ordinary edit-then-check cycle quiet. The probes are committed rather than performed once: evidence-self-test gains "PASS from another commit" and "PASS with no commit recorded", both required to be refused, alongside the existing PASS control that stops the check being satisfied by refusing everything. Verification-objective: a stored gate verdict may only vouch for a cited test's run-status when it is attributable to the tree being checked, so that a binding cannot be validated by a run that predates it. Review: none — mirroring the toolchain branch three lines above it, with the defect sabotage-measured first and the new behaviour probed in both directions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0188ATCj6DZ9aRVQ2vSirua6
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.
trace.pynow compares the stored verdict'scommitagainst HEAD, degrading to the same "unverified" NOTE a toolchain mismatch already produces. Two probes added toevidence-self-test.The defect
_gate_log's own comment states the rule —— and the code checked two of the three:
INVALID(tree/HEAD moved during the run) and a toolchain mismatch. The commit was never compared.Found by relying on it. #1412 added an
enforcedrequirement with a fresh// VERIFIES:binding, andscripts/trace.sh checkreturned PASS on a verdict taken at an earlier commit — one that predated the binding.Sabotage-measured before the fix, with a control proving the surrounding machinery does fire:
Scope, stated narrowly
gate.shgate.sh:203exportsGATE_LOG, which short-circuits before the verdict path, so the live log is used and is attributable by constructiontarget/, so it already reportsno gate log in target/ — run-status of enforced bindings unverifiedscripts/trace.sh checkThe standalone path is the one a developer runs while authoring a binding, which is exactly when the evidence claim is first made. The sharper failure it permitted: rename a test and bind the old name — the stale log still records that name passing, so the binding validates against a test that no longer exists.
Degraded, not fatal — deliberately
The toolchain case returns
(None, reason)and the caller reports a NOTE. Matching it gives "not attributable" a single behaviour, and keeps an ordinary edit-then-check cycle quiet. Verified both ways after the fix:The probes are committed, not performed once
evidence-self-testgains "PASS from another commit" and "PASS with no commit recorded", both required to be refused — alongside the existing PASS control that stops the check being satisfied by refusing everything. The unrecorded case mirrors the toolchain one: every verdict written beforegate.shrecorded a commit looks like that, and must not be trusted by default.Verification
All eleven steps. The gate's own
trace checkstep passing is the load-bearing part here: it confirms theGATE_LOGshort-circuit means this change cannot affect the in-gate path, which is the scope claim above rather than an assumption.Verification-objective: a stored gate verdict may only vouch for a cited test's run-status when it is attributable to the tree being checked, so that a binding cannot be validated by a run that predates it.
Review: none — mirroring the toolchain branch three lines above it, with the defect sabotage-measured first and the new behaviour probed in both directions.
🤖 Generated with Claude Code
https://claude.ai/code/session_0188ATCj6DZ9aRVQ2vSirua6