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    /// An admitted metadata-only count has no available exact cardinality.
1110    #[cold]
1111    #[inline(never)]
1112    pub(crate) fn query_exact_count_metadata_unavailable() -> Self {
1113        Self {
1114            class: ErrorClass::Unsupported,
1115            origin: ErrorOrigin::Query,
1116            detail: Some(ErrorDetail::Query(
1117                QueryErrorDetail::ExactCountMetadataUnavailable,
1118            )),
1119        }
1120    }
1121
1122    /// Detached explain rendering exceeded its fixed output policy, not a
1123    /// request/execution budget. Retain numeric facts without report contents.
1124    pub(crate) fn query_explain_output_exceeded(limit: u64, observed: u64) -> Self {
1125        Self::with_diagnostic_facts(
1126            ErrorClass::Unsupported,
1127            ErrorOrigin::Query,
1128            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
1129                boundary: diagnostic_code::RuntimeBoundaryCode::QueryExplainOutputExceeded,
1130            }),
1131            vec![
1132                (diagnostic_code::DiagnosticFactTag::Limit, limit),
1133                (diagnostic_code::DiagnosticFactTag::Actual, observed),
1134            ],
1135        )
1136    }
1137
1138    /// Derived diagnostic access depth exceeded its fixed projection policy.
1139    /// This does not reject or change the identity of an ordinary query.
1140    pub(crate) fn query_explain_depth_exceeded(limit: u64, observed: u64) -> Self {
1141        Self::with_diagnostic_facts(
1142            ErrorClass::Unsupported,
1143            ErrorOrigin::Query,
1144            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
1145                boundary: diagnostic_code::RuntimeBoundaryCode::QueryExplainDepthExceeded,
1146            }),
1147            vec![
1148                (diagnostic_code::DiagnosticFactTag::Limit, limit),
1149                (diagnostic_code::DiagnosticFactTag::Actual, observed),
1150            ],
1151        )
1152    }
1153
1154    /// Construct a query-origin conflict for execution against a superseded
1155    /// accepted schema revision.
1156    #[cold]
1157    #[inline(never)]
1158    pub(crate) fn query_stale_accepted_schema_revision(
1159        expected_revision: u64,
1160        current_revision: Option<u64>,
1161    ) -> Self {
1162        let mut facts = Vec::with_capacity(1 + usize::from(current_revision.is_some()));
1163        facts.push((
1164            diagnostic_code::DiagnosticFactTag::ExpectedRevision,
1165            expected_revision,
1166        ));
1167        if let Some(current_revision) = current_revision {
1168            facts.push((
1169                diagnostic_code::DiagnosticFactTag::CurrentRevision,
1170                current_revision,
1171            ));
1172        }
1173        Self::with_diagnostic_facts(ErrorClass::Conflict, ErrorOrigin::Query, None, facts)
1174    }
1175
1176    /// Construct a query-origin SQL DDL admission error with structured detail.
1177    #[cold]
1178    #[inline(never)]
1179    #[cfg(feature = "sql")]
1180    pub(crate) fn query_schema_ddl_admission(error: SchemaDdlAdmissionError) -> Self {
1181        Self {
1182            class: ErrorClass::Unsupported,
1183            origin: ErrorOrigin::Query,
1184            detail: Some(ErrorDetail::Query(QueryErrorDetail::SchemaDdlAdmission {
1185                error,
1186            })),
1187        }
1188    }
1189
1190    /// Construct a query-origin numeric overflow error with structured detail.
1191    #[cold]
1192    #[inline(never)]
1193    pub(crate) fn query_numeric_overflow() -> Self {
1194        Self {
1195            class: ErrorClass::Unsupported,
1196            origin: ErrorOrigin::Query,
1197            detail: Some(ErrorDetail::Query(QueryErrorDetail::NumericOverflow)),
1198        }
1199    }
1200
1201    /// Construct a query-origin non-representable numeric result error with
1202    /// structured detail.
1203    #[cold]
1204    #[inline(never)]
1205    pub(crate) fn query_numeric_not_representable() -> Self {
1206        Self {
1207            class: ErrorClass::Unsupported,
1208            origin: ErrorOrigin::Query,
1209            detail: Some(ErrorDetail::Query(
1210                QueryErrorDetail::NumericNotRepresentable,
1211            )),
1212        }
1213    }
1214
1215    /// Construct a serialize-origin internal error.
1216    #[cold]
1217    #[inline(never)]
1218    pub(crate) fn serialize_internal() -> Self {
1219        Self::new(ErrorClass::Internal, ErrorOrigin::Serialize)
1220    }
1221
1222    /// Construct the canonical persisted-row encode internal error.
1223    pub(crate) fn persisted_row_encode_failed(_detail: impl Sized) -> Self {
1224        Self::persisted_row_encode_internal()
1225    }
1226
1227    /// Construct the compact persisted-row encode internal error.
1228    pub(crate) fn persisted_row_encode_internal() -> Self {
1229        Self::serialize_internal()
1230    }
1231
1232    /// Construct the compact persisted-row field encode internal error.
1233    pub(crate) fn persisted_row_field_encode_internal(_field_name: &str) -> Self {
1234        Self::persisted_row_encode_internal()
1235    }
1236
1237    /// Construct a store-origin corruption error.
1238    #[cold]
1239    #[inline(never)]
1240    pub(crate) fn store_corruption() -> Self {
1241        Self::new(ErrorClass::Corruption, ErrorOrigin::Store)
1242    }
1243
1244    /// Construct a store-origin commit-marker corruption error.
1245    pub(crate) fn commit_corruption() -> Self {
1246        Self::store_corruption()
1247    }
1248
1249    /// Construct a store-origin commit-marker component corruption error.
1250    pub(crate) fn commit_component_corruption() -> Self {
1251        Self::commit_corruption()
1252    }
1253
1254    /// Construct the canonical commit-marker id generation internal error.
1255    pub(crate) fn commit_id_generation_failed() -> Self {
1256        Self::store_internal()
1257    }
1258
1259    /// Construct the canonical commit-marker payload u32-length-limit error.
1260    pub(crate) fn commit_marker_payload_exceeds_u32_length_limit() -> Self {
1261        Self::store_unsupported()
1262    }
1263
1264    /// Construct the canonical commit-marker component invalid-length corruption error.
1265    pub(crate) fn commit_component_length_invalid(actual_length: usize, limit: usize) -> Self {
1266        Self::with_diagnostic_facts(
1267            ErrorClass::Corruption,
1268            ErrorOrigin::Store,
1269            None,
1270            vec![
1271                (
1272                    diagnostic_code::DiagnosticFactTag::ComponentKind,
1273                    diagnostic_code::DiagnosticComponentKind::CommitDataKey.raw(),
1274                ),
1275                (
1276                    diagnostic_code::DiagnosticFactTag::ActualLength,
1277                    actual_length as u64,
1278                ),
1279                (diagnostic_code::DiagnosticFactTag::Limit, limit as u64),
1280            ],
1281        )
1282    }
1283
1284    /// Construct the canonical commit-marker max-size corruption error.
1285    pub(crate) fn commit_marker_exceeds_max_size() -> Self {
1286        Self::commit_corruption()
1287    }
1288
1289    /// Construct the canonical commit-control slot max-size unsupported error.
1290    pub(crate) fn commit_control_slot_exceeds_max_size() -> Self {
1291        Self::store_unsupported()
1292    }
1293
1294    /// Construct the canonical commit-control marker-bytes length-limit error.
1295    pub(crate) fn commit_control_slot_marker_bytes_exceed_u32_length_limit() -> Self {
1296        Self::store_unsupported()
1297    }
1298
1299    /// Construct an index-origin corruption error.
1300    #[cold]
1301    #[inline(never)]
1302    pub(crate) fn index_corruption() -> Self {
1303        Self::new(ErrorClass::Corruption, ErrorOrigin::Index)
1304    }
1305
1306    /// Construct the canonical unique-validation corruption wrapper.
1307    pub(crate) fn index_unique_validation_corruption() -> Self {
1308        Self::index_plan_index_corruption()
1309    }
1310
1311    /// Construct the canonical structural index-entry corruption wrapper.
1312    pub(crate) fn structural_index_entry_corruption() -> Self {
1313        Self::index_plan_index_corruption()
1314    }
1315
1316    /// Construct the canonical missing new entity-key invariant during unique validation.
1317    pub(crate) fn index_unique_validation_entity_key_required() -> Self {
1318        Self::index_invariant()
1319    }
1320
1321    /// Construct the canonical unique-validation structural row-decode corruption error.
1322    pub(crate) fn index_unique_validation_row_deserialize_failed() -> Self {
1323        Self::index_plan_serialize_corruption()
1324    }
1325
1326    /// Construct the canonical unique-validation primary-key slot decode corruption error.
1327    pub(crate) fn index_unique_validation_primary_key_decode_failed() -> Self {
1328        Self::index_plan_serialize_corruption()
1329    }
1330
1331    /// Construct the canonical unique-validation stored key rebuild corruption error.
1332    pub(crate) fn index_unique_validation_key_rebuild_failed() -> Self {
1333        Self::index_plan_serialize_corruption()
1334    }
1335
1336    /// Construct the canonical unique-validation missing-row corruption error.
1337    pub(crate) fn index_unique_validation_row_required() -> Self {
1338        Self::index_plan_store_corruption()
1339    }
1340
1341    /// Construct the canonical index-only predicate missing-component invariant.
1342    pub(crate) fn index_only_predicate_component_required() -> Self {
1343        Self::index_invariant()
1344    }
1345
1346    /// Construct the canonical index-scan continuation-envelope invariant.
1347    pub(crate) fn index_scan_continuation_anchor_within_envelope_required() -> Self {
1348        Self::index_invariant()
1349    }
1350
1351    /// Construct the canonical index-scan continuation-advancement invariant.
1352    pub(crate) fn index_scan_continuation_advancement_required() -> Self {
1353        Self::index_invariant()
1354    }
1355
1356    /// Construct the canonical index-scan key-decode corruption error.
1357    pub(crate) fn index_scan_key_corrupted_during(
1358        _context: &'static str,
1359        _err: impl Sized,
1360    ) -> Self {
1361        Self::index_corruption()
1362    }
1363
1364    /// Construct the canonical index-scan missing projection-component invariant.
1365    pub(crate) fn index_projection_component_required(
1366        _index_name: &str,
1367        _component_index: usize,
1368    ) -> Self {
1369        Self::index_invariant()
1370    }
1371
1372    /// Construct the canonical scan-time index-entry decode corruption error.
1373    pub(crate) fn index_entry_decode_failed() -> Self {
1374        Self::index_corruption()
1375    }
1376
1377    /// Construct a serialize-origin corruption error.
1378    pub(crate) fn serialize_corruption() -> Self {
1379        Self::new(ErrorClass::Corruption, ErrorOrigin::Serialize)
1380    }
1381
1382    /// Construct the compact persisted-row decode corruption error.
1383    pub(crate) fn persisted_row_decode_corruption() -> Self {
1384        Self::serialize_corruption()
1385    }
1386
1387    /// Construct a persisted-row layout-window corruption error.
1388    pub(crate) fn persisted_row_layout_outside_accepted_window(
1389        row_layout: u32,
1390        history_floor: u32,
1391        current_layout: u32,
1392    ) -> Self {
1393        Self::with_diagnostic_facts(
1394            ErrorClass::Corruption,
1395            ErrorOrigin::Serialize,
1396            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
1397                boundary:
1398                    diagnostic_code::RuntimeBoundaryCode::PersistedRowLayoutOutsideAcceptedWindow,
1399            }),
1400            vec![
1401                (
1402                    diagnostic_code::DiagnosticFactTag::RowLayout,
1403                    u64::from(row_layout),
1404                ),
1405                (
1406                    diagnostic_code::DiagnosticFactTag::HistoryFloor,
1407                    u64::from(history_floor),
1408                ),
1409                (
1410                    diagnostic_code::DiagnosticFactTag::CurrentLayout,
1411                    u64::from(current_layout),
1412                ),
1413            ],
1414        )
1415    }
1416
1417    /// Construct a persisted-row stamped-layout slot-count corruption error.
1418    pub(crate) fn persisted_row_slot_count_mismatch(
1419        row_layout: u32,
1420        expected_slot_count: usize,
1421        actual_slot_count: usize,
1422    ) -> Self {
1423        Self::with_diagnostic_facts(
1424            ErrorClass::Corruption,
1425            ErrorOrigin::Serialize,
1426            Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
1427                boundary: diagnostic_code::RuntimeBoundaryCode::PersistedRowSlotCountMismatch,
1428            }),
1429            vec![
1430                (
1431                    diagnostic_code::DiagnosticFactTag::RowLayout,
1432                    u64::from(row_layout),
1433                ),
1434                (
1435                    diagnostic_code::DiagnosticFactTag::ExpectedSlotCount,
1436                    expected_slot_count as u64,
1437                ),
1438                (
1439                    diagnostic_code::DiagnosticFactTag::ActualSlotCount,
1440                    actual_slot_count as u64,
1441                ),
1442            ],
1443        )
1444    }
1445
1446    /// Construct the canonical persisted-row field decode corruption error.
1447    pub(crate) fn persisted_row_field_decode_failed(field_name: &str, _detail: impl Sized) -> Self {
1448        Self::persisted_row_field_decode_corruption(field_name)
1449    }
1450
1451    /// Construct the compact persisted-row field decode corruption error.
1452    pub(crate) fn persisted_row_field_decode_corruption(_field_name: &str) -> Self {
1453        Self::persisted_row_decode_corruption()
1454    }
1455
1456    /// Construct the canonical persisted-row field-kind decode corruption error.
1457    pub(crate) fn persisted_row_field_kind_decode_failed(
1458        field_name: &str,
1459        _field_kind: impl fmt::Debug,
1460        _detail: impl Sized,
1461    ) -> Self {
1462        Self::persisted_row_field_decode_corruption(field_name)
1463    }
1464
1465    /// Construct the canonical persisted-row scalar-payload length corruption error.
1466    pub(crate) fn persisted_row_field_payload_exact_len_required(field_name: &str) -> Self {
1467        Self::persisted_row_field_decode_corruption(field_name)
1468    }
1469
1470    /// Construct the canonical persisted-row scalar-payload empty-body corruption error.
1471    pub(crate) fn persisted_row_field_payload_must_be_empty(field_name: &str) -> Self {
1472        Self::persisted_row_field_decode_corruption(field_name)
1473    }
1474
1475    /// Construct the canonical persisted-row scalar-payload invalid-byte corruption error.
1476    pub(crate) fn persisted_row_field_payload_invalid_byte(field_name: &str) -> Self {
1477        Self::persisted_row_field_decode_corruption(field_name)
1478    }
1479
1480    /// Construct the canonical persisted-row scalar-payload non-finite corruption error.
1481    pub(crate) fn persisted_row_field_payload_non_finite(field_name: &str) -> Self {
1482        Self::persisted_row_field_decode_corruption(field_name)
1483    }
1484
1485    /// Construct the canonical persisted-row invalid text payload corruption error.
1486    pub(crate) fn persisted_row_field_text_payload_invalid_utf8(field_name: &str) -> Self {
1487        Self::persisted_row_field_decode_corruption(field_name)
1488    }
1489
1490    /// Construct the canonical persisted-row structural slot-lookup invariant.
1491    pub(crate) fn persisted_row_slot_lookup_out_of_bounds(_model_path: &str, _slot: usize) -> Self {
1492        Self::index_invariant()
1493    }
1494
1495    /// Construct the canonical persisted-row structural slot-cache invariant.
1496    pub(crate) fn persisted_row_slot_cache_lookup_out_of_bounds(
1497        _model_path: &str,
1498        _slot: usize,
1499    ) -> Self {
1500        Self::index_invariant()
1501    }
1502
1503    /// Construct the canonical persisted-row primary-key decode corruption error.
1504    pub(crate) fn persisted_row_primary_key_not_primary_key_encodable(
1505        _data_key: impl fmt::Debug,
1506        _detail: impl Sized,
1507    ) -> Self {
1508        Self::persisted_row_decode_corruption()
1509    }
1510
1511    /// Construct the canonical persisted-row missing primary-key slot corruption error.
1512    pub(crate) fn persisted_row_primary_key_slot_missing(_data_key: impl fmt::Debug) -> Self {
1513        Self::persisted_row_decode_corruption()
1514    }
1515
1516    /// Construct the canonical persisted-row key mismatch corruption error.
1517    pub(crate) fn persisted_row_key_mismatch() -> Self {
1518        Self::store_corruption()
1519    }
1520
1521    /// Construct the canonical persisted-row missing declared-field corruption error.
1522    pub(crate) fn persisted_row_declared_field_missing(field_name: &str) -> Self {
1523        Self::persisted_row_field_decode_corruption(field_name)
1524    }
1525
1526    /// Construct the canonical reverse-index entry corruption error.
1527    pub(crate) fn reverse_index_entry_corrupted(
1528        _source_path: &str,
1529        _field_name: &str,
1530        _target_path: &str,
1531        _index_key: impl fmt::Debug,
1532        _detail: impl Sized,
1533    ) -> Self {
1534        Self::index_corruption()
1535    }
1536
1537    /// Construct the canonical relation-target store missing internal error.
1538    pub(crate) fn relation_target_store_missing(
1539        _source_path: &str,
1540        _field_name: &str,
1541        _target_path: &str,
1542        _store_path: &str,
1543        _detail: impl Sized,
1544    ) -> Self {
1545        Self::executor_internal()
1546    }
1547
1548    /// Identify the accepted source and relation when runtime contract compilation fails.
1549    pub(crate) fn with_relation_identity(self, entity_tag: u64, relation_id: u32) -> Self {
1550        if self.diagnostic().error_code() != diagnostic_code::ErrorCode::RUNTIME_INTERNAL {
1551            return self;
1552        }
1553        let mut facts = vec![
1554            (diagnostic_code::DiagnosticFactTag::EntityTag, entity_tag),
1555            (
1556                diagnostic_code::DiagnosticFactTag::RelationId,
1557                u64::from(relation_id),
1558            ),
1559        ];
1560        facts.extend(self.diagnostic_facts());
1561        Self::with_diagnostic_facts(self.class, self.origin, None, facts)
1562    }
1563
1564    /// Construct one accepted relation target primary-key arity mismatch.
1565    pub(crate) fn relation_target_primary_key_arity_mismatch(
1566        expected_arity: usize,
1567        actual_arity: usize,
1568    ) -> Self {
1569        Self::with_diagnostic_facts(
1570            ErrorClass::Internal,
1571            ErrorOrigin::Executor,
1572            None,
1573            vec![
1574                (
1575                    diagnostic_code::DiagnosticFactTag::ComponentKind,
1576                    diagnostic_code::DiagnosticComponentKind::RelationTargetPrimaryKey.raw(),
1577                ),
1578                (
1579                    diagnostic_code::DiagnosticFactTag::ExpectedArity,
1580                    expected_arity as u64,
1581                ),
1582                (
1583                    diagnostic_code::DiagnosticFactTag::ActualArity,
1584                    actual_arity as u64,
1585                ),
1586            ],
1587        )
1588    }
1589
1590    /// Construct the canonical relation-target key decode corruption error.
1591    pub(crate) fn relation_target_key_decode_failed(
1592        _context_label: &str,
1593        _source_path: &str,
1594        _field_name: &str,
1595        _target_path: &str,
1596        _detail: impl Sized,
1597    ) -> Self {
1598        Self::identity_corruption()
1599    }
1600
1601    /// Construct the canonical relation-target entity mismatch corruption error.
1602    pub(crate) fn relation_target_entity_mismatch(
1603        _context_label: &str,
1604        _source_path: &str,
1605        _field_name: &str,
1606        _target_path: &str,
1607        _target_entity_name: &str,
1608        expected_tag: u64,
1609        actual_tag: u64,
1610    ) -> Self {
1611        Self::with_diagnostic_facts(
1612            ErrorClass::Corruption,
1613            ErrorOrigin::Store,
1614            None,
1615            vec![
1616                (
1617                    diagnostic_code::DiagnosticFactTag::ExpectedEntityTag,
1618                    expected_tag,
1619                ),
1620                (
1621                    diagnostic_code::DiagnosticFactTag::ActualEntityTag,
1622                    actual_tag,
1623                ),
1624            ],
1625        )
1626    }
1627
1628    /// Construct the canonical relation-source row decode corruption error.
1629    pub(crate) fn relation_source_row_decode_failed(
1630        _source_path: &str,
1631        _field_name: &str,
1632        _target_path: &str,
1633        _detail: impl Sized,
1634    ) -> Self {
1635        Self::persisted_row_decode_corruption()
1636    }
1637
1638    /// Construct the canonical relation-source unsupported scalar relation-key corruption error.
1639    pub(crate) fn relation_source_row_unsupported_scalar_relation_key(
1640        _source_path: &str,
1641        _field_name: &str,
1642        _target_path: &str,
1643    ) -> Self {
1644        Self::persisted_row_decode_corruption()
1645    }
1646
1647    /// Construct the canonical unsupported relation key-kind corruption error.
1648    pub(crate) fn relation_source_row_unsupported_key_kind(_field_kind: impl fmt::Debug) -> Self {
1649        Self::persisted_row_decode_corruption()
1650    }
1651
1652    /// Construct the canonical covering-component empty-payload corruption error.
1653    pub(crate) fn bytes_covering_component_payload_empty() -> Self {
1654        Self::index_corruption()
1655    }
1656
1657    /// Construct the canonical covering-component truncated bool corruption error.
1658    pub(crate) fn bytes_covering_bool_payload_truncated() -> Self {
1659        Self::index_corruption()
1660    }
1661
1662    /// Construct the canonical covering-component invalid-length corruption error.
1663    pub(crate) fn bytes_covering_component_payload_invalid_length() -> Self {
1664        Self::index_corruption()
1665    }
1666
1667    /// Construct the canonical covering-component invalid-bool corruption error.
1668    pub(crate) fn bytes_covering_bool_payload_invalid_value() -> Self {
1669        Self::index_corruption()
1670    }
1671
1672    /// Construct the canonical covering-component invalid text terminator corruption error.
1673    pub(crate) fn bytes_covering_text_payload_invalid_terminator() -> Self {
1674        Self::index_corruption()
1675    }
1676
1677    /// Construct the canonical covering-component trailing-text corruption error.
1678    pub(crate) fn bytes_covering_text_payload_trailing_bytes() -> Self {
1679        Self::index_corruption()
1680    }
1681
1682    /// Construct the canonical covering-component invalid-UTF-8 text corruption error.
1683    pub(crate) fn bytes_covering_text_payload_invalid_utf8() -> Self {
1684        Self::index_corruption()
1685    }
1686
1687    /// Construct the canonical covering-component invalid text escape corruption error.
1688    pub(crate) fn bytes_covering_text_payload_invalid_escape_byte() -> Self {
1689        Self::index_corruption()
1690    }
1691
1692    /// Construct the canonical covering-component missing text terminator corruption error.
1693    pub(crate) fn bytes_covering_text_payload_missing_terminator() -> Self {
1694        Self::index_corruption()
1695    }
1696
1697    /// Construct an identity-origin corruption error.
1698    pub(crate) fn identity_corruption() -> Self {
1699        Self::new(ErrorClass::Corruption, ErrorOrigin::Identity)
1700    }
1701
1702    /// Construct the canonical identity-control-state corruption error.
1703    pub(crate) fn identity_state_corruption() -> Self {
1704        Self::identity_corruption()
1705    }
1706
1707    /// Construct the typed stale high-water conflict for identity publication.
1708    pub(crate) fn identity_state_conflict() -> Self {
1709        Self::new(ErrorClass::Conflict, ErrorOrigin::Identity)
1710    }
1711
1712    /// Construct the bounded identity-state inventory exhaustion error.
1713    pub(crate) fn identity_state_capacity_exhausted() -> Self {
1714        Self::new(ErrorClass::Unsupported, ErrorOrigin::Identity)
1715    }
1716
1717    /// Construct the exact unsigned identity-domain exhaustion error.
1718    pub(crate) fn identity_exhausted() -> Self {
1719        Self::new(ErrorClass::Unsupported, ErrorOrigin::Identity)
1720    }
1721
1722    /// Construct the bounded pre-key candidate-count exhaustion error.
1723    pub(crate) fn identity_candidate_count_exhausted() -> Self {
1724        Self::new(ErrorClass::Unsupported, ErrorOrigin::Identity)
1725    }
1726
1727    /// Construct a store-origin unsupported error.
1728    #[cold]
1729    #[inline(never)]
1730    pub(crate) fn store_unsupported() -> Self {
1731        Self::new(ErrorClass::Unsupported, ErrorOrigin::Store)
1732    }
1733
1734    /// Construct the typed optimistic/idempotency conflict for schema application.
1735    pub(crate) fn schema_application_conflict() -> Self {
1736        Self::new(ErrorClass::Conflict, ErrorOrigin::Store)
1737    }
1738
1739    /// Construct one typed source-migration lifecycle or planning result.
1740    pub(crate) fn schema_migration(reason: diagnostic_code::SchemaMigrationCode) -> Self {
1741        let class = match reason.diagnostic_code() {
1742            diagnostic_code::DiagnosticCode::RuntimeConflict => ErrorClass::Conflict,
1743            diagnostic_code::DiagnosticCode::RuntimeCorruption => ErrorClass::Corruption,
1744            diagnostic_code::DiagnosticCode::RuntimeUnsupported => ErrorClass::Unsupported,
1745            _ => ErrorClass::Internal,
1746        };
1747        Self {
1748            class,
1749            origin: ErrorOrigin::Store,
1750            detail: Some(ErrorDetail::Store(StoreError::SchemaMigration { reason })),
1751        }
1752    }
1753
1754    /// Construct the canonical schema DDL publication race error.
1755    pub(crate) fn schema_ddl_publication_race_lost(_entity_path: &str) -> Self {
1756        Self {
1757            class: ErrorClass::Unsupported,
1758            origin: ErrorOrigin::Store,
1759            detail: Some(ErrorDetail::Store(StoreError::SchemaDdlPublicationRaceLost)),
1760        }
1761    }
1762
1763    /// Construct the canonical current physical-rewrite migration rejection.
1764    #[cfg(feature = "sql")]
1765    pub(crate) fn schema_ddl_rewrite_requires_migration(_entity_path: &str) -> Self {
1766        Self {
1767            class: ErrorClass::Unsupported,
1768            origin: ErrorOrigin::Store,
1769            detail: Some(ErrorDetail::Store(
1770                StoreError::SchemaDdlRewriteRequiresMigration,
1771            )),
1772        }
1773    }
1774
1775    /// Construct the fail-closed journal mutation-revision exhaustion error.
1776    pub(crate) fn journal_mutation_revision_exhausted() -> Self {
1777        Self {
1778            class: ErrorClass::Unsupported,
1779            origin: ErrorOrigin::Store,
1780            detail: Some(ErrorDetail::Store(
1781                StoreError::JournalMutationRevisionExhausted,
1782            )),
1783        }
1784    }
1785
1786    /// Construct a bounded schema-transition resource rejection.
1787    pub(crate) fn schema_transition_budget_exceeded(
1788        resource: SchemaTransitionBudgetResource,
1789    ) -> Self {
1790        Self {
1791            class: ErrorClass::Unsupported,
1792            origin: ErrorOrigin::Store,
1793            detail: Some(ErrorDetail::Store(
1794                StoreError::SchemaTransitionBudgetExceeded { resource },
1795            )),
1796        }
1797    }
1798
1799    /// Construct the canonical unsupported persisted entity-tag store error.
1800    pub(crate) fn unsupported_entity_tag_in_data_store(
1801        _entity_tag: crate::types::EntityTag,
1802    ) -> Self {
1803        Self::store_unsupported()
1804    }
1805
1806    /// Construct the canonical commit-memory id registration failure.
1807    pub(crate) fn commit_memory_id_registration_failed(_err: impl Sized) -> Self {
1808        Self::store_internal()
1809    }
1810
1811    /// Construct an index-origin unsupported error.
1812    pub(crate) fn index_unsupported() -> Self {
1813        Self::new(ErrorClass::Unsupported, ErrorOrigin::Index)
1814    }
1815
1816    /// Construct the canonical index-key component size-limit unsupported error.
1817    pub(crate) fn index_component_exceeds_max_size_at(
1818        entity_tag: u64,
1819        physical_generation: u64,
1820        component_index: usize,
1821        actual_length: usize,
1822        limit: usize,
1823    ) -> Self {
1824        Self::with_diagnostic_facts(
1825            ErrorClass::Unsupported,
1826            ErrorOrigin::Index,
1827            None,
1828            vec![
1829                (diagnostic_code::DiagnosticFactTag::EntityTag, entity_tag),
1830                (
1831                    diagnostic_code::DiagnosticFactTag::PhysicalGeneration,
1832                    physical_generation,
1833                ),
1834                (
1835                    diagnostic_code::DiagnosticFactTag::ComponentIndex,
1836                    component_index as u64,
1837                ),
1838                (
1839                    diagnostic_code::DiagnosticFactTag::ComponentKind,
1840                    diagnostic_code::DiagnosticComponentKind::IndexKeyComponent.raw(),
1841                ),
1842                (
1843                    diagnostic_code::DiagnosticFactTag::ActualLength,
1844                    actual_length as u64,
1845                ),
1846                (diagnostic_code::DiagnosticFactTag::Limit, limit as u64),
1847            ],
1848        )
1849    }
1850
1851    /// Construct the canonical index-key component size-limit error when the
1852    /// generic caller has not retained one accepted index identity.
1853    pub(crate) fn index_component_exceeds_max_size() -> Self {
1854        Self::index_unsupported()
1855    }
1856
1857    /// Construct a serialize-origin unsupported error.
1858    pub(crate) fn serialize_unsupported() -> Self {
1859        Self::new(ErrorClass::Unsupported, ErrorOrigin::Serialize)
1860    }
1861
1862    /// Construct a cursor-origin invalid-continuation error.
1863    pub(crate) fn cursor_invalid_continuation() -> Self {
1864        Self::new(ErrorClass::Unsupported, ErrorOrigin::Cursor)
1865    }
1866
1867    /// Construct a serialize-origin incompatible persisted-format error.
1868    pub(crate) fn serialize_incompatible_persisted_format() -> Self {
1869        Self::new(
1870            ErrorClass::IncompatiblePersistedFormat,
1871            ErrorOrigin::Serialize,
1872        )
1873    }
1874
1875    /// Construct a query-origin unsupported error preserving one SQL parser
1876    /// unsupported-feature code in structured error detail.
1877    #[cfg(feature = "sql")]
1878    pub(crate) fn query_unsupported_sql_feature(feature: diagnostic_code::SqlFeatureCode) -> Self {
1879        Self {
1880            class: ErrorClass::Unsupported,
1881            origin: ErrorOrigin::Query,
1882            detail: Some(ErrorDetail::Query(
1883                QueryErrorDetail::UnsupportedSqlFeature { feature },
1884            )),
1885        }
1886    }
1887
1888    /// Construct a query-origin unsupported SQL lowering error preserving one
1889    /// compact lowering reason in structured error detail.
1890    #[cfg(feature = "sql")]
1891    pub(crate) fn query_sql_lowering(reason: diagnostic_code::SqlLoweringCode) -> Self {
1892        Self {
1893            class: ErrorClass::Unsupported,
1894            origin: ErrorOrigin::Query,
1895            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlLowering { reason })),
1896        }
1897    }
1898
1899    /// Construct one query-origin SQL lowering error with bounded numeric context.
1900    #[cfg(feature = "sql")]
1901    pub(crate) fn query_sql_lowering_with_facts(
1902        reason: diagnostic_code::SqlLoweringCode,
1903        facts: Vec<(diagnostic_code::DiagnosticFactTag, u64)>,
1904    ) -> Self {
1905        Self::with_diagnostic_facts(
1906            ErrorClass::Unsupported,
1907            ErrorOrigin::Query,
1908            Some(diagnostic_code::DiagnosticDetail::SqlLowering { reason }),
1909            facts,
1910        )
1911    }
1912
1913    /// Construct a query-origin unsupported projection error preserving one
1914    /// compact projection reason in structured error detail.
1915    pub(crate) fn query_unsupported_projection(
1916        reason: diagnostic_code::QueryProjectionCode,
1917    ) -> Self {
1918        Self {
1919            class: ErrorClass::Unsupported,
1920            origin: ErrorOrigin::Query,
1921            detail: Some(ErrorDetail::Query(
1922                QueryErrorDetail::UnsupportedProjection { reason },
1923            )),
1924        }
1925    }
1926
1927    /// Construct a query-origin unsupported error preserving one SQL endpoint
1928    /// surface mismatch in structured error detail.
1929    #[cfg(feature = "sql")]
1930    pub(crate) fn query_sql_surface_mismatch(
1931        mismatch: diagnostic_code::SqlSurfaceMismatchCode,
1932    ) -> Self {
1933        Self {
1934            class: ErrorClass::Unsupported,
1935            origin: ErrorOrigin::Query,
1936            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlSurfaceMismatch {
1937                mismatch,
1938            })),
1939        }
1940    }
1941
1942    /// Construct a query-origin unsupported SQL write boundary error.
1943    pub(crate) fn query_sql_write_boundary(
1944        boundary: diagnostic_code::SqlWriteBoundaryCode,
1945    ) -> Self {
1946        Self {
1947            class: ErrorClass::Unsupported,
1948            origin: ErrorOrigin::Query,
1949            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlWriteBoundary {
1950                boundary,
1951            })),
1952        }
1953    }
1954
1955    /// Construct one query-origin SQL write-boundary error with bounded numeric context.
1956    pub(crate) fn query_sql_write_boundary_with_facts(
1957        boundary: diagnostic_code::SqlWriteBoundaryCode,
1958        facts: Vec<(diagnostic_code::DiagnosticFactTag, u64)>,
1959    ) -> Self {
1960        Self::with_diagnostic_facts(
1961            ErrorClass::Unsupported,
1962            ErrorOrigin::Query,
1963            Some(diagnostic_code::DiagnosticDetail::SqlWriteBoundary { boundary }),
1964            facts,
1965        )
1966    }
1967
1968    pub fn store_not_found(_key: impl Sized) -> Self {
1969        Self {
1970            class: ErrorClass::NotFound,
1971            origin: ErrorOrigin::Store,
1972            detail: Some(ErrorDetail::Store(StoreError::NotFound)),
1973        }
1974    }
1975
1976    /// Construct a standardized unsupported-entity-path error.
1977    pub fn unsupported_entity_path(_path: impl Sized) -> Self {
1978        Self::store_unsupported()
1979    }
1980
1981    /// Construct an index-plan corruption error with a canonical prefix.
1982    #[cold]
1983    #[inline(never)]
1984    pub(crate) fn index_plan_corruption(origin: ErrorOrigin) -> Self {
1985        Self::new(ErrorClass::Corruption, origin)
1986    }
1987
1988    /// Construct an index-plan corruption error for index-origin failures.
1989    #[cold]
1990    #[inline(never)]
1991    pub(crate) fn index_plan_index_corruption() -> Self {
1992        Self::index_plan_corruption(ErrorOrigin::Index)
1993    }
1994
1995    /// Construct an index-plan corruption error for store-origin failures.
1996    #[cold]
1997    #[inline(never)]
1998    pub(crate) fn index_plan_store_corruption() -> Self {
1999        Self::index_plan_corruption(ErrorOrigin::Store)
2000    }
2001
2002    /// Construct an index-plan corruption error for serialize-origin failures.
2003    #[cold]
2004    #[inline(never)]
2005    pub(crate) fn index_plan_serialize_corruption() -> Self {
2006        Self::index_plan_corruption(ErrorOrigin::Serialize)
2007    }
2008
2009    /// Construct an index-plan invariant violation error with a canonical prefix.
2010    #[cfg(test)]
2011    pub(crate) fn index_plan_invariant(origin: ErrorOrigin) -> Self {
2012        Self::new(ErrorClass::InvariantViolation, origin)
2013    }
2014
2015    /// Construct an index-plan invariant violation error for store-origin failures.
2016    #[cfg(test)]
2017    pub(crate) fn index_plan_store_invariant() -> Self {
2018        Self::index_plan_invariant(ErrorOrigin::Store)
2019    }
2020
2021    /// Construct an index-origin conflict without claiming accepted identity.
2022    ///
2023    /// Live accepted uniqueness violations use compact accepted-constraint facts.
2024    /// Schema-domain staging and activation findings use this compact
2025    /// classification before an accepted write-admission diagnostic exists.
2026    pub(crate) fn index_conflict() -> Self {
2027        Self::new(ErrorClass::Conflict, ErrorOrigin::Index)
2028    }
2029}
2030
2031impl From<diagnostic_code::QueryReadAdmissionCode> for InternalError {
2032    fn from(reason: diagnostic_code::QueryReadAdmissionCode) -> Self {
2033        Self {
2034            class: ErrorClass::Unsupported,
2035            origin: ErrorOrigin::Query,
2036            detail: Some(ErrorDetail::Query(QueryErrorDetail::QueryReadAdmission {
2037                reason,
2038            })),
2039        }
2040    }
2041}
2042
2043impl fmt::Debug for InternalError {
2044    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2045        fmt_compact_diagnostic(
2046            f,
2047            self.diagnostic_code(),
2048            self.detail
2049                .as_ref()
2050                .and_then(ErrorDetail::diagnostic_detail),
2051        )
2052    }
2053}
2054
2055impl fmt::Display for InternalError {
2056    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2057        f.write_str(self.message())
2058    }
2059}
2060
2061impl std::error::Error for InternalError {}
2062
2063///
2064/// ConstraintValuePathComponent
2065///
2066/// Stable accepted identity or finite-value coordinate in one targeted-rule
2067/// violation. Display names are deliberately absent so renames cannot change
2068/// the diagnostic identity.
2069///
2070
2071#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
2072pub enum ConstraintValuePathComponent {
2073    /// Persisted root field whose admitted value was traversed.
2074    RootField { field_id: u32 },
2075
2076    /// Accepted record member selected by immutable composite/member identity.
2077    RecordMember {
2078        composite_type_id: u32,
2079        member_id: u32,
2080    },
2081
2082    /// Tuple element selected by accepted composite identity and ordinal.
2083    TupleElement {
2084        composite_type_id: u32,
2085        ordinal: u32,
2086    },
2087
2088    /// Transparent accepted newtype boundary.
2089    Newtype { composite_type_id: u32 },
2090
2091    /// Selected accepted enum variant.
2092    EnumVariant { enum_type_id: u32, variant_id: u32 },
2093
2094    /// List element in admitted order.
2095    ListElement { index: u32 },
2096
2097    /// Set element in canonical admitted order.
2098    SetElement { index: u32 },
2099
2100    /// Map key in canonical entry order.
2101    MapEntryKey { index: u32 },
2102
2103    /// Map value in canonical entry order.
2104    MapEntryValue { index: u32 },
2105}
2106
2107impl fmt::Display for ConstraintValuePathComponent {
2108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2109        match self {
2110            Self::RootField { field_id } => write!(f, "field#{field_id}"),
2111            Self::RecordMember {
2112                composite_type_id,
2113                member_id,
2114            } => write!(f, "record#{composite_type_id}.member#{member_id}"),
2115            Self::TupleElement {
2116                composite_type_id,
2117                ordinal,
2118            } => write!(f, "tuple#{composite_type_id}[{ordinal}]"),
2119            Self::Newtype { composite_type_id } => write!(f, "newtype#{composite_type_id}"),
2120            Self::EnumVariant {
2121                enum_type_id,
2122                variant_id,
2123            } => write!(f, "enum#{enum_type_id}.variant#{variant_id}"),
2124            Self::ListElement { index } => write!(f, "list[{index}]"),
2125            Self::SetElement { index } => write!(f, "set[{index}]"),
2126            Self::MapEntryKey { index } => write!(f, "map[{index}].key"),
2127            Self::MapEntryValue { index } => write!(f, "map[{index}].value"),
2128        }
2129    }
2130}
2131
2132///
2133/// ConstraintValuePath
2134///
2135/// Bounded typed path to the first deterministic failing value occurrence.
2136///
2137
2138#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
2139pub struct ConstraintValuePath {
2140    components: Vec<ConstraintValuePathComponent>,
2141}
2142
2143impl ConstraintValuePath {
2144    /// Build one already-bounded accepted occurrence path.
2145    #[must_use]
2146    pub(crate) const fn new(components: Vec<ConstraintValuePathComponent>) -> Self {
2147        Self { components }
2148    }
2149
2150    /// Borrow the stable accepted components.
2151    #[must_use]
2152    pub const fn components(&self) -> &[ConstraintValuePathComponent] {
2153        self.components.as_slice()
2154    }
2155}
2156
2157impl fmt::Display for ConstraintValuePath {
2158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2159        for (ordinal, component) in self.components.iter().enumerate() {
2160            if ordinal != 0 {
2161                f.write_str("/")?;
2162            }
2163            component.fmt(f)?;
2164        }
2165        Ok(())
2166    }
2167}
2168
2169///
2170/// ConstraintValidationFindingOutput
2171///
2172/// Bounded historical validation evidence returned only by explicit schema
2173/// validation operations. Names are resolved by host tooling from the exact
2174/// accepted fingerprint and immutable numeric identities.
2175///
2176
2177#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
2178pub struct ConstraintValidationFindingOutput {
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}
2187
2188impl ConstraintValidationFindingOutput {
2189    /// Build one already-bounded historical validation finding.
2190    #[must_use]
2191    pub(crate) const fn new(
2192        accepted_schema_fingerprint: [u8; 16],
2193        entity_tag: u64,
2194        constraint_id: u32,
2195        primary_key: Vec<u8>,
2196        field_ids: Vec<u32>,
2197        value_path: Option<ConstraintValuePath>,
2198        error_code: u16,
2199    ) -> Self {
2200        Self {
2201            accepted_schema_fingerprint,
2202            entity_tag,
2203            constraint_id,
2204            primary_key,
2205            field_ids,
2206            value_path,
2207            error_code,
2208        }
2209    }
2210
2211    /// Return the exact accepted-schema fingerprint that binds every numeric identity.
2212    #[must_use]
2213    pub const fn accepted_schema_fingerprint(&self) -> [u8; 16] {
2214        self.accepted_schema_fingerprint
2215    }
2216
2217    /// Return the stable accepted entity identity.
2218    #[must_use]
2219    pub const fn entity_tag(&self) -> u64 {
2220        self.entity_tag
2221    }
2222
2223    /// Return the stable accepted constraint identity.
2224    #[must_use]
2225    pub const fn constraint_id(&self) -> u32 {
2226        self.constraint_id
2227    }
2228
2229    /// Borrow the bounded canonical persisted primary-key locator.
2230    #[must_use]
2231    pub const fn primary_key(&self) -> &[u8] {
2232        self.primary_key.as_slice()
2233    }
2234
2235    /// Borrow immutable accepted field identities implicated by the finding.
2236    #[must_use]
2237    pub const fn field_ids(&self) -> &[u32] {
2238        self.field_ids.as_slice()
2239    }
2240
2241    /// Borrow the typed concrete value path for a targeted-rule violation.
2242    #[must_use]
2243    pub const fn value_path(&self) -> Option<&ConstraintValuePath> {
2244        self.value_path.as_ref()
2245    }
2246
2247    /// Return the compact stable error code for this exact failure.
2248    #[must_use]
2249    pub const fn error_code(&self) -> diagnostic_code::ErrorCode {
2250        diagnostic_code::ErrorCode::from_raw(self.error_code)
2251    }
2252
2253    /// Return the broad public error class derived from the compact code.
2254    #[must_use]
2255    pub const fn error_class(&self) -> diagnostic_code::ErrorClass {
2256        self.error_code().class()
2257    }
2258}
2259
2260/// Complete bounded numeric authority needed to publish E210 or E212 facts.
2261#[derive(Clone)]
2262pub(crate) struct AcceptedConstraintFactContext {
2263    fingerprint_method: u8,
2264    accepted_schema_fingerprint: [u8; 16],
2265    entity_tag: u64,
2266    constraint_id: u32,
2267    constraint_kind: diagnostic_code::DiagnosticConstraintKind,
2268    mutation: Option<MutationDiagnosticContext>,
2269    value_path: Option<ConstraintValuePath>,
2270}
2271
2272impl AcceptedConstraintFactContext {
2273    #[must_use]
2274    pub(crate) fn write_admission(
2275        fingerprint_method: u8,
2276        accepted_schema_fingerprint: [u8; 16],
2277        entity_tag: u64,
2278        constraint_id: u32,
2279        constraint_kind: diagnostic_code::DiagnosticConstraintKind,
2280        mutation: Option<MutationDiagnosticContext>,
2281        value_path: Option<ConstraintValuePath>,
2282    ) -> Self {
2283        debug_assert!(mutation.is_none_or(|context| context.entity_tag() == entity_tag));
2284        Self {
2285            fingerprint_method,
2286            accepted_schema_fingerprint,
2287            entity_tag,
2288            constraint_id,
2289            constraint_kind,
2290            mutation,
2291            value_path,
2292        }
2293    }
2294
2295    fn facts(self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
2296        let path_len = self
2297            .value_path
2298            .as_ref()
2299            .map_or(0, |path| path.components().len());
2300        let mutation_fact_count = self.mutation.map_or(0, |mutation| {
2301            1 + usize::from(mutation.batch_position.is_some())
2302        });
2303        let mut facts = Vec::with_capacity(7 + mutation_fact_count + path_len);
2304        append_accepted_schema_facts(
2305            &mut facts,
2306            self.fingerprint_method,
2307            self.accepted_schema_fingerprint,
2308        );
2309        facts.push((
2310            diagnostic_code::DiagnosticFactTag::EntityTag,
2311            self.entity_tag,
2312        ));
2313        facts.push((
2314            diagnostic_code::DiagnosticFactTag::ConstraintId,
2315            u64::from(self.constraint_id),
2316        ));
2317        facts.push((
2318            diagnostic_code::DiagnosticFactTag::ConstraintKind,
2319            self.constraint_kind.raw(),
2320        ));
2321        facts.push((
2322            diagnostic_code::DiagnosticFactTag::ConstraintContext,
2323            diagnostic_code::DiagnosticConstraintContext::WriteAdmission.raw(),
2324        ));
2325        if let Some(mutation) = self.mutation {
2326            mutation.append_operation_facts(&mut facts);
2327        }
2328        if let Some(path) = self.value_path {
2329            for component in path.components {
2330                facts.push(constraint_value_path_fact(component));
2331            }
2332        }
2333        debug_assert!(facts.len() <= diagnostic_code::MAX_PUBLIC_DIAGNOSTIC_FACTS);
2334        facts
2335    }
2336}
2337
2338/// Mutation and constraint errors use the same lossless accepted-schema identity.
2339fn append_accepted_schema_facts(
2340    facts: &mut Vec<(diagnostic_code::DiagnosticFactTag, u64)>,
2341    method: u8,
2342    fingerprint: [u8; 16],
2343) {
2344    facts.extend([
2345        (
2346            diagnostic_code::DiagnosticFactTag::AcceptedSchemaFingerprintMethod,
2347            u64::from(method),
2348        ),
2349        (
2350            diagnostic_code::DiagnosticFactTag::AcceptedSchemaFingerprintHigh,
2351            u64::from_be_bytes([
2352                fingerprint[0],
2353                fingerprint[1],
2354                fingerprint[2],
2355                fingerprint[3],
2356                fingerprint[4],
2357                fingerprint[5],
2358                fingerprint[6],
2359                fingerprint[7],
2360            ]),
2361        ),
2362        (
2363            diagnostic_code::DiagnosticFactTag::AcceptedSchemaFingerprintLow,
2364            u64::from_be_bytes([
2365                fingerprint[8],
2366                fingerprint[9],
2367                fingerprint[10],
2368                fingerprint[11],
2369                fingerprint[12],
2370                fingerprint[13],
2371                fingerprint[14],
2372                fingerprint[15],
2373            ]),
2374        ),
2375    ]);
2376}
2377
2378fn constraint_value_path_fact(
2379    component: ConstraintValuePathComponent,
2380) -> (diagnostic_code::DiagnosticFactTag, u64) {
2381    use diagnostic_code::DiagnosticFactTag;
2382    match component {
2383        ConstraintValuePathComponent::RootField { field_id } => {
2384            (DiagnosticFactTag::RootField, u64::from(field_id))
2385        }
2386        ConstraintValuePathComponent::RecordMember {
2387            composite_type_id,
2388            member_id,
2389        } => (
2390            DiagnosticFactTag::RecordMember,
2391            diagnostic_code::pack_u32_pair(composite_type_id, member_id),
2392        ),
2393        ConstraintValuePathComponent::TupleElement {
2394            composite_type_id,
2395            ordinal,
2396        } => (
2397            DiagnosticFactTag::TupleElement,
2398            diagnostic_code::pack_u32_pair(composite_type_id, ordinal),
2399        ),
2400        ConstraintValuePathComponent::Newtype { composite_type_id } => {
2401            (DiagnosticFactTag::Newtype, u64::from(composite_type_id))
2402        }
2403        ConstraintValuePathComponent::EnumVariant {
2404            enum_type_id,
2405            variant_id,
2406        } => (
2407            DiagnosticFactTag::EnumVariant,
2408            diagnostic_code::pack_u32_pair(enum_type_id, variant_id),
2409        ),
2410        ConstraintValuePathComponent::ListElement { index } => {
2411            (DiagnosticFactTag::ListElement, u64::from(index))
2412        }
2413        ConstraintValuePathComponent::SetElement { index } => {
2414            (DiagnosticFactTag::SetElement, u64::from(index))
2415        }
2416        ConstraintValuePathComponent::MapEntryKey { index } => {
2417            (DiagnosticFactTag::MapEntryKey, u64::from(index))
2418        }
2419        ConstraintValuePathComponent::MapEntryValue { index } => {
2420            (DiagnosticFactTag::MapEntryValue, u64::from(index))
2421        }
2422    }
2423}
2424
2425///
2426/// ErrorDetail
2427///
2428/// Structured, origin-specific error detail carried by [`InternalError`].
2429/// This enum is intentionally extensible.
2430///
2431
2432pub enum ErrorDetail {
2433    /// Compact code/detail plus safe numeric context for one public failure.
2434    DiagnosticFacts(Box<DiagnosticFactDetail>),
2435    /// Executor-owned mutation and query execution details.
2436    Executor(ExecutorErrorDetail),
2437    Store(StoreError),
2438    Query(QueryErrorDetail),
2439    Recovery(RecoveryErrorDetail),
2440    // Future-proofing:
2441    // Index(IndexError),
2442}
2443
2444/// Executor-specific structured error detail.
2445pub enum ExecutorErrorDetail {
2446    /// A complete insert or replacement omitted one or more required fields.
2447    MutationRequiredFieldMissing,
2448    /// A logical mutation would move accepted managed time backward.
2449    MutationManagedTimestampRegression,
2450    /// A caller explicitly authored a field owned by accepted database policy.
2451    MutationDatabaseOwnedFieldExplicit,
2452    /// A mixed structural mutation batch contained no operations.
2453    MutationBatchEmpty,
2454    /// A mixed structural mutation batch exceeded its operation-count bound.
2455    MutationBatchTooManyItems,
2456    /// A mixed structural mutation batch exceeded its staged-byte bound.
2457    MutationBatchStagedBytesExceeded,
2458    /// A mixed structural mutation result exceeded its encoded response bound.
2459    MutationBatchResultBytesExceeded,
2460    /// A mixed structural mutation batch crossed an accepted store boundary.
2461    MutationBatchStoreMismatch,
2462    /// A mixed structural mutation batch exceeded its distinct-entity bound.
2463    MutationBatchTooManyEntities,
2464    /// More than one mixed structural operation targeted the same accepted key.
2465    MutationBatchDuplicateKey,
2466    /// Accepted row-constraint metadata or compiled state was inconsistent.
2467    AcceptedRowConstraintProgramCorrupt,
2468}
2469
2470///
2471/// RecoveryErrorDetail
2472///
2473/// Recovery-origin structured error detail payload.
2474///
2475
2476pub enum RecoveryErrorDetail {
2477    UnsupportedFormatVersion { found: Option<u16>, required: u16 },
2478
2479    MalformedFormatMarker { reason: RecoveryFormatMarkerError },
2480}
2481
2482/// Store boot-marker corruption classification.
2483#[derive(Clone, Copy, Eq, PartialEq)]
2484pub enum RecoveryFormatMarkerError {
2485    Magic,
2486    Checksum,
2487    State,
2488}
2489
2490impl RecoveryFormatMarkerError {
2491    const fn diagnostic_decode_reason(self) -> diagnostic_code::DiagnosticDecodeReason {
2492        match self {
2493            Self::Magic => diagnostic_code::DiagnosticDecodeReason::RecoveryMarkerMagic,
2494            Self::Checksum => diagnostic_code::DiagnosticDecodeReason::RecoveryMarkerChecksum,
2495            Self::State => diagnostic_code::DiagnosticDecodeReason::RecoveryMarkerState,
2496        }
2497    }
2498}
2499
2500///
2501/// StoreError
2502///
2503/// Store-specific structured error detail.
2504/// Never returned directly; always wrapped in [`ErrorDetail::Store`].
2505///
2506
2507pub enum StoreError {
2508    NotFound,
2509
2510    Corrupt,
2511
2512    InvariantViolation,
2513
2514    SchemaDdlPublicationRaceLost,
2515
2516    SchemaDdlRewriteRequiresMigration,
2517
2518    SchemaMigration {
2519        reason: diagnostic_code::SchemaMigrationCode,
2520    },
2521
2522    SchemaRowLayoutVersionExhausted,
2523
2524    JournalMutationRevisionExhausted,
2525
2526    SchemaTransitionBudgetExceeded {
2527        resource: SchemaTransitionBudgetResource,
2528    },
2529
2530    /// A generated field would collide with an accepted DDL-owned slot.
2531    SchemaGeneratedFieldAfterDdlField,
2532
2533    /// A live generated constraint activation no longer matches its proposal.
2534    SchemaGeneratedConstraintActivationStale,
2535}
2536
2537///
2538/// QueryErrorDetail
2539///
2540/// Query-origin structured error detail payload.
2541///
2542
2543pub enum QueryErrorDetail {
2544    /// The exact-count shape is supported, but its metadata is unavailable.
2545    ExactCountMetadataUnavailable,
2546
2547    NumericOverflow,
2548
2549    NumericNotRepresentable,
2550
2551    UnsupportedSqlFeature {
2552        feature: diagnostic_code::SqlFeatureCode,
2553    },
2554
2555    SqlLowering {
2556        reason: diagnostic_code::SqlLoweringCode,
2557    },
2558
2559    UnsupportedProjection {
2560        reason: diagnostic_code::QueryProjectionCode,
2561    },
2562
2563    UnknownAggregateTargetField,
2564
2565    QueryReadAdmission {
2566        reason: diagnostic_code::QueryReadAdmissionCode,
2567    },
2568
2569    SqlSurfaceMismatch {
2570        mismatch: diagnostic_code::SqlSurfaceMismatchCode,
2571    },
2572
2573    SqlWriteBoundary {
2574        boundary: diagnostic_code::SqlWriteBoundaryCode,
2575    },
2576
2577    SchemaDdlAdmission {
2578        error: SchemaDdlAdmissionError,
2579    },
2580
2581    StaleSchemaRevision,
2582}
2583
2584impl fmt::Display for QueryErrorDetail {
2585    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2586        f.write_str(COMPACT_QUERY_DIAGNOSTIC_MESSAGE)
2587    }
2588}
2589
2590impl std::error::Error for QueryErrorDetail {}
2591
2592///
2593/// SchemaTransitionBudgetResource
2594///
2595/// Query-visible identity of the exact schema-transition resource cap that
2596/// rejected a complete validation or derived-state stage.
2597///
2598
2599#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2600pub enum SchemaTransitionBudgetResource {
2601    /// Number of physical deletion keys retained for replacement.
2602    DeletionKeys,
2603    /// Number of row-derived projection entries retained for validation.
2604    ProjectionEntries,
2605    /// Deterministic projection and physical-classification work units.
2606    ProjectionWorkUnits,
2607    /// Number of authoritative source rows.
2608    SourceRows,
2609    /// Cumulative bytes of authoritative source rows.
2610    SourceRowBytes,
2611    /// Retained raw payloads plus deterministic-sort workspace bytes.
2612    StagedRawBytes,
2613}
2614
2615///
2616/// SchemaDdlAdmissionError
2617///
2618/// Stable query-visible SQL DDL admission reason. Human diagnostics may carry
2619/// extra version, fingerprint, and target facts beside this machine-readable
2620/// variant.
2621///
2622
2623#[derive(Clone, Copy, Eq, PartialEq)]
2624pub enum SchemaDdlAdmissionError {
2625    MissingExpectedSchemaVersion,
2626
2627    MissingNextSchemaVersion,
2628
2629    StaleExpectedSchemaVersion,
2630
2631    InvalidExpectedSchemaVersion,
2632
2633    InvalidNextSchemaVersion,
2634
2635    AcceptedSchemaChangeWithoutVersionBump,
2636
2637    EmptyVersionBump,
2638
2639    VersionGap,
2640
2641    VersionRollback,
2642
2643    FingerprintMethodMismatch,
2644
2645    UnsupportedTransitionClass,
2646
2647    PhysicalRunnerMissing,
2648
2649    ValidationFailed,
2650
2651    PublicationRaceLost,
2652
2653    InvalidAddColumnDefault,
2654
2655    InvalidAlterColumnDefault,
2656
2657    RowLayoutVersionExhausted,
2658
2659    GeneratedIndexDropRejected,
2660
2661    SchemaRewriteRequiresMigration,
2662
2663    SchemaTransitionBudgetExceeded {
2664        resource: SchemaTransitionBudgetResource,
2665    },
2666
2667    GeneratedFieldDefaultChangeRejected,
2668
2669    GeneratedFieldNullabilityChangeRejected,
2670}
2671
2672impl fmt::Display for SchemaDdlAdmissionError {
2673    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2674        f.write_str(COMPACT_QUERY_DIAGNOSTIC_MESSAGE)
2675    }
2676}
2677
2678impl std::error::Error for SchemaDdlAdmissionError {}
2679
2680impl fmt::Debug for ErrorDetail {
2681    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2682        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2683    }
2684}
2685
2686impl fmt::Debug for ExecutorErrorDetail {
2687    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2688        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2689    }
2690}
2691
2692impl fmt::Debug for StoreError {
2693    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2694        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2695    }
2696}
2697
2698impl fmt::Debug for QueryErrorDetail {
2699    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2700        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2701    }
2702}
2703
2704impl fmt::Debug for RecoveryErrorDetail {
2705    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2706        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2707    }
2708}
2709
2710impl fmt::Debug for RecoveryFormatMarkerError {
2711    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2712        fmt_compact_diagnostic(
2713            f,
2714            diagnostic_code::DiagnosticCode::RuntimeCorruption,
2715            Some(diagnostic_code::DiagnosticDetail::RuntimeKind {
2716                kind: diagnostic_code::RuntimeErrorKind::Corruption,
2717            }),
2718        )
2719    }
2720}
2721
2722impl fmt::Debug for SchemaDdlAdmissionError {
2723    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2724        fmt_compact_diagnostic(
2725            f,
2726            diagnostic_code::DiagnosticCode::SchemaDdlAdmission,
2727            Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2728                reason: self.diagnostic_code(),
2729            }),
2730        )
2731    }
2732}
2733
2734fn fmt_compact_diagnostic(
2735    f: &mut fmt::Formatter<'_>,
2736    code: diagnostic_code::DiagnosticCode,
2737    detail: Option<diagnostic_code::DiagnosticDetail>,
2738) -> fmt::Result {
2739    write!(
2740        f,
2741        "{}",
2742        diagnostic_code::ErrorCode::from_parts(code, detail).raw()
2743    )
2744}
2745
2746impl ErrorDetail {
2747    /// Return the compact diagnostic code for this structured detail.
2748    #[must_use]
2749    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2750        match self {
2751            Self::DiagnosticFacts(detail) => detail.diagnostic.code(),
2752            Self::Executor(error) => error.diagnostic_code(),
2753            Self::Store(error) => error.diagnostic_code(),
2754            Self::Query(error) => error.diagnostic_code(),
2755            Self::Recovery(error) => error.diagnostic_code(),
2756        }
2757    }
2758
2759    /// Return compact structured diagnostic detail when the payload carries one.
2760    #[must_use]
2761    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2762        match self {
2763            Self::DiagnosticFacts(detail) => detail.diagnostic.detail().copied(),
2764            Self::Executor(error) => error.diagnostic_detail(),
2765            Self::Store(error) => error.diagnostic_detail(),
2766            Self::Query(error) => error.diagnostic_detail(),
2767            Self::Recovery(error) => error.diagnostic_detail(),
2768        }
2769    }
2770
2771    /// Project safe typed detail into canonical public numeric facts.
2772    #[must_use]
2773    #[cold]
2774    #[inline(never)]
2775    pub fn diagnostic_facts(&self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
2776        match self {
2777            Self::DiagnosticFacts(detail) => detail.facts.clone(),
2778            Self::Executor(error) => error.diagnostic_facts(),
2779            Self::Query(error) => error.diagnostic_facts(),
2780            Self::Recovery(error) => error.diagnostic_facts(),
2781            Self::Store(_) => Vec::new(),
2782        }
2783    }
2784}
2785
2786impl ExecutorErrorDetail {
2787    /// Return the compact diagnostic code for this executor detail.
2788    #[must_use]
2789    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2790        match self {
2791            Self::MutationRequiredFieldMissing
2792            | Self::MutationDatabaseOwnedFieldExplicit
2793            | Self::MutationBatchEmpty
2794            | Self::MutationBatchTooManyItems
2795            | Self::MutationBatchTooManyEntities
2796            | Self::MutationBatchStagedBytesExceeded
2797            | Self::MutationBatchResultBytesExceeded => {
2798                diagnostic_code::DiagnosticCode::RuntimeUnsupported
2799            }
2800            Self::MutationBatchStoreMismatch | Self::MutationBatchDuplicateKey => {
2801                diagnostic_code::DiagnosticCode::RuntimeConflict
2802            }
2803            Self::MutationManagedTimestampRegression => {
2804                diagnostic_code::DiagnosticCode::RuntimeInvariantViolation
2805            }
2806            Self::AcceptedRowConstraintProgramCorrupt => {
2807                diagnostic_code::DiagnosticCode::RuntimeCorruption
2808            }
2809        }
2810    }
2811
2812    /// Return compact structured diagnostic detail for this executor detail.
2813    #[must_use]
2814    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2815        match self {
2816            Self::MutationRequiredFieldMissing => {
2817                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2818                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationRequiredFieldMissing,
2819                })
2820            }
2821            Self::MutationDatabaseOwnedFieldExplicit => {
2822                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2823                    boundary:
2824                        diagnostic_code::RuntimeBoundaryCode::MutationDatabaseOwnedFieldExplicit,
2825                })
2826            }
2827            Self::MutationBatchEmpty => Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2828                boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchEmpty,
2829            }),
2830            Self::MutationBatchTooManyItems => {
2831                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2832                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchTooManyItems,
2833                })
2834            }
2835            Self::MutationBatchStagedBytesExceeded => {
2836                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2837                    boundary:
2838                        diagnostic_code::RuntimeBoundaryCode::MutationBatchStagedBytesExceeded,
2839                })
2840            }
2841            Self::MutationBatchResultBytesExceeded => {
2842                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2843                    boundary:
2844                        diagnostic_code::RuntimeBoundaryCode::MutationBatchResultBytesExceeded,
2845                })
2846            }
2847            Self::MutationBatchStoreMismatch => {
2848                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2849                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchStoreMismatch,
2850                })
2851            }
2852            Self::MutationBatchTooManyEntities => {
2853                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2854                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchTooManyEntities,
2855                })
2856            }
2857            Self::MutationBatchDuplicateKey => {
2858                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2859                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchDuplicateKey,
2860                })
2861            }
2862            Self::MutationManagedTimestampRegression => {
2863                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2864                    boundary:
2865                        diagnostic_code::RuntimeBoundaryCode::MutationManagedTimestampRegression,
2866                })
2867            }
2868            Self::AcceptedRowConstraintProgramCorrupt => {
2869                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2870                    boundary:
2871                        diagnostic_code::RuntimeBoundaryCode::AcceptedRowConstraintProgramCorrupt,
2872                })
2873            }
2874        }
2875    }
2876
2877    /// Project safe mutation detail into canonical public numeric facts.
2878    #[must_use]
2879    #[cold]
2880    #[inline(never)]
2881    pub const fn diagnostic_facts(&self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
2882        Vec::new()
2883    }
2884}
2885
2886impl RecoveryErrorDetail {
2887    /// Return the compact diagnostic code for this recovery detail.
2888    #[must_use]
2889    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2890        match self {
2891            Self::UnsupportedFormatVersion { .. } => {
2892                diagnostic_code::DiagnosticCode::RuntimeIncompatiblePersistedFormat
2893            }
2894            Self::MalformedFormatMarker { .. } => {
2895                diagnostic_code::DiagnosticCode::RuntimeCorruption
2896            }
2897        }
2898    }
2899
2900    /// Return compact structured diagnostic detail for this recovery detail.
2901    #[must_use]
2902    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2903        let kind = match self {
2904            Self::UnsupportedFormatVersion { .. } => {
2905                diagnostic_code::RuntimeErrorKind::IncompatiblePersistedFormat
2906            }
2907            Self::MalformedFormatMarker { .. } => diagnostic_code::RuntimeErrorKind::Corruption,
2908        };
2909
2910        Some(diagnostic_code::DiagnosticDetail::RuntimeKind { kind })
2911    }
2912
2913    /// Project database-format recovery context without retaining marker bytes.
2914    #[must_use]
2915    pub fn diagnostic_facts(&self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
2916        match self {
2917            Self::UnsupportedFormatVersion { found, required } => {
2918                let mut facts = Vec::with_capacity(usize::from(found.is_some()) + 1);
2919                facts.push((
2920                    diagnostic_code::DiagnosticFactTag::ExpectedVersion,
2921                    u64::from(*required),
2922                ));
2923                if let Some(found) = found {
2924                    facts.push((
2925                        diagnostic_code::DiagnosticFactTag::ActualVersion,
2926                        u64::from(*found),
2927                    ));
2928                }
2929                facts
2930            }
2931            Self::MalformedFormatMarker { reason } => vec![(
2932                diagnostic_code::DiagnosticFactTag::DecodeReason,
2933                reason.diagnostic_decode_reason().raw(),
2934            )],
2935        }
2936    }
2937}
2938
2939impl StoreError {
2940    /// Return the compact diagnostic code for this store detail.
2941    #[must_use]
2942    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2943        match self {
2944            Self::NotFound => diagnostic_code::DiagnosticCode::StoreNotFound,
2945            Self::Corrupt => diagnostic_code::DiagnosticCode::StoreCorruption,
2946            Self::InvariantViolation => diagnostic_code::DiagnosticCode::StoreInvariantViolation,
2947            Self::SchemaDdlPublicationRaceLost
2948            | Self::SchemaDdlRewriteRequiresMigration
2949            | Self::SchemaRowLayoutVersionExhausted
2950            | Self::SchemaTransitionBudgetExceeded { .. } => {
2951                diagnostic_code::DiagnosticCode::SchemaDdlAdmission
2952            }
2953            Self::JournalMutationRevisionExhausted | Self::SchemaGeneratedFieldAfterDdlField => {
2954                diagnostic_code::DiagnosticCode::RuntimeUnsupported
2955            }
2956            Self::SchemaGeneratedConstraintActivationStale => {
2957                diagnostic_code::DiagnosticCode::RuntimeConflict
2958            }
2959            Self::SchemaMigration { reason } => reason.diagnostic_code(),
2960        }
2961    }
2962
2963    /// Return compact structured diagnostic detail when the store error has one.
2964    #[must_use]
2965    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2966        match self {
2967            Self::SchemaDdlPublicationRaceLost => {
2968                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2969                    reason: diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost,
2970                })
2971            }
2972            Self::SchemaDdlRewriteRequiresMigration => {
2973                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2974                    reason: diagnostic_code::SchemaDdlAdmissionCode::SchemaRewriteRequiresMigration,
2975                })
2976            }
2977            Self::SchemaMigration { reason } => {
2978                Some(diagnostic_code::DiagnosticDetail::SchemaMigration { reason: *reason })
2979            }
2980            Self::SchemaRowLayoutVersionExhausted => {
2981                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2982                    reason: diagnostic_code::SchemaDdlAdmissionCode::RowLayoutVersionExhausted,
2983                })
2984            }
2985            Self::JournalMutationRevisionExhausted => {
2986                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2987                    boundary:
2988                        diagnostic_code::RuntimeBoundaryCode::JournalMutationRevisionExhausted,
2989                })
2990            }
2991            Self::SchemaTransitionBudgetExceeded { .. } => {
2992                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2993                    reason: diagnostic_code::SchemaDdlAdmissionCode::SchemaTransitionBudgetExceeded,
2994                })
2995            }
2996            Self::SchemaGeneratedFieldAfterDdlField => {
2997                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2998                    boundary: diagnostic_code::RuntimeBoundaryCode::GeneratedFieldAfterDdlField,
2999                })
3000            }
3001            Self::SchemaGeneratedConstraintActivationStale => {
3002                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
3003                    boundary:
3004                        diagnostic_code::RuntimeBoundaryCode::GeneratedConstraintActivationStale,
3005                })
3006            }
3007            Self::NotFound | Self::Corrupt | Self::InvariantViolation => None,
3008        }
3009    }
3010}
3011
3012impl QueryErrorDetail {
3013    /// Return the compact diagnostic code for this query detail.
3014    #[must_use]
3015    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
3016        match self {
3017            Self::ExactCountMetadataUnavailable => {
3018                diagnostic_code::DiagnosticCode::QueryExactCountMetadataUnavailable
3019            }
3020            Self::NumericOverflow => diagnostic_code::DiagnosticCode::QueryNumericOverflow,
3021            Self::NumericNotRepresentable => {
3022                diagnostic_code::DiagnosticCode::QueryNumericNotRepresentable
3023            }
3024            Self::UnsupportedSqlFeature { .. } => {
3025                diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature
3026            }
3027            Self::SqlLowering { .. } => diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature,
3028            Self::UnsupportedProjection { .. } => {
3029                diagnostic_code::DiagnosticCode::QueryUnsupportedProjection
3030            }
3031            Self::UnknownAggregateTargetField => {
3032                diagnostic_code::DiagnosticCode::QueryUnknownAggregateTargetField
3033            }
3034            Self::QueryReadAdmission { .. } => diagnostic_code::DiagnosticCode::QueryReadAdmission,
3035            Self::SqlSurfaceMismatch { .. } => {
3036                diagnostic_code::DiagnosticCode::QuerySqlSurfaceMismatch
3037            }
3038            Self::SqlWriteBoundary { .. } => diagnostic_code::DiagnosticCode::QuerySqlWriteBoundary,
3039            Self::SchemaDdlAdmission { .. } => diagnostic_code::DiagnosticCode::SchemaDdlAdmission,
3040            Self::StaleSchemaRevision => diagnostic_code::DiagnosticCode::RuntimeConflict,
3041        }
3042    }
3043
3044    /// Return compact structured diagnostic detail when the query detail has one.
3045    #[must_use]
3046    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
3047        match self {
3048            Self::UnsupportedSqlFeature { feature } => {
3049                Some(diagnostic_code::DiagnosticDetail::UnsupportedSqlFeature { feature: *feature })
3050            }
3051            Self::SqlLowering { reason } => {
3052                Some(diagnostic_code::DiagnosticDetail::SqlLowering { reason: *reason })
3053            }
3054            Self::UnsupportedProjection { reason } => {
3055                Some(diagnostic_code::DiagnosticDetail::QueryProjection { reason: *reason })
3056            }
3057            Self::QueryReadAdmission { reason } => {
3058                Some(diagnostic_code::DiagnosticDetail::QueryReadAdmission { reason: *reason })
3059            }
3060            Self::SqlSurfaceMismatch { mismatch } => {
3061                Some(diagnostic_code::DiagnosticDetail::SqlSurfaceMismatch {
3062                    mismatch: *mismatch,
3063                })
3064            }
3065            Self::SqlWriteBoundary { boundary } => {
3066                Some(diagnostic_code::DiagnosticDetail::SqlWriteBoundary {
3067                    boundary: *boundary,
3068                })
3069            }
3070            Self::SchemaDdlAdmission { error } => {
3071                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
3072                    reason: error.diagnostic_code(),
3073                })
3074            }
3075            Self::ExactCountMetadataUnavailable
3076            | Self::NumericOverflow
3077            | Self::NumericNotRepresentable
3078            | Self::UnknownAggregateTargetField
3079            | Self::StaleSchemaRevision => None,
3080        }
3081    }
3082
3083    /// Project safe query detail into canonical public numeric facts.
3084    #[must_use]
3085    #[cold]
3086    #[inline(never)]
3087    pub const fn diagnostic_facts(&self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
3088        Vec::new()
3089    }
3090}
3091
3092impl SchemaDdlAdmissionError {
3093    /// Return the compact diagnostic code for this SQL DDL admission reason.
3094    #[must_use]
3095    pub const fn diagnostic_code(&self) -> diagnostic_code::SchemaDdlAdmissionCode {
3096        match self {
3097            Self::MissingExpectedSchemaVersion => {
3098                diagnostic_code::SchemaDdlAdmissionCode::MissingExpectedSchemaVersion
3099            }
3100            Self::MissingNextSchemaVersion => {
3101                diagnostic_code::SchemaDdlAdmissionCode::MissingNextSchemaVersion
3102            }
3103            Self::StaleExpectedSchemaVersion => {
3104                diagnostic_code::SchemaDdlAdmissionCode::StaleExpectedSchemaVersion
3105            }
3106            Self::InvalidExpectedSchemaVersion => {
3107                diagnostic_code::SchemaDdlAdmissionCode::InvalidExpectedSchemaVersion
3108            }
3109            Self::InvalidNextSchemaVersion => {
3110                diagnostic_code::SchemaDdlAdmissionCode::InvalidNextSchemaVersion
3111            }
3112            Self::AcceptedSchemaChangeWithoutVersionBump => {
3113                diagnostic_code::SchemaDdlAdmissionCode::AcceptedSchemaChangeWithoutVersionBump
3114            }
3115            Self::EmptyVersionBump => diagnostic_code::SchemaDdlAdmissionCode::EmptyVersionBump,
3116            Self::VersionGap => diagnostic_code::SchemaDdlAdmissionCode::VersionGap,
3117            Self::VersionRollback => diagnostic_code::SchemaDdlAdmissionCode::VersionRollback,
3118            Self::FingerprintMethodMismatch => {
3119                diagnostic_code::SchemaDdlAdmissionCode::FingerprintMethodMismatch
3120            }
3121            Self::UnsupportedTransitionClass => {
3122                diagnostic_code::SchemaDdlAdmissionCode::UnsupportedTransitionClass
3123            }
3124            Self::PhysicalRunnerMissing => {
3125                diagnostic_code::SchemaDdlAdmissionCode::PhysicalRunnerMissing
3126            }
3127            Self::ValidationFailed => diagnostic_code::SchemaDdlAdmissionCode::ValidationFailed,
3128            Self::PublicationRaceLost => {
3129                diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost
3130            }
3131            Self::InvalidAddColumnDefault => {
3132                diagnostic_code::SchemaDdlAdmissionCode::InvalidAddColumnDefault
3133            }
3134            Self::InvalidAlterColumnDefault => {
3135                diagnostic_code::SchemaDdlAdmissionCode::InvalidAlterColumnDefault
3136            }
3137            Self::GeneratedIndexDropRejected => {
3138                diagnostic_code::SchemaDdlAdmissionCode::GeneratedIndexDropRejected
3139            }
3140            Self::SchemaRewriteRequiresMigration => {
3141                diagnostic_code::SchemaDdlAdmissionCode::SchemaRewriteRequiresMigration
3142            }
3143            Self::SchemaTransitionBudgetExceeded { .. } => {
3144                diagnostic_code::SchemaDdlAdmissionCode::SchemaTransitionBudgetExceeded
3145            }
3146            Self::GeneratedFieldDefaultChangeRejected => {
3147                diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldDefaultChangeRejected
3148            }
3149            Self::GeneratedFieldNullabilityChangeRejected => {
3150                diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldNullabilityChangeRejected
3151            }
3152            Self::RowLayoutVersionExhausted => {
3153                diagnostic_code::SchemaDdlAdmissionCode::RowLayoutVersionExhausted
3154            }
3155        }
3156    }
3157}
3158
3159///
3160/// ErrorClass
3161/// Internal error taxonomy for runtime classification.
3162/// Not a stable API; may change without notice.
3163///
3164
3165#[repr(u8)]
3166#[derive(Clone, Copy, Eq, PartialEq)]
3167pub enum ErrorClass {
3168    Corruption,
3169    IncompatiblePersistedFormat,
3170    NotFound,
3171    Internal,
3172    Conflict,
3173    Unsupported,
3174    InvariantViolation,
3175}
3176
3177impl ErrorClass {
3178    /// Return a compact diagnostic code for this broad class and origin pair.
3179    #[must_use]
3180    pub const fn diagnostic_code(self, origin: ErrorOrigin) -> diagnostic_code::DiagnosticCode {
3181        match self {
3182            Self::Corruption if matches!(origin, ErrorOrigin::Store) => {
3183                diagnostic_code::DiagnosticCode::StoreCorruption
3184            }
3185            Self::Corruption => diagnostic_code::DiagnosticCode::RuntimeCorruption,
3186            Self::IncompatiblePersistedFormat => {
3187                diagnostic_code::DiagnosticCode::RuntimeIncompatiblePersistedFormat
3188            }
3189            Self::NotFound if matches!(origin, ErrorOrigin::Store) => {
3190                diagnostic_code::DiagnosticCode::StoreNotFound
3191            }
3192            Self::NotFound => diagnostic_code::DiagnosticCode::RuntimeNotFound,
3193            Self::Internal => diagnostic_code::DiagnosticCode::RuntimeInternal,
3194            Self::Conflict => diagnostic_code::DiagnosticCode::RuntimeConflict,
3195            Self::Unsupported if matches!(origin, ErrorOrigin::Cursor) => {
3196                diagnostic_code::DiagnosticCode::QueryInvalidContinuationCursor
3197            }
3198            Self::Unsupported => diagnostic_code::DiagnosticCode::RuntimeUnsupported,
3199            Self::InvariantViolation if matches!(origin, ErrorOrigin::Store) => {
3200                diagnostic_code::DiagnosticCode::StoreInvariantViolation
3201            }
3202            Self::InvariantViolation => diagnostic_code::DiagnosticCode::RuntimeInvariantViolation,
3203        }
3204    }
3205}
3206
3207impl fmt::Debug for ErrorClass {
3208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3209        write!(f, "{}", *self as u8)
3210    }
3211}
3212
3213///
3214/// ErrorOrigin
3215/// Internal origin taxonomy for runtime classification.
3216/// Not a stable API; may change without notice.
3217///
3218
3219#[repr(u8)]
3220#[derive(Clone, Copy, Eq, PartialEq)]
3221pub enum ErrorOrigin {
3222    Serialize,
3223    Store,
3224    Index,
3225    Identity,
3226    Query,
3227    Planner,
3228    Cursor,
3229    Recovery,
3230    Response,
3231    Executor,
3232    Interface,
3233}
3234
3235impl ErrorOrigin {
3236    /// Return the compact diagnostic origin for this internal origin.
3237    #[must_use]
3238    pub const fn diagnostic_origin(self) -> diagnostic_code::ErrorOrigin {
3239        match self {
3240            Self::Serialize => diagnostic_code::ErrorOrigin::Serialize,
3241            Self::Store => diagnostic_code::ErrorOrigin::Store,
3242            Self::Index => diagnostic_code::ErrorOrigin::Index,
3243            Self::Identity => diagnostic_code::ErrorOrigin::Identity,
3244            Self::Query => diagnostic_code::ErrorOrigin::Query,
3245            Self::Planner => diagnostic_code::ErrorOrigin::Planner,
3246            Self::Cursor => diagnostic_code::ErrorOrigin::Cursor,
3247            Self::Recovery => diagnostic_code::ErrorOrigin::Recovery,
3248            Self::Response => diagnostic_code::ErrorOrigin::Response,
3249            Self::Executor => diagnostic_code::ErrorOrigin::Executor,
3250            Self::Interface => diagnostic_code::ErrorOrigin::Interface,
3251        }
3252    }
3253}
3254
3255impl fmt::Debug for ErrorOrigin {
3256    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3257        write!(f, "{}", *self as u8)
3258    }
3259}