Repository navigation
Exact tag: verified needs a check that compares the output with an exact value - #46
solvyxtech wants to merge 3 commits into
Conversation
…exact value On the 2026-10-07 mmhd lane (MiniMax M3 worker, Haiku 5.5 judge) cron-next was labelled verified on four judge checks that all tested properties: 8 lines, each minute a multiple of 15, sorted, unique; "every date is the 13th or a Friday"; two runs agree; "starts with 2024-06-1". The grader asked for */15 from 2025-01-01 00:07 and got 00:45, 01:00, 01:45 instead of 00:15, 00:30, 00:45. Wrong output with the right shape passes every property check. src/tiers.ts assertsExact tags a check "exact" (beside "value") when it compares the work's output with an exact expected value: equality with a literal or a table of literal cases, diff against literal text, grep -x of a literal line, `is None` on literal input, a specific non-zero exit/HTTP status, a worked-out value found in a program's output on literal input, or an oracle built from the input files. Counts, sizes, types, modulo, prefixes, membership, formats, one constant across every item, two unknowns, checks that cannot fail, and patterns tried on the check's own lines are properties. tierOf now earns "verified" only from a passing independent exact check; passing independent value checks that are all properties give "passed-checks" with the reason "passed checks that test properties only". The post-work audit drops such a check with V-property-only. The reference writer's check compares against two references and is tagged exact. Prompts: the drafter now asks, for each functional requirement, at least one check that compares the output on a concrete input with an exact expected output worked out from the task text, with its source quoted or derived in a new `expect` field, plus edge and near-miss inputs (boundaries, empty input, 1.2.3.4.5 for an IP rule, both sides of an either/or rule). The old line that told drafters to test properties instead of working values out is gone. The critic sees `expect` and is told a derivation is not a guess. The audit prompt asks for exact comparisons and edge inputs, drops its count example, and takes `expect` too. Replayed over 2026-10-07 (bench/local/exact_replay.py): of the 81 verifieds the base rule grants, every grader-wrong one (2/2) is removed and 56 of 79 grader-right ones (71%) are kept.
…d verifieds
Joins every run on the day's lanes to its grader row (the .regraded.jsonl
twin when there is one), takes the passing sealed checks that could have
carried each recorded "verified" (value at --base, not the worker's), and
reports how many the exact rule keeps and removes, split by grader verdict,
over two populations: every recorded verified (A), and those the base rule
still grants with an independent author on the receipt (B, the gate).
A right A wrong B right B wrong
recorded verified 113 8 79 2
--literal-only kept 53 (47%) 0 48 (61%) 0
shipped rule kept 61 (54%) 0 56 (71%) 0
The literal-only rule failed the two-thirds bar, so it was narrowed with
containment of a worked-out value and an oracle form; that passes in B. In A
11 right runs rest only on commands that also carried a wrong run, so no
command-level rule can keep them.
solvyxtech
left a comment
There was a problem hiding this comment.
Verdict: changes requested (3 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. The cron-next miss is exactly the case a value tag can't catch, and gating "verified" on an exact comparison is the right fix. Running the replay over both populations, with --literal-only as a baseline, is the right way to decide it. The branch goes claude/exact-values into claude/value-classifier, there's no subscription backend and nothing touches fine-tune data. One pairing problem is listed as blocking 3 below.
I tested against c5dd6d4. npm run typecheck is clean, and the full suite passes 2179/2179 with ~/.local/bin off PATH. The runner still exited 1 afterwards because of the same coverage-reporter error on this box (Cannot read properties of undefined (reading 'line')), which is a local issue and not this PR. CI did not run: typecheck-and-test wasn't 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 / assertsExact / evidenceTags at c5dd6d4.
Blocking
1. The containment form (rule 5) accepts an expected value that is already in the literal input, so a program that echoes its input or crashes on it earns "exact". Confidence: high (reproduced on the function and with a real script).
- Where:
src/tiers.ts:1033(containsValue), the literal-input test atsrc/tiers.ts:1050. - Scenario:
python3 tool.py '2024-06-13' 2>&1 | grep -q '2024-06-13'is tagged["task","value","exact"]. So arenode cli.js 'abc123' 2>&1 | grep -q abc123andpython3 nextrun.py '0 9 * * *' '2023-01-01 09:00' 1 | grep -q '2023-01-01 09:00'. Before the work,tool.pyis missing, the error doesn't contain the date, and the check fails, so it counts as discriminating. A worker that writesdatetime.strptime(sys.argv[1], "%Y/%m/%d")then crashes withValueError: time data '2024-06-13' does not match format, grep finds the date, and the run becomes "verified". I ran exactly this: before=1, after=0. A program that just prints its arguments passes too. - Why it matters: the base rule already tags this check value, so it isn't new exposure compared with the parent branch. But exact is meant to be the stricter gate, and this form was added to get over the two-thirds bar. As written, a crash passes the gate that's supposed to require a worked-out value.
- Fix: refuse when the pattern body (or any 4+ character run of it containing a digit) appears in the upstream stage's literal arguments or redirect target. Also refuse when stderr is merged (
2>&1,|&), since a traceback or usage error is never the expected output. Add the three probes above to the property list intest/exact-values.test.ts.
2. A name bound to one literal gets past the rule that a constant compared across every item is a property. Because value and exact are judged over the whole command, a count check plus that loop gets both tags. Confidence: high (reproduced).
- Where:
src/tiers.ts:924(if (quantified(run, at) && !names.has(expected.trim())) continue;) together withexactNamesatsrc/tiers.ts:673, which puts scalar constants (ok = True) in the same set as loop variables over a case table. Alsosrc/tiers.ts:1071, where the two tags are computed independently. - Scenario:
out=cron.next('*/15 * * * *','2025-01-01 00:07',8); assert len(out) == 8; ok=True; assert all(cron.valid(t) == ok for t in out)is tagged value + exact.len(out) == 8supplies value (the base rule), andall(... == ok ...)supplies exact, becauseokis innames. Spelled with a literal (== True), the sameall()is correctly refused. That is cron-next again: a count plus "every item passes the validator" holds for output that skips every other occurrence. - Why it matters: the docstring says "the same constant compared across every item ... is still a property", and the only thing that changes the answer here is giving the constant a name.
- Fix: in
exactNames, keep loop variables that come from unpacking a literal table separately from names bound to one scalar or string. At line 924, exempt only the loop-variable kind (thefor inp, want in CASESshape). Longer term, require the value and the exact verdict to come from the same assertion, not from anywhere in the command.
3. A competitor name and model ID in the test file. Confidence: high on the rule.
- Where:
test/exact-values.test.ts:5("MiniMax M3 worker, Haiku 5.5 judge") andtest/exact-values.test.ts:97(model: "claude-haiku-5-5"). Line 98 also has"minimax-m3". - Why it matters: Tyler's decision on #33 was that no competitor name appears anywhere in the repo, not just in product copy, and the base branch currently has none in
src/ortest/. This PR adds the first ones. The bench helper's--basedefault,origin/claude/value-classifier, is a branch ref and is fine. - Fix: use generic IDs in the fixture (
judge-small,worker-a) and describe the lane generically in the comment ("a small judge model on the 2026-10-07 lane"). The PR body and commit message say the same thing. The commit message can't change without a history rewrite, which is Tyler's call, but the body can be edited.
Non-blocking
4. One exact check whose expected value is trivial can carry "verified" on a stub. Confidence: medium (reproduced on the function. How often it happens is unmeasured).
- Where:
exactEquality/statusExpected(src/tiers.ts:714). Nothing weighs which literal is expected. - Scenario:
assert tool.parse('') == [],assert ip.valid('1.2.3.4.5') == Falseandpython3 tool.py --bad; [ $? -eq 1 ]are all value + exact.def parse(s): return [],def valid(a): return Falseandsys.exit(1)pass them. Each fails before the work, because the module is missing, so each counts as discriminating. The new drafter prompt asks specifically for empty input and near-miss inputs (src/criteria.ts), so a lane where the only exact check is the edge case becomes more likely. - Fix: don't let a check whose only expected literal is empty, zero, False or None, or a non-zero exit, be the only exact check behind "verified". Require at least one exact check with a non-trivial expected value alongside it.
5. The prompt reversal isn't measured by the replay. Confidence: medium.
- Where:
src/criteria.tsremoves "Never write an expected value you worked out yourself ... Your own arithmetic is where checks go wrong".src/criteria.ts:868tells the critic that a derivation inexpect"is not a guess". - Scenario: the replay reclassifies checks drafted under the OLD prompt, so it shows the tag is safe. It says nothing about how often the drafter now writes a wrong worked-out value. A wrong derivation fails correct work, which gives a false refusal or
A-fails-on-work, and the critic has now been told to let derivations through. The old rule was there because judge arithmetic went wrong. - Suggestion: before this goes to main, rerun one lane with the new prompts and report how many grader-right runs are refused because a sealed check's expected value was wrong. Consider having the critic re-derive any
expectwith arithmetic in it, rather than accepting it as given.
6. The oracle form (rule 6) treats any file the check opens as the task's input, including files the work wrote. Confidence: medium (reproduced on assertsExact. It only matters when another clause in the same command supplies value).
- Where:
READS_INPUTatsrc/tiers.ts:933, used byoracleEqualityatsrc/tiers.ts:944. - Scenario:
rows=json.load(open("out.json")); exp=[...rows]; assert open("out.txt").read().splitlines()==expandexp=open("out.txt").read().splitlines(); assert open("out.txt").read().splitlines()==expboth returnassertsExacttrue. That is two of the work's own outputs, or one output compared with itself, which is the "two unknowns" property. Separately, the PR's own positive oracle example intest/exact-values.test.ts:51returnsassertsValuefalse, soevidenceTagsnever tags it exact and the audit drops it atV-no-valuefirst. The test passes only because it callsassertsExactdirectly. - Fix: require that the opened file existed before the work with the same digest (the
goldensBefore/valueUnprovenmechanism already does this for expected files), or drop the form if the replay shows it isn't needed in population B. Add anevidenceTagsassertion for each positive example so a dead form shows up.
7. Small things.
tierOfin advisory mode: the new property-onlypassed-checksreturn drops...gap, unlike thepassed-untestedandpassed-own-checksreturns next to it. That matches the existing fallback, but it means an "unconfirmed" review isn't recorded on a property-only run. Low.- Sealed criteria carry tags from draft time, so a session sealed before this lands has no
exacttag and can't earn "verified" afterwards, and its reason will say "tests properties only" even forassert f('x') == 'y'. Recomputeexactfromrunat tier time, or note it in the CHANGELOG. Low-medium. - The positive example
bash -c 'git rev-parse --abbrev-ref HEAD | grep -qx master'(test line 49) counts as exact. That's reasonable for a branch task, but it shows that rule 3 doesn't check whether the deliverable ran at all. That's worth a sentence in the docstring.
Tests I'd add
- The three containment probes in finding 1, plus a
2>&1traceback case, as property. - The named-constant
all()/forcases in finding 2, with and without alen()value clause, as property. Keepfor inp, want in CASESexact. evidenceTags(not justassertsExact) for every positive example.- A
tierOfcase where the only independent exact check expects[]orFalse.
… of exact checks outgrew the ceiling)
|
Live A/B (Haiku 5.5 worker, Sonnet 5.5 judge, 20 tasks, same bench): baseline build (#44+#45) 20/20 pass, 12 verified (60%), 0 wrong; this PR (with the cut-off retry fix, 87d…) 20/20 pass, 7 verified (35%), 0 wrong. The exact rule plus the longer drafting prompt cost recall with no precision gain on this sample. Decision: keep the exact rule opt-in (it still removes the cron-next class in replay), keep the cut-off retry in ask.ts as a general fix; do not make the rule default. Next: confirm the 60% baseline at charter scale. |
solvyxtech
left a comment
There was a problem hiding this comment.
Verdict: CHANGES REQUESTED
Prior items: 0 of 3 blockers fixed, and non-blocking items 4–7 are still open. The only code change since my last review (5450990992, at c5dd6d4) is f213246, which retries a cut-off reply in src/ask.ts. src/tiers.ts, src/criteria.ts, src/post-audit.ts and test/exact-values.test.ts are byte-identical to what I reviewed. This pass adds one new blocker and two new non-blocking items.
Re-review from Grok. Comment only, nothing merged. Tested at f213246: npm run typecheck is clean. The full suite passed 2179 of 2180. The one failure was acp-deadline "ends the job on time…", which timed out because I was running three suites at once on this box. Run on its own, that file passes 13 of 13. CI did not run because of the account-wide GitHub Actions billing or spending-limit block, so a red or missing check here isn't a code finding.
Blocking
1. NEW: The code makes the exact rule the default, but the PR's own decision says it should be opt-in. Confidence: high (code read; there's no flag anywhere).
- Where:
src/tiers.ts:1413-1433.exactIndependentis always required forearned, and nothing intiers.ts,engine.tsorpost-audit.tschecks an env var or option. The audit gate atsrc/post-audit.ts:519(V-property-only) and the drafting-prompt rewrite atsrc/criteria.ts:536-547are also unconditional. - Scenario: your A/B comment from 9:13 PM PT (20 tasks, same bench) found that the baseline (#44+#45) verified 12 of 20 with 0 wrong, and this PR verified 7 of 20 with 0 wrong. It concludes: "keep the exact rule opt-in … do not make the rule default." Merging this head does the opposite. Every run drops to the A/B's 35% verified rate, with no precision gain measured on that sample, one day before an npm publish.
- Why it matters: the change and the decision recorded on the PR disagree. Anyone who merges based on the comment ships the measured recall loss.
- Fix: put the exact requirement (tier, audit gate, and the drafting prompt that goes with it) behind an explicit option such as
--require-exactorMAAT_REQUIRE_EXACT=1, defaulting to off. Add atierOftest for each setting. Or, if the plan has changed, update the PR body and say so. If the cut-off retry is the part you want to land now, it could go in its own small PR.
2. STILL OPEN: The containment form (rule 5) passes a crash whose error message repeats the input. Confidence: high (re-checked at this head). src/tiers.ts:1033 (containsValue). python3 tool.py '2024-06-13' 2>&1 | grep -q '2024-06-13' is still tagged ["task","value","exact"]. Details and the fix are in the previous review.
3. STILL OPEN: A named constant gets past the same-constant-across-items rule. Confidence: high (re-checked). src/tiers.ts:924 and exactNames at :673. The len(out) == 8; ok=True; assert all(cron.valid(t) == ok for t in out) probe is still tagged value + exact. That's the cron-next false "verified" this PR exists to remove.
4. STILL OPEN: Competitor names are in the repo. Confidence: high on the rule. They're at test/exact-values.test.ts:5 ("MiniMax M3 worker, Haiku 5.5 judge"), :97 (claude-haiku-5-5) and :98 (minimax-m3). The fix is still generic IDs. The commit message f7d5bb2 names them too, and changing that means rewriting history, which is Tyler's call. The A/B comment on this PR also names the models. That isn't repo content, but please keep it out of any squash message.
Non-blocking
5. NEW: The cut-off retry multiplies the caller's own ceiling with no cap, and if the retry fails, the clear "cut off" result is lost. Confidence: high on the mechanism (reproduced with a scripted provider), medium on how often it happens.
- Where:
src/ask.ts:119-120. - Scenario A (hosted):
writeReferenceasks withREFERENCE_MAX_TOKENS = 16_000(src/reference.ts:434). A cut-off reply now triggers a second request withmax_tokens: 64000, and providers whose output cap is under that answer 400. With a scripted provider I got ceilings[16000, 64000]and the result{ok:false, error:"HTTP 400 writing a reference check"}. Before this change, the caller gotcutOff: trueand reported "cut off at the token limit". The mission planner works the same way, going from6000to24000(src/mission.ts:866). - Scenario B (self-hosted):
askTimeoutMsat default local speeds gives a 64,000-token retry an allowance of about 220 minutes, and a 24,000-token one about 87 minutes. The retry holds the server's only turn (takeTurn) the whole time, so with nodeadlineAt(interactive drafting) every other ask to that box waits behind it. Before this change, a reply that had text in it was never retried. - Fix: cap the retry ceiling (for example
Math.min(bigger, ASK_MAX_TOKENS * ASK_RETRY_FACTOR), or skip the retry when the caller passed its ownmaxTokens). If the second ask fails, return the first cut-off result so the caller's "cut off at the token limit" message survives. Add a test for "second ask returns 400" alongside the two new ones.
6. NEW (exists on the base too, but this PR's gate is supposed to be the strict one): Python assertions that can never fail are tagged exact. Confidence: high (reproduced with real python3).
- Where:
assertsExactinsrc/tiers.ts, andcannotFailinsrc/checklint.ts:403, which misses all four forms below. - Each of these gets
["task","value","exact"]fromevidenceTags, andcannotFail,swallowsExitandrunnerPipedAwayall returnnull:python3 -c "import tool; assert (tool.f('a') == 'x', 'msg')". A non-empty tuple is always truthy, and Python itself warns "assertion is always true".python3 -c "import tool; assert tool.f('a') == 'x' or True"python3 -c "import tool; assert tool.f('a') == 'x' if False else True"python3 -O -c "import tool; assert tool.f('a') == 'x'"(andPYTHONOPTIMIZE=1), where the assert is stripped.
- Scenario: before the work,
import toolfails, so each check exits 1 and counts as discriminating. Withdef f(a): return 'WRONG'they exit 0. I ran the first three and got pre=1, post=0 for all three. A judge that writes the parenthesized-assert mistake, which is a classic one, earns "verified" for wrong work. Theif [ … = "7" ]; then echo ok; fishell form is correctly caught bycannotFail. These Python forms aren't. - Fix: in
cannotFail, flagassert (followed by a top-level comma,or True/or 1at the top level of an assert, a conditional expression as the assert's test, and-O/PYTHONOPTIMIZE. Cheaper still: run Python checks with-W error::SyntaxWarningat seal time. Add all four to the property list intest/exact-values.test.ts.
7. STILL OPEN from the last review: 4 (one trivial exact literal, like [], False or exit 1, can carry "verified" on a stub), 5 (the prompt reversal isn't measured by the replay; your A/B now suggests it costs recall), 6 (the oracle form trusts files the work wrote), and 7 (the advisory passed-checks return drops gap, sessions sealed before this lands have no exact tag, and rule 3 doesn't check that the deliverable ran).
Stack note
#45 and #46 both branch from claude/value-classifier and both edit src/ask.ts. A trial merge of the two is clean. Note that with both in, the cut-off retry here calls askOnce, and #45's temperature retry still recurses inside askOnce while holding the local turn. So #45's deadlock is reachable from this path too, until it's fixed there.
|
Round-3 review, item by item. Fixed in claude/integration-all@3d1ca16 (#49); this branch is superseded by integration.
|
Why
On the 2026-10-07 mmhd lane (MiniMax M3 worker, Haiku 5.5 judge, #43 build), cron-next was labelled verified and the grader failed it. For
*/15 * * * *from 2025-01-01 00:07 the work printed 00:45, 01:00, 01:45 instead of 00:15, 00:30, 00:45. The judge's four checks all tested properties: 8 lines, each minute %15 == 0, sorted, unique; "every date is the 13th or a Friday"; two runs agree; "starts with 2024-06-1". Wrong output with the right shape passes every one of them.What
Tier rule (
src/tiers.ts):assertsExacttags a checkexact, next tovalue.tierOfgrants "verified" only on a passing independent exact check. Independent value checks that all test properties now getpassed-checkswith the reason "passed checks that test properties only".diffagainst literal text or a pre-work golden file;grep -xof a literal line;is Noneon literal input; a specific non-zero exit or HTTP status; a worked-out value found in a program's output on literal input; an oracle built from the input files.all(),find -exec, loops), two unknowns compared, checks that cannot fail, and regex patterns tried on the check's own lines.Post-work audit: the new
V-property-onlygate runs afterV-no-value.Prompts (
src/criteria.tsdrafter and critic,src/post-audit.tsaudit):expectfield that quotes or derives that expected output.1.2.3.4.5for an IP rule, both sides of an either/or rule.expectand is told that a derivation is not a guess.Tests:
test/exact-values.test.tscovers:Test fixtures that expect "verified" now carry the
exacttag.Replay gate (
bench/local/exact_replay.py)Lanes last written 2026-10-07, with regraded verdicts where present. A = every recorded verified. B = the verifieds the base rule still grants (an independent author recorded on the receipt). B is the gate.
--literal-onlykeptfind -exec,|| echo failandprint(len(header) == 5)checks). No command-level rule can keep those.npm run checkpasses with~/.local/binoff PATH: 2179 tests.