Skip to main content

eredu_runtime/
parameter.rs

1//! Backend-neutral checkpoint materialization and parameter binding.
2
3use std::{collections::BTreeMap, marker::PhantomData};
4
5use eredu_checkpoint::{
6    recipe::{AtomicRecipeSet, DerivedWeightRecipe, RecipeCatalog, RecipeDtype, RecipeError},
7    store::{
8        CheckpointSource, ReadPolicy, SharedCheckpointSource, StoreError, TensorReadRequest,
9        TensorSelection,
10    },
11};
12use eredu_nn::{
13    ParameterId, ParameterMetadata, ParameterVisitor, ParameterVisitorMut, Parameterized,
14};
15
16use crate::{
17    ParameterBackend, ReplicatedTextMaterializationTask, ResidencyDeclarationError, WeightBinding,
18    WeightBindingPlan, WeightLoweringKind,
19};
20
21/// One logical parameter target and the exact recipe that produces it.
22#[derive(Debug, Clone, Eq, PartialEq)]
23pub struct PlannedBinding {
24    /// Stable parameter identity populated by this binding.
25    pub target_name: String,
26    /// Exact logical shape required by the destination.
27    pub expected_shape: Vec<usize>,
28    /// Logical dtype required by the destination.
29    pub expected_dtype: RecipeDtype,
30    /// Declarative transformation from checkpoint sources to the target.
31    pub recipe: DerivedWeightRecipe,
32}
33
34impl PlannedBinding {
35    /// Creates a direct full-source declaration.
36    pub fn direct(
37        target_name: impl Into<String>,
38        source_key: impl Into<String>,
39        expected_shape: impl Into<Vec<usize>>,
40        expected_dtype: RecipeDtype,
41    ) -> Self {
42        Self {
43            target_name: target_name.into(),
44            expected_shape: expected_shape.into(),
45            expected_dtype,
46            recipe: DerivedWeightRecipe::source(source_key, TensorSelection::Full),
47        }
48    }
49}
50
51/// Canonical deterministic declaration for one atomic parameter-binding unit.
52#[derive(Debug, Clone, Eq, PartialEq)]
53pub struct BindingPlan {
54    bindings: Vec<PlannedBinding>,
55    shared_source_keys: std::collections::BTreeSet<String>,
56    permitted_dtype_conversions: BTreeMap<String, Vec<RecipeDtype>>,
57}
58
59impl BindingPlan {
60    /// Builds a plan in which every physical source claim is exclusive.
61    pub fn new(bindings: Vec<PlannedBinding>) -> Result<Self, BindingPlanError> {
62        Self::with_explicit_exceptions(bindings, std::collections::BTreeSet::new(), BTreeMap::new())
63    }
64
65    /// Builds a plan with an explicit allowlist for intentionally shared sources.
66    pub fn allowing_shared_sources(
67        bindings: Vec<PlannedBinding>,
68        shared_source_keys: std::collections::BTreeSet<String>,
69    ) -> Result<Self, BindingPlanError> {
70        Self::with_explicit_exceptions(bindings, shared_source_keys, BTreeMap::new())
71    }
72
73    /// Builds a plan with explicit shared-source and native source-dtype declarations.
74    ///
75    /// Admitted source dtypes retain their recipe metadata and byte accounting.
76    /// This declaration does not insert a dtype conversion.
77    pub fn with_explicit_exceptions(
78        mut bindings: Vec<PlannedBinding>,
79        shared_source_keys: std::collections::BTreeSet<String>,
80        permitted_dtype_conversions: BTreeMap<String, Vec<RecipeDtype>>,
81    ) -> Result<Self, BindingPlanError> {
82        bindings.sort_by(|left, right| left.target_name.cmp(&right.target_name));
83        let mut targets = std::collections::BTreeSet::new();
84        let mut claims = BTreeMap::<String, String>::new();
85        for binding in &bindings {
86            if binding.target_name.trim().is_empty() {
87                return Err(BindingPlanError::EmptyTarget);
88            }
89            if binding.expected_shape.contains(&0) {
90                return Err(BindingPlanError::InvalidTargetShape {
91                    target: binding.target_name.clone(),
92                    shape: binding.expected_shape.clone(),
93                });
94            }
95            binding
96                .expected_shape
97                .iter()
98                .try_fold(1usize, |count, dimension| count.checked_mul(*dimension))
99                .ok_or_else(|| BindingPlanError::TargetShapeOverflow {
100                    target: binding.target_name.clone(),
101                })?;
102            if !targets.insert(binding.target_name.clone()) {
103                return Err(BindingPlanError::DuplicateTarget {
104                    target: binding.target_name.clone(),
105                });
106            }
107            let source_keys = binding.recipe.source_keys();
108            if source_keys.is_empty() {
109                return Err(BindingPlanError::EmptyRecipeSources {
110                    target: binding.target_name.clone(),
111                });
112            }
113            for source in source_keys {
114                if source.trim().is_empty() {
115                    return Err(BindingPlanError::InvalidSourceKey {
116                        target: binding.target_name.clone(),
117                    });
118                }
119                if shared_source_keys.contains(source) {
120                    continue;
121                }
122                if let Some(first) = claims.insert(source.into(), binding.target_name.clone()) {
123                    return Err(BindingPlanError::DuplicateSourceClaim {
124                        source_key: source.into(),
125                        first,
126                        second: binding.target_name.clone(),
127                    });
128                }
129            }
130        }
131        Ok(Self {
132            bindings,
133            shared_source_keys,
134            permitted_dtype_conversions,
135        })
136    }
137
138    /// Returns declarations in deterministic target order.
139    pub fn bindings(&self) -> &[PlannedBinding] {
140        &self.bindings
141    }
142
143    /// Returns distinct physical source keys in lexical order.
144    pub fn source_keys(&self) -> Vec<&str> {
145        let mut keys = std::collections::BTreeSet::new();
146        for binding in &self.bindings {
147            keys.extend(binding.recipe.source_keys());
148        }
149        keys.into_iter().collect()
150    }
151
152    /// Returns sources explicitly declared as shared by multiple targets.
153    pub fn shared_source_keys(&self) -> &std::collections::BTreeSet<String> {
154        &self.shared_source_keys
155    }
156
157    /// Infers every recipe and lowers this declaration to residency bindings.
158    ///
159    /// An authoritative materialized overlay wins over its semantic recipe.
160    pub fn build_bindings(
161        &self,
162        source: &dyn CheckpointSource,
163    ) -> Result<Vec<WeightBinding>, BindingPlanError> {
164        let mut output = Vec::with_capacity(self.bindings.len());
165        for planned in &self.bindings {
166            let recipe = if source.is_authoritative_materialized_key(&planned.target_name) {
167                DerivedWeightRecipe::source(&planned.target_name, TensorSelection::Full)
168            } else {
169                planned.recipe.clone()
170            };
171            let metadata = recipe.infer(source)?;
172            if metadata.shape() != planned.expected_shape {
173                return Err(BindingPlanError::ShapeMismatch {
174                    target: planned.target_name.clone(),
175                    expected: planned.expected_shape.clone(),
176                    actual: metadata.shape().to_vec(),
177                });
178            }
179            let explicitly_permitted = self
180                .permitted_dtype_conversions
181                .get(&planned.target_name)
182                .is_some_and(|dtypes| dtypes.contains(metadata.dtype()));
183            if planned.expected_dtype != *metadata.dtype() && !explicitly_permitted {
184                return Err(BindingPlanError::DtypeMismatch {
185                    target: planned.target_name.clone(),
186                    expected: planned.expected_dtype.clone(),
187                    actual: metadata.dtype().clone(),
188                });
189            }
190            let binding = match recipe {
191                DerivedWeightRecipe::Source { key, selection } => {
192                    WeightBinding::new(&planned.target_name, key, selection, metadata.byte_len())?
193                }
194                recipe => {
195                    WeightBinding::from_recipe(&planned.target_name, recipe, metadata.byte_len())?
196                }
197            };
198            output.push(binding.with_logical_target(&planned.target_name)?);
199        }
200        Ok(output)
201    }
202}
203
204/// Invalid canonical binding declarations or inferred outputs.
205#[derive(Debug, thiserror::Error)]
206pub enum BindingPlanError {
207    /// A declaration has no stable destination identity.
208    #[error("binding target must not be empty")]
209    EmptyTarget,
210    /// A destination contains a zero-sized dimension.
211    #[error("binding target {target:?} has invalid shape {shape:?}")]
212    InvalidTargetShape {
213        /// Invalid target identity.
214        target: String,
215        /// Invalid logical shape.
216        shape: Vec<usize>,
217    },
218    /// Destination element-count computation overflowed.
219    #[error("binding target {target:?} shape element count overflows")]
220    TargetShapeOverflow {
221        /// Target whose element count overflowed.
222        target: String,
223    },
224    /// Two declarations claim the same destination.
225    #[error("binding plan contains duplicate target {target:?}")]
226    DuplicateTarget {
227        /// Repeated target identity.
228        target: String,
229    },
230    /// A recipe contains no physical source.
231    #[error("binding target {target:?} has a recipe with no checkpoint sources")]
232    EmptyRecipeSources {
233        /// Target with no physical source.
234        target: String,
235    },
236    /// A recipe contains an empty physical source identity.
237    #[error("binding target {target:?} has an empty checkpoint source key")]
238    InvalidSourceKey {
239        /// Target containing the invalid source.
240        target: String,
241    },
242    /// Two destinations claimed an undeclared shared physical source.
243    #[error("checkpoint source {source_key:?} is claimed by both {first:?} and {second:?}")]
244    DuplicateSourceClaim {
245        /// Physical source claimed twice.
246        source_key: String,
247        /// First target claiming the source.
248        first: String,
249        /// Second target claiming the source.
250        second: String,
251    },
252    /// Inferred and declared shapes differ.
253    #[error("binding target {target:?} expects shape {expected:?}, recipe produces {actual:?}")]
254    ShapeMismatch {
255        /// Stable target identity.
256        target: String,
257        /// Declared logical shape.
258        expected: Vec<usize>,
259        /// Inferred recipe shape.
260        actual: Vec<usize>,
261    },
262    /// Inferred and declared dtypes differ.
263    #[error("binding target {target:?} expects dtype {expected:?}, recipe produces {actual:?}")]
264    DtypeMismatch {
265        /// Stable target identity.
266        target: String,
267        /// Declared logical dtype.
268        expected: RecipeDtype,
269        /// Inferred recipe dtype.
270        actual: RecipeDtype,
271    },
272    /// Recipe inference failed.
273    #[error(transparent)]
274    Recipe(#[from] RecipeError),
275    /// Lowered residency declaration is invalid.
276    #[error(transparent)]
277    Declaration(#[from] ResidencyDeclarationError),
278}
279
280/// Backend-described geometry of one statically traversed parameter slot.
281#[derive(Debug, Clone, Eq, PartialEq)]
282pub struct ParameterBindingTarget {
283    /// Logical shape expected by the destination.
284    pub shape: Vec<usize>,
285    /// Logical dtype expected by the destination.
286    pub dtype: RecipeDtype,
287    /// Source dtypes the backend can bind to this slot in addition to `dtype`.
288    ///
289    /// Admission preserves the recipe's dtype and byte accounting; it does not
290    /// request conversion to the unloaded slot's initial dtype.
291    pub permitted_source_dtypes: Vec<RecipeDtype>,
292}
293
294/// A canonical binding plan paired with fully qualified logical destinations.
295#[derive(Debug, Clone)]
296pub struct ModuleBindingPlan {
297    plan: BindingPlan,
298    logical_targets: BTreeMap<String, String>,
299}
300
301impl ModuleBindingPlan {
302    /// Returns the canonical declarative plan.
303    pub const fn plan(&self) -> &BindingPlan {
304        &self.plan
305    }
306
307    /// Resolves this declaration against one exact checkpoint source.
308    pub fn build_bindings(
309        &self,
310        source: &dyn CheckpointSource,
311    ) -> Result<Vec<WeightBinding>, ModuleBindingPlanError> {
312        self.plan
313            .build_bindings(source)?
314            .into_iter()
315            .map(|binding| {
316                let target = self
317                    .logical_targets
318                    .get(binding.name())
319                    .expect("module binding declaration retains every target");
320                binding
321                    .with_logical_target(target)
322                    .map_err(ModuleBindingPlanError::from)
323            })
324            .collect()
325    }
326}
327
328/// Builds the singular declarative plan by traversing any neutral parameterized module.
329///
330/// `describe` is the backend's only type-binding hook: it exposes native slot
331/// geometry but cannot select sources, inspect payloads, or publish values.
332pub fn build_module_binding_plan<P, M, F, D>(
333    module: &M,
334    prefix: &str,
335    source: &dyn CheckpointSource,
336    mut recipes: BTreeMap<String, DerivedWeightRecipe>,
337    shared_source_keys: std::collections::BTreeSet<String>,
338    excluded: F,
339    describe: D,
340) -> Result<ModuleBindingPlan, ModuleBindingPlanError>
341where
342    P: 'static,
343    M: Parameterized<P>,
344    F: Fn(&str) -> bool,
345    D: Fn(&P) -> Option<ParameterBindingTarget>,
346{
347    struct Collector<'a, P> {
348        parameters: BTreeMap<String, &'a P>,
349        duplicate: Option<String>,
350    }
351
352    impl<'a, P: 'a> ParameterVisitor<'a, P> for Collector<'a, P> {
353        fn visit(&mut self, metadata: ParameterMetadata, parameter: &'a P) {
354            let name = metadata.id.as_str().to_owned();
355            if self.parameters.insert(name.clone(), parameter).is_some() {
356                self.duplicate = Some(name);
357            }
358        }
359    }
360
361    let mut collector = Collector {
362        parameters: BTreeMap::new(),
363        duplicate: None,
364    };
365    module.visit_parameters(&mut collector);
366    if let Some(parameter) = collector.duplicate {
367        return Err(ModuleBindingPlanError::DuplicateParameter { parameter });
368    }
369
370    recipes.retain(|name, _| !excluded(name));
371    let source_keys = source
372        .source_keys()
373        .into_iter()
374        .collect::<std::collections::BTreeSet<_>>();
375    let mut declarations = Vec::new();
376    let mut logical_targets = BTreeMap::new();
377    let mut permitted_dtype_conversions = BTreeMap::new();
378
379    for (local_name, parameter) in collector
380        .parameters
381        .into_iter()
382        .filter(|(name, _)| !excluded(name))
383    {
384        let destination = qualify_parameter(prefix, &local_name);
385        logical_targets.insert(local_name.clone(), destination.clone());
386        let description = describe(parameter).ok_or_else(|| {
387            ModuleBindingPlanError::InvalidParameterRepresentation {
388                parameter: destination.clone(),
389            }
390        })?;
391
392        let recipe = if source.is_authoritative_materialized_key(&destination) {
393            recipes.remove(&local_name);
394            DerivedWeightRecipe::source(destination.clone(), TensorSelection::Full)
395        } else if let Some(recipe) = recipes.remove(&local_name) {
396            recipe
397        } else if source_keys.contains(&destination) {
398            DerivedWeightRecipe::source(destination.clone(), TensorSelection::Full)
399        } else {
400            return Err(ModuleBindingPlanError::MissingParameter { destination });
401        };
402        if !description.permitted_source_dtypes.is_empty() {
403            permitted_dtype_conversions
404                .insert(local_name.clone(), description.permitted_source_dtypes);
405        }
406        declarations.push(PlannedBinding {
407            target_name: local_name,
408            expected_shape: description.shape,
409            expected_dtype: description.dtype,
410            recipe,
411        });
412    }
413
414    if !recipes.is_empty() {
415        return Err(ModuleBindingPlanError::UnknownRecipeParameters {
416            parameters: recipes.into_keys().collect(),
417        });
418    }
419    Ok(ModuleBindingPlan {
420        plan: BindingPlan::with_explicit_exceptions(
421            declarations,
422            shared_source_keys,
423            permitted_dtype_conversions,
424        )?,
425        logical_targets,
426    })
427}
428
429/// Builds canonical bindings for an exact architecture-selected task partition.
430///
431/// Module traversal supplies destination geometry only. Task identities (including
432/// admitted aliases), physical provenance, recipes, companions, and coverage are
433/// validated here before any backend payload or native operation. `lower_mxfp4`
434/// is the sole backend hook and may only replace an admitted derived F4 recipe for
435/// an MXFP4 executable. When `local_layout` is present, its exact placements are
436/// applied before destination-shape validation. Selected transform outputs are
437/// already rank-local and are never placed a second time.
438pub fn build_exact_replicated_text_bindings<P, M, D, L, E>(
439    module: &M,
440    source: &dyn CheckpointSource,
441    tasks: &[&ReplicatedTextMaterializationTask],
442    addressable_parameters: &std::collections::BTreeSet<String>,
443    local_layout: Option<&crate::LocalModelLayout>,
444    describe: D,
445    mut lower_mxfp4: L,
446) -> Result<Vec<WeightBinding>, ModuleBindingPlanError>
447where
448    P: 'static,
449    M: Parameterized<P>,
450    D: Fn(&P) -> Option<ParameterBindingTarget>,
451    L: FnMut(
452        &ReplicatedTextMaterializationTask,
453        DerivedWeightRecipe,
454        &dyn CheckpointSource,
455    ) -> Result<DerivedWeightRecipe, E>,
456    E: std::fmt::Display,
457{
458    struct Collector<'a, P> {
459        parameters: BTreeMap<String, (&'a P, ParameterBindingTarget)>,
460        duplicate: Option<String>,
461    }
462    impl<'a, P: 'a> ParameterVisitor<'a, P> for Collector<'a, P> {
463        fn visit(&mut self, metadata: ParameterMetadata, parameter: &'a P) {
464            let name = metadata.id.as_str().to_owned();
465            // The description is filled after traversal because the visitor cannot
466            // retain a borrowed callback without complicating this neutral API.
467            if self
468                .parameters
469                .insert(
470                    name.clone(),
471                    (
472                        parameter,
473                        ParameterBindingTarget {
474                            shape: Vec::new(),
475                            dtype: RecipeDtype::Other(String::new()),
476                            permitted_source_dtypes: Vec::new(),
477                        },
478                    ),
479                )
480                .is_some()
481            {
482                self.duplicate = Some(name);
483            }
484        }
485    }
486
487    let mut collector = Collector {
488        parameters: BTreeMap::new(),
489        duplicate: None,
490    };
491    module.visit_parameters(&mut collector);
492    if let Some(parameter) = collector.duplicate {
493        return Err(ModuleBindingPlanError::DuplicateParameter { parameter });
494    }
495    for (name, (parameter, description)) in &mut collector.parameters {
496        *description = describe(parameter).ok_or_else(|| {
497            ModuleBindingPlanError::InvalidParameterRepresentation {
498                parameter: name.clone(),
499            }
500        })?;
501    }
502
503    let parameter_names = collector
504        .parameters
505        .keys()
506        .cloned()
507        .collect::<std::collections::BTreeSet<_>>();
508    let mut covered = std::collections::BTreeSet::new();
509    let mut declarations = Vec::new();
510    let mut conversions = BTreeMap::new();
511    let mut locally_materialized = std::collections::BTreeSet::new();
512    let shared_source_keys = tasks
513        .iter()
514        .flat_map(|task| task.shared_source_keys().iter().cloned())
515        .collect();
516
517    for task in tasks {
518        let candidates = std::iter::once(task.name())
519            .chain(task.aliases().iter().map(String::as_str))
520            .filter(|candidate| parameter_names.contains(*candidate))
521            .collect::<std::collections::BTreeSet<_>>();
522        if candidates.len() != 1 {
523            return Err(ModuleBindingPlanError::ExactTask {
524                details: format!(
525                    "task {:?} resolves to {} module destinations through its canonical identity and aliases: {candidates:?}",
526                    task.name(),
527                    candidates.len()
528                ),
529            });
530        }
531        let target = (*candidates.first().expect("one candidate was validated")).to_owned();
532        if !covered.insert(target.clone()) {
533            return Err(ModuleBindingPlanError::ExactTask {
534                details: format!("module destination {target:?} is claimed more than once"),
535            });
536        }
537        validate_exact_task_provenance(task, source)?;
538        let is_local_transform = matches!(
539            task.lowering(),
540            WeightLoweringKind::Transform | WeightLoweringKind::DerivedTransform
541        );
542        if is_local_transform {
543            locally_materialized.insert(target.clone());
544        }
545        let recipe = exact_task_recipe(task, source, &mut lower_mxfp4)?;
546        push_exact_declaration(
547            &collector.parameters,
548            &mut declarations,
549            &mut conversions,
550            target,
551            recipe,
552            task.permitted_native_source_dtypes(),
553        )?;
554
555        for companion in task.output_companions() {
556            let target = companion.name().to_owned();
557            if is_local_transform {
558                locally_materialized.insert(target.clone());
559            }
560            if !parameter_names.contains(&target) {
561                return Err(ModuleBindingPlanError::ExactTask {
562                    details: format!(
563                        "task {:?} companion {:?} has no module destination",
564                        task.name(),
565                        companion.name()
566                    ),
567                });
568            }
569            if !covered.insert(target.clone()) {
570                return Err(ModuleBindingPlanError::ExactTask {
571                    details: format!("companion destination {target:?} is claimed more than once"),
572                });
573            }
574            let recipe = if let Some(exact) = companion.materialization_task() {
575                validate_exact_task_provenance(exact, source)?;
576                exact_task_recipe(exact, source, &mut lower_mxfp4)?
577            } else if let (Some(recipe), Some(expected)) =
578                (companion.derived_recipe(), companion.derived_output())
579            {
580                let actual = recipe.infer(source)?;
581                if &actual != expected {
582                    return Err(ModuleBindingPlanError::ExactTask {
583                        details: format!(
584                            "companion {:?} differs from its admitted derived output",
585                            companion.name()
586                        ),
587                    });
588                }
589                recipe.clone()
590            } else if let Some(physical) = companion.catalog_source() {
591                validate_physical_source(physical, source)?;
592                DerivedWeightRecipe::source(physical.catalog_key(), TensorSelection::Full)
593            } else if matches!(
594                task.lowering(),
595                WeightLoweringKind::Transform | WeightLoweringKind::DerivedTransform
596            ) || matches!(
597                task.source_encoding(),
598                eredu_checkpoint::SourceTensorEncoding::Gguf { .. }
599            ) {
600                if !source.is_authoritative_materialized_key(companion.name()) {
601                    return Err(ModuleBindingPlanError::ExactTask {
602                        details: format!(
603                            "generated companion {:?} has no authoritative materialized output",
604                            companion.name()
605                        ),
606                    });
607                }
608                DerivedWeightRecipe::source(companion.name(), TensorSelection::Full)
609            } else {
610                return Err(ModuleBindingPlanError::ExactTask {
611                    details: format!(
612                        "companion {:?} has neither an exact task nor causal source",
613                        companion.name()
614                    ),
615                });
616            };
617            push_exact_declaration(
618                &collector.parameters,
619                &mut declarations,
620                &mut conversions,
621                target,
622                recipe,
623                companion.materialization_task().map_or(
624                    &[][..],
625                    ReplicatedTextMaterializationTask::permitted_native_source_dtypes,
626                ),
627            )?;
628        }
629    }
630
631    let missing = parameter_names
632        .difference(&covered)
633        .filter(|name| !addressable_parameters.contains(*name))
634        .cloned()
635        .collect::<Vec<_>>();
636    if !missing.is_empty() {
637        return Err(ModuleBindingPlanError::ExactTask {
638            details: format!("module parameters have no exact materialization task: {missing:?}"),
639        });
640    }
641
642    // The module already has rank-local slots. Place the admitted source recipe
643    // before validating its output against those slots, never after validation.
644    // Transform tasks have already produced their selected rank-local overlays.
645    if let Some(layout) = local_layout {
646        let bindings = declarations
647            .iter()
648            .filter(|declaration| !locally_materialized.contains(&declaration.target_name))
649            .map(|declaration| {
650                Ok(WeightBinding::from_recipe(
651                    &declaration.target_name,
652                    declaration.recipe.clone(),
653                    declaration.recipe.infer(source)?.byte_len(),
654                )?
655                .with_logical_target(&declaration.target_name)?)
656            })
657            .collect::<Result<Vec<_>, ModuleBindingPlanError>>()?;
658        let placed = crate::place_weight_bindings(bindings, source, layout).map_err(|error| {
659            ModuleBindingPlanError::ExactTask {
660                details: error.to_string(),
661            }
662        })?;
663        // Placement preserves input order and produces exactly one output per
664        // binding; logical identities remain the declarations' exact targets.
665        for (declaration, binding) in declarations
666            .iter_mut()
667            .filter(|declaration| !locally_materialized.contains(&declaration.target_name))
668            .zip(placed)
669        {
670            declaration.recipe = binding.source_recipe();
671        }
672    }
673
674    BindingPlan::with_explicit_exceptions(declarations, shared_source_keys, conversions)?
675        .build_bindings(source)
676        .map_err(Into::into)
677}
678
679fn push_exact_declaration<P>(
680    parameters: &BTreeMap<String, (&P, ParameterBindingTarget)>,
681    declarations: &mut Vec<PlannedBinding>,
682    conversions: &mut BTreeMap<String, Vec<RecipeDtype>>,
683    target: String,
684    recipe: DerivedWeightRecipe,
685    explicitly_permitted_source_dtypes: &[RecipeDtype],
686) -> Result<(), ModuleBindingPlanError> {
687    let description = &parameters
688        .get(&target)
689        .expect("validated module destination remains present")
690        .1;
691    let mut permitted = description.permitted_source_dtypes.clone();
692    for dtype in explicitly_permitted_source_dtypes {
693        if !permitted.contains(dtype) {
694            permitted.push(dtype.clone());
695        }
696    }
697    if !permitted.is_empty() {
698        conversions.insert(target.clone(), permitted);
699    }
700    declarations.push(PlannedBinding {
701        target_name: target,
702        expected_shape: description.shape.clone(),
703        expected_dtype: description.dtype.clone(),
704        recipe,
705    });
706    Ok(())
707}
708
709fn validate_exact_task_provenance(
710    task: &ReplicatedTextMaterializationTask,
711    source: &dyn CheckpointSource,
712) -> Result<(), ModuleBindingPlanError> {
713    if matches!(
714        task.lowering(),
715        WeightLoweringKind::Transform | WeightLoweringKind::DerivedTransform
716    ) {
717        return Ok(());
718    }
719    let declared = task
720        .sources()
721        .iter()
722        .map(String::as_str)
723        .collect::<std::collections::BTreeSet<_>>();
724    let physical = task
725        .physical_sources()
726        .iter()
727        .map(|source| source.catalog_key())
728        .collect::<std::collections::BTreeSet<_>>();
729    if declared != physical || physical.len() != task.physical_sources().len() {
730        return Err(ModuleBindingPlanError::ExactTask {
731            details: format!(
732                "task {:?} has inconsistent exact physical-source coverage",
733                task.name()
734            ),
735        });
736    }
737    for physical in task.physical_sources() {
738        validate_physical_source(physical, source)?;
739    }
740    Ok(())
741}
742
743fn validate_physical_source(
744    physical: &crate::ReplicatedTextPhysicalSource,
745    source: &dyn CheckpointSource,
746) -> Result<(), ModuleBindingPlanError> {
747    let provenance = source.source_provenance(physical.catalog_key())?;
748    let metadata = source.source_metadata(physical.catalog_key())?;
749    if provenance.catalog_key != physical.catalog_key()
750        || provenance.physical_tensor != physical.tensor()
751        || provenance.output != physical.output()
752        || provenance.backing_shard.as_deref() != Some(physical.shard())
753        || provenance.source_encoding != *physical.source_encoding()
754        || metadata.encoded_byte_len != physical.encoded_byte_len()
755    {
756        return Err(ModuleBindingPlanError::ExactTask {
757            details: format!(
758                "physical source {:?} differs from admitted provenance",
759                physical.catalog_key()
760            ),
761        });
762    }
763    Ok(())
764}
765
766fn exact_task_recipe<L, E>(
767    task: &ReplicatedTextMaterializationTask,
768    source: &dyn CheckpointSource,
769    lower_mxfp4: &mut L,
770) -> Result<DerivedWeightRecipe, ModuleBindingPlanError>
771where
772    L: FnMut(
773        &ReplicatedTextMaterializationTask,
774        DerivedWeightRecipe,
775        &dyn CheckpointSource,
776    ) -> Result<DerivedWeightRecipe, E>,
777    E: std::fmt::Display,
778{
779    match task.lowering() {
780        WeightLoweringKind::Transform | WeightLoweringKind::DerivedTransform => {
781            if !source.is_authoritative_materialized_key(task.name()) {
782                return Err(ModuleBindingPlanError::ExactTask {
783                    details: format!(
784                        "transformed task {:?} has no authoritative materialized output",
785                        task.name()
786                    ),
787                });
788            }
789            Ok(DerivedWeightRecipe::source(
790                task.name(),
791                TensorSelection::Full,
792            ))
793        }
794        WeightLoweringKind::Direct => {
795            let [key] = task.sources() else {
796                return Err(ModuleBindingPlanError::ExactTask {
797                    details: format!("direct task {:?} must name exactly one source", task.name()),
798                });
799            };
800            Ok(DerivedWeightRecipe::source(key, TensorSelection::Full))
801        }
802        WeightLoweringKind::Derived => {
803            let recipe =
804                task.source_recipe()
805                    .map_err(|error| ModuleBindingPlanError::ExactTask {
806                        details: error.to_string(),
807                    })?;
808            let admitted = recipe.infer(source)?;
809            if task
810                .derived_output()
811                .is_some_and(|expected| expected != &admitted)
812            {
813                return Err(ModuleBindingPlanError::ExactTask {
814                    details: format!(
815                        "derived task {:?} differs from its admitted output",
816                        task.name()
817                    ),
818                });
819            }
820            if task.executable() == eredu_checkpoint::LinearFormat::MxFp4
821                && admitted.dtype() == &RecipeDtype::F4
822            {
823                lower_mxfp4(task, recipe, source).map_err(|error| {
824                    ModuleBindingPlanError::ExactTask {
825                        details: format!(
826                            "MXFP4 recipe lowering for {:?} failed: {error}",
827                            task.name()
828                        ),
829                    }
830                })
831            } else {
832                Ok(recipe)
833            }
834        }
835    }
836}
837
838fn qualify_parameter(prefix: &str, name: &str) -> String {
839    if prefix.is_empty() {
840        name.to_owned()
841    } else {
842        format!("{prefix}.{name}")
843    }
844}
845
846/// Failure while converting a neutral module traversal into a binding plan.
847#[derive(Debug, thiserror::Error)]
848pub enum ModuleBindingPlanError {
849    /// Immutable traversal repeated a stable parameter identity.
850    #[error("module traversal repeats parameter {parameter:?}")]
851    DuplicateParameter {
852        /// Repeated identity.
853        parameter: String,
854    },
855    /// A native parameter could not expose portable geometry.
856    #[error("parameter {parameter:?} has an invalid native representation")]
857    InvalidParameterRepresentation {
858        /// Invalid destination.
859        parameter: String,
860    },
861    /// No recipe, overlay, or direct checkpoint source exists.
862    #[error("checkpoint is missing parameter {destination:?}")]
863    MissingParameter {
864        /// Fully qualified logical destination.
865        destination: String,
866    },
867    /// Recipe declarations remained after complete module traversal.
868    #[error("recipes target unknown or excluded parameters: {parameters:?}")]
869    UnknownRecipeParameters {
870        /// Unknown local identities.
871        parameters: Vec<String>,
872    },
873    /// An exact architecture-selected task disagreed with its module or source.
874    #[error("exact materialization task is invalid: {details}")]
875    ExactTask {
876        /// Exact causal mismatch.
877        details: String,
878    },
879    /// Checkpoint metadata access failed.
880    #[error(transparent)]
881    Store(#[from] StoreError),
882    /// Recipe metadata inference failed while validating an exact task.
883    #[error(transparent)]
884    Recipe(#[from] RecipeError),
885    /// The canonical declaration was invalid.
886    #[error(transparent)]
887    Plan(#[from] BindingPlanError),
888    /// A lowered residency binding was invalid.
889    #[error(transparent)]
890    Declaration(#[from] ResidencyDeclarationError),
891}
892
893/// Consumes one atomic recipe publication into owner bindings and lightweight
894/// logical aliases without cloning any derived recipe.
895pub fn bindings_from_recipe_set<C: RecipeCatalog + ?Sized>(
896    catalog: &C,
897    set: AtomicRecipeSet,
898) -> Result<Vec<WeightBinding>, RecipeBindingError> {
899    let (outputs, aliases) = set.into_parts();
900    let mut bytes = BTreeMap::new();
901    for (name, recipe) in &outputs {
902        bytes.insert(name.clone(), recipe.infer(catalog)?.byte_len());
903    }
904    let mut bindings = outputs
905        .into_iter()
906        .map(|(name, recipe)| {
907            let expected = bytes[&name];
908            WeightBinding::from_recipe(name, recipe, expected)
909        })
910        .collect::<Result<Vec<_>, _>>()?;
911    for (alias, owner) in aliases {
912        bindings.push(WeightBinding::alias(alias, owner.clone(), bytes[&owner])?);
913    }
914    WeightBindingPlan::new(&bindings)?;
915    Ok(bindings)
916}
917
918/// Failure while lowering an atomic neutral recipe set into runtime bindings.
919#[derive(Debug, thiserror::Error)]
920pub enum RecipeBindingError {
921    /// Recipe metadata inference failed.
922    #[error(transparent)]
923    Recipe(#[from] RecipeError),
924    /// A runtime binding declaration was invalid.
925    #[error(transparent)]
926    Declaration(#[from] ResidencyDeclarationError),
927}
928
929/// One fully realized atomic unit keyed by stable parameter identity.
930pub struct MaterializedUnit<B: ParameterBackend> {
931    weights: BTreeMap<ParameterId, B::MaterializedWeight>,
932}
933
934/// An immutable binding unit admitted by one backend against one exact source.
935///
936/// Construction is intentionally restricted to [`select_bindings`]. Consuming
937/// this token materializes the already selected operations without asking the
938/// backend to select them again.
939pub struct SelectedBindingPlan<B: ParameterBackend> {
940    source: SharedCheckpointSource,
941    bindings: Vec<WeightBinding>,
942    backend: PhantomData<fn() -> B>,
943}
944
945impl<B: ParameterBackend> std::fmt::Debug for SelectedBindingPlan<B> {
946    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
947        formatter
948            .debug_struct("SelectedBindingPlan")
949            .field("bindings", &self.bindings)
950            .finish_non_exhaustive()
951    }
952}
953
954fn validated_binding_plan<'a, B: ParameterBackend>(
955    source: &dyn CheckpointSource,
956    bindings: &'a [WeightBinding],
957) -> Result<WeightBindingPlan<'a>, ParameterOrchestrationError<B::ParameterError>> {
958    let plan = WeightBindingPlan::new(bindings)?;
959    for binding in plan.owners() {
960        let inferred = binding.source_recipe().infer(source)?;
961        if inferred.byte_len() != binding.expected_bytes() {
962            return Err(ParameterOrchestrationError::ByteMismatch {
963                parameter: binding.name().to_owned(),
964                expected: binding.expected_bytes(),
965                actual: inferred.byte_len(),
966            });
967        }
968    }
969    for binding in plan.owners() {
970        B::preflight_recipe(&binding.source_recipe(), source)
971            .map_err(ParameterOrchestrationError::Backend)?;
972    }
973    Ok(plan)
974}
975
976/// Validates a complete binding unit before any payload read or native work.
977pub fn preflight_bindings<B: ParameterBackend>(
978    source: &dyn CheckpointSource,
979    bindings: &[WeightBinding],
980) -> Result<(), ParameterOrchestrationError<B::ParameterError>> {
981    validated_binding_plan::<B>(source, bindings).map(|_| ())
982}
983
984/// Selects a complete immutable binding unit against one exact source.
985///
986/// Selection validates every declaration, recipe, byte count, and backend
987/// capability without reading payloads, allocating native tensors, or
988/// publishing parameters. The returned token owns both the source and tasks so
989/// later materialization cannot substitute either or repeat backend selection.
990pub fn select_bindings<B: ParameterBackend>(
991    source: SharedCheckpointSource,
992    bindings: Vec<WeightBinding>,
993) -> Result<SelectedBindingPlan<B>, ParameterOrchestrationError<B::ParameterError>> {
994    validated_binding_plan::<B>(source.as_ref(), &bindings)?;
995    Ok(SelectedBindingPlan {
996        source,
997        bindings,
998        backend: PhantomData,
999    })
1000}
1001
1002impl<B: ParameterBackend> MaterializedUnit<B> {
1003    /// Creates a realized unit from already completed backend-native weights.
1004    ///
1005    /// This supports bounded backend executors while preserving the singular
1006    /// neutral traversal and atomic publication path.
1007    pub fn try_from_weights(
1008        weights: impl IntoIterator<Item = (ParameterId, B::MaterializedWeight)>,
1009    ) -> Result<Self, ParameterOrchestrationError<B::ParameterError>> {
1010        let mut collected = BTreeMap::new();
1011        for (id, weight) in weights {
1012            if collected.insert(id.clone(), weight).is_some() {
1013                return Err(ParameterOrchestrationError::DuplicateBinding { parameter: id });
1014            }
1015        }
1016        Ok(Self { weights: collected })
1017    }
1018
1019    /// Returns the number of realized parameter values.
1020    pub fn len(&self) -> usize {
1021        self.weights.len()
1022    }
1023
1024    /// Returns whether this unit contains no values.
1025    pub fn is_empty(&self) -> bool {
1026        self.weights.is_empty()
1027    }
1028
1029    /// Returns whether a stable parameter identity is present.
1030    pub fn contains(&self, id: &ParameterId) -> bool {
1031        self.weights.contains_key(id)
1032    }
1033}
1034
1035/// Materializes one atomic unit without inspecting backend-native values.
1036///
1037/// Each encoded source remains retained by its backend materialization guard
1038/// until exact completion. The returned weights are independently owned native
1039/// handles ready for binding.
1040pub fn materialize_bindings<B: ParameterBackend>(
1041    source: &dyn CheckpointSource,
1042    bindings: &[WeightBinding],
1043    context: &B::MaterializationContext,
1044) -> Result<MaterializedUnit<B>, ParameterOrchestrationError<B::ParameterError>> {
1045    let plan = validated_binding_plan::<B>(source, bindings)?;
1046    materialize_validated_plan::<B>(source, plan, context)
1047}
1048
1049/// Materializes a previously selected binding unit without repeating backend
1050/// capability selection.
1051pub fn materialize_selected_bindings<B: ParameterBackend>(
1052    selected: SelectedBindingPlan<B>,
1053    context: &B::MaterializationContext,
1054) -> Result<MaterializedUnit<B>, ParameterOrchestrationError<B::ParameterError>> {
1055    let SelectedBindingPlan {
1056        source,
1057        bindings,
1058        backend: _,
1059    } = selected;
1060    let plan = WeightBindingPlan::new(&bindings)
1061        .expect("selected binding declarations remain immutable after admission");
1062    materialize_validated_plan::<B>(source.as_ref(), plan, context)
1063}
1064
1065fn materialize_validated_plan<B: ParameterBackend>(
1066    source: &dyn CheckpointSource,
1067    plan: WeightBindingPlan<'_>,
1068    context: &B::MaterializationContext,
1069) -> Result<MaterializedUnit<B>, ParameterOrchestrationError<B::ParameterError>> {
1070    let mut weights = BTreeMap::new();
1071    for binding in plan.owners() {
1072        let materialization = match binding.recipe() {
1073            Some(recipe) => B::materialize_recipe(recipe, source, context),
1074            None => {
1075                let lease = source.acquire_lease(TensorReadRequest {
1076                    key: binding.checkpoint_key().to_owned(),
1077                    selection: binding.selection().clone(),
1078                    policy: ReadPolicy::RequireBounded,
1079                })?;
1080                B::materialize(lease, context)
1081            }
1082        }
1083        .map_err(ParameterOrchestrationError::Backend)?;
1084        let weight = B::finish_materialization(materialization)
1085            .map_err(ParameterOrchestrationError::Backend)?;
1086        let id = ParameterId::new(binding.name()).map_err(|error| {
1087            ParameterOrchestrationError::InvalidParameterIdentity(error.to_string())
1088        })?;
1089        if weights.insert(id.clone(), weight).is_some() {
1090            return Err(ParameterOrchestrationError::DuplicateBinding { parameter: id });
1091        }
1092    }
1093    for (alias, owner) in plan.aliases() {
1094        let owner_id = ParameterId::new(owner.name()).map_err(|error| {
1095            ParameterOrchestrationError::InvalidParameterIdentity(error.to_string())
1096        })?;
1097        let weight = weights
1098            .get(&owner_id)
1099            .expect("validated owner was materialized before aliases");
1100        let weight =
1101            B::share_materialized_weight(weight).map_err(ParameterOrchestrationError::Backend)?;
1102        let alias_id = ParameterId::new(alias.name()).map_err(|error| {
1103            ParameterOrchestrationError::InvalidParameterIdentity(error.to_string())
1104        })?;
1105        weights.insert(alias_id, weight);
1106    }
1107    Ok(MaterializedUnit { weights })
1108}
1109
1110/// Binds a realized unit into a statically traversed native module.
1111///
1112/// Binding is keyed exclusively by stable parameter identity. Missing and
1113/// unexpected values fail closed; native tensor shape or storage remains
1114/// backend-owned.
1115pub fn bind_materialized_unit<B, M>(
1116    module: &mut M,
1117    unit: MaterializedUnit<B>,
1118) -> Result<(), ParameterOrchestrationError<B::ParameterError>>
1119where
1120    B: ParameterBackend,
1121    M: Parameterized<B::Parameter>,
1122{
1123    bind_materialized_unit_excluding::<B, M, _>(module, unit, |_| false)
1124}
1125
1126/// Binds a realized unit while leaving explicitly excluded destinations untouched.
1127pub fn bind_materialized_unit_excluding<B, M, F>(
1128    module: &mut M,
1129    mut unit: MaterializedUnit<B>,
1130    excluded: F,
1131) -> Result<(), ParameterOrchestrationError<B::ParameterError>>
1132where
1133    B: ParameterBackend,
1134    M: Parameterized<B::Parameter>,
1135    F: Fn(&ParameterId) -> bool,
1136{
1137    struct Validator<'a, B: ParameterBackend> {
1138        weights: &'a BTreeMap<ParameterId, B::MaterializedWeight>,
1139        visited: BTreeMap<ParameterId, ()>,
1140        error: Option<ParameterOrchestrationError<B::ParameterError>>,
1141        excluded: &'a dyn Fn(&ParameterId) -> bool,
1142    }
1143
1144    impl<'a, 'value, B: ParameterBackend> ParameterVisitor<'value, B::Parameter> for Validator<'a, B> {
1145        fn visit(&mut self, metadata: ParameterMetadata, parameter: &'value B::Parameter) {
1146            if self.error.is_some() {
1147                return;
1148            }
1149            if (self.excluded)(&metadata.id) {
1150                return;
1151            }
1152            if self.visited.insert(metadata.id.clone(), ()).is_some() {
1153                self.error = Some(ParameterOrchestrationError::DuplicateParameter {
1154                    parameter: metadata.id,
1155                });
1156                return;
1157            }
1158            let Some(weight) = self.weights.get(&metadata.id) else {
1159                self.error = Some(ParameterOrchestrationError::MissingBinding {
1160                    parameter: metadata.id,
1161                });
1162                return;
1163            };
1164            if let Err(error) = B::validate_bind(parameter, weight) {
1165                self.error = Some(ParameterOrchestrationError::Backend(error));
1166            }
1167        }
1168    }
1169
1170    let mut validator = Validator::<B> {
1171        weights: &unit.weights,
1172        visited: BTreeMap::new(),
1173        error: None,
1174        excluded: &excluded,
1175    };
1176    module.visit_parameters(&mut validator);
1177    if let Some(error) = validator.error {
1178        return Err(error);
1179    }
1180    let unexpected = unit
1181        .weights
1182        .keys()
1183        .filter(|id| !validator.visited.contains_key(*id))
1184        .cloned()
1185        .collect::<Vec<_>>();
1186    if !unexpected.is_empty() {
1187        return Err(ParameterOrchestrationError::UnexpectedBindings {
1188            parameters: unexpected,
1189        });
1190    }
1191
1192    struct MutableTopologyValidator<'a> {
1193        expected: &'a BTreeMap<ParameterId, ()>,
1194        visited: BTreeMap<ParameterId, ()>,
1195        unexpected: Vec<ParameterId>,
1196        duplicate: Option<ParameterId>,
1197        excluded: &'a dyn Fn(&ParameterId) -> bool,
1198    }
1199
1200    impl<'a, 'value, P: 'value> ParameterVisitorMut<'value, P> for MutableTopologyValidator<'a> {
1201        fn visit_mut(&mut self, metadata: ParameterMetadata, _: &'value mut P) {
1202            if self.duplicate.is_some() {
1203                return;
1204            }
1205            if (self.excluded)(&metadata.id) {
1206                return;
1207            }
1208            if self.visited.insert(metadata.id.clone(), ()).is_some() {
1209                self.duplicate = Some(metadata.id);
1210            } else if !self.expected.contains_key(&metadata.id) {
1211                self.unexpected.push(metadata.id);
1212            }
1213        }
1214    }
1215
1216    let mut mutable_topology = MutableTopologyValidator {
1217        expected: &validator.visited,
1218        visited: BTreeMap::new(),
1219        unexpected: Vec::new(),
1220        duplicate: None,
1221        excluded: &excluded,
1222    };
1223    module.visit_parameters_mut(&mut mutable_topology);
1224    if let Some(parameter) = mutable_topology.duplicate {
1225        return Err(ParameterOrchestrationError::DuplicateParameter { parameter });
1226    }
1227    let mut mismatch = mutable_topology.unexpected;
1228    mismatch.extend(
1229        validator
1230            .visited
1231            .keys()
1232            .filter(|id| !mutable_topology.visited.contains_key(*id))
1233            .cloned(),
1234    );
1235    if !mismatch.is_empty() {
1236        mismatch.sort();
1237        mismatch.dedup();
1238        return Err(ParameterOrchestrationError::ParameterTraversalMismatch {
1239            parameters: mismatch,
1240        });
1241    }
1242
1243    struct Binder<'a, B: ParameterBackend> {
1244        weights: &'a mut BTreeMap<ParameterId, B::MaterializedWeight>,
1245        excluded: &'a dyn Fn(&ParameterId) -> bool,
1246    }
1247
1248    impl<'a, 'value, B: ParameterBackend> ParameterVisitorMut<'value, B::Parameter> for Binder<'a, B> {
1249        fn visit_mut(&mut self, metadata: ParameterMetadata, parameter: &'value mut B::Parameter) {
1250            if (self.excluded)(&metadata.id) {
1251                return;
1252            }
1253            let weight = self
1254                .weights
1255                .remove(&metadata.id)
1256                .expect("prepublication mutable traversal validated every binding identity");
1257            B::bind(parameter, weight);
1258        }
1259    }
1260
1261    let mut binder = Binder::<B> {
1262        weights: &mut unit.weights,
1263        excluded: &excluded,
1264    };
1265    module.visit_parameters_mut(&mut binder);
1266    assert!(
1267        unit.weights.is_empty(),
1268        "prepublication mutable traversal validated complete binding consumption"
1269    );
1270    Ok(())
1271}
1272
1273/// Failure in backend-neutral parameter materialization or binding.
1274#[derive(Debug, thiserror::Error)]
1275pub enum ParameterOrchestrationError<E>
1276where
1277    E: std::error::Error + Send + Sync + 'static,
1278{
1279    /// Binding aliases were invalid before materialization began.
1280    #[error(transparent)]
1281    Declaration(#[from] ResidencyDeclarationError),
1282    /// Neutral checkpoint access failed.
1283    #[error(transparent)]
1284    Store(#[from] StoreError),
1285    /// Neutral recipe validation failed.
1286    #[error(transparent)]
1287    Recipe(#[from] RecipeError),
1288    /// A stable runtime parameter identity was invalid.
1289    #[error("invalid runtime parameter identity: {0}")]
1290    InvalidParameterIdentity(String),
1291    /// Two bindings targeted the same stable parameter.
1292    #[error("duplicate materialized binding for parameter {parameter}")]
1293    DuplicateBinding {
1294        /// Duplicated identity.
1295        parameter: ParameterId,
1296    },
1297    /// A module traversal repeated one stable destination identity.
1298    #[error("module parameter traversal repeats identity {parameter}")]
1299    DuplicateParameter {
1300        /// Repeated module parameter identity.
1301        parameter: ParameterId,
1302    },
1303    /// Immutable and mutable module traversals declared different identities.
1304    #[error("immutable and mutable module parameter traversals disagree: {parameters:?}")]
1305    ParameterTraversalMismatch {
1306        /// Identities present in only one traversal.
1307        parameters: Vec<ParameterId>,
1308    },
1309    /// Inferred and declared materialized byte sizes disagreed.
1310    #[error("parameter {parameter:?} declares {expected} bytes but its recipe produces {actual}")]
1311    ByteMismatch {
1312        /// Stable binding name.
1313        parameter: String,
1314        /// Declared bytes.
1315        expected: u64,
1316        /// Inferred bytes.
1317        actual: u64,
1318    },
1319    /// A traversed parameter had no realized value.
1320    #[error("materialized unit has no value for parameter {parameter}")]
1321    MissingBinding {
1322        /// Missing parameter identity.
1323        parameter: ParameterId,
1324    },
1325    /// Realized values remained after complete parameter traversal.
1326    #[error("materialized unit contains values for unknown parameters: {parameters:?}")]
1327    UnexpectedBindings {
1328        /// Unknown parameter identities.
1329        parameters: Vec<ParameterId>,
1330    },
1331    /// Backend-native realization or binding failed.
1332    #[error("backend parameter operation failed: {0}")]
1333    Backend(E),
1334}