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() != &ProjectionConfig::rust() {
931        return Err(authority_error(
932            "generated Rust schema package does not match the exact shipped projection configuration",
933        ));
934    }
935    if let Some(authority) = authority {
936        return type_bridge_schema_codegen::verify_projection_evidence(authority, projection)
937            .map_err(|error| Error::SchemaVerification {
938                message: "generated Rust schema package does not match compiled schema authority and exact shipped emitter evidence"
939                    .into(),
940                source: Some(Box::new(error)),
941            });
942    }
943
944    if projection_uses_ordered_collections(projection) {
945        return Err(authority_error(
946            "ordered Rust schema packages require compiled schema authority",
947        ));
948    }
949    let resources = emitter
950        .code_resources()
951        .map_err(|error| Error::SchemaVerification {
952            message: "legacy Rust schema package resource evidence cannot be reconstructed".into(),
953            source: Some(Box::new(error)),
954        })?;
955    if projection.generator_handlers() != emitter.generator_handlers()
956        || projection.code_resources() != resources
957    {
958        return Err(authority_error(
959            "legacy Rust schema package does not match the exact shipped handler and resource evidence",
960        ));
961    }
962
963    Ok(())
964}
965
966fn projection_uses_ordered_collections(projection: &RuntimeProjection) -> bool {
967    projection.models().values().any(|model| {
968        model
969            .query_tokens()
970            .fields()
971            .values()
972            .any(|field| !field.multiplicity().collection_mode().is_unordered())
973            || model
974                .query_tokens()
975                .roles()
976                .values()
977                .any(|role| !role.multiplicity().collection_mode().is_unordered())
978    })
979}
980
981fn authority_error(message: &'static str) -> Error {
982    Error::SchemaVerification {
983        message: message.into(),
984        source: None,
985    }
986}
987
988fn projection_evidence_error(presence: SdkProjectionEvidenceSlotPresence) -> Error {
989    Error::projection_evidence_rejection(presence)
990}
991
992fn classify_admission_error(
993    successor: bool,
994    semantic_fingerprint_presence: SdkProjectionEvidenceSlotPresence,
995    error: Error,
996) -> Error {
997    if successor {
998        projection_evidence_error(semantic_fingerprint_presence)
999    } else {
1000        error
1001    }
1002}
1003
1004#[cfg(test)]
1005mod tests {
1006    use super::*;
1007    use serde_json::Value;
1008    use type_bridge_contract::codec::to_canonical_json;
1009    use type_bridge_contract::fingerprint::{
1010        CanonicalizationVersion, Fingerprint, FingerprintDomain, SemanticProfileId,
1011    };
1012    use type_bridge_contract::managed_scope::ManagedScopeId;
1013    use type_bridge_contract::projection::{BindingTarget, ProjectionConfig};
1014    use type_bridge_contract::schema::{DocumentId, encode_declared_schema};
1015    use type_bridge_contract::sdk_diagnostic::{
1016        SdkDiagnosticCategory, SdkDiagnosticDetailValue, SdkDiagnosticPathSegment,
1017        SdkExecutionDiagnostic,
1018    };
1019    use type_bridge_schema::{
1020        ManagedDeltaContext, SCHEMA_AUTHORITY_FINGERPRINT_CANONICALIZATION,
1021        SCHEMA_AUTHORITY_FINGERPRINT_DOMAIN, SchemaDocumentSet, build_schema_authority,
1022        encode_schema_authority, normalize_documents, project, resolve,
1023    };
1024    use type_bridge_schema_codegen::{PythonEmitter, RustEmitter};
1025    use type_bridge_schema_migration::{
1026        MigrationHistoryGraph, VerifiedMigrationHistoryBundle,
1027        encode_verified_migration_history_bundle,
1028    };
1029
1030    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1031    struct TestSchema;
1032    impl sealed::Sealed for TestSchema {}
1033    impl Schema for TestSchema {}
1034
1035    fn leak(bytes: Vec<u8>) -> &'static str {
1036        Box::leak(String::from_utf8(bytes).unwrap().into_boxed_str())
1037    }
1038
1039    fn generated_package(source: &str, scope: &str) -> SchemaPackage<TestSchema> {
1040        package_with_evidence(source, scope, None, None)
1041    }
1042
1043    fn package_with_evidence(
1044        source: &str,
1045        scope: &str,
1046        handlers: Option<Vec<type_bridge_contract::projection::ProjectionHandler>>,
1047        resources: Option<Vec<type_bridge_contract::projection::CodeResourceDigest>>,
1048    ) -> SchemaPackage<TestSchema> {
1049        let documents =
1050            SchemaDocumentSet::parse([(DocumentId::new("authority-test.yaml").unwrap(), source)])
1051                .unwrap();
1052        let declared = normalize_documents(&documents).unwrap();
1053        let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1054        let resolved = resolve(&declared, &profile).unwrap();
1055        let authority = build_schema_authority(
1056            &declared,
1057            declared.required_capabilities(),
1058            &ManagedDeltaContext::new(
1059                ManagedScopeId::new(scope).unwrap(),
1060                profile,
1061                schema_authority_capability_vocabulary(),
1062            ),
1063        )
1064        .unwrap();
1065        let emitter = RustEmitter::new();
1066        let handlers = handlers.unwrap_or_else(|| emitter.generator_handlers_for(&resolved));
1067        let resources = resources.unwrap_or_else(|| emitter.code_resources_for(&resolved).unwrap());
1068        let projection = project(
1069            &resolved,
1070            BindingTarget::Rust,
1071            &ProjectionConfig::rust(),
1072            &handlers,
1073            &resources,
1074        )
1075        .unwrap();
1076        SchemaPackage::new_with_authority(
1077            leak(to_canonical_json(projection.semantic_fingerprint()).unwrap()),
1078            leak(to_canonical_json(projection.projection_fingerprint()).unwrap()),
1079            leak(to_canonical_json(&projection).unwrap()),
1080            leak(encode_schema_authority(&authority)),
1081            leak(encode_declared_schema(&declared).unwrap()),
1082            Box::leak(scope.to_owned().into_boxed_str()),
1083            "typedb-3.12.1/v1",
1084        )
1085    }
1086
1087    fn without_authority(package: SchemaPackage<TestSchema>) -> SchemaPackage<TestSchema> {
1088        SchemaPackage::new(
1089            package.semantic_fingerprint_json,
1090            package.projection_fingerprint_json,
1091            package.runtime_projection_json,
1092        )
1093    }
1094
1095    fn with_runtime_projection(
1096        package: SchemaPackage<TestSchema>,
1097        runtime_projection_json: &'static str,
1098    ) -> SchemaPackage<TestSchema> {
1099        SchemaPackage::new_with_authority(
1100            package.semantic_fingerprint_json,
1101            package.projection_fingerprint_json,
1102            runtime_projection_json,
1103            package.schema_authority_json.unwrap(),
1104            package.declared_schema_json.unwrap(),
1105            package.managed_scope_id.unwrap(),
1106            package.semantic_profile_id.unwrap(),
1107        )
1108    }
1109
1110    fn with_envelope(
1111        package: SchemaPackage<TestSchema>,
1112        envelope: &'static str,
1113    ) -> SchemaPackage<TestSchema> {
1114        SchemaPackage::new_with_authority(
1115            package.semantic_fingerprint_json,
1116            package.projection_fingerprint_json,
1117            package.runtime_projection_json,
1118            envelope,
1119            package.declared_schema_json.unwrap(),
1120            package.managed_scope_id.unwrap(),
1121            package.semantic_profile_id.unwrap(),
1122        )
1123    }
1124
1125    fn with_semantic_fingerprint(
1126        package: SchemaPackage<TestSchema>,
1127        semantic_fingerprint_json: &'static str,
1128    ) -> SchemaPackage<TestSchema> {
1129        SchemaPackage::new_with_authority(
1130            semantic_fingerprint_json,
1131            package.projection_fingerprint_json,
1132            package.runtime_projection_json,
1133            package.schema_authority_json.unwrap(),
1134            package.declared_schema_json.unwrap(),
1135            package.managed_scope_id.unwrap(),
1136            package.semantic_profile_id.unwrap(),
1137        )
1138    }
1139
1140    fn canonical(value: &Value) -> &'static str {
1141        leak(to_canonical_json(value).unwrap())
1142    }
1143
1144    #[test]
1145    fn authority_backed_package_opens_and_fences_generated_migration_catalogs_offline() {
1146        let package = generated_package(
1147            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1148            "rust-migration-catalog",
1149        );
1150        let graph = MigrationHistoryGraph::from_verified(std::iter::empty::<
1151            type_bridge_schema_migration::VerifiedSchemaMigrationManifest,
1152        >())
1153        .unwrap();
1154        let bundle = VerifiedMigrationHistoryBundle::from_graph(&graph).unwrap();
1155        let bytes = encode_verified_migration_history_bundle(&bundle).unwrap();
1156        let catalog = package
1157            .open_migration_catalog(&bytes)
1158            .expect("verified generated bundle opens without provider I/O");
1159        assert!(catalog.is_empty());
1160        assert_eq!(catalog.fingerprint(), bundle.fingerprint());
1161        let preview = catalog
1162            .preview_apply(Vec::new(), None)
1163            .expect("empty generated catalog previews without provider I/O");
1164        assert!(preview.is_apply());
1165        assert!(preview.is_empty());
1166        assert!(!preview.execution_authorized());
1167        let approvals = preview
1168            .approval_builder()
1169            .finish()
1170            .expect("empty exact approval set freezes");
1171        assert!(approvals.is_empty());
1172        let executable = preview
1173            .authorize(&approvals)
1174            .expect("fresh plan is authorized from the exact preview owner");
1175        assert!(executable.execution_authorized());
1176        let foreign_preview = catalog.preview_apply(Vec::new(), None).unwrap();
1177        let mismatch = foreign_preview
1178            .authorize(&approvals)
1179            .expect_err("approval owner mismatch rejects");
1180        assert!(
1181            mismatch
1182                .message()
1183                .contains("migration_approval_plan_mismatch")
1184        );
1185
1186        let mut tampered: Value = serde_json::from_slice(&bytes).unwrap();
1187        tampered["format"] = Value::String("foreign.history/v1".to_owned());
1188        let tampered = to_canonical_json(&tampered).unwrap();
1189        let error = package
1190            .open_migration_catalog(&tampered)
1191            .expect_err("foreign bundle rejects before provider I/O");
1192        assert!(matches!(error, Error::SchemaVerification { .. }));
1193    }
1194
1195    fn resign(value: &mut Value) {
1196        let content = to_canonical_json(&value["content"]).unwrap();
1197        let fingerprint = Fingerprint::compute(
1198            FingerprintDomain::new(SCHEMA_AUTHORITY_FINGERPRINT_DOMAIN).unwrap(),
1199            CanonicalizationVersion::new(SCHEMA_AUTHORITY_FINGERPRINT_CANONICALIZATION).unwrap(),
1200            None,
1201            &content,
1202        );
1203        value["authority_fingerprint"] = serde_json::to_value(fingerprint).unwrap();
1204    }
1205
1206    fn assert_projection_evidence_mismatch(error: &Error) {
1207        assert_eq!(error.category(), crate::ErrorCategory::Integrity);
1208        assert_eq!(error.model_validation_phase(), None);
1209        assert_eq!(error.code(), Some("projection_evidence_mismatch"));
1210        assert_eq!(
1211            error.path().expect("evidence mismatch has a stable path"),
1212            ["projection_evidence"],
1213        );
1214        assert_eq!(
1215            error.message(),
1216            "Generated projection evidence does not match the verified schema package",
1217        );
1218        assert!(matches!(
1219            error
1220                .diagnostic_path()
1221                .expect("evidence mismatch retains its typed path"),
1222            [crate::ErrorPathSegment::Argument(name)] if name == "projection_evidence"
1223        ));
1224        assert!(
1225            error
1226                .details()
1227                .expect("evidence mismatch retains typed details")
1228                .is_empty()
1229        );
1230
1231        let diagnostic = std::error::Error::source(error)
1232            .and_then(|source| source.downcast_ref::<SdkExecutionDiagnostic>())
1233            .expect("the public Rust error retains the common SDK diagnostic");
1234        assert_eq!(diagnostic.category(), SdkDiagnosticCategory::Integrity);
1235        assert!(matches!(
1236            diagnostic.path(),
1237            [SdkDiagnosticPathSegment::Argument(name)]
1238                if name.as_str() == "projection_evidence"
1239        ));
1240        assert!(diagnostic.details().is_empty());
1241    }
1242
1243    fn assert_missing_semantic_fingerprint(error: &Error) {
1244        assert_eq!(error.category(), crate::ErrorCategory::Integrity);
1245        assert_eq!(error.model_validation_phase(), None);
1246        assert_eq!(error.code(), Some("projection_evidence_mismatch"));
1247        assert_eq!(
1248            error.path().expect("missing evidence has a stable path"),
1249            ["projection_evidence", "[0]", "semantic_schema_fingerprint",],
1250        );
1251        assert!(matches!(
1252            error
1253                .diagnostic_path()
1254                .expect("missing evidence retains its typed path"),
1255            [
1256                crate::ErrorPathSegment::Argument(argument),
1257                crate::ErrorPathSegment::Index(0),
1258                crate::ErrorPathSegment::ContractIdentity(identity),
1259            ] if argument == "projection_evidence"
1260                && identity == "semantic_schema_fingerprint"
1261        ));
1262        assert_eq!(
1263            error
1264                .details()
1265                .expect("missing evidence retains typed details"),
1266            &std::collections::BTreeMap::from([
1267                (
1268                    "actual_occurrence_count".to_owned(),
1269                    crate::ErrorDetail::Long(0),
1270                ),
1271                (
1272                    "expected_occurrence_count".to_owned(),
1273                    crate::ErrorDetail::Long(1),
1274                ),
1275                (
1276                    "foreign_package".to_owned(),
1277                    crate::ErrorDetail::Boolean(false),
1278                ),
1279            ]),
1280        );
1281
1282        let diagnostic = std::error::Error::source(error)
1283            .and_then(|source| source.downcast_ref::<SdkExecutionDiagnostic>())
1284            .expect("the Rust error retains the exact common SDK diagnostic");
1285        assert!(matches!(
1286            diagnostic.path(),
1287            [
1288                SdkDiagnosticPathSegment::Argument(argument),
1289                SdkDiagnosticPathSegment::Index(0),
1290                SdkDiagnosticPathSegment::ContractIdentity(identity),
1291            ] if argument.as_str() == "projection_evidence"
1292                && identity.as_str() == "semantic_schema_fingerprint"
1293        ));
1294        assert_eq!(
1295            diagnostic
1296                .details()
1297                .iter()
1298                .map(|(name, value)| (name.as_str(), value))
1299                .collect::<Vec<_>>(),
1300            vec![
1301                (
1302                    "actual_occurrence_count",
1303                    &SdkDiagnosticDetailValue::Count(0),
1304                ),
1305                (
1306                    "expected_occurrence_count",
1307                    &SdkDiagnosticDetailValue::Count(1),
1308                ),
1309                ("foreign_package", &SdkDiagnosticDetailValue::Boolean(false),),
1310            ],
1311        );
1312    }
1313
1314    #[test]
1315    fn compiled_authority_is_fully_verified_and_bound_to_projection() {
1316        let package = generated_package(
1317            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1318            "rust-authority-test",
1319        );
1320        let (projection, authority) = package.verify_and_install_with_authority().unwrap();
1321        let authority = authority.expect("generated package has compiled authority");
1322        assert_eq!(
1323            projection.projection().semantic_fingerprint(),
1324            authority.resolved_schema().semantic_fingerprint(),
1325        );
1326
1327        let foreign = generated_package(
1328            "format: typebridge.schema/v2\nentities:\n  organization: {}\n",
1329            "rust-authority-test",
1330        );
1331        let mismatched: SchemaPackage<TestSchema> = SchemaPackage::new_with_authority(
1332            package.semantic_fingerprint_json,
1333            package.projection_fingerprint_json,
1334            package.runtime_projection_json,
1335            foreign.schema_authority_json.unwrap(),
1336            foreign.declared_schema_json.unwrap(),
1337            foreign.managed_scope_id.unwrap(),
1338            foreign.semantic_profile_id.unwrap(),
1339        );
1340        let error = mismatched
1341            .verify()
1342            .expect_err("foreign semantic authority must not bind to the projection");
1343        assert_projection_evidence_mismatch(&error);
1344    }
1345
1346    #[test]
1347    fn ordered_package_requires_exact_successor_handler_and_compiled_authority() {
1348        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";
1349
1350        let valid = generated_package(ORDERED, "rust-ordered-evidence");
1351        let installed = valid.verify_and_install().unwrap();
1352        assert_eq!(
1353            installed.projection().generator_handlers(),
1354            [type_bridge_contract::projection::ProjectionHandler::rust_v2()],
1355        );
1356        let resource_ids = installed
1357            .projection()
1358            .code_resources()
1359            .iter()
1360            .map(|resource| resource.id().as_str())
1361            .collect::<Vec<_>>();
1362        assert_eq!(
1363            resource_ids,
1364            [
1365                "typebridge.generator.rust.cargo-toml",
1366                "typebridge.generator.rust.runtime-source",
1367            ],
1368        );
1369        assert_ne!(
1370            installed.projection().code_resources(),
1371            RustEmitter::new().code_resources().unwrap(),
1372            "the ordered package must carry the successor runtime-resource digest",
1373        );
1374
1375        let legacy = package_with_evidence(
1376            ORDERED,
1377            "rust-ordered-evidence",
1378            Some(vec![
1379                type_bridge_contract::projection::ProjectionHandler::rust_v1(),
1380            ]),
1381            Some(RustEmitter::new().code_resources().unwrap()),
1382        );
1383        let error = legacy
1384            .verify()
1385            .expect_err("an ordered package cannot claim the legacy Rust ledger");
1386        assert_projection_evidence_mismatch(&error);
1387
1388        let authorityless = without_authority(valid);
1389        let error = authorityless
1390            .verify()
1391            .expect_err("ordered packages require reconstructable schema authority");
1392        assert_projection_evidence_mismatch(&error);
1393
1394        let diagnostic = authorityless
1395            .generated_projection_validator()
1396            .err()
1397            .expect("ordered successor validation must reject the detached package");
1398        assert_eq!(diagnostic.code(), "projection_evidence_mismatch");
1399        assert_eq!(diagnostic.field(), "projection_evidence");
1400    }
1401
1402    #[test]
1403    fn package_rejects_missing_forged_and_foreign_resource_evidence() {
1404        const UNORDERED: &str = "format: typebridge.schema/v2\nentities:\n  person: {}\n";
1405        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";
1406
1407        let missing = package_with_evidence(
1408            ORDERED,
1409            "rust-resource-evidence",
1410            Some(vec![
1411                type_bridge_contract::projection::ProjectionHandler::rust_v2(),
1412            ]),
1413            Some(Vec::new()),
1414        );
1415        let error = missing
1416            .verify()
1417            .expect_err("ordered resource evidence is mandatory");
1418        assert_projection_evidence_mismatch(&error);
1419
1420        let emitter = RustEmitter::new();
1421        let documents =
1422            SchemaDocumentSet::parse([(DocumentId::new("forged-resource.yaml").unwrap(), ORDERED)])
1423                .unwrap();
1424        let declared = normalize_documents(&documents).unwrap();
1425        let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1426        let resolved = resolve(&declared, &profile).unwrap();
1427        let mut forged_resources = emitter.code_resources_for(&resolved).unwrap();
1428        let first_id = forged_resources[0].id().as_str().to_owned();
1429        forged_resources[0] = type_bridge_contract::projection::CodeResourceDigest::from_bytes(
1430            first_id,
1431            b"forged Rust emitter resource",
1432        )
1433        .unwrap();
1434        let forged = package_with_evidence(
1435            ORDERED,
1436            "rust-resource-evidence",
1437            Some(emitter.generator_handlers_for(&resolved)),
1438            Some(forged_resources),
1439        );
1440        let error = forged
1441            .verify()
1442            .expect_err("self-consistent forged resource evidence must reject");
1443        assert_projection_evidence_mismatch(&error);
1444
1445        let foreign_resources = type_bridge_schema_codegen::PythonEmitter::new()
1446            .code_resources_for(&resolved)
1447            .unwrap();
1448        let foreign = package_with_evidence(
1449            ORDERED,
1450            "rust-resource-evidence",
1451            Some(emitter.generator_handlers_for(&resolved)),
1452            Some(foreign_resources),
1453        );
1454        let error = foreign
1455            .verify()
1456            .expect_err("foreign binding resource evidence must reject");
1457        assert_projection_evidence_mismatch(&error);
1458
1459        let valid_legacy = generated_package(UNORDERED, "rust-resource-evidence");
1460        let installed_legacy = valid_legacy.verify_and_install().unwrap();
1461        assert_eq!(
1462            installed_legacy.projection().generator_handlers(),
1463            [type_bridge_contract::projection::ProjectionHandler::rust_v1()],
1464        );
1465        assert_eq!(
1466            installed_legacy.projection().code_resources(),
1467            RustEmitter::new().code_resources().unwrap(),
1468        );
1469        assert!(without_authority(valid_legacy).verify().is_ok());
1470    }
1471
1472    #[test]
1473    fn successor_admission_classifies_only_absent_detached_semantic_fingerprint() {
1474        let package = generated_package(
1475            "format: typebridge.schema/v2\nattributes:\n  tag: { value: string }\nentities:\n  person:\n    owns:\n      tag:\n        card: 1\n        ordered: true\n",
1476            "rust-missing-semantic-evidence",
1477        );
1478        let malformed = with_semantic_fingerprint(package, "{")
1479            .verify()
1480            .expect_err("nonempty malformed semantic evidence must reject");
1481        assert_projection_evidence_mismatch(&malformed);
1482
1483        let error = with_semantic_fingerprint(package, "")
1484            .verify()
1485            .expect_err("an absent detached semantic fingerprint must reject");
1486        assert_missing_semantic_fingerprint(&error);
1487    }
1488
1489    #[test]
1490    fn package_rejects_stale_and_reordered_evidence() {
1491        const UNORDERED: &str = "format: typebridge.schema/v2\nentities:\n  person: {}\n";
1492        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";
1493
1494        let emitter = RustEmitter::new();
1495        let ordered_documents =
1496            SchemaDocumentSet::parse([(DocumentId::new("stale-resource.yaml").unwrap(), ORDERED)])
1497                .unwrap();
1498        let ordered_declared = normalize_documents(&ordered_documents).unwrap();
1499        let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1500        let ordered_resolved = resolve(&ordered_declared, &profile).unwrap();
1501        let stale_successor = package_with_evidence(
1502            UNORDERED,
1503            "rust-stale-evidence",
1504            Some(emitter.generator_handlers_for(&ordered_resolved)),
1505            Some(emitter.code_resources_for(&ordered_resolved).unwrap()),
1506        );
1507        let error = stale_successor
1508            .verify()
1509            .expect_err("an unordered package cannot claim successor evidence");
1510        assert_projection_evidence_mismatch(&error);
1511
1512        let ordered = generated_package(ORDERED, "rust-reordered-evidence");
1513        let mut runtime: Value = serde_json::from_str(ordered.runtime_projection_json).unwrap();
1514        runtime["code_resources"]
1515            .as_array_mut()
1516            .expect("runtime projection has a resource ledger")
1517            .swap(0, 1);
1518        let reordered = with_runtime_projection(ordered, canonical(&runtime));
1519        let error = reordered
1520            .verify()
1521            .expect_err("resource ledger wire order is canonical and cannot be changed");
1522        assert_projection_evidence_mismatch(&error);
1523    }
1524
1525    #[test]
1526    fn authority_backed_admission_normalizes_malformed_extra_and_duplicate_evidence() {
1527        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";
1528        let package = generated_package(ORDERED, "rust-malformed-evidence");
1529
1530        let malformed = with_envelope(package, "{")
1531            .verify()
1532            .expect_err("malformed authority evidence must reject");
1533        assert_projection_evidence_mismatch(&malformed);
1534
1535        let runtime: Value = serde_json::from_str(package.runtime_projection_json).unwrap();
1536        let resource = runtime["code_resources"]
1537            .as_array()
1538            .and_then(|resources| resources.first())
1539            .cloned()
1540            .expect("successor projection carries resource evidence");
1541
1542        let mut duplicate_runtime = runtime.clone();
1543        duplicate_runtime["code_resources"]
1544            .as_array_mut()
1545            .unwrap()
1546            .push(resource.clone());
1547        let duplicate = with_runtime_projection(package, canonical(&duplicate_runtime))
1548            .verify()
1549            .expect_err("duplicate resource evidence must reject");
1550        assert_projection_evidence_mismatch(&duplicate);
1551
1552        let mut extra_resource = resource;
1553        extra_resource["id"] = Value::String("typebridge.generator.rust.zzz-extra".into());
1554        let mut extra_runtime = runtime;
1555        extra_runtime["code_resources"]
1556            .as_array_mut()
1557            .unwrap()
1558            .push(extra_resource);
1559        let extra = with_runtime_projection(package, canonical(&extra_runtime))
1560            .verify()
1561            .expect_err("extra resource evidence must reject");
1562        assert_projection_evidence_mismatch(&extra);
1563    }
1564
1565    #[test]
1566    fn compiled_authority_rejects_missing_and_stale_outer_fingerprints() {
1567        let package = generated_package(
1568            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1569            "rust-authority-test",
1570        );
1571        let original: Value = serde_json::from_str(package.schema_authority_json.unwrap()).unwrap();
1572
1573        let mut missing = original.clone();
1574        missing
1575            .as_object_mut()
1576            .unwrap()
1577            .remove("authority_fingerprint");
1578        let error = with_envelope(package, canonical(&missing))
1579            .verify()
1580            .expect_err("missing authority fingerprint must reject");
1581        assert_projection_evidence_mismatch(&error);
1582
1583        let mut stale = original;
1584        stale["authority_fingerprint"]["digest"] = "0".repeat(64).into();
1585        let error = with_envelope(package, canonical(&stale))
1586            .verify()
1587            .expect_err("stale authority fingerprint must reject");
1588        assert_projection_evidence_mismatch(&error);
1589    }
1590
1591    #[test]
1592    fn compiled_authority_reconstructs_managed_state_and_rejects_unsupported_claims() {
1593        let package = generated_package(
1594            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1595            "rust-authority-test",
1596        );
1597        let original: Value = serde_json::from_str(package.schema_authority_json.unwrap()).unwrap();
1598
1599        let mut managed = original.clone();
1600        managed["content"]["managed_state"]["declared_identity"]["digest"] = "0".repeat(64).into();
1601        resign(&mut managed);
1602        let error = with_envelope(package, canonical(&managed))
1603            .verify()
1604            .expect_err("detached managed-state evidence must reject");
1605        assert_projection_evidence_mismatch(&error);
1606
1607        let mut capabilities = original.clone();
1608        capabilities["content"]["required_capabilities"] =
1609            Value::Array(vec![Value::String("unsupported.runtime".into())]);
1610        resign(&mut capabilities);
1611        let error = with_envelope(package, canonical(&capabilities))
1612            .verify()
1613            .expect_err("unsupported artifact capability must fail closed");
1614        assert_projection_evidence_mismatch(&error);
1615
1616        let mut version = original;
1617        version["content"]["authority_version"] =
1618            Value::String("typebridge.schema-authority/v2".into());
1619        resign(&mut version);
1620        let error = with_envelope(package, canonical(&version))
1621            .verify()
1622            .expect_err("unsupported artifact version must fail closed");
1623        assert_projection_evidence_mismatch(&error);
1624    }
1625
1626    #[test]
1627    fn compiled_authority_rejects_oversize_and_detached_evidence() {
1628        let package = generated_package(
1629            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1630            "rust-authority-test",
1631        );
1632        let oversized = Box::leak(" ".repeat(MAX_SCHEMA_AUTHORITY_BYTES + 1).into_boxed_str());
1633        let error = with_envelope(package, oversized)
1634            .verify()
1635            .expect_err("oversize authority must fail before parsing");
1636        assert_projection_evidence_mismatch(&error);
1637
1638        let detached: SchemaPackage<TestSchema> = SchemaPackage::new_with_authority(
1639            package.semantic_fingerprint_json,
1640            package.projection_fingerprint_json,
1641            package.runtime_projection_json,
1642            package.schema_authority_json.unwrap(),
1643            package.declared_schema_json.unwrap(),
1644            "other-scope",
1645            package.semantic_profile_id.unwrap(),
1646        );
1647        let error = detached
1648            .verify()
1649            .expect_err("detached scope must not override compiled authority");
1650        assert_projection_evidence_mismatch(&error);
1651    }
1652
1653    #[test]
1654    fn schema_package_fingerprint_verification() {
1655        let documents = SchemaDocumentSet::parse([(
1656            DocumentId::new("test.yaml").unwrap(),
1657            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1658        )])
1659        .unwrap();
1660        let declared = normalize_documents(&documents).unwrap();
1661        let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1662        let resolved = resolve(&declared, &profile).unwrap();
1663        let emitter = RustEmitter::new();
1664        let resources = emitter.code_resources().unwrap();
1665        let projection = project(
1666            &resolved,
1667            BindingTarget::Rust,
1668            &ProjectionConfig::rust(),
1669            &emitter.generator_handlers(),
1670            &resources,
1671        )
1672        .unwrap();
1673
1674        let semantic_json =
1675            String::from_utf8(to_canonical_json(projection.semantic_fingerprint()).unwrap())
1676                .unwrap();
1677        let projection_json =
1678            String::from_utf8(to_canonical_json(projection.projection_fingerprint()).unwrap())
1679                .unwrap();
1680        let runtime_json = String::from_utf8(to_canonical_json(&projection).unwrap()).unwrap();
1681
1682        let semantic_ref: &'static str = Box::leak(semantic_json.into_boxed_str());
1683        let projection_ref: &'static str = Box::leak(projection_json.into_boxed_str());
1684        let runtime_ref: &'static str = Box::leak(runtime_json.into_boxed_str());
1685
1686        let valid_package: SchemaPackage<TestSchema> =
1687            SchemaPackage::new(semantic_ref, projection_ref, runtime_ref);
1688        assert!(valid_package.verify().is_ok());
1689
1690        let tampered_package: SchemaPackage<TestSchema> =
1691            SchemaPackage::new(semantic_ref, r#""rust/v1-tampered""#, runtime_ref);
1692        match tampered_package.verify() {
1693            Err(err) => {
1694                use std::error::Error as _;
1695                assert_eq!(err.category(), crate::ErrorCategory::Schema);
1696                assert_eq!(err.code(), None);
1697                assert_eq!(err.path(), None);
1698                assert!(err.source().is_some());
1699            }
1700            Ok(_) => panic!("tampered schema package must fail verification"),
1701        }
1702    }
1703
1704    #[test]
1705    fn rejects_non_rust_target_projection() {
1706        let documents = SchemaDocumentSet::parse([(
1707            DocumentId::new("test.yaml").unwrap(),
1708            "format: typebridge.schema/v2\nentities:\n  person: {}\n",
1709        )])
1710        .unwrap();
1711        let declared = normalize_documents(&documents).unwrap();
1712        let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1713        let resolved = resolve(&declared, &profile).unwrap();
1714        let py_emitter = PythonEmitter::new();
1715        let py_resources = py_emitter.code_resources().unwrap();
1716        let py_projection = project(
1717            &resolved,
1718            BindingTarget::Python,
1719            &ProjectionConfig::python(),
1720            &py_emitter.generator_handlers(),
1721            &py_resources,
1722        )
1723        .unwrap();
1724
1725        let semantic_json =
1726            String::from_utf8(to_canonical_json(py_projection.semantic_fingerprint()).unwrap())
1727                .unwrap();
1728        let projection_json =
1729            String::from_utf8(to_canonical_json(py_projection.projection_fingerprint()).unwrap())
1730                .unwrap();
1731        let runtime_json = String::from_utf8(to_canonical_json(&py_projection).unwrap()).unwrap();
1732
1733        let semantic_ref: &'static str = Box::leak(semantic_json.into_boxed_str());
1734        let projection_ref: &'static str = Box::leak(projection_json.into_boxed_str());
1735        let runtime_ref: &'static str = Box::leak(runtime_json.into_boxed_str());
1736
1737        let py_package: SchemaPackage<TestSchema> =
1738            SchemaPackage::new(semantic_ref, projection_ref, runtime_ref);
1739        let err = py_package.verify().unwrap_err();
1740        assert_eq!(err.category(), crate::ErrorCategory::Schema);
1741        assert_eq!(err.code(), None);
1742        assert_eq!(err.path(), None);
1743        assert!(err.to_string().contains("target mismatch") || err.to_string().contains("Rust"));
1744    }
1745}