You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ggml_dtype_element_size is ordered by its own comment, not by ggml id: BF16 reads 0.375 B/elem instead of 2.0, so the #2569 truncation check is fail-open on BF16 GGUFs #3583
Found by the correctness lane (tab 2) while deriving TRAITS[43]; verified here by reading the table on origin/main.
crates/aprender-core/src/format/safetensors.rs::ggml_dtype_element_size indexes a 31-entry array by ggml type id, but the array is laid out in the order of its own comment, and the two diverge from index 26:
index
the comment says
ggml actually says
table value
correct
26
BF16
I32
2.0
4.0
27
I32
I64
4.0
8.0
28
I64
F64
8.0
8.0
29
F64
IQ1_M
8.0
0.375
30
IQ1_M
BF16
0.375
2.0
So ggml_dtype_element_size(30) — BF16 — returns 0.375 bytes per element instead of 2.0, a 5.3× under-report. And ids 31–42 (TQ1_0, TQ2_0, MXFP4, NVFP4, Q1_0, Q2_0, …) fall off the end of a 31-entry array into unwrap_or(4.0).
Why this is P0 rather than a display bug
The same function computes the extent used by the #2569 truncation check, at safetensors.rs:36:
let size_bytes = (num_elements asf64*ggml_dtype_element_size(meta.dtype))asu64;
…
check_tensor_table_fits("GGUF", data.len()asu64, reader.data_offsetasu64, extents)?;
A BF16 GGUF's tensors are therefore asserted to occupy a fifth of their real extent, so a file truncated by up to ~80 % of its tensor data passes the check. The gate that exists to refuse a truncated model is fail-open on BF16, which is a common GGUF dtype. apr tensors also under-reports those sizes by 5.3× to the user.
The shape of the defect, which is the reason to fix it properly
This table was already partially fixed once: PMAT-869 corrected four K-quant rows in June and stopped. A hand-maintained mirror of an upstream table gets repaired a few rows at a time, and each repair leaves the rest looking reviewed.
The fix is not an eleventh hand-typed row. It is a lookup into the TRAITS table that #3581 is landing — one table, extracted from upstream at the pinned comparator by a committed command, with a fixture and a mutation proof. That is the entire argument for slice M, arriving with a user-visible casualty attached.
Acceptance
ggml_dtype_element_size (or its replacement) resolves through TRAITS, not a local array. No second copy of upstream's table survives in aprender-core.
Positive control, both directions: a BF16 GGUF truncated below its real extent is refused by check_tensor_table_fits; the same file intact passes. Today the first case passes.
apr tensors reports BF16 sizes within rounding of num_elements * 2.
Every id ggml defines resolves — ids 31–42 no longer land on the 4.0 default.
The mutation: change one TRAITS row and the check must turn RED.
Sequencing
Lands immediately after #3581, not inside it — slice M stays a clean, reviewable table-and-fixture change, and this is its first consumer. It is also a row for #3431's reconciliation table: verdict NEVER-WORKED for the misordered rows (they have never been right since the ids past 25 existed), measured on the published binary.
Credit: found and measured by the correctness lane; table read and truncation-path impact confirmed here.
Required before this lands: the gate's positive control, in this PR — not after TRAITS
A BF16 GGUF truncated by 50 % must be refused. Today it passes. Commit that fixture here, so:
the fix is genuinely RED→GREEN rather than a value change nobody can see fail, and
the same fixture stands guard when TRAITS replaces the lookup — the replacement is the moment this could silently regress, and a fixture committed now is the only thing that will notice.
Both directions, as usual: the truncated file is refused; the intact file passes. A fixture that only proves the refusal cannot tell a working gate from one that refuses everything.
Sibling evidence, and it is stronger than PMAT-869
crates/aprender-core/src/format/tensors.rs::ggml_dtype_name — the array next door — carries this comment on origin/maintoday:
"The tail (24-30) was misordered — BF16 was at 26, mislabeling BF16 tensors (code 30) as IQ1_M, I32 (26) as BF16, etc. Correct order per ggml.h: I8=24, I16=25, I32=26, I64=27, F64=28, IQ1_M=29, BF16=30."
This exact defect was found, fixed in the name table, and written down — while the size table in the file beside it kept the bug. The correct ordering has been sitting in a comment in this crate while the truncation gate stayed fail-open. That is the whole argument for a single extracted table, and it is why the fix here is a TRAITS lookup rather than a twelfth hand-typed row.
Full enumeration of every id-indexed mirror, ordered by blast radius, is on #3421 — six sites, two of them positional arrays (the dangerous form: a wrong row shifts every row after it, invisibly), four match-on-id (which mislabel one arm and stay legible).
Also in scope here, since the array is 31 entries
Ids 31–42 (TQ1_0, TQ2_0, MXFP4, NVFP4, Q1_0, Q2_0, …) fall off the end onto unwrap_or(4.0). After the TRAITS lookup every id ggml defines must resolve; no id may land on a default, and the positive control should include one of the post-30 types.
milestone → 0.74.0 per docs/roadmaps/milestone-triage-2026-09-23.md (#4024), operator approval 2026-09-23 (relayed by aprender-cf). Reason: quant/tensor/arch dispatch consolidation: Any Model core (#4001)
Parked by APR-EPIC-001: over S3 budget (rank 7/142). Reopen = pull. Applied by aprender-d1: claude+agy both said close, apr shadow row recorded (handoff/close-quorum-2026-09-25.json).
Activity
noahgift commentedon Sep 20, 2026
Required before this lands: the gate's positive control, in this PR — not after TRAITS
A BF16 GGUF truncated by 50 % must be refused. Today it passes. Commit that fixture here, so:
TRAITSreplaces the lookup — the replacement is the moment this could silently regress, and a fixture committed now is the only thing that will notice.Both directions, as usual: the truncated file is refused; the intact file passes. A fixture that only proves the refusal cannot tell a working gate from one that refuses everything.
Sibling evidence, and it is stronger than PMAT-869
crates/aprender-core/src/format/tensors.rs::ggml_dtype_name— the array next door — carries this comment onorigin/maintoday:This exact defect was found, fixed in the name table, and written down — while the size table in the file beside it kept the bug. The correct ordering has been sitting in a comment in this crate while the truncation gate stayed fail-open. That is the whole argument for a single extracted table, and it is why the fix here is a
TRAITSlookup rather than a twelfth hand-typed row.Full enumeration of every id-indexed mirror, ordered by blast radius, is on #3421 — six sites, two of them positional arrays (the dangerous form: a wrong row shifts every row after it, invisibly), four
match-on-id (which mislabel one arm and stay legible).Also in scope here, since the array is 31 entries
Ids 31–42 (
TQ1_0,TQ2_0,MXFP4,NVFP4,Q1_0,Q2_0, …) fall off the end ontounwrap_or(4.0). After theTRAITSlookup every id ggml defines must resolve; no id may land on a default, and the positive control should include one of the post-30 types.PMAT-3430 Phases 3-4: three ggml enums become one, and the count is n…
feat(#3686): a gate for "the corrected replacement has zero callers" …
noahgift commentedon Sep 23, 2026
milestone → 0.74.0 per docs/roadmaps/milestone-triage-2026-09-23.md (#4024), operator approval 2026-09-23 (relayed by aprender-cf). Reason: quant/tensor/arch dispatch consolidation: Any Model core (#4001)
docs(roadmap): quorum round 6 -- #3845 reverted as a DECIDE with #3978,
docs(roadmap): quorum round 7 -- #3714 reverted and #3839 relabelled …
noahgift commentedon Sep 25, 2026
Parked by APR-EPIC-001: over S3 budget (rank 7/142). Reopen = pull. Applied by aprender-d1: claude+agy both said close, apr shadow row recorded (handoff/close-quorum-2026-09-25.json).
PMAT-3430 Phases 3-4: three ggml enums become one (M1 complete) (paim…