fix(GATE-OP-PARITY-MANIFEST): refuse a throwing golden by name instead of aborting the pass (#776) - #853
Merged
Merged
Conversation
…d of aborting the pass (#776) Closes #776. `test_op_parity` threw `json.exception.type_error.302` out of the CPU golden pass instead of failing an assertion, so the walker stopped at the first bad manifest and left every golden behind it unchecked. The check that goes silent with it is `no runner for op`, which is what caught #559's missing runner arm. A gate that stops gating is the defect here, not the red line. The artifact that threw was `tests/parity/goldens/minimax_music3_oracle/manifest.json`, and that half is already fixed: #755 landed `043e56862` eight minutes after #776 was filed, so the two issues describe one capture. Reproducing #776 on the FIXED tree is what shows the rest of the hole. #755 closed which files the walker accepts. It did not close what happens when an accepted file throws anyway. Setting one tensor `dtype` to `null` in `rmsnorm_f32_8x128` still threw `type_error.302` at the TEST_CASE line and took the pass from 142 assertions to 37, with 45 committed goldens never reached. Two throw sites remained: `json::parse` on the manifest, and any runner reading a field out of it. Both now funnel through `GuardGoldenStage`, which turns a `std::exception` into a `FAIL_CHECK` naming `goldens/<case>/manifest.json`, quotes the original exception, and continues to the next golden. The dispatch chain moves into `RunGoldenCase` so its `continue` statements become `return false`, which is what lets one case be caught without unwinding the pass. The sibling `#755` audit case gets the same guard, because it reads every committed manifest and must not stop at the first unparsable one either. `std::exception` is the deliberate width. It covers json's `type_error` and `parse_error` and the `std::runtime_error` that `LoadTensor` raises for a shape or dtype-size mismatch, none of which name a file the caller already knows. doctest's `TestFailureException` is not derived from `std::exception` (`third_party/doctest/doctest.h:2563`), so a `REQUIRE` or `FAIL` inside a runner still aborts as before. That is load-bearing: the `no runner for op` refusal is a `FAIL`, and a guard that swallowed it would have made this red go away without restoring the gate. Mutation evidence, tree restored byte-for-byte after each (823 files, md5). Before: nulled `dtype` throws, 37 assertions run. After: refused as `goldens/rmsnorm_f32_8x128/manifest.json: the runner for op "rmsnorm" threw ...` with 45 op cases still run and 152 assertions still checked. An unparsable manifest and a `null` or absent `"op"` are refused by name the same way. A manifest naming `no_such_runner_op_559` still hits `FATAL ERROR: no runner for op`, so the #559 guard still bites. Clean tree is green at 13 cases and 153 assertions, 46 op cases and 1 non-op skip, unchanged from before the change, and green again under the `sanitize-cpu (address,undefined)` lane with the job's own environment. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
`main` moved while this branch was under gate, and `51e0cb5b1` (POLICY-ISSUE-INTAKE, #840) relocated the issue table out of `.agents/roadmap_v1.md` into the append-only `.agents/issue-index.md`. The roadmap conflicted for exactly that reason. Resolved per the keyed-record rule: `.agents/roadmap_v1.md` is taken from `origin/main` wholesale and now matches it byte for byte, and the #776 row is reapplied to its new surface as an append at the end of `.agents/issue-index.md`. Nothing else in the merge conflicted. `test_op_parity` was rebuilt and rerun on the merge result: 13 cases, 153 assertions, 46 op cases, 1 non-op skip, SUCCESS. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
localai-bot
pushed a commit
that referenced
this pull request
Aug 15, 2026
Two commits landed while this row was in flight, #853 and #831. Neither touches a surface this row changes. The issue index conflicted textually because both sides appended a row, and `merge=union` resolved it by keeping both, which is the behaviour #840 installed the driver for. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
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 gate had stopped gating (issue #776)
test_op_parityTHREWjson.exception.type_error.302out of the CPU golden pass instead of failing an assertion. A throw at manifest-read time aborts the whole test case, so every golden after the offender goes unchecked — including the walker'sno runner for oprefusal, which is the check that caught #559's missing runner arm. That is the deliverable here: restoring the gate, not clearing the red line.Linked in three places: this body,
.agents/issue-index.md, andtests/parity/goldens/README.md, which is where the walker's contract is declared. No spec is owed — this is the in-flow bug-fix path, the same one #755 took.mainmoved under this branch mid-gate and #840 relocated the issue table out ofroadmap_v1.mdinto the append-only index, so the merge commit takesroadmap_v1.mdfromorigin/mainwholesale (byte-identical to it) and reapplies the #776 row as an append to.agents/issue-index.md. The focused gate was rebuilt and rerun on the merge result.Which manifest, and how it was found
tests/parity/goldens/minimax_music3_oracle/manifest.json— an oracle capture with no"op"key at all. Found by auditing all 66 committed manifests (it is the only one that is not an op golden), then attributed decisively: rebuilding the pre-fix source (043e56862^) reproduced the exact CI line, and moving that one directory out of the tree turned the same binary green.70 assertions against 123. The throw was hiding a third of the gate.
That half is already fixed — and it is not the whole hole
#776 duplicates #755 for its stated symptom. #755 was filed at 13:08 UTC, #776 at 16:36 UTC, and
043e56862(PR #761) closed #755 at 16:44 UTC — eight minutes later. So the malformed manifest is repaired onmainalready, andtest_op_parityis green there. Reporting only that would have been accurate and useless.What #761 closed was the walker's INPUT set. It did not close the walker's EXCEPTION surface. Reproducing #776's shape on the fixed tree shows the difference. Setting one tensor
dtypetonullinrmsnorm_f32_8x128:Identical exception, identical failure mode, 45 committed goldens never reached — on a tree where #776 was supposed to be fixed. Two throw sites remained:
json::parseon the manifest, and any runner reading a field out of it.The fix
Both sites funnel through
GuardGoldenStage, which converts astd::exceptioninto aFAIL_CHECKnaminggoldens/<case>/manifest.json, quotes the original exception so the diagnosis survives, and continues to the next golden. The dispatch chain moves intoRunGoldenCaseso itscontinuestatements becomereturn false— that is what lets one case be caught without unwinding the pass. The sibling#755audit case gets the same guard, since it reads every committed manifest and must not stop at the first unparsable one either.std::exceptionis the deliberate width: it covers json'stype_errorandparse_errorand thestd::runtime_errorthatLoadTensorraises for a shape or dtype-size mismatch — none of which name a file the caller already knows. doctest'sTestFailureExceptionis not derived fromstd::exception(third_party/doctest/doctest.h:2563), so aREQUIREorFAILinside a runner still aborts as before. That is load-bearing rather than incidental: theno runner for oprefusal is aFAIL, and a guard that swallowed it would have made this red go away while quietly removing the gate.Mutation evidence
Tree restored byte-for-byte after each (823 files,
md5sum -cclean).rmsnorm_f32_8x128tensordtype: nullERROR: goldens/rmsnorm_f32_8x128/manifest.json: the runner for op "rmsnorm" threw "[json.exception.type_error.302] ..."— 45 op cases still ran, 152 assertions still checked (was: THREW, 37)ERROR: goldens/mutation_unparsable/manifest.json: parsing manifest.json threw "[json.exception.parse_error.101] ..."— 46 op cases still ran, noTHREWanywhere{"op": null}ERROR: goldens/mutation_null_op/manifest.json declares a non-string "op" (null)— pass continues{"op": "no_such_runner_op_559"}FATAL ERROR: no runner for op 'no_such_runner_op_559' — add one before committing goldensThe last row is the one that matters most. The #559 guard still bites, and still aborts, exactly as it did before. Without it, the first three rows would only prove the red was silenced.
A new test case drives the same properties without touching committed evidence: the raw
type_error.302names no file, the guarded form names the case and preserves the originalwhat(), a case after the offender still runs, andTestFailureExceptionpropagates through the guard rather than being swallowed.Green
RunGoldenCaseextraction did not change what is gated.sanitize-cpu (address,undefined)lane, built with the job's own flags and run under its env (VT_POOL_BYPASS=1,ASAN_OPTIONS,UBSAN_OPTIONS): SUCCESS. The main RED on sanitize-cpu: test_op_parity throws json type_error.302 — a golden manifest field is null where a string is required #776 half of that lane's red is closed.test_nemotron_h_scaffold(main RED on sanitize-cpu: ForwardNemotronHForCausalLM static_casts a LoadedModel that is not a NemotronHLoadedModel #775) is a separate agent's fix and still red there.scripts/agent-preflight.sh: green.test_cpu_x86_llamacpp_floorfailed once under contention from the other agents on this box and passes standalone (10/10) — the documented load-sensitivity, not a real failure.The unguarded-read shape elsewhere
Surveyed deliberately. Within
test_op_parity.cppthere are ~167 unguardedm.at(...)/m[...]reads inside runners; they are the residual this change covers generically rather than site-by-site, since the guard catches whatever any of them throws and names the manifest. Elsewhere intests/, the otherjson::parsecall sites read a single named fixture owned by their own test case — a throw there fails one case whose fixture is already named, not a walker over an open input set. The walker-shaped risk, where one bad file silences everything behind it, was unique toRunGoldenPassand is now closed. No follow-up issue is owed.FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Claude:claude-opus-5 [Claude Code]