Skip to main content

icydb_core/error/
mod.rs

1//! Module: error
2//!
3//! Defines the canonical runtime error taxonomy for `icydb-core`.
4//! This module owns the shared error classes, origins, details, and
5//! constructor entry points used across storage, planning, execution, and
6//! serialization boundaries.
7
8#[cfg(test)]
9mod tests;
10
11use candid::CandidType;
12use icydb_diagnostic_code as diagnostic_code;
13use serde::Deserialize;
14use std::fmt;
15
16pub(crate) const COMPACT_QUERY_DIAGNOSTIC_MESSAGE: &str = "query diagnostic";
17const COMPACT_RUNTIME_DIAGNOSTIC_MESSAGE: &str = "runtime diagnostic";
18const COMPACT_STORE_DIAGNOSTIC_MESSAGE: &str = "store diagnostic";
19const COMPACT_INDEX_DIAGNOSTIC_MESSAGE: &str = "index diagnostic";
20const COMPACT_SERIALIZE_DIAGNOSTIC_MESSAGE: &str = "serialize diagnostic";
21const COMPACT_IDENTITY_DIAGNOSTIC_MESSAGE: &str = "identity diagnostic";
22
23const fn compact_message_for(_class: ErrorClass, origin: ErrorOrigin) -> &'static str {
24    match origin {
25        ErrorOrigin::Serialize => COMPACT_SERIALIZE_DIAGNOSTIC_MESSAGE,
26        ErrorOrigin::Store => COMPACT_STORE_DIAGNOSTIC_MESSAGE,
27        ErrorOrigin::Index => COMPACT_INDEX_DIAGNOSTIC_MESSAGE,
28        ErrorOrigin::Identity => COMPACT_IDENTITY_DIAGNOSTIC_MESSAGE,
29        ErrorOrigin::Query | ErrorOrigin::Planner | ErrorOrigin::Response => {
30            COMPACT_QUERY_DIAGNOSTIC_MESSAGE
31        }
32        ErrorOrigin::Cursor
33        | ErrorOrigin::Recovery
34        | ErrorOrigin::Executor
35        | ErrorOrigin::Interface => COMPACT_RUNTIME_DIAGNOSTIC_MESSAGE,
36    }
37}
38
39// ============================================================================
40// INTERNAL ERROR TAXONOMY — ARCHITECTURAL CONTRACT
41// ============================================================================
42//
43// This file defines the canonical runtime error classification system for
44// icydb-core. It is the single source of truth for:
45//
46//   • ErrorClass   (semantic domain)
47//   • ErrorOrigin  (subsystem boundary)
48//   • Structured detail payloads
49//   • Canonical constructor entry points
50//
51// -----------------------------------------------------------------------------
52// DESIGN INTENT
53// -----------------------------------------------------------------------------
54//
55// 1. InternalError is a *taxonomy carrier*, not a formatting utility.
56//
57//    - ErrorClass represents semantic meaning (corruption, invariant_violation,
58//      unsupported, etc).
59//    - ErrorOrigin represents the subsystem boundary (store, index, query,
60//      executor, serialize, interface, etc).
61//    - The (class, origin) pair must remain stable and intentional.
62//
63// 2. Call sites MUST prefer canonical constructors.
64//
65//    Do NOT construct errors manually via:
66//        InternalError::new(class, origin)
67//    unless you are defining a new canonical helper here.
68//
69//    If a pattern appears more than once, centralize it here.
70//
71// 3. Constructors in this file must represent real architectural boundaries.
72//
73//    Add a new helper ONLY if it:
74//
75//      • Encodes a cross-cutting invariant,
76//      • Represents a subsystem boundary,
77//      • Or prevents taxonomy drift across call sites.
78//
79//    Do NOT add feature-specific helpers.
80//    Do NOT add one-off formatting helpers.
81//    Do NOT turn this file into a generic message factory.
82//
83// 4. ErrorDetail must align with ErrorOrigin.
84//
85//    If detail is present, it MUST correspond to the origin.
86//    Do not attach mismatched detail variants.
87//
88// 5. Plan-layer errors are NOT runtime failures.
89//
90//    PlanError and CursorPlanError must be translated into
91//    executor/query invariants via the canonical mapping functions.
92//    Do not leak plan-layer error types across execution boundaries.
93//
94// 6. Preserve taxonomy stability.
95//
96//    Do NOT:
97//      • Merge error classes.
98//      • Reclassify corruption as internal.
99//      • Downgrade invariant violations.
100//      • Introduce ambiguous class/origin combinations.
101//
102//    Any change to ErrorClass or ErrorOrigin is an architectural change
103//    and must be reviewed accordingly.
104//
105// -----------------------------------------------------------------------------
106// NON-GOALS
107// -----------------------------------------------------------------------------
108//
109// This is NOT:
110//
111//   • A public API contract.
112//   • A generic error abstraction layer.
113//   • A feature-specific message builder.
114//   • A dumping ground for temporary error conversions.
115//
116// -----------------------------------------------------------------------------
117// MAINTENANCE GUIDELINES
118// -----------------------------------------------------------------------------
119//
120// When modifying this file:
121//
122//   1. Ensure classification semantics remain consistent.
123//   2. Avoid constructor proliferation.
124//   3. Prefer narrow, origin-specific helpers over ad-hoc new(...).
125//   4. Keep formatting minimal and standardized.
126//   5. Keep this file boring and stable.
127//
128// If this file grows rapidly, something is wrong at the call sites.
129//
130// ============================================================================
131
132/// Fixed-size accepted mutation identity carried through admission and staging.
133/// Numeric fact vectors are allocated only when constructing a failure.
134#[derive(Clone, Copy, Debug)]
135pub(crate) struct MutationDiagnosticContext {
136    fingerprint_method: u8,
137    accepted_schema_fingerprint: [u8; 16],
138    entity_tag: u64,
139    operation: diagnostic_code::DiagnosticMutationOperation,
140    batch_position: Option<u32>,
141}
142
143impl MutationDiagnosticContext {
144    /// Bind a mutation to its accepted schema, entity, operation, and input.
145    #[must_use]
146    pub(crate) const fn new(
147        fingerprint_method: u8,
148        accepted_schema_fingerprint: [u8; 16],
149        entity_tag: u64,
150        operation: diagnostic_code::DiagnosticMutationOperation,
151        batch_position: u32,
152    ) -> Self {
153        Self {
154            fingerprint_method,
155            accepted_schema_fingerprint,
156            entity_tag,
157            operation,
158            batch_position: Some(batch_position),
159        }
160    }
161
162    /// Bind a failure to an operation before any concrete input row is selected.
163    #[must_use]
164    pub(crate) const fn operation_only(
165        fingerprint_method: u8,
166        accepted_schema_fingerprint: [u8; 16],
167        entity_tag: u64,
168        operation: diagnostic_code::DiagnosticMutationOperation,
169    ) -> Self {
170        Self {
171            fingerprint_method,
172            accepted_schema_fingerprint,
173            entity_tag,
174            operation,
175            batch_position: None,
176        }
177    }
178
179    fn facts(self, field_id: Option<u32>) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
180        let mut facts = Vec::with_capacity(
181            5 + usize::from(field_id.is_some()) + usize::from(self.batch_position.is_some()),
182        );
183        append_accepted_schema_facts(
184            &mut facts,
185            self.fingerprint_method,
186            self.accepted_schema_fingerprint,
187        );
188        facts.push((
189            diagnostic_code::DiagnosticFactTag::EntityTag,
190            self.entity_tag,
191        ));
192        if let Some(field_id) = field_id {
193            facts.push((
194                diagnostic_code::DiagnosticFactTag::FieldId,
195                u64::from(field_id),
196            ));
197        }
198        self.append_operation_facts(&mut facts);
199        facts
200    }
201
202    #[must_use]
203    pub(crate) const fn entity_tag(self) -> u64 {
204        self.entity_tag
205    }
206
207    fn append_operation_facts(self, facts: &mut Vec<(diagnostic_code::DiagnosticFactTag, u64)>) {
208        facts.push((
209            diagnostic_code::DiagnosticFactTag::MutationOperation,
210            self.operation.raw(),
211        ));
212        if let Some(batch_position) = self.batch_position {
213            facts.push((
214                diagnostic_code::DiagnosticFactTag::BatchPosition,
215                u64::from(batch_position),
216            ));
217        }
218    }
219}
220
221/// Numeric context retained behind one thin error-only allocation.
222pub struct DiagnosticFactDetail {
223    diagnostic: diagnostic_code::Diagnostic,
224    facts: Vec<(diagnostic_code::DiagnosticFactTag, u64)>,
225}
226
227///
228/// InternalError
229///
230/// Structured runtime error with a stable internal classification.
231/// Not a stable API; intended for internal use and may change without notice.
232///
233
234pub struct InternalError {
235    pub(crate) class: ErrorClass,
236    pub(crate) origin: ErrorOrigin,
237
238    /// Optional structured error detail.
239    /// The variant (if present) must correspond to `origin`.
240    pub(crate) detail: Option<ErrorDetail>,
241}
242
243#[expect(
244    clippy::missing_const_for_fn,
245    reason = "internal error constructors stay non-const so compact diagnostic construction does not force const churn across subsystem helper seams"
246)]
247impl InternalError {
248    /// Construct an InternalError with optional origin-specific detail.
249    /// This constructor provides default StoreError details for certain
250    /// (class, origin) combinations but does not guarantee a detail payload.
251    #[must_use]
252    #[cold]
253    #[inline(never)]
254    pub fn new(class: ErrorClass, origin: ErrorOrigin) -> Self {
255        let detail = match (class, origin) {
256            (ErrorClass::Corruption, ErrorOrigin::Store) => {
257                Some(ErrorDetail::Store(StoreError::Corrupt))
258            }
259            (ErrorClass::InvariantViolation, ErrorOrigin::Store) => {
260                Some(ErrorDetail::Store(StoreError::InvariantViolation))
261            }
262            _ => None,
263        };
264
265        Self {
266            class,
267            origin,
268            detail,
269        }
270    }
271
272    /// Return the internal error class taxonomy.
273    #[must_use]
274    pub const fn class(&self) -> ErrorClass {
275        self.class
276    }
277
278    /// Return the internal error origin taxonomy.
279    #[must_use]
280    pub const fn origin(&self) -> ErrorOrigin {
281        self.origin
282    }
283
284    /// Return the rendered internal error message.
285    #[must_use]
286    pub const fn message(&self) -> &'static str {
287        compact_message_for(self.class, self.origin)
288    }
289
290    /// Return the optional structured detail payload.
291    #[must_use]
292    pub const fn detail(&self) -> Option<&ErrorDetail> {
293        self.detail.as_ref()
294    }
295
296    /// Return compact diagnostic identity for this internal error.
297    #[must_use]
298    pub fn diagnostic(&self) -> diagnostic_code::Diagnostic {
299        diagnostic_code::Diagnostic::new(
300            self.diagnostic_code(),
301            self.origin.diagnostic_origin(),
302            self.detail
303                .as_ref()
304                .and_then(ErrorDetail::diagnostic_detail),
305        )
306    }
307
308    /// Project typed internal context into canonical public numeric facts.
309    #[must_use]
310    #[cold]
311    #[inline(never)]
312    pub fn diagnostic_facts(&self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
313        self.detail
314            .as_ref()
315            .map_or_else(Vec::new, ErrorDetail::diagnostic_facts)
316    }
317
318    /// Return the compact diagnostic code for this internal error.
319    #[must_use]
320    pub fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
321        self.detail.as_ref().map_or_else(
322            || self.class.diagnostic_code(self.origin),
323            ErrorDetail::diagnostic_code,
324        )
325    }
326
327    /// Consume and return the rendered internal error message.
328    #[must_use]
329    pub fn into_message(self) -> String {
330        self.message().to_string()
331    }
332
333    /// Construct an error while preserving an explicit class/origin taxonomy pair.
334    #[cold]
335    #[inline(never)]
336    pub(crate) fn classified(class: ErrorClass, origin: ErrorOrigin) -> Self {
337        Self::new(class, origin)
338    }
339
340    #[cold]
341    #[inline(never)]
342    fn with_diagnostic_facts(
343        class: ErrorClass,
344        origin: ErrorOrigin,
345        detail: Option<diagnostic_code::DiagnosticDetail>,
346        facts: Vec<(diagnostic_code::DiagnosticFactTag, u64)>,
347    ) -> Self {
348        let code = match detail {
349            Some(detail) => detail.diagnostic_code(),
350            None => class.diagnostic_code(origin),
351        };
352        let diagnostic = diagnostic_code::Diagnostic::new(code, origin.diagnostic_origin(), detail);
353        if diagnostic_code::validate_known_diagnostic_fact_schema(
354            diagnostic.error_code(),
355            facts.as_slice(),
356        )
357        .is_err()
358        {
359            return Self::new(ErrorClass::InvariantViolation, origin);
360        }
361        Self {
362            class,
363            origin,
364            detail: Some(ErrorDetail::DiagnosticFacts(Box::new(
365                DiagnosticFactDetail { diagnostic, facts },
366            ))),
367        }
368    }
369
370    #[cold]
371    #[inline(never)]
372    fn mutation_boundary_with_facts(
373        class: ErrorClass,
374        boundary: diagnostic_code::RuntimeBoundaryCode,
375        facts: Vec<(diagnostic_code::DiagnosticFactTag, u64)>,
376    ) -> Self {
377        Self::with_diagnostic_facts(
378            class,
379            ErrorOrigin::Executor,
380            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary { boundary }),
381            facts,
382        )
383    }
384
385    #[cold]
386    #[inline(never)]
387    fn exact_key_batch_boundary_with_facts(
388        boundary: diagnostic_code::RuntimeBoundaryCode,
389        facts: Vec<(diagnostic_code::DiagnosticFactTag, u64)>,
390    ) -> Self {
391        Self::with_diagnostic_facts(
392            ErrorClass::Unsupported,
393            ErrorOrigin::Query,
394            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary { boundary }),
395            facts,
396        )
397    }
398
399    /// Construct a query-boundary error for a named entity absent from accepted schema authority.
400    pub(crate) fn sql_query_entity_not_found() -> Self {
401        Self::with_diagnostic_facts(
402            ErrorClass::NotFound,
403            ErrorOrigin::Interface,
404            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
405                boundary: diagnostic_code::RuntimeBoundaryCode::SqlQueryEntityNotFound,
406            }),
407            Vec::new(),
408        )
409    }
410
411    /// Construct an executor-origin hard execution-budget rejection.
412    #[cold]
413    #[inline(never)]
414    pub(crate) fn execution_budget_exceeded(
415        resource: diagnostic_code::DiagnosticExecutionBudgetResource,
416        limit: u64,
417        observed: u64,
418        scope: diagnostic_code::DiagnosticExecutionBudgetScope,
419        lane: diagnostic_code::DiagnosticExecutionLane,
420        normalized_shape_fingerprint_prefix: u64,
421    ) -> Self {
422        Self::with_diagnostic_facts(
423            ErrorClass::Unsupported,
424            ErrorOrigin::Executor,
425            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
426                boundary: diagnostic_code::RuntimeBoundaryCode::ExecutionBudgetExceeded,
427            }),
428            vec![
429                (
430                    diagnostic_code::DiagnosticFactTag::BudgetResource,
431                    resource.raw(),
432                ),
433                (diagnostic_code::DiagnosticFactTag::Limit, limit),
434                (diagnostic_code::DiagnosticFactTag::Actual, observed),
435                (
436                    diagnostic_code::DiagnosticFactTag::ExecutionBudgetScope,
437                    scope.raw(),
438                ),
439                (
440                    diagnostic_code::DiagnosticFactTag::ExecutionLane,
441                    lane.raw(),
442                ),
443                (
444                    diagnostic_code::DiagnosticFactTag::QueryShapeFingerprintPrefix,
445                    normalized_shape_fingerprint_prefix,
446                ),
447            ],
448        )
449    }
450
451    /// Construct a deterministic mutation relation-budget rejection.
452    #[cold]
453    #[inline(never)]
454    pub(crate) fn relation_budget_exceeded(
455        resource: diagnostic_code::DiagnosticExecutionBudgetResource,
456        limit: u64,
457        observed: u64,
458    ) -> Self {
459        Self::execution_budget_exceeded(
460            resource,
461            limit,
462            observed,
463            diagnostic_code::DiagnosticExecutionBudgetScope::Execution,
464            diagnostic_code::DiagnosticExecutionLane::Mutation,
465            0,
466        )
467    }
468
469    /// Construct an executor-origin rejection for one indivisible page unit.
470    #[cold]
471    #[inline(never)]
472    pub(crate) fn page_unit_too_large(
473        resource: diagnostic_code::DiagnosticExecutionBudgetResource,
474        limit: u64,
475        attempted: u64,
476    ) -> Self {
477        Self::with_diagnostic_facts(
478            ErrorClass::Unsupported,
479            ErrorOrigin::Executor,
480            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
481                boundary: diagnostic_code::RuntimeBoundaryCode::PageUnitTooLarge,
482            }),
483            vec![
484                (
485                    diagnostic_code::DiagnosticFactTag::BudgetResource,
486                    resource.raw(),
487                ),
488                (diagnostic_code::DiagnosticFactTag::Limit, limit),
489                (diagnostic_code::DiagnosticFactTag::Actual, attempted),
490            ],
491        )
492    }
493
494    /// Rebuild this error with a new origin while preserving class taxonomy.
495    ///
496    /// Numeric facts are origin-independent and remain safe after recovery
497    /// relabeling. Other origin-scoped detail payloads are dropped.
498    #[cold]
499    #[inline(never)]
500    pub(crate) fn with_origin(self, origin: ErrorOrigin) -> Self {
501        match self.detail {
502            Some(ErrorDetail::DiagnosticFacts(detail)) => Self::with_diagnostic_facts(
503                self.class,
504                origin,
505                detail.diagnostic.detail().copied(),
506                detail.facts,
507            ),
508            _ => Self::classified(self.class, origin),
509        }
510    }
511
512    /// Construct an index-origin invariant violation.
513    #[cold]
514    #[inline(never)]
515    pub(crate) fn index_invariant() -> Self {
516        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Index)
517    }
518
519    /// Construct the canonical index field-count invariant for key building.
520    pub(crate) fn index_key_field_count_exceeds_max(
521        entity_tag: u64,
522        physical_generation: u64,
523        field_count: usize,
524        max_fields: usize,
525    ) -> Self {
526        Self::with_diagnostic_facts(
527            ErrorClass::InvariantViolation,
528            ErrorOrigin::Index,
529            None,
530            vec![
531                (diagnostic_code::DiagnosticFactTag::EntityTag, entity_tag),
532                (
533                    diagnostic_code::DiagnosticFactTag::PhysicalGeneration,
534                    physical_generation,
535                ),
536                (
537                    diagnostic_code::DiagnosticFactTag::ComponentKind,
538                    diagnostic_code::DiagnosticComponentKind::IndexKey.raw(),
539                ),
540                (
541                    diagnostic_code::DiagnosticFactTag::ActualArity,
542                    field_count as u64,
543                ),
544                (
545                    diagnostic_code::DiagnosticFactTag::Maximum,
546                    max_fields as u64,
547                ),
548            ],
549        )
550    }
551
552    /// Construct the canonical index-expression source-type mismatch invariant.
553    pub(crate) fn index_expression_source_type_mismatch(
554        _index_name: &str,
555        _expression: impl Sized,
556        _expected: impl Sized,
557        _source_label: &str,
558    ) -> Self {
559        Self::index_invariant()
560    }
561
562    /// Construct a planner-origin invariant violation for executor-boundary
563    /// contract drift.
564    #[cold]
565    #[inline(never)]
566    pub(crate) fn planner_executor_invariant() -> Self {
567        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Planner)
568    }
569
570    /// Construct a query-origin invariant violation for executor-boundary
571    /// contract drift.
572    #[cold]
573    #[inline(never)]
574    pub(crate) fn query_executor_invariant() -> Self {
575        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Query)
576    }
577
578    /// Construct a cursor-origin invariant violation for executor-boundary
579    /// contract drift.
580    #[cold]
581    #[inline(never)]
582    pub(crate) fn cursor_executor_invariant() -> Self {
583        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Cursor)
584    }
585
586    /// Construct an executor-origin invariant violation.
587    #[cold]
588    #[inline(never)]
589    pub(crate) fn executor_invariant() -> Self {
590        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Executor)
591    }
592
593    /// Construct an executor-origin internal error.
594    #[cold]
595    #[inline(never)]
596    pub(crate) fn executor_internal() -> Self {
597        Self::new(ErrorClass::Internal, ErrorOrigin::Executor)
598    }
599
600    /// Construct an executor-origin unsupported error.
601    #[cold]
602    #[inline(never)]
603    pub(crate) fn executor_unsupported() -> Self {
604        Self::new(ErrorClass::Unsupported, ErrorOrigin::Executor)
605    }
606
607    /// Construct an executor-origin database-owned-field authorship rejection.
608    #[cold]
609    #[inline(never)]
610    pub(crate) fn mutation_database_owned_field_explicit(
611        context: MutationDiagnosticContext,
612        field_id: u32,
613    ) -> Self {
614        Self::mutation_boundary_with_facts(
615            ErrorClass::Unsupported,
616            diagnostic_code::RuntimeBoundaryCode::MutationDatabaseOwnedFieldExplicit,
617            context.facts(Some(field_id)),
618        )
619    }
620
621    /// Construct an executor-origin required-field omission rejection.
622    #[must_use]
623    #[cold]
624    #[inline(never)]
625    pub(crate) fn mutation_required_field_missing(
626        context: MutationDiagnosticContext,
627        field_id: u32,
628    ) -> Self {
629        Self::mutation_boundary_with_facts(
630            ErrorClass::Unsupported,
631            diagnostic_code::RuntimeBoundaryCode::MutationRequiredFieldMissing,
632            context.facts(Some(field_id)),
633        )
634    }
635
636    /// Construct an executor-origin managed-timestamp clock regression.
637    #[must_use]
638    #[cold]
639    #[inline(never)]
640    pub(crate) fn mutation_managed_timestamp_regression(
641        context: MutationDiagnosticContext,
642    ) -> Self {
643        Self::mutation_boundary_with_facts(
644            ErrorClass::InvariantViolation,
645            diagnostic_code::RuntimeBoundaryCode::MutationManagedTimestampRegression,
646            context.facts(None),
647        )
648    }
649
650    /// Construct an executor-origin accepted constraint or activation-gate violation.
651    pub(crate) fn mutation_constraint_violation(context: AcceptedConstraintFactContext) -> Self {
652        Self::mutation_boundary_with_facts(
653            ErrorClass::InvariantViolation,
654            diagnostic_code::RuntimeBoundaryCode::ConstraintViolation,
655            context.facts(),
656        )
657    }
658
659    /// Construct an executor-origin corruption failure for row-constraint authority.
660    pub(crate) fn accepted_row_constraint_program_corrupt() -> Self {
661        Self {
662            class: ErrorClass::Corruption,
663            origin: ErrorOrigin::Executor,
664            detail: Some(ErrorDetail::Executor(
665                ExecutorErrorDetail::AcceptedRowConstraintProgramCorrupt,
666            )),
667        }
668    }
669
670    /// Construct one typed migration conflict for an incomplete activation gate.
671    pub(crate) fn mutation_constraint_activation_write_blocked(
672        context: AcceptedConstraintFactContext,
673    ) -> Self {
674        Self::mutation_boundary_with_facts(
675            ErrorClass::Conflict,
676            diagnostic_code::RuntimeBoundaryCode::ConstraintActivationWriteBlocked,
677            context.facts(),
678        )
679    }
680
681    /// Construct a query-origin scalar page invariant for missing order at the cursor boundary.
682    pub(crate) fn scalar_page_cursor_boundary_order_required() -> Self {
683        Self::query_executor_invariant()
684    }
685
686    /// Construct a query-origin scalar page invariant for cursor-before-ordering drift.
687    pub(crate) fn scalar_page_cursor_boundary_after_ordering_required() -> Self {
688        Self::query_executor_invariant()
689    }
690
691    /// Construct a query-origin scalar page invariant for pagination-before-ordering drift.
692    pub(crate) fn scalar_page_pagination_after_ordering_required() -> Self {
693        Self::query_executor_invariant()
694    }
695
696    /// Construct a query-origin scan invariant for missing index-prefix executable specs.
697    pub(crate) fn secondary_index_prefix_spec_required() -> Self {
698        Self::query_executor_invariant()
699    }
700
701    /// Construct a query-origin scan invariant for missing index-range executable specs.
702    pub(crate) fn index_range_limit_spec_required() -> Self {
703        Self::query_executor_invariant()
704    }
705
706    /// Construct an executor-origin mutation conflict for duplicate atomic save keys.
707    #[cold]
708    #[inline(never)]
709    pub(crate) fn mutation_atomic_save_duplicate_key(
710        entity_tag: u64,
711        first_position: u32,
712        duplicate_position: u32,
713    ) -> Self {
714        Self::mutation_boundary_with_facts(
715            ErrorClass::Conflict,
716            diagnostic_code::RuntimeBoundaryCode::MutationBatchDuplicateKey,
717            vec![
718                (diagnostic_code::DiagnosticFactTag::EntityTag, entity_tag),
719                (
720                    diagnostic_code::DiagnosticFactTag::FirstBatchPosition,
721                    u64::from(first_position),
722                ),
723                (
724                    diagnostic_code::DiagnosticFactTag::DuplicateBatchPosition,
725                    u64::from(duplicate_position),
726                ),
727            ],
728        )
729    }
730
731    /// Construct an executor-origin empty mixed-mutation batch rejection.
732    #[cold]
733    #[inline(never)]
734    pub(crate) fn mutation_batch_empty() -> Self {
735        Self::mutation_boundary_with_facts(
736            ErrorClass::Unsupported,
737            diagnostic_code::RuntimeBoundaryCode::MutationBatchEmpty,
738            vec![(diagnostic_code::DiagnosticFactTag::ActualCount, 0)],
739        )
740    }
741
742    /// Construct an executor-origin mixed-mutation item-bound rejection.
743    #[cold]
744    #[inline(never)]
745    pub(crate) fn mutation_batch_too_many_items(actual_count: usize, limit: usize) -> Self {
746        Self::mutation_boundary_with_facts(
747            ErrorClass::Unsupported,
748            diagnostic_code::RuntimeBoundaryCode::MutationBatchTooManyItems,
749            vec![
750                (
751                    diagnostic_code::DiagnosticFactTag::ActualCount,
752                    actual_count as u64,
753                ),
754                (diagnostic_code::DiagnosticFactTag::Limit, limit as u64),
755            ],
756        )
757    }
758
759    /// Construct an executor-origin mixed-mutation staged-byte-bound rejection.
760    #[cold]
761    #[inline(never)]
762    pub(crate) fn mutation_batch_staged_bytes_exceeded(
763        actual_bytes: Option<usize>,
764        limit: usize,
765    ) -> Self {
766        let mut facts = Vec::with_capacity(1 + usize::from(actual_bytes.is_some()));
767        if let Some(actual_bytes) = actual_bytes {
768            facts.push((
769                diagnostic_code::DiagnosticFactTag::ActualLength,
770                actual_bytes as u64,
771            ));
772        }
773        facts.push((diagnostic_code::DiagnosticFactTag::Limit, limit as u64));
774        Self::mutation_boundary_with_facts(
775            ErrorClass::Unsupported,
776            diagnostic_code::RuntimeBoundaryCode::MutationBatchStagedBytesExceeded,
777            facts,
778        )
779    }
780
781    /// Construct an executor-origin mixed-mutation result-byte-bound rejection.
782    #[cold]
783    #[inline(never)]
784    pub(crate) fn mutation_batch_result_bytes_exceeded(actual_bytes: usize, limit: usize) -> Self {
785        Self::mutation_boundary_with_facts(
786            ErrorClass::Unsupported,
787            diagnostic_code::RuntimeBoundaryCode::MutationBatchResultBytesExceeded,
788            vec![
789                (
790                    diagnostic_code::DiagnosticFactTag::ActualLength,
791                    actual_bytes as u64,
792                ),
793                (diagnostic_code::DiagnosticFactTag::Limit, limit as u64),
794            ],
795        )
796    }
797
798    /// Construct an executor-origin prepared-commit work-bound rejection.
799    #[cold]
800    #[inline(never)]
801    pub(crate) fn mutation_batch_commit_work_exceeded(
802        actual_units: Option<usize>,
803        limit: usize,
804    ) -> Self {
805        let mut facts = Vec::with_capacity(1 + usize::from(actual_units.is_some()));
806        if let Some(actual_units) = actual_units {
807            facts.push((
808                diagnostic_code::DiagnosticFactTag::ActualCount,
809                actual_units as u64,
810            ));
811        }
812        facts.push((diagnostic_code::DiagnosticFactTag::Limit, limit as u64));
813        Self::mutation_boundary_with_facts(
814            ErrorClass::Unsupported,
815            diagnostic_code::RuntimeBoundaryCode::MutationBatchCommitWorkExceeded,
816            facts,
817        )
818    }
819
820    /// Construct the retryable cumulative journal-backlog pressure boundary.
821    pub(crate) fn convergence_backlog_pressure(
822        resource: diagnostic_code::DiagnosticBacklogResource,
823        current: u64,
824        proposed: u64,
825        limit: u64,
826    ) -> Self {
827        Self::mutation_boundary_with_facts(
828            ErrorClass::Conflict,
829            diagnostic_code::RuntimeBoundaryCode::ConvergenceBacklogPressure,
830            vec![
831                (
832                    diagnostic_code::DiagnosticFactTag::BacklogResource,
833                    resource.raw(),
834                ),
835                (diagnostic_code::DiagnosticFactTag::CurrentCount, current),
836                (diagnostic_code::DiagnosticFactTag::ProposedCount, proposed),
837                (diagnostic_code::DiagnosticFactTag::Limit, limit),
838            ],
839        )
840    }
841
842    /// Construct a query-origin exact-key item-bound rejection.
843    #[cold]
844    #[inline(never)]
845    pub(crate) fn exact_key_batch_too_many_items(actual_count: usize, limit: usize) -> Self {
846        Self::exact_key_batch_boundary_with_facts(
847            diagnostic_code::RuntimeBoundaryCode::ExactKeyBatchTooManyItems,
848            vec![
849                (
850                    diagnostic_code::DiagnosticFactTag::ActualCount,
851                    actual_count as u64,
852                ),
853                (diagnostic_code::DiagnosticFactTag::Limit, limit as u64),
854            ],
855        )
856    }
857
858    /// Construct a query-origin exact-key input-byte rejection.
859    #[cold]
860    #[inline(never)]
861    pub(crate) fn exact_key_batch_input_bytes_exceeded(actual_bytes: usize, limit: usize) -> Self {
862        Self::exact_key_batch_bytes_exceeded(
863            diagnostic_code::RuntimeBoundaryCode::ExactKeyBatchInputBytesExceeded,
864            actual_bytes,
865            limit,
866        )
867    }
868
869    /// Construct a query-origin exact-key stored-row-byte rejection.
870    #[cold]
871    #[inline(never)]
872    pub(crate) fn exact_key_batch_stored_bytes_exceeded(actual_bytes: usize, limit: usize) -> Self {
873        Self::exact_key_batch_bytes_exceeded(
874            diagnostic_code::RuntimeBoundaryCode::ExactKeyBatchStoredBytesExceeded,
875            actual_bytes,
876            limit,
877        )
878    }
879
880    /// Construct a query-origin exact-key result-byte rejection.
881    #[cold]
882    #[inline(never)]
883    pub(crate) fn exact_key_batch_result_bytes_exceeded(actual_bytes: usize, limit: usize) -> Self {
884        Self::exact_key_batch_bytes_exceeded(
885            diagnostic_code::RuntimeBoundaryCode::ExactKeyBatchResultBytesExceeded,
886            actual_bytes,
887            limit,
888        )
889    }
890
891    #[cold]
892    #[inline(never)]
893    fn exact_key_batch_bytes_exceeded(
894        boundary: diagnostic_code::RuntimeBoundaryCode,
895        actual_bytes: usize,
896        limit: usize,
897    ) -> Self {
898        Self::exact_key_batch_boundary_with_facts(
899            boundary,
900            vec![
901                (
902                    diagnostic_code::DiagnosticFactTag::ActualLength,
903                    actual_bytes as u64,
904                ),
905                (diagnostic_code::DiagnosticFactTag::Limit, limit as u64),
906            ],
907        )
908    }
909
910    /// Construct an executor-origin cross-store batch rejection.
911    #[cold]
912    #[inline(never)]
913    pub(crate) fn mutation_batch_store_mismatch(
914        batch_position: u32,
915        expected_entity_tag: u64,
916        actual_entity_tag: u64,
917    ) -> Self {
918        Self::mutation_boundary_with_facts(
919            ErrorClass::Conflict,
920            diagnostic_code::RuntimeBoundaryCode::MutationBatchStoreMismatch,
921            vec![
922                (
923                    diagnostic_code::DiagnosticFactTag::BatchPosition,
924                    u64::from(batch_position),
925                ),
926                (
927                    diagnostic_code::DiagnosticFactTag::ExpectedEntityTag,
928                    expected_entity_tag,
929                ),
930                (
931                    diagnostic_code::DiagnosticFactTag::ActualEntityTag,
932                    actual_entity_tag,
933                ),
934            ],
935        )
936    }
937
938    /// Construct an executor-origin distinct-entity-bound rejection.
939    #[cold]
940    #[inline(never)]
941    pub(crate) fn mutation_batch_too_many_entities(actual_count: usize, limit: usize) -> Self {
942        Self::mutation_boundary_with_facts(
943            ErrorClass::Unsupported,
944            diagnostic_code::RuntimeBoundaryCode::MutationBatchTooManyEntities,
945            vec![
946                (
947                    diagnostic_code::DiagnosticFactTag::ActualCount,
948                    actual_count as u64,
949                ),
950                (diagnostic_code::DiagnosticFactTag::Limit, limit as u64),
951            ],
952        )
953    }
954
955    /// Construct an executor-origin mutation invariant for index-store generation drift.
956    pub(crate) fn mutation_index_store_generation_changed(
957        _expected_generation: u64,
958        _observed_generation: u64,
959    ) -> Self {
960        Self::executor_invariant()
961    }
962
963    /// Construct a planner-origin invariant violation.
964    #[cold]
965    #[inline(never)]
966    pub(crate) fn planner_invariant() -> Self {
967        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Planner)
968    }
969
970    /// Construct a planner-origin invalid-logical-plan invariant.
971    pub(crate) fn query_invalid_logical_plan() -> Self {
972        Self::planner_invariant()
973    }
974
975    /// Construct a store-origin invariant violation.
976    pub(crate) fn store_invariant() -> Self {
977        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Store)
978    }
979
980    /// Construct a store-origin internal error.
981    #[cold]
982    #[inline(never)]
983    pub(crate) fn store_internal() -> Self {
984        Self::new(ErrorClass::Internal, ErrorOrigin::Store)
985    }
986
987    /// Construct the canonical unconfigured commit-memory id internal error.
988    pub(crate) fn commit_memory_id_unconfigured() -> Self {
989        Self::store_internal()
990    }
991
992    /// Construct the canonical initialized commit-store lookup invariant.
993    pub(crate) fn commit_store_uninitialized() -> Self {
994        Self::store_invariant()
995    }
996
997    /// Construct the canonical database-incarnation generation failure.
998    pub(crate) fn database_incarnation_generation_failed() -> Self {
999        Self::store_internal()
1000    }
1001
1002    /// Construct the canonical zero database-incarnation corruption error.
1003    pub(crate) fn database_incarnation_invalid() -> Self {
1004        Self::store_corruption()
1005    }
1006
1007    /// Construct a recovery-origin incompatible store-format error.
1008    pub(crate) fn recovery_unsupported_database_format(found: Option<u16>, required: u16) -> Self {
1009        Self {
1010            class: ErrorClass::IncompatiblePersistedFormat,
1011            origin: ErrorOrigin::Recovery,
1012            detail: Some(ErrorDetail::Recovery(
1013                RecoveryErrorDetail::UnsupportedFormatVersion { found, required },
1014            )),
1015        }
1016    }
1017
1018    /// Construct a recovery-origin malformed store-format marker error.
1019    pub(crate) fn recovery_malformed_database_format_marker(
1020        reason: RecoveryFormatMarkerError,
1021    ) -> Self {
1022        Self {
1023            class: ErrorClass::Corruption,
1024            origin: ErrorOrigin::Recovery,
1025            detail: Some(ErrorDetail::Recovery(
1026                RecoveryErrorDetail::MalformedFormatMarker { reason },
1027            )),
1028        }
1029    }
1030
1031    /// Construct a recovery-origin boot control-memory failure.
1032    pub(crate) fn recovery_database_format_control_unavailable() -> Self {
1033        Self::new(ErrorClass::Internal, ErrorOrigin::Recovery)
1034    }
1035
1036    /// Construct the retryable internal boundary returned while bounded startup recovery remains.
1037    pub(crate) fn recovery_pending() -> Self {
1038        Self::with_diagnostic_facts(
1039            ErrorClass::Conflict,
1040            ErrorOrigin::Recovery,
1041            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
1042                boundary: diagnostic_code::RuntimeBoundaryCode::DatabaseStartupRecoveryPending,
1043            }),
1044            Vec::new(),
1045        )
1046    }
1047
1048    /// Construct fail-closed corruption for the bounded startup control cell.
1049    pub(crate) fn startup_control_corruption() -> Self {
1050        Self::new(ErrorClass::Corruption, ErrorOrigin::Recovery)
1051    }
1052
1053    /// Construct a commit control-memory growth failure.
1054    pub(crate) fn commit_control_memory_growth_failed() -> Self {
1055        Self::store_internal()
1056    }
1057
1058    /// Construct a store-format memory registration failure.
1059    #[cfg(not(test))]
1060    pub(crate) fn database_format_memory_registration_failed(_err: impl Sized) -> Self {
1061        Self::store_internal()
1062    }
1063
1064    /// Construct the canonical recovered-effect verification failure.
1065    pub(crate) fn recovery_effect_verification_failed() -> Self {
1066        Self::store_corruption()
1067    }
1068
1069    /// Construct an index-origin internal error.
1070    #[cold]
1071    #[inline(never)]
1072    pub(crate) fn index_internal() -> Self {
1073        Self::new(ErrorClass::Internal, ErrorOrigin::Index)
1074    }
1075
1076    /// Construct the canonical missing old entity-key internal error for structural index removal.
1077    pub(crate) fn structural_index_removal_entity_key_required() -> Self {
1078        Self::index_internal()
1079    }
1080
1081    /// Construct the canonical missing new entity-key internal error for structural index insertion.
1082    pub(crate) fn structural_index_insertion_entity_key_required() -> Self {
1083        Self::index_internal()
1084    }
1085
1086    /// Construct the canonical missing old entity-key internal error for index commit-op removal.
1087    pub(crate) fn index_commit_op_old_entity_key_required() -> Self {
1088        Self::index_internal()
1089    }
1090
1091    /// Construct the canonical missing new entity-key internal error for index commit-op insertion.
1092    pub(crate) fn index_commit_op_new_entity_key_required() -> Self {
1093        Self::index_internal()
1094    }
1095
1096    /// Construct a query-origin internal error.
1097    #[cfg(test)]
1098    pub(crate) fn query_internal() -> Self {
1099        Self::new(ErrorClass::Internal, ErrorOrigin::Query)
1100    }
1101
1102    /// Construct a query-origin unsupported error.
1103    #[cold]
1104    #[inline(never)]
1105    pub(crate) fn query_unsupported() -> Self {
1106        Self::new(ErrorClass::Unsupported, ErrorOrigin::Query)
1107    }
1108
1109    /// Detached explain rendering exceeded its fixed output policy, not a
1110    /// request/execution budget. Retain numeric facts without report contents.
1111    pub(crate) fn query_explain_output_exceeded(limit: u64, observed: u64) -> Self {
1112        Self::with_diagnostic_facts(
1113            ErrorClass::Unsupported,
1114            ErrorOrigin::Query,
1115            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
1116                boundary: diagnostic_code::RuntimeBoundaryCode::QueryExplainOutputExceeded,
1117            }),
1118            vec![
1119                (diagnostic_code::DiagnosticFactTag::Limit, limit),
1120                (diagnostic_code::DiagnosticFactTag::Actual, observed),
1121            ],
1122        )
1123    }
1124
1125    /// Derived diagnostic access depth exceeded its fixed projection policy.
1126    /// This does not reject or change the identity of an ordinary query.
1127    pub(crate) fn query_explain_depth_exceeded(limit: u64, observed: u64) -> Self {
1128        Self::with_diagnostic_facts(
1129            ErrorClass::Unsupported,
1130            ErrorOrigin::Query,
1131            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
1132                boundary: diagnostic_code::RuntimeBoundaryCode::QueryExplainDepthExceeded,
1133            }),
1134            vec![
1135                (diagnostic_code::DiagnosticFactTag::Limit, limit),
1136                (diagnostic_code::DiagnosticFactTag::Actual, observed),
1137            ],
1138        )
1139    }
1140
1141    /// Construct a query-origin conflict for execution against a superseded
1142    /// accepted schema revision.
1143    #[cold]
1144    #[inline(never)]
1145    pub(crate) fn query_stale_accepted_schema_revision(
1146        expected_revision: u64,
1147        current_revision: Option<u64>,
1148    ) -> Self {
1149        let mut facts = Vec::with_capacity(1 + usize::from(current_revision.is_some()));
1150        facts.push((
1151            diagnostic_code::DiagnosticFactTag::ExpectedRevision,
1152            expected_revision,
1153        ));
1154        if let Some(current_revision) = current_revision {
1155            facts.push((
1156                diagnostic_code::DiagnosticFactTag::CurrentRevision,
1157                current_revision,
1158            ));
1159        }
1160        Self::with_diagnostic_facts(ErrorClass::Conflict, ErrorOrigin::Query, None, facts)
1161    }
1162
1163    /// Construct a query-origin SQL DDL admission error with structured detail.
1164    #[cold]
1165    #[inline(never)]
1166    #[cfg(feature = "sql")]
1167    pub(crate) fn query_schema_ddl_admission(error: SchemaDdlAdmissionError) -> Self {
1168        Self {
1169            class: ErrorClass::Unsupported,
1170            origin: ErrorOrigin::Query,
1171            detail: Some(ErrorDetail::Query(QueryErrorDetail::SchemaDdlAdmission {
1172                error,
1173            })),
1174        }
1175    }
1176
1177    /// Construct a query-origin numeric overflow error with structured detail.
1178    #[cold]
1179    #[inline(never)]
1180    pub(crate) fn query_numeric_overflow() -> Self {
1181        Self {
1182            class: ErrorClass::Unsupported,
1183            origin: ErrorOrigin::Query,
1184            detail: Some(ErrorDetail::Query(QueryErrorDetail::NumericOverflow)),
1185        }
1186    }
1187
1188    /// Construct a query-origin non-representable numeric result error with
1189    /// structured detail.
1190    #[cold]
1191    #[inline(never)]
1192    pub(crate) fn query_numeric_not_representable() -> Self {
1193        Self {
1194            class: ErrorClass::Unsupported,
1195            origin: ErrorOrigin::Query,
1196            detail: Some(ErrorDetail::Query(
1197                QueryErrorDetail::NumericNotRepresentable,
1198            )),
1199        }
1200    }
1201
1202    /// Construct a serialize-origin internal error.
1203    #[cold]
1204    #[inline(never)]
1205    pub(crate) fn serialize_internal() -> Self {
1206        Self::new(ErrorClass::Internal, ErrorOrigin::Serialize)
1207    }
1208
1209    /// Construct the canonical persisted-row encode internal error.
1210    pub(crate) fn persisted_row_encode_failed(_detail: impl Sized) -> Self {
1211        Self::persisted_row_encode_internal()
1212    }
1213
1214    /// Construct the compact persisted-row encode internal error.
1215    pub(crate) fn persisted_row_encode_internal() -> Self {
1216        Self::serialize_internal()
1217    }
1218
1219    /// Construct the compact persisted-row field encode internal error.
1220    pub(crate) fn persisted_row_field_encode_internal(_field_name: &str) -> Self {
1221        Self::persisted_row_encode_internal()
1222    }
1223
1224    /// Construct a store-origin corruption error.
1225    #[cold]
1226    #[inline(never)]
1227    pub(crate) fn store_corruption() -> Self {
1228        Self::new(ErrorClass::Corruption, ErrorOrigin::Store)
1229    }
1230
1231    /// Construct a store-origin commit-marker corruption error.
1232    pub(crate) fn commit_corruption() -> Self {
1233        Self::store_corruption()
1234    }
1235
1236    /// Construct a store-origin commit-marker component corruption error.
1237    pub(crate) fn commit_component_corruption() -> Self {
1238        Self::commit_corruption()
1239    }
1240
1241    /// Construct the canonical commit-marker id generation internal error.
1242    pub(crate) fn commit_id_generation_failed() -> Self {
1243        Self::store_internal()
1244    }
1245
1246    /// Construct the canonical commit-marker payload u32-length-limit error.
1247    pub(crate) fn commit_marker_payload_exceeds_u32_length_limit() -> Self {
1248        Self::store_unsupported()
1249    }
1250
1251    /// Construct the canonical commit-marker component invalid-length corruption error.
1252    pub(crate) fn commit_component_length_invalid(actual_length: usize, limit: usize) -> Self {
1253        Self::with_diagnostic_facts(
1254            ErrorClass::Corruption,
1255            ErrorOrigin::Store,
1256            None,
1257            vec![
1258                (
1259                    diagnostic_code::DiagnosticFactTag::ComponentKind,
1260                    diagnostic_code::DiagnosticComponentKind::CommitDataKey.raw(),
1261                ),
1262                (
1263                    diagnostic_code::DiagnosticFactTag::ActualLength,
1264                    actual_length as u64,
1265                ),
1266                (diagnostic_code::DiagnosticFactTag::Limit, limit as u64),
1267            ],
1268        )
1269    }
1270
1271    /// Construct the canonical commit-marker max-size corruption error.
1272    pub(crate) fn commit_marker_exceeds_max_size() -> Self {
1273        Self::commit_corruption()
1274    }
1275
1276    /// Construct the canonical commit-control slot max-size unsupported error.
1277    pub(crate) fn commit_control_slot_exceeds_max_size() -> Self {
1278        Self::store_unsupported()
1279    }
1280
1281    /// Construct the canonical commit-control marker-bytes length-limit error.
1282    pub(crate) fn commit_control_slot_marker_bytes_exceed_u32_length_limit() -> Self {
1283        Self::store_unsupported()
1284    }
1285
1286    /// Construct an index-origin corruption error.
1287    #[cold]
1288    #[inline(never)]
1289    pub(crate) fn index_corruption() -> Self {
1290        Self::new(ErrorClass::Corruption, ErrorOrigin::Index)
1291    }
1292
1293    /// Construct the canonical unique-validation corruption wrapper.
1294    pub(crate) fn index_unique_validation_corruption() -> Self {
1295        Self::index_plan_index_corruption()
1296    }
1297
1298    /// Construct the canonical structural index-entry corruption wrapper.
1299    pub(crate) fn structural_index_entry_corruption() -> Self {
1300        Self::index_plan_index_corruption()
1301    }
1302
1303    /// Construct the canonical missing new entity-key invariant during unique validation.
1304    pub(crate) fn index_unique_validation_entity_key_required() -> Self {
1305        Self::index_invariant()
1306    }
1307
1308    /// Construct the canonical unique-validation structural row-decode corruption error.
1309    pub(crate) fn index_unique_validation_row_deserialize_failed() -> Self {
1310        Self::index_plan_serialize_corruption()
1311    }
1312
1313    /// Construct the canonical unique-validation primary-key slot decode corruption error.
1314    pub(crate) fn index_unique_validation_primary_key_decode_failed() -> Self {
1315        Self::index_plan_serialize_corruption()
1316    }
1317
1318    /// Construct the canonical unique-validation stored key rebuild corruption error.
1319    pub(crate) fn index_unique_validation_key_rebuild_failed() -> Self {
1320        Self::index_plan_serialize_corruption()
1321    }
1322
1323    /// Construct the canonical unique-validation missing-row corruption error.
1324    pub(crate) fn index_unique_validation_row_required() -> Self {
1325        Self::index_plan_store_corruption()
1326    }
1327
1328    /// Construct the canonical index-only predicate missing-component invariant.
1329    pub(crate) fn index_only_predicate_component_required() -> Self {
1330        Self::index_invariant()
1331    }
1332
1333    /// Construct the canonical index-scan continuation-envelope invariant.
1334    pub(crate) fn index_scan_continuation_anchor_within_envelope_required() -> Self {
1335        Self::index_invariant()
1336    }
1337
1338    /// Construct the canonical index-scan continuation-advancement invariant.
1339    pub(crate) fn index_scan_continuation_advancement_required() -> Self {
1340        Self::index_invariant()
1341    }
1342
1343    /// Construct the canonical index-scan key-decode corruption error.
1344    pub(crate) fn index_scan_key_corrupted_during(
1345        _context: &'static str,
1346        _err: impl Sized,
1347    ) -> Self {
1348        Self::index_corruption()
1349    }
1350
1351    /// Construct the canonical index-scan missing projection-component invariant.
1352    pub(crate) fn index_projection_component_required(
1353        _index_name: &str,
1354        _component_index: usize,
1355    ) -> Self {
1356        Self::index_invariant()
1357    }
1358
1359    /// Construct the canonical scan-time index-entry decode corruption error.
1360    pub(crate) fn index_entry_decode_failed() -> Self {
1361        Self::index_corruption()
1362    }
1363
1364    /// Construct a serialize-origin corruption error.
1365    pub(crate) fn serialize_corruption() -> Self {
1366        Self::new(ErrorClass::Corruption, ErrorOrigin::Serialize)
1367    }
1368
1369    /// Construct the compact persisted-row decode corruption error.
1370    pub(crate) fn persisted_row_decode_corruption() -> Self {
1371        Self::serialize_corruption()
1372    }
1373
1374    /// Construct a persisted-row layout-window corruption error.
1375    pub(crate) fn persisted_row_layout_outside_accepted_window(
1376        row_layout: u32,
1377        history_floor: u32,
1378        current_layout: u32,
1379    ) -> Self {
1380        Self::with_diagnostic_facts(
1381            ErrorClass::Corruption,
1382            ErrorOrigin::Serialize,
1383            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
1384                boundary:
1385                    diagnostic_code::RuntimeBoundaryCode::PersistedRowLayoutOutsideAcceptedWindow,
1386            }),
1387            vec![
1388                (
1389                    diagnostic_code::DiagnosticFactTag::RowLayout,
1390                    u64::from(row_layout),
1391                ),
1392                (
1393                    diagnostic_code::DiagnosticFactTag::HistoryFloor,
1394                    u64::from(history_floor),
1395                ),
1396                (
1397                    diagnostic_code::DiagnosticFactTag::CurrentLayout,
1398                    u64::from(current_layout),
1399                ),
1400            ],
1401        )
1402    }
1403
1404    /// Construct a persisted-row stamped-layout slot-count corruption error.
1405    pub(crate) fn persisted_row_slot_count_mismatch(
1406        row_layout: u32,
1407        expected_slot_count: usize,
1408        actual_slot_count: usize,
1409    ) -> Self {
1410        Self::with_diagnostic_facts(
1411            ErrorClass::Corruption,
1412            ErrorOrigin::Serialize,
1413            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
1414                boundary: diagnostic_code::RuntimeBoundaryCode::PersistedRowSlotCountMismatch,
1415            }),
1416            vec![
1417                (
1418                    diagnostic_code::DiagnosticFactTag::RowLayout,
1419                    u64::from(row_layout),
1420                ),
1421                (
1422                    diagnostic_code::DiagnosticFactTag::ExpectedSlotCount,
1423                    expected_slot_count as u64,
1424                ),
1425                (
1426                    diagnostic_code::DiagnosticFactTag::ActualSlotCount,
1427                    actual_slot_count as u64,
1428                ),
1429            ],
1430        )
1431    }
1432
1433    /// Construct the canonical persisted-row field decode corruption error.
1434    pub(crate) fn persisted_row_field_decode_failed(field_name: &str, _detail: impl Sized) -> Self {
1435        Self::persisted_row_field_decode_corruption(field_name)
1436    }
1437
1438    /// Construct the compact persisted-row field decode corruption error.
1439    pub(crate) fn persisted_row_field_decode_corruption(_field_name: &str) -> Self {
1440        Self::persisted_row_decode_corruption()
1441    }
1442
1443    /// Construct the canonical persisted-row field-kind decode corruption error.
1444    pub(crate) fn persisted_row_field_kind_decode_failed(
1445        field_name: &str,
1446        _field_kind: impl fmt::Debug,
1447        _detail: impl Sized,
1448    ) -> Self {
1449        Self::persisted_row_field_decode_corruption(field_name)
1450    }
1451
1452    /// Construct the canonical persisted-row scalar-payload length corruption error.
1453    pub(crate) fn persisted_row_field_payload_exact_len_required(field_name: &str) -> Self {
1454        Self::persisted_row_field_decode_corruption(field_name)
1455    }
1456
1457    /// Construct the canonical persisted-row scalar-payload empty-body corruption error.
1458    pub(crate) fn persisted_row_field_payload_must_be_empty(field_name: &str) -> Self {
1459        Self::persisted_row_field_decode_corruption(field_name)
1460    }
1461
1462    /// Construct the canonical persisted-row scalar-payload invalid-byte corruption error.
1463    pub(crate) fn persisted_row_field_payload_invalid_byte(field_name: &str) -> Self {
1464        Self::persisted_row_field_decode_corruption(field_name)
1465    }
1466
1467    /// Construct the canonical persisted-row scalar-payload non-finite corruption error.
1468    pub(crate) fn persisted_row_field_payload_non_finite(field_name: &str) -> Self {
1469        Self::persisted_row_field_decode_corruption(field_name)
1470    }
1471
1472    /// Construct the canonical persisted-row invalid text payload corruption error.
1473    pub(crate) fn persisted_row_field_text_payload_invalid_utf8(field_name: &str) -> Self {
1474        Self::persisted_row_field_decode_corruption(field_name)
1475    }
1476
1477    /// Construct the canonical persisted-row structural slot-lookup invariant.
1478    pub(crate) fn persisted_row_slot_lookup_out_of_bounds(_model_path: &str, _slot: usize) -> Self {
1479        Self::index_invariant()
1480    }
1481
1482    /// Construct the canonical persisted-row structural slot-cache invariant.
1483    pub(crate) fn persisted_row_slot_cache_lookup_out_of_bounds(
1484        _model_path: &str,
1485        _slot: usize,
1486    ) -> Self {
1487        Self::index_invariant()
1488    }
1489
1490    /// Construct the canonical persisted-row primary-key decode corruption error.
1491    pub(crate) fn persisted_row_primary_key_not_primary_key_encodable(
1492        _data_key: impl fmt::Debug,
1493        _detail: impl Sized,
1494    ) -> Self {
1495        Self::persisted_row_decode_corruption()
1496    }
1497
1498    /// Construct the canonical persisted-row missing primary-key slot corruption error.
1499    pub(crate) fn persisted_row_primary_key_slot_missing(_data_key: impl fmt::Debug) -> Self {
1500        Self::persisted_row_decode_corruption()
1501    }
1502
1503    /// Construct the canonical persisted-row key mismatch corruption error.
1504    pub(crate) fn persisted_row_key_mismatch() -> Self {
1505        Self::store_corruption()
1506    }
1507
1508    /// Construct the canonical persisted-row missing declared-field corruption error.
1509    pub(crate) fn persisted_row_declared_field_missing(field_name: &str) -> Self {
1510        Self::persisted_row_field_decode_corruption(field_name)
1511    }
1512
1513    /// Construct the canonical reverse-index entry corruption error.
1514    pub(crate) fn reverse_index_entry_corrupted(
1515        _source_path: &str,
1516        _field_name: &str,
1517        _target_path: &str,
1518        _index_key: impl fmt::Debug,
1519        _detail: impl Sized,
1520    ) -> Self {
1521        Self::index_corruption()
1522    }
1523
1524    /// Construct the canonical relation-target store missing internal error.
1525    pub(crate) fn relation_target_store_missing(
1526        _source_path: &str,
1527        _field_name: &str,
1528        _target_path: &str,
1529        _store_path: &str,
1530        _detail: impl Sized,
1531    ) -> Self {
1532        Self::executor_internal()
1533    }
1534
1535    /// Identify the accepted source and relation when runtime contract compilation fails.
1536    pub(crate) fn with_relation_identity(self, entity_tag: u64, relation_id: u32) -> Self {
1537        if self.diagnostic().error_code() != diagnostic_code::ErrorCode::RUNTIME_INTERNAL {
1538            return self;
1539        }
1540        let mut facts = vec![
1541            (diagnostic_code::DiagnosticFactTag::EntityTag, entity_tag),
1542            (
1543                diagnostic_code::DiagnosticFactTag::RelationId,
1544                u64::from(relation_id),
1545            ),
1546        ];
1547        facts.extend(self.diagnostic_facts());
1548        Self::with_diagnostic_facts(self.class, self.origin, None, facts)
1549    }
1550
1551    /// Construct one accepted relation target primary-key arity mismatch.
1552    pub(crate) fn relation_target_primary_key_arity_mismatch(
1553        expected_arity: usize,
1554        actual_arity: usize,
1555    ) -> Self {
1556        Self::with_diagnostic_facts(
1557            ErrorClass::Internal,
1558            ErrorOrigin::Executor,
1559            None,
1560            vec![
1561                (
1562                    diagnostic_code::DiagnosticFactTag::ComponentKind,
1563                    diagnostic_code::DiagnosticComponentKind::RelationTargetPrimaryKey.raw(),
1564                ),
1565                (
1566                    diagnostic_code::DiagnosticFactTag::ExpectedArity,
1567                    expected_arity as u64,
1568                ),
1569                (
1570                    diagnostic_code::DiagnosticFactTag::ActualArity,
1571                    actual_arity as u64,
1572                ),
1573            ],
1574        )
1575    }
1576
1577    /// Construct the canonical relation-target key decode corruption error.
1578    pub(crate) fn relation_target_key_decode_failed(
1579        _context_label: &str,
1580        _source_path: &str,
1581        _field_name: &str,
1582        _target_path: &str,
1583        _detail: impl Sized,
1584    ) -> Self {
1585        Self::identity_corruption()
1586    }
1587
1588    /// Construct the canonical relation-target entity mismatch corruption error.
1589    pub(crate) fn relation_target_entity_mismatch(
1590        _context_label: &str,
1591        _source_path: &str,
1592        _field_name: &str,
1593        _target_path: &str,
1594        _target_entity_name: &str,
1595        expected_tag: u64,
1596        actual_tag: u64,
1597    ) -> Self {
1598        Self::with_diagnostic_facts(
1599            ErrorClass::Corruption,
1600            ErrorOrigin::Store,
1601            None,
1602            vec![
1603                (
1604                    diagnostic_code::DiagnosticFactTag::ExpectedEntityTag,
1605                    expected_tag,
1606                ),
1607                (
1608                    diagnostic_code::DiagnosticFactTag::ActualEntityTag,
1609                    actual_tag,
1610                ),
1611            ],
1612        )
1613    }
1614
1615    /// Construct the canonical relation-source row decode corruption error.
1616    pub(crate) fn relation_source_row_decode_failed(
1617        _source_path: &str,
1618        _field_name: &str,
1619        _target_path: &str,
1620        _detail: impl Sized,
1621    ) -> Self {
1622        Self::persisted_row_decode_corruption()
1623    }
1624
1625    /// Construct the canonical relation-source unsupported scalar relation-key corruption error.
1626    pub(crate) fn relation_source_row_unsupported_scalar_relation_key(
1627        _source_path: &str,
1628        _field_name: &str,
1629        _target_path: &str,
1630    ) -> Self {
1631        Self::persisted_row_decode_corruption()
1632    }
1633
1634    /// Construct the canonical unsupported relation key-kind corruption error.
1635    pub(crate) fn relation_source_row_unsupported_key_kind(_field_kind: impl fmt::Debug) -> Self {
1636        Self::persisted_row_decode_corruption()
1637    }
1638
1639    /// Construct the canonical covering-component empty-payload corruption error.
1640    pub(crate) fn bytes_covering_component_payload_empty() -> Self {
1641        Self::index_corruption()
1642    }
1643
1644    /// Construct the canonical covering-component truncated bool corruption error.
1645    pub(crate) fn bytes_covering_bool_payload_truncated() -> Self {
1646        Self::index_corruption()
1647    }
1648
1649    /// Construct the canonical covering-component invalid-length corruption error.
1650    pub(crate) fn bytes_covering_component_payload_invalid_length() -> Self {
1651        Self::index_corruption()
1652    }
1653
1654    /// Construct the canonical covering-component invalid-bool corruption error.
1655    pub(crate) fn bytes_covering_bool_payload_invalid_value() -> Self {
1656        Self::index_corruption()
1657    }
1658
1659    /// Construct the canonical covering-component invalid text terminator corruption error.
1660    pub(crate) fn bytes_covering_text_payload_invalid_terminator() -> Self {
1661        Self::index_corruption()
1662    }
1663
1664    /// Construct the canonical covering-component trailing-text corruption error.
1665    pub(crate) fn bytes_covering_text_payload_trailing_bytes() -> Self {
1666        Self::index_corruption()
1667    }
1668
1669    /// Construct the canonical covering-component invalid-UTF-8 text corruption error.
1670    pub(crate) fn bytes_covering_text_payload_invalid_utf8() -> Self {
1671        Self::index_corruption()
1672    }
1673
1674    /// Construct the canonical covering-component invalid text escape corruption error.
1675    pub(crate) fn bytes_covering_text_payload_invalid_escape_byte() -> Self {
1676        Self::index_corruption()
1677    }
1678
1679    /// Construct the canonical covering-component missing text terminator corruption error.
1680    pub(crate) fn bytes_covering_text_payload_missing_terminator() -> Self {
1681        Self::index_corruption()
1682    }
1683
1684    /// Construct an identity-origin corruption error.
1685    pub(crate) fn identity_corruption() -> Self {
1686        Self::new(ErrorClass::Corruption, ErrorOrigin::Identity)
1687    }
1688
1689    /// Construct the canonical identity-control-state corruption error.
1690    pub(crate) fn identity_state_corruption() -> Self {
1691        Self::identity_corruption()
1692    }
1693
1694    /// Construct the typed stale high-water conflict for identity publication.
1695    pub(crate) fn identity_state_conflict() -> Self {
1696        Self::new(ErrorClass::Conflict, ErrorOrigin::Identity)
1697    }
1698
1699    /// Construct the bounded identity-state inventory exhaustion error.
1700    pub(crate) fn identity_state_capacity_exhausted() -> Self {
1701        Self::new(ErrorClass::Unsupported, ErrorOrigin::Identity)
1702    }
1703
1704    /// Construct the exact unsigned identity-domain exhaustion error.
1705    pub(crate) fn identity_exhausted() -> Self {
1706        Self::new(ErrorClass::Unsupported, ErrorOrigin::Identity)
1707    }
1708
1709    /// Construct the bounded pre-key candidate-count exhaustion error.
1710    pub(crate) fn identity_candidate_count_exhausted() -> Self {
1711        Self::new(ErrorClass::Unsupported, ErrorOrigin::Identity)
1712    }
1713
1714    /// Construct a store-origin unsupported error.
1715    #[cold]
1716    #[inline(never)]
1717    pub(crate) fn store_unsupported() -> Self {
1718        Self::new(ErrorClass::Unsupported, ErrorOrigin::Store)
1719    }
1720
1721    /// Construct the typed optimistic/idempotency conflict for schema application.
1722    pub(crate) fn schema_application_conflict() -> Self {
1723        Self::new(ErrorClass::Conflict, ErrorOrigin::Store)
1724    }
1725
1726    /// Construct one typed source-migration lifecycle or planning result.
1727    pub(crate) fn schema_migration(reason: diagnostic_code::SchemaMigrationCode) -> Self {
1728        let class = match reason.diagnostic_code() {
1729            diagnostic_code::DiagnosticCode::RuntimeConflict => ErrorClass::Conflict,
1730            diagnostic_code::DiagnosticCode::RuntimeCorruption => ErrorClass::Corruption,
1731            diagnostic_code::DiagnosticCode::RuntimeUnsupported => ErrorClass::Unsupported,
1732            _ => ErrorClass::Internal,
1733        };
1734        Self {
1735            class,
1736            origin: ErrorOrigin::Store,
1737            detail: Some(ErrorDetail::Store(StoreError::SchemaMigration { reason })),
1738        }
1739    }
1740
1741    /// Construct the canonical schema DDL publication race error.
1742    pub(crate) fn schema_ddl_publication_race_lost(_entity_path: &str) -> Self {
1743        Self {
1744            class: ErrorClass::Unsupported,
1745            origin: ErrorOrigin::Store,
1746            detail: Some(ErrorDetail::Store(StoreError::SchemaDdlPublicationRaceLost)),
1747        }
1748    }
1749
1750    /// Construct the canonical current physical-rewrite migration rejection.
1751    #[cfg(feature = "sql")]
1752    pub(crate) fn schema_ddl_rewrite_requires_migration(_entity_path: &str) -> Self {
1753        Self {
1754            class: ErrorClass::Unsupported,
1755            origin: ErrorOrigin::Store,
1756            detail: Some(ErrorDetail::Store(
1757                StoreError::SchemaDdlRewriteRequiresMigration,
1758            )),
1759        }
1760    }
1761
1762    /// Construct the fail-closed journal mutation-revision exhaustion error.
1763    pub(crate) fn journal_mutation_revision_exhausted() -> Self {
1764        Self {
1765            class: ErrorClass::Unsupported,
1766            origin: ErrorOrigin::Store,
1767            detail: Some(ErrorDetail::Store(
1768                StoreError::JournalMutationRevisionExhausted,
1769            )),
1770        }
1771    }
1772
1773    /// Construct a bounded schema-transition resource rejection.
1774    pub(crate) fn schema_transition_budget_exceeded(
1775        resource: SchemaTransitionBudgetResource,
1776    ) -> Self {
1777        Self {
1778            class: ErrorClass::Unsupported,
1779            origin: ErrorOrigin::Store,
1780            detail: Some(ErrorDetail::Store(
1781                StoreError::SchemaTransitionBudgetExceeded { resource },
1782            )),
1783        }
1784    }
1785
1786    /// Construct the canonical unsupported persisted entity-tag store error.
1787    pub(crate) fn unsupported_entity_tag_in_data_store(
1788        _entity_tag: crate::types::EntityTag,
1789    ) -> Self {
1790        Self::store_unsupported()
1791    }
1792
1793    /// Construct the canonical commit-memory id registration failure.
1794    pub(crate) fn commit_memory_id_registration_failed(_err: impl Sized) -> Self {
1795        Self::store_internal()
1796    }
1797
1798    /// Construct an index-origin unsupported error.
1799    pub(crate) fn index_unsupported() -> Self {
1800        Self::new(ErrorClass::Unsupported, ErrorOrigin::Index)
1801    }
1802
1803    /// Construct the canonical index-key component size-limit unsupported error.
1804    pub(crate) fn index_component_exceeds_max_size_at(
1805        entity_tag: u64,
1806        physical_generation: u64,
1807        component_index: usize,
1808        actual_length: usize,
1809        limit: usize,
1810    ) -> Self {
1811        Self::with_diagnostic_facts(
1812            ErrorClass::Unsupported,
1813            ErrorOrigin::Index,
1814            None,
1815            vec![
1816                (diagnostic_code::DiagnosticFactTag::EntityTag, entity_tag),
1817                (
1818                    diagnostic_code::DiagnosticFactTag::PhysicalGeneration,
1819                    physical_generation,
1820                ),
1821                (
1822                    diagnostic_code::DiagnosticFactTag::ComponentIndex,
1823                    component_index as u64,
1824                ),
1825                (
1826                    diagnostic_code::DiagnosticFactTag::ComponentKind,
1827                    diagnostic_code::DiagnosticComponentKind::IndexKeyComponent.raw(),
1828                ),
1829                (
1830                    diagnostic_code::DiagnosticFactTag::ActualLength,
1831                    actual_length as u64,
1832                ),
1833                (diagnostic_code::DiagnosticFactTag::Limit, limit as u64),
1834            ],
1835        )
1836    }
1837
1838    /// Construct the canonical index-key component size-limit error when the
1839    /// generic caller has not retained one accepted index identity.
1840    pub(crate) fn index_component_exceeds_max_size() -> Self {
1841        Self::index_unsupported()
1842    }
1843
1844    /// Construct a serialize-origin unsupported error.
1845    pub(crate) fn serialize_unsupported() -> Self {
1846        Self::new(ErrorClass::Unsupported, ErrorOrigin::Serialize)
1847    }
1848
1849    /// Construct a cursor-origin invalid-continuation error.
1850    pub(crate) fn cursor_invalid_continuation() -> Self {
1851        Self::new(ErrorClass::Unsupported, ErrorOrigin::Cursor)
1852    }
1853
1854    /// Construct a serialize-origin incompatible persisted-format error.
1855    pub(crate) fn serialize_incompatible_persisted_format() -> Self {
1856        Self::new(
1857            ErrorClass::IncompatiblePersistedFormat,
1858            ErrorOrigin::Serialize,
1859        )
1860    }
1861
1862    /// Construct a query-origin unsupported error preserving one SQL parser
1863    /// unsupported-feature code in structured error detail.
1864    #[cfg(feature = "sql")]
1865    pub(crate) fn query_unsupported_sql_feature(feature: diagnostic_code::SqlFeatureCode) -> Self {
1866        Self {
1867            class: ErrorClass::Unsupported,
1868            origin: ErrorOrigin::Query,
1869            detail: Some(ErrorDetail::Query(
1870                QueryErrorDetail::UnsupportedSqlFeature { feature },
1871            )),
1872        }
1873    }
1874
1875    /// Construct a query-origin unsupported SQL lowering error preserving one
1876    /// compact lowering reason in structured error detail.
1877    #[cfg(feature = "sql")]
1878    pub(crate) fn query_sql_lowering(reason: diagnostic_code::SqlLoweringCode) -> Self {
1879        Self {
1880            class: ErrorClass::Unsupported,
1881            origin: ErrorOrigin::Query,
1882            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlLowering { reason })),
1883        }
1884    }
1885
1886    /// Construct one query-origin SQL lowering error with bounded numeric context.
1887    #[cfg(feature = "sql")]
1888    pub(crate) fn query_sql_lowering_with_facts(
1889        reason: diagnostic_code::SqlLoweringCode,
1890        facts: Vec<(diagnostic_code::DiagnosticFactTag, u64)>,
1891    ) -> Self {
1892        Self::with_diagnostic_facts(
1893            ErrorClass::Unsupported,
1894            ErrorOrigin::Query,
1895            Some(diagnostic_code::DiagnosticDetail::SqlLowering { reason }),
1896            facts,
1897        )
1898    }
1899
1900    /// Construct a query-origin unsupported projection error preserving one
1901    /// compact projection reason in structured error detail.
1902    pub(crate) fn query_unsupported_projection(
1903        reason: diagnostic_code::QueryProjectionCode,
1904    ) -> Self {
1905        Self {
1906            class: ErrorClass::Unsupported,
1907            origin: ErrorOrigin::Query,
1908            detail: Some(ErrorDetail::Query(
1909                QueryErrorDetail::UnsupportedProjection { reason },
1910            )),
1911        }
1912    }
1913
1914    /// Construct a query-origin unsupported error preserving one SQL endpoint
1915    /// surface mismatch in structured error detail.
1916    #[cfg(feature = "sql")]
1917    pub(crate) fn query_sql_surface_mismatch(
1918        mismatch: diagnostic_code::SqlSurfaceMismatchCode,
1919    ) -> Self {
1920        Self {
1921            class: ErrorClass::Unsupported,
1922            origin: ErrorOrigin::Query,
1923            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlSurfaceMismatch {
1924                mismatch,
1925            })),
1926        }
1927    }
1928
1929    /// Construct a query-origin unsupported SQL write boundary error.
1930    pub(crate) fn query_sql_write_boundary(
1931        boundary: diagnostic_code::SqlWriteBoundaryCode,
1932    ) -> Self {
1933        Self {
1934            class: ErrorClass::Unsupported,
1935            origin: ErrorOrigin::Query,
1936            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlWriteBoundary {
1937                boundary,
1938            })),
1939        }
1940    }
1941
1942    /// Construct one query-origin SQL write-boundary error with bounded numeric context.
1943    pub(crate) fn query_sql_write_boundary_with_facts(
1944        boundary: diagnostic_code::SqlWriteBoundaryCode,
1945        facts: Vec<(diagnostic_code::DiagnosticFactTag, u64)>,
1946    ) -> Self {
1947        Self::with_diagnostic_facts(
1948            ErrorClass::Unsupported,
1949            ErrorOrigin::Query,
1950            Some(diagnostic_code::DiagnosticDetail::SqlWriteBoundary { boundary }),
1951            facts,
1952        )
1953    }
1954
1955    pub fn store_not_found(_key: impl Sized) -> Self {
1956        Self {
1957            class: ErrorClass::NotFound,
1958            origin: ErrorOrigin::Store,
1959            detail: Some(ErrorDetail::Store(StoreError::NotFound)),
1960        }
1961    }
1962
1963    /// Construct a standardized unsupported-entity-path error.
1964    pub fn unsupported_entity_path(_path: impl Sized) -> Self {
1965        Self::store_unsupported()
1966    }
1967
1968    /// Construct an index-plan corruption error with a canonical prefix.
1969    #[cold]
1970    #[inline(never)]
1971    pub(crate) fn index_plan_corruption(origin: ErrorOrigin) -> Self {
1972        Self::new(ErrorClass::Corruption, origin)
1973    }
1974
1975    /// Construct an index-plan corruption error for index-origin failures.
1976    #[cold]
1977    #[inline(never)]
1978    pub(crate) fn index_plan_index_corruption() -> Self {
1979        Self::index_plan_corruption(ErrorOrigin::Index)
1980    }
1981
1982    /// Construct an index-plan corruption error for store-origin failures.
1983    #[cold]
1984    #[inline(never)]
1985    pub(crate) fn index_plan_store_corruption() -> Self {
1986        Self::index_plan_corruption(ErrorOrigin::Store)
1987    }
1988
1989    /// Construct an index-plan corruption error for serialize-origin failures.
1990    #[cold]
1991    #[inline(never)]
1992    pub(crate) fn index_plan_serialize_corruption() -> Self {
1993        Self::index_plan_corruption(ErrorOrigin::Serialize)
1994    }
1995
1996    /// Construct an index-plan invariant violation error with a canonical prefix.
1997    #[cfg(test)]
1998    pub(crate) fn index_plan_invariant(origin: ErrorOrigin) -> Self {
1999        Self::new(ErrorClass::InvariantViolation, origin)
2000    }
2001
2002    /// Construct an index-plan invariant violation error for store-origin failures.
2003    #[cfg(test)]
2004    pub(crate) fn index_plan_store_invariant() -> Self {
2005        Self::index_plan_invariant(ErrorOrigin::Store)
2006    }
2007
2008    /// Construct an index-origin conflict without claiming accepted identity.
2009    ///
2010    /// Live accepted uniqueness violations use compact accepted-constraint facts.
2011    /// Schema-domain staging and activation findings use this compact
2012    /// classification before an accepted write-admission diagnostic exists.
2013    pub(crate) fn index_conflict() -> Self {
2014        Self::new(ErrorClass::Conflict, ErrorOrigin::Index)
2015    }
2016}
2017
2018impl From<diagnostic_code::QueryReadAdmissionCode> for InternalError {
2019    fn from(reason: diagnostic_code::QueryReadAdmissionCode) -> Self {
2020        Self {
2021            class: ErrorClass::Unsupported,
2022            origin: ErrorOrigin::Query,
2023            detail: Some(ErrorDetail::Query(QueryErrorDetail::QueryReadAdmission {
2024                reason,
2025            })),
2026        }
2027    }
2028}
2029
2030impl fmt::Debug for InternalError {
2031    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2032        fmt_compact_diagnostic(
2033            f,
2034            self.diagnostic_code(),
2035            self.detail
2036                .as_ref()
2037                .and_then(ErrorDetail::diagnostic_detail),
2038        )
2039    }
2040}
2041
2042impl fmt::Display for InternalError {
2043    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2044        f.write_str(self.message())
2045    }
2046}
2047
2048impl std::error::Error for InternalError {}
2049
2050///
2051/// ConstraintValuePathComponent
2052///
2053/// Stable accepted identity or finite-value coordinate in one targeted-rule
2054/// violation. Display names are deliberately absent so renames cannot change
2055/// the diagnostic identity.
2056///
2057
2058#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
2059pub enum ConstraintValuePathComponent {
2060    /// Persisted root field whose admitted value was traversed.
2061    RootField { field_id: u32 },
2062
2063    /// Accepted record member selected by immutable composite/member identity.
2064    RecordMember {
2065        composite_type_id: u32,
2066        member_id: u32,
2067    },
2068
2069    /// Tuple element selected by accepted composite identity and ordinal.
2070    TupleElement {
2071        composite_type_id: u32,
2072        ordinal: u32,
2073    },
2074
2075    /// Transparent accepted newtype boundary.
2076    Newtype { composite_type_id: u32 },
2077
2078    /// Selected accepted enum variant.
2079    EnumVariant { enum_type_id: u32, variant_id: u32 },
2080
2081    /// List element in admitted order.
2082    ListElement { index: u32 },
2083
2084    /// Set element in canonical admitted order.
2085    SetElement { index: u32 },
2086
2087    /// Map key in canonical entry order.
2088    MapEntryKey { index: u32 },
2089
2090    /// Map value in canonical entry order.
2091    MapEntryValue { index: u32 },
2092}
2093
2094impl fmt::Display for ConstraintValuePathComponent {
2095    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2096        match self {
2097            Self::RootField { field_id } => write!(f, "field#{field_id}"),
2098            Self::RecordMember {
2099                composite_type_id,
2100                member_id,
2101            } => write!(f, "record#{composite_type_id}.member#{member_id}"),
2102            Self::TupleElement {
2103                composite_type_id,
2104                ordinal,
2105            } => write!(f, "tuple#{composite_type_id}[{ordinal}]"),
2106            Self::Newtype { composite_type_id } => write!(f, "newtype#{composite_type_id}"),
2107            Self::EnumVariant {
2108                enum_type_id,
2109                variant_id,
2110            } => write!(f, "enum#{enum_type_id}.variant#{variant_id}"),
2111            Self::ListElement { index } => write!(f, "list[{index}]"),
2112            Self::SetElement { index } => write!(f, "set[{index}]"),
2113            Self::MapEntryKey { index } => write!(f, "map[{index}].key"),
2114            Self::MapEntryValue { index } => write!(f, "map[{index}].value"),
2115        }
2116    }
2117}
2118
2119///
2120/// ConstraintValuePath
2121///
2122/// Bounded typed path to the first deterministic failing value occurrence.
2123///
2124
2125#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
2126pub struct ConstraintValuePath {
2127    components: Vec<ConstraintValuePathComponent>,
2128}
2129
2130impl ConstraintValuePath {
2131    /// Build one already-bounded accepted occurrence path.
2132    #[must_use]
2133    pub(crate) const fn new(components: Vec<ConstraintValuePathComponent>) -> Self {
2134        Self { components }
2135    }
2136
2137    /// Borrow the stable accepted components.
2138    #[must_use]
2139    pub const fn components(&self) -> &[ConstraintValuePathComponent] {
2140        self.components.as_slice()
2141    }
2142}
2143
2144impl fmt::Display for ConstraintValuePath {
2145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2146        for (ordinal, component) in self.components.iter().enumerate() {
2147            if ordinal != 0 {
2148                f.write_str("/")?;
2149            }
2150            component.fmt(f)?;
2151        }
2152        Ok(())
2153    }
2154}
2155
2156///
2157/// ConstraintValidationFindingOutput
2158///
2159/// Bounded historical validation evidence returned only by explicit schema
2160/// validation operations. Names are resolved by host tooling from the exact
2161/// accepted fingerprint and immutable numeric identities.
2162///
2163
2164#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
2165pub struct ConstraintValidationFindingOutput {
2166    accepted_schema_fingerprint: [u8; 16],
2167    entity_tag: u64,
2168    constraint_id: u32,
2169    primary_key: Vec<u8>,
2170    field_ids: Vec<u32>,
2171    value_path: Option<ConstraintValuePath>,
2172    error_code: u16,
2173}
2174
2175impl ConstraintValidationFindingOutput {
2176    /// Build one already-bounded historical validation finding.
2177    #[must_use]
2178    pub(crate) const fn new(
2179        accepted_schema_fingerprint: [u8; 16],
2180        entity_tag: u64,
2181        constraint_id: u32,
2182        primary_key: Vec<u8>,
2183        field_ids: Vec<u32>,
2184        value_path: Option<ConstraintValuePath>,
2185        error_code: u16,
2186    ) -> Self {
2187        Self {
2188            accepted_schema_fingerprint,
2189            entity_tag,
2190            constraint_id,
2191            primary_key,
2192            field_ids,
2193            value_path,
2194            error_code,
2195        }
2196    }
2197
2198    /// Return the exact accepted-schema fingerprint that binds every numeric identity.
2199    #[must_use]
2200    pub const fn accepted_schema_fingerprint(&self) -> [u8; 16] {
2201        self.accepted_schema_fingerprint
2202    }
2203
2204    /// Return the stable accepted entity identity.
2205    #[must_use]
2206    pub const fn entity_tag(&self) -> u64 {
2207        self.entity_tag
2208    }
2209
2210    /// Return the stable accepted constraint identity.
2211    #[must_use]
2212    pub const fn constraint_id(&self) -> u32 {
2213        self.constraint_id
2214    }
2215
2216    /// Borrow the bounded canonical persisted primary-key locator.
2217    #[must_use]
2218    pub const fn primary_key(&self) -> &[u8] {
2219        self.primary_key.as_slice()
2220    }
2221
2222    /// Borrow immutable accepted field identities implicated by the finding.
2223    #[must_use]
2224    pub const fn field_ids(&self) -> &[u32] {
2225        self.field_ids.as_slice()
2226    }
2227
2228    /// Borrow the typed concrete value path for a targeted-rule violation.
2229    #[must_use]
2230    pub const fn value_path(&self) -> Option<&ConstraintValuePath> {
2231        self.value_path.as_ref()
2232    }
2233
2234    /// Return the compact stable error code for this exact failure.
2235    #[must_use]
2236    pub const fn error_code(&self) -> diagnostic_code::ErrorCode {
2237        diagnostic_code::ErrorCode::from_raw(self.error_code)
2238    }
2239
2240    /// Return the broad public error class derived from the compact code.
2241    #[must_use]
2242    pub const fn error_class(&self) -> diagnostic_code::ErrorClass {
2243        self.error_code().class()
2244    }
2245}
2246
2247/// Complete bounded numeric authority needed to publish E210 or E212 facts.
2248#[derive(Clone)]
2249pub(crate) struct AcceptedConstraintFactContext {
2250    fingerprint_method: u8,
2251    accepted_schema_fingerprint: [u8; 16],
2252    entity_tag: u64,
2253    constraint_id: u32,
2254    constraint_kind: diagnostic_code::DiagnosticConstraintKind,
2255    mutation: Option<MutationDiagnosticContext>,
2256    value_path: Option<ConstraintValuePath>,
2257}
2258
2259impl AcceptedConstraintFactContext {
2260    #[must_use]
2261    pub(crate) fn write_admission(
2262        fingerprint_method: u8,
2263        accepted_schema_fingerprint: [u8; 16],
2264        entity_tag: u64,
2265        constraint_id: u32,
2266        constraint_kind: diagnostic_code::DiagnosticConstraintKind,
2267        mutation: Option<MutationDiagnosticContext>,
2268        value_path: Option<ConstraintValuePath>,
2269    ) -> Self {
2270        debug_assert!(mutation.is_none_or(|context| context.entity_tag() == entity_tag));
2271        Self {
2272            fingerprint_method,
2273            accepted_schema_fingerprint,
2274            entity_tag,
2275            constraint_id,
2276            constraint_kind,
2277            mutation,
2278            value_path,
2279        }
2280    }
2281
2282    fn facts(self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
2283        let path_len = self
2284            .value_path
2285            .as_ref()
2286            .map_or(0, |path| path.components().len());
2287        let mutation_fact_count = self.mutation.map_or(0, |mutation| {
2288            1 + usize::from(mutation.batch_position.is_some())
2289        });
2290        let mut facts = Vec::with_capacity(7 + mutation_fact_count + path_len);
2291        append_accepted_schema_facts(
2292            &mut facts,
2293            self.fingerprint_method,
2294            self.accepted_schema_fingerprint,
2295        );
2296        facts.push((
2297            diagnostic_code::DiagnosticFactTag::EntityTag,
2298            self.entity_tag,
2299        ));
2300        facts.push((
2301            diagnostic_code::DiagnosticFactTag::ConstraintId,
2302            u64::from(self.constraint_id),
2303        ));
2304        facts.push((
2305            diagnostic_code::DiagnosticFactTag::ConstraintKind,
2306            self.constraint_kind.raw(),
2307        ));
2308        facts.push((
2309            diagnostic_code::DiagnosticFactTag::ConstraintContext,
2310            diagnostic_code::DiagnosticConstraintContext::WriteAdmission.raw(),
2311        ));
2312        if let Some(mutation) = self.mutation {
2313            mutation.append_operation_facts(&mut facts);
2314        }
2315        if let Some(path) = self.value_path {
2316            for component in path.components {
2317                facts.push(constraint_value_path_fact(component));
2318            }
2319        }
2320        debug_assert!(facts.len() <= diagnostic_code::MAX_PUBLIC_DIAGNOSTIC_FACTS);
2321        facts
2322    }
2323}
2324
2325/// Mutation and constraint errors use the same lossless accepted-schema identity.
2326fn append_accepted_schema_facts(
2327    facts: &mut Vec<(diagnostic_code::DiagnosticFactTag, u64)>,
2328    method: u8,
2329    fingerprint: [u8; 16],
2330) {
2331    facts.extend([
2332        (
2333            diagnostic_code::DiagnosticFactTag::AcceptedSchemaFingerprintMethod,
2334            u64::from(method),
2335        ),
2336        (
2337            diagnostic_code::DiagnosticFactTag::AcceptedSchemaFingerprintHigh,
2338            u64::from_be_bytes([
2339                fingerprint[0],
2340                fingerprint[1],
2341                fingerprint[2],
2342                fingerprint[3],
2343                fingerprint[4],
2344                fingerprint[5],
2345                fingerprint[6],
2346                fingerprint[7],
2347            ]),
2348        ),
2349        (
2350            diagnostic_code::DiagnosticFactTag::AcceptedSchemaFingerprintLow,
2351            u64::from_be_bytes([
2352                fingerprint[8],
2353                fingerprint[9],
2354                fingerprint[10],
2355                fingerprint[11],
2356                fingerprint[12],
2357                fingerprint[13],
2358                fingerprint[14],
2359                fingerprint[15],
2360            ]),
2361        ),
2362    ]);
2363}
2364
2365fn constraint_value_path_fact(
2366    component: ConstraintValuePathComponent,
2367) -> (diagnostic_code::DiagnosticFactTag, u64) {
2368    use diagnostic_code::DiagnosticFactTag;
2369    match component {
2370        ConstraintValuePathComponent::RootField { field_id } => {
2371            (DiagnosticFactTag::RootField, u64::from(field_id))
2372        }
2373        ConstraintValuePathComponent::RecordMember {
2374            composite_type_id,
2375            member_id,
2376        } => (
2377            DiagnosticFactTag::RecordMember,
2378            diagnostic_code::pack_u32_pair(composite_type_id, member_id),
2379        ),
2380        ConstraintValuePathComponent::TupleElement {
2381            composite_type_id,
2382            ordinal,
2383        } => (
2384            DiagnosticFactTag::TupleElement,
2385            diagnostic_code::pack_u32_pair(composite_type_id, ordinal),
2386        ),
2387        ConstraintValuePathComponent::Newtype { composite_type_id } => {
2388            (DiagnosticFactTag::Newtype, u64::from(composite_type_id))
2389        }
2390        ConstraintValuePathComponent::EnumVariant {
2391            enum_type_id,
2392            variant_id,
2393        } => (
2394            DiagnosticFactTag::EnumVariant,
2395            diagnostic_code::pack_u32_pair(enum_type_id, variant_id),
2396        ),
2397        ConstraintValuePathComponent::ListElement { index } => {
2398            (DiagnosticFactTag::ListElement, u64::from(index))
2399        }
2400        ConstraintValuePathComponent::SetElement { index } => {
2401            (DiagnosticFactTag::SetElement, u64::from(index))
2402        }
2403        ConstraintValuePathComponent::MapEntryKey { index } => {
2404            (DiagnosticFactTag::MapEntryKey, u64::from(index))
2405        }
2406        ConstraintValuePathComponent::MapEntryValue { index } => {
2407            (DiagnosticFactTag::MapEntryValue, u64::from(index))
2408        }
2409    }
2410}
2411
2412///
2413/// ErrorDetail
2414///
2415/// Structured, origin-specific error detail carried by [`InternalError`].
2416/// This enum is intentionally extensible.
2417///
2418
2419pub enum ErrorDetail {
2420    /// Compact code/detail plus safe numeric context for one public failure.
2421    DiagnosticFacts(Box<DiagnosticFactDetail>),
2422    /// Executor-owned mutation and query execution details.
2423    Executor(ExecutorErrorDetail),
2424    Store(StoreError),
2425    Query(QueryErrorDetail),
2426    Recovery(RecoveryErrorDetail),
2427    // Future-proofing:
2428    // Index(IndexError),
2429}
2430
2431/// Executor-specific structured error detail.
2432pub enum ExecutorErrorDetail {
2433    /// A complete insert or replacement omitted one or more required fields.
2434    MutationRequiredFieldMissing,
2435    /// A logical mutation would move accepted managed time backward.
2436    MutationManagedTimestampRegression,
2437    /// A caller explicitly authored a field owned by accepted database policy.
2438    MutationDatabaseOwnedFieldExplicit,
2439    /// A mixed structural mutation batch contained no operations.
2440    MutationBatchEmpty,
2441    /// A mixed structural mutation batch exceeded its operation-count bound.
2442    MutationBatchTooManyItems,
2443    /// A mixed structural mutation batch exceeded its staged-byte bound.
2444    MutationBatchStagedBytesExceeded,
2445    /// A mixed structural mutation result exceeded its encoded response bound.
2446    MutationBatchResultBytesExceeded,
2447    /// A mixed structural mutation batch crossed an accepted store boundary.
2448    MutationBatchStoreMismatch,
2449    /// A mixed structural mutation batch exceeded its distinct-entity bound.
2450    MutationBatchTooManyEntities,
2451    /// More than one mixed structural operation targeted the same accepted key.
2452    MutationBatchDuplicateKey,
2453    /// Accepted row-constraint metadata or compiled state was inconsistent.
2454    AcceptedRowConstraintProgramCorrupt,
2455}
2456
2457///
2458/// RecoveryErrorDetail
2459///
2460/// Recovery-origin structured error detail payload.
2461///
2462
2463pub enum RecoveryErrorDetail {
2464    UnsupportedFormatVersion { found: Option<u16>, required: u16 },
2465
2466    MalformedFormatMarker { reason: RecoveryFormatMarkerError },
2467}
2468
2469/// Store boot-marker corruption classification.
2470#[derive(Clone, Copy, Eq, PartialEq)]
2471pub enum RecoveryFormatMarkerError {
2472    Magic,
2473    Checksum,
2474    State,
2475}
2476
2477impl RecoveryFormatMarkerError {
2478    const fn diagnostic_decode_reason(self) -> diagnostic_code::DiagnosticDecodeReason {
2479        match self {
2480            Self::Magic => diagnostic_code::DiagnosticDecodeReason::RecoveryMarkerMagic,
2481            Self::Checksum => diagnostic_code::DiagnosticDecodeReason::RecoveryMarkerChecksum,
2482            Self::State => diagnostic_code::DiagnosticDecodeReason::RecoveryMarkerState,
2483        }
2484    }
2485}
2486
2487///
2488/// StoreError
2489///
2490/// Store-specific structured error detail.
2491/// Never returned directly; always wrapped in [`ErrorDetail::Store`].
2492///
2493
2494pub enum StoreError {
2495    NotFound,
2496
2497    Corrupt,
2498
2499    InvariantViolation,
2500
2501    SchemaDdlPublicationRaceLost,
2502
2503    SchemaDdlRewriteRequiresMigration,
2504
2505    SchemaMigration {
2506        reason: diagnostic_code::SchemaMigrationCode,
2507    },
2508
2509    SchemaRowLayoutVersionExhausted,
2510
2511    JournalMutationRevisionExhausted,
2512
2513    SchemaTransitionBudgetExceeded {
2514        resource: SchemaTransitionBudgetResource,
2515    },
2516
2517    /// A generated field would collide with an accepted DDL-owned slot.
2518    SchemaGeneratedFieldAfterDdlField,
2519
2520    /// A live generated constraint activation no longer matches its proposal.
2521    SchemaGeneratedConstraintActivationStale,
2522}
2523
2524///
2525/// QueryErrorDetail
2526///
2527/// Query-origin structured error detail payload.
2528///
2529
2530pub enum QueryErrorDetail {
2531    NumericOverflow,
2532
2533    NumericNotRepresentable,
2534
2535    UnsupportedSqlFeature {
2536        feature: diagnostic_code::SqlFeatureCode,
2537    },
2538
2539    SqlLowering {
2540        reason: diagnostic_code::SqlLoweringCode,
2541    },
2542
2543    UnsupportedProjection {
2544        reason: diagnostic_code::QueryProjectionCode,
2545    },
2546
2547    UnknownAggregateTargetField,
2548
2549    QueryReadAdmission {
2550        reason: diagnostic_code::QueryReadAdmissionCode,
2551    },
2552
2553    SqlSurfaceMismatch {
2554        mismatch: diagnostic_code::SqlSurfaceMismatchCode,
2555    },
2556
2557    SqlWriteBoundary {
2558        boundary: diagnostic_code::SqlWriteBoundaryCode,
2559    },
2560
2561    SchemaDdlAdmission {
2562        error: SchemaDdlAdmissionError,
2563    },
2564
2565    StaleSchemaRevision,
2566}
2567
2568impl fmt::Display for QueryErrorDetail {
2569    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2570        f.write_str(COMPACT_QUERY_DIAGNOSTIC_MESSAGE)
2571    }
2572}
2573
2574impl std::error::Error for QueryErrorDetail {}
2575
2576///
2577/// SchemaTransitionBudgetResource
2578///
2579/// Query-visible identity of the exact schema-transition resource cap that
2580/// rejected a complete validation or derived-state stage.
2581///
2582
2583#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2584pub enum SchemaTransitionBudgetResource {
2585    /// Number of physical deletion keys retained for replacement.
2586    DeletionKeys,
2587    /// Number of row-derived projection entries retained for validation.
2588    ProjectionEntries,
2589    /// Deterministic projection and physical-classification work units.
2590    ProjectionWorkUnits,
2591    /// Number of authoritative source rows.
2592    SourceRows,
2593    /// Cumulative bytes of authoritative source rows.
2594    SourceRowBytes,
2595    /// Retained raw payloads plus deterministic-sort workspace bytes.
2596    StagedRawBytes,
2597}
2598
2599///
2600/// SchemaDdlAdmissionError
2601///
2602/// Stable query-visible SQL DDL admission reason. Human diagnostics may carry
2603/// extra version, fingerprint, and target facts beside this machine-readable
2604/// variant.
2605///
2606
2607#[derive(Clone, Copy, Eq, PartialEq)]
2608pub enum SchemaDdlAdmissionError {
2609    MissingExpectedSchemaVersion,
2610
2611    MissingNextSchemaVersion,
2612
2613    StaleExpectedSchemaVersion,
2614
2615    InvalidExpectedSchemaVersion,
2616
2617    InvalidNextSchemaVersion,
2618
2619    AcceptedSchemaChangeWithoutVersionBump,
2620
2621    EmptyVersionBump,
2622
2623    VersionGap,
2624
2625    VersionRollback,
2626
2627    FingerprintMethodMismatch,
2628
2629    UnsupportedTransitionClass,
2630
2631    PhysicalRunnerMissing,
2632
2633    ValidationFailed,
2634
2635    PublicationRaceLost,
2636
2637    InvalidAddColumnDefault,
2638
2639    InvalidAlterColumnDefault,
2640
2641    RowLayoutVersionExhausted,
2642
2643    GeneratedIndexDropRejected,
2644
2645    SchemaRewriteRequiresMigration,
2646
2647    SchemaTransitionBudgetExceeded {
2648        resource: SchemaTransitionBudgetResource,
2649    },
2650
2651    GeneratedFieldDefaultChangeRejected,
2652
2653    GeneratedFieldNullabilityChangeRejected,
2654}
2655
2656impl fmt::Display for SchemaDdlAdmissionError {
2657    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2658        f.write_str(COMPACT_QUERY_DIAGNOSTIC_MESSAGE)
2659    }
2660}
2661
2662impl std::error::Error for SchemaDdlAdmissionError {}
2663
2664impl fmt::Debug for ErrorDetail {
2665    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2666        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2667    }
2668}
2669
2670impl fmt::Debug for ExecutorErrorDetail {
2671    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2672        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2673    }
2674}
2675
2676impl fmt::Debug for StoreError {
2677    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2678        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2679    }
2680}
2681
2682impl fmt::Debug for QueryErrorDetail {
2683    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2684        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2685    }
2686}
2687
2688impl fmt::Debug for RecoveryErrorDetail {
2689    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2690        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2691    }
2692}
2693
2694impl fmt::Debug for RecoveryFormatMarkerError {
2695    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2696        fmt_compact_diagnostic(
2697            f,
2698            diagnostic_code::DiagnosticCode::RuntimeCorruption,
2699            Some(diagnostic_code::DiagnosticDetail::RuntimeKind {
2700                kind: diagnostic_code::RuntimeErrorKind::Corruption,
2701            }),
2702        )
2703    }
2704}
2705
2706impl fmt::Debug for SchemaDdlAdmissionError {
2707    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2708        fmt_compact_diagnostic(
2709            f,
2710            diagnostic_code::DiagnosticCode::SchemaDdlAdmission,
2711            Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2712                reason: self.diagnostic_code(),
2713            }),
2714        )
2715    }
2716}
2717
2718fn fmt_compact_diagnostic(
2719    f: &mut fmt::Formatter<'_>,
2720    code: diagnostic_code::DiagnosticCode,
2721    detail: Option<diagnostic_code::DiagnosticDetail>,
2722) -> fmt::Result {
2723    write!(
2724        f,
2725        "{}",
2726        diagnostic_code::ErrorCode::from_parts(code, detail).raw()
2727    )
2728}
2729
2730impl ErrorDetail {
2731    /// Return the compact diagnostic code for this structured detail.
2732    #[must_use]
2733    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2734        match self {
2735            Self::DiagnosticFacts(detail) => detail.diagnostic.code(),
2736            Self::Executor(error) => error.diagnostic_code(),
2737            Self::Store(error) => error.diagnostic_code(),
2738            Self::Query(error) => error.diagnostic_code(),
2739            Self::Recovery(error) => error.diagnostic_code(),
2740        }
2741    }
2742
2743    /// Return compact structured diagnostic detail when the payload carries one.
2744    #[must_use]
2745    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2746        match self {
2747            Self::DiagnosticFacts(detail) => detail.diagnostic.detail().copied(),
2748            Self::Executor(error) => error.diagnostic_detail(),
2749            Self::Store(error) => error.diagnostic_detail(),
2750            Self::Query(error) => error.diagnostic_detail(),
2751            Self::Recovery(error) => error.diagnostic_detail(),
2752        }
2753    }
2754
2755    /// Project safe typed detail into canonical public numeric facts.
2756    #[must_use]
2757    #[cold]
2758    #[inline(never)]
2759    pub fn diagnostic_facts(&self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
2760        match self {
2761            Self::DiagnosticFacts(detail) => detail.facts.clone(),
2762            Self::Executor(error) => error.diagnostic_facts(),
2763            Self::Query(error) => error.diagnostic_facts(),
2764            Self::Recovery(error) => error.diagnostic_facts(),
2765            Self::Store(_) => Vec::new(),
2766        }
2767    }
2768}
2769
2770impl ExecutorErrorDetail {
2771    /// Return the compact diagnostic code for this executor detail.
2772    #[must_use]
2773    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2774        match self {
2775            Self::MutationRequiredFieldMissing
2776            | Self::MutationDatabaseOwnedFieldExplicit
2777            | Self::MutationBatchEmpty
2778            | Self::MutationBatchTooManyItems
2779            | Self::MutationBatchTooManyEntities
2780            | Self::MutationBatchStagedBytesExceeded
2781            | Self::MutationBatchResultBytesExceeded => {
2782                diagnostic_code::DiagnosticCode::RuntimeUnsupported
2783            }
2784            Self::MutationBatchStoreMismatch | Self::MutationBatchDuplicateKey => {
2785                diagnostic_code::DiagnosticCode::RuntimeConflict
2786            }
2787            Self::MutationManagedTimestampRegression => {
2788                diagnostic_code::DiagnosticCode::RuntimeInvariantViolation
2789            }
2790            Self::AcceptedRowConstraintProgramCorrupt => {
2791                diagnostic_code::DiagnosticCode::RuntimeCorruption
2792            }
2793        }
2794    }
2795
2796    /// Return compact structured diagnostic detail for this executor detail.
2797    #[must_use]
2798    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2799        match self {
2800            Self::MutationRequiredFieldMissing => {
2801                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2802                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationRequiredFieldMissing,
2803                })
2804            }
2805            Self::MutationDatabaseOwnedFieldExplicit => {
2806                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2807                    boundary:
2808                        diagnostic_code::RuntimeBoundaryCode::MutationDatabaseOwnedFieldExplicit,
2809                })
2810            }
2811            Self::MutationBatchEmpty => Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2812                boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchEmpty,
2813            }),
2814            Self::MutationBatchTooManyItems => {
2815                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2816                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchTooManyItems,
2817                })
2818            }
2819            Self::MutationBatchStagedBytesExceeded => {
2820                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2821                    boundary:
2822                        diagnostic_code::RuntimeBoundaryCode::MutationBatchStagedBytesExceeded,
2823                })
2824            }
2825            Self::MutationBatchResultBytesExceeded => {
2826                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2827                    boundary:
2828                        diagnostic_code::RuntimeBoundaryCode::MutationBatchResultBytesExceeded,
2829                })
2830            }
2831            Self::MutationBatchStoreMismatch => {
2832                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2833                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchStoreMismatch,
2834                })
2835            }
2836            Self::MutationBatchTooManyEntities => {
2837                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2838                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchTooManyEntities,
2839                })
2840            }
2841            Self::MutationBatchDuplicateKey => {
2842                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2843                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchDuplicateKey,
2844                })
2845            }
2846            Self::MutationManagedTimestampRegression => {
2847                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2848                    boundary:
2849                        diagnostic_code::RuntimeBoundaryCode::MutationManagedTimestampRegression,
2850                })
2851            }
2852            Self::AcceptedRowConstraintProgramCorrupt => {
2853                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2854                    boundary:
2855                        diagnostic_code::RuntimeBoundaryCode::AcceptedRowConstraintProgramCorrupt,
2856                })
2857            }
2858        }
2859    }
2860
2861    /// Project safe mutation detail into canonical public numeric facts.
2862    #[must_use]
2863    #[cold]
2864    #[inline(never)]
2865    pub const fn diagnostic_facts(&self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
2866        Vec::new()
2867    }
2868}
2869
2870impl RecoveryErrorDetail {
2871    /// Return the compact diagnostic code for this recovery detail.
2872    #[must_use]
2873    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2874        match self {
2875            Self::UnsupportedFormatVersion { .. } => {
2876                diagnostic_code::DiagnosticCode::RuntimeIncompatiblePersistedFormat
2877            }
2878            Self::MalformedFormatMarker { .. } => {
2879                diagnostic_code::DiagnosticCode::RuntimeCorruption
2880            }
2881        }
2882    }
2883
2884    /// Return compact structured diagnostic detail for this recovery detail.
2885    #[must_use]
2886    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2887        let kind = match self {
2888            Self::UnsupportedFormatVersion { .. } => {
2889                diagnostic_code::RuntimeErrorKind::IncompatiblePersistedFormat
2890            }
2891            Self::MalformedFormatMarker { .. } => diagnostic_code::RuntimeErrorKind::Corruption,
2892        };
2893
2894        Some(diagnostic_code::DiagnosticDetail::RuntimeKind { kind })
2895    }
2896
2897    /// Project database-format recovery context without retaining marker bytes.
2898    #[must_use]
2899    pub fn diagnostic_facts(&self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
2900        match self {
2901            Self::UnsupportedFormatVersion { found, required } => {
2902                let mut facts = Vec::with_capacity(usize::from(found.is_some()) + 1);
2903                facts.push((
2904                    diagnostic_code::DiagnosticFactTag::ExpectedVersion,
2905                    u64::from(*required),
2906                ));
2907                if let Some(found) = found {
2908                    facts.push((
2909                        diagnostic_code::DiagnosticFactTag::ActualVersion,
2910                        u64::from(*found),
2911                    ));
2912                }
2913                facts
2914            }
2915            Self::MalformedFormatMarker { reason } => vec![(
2916                diagnostic_code::DiagnosticFactTag::DecodeReason,
2917                reason.diagnostic_decode_reason().raw(),
2918            )],
2919        }
2920    }
2921}
2922
2923impl StoreError {
2924    /// Return the compact diagnostic code for this store detail.
2925    #[must_use]
2926    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2927        match self {
2928            Self::NotFound => diagnostic_code::DiagnosticCode::StoreNotFound,
2929            Self::Corrupt => diagnostic_code::DiagnosticCode::StoreCorruption,
2930            Self::InvariantViolation => diagnostic_code::DiagnosticCode::StoreInvariantViolation,
2931            Self::SchemaDdlPublicationRaceLost
2932            | Self::SchemaDdlRewriteRequiresMigration
2933            | Self::SchemaRowLayoutVersionExhausted
2934            | Self::SchemaTransitionBudgetExceeded { .. } => {
2935                diagnostic_code::DiagnosticCode::SchemaDdlAdmission
2936            }
2937            Self::JournalMutationRevisionExhausted | Self::SchemaGeneratedFieldAfterDdlField => {
2938                diagnostic_code::DiagnosticCode::RuntimeUnsupported
2939            }
2940            Self::SchemaGeneratedConstraintActivationStale => {
2941                diagnostic_code::DiagnosticCode::RuntimeConflict
2942            }
2943            Self::SchemaMigration { reason } => reason.diagnostic_code(),
2944        }
2945    }
2946
2947    /// Return compact structured diagnostic detail when the store error has one.
2948    #[must_use]
2949    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2950        match self {
2951            Self::SchemaDdlPublicationRaceLost => {
2952                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2953                    reason: diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost,
2954                })
2955            }
2956            Self::SchemaDdlRewriteRequiresMigration => {
2957                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2958                    reason: diagnostic_code::SchemaDdlAdmissionCode::SchemaRewriteRequiresMigration,
2959                })
2960            }
2961            Self::SchemaMigration { reason } => {
2962                Some(diagnostic_code::DiagnosticDetail::SchemaMigration { reason: *reason })
2963            }
2964            Self::SchemaRowLayoutVersionExhausted => {
2965                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2966                    reason: diagnostic_code::SchemaDdlAdmissionCode::RowLayoutVersionExhausted,
2967                })
2968            }
2969            Self::JournalMutationRevisionExhausted => {
2970                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2971                    boundary:
2972                        diagnostic_code::RuntimeBoundaryCode::JournalMutationRevisionExhausted,
2973                })
2974            }
2975            Self::SchemaTransitionBudgetExceeded { .. } => {
2976                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2977                    reason: diagnostic_code::SchemaDdlAdmissionCode::SchemaTransitionBudgetExceeded,
2978                })
2979            }
2980            Self::SchemaGeneratedFieldAfterDdlField => {
2981                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2982                    boundary: diagnostic_code::RuntimeBoundaryCode::GeneratedFieldAfterDdlField,
2983                })
2984            }
2985            Self::SchemaGeneratedConstraintActivationStale => {
2986                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2987                    boundary:
2988                        diagnostic_code::RuntimeBoundaryCode::GeneratedConstraintActivationStale,
2989                })
2990            }
2991            Self::NotFound | Self::Corrupt | Self::InvariantViolation => None,
2992        }
2993    }
2994}
2995
2996impl QueryErrorDetail {
2997    /// Return the compact diagnostic code for this query detail.
2998    #[must_use]
2999    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
3000        match self {
3001            Self::NumericOverflow => diagnostic_code::DiagnosticCode::QueryNumericOverflow,
3002            Self::NumericNotRepresentable => {
3003                diagnostic_code::DiagnosticCode::QueryNumericNotRepresentable
3004            }
3005            Self::UnsupportedSqlFeature { .. } => {
3006                diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature
3007            }
3008            Self::SqlLowering { .. } => diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature,
3009            Self::UnsupportedProjection { .. } => {
3010                diagnostic_code::DiagnosticCode::QueryUnsupportedProjection
3011            }
3012            Self::UnknownAggregateTargetField => {
3013                diagnostic_code::DiagnosticCode::QueryUnknownAggregateTargetField
3014            }
3015            Self::QueryReadAdmission { .. } => diagnostic_code::DiagnosticCode::QueryReadAdmission,
3016            Self::SqlSurfaceMismatch { .. } => {
3017                diagnostic_code::DiagnosticCode::QuerySqlSurfaceMismatch
3018            }
3019            Self::SqlWriteBoundary { .. } => diagnostic_code::DiagnosticCode::QuerySqlWriteBoundary,
3020            Self::SchemaDdlAdmission { .. } => diagnostic_code::DiagnosticCode::SchemaDdlAdmission,
3021            Self::StaleSchemaRevision => diagnostic_code::DiagnosticCode::RuntimeConflict,
3022        }
3023    }
3024
3025    /// Return compact structured diagnostic detail when the query detail has one.
3026    #[must_use]
3027    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
3028        match self {
3029            Self::UnsupportedSqlFeature { feature } => {
3030                Some(diagnostic_code::DiagnosticDetail::UnsupportedSqlFeature { feature: *feature })
3031            }
3032            Self::SqlLowering { reason } => {
3033                Some(diagnostic_code::DiagnosticDetail::SqlLowering { reason: *reason })
3034            }
3035            Self::UnsupportedProjection { reason } => {
3036                Some(diagnostic_code::DiagnosticDetail::QueryProjection { reason: *reason })
3037            }
3038            Self::QueryReadAdmission { reason } => {
3039                Some(diagnostic_code::DiagnosticDetail::QueryReadAdmission { reason: *reason })
3040            }
3041            Self::SqlSurfaceMismatch { mismatch } => {
3042                Some(diagnostic_code::DiagnosticDetail::SqlSurfaceMismatch {
3043                    mismatch: *mismatch,
3044                })
3045            }
3046            Self::SqlWriteBoundary { boundary } => {
3047                Some(diagnostic_code::DiagnosticDetail::SqlWriteBoundary {
3048                    boundary: *boundary,
3049                })
3050            }
3051            Self::SchemaDdlAdmission { error } => {
3052                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
3053                    reason: error.diagnostic_code(),
3054                })
3055            }
3056            Self::NumericOverflow
3057            | Self::NumericNotRepresentable
3058            | Self::UnknownAggregateTargetField
3059            | Self::StaleSchemaRevision => None,
3060        }
3061    }
3062
3063    /// Project safe query detail into canonical public numeric facts.
3064    #[must_use]
3065    #[cold]
3066    #[inline(never)]
3067    pub const fn diagnostic_facts(&self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
3068        Vec::new()
3069    }
3070}
3071
3072impl SchemaDdlAdmissionError {
3073    /// Return the compact diagnostic code for this SQL DDL admission reason.
3074    #[must_use]
3075    pub const fn diagnostic_code(&self) -> diagnostic_code::SchemaDdlAdmissionCode {
3076        match self {
3077            Self::MissingExpectedSchemaVersion => {
3078                diagnostic_code::SchemaDdlAdmissionCode::MissingExpectedSchemaVersion
3079            }
3080            Self::MissingNextSchemaVersion => {
3081                diagnostic_code::SchemaDdlAdmissionCode::MissingNextSchemaVersion
3082            }
3083            Self::StaleExpectedSchemaVersion => {
3084                diagnostic_code::SchemaDdlAdmissionCode::StaleExpectedSchemaVersion
3085            }
3086            Self::InvalidExpectedSchemaVersion => {
3087                diagnostic_code::SchemaDdlAdmissionCode::InvalidExpectedSchemaVersion
3088            }
3089            Self::InvalidNextSchemaVersion => {
3090                diagnostic_code::SchemaDdlAdmissionCode::InvalidNextSchemaVersion
3091            }
3092            Self::AcceptedSchemaChangeWithoutVersionBump => {
3093                diagnostic_code::SchemaDdlAdmissionCode::AcceptedSchemaChangeWithoutVersionBump
3094            }
3095            Self::EmptyVersionBump => diagnostic_code::SchemaDdlAdmissionCode::EmptyVersionBump,
3096            Self::VersionGap => diagnostic_code::SchemaDdlAdmissionCode::VersionGap,
3097            Self::VersionRollback => diagnostic_code::SchemaDdlAdmissionCode::VersionRollback,
3098            Self::FingerprintMethodMismatch => {
3099                diagnostic_code::SchemaDdlAdmissionCode::FingerprintMethodMismatch
3100            }
3101            Self::UnsupportedTransitionClass => {
3102                diagnostic_code::SchemaDdlAdmissionCode::UnsupportedTransitionClass
3103            }
3104            Self::PhysicalRunnerMissing => {
3105                diagnostic_code::SchemaDdlAdmissionCode::PhysicalRunnerMissing
3106            }
3107            Self::ValidationFailed => diagnostic_code::SchemaDdlAdmissionCode::ValidationFailed,
3108            Self::PublicationRaceLost => {
3109                diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost
3110            }
3111            Self::InvalidAddColumnDefault => {
3112                diagnostic_code::SchemaDdlAdmissionCode::InvalidAddColumnDefault
3113            }
3114            Self::InvalidAlterColumnDefault => {
3115                diagnostic_code::SchemaDdlAdmissionCode::InvalidAlterColumnDefault
3116            }
3117            Self::GeneratedIndexDropRejected => {
3118                diagnostic_code::SchemaDdlAdmissionCode::GeneratedIndexDropRejected
3119            }
3120            Self::SchemaRewriteRequiresMigration => {
3121                diagnostic_code::SchemaDdlAdmissionCode::SchemaRewriteRequiresMigration
3122            }
3123            Self::SchemaTransitionBudgetExceeded { .. } => {
3124                diagnostic_code::SchemaDdlAdmissionCode::SchemaTransitionBudgetExceeded
3125            }
3126            Self::GeneratedFieldDefaultChangeRejected => {
3127                diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldDefaultChangeRejected
3128            }
3129            Self::GeneratedFieldNullabilityChangeRejected => {
3130                diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldNullabilityChangeRejected
3131            }
3132            Self::RowLayoutVersionExhausted => {
3133                diagnostic_code::SchemaDdlAdmissionCode::RowLayoutVersionExhausted
3134            }
3135        }
3136    }
3137}
3138
3139///
3140/// ErrorClass
3141/// Internal error taxonomy for runtime classification.
3142/// Not a stable API; may change without notice.
3143///
3144
3145#[repr(u8)]
3146#[derive(Clone, Copy, Eq, PartialEq)]
3147pub enum ErrorClass {
3148    Corruption,
3149    IncompatiblePersistedFormat,
3150    NotFound,
3151    Internal,
3152    Conflict,
3153    Unsupported,
3154    InvariantViolation,
3155}
3156
3157impl ErrorClass {
3158    /// Return a compact diagnostic code for this broad class and origin pair.
3159    #[must_use]
3160    pub const fn diagnostic_code(self, origin: ErrorOrigin) -> diagnostic_code::DiagnosticCode {
3161        match self {
3162            Self::Corruption if matches!(origin, ErrorOrigin::Store) => {
3163                diagnostic_code::DiagnosticCode::StoreCorruption
3164            }
3165            Self::Corruption => diagnostic_code::DiagnosticCode::RuntimeCorruption,
3166            Self::IncompatiblePersistedFormat => {
3167                diagnostic_code::DiagnosticCode::RuntimeIncompatiblePersistedFormat
3168            }
3169            Self::NotFound if matches!(origin, ErrorOrigin::Store) => {
3170                diagnostic_code::DiagnosticCode::StoreNotFound
3171            }
3172            Self::NotFound => diagnostic_code::DiagnosticCode::RuntimeNotFound,
3173            Self::Internal => diagnostic_code::DiagnosticCode::RuntimeInternal,
3174            Self::Conflict => diagnostic_code::DiagnosticCode::RuntimeConflict,
3175            Self::Unsupported if matches!(origin, ErrorOrigin::Cursor) => {
3176                diagnostic_code::DiagnosticCode::QueryInvalidContinuationCursor
3177            }
3178            Self::Unsupported => diagnostic_code::DiagnosticCode::RuntimeUnsupported,
3179            Self::InvariantViolation if matches!(origin, ErrorOrigin::Store) => {
3180                diagnostic_code::DiagnosticCode::StoreInvariantViolation
3181            }
3182            Self::InvariantViolation => diagnostic_code::DiagnosticCode::RuntimeInvariantViolation,
3183        }
3184    }
3185}
3186
3187impl fmt::Debug for ErrorClass {
3188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3189        write!(f, "{}", *self as u8)
3190    }
3191}
3192
3193///
3194/// ErrorOrigin
3195/// Internal origin taxonomy for runtime classification.
3196/// Not a stable API; may change without notice.
3197///
3198
3199#[repr(u8)]
3200#[derive(Clone, Copy, Eq, PartialEq)]
3201pub enum ErrorOrigin {
3202    Serialize,
3203    Store,
3204    Index,
3205    Identity,
3206    Query,
3207    Planner,
3208    Cursor,
3209    Recovery,
3210    Response,
3211    Executor,
3212    Interface,
3213}
3214
3215impl ErrorOrigin {
3216    /// Return the compact diagnostic origin for this internal origin.
3217    #[must_use]
3218    pub const fn diagnostic_origin(self) -> diagnostic_code::ErrorOrigin {
3219        match self {
3220            Self::Serialize => diagnostic_code::ErrorOrigin::Serialize,
3221            Self::Store => diagnostic_code::ErrorOrigin::Store,
3222            Self::Index => diagnostic_code::ErrorOrigin::Index,
3223            Self::Identity => diagnostic_code::ErrorOrigin::Identity,
3224            Self::Query => diagnostic_code::ErrorOrigin::Query,
3225            Self::Planner => diagnostic_code::ErrorOrigin::Planner,
3226            Self::Cursor => diagnostic_code::ErrorOrigin::Cursor,
3227            Self::Recovery => diagnostic_code::ErrorOrigin::Recovery,
3228            Self::Response => diagnostic_code::ErrorOrigin::Response,
3229            Self::Executor => diagnostic_code::ErrorOrigin::Executor,
3230            Self::Interface => diagnostic_code::ErrorOrigin::Interface,
3231        }
3232    }
3233}
3234
3235impl fmt::Debug for ErrorOrigin {
3236    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3237        write!(f, "{}", *self as u8)
3238    }
3239}