Skip to main content

type_bridge_schema/
adoption.rs

1//! Pure adoption of canonical observed schema as an immutable managed baseline.
2
3use std::collections::BTreeSet;
4
5use type_bridge_contract::capability::CapabilitySet;
6use type_bridge_contract::diagnostic::{Diagnostic, DiagnosticCategory, DiagnosticCode};
7use type_bridge_contract::fingerprint::SemanticProfileId;
8use type_bridge_contract::schema::{
9    DeclaredSchema, ManagedDeclaredIdentityFingerprint, ManagedFactSelection, ManagedSchemaState,
10    ManagedScopeId, ManagedSemanticSchemaFingerprint, SchemaDiagnostic, SchemaDiagnostics,
11    SchemaFactId, SchemaOperation, SourcedSchemaFact,
12};
13use type_bridge_contract::semantic_profile::SemanticProfile;
14
15use crate::{
16    BoundManagedSchemaScope, CanonicalObservedSchema, ManagedSchemaScope, ResolvedSchema,
17    managed_declared_identity_fingerprint, managed_semantic_schema_fingerprint,
18    resolve_schema_with_capabilities,
19};
20
21/// An immutable, operation-free starting point adopted from canonical observation.
22#[derive(Clone, Debug)]
23pub struct AdoptionBaseline {
24    declared_schema: DeclaredSchema,
25    resolved_schema: ResolvedSchema,
26    bound_scope: BoundManagedSchemaScope,
27    semantic_profile: SemanticProfileId,
28    managed_state: ManagedSchemaState,
29    operations: Vec<SchemaOperation>,
30}
31
32impl AdoptionBaseline {
33    /// Return the rebuilt managed-only direct declaration.
34    pub const fn declared_schema(&self) -> &DeclaredSchema {
35        &self.declared_schema
36    }
37
38    /// Return the validated pure resolution of the adopted declaration.
39    pub const fn resolved_schema(&self) -> &ResolvedSchema {
40        &self.resolved_schema
41    }
42
43    /// Return the fixed exclusive scope binding and complete selection.
44    pub const fn bound_scope(&self) -> &BoundManagedSchemaScope {
45        &self.bound_scope
46    }
47
48    /// Return the exact semantic profile used by canonicalization and resolution.
49    pub const fn semantic_profile(&self) -> &SemanticProfileId {
50        &self.semantic_profile
51    }
52
53    /// Return the exact managed declaration and semantic fingerprints with context.
54    pub const fn managed_state(&self) -> &ManagedSchemaState {
55        &self.managed_state
56    }
57
58    /// Return the exact managed declared-identity fingerprint.
59    pub const fn managed_declared_identity(&self) -> &ManagedDeclaredIdentityFingerprint {
60        self.managed_state.managed_declared_identity()
61    }
62
63    /// Return the exact managed semantic-schema fingerprint.
64    pub const fn managed_semantic_schema(&self) -> &ManagedSemanticSchemaFingerprint {
65        self.managed_state.managed_semantic_schema()
66    }
67
68    /// Adoption records a baseline and never authorizes or schedules executable operations.
69    pub fn operations(&self) -> &[SchemaOperation] {
70        &self.operations
71    }
72}
73
74/// Adopt canonical observed direct facts as one exclusive managed baseline.
75pub fn adopt_observed_schema(
76    observed: &CanonicalObservedSchema,
77    scope_id: ManagedScopeId,
78    semantic_profile: &SemanticProfileId,
79    available_capabilities: &CapabilitySet,
80) -> Result<AdoptionBaseline, SchemaDiagnostics> {
81    SemanticProfile::resolve(semantic_profile).map_err(no_source)?;
82    if observed.semantic_profile() != semantic_profile {
83        return Err(adoption_failure(
84            "adoption_semantic_profile_mismatch",
85            "the adoption profile differs from the profile used to canonicalize observation",
86        ));
87    }
88    observed
89        .direct_schema()
90        .required_capabilities()
91        .ensure_supported_by(available_capabilities)
92        .map_err(no_source)?;
93
94    let selected: BTreeSet<SchemaFactId> = observed.managed_scope().iter().cloned().collect();
95    let mut sourced = Vec::with_capacity(selected.len());
96    for id in &selected {
97        let fact = observed.direct_schema().fact(id).ok_or_else(|| {
98            adoption_failure(
99                "adoption_scope_fact_mismatch",
100                "the canonical managed selection references an absent direct fact",
101            )
102        })?;
103        let source = observed
104            .direct_schema()
105            .source(id)
106            .cloned()
107            .ok_or_else(|| {
108                adoption_failure(
109                    "adoption_scope_source_mismatch",
110                    "the canonical managed selection references a fact without provenance",
111                )
112            })?;
113        sourced.push(SourcedSchemaFact::new(fact.clone(), source));
114    }
115
116    let declared_schema = DeclaredSchema::from_facts(
117        observed.direct_schema().format(),
118        observed.direct_schema().required_capabilities().clone(),
119        sourced,
120    )?;
121    let rebuilt_ids: BTreeSet<_> = declared_schema.facts().map(|fact| fact.id()).collect();
122    if rebuilt_ids != selected {
123        return Err(adoption_failure(
124            "adoption_scope_selection_mismatch",
125            "the rebuilt declaration is not exactly the canonical managed direct selection",
126        ));
127    }
128
129    let bound_scope = ManagedSchemaScope::bind_exclusive(scope_id.clone(), &declared_schema)?;
130    if bound_scope.binding().id() != &scope_id || bound_scope.selection().iter().ne(selected.iter())
131    {
132        return Err(adoption_failure(
133            "adoption_exclusive_scope_mismatch",
134            "the fixed exclusive scope does not exactly bind the requested direct selection",
135        ));
136    }
137
138    let resolved_schema = resolve_schema_with_capabilities(
139        &declared_schema,
140        semantic_profile,
141        available_capabilities,
142    )?;
143    let selection =
144        ManagedFactSelection::new(bound_scope.selection().iter().cloned()).map_err(no_source)?;
145    let declared_fingerprint =
146        managed_declared_identity_fingerprint(&declared_schema, &bound_scope)?;
147    let semantic_fingerprint =
148        managed_semantic_schema_fingerprint(&declared_schema, semantic_profile, &bound_scope)?;
149    let managed_state = ManagedSchemaState::new(
150        declared_schema.format(),
151        declared_schema.required_capabilities().clone(),
152        bound_scope.binding().clone(),
153        selection,
154        declared_schema.declared_identity_fingerprint().clone(),
155        declared_fingerprint,
156        semantic_fingerprint,
157    )
158    .map_err(no_source)?;
159
160    Ok(AdoptionBaseline {
161        declared_schema,
162        resolved_schema,
163        bound_scope,
164        semantic_profile: semantic_profile.clone(),
165        managed_state,
166        operations: Vec::new(),
167    })
168}
169
170fn adoption_failure(code: &'static str, message: &'static str) -> SchemaDiagnostics {
171    no_source(Diagnostic::new(
172        DiagnosticCategory::InvalidContract,
173        DiagnosticCode::new(code).expect("static adoption diagnostic code is canonical"),
174        message,
175    ))
176}
177
178fn no_source(diagnostic: Diagnostic) -> SchemaDiagnostics {
179    SchemaDiagnostics::one(SchemaDiagnostic::new(diagnostic, None))
180}