Skip to content

feat(MODEL-MM-deepseek-v4-deepseek-v4-for-causal-lm): run the DeepSeek-V4-Flash-Vision tower on CUDA against llama.cpp b10766 - #3163

Open
localai-org-maint-bot wants to merge 113 commits into
mainfrom
row/MODEL-MM-deepseek-v4-deepseek-v4-for-causal-lm
Open

feat(MODEL-MM-deepseek-v4-deepseek-v4-for-causal-lm): run the DeepSeek-V4-Flash-Vision tower on CUDA against llama.cpp b10766#3163
localai-org-maint-bot wants to merge 113 commits into
mainfrom
row/MODEL-MM-deepseek-v4-deepseek-v4-for-causal-lm

Conversation

@localai-org-maint-bot

Copy link
Copy Markdown
Collaborator

The vision arm of DeepSeek-V4-Flash-Vision now loads, runs on a CUDA device and
matches the pinned oracle. It does not yet serve an image end to end, and this
change says where it stops.

What this lands

The tower runs on the device and matches the oracle. On thor:gpu0
(sm_110), all four lead_pad rungs run on provider: cuda and compare directly
against llama.cpp b10766: the four sentinel kinds are byte-exact (max_abs 0.0), the block permutation is the identity for 100/100 image rows, cells mean
relative L2 is 2.884% at cosine 0.99939, and vit mean relative L2 is 1.872%.
The device arm is closer to the oracle than our own CPU arm (3.83% / 0.99899).
This was red first: every rung aborted on DeepSeek-V4 vision queue and weights must share one device while the same binary's CPU arm reproduced the W6 block
byte for byte, which is what makes it a device result rather than a build
difference.

The staging fix is gated on a CPU host. EnsureResident stages the tower to
the queue's device. A record previously claimed a per-block staging miss could
only be caught on a leased device; that was false. A hand-built kXPU queue and
a counting backend reach EnsureResident through the production Forward, so a
miss on any block index now reds on a CPU box with no GPU: 23 staged copies at
depth 2, 15 if block 1 is skipped, per-block slope 8.

The official safetensors vision loader. 267 vision tensors in shard 1 were
never read and — because the dense name-map pass has no leftover refusal — never
even counted. They are now loaded and accounted for on both the dense and EXL3
arms, reached from LoadDeepseekV4ForCausalLM's safetensors branch. Weights are
copied out of the mmap rather than borrowed: safetensors offsets carry no
alignment guarantee, and this tree's own fixture writer forces an odd payload
base, so a borrowed bf16 view can start at an address no bf16 access may assume.

The tensor map is pinned to the released artifact. A 571-line offline
checker holds the loader to the measured shard-1 header — 72,633 tensors, 267 of
them vision, 932,786,176 payload bytes — with three recorded fixtures. Mutating
the recorded vision count 267 -> 266 exits 1 naming both disagreements.

The parity harness can now fail. This is the largest correction here. The
W6/W7 comparator had no bound and exactly one non-zero exit: a drift from 3.83%
to 12% produced rc 0, a well-formed report and a DONE banner. Four drivers
recorded tee's exit status instead of the command's, wrote a steps.txt
nothing read back, and treated a step that never ran as a step that passed. Five
rounds of repair and five independent reviews later, every judging key, every
stage and the separability precondition must be declared; a non-finite statistic
is a failure rather than a silently disabled bound; the permutation margin is
derived per run from the bf16 grid spacing rather than a guessed constant; and a
new 48-case suite reds when any of it is reverted.

Scope

91 commits, spanning W1 through W7 of this row across several sessions. Roughly
half the volume is recorded data rather than code: goldens.json is 16,387
lines, the shard-1 header manifest 1,755, the GGUF name manifest 1,371.

Verification

Release build, VLLM_CPP_CUDA=OFF on this host. Family gate 26/26 with zero Not
Run and every binary confirmed present; the W6 comparator suite 48/48; preflight
clean with no failing gate. Device numbers come from rc leases on thor:gpu0
(jobs 14908980, 665b2427, c472faab); test_cuda_deepseek_v4 ran 29 cases
and 90,082 assertions with zero skips on sm_110, and exits 77 on a CPU host, so
its green is a device green.

What this does NOT do

#2411 stays open. A served image on a CUDA build still fails, now at an
anonymous vt: MatVec weight size mismatch in Gemm's host-float fallback --
device-specific only in reachability, since a CPU build refuses earlier at
kDevicePending. The blocker moved three times under measurement (fp8_ds_mla ->
vision residency, closed -> MatVec, live), and each move was visible only
because the previous one was genuinely repaired.

The real 156.287 GiB safetensors payload has never been read. Names, shapes,
dtypes and counts are pinned to the measured header; values are proven only
against synthetic fixtures. The W6/W7 jobs have not been re-run under the
repaired harness, which needs a lease, so no real leg has yet reported its own
margin or rounding scale.

The 4.9% / 0.998 bound should not be quoted as independent evidence: given
condition (2), the triangle inequality already forces it. The conditions doing
real work are sentinels-exact-with-identity-permutation and the f32 arm inside
the oracle's own floor.

Eight local issues stay open and are named in the records: the MatVec blocker;
test_deepseek_v4_mm_chat's image branch encoding a CPU-only premise that can
never hold on CUDA; 8 of 20 mm_reach cases failing on aarch64 from the i8mm
quant repack, proven by a same-binary A/B; test_serve_deepseek_v4_mm timing
out at 1800 s with no output and no guessed cause; three unproven W6 harness
weaknesses; moved spec line anchors in the W7-CUDA driver; three malformed
bounds inputs that crash instead of reaching the ERROR exit; and a stray
NN - <text> line inside a ctest FAILED block read as a test name.

FOLLOWING_AGENTS_PROTOCOL

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

mudler added 30 commits August 31, 2026 15:19
…ion support (#2411)

DeepSeek publishes the vision checkpoint under the existing text architecture string, but neither pinned nor current vLLM implements its image path. A separate multimodal row keeps the text model's evidence intact while the processor, tower, merge, serving, and quantized arm are incomplete.

The model-author runtime is the only complete vision reference. Pin it as a narrow secondary oracle with gateable set to no until a leased TP4 run loads the 156.287 GiB artifact and records generated output.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:gpt-5.6-sol [OMP]
The task branch was pinned before concurrent changes reached origin/main. Merge the current base so the staged and commit protocol gates evaluate the tree that would land.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:gpt-5.6-sol [OMP]
…and image processors (#2411)

DeepSeek-V4 Flash Vision needs the model-author prompt and pixel layouts before later waves can attach the vision tower. This slice ports the pinned encoder, Pillow-compatible preprocessing, N-layout expansion, and named failures into the shared multimodal containers.

The prompt encoder emits all six pinned task transitions. It rejects every non-null non-string or unknown task before rendering, while a null task remains absent.

The input bridge checked-computes the required patch feature width. It rejects malformed shared image rows before extent checks or downstream indexing.

Model inference does not reach W1 yet. W4 wires this processor into the registered model forward. W5 wires the runner, public ABI, and OpenAI server. Row MODEL-MM-deepseek-v4-deepseek-v4-for-causal-lm and issue #2411 own both waves.

The focused gates cover every pinned encoder case and seven deterministic Pillow 12.1.1 resize fixtures. Sixty-two production-source mutations make every W1 guarantee red.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:gpt-5.6-sol [OMP]
…OCm, and Vulkan gates (#2411)

The developer requires every accelerator path to run on resource-controller devices. A CUDA pass cannot stand in for ROCm or Vulkan, and no direct SSH run can stand in for a lease.

The spec now assigns separate CUDA, ROCm, and Vulkan waves. It pins the current fleet choices and records the measured RADV GFX1151 Vulkan device on strix:gpu0.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:gpt-5.6-sol [OMP]
…tower and aligner (#2411)

DeepSeek-V4 Flash Vision turns pixels into language rows in two stages that W1 could not reach: a 32-layer-capable native-resolution ViT and the downsample-3 aligner. This slice ports both as a config-driven composition over the public vt operations, so CUDA, ROCm and Vulkan run one implementation.

The tower is non-causal over a whole image and carries no learned position table. Position comes from a 2-D RoPE whose height frequencies precede its width frequencies, exactly as the pinned torch.stack([hpos, wpos]).flatten(1) orders them. RMSNorm weights stay f32 and q/k widen to f32 across the rotary multiply, because the pinned module widens there and narrows once.

The aligner zero-pads the patch grid to a multiple of three, unfolds non-overlapping 3x3 cells in F.unfold's [channel, dy, dx] order and projects them through GELU into the language width. A cached scalar gather maps every spatial pad cell onto one appended all-zero patch row, so the unfold needs no separate padded buffer.

Reduced-shape goldens come from a direct transcription of inference/vision.py at the pinned revision under torch 2.11.0. They cover the RoPE tables, patch embedding, per-block outputs, final norm, unfold order and both aligner stages.

Model inference does not reach W2. W3 owns weights, W4 wires the registered model forward and image-span visibility, and W5 wires the runner, public ABI and OpenAI server. Row MODEL-MM-deepseek-v4-deepseek-v4-for-causal-lm and issue #2411 own those waves, and the row spec lists them under Owed.

check-attention-rung-consistency was red here. The tower's naive-rung reason sat 46 lines above its vt::Attention call and the checker reads 20. The reason now sits on the call.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…t users run, and the two behaviours this spec missed (#2411)

This spec was written on 2026-08-31, when no quantization of DeepSeek-V4 Flash Vision existed and no runtime outside the checkpoint repository could run it. Both facts changed, and the row was still planning against the old ones.

The developer named unsloth/DeepSeek-V4-Flash-Vision-Exp-GGUF as the artifact that has to work. It ships the llama.cpp two-file shape, which this spec listed as a non-goal and a stop condition. That non-goal is withdrawn, with the reason recorded beside it: it was written to stop the row from asking users to reassemble weights, and producing a private combined artifact nobody downloads is now the thing that would do that. clip_mmproj_gguf.cpp already reads the two-file shape for Qwen3-VL, so no container is invented. UD-IQ1_S plus mmproj-BF16 is 77.65 GiB resident and fits one GB10, which makes the single-device gate reachable for the first time. The revision and four SHA-256 values are pinned because unsloth re-quantizes in place under unchanged names.

llama.cpp merged deepseek4 vision on 2026-09-02. Release b10766 is the merge commit and the first release carrying tools/mtmd/models/deepseek4v.cpp, which returns 404 at the stock llama-cpp pin b10451, 315 commits behind. It is registered as llama-cpp-dsv4vision rather than by advancing the stock pin, because every floor already measured against b10451 means what that release does. It is not a second algorithm source; it is the first runnable reference for the exact artifact, on hardware this project leases, at a quantization our arm can match.

Reading that implementation found two DeepSeek-V4 language behaviours that change under image input and that this spec never mentioned. The text GGUF's first shard holds 43 tensors and nothing else: one exp_probs_b_vl bias per language layer, the MoE routing bias for image tokens, which on the three hash layers replaces tid2eid routing rather than supplementing it. And the 128-token sliding window is not applied inside an image span. Both are exactly the failure risk 3 already names: drop either and the argmax stays plausible while the token gate stays green. They are specified, given tests that cannot pass on tokens alone, and listed under Owed.

Removing the new AGENTS.md registry row makes check-oracle-pins.py red with "pinned in .agents/oracles/ but absent from the AGENTS.md table"; restoring it returns "oracle-pins ok (15 oracles pinned)". check-agent-record, check-symbol-anchors and check-now-current pass.

No product code changes here. W3 owns the loader work, W4 owns the router bias and the window.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
The branch was pinned 1218 commits ago, which made agent-preflight.sh skip both commit gates because origin/main was not an ancestor of HEAD. Merge the current base so the protocol gates evaluate the tree that would land.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
… load the image-token routing bias

DeepSeek-V4-Flash-Vision carries a second per-layer MoE routing bias, and this
tree could not read the checkpoint that holds it. The GGUF arm's totality gate
reported `blk.0.exp_probs_b_vl.bias` as a leftover its name map does not cover.
The EXL3 safetensors arm refused `layers.0.ffn.gate.bias_vl` by name as a tensor
no arm routes. The official dense arm accepted both and counted neither.

The tensor is the bias the router adds when the token being routed is an image
token. The pinned `unsloth/DeepSeek-V4-Flash-Vision-Exp-GGUF` `UD-IQ1_S` build
carries one f32 `[256]` vector for every one of its 43 language layers, and its
first shard holds those 43 tensors and nothing else. That was read from the
shipped header on 2026-09-05 by an HTTP range request over the first 14 MB.

Both weight arms now account for the tensor and load it beside the text bias, on
every layer. The read sits outside the hash branch on purpose. A text token on a
hash layer routes through `tid2eid` and takes no bias at all, which is why the
converter emits no `ffn.gate.bias` there. An image token has no meaningful token
id to hash, so on those three layers this bias is the only routing input it has.
A read inside the `else` would drop them silently.

The read is optional in both arms. A DeepSeek-V4 text checkpoint carries none of
these tensors, so a required read would refuse every text checkpoint the tree
already loads. llama.cpp marks the same tensor `TENSOR_NOT_REQUIRED` in PR
#28154, at oracle `llama-cpp-dsv4vision`.

Nothing selects the loaded bias yet, and this slice therefore lands unreached.
Per-token selection between the two biases, the hash-layer replacement at
forward time and the non-causal image-span window are W4's work. Row
`MODEL-MM-deepseek-v4-deepseek-v4-for-causal-lm` owns that wiring, issue #2411
tracks it, and `.agents/specs/deepseek-v4-flash-vision.md` lists all three under
`## Owed`.

Refs #2411

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…guage-side tensor delta at 43 names (#2411)

W3B found and loaded the image-token routing bias, but nothing said whether it was the ONLY thing the vision checkpoint's language half needs. A port map that is complete by inspection is how a fourth tensor family gets discovered at load time, on a leased box, after W4 is written.

Range-reading all three UD-IQ1_S shard headers of the pinned unsloth vision repository and diffing the 1371 names against scripts/dsv4_gguf_manifest_names.txt, the 1328-name TEXT manifest check-dsv4-gguf-namemap.py already pins, gives 43 names present in vision and absent from text, all of them blk.N.exp_probs_b_vl.bias, and zero names present in text and absent from vision. The two artifacts declare the same block count, expert count, hash-layer count and compress-ratio array, so the counts compare.

The language half of this model therefore needs nothing from this port beyond that one bias, and the whole tower, aligner and sentinel group lives in mmproj-BF16.gguf, which W3A owns. W4 has no third unknown family waiting for it.

The manifest is committed as a fixture rather than left in a session, because the checker that would read it cannot see this artifact yet: it generates 1328 expected names and asserts exact set-equality, so the vision artifact fails it by construction and no gate covers the shipped vehicle's language half. Extending it is a semantic checker change and needs its own red-before evidence, so it is owed rather than done here, with this measurement as the input.

No product code changes. No correctness or performance claim.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…ek4v mmproj into the W2 vision types (#2411)

The published artifact users download is a llama.cpp two-file pair, and this
tree could read only half of it. `unsloth/DeepSeek-V4-Flash-Vision-Exp-GGUF`
ships a `mmproj-BF16.gguf` whose `clip.projector_type` is `deepseek4v`, and the
`clip` mmproj reader's scope was `qwen3vl_merger` alone. W2 already has the
tower and the aligner. This gives them their weights.

Four layout mismatches separate what the file stores from what W2 consumes, and
none of them crashes when it is wrong. Each one loads, runs, and produces a
fluent, wrong model, so each one is derived from a pinned source rather than
guessed, and each one has its own value-exact case.

Attention is stored as three separate projections and W2 wants one fused
weight. The fused row order is q, k, v because that is where
`deepseek_v4_vision.cpp` slices the three back out with `RowSlice`. The MLP is
stored as separate gate and up matrices and W2 wants one merged operand. Gate
comes first because that is the half `vt::SiluAndMul` applies SiLU to, and it is
the half the pinned converter's `gate, up = data_torch.chunk(2, dim=0)` took.
The patch embedding is stored as a 4-D conv2d weight and W2 wants the 2-D torch
Linear weight it came from; the converter produced the conv2d shape with a pure
reshape of an `nn.Linear` over an `F.unfold`, so flattening it back is the
identity in [channel, dy, dx] order and not a permutation. Every bias, every
norm weight and the patch embedding are stored f32, which is llama.cpp's
small-tensor convention rather than the checkpoint's dtype, so the linears
narrow to the model dtype while the RMSNorm weights stay f32, exactly as W2's
contract states. A reader that passed the file's dtype through would move twice
the bytes on the model path and leave every token identical.

The Qwen3-VL refusal keeps rejecting `deepseek4v`. `model_loader.cpp` goes
straight from `RefuseUnsupportedClipMmproj` into
`LoadQwen3VLVisionFromClipMmproj`, so widening that one refusal would route a
DeepSeek projector into the Qwen3-VL reader. The new arm has its own refusal
beside it, and a case asserts the old one still fires.

The tensor map closes in both directions, for the reason the Qwen3-VL
accounting already records: a projector whose extra tensors are dropped silently
produces a tower that runs and is wrong. The enumeration returns 427 names at
depth 32, which is the artifact's own tensor count.

NOTHING REACHES THIS READER YET. `RefuseUnsupportedDeepSeekV4ClipMmproj`,
`DeepSeekV4ClipMmprojVisionConfig`, `LoadDeepSeekV4VisionFromClipMmproj`,
`DeepSeekV4ClipMmprojExpectedTensors` and
`RefuseUnaccountedDeepSeekV4ClipMmproj` have no production call site, and the
four sentinel vectors the reader returns have no consumer. Row
`MODEL-MM-deepseek-v4-deepseek-v4-for-causal-lm` owns the wiring in W4, issue
[#2411](#2411) tracks it, and the row's
spec lists both gaps under `## Owed`. The real 934,462,656-byte file has also
never been read by this code; the fixture is synthetic and built to the
artifact's measured header, and W3 owes the first load.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
The row lands as one pull request, so the deepseek4v mmproj reader joins the branch that already carries W1, W2 and the spec amendment.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
The row lands as one pull request, so the image-token routing bias accounting joins the branch that already carries W1, W2, W3A and the spec amendment.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
The row branch was pinned before the oracle-pin record repair reached origin/main, so its tools suite still failed on an assertion main no longer carries. Merge the current base so the protocol gates evaluate the tree that would land.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…e MODEL pin was bumped for (#2411)

The W0 spec commit `b1122cf5a` took the MODEL matrix pin in
`scripts/check-agent-record.py` from 379 to 380 and wrote its justification
block, but it changed no test. `scripts/check-pr-size.py` refuses a checker
change that carries no semantic mutation evidence in the paired suite, so the
row gate reported `checker change 'scripts/check-agent-record.py' requires
semantic mutation evidence in tests/scripts/test_agent_record.py`. That gate is
CI-only, because it needs `--base` and `--head`, so `agent-preflight.sh` skips
it and nothing local caught the omission.

The evidence a bumped count owes is not the constant. Asserting the pin reads
380 restates the line the checker already holds and proves nothing about the
tree. The invariant is that the pin and the rows agree, and that the row this
bump was made for is one of the rows being counted. The new test names
`MODEL-MM-deepseek-v4-deepseek-v4-for-causal-lm`, requires exactly one of it in
`model-matrix.md`, and pins its lifecycle state.

The hazard here is the opposite of the one the qwen4-exp and glm5-next tests
guard. Those resist splitting one model into two or three rows. This row shares
the architecture string `DeepseekV4ForCausalLM` with the existing text row, so
a reader re-deriving the count from architecture strings reads it as a
duplicate. Deleting it as one returns the tree to 379 and the pin follows,
leaving the file internally consistent and silently short the entire image
path. The test therefore asserts that both rows carry the string, that only the
multimodal row qualifies it with `vision_n_layers=32`, and that the
`deepseek-v4` lane holds exactly its four named rows.

The running history in `test_model_row_ratchet_is_load_bearing` stopped at 375
while the pin had moved four more times. That history is what a later
re-derivation reads to tell a bump that followed a row from a bump that
silenced a failure, so it is extended through 379 -> 380.

`.agents/engine-matrix.md` rides along because this change made it stale. The
`ENG-RECORD-ANCHOR-RATCHET` row cites two line anchors inside the test file
this commit edits, and inserting the test moved both by 76 lines. The row's own
prose records this failure mode, and the two-way anchor ratchet turns it into a
red gate, so the citations are re-pointed at the symbols they already named.
Only the two line numbers change.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…ower the Owed entry its commit body claimed (#2411)

W2's commit body (`8c10cad16`) ends "Row
MODEL-MM-deepseek-v4-deepseek-v4-for-causal-lm and issue #2411 own those waves,
and the row spec lists them under Owed". The row spec did not. `## Owed` named
W1's processors, W3A's mmproj reader, the `exp_probs_b_vl` trio, the GGUF name
map and the three device lanes, and nothing at all about the W2 vision tower or
the aligner. AGENTS.md, "Nothing lands dead", asks for the spec's `## Owed`
specifically; `## Now` is a different surface with a different trigger, and it
carried the only prose that mentioned W2 being unreached.

The pull request body becomes the landed commit message here, so an unrepaired
claim would have put a false statement on `main`. The entry now says what is
unreached, names the row that owns the wiring and cites the issue.

Measured rather than asserted. `grep -rn 'DeepSeekV4Vision\b'` over `src`,
`include`, `examples` and `tests` returns no construction of the class outside
`deepseek_v4_vision.cpp` and its test, and the only non-test file that includes
the W2 header is `clip_mmproj_gguf.h`, which takes the config and weights types
for W3A's reader and is itself already listed as unreached.

The other three Owed claims on this branch were checked the same way and are
true as written. W3A (`303e69108`) claims its five reader symbols and the four
sentinel vectors are listed, and they are. W3B (`ebca4db83`) claims all three
`exp_probs_b_vl` behaviours are listed, and per-token selection, the hash-layer
replacement and the non-causal image-span window are each named. The spec
amendment (`7fb23f584`) claims the two missed language behaviours are owed, and
the same bullet covers both.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…ng bias BYTES and refuse a narrow one

A fresh review of W3B (`ebca4db83`) found that the arm which previously refused
the vision checkpoint outright is the one arm whose loaded bytes nothing checks.
The EXL3 carried arm's case asserted the width of `gate_bias_vl`, that it was not
empty, and that `text[i] != vl[i]`. Two defects pass all three. A slot filled
with zeros keeps its width, and a SWAP of the two biases keeps them unequal. The
suite therefore proved the arm ROUTES the name while the commit and the spec both
claimed it LOADS the bias. W4 selects between the two biases per token, so a
transposition there routes every image token on the text bias and every text token
on the vision bias, changing which of 256 experts fire with no crash and no gate
red. That is the failure the spec's risk 3 names.

The case now asserts every element of `gate_bias_vl` on every layer, and every
element of `gate_bias` on the gated layer, against `dsv4_exl3_fixture::CarriedValue`
for the tensor's own name. Both reviewer mutations were reapplied and both are now
red. The all-zero `Account` plus `assign` mutation gives four failures reading
`CHECK( 0 == Approx( -0.999559 ) )`; the gated-layer transposition gives four more,
`CHECK( 0.0988742 == Approx( -1.24119 ) )` on the vision slot and
`CHECK( -1.24119 == Approx( 0.0988742 ) )` on the text slot. The loader was
correct for this finding and no product line changed for it.

The GGUF arm validated no width at all. `V4GgufCtx::Vec` checks the residency the
policy elected and the role a tensor was routed under, and no geometry, so a
`[E-1]` router bias published under an unchanged name loaded in silence. The
router indexes that vector by expert id, so the next reader walks off the end of a
short host `std::vector<float>` instead of getting a refusal. A checkpoint
re-quantized in place keeps its file name, which is why the porting rule asks for
a sha256 beside the repo id, so this is a live shape rather than a hypothetical.
The new `V4GgufCtx::Vec1D` takes the expected width and refuses. Red before: a
file whose KV declares `expert_count` and whose tensor is one narrower raised
nothing, and both new cases failed against an empty message. Green after:
`deepseek-v4 gguf: blk.0.exp_probs_b_vl.bias must be a 1-D [4] vector
(n_routed_experts), got rank 1 first dim 3`.

BOTH router biases are now checked, not only the vision one. The text
`exp_probs_b.bias` beside it carried the identical weakness. It is a long-standing
gap rather than a regression W3B introduced, and this widens the finding's scope
deliberately: repairing one while leaving its neighbour would leave the two to
drift the first time either is touched, and the safetensors arm already gets this
guarantee from `carried.Float(..., {ne})` while `glm5_next_loader.cpp` and
`glm_moe_dsa_loader.cpp` already get it from `LoadVecF32(g, name, e)`.

No arm requires the bias to be present on all layers or on none, and that is now
a stated decision rather than an accident. The choice is to MIRROR the oracle.
llama.cpp declares `ffn_exp_probs_b_vl` with `TENSOR_NOT_REQUIRED` for each layer
independently, so a partially converted file loads there, and refusing a file the
oracle accepts is a divergence that would need its own justification. This one has
none: the empty slot is a state the consumer must already handle, because a text
checkpoint presents it on every layer. W3B's "on every layer of a vision artifact
and on no layer of a text one" describes the two published artifacts and is not a
constraint the loader enforces. A new case pins the per-layer behaviour, so a later
change to an all-or-nothing refusal is a red test somebody has to argue with.

The spec's section 1 opened by calling the shard "the unsloth text GGUF's first
shard". The shard belongs to the language half of the VISION repository
`unsloth/DeepSeek-V4-Flash-Vision-Exp-GGUF`, and a genuine DeepSeek-V4 text
checkpoint carries none of these tensors, which is what the whole optionality
design rests on. As written, W4 could have read that paragraph as its brief and
concluded the opposite. The wording is repaired and the section is otherwise
unchanged.

`test_deepseek_v4_mm_loader` goes from 6 cases and 83 assertions to 9 and 105. The
other four suites hold their reviewed values: `gguf_load` 19 cases and 1056
assertions, `exl3_loader` 22 and 613, `moe` 12 and 716, `forward` 6 and 34. Every
mutated source was restored byte for byte and verified by SHA-256.

Nothing here selects the loaded bias. W4 still owns the per-token choice, the
hash-layer replacement and the image-span window, row
`MODEL-MM-deepseek-v4-deepseek-v4-for-causal-lm` owns that wiring, issue #2411
tracks it, and `.agents/specs/deepseek-v4-flash-vision.md` lists all three under
`## Owed`.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
… attn_qkv arm by name and bound the geometry

The header claimed that `gguf-py/gguf/tensor_mapping.py` maps
`vision.blocks.{bid}.attn.wqkv` to V_ENC_ATTN_QKV, "which the shared mmproj
base then writes as three SEPARATE `attn_q` / `attn_k` / `attn_v` tensors".
That mechanism does not exist at the pin. `gguf-py/gguf/constants.py` spells
V_ENC_ATTN_QKV `v.blk.{bid}.attn_qkv`, `conversion/base.py` contains no
occurrence of `qkv` at all, the only converter that splits a fused vision qkv
is the model-specific `conversion/qwenvl.py`, and
`conversion/deepseek.py::DeepseekV4FlashVisionModel.modify_tensors` splits
`mlp.w1` only. So a projector converted by the oracle's own
`convert_hf_to_gguf.py` carries the fused form and this reader cannot load it.

The shipped `unsloth/DeepSeek-V4-Flash-Vision-Exp-GGUF` mmproj carries the
SPLIT form, so the gap does not block the shipped vehicle. It does block
converting the checkpoint with the oracle's own script, which is a
quant-matched denominator W6 may need. The fused arm is therefore refused BY
NAME rather than implemented, and `## Owed` records it against issue #2411.

The refusal has to arrive before `RefuseUnaccountedDeepSeekV4ClipMmproj`.
That one reported "carries 64 tensor(s) that this build's deepseek4v reader
NEVER reads", which blames the artifact for a gap in this build and sends the
reader to re-convert a file that is already correct.

The geometry read from `clip.*` was also unbounded. `KvInt` widens every
integer spelling, so a signed `block_count` of -1 and an unsigned one of four
billion both reached `blocks.resize(static_cast<size_t>(config.depth))` on a
path that runs on a user-supplied `--mmproj`, and surfaced as `length_error`
or `bad_alloc` naming neither the file nor the key. Every field is now bounded
where the key that carried it can still be named.

The new block_count case asserts on the PARSED VALUE and not on the allocation
failing. A red-first case for an unbounded allocation performs the allocation
by construction: an earlier draft used four billion, asked for about 80 GB and
tripped the global Linux OOM killer twice on the development box. A test whose
only failure mode is `bad_alloc` is a crash, not a gate.

Red before: 18 cases, 16 passed, 2 failed, 2198 assertions with 7 failed. The
fused case failed on "NOT IMPLEMENTED", on "2411" and on the absence of "NEVER
reads". The block_count case failed on all four message assertions. Green
after: 18 cases and 2198 assertions pass. `test_clip_mmproj_gguf` stays 9 cases
and 272 assertions, because the Qwen3-VL arm is deliberately untouched.

Three more gaps are recorded and not fixed: the unkeyed vision `rope_theta`,
which llama.cpp hardcodes and the converter asserts nothing about; the four
`clip.vision.image_*` preprocessor keys the real file carries and nothing here
reads; and the `general.alignment` fallback the fixture never exercises.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…, the frequency decay and the aligner row order measurable (#2411)

W2's two golden fixtures are hidden 8 / heads 2 and hidden 16 / heads 4, so both
are head_dim 4. That makes rope_dim 2 and leaves EXACTLY ONE frequency per axis,
at exponent 2*0/rope_dim = 0, so inv_freq[0] is theta**0 = 1.0 for any theta
whatsoever. Two mutations were therefore invisible: pinning `rope_theta` to a
literal 10000.0 while a fixture declares 1234.0, and halving the exponent
denominator, which is the classic transcription slip. Production is head_dim 64,
i.e. 16 frequencies.

The same reduction degenerated the aligner. Every fixture grid ((2,5), (3,3),
(3,4)) aligns to ONE merged row at downsample_ratio 3, and with block_rows == 1 a
row-major and a column-major walk of the merged grid are the same sequence, so
swapping the two loops changed nothing. That order is not cosmetic: it fixes the
spatial arrangement of the image tokens W4 hands the language model, and a
448x448 image (32x32 patches, 11x11 merged) would emit transposed rows with the
token count, the dtype and every golden unchanged. The dedicated unfold case
could not catch it either, because it builds its expectation with the same loop
nesting as the implementation and is a tautology on this axis.

A third fixture closes both. head_dim 16 gives four frequencies at exponents 0,
1/4, 1/2 and 3/4, and its theta 7919.0 is neither the default nor either other
fixture's. Its grids 4x5 and 7x4 merge to 2x2 and 3x2, so both distinguish the
two orders. One head is deliberate: heads 2 and 4 are already covered, and
holding hidden_size at 16 keeps every stage inside the committed tolerances.

REGENERATED WITH THE COMMITTED GENERATOR, which is the only option. There is no
local checkout of the model-author pin and no network access to it, so the
formulas cannot be re-derived from source; a transcription error shared between
the generator and a new fixture would not be caught by adding fixtures from that
generator. What can be checked was: the local torch is 2.11.0+cu130, the exact
version the fixture records, and re-running the generator before this change
reproduced the committed goldens BYTE-FOR-BYTE. The new file is a pure insertion
of 7448 lines, so the two original fixtures are untouched.

Two independent oracles say the row order is row-major, so the code is correct
and was merely ungated; nothing here changes it. The model author's
`inference/vision.py` is the algorithm oracle. llama.cpp release `b10766` =
`9400c8946e4da5e7694f2c26d6d4e50e14b690fa` (oracle `llama-cpp-dsv4vision`) maps
merged cell (r, c) to aligner row `r * n_llm_w + c` in `clip.cpp`'s `set_input`
for `PROJECTOR_TYPE_DEEPSEEK4V`, and its graph in `tools/mtmd/models/deepseek4v.cpp`
(blob `ffe8f59d9997` at that pin) reaches the same order through `ggml_im2col`
over a [x, y, n_embd] tensor reshaped `[ne0, ne1*ne2]`. The new row-order case
takes its destination index from that formula rather than from our loop nesting,
so it is not a second copy of the implementation.

THE GELU STAGE BOUND IS NOW DERIVED RATHER THAN DECLARED, and that is a finding
rather than a concession. The declared 0.01f was LOWER than the 0.016f allowed
for the `aligner_hidden` buffer that feeds it, which is not derivable:
sup|GELU'| is about 1.0839, so GELU can amplify the error it is handed by about
8.4% and can never be relied on to shrink it. Measured per case, input -> output:
0.0078125 -> 0.0078125, 0.0078125 -> 0.0078125, 0.015625 -> 0.00878906,
0.0078125 -> 0.00390625, 0.0136719 -> 0.0117188. Every case ATTENUATES and none
reaches the ceiling, so no divergence enters at this stage; the pre-existing
`heads4_depth1` case already ran at 0.015625 against 0.016, one bf16 ulp from
failing, which is how close the declared value always was. The derived bound is
TIGHTER than 0.01f for three of the five cases, and `aligner_hidden` keeps its
own absolute cap, so the stage stays transitively bounded at 0.0173f.

Every stage upstream of GELU on the failing case is at or below what the
pre-existing fixtures already produce: patch 0.00195312 against 0.004, vision and
unfold 0.015625 against 0.024 where an existing case reaches 0.0234375, and
aligner_hidden 0.0136719 against 0.016 where an existing case reaches 0.015625.
The new geometry is not worse anywhere.

Red before, green after, each mutation rebuilt (3 ninja steps) and restored
byte-for-byte to sha256 6006b685da095ade85c2c353083860f81b04f183f3d0491bacc85899617b3aa0:
pinning theta to a literal was GREEN before and now reds the 2-D RoPE case;
halving the exponent denominator was GREEN before and now reds the RoPE case, the
aligner_hidden bound and the new frequency-ladder guard; swapping the block_row
and block_column loops was GREEN before and now reds the unfold golden, the
output golden and the new row-order case at its first mismatched element.

Two coverage guards keep the geometry from being reduced away again. One fails
unless a fixture has at least four distinct frequencies and a non-default theta;
the other fails unless a case has a merged grid larger than one in both
dimensions.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…repair evidence

The five mutations that prove the new gate detects what it claims live only in
this session's terminal until they are written down. The spec is where a later
reader looks for what a wave measured, so the mutation table, the failed
assertion counts and the before/after suite counts go here.

The transpose row carries the reason its number is 132: that is 144 elements
less the 12 on the diagonal, which is exactly the set a transpose can move. The
index-0 check the repair replaced would have reddened on none of them, so the
figure is what separates a value walk from a spot check.

The section also records why the out-of-range block_count case uses 4096 rather
than the four billion the defect admits. A red-first case for an unbounded
allocation performs the allocation, and the first draft took the development box
down twice through the global OOM killer.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…on path's memory format and its per-layer pool traffic (#2411)

Two W2 guarantees were stated in comments and measured by nothing.

THE MEMORY FORMAT. `deepseek_v4_vision.cpp` says the tower is bf16 on the model
path with two annotated f32 exceptions, and widening the attention-output buffer
to f32 left the whole suite green. AGENTS.md says exactly why: a token gate
CANNOT detect a dtype that is too wide, because the values still match while the
path moves twice the bytes, and `vt::MatmulBT` and `vt::Add` accept the mix
without complaint. The forward now reports the dtype of every internal scratch
buffer it allocates, in allocation order, through an optional capture field that
production never sets, and the test asserts the exact sequence. The two f32
entries are named and keep their reason: the pinned `apply_rotary` widens q and k
before the f32 cos/sin multiply and narrows once afterward. The count of f32
entries is asserted from the other end as well, so a new wide buffer cannot be
added without the case failing.

THE PER-LAYER SCRATCH. The existing allocation case compares driver Alloc counts
and pool statistics across two Forward calls at one shape, and a per-layer pooled
Get/Put inside a single call reuses the same warm block and changes neither.
Moving the `normalized` buffer into the block loop was therefore invisible, and
at the production depth of 32 that form costs 32 extra pool round trips per
image.

Bounding `misses` after a single Forward, which the review proposed, does not
reach it either, and that is measured rather than argued. One Forward from a
drained pool, hoisted against un-hoisted:

  depth 2   17 gets (13 misses,  4 hits)   vs   18 gets (13 misses,  5 hits)
  depth 4   21 gets (13 misses,  8 hits)   vs   24 gets (13 misses, 11 hits)
  depth 8   29 gets (13 misses, 16 hits)   vs   36 gets (13 misses, 23 hits)

`misses` is 13 in both forms at every depth: the fixed working set is identical
and the pool serves every extra request from its own free list. The observable
that separates them is pool GET traffic PER LAYER, 2 hoisted against 3
un-hoisted. The case measures one Forward at two depths and asserts the slope is
2, the two buffers the shared MlpGateUpMethodBase seam legitimately owns
(`UnquantizedMlpGateUpMethod::Apply`'s merged gate_up output and the activation
it returns). Everything this file allocates is hoisted and contributes 0. The
depth-independent `misses` bound is kept beside it because it is true and worth
holding, not because it can see this.

Red before, green after. Both mutations were GREEN on the tree before this
change. Each was rebuilt (3 ninja steps, never 0) and restored byte-for-byte to
sha256 6006b685da095ade85c2c353083860f81b04f183f3d0491bacc85899617b3aa0, and the
tree was rebuilt and re-run green after each restore.

Widening `projected` to f32 now reds the scratch-dtype sequence and the f32
count. Un-hoisting `normalized` now reds the slope; because the recorder names
that buffer, the reviewer's literal edit no longer compiles and the mutation has
to carry the record line with it, which also reds the name sequence. So the
slope was proved load-bearing ON ITS OWN by a second mutation that adds one
per-layer scratch buffer the recorder never sees: it reds
`per_layer == kPerLayerPooledBuffers` and NOTHING else, 1 failing assertion in
the whole suite.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…borrow dropping the load-time repack markers (#2411)

`BorrowResidentWeight` hands each block's gate-up weight to the shared
MlpGateUpMethodBase seam as an OwnedTensor. It copied dtype, rank, shape, `nk`,
bytes and `d_dev` and stopped, so `repacked`, `q8_0_aligned` and
`elem_kn_repacked` were lost at the boundary. Those three say the BYTES were
rewritten at load into a different block interleave or orientation while the byte
count and the [N,K] shape are unchanged, so nothing downstream can notice:
`ValidateTensor` does not check them and no value gate can see them.

THIS IS THE DEFECT `main` ALREADY FIXED AT `7a937db8a` (#2031), re-introduced in
a private helper. There the shared `dense_attn::ResidentWeight` built its tensor
with `MakeTensor`, which never copies the flags, and an i8mm-interleaved
`block_q8_0x4` buffer (136-byte blocks) reached `kMatmulBTQuant` flagged as flat
`q8_0` (34-byte blocks). It decoded to NaN, then all-zero logits, then token id
0, with nothing logged, because the `lm_head` GEMM swallowed the NaN. That is the
qwen4_exp degenerate output, and it cost a debugging campaign.

PROPAGATE, DO NOT REFUSE. A fail-closed check here would reject a repacked weight
rather than decode it, which removes the CPU i8mm fast path instead of fixing the
loss; this tree has made that mistake before. `ResidentWeight` re-reads `repacked`
and `elem_kn_repacked` off the OwnedTensor on its host-alias arm and keeps its own
audit guard on the device-staging arm, so the markers only ever had to survive
the borrow.

NOTHING MOVES ON THIS HOST, and that is why it needs a structural gate rather
than a numeric one. `vt::cpu::QuantRepackActive()` is true only on an aarch64
i8mm host, so on x86 both markers are always false and no golden can shift. W3A's
mmproj reader is what makes it live rather than latent, because it can now hand
this tower block-quantized weights. The gate therefore reads the markers the seam
actually holds, through an observable accessor documented like the existing
`cached_geometry_count`, and the forward is deliberately not run: this fixture's
bytes are not physically [K,N], so a forward under `elem_kn_repacked` would read a
genuinely mislabelled buffer.

Three mutations, each applied, rebuilt (3 ninja steps) and restored byte-for-byte
to sha256 6006b685da095ade85c2c353083860f81b04f183f3d0491bacc85899617b3aa0, with
the tree rebuilt and re-run green after each restore. Dropping `repacked`, the
state W2 shipped, reds two assertions; dropping `q8_0_aligned` reds two; dropping
`elem_kn_repacked` reds two. Each marker is therefore individually load-bearing.
An unmarked sibling block is asserted to come back unmarked, so a stub that
answers true cannot pass, and the accessor's own range refusal is covered.

ONE GAP STAYS OPEN AND IS NOT THIS ROW'S TO CLOSE. `ResidentWeight`'s
device-staging arm returns `MakeTensor(w.d_dev.get(), ...)`, which carries no
markers at all, so `q8_0_aligned` cannot reach a CUDA Q8_0 GEMM through that arm
for ANY model. That is a shared-seam gap in `dense_attn_block.h`, not in this
file, and it is recorded in the row spec rather than repaired here.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…eader from repeating the split-qkv premise

The file's opening comment listed the separate `attn_q` / `attn_k` / `attn_v`
storage as a property of the format. It is a property of the shipped artifact.
The header and the spec were corrected in the same repair, and a third copy of
the retracted claim is how it gets read back in as true.

The comment now also names the four refusals the suite gates, because a reader
who only sees the four layout joins listed will not expect the refusal cases.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…ion weight refusal so each one is load-bearing (#2411)

`ValidateWeights` makes fifteen `ValidateTensor` calls and nothing reached any of
them. Deleting the `aligner_w2_weight` refusal outright left the suite green, and
the header's "RMSNorm weights stay f32" claim rested on three refusals nothing
drove, so relaxing one to accept the model dtype was green as well. A refusal
nothing reaches is not a contract, it is a comment.

One table of 35 rows rather than 35 cases, and every row is individually
falsifiable rather than collectively. Each of the fifteen tensors is offered with
the wrong dtype and with the wrong shape; the two remaining `ValidateTensor`
branches, a mismatched rank and absent storage, are driven on one representative
tensor each.

The dtypes are driven in BOTH directions. A model-dtype weight is offered as f32
and an f32 norm is offered as bf16, so the f32 polarity of the three RMSNorm
weights cannot be flipped silently in either direction. Two blocks are covered
rather than only block 0, because these checks sit inside a loop over every
block, and a row that only ever looked at block 0 could not tell a loop from a
single test.

Red before, green after, each mutation rebuilt (3 ninja steps) and restored
byte-for-byte to sha256 6006b685da095ade85c2c353083860f81b04f183f3d0491bacc85899617b3aa0,
with the tree rebuilt and re-run green after each restore. Every one was GREEN
before this change, and each now reds EXACTLY the rows that name it and no
others:

  delete the aligner_w2_weight refusal   3 rows: its dtype, shape and storage
  relax norm1 from f32 to any dtype      2 rows: block 0 and block 1 norm1 dtype
  widen the MLP w1 shape to accept any   1 row:  block 1 MLP w1 shape

The third mutation is not one the review named. It was added because the first
two only move dtype rows, and without it "each row is individually falsifiable"
would be an untested claim about the shape half of the table.

The row label is captured as a std::string rather than as the bare const char*
it is stored as, because doctest stringifies a character pointer as a pointer and
a failing row was unidentifiable.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…ter-bias width before materializing it

`V4GgufCtx::Vec1D` read the width off the tensor it had already loaded, so it
dequantized first and refused second. For the `[E-1]` shape the two new cases
feed, the order does not matter, because a bias one element short costs nothing
to materialize. For a corrupt or absurd declared width it decides what the
failure looks like: the loader would try the allocation the header asks for and
surface `bad_alloc`, or get killed, instead of the named refusal. A guard whose
only failure mode is a failed allocation is a crash rather than a gate, and an
allocation sized from an unvalidated header takes the machine down with it
rather than one test. A sibling suite in this tree fed an absurd `block_count`
into an unbounded resize, reserved about 80 GB, and tripped the global OOM
killer.

The width now comes from the file header through `GgufFile::Get`, and the
refusal happens before `Vec` is called at all. The message is unchanged, so both
`NARROW` cases still assert on the tensor name and the owed width rather than on
an allocation failing.

Mutation, on this head: deleting the `VT_CHECK` and returning `Vec(name, role)`
directly makes `dsv4 vision GGUF: a NARROW exp_probs_b_vl is REFUSED, not read
past` and `dsv4 TEXT GGUF: a NARROW exp_probs_b is REFUSED too` fail together,
four assertions against an empty message, which is the same red the guard was
introduced against. The source was restored byte for byte, SHA-256
`8b282c47a11c3f3346001ab28678ba0195e42206b4941dbdabe1922b7ca4a565`.

The five-suite gate passes under a 6 GB address-space cap.
`test_deepseek_v4_mm_loader` holds 9 cases and 105 assertions, `gguf_load` 19 and
1056, `exl3_loader` 22 and 613, `moe` 12 and 716, `forward` 6 and 34.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…width guard reads the header

The repair evidence said `Vec1D` takes the expected width and refuses, and it
did not say WHERE the width comes from. That is the part a later reader needs,
because moving the check onto the loaded tensor would look like a harmless
simplification and would quietly turn a named refusal into a failed allocation.
The section now states the ordering, the reason for it, and the mutation that
holds it.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
… repair evidence and size the geometry cache for W4 (#2411)

The seven review findings and their repairs now have one place a reader can find
them, with the numbers rather than the verdicts: the two fixture degeneracies and
why a reduced fixture could not see `rope_theta` or the aligner row order, the
llama.cpp confirmation of row-major, the measured per-stage error propagation
behind the one tolerance that changed, and the pool measurement showing that
bounding `misses` cannot see un-hoisted per-layer scratch.

It also states the limit on the fixture provenance instead of leaving it implied.
There is no local checkout of the model-author pin and no network access to it,
so the goldens can only be regenerated with the committed generator, and a
transcription error shared between that generator and a new fixture would not be
caught by adding fixtures from it. What could be checked was checked and is
recorded: the local torch matches the recorded version, and the generator
reproduced the committed goldens byte-for-byte before the change.

W4 GETS A NUMBER IT WOULD OTHERWISE FIND AT RUNTIME. The `IndexSelect` gather
index is `aligned_rows * hidden_size * downsample_ratio^2` i32 per cached
geometry. At the production hidden_size 1024 and ratio 3, a 73x73 patch grid
gives 625 aligned rows and a 5,760,000-entry index, 23.04 MB, and
`kGeometryCacheCapacity` is 8, so a full cache is 184.32 MB of gather indices
alone. The other per-geometry tensors are small beside it, 1.36 MB of f32 RoPE
and 21.3 kB of positions. W4 owns whether eight geometries is right for the image
sizes the server admits and whether that index should be computed instead of
cached.

One shared-seam gap is recorded rather than repaired, because it is not this
row's: `ResidentWeight`'s device-staging arm returns a tensor with no markers at
all, so `q8_0_aligned` cannot reach a CUDA Q8_0 GEMM through it for any model.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
The row lands as one pull request. This brings in the four W3B findings: the EXL3 arm's loaded bytes are now asserted rather than only counted, both router-bias widths are refused before materialization, per-layer optionality is pinned as a deliberate mirror of the oracle, and the spec no longer says a text GGUF carries the vision bias.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…evidence counts the repairs superseded (#2411)

The W3B repair wave took `test_deepseek_v4_mm_loader` from 6 cases and 83 assertions to 9 and 105, and recorded the new figure in its own evidence section. The original W3B paragraph still stated the old counts as present tense, so the spec asserted two different sizes for one suite and a reader had no way to tell which was current.

The repair agent could not fix this: the paragraph sits outside the two spec locations its scope allowed, and it correctly said so rather than reaching past its boundary.

The old counts are dated rather than overwritten. They are the record of what W3B measured when it landed, which is what the evidence section is for; restating them as the repair's numbers would erase the wave's own result.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
The row lands as one pull request. This brings in the six W3A findings: the false split-qkv provenance is corrected and the fused attn_qkv layout the pinned converter actually emits is now refused by name rather than blamed on the file, the geometry read from clip.* is bounded, and the attention output projection, the square aligner linear, the shape guard and the use_silu branch each gained a case that a mutation reddens.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…n capture refusals, the other half of the 21 ValidateTensor sites (#2411)

The weight table landed one commit ago covers fifteen `ValidateTensor` call
sites. There are 21. The other five sit behind `ValidateCaptureTensor`, and
replacing that helper's entire body with a no-op left the suite GREEN: the stage
goldens hand it CORRECT captures, so they exercise the happy path and no
refusal at all. That is the same finding as the weight half and it is closed the
same way.

These refusals are gate-facing rather than production-facing, because production
passes nullptr and copies nothing. That is the reason to drive them rather than a
reason to skip them. A capture contract nothing checks lets a future parity gate
hand the tower a wrongly shaped or wrongly typed buffer and then compare against
whatever happens to be in it, which is a green that measured nothing.

Fourteen rows: dtype and shape for all six capture tensors, the device check on
one of them, and the block-capture count. The device row fabricates a tensor
whose device field says CUDA without allocating anything, because validation
throws before the pointer is ever read, so the branch is reachable on a CPU-only
host.

Red before, green after. Neutering `ValidateCaptureTensor` was GREEN before this
change and now reds 13 of the 14 rows. The fourteenth stays green under that
mutation and correctly so: the block-capture count is a separate check in
`ValidateVisionIo`, not a `ValidateCaptureTensor` call, so a row that failed
there would mean the table was testing something other than what it names. The
mutation was rebuilt (3 ninja steps) and restored byte-for-byte to sha256
6006b685da095ade85c2c353083860f81b04f183f3d0491bacc85899617b3aa0, and the tree
was rebuilt and re-run green after the restore.

The spec also records that both new coverage guards were proved non-vacuous
rather than assumed to be. A guard that passes because it asserts nothing is the
same failure as the degenerate fixture it exists to prevent: setting the new
fixture's theta back to the 10000.0 default reds the frequency guard at 0 >= 1,
and replacing its grids with a single (2,5) reds the row-order guard.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
… the first device run of the vision arm

`### W7-CUDA evidence` records both leases with their job ids, the sm_110 build
proof (CUDA 13.0.88, 41 `.cu.o` all sm_110, `fa2: DISABLED` for [110]), the red,
the fix, and the device block measured against W6's CPU block and against
llama.cpp `b10766` directly -- judged against W6's declared bound rather than a
number chosen here.

`## Owed` is corrected where this wave falsified it. W4's windowed `dev_attn`
refusal was recorded as UNMEASURED because no CPU build can execute it; it fired
at `deepseek_v4.cpp:1319` and is now measured. Its image-span half still has not,
and the two device routers' media refusal was never driven, so both are recorded
as unmeasured rather than assumed to work.

Four gaps this wave found are added. A served image on CUDA still cannot be
answered: the blocker MOVED from vision residency to `vt: MatVec weight size
mismatch`. `test_deepseek_v4_mm_chat`'s image branch encodes a CPU-only premise
-- it asserts an error naming `W7-device`, which `ForwardDevice` can only emit
when the device kernels are ABSENT -- so it cannot hold on any CUDA build. Eight
`mm_reach` cases fail on aarch64 for a host-side quant-repack reason, PROVEN by
an A/B on the same binary (repack on: 12 of 20 pass; `VT_CPU_QUANT_REPACK=0`: 20
of 20), so it is not a device failure. And `test_serve_deepseek_v4_mm` times out
with no output, which nothing here explains.

`## Now` is updated because it asserted that a CUDA build would still refuse an
image step in the device routers, which this wave falsified.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…DA review findings, and file what only prose recorded

An independent review of the W7-CUDA wave (`4abe547d2`, `e0db328ca`, `c6ded626e`)
confirmed the vision-tower staging fix and returned six non-blocking findings.
This repairs them.

The `dev_attn` comment said the spec records the windowed refusal as unmeasured,
which the same wave had already falsified. It now says the refusal was measured
on `thor:gpu0`, names `### W7-CUDA evidence` as where that measurement lives, and
states that a lease run produced it rather than a committed test.

`MakeTinyVisionTower` built ONE block, so the only cases that gate
`EnsureResident` could not detect a staging miss on any block index above zero --
and the per-block loop is exactly where such a regression lands. The fixture now
carries two blocks, each over its own arena slice, and the device case asserts
every block rather than the first: after staging it destroys the host arena and
runs the SAME staged tower again, so a weight that was never copied changes the
second answer. A new case that needs no CUDA forbids the fixture from degenerating
back to one block; with `depth` returned to 1 it fails `REQUIRE( 1 >= 2 )`.

The stale-cross-reference record claimed four citations of `deepseek_v4.cpp:413`
and the tree carries three: `deepseek_v4_weights.cpp:1068` names the anonymous
message with no line number. The count and the list are corrected, and the three
real citations now read `:504`, where the guard sits.

Two statements are made exact rather than dropped. The probe's W6 arm produces
byte-identical `.f32` artifacts and a stdout that now carries one extra
`provider:` line, which is what "byte-unchanged" was being used to mean. And the
f32 arm refuses a device BEFORE it writes any artifact, so a refused run can no
longer leave one valid-looking file for a later compare to consume.

Three things this wave's green does NOT cover are recorded under `## Owed`
instead of being implied: no committed test drives the windowed `dev_attn`
refusal, so its MEASURED label is history; making `EnsureResident` a no-op leaves
the whole CPU family gate green, so no committed gate protects the staging fix;
and `dsv4v_w6_compare.py` carries no bound and emits no verdict, so the 4.9% /
0.998 judgement is prose arithmetic a reader performs on its output.

Four defects the wave found lived only as spec prose under the umbrella issue.
Each now has a canonical local issue under
`.agents/issues/MODEL-MM-deepseek-v4-deepseek-v4-for-causal-lm/`: the
`vt: MatVec weight size mismatch` blocker a served CUDA image meets, the
`mm_chat` image branch whose expectation can only hold on a build without device
kernels, the aarch64 quant-repack failures proven by an A/B on one binary, and
the `test_serve_deepseek_v4_mm` timeout whose cause is not guessed. A fifth,
under `MODEL-DSV4-EXL3`, records that `test_deepseek_v4_exl3_forward_loop_arm` is
the one ctest entry of 27 in this family with no build target behind it.

Gate: the CPU family gate is 25 of 25 passed with `test_cuda_deepseek_v4` skipped
at 77; 25 of the 27 registered tests had a binary present, the two absent being
that loop-arm entry and `test_serve_deepseek_v4_mm`, which was not built here. No
GPU was used and no lease was taken. Issue #2411.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…le spec anchors, and gate the per-block vision staging on a CPU host

Second-round review of 4ccee2f40 found two defects in this row's records, and
one of them was a claim that a gate was impossible when it was not.

THE ANCHORS. 4ccee2f40 added six comment lines above the windowed `dev_attn`
guard, which moved it from `deepseek_v4.cpp:1319` to `:1325` and made the spec's
two citations of it point at a comment. Re-grepping every `file:line` citation
in the spec found three more that were already stale: `deepseek_v4.cpp:4345` for
the `kDevicePending` refusal, which is at `:4415`; `model_registry.h:299-356`
for `MultiModalForwardInput`, which is at `:397-403`; and `inputs.h:20-92`,
whose four named members span `:20-136`. All five are corrected and each is
re-verified against the tree. `api_server.cpp:373` and
`test_deepseek_v4_exl3_forward.cpp:443,446` were checked and are ACCURATE, so
they are left alone, the second belonging to `MODEL-DSV4-EXL3` in any case. The
`:2958` and `test_deepseek_v4_mm_reach.cpp` citations sit inside verbatim
historical logs and are deliberately NOT renumbered, because renumbering a
pasted log would falsify the evidence it records.

THE GATE, which is the substantive half. A `## Owed` entry recorded that a
per-block staging miss in `DeepSeekV4Vision::Impl::EnsureResident` could be
caught only on a leased device. That is false. `EnsureResident` keys on
`queue.device != weights.device` and stages through the backend the tower was
CONSTRUCTED with, and neither is a CUDA predicate; `deepseek_v4_vision.cpp`
resolves no backend from the registry at all. `vt::Queue` is a plain aggregate
and `vt::Backend` has six pure virtuals, so a hand-built non-CPU queue and a
host-memory fake backend run the staging loop on an ordinary CPU box.

`test_deepseek_v4_vision.cpp` now counts the staged allocations and copies
through the production `DeepSeekV4Vision::Forward`: 15 at depth 1 and 23 at
depth 2, which is `2 + 8*depth + 5`, with the per-block slope asserted
separately. The forward refuses by name straight after staging because the
patches stay on the host, and that message is asserted, so a case that stopped
reaching the staging loop cannot pass quietly.

PROVEN to catch the defect rather than merely to run. Making the loop skip
blocks with index > 0 rebuilt clean (rc 0) and moved the binary md5 from
cc44ef7cd0d230402de70219cf54d9d2 to df9c8239f73e67734116968e303c9b7a, and the
case then read `test cases: 1 | 0 passed | 1 failed` on `CHECK( 15 == 23 )` and
slope `CHECK( 0 == 8 )`. Restoring the file byte-for-byte returned sha256
c614f8174e8982ddd8b4a51e0a8f94005aa7c0b9981257d155ebae205f05dab4 and, after
forcing a recompile, the md5 and the green. The first restore attempt did NOT
rebuild, because `cp -p` preserved the old mtime and ninja skipped the
translation unit; the verdict was re-taken only after the md5 was proven back.

The old entry's true half is kept: a no-op `EnsureResident` leaves every other
CPU case green, because it returns on its first line for a CPU queue with host
weights. The leased-device gate for a REAL device copy is still owed.

A third entry said no committed test sets `VT_V4_DEVICE_ATTN`. Literally true
of ctest, but misleading: `tools/parity/dsv4v_w7_cuda.sh:207` drives that
refusal and greps for it. The entry now names that harness and keeps what
matters, that it is not ctest-registered and needs a manual lease run.

Gate on this aarch64 CPU host, VLLM_CPP_CUDA=OFF: test_deepseek_v4_vision
16 | 16 passed | 0 failed, 7420 assertions, up from 15 and 7412. No GPU was
used and none of this needed one. 1 of the 26 registered deepseek_v4 ctest
entries has a binary in this build, because one target was built by name; the
other 25 would report ctest "Not Run" rather than a pass.

FOLLOWING_AGENTS_PROTOCOL

Refs: ISSUE-LOCAL-01M29PH41YKXY2EF05AVRH96QC
Refs: #2411

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…m's vision accounting and make W6 able to fail

A fresh review of the safetensors port and the W6 parity harness found one
ungated product path and four harness or record defects. All five are repaired
here.

THE EXL3 ARM'S OFFICIAL-VISION ACCOUNTING WAS GATED BY NOTHING. The block in
LoadDeepseekV4Exl3 that requires each expected vision tensor, routes it and
counts it could be deleted whole, and both loader suites stayed fully green --
16 of 16 and 22 of 22 -- while its dense twin reds exactly one case. Nothing
reached it because every committed vision fixture used OfficialVisionOptions,
the released vehicle's DENSE shape, so no case ever handed the loader an exl3
quant_method checkpoint that also carried the vision group. Exl3VisionOptions
supplies that fixture and one case drives the production loader entry through
it, requiring has_exl3_weights so it cannot pass against the dense twin and
checking the vision group is counted exactly once. Deleting the block now reds
it, with the arm's own by-name refusal as the message.

THE PARITY DRIVERS RECORDED TEE'S EXIT STATUS. Under `set -u` with no pipefail,
`cmd | tee f; step name $?` records the status of tee, which succeeds whenever
it can write the file. The worst instance was the image generator, the single
input the whole gate rests on: a failure recorded `image RC=0` and the sweep
went on against whatever img392.rgb a previous run had left in a persistent NAS
directory nothing clears. The three drivers take pipefail, the image step reads
PIPESTATUS and then refuses on failure or on an empty image, and the stale image
is deleted first. Compounding it, every driver appended a status to steps.txt
that nothing ever read back, so all three ended on their DONE banner at rc 0 no
matter what failed; each now reads it back and exits non-zero naming the failing
steps.

THE COMPARATOR ENFORCED NO BOUND. dsv4v_w6_compare.py returned 0 for every shape
that matched, whatever the magnitude, so a run drifting from 3.83% to 12% would
have produced a well-formed report, RC=0 and no signal. It now reads its bounds
from a committed record -- kept out of the comparator so that no wave can derive
a bound from the run it is judging -- and exits 1 when the bound is exceeded and
3 when no rule matches the tag, because an unjudged leg must never read as a
pass.

AND THE BOUND ITSELF CLAIMED MORE THAN IT MEASURES. W6's condition 3 bounds the
shipped bf16 path by 3.34% + 1.57% = 4.9%, but both addends were measured in the
same session as the 3.83% being judged, and condition 2 independently
establishes 1.34% against a 1.57% floor, so the triangle inequality already
forces 4.68% < 4.9%. Condition 3 is close to an identity given the other two,
not an independent test. It is kept, because a gross regression still trips it
and it is now machine-enforced, but the spec says plainly that it may not be
quoted as independent evidence and that conditions 1 and 2 carry the result.
Two downstream sites that quoted 4.9% carry the same qualification.

Two smaller repairs ride along. docs/FEATURES.md still said the real
mmproj-BF16.gguf had never been read, which the row's own spec falsifies at the
same head. And the W6 input-stage comparison rearranges the oracle's buffer into
OUR claimed patch order before writing it, so it asserts the ordering rather
than measuring it; the code and the spec now say so, and the block-level
permutation check remains the gate's actual ordering evidence.

src/ and include/ are byte-identical to the base commit: the only compiled file
this change touches is the test.

Three harness weaknesses a reviewer flagged without mutating -- a guessed vit
re-layout the report does not record, a normalisation the probe hardcodes
instead of taking from the shipped processor, and a best_match that does not
require distinct reference rows -- were neither reproduced nor repaired here.
They are recorded in an open row-owned issue rather than left in prose.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…parity harness unable to pass when it should fail

eb6009f made dsv4v_w6_compare.py judge a leg and gave three drivers pipefail
and a steps readback. A fresh review found seven ways the result could still
report success over a failure. Every one is reproduced on synthetic data with
the exit code before and after, and none was argued from reading.

THE COMPARATOR.

A non-finite judged statistic is now a failure, and a judged profile refuses a
degenerate row. One image row that is zero on BOTH sides made stats() return nan
for that row, the mean over rows nan, and both `nan > 0.049` and `nan < 0.998`
False, so a single row silently disabled both magnitude bounds for all 100.
Measured: 99 rows at 50% off plus one zero row printed VERDICT PASS and exited
0; it now exits 1 naming `degenerate` and `NOT FINITE`, while the same data
without the zero row still fails at 49.9835% and a clean 2% leg still passes.
An all-zero aligner cell is treated as a defect rather than an average to
absorb, because it means the tower produced nothing for that cell.

A missing or non-boolean `judged` key, and a tag mapping to a profile the file
does not define, are ERROR on the new exit 4. `profile.get("judged", False)`
read an incomplete profile as DIAGNOSTIC and exited 0 on 50%-off data. Only an
explicit `judged: false` is a diagnostic leg now.

Stages a profile declares are required to be PRESENT and are judged. Only
image_rows was ever judged, so an absent vit, a vit 100x wrong and a
shape-mismatched input each printed VERDICT PASS; each now exits 1. The spec
cites the vit numbers as evidence that the error does not jump at a stage, and
that sentence rested on nothing executable until this existed. No fresh literal
was invented: the vit bound is built exactly as the existing cells bound is,
from the same recorded measurements at the vit stage, 2.07% + 1.00% = 3.07%,
measured inside it at 2.45%, 1.872% and 1.51%; the f32 arm gets the 1.00% vit
floor against its measured 0.88%. The `cells` STAGE is diagnostic_only with its
reason, being the same aligner rows image_rows already covers in pre-layout
order, and the CLI leg gets its own block-only profile because llama-mtmd-cli
writes only the block, so requiring stages there would fail a correct leg.

THE DRIVERS.

All four read their steps back against an EXPECTED list. `awk '!/ RC=0$/' |
wc -l` counts an ABSENT step as zero failures, so a steps.txt holding only
configure and build, with every comparison never having run, gave BAD=0 and
exit 0; an empty file did too. A line no readback can parse is also refused.

dsv4v_w7_cuda.sh was never repaired and it is the driver that produced the
2.884% / 0.99939 / 1.872% figures the spec quotes. It carried `set -u` with no
pipefail, wrote a bare `cpu_control identical` into its own steps.txt, never
read steps.txt back, and ended on `### W7_CUDA_DONE` with no status derived from
any step. It now takes pipefail, records a real RC= line per step including the
control, checks every copy, reads its steps back, and cannot reach its DONE
banner when a step failed.

ONE JUDGEMENT CALL, stated because it is not mechanical: a device REFUSAL on
sm_110 is recorded under its own step name rather than as a failing step. thor
is outside the vendored FA-2 arch set, so an FA-2-gated path can only refuse
there and the refusal is the result that job collects; making any non-zero step
fail would fail the driver on the box its own header says can only refuse. An
unexplained non-zero exit with no refusal message in the log is still a failure,
so the classification cannot absorb a crash.

The three byte-for-byte controls the file headers themselves call load-bearing
each record a step and fail the job on mismatch; they printed a message and
recorded nothing, so a build that did not reproduce the recorded block still
ended green. Every cp whose result matters clears its destination first and
records a step, so a failed copy can no longer leave the previous run's file for
the comparator to judge out of a persistent NAS directory nothing clears.

EVIDENCE. tests/scripts/test_dsv4v_w6_compare.py, 18 cases, registered in
agent-preflight.sh SUITES and green there. It executes each driver's OWN
readback block, extracted from its EXPECTED line, rather than a copy of the
logic. Mutation-proven rather than asserted: reverting the comparator to
eb6009f changed its md5 from 71c8988c to c2636b3b and turned 6 cases red;
reverting dsv4v_w7_cuda.sh changed its md5 to 1ffe1431 and turned 8 red; both
were restored and cmp reports them byte-for-byte identical with the suite green.

WHAT IS NOT CLOSED. The W7-CUDA job has NOT been re-run under the repaired
driver; that needs a thor:gpu0 lease this wave did not hold, and it is recorded
under the spec's `## Owed`. The 2.884% / 0.99939 / 1.872% figures are NOT
withdrawn: they were produced red-first and that job's CPU control reproduced
W6's block byte for byte. What the Owed entry records is that the harness around
them could not then have reported a failed step.

Also recorded on ISSUE-LOCAL-01M29SAM8CKS2XQM8EPEEN9Z67, which stays OPEN: its
item (1) is fixed, the vit re-layout now reports `oracle_relaid_out` so a reader
can tell whether the numbers came from the file or from the guessed
re-indexing; and its item (3) is now MEASURED rather than plausible, because
fixture rows built from a smooth ramp were near-parallel enough that bf16
rounding noise made best_match's argmax arbitrary and the identity permutation
read 17 of 100 on three datasets including a clean one.

Closes ISSUE-LOCAL-01M29ZEWHZZ8A97B9JKAE171QN

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…dging key, and judge the W7 steps against what the record says they do

The previous repair made `judged` mandatory and left the same shape on every
other judging key: they are read with `profile.get(...)`, and a bound whose
limit is None is not applied, so a judged profile that OMITTED a key was judged
without it. Measured on data whose every image row was 50% off, tag lp0,
profile shipped_bf16: dropping `mean_rel_l2_max` exited 0 VERDICT PASS, dropping
it with `mean_cos_min` exited 0 PASS, and a profile holding `judged` alone
exited 0 PASS. Fixing that per key is what produced this commit, so it is fixed
as a class: the judging keys are declared once in PROFILE_KEYS and STAGE_KEYS,
validate_profile() checks the resolved profile before anything is judged, and a
missing, mistyped or wrongly typed key is ERROR with exit 4. A bound this
project decided not to apply is written as null and names its reason under
`unbounded`, so _check_bound can reach limit=None only through a declaration.
Each of those four cases is now exit 4, and so is dropping any one of the seven
keys individually; the clean leg still exits 0.

Three consequences of that file becoming complete rather than partial. A
diagnostic_only stage is still required to be PRESENT: the flag was tested
before the presence check, so an absent `cells` stage exited 0 PASS against a
bounds file that says its presence is reported, and it is now 1 FAIL. The f32
arm's `input` stage is declared PRESENCE-ONLY with its reason instead of being
bounded by nothing while its prose implied otherwise; no recorded f32-arm
measurement exists at that stage, and removing a key from that rule now exits 4
where it exited 0 PASS. And the comparator narrates what it judged, one JUDGED
line per bound, stage and presence-only rule, because a verdict that does not
say what it covered cannot be read for what it left out.

The W7 driver could not fail on a crash and would have failed on a clean run.
Its refusal classifier matched the bare string `must be`, which ordinary
assertion text carries: `Assertion failed: n must be positive` and a
std::out_of_range `vector index must be less than size` were both filed as the
expected device refusal with RC=0, and both are UNEXPLAINED now that every
alternative is anchored to a message the product owns. In the other direction
its readback required every step to be zero, which this row's own recorded thor
run does not satisfy: ctest_cuda is non-zero for the aarch64 i8mm repack
failures, the test_serve_deepseek_v4_mm timeout and the mm_chat CPU-only
premise, and dev_attn_on is non-zero because the device decode path refusing is
the POINT of that step. Read back over that recorded run the old block exits 1
with `### FAILING STEPS` and no banner, and the new one exits 0. The
classification is not an excuse: an unattributed suite, a non-zero naming no
failing test, a non-zero with no log, and a dev_attn_on that PASSES each fail.

The three W6 drivers printed their `### W6_*_DONE` banner and then exited 1, so
a log grep for the banner read a failed run as finished; the exit comes first
now and the test executes each driver's own readback rather than asserting a
substring is present in the file. best_match returns the margin between the
winner and the runner-up, and every judged profile declares
`best_match_margin_min`, so the identity-permutation condition the spec leans on
for ORDERING evidence carries the separability precondition it never had. That
0.01 is DECLARED, not recorded, and its provenance says so: the degenerate ramp
measures 1.47e-09 and the fixture 0.229, while no real leg has reported a margin
because the statistic did not exist until now.

Mutation-proven rather than asserted: deleting the max_degenerate_rows refusal
from judge() reds test_the_degenerate_row_rule_is_load_bearing on its own, and
deleting the margin check reds the near-parallel case; both files were restored
byte-for-byte, proven by cmp and md5 10d99232afe1aad76d6a548dc81617de, with the
suite green again. The degenerate rule cannot be isolated through a data file at
all, because stats() returns nan for any zero-norm row and the isfinite guard
then carries the case, so that test judges a synthetic report with finite means.
tests/scripts/test_dsv4v_w6_compare.py goes from 18 to 42 cases, rc=0. No C++ is
touched. The W6 and W7-CUDA jobs have NOT been re-run under the repaired
harness, which needs a lease this wave did not hold.

Closes ISSUE-LOCAL-01M2A27NZR23T89WXRGX2RNN5C

ISSUE-LOCAL-01M29SAM8CKS2XQM8EPEEN9Z67 stays OPEN: its item (3) is fixed by the
margin gate here, and its item (2), the probe's hardcoded f32 normalisation, is
untouched.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…mbership, and stop judging the device refusal from a prefiltered excerpt

A fifth fresh review of the W6/W7 parity harness found one silent fail-open and
three false-red routes. Each is fixed here and each is demonstrated on a concrete
dataset with the exit code on both sides, the OLD side being the harness as it
stands at 5c2cf83.

A STAGE DROPPED FROM A PROFILE WAS JUDGED BY NOTHING. PROFILE_KEYS forced the
`stages` KEY to exist and constrained its MEMBERSHIP not at all, so deleting a
whole stage RULE was the one way left to drop a bound while declaring nothing:
every ordinary judging key had to be written or the run was ERROR. Measured on a
leg whose vit file was 100x wrong, dropping the `vit` rule printed `stage 'vit':
NOT REQUIRED by this profile`, VERDICT PASS and exited 0, and `"stages": {}` did
the same, while the identical data with the rule present exited 1 at 9900.3609%.
validate_profile() now requires a judged profile to name every stage in STAGES,
and a stage it does not require is written as null with its reason under
`unbounded` as `stages.<name>`. Dropping a rule is 4 ERROR, an emptied map is 4
ERROR, a bare null is 4 ERROR, and a declared null passes while printing the
reason. shipped_bf16_block_only carried `"stages": {}` and now declares all three
stages null, because llama-mtmd-cli writes only the block.

THE DEVICE REFUSAL WAS CLASSIFIED FROM A PREFILTERED EXCERPT. dev-attn-refusal.txt
is a grep of dev-attn-on.log piped through `head -20`, and it discards two of the
three refusal families the classifier is anchored to. Measured: a log holding
`DeepSeek-V4 vision compute dtype must be bf16` classifies rc=0 as the full log
and rc=1 as the filtered file, which is 0 bytes, and the kDevicePending text does
the same. Driving the shipped readback block with such a log went rc=1 with
`### UNEXPLAINED dev_attn_on` and W7_CUDA_FAILED, and is now rc=0. This would
have fired on the next thor lease. The excerpt is kept for a human reader and is
marked as judged by nothing.

THE REFUSAL ANCHOR COVERED 23 OF 61 MESSAGES WHILE CLAIMING TO COVER ALL. The
comment called `DeepSeek-V4 vision .*must ` the whole Invalid() vocabulary, "27
distinct messages". Counted in deepseek_v4_vision.cpp: 61 distinct
`DeepSeek-V4 vision*` literals, 23 matched and 38 did not, including every
ValidateTensor label at :103-146 and every overflow refusal. A probe leg refused
by one of those was recorded `_unexplained` and failed the job. The anchor is now
the product's own prefix, and across all 366 emitted forms the rejected count
goes from 38 distinct literals to 0. The round-4 fix is NOT undone: `must be` is
not re-admitted, and all five crash strings are still rejected, because no
assertion or standard exception carries this product's message prefix.

THE MARGIN BOUND WAS A DECLARED CONSTANT, AND IT WOULD HAVE RED CORRECT RUNS.
best_match_margin_min was 0.01 and the bounds file said in its own provenance
that the number was declared rather than recorded. Measured with the shipped
best_match() at realistic width (D=1280; the fixture uses D=16), an ordinary
photographic geometry falls under it: a shared global component with 10% and 5%
per-cell detail gives margins 0.00834 and 0.00210, and a 20-cell flat region
gives 0.00875 and 0.00221, while the identity stayed best for 100 of 100 rows in
all four. min_best_margin is a MIN over the cells, so one flat pair decides a run.
The key is replaced by best_match_margin_above_bf16_rounding, and the margin is
judged against the run's own bf16_rounding_scale, derived from the rows it read
as the largest direction change half a bf16 ULP can cause. The five photographic
geometries go 1 FAIL to 0 PASS and the degenerate ramp stays 1 FAIL, 1.47e-09
against 7.51e-06. The scale is built from the bf16 GRID SPACING rather than the
residual `x - bf16(x)`, which is exactly 0 for data already on the grid, as both
sides are by the time they reach a file. The run also now says what KIND of bound
it applied.

The suite goes from 42 to 48 cases at rc=0, and red-first is proven as a suite
rather than asserted: the new test file run against the ORIGINAL 5c2cf83
tools/parity gives 23 failures and 2 errors covering every new guarantee.

Four further review findings are FILED rather than fixed, to keep this wave
scoped: ISSUE-LOCAL-01M2A5T6AP63M5F3D9CT9NBPKX (stale spec line anchors in
shipped comments), ISSUE-LOCAL-01M2A5T6N98WABAAXZH4D52CRR (three malformed bounds
inputs crash instead of reaching exit 4) and ISSUE-LOCAL-01M2A5T6ZPD6QXSV3Q3MSAXVJB
(a stray `NN - <text>` line inside a ctest FAILED block). A fourth,
ISSUE-LOCAL-01M2A5T7B187YJ9GMFSWAGB20P, is closed because this change falsified
it. Closes ISSUE-LOCAL-01M2A5QKV6H5038W2M9BG9K7MG.

RE-RUNNING THE W6 AND W7-CUDA JOBS UNDER THE REPAIRED HARNESS IS STILL OWED: it
needs a thor:gpu0 lease this wave did not hold, so no real leg has yet reported
its own min_best_margin or bf16_rounding_scale.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
Main gained 11 commits while this row was in flight, including the PR-body
trailer gate (a253247, b7fb4e5) and two DeepSeek-V4.1 rows. One file
conflicted: .agents/model-matrix.md, a keyed record. It was resolved by taking
origin/main's version whole and reapplying only this row's edits, never the
three-way result: the architecture row and the MODEL-MM inventory row, plus the
five counters that one ACTIVE row moves (ACTIVE 13->14, Total 381->382, rollup
381->382 and "four"->"five", engaged 57->58). The 356 + 21 = 377 architecture
split and the qwen4exp/glm5next reconciliation paragraph are main's, unchanged.

Verified: the state histogram sums to 382 and matches the printed Total; the
engaged table holds 58 data rows against main's 57; every line main added to
this file survives byte-identical except the Total it superseded; and the diff
against origin/main is exactly the five intended counter lines.

FOLLOWING_AGENTS_PROTOCOL

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

Main gained 15 further commits while this row was being gated: the
QUANT-GGUF-IQ4_NL ROCm work, BACKEND-TENSTORRENT-KEEPQUANT W4d, and a
re-derived q4km Tenstorrent capture golden (#2959). This merge conflicted
nowhere and auto-merged 22 files.

A clean auto-merge of a keyed record is the case this project warns about,
because both sides' edits apply and a removal can land invisibly. So
.agents/model-matrix.md was checked line by line against origin/main rather
than trusted: nothing main added is absent, and the only lines of main's that
this branch replaces are the two counters its one ACTIVE row moves
(ACTIVE 13->14, Total 381->382). The histogram sums to 382 and matches the
printed Total; the engaged table holds 58 data rows and its prose says 58.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…st GEMM refused

A served image on a CUDA build died in `vt: MatVec weight size mismatch at
deepseek_v4.cpp:504`. That guard is `Gemm`'s host-float fallback, and it named
no tensor, no layer and no geometry -- while the sibling keep-quant arm refused
the SAME wrong shape by name. The asymmetry is the defect: which tensor and
which layer were UNMEASURED precisely because the throw carried none of it, so
recovering the geometry needed an instrumented device run under a lease.

`MatVec` and `Gemm` now take the tensor name and the layer index, and the host
arm refuses in the keep-quant arm's own vocabulary:

    deepseek-v4 host GEMM: weight size mismatch: tensor `comp_wgate` layer 3
    want [N=6,K=8] = 48 elements, got 96 elements

The keep-quant arm gets the same two identifiers, so both arms now name the
same thing rather than one naming half of it.

THE LABELS ARE REQUIRED, NOT DEFAULTED. A defaulted label leaves a call site
anonymous, which is the defect this repairs; making them required means a call
site that forgets one is a `-Werror` build failure rather than a silent gap.
`check-tree-compiles` compiled 685 of 685 translation units in scope, which is
what proves no site was missed.

`VT_CHECK` evaluates its message inside the failure branch, so naming the site
costs nothing on the path that succeeds.

RED-FIRST. The new `test_deepseek_v4_forward` case enters through the
production entry `DeepseekV4ForwardHost` -- `Gemm` and `MatVec` are file-local
and no test can construct either -- and asserts content the anonymous string
does not contain: the tensor, the layer, both geometries and the actual element
count. One subcase breaks a not-layer-scoped weight (`lm_head`), the other
breaks `wq_a` on LAYER 2 and asserts the message says layer 2 and not layer 0,
so a hard-coded or first-layer index fails it. Both fail on the pre-repair tree
and pass on this one.

This is a DIAGNOSTIC change and that is the whole of it. It does not repair the
served-image mismatch, which stays owed by
ISSUE-LOCAL-01M29KEXRT2GCS6C53DT2S3SPX and #2411; it is what makes that
mismatch readable. The comments in `deepseek_v4.cpp` and
`deepseek_v4_weights.cpp` that cited the anonymous throw as the reason a
by-name loader refusal exists are corrected: those refusals still earn their
place, because they name WHAT IS MISSING and every mismatched tensor at once,
which a per-GEMM throw reports one at a time.

`tools/parity/dsv4v_matvec_named.sh` is the thor:gpu0 recipe that measures the
red, the green and the served image. It reads its own steps back, asserts the
two test binaries' md5s differ (`-Werror` means a mutant that fails to compile
leaves the OLD binary and a re-run prints SUCCESS), reads doctest verdicts off
`test cases:` rather than `Status:` (a non-matching `-tc` prints `test cases: 0`
AND `SUCCESS!`), and treats a PASSING red as a falsification rather than a pass.

FOLLOWING_AGENTS_PROTOCOL

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

The naming commit landed, and it made this row's spec say things that are no
longer true. A record the tree falsifies is the failure AGENTS.md names, and a
record edit rides in the pull request whose change made it stale, so it rides
here.

What changed, and nothing beyond it:

- The blocker table's row 3 quoted the anonymous message as the LIVE blocker.
  The blocker IS still live -- the wrong shape is still thrown and a served
  image still does not complete -- but its message is now named, so the row says
  "LIVE, and now NAMED" rather than carrying a string the tree no longer emits.
- "THE ASYMMETRY IS THE FINDING" described an asymmetry this branch closed. It
  now records that closing it is a DIAGNOSTIC and not the repair, which is the
  distinction the entry exists to hold.
- "WHAT IS UNMEASURED" still says UNMEASURED, deliberately. The naming commit
  builds the instrument; it is not the measurement. The tensor, the layer and
  the geometry land in this entry only once a lease has actually printed them,
  and until that line exists the entry refuses to name a plausible candidate.
- The stale cross-reference paragraph tracked this throw's LINE NUMBER, which
  went stale twice (`:413`, then `:504`). The comments now quote the MESSAGE and
  the function instead, so the paragraph records that the citations are
  line-free rather than correcting a number a third time.

THE SPEC NAMES THE ISSUE BY DIRECTORY, NOT BY ID, and that is a rule rather
than a preference. A first draft of this edit wrote the stable
`ISSUE-LOCAL-...` ID into the entry, which sits under the spec's `## Owed`, and
`scripts/check-agent-record.py` went from rc=0 to rc=1 on exactly that: a
row-owned issue must not retain an owed reference, because an ID listed as owed
is how a ROWLESS issue is tracked while a row-owned one is tracked by its
directory. The checker caught it before it shipped and the entry now explains
the constraint so the next editor does not repeat it.

The issue file records the same split: one of its two owed halves is done, it
STAYS OPEN for the other, and the geometry remains unmeasured.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…on manifest gate

The pr-size gate refused this pull request because a new 571-line checker
carried no mutation evidence at the path the gate derives for it. The
checker's 139 manifest cases live in a C++ loader test, which that gate
cannot read.

The new suite performs each failure instead of asserting it. It copies the
checker and its three fixtures into a scratch tree, breaks one guarantee,
and requires a named diagnostic and a counted number of disagreements. The
counts are measured, not predicted. A bent vision count reds twice, because
both manifests record it and the checker holds them to each other. One
changed vision shape reds four times, because a rejected shape also leaves
the records that the two digests cover. A config.json edit reds ten times
including config_sha256, so the config and the manifests cannot drift
apart. One case replaces urllib.request.urlopen with a function that fails
the test, which proves the default mode reaches no network.

check-pr-size.py gains a CREATION_MUTATIONS entry because this checker is
absent at the range base. Without it the evidence run raised "absent at
BASE and has no closed creation mutation", which reads an absent checker as
a broken one. test_check_pr_size.py pins the entry and is the red-before
evidence for that change.

Measured: "Ran 28 tests" and OK at head. Under the disabled stub the suite
reports "Ran 28 tests" then "FAILED (failures=19, errors=19)", and no case
survives.

The suite runs in both lanes, so a lane executes it: the SUITES array of
scripts/agent-preflight.sh and the DeepSeek-V4 Vision step of
.github/workflows/ci.yml.

This change answers ISSUE-LOCAL-01M2BXFMFDNZQD41HY629KAGCC, which the row
issue #2411 owns.

FOLLOWING_AGENTS_PROTOCOL

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

A served image on a CUDA build died at the first GEMM of layer 0. The named
refusal this branch added says exactly what happened, and it is the whole
diagnosis:

    vt: deepseek-v4 host GEMM: weight size mismatch: tensor `wq_a` layer 0
    want [N=32,K=32] = 1024 elements, got 0 elements

`got 0` is the tell. This was never a wrong shape -- it is a weight that
reached NEITHER arm. `DeepseekV4Model::ForwardDevice` built its backend with
`gguf=nullptr` and bound only the EXL3 tower, so `ForwardComposeImpl` read
`kq_src = be.gguf != nullptr` as false and handed every layer `Lq = nullptr`.
Every `Gemm` then fell to the HOST-float arm, and on a GGUF load the host MLA
tower is empty BY DESIGN -- `deepseek_v4_weights.cpp` asserts `hl.wq_a.empty()`
in as many words, because the weight is meant to be consumed keep-quant. Layer
0's `wq_a` is simply the first GEMM the composition performs.

The sibling `DeepseekV4Model::Forward` has always dispatched on
`has_gguf_weights`; this entry never did. `git log -S'dev_be.gguf'` finds
nothing, so the device entry has not bound the tower since `dev_be` was
introduced in d7d1ee9.

WHY NO GATE CAUGHT IT. The registry sends the runner's default `gather_logits`
path to `ForwardDevice` unconditionally (deepseek_v4_registry.cpp:246), yet no
test has ever driven that entry with a GGUF tower: every `ForwardDevice` case
under `tests/` belongs to another architecture, and `test_cuda_deepseek_v4.cpp`'s
own `ForwardDevice ASSEMBLES` case sets `has_host_weights` with no GGUF tower at
all. A CPU build cannot reach the defect either, because `kDevicePending`
refuses first. So the combination "device entry + GGUF checkpoint" -- which is
what serving a real checkpoint DOES -- was unexecuted.

THIS BINDS THE TOWER RATHER THAN DELEGATING to `DeepseekV4ForwardGguf`, because
the four device op families are the point of this entry and delegating would
silently drop them. It is not a new combination: `Gemm`'s keep-quant arm already
retags the block views to the queue's device, and binding `gguf` sets
`dsa_dense` (:926), which turns the indexer and compressor arms off on every
layer -- exactly what the GGUF sibling already does, so no DSA arm sees a
half-bound backend.

NOT CLAIMED HERE: that an image now serves. That is a measurement, it needs a
CUDA lease because a CPU build cannot reach this code, and
`tools/parity/dsv4v_device_gguf_bind.sh` is the recipe that takes it. The driver
gates on the `image: ` LINE rather than on the suite's exit code, because
`test_deepseek_v4_mm_chat` also carries the separately-filed CPU-only-premise
assertion and so exits non-zero whatever the image does; it asserts the shipped
tree really carries this fix before believing any result, and it reports a
"stopped somewhere new" outcome as a named next blocker rather than as success.

FOLLOWING_AGENTS_PROTOCOL

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

The spec promised these values would land "only when a lease has actually
printed them". A lease has printed them, so the entry that said UNMEASURED is
now false and is corrected.

MEASURED on thor:gpu0 (sm_110, CUDA 13.0.88, aarch64), rc job
04f39bcb-5636-4043-9cfc-8bdebd4862ec, from the served image:

    vt: deepseek-v4 host GEMM: weight size mismatch: tensor `wq_a` layer 0
    want [N=32,K=32] = 1024 elements, got 0 elements

Tensor `wq_a`, layer 0, N=K=32 -- the fixture's q_lora_rank by hidden_size.
Recovering this needed the naming commit: the anonymous throw carried none of
it, which is exactly why the values sat unmeasured through three blocker moves.

ROOT CAUSE, and `got 0` is what rules the shape hypothesis out. A wrong shape
gives a wrong COUNT; zero means the weight reached NEITHER arm. `ForwardDevice`
built its backend with `gguf=nullptr`, so `ForwardComposeImpl` read
`kq_src = be.gguf != nullptr` as false, handed every layer `Lq = nullptr`, and
every `Gemm` fell to the host arm -- whose MLA tower is empty BY DESIGN on a
GGUF load, because the weight is meant to be consumed keep-quant. Layer 0's
`wq_a` is merely the first GEMM the composition performs.

The record also says why no gate held it: the registry routes the default
`gather_logits` path to that entry unconditionally, while no test has ever
driven it with a GGUF tower, and a CPU build cannot reach it because
`kDevicePending` refuses first.

WHAT THIS COMMIT DOES NOT CLAIM. It does not say an image serves. The repair is
committed, and whether a served image COMPLETES or stops at a further blocker is
a separate measurement under a lease; the entry states that in those words so a
reader cannot mistake a diagnosis for an outcome. The issue stays OPEN for
exactly that reason.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…built KV groups

`vllm serve` aborted on every DeepSeek-V4 checkpoint. `test_serve_deepseek_v4_mm` died with SIGABRT inside `UnitaryKVCacheCoordinator` (`kv_cache_coordinator.cpp:350`, `!enable_caching || hash_block_size == block_size_`), so the server never answered and the suite reported `test cases: 2 | 0 passed | 2 failed`.

Measured on a CPU Debug build at 5f86f9d with a temporary probe in that constructor: `groups=1 enable_caching=1 hash_block_size=256 block_size_=64 scheduler_block_size=256`, and `group[0] block_size=64` over 3 layers. The engine resolves 256 from DeepSeek-V4's `kv_block_size_floor` and builds the KV config at 256, but `MakeDeepseekV4KVCache` publishes the SWA group at a fixed 64, because upstream fixes that size by tensor sharing rather than by configuration (`vllm/v1/attention/backends/mla/sparse_swa.py:76-83`, `self.block_size = 64`). The engine then passed 256 as both the scheduler block size and the hash block size, because `scheduler.cpp` spelled `/*hash_block_size=*/block_size`.

Upstream does two things this tree did not. It re-derives the cache block size from the groups that were actually built, `cache_config.block_size = min(g.kv_cache_spec.block_size for g in kv_cache_groups)` (`vllm/v1/engine/core.py:335-338` @ `e126687a9a`), and it then resolves that into a separate scheduler block size and hash block size and passes both to the Scheduler (`core.py:158-170`, `scheduler.py:76,268-270`), pairing the request block hasher with the hash size (`core.py:232`). Our `resolve_kv_cache_block_sizes` was already ported at `kv_cache_utils.cpp:640` and had no production caller, so both derivations were missing. This change adds `LoadedEngine::ResolveSchedulerBlockSizes` in the position upstream derives them, after the KV config exists and before the Scheduler is built, and threads the pair through `MakeScheduler` into `Scheduler` and `AsyncScheduler`. The new parameter defaults to 0, which is upstream's `None`, so every existing call site keeps its current meaning.

The fix is the derivation, not the assertion. The assertion is upstream's own (`kv_cache_coordinator.py:516` @ the pin) and is untouched.

RED: `test cases: 2 | 0 passed | 2 failed`, binary md5 8f1cf89ce2fd589b83a0019b9eb5b084. GREEN: `test cases: 2 | 2 passed | 0 failed`, binary md5 b7a5767653eb84b5d9cd3a50e8c08bff. Eleven neighbouring suites stay green: test_scheduler 48/48, test_scheduler_lpm 6/6, test_engine_core 6/6, test_kv_cache_coordinator 21/21, test_prefix_match_unit 8/8, test_prefix_cache_stats 12/12, test_loaded_engine_dense 30/30, test_dspark_draft_routing 7/7, test_deepseek_v4_mm_chat 8/8, test_deepseek_v4_mm_reach 20/20, test_deepseek_v4_mm_loader 17/17.

A real Flash checkpoint publishes up to seven groups at block sizes 256, 64, 4 and 8, which no single hash granularity divides, so it still aborts in `HybridKVCacheCoordinator` on the deferred `BlockHashListWithBlockSize` path. That is pre-existing rather than introduced here, because the same configuration aborted before with 256 against 64 instead of the GCD, and it is recorded under `## Owed` in `.agents/specs/deepseek-v4-flash-vision.md`, including that the coordinator must refuse by name instead of asserting.

This is also the engine-side half of the threading KV-PREFIX-MATCH-UNIT W3 deferred by name. `d957d94f6` landed `resolve_kv_cache_block_sizes` and recorded that "the scheduler still hardcodes hash_block_size = block_size, so the default/production path is byte-identical". That sentence is now false, so the records that carried it ride with this change: the `KV-PREFIX-MATCH-UNIT` rows in `.agents/engine-matrix.md` and `.agents/feature-matrix.md`, and the port map, gate and W-breakdown entries in `.agents/specs/prefix-match-unit.md`. Only the FINER-THAN-A-GROUP case is still deferred, because that is the one the `KV-BLOCK-POOL` align path gates (`block_pool.cpp:93,220`), along with the mamba partial-tail stop. The `## Our baseline` section of that spec is left as written: it is a snapshot of the tree at W1 time, and editing it would falsify history rather than repair staleness.

The dense default stays byte-identical. One group whose block size equals the configured one resolves to exactly the pair the scheduler was already given, which is why the 48-case `test_scheduler` suite and the rest are unmoved.

Closes ISSUE-LOCAL-01M2BXYS2R8ZNC68QYGHK4RVQH.

FOLLOWING_AGENTS_PROTOCOL

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

`test_deepseek_v4_mm_chat`'s image branch asserted that the served error names
`W7-device`. That is `kDevicePending`, which `ForwardDevice` emits from
`VT_CHECK(V4DeviceKernelsAvailable(), kDevicePending)` -- a predicate that is
FALSE exactly when the V4 device kernels are ABSENT. On a build carrying them
the refusal cannot fire, so the assertion was not merely unmet, it was
UNSATISFIABLE: the case encoded the absent-kernel build as a premise
(ISSUE-LOCAL-01M29KF3SNQCR8163840XSBK5S).

The branch now selects on `vllm::deepseek_v4::V4DeviceKernelsAvailable()`, the
same symbol `test_cuda_deepseek_v4.cpp` uses, so the two suites agree on what
"this build carries the V4 device kernels" means. Without them the old
expectation stands, because `kDevicePending` really is what that build emits.

WHAT A CUDA BUILD ANSWERS IS MEASURED, NOT ASSUMED. On `thor:gpu0` (sm_110,
CUDA 13.0.88), rc job `1b46515d-8caf-4c49-823e-efc7a1f3e3f4`, the request now
travels the whole registered forward and stops at the MoE router's own named
refusal: an image step routes on the vision bias `exp_probs_b_vl`, and the
device router takes ONE bias for the call with no per-row selector, so it
refuses BY NAME rather than routing image rows on the text bias. That device arm
is W4-era code owed by #2411 W7-CUDA.

IT ASSERTS THAT REFUSAL, not merely "not W7-device". A bare inequality would
accept ANY failure, including a regression that stopped the request earlier --
which this row has already lived through twice, at the vision-residency refusal
and then at the host GEMM's `wq_a` layer 0. When W7-CUDA lands the per-row bias,
the request stops failing and takes the `served` branch instead.

RECORDS. ISSUE-LOCAL-01M29KEXRT2GCS6C53DT2S3SPX is CLOSED with dated evidence:
both halves it owed -- name the refusal, root-cause it -- are done and measured,
and its blocker is gone from the served path. It does NOT close because an image
serves; it closes because the anonymous MatVec refusal it describes is no longer
live, while the new stop belongs to a different owner. The spec's blocker table
records that move: row 3 CLOSED, row 4 (the MoE vision-bias device router) LIVE,
and the count of measured blocker moves goes from three to four.

The driver gains a `TARBALL` selector so a later wave need not overwrite an
earlier wave's staged tree -- overwriting is how a run ends up measuring a tree
nobody can identify afterwards -- and a `mm_chat_suite_green` step that gates on
`0 failed` from doctest's COUNTED line, since this suite read
`8 | 7 passed | 1 failed` before this change. Its header no longer claims the
suite "exits non-zero whatever happens", which this commit falsifies.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…n CUDA

The device-aware image expectation is measured green on a build that carries the
V4 device kernels, so the issue it repairs closes with that evidence rather than
on the strength of the change having been written.

thor:gpu0 (sm_110, CUDA 13.0.88), output dir `bind-20260912-235206`, measuring
the exact tree of 5ffc29b: `test_deepseek_v4_mm_chat` reads
`test cases: 8 | 8 passed | 0 failed | 0 skipped` with `mm_chat_suite_green
RC=0`. The red it replaces was measured on the SAME box and the SAME suite at
`8 | 7 passed | 1 failed`, the one failure being the unsatisfiable `W7-device`
assertion. Verdicts come off doctest's COUNTED line, never `Status:`. Nothing
regressed beside it: `test_deepseek_v4_forward` 7 of 7 and
`test_deepseek_v4_mm_reach` 20 of 20 under `VT_CPU_QUANT_REPACK=0`.

THE DRIVER'S BLOCKER-PRINT WAS REPORTING THE WRONG LINE, and that is repaired
here because a recipe that misnames a cause is worse than one that prints
nothing. `grep 'image: '` also matches the PNG and data-URI residual INFO lines,
which read `chat image: ...` and are NOT the engine's stop: on the run that
prompted this, the bare pattern printed three container-decode residuals while
the actual refusal sat ~70 lines further down. It now greps the case's own
`MESSAGE: image: ` line and `engine-fatal`. No `step` decision changes, so the
gate this run passed is the same gate; only what a reader is handed changes.

The engine's stop is quoted from the log rather than inferred from the suite
passing: `vt: deepseek-v4 MoE: this step carries image rows, which route on the
vision bias exp_probs_b_vl, and the device router takes one bias for the whole
call with no per-row selector`, at deepseek_v4.cpp:437. That is W4-era code with
its own named refusal, owed by #2411 W7-CUDA, and it is the live blocker for a
served image -- not a regression from this branch.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
… fixed Owed entry

Two statements in this spec were falsified by this branch's own measurements,
and a record the tree falsifies is the failure AGENTS.md names.

FIRST, the entry said "WHAT REMAINS OWED IS THE OUTCOME ... never a working
image". The outcome has since been measured twice under the lease
(`bind-20260912-232118`, `bind-20260912-235206`): the `wq_a` refusal is GONE
(`image_past_wq_a` RC=0), the request travels the whole registered forward, and
it STOPS at blocker 4 in this entry's own table -- the MoE vision-bias device
router, W4-era code refusing by name. The entry now says that, and says plainly
that it claims a root cause, a repair and a MOVED blocker, never a served image.
What a served image still needs is the device MoE arm, which belongs to #2411
W7-CUDA rather than to this entry.

SECOND, the `## Owed` bullet describing `test_deepseek_v4_mm_chat`'s CPU-only
premise was still written as an open defect after this branch repaired it. It
now records the repair and the two-way measurement on the same box and suite:
`8 | 7 passed | 1 failed` before, `8 | 8 passed | 0 failed` after. Leaving a
fixed item under Owed is how an intake grows without an exit, which this
protocol calls out by name.

Neither edit claims an image serves, because it does not.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…-Vision row

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…d into the DeepSeek-V4-Flash-Vision row

FOLLOWING_AGENTS_PROTOCOL

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

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

Main gained 50 commits while the three CI repairs were built. This merge
conflicted nowhere and auto-merged 83 files.

A clean auto-merge of a keyed record is the case this project warns about, so
.agents/model-matrix.md was checked against origin/main line by line rather than
trusted: nothing main added is absent, and the only lines of main's that this
branch replaces are the two counters its one ACTIVE row moves (ACTIVE 13->14,
Total 381->382). The histogram sums to 382 and matches the printed Total.

The three repairs this integration carries were each re-verified present after
the merge, by their load-bearing markers rather than by the merge reporting
success: the ForwardDevice gguf binding, resolve_kv_cache_block_sizes reached
from three sites, the named host GEMM refusal, the manifest mutation suite and
its CREATION_MUTATIONS entry.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…prediction the tree falsifies

`.agents/specs/deepseek-v4-flash-vision.md` `## Owed` said a real DeepSeek-V4-Flash checkpoint ABORTS in `HybridKVCacheCoordinator` on its `block_size == hash_block_size` assert, and that the coordinator must therefore learn to refuse by name. That was a prediction written from reading the code, and a probe through the production loader disproves it. This commit replaces the prediction with the measurement and adds the probe that made it.

WHAT WAS MEASURED. A GGUF fixture carrying real `compress_ratios` {4, 128, 0} driven through `LoadedEngine::FromModelDir` on its DEFAULT configuration. The topology half of the prediction holds exactly: `MakeDeepseekV4KVCache` publishes seven groups at block sizes {256, 256, 256, 64, 4, 4, 8}, and `resolve_kv_cache_block_sizes` resolves scheduler 256 (the LCM) and hash 4 (the GCD), which the engine has derived since `c9129fb7b`.

WHAT DOES NOT HOLD. The engine never reaches the coordinator. `ApplyCacheDType` runs while `kv_cfg_` is being initialized, which precedes `scheduler_block_size_` and `scheduler_` in the `LoadedEngine` constructor's initializer list, and `RetypeAttentionSpec` refuses any `MLAAttentionSpec` BY NAME at `src/vllm/v1/kv_cache_interface.cpp:398` — the fp8_ds_mla page formula landed with no store and no read (#2455, owed to KV-DSV4-MULTICACHE W8). The compressed-latent groups are exactly the groups a non-zero `compress_ratio` adds, so the ratios that build the multi-group topology are also what trips that guard. A named refusal is a message rather than an abort, so the serve-time outcome today is already the acceptable one, for a different reason than the record claimed.

WHY THE ASSERT IS UNREACHABLE RATHER THAN MERELY UNTRIGGERED, which is the load-bearing part. DeepSeek-V4 is the only architecture in this tree that publishes groups with DIFFERING block sizes; every other multi-group registry — `glm5_next`, `kimi_linear`, `nemotron_h`, `qwen4_exp`, `qwen3_5_common` — hands the same `block_size` variable to every group it publishes. So no production entry point can reach `kv_cache_coordinator.cpp:386` or `block_pool.cpp:93,220` today.

WHAT IS DELIBERATELY NOT HERE. The `BlockHashListWithBlockSize` port. It would be a converting view that nothing can reach, which is dead code with paperwork attached, and this row has already paid for that shape more than once. #2455 / W8 is what makes the path reachable, and the port is owed behind it. So that W8 does not have to rediscover the shape, `## Owed` now records it with anchors, all @ `e126687a9a`: `kv_cache_utils.py:678-770` (LCM/GCD, already ported and wired), `:2358-2464` (`BlockHashListWithBlockSize` / `resolve_block_hashes`, NOT ported — a coarse block's hash is the last fine hash inside it), `kv_cache_coordinator.py:608-613` (upstream asserts DIVISIBILITY only, so our equality at `kv_cache_coordinator.cpp:386` is a local deferral marker and not upstream's), and the four call sites to mirror.

THE TEST ASSERTS TODAY'S BEHAVIOUR, not a future one. `test_deepseek_v4_multigroup_kv` pins the seven-group topology and the (256, 4) resolution, then requires the load to be REFUSED naming `fp8_ds_mla` and #2455, and NOT naming `hash_block_size`. Its second case is a tripwire: when that refusal stops firing, W8 has landed and the coordinator assert becomes reachable, which is stated in the case itself.

EVIDENCE. RED (the prediction's fixture, asserting a successful load): `test cases: 2 | 1 passed | 1 failed`, binary md5 `9cc3b4fd8338c6483d17bf481b3459df`, the failure being the verbatim fp8_ds_mla refusal quoted above. GREEN (asserting the refusal): `test cases: 2 | 2 passed | 0 failed`, `assertions: 16 | 16 passed`, binary md5 `479f344f97ba81ff2d3557043f96c2d0` — a CHANGED binary, so the verdict is not a stale artifact.

THE SHARED FIXTURE EDIT IS BYTE-NEUTRAL. `BuildDeepseek4Gguf` gained a defaulted `compress_ratios` parameter and the per-layer DSA compressor/indexer tensors that real ratios require; empty (the default) writes the all-zero array every existing caller relied on. Its five consumers are unmoved: `test_serve_deepseek_v4_mm` 2/2, `test_deepseek_v4_mm_reach` 20/20, `test_deepseek_v4_mm_loader` 17/17, `test_deepseek_v4_mm_chat` 8/8, `test_deepseek_v4_gguf_load` 19/19.

No file under `src/` or `include/` is touched: the upstream assertion is exactly where it was.

Records: ISSUE-LOCAL-01M2C0JBH6CNPYGQXNF0A1WG0H is corrected to state the measurement and stays OPEN, because a multi-group DeepSeek-V4 still cannot prefix-cache.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…evice MoE router

`DispRoute` refuses any step carrying image rows on the two DEVICE router arms,
because `MoeDeviceKernels::route` and `route_ip` each take ONE bias pointer for
the whole call and have no per-row selector. That refusal is correct and
deliberate: routing an image row on the text bias would be fluent and wrong. What
is missing is the selector, and this section scopes it before any code is
written.

It is the LAST KNOWN BLOCKER to serving an image on CUDA, and that is a measured
position rather than an expectation. `ForwardDevice` builds
`V4Backend dev_be{/*device=*/true, ...}` and the registry sends the runner's
default gather-logits path there for every request on this architecture, so every
served request takes the `be.device` arm. With the vision-residency and
keep-quant-binding repairs above it in, a served image travels the whole
registered forward and stops exactly here.

The section records the ONE predicate both the refusal and the route must share,
transcribed from the host arm rather than restated, because a refusal whose
predicate differs from its routing condition is how a silent wrong answer
happens and this row has already met that defect class. It cites vLLM as the
primary anchor for the arithmetic of each arm and names the point where vLLM
stops: vLLM takes one `e_score_correction_bias` and implements no vision bias, so
the per-row selection between two biases is the secondary oracles' ground. It
also states plainly that a CPU build CANNOT gate this work, since `ForwardDevice`
refuses first at `kDevicePending`, so no CPU green is device coverage.

The design section sits OUTSIDE `## Owed` deliberately. That heading spans to
`## Now`, and `check-agent-record.py` reds a row-owned issue ID mentioned in a
spec's `## Owed`, so the new local issue is named here and nowhere in that range.

ISSUE-LOCAL-01M2C26CSZWB7WVRS5H7YPW4S8

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…-Flash-Vision row

The issue and the spec Owed entry both asserted that a real Flash checkpoint
aborts in HybridKVCacheCoordinator. Measurement disproved it: the checkpoint
never reaches the coordinator, because ApplyCacheDType runs during kv_cfg_ init
and RetypeAttentionSpec refuses any MLAAttentionSpec by name at
kv_cache_interface.cpp:398. The coordinator equality assert is latent, not live,
and what a real checkpoint needs next is #2455 / KV-DSV4-MULTICACHE W8.

FOLLOWING_AGENTS_PROTOCOL

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

Three further commits landed on main while this row was gating. This merge
conflicted nowhere and auto-merged 8 files.

.agents/model-matrix.md auto-merged, which is the case this project warns about,
so it was checked against origin/main line by line rather than trusted: nothing
main added is absent, and the only lines of main's this branch replaces are the
two counters its one ACTIVE row moves (ACTIVE 13->14, Total 381->382). The
histogram sums to 382 and matches the printed Total.

The repairs this branch carries were re-verified present after the merge by
their load-bearing markers rather than by the merge reporting success: the
ForwardDevice gguf binding, resolve_kv_cache_block_sizes reached from three
sites, the named host GEMM refusal, and the manifest mutation suite.

FOLLOWING_AGENTS_PROTOCOL

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

`DispRoute` refused every step carrying image rows on the two DEVICE router arms,
because `MoeDeviceKernels::route` and `route_ip` each took ONE bias pointer for
the whole call. That refusal was correct: routing an image row on the text bias
would have been fluent and wrong. What was missing was the selector, and this
adds it rather than widening the refusal.

THIS IS THE LAST KNOWN BLOCKER TO SERVING AN IMAGE ON CUDA, and that is measured
rather than expected. `DeepseekV4Model::ForwardDevice` builds
`V4Backend dev_be{/*device=*/true, ...}` and the registry sends the runner's
default gather-logits path there for every request on this architecture, so every
served request takes the `be.device` arm. With the vision-residency and
keep-quant-binding repairs above it in, a served image travels the whole
registered forward and stops exactly here.

ONE PREDICATE, TRANSCRIBED RATHER THAN RE-DERIVED. A refusal whose predicate
differs from its routing condition is how a silent wrong answer happens, and this
row has already met that defect class. Both CUDA kernels now compute, per row,
`media = has_vbias && is_media && is_media[t] != 0`, then take
`row_bias = media ? vision_bias : bias`, `row_has_bias = media ? 1 : has_bias`
and `row_is_hash = is_hash && !media`. That is `SqrtSoftplusRouteTopk`'s rule
verbatim: an image row takes `exp_probs_b_vl` and the learned top-k, and on a
HASH layer that REPLACES the tid2eid route for that row alone while a text row in
the same step still hashes. Weights are still gathered from the UNBIASED scores.

BOTH kernels carry it, not just the default one. `RouteWarpKernel` is shipped-on
and `RouteKernel` is its A/B baseline, and the two must stay bit-identical on a
media step as well as a text one. All 32 lanes of a warp serve one row, so they
agree on `media` with no divergence.

THE REFUSAL WAS ANSWERED, NOT DELETED. The genuinely-unavailable case is a
DIFFERENT predicate and keeps its own refusal one level up in `MoeBlock`: a step
carrying image rows whose LAYER has no `exp_probs_b_vl` is refused by name, with
the width check beside it. That one is host-side and arm-independent, so a text
checkpoint asked to route an image is still refused on every arm.

A TEXT STEP IS BYTE-IDENTICAL. An empty mask or an absent vision bias means no
row is media -- the host arm's own `any_media` rule -- and `RouteLaunch` pads the
two new buffers exactly as it already pads `bias`, `in_tokens` and `hashtab`, so
no kernel argument is ever a dangling pointer.

The two resident single-token decode arms pass the no-media values. They refuse
an out-of-vocabulary identifier before the router, so no image row can reach
them; widening that refusal is separate work and is NOT done here. The decode
graph's guard keeps its `embed`-bound reason and loses the one the tree has now
falsified.

WHAT IS PROVEN, AND ON WHAT. On x86-64 CPU: build green under -Werror, all three
binaries' md5 CHANGED, `test_deepseek_v4_moe` 14/14 (731 assertions),
`test_deepseek_v4_mm_reach` 20/20 (178), `test_deepseek_v4_mm_chat` 8/8 (650).
That is NOT device coverage and is not offered as any: a CPU build cannot reach
this code, because `ForwardDevice` refuses first at
`VT_CHECK(V4DeviceKernelsAvailable(), kDevicePending)` -- observed in the CPU
`mm_chat` run, which stops exactly there.

THE DEVICE ARM IS MEASURED, on `thor:gpu0` (NVIDIA Thor, compute_cap 11.0,
driver 595.78, CUDA 13.0.88, aarch64), rc job
`bc9c74ce-e9f6-47e1-bde0-bb0939ad0ed8`. There is no local nvcc, so that run is
the FIRST compile of `cuda_deepseek_v4.cu` with the selector: 41 `.cu.o`, the V4
objects `sm_110`, linked against `libcudart.so.13` and `libcublasLt.so.13`,
binaries built by that run (`test_cuda_deepseek_v4` md5
`12b23adb4b6b4741bad7c19626f70211`, `test_deepseek_v4_mm_chat` md5
`625faa37ecdfa3716c38a3ba8ddec628`).

  - the focused case `W7-CUDA per-row vision bias`: `test cases: 1 | 1 passed`,
    102 assertions. The matched count is read explicitly, because a non-matching
    `-tc` prints `test cases: 0` AND `Status: SUCCESS!`;
  - the whole CUDA V4 suite: `31 | 31 passed | 0 failed | 0 skipped`, 90193
    assertions -- zero skipped, so no case silently sat out;
  - `test_deepseek_v4_mm_chat`: 8/8, 650 assertions, and the served image reads
    `image: served`, with `VT_CPU_QUANT_REPACK` ON and OFF alike, so the answer
    is not an artefact of the aarch64 i8mm repack path.

AN IMAGE SERVES ON CUDA. The limits are stated rather than left to be assumed:
the fixture is the SYNTHETIC lang GGUF plus `deepseek4v` mmproj built by the
suite and driven through the production entry point, NOT the released
checkpoint, so no real-weight image answer exists. AND THE REACHABILITY MUTATION
WAS NOT RUN: deleting the `DispRoute` pass-through and confirming the focused
gate reds is what would prove the green rests on the production call site rather
than on a class that merely works. The spec records both under `## Owed`.

`test_deepseek_v4_mm_chat`'s device branch is the gate and was RED-first: it
asserts the served-image error is EMPTY, not merely "not the old refusal", so a
regression that stopped the request earlier cannot pass it.

The records this result falsified are repaired in the same commit: the two
`## Owed` device-router bullets, the blocker table (row 4 CLOSED, and a row 5
that says "no further blocker is KNOWN on the synthetic fixture", never "none
exists"), `## Now`, and the FEATURES.md sentence that read "NO IMAGE HAS BEEN
SERVED END TO END".

ISSUE-LOCAL-01M2C26CSZWB7WVRS5H7YPW4S8

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…sion row

An image now serves on CUDA. On thor:gpu0 (NVIDIA Thor, compute_cap 11.0,
driver 595.78, CUDA 13.0.88, aarch64), rc job bc9c74ce, a two-image chat request
travels create_chat_completion -> the MM seam -> AsyncLLM -> Scheduler ->
GPUModelRunner -> ModelRegistry::Forward and returns `image: served`, with
VT_CPU_QUANT_REPACK on and off alike, so it is not the aarch64 repack path.
Focused case 1/1 with 102 assertions; the CUDA V4 suite 31/31 with 90193
assertions and zero skips; mm_chat 8/8.

The W4 refusal was ANSWERED, not deleted. Both device routers now carry the
per-row selector, so DispRoute holds no VT_CHECK at all, while the genuinely
unavailable case keeps its own by-name refusal one level up in MoeBlock: a step
with image rows on a layer lacking `exp_probs_b_vl` is refused there, host-side
and arm-independent, on every arm.

Two limits are recorded in the spec's `## Owed` rather than papered over: the
fixture is the synthetic lang GGUF plus deepseek4v mmproj, NOT the released
checkpoint, so no real-weight image answer exists; and the reachability mutation
has not been run, so nothing yet proves this green rests on the production call
site rather than on a class that merely works. A third is owed too: the vLLM
router was read at 5559679229, the prior parity pin, not the active e126687a9a.

FOLLOWING_AGENTS_PROTOCOL

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

One commit landed on main during the previous integration, a QWEN4-EXP MoE
adapter residency fix unrelated to this row. It auto-merged across 5 files with
no conflict.

.agents/model-matrix.md auto-merged again, so it was checked rather than
trusted: both of this row's entries are present exactly once, the state
histogram sums to 382 and matches the printed Total, and the five repairs this
branch carries were re-verified by their load-bearing markers after the merge --
the ForwardDevice gguf binding, resolve_kv_cache_block_sizes reached from three
sites, the named host GEMM refusal, the per-row vision bias selector, and the
manifest mutation suite.

FOLLOWING_AGENTS_PROTOCOL

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

One QWEN4-EXP spec commit landed on main during the gate run; it auto-merged
across 2 record files with no conflict and touches nothing this row builds.

.agents/model-matrix.md was checked rather than trusted, as on every previous
integration: both of this row's entries appear exactly once, the state histogram
sums to 382 and matches the printed Total, and the five repairs were re-verified
present by their load-bearing markers after the merge.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
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