spec(#810 A2-Q): split the quantized arms into A2-Q1 and A2-Q2, because their blockers differ (#517) - #988
Merged
Merged
Conversation
…se their blockers differ (#517) A2-Q was briefed as one unit covering NemotronH's mamba, MoE and lm_head device arms. Scoping it against the tree measured three independent quantization mechanisms with three independent failure modes, and the reason to split is not size. The two halves have DIFFERENT blockers. A2-Q1 (FP8 W8A8 mamba) cannot run on Thor at all until #960 lands. A2-Q2 (NVFP4 MoE + lm_head) builds and runs there today, but cannot yet be trusted to report a number (#962). Bundling them makes the NVFP4 half wait on a build-file fix it does not need, so this commit gives each half its own spec and its own declared dependency. The measured Thor finding is recorded in full in A2-Q1 §3, because it inverted twice during scoping and cannot be re-derived cheaply. Thor has HALF an fp8 arm, denied by a build accident rather than by silicon: * The GEMM half works. A standalone probe mirroring BuildFp8Plan's exact key (cuda_matmul.cu:533) on real sm_110 silicon returns an fp8-e4m3 TN heuristic for all six NemotronH mamba shapes at both bf16 and f32 D -- 12 of 12 plans available. * The activation-quant half is missing. kQuantFp8Static's only CUDA registration is cuda_matmul_fp8_cutlass.cu:376, and CMakeLists.txt :1668-1669 compiles that TU only when VT_CUTLASS_FP8_ARCHS is non-empty -- empty on sm_110. The kernel body (:353-370) is a plain elementwise scale-and-convert with zero CUTLASS references. It is trapped in a CUTLASS-gated TU while its GEMM partner is available on the same arch. MatmulFp8CutlassD's guard keys on kMatmulFp8CublasLt, which IS registered, so the guard passes on Thor and the code crashes one call later on the portable reference tier -- which is what #960 measured. The relocation unblocks every non-CUTLASS CUDA arch, not just Thor and not just this model, so it is dispatched as its own unit and A2-Q1 declares it a base rather than carrying it. The dependency is written against the ISSUE: on 2026-08-16 `git ls-remote --heads origin` showed no ref for the dispatched row, and a base has to be something git can reach. Two corrections to the brief are recorded rather than quietly applied. The MoE arm does NOT take a per-expert [K,N] pointer array -- that is kMoeGroupedGemmBf16. MoeGroupedGemmNvfp4Marlin takes a rank-3 strided arena [E, K/16, N*8/pack], validated at src/vt/ops.cpp:884, needing a load-time repack measured at ~16.5 GB device-resident across the 23 MoE layers. And PrepareNemotronHForCausalLM (nemotron_h_registry.cpp:113-118) is a pure no-op that must be implemented to host that repack pre-capture, which makes it a shared-seam change rather than a model-local detail. A2-Q2 §4.3 turns the lm_head residency into an explicit decision. There are two functions named MarlinDenseResidentFor with different safety, selected by translation unit: dense_nvfp4_gemm.h:379 keys a process-static cache on the weight's ADDRESS, while qwen3_5.cpp:2429 uses the resident_marlin ResidentSlot that #237 added to fix exactly that. The shared seam holds the unsafe one, so following the seam gets you the defect and hand-rolling gets you the fix. Filed as #984; A2-Q2 is the second-consumer condition an address key cannot survive, so it must choose deliberately instead of defaulting. Both specs keep all three G-SAFE clauses at nemotron_h_registry.cpp:162 intact, and both inherit A2-R's unreached posture -- the device forward still has no production caller until A2-P, which is stated in each spec's Owed section rather than left to be discovered. No product code and no lifecycle change. Per the governing spec's §1.4 the implementation is a separate PR by a different agent and the reviewer is a third, which is why this spec is written by the agent that scoped the work and will not implement it. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
Concurrent landing. #985 touches .agents/specs/expert-streaming.md and this branch touches only the three NemotronH spec files, so the trees are disjoint. Re-gated after the merge rather than read from the diff: a clean merge is not a merge that builds the behaviour either side had (#818). 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 16, 2026
`origin/main` advanced to `4496ef196` (#988). It touches three NemotronH spec files and nothing else — no source, no test, no build file and no keyed record — so this merge changes nothing this row builds or gates, and the full gate above still describes the tree. Stated rather than assumed: `git diff --name-only e770a39 4496ef1` returns `.agents/specs/nemotron-h-a2q1-fp8-mamba.md`, `.agents/specs/nemotron-h-a2q2-nvfp4-moe-lmhead.md` and `.agents/specs/nemotron-h-abi-e2e.md`, and no fourth path. 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 16, 2026
…# Now` The `## Now` line said "landed on `main`", which it is not: it is open as #991, measured on both gate hosts, awaiting a fresh review and an operator merge. A `## Now` that anticipates its own landing is exactly the record that cannot be trusted afterwards -- the whole point of the line is that a reader learns the position without asking anyone. It also names the only red lanes and why they are not this row's: `windows-msvc-cpu`/`windows-msvc-vulkan` fail at the identical step on #988 and #982, which share no code with this change, so the arm is matched rather than assumed; the cause is #968 under #584's PR-only lane and a fix is already open as #983. Issue #960. 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 16, 2026
Pinned SHA, per #841. main moved four commits while this row was measuring and re-landing: #993, #982, #988 and #992. One of them is directly relevant -- #988 split #810's A2-Q into A2-Q1 and A2-Q2, and A2-Q1 is the unit this row exists to unblock -- and none of them touch the fp8 quant registration, the CUDA source list, or either checker this branch edits. `.agents/issue-index.md` is the only keyed record both sides wrote. Verified afterwards that origin/main's copy is a strict PREFIX of the merged file and that only this branch's own two rows follow it, which is what append-only means and what a clean-but-wrong interleave would break. 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 16, 2026
…nostic "Known-red" is not a measurement, and this file was carrying it as one. The two `windows-msvc-*` jobs now carry the diagnostic that failed them: `warning C4244: '=': conversion from 'const double' to 'float'`, raised inside MSVC's own `<vector>` while compiling `src/vllm/multimodal/ltx2_video.cpp`, promoted to `error C2220` by `/WX`. That is #968, its fix is already open as #983, and `main` carries no baseline for those jobs because #584 makes them PR-only. The matched arm is recorded too: #988 and #982 fail at the identical step and share no file with this row. Also records the post-merge full gate: 491/491 rather than 489/489, because `3ce1cf7c7` adds two suites. Quoting the old number after a merge that changed the denominator would be a stale measurement presented as a current one. Issue #960. 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.
Splits A2-Q of
.agents/specs/nemotron-h-abi-e2e.mdinto two units with their own specs. Issue #810, parent row #517. Records only: no product code, no lifecycle change.Per the governing spec's §1.4, the implementation is a separate PR by a different agent and the reviewer is a third. This spec is written by the agent that scoped the work and will not implement either half — a fresh implementer is not fresh if it authored the spec it works from.
Why split, and why it is not about size
A2-Q was briefed as one unit covering the mamba, MoE and
lm_headdevice arms. Scoping measured three independent quantization mechanisms with three independent failure modes — but the decisive reason is that the two halves have different blockers:nemotron-h-a2q1-fp8-mamba.mdlm_headnemotron-h-a2q2-nvfp4-moe-lmhead.mdBundling them makes the NVFP4 half wait on a build-file fix it does not need.
The Thor finding, measured
Recorded in full at A2-Q1 §3 because it inverted twice during scoping. Thor has half an fp8 arm, denied by a build accident rather than by silicon.
The GEMM half works. A standalone probe mirroring
BuildFp8Plan's exact key (src/vt/cuda/cuda_matmul.cu:533) — A = e4m3 opT, B = e4m3 opN,CUBLAS_COMPUTE_32F, scaleCUDA_R_32F, epilogue DEFAULT — on real sm_110 silicon (NVIDIA Thor, driver 13020, cublasLt 130002):The six shapes are NemotronH's own
in_projandout_projat decode, small-batch and prefill widths, plus a square control.The activation-quant half is missing:
MatmulFp8CutlassD's guard (dense_fp8_gemm.h:130) keys onkMatmulFp8CublasLt, which IS registered, so the guard passes on Thor and the code crashes one call later on the portable reference tier — which is what #960 measured as a SIGSEGV. The relocation unblocks every non-CUTLASS CUDA arch, so it is dispatched as its own unit; A2-Q1 declares it a base rather than carrying it.The dependency is written against the issue, not a branch name.
git ls-remote --heads originon 2026-08-16 showed no ref for the dispatched row — it is working locally, pre-push. A base has to be something git can reach.Two corrections to the brief, recorded rather than quietly applied
The MoE arm does not take a per-expert
[K,N]pointer array. That iskMoeGroupedGemmBf16(include/vt/ops.h:1642).MoeGroupedGemmNvfp4Marlin(:1685) takes a rank-3 strided arena[E, K/16, N*8/pack], validated atsrc/vt/ops.cpp:884, with processed scales and global scales. That needs a load-time repack, which the pointer array does not — measured at ~16.5 GB device-resident across the 23 MoE layers, with the per-slab arithmetic in A2-Q2 §3.PrepareNemotronHForCausalLMis a pure no-op (nemotron_h_registry.cpp:113-118, three(void)casts) and must be implemented to host that repack pre-capture, asPrepareQwen3_5Dense(qwen3_5_dense.cpp:104-115) does. That makes it a shared-seam change, not a model-local detail.The
lm_headresidency is now a decision, not a defaultThere are two functions named
MarlinDenseResidentFor, different safety, selected by translation unit:Each is used by the
MatmulNvfp4W4A16Din its own TU (:730and:2521). The shared seam holds the unsafe one, which inverts the incentive the seam rule creates: following the seam gets you the defect, hand-rolling gets you the fix, and the safe behaviour is not discoverable from the header. Filed as #984. A2-Q2 is exactly the second-consumer condition an address key cannot survive —lm_headplus 5935 expert projections through one header — so A2-Q2 §4.3 requires the choice to be argued in the commit rather than defaulted.G-SAFE and reachability
Both specs keep all three clauses at
src/vllm/model_executor/models/nemotron_h_registry.cpp:162—attn_kv.empty() && gdn_state.empty() && num_reqs <= 1— fully intact. Neither unit creates paging, carried state or batching. A2-P is where it narrows.Both inherit A2-R's unreached posture:
NemotronHDeviceForwardis called only fromtest_nemotron_h_forward.cpp:1805, and A2-P owns wiring it throughModelRegistry::Forward. Each spec states this under## Owedrather than leaving it to be discovered. A2-Q2 adds the nuance that itsPreparehook is reached from production load even while the forward that consumes it is not.Gates
scripts/agent-preflight.shgreen on this branch. Baseline re-measured at the pinned base0e1bee42fin a clean detached worktree: all 26 record gates green including the seven #873 ones, Release build 0 warnings 1441/1441,ctest -R "nemotron_h|ops_mamba2|ops_fp8|linear_method"13/13.Not touched
.agents/issue-index.mdis unchanged deliberately. #810 already has a row, and it is linked from both new specs and from this body, so the three places agree. #960, #962 and #984 were filed or dispatched by others; appending rows for them here risks the clean-but-wrong duplicate that append-only union merges produce when two branches record the same intent. They are instead listed under each spec's## Owed, which is whatcheck-agent-record.pyreads.Closes nothing. Unblocks the dispatch of both implementations to fresh agents.
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]