Repository navigation
Stop dropping good drafted checks; keep pre-work passes as refuse-only guards - #43
solvyxtech wants to merge 2 commits into
Conversation
drop_audit.py collects every drop line the lanes wrote to stderr (trial, review before sealing, redraft, cover draft, critic), groups them by reason and samples commands. --run executes each dropped command in the bench image on the task's pristine tree and on the lane's finished tree, and grades the finished tree, so a check that failed before the work and passed on accepted work is counted as a good check thrown away. --lint re-reads the drops with a build's dist/ to show which it still makes, and which P1 drops it keeps as guards.
Measured over the 2026-10-07 lanes with bench/local/drop_audit.py: - The absolute-path rule read the raw command, so a sed program's /g and a value glued onto sys.argv[1] + "/old.py" were paths (all 4 of its drops that day). src/shellwords.ts parses words, quotes, substitutions, redirects and heredocs, and absolutePathArgs returns only what programs open: operands, redirect targets, assignments, --opt= values, and string literals of inline programs that are a path on their own. sed and awk programs, grep patterns, jq filters, printf/echo data, find patterns and test's compared strings are skipped. - A trial of ./rotate.sh before rotate.sh exists exits 127, and was dropped as 'the command was not found' (6 checks). A project path the shell could not open that is absent from the tree is the deliverable missing: it counts as failing before the work. A tool missing from PATH is still broken. - L15 read touch -d's DATE as a file the check creates, lost track of f="$d/x", and of > "$(mktemp -d)/out" (13 drops). - L16 read '...|| exit 1; done; exit 0' and 'cond && exit 1 || exit 0' as checks that cannot fail (4 drops). - A check naming the project by its absolute path is pointed at the copy it is tried on before the work, and at the bar's throwaway copy. Checks the pre-work screen drops for passing before the work (P1) are sealed as refuse-only guards (tag guard): they run at the bar and can refuse, tierOf never counts them, receipts and the CLI label them. A check that pins today's output of the project's program on its own data is not kept: 13 of the 19 P1 drops that failed an accepted finished tree were of that shape.
solvyxtech
left a comment
There was a problem hiding this comment.
Verdict: changes requested (1 blocking, 5 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 holds up. Keeping P1 drops as refuse-only guards, the shell word parser for strayPath, and the L15/L16 fixes are the right calls, and the drop audit makes the table easy to trust. Pairing rules are clean: no competitor names in the added lines, no subscription backend, nothing touching fine-tune data, and the branch goes claude/false-drops into claude/discriminating-verified.
Tested against e1c4668: tsc is clean for both configs, false-drops plus discriminating-checks pass (37/37), and the full suite passes 2102/2102 locally. CI did not run on this PR: the typecheck-and-test job was never started because of a GitHub Actions billing or spending-limit block on the account, so the red check isn't a code failure.
Blocking
1. Rewriting the project path in the whole command breaks checks whose data mentions that path, and the seal-time screen then counts them as discriminating. Confidence: high (reproduced).
- Where:
src/scratch.ts:96(replacePathPrefix) andsrc/scratch.ts:345(map), used atsrc/bar.ts:2208(copy.map(check.run)) andsrc/criteria.ts:407(roots.reduce(... replacePathPrefix ...)). - Scenario: the project lives at
/app, as every harbor task does. A drafted check readsgrep -q 'root /app/public;' nginx.conf, orjq -e '.out_dir == "/app/out"' config.json, orgrep -q 'WORKDIR /app' Dockerfile.replacePathPrefixrewrites every whole-path occurrence, including the ones inside the grep pattern and the jq literal, so the check runs asgrep -q 'root /tmp/<copy>/public;' nginx.conf. That can never match. I reproduced it end to end: in a project whosenginx.confcontains exactlyroot /tmp/p43proj/public;,preflightCriteriawithrootset returnsfailed: ['root-kept']and nopassed. - Why it matters: the check isn't just broken. The screen records it as failing before the work, so it gets sealed and counts as discriminating, and then it fails at the bar on correct work every time, through all retries. Before this PR, the same check passed before the work and was dropped as P1, which was harmless.
- Fix: rewrite only words the shell will open as paths.
absolutePathArgsalready finds those (operands, redirect targets,cdtargets, assignments), so map just those word spans and leave patterns, programs and quoted data alone. Running the copy at the project's own path, with a mount namespace orchrootinside the bench container, would also work. Add a regression test with a grep pattern and a jq literal that contain the project path.
Non-blocking
2. A missing dependency or build output now counts as "failed before the work". Confidence: medium (helper reproduced; whole flow not reproduced).
- Where:
src/criteria.ts:321(missingDeliverable) andsrc/criteria.ts:431. - Scenario:
missingDeliverable("sh: 1: node_modules/.bin/vitest: not found", cwd)returns the path, so a check that would have passed before the work if dependencies were installed (node_modules/.bin/vitest run,.venv/bin/pytest,./build/tool --selftest) gets pushed tofailed. If the worker runsnpm installor a build, the check flips to passing and earns discriminating credit under--require-discriminating, even though the logic it tests never changed. - Fix: don't credit paths under
LINKED_DIRSor git-ignored paths, and ideally only the deliverable the task text names (as in./rotate.sh).
3. pinsCurrentValue misses common pin shapes, so they still get sealed as guards that refuse the correct fix. Confidence: medium (reproduced on the function).
- Where:
src/criteria.ts:1069. - Scenario:
python3 report.py | grep -qx 'total: 42'and[ "$(wc -l < out.csv)" -eq 12 ]both returnfalse. The regex only knows the anchored'^…$'grep and==/=forms, not-x/-Fxor-eq/-necomparisons. If the task changes that number, the guard refuses correct work, which is the exact failure the filter exists to prevent. - Fix: handle
grep -x/-Fxliterals and numeric-eqagainst a non-zero literal, and add those to the tests.
4. exitsEarlier also clears an exit N that can't end the check's shell. Confidence: medium (reproduced on swallowsExit).
- Where:
src/evidence.ts:148andsrc/evidence.ts:153. - Scenario:
ls *.txt | while read f; do test -s "$f" || exit 1; done; exit 0. The loop body runs in a pipeline subshell, soexit 1never ends the outer shell and the check always exits 0. Same withf() { exit 1; }; true; exit 0. Both now returnnull, which means "can fail". When the pre-work try runs, P1 catches these (and at worst they become guards that never refuse). But when there's no copy (tree too big,MAAT_CHECK_COPY=0, worktree.git), L16 is the only screen and a check that can't fail gets sealed. - Fix: ignore an
exitinside a pipeline stage or a function body (name() { … }).
5. The drafter's own output can set guard: true. Confidence: medium (code reading).
- Where:
src/criteria.ts:110(sanitizeCriteria). - Scenario: the model's draft can mark checks
guard: trueand so get 4 guard slots on top of the 4 check slots, skipping theguardsFrompin filter and dedupe. The effect is limited to refusals, but guards should only come from the P1 screen. - Fix: strip
guardfrom model output at the drafter boundary and let onlyguardsFromset it.session:runcan still keep it on re-sanitize.
6. missingDeliverable checks the path from the copy's root. Confidence: low (code reading).
- Where:
src/criteria.ts:321. - Scenario: in
cd sub && ./tool.sh, the path the shell reports is relative tosub, butstatSync(join(cwd, p))looks at the root. Atool.shat the root hides one that's missing insub, and the reverse happens too. - Fix: skip crediting when the command
cds first, or resolve the path against thecdtarget.
solvyxtech
left a comment
There was a problem hiding this comment.
Verdict: CHANGES REQUESTED
Prior items: 0 of 1 blocker fixed. Path rewriting still replaces /app inside grep patterns and jq strings, so those checks get sealed as proof and then refuse correct work. Non-blocking items 2–6 are still open. The head is still e1c4668, the commit I reviewed in 5450523000, and the base hasn't moved.
Fresh pass from Grok, comment only. I re-ran it at e1c4668: npm run typecheck is clean, and the suite passed 2102 of 2102. CI hasn't run because of the account-wide GitHub Actions billing block. The only new item is a stack note, non-blocking and high confidence: this PR and #42 both branch from claude/discriminating-verified, and a trial merge conflicts in src/criteria.ts. Whichever lands second needs a rebase. The fix for blocker 1 is unchanged: map only the word spans absolutePathArgs returns, and add a regression test with the project path inside a grep pattern and inside a jq literal.
|
Round-3 review, item by item. Fixed in claude/integration-all@3d1ca16 (#49); this branch is superseded by integration.
|
Maat's own seal-time screens were throwing away good checks a strong judge drafted. Measured first with
bench/local/drop_audit.py(every drop line of the 2026-10-07 lanes up to 18:20, 389 lines, 37 lanes; each dropped command run inmaat-bench:ebc6addc-uon the pristine task tree and the finished tree, which was graded).…|| exit 1; done; exit 0,cond && exit 1 || exit 0touch -d DATE,f="$d/…",> "$(mktemp -d)/x"./rotate.shbefore it exists.git/g,sys.argv[1]+"/old.py"Fixes
src/shellwords.ts: a shell word parser;strayPathreads only what programs open.touch/mkdir, mktemp-built variables, redirects into$(mktemp -d).exit N, and&& exit 1 || exit 0.guard, run at the bar, able to refuse, excluded bytierOf, labelled in receipts. Pins of today's output (jq -e '.zoe == "-3.50"',PRAGMA user_version … '^1$',test -f cache.py) are not kept: 13 of 19 P1 checks that failed an accepted tree were pins; 7 of 147 that held are lost by the filter. Residual risk: ~6 guards on correct trees would still refuse (2 are rerun artifacts).npm run checkgreen (2102 tests) with ~/.local/bin off PATH.