Skip to content

gpu-gates, model: the Qwen3.6 fixture tier - #128

Merged
midagedev merged 28 commits into
mainfrom
w3q35f2f
Oct 14, 2026
Merged

midagedev merged 28 commits into
mainfrom
w3q35f2f

Conversation

@midagedev

@midagedev midagedev commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Qwen3.6 (qwen35moe) fixture tier: F1 + f2a–f2f. Target 0.3.3. B1 (the 8-bit/32 gemv for Q8_0 sites) is split out of this PR; see "Not in this PR".

What's in it

  • A fixture family for Qwen3.6 (model/refset/tools), its unjoined variants, the refset-publish licence row (Apache 2.0) and the fixture dump card.
  • The Qwen3.6 e2e, moe and attn gates run on the fixture tier clause by clause (Tag::Oracle on the real sets, Tag::FixtureOracle on the fixture's), with the shape read from the file's header.
  • Shingler folds: flip::picks_its_top owns the router-pick check and its margin (both e2e gates call it); q36_fixture reads the header's layer kind (qwen35moe::hparams::Kind), with a check-arch allowlist row and its reason; gate_qwen35moe_e2e refuses a saved --model-file or --model-file=PATH by name (the real tier reads BLOOMERY_REF_MODEL); gate_qwen35moe_moe reads ik's sets from refset::arch::qwen35moe::{IK, fixture::IK} (same order: batch, step-4, d1k) instead of its own lists.
  • No crates/gpu/src change and no unsafe-ratchet move. Lint ratchet stays 41.

Evidence

  • Head bcd8f7e on main 3928520, 25 commits, 32 files, +3648/−604 (44dee347 rebased; main's 98eb61d moved the e2e bin's greedy steps, so the fixture gates ran again on bcd8f7e).
  • Mac static of 44dee3476084: fmt-check rc 0, lint rc 0, combos rc 0, check-arch rc 0, check-comments rc 0, check-defaults rc 0, check-levers rc 0, check-loads rc 0, check-ptx-names rc 0, check-records rc 0, check-recipes rc 0, check-rustflags rc 0, check-unsafe rc 0, check-waits rc 0; ok, remote rc 0, wall 111 s
  • Fixture GPU runs (RTX A6000, driver 615.71.09, TIER=fixture): on 44dee347 e2e, moe, attn each exit 0 (jobs s215, s221, s232); on bcd8f7e the three in one job (s248), gate_qwen35moe_e2e: PASS, gate_qwen35moe_moe: PASS, gate_qwen35moe_attn: PASS; no : FAIL line in either

Not in this PR

  • B1 is parked for a v2. Its A/B against this PR's base 1676adb (A6000, Qwen3.6-35B-A3B-UD-Q4_K_M, 6 rounds, depth 6) measured decode 152.73 → 149.31 tok/s, −2.24 % ± 0.49 %, outside docs/cards/q35-q8rule.card's 2.0 % margin, and pp @ P = 6 337.62 → 281.40 tok/s (−16.7 %). Its FAIL-first showed the 8-bit/32 rule is owned by the e2e's host-rule clause, which this PR no longer pins.
  • Verify, dspark and the real-tier e2e were not part of these runs.

Prepared by the bloomery assistant on behalf of @midagedev.

@github-actions github-actions Bot added documentation Improvements or additions to documentation gpu The card engine: crates/gpu*, crates/app host Host crates: qdot, threads, engram, engram-lab, runtime, sampler, levers, decision, vision, refset model The CPU engine and model files: crates/model, models, hf, gguf, tokenizer, jinja needs-gpu Changes code a card must check, and carries no gate run on a card placement Placement planning: crates/placement tools Tools, recipes, CI and the toolchain: tools/, justfile, .github/, Cargo labels Oct 13, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: MUST_FIX: Device quantization and host reference quantization each duplicate an existing rule.

Must fix

1. Share the device's 32-value quantization policy

crates/gpu/src/q8f32.rs:1142 copies the octet maximum, zero-block scale and non-finite refusal from crates/gpu/src/gemm/act32.rs:385. Both already call the shared rounding core, q8_quad. The duplicated block policy is a kind B defect under R14; both paths promise identical codes.
Cost: share it before GEMV and GEMM fixes diverge.

Add this core to crates/gpu/src/cores.rs:

/// Quantize one lane's quad; all 32 lanes call together.
#[inline(always)]
pub(crate) fn quant32_quad(v: [f32; 4], lane: usize) -> (u32, i32, f32, bool) {
    use cuda_device::warp;
    let bad = warp::ballot(!crate::fault::quad_finite(v));
    let refused = (bad >> (8 * (lane >> 3))) & 0xff != 0;
    let mut amax = v[0].abs().max(v[1].abs()).max(v[2].abs()).max(v[3].abs());
    amax = amax.max(warp::shuffle_xor_f32(amax, 1));
    amax = amax.max(warp::shuffle_xor_f32(amax, 2));
    amax = amax.max(warp::shuffle_xor_f32(amax, 4));
    let d = if amax > 0.0 { amax / 127.0 } else { 1.0 };
    let (word, sum) = q8_quad(v, d);
    if refused { (0, 0, f32::NAN, true) } else { (word, sum, d, false) }
}

Replace q32_quad with:

#[inline(always)]
fn q32_quad(v: [f32; 4], lane: usize) -> (u32, f32, bool) {
    let (word, _, d, refused) = crate::cores::quant32_quad(v, lane);
    (word, d, refused)
}

In quant32_group, replace the computation from let bad through its final refusal tuple with:

let (word, quad, dv, refused) = crate::cores::quant32_quad(v, lane);
let mut sum = quad;
sum += warp::shuffle_xor(sum as u32, 1) as i32;
sum += warp::shuffle_xor(sum as u32, 2) as i32;
sum += warp::shuffle_xor(sum as u32, 4) as i32;

Remove the unused q8_quad and quad_finite imports from q8f32. Check identical PTX for this extraction.

2. Share the host quantization reference

crates/gpu-gates/src/bin/gate_qwen4exp_mtp.rs:774 and crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs:2655 both add the same scalar rounding rule already implemented by crates/gpu-gates/src/act_rule.rs:478. Only the block width and input conversion differ. This is a kind B defect under R14.
Cost: share it before the gates' reference rules diverge.

Generalize the existing implementation in act_rule:

/// Reconstruct q8 values with an f32 scale per block, exactly in f64.
#[must_use]
pub fn q8_exact(x: &[f32], block: usize) -> Vec<f64> {
    assert!(block > 0, "q8 block must contain values");
    x.chunks(block).flat_map(|b| {
        let amax = b.iter().fold(0.0f32, |a, &v| a.max(v.abs()));
        let d = if amax > 0.0 { amax / 127.0 } else { 1.0 };
        b.iter().map(move |&v|
            f64::from((v / d).round().clamp(-127.0, 127.0)) * f64::from(d))
    }).collect()
}

#[must_use]
pub fn q8_1_exact(x: &[f32]) -> Vec<f64> {
    q8_exact(x, Q8_1_BLOCK)
}

Keep q8_1_exact's existing documentation. Replace q8_rule32 with:

fn q8_rule32(x: &[f64]) -> Vec<f64> {
    let x: Vec<f32> = x.iter().map(|&v| v as f32).collect();
    bloomery_gpu_gates::act_rule::q8_exact(&x, 32)
}

Replace the e2e clause's let mut q and quantization loop with:

let q = bloomery_gpu_gates::act_rule::q8_exact(&xn, 32);

Non-blocking

⚠ Attach the occupancy and precision proof: docs/cards/q35-q8rule.card:21 reports 55 versus 40 registers and four versus six blocks per SM. Staging adds 2 * ceil(k/2048) barriers per block (q8f32.rs:1830); the UD decode uses 250 affected launches/token [derived, Qwen3.6 UD-Q4_K_M, A6000, n=1, depth 6]. AGENTS.md's occupancy and precision classes require numeric gates and one timed A/B. Provide the planned A/B and e2e output for unjoined and unjoinedq6; the base fixture skips the host-rule assertion at gate_qwen35moe_e2e.rs:2598.

Checked

Duplicates: both findings confirmed against the existing owners in the base tree; all cited new copies are added by the diff.
Fixes: the device extraction preserves refused octets as zero codes, zero sums and NaN scales; the host extraction preserves f32 rounding and f64 reconstruction.
Performance: staging barriers, fixture type maps and the host-rule skip read.
Escape catalog: C1–C4 do not apply to the retained findings. No ledger lessons are in force.

Notes

The PR body gives gate exit summaries, but no new-clause FAIL-first output, nvidia-smi line or gate commands with output. Validation was static; no gates or timings were executed.

Card check: needs-gpu. This pull request changes crates/gpu-gates/src/bin/gate_qwen35moe_attn.rs, crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs, crates/gpu-gates/src/bin/gate_qwen35moe_moe.rs … and carries no gate run on a card (CONTRIBUTING's evidence list); a maintainer adds gpu-verified after one.

🔨 Shingler, bloomery's review bot: approves a clean pull request and requests changes on a must-fix (how it works)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: MUST_FIX: The change duplicates a shared fixture policy and misstates the Q8_0 head's activation form.

Must fix

1. Keep the no-table policy in one owner

crates/model/src/arch/qwen35moe/fixture/qwen35moe.rs:173 copies crates/model/src/fixture/spec.rs:247. The dimensions policy also repeats in crates/model/src/arch/glm5next/fixture.rs:378. Both no-table implementations also use the same key refusal. These are the same no-table policy, not architecture facts (kind B, R14).
Putting the defaults in the common trait now prevents each fixture from owning another copy.
Replace Tables with this definition. Remove key and dims from Gate and NoTables, and the identical dims override from MapTables. Remove the now-unused meta import from arch/qwen35moe/fixture/qwen35moe.rs:

pub trait Tables {
    /// Resolve a table key; families without tables reject it.
    fn key(&self, key: &str, _suffix: &str, _v: &Value) -> Result<Value, FixtureError> {
        Err(meta(key, "is a table key of a family with no tables"))
    }

    /// Return replacement tensor dimensions; None keeps the source dimensions.
    fn dims(
        &self,
        _name: &str,
        _layer: Option<usize>,
        _leaf: &str,
        _dims: &[u64],
    ) -> Result<Option<Vec<u64>>, FixtureError> {
        Ok(None)
    }

    /// Return the lines printed with a plan.
    fn lines(&self) -> Vec<String>;
}

2. Correct the Q8_0 head comment

crates/gpu/src/arch/qwen3moe/body38.rs:1639 says the head quantizes activations into blocks of 32. crates/gpu/src/head.rs:332 still calls enqueue_q8_0_gemv, which multiplies f32 activations directly (kind B).
Correcting this now prevents later numeric changes from relying on the wrong head contract.
Replace the two added comment lines with:

        // as the Q8_0 form's, whose head gemv reads f32 rows directly.

Non-blocking

⚠ Supply the kernel change's measured proof: crates/gpu/src/q8f32.rs:1817.
For single-column decode, each block loads 4 × k f32 activation bytes and executes 2 × ceil(k/2048) barriers [derived]. The prediction card derives 250 affected launches/token for Qwen3.6 UD-Q4_K_M and records occupancy changing from six to four blocks/SM on sm_86 (A6000).
This changes occupancy and float precision. Attach the owning gates, PTX comparison and timed A/B required by the base AGENTS.md change-class table. The PR body says the A/B was not run.

⚠ Cover the new gemv's fault path directly: crates/gpu/src/q8f32.rs:1812.
Deleting this fault.raise, or its multi-column counterpart, leaves the new finite-input fixture assertions unchanged.
Add a direct gate for NaN/infinity, the fault word and NaN outputs, with partial and chunk-boundary widths and both column arms. Attach its FAIL-first output.

Checked

Duplicates: the cited Gate, NoTables and MapTables methods were read; the two dryer dimension pairs share the same policy.
Complexity: both new Q8 kernels retain their staging, dot and store loops; l0_host_rule normalizes, quantizes and checks the host dot.
Silent failure: quant32_quad returns a NaN scale on refusal; both new gemvs raise QuantColumn and propagate NaN outputs.
Tests: the new host-rule clause checks finite fixture outputs; deleting either new fault.raise leaves those assertions unchanged. No direct gate calls the new q32 launcher.
Gates: the base contract requires owning gates and a timed A/B for occupancy or precision changes.
Escape catalog and ledger: C1–C4 do not apply to these findings; no lessons are in force.

Notes

Validation was static. GPU gates were not run here.

Card check: needs-gpu. This pull request changes crates/gpu-gates/src/act_rule.rs, crates/gpu-gates/src/bin/gate_qwen35moe_attn.rs, crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs … and carries no gate run on a card (CONTRIBUTING's evidence list); a maintainer adds gpu-verified after one.

🔨 Shingler, bloomery's review bot: approves a clean pull request and requests changes on a must-fix (how it works)

Comment thread crates/gpu/src/arch/qwen3moe/body38.rs Outdated
Comment on lines +1639 to +1640
// as the Q8_0 form's, which quantizes the f32 rows into blocks of 32
// inside its gemv.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The Q8_0 head calls enqueue_q8_0_gemv at head.rs:332; that kernel reads f32 activations directly.

Suggested change
// as the Q8_0 form's, which quantizes the f32 rows into blocks of 32
// inside its gemv.
// as the Q8_0 form's, whose head gemv reads f32 rows directly.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since the last review:

Shared no-table policy: fixed at crates/model/src/fixture/spec.rs:219. Tables owns the defaults; the duplicate overrides are removed.

Q8_0 head comment: fixed at crates/gpu/src/arch/qwen3moe/body38.rs:1639. It now describes the direct f32 activation reads used by Head::enqueue.

Verdict: CLEAN: Both earlier must-fix items are fixed, and the incremental diff introduces no merge-blocking defect.

Non-blocking

⚠ Kernel performance proof: still open at crates/gpu/src/q8f32.rs:1817.
Single-column decode loads 4 × k f32 activation bytes per block and executes 2 × ceil(k/2048) barriers [derived]. The prediction card describes 250 affected launches/token for Qwen3.6 UD-Q4_K_M and six-to-four blocks/SM on sm_86 (A6000).
The occupancy/geometry and precision change classes require owning gates and a timed A/B. Attach those results and the PTX comparison before later tuning adds another variable. The body reports fixture gate exits but explicitly excludes the A/B.

⚠ Direct fault-path coverage: still open at crates/gpu/src/q8f32.rs:1812.
Deleting either new gemv's fault.raise(QuantColumn) leaves the finite-input fixture assertions unchanged.
Add a direct gate asserting the fault word and NaN outputs for NaN/infinity, partial widths and chunk boundaries, in both column arms. Include FAIL-first output to isolate recurrence before more callers use these kernels.

Checked

Duplicates: 0 new APIs; 2 default methods relocated and checked. All 3 dryer pairs read: architecture-specific delegates, not duplicate policies.
Complexity: crapper's 15 flagged functions are outside this incremental diff; no new branch or complexity growth.
Silent failure: 2 relocated defaults followed. Unsupported table keys propagate a named Metadata error; None dimensions preserve the source dimensions.
Tests: no new or changed tests. The earlier fault.raise mutant remains undetected by the finite-input assertions.
Gates: no pins, bands, clauses or tests changed in this increment.
Adversarial: table-key refusal still propagates; PLE and Engram dimension overrides remain active; the Q8_0 head still reads f32 rows.
Performance and unsafe: this increment changes fixture planning and a comment; no decode work or unsafe operations added.
Escape catalog: C1–C4 match no incremental hunk. No ledger lessons are in force.

Notes

Validation was static; GPU gates were not run here. The reported A6000 fixture run lacks the nvidia-smi line and exact just gate commands with output for the reviewed commits, so gpu.json records evidence=false.

Card check: needs-gpu. This pull request changes crates/gpu-gates/src/act_rule.rs, crates/gpu-gates/src/bin/gate_qwen35moe_attn.rs, crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs … and carries no gate run on a card (CONTRIBUTING's evidence list); a maintainer adds gpu-verified after one.

Not approved: this pull request changes AGENTS.md, which changes the bot, CI or the toolchain and needs a person's approval.

🔨 Shingler, bloomery's review bot: approves a clean pull request and requests changes on a must-fix (how it works)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: MUST_FIX: the added gate comments name a nonexistent constant and retain prohibited test history.

Must fix

1. Name the existing probe band

crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs:1610 FREE_PROBE_EXACT has no declaration. The whole-card and placed no-host callers pass FREE_PROBE_CARD. This is a kind B defect: the comment directs readers to a nonexistent pin.
Correct the reference now so later gate changes use the actual band.

    /// within `probe_rel` ([`FREE_PROBE_CARD`], [`FREE_PROBE_HOST`]). The real

2. Remove test history

crates/model/tests/fixture.rs:2856 The added comment records the first generation's measured byte count. AGENTS.md's comment contract excludes history; check-comments.sh does not catch this byte count in tests. This is a kind B defect with a mechanical fix. Remove the history while adding the gate so its comment states the current contract. Move the measurement to the commit message and replace the four-line comment with:

/// Contract 20: the recorded card budget makes the written file's plan hold
/// half of the experts on every layer, none of which is host-only. The gate
/// holds the budget's round trip through the header.

Non-blocking

⚠ Supply the decode A/B: crates/gpu/src/site.rs:280. The replacement adds 2 * ceil(k/2048) block barriers per block per Q8 site [derived]. The prediction card counts 250 such sites per Qwen3.6 UD decode token and reports sm_86 occupancy falling from six to four blocks per SM. AGENTS classifies this as occupancy/geometry and precision changes. Supply the owning gates, the gate-gpu-e2e count pin, and the same-lease just ab-decode output on the contributor's card.

⚠ Supply FAIL-first output: crates/gpu-gates/src/bin/gate_p1.rs:401. The body reports green fixture runs without red mutant runs. Drop fault.raise at q8f32.rs:1812, then restore the old f32-row gemv at site.rs:280. Supply red just gate-gpu-p1 and unjoined-fixture just gate-gpu-qwen35moe-e2e output, followed by green head runs. This establishes recurrence prevention before relying on the new checks.

Checked

Duplicates: confirmed the issue candidate's local helper repeats the base tree's shared q8_1 quantization policy; the local helper is outside the diff.
Complexity: no standalone complexity finding remains.
Silent failure: the planted-input gate compares the returned fault with QuantColumn.
Tests: inspected the dropped-fault and activation-form assertions; hardware mutants were not run.
Gates: verified the probe constants and their whole-card, placed no-host and host callers.
Escape catalog: no C1-C4 exception applies to these findings. No ledger lessons are active.

Notes

GPU evidence lacks the nvidia-smi line and gate commands with output. CUDA and model gates were not run during this review.

Card check: needs-gpu. This pull request changes crates/gpu-gates/src/act_rule.rs, crates/gpu-gates/src/bin/gate_p1.rs, crates/gpu-gates/src/bin/gate_qwen35moe_attn.rs … and carries no gate run on a card (CONTRIBUTING's evidence list); a maintainer adds gpu-verified after one.

🔨 Shingler, bloomery's review bot: approves a clean pull request and requests changes on a must-fix (how it works)

Filed outside this PR: #160

/// a flip is no near tie, when a site's ids are not the top set of its own
/// logits, when the held rows fall below [`FREE_COVER_MIN`] of the
/// rows ik kept, or when the probe does not replay the chain's layers
/// within `probe_rel` ([`FREE_PROBE_EXACT`], [`FREE_PROBE_HOST`]). The real

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

FREE_PROBE_EXACT does not exist; these callers use FREE_PROBE_CARD.

Suggested change
/// within `probe_rel` ([`FREE_PROBE_EXACT`], [`FREE_PROBE_HOST`]). The real
/// within `probe_rel` ([`FREE_PROBE_CARD`], [`FREE_PROBE_HOST`]). The real

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: MUST_FIX: the router check accepts invalid selections, and new code duplicates existing owners.

Must fix

1. Validate distinct, bounded router picks through one owner

crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs:1521: route_probe passes the device ids to this check without distinctness validation. Eight copies of the highest expert pass low >= high; if that expert is in ik's set, the subset comparison skips the flip. inconsistent stays empty, so free accepts the router self-check at line 1748. Out-of-range ids also pass the self-check when their NaNs are discarded by f32::min.
crates/gpu-gates/src/bin/gate_qwen4exp_e2e.rs:1110 already checks distinctness and bounds. Share it (R14) before more gates adopt this copy.

Move the checker to flip.rs, replacing the private Qwen3.8 checker:

/// Whether ids are n_used distinct experts at the top of finite logits.
pub fn picks_its_top(ids: &[u32], logits: &[f32], n_used: usize) -> bool {
    if ids.len() != n_used
        || logits.iter().any(|v| !v.is_finite())
        || ids.iter().enumerate().any(|(i, &e)| {
            e as usize >= logits.len() || ids[..i].contains(&e)
        })
    {
        return false;
    }
    let low = ids.iter().map(|&e| logits[e as usize])
        .fold(f32::INFINITY, f32::min);
    logits.iter().enumerate().all(|(e, &v)| {
        ids.iter().any(|&id| id as usize == e) || v <= low
    })
}

Replace the new low/high calculation with:

let consistent = flip::picks_its_top(ours, ours_lg, n_used);

Replace Qwen3.8's private-check call with:

if r.logits.len() != N_EXPERT
    || !flip::picks_its_top(&r.ids, &r.logits, N_USED)
{
    inconsistent.push((l, t));
}

Delete both old implementations.

2. Reuse the router margin owner

crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs:1534 repeats the lowest-kept/highest-left calculation in crates/gpu-gates/src/flip.rs:115 and in forced. Reuse this router fact's owner now (R14), so later changes cannot diverge.

Replace each local min/max calculation in route_sites and forced with:

let margin = flip::margin(lg, theirs) as f32;

Replace min_in - max_out with margin in the struct, comparisons and print. The cast preserves f32 rounding.

3. Use the header's layer kind directly

crates/gpu-gates/src/q36_fixture.rs:62 duplicates crates/model/src/arch/qwen35moe/hparams.rs:76. The exhaustive conversion adds no invariant. Remove the second type and conversion before another caller adopts them (R14).

Replace the new enum with:

pub use model::arch::qwen35moe::hparams::Kind;

Replace Kind::Delta with Kind::DeltaRule throughout q36_fixture.rs. In header_shape, replace the mapped kinds declaration with:

let kinds = hp.kinds.clone();

In the gate, remove Kind from the q36_fixture import and Kind as HeaderKind from the hparams import.

4. Remove the tuple-only quantizer wrapper

crates/gpu/src/q8f32.rs:1140 only calls cores::quant32_quad and drops one tuple field. Remove this extra function now; both callers already have the common owner.

Delete q32_quad and use this at both staging sites:

let (word, _, dv, refused) = crate::cores::quant32_quad(v, lane);

Non-blocking

⚠ Q8_0 dispatch proof: crates/gpu/src/q8f32.rs:1731 adds 2 × ceil(K/2048) block barriers per launch [derived], across the card's stated 250 affected launches per UD decode token. Reported occupancy falls from six to four blocks per SM. The “occupancy / geometry” and “float sum order or precision” classes require the owning gates, count pin and timed A/B. Provide these outputs, FAIL-first, the nvidia-smi line, both commits and model file; the fixture-run summary does not supply that evidence.

Optimization headroom

  • D, More at once: crates/gpu/src/q8f32.rs:1746, sharing staging across 16 output rows instead of eight halves activation reads from 4K × ceil(N/8) to 4K × ceil(N/16) bytes per site/token [derived]. Check register pressure and timing before adopting it.

Checked

Duplicates: the router checker, margin and layer-kind owners, and the tuple-only wrapper are confirmed.
Complexity: the cited changes were read; no kernel split is requested.
Silent failure: the router NaN default reaches the consistency verdict.
Tests: the repeated-highest-id case passes both existing comparisons; the proposed checker rejects it. CUDA gates were not run.
Gates: the fixture free-clause bounds carry dated PIN reasons.
Escape catalog: none of the retained findings matches C1–C4. No ledger lessons are in force.

Card check: needs-gpu. This pull request changes crates/gpu-gates/src/act_rule.rs, crates/gpu-gates/src/bin/gate_p1.rs, crates/gpu-gates/src/bin/gate_qwen35moe_attn.rs … and carries no gate run on a card (CONTRIBUTING's evidence list); a maintainer adds gpu-verified after one.

🔨 Shingler, bloomery's review bot: approves a clean pull request and requests changes on a must-fix (how it works)

Comment thread crates/gpu/src/q8f32.rs Outdated
} else {
[0.0; 4]
};
let (word, dv, refused) = q32_quad(v, lane);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Use the existing quantizer directly and discard its unused sum field; remove q32_quad.

Suggested change
let (word, dv, refused) = q32_quad(v, lane);
let (word, _, dv, refused) = crate::cores::quant32_quad(v, lane);

Comment thread crates/gpu/src/q8f32.rs Outdated
} else {
[0.0; 4]
};
let (word, dv, refused) = q32_quad(v, lane);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Use the existing quantizer directly and discard its unused sum field; remove q32_quad.

Suggested change
let (word, dv, refused) = q32_quad(v, lane);
let (word, _, dv, refused) = crate::cores::quant32_quad(v, lane);

@midagedev midagedev changed the title gpu-gates, gpu, model: the Qwen3.6 fixture tier, and Q8_0 sites' gemv reads 8-bit blocks of 32 gpu-gates, model: the Qwen3.6 fixture tier Oct 14, 2026
@github-actions github-actions Bot removed the documentation Improvements or additions to documentation label Oct 14, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: MUST_FIX: The gate can check the wrong model, and the set inventories duplicate existing owners.

Must fix

1. Reject the removed model-file option (kind A)

crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs:5262: In the real tier with BLOOMERY_REF_MODEL naming the oracle model, a saved command with --model-file OTHER now ignores OTHER. init() reads ref_model_path(), text_file() is false, and no remaining argument check rejects the flag. The gate opens the configured oracle model and can report PASS without checking OTHER.
Rejecting the retired flag now prevents saved verification commands from producing evidence for the wrong file.

Insert before init():

if std::env::args().skip(1).any(|a| a == "--model-file") {
    return Err(
        "--model-file is no longer supported; select the model through BLOOMERY_REF_MODEL"
            .into(),
    );
}

2. Use the families' set inventories (kind B, R14)

crates/gpu-gates/src/bin/gate_qwen35moe_moe.rs:221 repeats the ordered list owned by crates/refset/src/arch/qwen35moe/fixture.rs:27. REAL_SETS at line 220 likewise repeats crates/refset/src/arch/qwen35moe/mod.rs:44. Both contain exactly the same three sets in the same order as their owners.
Reusing the existing fields removes two constants before more cases depend on them.

Delete REAL_SETS and FIXTURE_SETS. Replace these functions:

fn open_sets(what: &str) -> Result<Vec<(&'static str, RefManifest)>, GateError> {
    let fixture = Tier::from_env()? == Tier::Fixture;
    let family = if fixture { &fx::IK } else { &oracle::IK };
    family.sets.iter().map(|&name| {
        let path = if fixture {
            tier::fixture_set(what, family, name)?
        } else {
            data_dir().join(name)
        };
        Ok((name, RefManifest::open(&path, family)?))
    }).collect()
}

fn ik_clause(what: &str, sets: usize) -> Result<(), GateError> {
    tier::run_clause(&format!("{what} on ik's real sets"), Tag::Oracle)?;
    let fixture = format!("{what} on ik's fixture sets");
    if tier::run_clause(&fixture, Tag::FixtureOracle)? {
        for name in fx::IK.sets.iter().take(sets) {
            tier::fixture_set(&fixture, &fx::IK, name)?;
        }
    }
    Ok(())
}

Replace both REAL_SETS.len() expressions with oracle::IK.sets.len(), and update the Ctx::sets doc to name the refset families.

Non-blocking

⚠ Revalidate the fixture pins after removing B1: crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs:1564. The PR reports that FREE_COVER_MIN and FREE_FLIPS_MAX were measured with B1 enabled and that this head has no completed fixture e2e/moe/attn run.
Run e2e on the base fixture and both variants, and moe/attn on the base fixture. Show FAIL-first for wrong router ids and a dropped state update. Include the card/driver line, both hashes, commands and output.

Checked

Cited code and changed lines checked against the full diff. The retired option's path was followed through initialization, model selection, loading and the final verdict. Both ordered inventories match their refset owners. The fixture constants have dated PIN reasons; their current-head validation remains advisory. No ledger lessons apply. No builds or GPU gates were executed during validation.

Card check: needs-gpu. This pull request changes crates/gpu-gates/src/bin/gate_qwen35moe_attn.rs, crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs, crates/gpu-gates/src/bin/gate_qwen35moe_moe.rs … and carries no gate run on a card (CONTRIBUTING's evidence list); a maintainer adds gpu-verified after one.

🔨 Shingler, bloomery's review bot: approves a clean pull request and requests changes on a must-fix (how it works)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: MUST_FIX: The FFN clause adds a redundant flag and repeats its caller's layout decision.

Must fix

1. Remove the redundant apart flag

crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs:3446 crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs:3569
Kind B, R14: apart_clause already owns this decision. The only caller passes the same header condition. A fixture without an apart layer returns before this call; the real tier runs both checks regardless of the condition. The second check cannot skip anything.
Remove the flag now so future layout changes have one decision point. Keep the FFN clause's tally with tier::sc. Apply this patch:

--- a/crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs
+++ b/crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs
@@ -3438,18 +3438,11 @@
     /// (j)'s FFN clause (module doc): each FFN type combination's first
     /// layer run alone on each arm against the host's f64 FFN under the
-    /// card's routing. `apart`: the header gives the file a layer whose
-    /// shared expert is apart (`tier::layout_clause`).
+    /// card's routing.
     fn unjoined_ffn(
         m: &mut Qwen35moeModel,
         toks: &[u32],
         n_layer: usize,
-        apart: bool,
     ) -> Result<bool, GateError> {
-        if !tier::layout_clause(
-            "(j) unjoined ffn: the shared expert's FFN half against the host's",
-            apart,
-        )? {
-            return Ok(true);
-        }
+        tier::sc("(j) unjoined ffn: the shared expert's FFN half against the host's")?;
         let path = unjoined_path();
         let split = Split::open(path).map_err(|e| format!("open {}: {e}", path.display()))?;
@@ -3621,5 +3614,5 @@
         ok &= p_ok;
         ok &= gemv_arm(&mut m, toks, &decode)?;
-        ok &= unjoined_ffn(&mut m, toks, want_layers, want.contains(&true))?;
+        ok &= unjoined_ffn(&mut m, toks, want_layers)?;
         let man = ik_set("(j) the step-1,024 set's prompt ids", Set::D1k)?;
         let (_, _, prompt) = man.step()?;

Checked

Duplicates/elements: the sole FFN caller repeats the decision already made by apart_clause. Item 1 removes a flag and a conditional without adding an owner.
Complexity/silent failure: both layout guards and the caller were traced. The later guard cannot defer a reachable call.
Tests: both proposed patch hunks match the head. Hardware gates were not executed.
Gates: tier::sc preserves the self-consistency tag and tally for reachable FFN calls.
Escapes: no C1–C4 exception or ledger lesson applies to this finding.

Notes

The body reports GPU pass summaries. Provide the nvidia-smi line, both tested commit hashes, model files, and exact just gate-* commands with output. Provide FAIL-first output for the new fixture clauses. GPU evidence is incomplete.

Card check: needs-gpu. This pull request changes crates/gpu-gates/src/bin/gate_qwen35moe_attn.rs, crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs, crates/gpu-gates/src/bin/gate_qwen35moe_moe.rs … and carries no gate run on a card (CONTRIBUTING's evidence list); a maintainer adds gpu-verified after one.

🔨 Shingler, bloomery's review bot: approves a clean pull request and requests changes on a must-fix (how it works)

midagedev added a commit that referenced this pull request Oct 14, 2026
…; apart_clause is the one decision

Shingler #128: unjoined() returns before this clause when the header gives
no apart layer (apart_clause), so the flag repeated that decision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: MUST_FIX: unused public fixture APIs violate R13.

Must fix

1. Keep implementation details private (kind B, R13)

crates/gpu-gates/src/q36_fixture.rs:23 and crates/model/src/arch/qwen35moe/fixture.rs:560 expose 19 items with no consumer in another crate: twelve launch constants, the Kind re-export, Mixer, Shared, Layer, Layer::kind(), Shape::from_layers(), and check_kinds_as().

The e2e binary imports only Shape and Site from q36_fixture. The remaining items are used within q36_fixture and its child tests. check_kinds_as() is called only by its own module and its child fixture module, which can access a private parent item.

Remove pub from all twelve launch constants (EMBED_LAUNCHES, HEAD_LAUNCHES, DELTA_MIXER_LAUNCHES, ATTN_MIXER_LAUNCHES, JOINED_FFN_LAUNCHES, ATTN_V_GEMV_LAUNCHES, PASS_QKV_COPY_LAUNCHES, PASS_V_LAUNCHES, UNJOINED_MIXER_LAUNCHES, APART_FFN_LAUNCHES, SLOT_DELTA_LAUNCHES, SLOT_ATTN_LAUNCHES), the Kind re-export, the three implementation types, Layer's fields, and the three methods/functions listed above. This mechanically removes unnecessary public surface before callers depend on it.

Non-blocking

⚠ FAIL-first evidence: crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs:1370 changes the state-ratio denominator in both tiers; the fixture route check at line 1707 accompanies changed free-running acceptance rules. The body supplies green runs and an excluded B1 mutation. Attach red runs for the retained clauses with a stale recurrent state and a wrong routed id, followed by restored green runs.

⚠ Current-head gate evidence: crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs:5248 changes the gate entry point's model-file argument handling. The reported GPU runs name bcd8f7e; this review covers 8ee3108. Attach the selected gate commands and output, the nvidia-smi line, both commit hashes and model file. Include the real-tier e2e result, which the body says was not run.

Checked

Confirmed the declarations and their callers, the proposed visibility reductions, and their added diff lines. Compared the changed state and route checks with the base and checked the evidence claims against the PR body. Neither the reviewer exclusions nor the escape catalog disqualifies these findings; no ledger lessons are in force. No inline comments or issue candidates were supplied.

Notes

Validation used source and diff inspection; no compilation or GPU runs were performed.

Card check: needs-gpu. This pull request changes crates/gpu-gates/src/bin/gate_qwen35moe_attn.rs, crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs, crates/gpu-gates/src/bin/gate_qwen35moe_moe.rs … and carries no gate run on a card (CONTRIBUTING's evidence list); a maintainer adds gpu-verified after one.

🔨 Shingler, bloomery's review bot: approves a clean pull request and requests changes on a must-fix (how it works)

midagedev and others added 3 commits October 15, 2026 05:19
…:placed_on

A placed load's one card holds every layer, the head and the token
embedding at its program's scratch. qwen3moe::place::machine spelled
the card out field by field; it now goes through placed_on, which is
plan_on with the token embedding and the scratch. Behaviour is the
same Machine, pinned by a placement unit test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fixture core in arch/qwen35moe/fixture.rs is shared by the Qwen3.8
spec and the new Qwen3.6 spec: the key rule, the 1-D fills (three block
norms added), the card experts over a family's machine and plan, and the
kind check by variant. The shared expert's gate is derived from the
width (1/sqrt(hidden)), equal in bits to the old literal at 2560, and a
source of another width is refused by name.

The Qwen3.6 spec maps layers [5, 11, 0, 39] and plans its card experts
with the Qwen3.5 body's expert rule on the qwen3moe program's machine.
refset gains the fx-ik-qwen35moe family (3 sets), ref-paths points the
qwen35moe row at the fixture dir, the dump script adds the fixture
tier's prefill routes on d1k, and fixture-variants gains unjoined.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The unjoined row retyped only the shared expert and the routed down,
so its mixers kept the fixture's Q4_K and the file was a layout no real
file has. The row now takes unsloth UD-Q4_K_M's per-layer types as its
header states them (every mixer site Q8_0 but beta and alpha, F32; a
Q8_0 shared expert apart; a Q5_K routed down), and unjoinedq6 is the
same map with the Q6_K down of the file's layers 34, 38 and 39.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
midagedev and others added 25 commits October 15, 2026 05:19
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ames Qwen/Qwen3.6-35B-A3B's LICENSE (Apache 2.0) at 995ad96e, copied with LF line ends as licences/qwen35moe.txt, so the fixture family's fx_ sets can publish beside the other three

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…xture_q36_plan (source layers [5, 11, 0, 39], the interval written as 2, the shared expert gate's bits at 1/sqrt(2048), the one-file fixture carries no split keys, kinds read back, a draft refused by name), hw_fixture_q36_budget (the recorded budget holds 128 of 256 experts on every layer; its round trip through the header) and hw_fixture_q36_refusals (no interval, width 2560, an uncovered key); the q38 gates call the same helpers (assert_plan_layers, assert_ssm_a_negative, assert_kinds_read_back, budget_round_trip, verify_edited, control_plans, plan_edited_source, refused_with) with every q38 assert kept

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e the tier names — q36_fixture::Shape holds each layer's kind and site types and gives the captured graphs' node counts as sums of per-layer launches (joined and unjoined layouts; a layer of neither is a named error, a slot count on an apart layer too), the e2e's Cfg/init reads the path from ref_model_path, the levers and the header's card budget, and every shape constant is a REAL_* witness (N_LAYER 40, N_ATTN 10, N_DELTA 30, Q6_QKV 14, Q6_V 6, NODES 520/537/91, UD 844/841, PLACED_BUDGET 12 GiB, HIDDEN); lib tests pin 520/537/91, 844/841, the fixture's 55/54/11 and the variants' 88/85

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ach clause that reads ik's sets is Tag::Oracle on the real sets in the real tier and Tag::FixtureOracle on the fixture's (fx-ik-qwen35moe, opened through tier::fixture_set) in the fixture tier, counted by expect_fixture_oracle; the moe bin picks its layers from the header (the first delta layer with a Q6_K down, then a Q4_K one) with N_LAYER, LAYERS [0, 5] and FAULT_LAYER as witnesses; the attn bin opens its file through ref_model_path (layer 3 an attention layer in both files, refused by name otherwise) and its recipe names BLOOMERY_MODEL=qwen35moe; gate-paths rows for what both bins now reach

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… on the base fixture and its two variants — every clause carries a tier tag: the comparisons with ik's sets (free at (c), (o2), (o7), forced, the two step sets, the wide arm at (u)(d) and (o5)) are Oracle on the real sets and FixtureOracle on the fixture family's (opened through tier::fixture_set, the variant's own under BLOOMERY_FIXTURE_VARIANT), counted by expect_fixture_oracle at 8; the rest are self-consistency, memguard scale; tier.rs gains Tag::NoLayoutLayer and layout_clause, so (j) and (o8) open the tier's file in the fixture tier and run on a variant whose shared experts are apart and are left on the joined base fixture, and (n) the other way round; (s) compares each layer's shared expert with the header's layout; (v) takes its 96 ids from the step-1,024 set (the batch set holds 5), refusing a shorter prompt by name

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d error, the free state term divides by max(gap, in_rel)

In the fixture tier, (c), (o2), (o7) and (t1) take flip::head_tie at the
run's measured head error. d1k's ik row is near flat: the gap is 1.1 σ_e,
against 13 σ_e on the real row. The real tier keeps the strict argmax.
(t1)'s state term divides by max(gap, in_rel): a layer's free state error
is its input error times the gain L0 shows (0.73), so the old term charged
it for the stack upstream. No band moves; each form carries a PIN line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lip unheld, and the host-rule clause pins the 8-bit/32 form

The real tier still holds every free-clause row. In the fixture tier a
flip that router_tie explains is left unheld, counted against
FREE_FLIPS_MAX, and refused when the held rows fall below FREE_COVER_MIN.
The host-rule clause compares layer 0's conv ring with the host dot in
the form the site declares.
The writer already skips a final rc 75, so a lock wait cannot feed the
budget median. Part 16 now checks the plain card-lock case (try=11, red)
the same way the yielded-item case already does.
ref-paths.sh gives qwen35moe its fixture directory. The self-test still
required the old self row, so check-recipes on 741a6c51 reported the five
failures that follow from that row. The table read, the directory bump,
the key part, and the mutant that points the row back at self now follow it.
Tables::key and Tables::dims are the refusal and the kept source dims.
Gate, NoTables and the glm map drop those copies.
The Qwen3.8 head comment matches the gemv: it reads f32 rows directly.
… budget contract keeps no history

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…6 gates reach on main

The MiniCPM text recipe runs gate_qwen35moe_e2e, which now reads the fixture
tier, act_rule and q36_fixture; the attn gate and the q38 serve rules reach
site.rs, tier.rs, models and placement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Four rows kept a recipe token after the description. The token is already
in the recipe column, beside main's MiniCPM text gate. site, models and
placement carried the token on the old tip; tier.rs gained it while
replaying onto main.
…e reads the header's layer kind

picks_its_top refuses duplicate, out-of-range and non-finite picks for both
the Qwen3.6 and Qwen3.8 gates; route_sites and the forced arm read
flip::margin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
B1's host-rule clause compared layer 0's conv ring with the 8-bit
codes of 32. That gemv is not on this branch. The wide arm still
compares the two runs at U_L0_REL, and the free clause keeps the
near-tie rule.
gate-gpu-minicpm-text runs gate_qwen35moe_e2e, and that bin names
q36_fixture::Shape. check-recipes refused the row for leaving the
recipe off it.
The real tier opens the file BLOOMERY_REF_MODEL names. A leftover
--model-file on a saved command was ignored, so the e2e gate could
pass without reading the file the flag named.

REAL_SETS and FIXTURE_SETS repeated the qwen35moe IK family and its
fixture mirror, in the same order. The moe gate reads those families.
The e2e gate already rejects a bare --model-file before init. The equals form names a file the same way and was still ignored.
…; apart_clause is the one decision

Shingler #128: unjoined() returns before this clause when the header gives
no apart layer (apart_clause), so the flag repeated that decision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ratio

The fixture tier still divides the free new-state term by the larger of the input gap and the layer input's distance. That form was pinned on the fixture's layer 2 and does not apply to the real tier.
The five-family spec array is the wrapped form rustfmt writes.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: CLEAN: No must-fix defect was found in the diff.

Non-blocking

⚠ Show FAIL-first for the fixture checks: crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs:1718. The PR reports passing runs but gives no failing run for the new coverage floor, flip cap or probe-agreement check. Attach mutant runs that exercise each refusal, followed by restored passing runs. This establishes their protection before later changes depend on them.

⚠ Supply GPU evidence for this head: justfile:934. The reported runs name bcd8f7e; the reviewed head is 31dc17a. Attach the nvidia-smi line, both commit hashes, model paths and exact just gate-* commands with output. This makes the verification attributable to the code being merged.

Checked

Duplicates: all three dryer pairs are short architecture adapters. Shared header validation already has one owner. Router-pick validation uses flip::picks_its_top.
Complexity: diagnostics flag free (18 → 32), header_shape (new, 18), Shape::from_header (new, 17), and other gate functions. Complexity alone is not a finding.
Silent failure: zero held rows produce zero coverage and fail the floor; non-finite router logits fail the top-set check.
Tests: statically traced all three new comparisons into ok; a failed comparison makes the clause fail. Hardware fixture tests remain ignored. Tests were not executed.
Gates: the fixture coverage, flip cap and probe bands have dated PIN reasons. Their FAIL-first run evidence is still missing.
Escapes: neither cited code block matches catalog C1–C4. No ledger lessons are in force.

Notes

Dryer did not scan gpu-gates. Validation was static; GPU and source-file fixture gates were not run.

Card check: needs-gpu. This pull request changes crates/gpu-gates/src/bin/gate_qwen35moe_attn.rs, crates/gpu-gates/src/bin/gate_qwen35moe_e2e.rs, crates/gpu-gates/src/bin/gate_qwen35moe_moe.rs … and carries no gate run on a card (CONTRIBUTING's evidence list); a maintainer adds gpu-verified after one.

🔨 Shingler, bloomery's review bot: approves a clean pull request and requests changes on a must-fix (how it works)

@midagedev
midagedev merged commit df9769d into main Oct 14, 2026
6 of 7 checks passed
midagedev added a commit that referenced this pull request Oct 14, 2026
…; apart_clause is the one decision

Shingler #128: unjoined() returns before this clause when the header gives
no apart layer (apart_clause), so the flag repeated that decision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gpu The card engine: crates/gpu*, crates/app host Host crates: qdot, threads, engram, engram-lab, runtime, sampler, levers, decision, vision, refset model The CPU engine and model files: crates/model, models, hf, gguf, tokenizer, jinja needs-gpu Changes code a card must check, and carries no gate run on a card placement Placement planning: crates/placement tools Tools, recipes, CI and the toolchain: tools/, justfile, .github/, Cargo

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant