feat(MODEL-MM-dots3-note): W4b-2 — the sliding arm on the decode path (#699) - #1969
Merged
Conversation
…#699) W4b-2 of the dots3-note row (#699). The 33 `sliding_attention` layers and the PADDED physical KV row are on the decode path, reached from `ModelRegistry::Forward`. W4b-1 landed the sliding maths as host code with no production call site and named W4b-2 as the row that would wire it; this is that wiring, plus the two vt kernels it needed. WHAT THE WINDOW IS, AND WHERE IT LIVES. `vt::MlaDecodeAttention` and `vt::MlaPrefillAttention` each grow an optional `AttentionWindow` — the `(left, right)` pair this tree already uses on `PagedAttentionArgs`, and literally the pair upstream hands FlashAttention (`run_sliding_window(..., causal=True, window_size=(sliding_window - 1, 0))`, `vllm/models/dots3_note/nvidia/attention.py:300` @ `bc2d63e650`). `std::nullopt` is the ABSENT state and it is a NOT-TAKEN branch, not a wide window: on the CPU decode the window is the loop's START BOUND, on the CUDA decode it moves `kv_start` in both split stages, and on the FA-2 prefill it reuses the paged launcher's own `is_causal = causal && !is_local` normalization. Upstream's `_gather_swa_kv_kernel` + `_apply_swa_score_mask_kernel` pair (`:49`, `:119`) is a Triton WORKSPACE strategy over a rounded-up gather; walking the paged block table directly over the same key range reaches the identical set with no gather and no mask, and the op tests prove that by comparing the windowed call against an UNWINDOWED call over the truncated key list rather than against a second copy of the same arithmetic. THE PADDED ROW NEEDED NO `vt` CHANGE AT ALL, which is the correction W4b-1 already recorded and this brick executed. `Tensor::Slice(2, 0, logical)` shrinks `shape[2]` and keeps both leading strides, every MLA cache op reads those strides from the tensor, and that IS upstream's `kv_cache[..., : self.head_size]` (`Dots3NotePaddedSparseImpl._logical_cache`, `attention.py:700-702`). The narrowing is one line in `Dots3NoteModel::ForwardDevice`. The gate reads the RAW cache bytes after a real forward: lanes [6, 10) of every slot a FULL layer wrote are still zero, while the same lanes on the sliding layers carry 28 non-zero values — the control that says the assertion is about the narrowing and not about a fixture that produces zeros anyway. TWO OF W4A'S THREE REFUSALS ARE LIFTED, AND THE THIRD IS NARROWED. The `sliding_attention` refusal and the PADDED-row refusal are gone from `Dots3NoteDeviceRefusal`. The per-step cache-row check STAYS — an engine allocates the cache separately from the config it was built from — and now compares against the PHYSICAL row, which is what the allocator is told. The `seq_len > index_topk` refusal STAYS and is now asked only of a config that HAS a full-attention layer, because `Dots3NoteSlidingAttention` sets `self.indexer = None` / `is_sparse = False` (`model.py:432-434`), so a pure-SWA config has no DSA anywhere. Two cases pin both halves. ONE NEW REFUSAL, NAMED RATHER THAN APPROXIMATED: a windowed prefill that also carries chunked CONTEXT. Upstream caps a sliding layer's gather at `min(seq_len, query_len + W - 1)` and runs one varlen call per request group (`attention.py:206`, `:594-654`); it never merges context chunks under a window, so there is no windowed form of `forward_mha`'s LSE merge to mirror. The seam throws instead of merging an unwindowed context into a windowed suffix. THE GATE. `test_dots3_note_attn` 35 cases / 3025 assertions (30 / 2418 at W4b-1), CPU-only, no GPU, no checkpoint, no speed claim. A MIXED config — `{full, sliding, full}`, dense MLPs, physical row 10 against the full arm's logical 6 — is loaded through the REAL registry and run through `ModelRegistry::Forward` TWICE against one cache pool: a 6-token PREFILL, then a DECODE of the seventh, over a SHUFFLED block table. Both are compared against a whole-model double reference that dispatches per layer kind into W3's `ref::Forward` and W4b-1's `sref::Forward` — a materialized MHA with no cache, no paging and the window as a direct positional predicate. Residue 0.0254 relative, bound 6e-2, and the three ratios are kept SEPARATE because merging them is spec §4.6's finding F1: bound/residue 2.36x, nearest-mechanism/bound 2.63x, nearest-mechanism/residue 6.22x. A port with NO window lands at 0.819, i.e. 13.6x the bound. `vt` op gates: `test_ops_mla_attn` 15 cases (11 before), `test_ops_mla_prefill` 6 (4 before). A window at least as wide as the sequence is BIT-IDENTICAL to no window on both ops, which is what says the absent state is a branch and not a mask. NO REGRESSION ON THE SEAM'S OTHER CALLERS: `test_mla_attention_block` 12 / 2247715 and `test_deepseek_v2_forward` 11 / 1052, both unmoved from the numbers §4.6 recorded, plus `test_deepseek_v2_decode_graph_seam` 3 / 230 and `test_ops_mla_cache` 9 / 2947. CUDA IS WRITTEN AND NOT GATED HERE. This box has no GPU. The CUDA decode's windowed split partition and the FA-2 prefill's local-mask normalization compile in the fat build but have not been RUN; the CUDA-vs-CPU window parity case is present and skips without a device. That is owed and named rather than implied. Row stays SPIKE. Under §6.4 option B this remains a consistency gate against an independent reference, not a correctness gate against an oracle — no vLLM instance for `dots3_note` runs on any host this project owns. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
…te-W4b-2 Routine integration of origin/main into the W4b-2 branch before pushing. The four incoming commits are issue-index appends only; the merge is a union of that append-only file and touches nothing this branch changed. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
…gate missed The spec section for W4b-2: the re-derived upstream anchors at `bc2d63e650`, why the window is a kernel bound here and a gather upstream, the padded row's one-line narrowing, the refusal table, the fixture's two forced retunings, the op gates' oracle-is-the-op-on-a-different-input design, and the six-arm seam byte-identity measurement. ONE FINDING FROM WRITING IT DOWN. The seam probe's first BASE run used doctest's `-ts=` — the test-SUITE filter — instead of `--test-case=`. It matched zero cases, printed `test cases: 0 | 0 passed | 0 failed | 13 skipped` and `Status: SUCCESS!`, and exited 0. Read without checking the case count, that is a clean pass with no fingerprints: the third of the four failure modes `scripts/mutation-harness.py`'s own docstring enumerates, met in the one place that was hand-driven rather than run through the harness. §4.8 records it. AND ONE GATE GAP THE MUTATION PASS FOUND. `M16-windowed-prefill-with-context- accepted` deleted the new seam refusal and the gate stayed GREEN, because the case that asserts it never made it out of the draft. It is here now, with two controls that keep it from passing vacuously: with no window the same call proceeds into `vt::MlaPrefillAttention` and fails there with a DIFFERENT exception type, and with a window but no chunk list it does not fire either. A refusal whose test does not exist is indistinguishable from a refusal that works. `test_dots3_note_attn` 36 cases / 3028 assertions. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
…d FEATURES The evidence W4b-2's §4.8 was still owing, plus the two projections its lifecycle touches. THE MUTATION TABLE, 27 rows through `scripts/mutation-harness.py` rather than a scratch driver — which is what §4.7 says W4b-1 should have used. Every count was RE-MEASURED at the final 36/3028 baseline instead of being carried over from the first pass at 35/3025, because a table mixing two baselines is an instrument reporting on a state it was not given. FIVE of the twenty-seven failed to BUILD on their first attempt, every one on `-Werror=unused-variable`, and each is printed with its compiler exit rather than tidied away. Nearly one row in five would have read as a passing test in a hand-driven pass; W4b-1 hit the same trap once in twenty-six and the harness caught it five times here without anyone looking. THREE GREEN ROWS, and they say three different things. M5 is a green the driver earned: forcing the CPU prefill's PASS-1 loop to start at 0 is a numeric no-op, because softmax is invariant to the constant subtracted before `exp` and passes 2 and 3 still sum the windowed range — M5b moves the bound's DEFINITION, reaches all three passes and reds. M16 found a real gate gap, already fixed in the previous commit. M19 and M20 apply the same leaked window to the DeepSeek path and disagree: `test_deepseek_v2_forward` is blind to it because its synthetic forward drives the prefill half, while `test_mla_attention_block` reds on 3 cases / 4 assertions. Together they say `impl.sliding_window` really is on the DeepSeek path and its 0 is load-bearing — which the byte-identity table needs and cannot supply alone, since identical output could also mean nothing ever read the field. The two vt op-gate baselines are MEASURED, not counted off `TEST_CASE` lines: both files were checked out at the base SHA `925a4a587`, rebuilt, run, then restored and verified byte-for-byte. `test_ops_mla_attn` 11 / 197113 → 15 / 246290; `test_ops_mla_prefill` 4 / 242156 → 6 / 329772. §7 marks W4b-2 DONE and opens W4b-3 for the three debts it named: the DSA lightning indexer's SELECTION, the windowed prefill with chunked CONTEXT, and the `rc` lease on `thor:gpu0` that gates the CUDA half. The split line is that the indexer shares nothing with the sliding window — the sliding layers carry no indexer at all. `docs/FEATURES.md` stops saying the dots3-note forward refuses. It has not since W4a, and it now runs both geometries; what still refuses on the RELEASED checkpoint is its first MoE layer. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
…te-W4b-2 Second integration of origin/main before pushing. The two incoming commits are ENG-POOL-BEST-FIT (#1930) and SPEC-DFLASH2's shared embedding copy (#1952); neither touches the MLA seam, the two `vt` MLA ops or anything dots3-note owns, and the merge is clean. The focused gate set was re-run after it. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
… refusal
Integrating `origin/main` merged clean, COMPILED clean, and took
`test_dots3_note_attn` from 36/3028 green to five cases throwing:
vt: resident weight: EMPTY tensor has no host bytes to alias
(host-alias arm, dtype f32, rank 0) dense_attn_block.h:206
The incoming change is #1952's review finding #1953: `ResidentWeight` now refuses
an empty weight, because an empty one aliases a null host pointer that no
downstream op can detect — every op validates rank, shape, dtype and device, and
the shape comes from the caller rather than from the bytes. The refusal is right.
W4b-2 was on the wrong side of it. It made BOTH rope caches resident up front,
while `MaterializeDots3NoteDevice` deliberately leaves the unused one empty: each
is 64 MiB at the released 524288 positions, so a config with no sliding layer
should not build the sliding table and a config with no full layer should not
build the full one. A layer only ever reads its own kind's cache, so the guard
costs nothing and the comment now says it is the CONTRACT rather than an
optimization.
Nothing about this was visible to the merge itself: the two branches touched
different files, `git merge` reported clean and `cmake --build` exited 0. It is
the "merge-tree CLEAN is not merge-tree BUILDS" trap one step further along,
where it builds too and only the gate can see it. Re-running the focused gate set
after the merge and before the push is what caught it; §4.8 records that.
`test_dots3_note_attn` back to 36 cases / 3028 assertions.
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…ot on scheduling
W4b-2's record said the CUDA half was owed against an `rc` lease on `thor:gpu0`.
That reads as work nobody has got round to. It is not: the lease cannot be taken
at all. Measured here with `rc devices` rather than taken from a report:
dgx:gpu0 unhealthy (no contact 1h27m50s)
orin:gpu0 unknown (no contact 1m17s)
thor:gpu0 unhealthy (no contact 1h16m32s)
Both CUDA hosts this row could use are QUARANTINED, and the third device is
`unknown` rather than healthy — it is an `orin` (sm_87) and not this row's host
in any case. Clearing a quarantined device needs an admin token, which is a
human's decision and not an agent's. So the blocker is hardware recovery, and
§4.8, `## Owed` and the two §7 bullets now say that with the measurement attached
instead of naming a lease as if it were schedulable.
A SECOND STATEMENT THE RECORD WAS RUNNING TOGETHER. "Written and not run" let
"written" imply "builds". The two CUDA files have not been COMPILED here either
— this box has no `nvcc` — and CI's `cuda-fat-build` is the only compile
verification this change can give them. Until that lane reports on this branch
the CUDA half is neither compiled nor executed, and the section says so in those
words.
Record-only: no source, test or gate file is touched, so the numbers in §4.8
stand as measured at `fa96f9557`.
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…ow gets a case, and three dead or stale lines go (#699) Six of the nine findings a fresh review raised on #1969 are discharged here; the three record-only ones ride in the following commit, which needs the mutation numbers this one makes measurable. F1. `tests/vt/test_ops_mla_prefill.cpp` gains a `HasCuda()`-skipping windowed CUDA-vs-CPU parity case. Before it, the FA-2 MLA-prefill launcher's `is_local` normalization — one of the two CUDA files W4b-2 changed — had no gate on any device: the file's only `HasCuda()` cases are pre-existing and unwindowed, and the PR's one windowed CUDA case is the DECODE sibling in `test_ops_mla_attn`. A later GPU lease would have discharged the decode half while the record read as covering both. The case SKIPS on this box, which has no CUDA device, so it raises `test_ops_mla_prefill` from 6 cases to 7 and its assertion count not at all (329772, unmoved). It is written and unexecuted, and it is recorded that way rather than counted as coverage. It deliberately does NOT assert the CPU case's wide-window bit-identity: a finite window sets `is_causal = false` and dispatches FA-2's LOCAL template, so on the GPU that agreement is numerical. F5a. `MlaBlockDims::sliding_window < 0` shipped without a test and its review mutation SURVIVED. The refusal is load-bearing — the ops read the window as `> 0`, so a negative value throws nowhere and silently degrades a windowed layer to full attention — so it now has a case, with controls on both sides of the boundary (0 is ABSENT and legal, 513 is legal) so it cannot pass on an implementation that refused every window. `test_mla_attention_block` 2247715 -> 2247718 assertions, 12 cases unmoved. F6. `step.rope_cache = &rope_full` in `dots3_note_device.cpp` is deleted. No code in that TU reads the field; the three models that do read it (`deepseek_v2`, `minicpm3`, `kimi_linear_device`) each have ONE per-model rope table, and dots3-note has two whose selection is a property of the layer. The assignment would have handed a future reader the full arm's rope on a sliding layer, or an empty `Tensor` on a pure-SWA config. A null fails at the first read instead, and the comment now says so. F7. `MlaBlockDims::has_sliding_window()` had no caller in `src`, `include` or `tests` and is dropped under `## Nothing lands dead`, with a note on why no call site was invented for it: every consumer wants the value, and `ForwardMlaAttentionBlock` assigns it unconditionally precisely so a 0 cannot be skipped. F8. Two comments in `test_dots3_note_attn.cpp` described the pre-retune four-layer fixture (`{full, sliding, full, sliding}`) that the committed `Spec::kinds` and the case's own `REQUIRE(num_hidden_layers == 3)` contradict. F9. `dots3_note_device.cpp` anchored upstream at `06ecec7a84` while the spec and this PR re-derive at `bc2d63e650`. Re-derived and MEASURED, not assumed: `git diff 06ecec7a84 bc2d63e650 -- vllm/models/dots3_note/` is empty, and the only `deepseek_v2.py` delta is two equal-length lines inside `DeepseekV2Attention` (`:457`, `:495`), a class dots3-note does not subclass, so every anchor holds unmoved. The old comment claimed that second diff was EMPTY; it is not, and naming the two lines is what makes the claim checkable. F5b is NOT fixed here and the code says why at the site. The per-step `ld.head_size() <= physical_row` check is UNREACHABLE, not merely untested: `physical_latent_row()` IS `swa.latent_row()`, so it is an identity on a sliding layer, and `ParseDots3NoteParams` already refuses the full-layer case at load. That is a stronger statement than the review's "backstopped by `Tensor::Slice`", and it is why the mutation survived. The following commit lists it under `## Owed`. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
…w measured otherwise, and one divergence it never said (#699) The record half of #1969's fresh review. Every claim below is re-measured at this head rather than transcribed, and the mutation rows carry their compiler exit. F1. §4.8's CUDA paragraph and its `## Owed` bullet named the two CUDA files together and then described ONE parity case. That reads as covering both halves. It did not: the prefill half had no windowed case on any device until the preceding commit added one. Both entries now name the halves separately, because a lease closes one at a time. They also carry the number that says neither has executed — `test_ops_mla_prefill` reads 7 cases / 329772 assertions, one more case than before and not one more assertion, which is what a skipped case looks like in a count. F3. §4.8 claimed §4.6's six-arm fingerprint table "is reproducible from outside the session that produced it". Two of six arms reproduce; four do not. Verified here before writing it down, and the diagnosis holds on all three legs: `git log d7d1ee9..925a4a5` over the five MLA files returns exactly one commit, `446ac1806`, which is W4a itself — and widening the sweep to all of `src/vt` adds only `Conv3d` and `Exl3Gemm`, which the MLA block does not reach; `grep -r` finds the fingerprints only in this spec, so the probe was never committed; and `q_lora_scale` does not exist at §4.6's base at all (`grep -c` is 0 there and 2 at §4.8's), so §4.6's probe could not have set the field its own table says arms 2-3 exist to cover. The byte counts agree on all six arms, so the two probes agreed on shapes and differed on values. It is a probe difference, not a behaviour change, and the section now says so with the general rule: a fingerprint from an uncommitted, hand-written probe is not a cross-session reproducible quantity. Committing the probe is what fixes it, neither scratch tree survives, and a third hand-written probe would be a third instrument — so it goes to `## Owed` rather than being faked here. F4. §4.8 explained M19's green by saying `test_deepseek_v2_forward`'s CPU forward drives the prefill half while `impl.sliding_window` reaches only the decode MQA — which implies the prefill half WOULD be caught. Measured: leaking the window into the prefill call instead is compiler exit 0, EXIT 0, 11 cases / 1052 assertions, zero failures, SURVIVED; the same mutation on `test_mla_attention_block` is compiler exit 0, EXIT 1, 4 cases / 2 assertions failing. The real reason is that those CPU cases have no value oracle for the attention output at all — `:443`, `:464`, `:482`, `:511` and `:537` each compare the model against itself under another configuration, or against nothing. The M19 conclusion survives; its map was drawn wrong. F2. A divergence W4b-2 created and never recorded. Upstream returns `SlidingWindowMLASpec` for a windowed layer (`mla_attention.py:1215-1219` @ `bc2d63e650`) and passes `sliding_window=config.sliding_window_size` per sliding layer (`model.py:457`); `MakeDots3NoteKVCache` emits one uniform `MLAAttentionSpec` for all 46. No correctness consequence — the window is applied on read and the W4b-2 gate proves it — but 33 of 46 layers then hold a full-length latent cache where upstream caps a windowed layer near the window, 513 against 524288. That is the class a token gate structurally cannot see, which is why `porting.md` asks for the memory format explicitly. §4.8 gains a subsection, `## Owed` gains an entry scoped to W4b-3, and `MakeDots3NoteKVCache`'s own comment — which still said the group split was "W4's, and NOT represented here" after W4b-2 made those layers run — now names the owning brick and the reason for each of the three things it defers. F5b. Recorded rather than fixed, with the site saying why. The per-step `ld.head_size() <= physical_row` check is UNREACHABLE, not merely untested, and that is stronger than the review's reading. R3 is the measurement: deleting the CONFIG-level refusal that closes the full-layer case reds `test_dots3_note_scaffold` at compiler exit 0, 1 case / 1 assertion, so the closure is gated rather than assumed. The `#1969 REVIEW-REPAIR rows` table records all six mutations run for this repair, each with its compiler exit, including the two GREEN ones and the before/after pair that shows R1 going from SURVIVED on the row gate to DETECTED once the new case exists. 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 26, 2026
…lkan red found while gating W2 (#1979) Not this branch's defect and not this branch's fix. `windows-msvc-vulkan` stops on `test_openai_api_server.exe exited with status -1073740791` on this pull request and on #1975, #1969 and #1967, whose diffs share no file under src/vllm/entrypoints/. The job is PR-only and never runs on main, so there is no green reference commit to bisect against, which is why four authors could each meet this red without any of them being able to attribute it. It rides here rather than in its own record-only pull request because the alternative to filing it now is not filing it: AGENTS.md requires a bug found during other work to get an issue that names an owner, and this one is owned by ENG-RELEASE-WINDOWS rather than by the row that found it. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-fable-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.
W4b-2 of the dots3-note row (#699). The 33
sliding_attentionlayers and thePADDED physical KV row are on the decode path, reached from
ModelRegistry::Forward. W4b-1 landed the sliding maths as host code with noproduction call site and named W4b-2 as the row that would wire it; this is that
wiring, plus the two windowed kernels it needed.
Upstream re-derived at vLLM
origin/main=bc2d63e650.git diff --stat d9fbe526c0 -- vllm/models/dots3_note/is EMPTY againstbc2d63e650and againstorigin/mainalike, measured in a local vLLM checkoutrather than assumed, so every §2.3 anchor W4b-1 corrected holds unchanged at the
newer head.
The window is a kernel bound here and a gather upstream, and that is the same function
vt::MlaDecodeAttentionandvt::MlaPrefillAttentioneach grow an optionalAttentionWindow— the(left, right)pair this tree already uses onPagedAttentionArgs, and literally the pair upstream hands FlashAttention(
run_sliding_window(..., causal=True, window_size=(sliding_window - 1, 0)),attention.py:300). Upstream's decode gathers[max(seq_len - GATHER_LEN, 0), …)into a workspace with
GATHER_LENrounded up to 8 and then masks the scores(
:76-79,:151-152); the gather is a SUPERSET and the mask makes it exact,because Triton needs a power-of-two tile. Walking the paged block table directly
over the same key range reaches the identical set with no gather and no mask.
std::nulloptis the ABSENT state and a NOT-TAKEN branch, not a wide window: theCPU decode's loop START moves, the CUDA decode's
kv_startmoves in both splitstages, the FA-2 prefill reuses the paged launcher's own
is_causal = causal && !is_localnormalization, and the CPU prefill's lowerbound moves. Both op gates prove that bit-for-bit rather than to a tolerance:
a window at least as wide as the sequence produces byte-identical output to no
window, which a mask applied afterwards could not.
The padded row needed zero
vtchangesTensor::Slice(2, 0, logical)shrinksshape[2]and keeps both leading strides,every MLA cache op sources its strides from the tensor, and that IS upstream's
kv_cache[..., : self.head_size](_logical_cache,attention.py:700-702). Thenarrowing is one line in
Dots3NoteModel::ForwardDevice. This is the correctionW4b-1 recorded, executed.
The evidence is the RAW cache bytes after a real forward, not an argument: lanes
[6, 10)of every slot a FULL layer wrote are still zero, with the CONTROL thatthe same lanes on the sliding layers carry 28 non-zero values.
Two of W4a's three refusals are lifted, the third is narrowed, one is new
sliding_attentionlayerseq_len > index_topkself.indexer = None(model.py:432-434)MlaBlockDims::sliding_windowThe RELEASED
dots-studio/dots3-note-prevconfig still refuses, now at layer 1'sMoE rather than layer 2's sliding attention, so nothing a user can run changed.
The gate
test_dots3_note_attn36 cases / 3028 assertions (30/2418 at W4b-1),CPU-only, no GPU, no checkpoint, no speed claim. A MIXED config
(
{full, sliding, full}, dense MLPs, physical row 10 against the full arm'slogical 6) is loaded through the REAL registry and run through
ModelRegistry::ForwardTWICE against one cache pool — a 6-token PREFILL then aDECODE of the seventh, over a SHUFFLED block table — and compared against a
whole-model double reference that dispatches per layer kind into W3's
ref::Forwardand W4b-1'ssref::Forward.Residue 0.0254 relative; bound 6e-2. Three ratios, kept SEPARATE because merging
them is the row's own finding F1: bound/residue 2.36x,
nearest-mechanism/bound 2.63x, nearest-mechanism/residue 6.22x. A port
with no window at all lands at 0.819 — 13.6x the bound.
The fixture was retuned twice, and a measurement forced both. The first draft
(four layers, thetas 41 vs 137) measured a residue of 0.119 with the nearest
mechanism at 0.106 — the nearest defect sat UNDER the quantisation floor. The fix
was the fixture, not the bound: thetas orders apart the way the released 5e4 vs
8e7 are, and three layers instead of four.
test_ops_mla_attn15 cases / 246290 assertions (11 / 197113 at the baseSHA, measured by checking those two test files out at the base, rebuilding and
running, then restoring them byte-for-byte);
test_ops_mla_prefill7 cases / 329772 assertions (6 / 329772 before the review repair; 4 / 242156
at the base). That +1 case is +0 assertions on purpose, and the count is the
statement: the case the repair added is a CUDA one, doctest scores a case that
returns before its first assertion as PASSED, and this box has no device. It is a
skip wearing a pass, it is written down as one, and it must not be read as new
coverage executed here. It WAS executed later, on a leased GPU — see the CUDA
section below.
Their oracle is the op itself on a DIFFERENT input — a windowed paged call
against an unwindowed call over the truncated key list — rather than a second
copy of the same arithmetic.
The mutation pass, through the harness this tree ships
27 rows through
scripts/mutation-harness.py, all re-measured at the final36/3028 baseline, plus six more for the review repair. Highlights, with the full
tables in spec §4.8:
(
impl.sliding_window = dims.sliding_window) and the PREFILL one(
…, dims.sliding_window)) each take the gate RED, so the window comes fromthe config through the real loader and the shared seam.
-Werror=unused-variable. Nearly one row in five would have read as a passingtest in a hand-driven pass; the compiler exit is printed beside every row and
each was re-run behind
((void)x, …).refusal left the gate green, because the case asserting it never made it out
of the draft. It is in the gate now with two controls, and M16b reds.
test_deepseek_v2_forwardand RED ontest_mla_attention_block. Togetherthey say
impl.sliding_windowreally is on the DeepSeek path and its 0 isload-bearing — which the byte-identity table needs and cannot supply alone,
since identical output could also mean nothing ever read the field.
What a fresh review changed, and what it corrected in the record
Nine findings, all discharged. Six are code and three are record; the record half
needed the mutation numbers the code half makes measurable, which is why it is a
second commit.
The FA-2 MLA-prefill windowed path had no test on any device. The record
named both CUDA files together and then described ONE parity case, which reads as
covering both halves. It did not: the windowed CUDA case was the DECODE sibling
in
test_ops_mla_attn, andtest_ops_mla_prefill's onlyHasCuda()cases arepre-existing and unwindowed. A later lease would have discharged the decode half
against a record that read as covering both.
test_ops_mla_prefillnow carriesCUDA MLA prefill: the sliding window matches the CPU reference, comparing thewindowed device call against the windowed CPU op, against the unwindowed device
call, and against the file's expanded single-query oracle. It deliberately does
NOT assert the CPU case's wide-window bit-identity: a finite window sets
is_causal = falseand dispatches FA-2's LOCAL template, so on the GPU thatagreement is numerical rather than byte-exact. Both entries in
## Owednow namethe two halves separately, because a lease closes one at a time.
A divergence this brick creates and never recorded. Upstream's
MLAAttention.get_kv_cache_specbranches on the window and returnsSlidingWindowMLASpec(..., sliding_window=self.sliding_window)(
vllm/model_executor/layers/attention/mla_attention.py:1215-1219@bc2d63e650), and every sliding layer sets one(
vllm/models/dots3_note/nvidia/model.py:457).MakeDots3NoteKVCacheemits oneuniform
v1::MLAAttentionSpecfor all 46. There is no correctnessconsequence — the window is applied on READ and the gate above proves it — but
33 of 46 layers then hold a full-length latent cache where upstream caps a
windowed layer near the window, 513 blocks against 524288 on the released
config. It is the single largest memory property of this architecture, and a
token gate structurally cannot see it, which is exactly why
porting.mdasks forthe memory format to be compared with the oracle explicitly. THREE pieces are
missing rather than one, so it is scoped to W4b-3 rather than fixed here. While this
branch sat in review,
SlidingWindowMLASpecLANDED onmain(KV-DSV4-MULTICACHEW1, #1960,
c1e6f3fb9), so the merge commit's owninclude/vllm/v1/kv_cache_interface.hDEFINES the type this branch's copy still lists as omitted. The divergence is
unchanged — we still emit one uniform spec for all 46 layers — but W4b-3 now
inherits the spec type and owes only the per-layer wiring, which is less than was
owed when the paragraph above was written. Verified at merge time rather than
assumed:
struct SlidingWindowMLASpec : SlidingWindowSpecatinclude/vllm/v1/kv_cache_interface.h:396onorigin/main.A byte-identity claim narrowed to what was actually measured. The record said
§4.6's six-arm fingerprint table "is reproducible from outside the session that
produced it". Two of six arms reproduce; four do not, and the first draft
generalised from the first two. The diagnosis is measured on three legs and is
not a behaviour change: exactly ONE commit touches the five shared MLA files
between the two bases and it is W4a itself;
grep -rfinds the fingerprints onlyin the spec, so neither probe was ever committed; and
q_lora_scaledid notexist at §4.6's base (
grep -cis 0 there and 2 at §4.8's), so §4.6's probecould not have set the field its own arms 2-3 are said to cover. The byte COUNTS
agree on all six arms, so the two probes agreed on shapes and differed on values.
The transferable rule is the reason this is in the record rather than quietly
corrected: a fingerprint from an uncommitted, hand-written probe is not a
cross-session reproducible quantity. Two probes sharing a prose label are two
instruments, and comparing their outputs measures the authors, not the code.
Committing the probe is what fixes it; neither scratch tree survives and a third
hand-written probe would be a third instrument, so it goes to
## Owed.A mutation's stated mechanism was wrong, and the conclusion survives it. The
record explained M19's green by saying
test_deepseek_v2_forward's CPU forwarddrives the PREFILL half while
impl.sliding_windowreaches only the decode MQA —which implies the prefill half WOULD be caught. Measured: leaking the window into
the prefill call instead is EXIT 0, 11 cases / 1052 assertions, zero failures,
SURVIVED. It misses a window leak on BOTH halves, because those CPU cases have no
value oracle for the attention output at all — each compares the model against
itself under another configuration, or against nothing.
A refusal that shipped untested now has a case.
MlaBlockDims::sliding_window < 0is load-bearing, because the ops read thewindow as
> 0, so a negative value throws nowhere and silently degrades awindowed layer to full attention. It has controls on both sides of the boundary
(0 is ABSENT and legal, 513 is legal) so it cannot pass on an implementation that
refused every window.
test_mla_attention_block2247715 -> 2247718assertions, 12 cases unmoved.
One refusal is DECLINED rather than tested, on a stronger reason than the
review's. The per-step
ld.head_size() <= physical_rowcheck is UNREACHABLE,not merely untested:
physical_latent_row()ISswa.latent_row(), so on asliding layer the comparison is an identity, and on a full layer
ParseDots3NoteParamshas already refused the violating config at load. R3 isthe measurement that turns that from an argument into a gate — deleting the
CONFIG-level refusal reds
test_dots3_note_scaffold, so the closure is gatedrather than assumed. The review's reading, that
Tensor::Slicebackstops it,understates it: the backstop is not reached either. It is kept as the executable
spelling of upstream's
assert physical_head_size >= self.head_size, the sitesays so, and
## Owedcarries it as an untested assertion.A dead assignment and an uncalled accessor are deleted.
step.rope_cache = &rope_fullhad no reader in its TU and would have handed afuture one the full arm's rope on a sliding layer, or an empty
Tensoron a pureSWA config; a null fails at the first read instead.
MlaBlockDims::has_sliding_window()had no caller anywhere and no call site wasinvented for it, because every consumer wants the value and
ForwardMlaAttentionBlockassigns it unconditionally precisely so a 0 cannot beskipped. Two stale comments describing the pre-retune four-layer fixture go with
them.
The seam is byte-identical on all six arms, measured within this session
Six arms across the seam's branch space (q_lora present/absent, both rope
layouts, both dtypes), BASE in a separate
git archivetree at925a4a587witha byte-identical probe (
md5sumequal), its own configure and its own build.Six for six identical, base against head. That is the claim this table was
built to make, and it is the claim it supports; the cross-session comparison it
also attempted is the one narrowed above.
test_mla_attention_block12 / 2247718 (12 / 2247715 before the reviewrepair added the negative-window case) and
test_deepseek_v2_forward11 / 1052;
test_deepseek_v2_decode_graph_seam3/230,test_ops_mla_cache9/2947,
test_dots3_note_scaffold26 / 110819.NOT run and named: the SACRED DeepSeek-V2-Lite e2e token gate needs a ~29.26 GiB
checkpoint on a CUDA host; this brick ran CPU-only on a box with neither.
## Nothing lands deadEverything this PR adds is reached from
ModelRegistry::Forward, and tworeachability mutations say so rather than the sentence: deleting the decode call
site (
impl.sliding_window = dims.sliding_window) and the prefill one(
… , dims.sliding_window)) each take the gate RED.What is NOT reached, declared: W4b-1's
dots3_note_attn.{h,cpp}slidingfunctions —
ForwardSlidingAttention,GatherSwaKv,ApplySwaScoreMask,BuildSlidingWindowMetadata,WritePaddedMlaCache,NarrowLogicalCacheRows—still have no production call site and did not gain one here, because the device
path reaches the same key set through the paged block table instead of
upstream's Triton gather-plus-mask. They are the gate's oracle, the status W3's
ForwardFullAttentionhas had since W4a. Owning rowMODEL-MM-dots3-note-dots3-note-for-causal-lm, issue #699, listed under## Owedin the spec.The CUDA half is compiled and EXECUTED, on sm_87
The two CUDA files this PR changes —
src/vt/cuda/cuda_mla_attn.cuandsrc/vt/cuda/cuda_flash_attn_fa2.cu— were written on a box with no GPU and nonvcc, and for most of this row's life the record had to say "written, notcompiled, not run". An
rc runlease onorin:gpu0(Jetson AGX Orin, sm_87,~36 minutes of device time) closed both halves of that. The SHA was PROVEN rather
than asserted: the job cloned in-container and refused to build unless
git rev-parse HEADequalled53424910dfa31fbd10bcb3296a12401eaed8ee54withgit status --porcelainempty.Compiled, on two toolchains. Both objects were deleted first, gencode was
read from
compile_commands.json, and real per-arch SASS was confirmed withcuobjdump --list-elfrather than a PTX leg.cuda_mla_attn.cusm_87.cubin, 1.56 MBcuda_flash_attn_fa2.cusm_87.cubin, 514 KBThe second column reproduces what CI's
cuda-fat-buildasks, on the toolchain ituses. FA-2 being ON was MEASURED three ways rather than inferred from the default
—
CUDA feature fa2: ENABLED for [87],VLLM_CPP_FLASH_ATTN:BOOL=ONinCMakeCache, and the generated manifest
VLLM_CPP_CUDA_FA2_COMPILED_ARCHS "87"—which matters because
cuda_flash_attn_fa2.cuenters the archive only whenVLLM_CPP_FLASH_ATTN(default ON), CUTLASS headers and a non-emptyVT_FA2_ARCHSall hold, andcuda-fat-buildsupplies the second with-DVLLM_CPP_CUTLASS_FETCH=ONand the third through an arch list that intersectsthe
fa2feature set at 80, 86, 87, 89, 120a and 121a.Executed — and the assertion counts are the proof. Same binaries in both
columns; the control is
CUDA_VISIBLE_DEVICES="", so the delta is the device andnot the build.
1 case / 0 assertions / SUCCESS!is exactly the shape these two cases have wornuntil today, and the right-hand column is the first execution either has ever
had. Whole-binary figures on device:
test_ops_mla_attn246,290 -> 2,401,528and
test_ops_mla_prefill329,772 -> 2,931,678. Every filter matched exactly onecase, so no zero-match false green; all exit codes 0 and no timeout.
Numerically correct. Windowed decode
MaxAbsDiff(gpu, cpu)is 2.38e-07,2.68e-07 and 2.68e-07 across the three split arms, against a
< 1e-3bar.Windowed prefill, with 475 (query, key) pairs dropped across 57 queries:
gpu_winvs
cpu_win0.00294137,gpu_nonevscpu_none0.00294137 andgpu_winvsexpanded0.00294137, all against< 3e-2— and the decisive one,gpu_winvs
gpu_none= 1.06055 against a> 1e-2bar. The window demonstrably BITESon the device, so the FA-2 launcher is not dropping
window_sizeon the floor.That is the exact defect the
is_localnormalization exists to prevent, nowmeasured rather than argued.
The wrinkle, stated rather than tidied away. Three of those four prefill
numbers are byte-identical at 0.00294137. The likely reason is that the global
max-error element sits on an UNWINDOWED row, where
gpu_win/gpu_noneandcpu_win/cpu_none/expandedeach coincide and all three become the samesubtraction — but that element was not instrumented, so this is inference and not
a measurement. It is not degeneracy: if the CPU arms agreed wholesale the second
assertion would read about 1.06, not 0.0029.
Two limits. Execution is proven on sm_87 only; the ten-arch result is
COMPILE-ONLY, because CUDA 13 cannot run against that box's NVRM 540.4.0 driver
(
cudaGetDeviceCount rc=35). And this is KERNEL-level parity on two ops. It isnot the e2e model gate, which is unrelated and still owed.
The two red CI lanes are pre-existing and unrelated, with the evidence to re-derive that
Neither red is attributable to this change, and both are named here rather than
left for a reader to assume.
build-test-cpufails at the TEST step, not the build:627 of 628 passed, and the single failure is
test_cpu_threadpoolattests/vt/test_cpu_threadpool.cpp:539,CHECK(ratio < 100.0)reading2 threads 0.48 us, 5 threads 52.638 us, ratio 109.662. It is a fixedwall-clock ratio guard whose denominator is the small-thread leg, so it fires
on GitHub's 4-vCPU runner while needing roughly a 37x blowup to catch a real
regression on a large box. Issue
#631 owns it and it is
deliberately unfixed, because raising a threshold to green a gate is a scope
change that needs its own spec. It touches no MLA, attention or model path.
windows-msvc-cpuandwindows-msvc-vulkanare the baseline-less lane,issues #503 and
#584. Controlled rather than
asserted: they fail identically on open PRs record(ORACLE-EXLLAMAV3-GATEABILITY): the pin does not build on aarch64, so the EXL3 arm still has no token oracle (#1901) #1967, docs: align public feature counts with the registry #1956 and fix(BACKEND-ROCM): charge the expanded size when every keep flag is off #1935, and ZERO
of the last 12 windows runs produced a
mainverdict, so there is no greenbaseline for this branch to have regressed from.
The merge that built clean and threw
Integrating
origin/maina second time merged clean, COMPILED clean, and tooktest_dots3_note_attnfrom 36/3028 green to five cases throwingresident weight: EMPTY tensor has no host bytes to alias. The incoming changeis #1952's review finding #1953:
ResidentWeightnow refuses an empty weight,because an empty one aliases a null host pointer no downstream op can detect.
The refusal is right and this branch was on the wrong side of it — it made BOTH
rope caches resident up front while the materializer deliberately leaves the
unused one empty (each is 64 MiB at 524288 positions). One guard per cache fixes
it, and the comment now says the guard is the CONTRACT rather than an
optimization.
The two branches touched different files, so nothing about this was visible to
the merge: it is "merge-tree CLEAN is not merge-tree BUILDS" one step further
along, where it builds too. Re-running the focused gate set AFTER the merge and
BEFORE the push is the only thing that caught it. Spec §4.8 records it.
What is owed, and the split I am proposing
The row is NOT finished, and the sm_87 result above does not narrow this list.
W4b-3, and the line is real: the DSA lightning indexer's SELECTION shares
nothing with the sliding window. The sliding layers have no indexer at all; the
indexer is on the FULL layers, and lifting
seq_len > index_topkneeds indexerweights on device, a logits kernel, a top-k and a SPARSE MLA attention kernel on
both backends. Bundling it here would have doubled a PR that already carries two
new kernel bounds and a seam extension. W4b-3 carries it, together with the
windowed-prefill-with-context refusal and the
SlidingWindowMLASpecdivergenceabove.
.agents/specs/dots3-note.mdis STALE against this body at the merge commit,in three places, and W4b-3 owns every one. The spec is the row's authoritative
record, so a merge that leaves it disagreeing with its own commit message is a
debt and is written down as one. It is not fixed on this branch because a push
now would cancel a
cuda-fat-buildthis change has already had destroyed twice,and the fresh reviewer sanctioned folding record-only corrections into W4b-3:
test_mla_attention_block12 / 2247715 and calls it "unmovedfrom the numbers §4.6 recorded". The measured value at this SHA is
12 / 2247718 — the implementer, a fresh reviewer and the operator each
measured it independently and agree.
2247718appears nowhere in the spec.This body says 2247718 and the spec contradicts it.
#### The #1969 REVIEW-REPAIR rowssection says "Five more rows" andits table lists six: R1, R1-control, R2, R3, R4 and R4b. This body says
six.
## Owedstill say the CUDAlease "cannot currently be taken" and name
thor:gpu0as the host thatdischarges it. Both statements are now false. The compile and the execution
above were discharged on
orin:gpu0, andthorcould never have dischargedthe PREFILL half at any point, because
VT_CUDA_FEATURE_TABLE'sfa2row is8.0,8.6,8.7,8.9,12.0a,12.1a— sm_87 carries FA-2 and thor's sm_110 does not.Left alone, the record claims blocked work that has in fact been done, on a
host that could not have done it.
Two comment-level corrections also go to W4b-3, named here so the miss is in
the history rather than only in a review thread. They are not fixed on this
branch for the same reason, and neither is worth a gate run of its own:
src/vllm/model_executor/models/dots3_note_device.cpp's upstream anchors forthe
deepseek_v2.pydiff are off by three. The lines are 460 and 498at
bc2d63e650, not 457 and 495 — 457 isqk_nope_head_dim: int,and 495 isa bare
). The substance of that correction is right; only the two numbersare wrong.
not literally one experiment: the CUDA case uses
h = kHeadsLite(16) wherethe CPU case uses
h = 4. Lengths, window and seeds do match.Still open, and blocked on things this row cannot buy. The end-to-end parity
gate against vLLM — token-exact or the ratified near-tie form, chosen by
measurement — plus every throughput, latency and memory axis, and the quantized
arms. No host this project owns runs the
dots3_noteoracle at any publishedprecision, and the row is beyond the parity pin.
## Owedin the spec carrieseach of these with its owner and #699.
Row stays SPIKE. Under §6.4 option B the CPU gate remains a consistency gate
against an independent reference, not a correctness gate against an oracle: no
vLLM instance for
dots3_noteruns on any host this project owns.FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]