fix(claim-evidence): refuse a check that cannot fail - #128
Merged
Conversation
At rung 4 and above, verdictFor graded a claim verified when its check exited zero and, only when an expectation was given, the output held it. Two evidence shapes passed that test without deciding anything. A check recorded without an expect value proves only that a command exited zero. A constant-emitter check prints a fixed string, so it returns the expectation it was compared against. Both now grade uncheckable, because the check did not decide the claim. They are not contradicted, because nothing contradicted the claim. A constant emitter is defined narrowly. The whole command is true or :, or the whole command is one echo or printf whose arguments hold no command substitution, no pipe, no command separator, no redirection from a file, and no variable reference. A check that reads a value still verifies. This does not catch a script that prints a hard-coded number; that needs independent re-derivation, which is rung 5. An execution that decided the claim still outranks both refusals. A missing input stays unrunnable and a nonzero exit stays contradicted. Behaviour below the threshold does not change. gradeFor returns the verdict with a note that names the refused shape. verdictFor keeps its signature and returns the verdict alone.
tangletools
approved these changes
Aug 15, 2026
tangletools
left a comment
Contributor
There was a problem hiding this comment.
✅ Auto-approved drewstone PR — 77e72461
This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.
This approval is provisional. It rests on the audit running. If the audit cannot run — for example the CLI bridge rejects it — this approval is dismissed rather than left standing, so an unrun check never reads as a passing one.
tangletools · auto-approval · reason: drewstone_author · 2026-08-15T03:04:26Z
This was referenced Aug 15, 2026
Merged
Merged
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.
The hole
verdictForgraded a claimverifiedwhen its check exited zero and — only when anexpectvalue was given — the output contained it. At rung 4 and above, where a claim asserts "a command reproduces a value", two evidence shapes passed that test without deciding anything. Both were found on a real corpus.Shape 1 — a check with no expectation.
check: true, noexpect. The command exits zero, prints nothing, and the output test is skipped because there is nothing to look for. An agent minted a rung-4 claim exactly this way (probe2: rung4 with check only), and it was auto-promoted into a shared knowledge store as a novel finding.Shape 2 — a check that prints its own expectation.
check: echo '<the expectation>'with a matchingexpect. The command returns the string it is compared against, so the comparison can only succeed. Three pages in a shared store carry this shape.The doctrine line these violate: an acceptance criterion that cannot fail is not one.
The change
At rung >=
CHECKABLE_RUNG_THRESHOLD, both shapes now gradeuncheckable— the check did not decide the claim. They are notcontradicted, because nothing contradicted the claim. The lattice already had the right verdict; the function was not reaching it.expectyieldsuncheckable: "exit code alone cannot verify a claim at this rung — record the value the check must print".uncheckablewith a note naming the shape.gradeFor(evidence, execution)is added and returns{ verdict, note }, so a grader can tell the author which shape was refused and what to record.verdictForkeeps its exact signature and returns the verdict alone, so no caller has to change.What counts as a constant emitter
The definition is mechanical and deliberately narrow. A check is a constant emitter when:
trueor:, orechoorprintfwhose arguments contain no command substitution ($(or a backtick), no pipe, no command separator, no redirection from a file, and no variable reference ($'and$"are quoting sigils, not variables).Anything else is treated as a real check.
echo "roots=$(grep -c root out.txt)"still verifies, and so doesecho $ROOTS, because both read a value from outside the command line.What this does NOT catch, and why
It cannot catch every check that cannot fail. A script that prints a hard-coded number, a command whose output an author copied into
expect, and anechobehind a shell alias are all indistinguishable from a real check at this layer. Detecting those needs an independent party to re-derive the value, which is what rung 5 means — it is not a job static inspection of a command string can do.So this refuses only the shapes that carry zero information on their face, where refusing costs the author nothing but a rewrite of the command. The limit is stated in the doc comment on
isConstantEmitterso the next reader does not mistake it for a complete defence.What is unchanged
check: 'true'at rung 3 still verifies.unrunnable, and a nonzero exit stayscontradicted.contradicted; empty output with an expectation stayssilent-check.Known asymmetry, deliberately left
assertGradeableEvidenceis the record-time gate and still acceptscheck: 'true'at rung 4; it only requires that some check exists. Grade time now refuses what record time admits. Closing that means changing a throw-behaviour contract at the recording boundary, which is a larger break than this fix, so it is named here rather than smuggled in.Proof
Every new test was run against the unmodified source first. Failures reproduce both gamed shapes:
The 12 that passed without the fix are the guards: command substitution still verifies, below-threshold behaviour is unchanged, and
unrunnable/contradictedstill outrank the new refusals. They prove the change is not a blanket tightening.With the fix, the same file is green, and so is the repository: