hf2q 0.1.2

Pure Rust CLI for converting HuggingFace models to hardware-optimized formats and serving them over an OpenAI-compatible API on Apple Silicon
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
//! `ApexPolicy` — pure-Rust port of `mudler/apex-quant`'s per-tier
//! tensor-type-file rules, dispatched through the same
//! `QuantPolicy::target_for(TensorRef) -> Result<GgmlType, _>`
//! contract as `StandardPolicy`.
//!
//! Per ADR-033 §"Plan" / Pa. Vendored mudler reference:
//! `/opt/hf2q/vendor/apex-quant/` @ pinned SHA
//! `63c5048b7dc9ff230f2397d7bc445ca28894b769`.
//!
//! Resolution order (per ADR Decision §3 + §9):
//!
//! 1. **Vision/audio gate** — handled UPSTREAM at the convert
//!    dispatcher (`is_vision_tensor_pattern` / `is_audio_tensor_pattern`
//!    in `src/quantize/ggml_quants/vision.rs`). `ApexPolicy::target_for`
//!    is **not called** for those tensors; modality-side weights emit
//!    F16 outside the policy.
//!
//! 2. **Fingerprint match** (Decision §9) — performed UPSTREAM by the
//!    Apex driver against `data/apex-references/manifest.json`. If a
//!    per-model override matched, `target_for` is bypassed for that
//!    tensor in favor of the vendored config's verbatim assignment.
//!    `ApexPolicy::target_for` is the **algorithmic-generator path**
//!    only. (Per-model override dispatch lives in P4a's CLI driver,
//!    not in this file — Pa is rules + classifier scaffolding only.)
//!
//! 3. **Role classification** — `classify_moe_tensor(arch, name)`
//!    returns a `MoeTensorRole`.
//!
//! 4. **Layer-region partition** — `exp_region / shared_region /
//!    attn_region` for routed/shared/attention tensors respectively.
//!    Asymmetric: EXP/SHARED use a 5-wide edge band; ATTN uses 3-wide.
//!
//! 5. **Picker** — `tier_rules(tier)` returns the 7-tuple; we pick
//!    the slot indexed by `(role, region)`. Globals (`token_embd`,
//!    `output`) and structural tensors (`Norm`, `RouterGate`) have
//!    hard-coded picks not in the per-tier table.
//!
//! Per [[feedback-no-loop-suppression-2026-05-17]]: unsupported
//! arch / tier / dense model / missing hparam → typed `Err`. Never
//! silent F16 demotion.

use super::super::ggml_type::GgmlType;
use super::super::tensor_ref::{ArchName, TensorRef};
use super::arches::{classify_moe_tensor, is_apex_supported_arch, MoeTensorRole};
use super::error::ApexError;
use super::mudler_config::MudlerConfig;
use super::rules::{
    attn_region, exp_region, shared_region, tier_rules, ApexTier, AttnRegion, ExpRegion,
    SharedRegion,
};

/// `ApexPolicy` — per-tier algorithmic quant-target picker for MoE
/// arches. Constructed once per `convert` invocation; consumed by the
/// per-tensor dispatch loop.
///
/// Construction validates that:
///   - `arch` is in the v1 Apex supported set
///     (`Qwen35Moe / Gemma4 / MiniMaxM2`)
///   - `n_expert > 1` (dense models error early; Apex is MoE-only
///     by design)
///   - `n_layers > 0`
///
/// Post-construction, `target_for` is infallible w.r.t. these
/// preconditions; the only runtime errors are `MissingLayerIndex` /
/// `LayerIndexOutOfRange` (dispatcher bugs).
///
/// ADR §9 per-model override: when the CLI driver's fingerprint
/// dispatch matches a manifest entry, it wraps the matched
/// `MudlerConfig` via [`ApexPolicy::with_mudler_override`]. The
/// override path lifts every `target_for` query through the vendored
/// per-tensor map and surfaces missing tensors as
/// [`ApexError::TensorNotInMudlerConfig`] — no silent fall-through to
/// the algorithmic generator (ADR §9 line 102: "the vendored config's
/// rules win over the algorithmic generator's output").
/// ADR-033 §Pi: arches for which an I-tier APEX request CAN be
/// honored when imatrix data is present.
///
/// Phase A v1: the imatrix subsystem can ingest an externally-produced
/// `.imatrix.gguf` (typically from stock `llama-imatrix`) for any of
/// the v1 Apex-supported MoE arches. The policy itself doesn't care
/// where the imatrix came from; it only needs to know that the
/// per-tensor dispatch surface (P4b's imatrix-aware quantizer wiring)
/// can consume the data for this arch.
///
/// `Llama3` and `MiniMaxM2` are convert-only arches (no hf2q inference
/// support); the in-tree generation path can't run them. Operators who
/// already have an external `.imatrix.gguf` for those arches can still
/// pass it via `--imatrix <path>` — at which point this list controls
/// whether the policy LAYER accepts it. Phase A keeps these out of
/// `SUPPORTED_FOR_IMATRIX` to stay aligned with ADR-033 §Pi line 359
/// ("Pi only runs against arches with hf2q inference support").
pub const SUPPORTED_FOR_IMATRIX: &[&str] = &["qwen3moe", "qwen35moe", "gemma4"];

#[derive(Debug, Clone, Copy)]
pub struct ApexPolicy {
    /// The selected Apex tier.
    pub tier: ApexTier,
    /// Source model's `config.json::num_hidden_layers` (auto-detected
    /// at convert time; no env-var override per ADR §"Per-model APEX
    /// config override").
    pub n_layers: u32,
    /// Source model's `config.json::num_experts` (Qwen35Moe naming) /
    /// `num_local_experts` (Mixtral naming) / equivalent per-arch
    /// field. Used at construction to gate the dense-model error;
    /// `target_for` doesn't re-check it.
    pub n_expert: u32,
    /// Arch for tensor classification.
    pub arch: ArchName,
    /// ADR §9 per-model override. When `Some`, `target_for` consults
    /// the vendored per-tensor map first; algorithmic generator is
    /// bypassed entirely. `'static` because the cache lives in the
    /// process-wide `mudler_config::cache_slot`. Defaults to `None`
    /// (algorithmic).
    pub mudler_override: Option<&'static MudlerConfig>,
}

impl ApexPolicy {
    /// Construct an `ApexPolicy`, validating preconditions up-front.
    /// Errors:
    ///   - `UnsupportedArch` if the arch isn't in the Apex v1 set.
    ///   - `DenseModelNotSupported` if `n_expert <= 1` (Apex is
    ///     MoE-only).
    ///   - `MissingHParam` if `n_layers == 0`.
    pub fn new(
        tier: ApexTier,
        arch: ArchName,
        n_layers: u32,
        n_expert: u32,
    ) -> Result<Self, ApexError> {
        if !is_apex_supported_arch(arch) {
            return Err(ApexError::unsupported_arch(arch));
        }
        if n_layers == 0 {
            return Err(ApexError::MissingHParam {
                hparam: "num_hidden_layers",
            });
        }
        if n_expert <= 1 {
            return Err(ApexError::DenseModelNotSupported {
                arch: arch.name(),
                n_expert,
            });
        }
        // ADR-033 §Pi: I-tier variants require per-row imatrix data.
        // v1's `tier_rules` maps {Quality,IQuality}, {Balanced,IBalanced},
        // {Compact,ICompact} to identical TierRules — so absent imatrix
        // data the I-tier silently produces bytes byte-identical to its
        // non-I sibling, defeating the operator's intent.
        //
        // `ApexPolicy::new` is the "no imatrix data supplied" entry
        // point and continues to reject I-tier on every arch — the
        // operator must use `new_with_imatrix` (which threads a
        // `LoadedImatrix` from `--imatrix <file>`) to request an I-tier.
        // Phase A's `SUPPORTED_FOR_IMATRIX` lists arches that can be
        // RUN against a pre-computed imatrix (i.e. arches the policy
        // knows how to dispatch tensors for — same set as the v1 Apex
        // supported MoE arches). Phase B will widen this set further
        // when in-tree generation lands.
        if tier.requires_imatrix() {
            return Err(ApexError::ImatrixRequiresInference {
                tier: tier.cli_name(),
                arch: arch.name(),
                supported_for_imatrix: SUPPORTED_FOR_IMATRIX,
            });
        }
        Ok(Self {
            tier,
            n_layers,
            n_expert,
            arch,
            mudler_override: None,
        })
    }

    /// Construct an `ApexPolicy` with imatrix data attached.
    ///
    /// This entry point accepts I-tier variants because the caller has
    /// committed to a `LoadedImatrix` (typically loaded from
    /// `--imatrix <file>` in Phase A; in Phase B from in-tree
    /// generation against a calibration corpus).
    ///
    /// The pre-conditions are the same as [`ApexPolicy::new`] minus the
    /// I-tier-requires-imatrix gate. Per ADR-033 §Pi the arch MUST be in
    /// [`SUPPORTED_FOR_IMATRIX`] — even with imatrix data present,
    /// convert-only arches (Llama3, MiniMaxM2) lack the per-tensor
    /// dispatch surface the imatrix-aware quantizer needs to consume
    /// the data.
    ///
    /// Phase A note: `_imatrix_provenance` is recorded only for
    /// diagnostic logging; the per-tensor `LoadedImatrix` data itself
    /// is consumed at the quantizer layer (P4b), not here. The policy
    /// layer's only job for I-tier is the upfront "yes we can run this
    /// configuration" gate; the per-row weighting is applied during
    /// `Quantizer::quantize(..., imatrix: Option<&[f32]>)`.
    pub fn new_with_imatrix(
        tier: ApexTier,
        arch: ArchName,
        n_layers: u32,
        n_expert: u32,
    ) -> Result<Self, ApexError> {
        if !is_apex_supported_arch(arch) {
            return Err(ApexError::unsupported_arch(arch));
        }
        if n_layers == 0 {
            return Err(ApexError::MissingHParam {
                hparam: "num_hidden_layers",
            });
        }
        if n_expert <= 1 {
            return Err(ApexError::DenseModelNotSupported {
                arch: arch.name(),
                n_expert,
            });
        }
        // If the I-tier is requested but the arch isn't in the
        // imatrix-supported set, the operator's I-tier intent can't be
        // honored even with imatrix data — surface the same typed
        // error as `new`, but with the (still-accurate) supported set.
        if tier.requires_imatrix() && !SUPPORTED_FOR_IMATRIX.contains(&arch.name()) {
            return Err(ApexError::ImatrixRequiresInference {
                tier: tier.cli_name(),
                arch: arch.name(),
                supported_for_imatrix: SUPPORTED_FOR_IMATRIX,
            });
        }
        Ok(Self {
            tier,
            n_layers,
            n_expert,
            arch,
            mudler_override: None,
        })
    }

    /// Attach an ADR §9 mudler per-model override to the policy.
    ///
    /// The override wins over `target_for`'s algorithmic generator for
    /// every tensor name present in the parsed [`MudlerConfig`]; a
    /// missing tensor surfaces as
    /// [`ApexError::TensorNotInMudlerConfig`] rather than fall
    /// through. Caller (CLI driver) is responsible for logging the
    /// match for debug transparency — see ADR §9 line 104's "surprise
    /// risk" mitigation.
    pub fn with_mudler_override(mut self, mudler: &'static MudlerConfig) -> Self {
        self.mudler_override = Some(mudler);
        self
    }

    /// Decide the disk-format `GgmlType` for one tensor.
    ///
    /// Resolution order per ADR Decision §3 (vision-gate and §9
    /// fingerprint lookup are upstream of this function):
    ///   1. Classify via `classify_moe_tensor(arch, name)`.
    ///   2. For role ∈ {RoutedExpert, SharedExpert, Attention, Ssm,
    ///      Other}: compute the layer-region, index into the per-tier
    ///      7-tuple.
    ///   3. For role ∈ {TokenEmbd, Output, RouterGate, Norm}: return
    ///      the hardcoded picks (Q6_K / Q5_0 / F32).
    pub fn target_for(&self, tensor: &TensorRef) -> Result<GgmlType, ApexError> {
        // ADR §9 per-model override: if the CLI driver attached a
        // mudler config, IT IS authoritative (line 102). We do NOT
        // fall through to the algorithmic generator on a tensor miss
        // — `mudler_config::MudlerConfig::target_for` surfaces a
        // typed `TensorNotInMudlerConfig` so the no-silent-fallback
        // rule holds.
        //
        // Exception: structural tensors that mudler's files do NOT
        // enumerate (norms, token_embd, output, router gate). For
        // those, the algorithmic generator's hardcoded picks (F32 /
        // Q6_K / Q5_0) apply — they're not part of mudler's per-tier
        // surface by design (mudler's `generate_config.sh` only emits
        // the routed/shared/attn/ssm lines). To keep the override
        // strict on the tensors mudler DOES enumerate (and silent on
        // the ones it doesn't), we apply override-first for the
        // four MoE roles below; the structural arms below fall
        // through to the algorithmic hardcodes.
        if let Some(mudler) = self.mudler_override {
            if mudler.contains_match(tensor.name) {
                return mudler.target_for(tensor.name);
            }
            // Structural tensors mudler doesn't enumerate: token_embd,
            // output, output_norm, blk.N.{attn,ffn}_norm, router gate
            // (ffn_gate_inp). Fall through to the algorithmic
            // hardcodes below — this preserves the override's
            // strictness on the enumerated tensors (per-layer attn /
            // exps / shexp / ssm) while letting the small structural
            // set use llama.cpp's defaults, exactly mirroring stock
            // `llama-quantize --tensor-type-file`'s semantics.
            let role = classify_moe_tensor(self.arch, tensor.name);
            match role {
                MoeTensorRole::TokenEmbd
                | MoeTensorRole::Output
                | MoeTensorRole::RouterGate
                | MoeTensorRole::Norm => {
                    // OK — fall through to algorithmic below.
                }
                MoeTensorRole::RoutedExpert
                | MoeTensorRole::SharedExpert
                | MoeTensorRole::Attention
                | MoeTensorRole::Ssm
                | MoeTensorRole::Other => {
                    // Mudler's surface SHOULD enumerate these. Missing
                    // entry → typed error per the strict-override rule.
                    return Err(ApexError::TensorNotInMudlerConfig {
                        source_path: mudler.source_path.to_string(),
                        tensor_name: tensor.name.to_string(),
                    });
                }
            }
        }

        let role = classify_moe_tensor(self.arch, tensor.name);

        // Per-tier rule table for `RoutedExpert / SharedExpert / Attention / Ssm`.
        let rules = tier_rules(self.tier);

        match role {
            // --- Global / structural tensors (no layer index needed) ---
            // Mudler convention: token_embd and output stay at Q6_K
            // (implicit — llama.cpp's quantize-tool default for these
            // when no `--token-embedding-type` / `--output-tensor-type`
            // override is given is Q6_K). We mirror that explicitly
            // so `target_for`'s contract stays "policy chooses
            // everything".
            MoeTensorRole::TokenEmbd => Ok(GgmlType::Q6_K),
            MoeTensorRole::Output => Ok(GgmlType::Q6_K),
            // Router gate is a small per-token expert selector. Mudler
            // doesn't list it in `generate_config.sh`, so llama.cpp's
            // default fires — preserved at Q5_0 (small, perf-critical).
            MoeTensorRole::RouterGate => Ok(GgmlType::Q5_0),
            // Norms are never quantized. F32 always.
            MoeTensorRole::Norm => Ok(GgmlType::F32),

            // --- Per-block tensors (layer-indexed) ---
            MoeTensorRole::RoutedExpert => {
                let layer = self.require_layer_index(tensor)?;
                Ok(match exp_region(layer, self.n_layers) {
                    ExpRegion::Edge => rules.edge_exp,
                    ExpRegion::Near => rules.near_exp,
                    ExpRegion::Mid => rules.mid_exp,
                })
            }
            MoeTensorRole::SharedExpert => {
                let layer = self.require_layer_index(tensor)?;
                Ok(match shared_region(layer, self.n_layers) {
                    SharedRegion::Edge => rules.edge_shared,
                    SharedRegion::Mid => rules.mid_shared,
                })
            }
            MoeTensorRole::Attention => {
                let layer = self.require_layer_index(tensor)?;
                Ok(match attn_region(layer, self.n_layers) {
                    AttnRegion::Edge => rules.edge_attn,
                    AttnRegion::Mid => rules.mid_attn,
                })
            }
            MoeTensorRole::Ssm => {
                // SSM tensors pair with attention's `attn_type` per
                // `generate_config.sh:189-192`.
                let layer = self.require_layer_index(tensor)?;
                Ok(match attn_region(layer, self.n_layers) {
                    AttnRegion::Edge => rules.edge_attn,
                    AttnRegion::Mid => rules.mid_attn,
                })
            }
            MoeTensorRole::Other => {
                // Catch-all: route to attention-region quant for the
                // active layer. Mudler's bash doesn't enumerate
                // "everything else"; this is hf2q's choice to keep
                // the policy total (no silent escape).
                //
                // For a global "Other" tensor (no layer_index — e.g.
                // a future un-classified global) we fall back to
                // mid_attn since we have no layer to index.
                let region = match tensor.layer_index {
                    Some(l) => attn_region(l, self.n_layers),
                    None => AttnRegion::Mid,
                };
                Ok(match region {
                    AttnRegion::Edge => rules.edge_attn,
                    AttnRegion::Mid => rules.mid_attn,
                })
            }
        }
    }

    /// Helper: require a layer index for per-block tensors. The
    /// convert dispatcher is responsible for parsing `blk.<i>.` and
    /// setting `TensorRef::layer_index`; this errors if it forgot.
    fn require_layer_index(&self, tensor: &TensorRef) -> Result<usize, ApexError> {
        let layer = tensor
            .layer_index
            .ok_or_else(|| ApexError::MissingLayerIndex {
                name: tensor.name.to_string(),
            })?;
        if (layer as u64) >= (self.n_layers as u64) {
            return Err(ApexError::LayerIndexOutOfRange {
                name: tensor.name.to_string(),
                layer_index: layer,
                n_layers: self.n_layers,
            });
        }
        Ok(layer)
    }
}

#[cfg(test)]
mod tests {
    use super::super::super::tensor_ref::SourceDtype;
    use super::*;

    /// Build a `TensorRef` for tests with a 4096×N shape.
    fn tref<'a>(name: &'a str, arch: ArchName, layer: Option<usize>) -> TensorRef<'a> {
        // `shape` outlives the call via 'static.
        static SHAPE: [usize; 2] = [4096, 4096];
        TensorRef {
            name,
            shape: &SHAPE,
            source_dtype: SourceDtype::BF16,
            arch,
            layer_index: layer,
        }
    }

    /// Constructor rejects unsupported arches with the typed error
    /// and supported-set list intact.
    #[test]
    fn apex_policy_unsupported_arch_errors() {
        let err = ApexPolicy::new(ApexTier::Quality, ArchName::Llama3, 32, 0).unwrap_err();
        match err {
            ApexError::UnsupportedArch { arch, supported } => {
                assert_eq!(arch, "llama");
                assert!(supported.contains(&"qwen3moe"));
                assert!(supported.contains(&"gemma4"));
                assert!(supported.contains(&"minimax-m2"));
            }
            other => panic!("expected UnsupportedArch, got {other:?}"),
        }

        // Bert is dense embedding-only — also UnsupportedArch.
        let err = ApexPolicy::new(ApexTier::Quality, ArchName::Bert, 24, 0).unwrap_err();
        assert!(matches!(err, ApexError::UnsupportedArch { .. }));
    }

    /// Constructor rejects dense MoE-capable arches with the typed
    /// error (Gemma 4 with n_expert=0 is treated as dense).
    #[test]
    fn apex_policy_dense_model_errors() {
        let err = ApexPolicy::new(ApexTier::Quality, ArchName::Gemma4, 30, 0).unwrap_err();
        assert!(matches!(err, ApexError::DenseModelNotSupported { .. }));

        let err = ApexPolicy::new(ApexTier::Quality, ArchName::Gemma4, 30, 1).unwrap_err();
        assert!(matches!(err, ApexError::DenseModelNotSupported { .. }));
    }

    /// At tier=quality, 40-layer Qwen35Moe:
    ///   blk.0.ffn_gate_exps.weight → Q6_K (edge_exp_quality)
    #[test]
    fn apex_policy_routed_expert_edge_layer_0() {
        let p = ApexPolicy::new(ApexTier::Quality, ArchName::Qwen35Moe, 40, 128).unwrap();
        let t = tref("blk.0.ffn_gate_exps.weight", ArchName::Qwen35Moe, Some(0));
        assert_eq!(p.target_for(&t).unwrap(), GgmlType::Q6_K);
    }

    /// At tier=quality, 40-layer Qwen35Moe:
    ///   blk.20.ffn_gate_exps.weight → IQ4_XS (mid_exp_quality)
    #[test]
    fn apex_policy_routed_expert_mid_layer_20() {
        let p = ApexPolicy::new(ApexTier::Quality, ArchName::Qwen35Moe, 40, 128).unwrap();
        let t = tref("blk.20.ffn_gate_exps.weight", ArchName::Qwen35Moe, Some(20));
        assert_eq!(p.target_for(&t).unwrap(), GgmlType::IQ4_XS);
    }

    /// Tier=mini surfaces edge_attn=Q4_K vs mid_attn=Q3_K asymmetry
    /// for the SAME tensor name at different layer indices.
    /// On a 30-layer model (gemma4-26b), layer 2 is ATTN_EDGE
    /// (`i <= 2`) and layer 3 is ATTN_MID.
    #[test]
    fn apex_policy_attention_edge_vs_mid() {
        let p = ApexPolicy::new(ApexTier::Mini, ArchName::Gemma4, 30, 8).unwrap();

        let t_edge = tref("blk.2.attn_q.weight", ArchName::Gemma4, Some(2));
        assert_eq!(p.target_for(&t_edge).unwrap(), GgmlType::Q4_K);

        let t_mid = tref("blk.3.attn_q.weight", ArchName::Gemma4, Some(3));
        assert_eq!(p.target_for(&t_mid).unwrap(), GgmlType::Q3_K);
    }

    /// Router gate is Q5_0 regardless of tier.
    #[test]
    fn apex_policy_router_gate_q5_0() {
        for tier in [
            ApexTier::Quality,
            ApexTier::Balanced,
            ApexTier::Compact,
            ApexTier::Mini,
        ] {
            let p = ApexPolicy::new(tier, ArchName::Qwen35Moe, 40, 128).unwrap();
            let t = tref("blk.5.ffn_gate_inp.weight", ArchName::Qwen35Moe, Some(5));
            assert_eq!(p.target_for(&t).unwrap(), GgmlType::Q5_0, "tier {tier:?}");
        }
    }

    /// Norms are F32 regardless of tier.
    #[test]
    fn apex_policy_norm_f32() {
        for tier in [
            ApexTier::Quality,
            ApexTier::Balanced,
            ApexTier::Compact,
            ApexTier::Mini,
        ] {
            let p = ApexPolicy::new(tier, ArchName::Qwen35Moe, 40, 128).unwrap();
            for name in [
                "blk.5.attn_norm.weight",
                "blk.5.ffn_norm.weight",
                "blk.5.attn_q_norm.weight",
                "output_norm.weight",
            ] {
                let layer = if name.starts_with("blk.") {
                    Some(5)
                } else {
                    None
                };
                let t = tref(name, ArchName::Qwen35Moe, layer);
                assert_eq!(
                    p.target_for(&t).unwrap(),
                    GgmlType::F32,
                    "tier {tier:?} name {name}"
                );
            }
        }
    }

    /// `token_embd.weight` → Q6_K regardless of tier.
    #[test]
    fn apex_policy_token_embd_q6_k() {
        for tier in [
            ApexTier::Quality,
            ApexTier::Balanced,
            ApexTier::Compact,
            ApexTier::Mini,
        ] {
            let p = ApexPolicy::new(tier, ArchName::Qwen35Moe, 40, 128).unwrap();
            let t = tref("token_embd.weight", ArchName::Qwen35Moe, None);
            assert_eq!(p.target_for(&t).unwrap(), GgmlType::Q6_K, "tier {tier:?}");
        }
    }

    /// Per-block tensor without `layer_index` → `MissingLayerIndex`
    /// (dispatcher contract bug).
    #[test]
    fn apex_policy_missing_layer_index_errors() {
        let p = ApexPolicy::new(ApexTier::Quality, ArchName::Qwen35Moe, 40, 128).unwrap();
        let t = tref("blk.0.ffn_gate_exps.weight", ArchName::Qwen35Moe, None);
        let err = p.target_for(&t).unwrap_err();
        assert!(matches!(err, ApexError::MissingLayerIndex { .. }));
    }

    /// `layer_index >= n_layers` → `LayerIndexOutOfRange`.
    #[test]
    fn apex_policy_layer_index_out_of_range_errors() {
        let p = ApexPolicy::new(ApexTier::Quality, ArchName::Qwen35Moe, 40, 128).unwrap();
        let t = tref("blk.40.ffn_gate_exps.weight", ArchName::Qwen35Moe, Some(40));
        let err = p.target_for(&t).unwrap_err();
        assert!(matches!(err, ApexError::LayerIndexOutOfRange { .. }));
    }

    /// Cross-check against the vendored verbatim config
    /// `vendor/apex-quant/configs/carnice_qwen36_mtp_quality.txt` —
    /// the operator's qwen3.6 41-layer production model class. At
    /// layer 5, ffn_gate_exps=Q5_K (NEAR_EXP_QUALITY), shared=Q8_0
    /// (EDGE/MID_SHARED_QUALITY both Q8_0), attn=Q6_K.
    #[test]
    fn apex_policy_matches_carnice_qwen36_layer_5() {
        // 41 layers (40 + 1 MTP per the vendored config's max blk.40).
        let p = ApexPolicy::new(ApexTier::Quality, ArchName::Qwen35Moe, 41, 128).unwrap();

        let exp = tref("blk.5.ffn_gate_exps.weight", ArchName::Qwen35Moe, Some(5));
        assert_eq!(p.target_for(&exp).unwrap(), GgmlType::Q5_K);

        let shexp = tref("blk.5.ffn_gate_shexp.weight", ArchName::Qwen35Moe, Some(5));
        assert_eq!(p.target_for(&shexp).unwrap(), GgmlType::Q8_0);

        let attn = tref("blk.5.attn_q.weight", ArchName::Qwen35Moe, Some(5));
        assert_eq!(p.target_for(&attn).unwrap(), GgmlType::Q6_K);
    }

    /// At mini tier on a 30-layer Gemma4 MoE, layer 10 routed-expert
    /// should be IQ2_S (mid_exp_mini), shared should be Q4_K
    /// (mid_shared_mini), attn should be Q3_K (mid_attn_mini).
    /// Cross-validated against `vendor/apex-quant/configs/gemma4_26b_mini.txt`.
    #[test]
    fn apex_policy_matches_gemma4_mini_layer_10() {
        let p = ApexPolicy::new(ApexTier::Mini, ArchName::Gemma4, 30, 8).unwrap();

        let exp = tref("blk.10.ffn_gate_exps.weight", ArchName::Gemma4, Some(10));
        assert_eq!(p.target_for(&exp).unwrap(), GgmlType::IQ2_S);

        let shexp = tref("blk.10.ffn_gate_shexp.weight", ArchName::Gemma4, Some(10));
        assert_eq!(p.target_for(&shexp).unwrap(), GgmlType::Q4_K);

        let attn = tref("blk.10.attn_q.weight", ArchName::Gemma4, Some(10));
        assert_eq!(p.target_for(&attn).unwrap(), GgmlType::Q3_K);
    }

    /// ADR §9 per-model override end-to-end smoke. Build an
    /// ApexPolicy at the algorithmic level, then attach the
    /// gemma4_26b_balanced.txt mudler config. The override MUST win
    /// for enumerated tensors and the structural fall-through MUST
    /// still produce Q5_0 / Q6_K / F32 for router-gate / token-embd
    /// / norms (mudler doesn't enumerate those).
    #[test]
    fn apex_policy_with_mudler_override_wins_for_enumerated_tensors() {
        use super::super::fingerprint::vendor_config_content;
        use super::super::mudler_config::MudlerConfig;

        // 30-layer Gemma4 MoE @ balanced — same arch as
        // gemma4_26b_balanced.txt. The vendored file sets
        // `blk.0.ffn_gate_exps=Q6_K` at edge layers and
        // `blk.5.ffn_gate_exps=Q5_K` at the near band.
        let content =
            vendor_config_content("vendor/apex-quant/configs/gemma4_26b_balanced.txt").unwrap();
        // The override leaks; that's intentional for the
        // process-wide cache. For this test we leak a fresh copy so
        // the `&'static` lifetime is satisfied without polluting the
        // production cache slot.
        let mudler: &'static MudlerConfig = Box::leak(Box::new(
            MudlerConfig::parse(content, "test/gemma4_26b_balanced.txt").unwrap(),
        ));
        let p = ApexPolicy::new(ApexTier::Balanced, ArchName::Gemma4, 30, 128)
            .unwrap()
            .with_mudler_override(mudler);

        // Override wins for enumerated routed-expert tensors.
        let exp_0 = tref("blk.0.ffn_gate_exps.weight", ArchName::Gemma4, Some(0));
        assert_eq!(p.target_for(&exp_0).unwrap(), GgmlType::Q6_K);
        let exp_5 = tref("blk.5.ffn_gate_exps.weight", ArchName::Gemma4, Some(5));
        assert_eq!(p.target_for(&exp_5).unwrap(), GgmlType::Q5_K);

        // Structural fall-through: router gate stays Q5_0
        // (algorithmic hardcode; mudler doesn't enumerate it).
        let rg = tref("blk.5.ffn_gate_inp.weight", ArchName::Gemma4, Some(5));
        assert_eq!(p.target_for(&rg).unwrap(), GgmlType::Q5_0);

        // token_embd is structural; falls through to algorithmic
        // Q6_K hardcode.
        let te = tref("token_embd.weight", ArchName::Gemma4, None);
        assert_eq!(p.target_for(&te).unwrap(), GgmlType::Q6_K);

        // Norms are structural; F32.
        let nm = tref("blk.5.attn_norm.weight", ArchName::Gemma4, Some(5));
        assert_eq!(p.target_for(&nm).unwrap(), GgmlType::F32);
    }

    /// ADR-033 §P4b — closes the override-equivalence hole in the
    /// transitive byte-cmp proof.
    ///
    /// The structural-invariant test
    /// (`p4b_tier_rules_i_variant_equals_non_i_sibling` in
    /// `acceptance.rs`) pins that `tier_rules(IQuality) ==
    /// tier_rules(Quality)` (and analogously). That covers the
    /// algorithmic path through `target_for`. The other branch of
    /// `target_for` (lines 277-310 of this file) lifts queries
    /// through `mudler_override` when one is attached; that branch
    /// must ALSO be tier-independent for the §P4b transitive proof
    /// to hold end-to-end.
    ///
    /// Mechanism (verified at policy.rs:277-310, current commit):
    ///   - When the mudler override `contains_match`, it returns
    ///     `mudler.target_for(name)` which reads only the mudler
    ///     config map — never `self.tier`.
    ///   - When the override misses, structural-tensor classification
    ///     (TokenEmbd/Output/RouterGate/Norm) falls through to the
    ///     algorithmic hardcodes that don't read `self.tier` either.
    ///   - Enumerated-tensor miss surfaces `TensorNotInMudlerConfig`
    ///     identically for both tier classes.
    ///
    /// This test pins those three facts. If a future commit adds a
    /// `if self.tier.requires_imatrix() { ... }` arm inside the
    /// override branch, the test fails and §P4b's transitive proof
    /// stays sound.
    #[test]
    fn p4b_mudler_override_is_tier_independent_for_i_and_non_i_siblings() {
        use super::super::fingerprint::vendor_config_content;
        use super::super::mudler_config::MudlerConfig;

        let content = vendor_config_content("vendor/apex-quant/configs/gemma4_26b_balanced.txt")
            .expect("vendored balanced config must be baked in");
        let mudler: &'static MudlerConfig = Box::leak(Box::new(
            MudlerConfig::parse(content, "test/gemma4_26b_balanced.txt:p4b")
                .expect("vendored config must parse"),
        ));

        let non_i = ApexPolicy::new(ApexTier::Balanced, ArchName::Gemma4, 30, 128)
            .expect("non-I policy must construct")
            .with_mudler_override(mudler);
        let i = ApexPolicy::new_with_imatrix(ApexTier::IBalanced, ArchName::Gemma4, 30, 128)
            .expect("I-tier policy must construct on Gemma4")
            .with_mudler_override(mudler);

        // Walk every tensor enumerated in the mudler config + a
        // representative structural-fall-through set. For each, the
        // I and non-I policies must produce identical output.
        //
        // Pin the exact enumerated count (not a `>= floor`) so an
        // upstream vendor-sync that shrinks OR grows the fixture by
        // even one entry is surfaced. The proof rests on the closed
        // fixture; drift must be reviewed, not silently absorbed.
        const EXPECTED_ENUMERATED_COUNT: usize = 450;
        assert_eq!(
            mudler.map.len(),
            EXPECTED_ENUMERATED_COUNT,
            "§P4b override-equivalence fixture drift: gemma4_26b_balanced.txt \
             now enumerates {} tensors (was {EXPECTED_ENUMERATED_COUNT}). If \
             the upstream vendor sync intentionally changed the surface, update \
             this constant + ADR-033 §P4b at the same commit.",
            mudler.map.len(),
        );
        for (name, _expected) in mudler.map.iter() {
            let canonical_name = format!("{name}.weight");
            let layer_index = canonical_name
                .strip_prefix("blk.")
                .and_then(|r| r.find('.').map(|i| (r, i)))
                .and_then(|(r, dot)| r[..dot].parse::<usize>().ok());
            let shape = [4096usize, 1];
            let tref = TensorRef {
                name: &canonical_name,
                shape: &shape,
                source_dtype: SourceDtype::BF16,
                arch: ArchName::Gemma4,
                layer_index,
            };
            let a = non_i.target_for(&tref).expect("non-I override path");
            let b = i.target_for(&tref).expect("I override path");
            assert_eq!(
                a, b,
                "§P4b override-equivalence: tensor `{canonical_name}` \
                 produced non-I={a:?}, I={b:?} — the override path must \
                 be tier-independent (see policy.rs:277-310). Update both \
                 the override branch and this test at the same commit.",
            );
        }

        // Structural fall-through arms: must produce IDENTICAL output
        // on I and non-I sibling policies. Hard-coded set covers
        // every MoeTensorRole that escapes the `mudler.contains_match`
        // gate at policy.rs:289-309.
        for (name, layer) in [
            ("token_embd.weight", None),
            ("output.weight", None),
            ("output_norm.weight", None),
            ("blk.5.attn_norm.weight", Some(5)),
            ("blk.5.ffn_norm.weight", Some(5)),
            ("blk.5.ffn_gate_inp.weight", Some(5)),
        ] {
            let shape = [4096usize, 1];
            let tref = TensorRef {
                name,
                shape: &shape,
                source_dtype: SourceDtype::BF16,
                arch: ArchName::Gemma4,
                layer_index: layer,
            };
            let a = non_i.target_for(&tref).expect("non-I structural arm");
            let b = i.target_for(&tref).expect("I structural arm");
            assert_eq!(
                a, b,
                "§P4b override-equivalence: structural tensor `{name}` \
                 produced non-I={a:?}, I={b:?} — fall-through arms in \
                 policy.rs must stay tier-independent.",
            );
        }

        // Strict override-miss error branch (policy.rs:297-309): when
        // a non-structural-role tensor is queried but not in the
        // override map, both I and non-I must surface
        // `TensorNotInMudlerConfig` with the same source_path +
        // tensor_name. Crafted out-of-range names: gemma4_26b config
        // enumerates blk.0..29; blk.99.* sits outside.
        //
        // Per codex review on commit 0acde511: the previous test
        // covered only `Ok(_)` parity; the error branch needs the
        // same equivalence to discharge the §P4b proof for the
        // override-miss path.
        for name in [
            "blk.99.ffn_gate_exps.weight",  // RoutedExpert
            "blk.99.ffn_gate_shexp.weight", // SharedExpert
            "blk.99.attn_q.weight",         // Attention
        ] {
            let shape = [4096usize, 1];
            let tref = TensorRef {
                name,
                shape: &shape,
                source_dtype: SourceDtype::BF16,
                arch: ArchName::Gemma4,
                layer_index: Some(99),
            };
            let a_res = non_i.target_for(&tref);
            let b_res = i.target_for(&tref);
            match (&a_res, &b_res) {
                (
                    Err(ApexError::TensorNotInMudlerConfig {
                        source_path: a_path,
                        tensor_name: a_name,
                    }),
                    Err(ApexError::TensorNotInMudlerConfig {
                        source_path: b_path,
                        tensor_name: b_name,
                    }),
                ) => {
                    assert_eq!(
                        a_path, b_path,
                        "§P4b override-miss source_path drift: non-I={a_path}, I={b_path}",
                    );
                    assert_eq!(
                        a_name, b_name,
                        "§P4b override-miss tensor_name drift: non-I={a_name}, I={b_name}",
                    );
                }
                _ => panic!(
                    "§P4b override-miss expected TensorNotInMudlerConfig from both \
                     siblings for `{name}` — non-I={a_res:?}, I={b_res:?}. If the \
                     enumerated arms changed semantics, update both this assertion \
                     and ADR-033 §P4b at the same commit."
                ),
            }
        }
    }

    /// ADR-033 §Pi gate: `ApexPolicy::new` (no imatrix data threaded)
    /// MUST reject any `i-*` tier with `ImatrixRequiresInference`. The
    /// I-tier opt-in path is `new_with_imatrix`, which Phase A wires
    /// from `--imatrix <file>`.
    #[test]
    fn apex_policy_new_rejects_i_tier() {
        for tier in [ApexTier::IQuality, ApexTier::IBalanced, ApexTier::ICompact] {
            let err = ApexPolicy::new(tier, ArchName::Gemma4, 30, 128).unwrap_err();
            match err {
                ApexError::ImatrixRequiresInference {
                    tier: t,
                    arch,
                    supported_for_imatrix,
                } => {
                    assert_eq!(t, tier.cli_name());
                    assert_eq!(arch, "gemma4");
                    // Phase A widened from `&[]` to the v1 imatrix-supported
                    // arch set; the error message tells the operator which
                    // arches can accept `--imatrix <file>`.
                    assert_eq!(supported_for_imatrix, &["qwen3moe", "qwen35moe", "gemma4"]);
                }
                other => panic!("expected ImatrixRequiresInference for {tier:?}, got {other:?}"),
            }
        }
    }

    /// ADR-033 §Pi: `new_with_imatrix` accepts I-tier on the
    /// imatrix-supported arches (Gemma4, Qwen35Moe).
    #[test]
    fn apex_policy_new_with_imatrix_accepts_i_tier_for_supported_arches() {
        for tier in [ApexTier::IQuality, ApexTier::IBalanced, ApexTier::ICompact] {
            let p = ApexPolicy::new_with_imatrix(tier, ArchName::Gemma4, 30, 128).unwrap();
            assert_eq!(p.tier, tier);
            let p2 = ApexPolicy::new_with_imatrix(tier, ArchName::Qwen35Moe, 40, 128).unwrap();
            assert_eq!(p2.tier, tier);
        }
    }

    /// `new_with_imatrix` rejects I-tier on convert-only arches that
    /// aren't in `SUPPORTED_FOR_IMATRIX` — even with imatrix data the
    /// policy layer can't dispatch the request.
    #[test]
    fn apex_policy_new_with_imatrix_rejects_i_tier_for_unsupported_arch() {
        let err = ApexPolicy::new_with_imatrix(ApexTier::IBalanced, ArchName::MiniMaxM2, 32, 128)
            .unwrap_err();
        assert!(matches!(err, ApexError::ImatrixRequiresInference { .. }));
    }

    /// `new_with_imatrix` also accepts non-I tiers (it's a strict
    /// super-set of `new`). Useful so the CLI can route both paths
    /// through the same constructor when imatrix data is present.
    #[test]
    fn apex_policy_new_with_imatrix_accepts_non_i_tiers() {
        for tier in [
            ApexTier::Quality,
            ApexTier::Balanced,
            ApexTier::Compact,
            ApexTier::Mini,
        ] {
            ApexPolicy::new_with_imatrix(tier, ArchName::Gemma4, 30, 128)
                .unwrap_or_else(|e| panic!("non-I tier {tier:?} rejected: {e}"));
        }
    }

    /// The non-I siblings (`Quality`, `Balanced`, `Compact`, `Mini`)
    /// MUST still construct cleanly — the §Pi gate is I-tier-only.
    #[test]
    fn apex_policy_accepts_non_i_tiers() {
        for tier in [
            ApexTier::Quality,
            ApexTier::Balanced,
            ApexTier::Compact,
            ApexTier::Mini,
        ] {
            ApexPolicy::new(tier, ArchName::Gemma4, 30, 128)
                .unwrap_or_else(|e| panic!("non-I tier {tier:?} rejected: {e}"));
        }
    }
}