Skip to content

spec(PP-QUANT-001): quant-type dispatch consolidation — design, contract, Phase 0 gate - #3420

Closed
alfredodeza wants to merge 6 commits into
mainfrom
spec/pp-quant-001-dispatch-consolidation
Closed

alfredodeza wants to merge 6 commits into
mainfrom
spec/pp-quant-001-dispatch-consolidation

Conversation

@alfredodeza

@alfredodeza alfredodeza commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

RED→GREEN receipt (run in-session, all restored byte-identical)

Mutation (contract FALSIFY id) Result
Q4K SUPERBLOCK_BYTES 144→145 (QDC-003) FAILED — "SUPERBLOCK_BYTES 145 != contract bytes 144"
new file matching match qtype { GGUF_TYPE_Q4_K … + loader.rs flipped to migrated: true (QDC-005) FAILED — names both zz_mutant_site.rs and gguf/loader.rs
from_id(4) → Some(Q4_0) (QDC-002) FAILED — "must be None"
delete the Q3_K row FAILED — gapless assertion
unmutated 5 passed; 0 failed

Test plan

  • cargo test -p aprender-serve --lib qdc_ — 5/5
  • pv validate + pv lint on the contract — PASS
  • scripts/check_contract_test_binding.sh — PASS (0 dangling; the two obligations that need Phase 1 code carry no test: citation by design)
  • scripts/check_tree_reader_tests.sh — registry equals derived set after --update
  • scripts/check_no_claim_literals.sh — PASS
  • cargo clippy -p aprender-serve --lib --tests -D warnings — RED on pre-existing findings in aprender-test-lib/pixel_coverage/* and forward_qwen35.rs (make raw seems to fail on a fresh mbp #2370 ceiling class), none in the files touched here

Refs #3418 · Epic #3421

keep-open: #3418 #3421 (epic/parent; not closed here)

🤖 Generated with Claude Code

)

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>
@github-actions

github-actions Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

§13.11 rung 1 — quorum shadow verdict

S13-SHADOW pr=3420 head=9b7fe4f7caadfaeeb3304ee81c54e24b130bfb19 verdict=REFUSE class=Q1 arm_rc=1

Shadow mode: this records a verdict and merges nothing. A refusal
to arm is not a block (§13 adds zero rows to §7) — the pull request is
exactly as green as it was.

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>
@alfredodeza alfredodeza changed the title docs(spec): PP-QUANT-001 — quant-type dispatch consolidation plan spec(PP-QUANT-001): quant-type dispatch consolidation — design, contract, Phase 0 gate Sep 17, 2026
…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>
@alfredodeza

Copy link
Copy Markdown
Contributor Author

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 Inference dispatch & architecture consolidation, tracked by epic #3423 jointly with PP-ARCH-001, replacing the earlier vague "may slip forward under the train rule" language. Spec updated accordingly (§0 header, §6.1, §7).

@noahgift noahgift left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@noahgift

Copy link
Copy Markdown
Member

Triage from the release babysit session (aprender-ba): the guard-tree red here is the same one as on #3499 and is a PR-body rule, not the diff:

FAIL  no `ont-delta:` line. §11.1: a sweep PR closes with a delta,
FAILED: §11.1 — a sweep that ends in prose and nothing else is not finished.

gate is red only because guard-tree is. Fix = add an ont-delta: line to the PR body, then push (an empty commit is enough) or close/reopen — the guard reads the frozen pull_request event payload, so a body edit alone never re-runs it. GitHub reports the branch MERGEABLE against today's main, so nothing else is in the way. All three of your open PRs (#3499, #3420, #3424) are blocked by this same line; I have asked the ontology owner whether the §11.1 predicate is meant to reach spec PRs at all.

@noahgift

Copy link
Copy Markdown
Member

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 (docs/specifications/APR-RELEASE-001-*.md) as a sweep sink, a *-MASTER.md design spec is the finding's home and not a sink, and check_pr_ont_delta.sh got the predicate self-test it never had. No body change needed — a rebase/update onto main (ce343c7 or later) re-runs guard-tree with the fixed guard.

@noahgift

Copy link
Copy Markdown
Member

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:

  1. guard-tree, §6 R-2 body guard: FAIL: non-closing ref(s) with no keep-open reason: #3418 #3421. The body cites those two issues without closing them; add one line each, keep-open: #3418 <reason> and keep-open: #3421 <reason>, then push (body edits alone never re-run the guard).
  2. guard-cargo, README claims: contracts/census.json .n_files=1799 but HEAD carries 1800 — this PR adds contracts/quant-dispatch-completeness-v1.yaml without regenerating the census. Run make contracts (pv census) and commit the updated contracts/census.json (and README count if make readme-sync moves it).

Everything else is green. #3424 has no such reds (no contract file added) and is running clean.

@noahgift

Copy link
Copy Markdown
Member

One more after the main merge, and this one is code, not body: workspace-test-shard (1/3) fails quantize::dispatch_contract_tests::tests::qdc_dispatch_sites_match_oracle (crates/aprender-serve/src/quantize/dispatch_contract_tests.rs:216) — PO-QDC-005 FAILED — contracts/quant-dispatch-completeness-v1.yaml dispatch_sites and the tree disagree. Main moved since this contract was written (the CB-200 lifts in #3533 among others), so the contract's dispatch_sites list no longer matches the merged tree; re-derive it against main 338f1d4 or later and the test goes green. So the full list for this PR: keep-open lines (#3418, #3421), make contracts for the census, and the dispatch_sites re-derivation.

@noahgift

Copy link
Copy Markdown
Member

Suggestion for the workspace-test-shard (1/3) red, from the neighbouring ticket (#3430 M1 / #3581) — not pushing anything to your branch, since this is an authoring judgement on your spec.

The failing assertion, verbatim from run 35469204174, job 105966962481:

PO-QDC-005 FAILED — contracts/quant-dispatch-completeness-v1.yaml `dispatch_sites` and the tree disagree.
Matching on a raw qtype but NOT listed (a new dispatch site — migrate it to quant_type_traits() or add a row): ["crates/aprender-serve/src/quantize/iq_dispatch.rs"]
Listed as open (migrated: false) but no longer matching (flip the row to migrated: true): []

One site, one direction: the tree has a raw-qtype match in crates/aprender-serve/src/quantize/iq_dispatch.rs that the contract's dispatch_sites does not list. The smallest honest change is the row with migrated: false — that records it as known and open, which is what it is, rather than claiming a migration that has not happened. Whether it should instead be migrated to quant_type_traits() in this PR is your call and not something I would decide for you.

Two other things on this PR, both measured, so you do not spend a CI round finding them:

  1. guard-cargo is the census, not your spec. FAIL contracts/census.json .n_files=1799 but HEAD carries 1800 contract file(s). Adding contracts/quant-dispatch-completeness-v1.yaml makes three tracked derivatives stale, and your diff regenerates none of them. I hit the identical thing on PMAT-3430 M1: one ggml tensor-type table, extracted from upstream rather than typed #3581 an hour ago and they surface one per CI round, because each is masked by the one before it:

    derivative regenerate with shows up as
    contracts/census.json make contracts (pv census contracts) guard-cargo, and a false row-4 self-test red downstream of it
    contracts/contracts.nt pv extract contracts workspace-test-shard, the_tracked_repo_graph_is_fresh (R-18)
    README CONTRACT_COUNT block make readme-sync guard-cargo, FALSIFY-README-002 — invisible until the census one is fixed

    Running all three before the next push saves two rounds on a pool that has been at zero idle most of today.

  2. guard-tree needs nothing from you. Its only FAIL row was non-closing ref(s) with no keep-open reason: #3418 #3421, and the body already carries the keep-open: line — I ran the guard against the live body and it returns rc=0 PASS. That red is a verdict on the body as it was 17 hours before the fix (run created 2026-09-19T21:02:26Z, body updated 2026-09-20T14:00:11Z); ci.yml reads the frozen event payload and has no edited trigger, so it clears itself on your next push and needs no separate re-trigger.

Also worth knowing: that shard reported 8576/21113 tests were not run due to test failure — fail-fast, so a single failing test hid the rest of the shard. Whatever is behind those 8,576 is still unmeasured on this branch; tracked as #3587.

Refs #3430, #3581

@noahgift

Copy link
Copy Markdown
Member

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)

@noahgift

Copy link
Copy Markdown
Member

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: spec/pp-quant-001-dispatch-consolidation @ 9b7fe4f7c.

@noahgift noahgift closed this Sep 24, 2026
guyernest pushed a commit to guyernest/aprender that referenced this pull request Sep 29, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind:code Work is a code change (derived rule, #4159) needs-owner Open work with no live owner — 0.70 batch triage; pick it up and reassign

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants