Repository navigation
spec(PP-QUANT-001): quant-type dispatch consolidation — design, contract, Phase 0 gate - #3420
alfredodeza wants to merge 6 commits into
Conversation
) Design doc + phase plan for consolidating the ~30-file scattered quant-type dispatch (#3418) into a single QuantBlockFormat/QuantCodebookFormat pair plus one dispatch entry point and a completeness contract gate. Placed on the 0.69 train per operator decision, overriding #3418's own request for a dedicated release cycle. Phase split (§3/§6) keeps only the low-risk Phase 0 inventory and Phase 1 trait+gate work bound to 0.69's train-leaves-by instant; the 30-file migration (Phase 2) and parity re-proof (Phase 3) are explicitly not bound to it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
§13.11 rung 1 — quorum shadow verdict Shadow mode: this records a verdict and merges nothing. A refusal |
QuantBlockFormat has associated consts and no-self-receiver methods, so dyn QuantBlockFormat cannot compile — the dispatch entry point described in §2.1 was unbuildable. Replaces it with a static table of QuantTypeTraits (plain data + fn pointers), the actual shape of llama.cpp's ggml_type_traits[] (a struct of fn pointers, never a vtable). Adds §2.1a recording why, and updates §2.3/§5 to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ign + a live gate (#3418) contracts/quant-dispatch-completeness-v1.yaml is the single enumeration the dispatch table will be checked against: all 35 live ggml tensor types with block/byte geometry transcribed from ggml-common.h static_asserts at llama.cpp 3173a5647, the 8 removed ids, GGML_TYPE_COUNT, the ten codebook grids, and the 30 dispatch sites (#3418's count, re-derived). 35 + 8 = 43, no gaps. PP-QUANT-001 §2.4 now carries the concrete Rust design: build on GgmlQuantType (there are already three partial registries; a fourth is the two-lists defect), QuantTypeTraits as plain data + fn pointers indexed by id, QuantCodebookFormat for the IQ/FP4 family with generated grid tables, hot kernels untouched, tensor_byte_size as the migration template. §4 corrects #3418's "16 named types" to 13 (three were metadata value types). quantize/dispatch_contract_tests.rs binds the contract to the tree today: removed ids rejected by from_id, the five QuantBlockFormat impls' geometry equals their rows, and the dispatch-site oracle equals the migrated:false set. Each shown RED by its FALSIFY-QDC mutation (145-byte Q4K, an unlisted site plus a falsely-flipped row, from_id(4), a deleted row), then GREEN. Registered in scripts/tree_reader_tests.txt; serde_yaml_ng promoted from build- to dev-dep. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ut §7 (#3418) Records the epic (#3421) and Phase 0 (contract + qdc_* tests) as done, and points to PP-ARCH-001-MASTER.md (#3422/#3423) — the shared-block forward-path consolidation this quant-dispatch table is a prerequisite for, since closing this document alone does not deliver "new architectures cost hundreds of lines": only ~85 of #3418's own 1,678-line Qwen3-MoE count is quant dispatch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…y slip" (#3418) Ticket/epic consolidation (per user decision 2026-09-17): #3422 is now a sub-issue of #3418 rather than a sibling ticket; epic #3421 is rescoped to Phase 0/1 only; Phase 2/3 (the actual blast-radius work #3418 asked to be dedicated) moves from vague "may slip forward under the train rule" language to a concrete home — milestone "Inference dispatch & architecture consolidation", tracked by epic #3423 jointly with PP-ARCH-001. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Ticket/epic consolidation (2026-09-17): #3422 (the shared-block forward-path finding) is now tracked as a sub-issue of #3418 rather than a sibling ticket — both are sub-problems of the same umbrella ("new architectures are too expensive to add"). Epic #3421 is rescoped to Phase 0/1 only (this PR's scope); Phase 2/3 now has a real home — milestone |
noahgift
left a comment
There was a problem hiding this comment.
Review — measured against origin/main @ a672677f4 and this branch's head (2026-09-17). Comment only; your text and titles are untouched.
The direction is right and two calls in here are better than what I would have proposed: a data table instead of dyn (§2.1a), and the contract as the universe with a test that reads it. 35 live / 8 removed / 13 consts / 16 variants all reproduce [V]. Five findings, in order of how much they change the plan.
1. The oracle's universe is shrunk by its own pattern — 3 files are invisible to it [V]
oracle_hits_line fires on GGUF_TYPE_Q4_K, GGUF_TYPE_Q4_0, or match … qtype. A file that dispatches on any other id only is not a site. Reconciling dispatch_sites (30) against every non-test file in aprender-serve/src naming any GGUF_TYPE_* (27): 24 in both, union 33.
- in the tree, not in the contract:
gguf/cuda/moe_ffn_forward_layer_cuda.rs(F32),gguf/inference/forward/forward_qwen35.rs(F32),gguf/loader_gguf_model_02.rs(Q3_K) - in the contract, not in my const-keyed scan:
api/model_source.rs,apr/cache.rs,convert/q4k_conversion_stats.rs,cuda/executor/layers/cublas_prefill/gemm.rs,cuda/executor/layers/gemv_dispatch.rs,cuda/types.rs
So both scans are invocation-shaped and each misses what the other finds. Since qdc_dispatch_sites_match_oracle is the ratchet for Phase 2, a site added with GGUF_TYPE_Q6_K today passes it. Suggest the oracle key on the enum/const namespace (GGUF_TYPE_[A-Z0-9_]+, GgmlQuantType::, WeightQuantType::) plus the match forms, and ship a must-match / must-not-match case table. Bare-integer arms (12 =>, qtype == 2) are a third form neither scan sees; upper bound 558 non-test lines across 10+ crates [U] — noisy, needs the case table before it is a baseline.
2. §2.4's registry table stops at the crate boundary — there are three ggml enums [V]
| enum | ids |
|---|---|
serve GgmlQuantType (gguf/types.rs) |
16 |
core GgmlType (aprender-core/src/format/gguf/types.rs:55 — the import path) |
12 — no Q5_0, Q5_1, Q2_K, Q3_K, Q5_K |
compute GgmlType (aprender-compute/src/inference/gguf.rs:27) |
15 |
Growing serve's enum to 35 leaves the importer at 12, so apr import and apr run will keep disagreeing about what a file contains. The enum + TRAITS want to live below all three (lowest common workspace dep, or a zero-dep leaf crate), with the three becoming re-exports → #3430. #3405 also added a fourth private name table (QTYPE_LABELS, 13 rows) that the same table replaces.
3. The defect history is two classes, and the larger one is not quant dispatch [V-diff]
Read from the five fix diffs, not the PR bodies:
| issue | fix | what the diff does | class |
|---|---|---|---|
| #1749 | #1751 | routes apr bench to the MoE path |
placeholder — re-route |
| #1789 | #1790, #1806 | validate_matmul_weight_shape rejects empty data; HTTP 501 |
placeholder — guard |
| #2535 | #2541 | validator skips dense FFN when arch is MoE | placeholder — guard |
| #3341 | #3405 | SUPPORTED_EXPERT_QTYPES + drift test |
qtype list |
| #3091 (reopen) | open | tensor_byte_size: 11 arms |
qtype list |
Mechanism of the 3-of-5 class: from_gguf_for_moe fills a MoE layer's dense FFN slots with dense_ffn_placeholder — byte_size: 0, qtype: 0. 0 is F32, so the placeholder is a well-formed F32 tensor ref until something indexes it (hence #1789's qtype=0 in the panic, and #3368). All three fixes guard a consumer; none removes the placeholder — it is still built on main, and the tied-lm_head path has a second instance (#2309/#2441). 63 non-test files read .ffn_{up,gate,down}_weight; 14 carry no guard token, including all 4 gguf/cuda consumers and the wgpu adapter (file-level heuristic, [U] as a semantic claim). Neither this spec nor PP-ARCH-001 covers it. Proposed as a sibling epic, not a change to yours: #3428 PP-TENSOR-001 (absence as a type; #3433–#3435).
Correction to my own 09:20Z comment on #3418: I classed #3091 as "arch". Its reopen is a qtype-list defect, so the split is 3 placeholder / 2 qtype-list, not 4 / 1.
4. tensor_byte_size first is the right Phase 2 opener — and it is smaller than the spec says
It lacks not just IQ* but Q3_K and Q5_1, which the crate already defines; and there are three byte-size functions, not one (transformer.rs:435, convert/q4k_conversion_stats.rs:6, convert/q4k_converter_helpers.rs:20). Reproduced on the published v0.68.0 asset with a 338 MB public file (details on #3091). Because byte_size = n / blck_size * type_size needs only the two numbers your contract already carries for all 35 rows, this one site can close for every id without a single new kernel → #3432.
5. Placement — flagged, not decided here
The operator directive of 2026-09-17 makes a slice of this 0.69.0 must-carry: fixtures (#3429) → one enum + TRAITS (#3430) → reconciliation gate (#3431, builds on your dispatch_contract_tests.rs rather than duplicating it) → byte-size + single refusal (#3432), then PP-TENSOR T1–T2. That overlaps the Phase 1 → dedicated-milestone boundary in #3423. I have not moved or re-scoped anything of yours; the per-directory retype tickets I minted (#3436–#3440, in 0.70.0) restate your Phase 2 and should fold into whichever placement wins. CI on main is p50 31 min / p95 62 min, ~69 serial slots per 72 h, merge queue builds 3 wide — throughput is not what forces a dedicated cycle; the Qwen2.5-Coder parity risk you cite is the real argument, and a per-PR perf gate on the merge sha answers it on a train.
Not done: no independent quorum has reviewed any of this — these are one reviewer's measurements. #3444 (my graph-builder ticket) duplicates your #3422 and I am closing it in favour of yours.
Ask: the branch is behind main and gate, guard-cargo, guard-tree, present are red — gh pr update-branch 3420 first; I have not diagnosed the four reds against a current base.
|
Triage from the release babysit session (aprender-ba): the
|
|
Update: the §11.1 predicate was the reach-too-far, not this PR. #3536 (PMAT-3535) merged at 20:04Z: the spec now names only the release spec ( |
|
Re-run on 9b7fe4f (main merged in): the §11.1 ont-delta red is gone as predicted. Two remaining reds, both this branch's, both small:
Everything else is green. #3424 has no such reds (no contract file added) and is running clean. |
|
One more after the main merge, and this one is code, not body: |
|
Suggestion for the The failing assertion, verbatim from run 35469204174, job 105966962481: One site, one direction: the tree has a raw-qtype match in Two other things on this PR, both measured, so you do not spend a CI round finding them:
Also worth knowing: that shard reported |
|
slipped_from: 0.69.0. §4.1 freeze for the 0.69 cut today (operator 2026-09-21: "ensure this happens"). Not on the 0.69 platform (#3669 + bump). Rides 0.70. — aprender-04 (cop) |
|
closed by operator 1-day PR rule; reopen/rebase to resume. No live owner session, red CI on the current head, not carried by main or any open integration PR. Linked issue: #3418 (open, milestone 0.74.0). Linked issues are not touched by this close. Branch is NOT deleted: |
…se spec, not every design spec — three maintainer PRs were red for a delta that did not exist; the predicate gets its self-test (Refs PMAT-3535) (paiml#3536) * §11.1 + check_pr_ont_delta: the docs/specifications sink is the release spec, not every design spec; the predicate gets its self-test (Refs PMAT-3535) Measured 2026-09-19 (aprender-ba): paiml#3499, paiml#3420 and paiml#3424 — a guard-regex fix and two design specs — RED on guard-tree with `FAIL no ont-delta: line`, each touching one file under docs/specifications/ and nothing else §11.1 names. The guard was FAITHFUL to the sentence, which named `docs/specifications/**` whole as a prose sink. A design spec is the finding's home, not a sink a sweep writes into; the sink under that directory is the train's own spec, where sweep findings are recorded. Spec and guard move together: §11.1 names `docs/specifications/APR-RELEASE-001-*.md` and says a `*-MASTER.md` design spec is not a sweep; ONT_SWEEP_SINKS follows (the file, not the directory). The predicate half had no self-test (all 15 rows fed check_body_text). predicate_self_test — aprender-ba's leg, 10 rows: crate source, a contract, ci.yml are not sweeps; night.yml, README, CLAUDE, the registry, a derived baseline, the release spec are — chains into --self-test. The falsifying row "a DESIGN spec is not a sweep" is want=1 and goes RED (want=1 got=0) with the directory-wide sink restored, so the leg discriminates. --self-test 15 + 10 passed; bashrs lint 0 errors. Pmat-Ticket: PMAT-3535 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * §11.1 and the guard name the same bytes; the design-spec row is named as the criterion names it (Refs PMAT-3535) Quorum round 1, lane 1: the spec said APR-RELEASE-001-*.md while ONT_SWEEP_SINKS said the exact file — 'verbatim' means the same string in both; and the row name carried a suffix the criterion did not. Criteria rewritten from the diff. Pmat-Ticket: PMAT-3535 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * check_pr_ont_delta: say in predicate_row that sweep_reason takes a FILE of paths (Refs PMAT-3535) Quorum round 2, lane 3 (flash) claimed, "measured", that predicate_row hands sweep_reason the temp path itself so six rows fail with got=1. It does not: sweep_reason reads the changed paths OUT of the file it is given (`while IFS= read -r f … done < "$changed"`), which is the shape main() hands it from --changed; predicate_row writes one path into a temp file and passes the file. Measured on this head, `bash scripts/check_pr_ont_delta.sh --self-test`: self-test 15 passed, 0 failed; predicate self-test 10 passed, 0 failed — and with the directory-wide sink restored exactly one row (a DESIGN spec is not a sweep) goes want=1 got=0. The claim was a misread of the function's contract; the comment now states that contract where the misread happened, so the next reader does not need to trace it. Pmat-Ticket: PMAT-3535 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * audit: quorum-PMAT-3535 round 3 AGREED 3/3 on ad99389 (Refs PMAT-3535) Round 1: two exactness findings (the row name, the sink spelled the same in spec and guard), both taken. Round 2: one lane misread sweep_reason's file argument; refuted by the measured self-test and the contract stated in a comment. Pmat-Ticket: PMAT-3535 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Noah Gift <noah@paiml.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Summary
docs/specifications/PP-QUANT-001-MASTER.md— the plan for ARCH: quant-type dispatch is duplicated across ~30 files with no single source of truth — propose a dedicated consolidation release #3418 (0.69 placement per operator decision, §6), now with the concrete Rust design in §2.4: build the dispatch table on the existingGgmlQuantType(three partial registries already exist — a fourth is the two-lists defect),QuantTypeTraitsas plain data +fnpointers indexed by ggml id (the actual shape ofggml_type_traits[], notdyn Trait— §2.1a explains why that cannot compile), aQuantCodebookFormattrait for the IQ/FP4 family with generated grid tables, hot kernels untouched,tensor_byte_size(CPU: Qwen3.5 / Qwen3.8 hybrid GGUFs are refused entirely — no fallback exists #3091's crash site) as the migration template.contracts/quant-dispatch-completeness-v1.yaml— Phase 0 done: all 35 live ggml tensor types with geometry transcribed fromggml-common.hstatic_asserts at llama.cpp3173a5647, 8 removed ids,type_count43, ten codebook grids, and the 30 dispatch sites.pv validate/pv lintgreen.crates/aprender-serve/src/quantize/dispatch_contract_tests.rs— fiveqdc_*tests binding the contract to the tree today: enumeration gapless, removed ids rejected byfrom_id, names agree, the fiveQuantBlockFormatimpls match their rows, and the dispatch-site oracle equals themigrated: falseset (so a 31st site, or a row falsely flipped, fails CI). Registered inscripts/tree_reader_tests.txt.RED→GREEN receipt (run in-session, all restored byte-identical)
SUPERBLOCK_BYTES144→145 (QDC-003)FAILED— "SUPERBLOCK_BYTES 145 != contract bytes 144"match qtype { GGUF_TYPE_Q4_K …+loader.rsflipped tomigrated: true(QDC-005)FAILED— names bothzz_mutant_site.rsandgguf/loader.rsfrom_id(4)→Some(Q4_0)(QDC-002)FAILED— "must be None"FAILED— gapless assertion5 passed; 0 failedTest plan
cargo test -p aprender-serve --lib qdc_— 5/5pv validate+pv linton the contract — PASSscripts/check_contract_test_binding.sh— PASS (0 dangling; the two obligations that need Phase 1 code carry notest:citation by design)scripts/check_tree_reader_tests.sh— registry equals derived set after--updatescripts/check_no_claim_literals.sh— PASScargo clippy -p aprender-serve --lib --tests -D warnings— RED on pre-existing findings inaprender-test-lib/pixel_coverage/*andforward_qwen35.rs(make raw seems to fail on a fresh mbp #2370 ceiling class), none in the files touched hereRefs #3418 · Epic #3421
keep-open: #3418 #3421 (epic/parent; not closed here)
🤖 Generated with Claude Code