feat(tools): durable per-request results and strict stdout classification in the eval harness - #1355
Conversation
The evidence tooling reads the engine's own stdout. Two of its lines are typed records -- the "== GLM C engine" banner and the "loaded in ..." line that follows it -- and every consumer that wants a field out of them was about to reimplement the same grammar. This adds one module that owns the exact text, the field ranges each number may take, and the errors a malformed line produces, so the consumers agree by construction. It also owns the canonical form of an ablation manifest: the engine reads a manifest a line at a time, drops the terminator and one carriage return before it, and digests the record followed by a single newline. canonical_manifest_bytes() is that rule written once for the Python side, so a checker and the engine cannot drift into hashing different bytes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
check_ablate_evidence.py re-checks one ablation manifest and its JSONL evidence artifact against an independently supplied config.json, without the engine and without any test binary: the config and manifest field bounds, every record's key set and field types, the header's binding to the config and to the manifest digest, and the truncation, replay and mismatch cases at each record boundary. Three cross-record invariants are enforced because the producer guarantees them for every row it can emit -- nll is never negative, corr agrees with the argmax-equals-gold comparison, and the argmax logit is never below the target's. Top-k order is deliberately not enforced: it is unsorted on the wire by design. The manifest is reduced through the shared canonical form before it is parsed or hashed, so a manifest saved with CRLF endings or without a final newline -- both of which the engine accepts and normalises -- is checked rather than refused, and binds the same digest the engine bound. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
eval_glm.py scored answers from whatever the engine printed and wrote its results only after the whole run finished. Two consequences: a foreign line on that stream was absorbed silently, and an interrupted run left nothing to read. Result rows are now appended per request, so a run that dies mid-task leaves a valid partial file carrying an INCOMPLETE marker and still prints the accuracy table over the rows that landed. Every line of the scoring stream is classified against the engine's documented SCORE-mode output and an unrecognised line is refused by name instead of ignored, and the numeric grammar accepts both the six-decimal form the engine ships today and the older %.17g form. Where the engine build cannot supply the evidence record the harness would bind to, the run is marked UNBOUND by name rather than reported as bound. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
pack_python.py reaches a file two ways: by following imports out from coli, and by spotting scripts that are launched as subprocesses. Only the first kind had its own imports followed, so a module that only a subprocess-launched script imports was left out of the release archive while --check still reported the archive complete. On dev this happens twice: tools/iq3_pack.py, which tools/convert_fp8_to_int4.py imports inside quant_e8(), and tools/engine_evidence.py, which this branch's eval_glm.py imports. Both are now reached. The test suite grows a real-tree assertion as well as fixture cases, because every existing case builds a disposable tree and so could not have caught either omission. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
On Windows, os.kill(pid, SIGTERM) is TerminateProcess with the exit code set to the signal number: no Python handler runs and the whole unittest process dies with status 15, which is how the Windows UCRT64 job reported `make: *** [test-python] Error 15` with no test summary. The behaviour the test asserts (a real SIGTERM converted into engine-child cleanup) is a POSIX mechanism; the SIGINT/exception half of the pair covers child cleanup on every platform. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
β¦ its newline The checker prints its PASS line with print(), so a Windows child hands the test CRLF where POSIX hands LF, and the byte-exact assertion failed on the Windows UCRT64 job (the only failure in a 914-test run). Nothing consumes that line byte-for-byte; the pin is on the content, so the test now folds CRLF to LF before comparing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Resolves the conflict JustVugg#1364 created, so this branch does not need a rebase. JustVugg#1364 landed the packer commit from this PR (cherry-picked, authorship intact) plus a second edge it left open: a data file opened next to a reached module. tools/pack_python.py therefore takes dev's version wholesale, which is a strict superset of this branch's -- the only lines unique here were the ones that commit replaced. tests/test_pack_python.py takes dev's version and keeps this branch's engine_evidence assertion as an ADDITIONAL real-tree case rather than a replacement. On dev that assertion could not run, because engine_evidence.py is introduced here; on this branch it pins the edge this branch adds, next to the one that pins the historical bug. 12 tests, all green.
|
Conflict resolved on your branch, pushed as What I did, so you can check rather than trust it:
12 tests, green locally. CI is running. One thing I noticed while verifying, worth a decision from you
Not a defect in your change: nothing in the code names it, and it takes So it is a decision, not a bug: if the checker is meant to be usable from a release archive, it wants the same explicit copy plus a Thanks again for #1359. Cherry-picking your commit rather than rewriting it was the least I could do after duplicating another contributor's work last week by not checking the PR list first. |
β¦erifies pack_python.py computes the archive from what coli reaches, so a tool whose only caller is a person typing argparse arguments never makes it in. k3_tokenizer.py has always been in that position and release.yml copies it by hand; check_ablate_evidence.py joined it with this branch. It verifies the ABLATE evidence that the packaged eval_glm.py produces, so an archive that ships the producer without the verifier is half a tool. Copy it explicitly, gate on it with the same test -f the tokenizer helper gets, and parse-check the packaged file. A real-tree test pins both halves of the decision: the walk does not reach either human-only tool (if it ever does, the explicit copy is redundant and the pin says so), and the workflow both copies and gates each one. A copy without a gate is how v1.10.0 shipped green with four broken commands (JustVugg#1296). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
@JustVugg: Thanks for patching the bugs, resolving the conflict on this branch, and the write-up. I checked the merge: pack_python.py at 9e96637 is dev's file with nothing of ours dropped, and test_pack_python.py keeps the engine_evidence case as an added assertion. I have pulled 9e96637 and have not rewritten the branch. On check_ablate_evidence.py: it should ship in the release archive. It verifies the evidence that eval_glm.py produces, and that harness is in the archive, so anyone running a released engine and producing ablation evidence wants the checker beside it. Added in ba2802a: the same copy and test -f gate that release.yml gives k3_tokenizer.py, plus a parse check, and a real-tree test that pins both halves (the walk does not reach either human-only tool; the workflow copies and gates each). #1356 [Authored by Claude Fable 5.1 in Claude Code, analysis in partnership with @monotophic.] |
|
Authored by Claude Fable 5.1 in Claude Code, analysis in partnership with @monotophic Merged current @JustVugg similar steering request to #1102 and #1353, this PR is part of the set to make the full checkpoint-quality GLM 5.2/5.3 containers work and to establish the OpenAI API instrumentation needed for container quality studies. Let me know if this PR is off target or otherwise not likely to merge. Thanks for all you do, this project is awesome :) |
|
Reviewed. The durability half is worth having on its own: appending results per request so an interrupted run leaves something behind, and classifying every stdout line instead of absorbing the unrecognised ones, are both real. Two things in the body no longer match the diff, and I would rather not merge a PR whose description misdescribes it. The And the evidence half is inert as it stands. My suggestion is to split: merge the durability fix now, with the body corrected, and bring the binding back when an engine actually writes that record. Carrying a reader ahead of its writer is the same pattern you flagged and offered to drop yourself in #1353. |
|
One more thing that applies to the set rather than to this PR, and it is the single change that would most help review. #1356 and #1357 each contain this PR's That also makes two statements stale. #1356 says one round-trip case fails until this PR lands, and #1357 says both modules fail to import without it. Neither is true of the diffs as pushed: each already carries both files. Could you rebase #1356 and #1357 onto this branch's head, so each diff shows only its own work? It cuts the review surface by nearly a third and makes the bodies true again. |
No engine build on dev writes the identity-bound wire form
("SCORE <ordinal> <sha256> <exact> <contlen> <greedy>"); SCORE_EVIDENCE
is read by nothing (0 files vs. 13 for the coli_omp_tune positive
control). Remove the dead binding machinery -- _SCORE_EVIDENCE_RE,
parse_score_evidence_result, ScoreStdoutClassifier's request_digests/
binding_mode/mode tracking, the SCORE_EVIDENCE=1 env set, and the
BOUND/UNBOUND reporting -- and the docstring paragraphs describing it.
The per-request durability append and the full stdout line
classification (banner/load preamble lifecycle, foreign-line refusal)
are unchanged.
Bring the binding back when an engine actually writes that record.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Split reused verbatim from upstream/p0-engine-preamble (b6070718), verified there by 18,093 old-vs-new behavioural comparisons with zero divergences. Keeps PreambleError, parse_engine_banner, parse_engine_loaded, parse_engine_preamble and their module constants. Removes ManifestFormError and canonical_manifest_bytes, which travel to JustVugg#1356 with their only consumer (check_ablate_evidence.py, removed from this PR next). The module docstring no longer describes the absent half. This transiently breaks check_ablate_evidence.py's import of the removed names; the next commit removes that module from this PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
check_ablate_evidence.py, its test module, and the three release.yml hunks that copy and gate it (the cp into dist/tools, the test -f presence gate, the ast.parse packaged-file check) move to JustVugg#1356, which owns their writer (canonical_manifest_bytes/ManifestFormError, removed from engine_evidence.py the previous commit). test_eval_glm.py's shared byte-limit test drops its ABLATE-side assertions (module gone) and keeps the EVAL-side ones unchanged; test_pack_python.py's HumanOnlyToolsShipExplicitly class is removed verbatim rather than edited, since check_ablate_evidence.py was one of its two pinned entries -- it is carried to colibri_lab/dispatch/2026-09-17-program-d-r2/CARRIED_TO_P2_test_pack_python_class.txt for JustVugg#1356 to restore alongside its own copy of the checker. test_engine_evidence_is_needed_by_the_real_tree, this branch's own addition to that file, is untouched. This is a forward commit that deletes -- intended, not a mistake. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
F3: engine_evidence's parse_engine_banner/parse_engine_loaded/ parse_engine_preamble raise a bare ValueError, not their own PreambleError, once a numeric preamble field exceeds Python's 4300-digit int-string conversion limit. This module's own contract is to refuse every non-canonical record with a named error; its three call sites into that shared module now catch ValueError alongside PreambleError so the bare exception cannot escape uncaught through classify_score_stdout, ScoreStdoutClassifier.classify, or is_score_preamble. engine_evidence.py itself is unchanged (shared verbatim with other in-flight branches); a coordinated fix there is outside this PR's authority. Added direct test_eval_glm.py coverage: the oversized-field contract fix (proof-of-bite against the pre-fix code, included in the worker report); a documentation pin for the "loaded index ..." literal-prefix collision in parse_engine_preamble's dispatch (no engine anywhere in this tree emits such a line); parse_engine_loaded's isfinite guard on load_s/resident_mb, which has no round-trip backstop the way parse_c17g does; and direct parse_c17g invocation (previously only exercised indirectly), which this branch's own investigation found to be equivalence-redundant with parse_c17g's round-trip format check for the fullmatch/decimal-digit/exponent-width regex components tried -- documented as such rather than claimed as bite it does not have. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
engine_evidence.py is shared verbatim across branches, not immutable -- a root fix here costs nothing while the sibling branches are unbuilt. F3 (root fix): parse_engine_banner/parse_engine_loaded now catch ValueError from their own int()/float() conversions and re-raise as PreambleError, so a numeric field beyond Python's 4300-digit int-string conversion limit refuses with this module's own named error instead of escaping as a bare ValueError from all three entry points. eval_glm.py's downstream catches of (PreambleError, ValueError) stay as defense in depth. F2: corrected premise -- check_data_logprob_gaps.py in this tree has no engine_evidence coupling, but JustVugg#1357's own (much larger) copy does, and its _global_header treats any problem entry, including a spurious malformed-preamble complaint, as a whole-transcript hard failure. Confirmed at primary source (grep across every "loaded"-prefixed printf in the C sources) that no engine anywhere emits "loaded index" or anything else that collides with the "loaded in" prefix test today, so per that finding, the fix is prose: parse_engine_preamble's docstring now states plainly that "starts like one of them" is a literal prefix test, not a semantic one, and names the collision. test_engine_evidence.py (also shared, also not frozen) gains: - IdotKernelRosterTest: every IDOT_KERNELS entry parsed from the real banner format, sourced from c/quant.h:582-594 with file:line citations ("neon" confirmed RAN via this host's own default-build compiler flags), plus a roster-size backstop -- closes the roster gap the previous round wrongly reported as already closed (that claim rested on eval_glm.py's own test, not on this module's own 45-test suite, which a scratch reduction to ("avx2",) alone left fully green). - test_trailing_newline_rejected on both parse_engine_banner and parse_engine_loaded: fullmatch requires consuming a trailing "\n"; $ alone does not, so match()/search() would accept a newline- terminated banner/load record that fullmatch correctly refuses -- the shape a real `for line in f:` caller actually hands over. - test_oversized_field_raises_preamble_error_not_bare_value_error on both functions, pinning the F3 root fix directly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
test_every_roster_entry_parses_from_real_banner_text iterates IDOT_KERNELS and asserts each entry parses to itself -- but _BANNER_RE is itself built from IDOT_KERNELS, so a same-length rename of any entry produces a banner the correspondingly-mutated regex still matches: the assertion compared the mutation against itself. RAN (reported and independently reproduced): renaming "neon" to "neom" left all 51 prior tests green. Adds test_roster_matches_frozen_expectation: a literal transcribed from c/quant.h:582-594's #if/#elif ladder, independent of IDOT_KERNELS and of _BANNER_RE, asserted with assertEqual against the roster directly. The per-entry parse loop and the length/uniqueness backstop are kept -- each still defends something real (kernel-dispatch/roster sync, and an accidental shrink) -- with the vacuous-gate limitation of the parse loop now stated explicitly in the class docstring. Checked every other test in this module for the same self-referential pattern (expectation derived from the artifact under test rather than an independent literal): none found. test_dispatches_to_banner/loaded compare parse_engine_preamble's output to parse_engine_banner/loaded's own output on the same input, which looks similar but is a legitimate differential check of dispatch routing specifically (a mutant that misroutes is caught; a mutant shared by both delegate functions is already independently covered by each function's own hardcoded-literal tests elsewhere in this file). Every other test asserts against a hardcoded literal (a dict, a number, a raised exception type). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
reachability, stale docstring, duplicated tree walk, vocab contract 1. The per-row out_f.flush() (c/tools/eval_glm.py) had no test that could detect losing it: test_result_rows_are_written_and_flushed_ incrementally reads the output file only after main() has unwound through its `finally` (which closes, hence flushes, out_f regardless of any per-row flush), so it defends the artifact after a clean-ish exit, not durability during the run. Added test_row_is_durable_on_disk_before_the_run_finishes: a probing fake stdout iterator opens a SECOND, independent file handle mid-run (main() has not returned) and asserts the previous row is already readable through it. RAN, proof-of-bite: removing the per-row flush fails this test with the durability assertion; restoring it passes clean. 2. F2 correction: the prior round's report called this "root-fixed", overstating it. Re-verified at primary source (no engine anywhere emits "loaded index" or anything colliding with either preamble prefix) -- there is nothing to tolerate, so prose was always the correct and complete fix, not a a compromise standing in for a code change. Tightened parse_engine_preamble's docstring to state plainly, up front, what is accepted and that everything else is refused, before explaining the prefix-collision characteristic. 3. Exhaustively re-tested every fullmatch call site in eval_glm.py against both .match() and .search() weakenings, full suite each time. Four mutants genuinely survive (leave the suite green): parse_c17g's and parse_score_result's fullmatch, each x2. parse_c17g's is UNREACHABLE via the classifier by construction (its only caller passes a substring _SCORE_RE already captured via that exact sub-pattern) and equivalent-in-practice for direct callers (round-trip format check); documented at the call site. parse_score_result's IS on the classifier's live path, but the one distinguishing input (a newline-terminated record -- $ matches before a trailing "\n" the same way engine_evidence.py's M11-M13 did) is provably unreachable through classify_score_stdout/ScoreStdoutClassifier.classify, which both strip exactly one trailing newline and refuse any other first. Direct callers are not protected by that guarantee, so closed with test_direct_call_rejects_trailing_newline_record. A third site (score_request_wire) does not survive -- already caught by an existing bad-request case -- left alone. 4. completion_error's docstring cited identity-binding/digest/OOV conditions this PR already deleted. Enumerated what can actually reach stream_error today (foreign/malformed/duplicated preamble, a non-finite or malformed SCORE token, a contlen mismatch, extra SCORE lines, an incomplete preamble at EOF) and decided fatal-for-the-whole- run remains proportionate for all of them: unlike the DATA/logprobs serving channel, this SCORE/benchmark channel has no legitimate non-finite case, and once one record fails to parse nothing guarantees earlier-looking-fine rows were not already touched by the same corruption. Docstring corrected to the live conditions; behavior unchanged. 5. Deduplicated the two PackPythonNeededTests methods that each independently called the expensive PACK.needed(HERE.parent) real-tree walk (~0.3s each) via a shared setUpClass computation. Halves this module's wall time (0.6s -> 0.3s, RAN). 6. score_snapshot_vocab's except clause caught json.JSONDecodeError but not its own ValueError parent -- json.loads() raises a bare ValueError (not JSONDecodeError) for a vocab_size literal beyond Python's 4300-digit int-string conversion limit, escaping main()'s only try/except (which catches EvidenceError to write the INCOMPLETE marker and return before ever launching the engine) and crashing outright with no marker written. Same error-contract class as F3; fixed the same way. RAN, proof-of-bite at both the unit level (score_snapshot_vocab itself) and end-to-end through main() (asserts rc=1 and an INCOMPLETE marker, not a crash). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Thank you β this was clear and constructive feedback. The only two places where we found different numbers, you had been charitable. We did a deep review on the set of three (1355, 1356, and 1357) and also integration testing with 1102 and 1353 on current dev (as of this posting). I think that what we've done is as good or better than your asks, but if you differ on our "improvements" we can deliver your asks directly. Two things first: the two places we didn't do what you asked to the letter. No rebase, but with reasonYou asked us to rebase #1356 and #1357 onto this branch's head so each diff shows only its own work. We are not We served the goal a different way: #1356 and #1357 were rebuilt fresh from current We chose the rebuild over the rebase because it re-parents reviewed content rather than rewriting it. For #1357 the If you would rather have the rebase, say so and we will do it. It is your history and your call; we did not want to force-push over your commit when there was a functional alternative.
|
| you asked | disposition | where it lives now |
|---|---|---|
"the body β¦ no longer match[es] the diff"; pack_python.py "already fixed on dev, merged as #1364" |
done β body corrected; the stale import-closure claim is gone. You are exactly right about what survives: the test additions plus the release.yml gate |
this PR's body |
| "the evidence half is inert β¦ nothing reads it, zero files" β split it out | done differently β the SCORE_EVIDENCE identity binding is parked; the durability half you said is "worth having on its own" stays and is now the whole of this PR. check_ablate_evidence.py moved to #1356 rather than parked (see above) |
this PR / #1356 |
| "rebase #1356 and #1357 onto this branch's head, so each diff shows only its own work" | done differently β rebuilt fresh from dev instead of rebased, to avoid force-pushing over 9e966378. Literal rebase offered above |
#1356, #1357 |
What changed in each PR, at a glance
| PR | files | lines added | moved in | moved out | parked | added on re-review |
|---|---|---|---|---|---|---|
| #1355 | 8 β 5 | +3,315 β +2,153 | β | the ablation-evidence checker, its test and the release.yml gate β #1356 |
the SCORE_EVIDENCE identity binding |
a durability guard that fails when the per-row flush is removed; a frozen kernel-roster literal |
| #1356 | 15 β 12 | +5,449 β +3,706 | the ablation-evidence checker, its test and the release.yml gate, from #1355 |
engine_evidence.py and its test β dropped, no importer left here |
β | the cap derived from CTX and checked ahead of all three allocations; removal of a one-implementation dispatch typedef; a real import check in the archive gate; a 2.01Γ logit_topk_select fix |
| #1357 | 11 β 4 | +7,464 β +2,319 | β | five files returned to the PR that owns each | check_native_mtp_witness.py + its test, 2,613 lines |
seven per-ordinal frame-order tests and a frozen order anchor; the coupling test made bidirectional |
| set | 34 β 21 | +16,228 β +8,178 |
Every ask across the set
One row per ask, in your order. Several asks were raised on one PR and resolved on another β that crossing is
the thing this table exists to make visible. The disposition is the same word each PR's own comment uses.
| your ask | asked on | resolved in | disposition | detail |
|---|---|---|---|---|
"the body β¦ no longer match[es] the diff"; pack_python.py "already fixed on dev, merged as #1364" |
#1355 | #1355 β this PR's body | done | this comment |
| "the evidence half is inert β¦ nothing reads it, zero files" β split it out | #1355 | #1355 (the SCORE_EVIDENCE binding removed) and #1356 (the checker, its test and the release.yml gate) |
done differently | this comment Β· https://github.com/JustVugg/colibri/pull/1356#issuecomment-5725636433 |
| "rebase #1356 and #1357 onto this branch's head, so each diff shows only its own work" | #1355 | #1356 and #1357 β both rebuilt fresh from dev rather than rebased |
done differently | this comment |
"is ABLATE_SCORE used by anyone outside your own tree?" |
#1356 | #1356 | done | https://github.com/JustVugg/colibri/pull/1356#issuecomment-5725636433 |
| "Could the writer collapse to a plain FILE pointer with the tests driving a tmpfile?" | #1356 | #1356 β c/colibri.c; the second structure you named still carries two production-path function pointers |
done for AblateWriter, NOT for AblateOutputRun |
https://github.com/JustVugg/colibri/pull/1356#issuecomment-5725636433 |
| "I would make the prose match what the cap actually bounds" | #1356 | #1356 β c/colibri.c, docs/ENVIRONMENT.md |
done, and then some | https://github.com/JustVugg/colibri/pull/1356#issuecomment-5725636433 |
| "send the witness alongside the engine change that emits the record it needs" | #1357 | monotophic/colibri codex/parked-mtp-witness @ e0474748 β out of the PR entirely, preserved on a branch, returning with the engine change |
parked | https://github.com/JustVugg/colibri/pull/1357#issuecomment-5725733595 |
| "the logprob-gap hardening as its own small PR now" | #1357 | #1357 β it is now the whole PR, 4 files | done | https://github.com/JustVugg/colibri/pull/1357#issuecomment-5725733595 |
"neither transcript tool is referenced by c/Makefile or by any workflow β¦ What is meant to invoke them?" |
#1357 | #1357 β answered; wiring offered on your word | answered | https://github.com/JustVugg/colibri/pull/1357#issuecomment-5725733595 |
There is also a c/Makefile interaction between #1356 and #1102 that asks nothing of you; it is noted on both
threads.
What crossed a PR boundary
Without this map, this PR's deletions read as us dropping your work. They are not deletions; they are relocations.
| file | was in | now in | why |
|---|---|---|---|
c/tools/check_ablate_evidence.py |
#1355 | #1356 | its writer is #1356's coli-ablate/2 mode |
c/tests/test_check_ablate_evidence.py |
#1355 | #1356 | follows its subject |
.github/workflows/release.yml |
#1355 | #1356 | the gate it adds is for the checker above |
c/tests/check_native_mtp_witness.py (+ its test) |
#1357 | parked | 2,613 lines waiting on an engine that emits MTPEMIT |
Merge order, so you can sweep without reading us
Merging #1355 first gives the smallest review for #1357, which then merges dev and its copy of the shared
foundation drops out mechanically. But any order merges cleanly β we proved it rather than assuming it: all six
orderings of the three branches merge with zero conflicts and produce one identical tree.
One honest exception, and it is ordinary: #1356 and #1102 both add entries to c/Makefile and will conflict
there β only there; the engine sources merge cleanly. Whichever lands second, we re-merge dev and push within a
day.
The duplication that is still there, and why
c/tools/engine_evidence.py and its test β 524 lines, two files β remain in #1355 and #1357, byte-identical.
#1356 no longer carries them at all: this revision moved the manifest helpers into check_ablate_evidence.py,
which left that PR importing nothing from the module, so shipping it there would have been 524 lines of code
nothing in the PR used β the very pattern you objected to. The duplication that remains is one pair across two
PRs, and it drops out when #1355 lands. The 6,700 lines you counted three times are now 524.
Details β origin accounting and evidence
maintainer-requested: body correction; SCORE_EVIDENCE binding parked; the de-duplication.
dev-motion: pack_python.py empty diff (your #1364 fixed it on dev).
re-review-found: the durability guard below.
One thing our own re-review found, disclosed because it is a real gap we shipped. This PR's headline feature is
incremental, per-row durability, and the test named for it could not detect losing it: deleting the per-row
flush() left the suite green. That is a test that manufactures confidence. There is now a guard that fails when
the flush is removed and passes when it is present β verified by running the mutation, not by inspection.
Also disclosed: the transcribed wire-format literals in these tools are hand transcriptions of the C source.
Module drift is pinned by tests; C-source drift is not. We have a follow-up in mind for that and are not
folding it in here.
Authored by Opus 5 in Claude Code, analysis in partnership with @Monotophic
Authored by Opus 5 in Claude Code, analysis in partnership with @monotophic
Revised 2026-09-17 in response to review. This PR is now the durability half only. The
ablation-evidence checker and its release-archive gate have moved to #1356, where their writer lives, and the
SCORE_EVIDENCEidentity binding is removed. Title updated to match: the previous one advertised two things thatare no longer here.
The evaluation harness reads scoring output off the engine's stdout, and today it trusts whatever it is handed: a
line it does not recognise is silently absorbed, and results are written only when the whole run finishes, so a
run that dies mid-task leaves nothing behind.
This changes both.
tools/eval_glm.pygains two properties it did not have. Results are appended per request, so a run that diesmid-task leaves a valid partial file with an
INCOMPLETEmarker and still prints the accuracy table over the rowsthat landed. And every line of the scoring stream is classified against the engine's documented SCORE-mode
output, so an unrecognised line is refused by name instead of silently absorbed.
tools/engine_evidence.pyowns the grammar of the engine's two typed startup lines β the== GLM C enginebannerand the
loaded in β¦record β so every consumer agrees by construction instead of each re-deriving the sameregular expressions and the same normalisation. It is the shared foundation the other two PRs in this group also
carry; see the note at the end.
Context. This PR is one of five independent contributions derived from a single locally-verified working tree
(the fp8 container line in #1102, the OpenAI-compatible server hardening in #1353, this harness work, the engine's
ablation scoring mode in #1356, and an engine-transcript check tool in #1357). This one carries the evaluation
harness and the shared preamble grammar. It stands alone β its whole suite is green with the rest of the set
absent (97 cases on a checkout of
devcarrying only these five files), and merging or declining the others doesnot affect it. The others are proposed separately, each with its own evidence. The set-level explanation and a traceability matrix covering every request across the three PRs are in the #1355 comment: .
Behavioral contract
exact-vs-greedy accounting, and the output format existing consumers read are unchanged, including
--drywithout a vocabulary and the accuracy table on a partial run.
truncated trailing record.
rejects what it does not.
Capstone matrix β one decisive artifact per claim; each would fail if the claim were false.
out_f.flush()attools/eval_glm.py:601andtests/test_eval_glm.py::test_row_is_durable_on_disk_before_the_run_finishesfails. The older test named for the same property βtest_result_rows_are_written_and_flushed_incrementallyβ stays green under that mutation, which is why the new one exists:547does not fail the test. It pins the per-row property, not the presence of aflush()calleval_glm.pydriven against a real container, polled until three complete rows were on disk, thenSIGKILLed β not a graceful stop, so the interpreter's flush-on-close never ran. A second, independent file handle opened afterwards found 3 complete rows, 9 fields each, no truncated trailing line. The control is what is missing: the file carries no# finishedrecord, which the harness writes last β proof the process really was killed mid-run rather than allowed to finishmain()driven end to end with rows observed on disk 1β2β3β4 mid-run, read through a second independent file handle. Stated exactly: the engine is a fakesubprocess.Popenreturning canned stdout, as this suite's own docstring says β no engine is launched here. The property under test is the harness's write-and-flush behaviour, which is where the defect was; proving it against a live engine is an integration cell, not a unit testtest_dev_copy_silently_accepts_a_foreign_numeric_lineprovesdev's classifier accepts the line1 1 1, shaped like its own grammar;test_new_copy_refuses_the_same_foreign_line_by_nameproves this branch raises on the identical input. Both run against the two implementations, not against a description of themneonβneom) fails the suite. An earlier fix that derived the expectation from the roster it policed passed that mutation, which is the reason for the literaldev's where it shoulddev's own copy ofeval_glm.pyextracted fromorigin/dev: the CLI surface is option-for-option identical, and--selftestproduces byte-identical output from both, each exiting 0. Scope stated plainly β this covers the accuracy-table and scoring-reduction path; it does not cover--dryagainst a real snapshot, which needs a container and is not run hereFuller matrix, what changed since the reviewed head, and origin accounting
What changed since
5e9b1131, the head reviewed on 2026-09-17pack_python.pyimport-closure paragraph is gone from this bodydev, so this branch's diff for that file is emptySCORE_EVIDENCEidentity binding removed (6 references β 0), and theUNBOUNDstatus with it (4 β 0)devreads that variable, so the reader shipped ahead of its writertools/check_ablate_evidence.py, its test, and therelease.ymlgate moved to #1356coli-ablate/2mode, so it moved rather than being parkedloaded inregular expressions read against the engine's own format strings and the kernel names it can printINCOMPLETEmarker, table printed, no truncated trailing recordmain()actually uses%.17gform, plusnan/inf/-infpython3 -m unittest tests.test_engine_evidence tests.test_eval_glm tests.test_pack_pythonondev+ these five filesRan 97 tests in 0.311s β¦ OKThe shared file, disclosed.
tools/engine_evidence.pyandtests/test_engine_evidence.pyβ 524 lines, twofiles β are byte-identical in this PR and #1357. #1356 no longer carries them: its revision left it importing
nothing from the module, so it was dropped there rather than shipped unused. That duplication is present by design until this PR lands;
the other two then merge
devand shed their copies mechanically. Merging this PR first gives the smallestreview surface for the other two, but any order merges cleanly β all six orderings were built in a scratch
worktree and produce one identical tree with zero conflicts.
A limitation we are disclosing rather than fixing here. The wire-format literals in this tooling are hand
transcriptions of formats printed by
c/colibri.c. Drift in the Python module is pinned by the tests above;drift in the C source is not β no test in this branch reads a C file. That predates this revision and a fix
would reopen the shared file across all three PRs, so it is noted as a follow-up rather than folded in.
Scope note: no file outside the five listed is touched, and no surrounding code was reformatted.
Durable vs current state: the harness properties, the preamble grammar and the refusal contract are durable.
The counts (97 cases) are current state, measured 2026-09-17 against base
cc756a63on macOS (Apple silicon).