Skip to main content

ferrum_types/
auto_config.rs

1//! Startup auto-configuration and selector decision trace types.
2//!
3//! This is the typed control-plane surface for gradually replacing M3 shell
4//! env bundles with validated model/hardware/workload driven selections.
5
6use crate::{
7    is_sha256_digest, parse_bool_env_value, parse_usize_env_value, AttentionExecutionPolicy,
8    CompiledNativeOperatorIdentity, ExecutionResourceAuthority, RuntimeConfigEffect,
9    RuntimeConfigEntry, RuntimeConfigSnapshot, RuntimeConfigSource,
10    CUDA_NATIVE_ADAPTIVE_V1_MAX_SEQUENCE_TOKENS,
11};
12use serde::{Deserialize, Serialize};
13use std::collections::BTreeMap;
14use thiserror::Error;
15
16pub const M3_QWEN3_30B_A3B_INT4_PRESET: &str = "m3_qwen3_30b_a3b_int4";
17pub const QWEN25_72B_GPTQ_INT4_2X4090_LAYER_SPLIT_PRESET: &str =
18    "qwen25_72b_gptq_int4_2x4090_layer_split";
19const DEFAULT_KV_BLOCK_SIZE_TOKENS: usize = 16;
20const DEFAULT_KV_BLOCKS: usize = 2048;
21const GIB: u64 = 1024 * 1024 * 1024;
22const FA2_NATIVE_MANIFEST_KEY: &str = "FERRUM_FA2_NATIVE_MANIFEST";
23const FA2_NATIVE_ARTIFACT_KEY: &str = "FERRUM_FA2_NATIVE_ARTIFACT";
24const FA2_NATIVE_SOURCE_SHA256_KEY: &str = "FERRUM_FA2_NATIVE_SOURCE_SHA256";
25const FA2_NATIVE_INPUTS_SHA256_KEY: &str = "FERRUM_FA2_NATIVE_INPUTS_SHA256";
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28pub struct ModelCapabilities {
29    pub architecture: String,
30    pub quantization: Option<String>,
31    pub moe: Option<MoeCapabilities>,
32    pub max_context_len: Option<usize>,
33    pub num_hidden_layers: Option<usize>,
34    pub head_dim: Option<usize>,
35    pub kv_heads: Option<usize>,
36    pub estimated_weight_bytes: Option<u64>,
37    pub recurrent_state_bytes_per_sequence: Option<u64>,
38    pub supported_dtypes: Vec<String>,
39    pub graph_safe_moe: bool,
40}
41
42impl ModelCapabilities {
43    pub fn unknown() -> Self {
44        Self {
45            architecture: "unknown".to_string(),
46            quantization: None,
47            moe: None,
48            max_context_len: None,
49            num_hidden_layers: None,
50            head_dim: None,
51            kv_heads: None,
52            estimated_weight_bytes: None,
53            recurrent_state_bytes_per_sequence: None,
54            supported_dtypes: vec!["fp16".to_string(), "fp32".to_string()],
55            graph_safe_moe: false,
56        }
57    }
58
59    pub fn qwen3_30b_a3b_gptq_int4() -> Self {
60        Self {
61            architecture: "qwen3_moe".to_string(),
62            quantization: Some("gptq_int4".to_string()),
63            moe: Some(MoeCapabilities {
64                num_experts: 128,
65                experts_per_token: 8,
66                moe_intermediate_size: Some(768),
67            }),
68            max_context_len: Some(40960),
69            num_hidden_layers: Some(48),
70            head_dim: Some(128),
71            kv_heads: Some(4),
72            // Conservative GPTQ int4 weight footprint including quant scales
73            // and loader/runtime overhead. This keeps the RTX 4090 M3 preset
74            // at the historical 2048 KV blocks while still allowing smaller
75            // GPUs to be downgraded before startup allocation.
76            estimated_weight_bytes: Some(18 * GIB),
77            recurrent_state_bytes_per_sequence: None,
78            supported_dtypes: vec!["fp16".to_string()],
79            graph_safe_moe: false,
80        }
81    }
82
83    pub fn qwen25_72b_gptq_int4() -> Self {
84        Self {
85            architecture: "qwen2".to_string(),
86            quantization: Some("gptq_int4".to_string()),
87            moe: None,
88            max_context_len: Some(32_768),
89            num_hidden_layers: Some(80),
90            head_dim: Some(128),
91            kv_heads: Some(8),
92            estimated_weight_bytes: Some(39 * GIB),
93            recurrent_state_bytes_per_sequence: None,
94            supported_dtypes: vec!["fp16".to_string()],
95            graph_safe_moe: false,
96        }
97    }
98}
99
100#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
101pub struct MoeCapabilities {
102    pub num_experts: usize,
103    pub experts_per_token: usize,
104    pub moe_intermediate_size: Option<usize>,
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
108pub struct HardwareCapabilities {
109    pub backend: String,
110    pub cuda_runtime: Option<String>,
111    pub compute_capability: Option<String>,
112    pub vram_bytes: Option<u64>,
113    pub sm_count: Option<u32>,
114    pub supported_dtypes: Vec<String>,
115    pub supported_kv_dtypes: Vec<String>,
116    pub graph_support: bool,
117    pub compiled_features: CompiledKernelFeatures,
118}
119
120impl HardwareCapabilities {
121    pub fn unknown() -> Self {
122        Self {
123            backend: "unknown".to_string(),
124            cuda_runtime: None,
125            compute_capability: None,
126            vram_bytes: None,
127            sm_count: None,
128            supported_dtypes: vec!["fp16".to_string(), "fp32".to_string()],
129            supported_kv_dtypes: vec!["fp16".to_string()],
130            graph_support: false,
131            compiled_features: CompiledKernelFeatures::default(),
132        }
133    }
134
135    pub fn rtx4090_cuda(features: CompiledKernelFeatures) -> Self {
136        Self {
137            backend: "cuda".to_string(),
138            cuda_runtime: None,
139            compute_capability: Some("8.9".to_string()),
140            vram_bytes: Some(24 * 1024 * 1024 * 1024),
141            sm_count: Some(128),
142            supported_dtypes: vec!["fp16".to_string(), "fp32".to_string()],
143            supported_kv_dtypes: vec!["fp16".to_string(), "int8".to_string()],
144            graph_support: true,
145            compiled_features: features,
146        }
147    }
148}
149
150#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
151pub struct CompiledNativeOperatorArtifact {
152    pub manifest_path: String,
153    pub artifact_path: String,
154    pub source_package_sha256: String,
155    pub inputs_sha256: String,
156    pub binary_sha256: String,
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
160pub struct CompiledKernelFeatures {
161    pub cuda: bool,
162    pub vllm_paged_attn: bool,
163    pub vllm_moe_marlin: bool,
164    pub cuda_graph: bool,
165    pub greedy_argmax: bool,
166    pub fa2_source: bool,
167    pub fa2_direct_ffi: bool,
168    pub fa2_native_operator_artifact: bool,
169    #[serde(default, skip_serializing_if = "Option::is_none")]
170    pub fa2_native_operator_artifact_metadata: Option<CompiledNativeOperatorArtifact>,
171    #[serde(default, skip_serializing_if = "Vec::is_empty")]
172    pub native_operator_artifacts: Vec<CompiledNativeOperatorIdentity>,
173}
174
175impl Default for CompiledKernelFeatures {
176    fn default() -> Self {
177        Self {
178            cuda: false,
179            vllm_paged_attn: false,
180            vllm_moe_marlin: false,
181            cuda_graph: false,
182            greedy_argmax: false,
183            fa2_source: false,
184            fa2_direct_ffi: false,
185            fa2_native_operator_artifact: false,
186            fa2_native_operator_artifact_metadata: None,
187            native_operator_artifacts: Vec::new(),
188        }
189    }
190}
191
192impl CompiledKernelFeatures {
193    pub fn m3_fast_path_without_fa2() -> Self {
194        Self {
195            cuda: true,
196            vllm_paged_attn: true,
197            vllm_moe_marlin: true,
198            cuda_graph: true,
199            greedy_argmax: true,
200            fa2_source: false,
201            fa2_direct_ffi: false,
202            fa2_native_operator_artifact: false,
203            fa2_native_operator_artifact_metadata: None,
204            native_operator_artifacts: Vec::new(),
205        }
206    }
207
208    pub fn m3_fast_path_with_source_fa2() -> Self {
209        Self {
210            fa2_source: true,
211            ..Self::m3_fast_path_without_fa2()
212        }
213    }
214
215    pub fn m3_fast_path_with_native_fa2_artifact() -> Self {
216        Self {
217            fa2_native_operator_artifact: true,
218            fa2_native_operator_artifact_metadata: Some(CompiledNativeOperatorArtifact {
219                manifest_path: "/tmp/native/fa2/native_operator_manifest.json".to_string(),
220                artifact_path: "/tmp/native/fa2/libferrum_native_fa2.a".to_string(),
221                source_package_sha256:
222                    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_string(),
223                inputs_sha256: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
224                    .to_string(),
225                binary_sha256: "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
226                    .to_string(),
227            }),
228            ..Self::m3_fast_path_without_fa2()
229        }
230    }
231}
232
233#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
234pub struct WorkloadProfile {
235    pub preset: Option<String>,
236    pub serving_mode: String,
237    pub target_concurrency: usize,
238    pub prompt_length_class: String,
239    pub output_length_class: String,
240    pub priority: WorkloadPriority,
241}
242
243impl WorkloadProfile {
244    pub fn serving_default() -> Self {
245        Self {
246            preset: None,
247            serving_mode: "openai_chat".to_string(),
248            target_concurrency: 1,
249            prompt_length_class: "unknown".to_string(),
250            output_length_class: "unknown".to_string(),
251            priority: WorkloadPriority::Balanced,
252        }
253    }
254
255    pub fn serving_default_for_hardware(hardware: &HardwareCapabilities) -> Self {
256        let mut profile = Self::serving_default();
257        if hardware.backend.eq_ignore_ascii_case("cuda")
258            || hardware.backend.eq_ignore_ascii_case("metal")
259        {
260            profile.target_concurrency = hardware
261                .vram_bytes
262                .map(vram_default_max_sequences)
263                .unwrap_or(4)
264                .max(1);
265        }
266        profile
267    }
268
269    pub fn m3_qwen3_30b_a3b_int4() -> Self {
270        Self {
271            preset: Some(M3_QWEN3_30B_A3B_INT4_PRESET.to_string()),
272            serving_mode: "bench_serve".to_string(),
273            target_concurrency: 32,
274            prompt_length_class: "random_256".to_string(),
275            output_length_class: "random_128".to_string(),
276            priority: WorkloadPriority::Throughput,
277        }
278    }
279
280    pub fn qwen25_72b_gptq_int4_2x4090_layer_split() -> Self {
281        Self {
282            preset: Some(QWEN25_72B_GPTQ_INT4_2X4090_LAYER_SPLIT_PRESET.to_string()),
283            serving_mode: "bench_serve".to_string(),
284            target_concurrency: 16,
285            prompt_length_class: "random_256".to_string(),
286            output_length_class: "random_128".to_string(),
287            priority: WorkloadPriority::Throughput,
288        }
289    }
290
291    fn is_m3_preset(&self) -> bool {
292        self.is_preset(M3_QWEN3_30B_A3B_INT4_PRESET)
293    }
294
295    fn is_preset(&self, preset: &str) -> bool {
296        self.preset.as_deref() == Some(preset)
297    }
298}
299
300impl Default for WorkloadProfile {
301    fn default() -> Self {
302        Self::serving_default()
303    }
304}
305
306#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
307#[serde(rename_all = "snake_case")]
308pub enum WorkloadPriority {
309    Latency,
310    Throughput,
311    Balanced,
312}
313
314#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
315pub struct ResolvedFerrumConfig {
316    pub schema_version: u32,
317    pub preset: Option<String>,
318    #[serde(default)]
319    pub execution_resource_authority: ExecutionResourceAuthority,
320    #[serde(default)]
321    pub requested_attention_policy: Option<AttentionExecutionPolicy>,
322    #[serde(default)]
323    pub compiled_attention_policy: Option<AttentionExecutionPolicy>,
324    pub runtime_config: RuntimeConfigSnapshot,
325    pub model_capabilities: ModelCapabilities,
326    pub hardware_capabilities: HardwareCapabilities,
327    pub workload_profile: WorkloadProfile,
328    pub decisions: Vec<AutoConfigDecision>,
329}
330
331impl ResolvedFerrumConfig {
332    pub fn effective_config_document(&self) -> serde_json::Value {
333        let backend = self.hardware_capabilities.backend.clone();
334        let requested_gpu_devices = self
335            .runtime_csv_usize("FERRUM_REQUESTED_GPU_DEVICES")
336            .or_else(|| default_gpu_devices_for_backend(&backend));
337        let selected_gpu_devices = self
338            .runtime_csv_usize("FERRUM_SELECTED_GPU_DEVICES")
339            .or_else(|| requested_gpu_devices.clone())
340            .or_else(|| default_gpu_devices_for_backend(&backend));
341        let cuda_device_count = self
342            .runtime_usize("FERRUM_CUDA_DEVICE_COUNT")
343            .or_else(|| {
344                backend.eq_ignore_ascii_case("cuda").then(|| {
345                    selected_gpu_devices
346                        .as_ref()
347                        .map(|devices| devices.len())
348                        .unwrap_or(1)
349                })
350            })
351            .unwrap_or(0);
352        let selected_distributed_strategy = self
353            .runtime_entry_value("FERRUM_SELECTED_DISTRIBUTED_STRATEGY")
354            .unwrap_or_else(|| {
355                if selected_gpu_devices
356                    .as_ref()
357                    .map(|devices| devices.len() > 1)
358                    .unwrap_or(false)
359                {
360                    "layer_split".to_string()
361                } else if backend.eq_ignore_ascii_case("cuda") {
362                    "single_gpu".to_string()
363                } else {
364                    "none".to_string()
365                }
366            });
367        let selected_layer_split_plan =
368            self.runtime_entry_value("FERRUM_SELECTED_LAYER_SPLIT_PLAN");
369        let selected_layer_split_stages =
370            self.runtime_json_value("FERRUM_SELECTED_LAYER_SPLIT_STAGES");
371        let selected_layer_split_stage_count = selected_layer_split_stages
372            .as_ref()
373            .and_then(|value| value.as_array().map(|stages| stages.len()))
374            .or_else(|| {
375                selected_layer_split_plan
376                    .as_ref()
377                    .and_then(|_| selected_gpu_devices.as_ref().map(Vec::len))
378            });
379        let requested_pipeline_mode = self.runtime_entry_value("FERRUM_LAYER_SPLIT_PIPELINE_MODE");
380        let selected_pipeline_mode = if selected_layer_split_plan.is_some() {
381            requested_pipeline_mode.unwrap_or_else(|| {
382                if selected_layer_split_stage_count == Some(2) {
383                    "overlapped".to_string()
384                } else {
385                    "batch".to_string()
386                }
387            })
388        } else {
389            "sequential".to_string()
390        };
391        let selected_max_sequences = self.selected_usize("max_sequences");
392        let selected_microbatch_size = if selected_layer_split_plan.is_some() {
393            selected_max_sequences.map(|max_sequences| {
394                if selected_pipeline_mode == "overlapped" {
395                    max_sequences.div_ceil(2).max(1)
396                } else {
397                    max_sequences
398                }
399            })
400        } else {
401            Some(1)
402        };
403        let selected_stage_bridge = selected_layer_split_plan.as_ref().map(|_| "host");
404        let selected_max_model_len = self.selected_usize("max_model_len");
405        let selected_kv_capacity = self.runtime_usize("FERRUM_KV_CAPACITY");
406        let selected_max_batched_tokens = self.selected_usize("max_batched_tokens");
407        let selected_recurrent_state_max_slots = self.selected_recurrent_state_max_slots();
408        let selected_admission_limit = effective_admission_limit(
409            self.execution_resource_authority,
410            selected_max_sequences,
411            selected_recurrent_state_max_slots,
412        );
413        let selected_attention_policy = self.selected_string("attention_execution_policy");
414        let selected_attention_impl = self.selected_string("attention_decode_backend");
415        serde_json::json!({
416            "schema_version": 1,
417            "preset": self.preset,
418            "env_hash": self.runtime_env_hash(),
419            "backend": backend.clone(),
420            "execution_resource_authority": self.execution_resource_authority,
421            "requested_gpu_devices": requested_gpu_devices.clone(),
422            "selected_gpu_devices": selected_gpu_devices.clone(),
423            "cuda_device_count": cuda_device_count,
424            "selected_distributed_strategy": selected_distributed_strategy.clone(),
425            "selected_layer_split_plan": selected_layer_split_plan.clone(),
426            "selected_layer_split_stages": selected_layer_split_stages,
427            "selected_pipeline_mode": selected_pipeline_mode,
428            "selected_microbatch_size": selected_microbatch_size,
429            "selected_stage_bridge": selected_stage_bridge,
430            "selected_weight_placement": if selected_layer_split_plan.is_some() { "layer_split" } else { "single_device" },
431            "selected_kv_layout": if backend.eq_ignore_ascii_case("cpu") { "contiguous" } else { "paged" },
432            "selected_attention_impl": selected_attention_impl,
433            "selected_attention_policy": selected_attention_policy,
434            "attention_execution": self.attention_execution_document(),
435            "selected_graph_mode": self.selected_graph_mode(),
436            "selected_max_sequences": selected_max_sequences,
437            "selected_max_model_len": selected_max_model_len,
438            "selected_kv_capacity": selected_kv_capacity,
439            "selected_max_batched_tokens": selected_max_batched_tokens,
440            "selected_recurrent_state_max_slots": selected_recurrent_state_max_slots,
441            "selected_admission_limit": selected_admission_limit,
442            "entries": self.runtime_config.entries,
443            "model_capabilities": self.model_capabilities,
444            "hardware_capabilities": self.hardware_capabilities,
445            "workload_profile": self.workload_profile,
446            "admission": self.admission_summary_document(),
447            "decisions": self.decisions,
448        })
449    }
450
451    pub fn admission_summary_document(&self) -> serde_json::Value {
452        let max_sequences = self.selected_usize("max_sequences");
453        let recurrent_state_max_slots = self.selected_recurrent_state_max_slots();
454        let effective_max_concurrent = effective_admission_limit(
455            self.execution_resource_authority,
456            max_sequences,
457            recurrent_state_max_slots,
458        );
459        let kv_blocks = self.selected_usize("kv_block_count");
460        let max_batched_tokens = self.selected_usize("max_batched_tokens");
461        let max_model_len = self.selected_usize("max_model_len");
462        let kv_capacity_tokens =
463            kv_blocks.map(|blocks| blocks.saturating_mul(DEFAULT_KV_BLOCK_SIZE_TOKENS));
464        let kv_bytes_per_token = kv_cache_bytes_per_token_for_model(&self.model_capabilities);
465        let recurrent_budget =
466            recurrent_state_budget_for(&self.model_capabilities, &self.hardware_capabilities);
467        let scheduler_policy = self
468            .selected_string("scheduler_admission_policy")
469            .unwrap_or_else(|| "unknown".to_string());
470        serde_json::json!({
471            "schema_version": 1,
472            "backend": self.hardware_capabilities.backend,
473            "model_architecture": self.model_capabilities.architecture,
474            "resource_authority": self.execution_resource_authority,
475            "source": "startup_preflight",
476            "scheduler_policy": scheduler_policy,
477            "effective_max_concurrent": effective_max_concurrent,
478            "maximum_active_sequences": max_sequences,
479            "maximum_scheduled_tokens": max_batched_tokens,
480            "queue_depth": 0u64,
481            "active_prefill": 0u64,
482            "active_decode": 0u64,
483            "current_batch_size": 0u64,
484            "rejected_requests_total": 0u64,
485            "failed_requests_total": 0u64,
486            "completed_requests_total": 0u64,
487            "max_sequences": max_sequences,
488            "recurrent_state_max_slots": if self.execution_resource_authority == ExecutionResourceAuthority::LegacyEngine {
489                recurrent_state_max_slots
490            } else {
491                None
492            },
493            "legacy_recurrent_state_max_slots_estimate": recurrent_state_max_slots,
494            "legacy_recurrent_state_limit_applies": self.execution_resource_authority == ExecutionResourceAuthority::LegacyEngine,
495            "kv_block_count": kv_blocks,
496            "kv_block_size_tokens": DEFAULT_KV_BLOCK_SIZE_TOKENS,
497            "kv_capacity_tokens": kv_capacity_tokens,
498            "max_model_length": max_model_len,
499            "max_batched_tokens": max_batched_tokens,
500            "memory_estimate": {
501                "vram_bytes": self.hardware_capabilities.vram_bytes,
502                "estimated_weight_bytes": self.model_capabilities.estimated_weight_bytes,
503                "kv_bytes_per_token": kv_bytes_per_token,
504                "recurrent_state_bytes_per_sequence": self.model_capabilities.recurrent_state_bytes_per_sequence,
505                "recurrent_state_budget_bytes": recurrent_budget.map(|budget| budget.remaining_bytes),
506                "recurrent_state_budget_raw_slots": recurrent_budget.map(|budget| budget.raw_slots),
507                "recurrent_state_budget_max_slots": recurrent_budget.map(|budget| budget.floored_slots),
508                "recurrent_state_capacity_bytes": match (recurrent_state_max_slots, self.model_capabilities.recurrent_state_bytes_per_sequence) {
509                    (Some(slots), Some(bytes_per_sequence)) => {
510                        (slots as u64).checked_mul(bytes_per_sequence)
511                    }
512                    _ => None,
513                },
514                "kv_capacity_bytes": match (kv_capacity_tokens, kv_bytes_per_token) {
515                    (Some(tokens), Some(bytes_per_token)) => {
516                        (tokens as u64).checked_mul(bytes_per_token)
517                    }
518                    _ => None,
519                },
520            },
521        })
522    }
523
524    pub fn decision_trace_jsonl(&self) -> Result<String, serde_json::Error> {
525        let mut out = String::new();
526        for decision in &self.decisions {
527            out.push_str(&serde_json::to_string(decision)?);
528            out.push('\n');
529        }
530        Ok(out)
531    }
532
533    pub fn runtime_env_hash(&self) -> String {
534        use sha2::{Digest, Sha256};
535
536        let bytes = serde_json::to_vec(&self.runtime_config.entries).unwrap_or_default();
537        let digest = Sha256::digest(bytes);
538        format!("sha256:{digest:x}")
539    }
540
541    fn selected_usize(&self, selection: &str) -> Option<usize> {
542        self.selected_string(selection)?.parse().ok()
543    }
544
545    fn selected_string(&self, selection: &str) -> Option<String> {
546        self.decisions
547            .iter()
548            .find(|decision| decision.selection == selection)
549            .map(|decision| decision.selected.clone())
550    }
551
552    fn selected_recurrent_state_max_slots(&self) -> Option<usize> {
553        self.selected_usize("recurrent_state_max_slots")
554            .or_else(|| self.runtime_usize("FERRUM_RECURRENT_STATE_MAX_SLOTS"))
555    }
556
557    fn selected_graph_mode(&self) -> Option<String> {
558        let decode_graph = self.selected_string("decode_graph_policy");
559        if decode_graph
560            .as_deref()
561            .is_some_and(|mode| mode != "graph_disabled")
562        {
563            return decode_graph;
564        }
565        self.selected_string("moe_graph_policy")
566    }
567
568    fn attention_execution_document(&self) -> serde_json::Value {
569        let compiled_policy = self
570            .compiled_attention_policy
571            .map(AttentionExecutionPolicy::as_runtime_value);
572        let requested_policy = self
573            .requested_attention_policy
574            .map(AttentionExecutionPolicy::as_runtime_value);
575        if self.execution_resource_authority != ExecutionResourceAuthority::PlanRuntime {
576            return serde_json::json!({
577                "schema_version": 1,
578                "resource_authority": self.execution_resource_authority,
579                "requested_policy": null,
580                "compiled_policy": null,
581                "selection_scope": "legacy_runtime",
582                "observed_variant_source": "legacy_backend_profile",
583                "legacy_attention_keys_apply": true,
584            });
585        }
586        let native_adaptive =
587            compiled_policy == Some(AttentionExecutionPolicy::NativeAdaptive.as_runtime_value());
588        serde_json::json!({
589            "schema_version": 1,
590            "resource_authority": self.execution_resource_authority,
591            "requested_policy": requested_policy,
592            "compiled_policy": compiled_policy,
593            "selection_scope": "per_invocation",
594            "observed_variant_source": "vnext.device_native_work",
595            "legacy_attention_keys_apply": false,
596            "decode": if native_adaptive && self.hardware_capabilities.backend.eq_ignore_ascii_case("cuda") {
597                serde_json::json!({
598                    "selector": "sequence_frontier",
599                    "short_variant": "vllm_paged_attention_v1_addressed",
600                    "long_variant": "vllm_paged_attention_v2_addressed",
601                    "short_max_sequence_tokens": CUDA_NATIVE_ADAPTIVE_V1_MAX_SEQUENCE_TOKENS,
602                })
603            } else {
604                serde_json::json!({
605                    "selector": "compiled_provider",
606                })
607            },
608            "prefill": {
609                "selector": "admitted_shape",
610                "variants_are_observed_at_runtime": true,
611            },
612        })
613    }
614
615    fn runtime_entry_value(&self, key: &str) -> Option<String> {
616        self.runtime_config
617            .entries
618            .iter()
619            .find(|entry| entry.key == key)
620            .map(|entry| entry.effective_value.clone())
621    }
622
623    fn runtime_usize(&self, key: &str) -> Option<usize> {
624        self.runtime_entry_value(key)?.parse().ok()
625    }
626
627    fn runtime_csv_usize(&self, key: &str) -> Option<Vec<usize>> {
628        let raw = self.runtime_entry_value(key)?;
629        let mut out = Vec::new();
630        for part in raw.split(',') {
631            let value = part.trim();
632            if value.is_empty() {
633                return None;
634            }
635            out.push(value.parse().ok()?);
636        }
637        Some(out)
638    }
639
640    fn runtime_json_value(&self, key: &str) -> Option<serde_json::Value> {
641        serde_json::from_str(&self.runtime_entry_value(key)?).ok()
642    }
643}
644
645#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
646pub struct AutoConfigDecision {
647    pub schema_version: u32,
648    pub selection: String,
649    pub selected: String,
650    pub source: AutoConfigSource,
651    pub source_key: Option<String>,
652    pub candidates: Vec<String>,
653    pub rejected: Vec<RejectedCandidate>,
654    pub affects: Vec<RuntimeConfigEffect>,
655}
656
657#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
658pub struct RejectedCandidate {
659    pub value: String,
660    pub reason: String,
661}
662
663#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
664#[serde(rename_all = "snake_case")]
665pub enum AutoConfigSource {
666    Default,
667    Cli,
668    ConfigFile,
669    Env,
670    ScriptCase,
671    ModelMetadata,
672    HardwareCapability,
673    MemoryProfile,
674    WorkloadPreset,
675    CompiledFeature,
676}
677
678#[derive(Debug, Clone, PartialEq, Eq, Error)]
679pub enum AutoConfigError {
680    #[error("{key}: invalid override: {reason}")]
681    InvalidOverride { key: String, reason: String },
682    #[error("{selection}: unsupported combination: {reason}")]
683    UnsupportedCombination { selection: String, reason: String },
684}
685
686pub struct FerrumConfigBuilder {
687    runtime_config: RuntimeConfigSnapshot,
688    model: ModelCapabilities,
689    hardware: HardwareCapabilities,
690    workload: WorkloadProfile,
691    execution_resource_authority: ExecutionResourceAuthority,
692}
693
694impl FerrumConfigBuilder {
695    pub fn new(runtime_config: RuntimeConfigSnapshot) -> Self {
696        Self {
697            runtime_config,
698            model: ModelCapabilities::unknown(),
699            hardware: HardwareCapabilities::unknown(),
700            workload: WorkloadProfile::default(),
701            execution_resource_authority: ExecutionResourceAuthority::LegacyEngine,
702        }
703    }
704
705    pub fn m3_qwen3_30b_a3b_int4(runtime_config: RuntimeConfigSnapshot) -> Self {
706        Self::new(runtime_config)
707            .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
708            .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(
709                CompiledKernelFeatures::m3_fast_path_without_fa2(),
710            ))
711            .with_workload_profile(WorkloadProfile::m3_qwen3_30b_a3b_int4())
712    }
713
714    pub fn with_model_capabilities(mut self, model: ModelCapabilities) -> Self {
715        self.model = model;
716        self
717    }
718
719    pub fn with_hardware_capabilities(mut self, hardware: HardwareCapabilities) -> Self {
720        self.hardware = hardware;
721        self
722    }
723
724    pub fn with_workload_profile(mut self, workload: WorkloadProfile) -> Self {
725        self.workload = workload;
726        self
727    }
728
729    pub fn with_execution_resource_authority(
730        mut self,
731        authority: ExecutionResourceAuthority,
732    ) -> Self {
733        self.execution_resource_authority = authority;
734        self
735    }
736
737    pub fn resolve(self) -> Result<ResolvedFerrumConfig, AutoConfigError> {
738        let mut decisions = Vec::new();
739        let cuda_backend = self.is_cuda_backend();
740        let plan_runtime =
741            self.execution_resource_authority == ExecutionResourceAuthority::PlanRuntime;
742        if plan_runtime {
743            for key in [
744                "FERRUM_USE_VLLM_PAGED_ATTN",
745                "FERRUM_VLLM_PAGED_ATTN_V1_SHORT",
746            ] {
747                if self.entry(key).is_some() {
748                    return Err(AutoConfigError::InvalidOverride {
749                        key: key.to_owned(),
750                        reason: format!(
751                            "legacy attention control does not apply to PlanRuntime; use FERRUM_ATTENTION_POLICY=auto|portable|native-adaptive"
752                        ),
753                    });
754                }
755            }
756        }
757        let plan_attention = plan_runtime
758            .then(|| self.resolve_plan_attention_policy())
759            .transpose()?;
760        // Any CUDA GPTQ/INT4 MoE model gets the vLLM-Marlin fast MoE path when
761        // the kernel is compiled — not only the m3 bench preset. `ferrum run`
762        // resolves with the serving-default workload (not the m3 preset), so
763        // without this it silently fell back to the slow host-route MoE
764        // (~9.7 vs ~59 tok/s on a 4090 for Qwen3-30B-A3B). Capability-gated,
765        // never model-name-gated.
766        let cuda_gptq_moe = cuda_backend
767            && self.model.moe.is_some()
768            && self.model.quantization.as_deref().is_some_and(|q| {
769                let q = q.to_ascii_lowercase();
770                q.contains("gptq") || q.contains("int4")
771            });
772        let cuda_qwen_moe = cuda_backend
773            && self.model.moe.is_some()
774            && qwen_moe_architecture_uses_vllm_paged_attn(&self.model.architecture);
775        let use_vllm_paged_attn = self.bool_value(
776            "FERRUM_USE_VLLM_PAGED_ATTN",
777            (self.workload.is_m3_preset() || cuda_qwen_moe)
778                && cuda_backend
779                && self.hardware.compiled_features.vllm_paged_attn,
780            AutoConfigSource::WorkloadPreset,
781        )?;
782        let fa_layout =
783            self.bool_value("FERRUM_FA_LAYOUT_VARLEN", false, AutoConfigSource::Default)?;
784        let fa2_source = self.bool_value("FERRUM_FA2_SOURCE", false, AutoConfigSource::Default)?;
785        let shim_present = self.raw("FERRUM_FA2_DIRECT_FFI_SHIM").is_some();
786        let fa2_direct_ffi = self.bool_value(
787            "FERRUM_FA2_DIRECT_FFI",
788            shim_present,
789            if shim_present {
790                AutoConfigSource::Env
791            } else {
792                AutoConfigSource::Default
793            },
794        )?;
795        let fa2_native_manifest = self.optional_string_value(FA2_NATIVE_MANIFEST_KEY)?;
796        let fa2_native_artifact = self.optional_string_value(FA2_NATIVE_ARTIFACT_KEY)?;
797        let fa2_native_source_sha256 = self.optional_string_value(FA2_NATIVE_SOURCE_SHA256_KEY)?;
798        let fa2_native_inputs_sha256 = self.optional_string_value(FA2_NATIVE_INPUTS_SHA256_KEY)?;
799        let vllm_v1_short = self.bool_value(
800            "FERRUM_VLLM_PAGED_ATTN_V1_SHORT",
801            use_vllm_paged_attn.value && self.model.head_dim.unwrap_or(128) <= 128,
802            AutoConfigSource::Default,
803        )?;
804        let vllm_moe = self.bool_value(
805            "FERRUM_VLLM_MOE",
806            (cuda_gptq_moe || (self.workload.is_m3_preset() && cuda_backend))
807                && self.hardware.compiled_features.vllm_moe_marlin,
808            AutoConfigSource::WorkloadPreset,
809        )?;
810        let device_route = self.bool_value(
811            "FERRUM_MOE_DEVICE_ROUTE",
812            vllm_moe.value,
813            AutoConfigSource::WorkloadPreset,
814        )?;
815        let pair_ids = self.bool_value(
816            "FERRUM_VLLM_MOE_PAIR_IDS",
817            vllm_moe.value,
818            AutoConfigSource::WorkloadPreset,
819        )?;
820        let graph = self.bool_value("FERRUM_MOE_GRAPH", false, AutoConfigSource::WorkloadPreset)?;
821        let batched_graph =
822            self.bool_value("FERRUM_BATCHED_GRAPH", false, AutoConfigSource::Default)?;
823        let reusable_execution = self.bool_value(
824            "FERRUM_REUSABLE_EXECUTION",
825            cuda_backend
826                && self.hardware.graph_support
827                && self.hardware.compiled_features.cuda_graph,
828            AutoConfigSource::HardwareCapability,
829        )?;
830        let unified_graph =
831            self.bool_value("FERRUM_UNIFIED_GRAPH", false, AutoConfigSource::Default)?;
832        let unified_graph_layers_only = self.bool_value(
833            "FERRUM_UNIFIED_GRAPH_LAYERS_ONLY",
834            false,
835            AutoConfigSource::Default,
836        )?;
837        let unified_graph_lm_head_eager = self.bool_value(
838            "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER",
839            false,
840            AutoConfigSource::Default,
841        )?;
842        let greedy = self.bool_value(
843            "FERRUM_GREEDY_ARGMAX",
844            (cuda_backend || self.hardware.backend.eq_ignore_ascii_case("metal"))
845                && self.hardware.compiled_features.greedy_argmax,
846            AutoConfigSource::HardwareCapability,
847        )?;
848        let prefix_cache = self.bool_value(
849            "FERRUM_PREFIX_CACHE",
850            false,
851            if self.workload.is_m3_preset() {
852                AutoConfigSource::WorkloadPreset
853            } else {
854                AutoConfigSource::Default
855            },
856        )?;
857        let default_max_sequences = self.default_max_sequences();
858        let max_sequences = self.usize_value(
859            "FERRUM_PAGED_MAX_SEQS",
860            default_max_sequences.value,
861            default_max_sequences.source,
862        )?;
863        if plan_runtime && self.entry("FERRUM_RECURRENT_STATE_MAX_SLOTS").is_some() {
864            return self.invalid(
865                "FERRUM_RECURRENT_STATE_MAX_SLOTS",
866                "legacy engine recurrent-state slots do not control the plan runtime; use max sequences as the protocol ceiling and let dynamic resource admission defer on live capacity",
867            );
868        }
869        let default_recurrent_state_max_slots = (!plan_runtime)
870            .then(|| self.default_recurrent_state_max_slots(&max_sequences))
871            .flatten();
872        let recurrent_state_max_slots = if default_recurrent_state_max_slots.is_some()
873            || self.entry("FERRUM_RECURRENT_STATE_MAX_SLOTS").is_some()
874        {
875            let default = default_recurrent_state_max_slots
876                .as_ref()
877                .unwrap_or(&max_sequences);
878            Some(self.usize_value(
879                "FERRUM_RECURRENT_STATE_MAX_SLOTS",
880                default.value,
881                default.source,
882            )?)
883        } else {
884            None
885        };
886        let default_kv_blocks = self.default_kv_blocks(&max_sequences);
887        let kv_blocks = self.usize_value(
888            "FERRUM_KV_MAX_BLOCKS",
889            default_kv_blocks.value,
890            default_kv_blocks.source,
891        )?;
892        let default_max_batched_tokens =
893            self.default_max_batched_tokens(&max_sequences, &kv_blocks);
894        let max_batched_tokens = self.usize_value(
895            "FERRUM_MAX_BATCHED_TOKENS",
896            default_max_batched_tokens.value,
897            default_max_batched_tokens.source,
898        )?;
899        let max_model_len = self.optional_usize_value("FERRUM_MAX_MODEL_LEN")?;
900        let default_prefill_first_until_active =
901            self.default_prefill_first_until_active(&max_sequences);
902        if !plan_runtime {
903            self.validate_attention(
904                use_vllm_paged_attn.value,
905                fa_layout.value,
906                fa2_source.value,
907                fa2_direct_ffi.value,
908                shim_present,
909                vllm_v1_short.value,
910            )?;
911            self.validate_fa2_native_artifact(
912                fa2_native_manifest.as_ref(),
913                fa2_native_artifact.as_ref(),
914                fa2_native_source_sha256.as_ref(),
915                fa2_native_inputs_sha256.as_ref(),
916                fa2_source.value,
917                fa2_direct_ffi.value,
918            )?;
919        }
920        self.validate_moe(
921            vllm_moe.value,
922            device_route.value,
923            pair_ids.value,
924            graph.value,
925        )?;
926        self.validate_batched_graph(batched_graph.value)?;
927        self.validate_unified_graph(
928            unified_graph.value,
929            unified_graph_layers_only.value,
930            unified_graph_lm_head_eager.value,
931        )?;
932        self.validate_memory(
933            kv_blocks.value,
934            max_sequences.value,
935            recurrent_state_max_slots.as_ref().map(|slots| slots.value),
936            max_batched_tokens.value,
937            max_model_len.as_ref().map(|value| value.value),
938        )?;
939        self.validate_dtypes()?;
940        self.validate_layer_split_pipeline_mode()?;
941        self.validate_sampling(greedy.value)?;
942
943        if let Some((_, compiled_attention)) = plan_attention.as_ref() {
944            decisions.push(self.plan_attention_execution_decision(compiled_attention));
945            decisions.push(self.plan_attention_phase_decision(
946                "attention_prefill_mixed_backend",
947                compiled_attention,
948            ));
949            decisions.push(
950                self.plan_attention_phase_decision("attention_decode_backend", compiled_attention),
951            );
952        } else {
953            decisions.push(self.attention_prefill_decision(
954                use_vllm_paged_attn.clone(),
955                fa_layout,
956                fa2_source,
957                fa2_direct_ffi,
958            ));
959            decisions.push(self.fa2_native_artifact_decision(
960                fa2_native_manifest.as_ref(),
961                fa2_native_artifact.as_ref(),
962            ));
963            decisions.push(self.fa2_native_runtime_selection_decision(
964                fa2_native_manifest.as_ref(),
965                fa2_native_artifact.as_ref(),
966            ));
967            decisions.push(
968                self.attention_decode_decision(use_vllm_paged_attn.clone(), vllm_v1_short.clone()),
969            );
970        }
971        // Materialize the auto-resolved fast-path MoE knobs into the effective
972        // config BEFORE moe_decision consumes them, so they reach the model
973        // (which reads FERRUM_*, not the decisions). Only auto-derived values —
974        // user/env entries are already present. Without this, `ferrum run`'s
975        // non-preset path resolved FERRUM_VLLM_MOE as a decision only and the
976        // model never saw it (~9.7 vs ~59 tok/s on a 4090 for Qwen3-30B-A3B).
977        let mut runtime_config = self.runtime_config.clone();
978        let legacy_attention_values = [
979            ("FERRUM_USE_VLLM_PAGED_ATTN", &use_vllm_paged_attn),
980            ("FERRUM_VLLM_PAGED_ATTN_V1_SHORT", &vllm_v1_short),
981        ];
982        for (key, resolved) in legacy_attention_values
983            .into_iter()
984            .filter(|_| !plan_runtime)
985            .chain([
986                ("FERRUM_VLLM_MOE", &vllm_moe),
987                ("FERRUM_MOE_DEVICE_ROUTE", &device_route),
988                ("FERRUM_VLLM_MOE_PAIR_IDS", &pair_ids),
989                ("FERRUM_BATCHED_GRAPH", &batched_graph),
990                ("FERRUM_REUSABLE_EXECUTION", &reusable_execution),
991                ("FERRUM_UNIFIED_GRAPH", &unified_graph),
992                (
993                    "FERRUM_UNIFIED_GRAPH_LAYERS_ONLY",
994                    &unified_graph_layers_only,
995                ),
996                (
997                    "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER",
998                    &unified_graph_lm_head_eager,
999                ),
1000                ("FERRUM_GREEDY_ARGMAX", &greedy),
1001            ])
1002        {
1003            if self.entry(key).is_none() {
1004                runtime_config.upsert(
1005                    key,
1006                    if resolved.value { "1" } else { "0" },
1007                    RuntimeConfigSource::MemoryProfile,
1008                );
1009            }
1010        }
1011        if self.entry("FERRUM_ATTENTION_POLICY").is_none() {
1012            if let Some((_, compiled_attention)) = plan_attention.as_ref() {
1013                runtime_config.upsert(
1014                    "FERRUM_ATTENTION_POLICY",
1015                    compiled_attention.value.as_runtime_value(),
1016                    RuntimeConfigSource::Default,
1017                );
1018            }
1019        }
1020        if let Some(until) = default_prefill_first_until_active.as_ref() {
1021            if self
1022                .entry("FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE")
1023                .is_none()
1024                && self.entry("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE").is_none()
1025            {
1026                runtime_config.upsert(
1027                    "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE",
1028                    until.value.to_string(),
1029                    RuntimeConfigSource::Default,
1030                );
1031            }
1032        }
1033        if let Some(slots) = recurrent_state_max_slots.as_ref() {
1034            if self.entry("FERRUM_RECURRENT_STATE_MAX_SLOTS").is_none() {
1035                runtime_config.upsert(
1036                    "FERRUM_RECURRENT_STATE_MAX_SLOTS",
1037                    slots.value.to_string(),
1038                    RuntimeConfigSource::MemoryProfile,
1039                );
1040            }
1041        }
1042        decisions.push(self.moe_decision(vllm_moe, device_route, pair_ids));
1043        decisions.push(self.graph_decision(graph));
1044        decisions.push(self.reusable_execution_decision(reusable_execution));
1045        decisions.push(self.decode_graph_decision(
1046            batched_graph,
1047            unified_graph,
1048            unified_graph_layers_only,
1049            unified_graph_lm_head_eager,
1050        ));
1051        decisions.push(self.scalar_decision(
1052            "kv_block_count",
1053            kv_blocks,
1054            RuntimeConfigEffect::Memory,
1055        ));
1056        decisions.push(self.scalar_decision(
1057            "max_sequences",
1058            max_sequences,
1059            RuntimeConfigEffect::Memory,
1060        ));
1061        if let Some(slots) = recurrent_state_max_slots {
1062            decisions.push(self.scalar_decision(
1063                "recurrent_state_max_slots",
1064                slots,
1065                RuntimeConfigEffect::Memory,
1066            ));
1067        }
1068        decisions.push(self.scalar_decision(
1069            "max_batched_tokens",
1070            max_batched_tokens,
1071            RuntimeConfigEffect::Performance,
1072        ));
1073        if let Some(max_model_len) = max_model_len {
1074            decisions.push(self.scalar_decision(
1075                "max_model_len",
1076                max_model_len,
1077                RuntimeConfigEffect::Memory,
1078            ));
1079        }
1080        decisions.push(self.prefix_cache_decision(prefix_cache));
1081        decisions.push(self.scheduler_decision(default_prefill_first_until_active)?);
1082        decisions.push(self.sampling_decision(greedy));
1083
1084        Ok(ResolvedFerrumConfig {
1085            schema_version: 1,
1086            preset: self.workload.preset.clone(),
1087            execution_resource_authority: self.execution_resource_authority,
1088            requested_attention_policy: plan_attention
1089                .as_ref()
1090                .map(|(requested, _)| requested.value),
1091            compiled_attention_policy: plan_attention.as_ref().map(|(_, compiled)| compiled.value),
1092            runtime_config,
1093            model_capabilities: self.model.clone(),
1094            hardware_capabilities: self.hardware.clone(),
1095            workload_profile: self.workload.clone(),
1096            decisions,
1097        })
1098    }
1099
1100    fn entries(&self) -> BTreeMap<&str, &str> {
1101        self.runtime_config
1102            .entries
1103            .iter()
1104            .map(|entry| (entry.key.as_str(), entry.effective_value.as_str()))
1105            .collect()
1106    }
1107
1108    fn raw(&self, key: &str) -> Option<&str> {
1109        self.entry(key).map(|entry| entry.effective_value.as_str())
1110    }
1111
1112    fn entry(&self, key: &str) -> Option<&RuntimeConfigEntry> {
1113        self.runtime_config
1114            .entries
1115            .iter()
1116            .find(|entry| entry.key == key)
1117    }
1118
1119    fn source_for_key(&self, key: &str, default_source: AutoConfigSource) -> AutoConfigSource {
1120        self.entry(key)
1121            .map(|entry| auto_config_source_from_runtime(entry.source))
1122            .unwrap_or(default_source)
1123    }
1124
1125    fn is_cuda_backend(&self) -> bool {
1126        self.hardware.backend.eq_ignore_ascii_case("cuda")
1127    }
1128
1129    fn is_accelerator_backend(&self) -> bool {
1130        self.is_cuda_backend() || self.hardware.backend.eq_ignore_ascii_case("metal")
1131    }
1132
1133    fn cuda_compute_capability_at_least(&self, major: u32, minor: u32) -> Option<bool> {
1134        let (actual_major, actual_minor) =
1135            parse_compute_capability(self.hardware.compute_capability.as_deref()?)?;
1136        Some((actual_major, actual_minor) >= (major, minor))
1137    }
1138
1139    fn default_max_sequences(&self) -> ResolvedValue<usize> {
1140        let target = self.workload.target_concurrency.max(1);
1141        let mut selected = target;
1142        if self.workload.is_m3_preset() {
1143            if let Some(sm_count) = self.hardware.sm_count {
1144                // The M3 throughput preset assumes a large GPU. On smaller
1145                // known GPUs, avoid auto-selecting a c32-sized admission
1146                // window before memory profiling has a chance to refine KV.
1147                selected = selected.min((sm_count as usize / 4).max(1));
1148            }
1149            if let Some(vram_bytes) = self.hardware.vram_bytes {
1150                selected = selected.min(vram_default_max_sequences(vram_bytes));
1151            }
1152        }
1153        ResolvedValue {
1154            value: selected.max(1),
1155            source: if selected < target {
1156                AutoConfigSource::HardwareCapability
1157            } else {
1158                AutoConfigSource::WorkloadPreset
1159            },
1160            source_key: None,
1161        }
1162    }
1163
1164    fn default_max_batched_tokens(
1165        &self,
1166        max_sequences: &ResolvedValue<usize>,
1167        kv_blocks: &ResolvedValue<usize>,
1168    ) -> ResolvedValue<usize> {
1169        let kv_token_capacity = kv_blocks
1170            .value
1171            .saturating_mul(DEFAULT_KV_BLOCK_SIZE_TOKENS)
1172            .max(max_sequences.value.max(1));
1173        let target = if self
1174            .workload
1175            .is_preset(QWEN25_72B_GPTQ_INT4_2X4090_LAYER_SPLIT_PRESET)
1176        {
1177            1536
1178        } else {
1179            max_sequences.value.max(1).saturating_mul(64)
1180        };
1181        let value = target
1182            .min(kv_token_capacity)
1183            .max(max_sequences.value.max(1));
1184        ResolvedValue {
1185            value,
1186            source: if max_sequences.source == AutoConfigSource::HardwareCapability
1187                || kv_blocks.source == AutoConfigSource::HardwareCapability
1188            {
1189                AutoConfigSource::HardwareCapability
1190            } else {
1191                AutoConfigSource::WorkloadPreset
1192            },
1193            source_key: None,
1194        }
1195    }
1196
1197    fn default_prefill_first_until_active(
1198        &self,
1199        max_sequences: &ResolvedValue<usize>,
1200    ) -> Option<ResolvedValue<usize>> {
1201        if max_sequences.value <= 1 || !self.is_accelerator_backend() {
1202            return None;
1203        }
1204        Some(ResolvedValue {
1205            value: max_sequences.value,
1206            source: AutoConfigSource::Default,
1207            source_key: None,
1208        })
1209    }
1210
1211    fn default_kv_blocks(&self, max_sequences: &ResolvedValue<usize>) -> ResolvedValue<usize> {
1212        let min_blocks = ceil_div(max_sequences.value.max(1), DEFAULT_KV_BLOCK_SIZE_TOKENS);
1213        if self
1214            .workload
1215            .is_preset(QWEN25_72B_GPTQ_INT4_2X4090_LAYER_SPLIT_PRESET)
1216        {
1217            return ResolvedValue {
1218                value: 1024.max(min_blocks),
1219                source: AutoConfigSource::WorkloadPreset,
1220                source_key: None,
1221            };
1222        }
1223        let target = DEFAULT_KV_BLOCKS.max(min_blocks);
1224        let selected = match (
1225            self.hardware.vram_bytes,
1226            self.model.estimated_weight_bytes,
1227            self.kv_cache_bytes_per_token(),
1228        ) {
1229            (Some(vram_bytes), Some(weight_bytes), Some(kv_bytes_per_token))
1230                if kv_bytes_per_token > 0 =>
1231            {
1232                let headroom = (vram_bytes / 10).max(2 * GIB);
1233                let available = vram_bytes.saturating_sub(weight_bytes.saturating_add(headroom));
1234                let kv_token_budget = (available / kv_bytes_per_token) as usize;
1235                let block_budget = kv_token_budget / DEFAULT_KV_BLOCK_SIZE_TOKENS;
1236                target.min(block_budget.max(min_blocks))
1237            }
1238            _ => target,
1239        };
1240        ResolvedValue {
1241            value: selected.max(1),
1242            source: if selected < target {
1243                AutoConfigSource::HardwareCapability
1244            } else {
1245                AutoConfigSource::WorkloadPreset
1246            },
1247            source_key: None,
1248        }
1249    }
1250
1251    fn default_recurrent_state_max_slots(
1252        &self,
1253        max_sequences: &ResolvedValue<usize>,
1254    ) -> Option<ResolvedValue<usize>> {
1255        let limit = self.recurrent_state_budget_max_slots()?;
1256        let selected = max_sequences.value.min(limit.max(1));
1257        Some(ResolvedValue {
1258            value: selected.max(1),
1259            source: if selected < max_sequences.value {
1260                AutoConfigSource::MemoryProfile
1261            } else {
1262                max_sequences.source
1263            },
1264            source_key: None,
1265        })
1266    }
1267
1268    fn recurrent_state_budget_max_slots(&self) -> Option<usize> {
1269        self.recurrent_state_budget()
1270            .map(|budget| budget.floored_slots)
1271    }
1272
1273    fn recurrent_state_budget(&self) -> Option<RecurrentStateBudget> {
1274        recurrent_state_budget_for(&self.model, &self.hardware)
1275    }
1276
1277    fn kv_cache_bytes_per_token(&self) -> Option<u64> {
1278        kv_cache_bytes_per_token_for_model(&self.model)
1279    }
1280
1281    fn bool_value(
1282        &self,
1283        key: &str,
1284        default: bool,
1285        default_source: AutoConfigSource,
1286    ) -> Result<ResolvedValue<bool>, AutoConfigError> {
1287        match self.entry(key) {
1288            Some(entry) => Ok(ResolvedValue {
1289                value: parse_bool_env_value(&entry.effective_value).map_err(|reason| {
1290                    AutoConfigError::InvalidOverride {
1291                        key: key.to_string(),
1292                        reason,
1293                    }
1294                })?,
1295                source: auto_config_source_from_runtime(entry.source),
1296                source_key: Some(key.to_string()),
1297            }),
1298            None => Ok(ResolvedValue {
1299                value: default,
1300                source: default_source,
1301                source_key: None,
1302            }),
1303        }
1304    }
1305
1306    fn usize_value(
1307        &self,
1308        key: &str,
1309        default: usize,
1310        default_source: AutoConfigSource,
1311    ) -> Result<ResolvedValue<usize>, AutoConfigError> {
1312        match self.entry(key) {
1313            Some(entry) => Ok(ResolvedValue {
1314                value: parse_usize_env_value(&entry.effective_value).map_err(|reason| {
1315                    AutoConfigError::InvalidOverride {
1316                        key: key.to_string(),
1317                        reason,
1318                    }
1319                })?,
1320                source: auto_config_source_from_runtime(entry.source),
1321                source_key: Some(key.to_string()),
1322            }),
1323            None => Ok(ResolvedValue {
1324                value: default,
1325                source: default_source,
1326                source_key: None,
1327            }),
1328        }
1329    }
1330
1331    fn optional_usize_value(
1332        &self,
1333        key: &str,
1334    ) -> Result<Option<ResolvedValue<usize>>, AutoConfigError> {
1335        match self.entry(key) {
1336            Some(entry) => Ok(Some(ResolvedValue {
1337                value: parse_usize_env_value(&entry.effective_value).map_err(|reason| {
1338                    AutoConfigError::InvalidOverride {
1339                        key: key.to_string(),
1340                        reason,
1341                    }
1342                })?,
1343                source: auto_config_source_from_runtime(entry.source),
1344                source_key: Some(key.to_string()),
1345            })),
1346            None => Ok(None),
1347        }
1348    }
1349
1350    fn optional_string_value(
1351        &self,
1352        key: &str,
1353    ) -> Result<Option<ResolvedValue<String>>, AutoConfigError> {
1354        match self.entry(key) {
1355            Some(entry) if entry.effective_value.trim().is_empty() => {
1356                Err(AutoConfigError::InvalidOverride {
1357                    key: key.to_string(),
1358                    reason: "must be non-empty".to_string(),
1359                })
1360            }
1361            Some(entry) => Ok(Some(ResolvedValue {
1362                value: entry.effective_value.clone(),
1363                source: auto_config_source_from_runtime(entry.source),
1364                source_key: Some(key.to_string()),
1365            })),
1366            None => Ok(None),
1367        }
1368    }
1369
1370    fn validate_attention(
1371        &self,
1372        use_vllm_paged_attn: bool,
1373        fa_layout: bool,
1374        fa2_source: bool,
1375        fa2_direct_ffi: bool,
1376        shim_present: bool,
1377        vllm_v1_short: bool,
1378    ) -> Result<(), AutoConfigError> {
1379        if use_vllm_paged_attn && !self.hardware.compiled_features.vllm_paged_attn {
1380            return self.invalid(
1381                "FERRUM_USE_VLLM_PAGED_ATTN",
1382                "vLLM paged attention is not compiled",
1383            );
1384        }
1385        if use_vllm_paged_attn && !self.is_cuda_backend() {
1386            return self.invalid(
1387                "FERRUM_USE_VLLM_PAGED_ATTN",
1388                "vLLM paged attention requires CUDA backend",
1389            );
1390        }
1391        if fa_layout && !use_vllm_paged_attn {
1392            return self.invalid(
1393                "FERRUM_FA_LAYOUT_VARLEN",
1394                "FA layout requires vLLM paged attention layout",
1395            );
1396        }
1397        if fa2_source {
1398            return self.invalid(
1399                "FERRUM_FA2_SOURCE",
1400                "source-linked FA2 path has been removed; use a native operator artifact",
1401            );
1402        }
1403        if fa2_direct_ffi && !self.hardware.compiled_features.fa2_direct_ffi {
1404            return self.invalid(
1405                "FERRUM_FA2_DIRECT_FFI",
1406                "direct FA2 FFI shim support is not compiled",
1407            );
1408        }
1409        if fa2_direct_ffi && !self.is_cuda_backend() {
1410            return self.invalid(
1411                "FERRUM_FA2_DIRECT_FFI",
1412                "direct FA2 FFI shim requires CUDA backend",
1413            );
1414        }
1415        if fa2_direct_ffi && self.cuda_compute_capability_at_least(8, 0) == Some(false) {
1416            return self.invalid(
1417                "FERRUM_FA2_DIRECT_FFI",
1418                "direct FA2 FFI shim requires CUDA compute capability >= 8.0",
1419            );
1420        }
1421        if fa2_direct_ffi && !shim_present {
1422            return self.invalid(
1423                "FERRUM_FA2_DIRECT_FFI",
1424                "requires FERRUM_FA2_DIRECT_FFI_SHIM",
1425            );
1426        }
1427        if fa2_source && fa2_direct_ffi {
1428            return self.unsupported(
1429                "attention_prefill_mixed_backend",
1430                "FA2 source and direct FFI shim cannot both own the prefill path",
1431            );
1432        }
1433        if vllm_v1_short && !use_vllm_paged_attn {
1434            return self.invalid(
1435                "FERRUM_VLLM_PAGED_ATTN_V1_SHORT",
1436                "short-context v1 requires vLLM paged attention",
1437            );
1438        }
1439        Ok(())
1440    }
1441
1442    fn validate_fa2_native_artifact(
1443        &self,
1444        manifest: Option<&ResolvedValue<String>>,
1445        artifact: Option<&ResolvedValue<String>>,
1446        source_sha256: Option<&ResolvedValue<String>>,
1447        inputs_sha256: Option<&ResolvedValue<String>>,
1448        fa2_source: bool,
1449        fa2_direct_ffi: bool,
1450    ) -> Result<(), AutoConfigError> {
1451        let configured = manifest.is_some() || artifact.is_some();
1452        if manifest.is_some() != artifact.is_some() {
1453            let key = if manifest.is_none() {
1454                FA2_NATIVE_MANIFEST_KEY
1455            } else {
1456                FA2_NATIVE_ARTIFACT_KEY
1457            };
1458            return self.invalid(
1459                key,
1460                "FA2 native operator manifest and artifact must be configured together",
1461            );
1462        }
1463        for (key, value) in [
1464            (FA2_NATIVE_SOURCE_SHA256_KEY, source_sha256),
1465            (FA2_NATIVE_INPUTS_SHA256_KEY, inputs_sha256),
1466        ] {
1467            if let Some(value) = value {
1468                if !is_sha256_digest(&value.value) {
1469                    return self.invalid(key, "must be a lowercase hex sha256 digest");
1470                }
1471                if !configured {
1472                    return self.invalid(
1473                        key,
1474                        "sha256 pins require FA2 native operator manifest and artifact",
1475                    );
1476                }
1477            }
1478        }
1479        if configured && !self.is_cuda_backend() {
1480            return self.invalid(
1481                FA2_NATIVE_MANIFEST_KEY,
1482                "FA2 native operator artifacts require CUDA backend",
1483            );
1484        }
1485        if configured && !self.hardware.compiled_features.fa2_native_operator_artifact {
1486            return self.invalid(
1487                FA2_NATIVE_MANIFEST_KEY,
1488                "FA2 native operator artifact config requires a binary built with a validated native operator artifact",
1489            );
1490        }
1491        if configured {
1492            let metadata = self
1493                .hardware
1494                .compiled_features
1495                .fa2_native_operator_artifact_metadata
1496                .as_ref()
1497                .ok_or_else(|| AutoConfigError::InvalidOverride {
1498                    key: FA2_NATIVE_MANIFEST_KEY.to_string(),
1499                    reason: "FA2 native operator artifact capability is missing build metadata"
1500                        .to_string(),
1501                })?;
1502            let manifest = manifest.expect("configured manifest is present");
1503            let artifact = artifact.expect("configured artifact is present");
1504            if manifest.value != metadata.manifest_path {
1505                return self.invalid(
1506                    FA2_NATIVE_MANIFEST_KEY,
1507                    "FA2 native operator manifest does not match the artifact linked into this binary",
1508                );
1509            }
1510            if artifact.value != metadata.artifact_path {
1511                return self.invalid(
1512                    FA2_NATIVE_ARTIFACT_KEY,
1513                    "FA2 native operator artifact does not match the artifact linked into this binary",
1514                );
1515            }
1516            if let Some(source_sha256) = source_sha256 {
1517                if source_sha256.value != metadata.source_package_sha256 {
1518                    return self.invalid(
1519                        FA2_NATIVE_SOURCE_SHA256_KEY,
1520                        "FA2 native operator source_package sha256 does not match the artifact linked into this binary",
1521                    );
1522                }
1523            }
1524            if let Some(inputs_sha256) = inputs_sha256 {
1525                if inputs_sha256.value != metadata.inputs_sha256 {
1526                    return self.invalid(
1527                        FA2_NATIVE_INPUTS_SHA256_KEY,
1528                        "FA2 native operator inputs sha256 does not match the artifact linked into this binary",
1529                    );
1530                }
1531            }
1532        }
1533        if configured && fa2_source {
1534            return self.unsupported(
1535                "attention_prefill_mixed_backend",
1536                "FA2 native operator artifact cannot be combined with removed source-linked FA2",
1537            );
1538        }
1539        if configured && fa2_direct_ffi {
1540            return self.unsupported(
1541                "attention_prefill_mixed_backend",
1542                "FA2 native operator artifact and diagnostic direct FFI shim cannot both own the prefill path",
1543            );
1544        }
1545        Ok(())
1546    }
1547
1548    fn validate_moe(
1549        &self,
1550        vllm_moe: bool,
1551        device_route: bool,
1552        pair_ids: bool,
1553        graph: bool,
1554    ) -> Result<(), AutoConfigError> {
1555        if vllm_moe && !self.hardware.compiled_features.vllm_moe_marlin {
1556            return self.invalid("FERRUM_VLLM_MOE", "vLLM Marlin MoE is not compiled");
1557        }
1558        if vllm_moe && !self.is_cuda_backend() {
1559            return self.invalid("FERRUM_VLLM_MOE", "vLLM Marlin MoE requires CUDA backend");
1560        }
1561        if device_route && !vllm_moe {
1562            return self.invalid(
1563                "FERRUM_MOE_DEVICE_ROUTE",
1564                "device route currently requires vLLM MoE",
1565            );
1566        }
1567        if pair_ids && !vllm_moe {
1568            return self.invalid(
1569                "FERRUM_VLLM_MOE_PAIR_IDS",
1570                "pair-id routing requires vLLM MoE",
1571            );
1572        }
1573        let graph_relevant = self.model.moe.is_some() || self.workload.is_m3_preset();
1574        if graph && graph_relevant && !self.hardware.graph_support {
1575            return self.invalid(
1576                "FERRUM_MOE_GRAPH",
1577                "hardware/backend does not support CUDA graph replay",
1578            );
1579        }
1580        if graph && graph_relevant && !self.hardware.compiled_features.cuda_graph {
1581            return self.invalid("FERRUM_MOE_GRAPH", "CUDA graph support is not compiled");
1582        }
1583        if graph && graph_relevant && !vllm_moe {
1584            return self.invalid(
1585                "FERRUM_MOE_GRAPH",
1586                "graph decode requires the graph-clean vLLM MoE path",
1587            );
1588        }
1589        if graph && graph_relevant && self.model.moe.is_some() && !self.model.graph_safe_moe {
1590            return self.unsupported(
1591                "moe_graph_policy",
1592                "model MoE path is not marked graph-safe",
1593            );
1594        }
1595        Ok(())
1596    }
1597
1598    fn validate_batched_graph(&self, graph: bool) -> Result<(), AutoConfigError> {
1599        if !graph {
1600            return Ok(());
1601        }
1602        if self.model.moe.is_some() {
1603            return self.invalid(
1604                "FERRUM_BATCHED_GRAPH",
1605                "legacy batched decode graph does not apply to MoE models",
1606            );
1607        }
1608        if !self.is_cuda_backend() {
1609            return self.invalid(
1610                "FERRUM_BATCHED_GRAPH",
1611                "legacy batched decode graph requires CUDA backend",
1612            );
1613        }
1614        if !self.hardware.graph_support {
1615            return self.invalid(
1616                "FERRUM_BATCHED_GRAPH",
1617                "hardware/backend does not support CUDA graph replay",
1618            );
1619        }
1620        if !self.hardware.compiled_features.cuda_graph {
1621            return self.invalid("FERRUM_BATCHED_GRAPH", "CUDA graph support is not compiled");
1622        }
1623        Ok(())
1624    }
1625
1626    fn validate_unified_graph(
1627        &self,
1628        graph: bool,
1629        layers_only: bool,
1630        lm_head_eager: bool,
1631    ) -> Result<(), AutoConfigError> {
1632        if layers_only && !graph {
1633            return self.invalid(
1634                "FERRUM_UNIFIED_GRAPH_LAYERS_ONLY",
1635                "layers-only unified graph capture requires FERRUM_UNIFIED_GRAPH=1",
1636            );
1637        }
1638        if lm_head_eager && !graph {
1639            return self.invalid(
1640                "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER",
1641                "lm-head-eager unified graph capture requires FERRUM_UNIFIED_GRAPH=1",
1642            );
1643        }
1644        if layers_only && lm_head_eager {
1645            return self.invalid(
1646                "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER",
1647                "lm-head-eager unified graph capture conflicts with layers-only capture",
1648            );
1649        }
1650        if !graph {
1651            return Ok(());
1652        }
1653        if self.model.moe.is_some() {
1654            return self.invalid(
1655                "FERRUM_UNIFIED_GRAPH",
1656                "unified decode graph does not apply to MoE models",
1657            );
1658        }
1659        if self.model.architecture.eq_ignore_ascii_case("gemma3") && !layers_only && !lm_head_eager
1660        {
1661            return self.invalid(
1662                "FERRUM_UNIFIED_GRAPH",
1663                "full unified decode graph is disabled for Gemma3 sandwich-norm models",
1664            );
1665        }
1666        if !self.is_cuda_backend() {
1667            return self.invalid(
1668                "FERRUM_UNIFIED_GRAPH",
1669                "unified decode graph requires CUDA backend",
1670            );
1671        }
1672        if !self.hardware.graph_support {
1673            return self.invalid(
1674                "FERRUM_UNIFIED_GRAPH",
1675                "hardware/backend does not support CUDA graph replay",
1676            );
1677        }
1678        if !self.hardware.compiled_features.cuda_graph {
1679            return self.invalid("FERRUM_UNIFIED_GRAPH", "CUDA graph support is not compiled");
1680        }
1681        Ok(())
1682    }
1683
1684    fn validate_sampling(&self, greedy: bool) -> Result<(), AutoConfigError> {
1685        if greedy && !self.hardware.compiled_features.greedy_argmax {
1686            return self.invalid("FERRUM_GREEDY_ARGMAX", "GPU argmax is not compiled");
1687        }
1688        if greedy
1689            && !(self.is_cuda_backend() || self.hardware.backend.eq_ignore_ascii_case("metal"))
1690        {
1691            return self.invalid(
1692                "FERRUM_GREEDY_ARGMAX",
1693                "greedy argmax requires CUDA or Metal backend",
1694            );
1695        }
1696        Ok(())
1697    }
1698
1699    fn validate_memory(
1700        &self,
1701        kv_blocks: usize,
1702        max_sequences: usize,
1703        recurrent_state_max_slots: Option<usize>,
1704        max_batched_tokens: usize,
1705        requested_max_model_len: Option<usize>,
1706    ) -> Result<(), AutoConfigError> {
1707        if kv_blocks == 0 {
1708            return self.invalid("FERRUM_KV_MAX_BLOCKS", "must be greater than zero");
1709        }
1710        if max_sequences == 0 {
1711            return self.invalid("FERRUM_PAGED_MAX_SEQS", "must be greater than zero");
1712        }
1713        if recurrent_state_max_slots == Some(0) {
1714            return self.invalid(
1715                "FERRUM_RECURRENT_STATE_MAX_SLOTS",
1716                "must be greater than zero",
1717            );
1718        }
1719        if self.execution_resource_authority == ExecutionResourceAuthority::LegacyEngine {
1720            if let Some(limit) = self.recurrent_state_budget_max_slots() {
1721                let recurrent_slots = recurrent_state_max_slots.unwrap_or(max_sequences);
1722                if recurrent_slots > limit {
1723                    let key = if self.entry("FERRUM_RECURRENT_STATE_MAX_SLOTS").is_some() {
1724                        "FERRUM_RECURRENT_STATE_MAX_SLOTS"
1725                    } else {
1726                        "FERRUM_PAGED_MAX_SEQS"
1727                    };
1728                    return Err(AutoConfigError::InvalidOverride {
1729                        key: key.to_string(),
1730                        reason: format!(
1731                            "recurrent-state slot pool exceeds the model/hardware memory budget: slots={recurrent_slots}, budget={limit}; use FERRUM_RECURRENT_STATE_MAX_SLOTS={limit}, lower --max-num-seqs, or a larger-memory GPU."
1732                        ),
1733                    });
1734                }
1735            }
1736        }
1737        if max_batched_tokens < max_sequences {
1738            return self.invalid(
1739                "FERRUM_MAX_BATCHED_TOKENS",
1740                "must be at least FERRUM_PAGED_MAX_SEQS",
1741            );
1742        }
1743        let kv_token_capacity = kv_blocks.saturating_mul(DEFAULT_KV_BLOCK_SIZE_TOKENS);
1744        if max_batched_tokens > kv_token_capacity {
1745            return self.invalid(
1746                "FERRUM_MAX_BATCHED_TOKENS",
1747                "exceeds KV cache token capacity",
1748            );
1749        }
1750        if let Some(max_model_len) = requested_max_model_len {
1751            if max_model_len == 0 {
1752                return self.invalid("FERRUM_MAX_MODEL_LEN", "must be greater than zero");
1753            }
1754            if let Some(model_max) = self.model.max_context_len {
1755                if max_model_len > model_max {
1756                    return self.invalid(
1757                        "FERRUM_MAX_MODEL_LEN",
1758                        "exceeds model metadata max context length",
1759                    );
1760                }
1761            }
1762            if max_model_len > kv_token_capacity {
1763                return self.invalid(
1764                    "FERRUM_KV_MAX_BLOCKS",
1765                    "KV cache token capacity is smaller than FERRUM_MAX_MODEL_LEN",
1766                );
1767            }
1768        }
1769        Ok(())
1770    }
1771
1772    fn validate_dtypes(&self) -> Result<(), AutoConfigError> {
1773        if let Some(dtype) = self.raw("FERRUM_DTYPE") {
1774            let dtype = dtype.to_ascii_lowercase();
1775            if !self.hardware.supported_dtypes.iter().any(|d| d == &dtype) {
1776                return self.invalid("FERRUM_DTYPE", "dtype is not supported by hardware profile");
1777            }
1778        }
1779        if let Some(dtype) = self.raw("FERRUM_KV_DTYPE") {
1780            let dtype = dtype.to_ascii_lowercase();
1781            if !self
1782                .hardware
1783                .supported_kv_dtypes
1784                .iter()
1785                .any(|d| d == &dtype)
1786            {
1787                return self.invalid(
1788                    "FERRUM_KV_DTYPE",
1789                    "KV dtype is not supported by hardware profile",
1790                );
1791            }
1792        }
1793        Ok(())
1794    }
1795
1796    fn validate_layer_split_pipeline_mode(&self) -> Result<(), AutoConfigError> {
1797        let Some(mode) = self.raw("FERRUM_LAYER_SPLIT_PIPELINE_MODE") else {
1798            return Ok(());
1799        };
1800        match mode.trim().to_ascii_lowercase().as_str() {
1801            "batch" | "overlapped" => Ok(()),
1802            _ => self.invalid(
1803                "FERRUM_LAYER_SPLIT_PIPELINE_MODE",
1804                "must be batch or overlapped",
1805            ),
1806        }
1807    }
1808
1809    fn resolve_plan_attention_policy(
1810        &self,
1811    ) -> Result<
1812        (
1813            ResolvedValue<AttentionExecutionPolicy>,
1814            ResolvedValue<AttentionExecutionPolicy>,
1815        ),
1816        AutoConfigError,
1817    > {
1818        let requested = match self.entry("FERRUM_ATTENTION_POLICY") {
1819            Some(entry) => ResolvedValue {
1820                value: AttentionExecutionPolicy::parse_runtime_value(&entry.effective_value)
1821                    .map_err(|reason| AutoConfigError::InvalidOverride {
1822                        key: "FERRUM_ATTENTION_POLICY".to_owned(),
1823                        reason,
1824                    })?,
1825                source: auto_config_source_from_runtime(entry.source),
1826                source_key: Some("FERRUM_ATTENTION_POLICY".to_owned()),
1827            },
1828            None => ResolvedValue {
1829                value: AttentionExecutionPolicy::Auto,
1830                source: AutoConfigSource::Default,
1831                source_key: None,
1832            },
1833        };
1834        // This preflight capability must match what the concrete runtime
1835        // descriptor advertises. Metal currently provides the portable vNext
1836        // provider; only the CUDA composition exposes native-adaptive attention.
1837        let native_adaptive_supported =
1838            self.is_cuda_backend() && self.hardware.compiled_features.vllm_paged_attn;
1839        let compiled = requested
1840            .value
1841            .resolve(native_adaptive_supported)
1842            .map_err(|reason| AutoConfigError::UnsupportedCombination {
1843                selection: "attention_execution_policy".to_owned(),
1844                reason,
1845            })?;
1846        Ok((
1847            requested.clone(),
1848            ResolvedValue {
1849                value: compiled,
1850                source: if requested.value == AttentionExecutionPolicy::Auto {
1851                    AutoConfigSource::CompiledFeature
1852                } else {
1853                    requested.source
1854                },
1855                source_key: requested.source_key.clone(),
1856            },
1857        ))
1858    }
1859
1860    fn plan_attention_execution_decision(
1861        &self,
1862        compiled: &ResolvedValue<AttentionExecutionPolicy>,
1863    ) -> AutoConfigDecision {
1864        let selected = compiled.value.as_runtime_value();
1865        self.decision(
1866            "attention_execution_policy",
1867            selected,
1868            compiled.source,
1869            compiled.source_key.clone(),
1870            ["native-adaptive", "portable"],
1871            self.rejected_except(
1872                selected,
1873                [
1874                    (
1875                        "native-adaptive",
1876                        "native adaptive provider is not selected or compiled",
1877                    ),
1878                    ("portable", "portable provider is not selected"),
1879                ],
1880            ),
1881            vec![
1882                RuntimeConfigEffect::Correctness,
1883                RuntimeConfigEffect::Performance,
1884            ],
1885        )
1886    }
1887
1888    fn plan_attention_phase_decision(
1889        &self,
1890        selection: &str,
1891        compiled: &ResolvedValue<AttentionExecutionPolicy>,
1892    ) -> AutoConfigDecision {
1893        let selected = match (
1894            self.hardware.backend.to_ascii_lowercase().as_str(),
1895            compiled.value,
1896        ) {
1897            ("cuda", AttentionExecutionPolicy::NativeAdaptive) => "cuda_native_adaptive",
1898            ("metal", AttentionExecutionPolicy::NativeAdaptive) => "metal_native_adaptive",
1899            (_, AttentionExecutionPolicy::Portable) => "portable",
1900            (_, AttentionExecutionPolicy::Auto) => "unresolved",
1901            _ => "portable",
1902        };
1903        self.decision(
1904            selection,
1905            selected,
1906            compiled.source,
1907            compiled.source_key.clone(),
1908            ["cuda_native_adaptive", "metal_native_adaptive", "portable"],
1909            self.rejected_except(
1910                selected,
1911                [
1912                    (
1913                        "cuda_native_adaptive",
1914                        "CUDA native adaptive provider is not selected",
1915                    ),
1916                    (
1917                        "metal_native_adaptive",
1918                        "Metal native adaptive provider is not selected",
1919                    ),
1920                    ("portable", "portable provider is not selected"),
1921                ],
1922            ),
1923            vec![
1924                RuntimeConfigEffect::Correctness,
1925                RuntimeConfigEffect::Performance,
1926            ],
1927        )
1928    }
1929
1930    fn attention_prefill_decision(
1931        &self,
1932        use_vllm_paged_attn: ResolvedValue<bool>,
1933        fa_layout: ResolvedValue<bool>,
1934        fa2_source: ResolvedValue<bool>,
1935        fa2_direct_ffi: ResolvedValue<bool>,
1936    ) -> AutoConfigDecision {
1937        let (selected, source, source_key) = if fa2_source.value {
1938            ("fa2_source", fa2_source.source, fa2_source.source_key)
1939        } else if fa2_direct_ffi.value {
1940            (
1941                "fa2_direct_ffi",
1942                fa2_direct_ffi.source,
1943                fa2_direct_ffi.source_key,
1944            )
1945        } else if fa_layout.value {
1946            ("fa_layout_varlen", fa_layout.source, fa_layout.source_key)
1947        } else if use_vllm_paged_attn.value {
1948            (
1949                "vllm_paged_varlen",
1950                use_vllm_paged_attn.source,
1951                use_vllm_paged_attn.source_key,
1952            )
1953        } else {
1954            ("legacy_paged_varlen", AutoConfigSource::Default, None)
1955        };
1956        self.decision(
1957            "attention_prefill_mixed_backend",
1958            selected,
1959            source,
1960            source_key,
1961            [
1962                "fa2_source",
1963                "fa2_direct_ffi",
1964                "fa_layout_varlen",
1965                "vllm_paged_varlen",
1966                "legacy_paged_varlen",
1967            ],
1968            self.rejected_except(
1969                selected,
1970                [
1971                    ("fa2_source", "source-linked FA2 path not selected"),
1972                    ("fa2_direct_ffi", "diagnostic direct FFI shim not selected"),
1973                    ("fa_layout_varlen", "FA-compatible layout not selected"),
1974                    ("vllm_paged_varlen", "vLLM paged varlen bridge not selected"),
1975                    (
1976                        "legacy_paged_varlen",
1977                        "a higher-priority attention path was selected",
1978                    ),
1979                ],
1980            ),
1981            vec![
1982                RuntimeConfigEffect::Performance,
1983                RuntimeConfigEffect::Memory,
1984            ],
1985        )
1986    }
1987
1988    fn fa2_native_artifact_decision(
1989        &self,
1990        manifest: Option<&ResolvedValue<String>>,
1991        artifact: Option<&ResolvedValue<String>>,
1992    ) -> AutoConfigDecision {
1993        let configured = manifest.is_some() && artifact.is_some();
1994        let selected = if configured {
1995            "configured"
1996        } else {
1997            "not_configured"
1998        };
1999        let (source, source_key) = manifest
2000            .map(|manifest| (manifest.source, manifest.source_key.clone()))
2001            .unwrap_or((AutoConfigSource::Default, None));
2002        self.decision(
2003            "fa2_native_operator_artifact",
2004            selected,
2005            source,
2006            source_key,
2007            ["configured", "not_configured"],
2008            self.rejected_except(
2009                selected,
2010                [
2011                    (
2012                        "configured",
2013                        "no typed FA2 native operator manifest/artifact was configured",
2014                    ),
2015                    (
2016                        "not_configured",
2017                        "typed FA2 native operator manifest/artifact is present",
2018                    ),
2019                ],
2020            ),
2021            vec![
2022                RuntimeConfigEffect::Correctness,
2023                RuntimeConfigEffect::Performance,
2024            ],
2025        )
2026    }
2027
2028    fn fa2_native_runtime_selection_decision(
2029        &self,
2030        manifest: Option<&ResolvedValue<String>>,
2031        artifact: Option<&ResolvedValue<String>>,
2032    ) -> AutoConfigDecision {
2033        let configured = manifest.is_some() && artifact.is_some();
2034        let selected = if configured {
2035            "not_selected"
2036        } else {
2037            "not_configured"
2038        };
2039        let (source, source_key) = manifest
2040            .map(|manifest| (manifest.source, manifest.source_key.clone()))
2041            .unwrap_or((AutoConfigSource::Default, None));
2042        self.decision(
2043            "fa2_native_operator_runtime_selection",
2044            selected,
2045            source,
2046            source_key,
2047            ["selected", "not_selected", "not_configured"],
2048            self.rejected_except(
2049                selected,
2050                [
2051                    (
2052                        "selected",
2053                        "FA2 native runtime dispatch requires artifact-selected actual model smoke and retention evidence",
2054                    ),
2055                    (
2056                        "not_selected",
2057                        "typed FA2 native operator manifest/artifact is present",
2058                    ),
2059                    (
2060                        "not_configured",
2061                        "no typed FA2 native operator manifest/artifact was configured",
2062                    ),
2063                ],
2064            ),
2065            vec![
2066                RuntimeConfigEffect::Correctness,
2067                RuntimeConfigEffect::Performance,
2068            ],
2069        )
2070    }
2071
2072    fn attention_decode_decision(
2073        &self,
2074        use_vllm_paged_attn: ResolvedValue<bool>,
2075        vllm_v1_short: ResolvedValue<bool>,
2076    ) -> AutoConfigDecision {
2077        let (selected, source, source_key) = if use_vllm_paged_attn.value {
2078            if vllm_v1_short.value {
2079                (
2080                    "vllm_paged_attn_v1_short",
2081                    vllm_v1_short.source,
2082                    vllm_v1_short.source_key,
2083                )
2084            } else {
2085                (
2086                    "vllm_paged_attn_v2",
2087                    vllm_v1_short.source,
2088                    vllm_v1_short.source_key,
2089                )
2090            }
2091        } else {
2092            ("legacy_paged_decode", use_vllm_paged_attn.source, None)
2093        };
2094        self.decision(
2095            "attention_decode_backend",
2096            selected,
2097            source,
2098            source_key,
2099            [
2100                "vllm_paged_attn_v1_short",
2101                "vllm_paged_attn_v2",
2102                "legacy_paged_decode",
2103            ],
2104            self.rejected_except(
2105                selected,
2106                [
2107                    (
2108                        "vllm_paged_attn_v1_short",
2109                        "short-context v1 decode not selected",
2110                    ),
2111                    ("vllm_paged_attn_v2", "v2 decode not selected"),
2112                    ("legacy_paged_decode", "legacy decode not selected"),
2113                ],
2114            ),
2115            vec![RuntimeConfigEffect::Performance],
2116        )
2117    }
2118
2119    fn moe_decision(
2120        &self,
2121        vllm_moe: ResolvedValue<bool>,
2122        device_route: ResolvedValue<bool>,
2123        pair_ids: ResolvedValue<bool>,
2124    ) -> AutoConfigDecision {
2125        let selected = if vllm_moe.value && device_route.value && pair_ids.value {
2126            "vllm_marlin_moe_device_route_pair_ids"
2127        } else if vllm_moe.value && device_route.value {
2128            "vllm_marlin_moe_device_route"
2129        } else if vllm_moe.value {
2130            "vllm_marlin_moe"
2131        } else {
2132            "legacy_moe"
2133        };
2134        self.decision(
2135            "moe_implementation",
2136            selected,
2137            vllm_moe.source,
2138            vllm_moe.source_key,
2139            [
2140                "vllm_marlin_moe_device_route_pair_ids",
2141                "vllm_marlin_moe_device_route",
2142                "vllm_marlin_moe",
2143                "legacy_moe",
2144            ],
2145            self.rejected_except(
2146                selected,
2147                [
2148                    (
2149                        "vllm_marlin_moe_device_route_pair_ids",
2150                        "pair-id device route not selected",
2151                    ),
2152                    (
2153                        "vllm_marlin_moe_device_route",
2154                        "device-route MoE not selected",
2155                    ),
2156                    ("vllm_marlin_moe", "vLLM Marlin MoE not selected"),
2157                    ("legacy_moe", "legacy MoE not selected"),
2158                ],
2159            ),
2160            vec![RuntimeConfigEffect::Performance],
2161        )
2162    }
2163
2164    fn graph_decision(&self, graph: ResolvedValue<bool>) -> AutoConfigDecision {
2165        let selected = if graph.value {
2166            "graph_clean_decode"
2167        } else {
2168            "graph_disabled"
2169        };
2170        self.decision(
2171            "moe_graph_policy",
2172            selected,
2173            graph.source,
2174            graph.source_key,
2175            ["graph_clean_decode", "graph_disabled"],
2176            self.rejected_except(
2177                selected,
2178                [
2179                    ("graph_clean_decode", "graph decode not selected"),
2180                    ("graph_disabled", "graph decode selected"),
2181                ],
2182            ),
2183            vec![
2184                RuntimeConfigEffect::Performance,
2185                RuntimeConfigEffect::Correctness,
2186            ],
2187        )
2188    }
2189
2190    fn decode_graph_decision(
2191        &self,
2192        batched_graph: ResolvedValue<bool>,
2193        unified_graph: ResolvedValue<bool>,
2194        unified_graph_layers_only: ResolvedValue<bool>,
2195        unified_graph_lm_head_eager: ResolvedValue<bool>,
2196    ) -> AutoConfigDecision {
2197        let selected = if unified_graph.value && unified_graph_layers_only.value {
2198            "unified_decode_graph_layers_only"
2199        } else if unified_graph.value && unified_graph_lm_head_eager.value {
2200            "unified_decode_graph_lm_head_eager"
2201        } else if unified_graph.value {
2202            "unified_decode_graph"
2203        } else if batched_graph.value {
2204            "legacy_batched_decode_graph"
2205        } else {
2206            "graph_disabled"
2207        };
2208        let source_value = if unified_graph_layers_only.value {
2209            unified_graph_layers_only
2210        } else if unified_graph_lm_head_eager.value {
2211            unified_graph_lm_head_eager
2212        } else if unified_graph.value
2213            || (!batched_graph.value && unified_graph.source != AutoConfigSource::Default)
2214        {
2215            unified_graph
2216        } else {
2217            batched_graph
2218        };
2219        self.decision(
2220            "decode_graph_policy",
2221            selected,
2222            source_value.source,
2223            source_value.source_key,
2224            [
2225                "unified_decode_graph_layers_only",
2226                "unified_decode_graph_lm_head_eager",
2227                "unified_decode_graph",
2228                "legacy_batched_decode_graph",
2229                "graph_disabled",
2230            ],
2231            self.rejected_except(
2232                selected,
2233                [
2234                    (
2235                        "unified_decode_graph_layers_only",
2236                        "layers-only unified decode graph not selected",
2237                    ),
2238                    (
2239                        "unified_decode_graph_lm_head_eager",
2240                        "lm-head-eager unified decode graph not selected",
2241                    ),
2242                    ("unified_decode_graph", "unified decode graph not selected"),
2243                    (
2244                        "legacy_batched_decode_graph",
2245                        "legacy batched decode graph not selected",
2246                    ),
2247                    ("graph_disabled", "decode graph selected"),
2248                ],
2249            ),
2250            vec![
2251                RuntimeConfigEffect::Performance,
2252                RuntimeConfigEffect::Correctness,
2253            ],
2254        )
2255    }
2256
2257    fn reusable_execution_decision(
2258        &self,
2259        reusable_execution: ResolvedValue<bool>,
2260    ) -> AutoConfigDecision {
2261        let selected = if reusable_execution.value {
2262            "enabled_when_runtime_capable"
2263        } else {
2264            "disabled"
2265        };
2266        self.decision(
2267            "reusable_execution_policy",
2268            selected,
2269            reusable_execution.source,
2270            reusable_execution.source_key,
2271            ["enabled_when_runtime_capable", "disabled"],
2272            self.rejected_except(
2273                selected,
2274                [
2275                    (
2276                        "enabled_when_runtime_capable",
2277                        "reusable device programs are disabled",
2278                    ),
2279                    ("disabled", "reusable device programs are enabled"),
2280                ],
2281            ),
2282            vec![
2283                RuntimeConfigEffect::Performance,
2284                RuntimeConfigEffect::Correctness,
2285            ],
2286        )
2287    }
2288
2289    fn scalar_decision(
2290        &self,
2291        selection: &str,
2292        value: ResolvedValue<usize>,
2293        effect: RuntimeConfigEffect,
2294    ) -> AutoConfigDecision {
2295        self.decision(
2296            selection,
2297            &value.value.to_string(),
2298            value.source,
2299            value.source_key,
2300            [value.value.to_string()],
2301            Vec::new(),
2302            vec![effect],
2303        )
2304    }
2305
2306    fn scheduler_decision(
2307        &self,
2308        default_prefill_first_until_active: Option<ResolvedValue<usize>>,
2309    ) -> Result<AutoConfigDecision, AutoConfigError> {
2310        let entries = self.entries();
2311        let prompt_scheduler =
2312            || -> Result<(String, AutoConfigSource, Option<String>), AutoConfigError> {
2313                let prompt_token_estimate = self.bool_value(
2314                    "FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE",
2315                    true,
2316                    AutoConfigSource::Default,
2317                )?;
2318                let selected = if prompt_token_estimate.value {
2319                    "prompt_token_estimate"
2320                } else {
2321                    "continuous_default"
2322                };
2323                Ok((
2324                    selected.to_string(),
2325                    prompt_token_estimate.source,
2326                    prompt_token_estimate.source_key,
2327                ))
2328            };
2329        let explicit_prefill_first = entries.get("FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE");
2330        let explicit_prefill_first_present = explicit_prefill_first.is_some();
2331        let implicit_prefill_first = if explicit_prefill_first.is_none()
2332            && !entries.contains_key("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE")
2333        {
2334            default_prefill_first_until_active
2335                .as_ref()
2336                .map(|until| until.value.to_string())
2337        } else {
2338            None
2339        };
2340        let prefill_first = explicit_prefill_first
2341            .copied()
2342            .or(implicit_prefill_first.as_deref());
2343        let active_decode_chunk = entries.get("FERRUM_ACTIVE_DECODE_PREFILL_CHUNK");
2344        if let Some(chunk) = active_decode_chunk {
2345            let chunk_value = parse_usize_env_value(chunk).map_err(|reason| {
2346                AutoConfigError::InvalidOverride {
2347                    key: "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK".to_string(),
2348                    reason,
2349                }
2350            })?;
2351            self.unsupported_if(
2352                chunk_value == 0,
2353                "scheduler_admission_policy",
2354                "active decode prefill chunk must be greater than zero",
2355            )?;
2356        }
2357        let explicit_prefill_step_chunk = entries.get("FERRUM_SCHED_PREFILL_STEP_CHUNK");
2358        let explicit_prefill_step_chunk_present = explicit_prefill_step_chunk.is_some();
2359        let prefill_step_chunk = explicit_prefill_step_chunk.copied();
2360        if let Some(chunk) = prefill_step_chunk {
2361            let chunk_value = parse_usize_env_value(chunk).map_err(|reason| {
2362                AutoConfigError::InvalidOverride {
2363                    key: "FERRUM_SCHED_PREFILL_STEP_CHUNK".to_string(),
2364                    reason,
2365                }
2366            })?;
2367            self.unsupported_if(
2368                chunk_value == 0,
2369                "scheduler_admission_policy",
2370                "scheduler prefill step chunk must be greater than zero",
2371            )?;
2372        }
2373        let (mut selected, mut source, mut source_key) = if let (Some(until), Some(chunk)) =
2374            (prefill_first, active_decode_chunk)
2375        {
2376            parse_usize_env_value(until).map_err(|reason| AutoConfigError::InvalidOverride {
2377                key: "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE".to_string(),
2378                reason,
2379            })?;
2380            parse_usize_env_value(chunk).map_err(|reason| AutoConfigError::InvalidOverride {
2381                key: "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK".to_string(),
2382                reason,
2383            })?;
2384            let key = "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK";
2385            (
2386                format!("prefill_first_until_active:{until}+active_decode_prefill_chunk:{chunk}"),
2387                self.source_for_key(key, AutoConfigSource::Default),
2388                Some(key.to_string()),
2389            )
2390        } else if let Some(chunk) = active_decode_chunk {
2391            parse_usize_env_value(chunk).map_err(|reason| AutoConfigError::InvalidOverride {
2392                key: "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK".to_string(),
2393                reason,
2394            })?;
2395            let key = "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK";
2396            (
2397                format!("active_decode_prefill_chunk:{chunk}"),
2398                self.source_for_key(key, AutoConfigSource::Default),
2399                Some(key.to_string()),
2400            )
2401        } else if let Some(until) = prefill_first {
2402            parse_usize_env_value(until).map_err(|reason| AutoConfigError::InvalidOverride {
2403                key: "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE".to_string(),
2404                reason,
2405            })?;
2406            let key = "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE";
2407            let (source, source_key) = if explicit_prefill_first_present {
2408                (
2409                    self.source_for_key(key, AutoConfigSource::Default),
2410                    Some(key.to_string()),
2411                )
2412            } else if let Some(default) = default_prefill_first_until_active.as_ref() {
2413                (default.source, default.source_key.clone())
2414            } else {
2415                (AutoConfigSource::Default, None)
2416            };
2417            (
2418                format!("prefill_first_until_active:{until}"),
2419                source,
2420                source_key,
2421            )
2422        } else if !entries.contains_key("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE") {
2423            if let Some(until) = default_prefill_first_until_active.as_ref() {
2424                (
2425                    format!("prefill_first_until_active:{}", until.value),
2426                    until.source,
2427                    until.source_key.clone(),
2428                )
2429            } else {
2430                prompt_scheduler()?
2431            }
2432        } else {
2433            prompt_scheduler()?
2434        };
2435        if let Some(chunk) = prefill_step_chunk {
2436            selected.push_str(&format!("+prefill_step_chunk:{chunk}"));
2437            if explicit_prefill_step_chunk_present && source_key.is_none() {
2438                let key = "FERRUM_SCHED_PREFILL_STEP_CHUNK";
2439                source = self.source_for_key(key, AutoConfigSource::Default);
2440                source_key = Some(key.to_string());
2441            }
2442        } else if default_prefill_first_until_active.is_some()
2443            && !entries.contains_key("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE")
2444        {
2445            selected.push_str("+prefill_token_budget:elastic");
2446        }
2447        Ok(self.decision(
2448            "scheduler_admission_policy",
2449            &selected,
2450            source,
2451            source_key,
2452            [
2453                "continuous_default",
2454                "prompt_token_estimate",
2455                "prefill_first_until_active",
2456                "prefill_first_until_active+active_decode_prefill_chunk",
2457                "active_decode_prefill_chunk",
2458                "prefill_step_chunk",
2459                "prefill_token_budget:elastic",
2460            ],
2461            Vec::new(),
2462            vec![RuntimeConfigEffect::Performance],
2463        ))
2464    }
2465
2466    fn prefix_cache_decision(&self, prefix_cache: ResolvedValue<bool>) -> AutoConfigDecision {
2467        let selected = if prefix_cache.value {
2468            "prefix_cache_enabled"
2469        } else {
2470            "prefix_cache_disabled"
2471        };
2472        self.decision(
2473            "prefix_cache_policy",
2474            selected,
2475            prefix_cache.source,
2476            prefix_cache.source_key,
2477            ["prefix_cache_enabled", "prefix_cache_disabled"],
2478            self.rejected_except(
2479                selected,
2480                [
2481                    ("prefix_cache_enabled", "prefix cache not selected"),
2482                    ("prefix_cache_disabled", "prefix cache enabled"),
2483                ],
2484            ),
2485            vec![
2486                RuntimeConfigEffect::Correctness,
2487                RuntimeConfigEffect::Performance,
2488                RuntimeConfigEffect::Memory,
2489            ],
2490        )
2491    }
2492
2493    fn sampling_decision(&self, greedy: ResolvedValue<bool>) -> AutoConfigDecision {
2494        let selected = if greedy.value {
2495            "gpu_greedy_argmax"
2496        } else {
2497            "logits_readback"
2498        };
2499        self.decision(
2500            "sampling_readback_path",
2501            selected,
2502            greedy.source,
2503            greedy.source_key,
2504            ["gpu_greedy_argmax", "logits_readback"],
2505            self.rejected_except(
2506                selected,
2507                [
2508                    ("gpu_greedy_argmax", "GPU argmax not selected"),
2509                    ("logits_readback", "logits readback not selected"),
2510                ],
2511            ),
2512            vec![
2513                RuntimeConfigEffect::Performance,
2514                RuntimeConfigEffect::Correctness,
2515            ],
2516        )
2517    }
2518
2519    fn decision<I, C>(
2520        &self,
2521        selection: &str,
2522        selected: &str,
2523        source: AutoConfigSource,
2524        source_key: Option<String>,
2525        candidates: I,
2526        rejected: Vec<RejectedCandidate>,
2527        affects: Vec<RuntimeConfigEffect>,
2528    ) -> AutoConfigDecision
2529    where
2530        I: IntoIterator<Item = C>,
2531        C: Into<String>,
2532    {
2533        AutoConfigDecision {
2534            schema_version: 1,
2535            selection: selection.to_string(),
2536            selected: selected.to_string(),
2537            source,
2538            source_key,
2539            candidates: candidates.into_iter().map(Into::into).collect(),
2540            rejected,
2541            affects,
2542        }
2543    }
2544
2545    fn rejected_except<I>(&self, selected: &str, candidates: I) -> Vec<RejectedCandidate>
2546    where
2547        I: IntoIterator<Item = (&'static str, &'static str)>,
2548    {
2549        candidates
2550            .into_iter()
2551            .filter(|(value, _)| *value != selected)
2552            .map(|(value, reason)| RejectedCandidate {
2553                value: value.to_string(),
2554                reason: reason.to_string(),
2555            })
2556            .collect()
2557    }
2558
2559    fn invalid<T>(&self, key: &str, reason: &str) -> Result<T, AutoConfigError> {
2560        Err(AutoConfigError::InvalidOverride {
2561            key: key.to_string(),
2562            reason: reason.to_string(),
2563        })
2564    }
2565
2566    fn unsupported<T>(&self, selection: &str, reason: &str) -> Result<T, AutoConfigError> {
2567        Err(AutoConfigError::UnsupportedCombination {
2568            selection: selection.to_string(),
2569            reason: reason.to_string(),
2570        })
2571    }
2572
2573    fn unsupported_if(
2574        &self,
2575        condition: bool,
2576        selection: &str,
2577        reason: &str,
2578    ) -> Result<(), AutoConfigError> {
2579        if condition {
2580            self.unsupported(selection, reason)
2581        } else {
2582            Ok(())
2583        }
2584    }
2585}
2586
2587fn kv_cache_bytes_per_token_for_model(model: &ModelCapabilities) -> Option<u64> {
2588    let layers = model.num_hidden_layers? as u64;
2589    let kv_heads = model.kv_heads? as u64;
2590    let head_dim = model.head_dim? as u64;
2591    layers
2592        .checked_mul(2)?
2593        .checked_mul(kv_heads)?
2594        .checked_mul(head_dim)?
2595        .checked_mul(2)
2596}
2597
2598fn qwen_moe_architecture_uses_vllm_paged_attn(architecture: &str) -> bool {
2599    architecture.eq_ignore_ascii_case("qwen3_moe")
2600        || architecture.eq_ignore_ascii_case("qwen3_5_moe")
2601}
2602
2603#[derive(Debug, Clone, PartialEq, Eq)]
2604struct ResolvedValue<T> {
2605    value: T,
2606    source: AutoConfigSource,
2607    source_key: Option<String>,
2608}
2609
2610#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2611struct RecurrentStateBudget {
2612    remaining_bytes: u64,
2613    bytes_per_sequence: u64,
2614    raw_slots: usize,
2615    floored_slots: usize,
2616}
2617
2618fn recurrent_state_budget_for(
2619    model: &ModelCapabilities,
2620    hardware: &HardwareCapabilities,
2621) -> Option<RecurrentStateBudget> {
2622    if !(hardware.backend.eq_ignore_ascii_case("cuda")
2623        || hardware.backend.eq_ignore_ascii_case("metal"))
2624    {
2625        return None;
2626    }
2627    let bytes_per_sequence = model.recurrent_state_bytes_per_sequence?.max(1);
2628    let vram_bytes = hardware.vram_bytes?;
2629    let weight_bytes = model.estimated_weight_bytes?;
2630    let remaining = vram_bytes.saturating_sub(weight_bytes);
2631    let raw_slots = (remaining / bytes_per_sequence) as usize;
2632    Some(RecurrentStateBudget {
2633        remaining_bytes: remaining,
2634        bytes_per_sequence,
2635        raw_slots,
2636        floored_slots: floor_power_of_two(raw_slots.max(1)),
2637    })
2638}
2639
2640fn parse_compute_capability(value: &str) -> Option<(u32, u32)> {
2641    let value = value.trim();
2642    if value.is_empty() {
2643        return None;
2644    }
2645    let (major, minor) = value.split_once('.').unwrap_or((value, "0"));
2646    Some((major.trim().parse().ok()?, minor.trim().parse().ok()?))
2647}
2648
2649fn vram_default_max_sequences(vram_bytes: u64) -> usize {
2650    match vram_bytes {
2651        bytes if bytes >= 20 * GIB => 32,
2652        bytes if bytes >= 12 * GIB => 16,
2653        bytes if bytes >= 8 * GIB => 8,
2654        _ => 4,
2655    }
2656}
2657
2658fn default_gpu_devices_for_backend(backend: &str) -> Option<Vec<usize>> {
2659    backend.eq_ignore_ascii_case("cuda").then(|| vec![0])
2660}
2661
2662fn ceil_div(value: usize, divisor: usize) -> usize {
2663    value.div_ceil(divisor)
2664}
2665
2666fn effective_admission_limit(
2667    authority: ExecutionResourceAuthority,
2668    max_sequences: Option<usize>,
2669    recurrent_state_max_slots: Option<usize>,
2670) -> Option<usize> {
2671    if authority == ExecutionResourceAuthority::PlanRuntime {
2672        return max_sequences;
2673    }
2674    match (max_sequences, recurrent_state_max_slots) {
2675        (Some(max_sequences), Some(recurrent_slots)) => Some(max_sequences.min(recurrent_slots)),
2676        (Some(max_sequences), None) => Some(max_sequences),
2677        (None, Some(recurrent_slots)) => Some(recurrent_slots),
2678        (None, None) => None,
2679    }
2680}
2681
2682fn floor_power_of_two(value: usize) -> usize {
2683    if value <= 1 {
2684        return 1;
2685    }
2686    1usize << (usize::BITS - 1 - value.leading_zeros())
2687}
2688
2689fn auto_config_source_from_runtime(source: RuntimeConfigSource) -> AutoConfigSource {
2690    match source {
2691        RuntimeConfigSource::Default => AutoConfigSource::Default,
2692        RuntimeConfigSource::ConfigFile => AutoConfigSource::ConfigFile,
2693        RuntimeConfigSource::Cli => AutoConfigSource::Cli,
2694        RuntimeConfigSource::Env => AutoConfigSource::Env,
2695        RuntimeConfigSource::ScriptCase => AutoConfigSource::ScriptCase,
2696        RuntimeConfigSource::MemoryProfile => AutoConfigSource::MemoryProfile,
2697    }
2698}
2699
2700#[cfg(test)]
2701mod tests {
2702    use super::*;
2703
2704    fn snapshot(vars: &[(&str, &str)]) -> RuntimeConfigSnapshot {
2705        RuntimeConfigSnapshot::from_env_vars(vars.iter().copied())
2706    }
2707
2708    fn snapshot_with_sources(vars: &[(&str, &str, RuntimeConfigSource)]) -> RuntimeConfigSnapshot {
2709        let mut entries: Vec<_> = vars
2710            .iter()
2711            .map(|(key, effective_value, source)| RuntimeConfigEntry {
2712                key: (*key).to_string(),
2713                effective_value: (*effective_value).to_string(),
2714                source: *source,
2715                affects: vec![RuntimeConfigEffect::Performance],
2716            })
2717            .collect();
2718        entries.sort_by(|a, b| a.key.cmp(&b.key));
2719        RuntimeConfigSnapshot { entries }
2720    }
2721
2722    fn m3(vars: &[(&str, &str)], features: CompiledKernelFeatures) -> FerrumConfigBuilder {
2723        FerrumConfigBuilder::new(snapshot(vars))
2724            .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
2725            .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(features))
2726            .with_workload_profile(WorkloadProfile::m3_qwen3_30b_a3b_int4())
2727    }
2728
2729    fn m3_with_hardware(
2730        vars: &[(&str, &str)],
2731        hardware: HardwareCapabilities,
2732    ) -> FerrumConfigBuilder {
2733        FerrumConfigBuilder::new(snapshot(vars))
2734            .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
2735            .with_hardware_capabilities(hardware)
2736            .with_workload_profile(WorkloadProfile::m3_qwen3_30b_a3b_int4())
2737    }
2738
2739    fn qwen35_moe_gptq_int4_model() -> ModelCapabilities {
2740        let mut model = ModelCapabilities::qwen3_30b_a3b_gptq_int4();
2741        model.architecture = "qwen3_5_moe".to_string();
2742        model.head_dim = Some(256);
2743        model.num_hidden_layers = Some(40);
2744        model.kv_heads = Some(8);
2745        model.estimated_weight_bytes = Some(24_419_939_760);
2746        model.recurrent_state_bytes_per_sequence = Some(32_931_840);
2747        model
2748    }
2749
2750    fn synthetic_tight_recurrent_state_model() -> ModelCapabilities {
2751        ModelCapabilities {
2752            architecture: "synthetic_recurrent_state".to_string(),
2753            quantization: None,
2754            moe: None,
2755            max_context_len: Some(262_144),
2756            num_hidden_layers: Some(40),
2757            head_dim: Some(256),
2758            kv_heads: Some(8),
2759            estimated_weight_bytes: Some(24_419_939_760),
2760            recurrent_state_bytes_per_sequence: Some(65_863_680),
2761            supported_dtypes: vec!["fp16".to_string()],
2762            graph_safe_moe: false,
2763        }
2764    }
2765
2766    fn qwen25_layer_split_runtime_entries(source: RuntimeConfigSource) -> RuntimeConfigSnapshot {
2767        snapshot_with_sources(&[
2768            ("FERRUM_REQUESTED_GPU_DEVICES", "0,1", source),
2769            ("FERRUM_SELECTED_GPU_DEVICES", "0,1", source),
2770            ("FERRUM_CUDA_DEVICE_COUNT", "2", source),
2771            (
2772                "FERRUM_SELECTED_DISTRIBUTED_STRATEGY",
2773                "layer_split",
2774                source,
2775            ),
2776            (
2777                "FERRUM_SELECTED_LAYER_SPLIT_PLAN",
2778                "stage0:cuda:0:layers=0-39;stage1:cuda:1:layers=40-79",
2779                source,
2780            ),
2781            ("FERRUM_LAYER_SPLIT_PIPELINE_MODE", "batch", source),
2782            ("FERRUM_MAX_MODEL_LEN", "4096", source),
2783            ("FERRUM_KV_MAX_BLOCKS", "1024", source),
2784            ("FERRUM_KV_CAPACITY", "1024", source),
2785            ("FERRUM_PAGED_MAX_SEQS", "16", source),
2786            ("FERRUM_MAX_BATCHED_TOKENS", "1536", source),
2787            ("FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE", "16", source),
2788        ])
2789    }
2790
2791    fn gemma3_gptq_model() -> ModelCapabilities {
2792        ModelCapabilities {
2793            architecture: "gemma3".to_string(),
2794            quantization: Some("gptq_int4".to_string()),
2795            moe: None,
2796            max_context_len: Some(131_072),
2797            num_hidden_layers: Some(62),
2798            head_dim: Some(256),
2799            kv_heads: Some(16),
2800            estimated_weight_bytes: Some(15 * GIB),
2801            recurrent_state_bytes_per_sequence: None,
2802            supported_dtypes: vec!["fp16".to_string()],
2803            graph_safe_moe: false,
2804        }
2805    }
2806
2807    fn expect_invalid_key(vars: &[(&str, &str)], key: &str) {
2808        expect_invalid_key_with_features(
2809            vars,
2810            key,
2811            CompiledKernelFeatures::m3_fast_path_without_fa2(),
2812        );
2813    }
2814
2815    fn expect_invalid_key_with_features(
2816        vars: &[(&str, &str)],
2817        key: &str,
2818        features: CompiledKernelFeatures,
2819    ) {
2820        expect_invalid_key_with_hardware(vars, key, HardwareCapabilities::rtx4090_cuda(features));
2821    }
2822
2823    fn expect_invalid_key_with_hardware(
2824        vars: &[(&str, &str)],
2825        key: &str,
2826        hardware: HardwareCapabilities,
2827    ) {
2828        let err = m3_with_hardware(vars, hardware)
2829            .resolve()
2830            .expect_err("override should fail");
2831        match err {
2832            AutoConfigError::InvalidOverride { key: actual, .. } => assert_eq!(actual, key),
2833            other => panic!("expected invalid override for {key}, got {other:?}"),
2834        }
2835    }
2836
2837    fn cpu_hardware_with_features(features: CompiledKernelFeatures) -> HardwareCapabilities {
2838        HardwareCapabilities {
2839            backend: "cpu".to_string(),
2840            supported_dtypes: vec!["fp32".to_string()],
2841            supported_kv_dtypes: vec!["fp16".to_string()],
2842            compiled_features: features,
2843            ..HardwareCapabilities::unknown()
2844        }
2845    }
2846
2847    #[test]
2848    fn m3_preset_selects_current_safe_fast_path_without_fa2() {
2849        let resolved = m3(&[], CompiledKernelFeatures::m3_fast_path_without_fa2())
2850            .resolve()
2851            .unwrap();
2852        let decisions: BTreeMap<_, _> = resolved
2853            .decisions
2854            .iter()
2855            .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
2856            .collect();
2857        assert_eq!(
2858            decisions["attention_prefill_mixed_backend"],
2859            "vllm_paged_varlen"
2860        );
2861        assert_eq!(
2862            decisions["attention_decode_backend"],
2863            "vllm_paged_attn_v1_short"
2864        );
2865        assert_eq!(
2866            decisions["moe_implementation"],
2867            "vllm_marlin_moe_device_route_pair_ids"
2868        );
2869        assert_eq!(decisions["moe_graph_policy"], "graph_disabled");
2870        assert_eq!(decisions["decode_graph_policy"], "graph_disabled");
2871        assert_eq!(decisions["prefix_cache_policy"], "prefix_cache_disabled");
2872        assert_eq!(decisions["sampling_readback_path"], "gpu_greedy_argmax");
2873        assert_eq!(
2874            resolved.preset.as_deref(),
2875            Some(M3_QWEN3_30B_A3B_INT4_PRESET)
2876        );
2877    }
2878
2879    #[test]
2880    fn cuda_gptq_moe_enables_vllm_marlin_without_m3_preset() {
2881        // `ferrum run` resolves with the serving-default workload, NOT the m3
2882        // bench preset, so the old `is_m3_preset()`-gated FERRUM_VLLM_MOE never
2883        // fired and the 30B fell back to the slow host-route MoE (~9.7 vs ~59
2884        // tok/s on a 4090). A CUDA GPTQ MoE must get the vLLM-Marlin fast path
2885        // on capability alone.
2886        let hardware =
2887            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
2888        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
2889        let resolved = FerrumConfigBuilder::new(snapshot(&[]))
2890            .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
2891            .with_hardware_capabilities(hardware)
2892            .with_workload_profile(workload)
2893            .resolve()
2894            .unwrap();
2895        let decisions: BTreeMap<_, _> = resolved
2896            .decisions
2897            .iter()
2898            .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
2899            .collect();
2900        assert_ne!(
2901            resolved.preset.as_deref(),
2902            Some(M3_QWEN3_30B_A3B_INT4_PRESET),
2903            "serving-default workload must not be the m3 preset"
2904        );
2905        assert_eq!(
2906            decisions["moe_implementation"], "vllm_marlin_moe_device_route_pair_ids",
2907            "CUDA GPTQ MoE should get the fast vLLM-Marlin path without the m3 preset"
2908        );
2909        // The decision is not enough — the model reads FERRUM_VLLM_MOE from the
2910        // effective config, not the decisions. The resolved knob must be a
2911        // runtime_config entry so `ferrum run`'s materialize/apply propagates it.
2912        let entry = resolved
2913            .runtime_config
2914            .entries
2915            .iter()
2916            .find(|e| e.key == "FERRUM_VLLM_MOE");
2917        assert_eq!(
2918            entry.map(|e| e.effective_value.as_str()),
2919            Some("1"),
2920            "resolved FERRUM_VLLM_MOE must be materialized into the effective config"
2921        );
2922    }
2923
2924    #[test]
2925    fn cuda_qwen3_moe_enables_vllm_paged_attn_without_m3_preset() {
2926        // `ferrum run` and ordinary `serve` use the serving-default workload,
2927        // not the m3 preset. Qwen3-MoE on CUDA with the VPA kernel compiled
2928        // must still select and materialize the paged-attention runtime knob,
2929        // otherwise the effective config/decision trace says "legacy" while
2930        // the model runtime can take the VPA path through its own defaults.
2931        let hardware =
2932            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
2933        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
2934        let resolved = FerrumConfigBuilder::new(snapshot(&[]))
2935            .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
2936            .with_hardware_capabilities(hardware)
2937            .with_workload_profile(workload)
2938            .resolve()
2939            .unwrap();
2940        let decisions: BTreeMap<_, _> = resolved
2941            .decisions
2942            .iter()
2943            .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
2944            .collect();
2945        assert_eq!(
2946            decisions["attention_decode_backend"], "vllm_paged_attn_v1_short",
2947            "CUDA Qwen3-MoE should get VPA decode without the m3 preset"
2948        );
2949        let entry = |key: &str| {
2950            resolved
2951                .runtime_config
2952                .entries
2953                .iter()
2954                .find(|entry| entry.key == key)
2955                .unwrap_or_else(|| panic!("missing runtime config entry {key}"))
2956        };
2957        assert_eq!(entry("FERRUM_USE_VLLM_PAGED_ATTN").effective_value, "1");
2958        assert_eq!(
2959            entry("FERRUM_VLLM_PAGED_ATTN_V1_SHORT").effective_value,
2960            "1"
2961        );
2962    }
2963
2964    #[test]
2965    fn cuda_qwen35_moe_enables_vllm_paged_attn_v2_without_m3_preset() {
2966        // Qwen3.5-MoE shares the Qwen MoE CUDA fast-path requirements, but its
2967        // full-attention head_dim is 256. The H256 path uses the v2 paged
2968        // attention launcher, so the default decision trace must not report
2969        // the H128-oriented v1-short path.
2970        let mut hardware =
2971            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
2972        hardware.vram_bytes = Some(48 * GIB);
2973        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
2974        let model = qwen35_moe_gptq_int4_model();
2975        let resolved = FerrumConfigBuilder::new(snapshot(&[]))
2976            .with_model_capabilities(model)
2977            .with_hardware_capabilities(hardware)
2978            .with_workload_profile(workload)
2979            .resolve()
2980            .unwrap();
2981        let decisions: BTreeMap<_, _> = resolved
2982            .decisions
2983            .iter()
2984            .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
2985            .collect();
2986        assert_eq!(
2987            decisions["attention_prefill_mixed_backend"],
2988            "vllm_paged_varlen"
2989        );
2990        assert_eq!(decisions["attention_decode_backend"], "vllm_paged_attn_v2");
2991        let entry = |key: &str| {
2992            resolved
2993                .runtime_config
2994                .entries
2995                .iter()
2996                .find(|entry| entry.key == key)
2997                .unwrap_or_else(|| panic!("missing runtime config entry {key}"))
2998        };
2999        assert_eq!(entry("FERRUM_USE_VLLM_PAGED_ATTN").effective_value, "1");
3000        assert_eq!(
3001            entry("FERRUM_VLLM_PAGED_ATTN_V1_SHORT").effective_value,
3002            "0"
3003        );
3004    }
3005
3006    #[test]
3007    fn recurrent_state_budget_caps_default_slots_without_model_vram_special_case() {
3008        let hardware =
3009            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3010        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3011        let resolved = FerrumConfigBuilder::new(snapshot(&[("FERRUM_PAGED_MAX_SEQS", "32")]))
3012            .with_model_capabilities(synthetic_tight_recurrent_state_model())
3013            .with_hardware_capabilities(hardware)
3014            .with_workload_profile(workload)
3015            .resolve()
3016            .unwrap();
3017        let decision = |selection: &str| {
3018            resolved
3019                .decisions
3020                .iter()
3021                .find(|decision| decision.selection == selection)
3022                .unwrap_or_else(|| panic!("missing decision {selection}"))
3023        };
3024
3025        assert_eq!(decision("max_sequences").selected, "32");
3026        assert_eq!(decision("recurrent_state_max_slots").selected, "16");
3027        assert_eq!(
3028            decision("recurrent_state_max_slots").source,
3029            AutoConfigSource::MemoryProfile
3030        );
3031        let entry = resolved
3032            .runtime_config
3033            .entries
3034            .iter()
3035            .find(|entry| entry.key == "FERRUM_RECURRENT_STATE_MAX_SLOTS")
3036            .expect("memory-profile recurrent slot cap should reach effective runtime config");
3037        assert_eq!(entry.effective_value, "16");
3038        assert_eq!(entry.source, RuntimeConfigSource::MemoryProfile);
3039        let doc = resolved.effective_config_document();
3040        assert_eq!(doc["selected_max_sequences"], serde_json::json!(32));
3041        assert_eq!(
3042            doc["selected_recurrent_state_max_slots"],
3043            serde_json::json!(16)
3044        );
3045        assert_eq!(doc["selected_admission_limit"], serde_json::json!(16));
3046        assert_eq!(
3047            doc["admission"]["effective_max_concurrent"],
3048            serde_json::json!(16)
3049        );
3050        assert_eq!(
3051            doc["admission"]["recurrent_state_max_slots"],
3052            serde_json::json!(16)
3053        );
3054        assert_eq!(
3055            doc["admission"]["memory_estimate"]["recurrent_state_bytes_per_sequence"],
3056            serde_json::json!(65_863_680u64)
3057        );
3058        assert_eq!(
3059            doc["admission"]["memory_estimate"]["recurrent_state_budget_bytes"],
3060            serde_json::json!(24u64 * GIB - 24_419_939_760u64)
3061        );
3062        assert_eq!(
3063            doc["admission"]["memory_estimate"]["recurrent_state_budget_raw_slots"],
3064            serde_json::json!(20)
3065        );
3066        assert_eq!(
3067            doc["admission"]["memory_estimate"]["recurrent_state_budget_max_slots"],
3068            serde_json::json!(16)
3069        );
3070        assert_eq!(
3071            doc["admission"]["memory_estimate"]["recurrent_state_capacity_bytes"],
3072            serde_json::json!(16u64 * 65_863_680u64)
3073        );
3074    }
3075
3076    #[test]
3077    fn qwen35_fast_recurrent_state_budget_selects_default_slots_without_vram_special_case() {
3078        let hardware =
3079            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3080        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3081        let resolved = FerrumConfigBuilder::new(snapshot(&[("FERRUM_PAGED_MAX_SEQS", "32")]))
3082            .with_model_capabilities(qwen35_moe_gptq_int4_model())
3083            .with_hardware_capabilities(hardware)
3084            .with_workload_profile(workload)
3085            .resolve()
3086            .unwrap();
3087        let decision = |selection: &str| {
3088            resolved
3089                .decisions
3090                .iter()
3091                .find(|decision| decision.selection == selection)
3092                .unwrap_or_else(|| panic!("missing decision {selection}"))
3093        };
3094
3095        assert_eq!(decision("max_sequences").selected, "32");
3096        assert_eq!(decision("recurrent_state_max_slots").selected, "32");
3097        assert_eq!(
3098            decision("recurrent_state_max_slots").source,
3099            AutoConfigSource::Env
3100        );
3101        let doc = resolved.effective_config_document();
3102        assert_eq!(doc["selected_max_sequences"], serde_json::json!(32));
3103        assert_eq!(
3104            doc["selected_recurrent_state_max_slots"],
3105            serde_json::json!(32)
3106        );
3107        assert_eq!(doc["selected_admission_limit"], serde_json::json!(32));
3108        assert_eq!(
3109            doc["admission"]["memory_estimate"]["recurrent_state_bytes_per_sequence"],
3110            serde_json::json!(32_931_840u64)
3111        );
3112        assert_eq!(
3113            doc["admission"]["memory_estimate"]["recurrent_state_budget_raw_slots"],
3114            serde_json::json!(40)
3115        );
3116        assert_eq!(
3117            doc["admission"]["memory_estimate"]["recurrent_state_budget_max_slots"],
3118            serde_json::json!(32)
3119        );
3120        assert_eq!(
3121            doc["admission"]["memory_estimate"]["recurrent_state_capacity_bytes"],
3122            serde_json::json!(32u64 * 32_931_840u64)
3123        );
3124    }
3125
3126    #[test]
3127    fn recurrent_state_budget_rejects_explicit_slot_pool_above_budget() {
3128        let hardware =
3129            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3130        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3131        let err = FerrumConfigBuilder::new(snapshot(&[
3132            ("FERRUM_PAGED_MAX_SEQS", "32"),
3133            ("FERRUM_RECURRENT_STATE_MAX_SLOTS", "32"),
3134        ]))
3135        .with_model_capabilities(synthetic_tight_recurrent_state_model())
3136        .with_hardware_capabilities(hardware)
3137        .with_workload_profile(workload)
3138        .resolve()
3139        .unwrap_err();
3140
3141        match err {
3142            AutoConfigError::InvalidOverride { key, reason } => {
3143                assert_eq!(key, "FERRUM_RECURRENT_STATE_MAX_SLOTS");
3144                assert!(
3145                    reason.contains("recurrent-state slot pool exceeds"),
3146                    "{reason}"
3147                );
3148                assert!(
3149                    reason.contains("FERRUM_RECURRENT_STATE_MAX_SLOTS=16"),
3150                    "{reason}"
3151                );
3152            }
3153            other => panic!("unexpected error: {other:?}"),
3154        }
3155    }
3156
3157    #[test]
3158    fn effective_admission_limit_respects_resource_authority() {
3159        let legacy = ExecutionResourceAuthority::LegacyEngine;
3160        let plan = ExecutionResourceAuthority::PlanRuntime;
3161        assert_eq!(
3162            effective_admission_limit(legacy, Some(32), Some(16)),
3163            Some(16)
3164        );
3165        assert_eq!(
3166            effective_admission_limit(legacy, Some(16), Some(32)),
3167            Some(16)
3168        );
3169        assert_eq!(effective_admission_limit(legacy, Some(32), None), Some(32));
3170        assert_eq!(effective_admission_limit(legacy, None, Some(16)), Some(16));
3171        assert_eq!(effective_admission_limit(legacy, None, None), None);
3172        assert_eq!(
3173            effective_admission_limit(plan, Some(32), Some(16)),
3174            Some(32)
3175        );
3176    }
3177
3178    #[test]
3179    fn plan_runtime_uses_dynamic_admission_and_typed_attention_authority() {
3180        let hardware =
3181            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3182        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3183        let resolved = FerrumConfigBuilder::new(snapshot(&[("FERRUM_PAGED_MAX_SEQS", "32")]))
3184            .with_model_capabilities(synthetic_tight_recurrent_state_model())
3185            .with_hardware_capabilities(hardware)
3186            .with_workload_profile(workload)
3187            .with_execution_resource_authority(ExecutionResourceAuthority::PlanRuntime)
3188            .resolve()
3189            .unwrap();
3190
3191        assert_eq!(
3192            resolved.execution_resource_authority,
3193            ExecutionResourceAuthority::PlanRuntime
3194        );
3195        assert_eq!(
3196            resolved.requested_attention_policy,
3197            Some(AttentionExecutionPolicy::Auto)
3198        );
3199        assert_eq!(
3200            resolved.compiled_attention_policy,
3201            Some(AttentionExecutionPolicy::NativeAdaptive)
3202        );
3203        assert!(resolved
3204            .decisions
3205            .iter()
3206            .all(|decision| decision.selection != "recurrent_state_max_slots"));
3207        assert!(resolved
3208            .runtime_config
3209            .entries
3210            .iter()
3211            .all(|entry| !matches!(
3212                entry.key.as_str(),
3213                "FERRUM_RECURRENT_STATE_MAX_SLOTS"
3214                    | "FERRUM_USE_VLLM_PAGED_ATTN"
3215                    | "FERRUM_VLLM_PAGED_ATTN_V1_SHORT"
3216            )));
3217
3218        let document = resolved.effective_config_document();
3219        assert_eq!(document["selected_max_sequences"], serde_json::json!(32));
3220        assert_eq!(document["selected_admission_limit"], serde_json::json!(32));
3221        assert_eq!(
3222            document["selected_attention_policy"],
3223            serde_json::json!("native-adaptive")
3224        );
3225        assert_eq!(
3226            document["attention_execution"]["requested_policy"],
3227            serde_json::json!("auto")
3228        );
3229        assert_eq!(
3230            document["attention_execution"]["compiled_policy"],
3231            serde_json::json!("native-adaptive")
3232        );
3233        assert_eq!(
3234            document["attention_execution"]["legacy_attention_keys_apply"],
3235            serde_json::json!(false)
3236        );
3237        assert_eq!(
3238            document["admission"]["resource_authority"],
3239            serde_json::json!("plan_runtime")
3240        );
3241        assert_eq!(
3242            document["admission"]["effective_max_concurrent"],
3243            serde_json::json!(32)
3244        );
3245        assert_eq!(
3246            document["admission"]["legacy_recurrent_state_limit_applies"],
3247            serde_json::json!(false)
3248        );
3249    }
3250
3251    #[test]
3252    fn plan_runtime_rejects_legacy_attention_overrides() {
3253        let hardware =
3254            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3255        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3256        for key in [
3257            "FERRUM_USE_VLLM_PAGED_ATTN",
3258            "FERRUM_VLLM_PAGED_ATTN_V1_SHORT",
3259        ] {
3260            let error = FerrumConfigBuilder::new(snapshot(&[(key, "0")]))
3261                .with_model_capabilities(synthetic_tight_recurrent_state_model())
3262                .with_hardware_capabilities(hardware.clone())
3263                .with_workload_profile(workload.clone())
3264                .with_execution_resource_authority(ExecutionResourceAuthority::PlanRuntime)
3265                .resolve()
3266                .unwrap_err();
3267            assert!(matches!(
3268                error,
3269                AutoConfigError::InvalidOverride {
3270                    key: rejected_key,
3271                    reason,
3272                } if rejected_key == key && reason.contains("FERRUM_ATTENTION_POLICY")
3273            ));
3274        }
3275    }
3276
3277    #[test]
3278    fn plan_runtime_rejects_legacy_recurrent_slot_override() {
3279        let hardware =
3280            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3281        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3282        let error = FerrumConfigBuilder::new(snapshot(&[
3283            ("FERRUM_PAGED_MAX_SEQS", "32"),
3284            ("FERRUM_RECURRENT_STATE_MAX_SLOTS", "16"),
3285        ]))
3286        .with_model_capabilities(synthetic_tight_recurrent_state_model())
3287        .with_hardware_capabilities(hardware)
3288        .with_workload_profile(workload)
3289        .with_execution_resource_authority(ExecutionResourceAuthority::PlanRuntime)
3290        .resolve()
3291        .unwrap_err();
3292
3293        assert!(matches!(
3294            error,
3295            AutoConfigError::InvalidOverride { key, .. }
3296                if key == "FERRUM_RECURRENT_STATE_MAX_SLOTS"
3297        ));
3298    }
3299
3300    #[test]
3301    fn metal_plan_runtime_preflight_matches_portable_runtime_capability() {
3302        let mut hardware = HardwareCapabilities::unknown();
3303        hardware.backend = "metal".to_owned();
3304        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3305        let resolved = FerrumConfigBuilder::new(snapshot(&[]))
3306            .with_model_capabilities(synthetic_tight_recurrent_state_model())
3307            .with_hardware_capabilities(hardware)
3308            .with_workload_profile(workload)
3309            .with_execution_resource_authority(ExecutionResourceAuthority::PlanRuntime)
3310            .resolve()
3311            .unwrap();
3312
3313        assert_eq!(
3314            resolved.compiled_attention_policy,
3315            Some(AttentionExecutionPolicy::Portable)
3316        );
3317        assert_eq!(
3318            resolved
3319                .decisions
3320                .iter()
3321                .find(|decision| decision.selection == "attention_decode_backend")
3322                .map(|decision| decision.selected.as_str()),
3323            Some("portable")
3324        );
3325    }
3326
3327    #[test]
3328    fn explicit_recurrent_state_slot_cap_can_keep_scheduler_width_above_state_pool() {
3329        let hardware =
3330            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3331        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3332        let resolved = FerrumConfigBuilder::new(snapshot(&[
3333            ("FERRUM_PAGED_MAX_SEQS", "32"),
3334            ("FERRUM_RECURRENT_STATE_MAX_SLOTS", "16"),
3335        ]))
3336        .with_model_capabilities(synthetic_tight_recurrent_state_model())
3337        .with_hardware_capabilities(hardware)
3338        .with_workload_profile(workload)
3339        .resolve()
3340        .unwrap();
3341        let decision = |selection: &str| {
3342            resolved
3343                .decisions
3344                .iter()
3345                .find(|decision| decision.selection == selection)
3346                .unwrap_or_else(|| panic!("missing decision {selection}"))
3347        };
3348        assert_eq!(decision("max_sequences").selected, "32");
3349        assert_eq!(decision("recurrent_state_max_slots").selected, "16");
3350        let entry = resolved
3351            .runtime_config
3352            .entries
3353            .iter()
3354            .find(|entry| entry.key == "FERRUM_RECURRENT_STATE_MAX_SLOTS")
3355            .expect("recurrent-state slot cap should reach effective runtime config");
3356        assert_eq!(entry.effective_value, "16");
3357    }
3358
3359    #[test]
3360    fn recurrent_state_budget_ignores_removed_qwen35_slot_alias() {
3361        let hardware =
3362            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3363        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3364        let resolved = FerrumConfigBuilder::new(snapshot(&[
3365            ("FERRUM_PAGED_MAX_SEQS", "32"),
3366            ("FERRUM_QWEN35_LINEAR_STATE_MAX_SLOTS", "7"),
3367        ]))
3368        .with_model_capabilities(synthetic_tight_recurrent_state_model())
3369        .with_hardware_capabilities(hardware)
3370        .with_workload_profile(workload)
3371        .resolve()
3372        .unwrap();
3373        let decision = resolved
3374            .decisions
3375            .iter()
3376            .find(|decision| decision.selection == "recurrent_state_max_slots")
3377            .expect("missing recurrent_state_max_slots decision");
3378        assert_eq!(decision.selected, "16");
3379        assert_eq!(decision.source, AutoConfigSource::MemoryProfile);
3380        assert_eq!(decision.source_key, None);
3381        assert!(resolved
3382            .runtime_config
3383            .entries
3384            .iter()
3385            .any(|entry| entry.key == "FERRUM_RECURRENT_STATE_MAX_SLOTS"
3386                && entry.effective_value == "16"));
3387    }
3388
3389    #[test]
3390    fn recurrent_state_budget_rejects_zero_slot_pool() {
3391        let hardware =
3392            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3393        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3394        let err = FerrumConfigBuilder::new(snapshot(&[
3395            ("FERRUM_PAGED_MAX_SEQS", "16"),
3396            ("FERRUM_RECURRENT_STATE_MAX_SLOTS", "0"),
3397        ]))
3398        .with_model_capabilities(synthetic_tight_recurrent_state_model())
3399        .with_hardware_capabilities(hardware)
3400        .with_workload_profile(workload)
3401        .resolve()
3402        .unwrap_err();
3403
3404        assert!(matches!(
3405            err,
3406            AutoConfigError::InvalidOverride { key, .. }
3407                if key == "FERRUM_RECURRENT_STATE_MAX_SLOTS"
3408        ));
3409    }
3410
3411    #[test]
3412    fn recurrent_state_budget_allows_c32_when_memory_budget_fits() {
3413        let mut hardware =
3414            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3415        hardware.vram_bytes = Some(48 * GIB);
3416        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3417        let resolved = FerrumConfigBuilder::new(snapshot(&[("FERRUM_PAGED_MAX_SEQS", "32")]))
3418            .with_model_capabilities(synthetic_tight_recurrent_state_model())
3419            .with_hardware_capabilities(hardware)
3420            .with_workload_profile(workload)
3421            .resolve()
3422            .unwrap();
3423        let max_sequences = resolved
3424            .decisions
3425            .iter()
3426            .find(|decision| decision.selection == "max_sequences")
3427            .unwrap();
3428        assert_eq!(max_sequences.selected, "32");
3429    }
3430
3431    #[test]
3432    fn cuda_qwen3_moe_vllm_paged_attn_env_opt_out_is_materialized() {
3433        let hardware =
3434            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3435        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3436        let resolved = FerrumConfigBuilder::new(snapshot(&[("FERRUM_USE_VLLM_PAGED_ATTN", "0")]))
3437            .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
3438            .with_hardware_capabilities(hardware)
3439            .with_workload_profile(workload)
3440            .resolve()
3441            .unwrap();
3442        let decisions: BTreeMap<_, _> = resolved
3443            .decisions
3444            .iter()
3445            .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
3446            .collect();
3447        assert_eq!(decisions["attention_decode_backend"], "legacy_paged_decode");
3448        let entry = resolved
3449            .runtime_config
3450            .entries
3451            .iter()
3452            .find(|entry| entry.key == "FERRUM_USE_VLLM_PAGED_ATTN")
3453            .expect("env opt-out should stay in effective config");
3454        assert_eq!(entry.effective_value, "0");
3455        assert_eq!(entry.source, RuntimeConfigSource::Env);
3456    }
3457
3458    #[test]
3459    fn qwen25_72b_layer_split_preset_selects_batch_tuned_defaults() {
3460        let resolved = FerrumConfigBuilder::new(qwen25_layer_split_runtime_entries(
3461            RuntimeConfigSource::Default,
3462        ))
3463        .with_model_capabilities(ModelCapabilities::qwen25_72b_gptq_int4())
3464        .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(
3465            CompiledKernelFeatures::m3_fast_path_without_fa2(),
3466        ))
3467        .with_workload_profile(WorkloadProfile::qwen25_72b_gptq_int4_2x4090_layer_split())
3468        .resolve()
3469        .unwrap();
3470        let decision = |selection: &str| {
3471            resolved
3472                .decisions
3473                .iter()
3474                .find(|decision| decision.selection == selection)
3475                .unwrap_or_else(|| panic!("missing decision {selection}"))
3476        };
3477
3478        assert_eq!(
3479            resolved.preset.as_deref(),
3480            Some(QWEN25_72B_GPTQ_INT4_2X4090_LAYER_SPLIT_PRESET)
3481        );
3482        assert_eq!(decision("kv_block_count").selected, "1024");
3483        assert_eq!(decision("max_sequences").selected, "16");
3484        assert_eq!(decision("max_batched_tokens").selected, "1536");
3485        assert_eq!(decision("max_model_len").selected, "4096");
3486        assert_eq!(
3487            decision("scheduler_admission_policy").selected,
3488            "prefill_first_until_active:16+prefill_token_budget:elastic"
3489        );
3490        assert_eq!(
3491            decision("scheduler_admission_policy").source,
3492            AutoConfigSource::Default
3493        );
3494
3495        let doc = resolved.effective_config_document();
3496        assert_eq!(doc["selected_pipeline_mode"], "batch");
3497        assert_eq!(doc["selected_microbatch_size"], 16);
3498        assert_eq!(doc["selected_kv_capacity"], 1024);
3499    }
3500
3501    #[test]
3502    fn source_fa2_is_rejected_even_when_legacy_feature_is_compiled() {
3503        let err = m3(
3504            &[("FERRUM_FA2_SOURCE", "1")],
3505            CompiledKernelFeatures::m3_fast_path_with_source_fa2(),
3506        )
3507        .resolve()
3508        .unwrap_err();
3509
3510        match err {
3511            AutoConfigError::InvalidOverride { key, reason } => {
3512                assert_eq!(key, "FERRUM_FA2_SOURCE");
3513                assert!(reason.contains("native operator artifact"));
3514            }
3515            AutoConfigError::UnsupportedCombination { .. } => {
3516                panic!("expected invalid FERRUM_FA2_SOURCE override")
3517            }
3518        }
3519    }
3520
3521    #[test]
3522    fn source_fa2_is_rejected_when_not_compiled() {
3523        expect_invalid_key(&[("FERRUM_FA2_SOURCE", "1")], "FERRUM_FA2_SOURCE");
3524    }
3525
3526    #[test]
3527    fn fa2_native_artifact_typed_config_is_recorded_without_selecting_runtime_path() {
3528        let resolved = m3(
3529            &[
3530                (
3531                    FA2_NATIVE_MANIFEST_KEY,
3532                    "/tmp/native/fa2/native_operator_manifest.json",
3533                ),
3534                (
3535                    FA2_NATIVE_ARTIFACT_KEY,
3536                    "/tmp/native/fa2/libferrum_native_fa2.a",
3537                ),
3538                (
3539                    FA2_NATIVE_SOURCE_SHA256_KEY,
3540                    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
3541                ),
3542                (
3543                    FA2_NATIVE_INPUTS_SHA256_KEY,
3544                    "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
3545                ),
3546            ],
3547            CompiledKernelFeatures::m3_fast_path_with_native_fa2_artifact(),
3548        )
3549        .resolve()
3550        .unwrap();
3551
3552        let decisions: BTreeMap<_, _> = resolved
3553            .decisions
3554            .iter()
3555            .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
3556            .collect();
3557        assert_eq!(decisions["fa2_native_operator_artifact"], "configured");
3558        assert_eq!(
3559            decisions["fa2_native_operator_runtime_selection"],
3560            "not_selected"
3561        );
3562        assert_ne!(decisions["attention_prefill_mixed_backend"], "fa2_native");
3563        assert!(resolved
3564            .runtime_config
3565            .entries
3566            .iter()
3567            .any(|entry| entry.key == FA2_NATIVE_MANIFEST_KEY
3568                && entry.source == RuntimeConfigSource::Env));
3569    }
3570
3571    #[test]
3572    fn fa2_native_runtime_selection_is_explicit_when_not_configured() {
3573        let resolved = m3(&[], CompiledKernelFeatures::m3_fast_path_without_fa2())
3574            .resolve()
3575            .unwrap();
3576        let decision = resolved
3577            .decisions
3578            .iter()
3579            .find(|decision| decision.selection == "fa2_native_operator_runtime_selection")
3580            .unwrap();
3581        assert_eq!(decision.selected, "not_configured");
3582        assert!(decision.rejected.iter().any(|candidate| {
3583            candidate.value == "selected"
3584                && candidate
3585                    .reason
3586                    .contains("artifact-selected actual model smoke")
3587        }));
3588    }
3589
3590    #[test]
3591    fn fa2_native_artifact_requires_manifest_artifact_pair_and_valid_pins() {
3592        expect_invalid_key(
3593            &[(
3594                FA2_NATIVE_MANIFEST_KEY,
3595                "/tmp/native/fa2/native_operator_manifest.json",
3596            )],
3597            FA2_NATIVE_ARTIFACT_KEY,
3598        );
3599        expect_invalid_key(
3600            &[(
3601                FA2_NATIVE_ARTIFACT_KEY,
3602                "/tmp/native/fa2/libferrum_native_fa2.a",
3603            )],
3604            FA2_NATIVE_MANIFEST_KEY,
3605        );
3606        expect_invalid_key(
3607            &[
3608                (
3609                    FA2_NATIVE_MANIFEST_KEY,
3610                    "/tmp/native/fa2/native_operator_manifest.json",
3611                ),
3612                (
3613                    FA2_NATIVE_ARTIFACT_KEY,
3614                    "/tmp/native/fa2/libferrum_native_fa2.a",
3615                ),
3616                (FA2_NATIVE_SOURCE_SHA256_KEY, "not-a-sha"),
3617            ],
3618            FA2_NATIVE_SOURCE_SHA256_KEY,
3619        );
3620        expect_invalid_key(
3621            &[(
3622                FA2_NATIVE_INPUTS_SHA256_KEY,
3623                "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
3624            )],
3625            FA2_NATIVE_INPUTS_SHA256_KEY,
3626        );
3627    }
3628
3629    #[test]
3630    fn fa2_native_artifact_requires_linked_binary_capability() {
3631        expect_invalid_key(
3632            &[
3633                (
3634                    FA2_NATIVE_MANIFEST_KEY,
3635                    "/tmp/native/fa2/native_operator_manifest.json",
3636                ),
3637                (
3638                    FA2_NATIVE_ARTIFACT_KEY,
3639                    "/tmp/native/fa2/libferrum_native_fa2.a",
3640                ),
3641            ],
3642            FA2_NATIVE_MANIFEST_KEY,
3643        );
3644    }
3645
3646    #[test]
3647    fn fa2_native_artifact_requires_linked_binary_metadata() {
3648        let mut features = CompiledKernelFeatures::m3_fast_path_without_fa2();
3649        features.fa2_native_operator_artifact = true;
3650
3651        let err = m3(
3652            &[
3653                (
3654                    FA2_NATIVE_MANIFEST_KEY,
3655                    "/tmp/native/fa2/native_operator_manifest.json",
3656                ),
3657                (
3658                    FA2_NATIVE_ARTIFACT_KEY,
3659                    "/tmp/native/fa2/libferrum_native_fa2.a",
3660                ),
3661            ],
3662            features,
3663        )
3664        .resolve()
3665        .unwrap_err();
3666        match err {
3667            AutoConfigError::InvalidOverride { key, reason } => {
3668                assert_eq!(key, FA2_NATIVE_MANIFEST_KEY);
3669                assert!(reason.contains("missing build metadata"));
3670            }
3671            other => panic!("expected invalid override, got {other:?}"),
3672        }
3673    }
3674
3675    #[test]
3676    fn fa2_native_artifact_must_match_linked_binary_metadata() {
3677        let features = CompiledKernelFeatures::m3_fast_path_with_native_fa2_artifact();
3678        expect_invalid_key_with_features(
3679            &[
3680                (
3681                    FA2_NATIVE_MANIFEST_KEY,
3682                    "/tmp/native/fa2/other_manifest.json",
3683                ),
3684                (
3685                    FA2_NATIVE_ARTIFACT_KEY,
3686                    "/tmp/native/fa2/libferrum_native_fa2.a",
3687                ),
3688            ],
3689            FA2_NATIVE_MANIFEST_KEY,
3690            features.clone(),
3691        );
3692        expect_invalid_key_with_features(
3693            &[
3694                (
3695                    FA2_NATIVE_MANIFEST_KEY,
3696                    "/tmp/native/fa2/native_operator_manifest.json",
3697                ),
3698                (
3699                    FA2_NATIVE_ARTIFACT_KEY,
3700                    "/tmp/native/fa2/libferrum_native_other.a",
3701                ),
3702            ],
3703            FA2_NATIVE_ARTIFACT_KEY,
3704            features.clone(),
3705        );
3706        expect_invalid_key_with_features(
3707            &[
3708                (
3709                    FA2_NATIVE_MANIFEST_KEY,
3710                    "/tmp/native/fa2/native_operator_manifest.json",
3711                ),
3712                (
3713                    FA2_NATIVE_ARTIFACT_KEY,
3714                    "/tmp/native/fa2/libferrum_native_fa2.a",
3715                ),
3716                (
3717                    FA2_NATIVE_SOURCE_SHA256_KEY,
3718                    "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
3719                ),
3720            ],
3721            FA2_NATIVE_SOURCE_SHA256_KEY,
3722            features.clone(),
3723        );
3724        expect_invalid_key_with_features(
3725            &[
3726                (
3727                    FA2_NATIVE_MANIFEST_KEY,
3728                    "/tmp/native/fa2/native_operator_manifest.json",
3729                ),
3730                (
3731                    FA2_NATIVE_ARTIFACT_KEY,
3732                    "/tmp/native/fa2/libferrum_native_fa2.a",
3733                ),
3734                (
3735                    FA2_NATIVE_INPUTS_SHA256_KEY,
3736                    "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
3737                ),
3738            ],
3739            FA2_NATIVE_INPUTS_SHA256_KEY,
3740            features,
3741        );
3742    }
3743
3744    #[test]
3745    fn fa2_native_artifact_requires_cuda_backend() {
3746        let hardware =
3747            cpu_hardware_with_features(CompiledKernelFeatures::m3_fast_path_without_fa2());
3748        expect_invalid_key_with_hardware(
3749            &[
3750                (
3751                    FA2_NATIVE_MANIFEST_KEY,
3752                    "/tmp/native/fa2/native_operator_manifest.json",
3753                ),
3754                (
3755                    FA2_NATIVE_ARTIFACT_KEY,
3756                    "/tmp/native/fa2/libferrum_native_fa2.a",
3757                ),
3758            ],
3759            FA2_NATIVE_MANIFEST_KEY,
3760            hardware,
3761        );
3762    }
3763
3764    #[test]
3765    fn hardware_capabilities_keep_m3_preset_on_compatible_backend_paths() {
3766        let resolved = m3_with_hardware(
3767            &[],
3768            cpu_hardware_with_features(CompiledKernelFeatures::m3_fast_path_with_source_fa2()),
3769        )
3770        .resolve()
3771        .unwrap();
3772        let decisions: BTreeMap<_, _> = resolved
3773            .decisions
3774            .iter()
3775            .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
3776            .collect();
3777
3778        assert_eq!(
3779            decisions["attention_prefill_mixed_backend"],
3780            "legacy_paged_varlen"
3781        );
3782        assert_eq!(decisions["attention_decode_backend"], "legacy_paged_decode");
3783        assert_eq!(decisions["moe_implementation"], "legacy_moe");
3784        assert_eq!(decisions["moe_graph_policy"], "graph_disabled");
3785        assert_eq!(decisions["sampling_readback_path"], "logits_readback");
3786    }
3787
3788    #[test]
3789    fn effective_config_document_records_cuda_gpu_device_selection() {
3790        let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[
3791            (
3792                "FERRUM_REQUESTED_GPU_DEVICES",
3793                "0,1",
3794                RuntimeConfigSource::Cli,
3795            ),
3796            (
3797                "FERRUM_SELECTED_GPU_DEVICES",
3798                "0,1",
3799                RuntimeConfigSource::Cli,
3800            ),
3801            ("FERRUM_CUDA_DEVICE_COUNT", "2", RuntimeConfigSource::Cli),
3802            (
3803                "FERRUM_SELECTED_DISTRIBUTED_STRATEGY",
3804                "layer_split",
3805                RuntimeConfigSource::Cli,
3806            ),
3807            (
3808                "FERRUM_SELECTED_LAYER_SPLIT_PLAN",
3809                "stage0:cuda:0:layers=0-39;stage1:cuda:1:layers=40-79",
3810                RuntimeConfigSource::Cli,
3811            ),
3812            (
3813                "FERRUM_SELECTED_LAYER_SPLIT_STAGES",
3814                r#"[{"stage":0,"device":0,"layer_start":0,"layer_end":39},{"stage":1,"device":1,"layer_start":40,"layer_end":79}]"#,
3815                RuntimeConfigSource::Cli,
3816            ),
3817            ("FERRUM_KV_CAPACITY", "512", RuntimeConfigSource::Cli),
3818        ]))
3819        .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(
3820            CompiledKernelFeatures::m3_fast_path_without_fa2(),
3821        ))
3822        .resolve()
3823        .unwrap();
3824
3825        let doc = resolved.effective_config_document();
3826        assert_eq!(doc["backend"], "cuda");
3827        assert_eq!(doc["requested_gpu_devices"], serde_json::json!([0, 1]));
3828        assert_eq!(doc["selected_gpu_devices"], serde_json::json!([0, 1]));
3829        assert_eq!(doc["cuda_device_count"], 2);
3830        assert_eq!(doc["selected_distributed_strategy"], "layer_split");
3831        assert_eq!(
3832            doc["selected_layer_split_plan"],
3833            "stage0:cuda:0:layers=0-39;stage1:cuda:1:layers=40-79"
3834        );
3835        assert_eq!(
3836            doc["selected_layer_split_stages"],
3837            serde_json::json!([
3838                {"stage": 0, "device": 0, "layer_start": 0, "layer_end": 39},
3839                {"stage": 1, "device": 1, "layer_start": 40, "layer_end": 79}
3840            ])
3841        );
3842        assert_eq!(doc["selected_weight_placement"], "layer_split");
3843        assert_eq!(doc["selected_pipeline_mode"], "overlapped");
3844        assert_eq!(doc["selected_stage_bridge"], "host");
3845        assert_eq!(
3846            doc["selected_microbatch_size"],
3847            serde_json::json!(doc["selected_max_sequences"].as_u64().unwrap().div_ceil(2))
3848        );
3849        assert_eq!(
3850            doc["selected_admission_limit"],
3851            doc["selected_max_sequences"]
3852        );
3853        assert_eq!(doc["selected_kv_capacity"], 512);
3854    }
3855
3856    #[test]
3857    fn effective_config_document_honors_explicit_layer_split_batch_mode() {
3858        let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[
3859            (
3860                "FERRUM_REQUESTED_GPU_DEVICES",
3861                "0,1",
3862                RuntimeConfigSource::Cli,
3863            ),
3864            (
3865                "FERRUM_SELECTED_GPU_DEVICES",
3866                "0,1",
3867                RuntimeConfigSource::Cli,
3868            ),
3869            (
3870                "FERRUM_SELECTED_DISTRIBUTED_STRATEGY",
3871                "layer_split",
3872                RuntimeConfigSource::Cli,
3873            ),
3874            (
3875                "FERRUM_SELECTED_LAYER_SPLIT_PLAN",
3876                "stage0:cuda:0:layers=0-39;stage1:cuda:1:layers=40-79",
3877                RuntimeConfigSource::Cli,
3878            ),
3879            (
3880                "FERRUM_LAYER_SPLIT_PIPELINE_MODE",
3881                "batch",
3882                RuntimeConfigSource::Cli,
3883            ),
3884            ("FERRUM_PAGED_MAX_SEQS", "16", RuntimeConfigSource::Cli),
3885        ]))
3886        .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(
3887            CompiledKernelFeatures::m3_fast_path_without_fa2(),
3888        ))
3889        .resolve()
3890        .unwrap();
3891
3892        let doc = resolved.effective_config_document();
3893        assert_eq!(doc["selected_pipeline_mode"], "batch");
3894        assert_eq!(doc["selected_microbatch_size"], 16);
3895    }
3896
3897    #[test]
3898    fn invalid_layer_split_pipeline_mode_is_rejected() {
3899        expect_invalid_key_with_hardware(
3900            &[("FERRUM_LAYER_SPLIT_PIPELINE_MODE", "serial")],
3901            "FERRUM_LAYER_SPLIT_PIPELINE_MODE",
3902            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2()),
3903        );
3904    }
3905
3906    #[test]
3907    fn hardware_incompatible_attention_and_sampling_overrides_are_rejected() {
3908        let cpu =
3909            cpu_hardware_with_features(CompiledKernelFeatures::m3_fast_path_with_source_fa2());
3910        expect_invalid_key_with_hardware(
3911            &[("FERRUM_USE_VLLM_PAGED_ATTN", "1")],
3912            "FERRUM_USE_VLLM_PAGED_ATTN",
3913            cpu.clone(),
3914        );
3915        expect_invalid_key_with_hardware(
3916            &[("FERRUM_VLLM_MOE", "1")],
3917            "FERRUM_VLLM_MOE",
3918            cpu.clone(),
3919        );
3920        expect_invalid_key_with_hardware(
3921            &[("FERRUM_GREEDY_ARGMAX", "1")],
3922            "FERRUM_GREEDY_ARGMAX",
3923            cpu.clone(),
3924        );
3925        expect_invalid_key_with_hardware(&[("FERRUM_FA2_SOURCE", "1")], "FERRUM_FA2_SOURCE", cpu);
3926
3927        let mut old_cuda = HardwareCapabilities::rtx4090_cuda(
3928            CompiledKernelFeatures::m3_fast_path_with_source_fa2(),
3929        );
3930        old_cuda.compute_capability = Some("7.5".to_string());
3931        expect_invalid_key_with_hardware(
3932            &[("FERRUM_FA2_SOURCE", "1")],
3933            "FERRUM_FA2_SOURCE",
3934            old_cuda,
3935        );
3936    }
3937
3938    #[test]
3939    fn hardware_capacity_sizes_default_sequence_budget_without_overriding_user_values() {
3940        let mut small_gpu =
3941            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3942        small_gpu.sm_count = Some(16);
3943        small_gpu.vram_bytes = Some(24 * 1024 * 1024 * 1024);
3944
3945        let resolved = m3_with_hardware(&[], small_gpu.clone()).resolve().unwrap();
3946        let decision = |selection: &str| {
3947            resolved
3948                .decisions
3949                .iter()
3950                .find(|decision| decision.selection == selection)
3951                .unwrap()
3952        };
3953        let max_sequences = decision("max_sequences");
3954        assert_eq!(max_sequences.selected, "4");
3955        assert_eq!(max_sequences.source, AutoConfigSource::HardwareCapability);
3956        let max_batched_tokens = decision("max_batched_tokens");
3957        assert_eq!(max_batched_tokens.selected, "256");
3958        assert_eq!(
3959            max_batched_tokens.source,
3960            AutoConfigSource::HardwareCapability
3961        );
3962
3963        let resolved = m3_with_hardware(&[("FERRUM_PAGED_MAX_SEQS", "16")], small_gpu)
3964            .resolve()
3965            .unwrap();
3966        let max_sequences = resolved
3967            .decisions
3968            .iter()
3969            .find(|decision| decision.selection == "max_sequences")
3970            .unwrap();
3971        assert_eq!(max_sequences.selected, "16");
3972        assert_eq!(max_sequences.source, AutoConfigSource::Env);
3973        assert_eq!(
3974            max_sequences.source_key.as_deref(),
3975            Some("FERRUM_PAGED_MAX_SEQS")
3976        );
3977    }
3978
3979    #[test]
3980    fn vram_capacity_caps_m3_default_sequence_budget() {
3981        let mut low_vram_gpu =
3982            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3983        low_vram_gpu.sm_count = Some(128);
3984        low_vram_gpu.vram_bytes = Some(7 * 1024 * 1024 * 1024);
3985
3986        let resolved = m3_with_hardware(&[], low_vram_gpu).resolve().unwrap();
3987        let max_sequences = resolved
3988            .decisions
3989            .iter()
3990            .find(|decision| decision.selection == "max_sequences")
3991            .unwrap();
3992        assert_eq!(max_sequences.selected, "4");
3993        assert_eq!(max_sequences.source, AutoConfigSource::HardwareCapability);
3994    }
3995
3996    #[test]
3997    fn memory_budget_keeps_rtx4090_m3_kv_blocks_but_caps_constrained_vram() {
3998        let resolved = m3(&[], CompiledKernelFeatures::m3_fast_path_without_fa2())
3999            .resolve()
4000            .unwrap();
4001        let decision = |selection: &str| {
4002            resolved
4003                .decisions
4004                .iter()
4005                .find(|decision| decision.selection == selection)
4006                .unwrap()
4007        };
4008        assert_eq!(decision("kv_block_count").selected, "2048");
4009        assert_eq!(
4010            decision("kv_block_count").source,
4011            AutoConfigSource::WorkloadPreset
4012        );
4013
4014        let mut constrained =
4015            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4016        constrained.vram_bytes = Some(20 * 1024 * 1024 * 1024);
4017        let resolved = m3_with_hardware(&[], constrained).resolve().unwrap();
4018        let decision = |selection: &str| {
4019            resolved
4020                .decisions
4021                .iter()
4022                .find(|decision| decision.selection == selection)
4023                .unwrap()
4024        };
4025        assert_eq!(decision("kv_block_count").selected, "2");
4026        assert_eq!(
4027            decision("kv_block_count").source,
4028            AutoConfigSource::HardwareCapability
4029        );
4030        assert_eq!(decision("max_batched_tokens").selected, "32");
4031        assert_eq!(
4032            decision("max_batched_tokens").source,
4033            AutoConfigSource::HardwareCapability
4034        );
4035    }
4036
4037    #[test]
4038    fn compute_capability_parser_accepts_major_minor_and_major_only() {
4039        assert_eq!(parse_compute_capability("8.9"), Some((8, 9)));
4040        assert_eq!(parse_compute_capability("9"), Some((9, 0)));
4041        assert_eq!(parse_compute_capability("N/A"), None);
4042    }
4043
4044    #[test]
4045    fn vram_capacity_tiers_are_monotonic() {
4046        assert_eq!(vram_default_max_sequences(24 * 1024 * 1024 * 1024), 32);
4047        assert_eq!(vram_default_max_sequences(16 * 1024 * 1024 * 1024), 16);
4048        assert_eq!(vram_default_max_sequences(8 * 1024 * 1024 * 1024), 8);
4049        assert_eq!(vram_default_max_sequences(6 * 1024 * 1024 * 1024), 4);
4050    }
4051
4052    #[test]
4053    fn accelerator_serving_default_uses_hardware_concurrency_budget() {
4054        let hardware =
4055            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4056        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4057        assert_eq!(workload.target_concurrency, 32);
4058
4059        let resolved = FerrumConfigBuilder::new(snapshot(&[]))
4060            .with_model_capabilities(ModelCapabilities::unknown())
4061            .with_hardware_capabilities(hardware)
4062            .with_workload_profile(workload)
4063            .resolve()
4064            .unwrap();
4065        let max_sequences = resolved
4066            .decisions
4067            .iter()
4068            .find(|decision| decision.selection == "max_sequences")
4069            .unwrap();
4070        assert_eq!(max_sequences.selected, "32");
4071        let scheduler = resolved
4072            .decisions
4073            .iter()
4074            .find(|decision| decision.selection == "scheduler_admission_policy")
4075            .unwrap();
4076        assert_eq!(
4077            scheduler.selected,
4078            "prefill_first_until_active:32+prefill_token_budget:elastic"
4079        );
4080        assert_eq!(scheduler.source, AutoConfigSource::Default);
4081        let scheduler_entry = resolved
4082            .runtime_config
4083            .entries
4084            .iter()
4085            .find(|entry| entry.key == "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE")
4086            .unwrap_or_else(|| panic!("missing FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE entry"));
4087        assert_eq!(scheduler_entry.effective_value, "32");
4088        assert_eq!(scheduler_entry.source, RuntimeConfigSource::Default);
4089        assert!(resolved
4090            .runtime_config
4091            .entries
4092            .iter()
4093            .all(|entry| entry.key != "FERRUM_SCHED_PREFILL_STEP_CHUNK"));
4094    }
4095
4096    #[test]
4097    fn accelerator_serving_default_enables_greedy_argmax_when_compiled() {
4098        let hardware =
4099            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4100        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4101        let resolved = FerrumConfigBuilder::new(snapshot(&[]))
4102            .with_model_capabilities(ModelCapabilities::unknown())
4103            .with_hardware_capabilities(hardware)
4104            .with_workload_profile(workload)
4105            .resolve()
4106            .unwrap();
4107        let sampling = resolved
4108            .decisions
4109            .iter()
4110            .find(|decision| decision.selection == "sampling_readback_path")
4111            .unwrap();
4112        assert_eq!(sampling.selected, "gpu_greedy_argmax");
4113        assert_eq!(sampling.source, AutoConfigSource::HardwareCapability);
4114        let greedy_entry = resolved
4115            .runtime_config
4116            .entries
4117            .iter()
4118            .find(|entry| entry.key == "FERRUM_GREEDY_ARGMAX")
4119            .unwrap_or_else(|| panic!("missing FERRUM_GREEDY_ARGMAX entry"));
4120        assert_eq!(greedy_entry.effective_value, "1");
4121    }
4122
4123    #[test]
4124    fn explicit_greedy_argmax_disable_keeps_logits_readback() {
4125        let hardware =
4126            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4127        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4128        let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[(
4129            "FERRUM_GREEDY_ARGMAX",
4130            "0",
4131            RuntimeConfigSource::Cli,
4132        )]))
4133        .with_model_capabilities(ModelCapabilities::unknown())
4134        .with_hardware_capabilities(hardware)
4135        .with_workload_profile(workload)
4136        .resolve()
4137        .unwrap();
4138        let sampling = resolved
4139            .decisions
4140            .iter()
4141            .find(|decision| decision.selection == "sampling_readback_path")
4142            .unwrap();
4143        assert_eq!(sampling.selected, "logits_readback");
4144        assert_eq!(sampling.source, AutoConfigSource::Cli);
4145        let greedy_entry = resolved
4146            .runtime_config
4147            .entries
4148            .iter()
4149            .find(|entry| entry.key == "FERRUM_GREEDY_ARGMAX")
4150            .unwrap_or_else(|| panic!("missing FERRUM_GREEDY_ARGMAX entry"));
4151        assert_eq!(greedy_entry.effective_value, "0");
4152    }
4153
4154    #[test]
4155    fn cpu_serving_default_keeps_single_sequence_budget() {
4156        let hardware = HardwareCapabilities {
4157            backend: "cpu".to_string(),
4158            supported_dtypes: vec!["fp32".to_string()],
4159            ..HardwareCapabilities::unknown()
4160        };
4161        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4162        assert_eq!(workload.target_concurrency, 1);
4163        let resolved = FerrumConfigBuilder::new(snapshot(&[]))
4164            .with_model_capabilities(ModelCapabilities::unknown())
4165            .with_hardware_capabilities(hardware)
4166            .with_workload_profile(workload)
4167            .resolve()
4168            .unwrap();
4169        let scheduler = resolved
4170            .decisions
4171            .iter()
4172            .find(|decision| decision.selection == "scheduler_admission_policy")
4173            .unwrap();
4174        assert_eq!(scheduler.selected, "prompt_token_estimate");
4175        assert!(resolved
4176            .runtime_config
4177            .entries
4178            .iter()
4179            .all(|entry| entry.key != "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE"));
4180        assert!(resolved
4181            .runtime_config
4182            .entries
4183            .iter()
4184            .all(|entry| entry.key != "FERRUM_SCHED_PREFILL_STEP_CHUNK"));
4185    }
4186
4187    #[test]
4188    fn validates_invalid_override_matrix() {
4189        expect_invalid_key(
4190            &[("FERRUM_USE_VLLM_PAGED_ATTN", "maybe")],
4191            "FERRUM_USE_VLLM_PAGED_ATTN",
4192        );
4193        expect_invalid_key(&[("FERRUM_PREFIX_CACHE", "maybe")], "FERRUM_PREFIX_CACHE");
4194        expect_invalid_key(
4195            &[
4196                ("FERRUM_FA_LAYOUT_VARLEN", "1"),
4197                ("FERRUM_USE_VLLM_PAGED_ATTN", "0"),
4198            ],
4199            "FERRUM_FA_LAYOUT_VARLEN",
4200        );
4201        expect_invalid_key(&[("FERRUM_FA2_DIRECT_FFI", "1")], "FERRUM_FA2_DIRECT_FFI");
4202        expect_invalid_key_with_features(
4203            &[("FERRUM_VLLM_MOE", "1")],
4204            "FERRUM_VLLM_MOE",
4205            CompiledKernelFeatures::default(),
4206        );
4207        expect_invalid_key(
4208            &[("FERRUM_MOE_DEVICE_ROUTE", "1"), ("FERRUM_VLLM_MOE", "0")],
4209            "FERRUM_MOE_DEVICE_ROUTE",
4210        );
4211        expect_invalid_key(
4212            &[("FERRUM_VLLM_MOE_PAIR_IDS", "1"), ("FERRUM_VLLM_MOE", "0")],
4213            "FERRUM_VLLM_MOE_PAIR_IDS",
4214        );
4215        expect_invalid_key(
4216            &[("FERRUM_MOE_GRAPH", "1"), ("FERRUM_VLLM_MOE", "0")],
4217            "FERRUM_MOE_GRAPH",
4218        );
4219        expect_invalid_key(&[("FERRUM_KV_MAX_BLOCKS", "0")], "FERRUM_KV_MAX_BLOCKS");
4220        expect_invalid_key(&[("FERRUM_PAGED_MAX_SEQS", "0")], "FERRUM_PAGED_MAX_SEQS");
4221        expect_invalid_key(
4222            &[
4223                ("FERRUM_PAGED_MAX_SEQS", "32"),
4224                ("FERRUM_MAX_BATCHED_TOKENS", "16"),
4225            ],
4226            "FERRUM_MAX_BATCHED_TOKENS",
4227        );
4228        expect_invalid_key(
4229            &[
4230                ("FERRUM_KV_MAX_BLOCKS", "16"),
4231                ("FERRUM_MAX_BATCHED_TOKENS", "512"),
4232            ],
4233            "FERRUM_MAX_BATCHED_TOKENS",
4234        );
4235        expect_invalid_key(&[("FERRUM_MAX_MODEL_LEN", "0")], "FERRUM_MAX_MODEL_LEN");
4236        expect_invalid_key(&[("FERRUM_MAX_MODEL_LEN", "50000")], "FERRUM_MAX_MODEL_LEN");
4237        expect_invalid_key(
4238            &[
4239                ("FERRUM_KV_MAX_BLOCKS", "16"),
4240                ("FERRUM_MAX_MODEL_LEN", "1024"),
4241            ],
4242            "FERRUM_KV_MAX_BLOCKS",
4243        );
4244        expect_invalid_key(&[("FERRUM_DTYPE", "bf16")], "FERRUM_DTYPE");
4245        expect_invalid_key(&[("FERRUM_KV_DTYPE", "fp8")], "FERRUM_KV_DTYPE");
4246        expect_invalid_key(
4247            &[
4248                ("FERRUM_VLLM_PAGED_ATTN_V1_SHORT", "1"),
4249                ("FERRUM_USE_VLLM_PAGED_ATTN", "0"),
4250            ],
4251            "FERRUM_VLLM_PAGED_ATTN_V1_SHORT",
4252        );
4253    }
4254
4255    #[test]
4256    fn requested_max_model_len_is_optional_and_reflected_when_valid() {
4257        let default_resolved = m3(&[], CompiledKernelFeatures::m3_fast_path_without_fa2())
4258            .resolve()
4259            .unwrap();
4260        assert!(!default_resolved
4261            .decisions
4262            .iter()
4263            .any(|decision| decision.selection == "max_model_len"));
4264
4265        let resolved = m3(
4266            &[
4267                ("FERRUM_KV_MAX_BLOCKS", "64"),
4268                ("FERRUM_MAX_MODEL_LEN", "1024"),
4269            ],
4270            CompiledKernelFeatures::m3_fast_path_without_fa2(),
4271        )
4272        .resolve()
4273        .unwrap();
4274        let max_model_len = resolved
4275            .decisions
4276            .iter()
4277            .find(|decision| decision.selection == "max_model_len")
4278            .unwrap();
4279        assert_eq!(max_model_len.selected, "1024");
4280        assert_eq!(
4281            max_model_len.source_key.as_deref(),
4282            Some("FERRUM_MAX_MODEL_LEN")
4283        );
4284    }
4285
4286    #[test]
4287    fn graph_enabled_with_graph_unsafe_moe_is_rejected() {
4288        let mut model = ModelCapabilities::qwen3_30b_a3b_gptq_int4();
4289        model.graph_safe_moe = false;
4290        let err = FerrumConfigBuilder::new(snapshot(&[("FERRUM_MOE_GRAPH", "1")]))
4291            .with_model_capabilities(model)
4292            .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(
4293                CompiledKernelFeatures::m3_fast_path_without_fa2(),
4294            ))
4295            .with_workload_profile(WorkloadProfile::m3_qwen3_30b_a3b_int4())
4296            .resolve()
4297            .expect_err("graph unsafe MoE must fail");
4298        assert!(matches!(
4299            err,
4300            AutoConfigError::UnsupportedCombination {
4301                selection,
4302                ..
4303            } if selection == "moe_graph_policy"
4304        ));
4305    }
4306
4307    #[test]
4308    fn batched_graph_override_materializes_decode_graph_policy() {
4309        let hardware =
4310            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4311        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4312        let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[(
4313            "FERRUM_BATCHED_GRAPH",
4314            "1",
4315            RuntimeConfigSource::Cli,
4316        )]))
4317        .with_model_capabilities(ModelCapabilities::unknown())
4318        .with_hardware_capabilities(hardware)
4319        .with_workload_profile(workload)
4320        .resolve()
4321        .unwrap();
4322        let decisions: BTreeMap<_, _> = resolved
4323            .decisions
4324            .iter()
4325            .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
4326            .collect();
4327        assert_eq!(
4328            decisions["decode_graph_policy"],
4329            "legacy_batched_decode_graph"
4330        );
4331        let entry = resolved
4332            .runtime_config
4333            .entries
4334            .iter()
4335            .find(|entry| entry.key == "FERRUM_BATCHED_GRAPH")
4336            .expect("batched graph entry");
4337        assert_eq!(entry.effective_value, "1");
4338        assert_eq!(
4339            resolved.effective_config_document()["selected_graph_mode"],
4340            "legacy_batched_decode_graph"
4341        );
4342    }
4343
4344    #[test]
4345    fn reusable_execution_defaults_on_without_enabling_legacy_graphs() {
4346        let hardware =
4347            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4348        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4349        let resolved = FerrumConfigBuilder::new(RuntimeConfigSnapshot::default())
4350            .with_model_capabilities(ModelCapabilities::unknown())
4351            .with_hardware_capabilities(hardware)
4352            .with_workload_profile(workload)
4353            .resolve()
4354            .unwrap();
4355        let entry = |key: &str| {
4356            resolved
4357                .runtime_config
4358                .entries
4359                .iter()
4360                .find(|entry| entry.key == key)
4361                .unwrap_or_else(|| panic!("missing {key} entry"))
4362        };
4363        let decisions: BTreeMap<_, _> = resolved
4364            .decisions
4365            .iter()
4366            .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
4367            .collect();
4368
4369        assert_eq!(entry("FERRUM_BATCHED_GRAPH").effective_value, "0");
4370        assert_eq!(entry("FERRUM_REUSABLE_EXECUTION").effective_value, "1");
4371        assert_eq!(decisions["decode_graph_policy"], "graph_disabled");
4372        assert_eq!(
4373            decisions["reusable_execution_policy"],
4374            "enabled_when_runtime_capable"
4375        );
4376    }
4377
4378    #[test]
4379    fn reusable_execution_explicit_disable_is_preserved() {
4380        let hardware =
4381            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4382        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4383        let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[(
4384            "FERRUM_REUSABLE_EXECUTION",
4385            "0",
4386            RuntimeConfigSource::Cli,
4387        )]))
4388        .with_model_capabilities(ModelCapabilities::unknown())
4389        .with_hardware_capabilities(hardware)
4390        .with_workload_profile(workload)
4391        .resolve()
4392        .unwrap();
4393        let entry = resolved
4394            .runtime_config
4395            .entries
4396            .iter()
4397            .find(|entry| entry.key == "FERRUM_REUSABLE_EXECUTION")
4398            .expect("reusable execution entry");
4399        let decision = resolved
4400            .decisions
4401            .iter()
4402            .find(|decision| decision.selection == "reusable_execution_policy")
4403            .expect("reusable execution decision");
4404
4405        assert_eq!(entry.effective_value, "0");
4406        assert_eq!(entry.source, RuntimeConfigSource::Cli);
4407        assert_eq!(decision.selected, "disabled");
4408    }
4409
4410    #[test]
4411    fn unified_graph_override_materializes_decode_graph_policy() {
4412        let hardware =
4413            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4414        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4415        let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[(
4416            "FERRUM_UNIFIED_GRAPH",
4417            "1",
4418            RuntimeConfigSource::Cli,
4419        )]))
4420        .with_model_capabilities(ModelCapabilities::unknown())
4421        .with_hardware_capabilities(hardware)
4422        .with_workload_profile(workload)
4423        .resolve()
4424        .unwrap();
4425        let decisions: BTreeMap<_, _> = resolved
4426            .decisions
4427            .iter()
4428            .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
4429            .collect();
4430        assert_eq!(decisions["decode_graph_policy"], "unified_decode_graph");
4431        let entry = resolved
4432            .runtime_config
4433            .entries
4434            .iter()
4435            .find(|entry| entry.key == "FERRUM_UNIFIED_GRAPH")
4436            .expect("unified graph entry");
4437        assert_eq!(entry.effective_value, "1");
4438        assert_eq!(
4439            resolved.effective_config_document()["selected_graph_mode"],
4440            "unified_decode_graph"
4441        );
4442    }
4443
4444    #[test]
4445    fn unified_graph_layers_only_override_materializes_decode_graph_policy() {
4446        let hardware =
4447            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4448        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4449        let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[
4450            ("FERRUM_UNIFIED_GRAPH", "1", RuntimeConfigSource::Cli),
4451            (
4452                "FERRUM_UNIFIED_GRAPH_LAYERS_ONLY",
4453                "1",
4454                RuntimeConfigSource::Cli,
4455            ),
4456        ]))
4457        .with_model_capabilities(gemma3_gptq_model())
4458        .with_hardware_capabilities(hardware)
4459        .with_workload_profile(workload)
4460        .resolve()
4461        .unwrap();
4462        let decisions: BTreeMap<_, _> = resolved
4463            .decisions
4464            .iter()
4465            .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
4466            .collect();
4467        assert_eq!(
4468            decisions["decode_graph_policy"],
4469            "unified_decode_graph_layers_only"
4470        );
4471        let entry = resolved
4472            .runtime_config
4473            .entries
4474            .iter()
4475            .find(|entry| entry.key == "FERRUM_UNIFIED_GRAPH_LAYERS_ONLY")
4476            .expect("unified graph layers-only entry");
4477        assert_eq!(entry.effective_value, "1");
4478        assert_eq!(
4479            resolved.effective_config_document()["selected_graph_mode"],
4480            "unified_decode_graph_layers_only"
4481        );
4482    }
4483
4484    #[test]
4485    fn unified_graph_lm_head_eager_override_materializes_decode_graph_policy() {
4486        let hardware =
4487            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4488        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4489        let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[
4490            ("FERRUM_UNIFIED_GRAPH", "1", RuntimeConfigSource::Cli),
4491            (
4492                "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER",
4493                "1",
4494                RuntimeConfigSource::Cli,
4495            ),
4496        ]))
4497        .with_model_capabilities(gemma3_gptq_model())
4498        .with_hardware_capabilities(hardware)
4499        .with_workload_profile(workload)
4500        .resolve()
4501        .unwrap();
4502        let decisions: BTreeMap<_, _> = resolved
4503            .decisions
4504            .iter()
4505            .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
4506            .collect();
4507        assert_eq!(
4508            decisions["decode_graph_policy"],
4509            "unified_decode_graph_lm_head_eager"
4510        );
4511        let entry = resolved
4512            .runtime_config
4513            .entries
4514            .iter()
4515            .find(|entry| entry.key == "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER")
4516            .expect("unified graph lm-head-eager entry");
4517        assert_eq!(entry.effective_value, "1");
4518        assert_eq!(
4519            resolved.effective_config_document()["selected_graph_mode"],
4520            "unified_decode_graph_lm_head_eager"
4521        );
4522    }
4523
4524    #[test]
4525    fn unified_graph_layers_only_requires_unified_graph() {
4526        let hardware =
4527            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4528        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4529        let err = FerrumConfigBuilder::new(snapshot(&[("FERRUM_UNIFIED_GRAPH_LAYERS_ONLY", "1")]))
4530            .with_model_capabilities(ModelCapabilities::unknown())
4531            .with_hardware_capabilities(hardware)
4532            .with_workload_profile(workload)
4533            .resolve()
4534            .expect_err("layers-only graph scope should require unified graph");
4535        assert!(matches!(
4536            err,
4537            AutoConfigError::InvalidOverride { key, .. }
4538                if key == "FERRUM_UNIFIED_GRAPH_LAYERS_ONLY"
4539        ));
4540    }
4541
4542    #[test]
4543    fn unified_graph_lm_head_eager_requires_unified_graph() {
4544        let hardware =
4545            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4546        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4547        let err =
4548            FerrumConfigBuilder::new(snapshot(&[("FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER", "1")]))
4549                .with_model_capabilities(ModelCapabilities::unknown())
4550                .with_hardware_capabilities(hardware)
4551                .with_workload_profile(workload)
4552                .resolve()
4553                .expect_err("lm-head-eager graph scope should require unified graph");
4554        assert!(matches!(
4555            err,
4556            AutoConfigError::InvalidOverride { key, .. }
4557                if key == "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER"
4558        ));
4559    }
4560
4561    #[test]
4562    fn unified_graph_lm_head_eager_conflicts_with_layers_only() {
4563        let hardware =
4564            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4565        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4566        let err = FerrumConfigBuilder::new(snapshot(&[
4567            ("FERRUM_UNIFIED_GRAPH", "1"),
4568            ("FERRUM_UNIFIED_GRAPH_LAYERS_ONLY", "1"),
4569            ("FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER", "1"),
4570        ]))
4571        .with_model_capabilities(ModelCapabilities::unknown())
4572        .with_hardware_capabilities(hardware)
4573        .with_workload_profile(workload)
4574        .resolve()
4575        .expect_err("lm-head-eager graph scope should conflict with layers-only graph scope");
4576        assert!(matches!(
4577            err,
4578            AutoConfigError::InvalidOverride { key, .. }
4579                if key == "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER"
4580        ));
4581    }
4582
4583    #[test]
4584    fn batched_graph_requires_cuda_graph_support() {
4585        let mut features = CompiledKernelFeatures::m3_fast_path_without_fa2();
4586        features.cuda_graph = false;
4587        let hardware = HardwareCapabilities::rtx4090_cuda(features);
4588        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4589        let err = FerrumConfigBuilder::new(snapshot(&[("FERRUM_BATCHED_GRAPH", "1")]))
4590            .with_model_capabilities(ModelCapabilities::unknown())
4591            .with_hardware_capabilities(hardware)
4592            .with_workload_profile(workload)
4593            .resolve()
4594            .expect_err("batched graph should require compiled graph support");
4595        assert!(matches!(
4596            err,
4597            AutoConfigError::InvalidOverride { key, .. } if key == "FERRUM_BATCHED_GRAPH"
4598        ));
4599    }
4600
4601    #[test]
4602    fn unified_graph_requires_cuda_graph_support() {
4603        let mut features = CompiledKernelFeatures::m3_fast_path_without_fa2();
4604        features.cuda_graph = false;
4605        let hardware = HardwareCapabilities::rtx4090_cuda(features);
4606        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4607        let err = FerrumConfigBuilder::new(snapshot(&[("FERRUM_UNIFIED_GRAPH", "1")]))
4608            .with_model_capabilities(ModelCapabilities::unknown())
4609            .with_hardware_capabilities(hardware)
4610            .with_workload_profile(workload)
4611            .resolve()
4612            .expect_err("unified graph should require compiled graph support");
4613        assert!(matches!(
4614            err,
4615            AutoConfigError::InvalidOverride { key, .. } if key == "FERRUM_UNIFIED_GRAPH"
4616        ));
4617    }
4618
4619    #[test]
4620    fn unified_graph_rejects_gemma3_sandwich_models() {
4621        let hardware =
4622            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4623        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4624        let err = FerrumConfigBuilder::new(snapshot(&[("FERRUM_UNIFIED_GRAPH", "1")]))
4625            .with_model_capabilities(gemma3_gptq_model())
4626            .with_hardware_capabilities(hardware)
4627            .with_workload_profile(workload)
4628            .resolve()
4629            .expect_err("Gemma3 unified graph should stay disabled until graph instantiate fits");
4630        assert!(matches!(
4631            err,
4632            AutoConfigError::InvalidOverride { key, .. } if key == "FERRUM_UNIFIED_GRAPH"
4633        ));
4634    }
4635
4636    #[test]
4637    fn batched_graph_rejects_non_cuda_backend() {
4638        let hardware =
4639            cpu_hardware_with_features(CompiledKernelFeatures::m3_fast_path_without_fa2());
4640        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4641        let err = FerrumConfigBuilder::new(snapshot(&[("FERRUM_BATCHED_GRAPH", "1")]))
4642            .with_model_capabilities(ModelCapabilities::unknown())
4643            .with_hardware_capabilities(hardware)
4644            .with_workload_profile(workload)
4645            .resolve()
4646            .expect_err("batched graph should require CUDA");
4647        assert!(matches!(
4648            err,
4649            AutoConfigError::InvalidOverride { key, .. } if key == "FERRUM_BATCHED_GRAPH"
4650        ));
4651    }
4652
4653    #[test]
4654    fn scheduler_active_chunk_combines_with_accelerator_prefill_first_default() {
4655        let resolved = m3(
4656            &[("FERRUM_ACTIVE_DECODE_PREFILL_CHUNK", "64")],
4657            CompiledKernelFeatures::m3_fast_path_without_fa2(),
4658        )
4659        .resolve()
4660        .unwrap();
4661        let scheduler = resolved
4662            .decisions
4663            .iter()
4664            .find(|decision| decision.selection == "scheduler_admission_policy")
4665            .unwrap();
4666        assert_eq!(
4667            scheduler.selected,
4668            "prefill_first_until_active:32+active_decode_prefill_chunk:64+prefill_token_budget:elastic"
4669        );
4670        assert_eq!(
4671            scheduler.source_key.as_deref(),
4672            Some("FERRUM_ACTIVE_DECODE_PREFILL_CHUNK")
4673        );
4674        let prefill_entry = resolved
4675            .runtime_config
4676            .entries
4677            .iter()
4678            .find(|entry| entry.key == "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE")
4679            .expect("accelerator default prefill-first should still be materialized");
4680        assert_eq!(prefill_entry.effective_value, "32");
4681        assert_eq!(prefill_entry.source, RuntimeConfigSource::Default);
4682        assert!(resolved
4683            .runtime_config
4684            .entries
4685            .iter()
4686            .all(|entry| entry.key != "FERRUM_SCHED_PREFILL_STEP_CHUNK"));
4687    }
4688
4689    #[test]
4690    fn scheduler_prefill_step_chunk_override_is_reflected_in_decision_trace() {
4691        let resolved = m3(
4692            &[("FERRUM_SCHED_PREFILL_STEP_CHUNK", "128")],
4693            CompiledKernelFeatures::m3_fast_path_without_fa2(),
4694        )
4695        .resolve()
4696        .unwrap();
4697        let scheduler = resolved
4698            .decisions
4699            .iter()
4700            .find(|decision| decision.selection == "scheduler_admission_policy")
4701            .unwrap();
4702        assert_eq!(
4703            scheduler.selected,
4704            "prefill_first_until_active:32+prefill_step_chunk:128"
4705        );
4706        assert_eq!(scheduler.source, AutoConfigSource::Env);
4707        assert_eq!(
4708            scheduler.source_key.as_deref(),
4709            Some("FERRUM_SCHED_PREFILL_STEP_CHUNK")
4710        );
4711        let step_entry = resolved
4712            .runtime_config
4713            .entries
4714            .iter()
4715            .find(|entry| entry.key == "FERRUM_SCHED_PREFILL_STEP_CHUNK")
4716            .expect("explicit prefill-step chunk should be preserved");
4717        assert_eq!(step_entry.effective_value, "128");
4718        assert_eq!(step_entry.source, RuntimeConfigSource::Env);
4719    }
4720
4721    #[test]
4722    fn cuda_gemma3_gptq_uses_generic_scheduler_default() {
4723        let hardware =
4724            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4725        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4726        let resolved = FerrumConfigBuilder::new(snapshot(&[]))
4727            .with_model_capabilities(gemma3_gptq_model())
4728            .with_hardware_capabilities(hardware)
4729            .with_workload_profile(workload)
4730            .resolve()
4731            .unwrap();
4732        let scheduler = resolved
4733            .decisions
4734            .iter()
4735            .find(|decision| decision.selection == "scheduler_admission_policy")
4736            .unwrap();
4737        assert_eq!(
4738            scheduler.selected,
4739            "prefill_first_until_active:32+prefill_token_budget:elastic"
4740        );
4741        assert_eq!(scheduler.source, AutoConfigSource::Default);
4742        assert!(resolved
4743            .runtime_config
4744            .entries
4745            .iter()
4746            .all(|entry| entry.key != "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK"));
4747        let entry = resolved
4748            .runtime_config
4749            .entries
4750            .iter()
4751            .find(|entry| entry.key == "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE")
4752            .expect("generic scheduler default should be materialized");
4753        assert_eq!(entry.effective_value, "32");
4754        assert_eq!(entry.source, RuntimeConfigSource::Default);
4755    }
4756
4757    #[test]
4758    fn explicit_scheduler_prompt_estimate_is_reflected_for_gemma3() {
4759        let hardware =
4760            HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4761        let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4762        let resolved =
4763            FerrumConfigBuilder::new(snapshot(&[("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE", "1")]))
4764                .with_model_capabilities(gemma3_gptq_model())
4765                .with_hardware_capabilities(hardware)
4766                .with_workload_profile(workload)
4767                .resolve()
4768                .unwrap();
4769        let scheduler = resolved
4770            .decisions
4771            .iter()
4772            .find(|decision| decision.selection == "scheduler_admission_policy")
4773            .unwrap();
4774        assert_eq!(scheduler.selected, "prompt_token_estimate");
4775        assert_eq!(
4776            scheduler.source_key.as_deref(),
4777            Some("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE")
4778        );
4779        assert!(resolved
4780            .runtime_config
4781            .entries
4782            .iter()
4783            .all(|entry| entry.key != "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK"));
4784    }
4785
4786    #[test]
4787    fn scheduler_prefill_first_is_default_accelerator_policy() {
4788        let resolved = m3(&[], CompiledKernelFeatures::m3_fast_path_without_fa2())
4789            .resolve()
4790            .unwrap();
4791        let scheduler = resolved
4792            .decisions
4793            .iter()
4794            .find(|decision| decision.selection == "scheduler_admission_policy")
4795            .unwrap();
4796        assert_eq!(
4797            scheduler.selected,
4798            "prefill_first_until_active:32+prefill_token_budget:elastic"
4799        );
4800        assert_eq!(scheduler.source, AutoConfigSource::Default);
4801        assert_eq!(scheduler.source_key, None);
4802        let entry = resolved
4803            .runtime_config
4804            .entries
4805            .iter()
4806            .find(|entry| entry.key == "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE")
4807            .expect("generic scheduler default should be materialized");
4808        assert_eq!(entry.effective_value, "32");
4809        assert_eq!(entry.source, RuntimeConfigSource::Default);
4810        assert!(resolved
4811            .runtime_config
4812            .entries
4813            .iter()
4814            .all(|entry| entry.key != "FERRUM_SCHED_PREFILL_STEP_CHUNK"));
4815    }
4816
4817    #[test]
4818    fn scheduler_prompt_token_estimate_can_be_disabled_in_decision_trace() {
4819        let resolved = m3(
4820            &[("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE", "0")],
4821            CompiledKernelFeatures::m3_fast_path_without_fa2(),
4822        )
4823        .resolve()
4824        .unwrap();
4825        let scheduler = resolved
4826            .decisions
4827            .iter()
4828            .find(|decision| decision.selection == "scheduler_admission_policy")
4829            .unwrap();
4830        assert_eq!(scheduler.selected, "continuous_default");
4831        assert_eq!(scheduler.source, AutoConfigSource::Env);
4832        assert_eq!(
4833            scheduler.source_key.as_deref(),
4834            Some("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE")
4835        );
4836    }
4837
4838    #[test]
4839    fn prefix_cache_override_is_reflected_in_decision_trace() {
4840        let resolved = m3(
4841            &[("FERRUM_PREFIX_CACHE", "1")],
4842            CompiledKernelFeatures::m3_fast_path_without_fa2(),
4843        )
4844        .resolve()
4845        .unwrap();
4846        let prefix_cache = resolved
4847            .decisions
4848            .iter()
4849            .find(|decision| decision.selection == "prefix_cache_policy")
4850            .unwrap();
4851        assert_eq!(prefix_cache.selected, "prefix_cache_enabled");
4852        assert_eq!(
4853            prefix_cache.source_key.as_deref(),
4854            Some("FERRUM_PREFIX_CACHE")
4855        );
4856    }
4857
4858    #[test]
4859    fn non_env_runtime_sources_are_preserved_in_decision_trace() {
4860        let runtime_config = snapshot_with_sources(&[
4861            (
4862                "FERRUM_FA_LAYOUT_VARLEN",
4863                "1",
4864                RuntimeConfigSource::ConfigFile,
4865            ),
4866            ("FERRUM_PAGED_MAX_SEQS", "48", RuntimeConfigSource::Cli),
4867            (
4868                "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE",
4869                "32",
4870                RuntimeConfigSource::ScriptCase,
4871            ),
4872        ]);
4873        let resolved = FerrumConfigBuilder::new(runtime_config)
4874            .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
4875            .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(
4876                CompiledKernelFeatures::m3_fast_path_without_fa2(),
4877            ))
4878            .with_workload_profile(WorkloadProfile::m3_qwen3_30b_a3b_int4())
4879            .resolve()
4880            .unwrap();
4881
4882        let decision = |selection: &str| {
4883            resolved
4884                .decisions
4885                .iter()
4886                .find(|decision| decision.selection == selection)
4887                .unwrap()
4888        };
4889        let attention = decision("attention_prefill_mixed_backend");
4890        assert_eq!(attention.selected, "fa_layout_varlen");
4891        assert_eq!(attention.source, AutoConfigSource::ConfigFile);
4892        assert_eq!(
4893            attention.source_key.as_deref(),
4894            Some("FERRUM_FA_LAYOUT_VARLEN")
4895        );
4896
4897        let max_sequences = decision("max_sequences");
4898        assert_eq!(max_sequences.selected, "48");
4899        assert_eq!(max_sequences.source, AutoConfigSource::Cli);
4900        assert_eq!(
4901            max_sequences.source_key.as_deref(),
4902            Some("FERRUM_PAGED_MAX_SEQS")
4903        );
4904
4905        let scheduler = decision("scheduler_admission_policy");
4906        assert_eq!(
4907            scheduler.selected,
4908            "prefill_first_until_active:32+prefill_token_budget:elastic"
4909        );
4910        assert_eq!(scheduler.source, AutoConfigSource::ScriptCase);
4911        assert_eq!(
4912            scheduler.source_key.as_deref(),
4913            Some("FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE")
4914        );
4915    }
4916
4917    #[test]
4918    fn renders_effective_config_and_decision_trace_artifacts() {
4919        let resolved = m3(&[], CompiledKernelFeatures::m3_fast_path_without_fa2())
4920            .resolve()
4921            .unwrap();
4922        let effective = resolved.effective_config_document();
4923        assert_eq!(effective["schema_version"], 1);
4924        assert!(effective["env_hash"]
4925            .as_str()
4926            .unwrap()
4927            .starts_with("sha256:"));
4928        assert!(effective["entries"].is_array());
4929        assert_eq!(effective["model_capabilities"]["architecture"], "qwen3_moe");
4930        assert_eq!(effective["hardware_capabilities"]["backend"], "cuda");
4931        assert_eq!(
4932            effective["workload_profile"]["preset"],
4933            M3_QWEN3_30B_A3B_INT4_PRESET
4934        );
4935        assert_eq!(
4936            effective["decisions"].as_array().unwrap().len(),
4937            resolved.decisions.len()
4938        );
4939        let trace = resolved.decision_trace_jsonl().unwrap();
4940        assert_eq!(trace.lines().count(), resolved.decisions.len());
4941        assert!(trace.contains("\"attention_prefill_mixed_backend\""));
4942    }
4943
4944    #[test]
4945    fn auto_config_artifacts_match_locked_schema_shape() {
4946        let resolved = FerrumConfigBuilder::m3_qwen3_30b_a3b_int4(snapshot_with_sources(&[
4947            (
4948                "FERRUM_FA_LAYOUT_VARLEN",
4949                "1",
4950                RuntimeConfigSource::ScriptCase,
4951            ),
4952            ("FERRUM_PAGED_MAX_SEQS", "32", RuntimeConfigSource::Cli),
4953        ]))
4954        .resolve()
4955        .unwrap();
4956
4957        let effective = resolved.effective_config_document();
4958        assert_eq!(effective["schema_version"], 1);
4959        assert!(effective["env_hash"]
4960            .as_str()
4961            .unwrap()
4962            .starts_with("sha256:"));
4963
4964        let entries = effective["entries"].as_array().unwrap();
4965        let keys: Vec<_> = entries
4966            .iter()
4967            .map(|entry| entry["key"].as_str().unwrap())
4968            .collect();
4969        let mut sorted_keys = keys.clone();
4970        sorted_keys.sort_unstable();
4971        assert_eq!(keys, sorted_keys);
4972        for entry in entries {
4973            assert!(entry["key"].as_str().unwrap().starts_with("FERRUM_"));
4974            assert!(entry["effective_value"].is_string());
4975            assert!(matches!(
4976                entry["source"].as_str().unwrap(),
4977                "default" | "config_file" | "cli" | "env" | "script_case" | "memory_profile"
4978            ));
4979            assert!(!entry["affects"].as_array().unwrap().is_empty());
4980        }
4981        assert_eq!(
4982            effective["model_capabilities"]["quantization"].as_str(),
4983            Some("gptq_int4")
4984        );
4985        assert_eq!(
4986            effective["model_capabilities"]["moe"]["experts_per_token"].as_u64(),
4987            Some(8)
4988        );
4989        assert_eq!(
4990            effective["hardware_capabilities"]["compute_capability"].as_str(),
4991            Some("8.9")
4992        );
4993        assert_eq!(
4994            effective["hardware_capabilities"]["compiled_features"]["vllm_moe_marlin"].as_bool(),
4995            Some(true)
4996        );
4997        assert_eq!(
4998            effective["workload_profile"]["target_concurrency"].as_u64(),
4999            Some(32)
5000        );
5001        assert_eq!(
5002            effective["workload_profile"]["priority"].as_str(),
5003            Some("throughput")
5004        );
5005        let admission = &effective["admission"];
5006        for field in [
5007            "effective_max_concurrent",
5008            "queue_depth",
5009            "active_prefill",
5010            "active_decode",
5011            "current_batch_size",
5012            "rejected_requests_total",
5013            "failed_requests_total",
5014            "completed_requests_total",
5015        ] {
5016            assert!(admission[field].is_number(), "admission.{field} missing");
5017        }
5018
5019        let trace = resolved.decision_trace_jsonl().unwrap();
5020        let trace_decisions: Vec<AutoConfigDecision> = trace
5021            .lines()
5022            .map(|line| serde_json::from_str(line).unwrap())
5023            .collect();
5024        assert_eq!(trace_decisions, resolved.decisions);
5025        assert_eq!(
5026            serde_json::from_value::<Vec<AutoConfigDecision>>(effective["decisions"].clone())
5027                .unwrap(),
5028            trace_decisions
5029        );
5030
5031        for decision in &trace_decisions {
5032            assert_eq!(decision.schema_version, 1);
5033            assert!(!decision.selection.trim().is_empty());
5034            assert!(!decision.selected.trim().is_empty());
5035            assert!(!decision.candidates.is_empty());
5036            assert!(!decision.affects.is_empty());
5037            if let Some(source_key) = &decision.source_key {
5038                assert!(source_key.starts_with("FERRUM_"));
5039            }
5040            for rejected in &decision.rejected {
5041                assert!(!rejected.value.trim().is_empty());
5042                assert!(!rejected.reason.trim().is_empty());
5043            }
5044        }
5045    }
5046}