Skip to content

measure(#810 A3): the token gate ran on real weights and FAILED 6/96, and the failure names the mechanism - #1158

Closed
localai-bot wants to merge 2 commits into
mainfrom
row/MODEL-NEMOTRON-H-A3-RESULT
Closed

measure(#810 A3): the token gate ran on real weights and FAILED 6/96, and the failure names the mechanism#1158
localai-bot wants to merge 2 commits into
mainfrom
row/MODEL-NEMOTRON-H-A3-RESULT

Conversation

@localai-bot

Copy link
Copy Markdown
Collaborator

measure(#810 A3): the token gate ran on real weights and FAILED 6/96, and the failure names the mechanism

The A3 end-to-end gate has run for the first time on the released
nemotron-3.5-lightning-30b-nvfp4 checkpoint, on GB10 sm_121a, through
include/vllm.h and nothing else. GATE_RC=1. Six of ninety-six tokens match.

Token 0 is correct on all three prompts, and decode then decays and collapses
onto the repeated id 1050 by about step five. That shape is the finding. Token 0
comes from prefill; every token after it comes from a decode step that must read
what the previous step wrote. Numeric drift wanders, it does not converge on one
id and sit there, so this is the recurrent carry rather than a noisy kernel, and
#1157 records where to look plus a cheap discriminator between the two halves.

This row's own documents predicted it. The parent spec matched 3/3 FIRST tokens
against a forward carrying no state at all and warned how little that proves;
this run reproduces 3/3 first tokens and then fails. A2-P's section 10 already
recorded that the synthetic fixture could not see the recurrent carry, and the
two numeric cases added to compensate turn out to be insufficient: the CPU gate
is 12/12 green at the very commit that produces this. A green class and a wrong
capability is the reachability failure this tree keeps naming, and it now has a
measurement instead of a warning.

Nothing about the build can explain it. CUDA 13.3.73, CFG_RC=0, VOID_FLAG=0 with
cutlass-nvfp4, cutlass-fp8, marlin-nvfp4 and fa2 all ENABLED for [121a] rather
than [121] or DISABLED, BUILD_RC=0, compile_errors=0. Nor was the instrument
mute: compared=96 over three prompts of width 32, full rows 3, short rows 0. A
run that had compared fewer would have exited 4 instead of 1.

Recorded as a result rather than as silence, and no band was widened to make it
read better. Repair is owed to a fresh implementer, because this row wrote the
driver that found it.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]

… and the failure names the mechanism

The A3 end-to-end gate has run for the first time on the released
nemotron-3.5-lightning-30b-nvfp4 checkpoint, on GB10 sm_121a, through
include/vllm.h and nothing else. GATE_RC=1. Six of ninety-six tokens match.

Token 0 is correct on all three prompts, and decode then decays and collapses
onto the repeated id 1050 by about step five. That shape is the finding. Token 0
comes from prefill; every token after it comes from a decode step that must read
what the previous step wrote. Numeric drift wanders, it does not converge on one
id and sit there, so this is the recurrent carry rather than a noisy kernel, and
#1157 records where to look plus a cheap discriminator between the two halves.

This row's own documents predicted it. The parent spec matched 3/3 FIRST tokens
against a forward carrying no state at all and warned how little that proves;
this run reproduces 3/3 first tokens and then fails. A2-P's section 10 already
recorded that the synthetic fixture could not see the recurrent carry, and the
two numeric cases added to compensate turn out to be insufficient: the CPU gate
is 12/12 green at the very commit that produces this. A green class and a wrong
capability is the reachability failure this tree keeps naming, and it now has a
measurement instead of a warning.

Nothing about the build can explain it. CUDA 13.3.73, CFG_RC=0, VOID_FLAG=0 with
cutlass-nvfp4, cutlass-fp8, marlin-nvfp4 and fa2 all ENABLED for [121a] rather
than [121] or DISABLED, BUILD_RC=0, compile_errors=0. Nor was the instrument
mute: compared=96 over three prompts of width 32, full rows 3, short rows 0. A
run that had compared fewer would have exited 4 instead of 1.

Recorded as a result rather than as silence, and no band was widened to make it
read better. Repair is owed to a fresh implementer, because this row wrote the
driver that found it.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
GitHub reported CONFLICTING; the merge resolves clean locally because GitHub
does not run the `merge=union` driver `.gitattributes:7` sets. The union
auto-merge of the index was DISCARDED anyway: main's file taken wholesale, only
rows whose KEY main lacks re-appended, and main's file asserted a strict PREFIX
of the result.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
@localai-bot

Copy link
Copy Markdown
Collaborator Author

Closing as superseded — and it would now REGRESS main rather than merely duplicate it.

Its content is already on main, more completely

#1221 landed as 0ea5d249f and recorded the same run in .agents/benchmark-record.md with the full arm table:

| decode         | the shipped path                              | 4/24   |
| fresh-prefill  | one token per completion, no decode step      | 24/24  |
| host           | CPU queue, where device_token_ids is null     | 96/96  |

main carries 36 matching entries for this run. The only line this PR would still add is "against the committed oracle golden."

Two ways merging it would now make main worse

1. It would add a claim that is false. Its nemotron-h-a2p-paged-forward.md edit says:

The §5.4 A3 end-to-end token gate HAS RUN and FAILED: 6/96 tokens, GATE_RC=1

The gate has since PASSED on GB10: TOKEN MATCH 96/96 over 3 prompts, full rows=3, short rows=0, mode=decode, STRICT PASS, against vllm=0.23.1rc1.dev1511+g555967922 on nemotron-3.5-lightning-30b-nvfp4 rev 29f2d1746d8f41e316523194b19018707749b1b1. Red-before with only nemotron_h_device.cpp reverted: 4/24 on the same binary and checkpoint. Artefacts at /usr/local/nas_share/rc/nh1157/{gate_fixed,gate_red,cfg}.out; cfg.log shows fp4-mma/cutlass-nvfp4/cutlass-fp8/marlin-nvfp4/fa2 all ENABLED for [121a].

2. It would DELETE text main now has. Its nemotron-h-abi-e2e.md diff removes the passage recording that "nothing can build a gate binary" was a host measurement reported as a container measurement — that the dgx host genuinely lacks nvcc/cmake since the 14 Aug reimage (#1019), but the host is not where work runs, and inside rc run the worker is Ubuntu 24.04 as uid 0 with the full toolchain. That lesson cost this row hours and is worth more than the record it sits beside.

Where the corrected state is landing

#1081 carries it, resolved against post-#1221 main: it corrects the one claim main is now stale on ("GB10 read 4/24 ... sm_121a re-run pending"), since that re-run has happened and passed. It keeps the discipline this PR established — still not a benchmark, no ratio claimed on any axis, with host-side NVFP4 lm_head (A2-Q2b) and the 46 FP8 W8A8 mamba projections (A2-Q1, #940) named as why.

Nothing measured here is lost. The work this PR did — running the first e2e gate and recording the divergence honestly rather than as a pass — is what produced #1157 and, through it, the fix.

localai-bot added a commit to VikashLoomba/vllm.cpp that referenced this pull request Aug 18, 2026
…urrent carry was never the cause (mudler#1221)

The A3 token gate diverged on real weights and the cause on record was
the
recurrent carry. It was not. `NemotronHPagedForward` embedded the HOST
`input.token_ids` while `ModelForwardInput::device_token_ids` was
non-null, and
that field's contract is that the host vector is STALE for decode rows
(`model_registry.h:314-324`) — not materialising it on the host is the
synchronize ENG-ASYNC-SCHED W4 exists to remove. So on the default CUDA
path
every decode step embedded the same placeholder id.

FOLLOWING_AGENTS_PROTOCOL

## What the measurement says

One binary, one checkpoint (released `nemotron-3.5-lightning-30b-nvfp4`,
revision `29f2d174`), one committed oracle golden, driven through
`include/vllm.h` alone:

| arm | takes a decode step? | result |
|---|---|---|
| host, decode | yes | **96/96, `STRICT PASS`, full rows 3** |
| GB10, decode | yes | **4/24** |
| GB10, fresh-prefill | no — one token per completion | **24/24** |

The GB10 `got` streams reproduce the earlier recorded run byte for byte,
so this
is the same defect on a fresh build and not drift. The build was not
degraded:
CUDA 13.x from the `ubuntu2404/sbsa` lane, `CFG_RC=0`, `cutlass-nvfp4` /
`cutlass-fp8` / `marlin-nvfp4` / `fa2` all `ENABLED for [121a]`,
`BUILD_RC=0`,
`compile_errors=0`, binary sha256 `b4677cdb7cf5…`.

The per-layer trace puts it at the first operation of the decode step
rather
than inferring it. At the prefill step host and GB10 agree to six digits
on all
52 layers. At the first decode step the gathered conv/SSM state is
IDENTICAL on
the two — `|conv|=310.374`, `|ssm|=3985.8` on both, so the recurrent
carry is
exact — while layer 0's embedding row differs, and it reads `0.228135`
on GB10
at BOTH decode steps although they consumed different tokens. A constant
embedding is a constant input id.

## What this refutes

[mudler#1157](mudler#1157) reasoned that
`gm.num_decodes` might classify a decode as a prefill so the gather
would hand
the mixer zeros. On real weights the trace reports `nd=1 np=0 init=[1]`
on every
decode step, and mutating that mask to `0` turns the A2-P CPU gate RED
(1 case,
6 assertions) — that gate was never blind to that defect. It was blind
to this
one structurally: the runner sets `device_token_ids` only under
`VLLM_CPP_CUDA` with a live device mirror, so no CPU gate can reach the
branch
at all. That is why the red-before here is a device measurement rather
than a
unit test.

## What else is in the change

**Instruments, all off by default.** `VT_NEMOTRON_H_DIAG` prints the
decode/prefill split, the state indices, the has-initial mask, and per
Mamba2
layer the L2 of the state gathered in and written out — it is what
separated the
carry from the input id, and it reads a healthy carry on the CPU
fixture, which
is what makes it usable as a negative control. `nemotron-h-gen
--fresh-prefill`
/ `--both-modes` generate the same stream without ever taking a decode
step,
over one engine load. `VT_NEMOTRON_H_DEVICE_MOE=0` was the bisect lever
for the
device MoE. Both env vars are documented in `docs/ENVIRONMENT.md`.

**Two coverage gaps found while bisecting, both at the decode shape.**
The
device MoE gate measured T=4 and T=2, two prefill widths, never the T=1
the
model spends its whole decode in. The FA-2 d128 decode op gate measured
GQA
ratios 2 and 4 (`16/8`, `32/8`), and NemotronH is `32/2`, ngroups 16 —
the path
is default-ON for any bf16 causal pure-decode at head_dim 128, so it is
not
Qwen3-dense-only as its comment claims. **Neither new case has run on a
GPU
yet**; a lease is queued to measure them, and they skip loudly
everywhere else.

**[mudler#1217](mudler#1217) is filed and
owed**,
not fixed here. The runner hands `device_token_ids` to whatever model
the step
routes to; the field's comment claims a model that ignores it is never
given
one, and nothing enforces that. Kimi-Linear was cut from this same
divergence
(`kimi_linear_device.cpp:2270-2280`) and NemotronH is the second. Both
closes —
an explicit `ModelFactory::honors_device_token_ids` with a runner
fallback, or a
checker over the registered `.forward` entry points — change a shared
seam or
checker semantics, which AGENTS.md sends through its own spec.

## Still owed

The sm_121a green-after re-run of the full 96-token gate under this fix.
The
fleet lease for it is queued; `docs/BENCHMARKS.md` records the device
leg as
pending rather than as a pass, and the host leg as the pass it is.

Note for the merger:
[mudler#1158](mudler#1158) is
open against the same `benchmark-record.md`, `STATUS.md`,
`BENCHMARKS.md` and
both specs, and records the failure this change explains. Whichever
lands second
should take the target-branch version whole and re-apply its own scoped
edit.

Closes mudler#1157

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]

---------

Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants