fix(ENG-EXPERT-STREAM): the CUDA blocker is a missing vt::MatmulBTAlphaBeta, and the Gemma-4 device-expert upload now refuses before it happens (#1126, #1205, #1197, #1218) - #1200
Merged
Conversation
…t a one-line port, and #1126's own step 3 would be a defect (#1126, #1197) `CudaBackend` still does not override `vt::Backend::DeviceMemoryInfo`, and this change deliberately does not add it. What it fixes is the record that says how to close #1126, because as filed that record would produce a defect. ## What was already true, and what was not #1126 was filed against a seam comment reading "ROCm/CUDA override with hipMemGetInfo/cudaMemGetInfo". That comment no longer exists. #1132 corrected it in both places that carried it, `include/vt/backend.h:78-93` and `gemma4_moe.cpp:440-448`, and recorded the gap under `## Owed` in `expert-streaming.md`. Every anchor either of them cites was re-verified exact against `fd64c76ee`: `rocm_backend.hip:338-345`, `gemma4_moe.cpp:440-448`, `gemma4_moe.cpp:506`, `backend.h:78-93`, `platforms/cuda.cpp:71` and `platforms/rocm.cpp:74`. Six examined, six correct. So the comment half of this issue is done, and the honest question left is whether to build the capability. The answer here is no, and the reason is not the one the issue gives. ## What Gemma4 does today on CUDA `Gemma4MoE` is the seam's only consumer. `FreeBytes` returns false on an absent probe, `MakeRoom` refuses on unknown (`gemma4_moe.cpp:506`), so the device-expert LRU admits nothing on every CUDA device and every routed expert takes `ExpertGeGLUHost` instead (`gemma4_moe.cpp:49-74`, reached at `:1493-1499`), for the life of the process, and nothing reports it. That function's name misleads and it is worth being exact, because the first reading here was wrong: it does not compute on the host. It re-copies the expert's BF16 bytes host-to-device into a scratch buffer (`:59-60`), runs the same `vt::MatmulBT` / `GeluAndMul` pair on the device, and drains the queue once per expert (`:73`). So what the dead LRU costs is bandwidth and a per-expert `Synchronize`, not a wrong answer — the issue's "falls back to host H2D on every expert" is the accurate description. The refuse-on-unknown polarity at that call site is right, so the defect is the missing probe rather than the consumer. ## Why the override is not built here Four things came out of re-reading it, and each raises the price. 1. **There is nothing to run it on.** The LRU needs `ex.is_fp8`, which only `LoadMoeFp8PerExpert` sets (`gemma4_weights.cpp:210-215`) from a per-expert `F8_E4M3` export. No such Gemma-4 checkpoint is pinned anywhere in `docs/USAGE.md`: all 32 lines there matching `gemma` case-insensitively were swept, and every checkpoint among them is the LTX-2.5 text tower (`gemma4-12b-with-proj*.safetensors`), not a per-expert FP8 MoE decoder. The woken path cannot be exercised, here or elsewhere, until one is. 2. **The CUDA consumers are not the ones the ROCm arm measured.** `ExpertGeGLUDeviceAccum` (`gemma4_moe.cpp:76-93`) is generic `vt::MatmulBT` / `GeluAndMul` / `MatmulBTAlphaBeta` and would run. But `RunGemma4FusedTopkExpertGeGLU` (`:1599`), `ExpertGeGLUDeviceBatched` (`:240`), `PeerCopyGemma4Fp8ExpertSlice` and `RunGemma4Fp8TopKOnExpertDevice` are HIP-only, and a non-HIP build links stubs that return `false`. The override would admit experts to VRAM and then route them through the serial per-expert fallback: a third path, measured by nobody. Nor is the swap token-neutral by construction — the resident arm accumulates with `vt::MatmulBTAlphaBeta` (`:90`) where the fallback writes its output outright (`:70`), so the two sum the top-k experts in a different order. 3. **The headroom test does not mean the same thing on the CUDA device this project gates on.** `MakeRoom` admits iff `free_b >= need + 1.5 GiB` (`gemma4_moe.cpp:514`), a constant tuned on discrete dual R9700s where free VRAM is distinct from host RAM. On a GB10 it is not. #1123 measured that `cudaMemGetInfo`'s `total` there is exactly `/proc/meminfo MemTotal` times 1024, which is why it reached for that instrument where `nvidia-smi` answers `[N/A]`; `free` is therefore reported over the same unified pool, and the host BF16 expert cache the device upload exists to relieve is drawn from that pool too. The admission test would double-count, on a box whose unified-memory OOM takes the host down with it. The `free` half of that is an inference from the measured `total` rather than a second measurement, and it wants confirming on the device before any override lands. 4. **The gap is isolated, not a pattern.** Comparing overrides one by one across `src/vt/cuda/cuda_backend.cu` and `src/vt/rocm/rocm_backend.hip`, `DeviceMemoryInfo` is the only optional `vt::Backend` seam ROCm answers and CUDA does not. A dead path with a truthful comment beats a live path nobody has exercised, and points 2 and 3 say the live path would not even be the one the lab throughput note was taken on. ## Which answer to "how much device memory" is authoritative The tree holds two, and they are not duplication. - `vt::Backend::DeviceMemoryInfo(free, total)` is a **live** probe that moves with contention. Authoritative for a **runtime admission** decision — can this allocation succeed right now — and for nothing else. - `vllm::platforms::ResidencyPolicy::device_memory_total_bytes` is a **total** probed once at platform registration, `0 == UNKNOWN`. Authoritative for a **load-time budget** verdict, which has to be reproducible and independent of whatever else the box is doing. This makes #1126's own closing plan wrong at step 3, which proposes that the #1123 fit check "can then read the budget from the backend seam". Collapsing the second onto the first would make the same checkpoint load or be refused depending on the page cache, which is the property `total`-not-`free` was chosen to avoid. What #1126 owes is its steps 1 and 2 **together**, the override and the Gemma4 measurement; never step 1 alone, and never step 3. Both seams already carry the division in prose (`backend.h:90-93`, `platforms/interface.h:61-69`); it is restated in `## Owed` because that is the record a reader of the issue acts on. ## A second defect, filed in flow Reading `MakeRoom` line by line turned up #1197. The slot cap is tested before the eviction loop (`gemma4_moe.cpp:498` against `:499-500`) and `EvictOne` (`:457`) is the only thing that shrinks `slots`, so after 24 admissions `VT_GEMMA4_EXPERT_EVICT=1` is a no-op for the life of the process and the cache degrades permanently to fill-only. It binds only when the slot cap is reached before the byte budget. Filed and not fixed, for the same reason as above rather than for effort: the one-line repair wakes more `hipFree` under load, which the surrounding comments call a permanent `kfd_wait` hang, so the current ordering may be deliberate. It is owned by `ENG-EXPERT-STREAM` and listed under `## Owed`. ## Why this is a record-only change The record is the work here: a filed gap whose closing plan is wrong in two ways, and a newly filed defect. It does not restate what #1132 landed — points 1 through 4, the authority split and #1197 are all new. Leaving it out means the next agent implements step 3 as written. No code changed, so there is nothing to mutate and no behaviour to gate. The gate run is the checker suite. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…phaBeta`, not a slower third path, and the arm now refuses by name (#1126, #1205) A fresh review returned FAIL on the record this repairs. The decision it argued for was right and five of its six substantive claims survive. Its central technical paragraph was wrong, and on a record-only change a wrong record is the defect. ## The finding that inverts it `vt::MatmulBTAlphaBeta` has NO CUDA implementation. `src/vt/fused_ops.cpp:102` dispatches to `rocm::MatmulBTAlphaBetaRocm` (`rocm_matmul_hipblaslt.hip:516`) under `#if defined(VLLM_CPP_HIP)` AND `q.device.type == kROCM`; every other device falls through to a throw. There is no CUDA, Vulkan, Metal or CPU arm. So the record's claim that `ExpertGeGLUDeviceAccum` "would run" on CUDA is false. What the override wakes is: `EnsureGemma4Fp8ExpertOnDevice` (`gemma4_moe.cpp:548-586`, no HIP guard) -> `lru.MakeRoom` at `:565` succeeding once `FreeBytes` can answer -> `true` at `:575` -> the call site at `:1486` -> `ExpertGeGLUDeviceAccum` at `:1487` -> `vt::MatmulBTAlphaBeta` at `:90` -> throw. The `try`/`catch (...)` at `:563-585` wraps only the upload; the compute at `:1487` is outside it, so the exception leaves the decode step rather than degrading to the host fallback. It is latent today only because the other route into that function, `same_dev` (`:730-731`), needs `ex.gate_up_dev`, assigned nowhere but `rocm_gemma4_experts.hip:207,226`. The resident arm is unreachable off ROCm, not safe. That makes the missing CUDA arm the actual blocker for #1126 step 1, recorded nowhere, and a stronger argument than any of the record's four. Filed as #1205. ## Three further corrections `ExpertGeGLUDeviceBatched` (`:240`) was named beside three genuine HIP-only stubs. It is not one: it sits outside the file's only `#ifndef VLLM_CPP_HIP` block (`:1574-1628`), has no HIP implementation and no header declaration, and is unconditionally `return false` under its own lab note at `:237-239`. It is disabled everywhere, ROCm included, so listing it inflated the ROCm/CUDA asymmetry. The other three are correct and stay. "The two arms sum the top-k experts in a different order" is false. Both run inside the same `for (int i = 0; i < top_k; ++i)` at `:1431`. The real difference is rounding and where the routing weight is applied: the resident arm folds `ww` into the GEMM `alpha` and accumulates in the epilogue with `beta` (`:1434`, `:1442`, `:90`), while the fallback writes the unweighted product to a BF16 buffer (`:67`, `:70`) and applies `ww` afterwards with separate BF16 kernels (`MulScalar` at `:1524`, `MulScalar` plus `Add` at `:1526-1527`). Two extra BF16 roundings per expert, weight applied post-rounding. Right conclusion, wrong mechanism. "Never step 3" overstated the case, in the spec and in the previous title. #1126 step 3 proposes the fit check read the budget from the backend seam, and that seam returns BOTH `free` and `total` (`include/vt/backend.h:94`). Reading `total` is contention-independent and legitimate. The defensible statement, and the one the row now asserts: step 3 may read only `total`, never `free`, and must keep the value probed once at platform registration (`include/vllm/platforms/interface.h:70-72`). Two smaller ones. `DevExpertLru::Note` also calls `slots.clear()` at `:522`; it is a device-index reset rather than an eviction, frees nothing, and is unreachable in a single-device process, so the #1197 row now names it and the next reader does not conclude the sweep missed it. And the H2D cost the row states in the present tense is conditional: the device LRU is `ex.is_fp8`-gated (`:969`, `:1484`), and by the row's own point (1) no per-expert FP8 Gemma-4 checkpoint is pinned, so on CUDA the fallback is never even asked for today. ## The code this uncovered `fused_ops.cpp` threw a bare "ROCm-only in this build" mid-decode for an unimplemented arm. AGENTS.md requires an unimplemented arm to refuse with a message that names the missing part, and that one named neither the device that asked nor where an implementation would go, so a caller could not tell a missing kernel from a missing build flag. It now names all three plus the issue. `tests/vt/test_gemma4_rocm_fp8_seams.cpp` gates it on a posed CUDA queue and on `kCPU`, `kVULKAN` and `kMETAL` — the refusal reads only `q.device.type`, so a CPU build can pose as any device and no ROCm hardware is needed. This is an in-flow gateable defect independent of #1126, and when someone writes the CUDA implementation the same test is what it has to satisfy. It pins the precondition without any checker that says do not fix it. Red before green: the three message assertions and the three device-name assertions failed against the old message. Mutation-proven twice, both compiled, both applied, three cases each, tree restored byte-exact: restoring the old message goes RED, and deleting the refusal outright goes RED. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
DeviceMemoryInfo override is not a one-line port, and #1126's own step 3 would be a defect (#1126, #1197)vt::MatmulBTAlphaBeta, not a slower third path, and the arm now refuses by name (#1126, #1205, #1197)
`1f4878fdc` (SPEC-DSPARK-QWEN3-ROUTING) landed while this row's review repair was in flight, so the preflight trailer and style gates skipped rather than reporting on this tree. Only `.agents/issue-index.md` overlapped, and it union-merged; the #1205 row this branch appends and the #1193 row main appends are both present and neither was edited. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
… row at the right numbered point (#1205) The `## Owed` rows now say the review repair re-audited every anchor the three rows cite against the repaired tree, 53 examined and 53 exact, so a reader can tell a re-verified anchor from a carried-over one. The #1205 row said "this row's point (1)" for a point that lives in the `DeviceMemoryInfo` row above it. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…e it happens, and stop telling a ROCm caller the kernel is missing (#1126, #1205, #1197, #1218) FOLLOWING_AGENTS_PROTOCOL The review of #1200 returned FAIL on three findings and recommended a fourth change. This is the repair, and the recommended change is the substance of it. THE GUARD. #1205 named a cheaper and reachable fix that the first pass did not take: refuse at the `gemma4_moe.cpp` call site BEFORE the upload rather than throwing after it. `EnsureGemma4Fp8ExpertOnDevice` now returns false at `gemma4_moe.cpp:571` when `vt::HasMatmulBTAlphaBeta(d.q)` is false, so the caller takes the host fallback already sitting in the `else` at `:1515-1521` and the decode step answers instead of throwing mid-decode. That matters more than a better message: the reviewer's M3 deleted the production call site at `gemma4_moe.cpp:90` and the old focused gate stayed fully green, because it constructed a `vt::Queue` by hand and so measured the class rather than the capability. The predicate is not a device-name test and not a build-macro test. `vt::MatmulBTAlphaBeta` dispatches on `HasMatmulBTAlphaBeta` itself (`src/vt/fused_ops.cpp:102-109`, dispatch at `:117`), so the two cannot disagree, and writing a CUDA arm wakes the device path with no edit at the call site. `tests/vllm/models/test_gemma4_moe_device_arm_guard.cpp` gates it through `vllm::RunGemma4Moe` — the layer entry `gemma4.cpp:634` calls — with a real `Gemma4MoeLayerWeights`, the real router, the real LRU and the real fallback. It decorates the registered CPU backend so `vt::Backend::DeviceMemoryInfo` ANSWERS, because that is the post-#1126 state and the only state in which the guard binds at all: on a stock build `MakeRoom` refuses on the absent probe, and deleting the guard would change nothing observable. Both directions of the probe are asserted before anything is concluded, so an instrument that failed to arm cannot report the guard proven by a run that never reached it. Deleting the guard turns that test RED with the exact `no implementation for device 'cpu'` throw; forcing the predicate to `true` turns it RED too. F1. The new refusal was wrong for the one case the old one got right. A kROCM queue in a build without `-DVLLM_CPP_HIP` falls through the same `#if`, and the message told it a kernel was missing and pointed it at a CUDA issue. It now gets its own message naming the absent BUILD FLAG, and the seams test poses kROCM alongside the four devices that made the old message look right. Say plainly what that test pins: a contract in a unit test, not observable behaviour. With the guard in place no production run off ROCm reaches either string. F2, F4, F6. #1205's issue body anchors matched `0acf2ca1d` and were eleven lines stale at `7beada17c`; this change moves them again by twenty-two. Every anchor in the cluster was re-derived against the final tree — 168 endpoints across nine rows, none out of range. `fused_ops.cpp:102` was the signature and the dispatch is at `:117`. The audit headline now states its SCOPE, because "53 examined, 53 exact, 0 stale" covered four spec rows and never included an issue body, which is exactly how the body went stale unnoticed. Several bare `:NNN` anchors also inherited the wrong file after a mid-sentence mention of a different one; those are spelled out. #1218 is filed and deliberately NOT fixed here. `EnsureGemma4Fp8NativeOnDevice` is the DEFAULT arm and carries the same shape, but it depends on three different ops and needs a predicate per op; reusing `HasMatmulBTAlphaBeta` there would be a guard naming the wrong arm, which is the exact defect this review just corrected in a refusal message. It is owned by `ENG-EXPERT-STREAM` and listed under `## Owed`. Two `.agents/issue-index.md` rows are EDITED rather than appended. Both were added by this branch and have not merged, so no other branch can hold a different version of them, and the append-only rule protects merged rows against a duplicating union merge. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [Claude Code]
FOLLOWING_AGENTS_PROTOCOL Bring the branch level with origin/main so the commit-trailer and commit-style gates run instead of skipping. Only .agents/issue-index.md differs, and it carries merge=union. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [Claude Code]
…kend after main, and name gemma4.cpp in full (#1205) FOLLOWING_AGENTS_PROTOCOL `vt::RegisterBackend` documents that all registration completes before `main()`, and the guard test calls it after. The contract buys the absence of synchronization on the read path, and this binary is single-threaded across the swap, so there is no concurrent reader to race — but a reviewer should not have to derive that, and an undiscussed contract violation reads as one that was not noticed. `gemma4.cpp` is also spelled in full. Two files in this tree carry that name and the shorter form resolved to the 123-line tool parser, where `:634` is out of range — the same class of defect as a stale line number. Comment-only: the diff contains no non-comment line. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [Claude Code]
FOLLOWING_AGENTS_PROTOCOL Bring the branch level with origin/main so the commit-trailer and commit-style gates run instead of skipping; a skipped gate reported nothing about this tree. `src/vt/rocm/rocm_backend.hip` moved on main and this row's records anchor `DeviceMemoryInfo` there, so every anchor is re-derived against the merged tree rather than assumed to have held. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [Claude Code]
…merge moved, and check anchors semantically rather than only for range (#1205) FOLLOWING_AGENTS_PROTOCOL Merging origin/main moved `RocmBackend::DeviceMemoryInfo` from `:338-345` to `:358-365`, and five citations across the spec and the issue index still pointed at the old lines. That is this row's third stale-anchor defect, and the first one caused by somebody else's diff rather than its own. The range-only audit did not catch it. An anchor that is still IN RANGE can point at the wrong line, and a checker that only asks "does this line exist" reports OK for exactly the failure it was written to find. The audit is now two instruments: 168 endpoints resolved for range, and 69 load-bearing anchors asserted against the substring the prose claims lives there. The second one is what found this. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [Claude Code]
…-byte (#1205) FOLLOWING_AGENTS_PROTOCOL Re-deriving the moved `rocm_backend.hip` anchor was done with a blanket string replace over `.agents/issue-index.md`, and it hit the `#1126` row as well as this branch's own. That row is already on `main`. Editing a merged row is exactly what the append-only rule forbids, because the union driver DUPLICATES an edited line instead of merging it — the gate caught it, and it was right to. The row is restored from `origin/main`, and the file now removes or edits nothing that main has: it adds three rows, #1197, #1205 and #1218, all appended by this branch. Those three are still editable because they have not merged, so no other branch can hold a different version of them. The merged `#1126` row keeps its pre-move anchor deliberately. One stale number in an append-only log is a smaller problem than a duplicated row, and the spec's `## Owed` entry carries the current anchor and says so. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [Claude Code]
FOLLOWING_AGENTS_PROTOCOL Level the branch with origin/main again so the trailer and style gates run instead of skipping. This merge brings a compiler-warning change (`cmake/CompilerWarnings.cmake`, the gcc 16 lane), so the tree is rebuilt and re-gated rather than assumed to still compile: a clean merge is not a merge that builds. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [Claude Code]
…1205) FOLLOWING_AGENTS_PROTOCOL `.agents/issue-index.md` carried two byte-identical `#1193` rows after merging origin/main, and `check-agent-record.py` refused: under `merge=union` a duplicate is what two branches appending the same issue look like, so the checker cannot tell that case from this one and is right to stop. The two copies were compared before either was touched, and only then was the stray one removed. The surviving row is the one whose position matches origin/main's ordering (after `#1190`); the deleted one sat inside this branch's own appended block, which is where the union driver placed it. The file now removes and edits nothing that main has, adds exactly `#1197`, `#1205` and `#1218`, and contains no duplicate row. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [Claude Code]
FOLLOWING_AGENTS_PROTOCOL Level the branch with origin/main so the trailer and style gates run against it rather than skipping. This merge brings `scripts/check-symbol-anchors.py`, a gate on exactly the failure this row has now produced three times, so it is run over this branch's records rather than merely inherited. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [Claude Code]
FOLLOWING_AGENTS_PROTOCOL Level the branch with origin/main so the trailer and style gates keep reporting rather than skipping. This merge carries #1234, which repairs the `test_check_gate_commands` red this branch inherited: that gate failed here on three files whose blob SHAs were identical to main's, so it was never this change, and it is now fixed at the source rather than worked around. No source file this row anchors is touched by the merge, and every anchor is re-checked against the merged tree anyway. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [Claude Code]
vt::MatmulBTAlphaBeta, not a slower third path, and the arm now refuses by name (#1126, #1205, #1197)vt::MatmulBTAlphaBeta, and the Gemma-4 device-expert upload now refuses before it happens (#1126, #1205, #1197, #1218)
localai-bot
pushed a commit
that referenced
this pull request
Aug 18, 2026
`origin/main` advanced to 65d6cda while this branch's review repair was being written: the gcc 16 build lane, the ROCm attention-backend selection, and `vt::MatmulFp8BlockScaled`. Sixty-five files on the incoming side, mostly additive. Four files are edited on both sides — `CMakeLists.txt`, `tests/CMakeLists.txt`, `include/vllm.h` and `.agents/issue-index.md` — and git reported no conflict in any of them. `.agents/specs/expert-streaming.md` is the one to watch, because open PR #1200 edits its `## Owed` table two rows above this branch's #1127 row; #1200 has not landed yet, so this merge does not meet it. If it lands first, the next merge takes the target-branch version of that file whole and re-applies the one scoped row, as AGENTS.md requires for a keyed record — never the automatic three-way result. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
localai-bot
pushed a commit
that referenced
this pull request
Aug 18, 2026
`origin/main` advanced to c20018f while the review repair's gate was running: the CUDA-graph dedup device record, and `GATE-SYMBOL-ANCHORS`, which replaces `file:line` citations with symbol citations and adds a checker for them. Three files are edited on both sides. `tests/vllm/entrypoints/test_gguf_device_fit_reach.cpp` takes an anchor rewrite at line 62 on the incoming side and this branch's "These two / three cases" correction at line 321; git reported no conflict and the two hunks are 250 lines apart. `docs/USAGE.md` and `.agents/issue-index.md` likewise merge without a conflict. `.agents/specs/expert-streaming.md` is untouched on the incoming side, so the #1127 `## Owed` row this branch edits still needs no manual reconciliation; PR #1200, which edits that table two rows above, is still open. The full gate is rerun on the merged tree rather than inherited from the pre-merge run, because a clean merge is not a merge that builds. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
The base moved while the guard was under review and repair. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
localai-bot
pushed a commit
that referenced
this pull request
Aug 18, 2026
The base moved while W2 was under review and repair, and #1200 landed edits to `.agents/specs/expert-streaming.md` two rows above this row's own entry. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code] # Conflicts: # .agents/specs/expert-streaming.md
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.
FOLLOWING_AGENTS_PROTOCOL
#1126says addingCudaBackend::DeviceMemoryInfowould letGemma4MoE'sdevice-expert LRU stop refusing on CUDA and wake a slower third path. Reading the
chain to establish that claim found something else: it would wake a throw, in
the middle of a decode step.
vt::MatmulBTAlphaBetahas no CUDA implementation atall, and that is what step 1 is actually blocked on. That is
#1205, and it wasrecorded nowhere.
What lands
The hazard is guarded, before the upload rather than after it.
EnsureGemma4Fp8ExpertOnDevicereturns false atsrc/vllm/model_executor/models/gemma4_moe.cpp:571whenvt::HasMatmulBTAlphaBeta(d.q)is false, so the caller takes the host fallbackalready sitting in the
elseat:1515-1521—EnsureGemma4Fp8ExpertCachedplusExpertGeGLUHost, slower and two extra BF16 roundings per expert — and the stepanswers instead of throwing. The upload's own
try/catch (...)at:585-607never covered the compute, so without this the exception left the decode step
rather than degrading.
The predicate is not a device-name test and not a build-macro test.
vt::MatmulBTAlphaBetadispatches onHasMatmulBTAlphaBetaitself(
src/vt/fused_ops.cpp:102-109, dispatch at:117), so the two cannot disagree:false there means the very next line throws, and writing a CUDA arm wakes the
device path in one edit with nothing to change at the call site.
The refusal names the right absence for each caller. "ROCm-only in this build"
named neither the device that asked nor where an implementation would go, so a
caller on CUDA could not tell a missing kernel from a missing build flag. The two
messages now are:
The second is for a kROCM queue, which reaches the same line in a build configured
without
-DVLLM_CPP_HIP. For that caller the kernel exists and the flag ismissing; telling them to write a kernel the tree already has would send them to fix
the wrong thing. The old message got that one case right and the first draft of
this change broke it.
Say plainly what the message gate pins: a contract in a unit test, not observable
behaviour. With the guard in place the throw is unreachable off ROCm in any
shipped configuration, so no production run prints either string. What
tests/vt/test_gemma4_rocm_fp8_seams.cppfixes is what a CUDA implementation hasto satisfy when someone writes one. That limitation is recorded under
## Owedrather than left for a reader to discover.
Four records are corrected, and one of them was the reason for this branch. The
comment on
vt::Backend::DeviceMemoryInfoclaimed "(ROCm/CUDA)" in both placesthat carried it.
#1126's own closing plan, step 3, would source a load-timebudget verdict from a live contention-dependent probe; it is narrowed here to
totalonly, probed once at platform registration, rather than rejected outright.#1197and#1205are filed with their chains traced.How it is proven
tests/vllm/models/test_gemma4_moe_device_arm_guard.cppenters throughvllm::RunGemma4Moe— the layer entrysrc/vllm/model_executor/models/gemma4.cpp:634calls — with a realGemma4MoeLayerWeights, the real router, the real device-expert LRU and the realfallback. Nothing in it constructs the LRU, the
Devorvt::MatmulBTAlphaBetabyhand.
It decorates the registered CPU backend so
vt::Backend::DeviceMemoryInfoanswers, and changes nothing else. That is the whole point: the hazard is latent on
every machine that can run this suite, because
MakeRoomrefuses unless that probeanswers and only ROCm overrides it. On a stock build the memory probe refuses the
upload and the guard never binds, so deleting the guard would be invisible — an
unmoved-mutation trap, not a proof.
#1126step 1 IS the change that makes theprobe answer on a device with no
MatmulBTAlphaBetaarm, so the test puts the treein exactly that state. Both directions of the probe are asserted before anything is
concluded, so an instrument that failed to arm cannot report the guard proven by a
run that never reached it.
Mutations on the merged tree. Each was applied, asserted applied by sha256 and
git diff --stat, rebuilt (a mutation that does not build is INVALID, not a pass),run with its exit code captured directly rather than through a pipe, then restored
and re-asserted by sha256. Every one compiled.
"ROCm-only in this build"messagereturn;)gemma4_moe.cpp:90HasMatmulBTAlphaBetatotruegemma4_moe.cpp:90M5 is the reachability proof, and its failure text is the evidence: the guard test
goes red with
vt::MatmulBTAlphaBeta: no implementation for device 'cpu'thrownout of
vllm::RunGemma4Moe. M4 shows the guard consults the predicate on theexecuted path rather than passing for some incidental reason. M7 shows
:90is notthe only production route into the refusal — with
:90gone the chain stillreaches it through
ExpertGeGLUTopKFusedGelu.M3 is reported as it came out, because it is the finding that started this
repair. Deleting
gemma4_moe.cpp:90leaves both suites green. For the guard testthat is correct and expected: with the guard in place the layer never reaches
:90at all, which is the point of the guard. For the seams suite it is the honest
consequence of what that suite is — a unit contract on a message that no correct
program reaches. A refusal a correct program never reaches has no production path
by construction, so no mutation can make one appear; the alternative would be
leaving the hazard unguarded so the string could be observed. That is stated under
## Owedrather than presented as reach.Green after restore:
test_gemma4_rocm_fp8_seams4 cases / 37 assertions rc 0,test_gemma4_moe_device_arm_guard1 case / 11 assertions rc 0. Both source filesback to their exact pre-mutation sha256.
Full gate on the merged tree:
cmake --build build -j 8rc 0, 0 ENOSPC,0 errors;
ctest --test-dir build -j 4523/523 passed, 0 failed, rc 0, with the twopre-existing checkpoint-gated skips (
test_modelopt_mixed_precision_checkpoint,test_voxtral_e2e). An earlier merge in this sequence changedcmake/CompilerWarnings.cmake, so that round was a clean rebuild from an emptybuild/rather than an incremental one, and it was also 523/523 rc 0.scripts/check-symbol-anchors.py, which arrived onmainduring this work andgates exactly the failure mode this row keeps producing, is green over these
records: 618 citations, in-repo checked 93, fresh 93, stale 0.
test_cpu_x86_llamacpp_floorfailed once during this session under box load andprinted its own precondition:
NO_QUIET_WINDOW after 30s (busy=110% builders=0 load=42.82 50.24 77.96)on a 20-core host — the load-dependent red of#618. Itis isolated rather than dismissed: the three files it reads have identical blob
SHAs on this branch and on
origin/main(
scripts/cpu-x86-llamacpp-floor.shbd7a5192…,docs/bench-evidence/cpu-x86-llamacpp-20260811.md94f8509c…,tests/scripts/test_cpu_x86_llamacpp_floor.py358927d1…), so this branch cannotmove it, and it passed on the quieter reruns.
What is owed
#1218—EnsureGemma4Fp8NativeOnDevice(gemma4_moe.cpp:611) has the same shapeand no guard, and it is the default arm:
VT_GEMMA4_FP8_NATIVEreturns truewhen unset (
:969-974), so#1126step 1 wakes it before it wakes the guarded one.Filed and not fixed here, and not for effort: the BF16 guard keys on ONE predicate
that is the same condition its own dispatch uses, which is what makes it honest,
whereas the twin depends on three different ops and needs a predicate per op.
Reusing
HasMatmulBTAlphaBetathere would be a guard naming the wrong arm — theexact defect this branch's review corrected in a refusal message.
#1205— the CUDAMatmulBTAlphaBetaitself. Abeta-accumulating BT GEMM oncuBLASLt is a kernel with its own correctness gate, no per-expert FP8 Gemma-4
checkpoint is pinned anywhere in
docs/USAGE.mdto exercise it on, and the hostthat found this has neither a ROCm nor a CUDA device.
#1197— the device LRU's slot cap sits ahead of its eviction loop, soVT_GEMMA4_EXPERT_EVICT=1goes inert after 24 admissions. Filed and not fixed: theone-line repair wakes more
hipFreeunder load, which the surrounding commentsrecord as a permanent
kfd_waithang, and deciding that needs the dual-RDNA4 box.Anchors
This row has now produced three stale-anchor defects, and the third was not caused
by its own diff. The audit therefore states its scope and uses two instruments.
The first repair covered the four
## Owedrows in this cluster: 53 examined, 53exact, 0 stale. It never included a GitHub issue body, which is exactly how
#1205's body came to be eleven lines stale at7beada17c. This repair re-derivedevery anchor in those four rows, in the two new
## Owedrows, in the#1197,#1205and#1218index rows and in the#1205and#1218issue bodies:168 endpoints across nine rows resolved for range, 0 out of range, plus 69
load-bearing anchors asserted against the substring the prose claims lives at that
line, 0 stale.
The second instrument is what earned its place. Merging
origin/mainmovedRocmBackend::DeviceMemoryInfofrom:338-345to:358-365, and the range-onlycheck reported OK because the old lines still existed — a checker that only asks
"does this line exist" passes exactly the failure it was written to find.
Several bare
:NNNanchors also inherited the wrong file after a mid-sentencemention of a different one;
gemma4.cpp:634in particular resolved to the 123-linetool parser, where it is out of range. Those are spelled out in full.
Three
.agents/issue-index.mdrows are appended:#1197,#1205and#1218. Allthree were added by this branch and have not merged, so editing them while the pull
request is open cannot duplicate anything. Two index defects were caught by the
gates rather than by review and are repaired: the anchor sweep edited the MERGED
#1126row, which is restored byte-for-byte fromorigin/mainand deliberatelykeeps its pre-move anchor because a duplicated row is worse than one stale number;
and a union merge left two byte-identical
#1193rows, one of which is removed.The file now removes and edits nothing that
mainhas, adds exactly three rows,and contains no duplicate.
Closes nothing.
#1126,#1197,#1205and#1218all stay open with theirremaining work stated above.
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [Claude Code]