gate(ROCM): M4 for Qwen3.5-0.8B GDN -- oracle-backed green near-tie gate - #559
Conversation
6f6ee84 to
5efcebc
Compare
|
Reviewed as part of a sweep over the open external PRs. The measurement behind this is real — I recomputed the verdict independently from the committed goldens and got exactly your numbers: 13/16 fail, 3 pass, max gap 14125 mnats at (prompt 8, tok 8), 6 prompts differing at token 0. Golden provenance also checks out: Three things before this can land. 1. It would turn
The guard is behaving correctly: the existing 2. The gate does not fail safe by device.
Related, and cheap: the checkpoint-absent path at 3. The RED anchor does not have teeth against the defect it names. The pass/fail decision is The shape that works is to land the gate with GREEN goldens alongside #570, keeping the RED capture as recorded evidence in the spec and the parity ledger, where it already lives in full. One more worth fixing while you are here: the anchor-drift message ends "— re-run qwen3-neartie-gap.py to refresh the gap golden". After #570, that Numbers to correct in the prose: the six token-0 divergences are 0.375/0.750/0.750/0.938/1.000/1.062 nats, so the max is 1.062 not 1.188, and only 5 of the 6 are over the 0.5-nat band. And On CI: No AMD hardware here, so nothing was executed on ROCm; findings 1–3 are static, and the verdict arithmetic above came from your committed files. |
5efcebc to
a77a3b3
Compare
|
All three findings accepted and reworked (commit a77a3b3, rebased onto current main):
Gate on gfx1100: 16/16 PASS (15/16 strict, 1/16 band, max gap 0.125 nats); 0.6B gate re-run green. |
30059ac to
c8ec69e
Compare
… validates an outside contributor's trailers (#773) (#782) Closes #773. `check-pr-size.py` and `check-commit-trailers.py` both required the base revision to be an ANCESTOR of head. CI passes `github.event.pull_request.base.sha`, the TIP of the base branch, which stops being an ancestor the moment main advances after the branch was cut -- continuously, on this repo. Measured on three open PRs before changing anything. Base is not an ancestor in any of them, and a merge base exists in all three: #506 ancestor=NO merge-base=e1087a881 #523 ancestor=NO merge-base=fdd452637 #559 ancestor=NO merge-base=fafa16f0f The consequence was not a noisy red check. Both checkers aborted BEFORE examining anything, so CI had never validated commit trailers on an external contribution: the gate enforcing FOLLOWING_AGENTS_PROTOCOL and Assisted-by: exited before reading a single commit. Across the external PRs reviewed this week, hand-checking by a reviewer was the only verification those trailers received. pr-size aborted identically, so path classification and the checker-evidence contract went unenforced on forks too. THE FIX. Diff from the merge base, which is what a pull request IS: `git diff A...B` is defined as `git diff $(git merge-base A B) B` and is what GitHub shows. Two-dot diffing against a moved main is not merely stricter, it is WRONG -- main's own commits render as reversions inside the contributor's diff, so paths they never touched get classified and charged to them. The new pr-size test asserts both halves: the PR's file present, main's absent. `executable_evidence` gets the same treatment, since the BASE version of a checker for the red-before half is the one at the merge base. WHAT DELIBERATELY DOES NOT MOVE. The old rule conflated two situations: ordinary divergence (merge base exists) now examines merge_base..head; unrelated histories (no merge base) STILL RAISES. Absence of information must never look like absence of work -- the script's own require_origin_main() docstring already states that principle for the other input. test_missing_and_nonancestor_objects_fail_closed uses an ORPHAN branch, so it still raises; only its regex changed, because the message now names what is actually wrong. Its assertRaises(ValueError) is untouched. The trailers case was SPLIT, not deleted: its divergent-branch half built two branches off a common root -- which share a merge base and are the ordinary shape of every PR -- so that half now asserts it validates, with the genuinely-unrelated case asserted separately. Nothing that used to fail closed stopped failing closed. Range changed, contract unchanged: test_a_bad_trailer_in_the_merge_base_range_is_still_reported puts a trailerless commit inside the new range and requires it still be reported. RED before on the unmodified checkers, GREEN after (74 passed, 148 subtests). Stop conditions checked individually rather than inferred. Full tests/scripts: 9 failed / 1359 passed, all nine pre-existing and reproduced on main. CI: agent-record and pr-size both SUCCESS on this PR -- the two checkers it repairs passing on a live PR. Remaining red is baseline only: windows-msvc-* are the PR-only arm (#584), and sanitize-cpu is red on main itself for #775 (test_nemotron_h_scaffold, nemotron_h_registry.cpp:112 downcasting a doctest StubModel to NemotronHLoadedModel). This PR touches no C++.
…d of aborting the pass (#776) (#853) Closes #776. `test_op_parity` THREW instead of failing an assertion: tests/parity/test_op_parity.cpp:1989: ERROR: test case THREW exception: [json.exception.type_error.302] type must be string, but is null The malformed input was tests/parity/goldens/minimax_music3_oracle/manifest.json -- an oracle capture with no "op" key at all, the only non-op manifest among the 66 committed. Attributed decisively rather than by inference: rebuilding the pre-fix source and moving that one directory aside turned the same binary green, 70 assertions -> 123. The throw was hiding a third of the gate. #776 IS A DUPLICATE, AND THAT IS NOT THE END OF IT. #755 is the same defect, closed by 043e568 (#761) eight minutes after #776 was filed, and the manifest is already repaired on main. Reporting only that would have been accurate and useless, because #761 closed the walker's INPUT SET and not its EXCEPTION SURFACE. Reproduced on the FIXED tree by nulling one tensor dtype in rmsnorm_f32_8x128: :2177: ERROR: test case THREW exception: [...type_error.302...] [doctest] assertions: 37 | 37 passed 37 where 153 should run -- 45 committed goldens never reached, on a tree where this was supposedly closed. The gate was one bad field away from silently not gating. WHY THAT MATTERS: test_op_parity is the guard that refuses a golden whose manifest names an op with no registered runner. It is what caught PR #559's missing runner arm. A throw at manifest-read time aborts the case, so every golden after the malformed one goes unchecked -- not merely a red job, a gate that had stopped gating. THE FIX, both halves. The two remaining throw sites (json::parse on the manifest, and any runner field read) funnel through GuardGoldenStage, which converts 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 moved into RunGoldenCase so its `continue`s became `return false`, which is what permits a per-case catch. std::exception is the deliberate width: doctest::detail::TestFailureException is NOT derived from it (third_party/doctest/doctest.h:2563). That is load-bearing -- the "no runner for op" refusal is a FAIL, and a wider catch would have cleared the red while REMOVING the gate. MUTATION EVIDENCE, tree restored byte-for-byte each time (823 files, md5sum -c): dtype: null in a real golden -> named by path; 45 op cases STILL RAN, 152 assertions still checked (was: THREW, 37) invalid JSON manifest -> named by path; 46 cases ran, no THREW {"op": null} / missing "op" -> refused by name, pass continues {"op": "no_such_runner_op_559"} -> FATAL ERROR: no runner for op ... -- the #559 guard STILL BITES The last row is the one that mattered; without it the others would only prove the red was silenced. GREEN: focused 13 cases / 153 assertions, with 46 op cases + 1 non-op skip -- counts IDENTICAL to before, so the extraction changed nothing about what is gated. sanitize-cpu (address,undefined) rebuilt with the job's own flags and run under its env: SUCCESS. pytest tests/scripts: 1400 passed, 9 failed, all three distinct failures reproduced on a clean origin/main worktree. Survey: ~167 unguarded m.at(...) reads live inside runners in this file and the guard covers them generically. Other json::parse sites in tests/ each read one named fixture owned by their own case, so a throw there fails one already-named test rather than a walker over an open input set. No follow-up owed. CI: merged with checks queued -- the runner pool has been saturated for hours and no job started on this head. The sanitize lane, which is the one this change exists for, was reproduced locally with that job's own build flags and environment. Note main moved mid-gate: #840 relocated the issue table out of roadmap_v1.md into the append-only .agents/issue-index.md. roadmap_v1.md was taken from origin/main wholesale and the #776 row reapplied as an append to the new index.
acea2be to
14d2850
Compare
Current-main rebase and fresh reviewRebased from
|
…lden lane in the Qwen3-0.6B near-tie gate Issue mudler#41 M4 milestone. The oracle is upstream vLLM AT THE PIN (555967922, runtime 0.23.1rc1.dev1511+g555967922 -- verified == the parity-pin block) built from source as a ROCm wheel for gfx1100 in rocm/vllm-dev:base and committed as the image vllm-rocm-oracle:555967922-gfx1100 (recipe: .agents/specs/rocm-m4-oracle.md). Gate result on this box (4x RX 7900 XTX, ROCm 7.14, enforce_eager, batch=1, flock ~/gpu.lock): oracle K=10 per-prompt greedy deterministic in every cell; our ROCm engine 16/16 prompts PASS -- 11/16 strict token-exact vs base greedy, 5/16 near-tie band, max teacher-forced gap 0.125 nats, 0 forward-divergent, 125/125 assertions, backend proof all 8 Qwen3-dense ops selections>0 and declines==0 on device 5. The p0 France/Italy flip is a literal 0.0000-nat tie in the oracle's own logits (the Metal M3b signature, reproduced on ROCm). Change shape: the parity gate's Metal/Tenstorrent device-golden lane gains the kROCM arm (identical anchor+band logic, ROCm's own oracle-backed golden pair); dgx base goldens byte-untouched; the *_rocm goldens are additive. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: pi:kimi-k3 [pi]
…he 0.8B GDN forward divergence, root-caused and fixed Issue mudler#41. The ROCm dispatcher for the fused full-attention preamble keyed its template on the SOURCE dtype (qgate.dtype) instead of the OUTPUT dtype like the CUDA lane (LaunchAttnPreambleOut switches on q_out.dtype). The 0.8B bf16 model runs bf16 QKV-projection output + f32 q/k/gate outs (the f32-attention path; no FA-2 on ROCm), which the src-keyed dispatch mis-launched as all-bf16 -- writing bf16 bits through the f32 out pointers. Evidence chain (gfx1100, flock): CPU==pinned-oracle token-exact / ROCm diverges at tok0; VT_DUMP_ACT per-layer residual dumps: layers 0-2 clean (<=9.6e-3), layer 3 (first full-attn) 2.1e-1; sub-stage dumps: block_out 4.4e-1 with post_input_norm clean; VT_DUMP_ATTN op dumps: fa0_q rms-rel 1.196 with qkv/gate clean; VT_ROCM_ATTN_CPU_REF=1 unchanged. Fix: dispatch on q_out.dtype; bf16-out requires bf16-src (VT_CHECK). Teeth: the cross-device AttnQkNormRopeGate case gains the bf16-src -> f32-out combo at the real 0.8B dims (Hq=8, Hkv=2, Dh=256, rot=64); mutation-proven (fails with the fix reverted, passes with it). Gates on this branch (gfx1100, flock ~/gpu.lock): test_backend_cross_device 19/19 (346 assertions, incl. the new combo); test_qwen3_paged_engine 16/16 unchanged; e2e 0.8B 'The capital of France is' -> ' Paris.\nThe capital of France is' == pinned oracle == our CPU. The M4 0.8B gate with the re-derived GREEN goldens lands in the stacked sibling on row/ROCM-M4-GDN-GATE; full HIP ctest zero-delta vs base measured there. Also: three pre-existing hipcc/clang-only build breaks that block ANY full HIP build on this toolchain: same-line double CAPTURE in test_qwen3_5_gdn_spec_routing.cpp and test_ops_quant_repack.cpp, and -Wunused-const-variable on kGdnLayers (uses compile out on HIP). Debug instrumentation env-gated and off by default (VT_DUMP_ACT / VT_DUMP_ACT_SUB / VT_DUMP_ATTN; deepseek_v4 VT_DUMP_ACT precedent; env-doc-allowlist updated). FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: pi:kimi-k3 [pi]
…e, landed green-shaped with the dispatch fix stacked below Issue mudler#41 M4, second model. NEW tests/parity/test_qwen35_paged_engine.cpp -- the GDN sibling of the Qwen3-dense SACRED gate: the standard 16-prompt battery through the full paged engine, anchor + <=0.5-nat near-tie band identical to the dense gate, backend proof over the GDN op set. Goldens captured from the pinned vLLM-ROCm oracle on gfx1100 (K=10 deterministic in every cell) with a manifest.json (oracle identity + pinned HF revision 2fc06364 resolved via NEW parity::Qwen35_08BSnapshot; check-snapshot-pins clean). VERDICT: 16/16 PASS (15/16 strict token-exact vs the oracle, 1/16 near-tie band, max gap 0.125 nats, 0 forward-divergent) with the stacked AttnQkNormRopeGate output-dtype fix below. The PRE-FIX capture (13/16 forward-divergent, max gap 14.125 nats) is retained as evidence in the spec and parity ledger, not as committed goldens no code can pass. Post-review shape (localai-bot sweep): the manifest op joins PendingRunnerOps() (the qwen36_gguf_greedy precedent -- a runner-less manifest op turned main's CPU CI red); the gate exits 77 (CTest Skipped) on any non-ROCm device and when the pinned checkpoint is absent, instead of comparing a foreign engine against ROCm-derived goldens or printing SUCCESS with zero assertions; and the anchor-drift REQUIRE now reads REGRESSION SUSPECTED -- re-deriving goldens is the last step of a justified re-capture, never the response to a failure. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: pi:kimi-k3 [pi]
Three missing Qwen3.5 gate artifacts currently return normally, so doctest reports false success. This specification binds absence to exit 77 and preserves bootstrap and malformed-array behavior. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: codex:gpt-5.6-sol [codex]
Missing Qwen3.5 gate artifacts now exit 77 through one shared prerequisite helper, which prevents zero-assertion doctest success. The subprocess regression drives the actual gate executable for each required file. Bootstrap still writes token IDs, then exits skipped until the correctness artifacts exist. Malformed present arrays continue through the existing hard-failure checks. Fixes mudler#1222 FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: codex:gpt-5.6-sol [codex]
Probe mode bypasses snapshot, model, and device checks. A normal return therefore turned the completed prerequisite check into zero-assertion success. Exit 86 now marks only a completed test probe. The subprocess control distinguishes it from success and unavailable exit 77. Fixes mudler#1222 FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: codex:gpt-5.6-sol [codex]
14d2850 to
4673a35
Compare
Final integration cutoffFinal head:
|
Brings the branch up to `main` so GitHub can compute the merge. GitHub does not honour the `merge=union` driver that `.gitattributes` sets on `.agents/issue-index.md`, so this branch read as conflicting while `git merge-tree` reported it clean. This merge is collapsed by the squash and exists only to make the pull request mergeable. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: claude:claude-opus-5 [claude-code]
Brings the branch up to `main` so GitHub can compute the merge, and resolves the three record conflicts as unions rather than picks, per AGENTS.md: take the complete target-branch version, then re-apply the scoped edit. `.agents/benchmark-record.md` keeps both appended sections, `main`'s ENG-CUDAGRAPH-BREAK W1 entry and this branch's ROCM-GEMMA4-PREFILL-PEER-BARRIER entry, because it is an append-only log and either side alone drops a measurement. `docs/STATUS.md` keeps `main`'s M4 gate results, which landed with mudler#559, and re-appends this branch's mudler#1047 item-3 attribution sentence. `docs/USAGE.md` keeps `main`'s KEEP recipe and its "This path does" wording from mudler#676, with this branch's prefill-peer lifetime sentence re-applied ahead of it. Every conflicted path was a record or documentation file. No `src/` or `include/` path conflicted, so no product source was hand-resolved here; the product changes this merge carries in are `main`'s own. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: claude:claude-opus-5 [claude-code]
…and Gemma-4 records `main` gained a ROCm ReshapeAndCache/PagedAttention composition test (#497), the ROCm M4 near-tie gate for Qwen3.5-0.8B GDN (#559), and the Gemma-4 ROCm KEEP recipe record (#676) while this branch was under gate. None of them touches the LTX-2.5 path, the CPU GEMM seam, or the two test files this branch edits. Merged rather than left behind so that the branch's gate runs against what it will land on, and so the trailer gates — which decline to run at all while the branch is behind — have an ancestor to compare against. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…sh (#1047) fix(rocm/gemma4): #839 retirement-safe prefill peer GeGLU Launch/Finish Fixes #839. ## The defect The Gemma-4 FP8 cross-device prefill helper was one monolithic function. Its donor unpinned the dequant cache immediately after enqueueing the GEMMs and before `hipEventRecord(ev_e)`, and the cache is process-wide, so a second worker could evict a zero-pin slot while the first expert stream was still reading it. Under batched-MoE prefill that is a use-after-free, and the observed symptom is a wedge after matched BEGIN/END with the process idle in `kfd_wait_on_events`. ## What changed `RunGemma4Fp8ExpertGeGLUPrefillOnExpertDevice` stays the public symbol and becomes a wrapper over `LaunchGemma4Fp8ExpertGeGLUPrefillPeer` and `FinishGemma4Fp8ExpertGeGLUPrefillPeer`. The cache pin now lives on the `PeerSlot` and is released only after the host has **observed** retirement, never after a mere enqueue: - Launch pins under the same mutex scope as `GetLocked` and stores `{cache_pin, cache_dev}` on the slot before any GEMM is enqueued. It never unpins. - Finish host-waits `ev_e`, then `hipStreamSynchronize` on the compute stream that received the output copy, and only then unpins under the lock. - A failed fill or ready-event keeps a fill lease; the lease is retired with `RetireFillLocked` after the producer-stream sync and never under `cache.mu`. - A restore failure after publish retires before it rethrows; a failed retire quarantines the pin rather than dropping it. - The same-dev arm follows the same rule. "Enqueue then unpin" is forbidden on both arms, because a `hipStreamWaitEvent` on the compute stream is not a host-side retirement proof. Peer-pipe overlap stays default OFF, so Launch and Finish run back-to-back on slot 0 and the event order is the one shipping today plus slot-scoped scratch. ## What the tests prove `tests/vt/test_ops_gemma4_prefill_peer.cpp` is 23 cases over the host lifetime model and the product source. The lifetime cases drive `HostLaunch`/`HostFinish`; the product is bound to them by source-slice gates that extract the real `Finish` and `RestoreComputeOrThrow` bodies out of `src/vt/rocm/rocm_gemma4_experts.hip`, compile them, run them, and require each mutation to red. Replacing the `hipSetDevice(compute_dev)` throw in the product `RestoreComputeOrThrow` with `(void)compute_dev;` fails three assertions. ## CI repairs in this revision Three checks were red for reasons that had nothing to do with the change under review, and all three are green on `main`. `build-test-cpu` and both `sanitize-cpu` legs failed because the test named one absolute HIP compiler from the contributor's box, `/opt/rocm-7.2.4/core-7.14/bin/hipcc`, in four places and CHECKed that it returned 0 — so any machine without that exact path **failed** rather than reporting that the gate had not run. The toolchain is now resolved (`VLLM_CPP_HIPCC`, `HIPCC`, `ROCM_PATH/bin/hipcc`, `hipcc` on PATH, `/opt/rocm/bin/hipcc`, each probed with `--version`) the way `tests/vt/test_ops_getblas_product.cpp:15-21` resolves its own precondition, and its absence prints a loud `HIP COMPILE GATE NOT RUN` banner on stderr instead of asserting. Measured on this tree: 23/23 cases and 199 assertions with no toolchain plus two banners, 23/23 and 205 assertions with `VLLM_CPP_HIPCC` set, the six recovered assertions being exactly the HIP legs. Both arms still red on the `RestoreComputeOrThrow` mutation, so the repair stopped the gate failing on absent hardware without widening it. Absent hipcc does not `exit(77)` the way `test_ops_getblas_product.cpp` does. That file is one HIP gate and nothing else, while this one carries 21 host-lifetime cases that are the only gate CI has on this change, and exiting would take those with it. The g++ leg of both compile gates still runs, so neither case can report a zero-assertion pass. `pr-size` refused the whole change with `could not classify` because nothing in `scripts/check-pr-size.py` matches `.agents/evidence/`. The five donor slices move beside the spec as `SPEC_EVIDENCE` `.log` files (`.txt` is unclassified too) and the manifest becomes `.agents/specs/rocm-gemma4-prefill-peer-helper-donor.md`. Renaming does not touch bytes and all five SHA256 values in the table verify unchanged. `docs/FEATURES.md` returns to `main`'s text. The branch had rewritten that row and lost the spec link, the `VT_GEMMA4_*`/`VT_ATTN_*` pointer and the `test_gemma4_rocm_fp8_seams` seam name in order to fit; `main`'s cell is already 219 of the 220 characters `check-public-doc-tables` allows. Nothing here owes that page: `check-doc-checkpoint` has keyed `feature_surface` off a changed `REGISTER_VLLM_MODEL` set since #595, and this change touches no file under `src/vllm/model_executor/models/`. The branch was rebuilt by rebase onto `affc2a7fd`, so it carries no untrailered merge commit and all ten original commits keep their authorship. The `docs/USAGE.md` conflict against #837's landed GetBlas text was resolved as a union: both paragraphs survive. ## What this does NOT close Named here and under `## Owed` in `.agents/specs/rocm-gemma4-prefill-peer-helper.md`. Row `ROCM-GEMMA4-PREFILL-PEER-HELPER` owns them and #839 stays open until they are discharged. - **No measurement for the new blocking retirement.** Finish now host-waits `hipEventSynchronize(ev_e)` and `hipStreamSynchronize(cst)` on every call, and the same-dev arm gains one too, where the path was fully asynchronous before. It runs per expert per layer during prefill, so it serialises a pipeline that used to overlap. The wait is required for correctness and correctness comes first, so it stays; what is missing is the number. Prefill throughput before and after on 2x R9700 needs the hardware, and no CI runner here has it. - **`PeerSlot s[2]` with only slot 0 reachable** from the production wrapper. - **The host simulator is a second implementation.** `HostLaunch`/`HostFinish` in `include/vt/rocm/rocm_gemma4_prefill_dequant_cache.h` are hand-written analogues of the product Launch/Finish, ~250 of that header's lines, compiled into every HIP build. Moving them under `tests/` is the fix; it was not done here because only a ROCm box compiles the one translation unit that consumes the header, so the move cannot be verified where this repair was made. ## Known-unrelated CI `windows-msvc-cpu` and `windows-msvc-vulkan` are red on every open PR from a break predating this branch (#503, #584). ## Landing note (maintainer) `main` was merged into this branch before the squash, and three record conflicts were resolved as unions rather than picks, per AGENTS.md: take the complete target-branch version, then re-apply the scoped edit. `.agents/benchmark-record.md` keeps both appended sections, since it is an append-only log and either side alone drops a measurement. `docs/STATUS.md` keeps the M4 gate results that landed with #559 and re-appends this branch's #1047 item-3 attribution sentence. `docs/USAGE.md` keeps the contributor KEEP recipe that landed with #676 and re-applies this branch's prefill-peer lifetime sentence ahead of it. No `src/` or `include/` path conflicted, so no product source was hand-resolved. The resolved tree was gated locally: the 42-test `test_check_gate_commands` suite, plus `check-agent-record`, `check-doc-checkpoint`, `check-issue-index-append-only`, `check-readme-structure`, `check-public-doc-tables` and `check-env-doc`, all green. On the CI verdict, stated precisely. This branch's own product changes are two files, `include/vt/rocm/rocm_gemma4_prefill_dequant_cache.h` and `src/vt/rocm/rocm_gemma4_experts.hip`, and the host lifetime suite `tests/vt/test_ops_gemma4_prefill_peer.cpp` that exercises the state machine. That host suite is what the 17 passing gates on head `de8d4760f` covered. The HIP compilation itself is not built by any lane in this repository's CI, so no CI run here has ever verified it, before or after this merge; the gfx1201 evidence is the contributor's. `windows-msvc-cpu` and `windows-msvc-vulkan` were the only two red gates and are red on every pull request here. The post-merge re-run was still queued behind 19 other jobs at merge time. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: ClaudeCode:claude-opus-5 [ClaudeCode]
M4 for Qwen3.5-0.8B (GDN) — the oracle-backed gate, landed green-shaped (issue #41)
Stacked: #549 (0.6B lane + oracle) ← the dispatch fix (#570) ← this gate. The fix lands below the gate, so every commit in the stack is individually green on the gate host and the committed goldens are ones the tree can actually reproduce.
Gate
test_qwen35_paged_engine.cpp— the GDN sibling of the Qwen3-dense SACRED gate: standard 16-prompt battery through the full paged engine, hard anchor + ≤0.5-nat near-tie band, backend proof over the 15-op GDN set. Goldens captured from the pinned vLLM-ROCm oracle on this box (K=10 per-prompt, deterministic in every cell) withmanifest.jsonrecording the oracle identity; the checkpoint resolves viaparity::HfSnapshotat the pinned HF revision2fc06364(check-snapshot-pins clean).Verdict: 16/16 PASS (15/16 strict token-exact vs the oracle, 1/16 near-tie band, max gap 0.125 nats, 0 forward-divergent).
Review-sweep rework (all three findings accepted)
qwen35_0_8b_greedyis inPendingRunnerOps()(theqwen36_gguf_greedyprecedent) — the manifest itself stays; its oracle identity is the point.Prose corrections from review
First-divergence maximum is 1.062 nats (not 1.188), 5 of the 6 token-0 divergences are over the band (not all), and the headline gap is at the first over-band position rather than a post-divergence maximum.
In-flow gate repair
Issue #1222 found three missing-artifact paths that returned normally and let doctest report false success. The gate now routes absent greedy, anchor, and gap artifacts through exit 77. A no-GPU subprocess regression drives the real executable and asserts
77/77/77; its complete-artifact control requires hard sentinel exit 86 so probe mode can never bypass snapshot/model/device checks as zero-assertion success. Bootstrap writes its token IDs and then exits skipped until correctness artifacts exist. Malformed present arrays remain hard failures.Fixes #1222.
Current-main integration cutoff (2026-08-18)
Rebased from
5b31bc30349d527ef0b4c1c8fcd7c86749b4cfa3to4673a35dd47087cae3f6f9808e4fcf64667bcbcfon recorded current-main cutoff65d6cdaed3e20e9bc70b4f9374fccafefefa7bd0. Exact six-commit replay of #549 → #570 → the green 0.8B gate → the three #1222 spec/repair commits. Fresh cutoff-head builds, focused gates, full repository preflight, and independent mutation review all pass.FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: pi:kimi-k3 [pi]
Assisted-by: codex:gpt-5.6-sol [codex]
Assisted-by: pi:gpt-5.6-sol [pi]