Skip to content

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
mainfrom
row/MODEL-MM-QWEN4-EXP-W6A
Open

feat(MODEL-MM-QWEN4-EXP): W6a — IQ4_NL, Q5_0 and a dequantizing gather, so the only artifact that fits can be opened#2019
localai-bot wants to merge 11 commits into
mainfrom
row/MODEL-MM-QWEN4-EXP-W6A

Conversation

@localai-bot

@localai-bot localai-bot commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

This is the wave that makes Qwen/Qwen3.8-Flash-Next loadable at all. Before it, no
published artifact of that model could be opened by this tree; after it,
unsloth/Qwen3.8-Flash-Next-GGUF UD-IQ1_S — 67.56 GiB in three shards,
general.architecture = qwen4exp, 1224 tensors — opens, and it is the only artifact
that 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,66 and 20 was the only one missing,
which is what that file uses for ffn_down_exps and for the 51.2B-parameter n-gram
table. Q5_0 (6) is added in the same change because tensor_type_fallback maps
Q4_K -> Q5_0 and IQ4_XS -> IQ4_NL, so which one a file carries depends on the
recipe and a -Q4_K_M build of this model lands on the other one. Both geometries
were read from the pinned llama-cpp oracle at b10451 via git archive, because
the 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:
KeepQuantKDim returned -1 for kEmbeddingTable, so a quantized n-gram table
expanded 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
kEmbeddingTable keep-quant eligible is a change to a SHARED policy with three
consumers, and only one was updated. deepseek_v4_weights.cpp and
laguna_weights.cpp both assert Route(...) == kExpandBf16 by name for
token_embd.weight, so both models' GGUF loads began throwing on CPU. Two things hid
it: the commit deleted kEmbeddingTable from the test case that asserted exactly that
invariant, and the only DeepSeek-V4 GGUF fixture stores token_embd.weight as F32 —
the single encoding that still routed expand_bf16. Both suites stayed green while
production 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, deliberately
leaving keep_f16 alone so the downstream assertion stays a live check rather than
becoming a tautology — verified by probe: kq 1->0, kf16 1->1. DeepSeek-V4's tied
lm_head is routed under kMatmulWeight, the role it actually has, so a tied file
gets 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_dot kernels also landed ungated: swapping IQ4_NL's nibble halves and
changing Q5_0's qh shift each survived all eight suites, including the tree's
dedicated numerical vec_dot gate, whose kWeightCases omitted both encodings while
carrying MXFP4 as the precedent. Both are now covered and both mutations are red. They
are not dead code: all 48 IQ4_NL ffn_down_exps dot through them.

Two smaller repairs. ResidentWeight had internal linkage, so the reachability test
built 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/qwen3next GGUFs with a quantized token_embd.weight now keep it
quantized on CPU — which is memory-only today (every GGUF-path gather has a bf16
output and F32ToBF16 is idempotent over the old path) but is a change to shipped
models, so docs/FEATURES.md says so rather than describing a new capability.

This wave is also the first to land product code, so it carries the row's READY -> ACTIVE transition and the CLAIM-* owner an ACTIVE row obliges. Three claims in the
matrix 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. ModelRegistry does not resolve
Qwen4ExpForConditionalGeneration, so the config builder is reached and the model is
not, 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 — DeviceQuantGatherSupported
is CPU-only and EmbeddingKernelCuda still refuses a block table — which is owed and
matters, 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.sh All gates green, rc=0 on the merge tree.
test_gguf_keep_quant 42/6340, test_ops_embedding_quant 6/1637, test_ops_quant_dot
28/210138, test_qwen4_exp_gguf_load_plan 10/7462, test_deepseek_v4_gguf_load
17/1045, test_laguna_gguf_load 3/25, plus the five qwen3_5 forward suites the bridge
rename 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 sequencing section at the wrong offset, in the
middle 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 main and is why this is not a typo fix. The spec's
## 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" — 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 = 128 to the KV
page size, so ratio 4 forces a page size of 4 and breaks tl.dot, whose tile needs

= 16. Filed as #2049 and fixed here rather than deferred.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Claude:claude-opus-5 [Claude Code]

mudler added 8 commits August 26, 2026 17:23
…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]
mudler added 3 commits August 26, 2026 23:38
…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]
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