feat(MODEL-MM-QWEN4-EXP): W6a — IQ4_NL, Q5_0 and a dequantizing gather, so the only artifact that fits can be opened - #2019
Open
localai-bot wants to merge 11 commits into
Open
feat(MODEL-MM-QWEN4-EXP): W6a — IQ4_NL, Q5_0 and a dequantizing gather, so the only artifact that fits can be opened#2019localai-bot wants to merge 11 commits into
localai-bot wants to merge 11 commits into
Conversation
…e qwen4exp config builder (#1989) Qwen3.8-Flash-Next has exactly one published artifact that fits any device this project owns. bf16 is ~360 GB, the official FP8 ~180 GB and NVFP4 ~128 GB against ~119.6 GiB usable on GB10; `unsloth/Qwen3.8-Flash-Next-GGUF UD-IQ1_S` is 67.56 GiB in three shards. So the GGUF arm is not a follow-up to a safetensors path, it is the path, and two things in this tree stopped it. The first was one missing `case`. Our reader knew ggml ids 0, 1, 2, 8, 10-14, 16, 18, 19, 22-28, 30, 39, 40, 41 and 66 and had no entry for 20, so `GgufFile::OpenOne` died at header parse before reaching a single tensor of shard 2. IQ4_NL is not a recipe preference there: `moe_intermediate_size` is 640 and the n-gram table's row is 160, neither a multiple of 256, so no K-quant can encode them and llama.cpp's own `tensor_type_fallback` drops `IQ4_XS -> IQ4_NL`. Q5_0 lands here in the same change because the same table maps `Q4_K -> Q5_0` for the same reason, and adding one type is the work of adding two. Both were previously asserted from memory; both are now READ at the pin, `src/llama-quant.cpp:374-405 @ b10451`, which discharges an UNVERIFIED item the spec was carrying. That reading also turned up `Q5_K -> Q5_1`, an encoding we still lack, recorded under `## Owed` rather than quietly added. The second was that a gather table could not stay quantized at all. `KeepQuantKDim` returned -1 for `kEmbeddingTable` and `qwen3_5_gguf_weights.cpp` asserted it by name, so a quantized 51.2 G-parameter n-gram table expanded to 102.4 GB of anonymous memory against 28.8 GB of blocks: the end of the box before the first forward. The -1 was CORRECT until now, and that ordering is why the policy change comes after the op rather than with it — without a dequantizing gather a kept table is bytes nothing can read. `vt::Embedding` now takes a block-quantized table and decodes ONE ROW per gathered id, which is what llama.cpp does in `ggml_compute_forward_get_rows_q`. The table's residency then follows the ordinary policy behind a gather-specific admission rule, because a gather asks a different question from a GEMM: it needs a row DECODER, not a `vec_dot` and an activation quantizer. Both decoders are gated BIT-EXACTLY against the pinned llama.cpp decoding real bytes of the shipped tensor, read by range request at absolute offset 364622656 of shard 2 — ten IQ4_NL blocks, two whole gather rows. A synthetic block cannot catch a misreading of the file, and a hand-transcribed expectation cannot catch a misread codebook, so the oracle was built from a clean `git archive b10451` rather than from a working checkout and asked for its own answer. The `qwen4exp` config builder gets its own translation unit and its own dispatch row rather than a fourth key on `HfConfigFromGguf`, which asserts its own three architectures by name; routing a fourth family there refuses as "qwen3_5 gguf:" and blames a model the user never mentioned, which is the #809 defect. Landed unreached, and named because it has to be: the builder IS reached through `kGgufArchArms`, but `ModelRegistry` does not resolve `Qwen4ExpForConditionalGeneration`, so a user passing the shipped file today gets a correct config and then a registry refusal. The model wiring is owed to #1978 and listed under `## Owed`. The CUDA gather arm is owed with it, and it is the expensive half: `EmbeddingKernelCuda` still refuses a block table, and a device-resident quantized table gathered on device is precisely the shape llama.cpp's #27742 does not have. No forward, no token claim and no speed claim from this wave. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
…ble the shared policy started keeping W6a made `GgufTensorRole::kEmbeddingTable` keep-quant eligible. That role has THREE consumers and only one of them was given the residency. `deepseek4` and `laguna` both hold `VT_CHECK(pol.Route(t, role) == kExpandBf16, "... must not keep quant blocks")` over `token_embd.weight`, and after W6a that route returns `kKeepQuant` on a CPU platform for every block-quantized table with whole-block rows — which is every published checkpoint of either model. Both loads threw at weight-load time, on `main`'s default policy, with no test able to see it. Neither model can read a kept table and neither can be taught to here. Both index the vocab matrix as a flat host `float` array (`hw.embed[tok * H + h]`, `ReadF32(weights.embed)`), and a tied file hands that same f32 image to the final projection. So each narrows the policy for that one tensor through the new `NoKeepQuant`, which STATES the narrowing the way `NoNvfp4` already does rather than asserting that nobody will elect a residency the loader cannot serve. It disables keep-quant only: `keep_f16` is left alone so the assertion downstream of it is still live and not a tautology. The tied DeepSeek-V4 head is routed under `kMatmulWeight`, the role it actually has, so `token_embd` is routed twice on a tied file exactly as `LoadEmbedAndHead` does it — a gather and a GEMM operand are two questions and they get two answers, both f32 here. Decoding the blocks per gathered row in these two forwards is model work, not policy work, and is recorded under `## Owed` against #1978. The reason nothing caught this is the fixtures, so they are the fix as much as the code is. The one deepseek4 GGUF in the tree stores `token_embd` as F32, the single encoding that still routes `expand_bf16`, and `LoadLagunaFromGgufShards` had no test whatsoever — this commit gives it its first. Both models now load a BLOCK-QUANTIZED table, and both threw before the narrowing: deepseek-v4 gguf: a embedding_table tensor must not keep quant blocks: token_embd.weight laguna gguf: a embedding_table tensor must not keep quant blocks: token_embd.weight Each case asserts more than "does not throw": for these two models the gather table's CONTENT may not depend on the residency policy at all, so the keep-quant load's table is compared byte-for-byte against the expand-everything load's, the table is shown non-zero, the GEMM tower is shown still keeping its blocks (the narrowing is role-scoped, not "keep-quant off"), and DeepSeek-V4 runs a forward on the result. Making `NoKeepQuant` a no-op reds both. `docs/FEATURES.md` said this was a new capability. It is also a residency DEFAULT change on already-shipped `qwen35`/`qwen35moe`/`qwen3next` GGUFs, whose quantized `token_embd` now stays compressed on CPU where it used to expand; the row now says so, says that tokens do not move because every GGUF-path gather writes bf16, and names the two families that are refused rather than served. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
…s, and the bridge the forward actually uses Four of the review's findings are the same defect wearing different clothes: a guarantee was claimed and nothing measured it. Each is repaired by making the mutation that used to survive go red. `VecDotQ5_0Q8_0` and `VecDotIQ4_NLQ8_0` shipped with no numerical gate at all. `tests/vt/test_ops_quant_dot.cpp` is the tree's dedicated one and its `kWeightCases` carried twelve encodings without these two, so swapping IQ4_NL's nibble halves (R12) and moving Q5_0's upper-half `qh` shift (R13) each survived all eight suites. Both dot against Q8_0, which already has a `from_float`, so the existing f64 dequantize-then-dot harness reaches them by adding two rows and nothing else — MXFP4, the other 32-element/Q8_0-activation type, was already the precedent. R12 and R13 are now RED. These kernels are live: all 48 `ffn_down_exps` of the shipped UD-IQ1_S are IQ4_NL. The reachability case for the dequantizing gather stopped one call short of the forward. It hand-built a `vt::Tensor` from `w.embed_tokens.bytes` instead of going through `ResidentWeight`, which is what the forward calls and which is where a block table's bytes are aliased without ever computing a per-element size. That helper had internal linkage, so no test could reach it; it now has a name, `Qwen3_5EmbeddingTable`, and all five embed call sites in `qwen3_5.cpp` go through it. The body is one line on purpose — this is a NAME for the bridge the forward already used, not a new path. The case now asserts the operand's dtype, shape and POINTER IDENTITY with the loader's bytes; offsetting the alias by one byte reds it. Both refusal cases in `test_ops_embedding_quant.cpp` named guards they did not measure. "REFUSES a block table whose row is not whole blocks" survived neutering the op's own precondition, because `vt::RowSizeBytes` rejects the same ragged K one level down and a bare `CHECK_THROWS` cannot tell two throws apart; it now asserts the message. "REFUSES a block table with no decoder" used Q8_K, which HAS a decoder — as does every other `vt::DType` block encoding, so that guard is unreachable by construction and no test can execute it. Saying so is worth more than a case that claims to pin it, so it is renamed to the bounds check it always measured, asserts that message, and shows an in-range id on the same table decoding fine. Two smaller things in the same spirit. The "routing table is TOTAL" case omitted ggml ids 6 and 20 — the two encodings W6a added — so its totality was over yesterday's surface; adding them moves the kept counts from 16+9 to 20+11 and those terms are still written out named rather than as one number. And the memory-only claim about the residency default flip holds only while every GGUF-path gather writes bf16: on an f32 output the two residencies genuinely disagree, so a case now pins that, bit-exactly against the same oracle, showing the kept-blocks answer is the exact one and the bf16 round-trip is the lossy one for most of the 320 golden values. Records. The wave's mutation table is COMMITTED, in the row spec, because the fresh review could not re-run any of the nine mutations the wave claimed — none was written down anywhere — and had to design fourteen of its own. The anchor count is corrected there too: W6a repaired THREE anchors, not nine, all three stale before it, and the ratchet lowering 37 -> 34 is exactly those three. One of them landed with a label disagreeing with its own href (`cpu_ops.cpp:1843` pointing at `#L2923`); the label now reads what the link means. A `DType` enum of 20 values became 22 and one comment still said 20. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
… the row transition and three corrections An `ACTIVE` row obliges a `CLAIM-*` owner, so this adds `.agents/claims/CLAIM-MODEL-MM-QWEN4-EXP.md` alongside the state change. Both record gates refused the change until it existed, which is the checker doing its job rather than an inconvenience. The transition was deliberately NOT made by any earlier wave. Four branches were live on `model-matrix.md` at once, and that file's row is a single shared cell — editing it from four places is the lock AGENTS.md "Records" names, so it was carried as a merge-time obligation for whichever wave landed first. W6a is that wave. Three claims in the row were true when written and are now false, and each is corrected in place rather than quietly dropped, because the row's whole `## Work breakdown` was reasoned from them: The artifact. `unsloth/Qwen3.8-Flash-Next-GGUF` was "a README with ZERO weight files"; it is now UD-IQ1_S at 67.56 GiB in three shards, `general.architecture = qwen4exp`, 1224 tensors, and it FITS GB10 with roughly 52 GiB of headroom where every safetensors artifact does not fit at all. The load blockers. The row said the chosen arm "does not load today" because `KeepQuantKDim` returns -1 for `kEmbeddingTable` and our reader cannot open ggml types 3/6/7/20. W6a cleared both halves for the types that matter: IQ4_NL (20) and Q5_0 (6) are in the reader, geometry read from the pinned llama.cpp oracle at `b10451` and decoders gated bit-exactly against its compiled output on real file bytes, so the three shards OPEN; and the gather table is keep-quant eligible behind a dequantizing gather, so the n-gram table no longer expands to 102.4 GB. Types 3 and 7 remain unsupported and are recorded as owed. The state. `READY` / "SPEC ONLY, NO PRODUCT CODE" was accurate at the merge base and false the moment product code landed. What this change does NOT claim. Nothing is REACHABLE: no `ModelRegistry` entry resolves `Qwen4ExpForConditionalGeneration`, so every landed slice is unreached by design and named under the spec's `## Owed`. There is no token number and no speed number, and the speed axis does not open until G4. W5 owns the wiring and is not yet filed. Tracked by #1989, under #1978. Gates: `check-agent-record` ok, `check-model-checklist` ok. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
Re-gated on the tree that will actually land. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
… and only the counters survive that by luck W1 and W6a each moved `MODEL-MM-qwen4-exp-...` from `READY` to `ACTIVE` on their own branch. Both were right to: AGENTS.md "Records" requires the matrix row to move with the lifecycle state, and each wave is the first product code from its own vantage. The collision is the problem, and a clean three-way merge will not surface it. The counts are safe by accident. Both branches make the IDENTICAL edit, ACTIVE 10 -> 11 and READY 4 -> 3, so a merge with base 10/4 and both sides 11/3 resolves to 11/3. Had the two edits DIFFERED by one, git would have merged them cleanly and applied BOTH, yielding 12 where 11 is correct — the failure AGENTS.md names under "Never store a measurement of one file inside another file". The note therefore says to verify those two numbers by COUNTING ROWS at every merge rather than trusting the merge. The claim owner is not safe at all. W1 wrote `CLAIM-MODEL-MM-QWEN4-EXP-W1` with its own claim file; W6a wrote `CLAIM-MODEL-MM-QWEN4-EXP` with its own. Two owners for one cell and two claim files for one row, which `check-agent-record.py` binds one-to-one. The row-level name wins, because the claim covers the campaign and not a wave, and whichever merges second drops its transition and its claim file. Recorded rather than silently reconciled because W2, W3 and W4 will each arrive at the same fork, and the instruction they need — if the row is already ACTIVE on main, drop the edit — is not derivable from their own branch. Tracked by #1989, under #1978. Gates: `check-agent-record` ok, `check-model-checklist` ok. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
… update, two anchors and a row-ID mention Moving `MODEL-MM-qwen4-exp-qwen4-exp-for-conditional-generation` from `READY` to `ACTIVE` reds three gates that the state change itself owes. All three are the record layer working, not incidental breakage. `test_qwen4_exp_row_is_inside_the_model_ratchet` pinned the row at `READY`. That assertion was written when the row was spec-only and is falsified by the transition it was written alongside. It is updated to `ACTIVE` and kept PINNED rather than loosened, with the reason at the site: it is the assertion that fires if a later wave moves the row without moving the rollup counts with it, which is exactly the shared counter hazard this branch records under the spec's `## Owed`. A reader who finds it red is told to count the matrix rows, not to edit the expectation to match. Editing that file shifted the two anchors `ENG-RECORD-ANCHOR-RATCHET` keeps INTO it, 1494 -> 1502 and 1562 -> 1570. Repaired to the true lines rather than banked as rot; the ratchet stays at its committed baseline. This is the third time in this row that touching `tests/scripts/test_agent_record.py` has staled that row's own anchors, which is precisely what that row exists to measure. `audit-live-rows` read the row as ABANDONED — "no branch, no commit on main mentioning the row ID" — because every branch and commit in this campaign uses the short label `MODEL-MM-QWEN4-EXP` while the matrix row ID is the long deterministic form. The auditor matches the row ID, so the two never met. Naming it here makes the row IN-FLIGHT, which is what it is: implemented on a branch, not yet on main. Tracked by #1989, under #1978. Gates: `tests/scripts/test_agent_record.py` 123 passed, `check-agent-record` ok, `check-model-checklist` ok, record-anchor rot back at its committed baseline. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
The branch was behind origin/main, which makes `check-commit-style.py --range origin/main..HEAD` refuse outright because the range base is no longer an ancestor of the range head. Merging restores that precondition so the landing gate can run. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…PPOSITE of the row's settled QSA mapping
Two spec defects, one introduced by this branch and one found while checking it.
This branch inserted its `### Merge sequencing` section at the wrong offset, in
the middle of a sentence. Line 26 ended `each named under `## Owed` and the rest
of that sentence — the out-of-scope list naming MTP depth > 1, the 1M-token RoPE
extension and any throughput claim — was left stranded 30 lines below as a
fragment opening with a bare backtick. The sentence is restored and the section
now sits between whole paragraphs.
The second is pre-existing on `main` and is the reason this is not a typo fix.
`## Why this needs a spec before code` item 2 read "QSA's twin in vLLM is
MiniMax-M3, not DeepSeek-V4 ... Building it on the DSA/MLA path is the wrong
port". That is the row's FIRST reading, which the campaign refuted, and the same
file already says the opposite twice — the Port map ("DeepSeek-V4 C4 indexer
lane ... NOT MiniMax-M3") and the Design heading — as do the matrix row and
#1978.
It is load-bearing rather than cosmetic. Item 2 lives in the section written so
that a fresh implementer does NOT re-derive the decision, W4 is the QSA wave and
W5 wires it, and a reader going top-down meets the stale instruction before the
corrected Design section. The two readings do not fail equally: M3 welds
`SPARSE_BLOCK_SIZE = 128` to the KV page size, so ratio 4 forces a page size of
4 and breaks `tl.dot`, whose tile needs >= 16. Item 2 now matches the Design
section, keeps the warning shape because the DSA/MLA reflex is still the trap,
and records that M3's real contribution is a wiring precedent rather than an
algorithm.
Filed as #2049 and fixed in the same flow, per "a bug that you find during other
work still needs an issue" — filing does not defer the fix.
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
origin/main advanced to f730eb1 while the previous operator gate was running, which put this branch behind its own base mid-run. The trailer gates are diff-scoped against origin/main, so both SKIPPED and the run still exited zero — "a skipped gate reported nothing about this tree". Merging restores the ancestry those gates need. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
origin/main advanced again while this branch waited on a saturated CI queue. The trailer gates are diff-scoped against origin/main and SKIP while still exiting zero when the base is not an ancestor of HEAD, so merging is what makes the next operator gate report on the tree that will actually land. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is the wave that makes
Qwen/Qwen3.8-Flash-Nextloadable at all. Before it, nopublished artifact of that model could be opened by this tree; after it,
unsloth/Qwen3.8-Flash-Next-GGUFUD-IQ1_S — 67.56 GiB in three shards,general.architecture = qwen4exp, 1224 tensors — opens, and it is the only artifactthat fits any host this project owns. Every safetensors form is out of reach: BF16
~360 GB, official FP8 ~180 GB, NVFP4 ~128 GB, against ~119.6 GiB usable on GB10.
Two encodings were the whole blocker. The reader knew ggml type ids
0,1,2,8,10..14,16,18,19,22..28,30,39,40,41,66and 20 was the only one missing,which is what that file uses for
ffn_down_expsand for the 51.2B-parameter n-gramtable. Q5_0 (6) is added in the same change because
tensor_type_fallbackmapsQ4_K -> Q5_0andIQ4_XS -> IQ4_NL, so which one a file carries depends on therecipe and a
-Q4_K_Mbuild of this model lands on the other one. Both geometrieswere read from the pinned
llama-cpporacle atb10451viagit archive, becausethe local checkout is dirty; the decoders are gated bit-exactly against that oracle's
own compiled output fed real bytes from the artifact — 180 B at absolute offset
364622656 of shard 2, ten IQ4_NL blocks, two whole gather rows.
The second blocker was policy, not encoding. A gather table could not stay quantized:
KeepQuantKDimreturned-1forkEmbeddingTable, so a quantized n-gram tableexpanded to bf16 and 51.2B parameters became 102.4 GB. That is now eligible behind a
dequantizing gather that decodes one row per gathered token rather than the table.
The first review of this change FAILED on a blocker, and the failure is worth
recording because the change's own tests were arranged to miss it. Making
kEmbeddingTablekeep-quant eligible is a change to a SHARED policy with threeconsumers, and only one was updated.
deepseek_v4_weights.cppandlaguna_weights.cppboth assertRoute(...) == kExpandBf16by name fortoken_embd.weight, so both models' GGUF loads began throwing on CPU. Two things hidit: the commit deleted
kEmbeddingTablefrom the test case that asserted exactly thatinvariant, and the only DeepSeek-V4 GGUF fixture stores
token_embd.weightas F32 —the single encoding that still routed
expand_bf16. Both suites stayed green whileproduction was broken.
The repair narrows the policy for that one tensor through a shared
NoKeepQuant(pol),mirroring the existing
NoNvfp4(pol). It disables keep-quant ONLY, deliberatelyleaving
keep_f16alone so the downstream assertion stays a live check rather thanbecoming a tautology — verified by probe:
kq 1->0, kf16 1->1. DeepSeek-V4's tiedlm_headis routed underkMatmulWeight, the role it actually has, so a tied filegets two different answers for the same tensor. And Laguna's GGUF loader, which had
no test at all, now has one.
The two new
vec_dotkernels also landed ungated: swapping IQ4_NL's nibble halves andchanging Q5_0's
qhshift each survived all eight suites, including the tree'sdedicated numerical
vec_dotgate, whosekWeightCasesomitted both encodings whilecarrying MXFP4 as the precedent. Both are now covered and both mutations are red. They
are not dead code: all 48 IQ4_NL
ffn_down_expsdot through them.Two smaller repairs.
ResidentWeighthad internal linkage, so the reachability testbuilt its tensor one call short of the production bridge; it is now
Qwen3_5EmbeddingTable, used by all five embed call sites, and the test asserts dtype,shape and pointer identity. And an existing-model residency DEFAULT flipped —
qwen35/qwen35moe/qwen3nextGGUFs with a quantizedtoken_embd.weightnow keep itquantized on CPU — which is memory-only today (every GGUF-path gather has a bf16
output and
F32ToBF16is idempotent over the old path) but is a change to shippedmodels, so
docs/FEATURES.mdsays so rather than describing a new capability.This wave is also the first to land product code, so it carries the row's
READY -> ACTIVEtransition and theCLAIM-*owner an ACTIVE row obliges. Three claims in thematrix row were true when written and false now, and each is corrected in place rather
than dropped, because the row's whole work breakdown was reasoned from them: the
artifact exists, both load blockers are cleared, and the row is no longer spec-only.
W1 made the same transition independently on its own branch; the collision, its
resolution, and the shared-counter hazard behind it are recorded in the spec, because
W2, W3 and W4 will each reach the same fork and cannot see it from inside their own
branch.
Nothing here is reachable.
ModelRegistrydoes not resolveQwen4ExpForConditionalGeneration, so the config builder is reached and the model isnot, proven by mutation. There is no token number and no speed number, and the speed
axis does not open until G4. The CUDA gather is deliberately absent —
DeviceQuantGatherSupportedis CPU-only and
EmbeddingKernelCudastill refuses a block table — which is owed andmatters, because a device-resident quantized table gathered on device is precisely the
advantage over llama.cpp's CPU-pinned one.
Tracked by #1989, under #1978.
Gates: full
agent-preflight.shAll gates green, rc=0 on the merge tree.test_gguf_keep_quant42/6340,test_ops_embedding_quant6/1637,test_ops_quant_dot28/210138,
test_qwen4_exp_gguf_load_plan10/7462,test_deepseek_v4_gguf_load17/1045,
test_laguna_gguf_load3/25, plus the five qwen3_5 forward suites the bridgerename touched. A fresh review returned FAIL, a different implementer repaired it, and
a fresh scoped re-review returned PASS with every repaired finding proved by re-running
the mutation that used to survive.
Two spec repairs ride along
This branch inserted its
### Merge sequencingsection at the wrong offset, in themiddle of a sentence: the out-of-scope list naming MTP depth > 1, the 1M-token RoPE
extension and any throughput claim was left stranded thirty lines below as a fragment
opening with a bare backtick. Restored.
The second was already on
mainand is why this is not a typo fix. The spec's## Why this needs a spec before codeitem 2 read "QSA's twin in vLLM is MiniMax-M3,not DeepSeek-V4 ... Building it on the DSA/MLA path is the wrong port" — the row's
FIRST reading, which this campaign refuted. The same file says the opposite twice, in
the Port map and the Design heading, as do the matrix row and #1978. It is load-bearing
because item 2 sits in the section written so a fresh implementer does NOT re-derive
the decision, W4 is the QSA wave, and a top-down reader meets the stale instruction
first. The readings do not fail equally: M3 welds
SPARSE_BLOCK_SIZE = 128to the KVpage size, so ratio 4 forces a page size of 4 and breaks
tl.dot, whose tile needsFOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Claude:claude-opus-5 [Claude Code]