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