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