Skip to main content

type_bridge/
schema.rs

1//! Schema package markers and type-branded installation handshake.
2
3use core::marker::PhantomData;
4use std::sync::Arc;
5
6use type_bridge_contract::projection::{ProjectionConfig, RuntimeProjection};
7use type_bridge_contract::schema::encode_declared_schema;
8use type_bridge_contract::sdk_diagnostic::{
9    SdkExecutionDiagnostic, SdkProjectionEvidenceSlotPresence,
10};
11use type_bridge_schema::{
12    MAX_SCHEMA_AUTHORITY_BYTES, VerifiedSchemaAuthority, decode_schema_authority,
13    schema_authority_capability_vocabulary,
14};
15use type_bridge_schema_codegen::RustEmitter;
16
17use crate::__codegen::{
18    CompleteModel, EncodedCreate, GroupedQueryValue, HydratedRow, HydrationCapability,
19    IntoEncodedCreate, IntoEncodedReference, IntoEncodedScalar, IntoEncodedStruct,
20    IntoHydratedSnapshot, MaterializeCreate, MaterializeModel, MaterializeReference,
21    MaterializeStruct, Model, StructValue, ValidationError, ValidationPath,
22};
23use crate::CanonicalCodecOptions;
24use crate::canonical_codec::{
25    CapturedCanonicalCodecControl, input_error as canonical_input_error,
26    output_error as canonical_output_error,
27};
28use crate::error::{Error, ModelValidationPhase, Result};
29
30#[doc(hidden)]
31pub mod sealed {
32    pub trait Sealed {}
33}
34
35/// A type-level marker representing a generated schema package.
36pub trait Schema: sealed::Sealed + Send + Sync + 'static {}
37
38/// Default marker representing an unbound database handle.
39#[derive(Clone, Copy, Debug, Eq, PartialEq)]
40pub struct Unbound;
41
42impl sealed::Sealed for Unbound {}
43impl Schema for Unbound {}
44
45fn canonical_contract_error(error: type_bridge_contract::diagnostic::Diagnostic) -> Error {
46    let code = error.code().as_str().to_owned();
47    Error::model_validation(
48        ModelValidationPhase::Input,
49        code,
50        Vec::new(),
51        "canonical projected-record contract rejected the value",
52        Some(Box::new(error)),
53    )
54}
55
56fn canonical_codec_error(error: type_bridge_orm::ProjectedCodecError) -> Error {
57    if matches!(
58        &error,
59        type_bridge_orm::ProjectedCodecError::Contract(diagnostic)
60            if diagnostic.code().as_str() == "projected_codec_declared_schema_mismatch"
61    ) {
62        return Error::from_sdk_execution(
63            SdkExecutionDiagnostic::projected_record_schema_mismatch(),
64            ModelValidationPhase::Input,
65        );
66    }
67    Error::model_validation(
68        ModelValidationPhase::Input,
69        "canonical_projected_codec_failure",
70        Vec::new(),
71        "installed projection rejected canonical record materialization",
72        Some(Box::new(error)),
73    )
74}
75
76/// Opaque installed projection used by generated successor runtimes.
77#[doc(hidden)]
78pub struct GeneratedProjectionValidator {
79    installed: Arc<type_bridge_orm::InstalledRuntimeProjection>,
80}
81
82impl GeneratedProjectionValidator {
83    /// Validate one generated create encoding through the common projected model authority.
84    pub fn validate_create(&self, encoded: &EncodedCreate) -> Result<(), ValidationError> {
85        crate::projected_codec::validate_encoded_create(encoded, &self.installed)
86            .map_err(generated_validation_error)
87    }
88
89    /// Validate one generated hydration row through the common projected model authority.
90    pub fn validate_hydration(&self, row: &HydratedRow) -> Result<(), ValidationError> {
91        crate::projected_codec::validate_hydrated_row(row, &self.installed)
92            .map_err(generated_validation_error)
93    }
94}
95
96fn generated_validation_error(error: Error) -> ValidationError {
97    let code = error
98        .code()
99        .expect("generated projected validation always returns a classified model error")
100        .to_owned();
101    let path = error
102        .path()
103        .map(generated_validation_path)
104        .unwrap_or_default();
105    ValidationError::new(path, code)
106}
107
108fn generated_validation_path(segments: &[String]) -> String {
109    let mut path = String::new();
110    for segment in segments {
111        if segment.starts_with('[') {
112            path.push_str(segment);
113        } else {
114            if !path.is_empty() {
115                path.push('.');
116            }
117            path.push_str(segment);
118        }
119    }
120    path
121}
122
123/// A generated schema package marker carrying fingerprint evidence branded by `S: Schema`.
124#[derive(Clone, Copy, Debug, Eq, PartialEq)]
125pub struct SchemaPackage<S: Schema> {
126    semantic_fingerprint_json: &'static str,
127    projection_fingerprint_json: &'static str,
128    runtime_projection_json: &'static str,
129    declared_schema_json: Option<&'static str>,
130    schema_authority_json: Option<&'static str>,
131    managed_scope_id: Option<&'static str>,
132    semantic_profile_id: Option<&'static str>,
133    marker: PhantomData<fn() -> S>,
134}
135
136impl<S: Schema> SchemaPackage<S> {
137    /// Encode one exact generated attribute value as canonical projected-record bytes.
138    pub fn encode_attribute<T>(&self, value: T) -> Result<Vec<u8>>
139    where
140        T: Model<Schema = S> + IntoEncodedScalar,
141    {
142        self.encode_attribute_controlled(value, &CanonicalCodecOptions::default())
143    }
144
145    /// Encode one generated attribute under cancellation, deadline, and tighten-only limits.
146    pub fn encode_attribute_controlled<T>(
147        &self,
148        value: T,
149        options: &CanonicalCodecOptions,
150    ) -> Result<Vec<u8>>
151    where
152        T: Model<Schema = S> + IntoEncodedScalar,
153    {
154        let control = CapturedCanonicalCodecControl::capture(options, false)?;
155        control.check()?;
156        let installed = self.verify_and_install()?;
157        let projected = crate::projected_codec::project_attribute_inferred(&value, &installed)?;
158        let record = type_bridge_orm::record_from_attribute(&installed, &projected)
159            .map_err(canonical_codec_error)?;
160        control.check_decoded_weight(record.decoded_weight())?;
161        control.check()?;
162        let bytes = record
163            .encode_with_limits(control.output_limits())
164            .map_err(canonical_output_error)?;
165        control.check()?;
166        Ok(bytes)
167    }
168
169    /// Decode canonical projected-record bytes as one exact generated attribute value.
170    pub fn decode_attribute<T>(&self, bytes: &[u8]) -> Result<T>
171    where
172        T: Model<Schema = S> + GroupedQueryValue,
173    {
174        self.decode_attribute_controlled(bytes, &CanonicalCodecOptions::default())
175    }
176
177    /// Decode one exact generated attribute under cancellation, deadline, and limits.
178    pub fn decode_attribute_controlled<T>(
179        &self,
180        bytes: &[u8],
181        options: &CanonicalCodecOptions,
182    ) -> Result<T>
183    where
184        T: Model<Schema = S> + GroupedQueryValue,
185    {
186        let control = CapturedCanonicalCodecControl::capture(options, false)?;
187        control.check()?;
188        let installed = self.verify_and_install()?;
189        let record = type_bridge_contract::projected_record::ProjectedRecord::decode_with_limits(
190            bytes,
191            control.input_limits(),
192        )
193        .map_err(canonical_input_error)?;
194        control.check_decoded_weight(record.decoded_weight())?;
195        control.check()?;
196        let value = type_bridge_orm::materialize_record(&installed, &record)
197            .map_err(canonical_codec_error)?;
198        let type_bridge_orm::ProjectedCodecValue::Attribute(value) = value else {
199            return Err(Error::model_validation(
200                ModelValidationPhase::Input,
201                "canonical_record_kind_mismatch",
202                Vec::new(),
203                "canonical record is not a generated attribute value",
204                None,
205            ));
206        };
207        let expected = type_bridge_contract::codec::from_canonical_json::<
208            type_bridge_contract::id::TypeId,
209        >(T::TYPE_ID_JSON.as_bytes())
210        .map_err(canonical_contract_error)?;
211        if value.attribute_type() != &expected {
212            return Err(Error::model_validation(
213                ModelValidationPhase::Input,
214                "canonical_attribute_type_mismatch",
215                Vec::new(),
216                "canonical attribute record does not match the requested generated type",
217                None,
218            ));
219        }
220        let scalar = crate::projected_codec::projected_to_decoded_attribute(&value)?;
221        let decoded = T::from_group_scalar(scalar).map_err(|error| {
222            Error::model_validation(
223                ModelValidationPhase::Input,
224                "canonical_attribute_materialization_failed",
225                Vec::new(),
226                "canonical attribute could not materialize as the requested generated type",
227                Some(Box::new(error)),
228            )
229        })?;
230        control.check()?;
231        Ok(decoded)
232    }
233
234    /// Construct a type-branded schema package marker from verified JSON evidence (generated-code SPI).
235    #[doc(hidden)]
236    #[must_use]
237    pub const fn new(
238        semantic_fingerprint_json: &'static str,
239        projection_fingerprint_json: &'static str,
240        runtime_projection_json: &'static str,
241    ) -> Self {
242        Self {
243            semantic_fingerprint_json,
244            projection_fingerprint_json,
245            runtime_projection_json,
246            declared_schema_json: None,
247            schema_authority_json: None,
248            managed_scope_id: None,
249            semantic_profile_id: None,
250            marker: PhantomData,
251        }
252    }
253
254    /// Construct a generated package carrying canonical remote query
255    /// authority in addition to verified runtime projection evidence.
256    #[doc(hidden)]
257    #[must_use]
258    pub const fn new_with_declared(
259        semantic_fingerprint_json: &'static str,
260        projection_fingerprint_json: &'static str,
261        runtime_projection_json: &'static str,
262        declared_schema_json: &'static str,
263    ) -> Self {
264        Self {
265            semantic_fingerprint_json,
266            projection_fingerprint_json,
267            runtime_projection_json,
268            declared_schema_json: Some(declared_schema_json),
269            schema_authority_json: None,
270            managed_scope_id: None,
271            semantic_profile_id: None,
272            marker: PhantomData,
273        }
274    }
275
276    /// Construct a generated package carrying one exact compiled schema
277    /// authority and its constructor-extracted query inputs.
278    #[doc(hidden)]
279    #[must_use]
280    pub const fn new_with_authority(
281        semantic_fingerprint_json: &'static str,
282        projection_fingerprint_json: &'static str,
283        runtime_projection_json: &'static str,
284        schema_authority_json: &'static str,
285        declared_schema_json: &'static str,
286        managed_scope_id: &'static str,
287        semantic_profile_id: &'static str,
288    ) -> Self {
289        Self {
290            semantic_fingerprint_json,
291            projection_fingerprint_json,
292            runtime_projection_json,
293            declared_schema_json: Some(declared_schema_json),
294            schema_authority_json: Some(schema_authority_json),
295            managed_scope_id: Some(managed_scope_id),
296            semantic_profile_id: Some(semantic_profile_id),
297            marker: PhantomData,
298        }
299    }
300
301    /// Encode one exact generated create payload as canonical projected-record bytes.
302    pub fn encode_create<T>(&self, value: T) -> Result<Vec<u8>>
303    where
304        T: IntoEncodedCreate,
305    {
306        self.encode_create_controlled(value, &CanonicalCodecOptions::default())
307    }
308
309    /// Encode one generated create under cancellation, deadline, and tighten-only limits.
310    pub fn encode_create_controlled<T>(
311        &self,
312        value: T,
313        options: &CanonicalCodecOptions,
314    ) -> Result<Vec<u8>>
315    where
316        T: IntoEncodedCreate,
317    {
318        let control = CapturedCanonicalCodecControl::capture(options, false)?;
319        control.check()?;
320        let installed = self.verify_and_install()?;
321        let projected = crate::projected_codec::project_create_inferred(value, &installed)?;
322        let record = type_bridge_orm::record_from_create(&installed, &projected)
323            .map_err(canonical_codec_error)?;
324        control.check_decoded_weight(record.decoded_weight())?;
325        control.check()?;
326        let bytes = record
327            .encode_with_limits(control.output_limits())
328            .map_err(canonical_output_error)?;
329        control.check()?;
330        Ok(bytes)
331    }
332
333    /// Decode canonical projected-record bytes as one exact generated create type.
334    pub fn decode_create<T>(&self, bytes: &[u8]) -> Result<T>
335    where
336        T: MaterializeCreate<Schema = S>,
337    {
338        self.decode_create_controlled(bytes, &CanonicalCodecOptions::default())
339    }
340
341    /// Decode one exact generated create under cancellation, deadline, and limits.
342    pub fn decode_create_controlled<T>(
343        &self,
344        bytes: &[u8],
345        options: &CanonicalCodecOptions,
346    ) -> Result<T>
347    where
348        T: MaterializeCreate<Schema = S>,
349    {
350        let control = CapturedCanonicalCodecControl::capture(options, false)?;
351        control.check()?;
352        let installed = self.verify_and_install()?;
353        let record = type_bridge_contract::projected_record::ProjectedRecord::decode_with_limits(
354            bytes,
355            control.input_limits(),
356        )
357        .map_err(canonical_input_error)?;
358        control.check_decoded_weight(record.decoded_weight())?;
359        control.check()?;
360        let value = type_bridge_orm::materialize_record(&installed, &record)
361            .map_err(canonical_codec_error)?;
362        let type_bridge_orm::ProjectedCodecValue::Create(value) = value else {
363            return Err(Error::model_validation(
364                ModelValidationPhase::Input,
365                "canonical_record_kind_mismatch",
366                Vec::new(),
367                "canonical record is not a generated create payload",
368                None,
369            ));
370        };
371        let decoded = crate::projected_codec::projected_to_decoded_create(&value, &installed)?;
372        let decoded =
373            T::materialize_create(&decoded, &ValidationPath::root()).map_err(|error| {
374                Error::model_validation(
375                    ModelValidationPhase::Input,
376                    "canonical_create_materialization_failed",
377                    Vec::new(),
378                    "canonical create could not materialize as the requested generated type",
379                    Some(Box::new(error)),
380                )
381            })?;
382        control.check()?;
383        Ok(decoded)
384    }
385
386    /// Encode one exact generated detached reference as canonical projected-record bytes.
387    pub fn encode_reference<T>(&self, value: T) -> Result<Vec<u8>>
388    where
389        T: IntoEncodedReference,
390    {
391        self.encode_reference_controlled(value, &CanonicalCodecOptions::default())
392    }
393
394    /// Encode one generated reference under cancellation, deadline, and tighten-only limits.
395    pub fn encode_reference_controlled<T>(
396        &self,
397        value: T,
398        options: &CanonicalCodecOptions,
399    ) -> Result<Vec<u8>>
400    where
401        T: IntoEncodedReference,
402    {
403        let control = CapturedCanonicalCodecControl::capture(options, false)?;
404        control.check()?;
405        let installed = self.verify_and_install()?;
406        let projected = crate::projected_codec::project_reference_inferred(value, &installed)?;
407        let record = type_bridge_orm::record_from_reference(&installed, &projected)
408            .map_err(canonical_codec_error)?;
409        control.check_decoded_weight(record.decoded_weight())?;
410        control.check()?;
411        let bytes = record
412            .encode_with_limits(control.output_limits())
413            .map_err(canonical_output_error)?;
414        control.check()?;
415        Ok(bytes)
416    }
417
418    /// Decode canonical projected-record bytes as one exact generated detached reference.
419    pub fn decode_reference<T>(&self, bytes: &[u8]) -> Result<T>
420    where
421        T: MaterializeReference<Schema = S>,
422    {
423        self.decode_reference_controlled(bytes, &CanonicalCodecOptions::default())
424    }
425
426    /// Decode one exact generated reference under cancellation, deadline, and limits.
427    pub fn decode_reference_controlled<T>(
428        &self,
429        bytes: &[u8],
430        options: &CanonicalCodecOptions,
431    ) -> Result<T>
432    where
433        T: MaterializeReference<Schema = S>,
434    {
435        let control = CapturedCanonicalCodecControl::capture(options, false)?;
436        control.check()?;
437        let installed = self.verify_and_install()?;
438        let record = type_bridge_contract::projected_record::ProjectedRecord::decode_with_limits(
439            bytes,
440            control.input_limits(),
441        )
442        .map_err(canonical_input_error)?;
443        control.check_decoded_weight(record.decoded_weight())?;
444        control.check()?;
445        let value = type_bridge_orm::materialize_record(&installed, &record)
446            .map_err(canonical_codec_error)?;
447        let type_bridge_orm::ProjectedCodecValue::Reference(value) = value else {
448            return Err(Error::model_validation(
449                ModelValidationPhase::Input,
450                "canonical_record_kind_mismatch",
451                Vec::new(),
452                "canonical record is not a generated reference",
453                None,
454            ));
455        };
456        let decoded = crate::projected_codec::projected_to_hydrated_player(&value, &installed)?;
457        let decoded =
458            T::materialize_reference(&decoded, &ValidationPath::root()).map_err(|error| {
459                Error::model_validation(
460                    ModelValidationPhase::Input,
461                    "canonical_reference_materialization_failed",
462                    Vec::new(),
463                    "canonical reference could not materialize as the requested generated type",
464                    Some(Box::new(error)),
465                )
466            })?;
467        control.check()?;
468        Ok(decoded)
469    }
470
471    /// Encode one exact generated hydrated model as a provider-free canonical snapshot.
472    pub fn encode_snapshot<T>(&self, value: T) -> Result<Vec<u8>>
473    where
474        T: IntoHydratedSnapshot + Model<Schema = S>,
475    {
476        self.encode_snapshot_controlled(value, &CanonicalCodecOptions::default())
477    }
478
479    /// Encode one generated snapshot under cancellation, deadline, and tighten-only limits.
480    pub fn encode_snapshot_controlled<T>(
481        &self,
482        value: T,
483        options: &CanonicalCodecOptions,
484    ) -> Result<Vec<u8>>
485    where
486        T: IntoHydratedSnapshot + Model<Schema = S>,
487    {
488        let control = CapturedCanonicalCodecControl::capture(options, false)?;
489        control.check()?;
490        let installed = self.verify_and_install()?;
491        let projected = crate::projected_codec::project_snapshot_inferred(value, &installed)?;
492        let record = type_bridge_orm::record_from_snapshot(&installed, &projected)
493            .map_err(canonical_codec_error)?;
494        control.check_decoded_weight(record.decoded_weight())?;
495        control.check()?;
496        let bytes = record
497            .encode_with_limits(control.output_limits())
498            .map_err(canonical_output_error)?;
499        control.check()?;
500        Ok(bytes)
501    }
502
503    /// Decode canonical snapshot bytes as one exact detached generated model.
504    pub fn decode_snapshot<T>(&self, bytes: &[u8]) -> Result<T>
505    where
506        T: CompleteModel<Schema = S> + MaterializeModel,
507    {
508        self.decode_snapshot_controlled(bytes, &CanonicalCodecOptions::default())
509    }
510
511    /// Decode one exact detached snapshot under cancellation, deadline, and limits.
512    pub fn decode_snapshot_controlled<T>(
513        &self,
514        bytes: &[u8],
515        options: &CanonicalCodecOptions,
516    ) -> Result<T>
517    where
518        T: CompleteModel<Schema = S> + MaterializeModel,
519    {
520        let control = CapturedCanonicalCodecControl::capture(options, false)?;
521        control.check()?;
522        let installed = self.verify_and_install()?;
523        let record = type_bridge_contract::projected_record::ProjectedRecord::decode_with_limits(
524            bytes,
525            control.input_limits(),
526        )
527        .map_err(canonical_input_error)?;
528        control.check_decoded_weight(record.decoded_weight())?;
529        control.check()?;
530        let value = type_bridge_orm::materialize_record(&installed, &record)
531            .map_err(canonical_codec_error)?;
532        let type_bridge_orm::ProjectedCodecValue::Snapshot(value) = value else {
533            return Err(Error::model_validation(
534                ModelValidationPhase::Hydration,
535                "canonical_record_kind_mismatch",
536                Vec::new(),
537                "canonical record is not a generated hydrated snapshot",
538                None,
539            ));
540        };
541        let mut row = crate::projected_codec::projected_to_hydrated_row(&value, &installed)?;
542        row.mark_detached_snapshot();
543        let decoded = T::materialize(&row, &HydrationCapability::new()).map_err(|error| {
544            crate::entity_codec::map_validation_error(error, ModelValidationPhase::Hydration)
545        })?;
546        control.check()?;
547        Ok(decoded)
548    }
549
550    /// Perform offline fingerprint, authority, and exact emitter-evidence verification
551    /// without connecting to a live server.
552    pub fn verify(&self) -> Result<()> {
553        let _ = self.verify_and_install_with_authority()?;
554        Ok(())
555    }
556
557    /// Verify this generated package and open an already schema-bound direct
558    /// database through the canonical connection policy.
559    ///
560    /// Server compatibility is discovered authoritatively; callers cannot
561    /// supply or override the server version used for admission.
562    #[cfg(feature = "typedb")]
563    pub async fn connect(
564        self,
565        policy: type_bridge_orm::DirectConnectionPolicy,
566    ) -> Result<crate::session::Database<S>> {
567        self.connect_with_cancellation(policy, type_bridge_orm::AnswerCancellation::default())
568            .await
569    }
570
571    /// Verify this generated package and open an already schema-bound direct
572    /// database with one wakeable connection-cancellation owner.
573    #[cfg(feature = "typedb")]
574    pub async fn connect_with_cancellation(
575        self,
576        policy: type_bridge_orm::DirectConnectionPolicy,
577        cancellation: type_bridge_orm::AnswerCancellation,
578    ) -> Result<crate::session::Database<S>> {
579        let (installed, authority) = self.verify_and_install_with_authority()?;
580        let match_registry = crate::session::build_match_registry(&installed)?;
581        let inner =
582            type_bridge_orm::Database::connect_direct(installed.as_ref(), &policy, cancellation)
583                .await
584                .map_err(Error::from_direct_connection)?;
585        Ok(crate::session::Database::from_bound_parts(
586            inner,
587            installed,
588            match_registry,
589            authority.map(|authority| authority.managed_scope().id().clone()),
590        ))
591    }
592
593    /// Install the package's exact projection for generated successor-runtime validation.
594    /// Package verification remains projection-evidence admission; generated-token package
595    /// fencing is provided by the nominal generated Rust types.
596    #[doc(hidden)]
597    pub fn generated_projection_validator(
598        &self,
599    ) -> std::result::Result<GeneratedProjectionValidator, ValidationError> {
600        self.verify_and_install()
601            .map(|installed| GeneratedProjectionValidator { installed })
602            .map_err(|_| {
603                ValidationError::new("projection_evidence", "projection_evidence_mismatch")
604            })
605    }
606
607    /// Encode one exact generated struct as canonical projected-record bytes.
608    pub fn encode_struct<T>(&self, value: T) -> Result<Vec<u8>>
609    where
610        T: IntoEncodedStruct + StructValue<Schema = S>,
611    {
612        self.encode_struct_controlled(value, &CanonicalCodecOptions::default())
613    }
614
615    /// Encode one generated struct under cancellation, deadline, and tighten-only limits.
616    pub fn encode_struct_controlled<T>(
617        &self,
618        value: T,
619        options: &CanonicalCodecOptions,
620    ) -> Result<Vec<u8>>
621    where
622        T: IntoEncodedStruct + StructValue<Schema = S>,
623    {
624        let control = CapturedCanonicalCodecControl::capture(options, false)?;
625        control.check()?;
626        let installed = self.verify_and_install()?;
627        let projected = crate::projected_codec::project_struct_inferred(value, &installed)?;
628        let record = type_bridge_orm::record_from_struct(&installed, &projected)
629            .map_err(canonical_codec_error)?;
630        control.check_decoded_weight(record.decoded_weight())?;
631        control.check()?;
632        let bytes = record
633            .encode_with_limits(control.output_limits())
634            .map_err(canonical_output_error)?;
635        control.check()?;
636        Ok(bytes)
637    }
638
639    /// Decode canonical projected-record bytes as one exact generated struct.
640    pub fn decode_struct<T>(&self, bytes: &[u8]) -> Result<T>
641    where
642        T: MaterializeStruct + StructValue<Schema = S>,
643    {
644        self.decode_struct_controlled(bytes, &CanonicalCodecOptions::default())
645    }
646
647    /// Decode one exact generated struct under cancellation, deadline, and limits.
648    pub fn decode_struct_controlled<T>(
649        &self,
650        bytes: &[u8],
651        options: &CanonicalCodecOptions,
652    ) -> Result<T>
653    where
654        T: MaterializeStruct + StructValue<Schema = S>,
655    {
656        let control = CapturedCanonicalCodecControl::capture(options, false)?;
657        control.check()?;
658        let installed = self.verify_and_install()?;
659        let record = type_bridge_contract::projected_record::ProjectedRecord::decode_with_limits(
660            bytes,
661            control.input_limits(),
662        )
663        .map_err(canonical_input_error)?;
664        control.check_decoded_weight(record.decoded_weight())?;
665        control.check()?;
666        let value = type_bridge_orm::materialize_record(&installed, &record)
667            .map_err(canonical_codec_error)?;
668        let type_bridge_orm::ProjectedCodecValue::Struct(value) = value else {
669            return Err(Error::model_validation(
670                ModelValidationPhase::Input,
671                "canonical_record_kind_mismatch",
672                Vec::new(),
673                "canonical record is not a generated struct",
674                None,
675            ));
676        };
677        let decoded = crate::projected_codec::projected_to_decoded_struct(&value, &installed)?;
678        let decoded =
679            T::materialize_struct(&decoded, &ValidationPath::root()).map_err(|error| {
680                Error::model_validation(
681                    ModelValidationPhase::Input,
682                    "canonical_struct_materialization_failed",
683                    Vec::new(),
684                    "canonical struct could not materialize as the requested generated type",
685                    Some(Box::new(error)),
686                )
687            })?;
688        control.check()?;
689        Ok(decoded)
690    }
691
692    /// Compose already canonical package records into one deterministic ordered archive.
693    pub fn encode_archive<I, B>(&self, records: I) -> Result<Vec<u8>>
694    where
695        I: IntoIterator<Item = B>,
696        B: AsRef<[u8]>,
697    {
698        self.encode_archive_controlled(records, &CanonicalCodecOptions::default())
699    }
700
701    /// Compose canonical records under cancellation, deadline, and tighten-only limits.
702    pub fn encode_archive_controlled<I, B>(
703        &self,
704        records: I,
705        options: &CanonicalCodecOptions,
706    ) -> Result<Vec<u8>>
707    where
708        I: IntoIterator<Item = B>,
709        B: AsRef<[u8]>,
710    {
711        let control = CapturedCanonicalCodecControl::capture(options, true)?;
712        control.check()?;
713        let installed = self.verify_and_install()?;
714        let mut verified = Vec::new();
715        for bytes in records {
716            control.check_record_count(verified.len().saturating_add(1))?;
717            control.check()?;
718            let record =
719                type_bridge_contract::projected_record::ProjectedRecord::decode_with_limits(
720                    bytes.as_ref(),
721                    control.input_limits(),
722                )
723                .map_err(canonical_input_error)?;
724            let _ = type_bridge_orm::materialize_record(&installed, &record)
725                .map_err(canonical_codec_error)?;
726            verified.push(record);
727        }
728        let archive = type_bridge_contract::projected_record::ProjectedArchive::try_new(verified)
729            .map_err(canonical_contract_error)?;
730        control.check_decoded_weight(archive.decoded_weight())?;
731        control.check()?;
732        let bytes = archive
733            .encode_with_limits(control.output_limits())
734            .map_err(canonical_output_error)?;
735        control.check()?;
736        Ok(bytes)
737    }
738
739    /// Strictly decode one complete package archive into canonical individual records.
740    pub fn decode_archive(&self, bytes: &[u8]) -> Result<Vec<Vec<u8>>> {
741        self.decode_archive_controlled(bytes, &CanonicalCodecOptions::default())
742    }
743
744    /// Decode one complete archive under cancellation, deadline, and tighten-only limits.
745    pub fn decode_archive_controlled(
746        &self,
747        bytes: &[u8],
748        options: &CanonicalCodecOptions,
749    ) -> Result<Vec<Vec<u8>>> {
750        let control = CapturedCanonicalCodecControl::capture(options, true)?;
751        control.check()?;
752        let installed = self.verify_and_install()?;
753        let archive = type_bridge_contract::projected_record::ProjectedArchive::decode_with_limits(
754            bytes,
755            control.input_limits(),
756        )
757        .map_err(canonical_input_error)?;
758        control.check_record_count(archive.records().len())?;
759        control.check_decoded_weight(archive.decoded_weight())?;
760        let mut records = Vec::with_capacity(archive.records().len());
761        let mut output_bytes = 0_usize;
762        for record in archive.records() {
763            control.check()?;
764            let _ = type_bridge_orm::materialize_record(&installed, record)
765                .map_err(canonical_codec_error)?;
766            let encoded = record
767                .encode_with_limits(control.output_limits())
768                .map_err(canonical_output_error)?;
769            output_bytes = output_bytes.saturating_add(encoded.len());
770            control.check_output_bytes(output_bytes)?;
771            records.push(encoded);
772        }
773        control.check()?;
774        Ok(records)
775    }
776
777    /// Return the semantic schema fingerprint JSON string (generated-code SPI).
778    #[doc(hidden)]
779    #[must_use]
780    pub const fn semantic_fingerprint_json(&self) -> &'static str {
781        self.semantic_fingerprint_json
782    }
783
784    /// Return the binding target projection fingerprint JSON string (generated-code SPI).
785    #[doc(hidden)]
786    #[must_use]
787    pub const fn projection_fingerprint_json(&self) -> &'static str {
788        self.projection_fingerprint_json
789    }
790
791    /// Return the canonical runtime projection JSON string (generated-code SPI).
792    #[doc(hidden)]
793    #[must_use]
794    pub const fn runtime_projection_json(&self) -> &'static str {
795        self.runtime_projection_json
796    }
797
798    pub(crate) const fn declared_schema_json(&self) -> Option<&'static str> {
799        self.declared_schema_json
800    }
801
802    /// Verify all package evidence and derive provider descriptors without provider I/O
803    /// (crate-internal).
804    pub(crate) fn verify_and_install(
805        &self,
806    ) -> Result<Arc<type_bridge_orm::InstalledRuntimeProjection>> {
807        self.verify_and_install_with_authority()
808            .map(|(projection, _authority)| projection)
809    }
810
811    pub(crate) fn verify_and_install_with_authority(
812        &self,
813    ) -> Result<(
814        Arc<type_bridge_orm::InstalledRuntimeProjection>,
815        Option<VerifiedSchemaAuthority>,
816    )> {
817        let authority_backed = self.schema_authority_json.is_some();
818        // The detached semantic-fingerprint string is canonical evidence slot
819        // zero. Empty bytes are the generated Rust install boundary's only
820        // representable absence; every nonempty shape remains merely present
821        // until the binding-neutral rejection classifier sees the failure.
822        let semantic_fingerprint_presence = if self.semantic_fingerprint_json.is_empty() {
823            SdkProjectionEvidenceSlotPresence::Absent
824        } else {
825            SdkProjectionEvidenceSlotPresence::Present
826        };
827        let authority = self.verify_embedded_authority().map_err(|error| {
828            classify_admission_error(authority_backed, semantic_fingerprint_presence, error)
829        })?;
830        let mut projection = type_bridge_orm::InstalledRuntimeProjection::from_verified_rust_json(
831            self.runtime_projection_json.as_bytes(),
832            self.semantic_fingerprint_json.as_bytes(),
833            self.projection_fingerprint_json.as_bytes(),
834        )
835        .map_err(|err| {
836            classify_admission_error(
837                authority_backed,
838                semantic_fingerprint_presence,
839                Error::SchemaVerification {
840                    message: err.to_string(),
841                    source: Some(Box::new(err)),
842                },
843            )
844        })?;
845        if let Some(authority) = authority.as_ref() {
846            projection = projection.with_declared_schema_identity(
847                authority
848                    .resolved_schema()
849                    .declared_identity_fingerprint()
850                    .clone(),
851            );
852        }
853        let successor =
854            authority_backed || projection_uses_ordered_collections(projection.projection());
855        if authority.as_ref().is_some_and(|authority| {
856            authority.resolved_schema().semantic_fingerprint()
857                != projection.projection().semantic_fingerprint()
858        }) {
859            return Err(classify_admission_error(
860                successor,
861                semantic_fingerprint_presence,
862                authority_error(
863                    "generated schema authority does not match the installed runtime projection",
864                ),
865            ));
866        }
867        verify_rust_projection_evidence(&projection, authority.as_ref()).map_err(|error| {
868            classify_admission_error(successor, semantic_fingerprint_presence, error)
869        })?;
870        Ok((Arc::new(projection), authority))
871    }
872
873    fn verify_embedded_authority(&self) -> Result<Option<VerifiedSchemaAuthority>> {
874        let parts = (
875            self.schema_authority_json,
876            self.declared_schema_json,
877            self.managed_scope_id,
878            self.semantic_profile_id,
879        );
880        let (Some(envelope), Some(declared), Some(scope), Some(profile)) = parts else {
881            if parts.0.is_none() && parts.2.is_none() && parts.3.is_none() {
882                return Ok(None);
883            }
884            return Err(authority_error(
885                "generated schema package contains incomplete compiled authority evidence",
886            ));
887        };
888        if envelope.len() > MAX_SCHEMA_AUTHORITY_BYTES {
889            return Err(authority_error(
890                "generated schema authority exceeds the canonical byte ceiling",
891            ));
892        }
893        let authority = decode_schema_authority(
894            envelope.as_bytes(),
895            &schema_authority_capability_vocabulary(),
896        )
897        .map_err(|error| Error::SchemaVerification {
898            message: format!(
899                "generated schema package contains invalid compiled authority ({:?})",
900                error.code()
901            ),
902            source: Some(Box::new(error)),
903        })?;
904        let reconstructed_declared =
905            encode_declared_schema(authority.declared_schema()).map_err(|error| {
906                Error::SchemaVerification {
907                    message: "generated schema authority declaration cannot be reconstructed"
908                        .into(),
909                    source: Some(Box::new(error)),
910                }
911            })?;
912        if reconstructed_declared != declared.as_bytes()
913            || authority.managed_scope().id().as_str() != scope
914            || authority.semantic_profile().id().as_str() != profile
915        {
916            return Err(authority_error(
917                "generated schema authority disagrees with its extracted query evidence",
918            ));
919        }
920        Ok(Some(authority))
921    }
922}
923
924fn verify_rust_projection_evidence(
925    installed: &type_bridge_orm::InstalledRuntimeProjection,
926    authority: Option<&VerifiedSchemaAuthority>,
927) -> Result<()> {
928    let projection = installed.projection();
929    let emitter = RustEmitter::new();
930    if projection.config().rust_naming_policy() != ProjectionConfig::rust().rust_naming_policy()
931        || projection.config().rust_create_policy() != ProjectionConfig::rust().rust_create_policy()
932    {
933        return Err(authority_error(
934            "generated Rust schema package does not match the exact shipped projection configuration",
935        ));
936    }
937    if let Some(authority) = authority {
938        return type_bridge_schema_codegen::verify_projection_evidence(authority, projection)
939            .map_err(|error| Error::SchemaVerification {
940                message: "generated Rust schema package does not match compiled schema authority and exact shipped emitter evidence"
941                    .into(),
942                source: Some(Box::new(error)),
943            });
944    }
945
946    if projection_uses_ordered_collections(projection) {
947        return Err(authority_error(
948            "ordered Rust schema packages require compiled schema authority",
949        ));
950    }
951    let resources = emitter
952        .code_resources()
953        .map_err(|error| Error::SchemaVerification {
954            message: "legacy Rust schema package resource evidence cannot be reconstructed".into(),
955            source: Some(Box::new(error)),
956        })?;
957    if projection.generator_handlers() != emitter.generator_handlers()
958        || projection.code_resources() != resources
959    {
960        return Err(authority_error(
961            "legacy Rust schema package does not match the exact shipped handler and resource evidence",
962        ));
963    }
964
965    Ok(())
966}
967
968fn projection_uses_ordered_collections(projection: &RuntimeProjection) -> bool {
969    projection.models().values().any(|model| {
970        model
971            .query_tokens()
972            .fields()
973            .values()
974            .any(|field| !field.multiplicity().collection_mode().is_unordered())
975            || model
976                .query_tokens()
977                .roles()
978                .values()
979                .any(|role| !role.multiplicity().collection_mode().is_unordered())
980    })
981}
982
983fn authority_error(message: &'static str) -> Error {
984    Error::SchemaVerification {
985        message: message.into(),
986        source: None,
987    }
988}
989
990fn projection_evidence_error(presence: SdkProjectionEvidenceSlotPresence) -> Error {
991    Error::projection_evidence_rejection(presence)
992}
993
994fn classify_admission_error(
995    successor: bool,
996    semantic_fingerprint_presence: SdkProjectionEvidenceSlotPresence,
997    error: Error,
998) -> Error {
999    if successor {
1000        projection_evidence_error(semantic_fingerprint_presence)
1001    } else {
1002        error
1003    }
1004}
1005
1006#[cfg(test)]
1007mod tests {
1008    use super::*;
1009    use serde_json::Value;
1010    use type_bridge_contract::codec::to_canonical_json;
1011    use type_bridge_contract::fingerprint::{
1012        CanonicalizationVersion, Fingerprint, FingerprintDomain, SemanticProfileId,
1013    };
1014    use type_bridge_contract::managed_scope::ManagedScopeId;
1015    use type_bridge_contract::projection::{BindingTarget, ProjectionConfig};
1016    use type_bridge_contract::schema::{DocumentId, encode_declared_schema};
1017    use type_bridge_contract::sdk_diagnostic::{
1018        SdkDiagnosticCategory, SdkDiagnosticDetailValue, SdkDiagnosticPathSegment,
1019        SdkExecutionDiagnostic,
1020    };
1021    use type_bridge_schema::{
1022        ManagedDeltaContext, SCHEMA_AUTHORITY_FINGERPRINT_CANONICALIZATION,
1023        SCHEMA_AUTHORITY_FINGERPRINT_DOMAIN, SchemaDocumentSet, build_schema_authority,
1024        encode_schema_authority, normalize_documents, project, resolve,
1025    };
1026    use type_bridge_schema_codegen::{PythonEmitter, RustEmitter};
1027    use type_bridge_schema_migration::{
1028        MigrationHistoryGraph, VerifiedMigrationHistoryBundle,
1029        encode_verified_migration_history_bundle,
1030    };
1031
1032    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1033    struct TestSchema;
1034    impl sealed::Sealed for TestSchema {}
1035    impl Schema for TestSchema {}
1036
1037    fn leak(bytes: Vec<u8>) -> &'static str {
1038        Box::leak(String::from_utf8(bytes).unwrap().into_boxed_str())
1039    }
1040
1041    fn generated_package(source: &str, scope: &str) -> SchemaPackage<TestSchema> {
1042        package_with_evidence(source, scope, None, None)
1043    }
1044
1045    fn package_with_evidence(
1046        source: &str,
1047        scope: &str,
1048        handlers: Option<Vec<type_bridge_contract::projection::ProjectionHandler>>,
1049        resources: Option<Vec<type_bridge_contract::projection::CodeResourceDigest>>,
1050    ) -> SchemaPackage<TestSchema> {
1051        let documents =
1052            SchemaDocumentSet::parse([(DocumentId::new("authority-test.yaml").unwrap(), source)])
1053                .unwrap();
1054        let declared = normalize_documents(&documents).unwrap();
1055        let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1056        let resolved = resolve(&declared, &profile).unwrap();
1057        let authority = build_schema_authority(
1058            &declared,
1059            declared.required_capabilities(),
1060            &ManagedDeltaContext::new(
1061                ManagedScopeId::new(scope).unwrap(),
1062                profile,
1063                schema_authority_capability_vocabulary(),
1064            ),
1065        )
1066        .unwrap();
1067        let emitter = RustEmitter::new();
1068        let handlers = handlers.unwrap_or_else(|| emitter.generator_handlers_for(&resolved));
1069        let resources = resources.unwrap_or_else(|| emitter.code_resources_for(&resolved).unwrap());
1070        let projection = project(
1071            &resolved,
1072            BindingTarget::Rust,
1073            &ProjectionConfig::rust(),
1074            &handlers,
1075            &resources,
1076        )
1077        .unwrap();
1078        SchemaPackage::new_with_authority(
1079            leak(to_canonical_json(projection.semantic_fingerprint()).unwrap()),
1080            leak(to_canonical_json(projection.projection_fingerprint()).unwrap()),
1081            leak(to_canonical_json(&projection).unwrap()),
1082            leak(encode_schema_authority(&authority)),
1083            leak(encode_declared_schema(&declared).unwrap()),
1084            Box::leak(scope.to_owned().into_boxed_str()),
1085            "typedb-3.12.1/v1",
1086        )
1087    }
1088
1089    fn without_authority(package: SchemaPackage<TestSchema>) -> SchemaPackage<TestSchema> {
1090        SchemaPackage::new(
1091            package.semantic_fingerprint_json,
1092            package.projection_fingerprint_json,
1093            package.runtime_projection_json,
1094        )
1095    }
1096
1097    fn with_runtime_projection(
1098        package: SchemaPackage<TestSchema>,
1099        runtime_projection_json: &'static str,
1100    ) -> SchemaPackage<TestSchema> {
1101        SchemaPackage::new_with_authority(
1102            package.semantic_fingerprint_json,
1103            package.projection_fingerprint_json,
1104            runtime_projection_json,
1105            package.schema_authority_json.unwrap(),
1106            package.declared_schema_json.unwrap(),
1107            package.managed_scope_id.unwrap(),
1108            package.semantic_profile_id.unwrap(),
1109        )
1110    }
1111
1112    fn with_envelope(
1113        package: SchemaPackage<TestSchema>,
1114        envelope: &'static str,
1115    ) -> SchemaPackage<TestSchema> {
1116        SchemaPackage::new_with_authority(
1117            package.semantic_fingerprint_json,
1118            package.projection_fingerprint_json,
1119            package.runtime_projection_json,
1120            envelope,
1121            package.declared_schema_json.unwrap(),
1122            package.managed_scope_id.unwrap(),
1123            package.semantic_profile_id.unwrap(),
1124        )
1125    }
1126
1127    fn with_semantic_fingerprint(
1128        package: SchemaPackage<TestSchema>,
1129        semantic_fingerprint_json: &'static str,
1130    ) -> SchemaPackage<TestSchema> {
1131        SchemaPackage::new_with_authority(
1132            semantic_fingerprint_json,
1133            package.projection_fingerprint_json,
1134            package.runtime_projection_json,
1135            package.schema_authority_json.unwrap(),
1136            package.declared_schema_json.unwrap(),
1137            package.managed_scope_id.unwrap(),
1138            package.semantic_profile_id.unwrap(),
1139        )
1140    }
1141
1142    fn canonical(value: &Value) -> &'static str {
1143        leak(to_canonical_json(value).unwrap())
1144    }
1145
1146    #[test]
1147    fn authority_backed_package_opens_and_fences_generated_migration_catalogs_offline() {
1148        let package = generated_package(
1149            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1150            "rust-migration-catalog",
1151        );
1152        let graph = MigrationHistoryGraph::from_verified(std::iter::empty::<
1153            type_bridge_schema_migration::VerifiedSchemaMigrationManifest,
1154        >())
1155        .unwrap();
1156        let bundle = VerifiedMigrationHistoryBundle::from_graph(&graph).unwrap();
1157        let bytes = encode_verified_migration_history_bundle(&bundle).unwrap();
1158        let catalog = package
1159            .open_migration_catalog(&bytes)
1160            .expect("verified generated bundle opens without provider I/O");
1161        assert!(catalog.is_empty());
1162        assert_eq!(catalog.fingerprint(), bundle.fingerprint());
1163        let preview = catalog
1164            .preview_apply(Vec::new(), None)
1165            .expect("empty generated catalog previews without provider I/O");
1166        assert!(preview.is_apply());
1167        assert!(preview.is_empty());
1168        assert!(!preview.execution_authorized());
1169        let approvals = preview
1170            .approval_builder()
1171            .finish()
1172            .expect("empty exact approval set freezes");
1173        assert!(approvals.is_empty());
1174        let executable = preview
1175            .authorize(&approvals)
1176            .expect("fresh plan is authorized from the exact preview owner");
1177        assert!(executable.execution_authorized());
1178        let foreign_preview = catalog.preview_apply(Vec::new(), None).unwrap();
1179        let mismatch = foreign_preview
1180            .authorize(&approvals)
1181            .expect_err("approval owner mismatch rejects");
1182        assert!(
1183            mismatch
1184                .message()
1185                .contains("migration_approval_plan_mismatch")
1186        );
1187
1188        let mut tampered: Value = serde_json::from_slice(&bytes).unwrap();
1189        tampered["format"] = Value::String("foreign.history/v1".to_owned());
1190        let tampered = to_canonical_json(&tampered).unwrap();
1191        let error = package
1192            .open_migration_catalog(&tampered)
1193            .expect_err("foreign bundle rejects before provider I/O");
1194        assert!(matches!(error, Error::SchemaVerification { .. }));
1195    }
1196
1197    fn resign(value: &mut Value) {
1198        let content = to_canonical_json(&value["content"]).unwrap();
1199        let fingerprint = Fingerprint::compute(
1200            FingerprintDomain::new(SCHEMA_AUTHORITY_FINGERPRINT_DOMAIN).unwrap(),
1201            CanonicalizationVersion::new(SCHEMA_AUTHORITY_FINGERPRINT_CANONICALIZATION).unwrap(),
1202            None,
1203            &content,
1204        );
1205        value["authority_fingerprint"] = serde_json::to_value(fingerprint).unwrap();
1206    }
1207
1208    fn assert_projection_evidence_mismatch(error: &Error) {
1209        assert_eq!(error.category(), crate::ErrorCategory::Integrity);
1210        assert_eq!(error.model_validation_phase(), None);
1211        assert_eq!(error.code(), Some("projection_evidence_mismatch"));
1212        assert_eq!(
1213            error.path().expect("evidence mismatch has a stable path"),
1214            ["projection_evidence"],
1215        );
1216        assert_eq!(
1217            error.message(),
1218            "Generated projection evidence does not match the verified schema package",
1219        );
1220        assert!(matches!(
1221            error
1222                .diagnostic_path()
1223                .expect("evidence mismatch retains its typed path"),
1224            [crate::ErrorPathSegment::Argument(name)] if name == "projection_evidence"
1225        ));
1226        assert!(
1227            error
1228                .details()
1229                .expect("evidence mismatch retains typed details")
1230                .is_empty()
1231        );
1232
1233        let diagnostic = std::error::Error::source(error)
1234            .and_then(|source| source.downcast_ref::<SdkExecutionDiagnostic>())
1235            .expect("the public Rust error retains the common SDK diagnostic");
1236        assert_eq!(diagnostic.category(), SdkDiagnosticCategory::Integrity);
1237        assert!(matches!(
1238            diagnostic.path(),
1239            [SdkDiagnosticPathSegment::Argument(name)]
1240                if name.as_str() == "projection_evidence"
1241        ));
1242        assert!(diagnostic.details().is_empty());
1243    }
1244
1245    fn assert_missing_semantic_fingerprint(error: &Error) {
1246        assert_eq!(error.category(), crate::ErrorCategory::Integrity);
1247        assert_eq!(error.model_validation_phase(), None);
1248        assert_eq!(error.code(), Some("projection_evidence_mismatch"));
1249        assert_eq!(
1250            error.path().expect("missing evidence has a stable path"),
1251            ["projection_evidence", "[0]", "semantic_schema_fingerprint",],
1252        );
1253        assert!(matches!(
1254            error
1255                .diagnostic_path()
1256                .expect("missing evidence retains its typed path"),
1257            [
1258                crate::ErrorPathSegment::Argument(argument),
1259                crate::ErrorPathSegment::Index(0),
1260                crate::ErrorPathSegment::ContractIdentity(identity),
1261            ] if argument == "projection_evidence"
1262                && identity == "semantic_schema_fingerprint"
1263        ));
1264        assert_eq!(
1265            error
1266                .details()
1267                .expect("missing evidence retains typed details"),
1268            &std::collections::BTreeMap::from([
1269                (
1270                    "actual_occurrence_count".to_owned(),
1271                    crate::ErrorDetail::Long(0),
1272                ),
1273                (
1274                    "expected_occurrence_count".to_owned(),
1275                    crate::ErrorDetail::Long(1),
1276                ),
1277                (
1278                    "foreign_package".to_owned(),
1279                    crate::ErrorDetail::Boolean(false),
1280                ),
1281            ]),
1282        );
1283
1284        let diagnostic = std::error::Error::source(error)
1285            .and_then(|source| source.downcast_ref::<SdkExecutionDiagnostic>())
1286            .expect("the Rust error retains the exact common SDK diagnostic");
1287        assert!(matches!(
1288            diagnostic.path(),
1289            [
1290                SdkDiagnosticPathSegment::Argument(argument),
1291                SdkDiagnosticPathSegment::Index(0),
1292                SdkDiagnosticPathSegment::ContractIdentity(identity),
1293            ] if argument.as_str() == "projection_evidence"
1294                && identity.as_str() == "semantic_schema_fingerprint"
1295        ));
1296        assert_eq!(
1297            diagnostic
1298                .details()
1299                .iter()
1300                .map(|(name, value)| (name.as_str(), value))
1301                .collect::<Vec<_>>(),
1302            vec![
1303                (
1304                    "actual_occurrence_count",
1305                    &SdkDiagnosticDetailValue::Count(0),
1306                ),
1307                (
1308                    "expected_occurrence_count",
1309                    &SdkDiagnosticDetailValue::Count(1),
1310                ),
1311                ("foreign_package", &SdkDiagnosticDetailValue::Boolean(false),),
1312            ],
1313        );
1314    }
1315
1316    #[test]
1317    fn compiled_authority_is_fully_verified_and_bound_to_projection() {
1318        let package = generated_package(
1319            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1320            "rust-authority-test",
1321        );
1322        let (projection, authority) = package.verify_and_install_with_authority().unwrap();
1323        let authority = authority.expect("generated package has compiled authority");
1324        assert_eq!(
1325            projection.projection().semantic_fingerprint(),
1326            authority.resolved_schema().semantic_fingerprint(),
1327        );
1328
1329        let foreign = generated_package(
1330            "format: typebridge.schema/v2\nentities:\n  organization: {}\n",
1331            "rust-authority-test",
1332        );
1333        let mismatched: SchemaPackage<TestSchema> = SchemaPackage::new_with_authority(
1334            package.semantic_fingerprint_json,
1335            package.projection_fingerprint_json,
1336            package.runtime_projection_json,
1337            foreign.schema_authority_json.unwrap(),
1338            foreign.declared_schema_json.unwrap(),
1339            foreign.managed_scope_id.unwrap(),
1340            foreign.semantic_profile_id.unwrap(),
1341        );
1342        let error = mismatched
1343            .verify()
1344            .expect_err("foreign semantic authority must not bind to the projection");
1345        assert_projection_evidence_mismatch(&error);
1346    }
1347
1348    #[test]
1349    fn ordered_package_requires_exact_successor_handler_and_compiled_authority() {
1350        const ORDERED: &str = "format: typebridge.schema/v2\nattributes:\n  tag: { value: string }\nentities:\n  person:\n    owns:\n      tag:\n        card: 1\n        ordered: true\n        distinct: true\n";
1351
1352        let valid = generated_package(ORDERED, "rust-ordered-evidence");
1353        let installed = valid.verify_and_install().unwrap();
1354        assert_eq!(
1355            installed.projection().generator_handlers(),
1356            [type_bridge_contract::projection::ProjectionHandler::rust_v2()],
1357        );
1358        let resource_ids = installed
1359            .projection()
1360            .code_resources()
1361            .iter()
1362            .map(|resource| resource.id().as_str())
1363            .collect::<Vec<_>>();
1364        assert_eq!(
1365            resource_ids,
1366            [
1367                "typebridge.generator.rust.cargo-toml",
1368                "typebridge.generator.rust.runtime-source",
1369            ],
1370        );
1371        assert_ne!(
1372            installed.projection().code_resources(),
1373            RustEmitter::new().code_resources().unwrap(),
1374            "the ordered package must carry the successor runtime-resource digest",
1375        );
1376
1377        let legacy = package_with_evidence(
1378            ORDERED,
1379            "rust-ordered-evidence",
1380            Some(vec![
1381                type_bridge_contract::projection::ProjectionHandler::rust_v1(),
1382            ]),
1383            Some(RustEmitter::new().code_resources().unwrap()),
1384        );
1385        let error = legacy
1386            .verify()
1387            .expect_err("an ordered package cannot claim the legacy Rust ledger");
1388        assert_projection_evidence_mismatch(&error);
1389
1390        let authorityless = without_authority(valid);
1391        let error = authorityless
1392            .verify()
1393            .expect_err("ordered packages require reconstructable schema authority");
1394        assert_projection_evidence_mismatch(&error);
1395
1396        let diagnostic = authorityless
1397            .generated_projection_validator()
1398            .err()
1399            .expect("ordered successor validation must reject the detached package");
1400        assert_eq!(diagnostic.code(), "projection_evidence_mismatch");
1401        assert_eq!(diagnostic.field(), "projection_evidence");
1402    }
1403
1404    #[test]
1405    fn package_rejects_missing_forged_and_foreign_resource_evidence() {
1406        const UNORDERED: &str = "format: typebridge.schema/v2\nentities:\n  person: {}\n";
1407        const ORDERED: &str = "format: typebridge.schema/v2\nattributes:\n  tag: { value: string }\nentities:\n  person:\n    owns:\n      tag:\n        card: 1\n        ordered: true\n";
1408
1409        let missing = package_with_evidence(
1410            ORDERED,
1411            "rust-resource-evidence",
1412            Some(vec![
1413                type_bridge_contract::projection::ProjectionHandler::rust_v2(),
1414            ]),
1415            Some(Vec::new()),
1416        );
1417        let error = missing
1418            .verify()
1419            .expect_err("ordered resource evidence is mandatory");
1420        assert_projection_evidence_mismatch(&error);
1421
1422        let emitter = RustEmitter::new();
1423        let documents =
1424            SchemaDocumentSet::parse([(DocumentId::new("forged-resource.yaml").unwrap(), ORDERED)])
1425                .unwrap();
1426        let declared = normalize_documents(&documents).unwrap();
1427        let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1428        let resolved = resolve(&declared, &profile).unwrap();
1429        let mut forged_resources = emitter.code_resources_for(&resolved).unwrap();
1430        let first_id = forged_resources[0].id().as_str().to_owned();
1431        forged_resources[0] = type_bridge_contract::projection::CodeResourceDigest::from_bytes(
1432            first_id,
1433            b"forged Rust emitter resource",
1434        )
1435        .unwrap();
1436        let forged = package_with_evidence(
1437            ORDERED,
1438            "rust-resource-evidence",
1439            Some(emitter.generator_handlers_for(&resolved)),
1440            Some(forged_resources),
1441        );
1442        let error = forged
1443            .verify()
1444            .expect_err("self-consistent forged resource evidence must reject");
1445        assert_projection_evidence_mismatch(&error);
1446
1447        let foreign_resources = type_bridge_schema_codegen::PythonEmitter::new()
1448            .code_resources_for(&resolved)
1449            .unwrap();
1450        let foreign = package_with_evidence(
1451            ORDERED,
1452            "rust-resource-evidence",
1453            Some(emitter.generator_handlers_for(&resolved)),
1454            Some(foreign_resources),
1455        );
1456        let error = foreign
1457            .verify()
1458            .expect_err("foreign binding resource evidence must reject");
1459        assert_projection_evidence_mismatch(&error);
1460
1461        let valid_legacy = generated_package(UNORDERED, "rust-resource-evidence");
1462        let installed_legacy = valid_legacy.verify_and_install().unwrap();
1463        assert_eq!(
1464            installed_legacy.projection().generator_handlers(),
1465            [type_bridge_contract::projection::ProjectionHandler::rust_v1()],
1466        );
1467        assert_eq!(
1468            installed_legacy.projection().code_resources(),
1469            RustEmitter::new().code_resources().unwrap(),
1470        );
1471        assert!(without_authority(valid_legacy).verify().is_ok());
1472    }
1473
1474    #[test]
1475    fn successor_admission_classifies_only_absent_detached_semantic_fingerprint() {
1476        let package = generated_package(
1477            "format: typebridge.schema/v2\nattributes:\n  tag: { value: string }\nentities:\n  person:\n    owns:\n      tag:\n        card: 1\n        ordered: true\n",
1478            "rust-missing-semantic-evidence",
1479        );
1480        let malformed = with_semantic_fingerprint(package, "{")
1481            .verify()
1482            .expect_err("nonempty malformed semantic evidence must reject");
1483        assert_projection_evidence_mismatch(&malformed);
1484
1485        let error = with_semantic_fingerprint(package, "")
1486            .verify()
1487            .expect_err("an absent detached semantic fingerprint must reject");
1488        assert_missing_semantic_fingerprint(&error);
1489    }
1490
1491    #[test]
1492    fn package_rejects_stale_and_reordered_evidence() {
1493        const UNORDERED: &str = "format: typebridge.schema/v2\nentities:\n  person: {}\n";
1494        const ORDERED: &str = "format: typebridge.schema/v2\nattributes:\n  tag: { value: string }\nentities:\n  person:\n    owns:\n      tag:\n        card: 1\n        ordered: true\n";
1495
1496        let emitter = RustEmitter::new();
1497        let ordered_documents =
1498            SchemaDocumentSet::parse([(DocumentId::new("stale-resource.yaml").unwrap(), ORDERED)])
1499                .unwrap();
1500        let ordered_declared = normalize_documents(&ordered_documents).unwrap();
1501        let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1502        let ordered_resolved = resolve(&ordered_declared, &profile).unwrap();
1503        let stale_successor = package_with_evidence(
1504            UNORDERED,
1505            "rust-stale-evidence",
1506            Some(emitter.generator_handlers_for(&ordered_resolved)),
1507            Some(emitter.code_resources_for(&ordered_resolved).unwrap()),
1508        );
1509        let error = stale_successor
1510            .verify()
1511            .expect_err("an unordered package cannot claim successor evidence");
1512        assert_projection_evidence_mismatch(&error);
1513
1514        let ordered = generated_package(ORDERED, "rust-reordered-evidence");
1515        let mut runtime: Value = serde_json::from_str(ordered.runtime_projection_json).unwrap();
1516        runtime["code_resources"]
1517            .as_array_mut()
1518            .expect("runtime projection has a resource ledger")
1519            .swap(0, 1);
1520        let reordered = with_runtime_projection(ordered, canonical(&runtime));
1521        let error = reordered
1522            .verify()
1523            .expect_err("resource ledger wire order is canonical and cannot be changed");
1524        assert_projection_evidence_mismatch(&error);
1525    }
1526
1527    #[test]
1528    fn authority_backed_admission_normalizes_malformed_extra_and_duplicate_evidence() {
1529        const ORDERED: &str = "format: typebridge.schema/v2\nattributes:\n  tag: { value: string }\nentities:\n  person:\n    owns:\n      tag:\n        card: 1\n        ordered: true\n";
1530        let package = generated_package(ORDERED, "rust-malformed-evidence");
1531
1532        let malformed = with_envelope(package, "{")
1533            .verify()
1534            .expect_err("malformed authority evidence must reject");
1535        assert_projection_evidence_mismatch(&malformed);
1536
1537        let runtime: Value = serde_json::from_str(package.runtime_projection_json).unwrap();
1538        let resource = runtime["code_resources"]
1539            .as_array()
1540            .and_then(|resources| resources.first())
1541            .cloned()
1542            .expect("successor projection carries resource evidence");
1543
1544        let mut duplicate_runtime = runtime.clone();
1545        duplicate_runtime["code_resources"]
1546            .as_array_mut()
1547            .unwrap()
1548            .push(resource.clone());
1549        let duplicate = with_runtime_projection(package, canonical(&duplicate_runtime))
1550            .verify()
1551            .expect_err("duplicate resource evidence must reject");
1552        assert_projection_evidence_mismatch(&duplicate);
1553
1554        let mut extra_resource = resource;
1555        extra_resource["id"] = Value::String("typebridge.generator.rust.zzz-extra".into());
1556        let mut extra_runtime = runtime;
1557        extra_runtime["code_resources"]
1558            .as_array_mut()
1559            .unwrap()
1560            .push(extra_resource);
1561        let extra = with_runtime_projection(package, canonical(&extra_runtime))
1562            .verify()
1563            .expect_err("extra resource evidence must reject");
1564        assert_projection_evidence_mismatch(&extra);
1565    }
1566
1567    #[test]
1568    fn compiled_authority_rejects_missing_and_stale_outer_fingerprints() {
1569        let package = generated_package(
1570            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1571            "rust-authority-test",
1572        );
1573        let original: Value = serde_json::from_str(package.schema_authority_json.unwrap()).unwrap();
1574
1575        let mut missing = original.clone();
1576        missing
1577            .as_object_mut()
1578            .unwrap()
1579            .remove("authority_fingerprint");
1580        let error = with_envelope(package, canonical(&missing))
1581            .verify()
1582            .expect_err("missing authority fingerprint must reject");
1583        assert_projection_evidence_mismatch(&error);
1584
1585        let mut stale = original;
1586        stale["authority_fingerprint"]["digest"] = "0".repeat(64).into();
1587        let error = with_envelope(package, canonical(&stale))
1588            .verify()
1589            .expect_err("stale authority fingerprint must reject");
1590        assert_projection_evidence_mismatch(&error);
1591    }
1592
1593    #[test]
1594    fn compiled_authority_reconstructs_managed_state_and_rejects_unsupported_claims() {
1595        let package = generated_package(
1596            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1597            "rust-authority-test",
1598        );
1599        let original: Value = serde_json::from_str(package.schema_authority_json.unwrap()).unwrap();
1600
1601        let mut managed = original.clone();
1602        managed["content"]["managed_state"]["declared_identity"]["digest"] = "0".repeat(64).into();
1603        resign(&mut managed);
1604        let error = with_envelope(package, canonical(&managed))
1605            .verify()
1606            .expect_err("detached managed-state evidence must reject");
1607        assert_projection_evidence_mismatch(&error);
1608
1609        let mut capabilities = original.clone();
1610        capabilities["content"]["required_capabilities"] =
1611            Value::Array(vec![Value::String("unsupported.runtime".into())]);
1612        resign(&mut capabilities);
1613        let error = with_envelope(package, canonical(&capabilities))
1614            .verify()
1615            .expect_err("unsupported artifact capability must fail closed");
1616        assert_projection_evidence_mismatch(&error);
1617
1618        let mut version = original;
1619        version["content"]["authority_version"] =
1620            Value::String("typebridge.schema-authority/v2".into());
1621        resign(&mut version);
1622        let error = with_envelope(package, canonical(&version))
1623            .verify()
1624            .expect_err("unsupported artifact version must fail closed");
1625        assert_projection_evidence_mismatch(&error);
1626    }
1627
1628    #[test]
1629    fn compiled_authority_rejects_oversize_and_detached_evidence() {
1630        let package = generated_package(
1631            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1632            "rust-authority-test",
1633        );
1634        let oversized = Box::leak(" ".repeat(MAX_SCHEMA_AUTHORITY_BYTES + 1).into_boxed_str());
1635        let error = with_envelope(package, oversized)
1636            .verify()
1637            .expect_err("oversize authority must fail before parsing");
1638        assert_projection_evidence_mismatch(&error);
1639
1640        let detached: SchemaPackage<TestSchema> = SchemaPackage::new_with_authority(
1641            package.semantic_fingerprint_json,
1642            package.projection_fingerprint_json,
1643            package.runtime_projection_json,
1644            package.schema_authority_json.unwrap(),
1645            package.declared_schema_json.unwrap(),
1646            "other-scope",
1647            package.semantic_profile_id.unwrap(),
1648        );
1649        let error = detached
1650            .verify()
1651            .expect_err("detached scope must not override compiled authority");
1652        assert_projection_evidence_mismatch(&error);
1653    }
1654
1655    #[test]
1656    fn schema_package_fingerprint_verification() {
1657        let documents = SchemaDocumentSet::parse([(
1658            DocumentId::new("test.yaml").unwrap(),
1659            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1660        )])
1661        .unwrap();
1662        let declared = normalize_documents(&documents).unwrap();
1663        let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1664        let resolved = resolve(&declared, &profile).unwrap();
1665        let emitter = RustEmitter::new();
1666        let resources = emitter.code_resources().unwrap();
1667        let projection = project(
1668            &resolved,
1669            BindingTarget::Rust,
1670            &ProjectionConfig::rust(),
1671            &emitter.generator_handlers(),
1672            &resources,
1673        )
1674        .unwrap();
1675
1676        let semantic_json =
1677            String::from_utf8(to_canonical_json(projection.semantic_fingerprint()).unwrap())
1678                .unwrap();
1679        let projection_json =
1680            String::from_utf8(to_canonical_json(projection.projection_fingerprint()).unwrap())
1681                .unwrap();
1682        let runtime_json = String::from_utf8(to_canonical_json(&projection).unwrap()).unwrap();
1683
1684        let semantic_ref: &'static str = Box::leak(semantic_json.into_boxed_str());
1685        let projection_ref: &'static str = Box::leak(projection_json.into_boxed_str());
1686        let runtime_ref: &'static str = Box::leak(runtime_json.into_boxed_str());
1687
1688        let valid_package: SchemaPackage<TestSchema> =
1689            SchemaPackage::new(semantic_ref, projection_ref, runtime_ref);
1690        assert!(valid_package.verify().is_ok());
1691
1692        let tampered_package: SchemaPackage<TestSchema> =
1693            SchemaPackage::new(semantic_ref, r#""rust/v1-tampered""#, runtime_ref);
1694        match tampered_package.verify() {
1695            Err(err) => {
1696                use std::error::Error as _;
1697                assert_eq!(err.category(), crate::ErrorCategory::Schema);
1698                assert_eq!(err.code(), None);
1699                assert_eq!(err.path(), None);
1700                assert!(err.source().is_some());
1701            }
1702            Ok(_) => panic!("tampered schema package must fail verification"),
1703        }
1704    }
1705
1706    #[test]
1707    fn rejects_non_rust_target_projection() {
1708        let documents = SchemaDocumentSet::parse([(
1709            DocumentId::new("test.yaml").unwrap(),
1710            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1711        )])
1712        .unwrap();
1713        let declared = normalize_documents(&documents).unwrap();
1714        let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1715        let resolved = resolve(&declared, &profile).unwrap();
1716        let py_emitter = PythonEmitter::new();
1717        let py_resources = py_emitter.code_resources().unwrap();
1718        let py_projection = project(
1719            &resolved,
1720            BindingTarget::Python,
1721            &ProjectionConfig::python(),
1722            &py_emitter.generator_handlers(),
1723            &py_resources,
1724        )
1725        .unwrap();
1726
1727        let semantic_json =
1728            String::from_utf8(to_canonical_json(py_projection.semantic_fingerprint()).unwrap())
1729                .unwrap();
1730        let projection_json =
1731            String::from_utf8(to_canonical_json(py_projection.projection_fingerprint()).unwrap())
1732                .unwrap();
1733        let runtime_json = String::from_utf8(to_canonical_json(&py_projection).unwrap()).unwrap();
1734
1735        let semantic_ref: &'static str = Box::leak(semantic_json.into_boxed_str());
1736        let projection_ref: &'static str = Box::leak(projection_json.into_boxed_str());
1737        let runtime_ref: &'static str = Box::leak(runtime_json.into_boxed_str());
1738
1739        let py_package: SchemaPackage<TestSchema> =
1740            SchemaPackage::new(semantic_ref, projection_ref, runtime_ref);
1741        let err = py_package.verify().unwrap_err();
1742        assert_eq!(err.category(), crate::ErrorCategory::Schema);
1743        assert_eq!(err.code(), None);
1744        assert_eq!(err.path(), None);
1745        assert!(err.to_string().contains("target mismatch") || err.to_string().contains("Rust"));
1746    }
1747}