Skip to content

EPIC: release train 0.68.2 (interrupt) — Qwen3 CPU+GPU verification, P0 #3477

Closed

Description

@alfredodeza

Release train 0.68.2 (interrupt) — Qwen3 CPU + GPU support, P0

Why this exists / relation to the 06x cadence (docs/specifications/06x-release-schedule.md): that spec's only
stated Qwen priorities are F/G — Qwen2.5-Coder-7B parity and its fine-tune story. Qwen3 was never an operator-declared
priority row, so under §1.1 ("scope that is not on the platform rides the next train") every Qwen3 ticket has drifted
forward release over release with no floor. This milestone is an explicit operator override of that drift, inserted
ahead of 0.69.0/0.70.0, per the 2026-09-18 decision: the next release ships Qwen3 CPU+GPU verification, full stop.

Investigation — how Qwen3 support ended up NOT in v0.68.1 (summary; full trail in ticket comments):

  1. Dense Qwen3 (Qwen3ForCausalLM) has no current proof it runs at all. The only historical verifications
    (Qwen3 GPU inference produces garbage output (architecture not supported) #479, qualify: Qwen3-4B self-test failures (Q4_K_M and Q8_0) #308, closed 2026-02/03) predate the entire monorepo consolidation and multiple forward-path bug classes
    found since (Q5_K layout fix, 8 Qwen3.5-forward defects). Nothing re-checked it. This wasn't "deprioritized" —
    it was undiscovered: P0: dense Qwen3 (Qwen3ForCausalLM) has no current proof it runs — last verification is 7 months stale and CI never covers it #3413 (P0) was filed 2026-09-17, the day after the 0.68.0 train closed, while
    investigating the adjacent Qwen3.5 ticket. It could not have ridden 0.68.1 because the gap wasn't known to exist
    until 0.68.0 had already shipped. It landed on milestone 0.69.0 by default (the only open train when filed), not
    by any prioritization decision.

  2. Qwen3.5 CPU (CPU: Qwen3.5 / Qwen3.8 hybrid GGUFs are refused entirely — no fallback exists #3091) DID get an explicit no-slip commitment for 0.68 (operator ruling 2026-09-15, row 68-Q1
    of the 0.68.0 epic EPIC: release train 0.68.0 — 06x-release-schedule.md §3 (0.68.0) #3079) and a PR landed (feat(qwen35): Qwen3.5 / Qwen3.8 hybrid GGUFs run on the CPU — apr run + apr chat (#3091) #3114/CPU: Qwen3.5 / Qwen3.8 hybrid GGUFs are refused entirely — no fallback exists #3091 merge). But the fix was incomplete: tensor_byte_size
    only handles 11 quant-type ids, and real downloaded quantizations (UD-IQ2_XXS, UD-IQ4_XS) crash with an
    unhandled-type refusal. The ticket's true state was hidden by three wrongful auto-closes — GitHub misparsed
    the literal string "no-close: CPU: Qwen3.5 / Qwen3.8 hybrid GGUFs are refused entirely — no fallback exists #3091" in unrelated PR bodies as a closing keyword (tracked separately as GitHub parses 'no-close: #N' as 'close: #N' — #3091 was closed twice by PRs whose bodies said no-close (D0 of the pmat 3.41.0 run); #3351 carries the same line for #3347 #3400,
    a real GitHub/tooling defect). Operator triage caught this 2026-09-17, reopened it, and rescoped it to depend on
    PP-QUANT-001 M4: tensor_byte_size and ONE refusal site read TRAITS — closes the #3091-reopen / #3341 class for all 35 ids at once #3432 (the quant-dispatch fix), which was itself slotted as 0.69.0 "must-carry" — so CPU: Qwen3.5 / Qwen3.8 hybrid GGUFs are refused entirely — no fallback exists #3091 landed on 0.70.0,
    two trains past where it was originally committed.

  3. Qwen3.5 GPU (GPU: implement Gated DeltaNet / SSM inference for Qwen3.5 / Qwen3.8 hybrid GGUFs #3090) and the umbrella feature ticket feat: Qwen3.5 (qwen35) SSM/Gated Delta Net inference support #3038 never received any no-slip ruling — they
    simply lost every capacity contest against the F/G-prioritized Qwen2.5-Coder work under the train's hard PR
    budget (8-20 PRs per 60h train, §1.2 of the schedule spec) and rode 0.70.0 by drift, same as P0: dense Qwen3 (Qwen3ForCausalLM) has no current proof it runs — last verification is 7 months stale and CI never covers it #3413 would have.

  4. Root mechanism common to both families: tensor_byte_size / quant-type dispatch has no single source of
    truth (ARCH: quant-type dispatch is duplicated across ~30 files with no single source of truth — propose a dedicated consolidation release #3418, EPIC EPIC: PP-QUANT-001 — quant-type dispatch, Phase 0/1 (0.69 train) #3421/EPIC: Inference dispatch & architecture consolidation (dedicated cycle) #3423) — the same class of gap that broke CPU: Qwen3.5 / Qwen3.8 hybrid GGUFs are refused entirely — no fallback exists #3091 on real files is why nothing here has
    a durable regression gate. PP-QUANT-001 M4: tensor_byte_size and ONE refusal site read TRAITS — closes the #3091-reopen / #3341 class for all 35 ids at once #3432 is the concrete fix.

Net effect: Qwen3 was never actually rejected from 0.68.1 — it was never on the manifest to begin with,
because (a) the dense-Qwen3 gap was discovered one day too late, and (b) the one Qwen3.5 commitment that did exist
was quietly undone by a tooling bug and only really diagnosed after 0.68.1 had already shipped.

Scope — P0, must land in 0.68.2

Issue What Depends on
#3413 Dense Qwen3 (Qwen3ForCausalLM) CPU + GPU verification — run real Qwen3-4B/8B GGUF on both, record evidence, add a qwen-story rung none — pure verification
#3091 Qwen3.5/3.8 CPU: stop refusing valid real-world quant types (IQ2_XXS, IQ4_XS, …) #3432
#3432 tensor_byte_size reads TRAITS instead of hand-maintained 11-arm match — unblocks #3091 for all 35 quant ids at once pulled forward from 0.69.0 must-carry
#3090 Qwen3.5/3.8 GPU: implement Gated DeltaNet / SSM inference (currently unimplemented, refuses cleanly) none — new work
#3038 Umbrella: Qwen3.5 SSM/GDN inference support — tracks #3090/#3091 to closure superseded in practice by #3090/#3091; kept open as the closing ticket

Acceptance

🤖 Generated with Claude Code

Activity

  1. added this to the 0.68.2 milestone on Sep 18, 2026
  2. added
    enhancementNew feature or request
    P0Critical priority
    epicEpic — multi-story umbrella
    on Sep 18, 2026
  3. noahgift commented on Sep 18, 2026

    @noahgift
    Member

    Measured 2026-09-18 — Alfredo's "it doesn't work", reproduced on two hosts

    Binaries pinned via . scripts/apr_bin.sh from a worktree at origin/main 4a538dd, built --features cuda. The published crates.io 0.68.1 installed with --features cuda behaves identically on every cell (lambda-vector). Control: Qwen2.5-Coder-1.5B Q4_K_M runs on CUDA cleanly on both hosts, no divergence — so nothing below is a box/driver problem.

    Cell lambda-vector (RTX 4090, sm_89) gx10 (GB10, cc 12.x, aarch64)
    Qwen3-1.7B dense, CPU ✅ correct ✅ correct
    Qwen3-1.7B dense, --gpu ❌ CUDA diverges pos 1: argmax 25 ≠ 198, cosine 0.4021 → wgpu rejected 0.87 → silent CPU fallback ❌ identical: argmax 25 ≠ 198, cosine 0.4079 → silent CPU fallback
    Qwen3-8B dense, --gpu ❌ diverges step 2, cosine 0.437 → CPU fallback ❌ diverges pos 1, cosine 0.8226 → CPU fallback
    Qwen3.5-0.8B hybrid, CPU ✅ correct (run and chat) ✅ correct
    Qwen3.5-0.8B hybrid, --gpu CPU fallback with notice (#3090, by design) same
    apr qa Qwen3-1.7B Golden Output FAIL "GPU output gibberish", Throughput PASS 263 tok/s, GPU Speedup PASS 12.2× Golden Output FAIL "Garbage detected: '�'", Throughput PASS 93.7 tok/s, GPU Speedup PASS 15.1×
    apr qa Qwen3.5-0.8B Capability Match FAIL (old "NEITHER CPU nor GPU" text), 8/12 gates SKIP same

    Finding A — dense Qwen3 on GPU is broken and hidden (#3413, now a reproduced defect, not a process gap). The same wrong argmax (25) at the same position on sm_89 and on Blackwell means a deterministic logic defect in the CUDA qwen3 forward, not precision or silicon. The parity guard catches it and falls back to CPU, so a user sees "works but slow" and a benchmark sees a 12–15× GPU speedup on garbage. apr qa is the only surface that names it, and apr qa on a dense Qwen3 is in no CI lane. Likely locus: the per-head QK RMSNorm that dense Qwen3 adds over Qwen2.5 (crates/aprender-serve/src/cuda/executor/layers/rope.rs::apply_qk_norm, run inside the trueno#243 manual graph, 535 kernels). Not yet root-caused op-by-op.

    Finding B — Qwen3.5 hybrid runs on CPU but the tooling still refuses it (#3091 / #3432). apr run/apr chat are correct on Q4_K_M; apr qa, apr check and the parity refusal still call realizar::gguf::unsupported_architecture_reason and print the pre-#3114 message. The UD-IQ2_XXS / UD-IQ4_XS crash in #3091 is untested here (no such quant on either host).

    Finding C — the contracts that should have caught A are proved against the wrong object. Detail in #3413. In short: qk-norm-v1 FALSIFY-QKN-006 compares CPU to a "GPU reference simulation", KANI-QK_NOR-008 "GPU kernel matches CPU reference" is a Kani harness (Kani cannot execute a CUDA kernel), apr-cpu-vs-gpu-output-parity-v1 has QA gate: not defined, and qwen3-e2e-verification-v1 QW3E-BND-005 asserts coverage(qwen3_contracts) = 1.0 while none of its 7 obligations mentions a backend. All four are green today.

    Acceptance addendum for this train (proposed — falsifiable, per host)

    1. apr qa <dense Qwen3 Q4_K_M> Golden Output PASS on GPU on sm_89 and on GB10, with the argmax-25 mutation (revert the fix) turning it RED. Ships as a CI leg on the GPU runners, not a nightly.
    2. apr run --gpu on dense Qwen3 emits no "diverges … falling back to CPU" line, and the decode runs on the GPU (trace line or throughput ≥ the ACVGOP-INV-010 floor, not "device: GPU" by intent).
    3. apr qa <Qwen3.5 Q4_K_M> Capability Match PASS on CPU (PP-QUANT-001 M4: tensor_byte_size and ONE refusal site read TRAITS — closes the #3091-reopen / #3341 class for all 35 ids at once #3432 collapses qa_capability.rs:98, parity_refusal.rs:157, ptx_map.rs:441 onto the one predicate the loader honours).
    4. Contract co-evolution (CLAUDE.md Rule 7): the falsifiers below land in the same PR as the fix, mutation-verified RED→GREEN.

    Provable-contract work required for full GPU support

    Contract Gap measured Required change
    contracts/qk-norm-v1.yaml FALSIFY-QKN-006 tests a simulation; KANI-QK_NOR-008 is vacuous for a CUDA kernel Replace QKN-006 with a real-kernel falsifier: run apply_qk_norm on device on the Qwen3-1.7B layer-0 q/k of a fixed prompt, compare to RMSNorm::forward per head, cosine ≥ 0.9999. Mark KANI-QK_NOR-008 strategy: not_applicable (CUDA) so L4/L5 stop counting it as proved. Bind it to a #[contract] on apply_qk_norm — today the only bindings are the two loaders and forward_cached.rs (CPU).
    contracts/apr-cpu-vs-gpu-output-parity-v1.yaml qa_gate: not defined; ACVGOP-INV-002 "greedy first-token argmax identical" is exactly what fails and nothing runs it on qwen3 Add a qa_gate that names the apr qa Golden-Output GPU check and a per-architecture model ladder (qwen2, qwen3, qwen3moe, llama, phi3) — one small Q4_K_M per arch. Add FALSIFY-CPU-GPU-015: "dense Qwen3 pos-1 argmax on CUDA == CPU"; mutate by skipping apply_qk_norm.
    contracts/qwen3-e2e-verification-v1.yaml 7 obligations, none backend-aware; QW3E-BND-005 coverage = 1.0 is self-declared Add QW3E-PAR-008 (invariant): forall p in probe positions ≥ 1: argmax_gpu(p) == argmax_cpu(p) ∧ cos ≥ 0.95 per gpu-cpu-parity-gate-v2; add QW3E-DEV-009 (liveness): decode ran on the selected device (no fallback line). Point QW3E-BND-005 at pv coverage output, not a literal 1.0.
    contracts/model-families/qwen3.yaml carries a CUDA training budget only (#3413 item 4) Add the inference verified_on: rows {host, cc, backend, sha, receipt} that qwen3_5.yaml already has, filled by the CI leg in acceptance 1.
    contracts/qwen35-hybrid-forward-v1.yaml, qwen35-e2e-verification-v1.yaml fine for CPU; nothing for the GPU GDN path For #3090: add QHF-GPU-008 "GDN recurrent state on device equals CPU state per layer per token (L∞ ≤ 1e-3 fp32)" and QE2E-DEV-008 "no [qwen35: … runs on the CPU] notice when --gpu is honoured".
    contracts/quant-dispatch-v1.yaml (#3432, unlanded) second refusal site FALSIFY: apr qa on a qwen35 GGUF must not print "NEITHER the CPU nor the GPU backend".

    SHACL / ONT-001 rows required (v4.4, entity: + shape: blocks in the YAML; .ttl is a generated export)

    Today 0 of 1818 contracts carry entity:/shape:, and the validator (ontology/shapes.rs, ONT-4b) has not landed in aprender (#3269, 0.70.0). These rows are therefore authored now, gated when ONT-4b lands — they must not be claimed as passing before that.

    1. contracts/model-qwen3-1p7b-q4km-gguf.yaml — entity: {type: gguf, ref: <sha256>}, shape.closed: true, properties: model:arch in [qwen3]; model:tensor with pattern "^blk\\.[0-9]+\\.attn_q_norm\\.weight$" minCount = model:layerCount, same for attn_k_norm; model:headDim = 128; model:parityReceipt minCount 1, resolves: receipt per backend (cuda-sm89, cuda-cc120, cpu). The missing receipt is what makes today's state a SHACL violation rather than an unanchored claim.
    2. contracts/model-qwen35-0p8b-q4km-gguf.yaml — same shape family with model:arch in [qwen35], model:tensor pattern ^blk\.[0-9]+\.ssm_a$ minCount ≥ 1, model:hybridLayout (attention/GDN interleave) present, model:parityReceipt for cpu now and cuda-* when GPU: implement Gated DeltaNet / SSM inference for Qwen3.5 / Qwen3.8 hybrid GGUFs #3090 lands.
    3. Class-level shape on type: gguf (SHACL targetClass, R-19 inherits down): every GGUF a release claims to support carries ≥ 1 model:parityReceipt whose backend is in the release's declared backend set. This is the shape that turns the Docs: publish a GPU-vs-CPU model architecture support table (no user-facing doc exists today) #3077 support table from prose into a validated projection.
    4. Code-entity shapes on apply_qk_norm and the GDN forward: entity: {type: code, ref: realizar::cuda::executor::layers::rope::apply_qk_norm}, ont:binds minCount 1, resolves: symbol (so the contract-to-kernel binding above becomes a census fact), ont:validUnder class ont:World naming {cc: [89, 120]}.
    5. Positive control (R-3 pc_shape): a planted GGUF projection with attn_q_norm absent must be rejected by shape 1; a planted receipt with backend: cpu only must fail shape 3 for a release that declares cuda. Without these the shapes are theater (memory: "a gate never green on its own target is not a gate").

    Ledger: probe logs on lambda-vector under the session scratchpad, on gx10 under ~/probe/; worktrees ~/src/aprender-qwen3-probe on both hosts at 4a538dd.

  4. noahgift commented on Sep 18, 2026

    @noahgift
    Member

    Process piece landed as a PR: #3479 — model capability ladder. contracts/model-capability-ladder-v1.yaml + scripts/model_ladder.sh (per-host runner) + scripts/check_model_ladder.sh (declared dogfood gate, 13-case self-test) + a T-2 Models row in APR-RELEASE-001 §4. Receipts at 4a538dd from lambda and gx10 are committed and the gate is RED on both (qwen3/cuda fell back + golden gibberish; qwen35 capability_match refused) with the control rung green — the 0.68.2 train cannot cut until #3413 and #3432 turn those rows green. The unit of release evidence is now (architecture, backend, silicon), never "a model ran".

  5. noahgift commented on Sep 18, 2026

    @noahgift
    Member

    ✅ #3479 merged to main 2026-09-18T12:46Z — the T-2 Models row is live: scripts/check_model_ladder.sh is a declared dogfood gate and is RED on both hosts until #3413 (qwen3/cuda) and #3432 (qwen35 capability_match) land. Before the 0.68.2 cut: rerun bash scripts/model_ladder.sh on lambda and gx10 at the cut sha and commit the receipts.

  6. noahgift commented on Sep 18, 2026

    @noahgift
    Member

    Coordination state 2026-09-18 ~13:15Z (assistant session). Fix branch PMAT-3477-qwen3-cuda-qk-norm (head 83f85a1, unpushed) measured on lambda via the ladder: qwen3-1.7b apr qa golden_output on GPU now PASSES (qk-norm-in-graph works) but apr run --gpu falls back on it AND on the qwen2.5 control — the new batched-prefill F2 validator rejects on cosine alone (one case argmax 29 == 29 still fell back); it must apply gpu-cpu-parity-gate-v2's per-position rule. qwen3-8b golden GPU still gibberish at that head. qwen35: two more refusal sites beyond the loader — apr qa capability_match (ladder side fixed by #3481: cpu-only rungs skip it) and apr qa tensor_contract via rosetta.validate → transformer.rs:91, a hard exit-5 abort with no report. Side PRs: #3480 contracts co-evolution (qk-norm-v1/parity/qwen3-e2e, 3/3 quorum, armed), #3481 ladder cpu-only rung. T-2 preflight dogfood on main e6dad31 in progress: red so far = model-ladder (by design), version-unpublished (pre-bump), and make coverage-check (cause not yet read).

  7. noahgift commented on Sep 18, 2026

    @noahgift
    Member

    Preview receipt on the fix branch head 8edf3bf, gx10 (GB10 cc 12.1, apr --version = 8edf3bf): model ladder 4/4 GREEN — qwen2-1.5b cpu+cuda, qwen3-1.7b cpu+cuda, qwen3-8b cpu+cuda, qwen35-0.8b cpu. First green four-rung receipt since the gate existed. Lambda counterpart pending the final head (#3484 will fold #3480/#3482/#3485 per the one-queue-entry decision). Not the counted run: that happens once on the final head on both hosts.

  8. added a commit that references this issue on Sep 18, 2026
  9. alfredodeza commented on Sep 18, 2026

    @alfredodeza
    ContributorAuthor

    Reopened — operator ruling (2026-09-18): GPU (#3090) is a MUST for this interrupt, not an optional non-goal

    This epic auto-closed 2026-09-18T12:45Z as a side effect of a merged PR's Closes #3477
    text, not from a check against the acceptance list below. At that moment #3090 had not
    shipped and had not yet even received its "dated non-goal" disposition — that comment
    landed on #3090 afterward. No one signed off that closing was correct. Reopening to
    replace the mechanical close with an actual decision.

    The acceptance line that created the ambiguity:

    #3090: Qwen3.5/3.8 GPU inference lands **or is scoped down to a dated, explicit non-goal with a reason**

    That "or" was written into this epic by the operator and is being read literally as
    permission to defer GPU. It is withdrawn as of this comment: for this interrupt, GPU
    is required, not optional.
    The reason to run this as an interrupt in the first place —
    jumping Qwen3 ahead of the normal train queue under §1.1 of the schedule spec — was that
    Qwen3 GPU support has been drifting for trains with no floor. Letting it defer again inside
    the one release built specifically to stop that drift defeats the interrupt's purpose.

    Acceptance checklist — measured state, 2026-09-18 ~15:30Z

    Item State
    #3413 dense Qwen3 CPU+GPU evidence Root-caused (two defects: manual CUDA graph drops apply_qk_norm from replay; batched prefill applies no QK-norm at all). Fix on branch PMAT-3477-qwen3-cuda-qk-norm, open PR #3484, still OPEN, not merged — CI run in progress at time of writing.
    #3432 quant-dispatch (tensor_byte_size reads TRAITS) OPEN, milestone 0.68.2, not yet merged.
    #3091 CPU real-quant fix (UD-IQ2_XXS/UD-IQ4_XS) OPEN, milestone 0.68.2, depends on #3432.
    #3090 GPU GDN/SSM device path OPEN, milestone 0.69.0 (moved off 0.68.2 today). No branch or PR exists — this is unstarted net-new kernel work (conv1d, recurrent state, gated delta rule), not a fix, and it also depends on #3413/#3484 landing first (shared attention path).
    Closing comment (what shipped / what slipped, evidence paths, tag instant) Never written — the issue auto-closed before this point was reached.

    Ruling

    1. GPU: implement Gated DeltaNet / SSM inference for Qwen3.5 / Qwen3.8 hybrid GGUFs #3090 moves back to milestone 0.68.2, P0, no non-goal exception.
    2. Sequencing is unavoidable: PMAT-3477: 0.68.2 — Qwen3 CUDA QK-norm restored in the graph and the batched prefill, FP8 prefill gated for QK-norm models, F2 guard judges the real prefill path; Qwen3.5 real IQ files load and run; apr qa certifies CPU-only qwen35 (#3413, #3432, #3091) #3484 (→ P0: dense Qwen3 (Qwen3ForCausalLM) has no current proof it runs — last verification is 7 months stale and CI never covers it #3413) must merge before GDN device work can start, since GDN's attention layers share the CUDA path PMAT-3477: 0.68.2 — Qwen3 CUDA QK-norm restored in the graph and the batched prefill, FP8 prefill gated for QK-norm models, F2 guard judges the real prefill path; Qwen3.5 real IQ files load and run; apr qa certifies CPU-only qwen35 (#3413, #3432, #3091) #3484 fixes. Land PMAT-3477: 0.68.2 — Qwen3 CUDA QK-norm restored in the graph and the batched prefill, FP8 prefill gated for QK-norm models, F2 guard judges the real prefill path; Qwen3.5 real IQ files load and run; apr qa certifies CPU-only qwen35 (#3413, #3432, #3091) #3484 first — do not block it further waiting on GDN.
    3. GDN device implementation starts immediately once PMAT-3477: 0.68.2 — Qwen3 CUDA QK-norm restored in the graph and the batched prefill, FP8 prefill gated for QK-norm models, F2 guard judges the real prefill path; Qwen3.5 real IQ files load and run; apr qa certifies CPU-only qwen35 (#3413, #3432, #3091) #3484 is merged: new branch off main, scoped explicitly to QHF-GPU-008 / QE2E-DEV-008 from the contract table already written in this epic's earlier comment, apr qa Golden Output GPU PASS on sm_89 and GB10 as the gate.
    4. This epic stays open — no auto-close — until all five checklist rows above are checked, including the closing comment. If GitHub auto-closes it again via a PR's Closes #3477, treat that as a bug to fix (drop the closing keyword from PR bodies referencing this epic; use Refs #3477 instead), not as a real close.
    5. If, after PMAT-3477: 0.68.2 — Qwen3 CUDA QK-norm restored in the graph and the batched prefill, FP8 prefill gated for QK-norm models, F2 guard judges the real prefill path; Qwen3.5 real IQ files load and run; apr qa certifies CPU-only qwen35 (#3413, #3432, #3091) #3484 lands, the GDN kernel work proves to need materially longer than the interrupt's timebox, that is a new finding to bring back here explicitly — not a silent re-slip.

    🤖 Generated with Claude Code

  10. 27 remaining items

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0Critical priorityenhancementNew feature or requestepicEpic — multi-story umbrella

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions