Repository navigation
Value tag: count behavioural assertions on literal input - #44
solvyxtech wants to merge 2 commits into
Conversation
…t-bracket parse
assertsValue (the `value` tag on sealed checks, and the post-work audit's
V-no-value gate) refused checks that call the work on literal input and
assert the outcome. Rule 5 now takes:
- `is None` / `is not None` on a call into the work with a literal
argument, and a literal `in` / `not in` its answer, under `assert`,
`sys.exit(0 if ... else 1)` or `if ...: sys.exit(1)`;
- a specific exception: `try: f('x')` / `except KeyError:` where only the
path that did not raise exits non-zero, `pytest.raises(E)`,
`assertRaises(E, ...)`, `assert.throws(...)`; never `except Exception`
or a handler that only prints;
- `grep` (plain, -q, -x; never -v, -c, -l) of a literal at the end of a
pipeline that runs a program (python3, node, make, curl, ./tool, ...);
`head -1 out.csv | grep` and `git log | grep` still do not count.
Rule 1 read a lone `=` as an assignment whenever a `;` or newline sat
before it, including inside `[ "$(python3 -c "a; b")" = "2" ]`. Such a
bracket is now read shell-aware, its operands as whole shell words, and
`$'...'` is a literal. A type probe compared that way (`print(type(x).__name__)"
= "int"`) still does not count.
Not widened: a pattern the work wrote, tried on the check's own strings
(`re.search(p, '10.0.0.1 2024-01-05')`). Replayed over 2026-10-07's lanes,
4 of the 7 runs such checks alone would have verified were grader failures,
all regex tasks whose judge examples missed the grader's case.
Structural checks (exists, isinstance, hasattr, len > 0), the truthiness of
an arbitrary call, and timing bounds keep no value tag.
…d rule against new
Collects every check command from today's lanes (sealed checks, released
checks, post-work audit checks in the journals and in audit_replay.py's
caches), tags each with src/tiers.ts at the base and in the working tree
(loaded from the .ts source), and reports:
1. what the old rule refuses, by category, with the new rule's verdict;
2. runs whose only blocker was "no check that ran the work asserted an
expected value" and that the new tag flips to verified, by grader
verdict;
3. with --audit-gates, audit checks dropped with V-no-value that the new
rule tags, run through the remaining gates in a bench container.
As shipped: 677 refused commands, 77 now tagged; 8 sealed-check runs flip
to verified, 8 grader-passed, 0 failed; 7 audit runs flip to
verified-audit, 7 passed, 0 failed.
solvyxtech
left a comment
There was a problem hiding this comment.
Verdict: changes requested (2 blocking, 4 non-blocking). Review only, nothing merged. Posted as a comment review because GitHub won't let this account request changes on its own PR.
The direction is right, and the replay is the right way to decide it. Withdrawing the "pattern the work wrote" form after the 4-of-7 grader failures was a good call, and the shell-aware bracket read fixes a real miss. Pairing rules are clean: no competitor names in the added lines (the only claude/ strings are the default --base branch ref in bench/local/value_replay.py), no subscription backend, nothing touching fine-tune data, and the branch goes claude/value-classifier into claude/post-work-audit.
Tested against 2ed2838: npm run typecheck is clean, test/tiers.test.ts passes 76/76, and the full suite passes 2132/2132 with ~/.local/bin off PATH. (On this box, Node 20.19.2, the runner still exited 1 afterwards because the coverage reporter threw Cannot read properties of undefined (reading 'line'). That looks local to the box rather than caused by this PR.) CI did not run: typecheck-and-test was never started because of the GitHub Actions billing or spending-limit block on the account, so the red check isn't a code failure.
Every probe below was run against the built assertsValue on this branch and on claude/post-work-audit. On the base branch all of them return false, and on this branch they all return true.
Blocking
1. A pipeline negated with ! gets the value tag, so "this output does not contain X" counts as asserting a value. Confidence: high (reproduced on the function).
- Where:
src/tiers.ts:243(grepsRunOutput) andsrc/tiers.ts:252. The-vexclusion only looks at grep's own flags. The leading!is stripped bystageProgramto find the runner, but nothing checks whether it negates the whole pipeline. - Scenario:
! python3 tool.py 2>&1 | grep -q Tracebackis tagged value.cannotFail,swallowsExitandrunnerPipedAwayall returnnullfor it, so it gets through L16 too. Take a task like "fix the crash in tool.py". Before the work the tool crashes, so the check fails and counts as discriminating. A worker that wrapsmain()intry/except: pass, or makes the tool print nothing, then passes it, and the run becomes "verified" on a check that only proves the tool printed no traceback. - Why it matters: this is exactly the
-vcase the PR says it excludes, just spelled differently. Before this PR the same run stopped at "no check asserted an expected value". - Fix: refuse rule 5c when the top-level command starts with
!, or when it is followed by&& exit 1,&& false, orthen exit 1(the "grep finds it, so fail" shapes). Add! python3 tool.py | grep -q TracebacktonotBehaviour.
2. is not None and a literal not in the result are weaker than truthiness, but they count while truthiness doesn't. Confidence: medium (reproduced on the function. The replay found no grader failure from these, but the sample of flips is 8 runs).
- Where:
src/tiers.ts:411(member, which allowsnot in) andsrc/tiers.ts:413(none, which allowsis not None). - Scenario:
assert pricing.best_coupon('item1', 1) is not Noneandassert '!' not in slug.slugify('Hello!')are both tagged. Before the work the function doesn't exist, so both fail and count as discriminating. A wrong implementation that returns"",0,Falseor[]passes both. Meanwhileassert tool.ok('x'), which at least rules outFalseand"", is correctly refused as "truthiness of an arbitrary call". The rule's own principle ("a check counted wrongly costs the word its meaning") points the other way here. - Fix: keep
is Noneand positivein, which name a specific value. Countis not Noneandnot inonly when they sit in anandwith a conjunct that already asserts a value. Otherwise drop them. Move the existingis not Noneexample inbehaviourtonotBehaviour, or pair it with a value.
Non-blocking
3. A runner that only reads a file counts, which goes against the stated rule that head -1 out.csv | grep doesn't. Confidence: medium (reproduced on the function).
- Where:
src/tiers.ts:240(RUNNER) andsrc/tiers.ts:258. - Scenario:
python3 -c "print(open('out.csv').read())" | grep -q 'id,name',bash -c 'head -1 out.csv' | grep -q 'id,name', andcat out.csv | python3 -c "import sys; print(sys.stdin.read())" | grep -q 'id,name'are all tagged. Each fails before the work whenout.csvis missing, so a worker that writes only a header row earns "verified". The same goes forpython3 -c "print('untitled')" | grep -q untitled, where the check prints its own literal. When there's a pre-work copy, P1 drops that last one, but with no copy it would be sealed with the value tag. - Fix: when the runner stage is
python3 -c,node -e,bash -corsh -c, require the inline code to contain adeliverableCall, or require the runner to be a script by path (./x,x.py). Also refuse a pipeline whose first stage iscat <file>.
4. A keyword argument makes any method call look like "the deliverable on literal input". Confidence: medium-high (reproduced).
- Where:
src/tiers.ts:264(NOT_DELIVERABLE) andsrc/tiers.ts:366-368(literalInputstripsname=, soencoding='utf-8'reads as a literal). - Scenario:
assert 'id,name' in Path('out.csv').read_text(encoding='utf-8')is tagged.Pathis opaque, butread_text(encoding='utf-8')then counts as a call into the work with literal input. It's the same file-shape read as finding 3. - Fix: don't count keyword arguments as literal input, and add
read_text,read_bytes,readlines,fetchall,decodeand similar toNOT_DELIVERABLE.
5. Exception handlers that catch the check's own failing exit, or everything. Confidence: medium on the mechanism, low on how often it happens (reproduced on the function).
- Where:
src/tiers.ts:496-499reads only the first name inexcept (...).src/tiers.ts:483doesn't requirewith. - Scenarios:
try: p.f('x'); sys.exit(1)followed byexcept SystemExit: passis tagged. The handler catches thesys.exit(1), so the check passes whateverfdoes.except (KeyError, Exception):is tagged, because onlyKeyErroris read.pytest.raises(KeyError)used as a statement withoutwith, followed byp.f('x'), is tagged. Here the check passes exactly when no exception is raised.
- Fix: refuse when any name in the except tuple is
SystemExit,BaseExceptionorException, and requirewithbeforepytest.raises(E)unless the call formpytest.raises(E, f, ...)is used.
6. Very short or alternated grep patterns. Confidence: low-medium.
- Where:
src/tiers.ts:257, which needs only 2 word characters. - Scenario:
python3 t.py | grep -qi 'er'matches nearly any error text, andpython3 t.py | grep -E 'ok|fail'passes on either outcome. Both are tagged. - Fix: require about 4 or more word characters, and refuse
|alternation under-E/egrep(or count only when every branch is a value).
No changes needed in bench/local/value_replay.py for the pairing rules. The docker gate mounts the tree and bench read-only, and nothing in it is tied to a provider. Once 1 and 2 are fixed, it would be worth rerunning the replay to confirm the 8 sealed and 7 audit flips still hold.
solvyxtech
left a comment
There was a problem hiding this comment.
Verdict: CHANGES REQUESTED
Prior items: 0 of 2 blockers fixed. ! cmd | grep -q X still gets the value tag and passes the can't-fail screen, and is not None and literal not in still count as value assertions. Non-blocking items 3–6 are still open. The head is still 2ed2838, the commit I reviewed in 5450739903, and the base hasn't moved.
Fresh pass from Grok, comment only. I re-ran it at 2ed2838: npm run typecheck is clean, and the suite passed 2132 of 2132. CI hasn't run because of the account-wide GitHub Actions billing block. One new note, non-blocking:
Comparisons that are never asserted, or can never fail, still get the value tag. This is pre-existing on claude/post-work-audit, so it isn't a regression from this PR, but this PR owns assertsValue. Confidence: high (reproduced with real python3).
python3 -c "import tool; tool.f('a') == 'x'"gets["task","value"]even though the comparison is a bare expression statement that nothing asserts.cannotFail(src/checklint.ts:403) andswallowsExitboth returnnull.- The same happens with
assert (tool.f('a') == 'x', 'msg')(always-true tuple),assert … or True,assert … if False else True, andpython3 -O/PYTHONOPTIMIZE=1. - Scenario: before the work,
import toolfails, so the check exits 1 and counts as discriminating. With anytool.f, includingreturn 'WRONG', it exits 0. I ran it and got pre=1, post=0. A weak judge writes one of these (the parenthesized assert is a classic mistake, and Python itself warns "assertion is always true"), and the run is labeled "verified" on wrong work. - Fix: require the comparison to be asserted (under
assert,if not …: exit/raise,sys.exit(0 if … else 1), or a test bracket), the wayassertsExactdoes in #46. TeachcannotFailthe four always-pass forms. Add them tonotBehaviourintest/tiers.test.ts. #46 gets the same note, because its exact tag accepts all four.
|
Round-3 review, item by item. Fixed in claude/integration-all@3d1ca16 (#49); this branch is superseded by integration.
|
assertsValuedecides thevaluetag on sealed checks and the post-work audit's V-no-value gate. It rejected real behavioural assertions. "No check that ran the work asserted an expected value" is the biggest remaining reason good-judge runs go unverified.What changes (
src/tiers.ts, rule 5 plus a rule 1 fix)is None/is not Noneon a call into the work with a literal argument, or a literalin/not inits answer. The result has to be asserted, throughassert,sys.exit(0 if … else 1)orif …: sys.exit(1).try/except KeyError,pytest.raises,assertRaises,assert.throws.except Exceptiondoes not count, and neither does a handler that only prints.grep(plain,-qor-x, never-v,-cor-l) of a literal at the end of a pipeline that runs a program.head -1 out.csv | grepandgit log | grepstill don't count.;or newline inside[ "$(python3 -c "a; b")" = "2" ]used to hide the bracket. These brackets are now read shell-aware, and the operands are compared as whole shell words.$'…'now counts as a literal. Type probes (type(x).__name__,isinstance) still don't count.re.search(p, '…')). With that form included, 4 of the 7 runs it alone would have verified were grader failures. All 4 were regex runs whose judge examples missedx 1.2.3.4.5 on 2024-01-05.isinstance,hasattr,len > 0), the truthiness of an arbitrary call, and timing bounds still get no tag.Replay (
bench/local/value_replay.py, run over the 2026-10-07 lanes; 722 runs, 1395 distinct commands)--audit-gates, gates run in the bench container): 71 checks dropped with V-no-value are now tagged. 7 runs flip to verified-audit: 7 passed, 0 failed.surfacetag isn't journalled. 6 of the 8 sealed flips have several passing checks, so those counts can only be too high.Tests: every real example from the audit journals is in
test/tiers.test.ts, both positive and negative.npm run checkis green with ~/.local/bin off PATH.🤖 Generated with Claude Code
https://claude.ai/code/session_01D63hP8FwZTxcamaLRq4jRt