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 relation-target entity mismatch corruption error.
1480    pub(crate) fn relation_target_entity_mismatch(expected_tag: u64, actual_tag: u64) -> Self {
1481        Self::with_diagnostic_facts(
1482            ErrorClass::Corruption,
1483            ErrorOrigin::Store,
1484            None,
1485            vec![
1486                (
1487                    diagnostic_code::DiagnosticFactTag::ExpectedEntityTag,
1488                    expected_tag,
1489                ),
1490                (
1491                    diagnostic_code::DiagnosticFactTag::ActualEntityTag,
1492                    actual_tag,
1493                ),
1494            ],
1495        )
1496    }
1497
1498    /// Construct the canonical covering-component empty-payload corruption error.
1499    pub(crate) fn bytes_covering_component_payload_empty() -> Self {
1500        Self::index_corruption()
1501    }
1502
1503    /// Construct the canonical covering-component truncated bool corruption error.
1504    pub(crate) fn bytes_covering_bool_payload_truncated() -> Self {
1505        Self::index_corruption()
1506    }
1507
1508    /// Construct the canonical covering-component invalid-length corruption error.
1509    pub(crate) fn bytes_covering_component_payload_invalid_length() -> Self {
1510        Self::index_corruption()
1511    }
1512
1513    /// Construct the canonical covering-component invalid-bool corruption error.
1514    pub(crate) fn bytes_covering_bool_payload_invalid_value() -> Self {
1515        Self::index_corruption()
1516    }
1517
1518    /// Construct the canonical covering-component invalid text terminator corruption error.
1519    pub(crate) fn bytes_covering_text_payload_invalid_terminator() -> Self {
1520        Self::index_corruption()
1521    }
1522
1523    /// Construct the canonical covering-component trailing-text corruption error.
1524    pub(crate) fn bytes_covering_text_payload_trailing_bytes() -> Self {
1525        Self::index_corruption()
1526    }
1527
1528    /// Construct the canonical covering-component invalid-UTF-8 text corruption error.
1529    pub(crate) fn bytes_covering_text_payload_invalid_utf8() -> Self {
1530        Self::index_corruption()
1531    }
1532
1533    /// Construct the canonical covering-component invalid text escape corruption error.
1534    pub(crate) fn bytes_covering_text_payload_invalid_escape_byte() -> Self {
1535        Self::index_corruption()
1536    }
1537
1538    /// Construct the canonical covering-component missing text terminator corruption error.
1539    pub(crate) fn bytes_covering_text_payload_missing_terminator() -> Self {
1540        Self::index_corruption()
1541    }
1542
1543    /// Construct an identity-origin corruption error.
1544    pub(crate) fn identity_corruption() -> Self {
1545        Self::new(ErrorClass::Corruption, ErrorOrigin::Identity)
1546    }
1547
1548    /// Construct the canonical identity-control-state corruption error.
1549    pub(crate) fn identity_state_corruption() -> Self {
1550        Self::identity_corruption()
1551    }
1552
1553    /// Construct the typed stale high-water conflict for identity publication.
1554    pub(crate) fn identity_state_conflict() -> Self {
1555        Self::new(ErrorClass::Conflict, ErrorOrigin::Identity)
1556    }
1557
1558    /// Construct the bounded identity-state inventory exhaustion error.
1559    pub(crate) fn identity_state_capacity_exhausted() -> Self {
1560        Self::new(ErrorClass::Unsupported, ErrorOrigin::Identity)
1561    }
1562
1563    /// Construct the exact unsigned identity-domain exhaustion error.
1564    pub(crate) fn identity_exhausted() -> Self {
1565        Self::new(ErrorClass::Unsupported, ErrorOrigin::Identity)
1566    }
1567
1568    /// Construct the bounded pre-key candidate-count exhaustion error.
1569    pub(crate) fn identity_candidate_count_exhausted() -> Self {
1570        Self::new(ErrorClass::Unsupported, ErrorOrigin::Identity)
1571    }
1572
1573    /// Construct a store-origin unsupported error.
1574    #[cold]
1575    #[inline(never)]
1576    pub(crate) fn store_unsupported() -> Self {
1577        Self::new(ErrorClass::Unsupported, ErrorOrigin::Store)
1578    }
1579
1580    /// Construct the typed optimistic/idempotency conflict for schema application.
1581    pub(crate) fn schema_application_conflict() -> Self {
1582        Self::new(ErrorClass::Conflict, ErrorOrigin::Store)
1583    }
1584
1585    /// Construct one typed source-migration lifecycle or planning result.
1586    pub(crate) fn schema_migration(reason: diagnostic_code::SchemaMigrationCode) -> Self {
1587        let class = match reason.diagnostic_code() {
1588            diagnostic_code::DiagnosticCode::RuntimeConflict => ErrorClass::Conflict,
1589            diagnostic_code::DiagnosticCode::RuntimeCorruption => ErrorClass::Corruption,
1590            diagnostic_code::DiagnosticCode::RuntimeUnsupported => ErrorClass::Unsupported,
1591            _ => ErrorClass::Internal,
1592        };
1593        Self {
1594            class,
1595            origin: ErrorOrigin::Store,
1596            detail: Some(ErrorDetail::Store(StoreError::SchemaMigration { reason })),
1597        }
1598    }
1599
1600    /// Construct the canonical schema DDL publication race error.
1601    pub(crate) fn schema_ddl_publication_race_lost() -> Self {
1602        Self {
1603            class: ErrorClass::Unsupported,
1604            origin: ErrorOrigin::Store,
1605            detail: Some(ErrorDetail::Store(StoreError::SchemaDdlPublicationRaceLost)),
1606        }
1607    }
1608
1609    /// Construct the canonical current physical-rewrite migration rejection.
1610    #[cfg(feature = "sql")]
1611    pub(crate) fn schema_ddl_rewrite_requires_migration() -> Self {
1612        Self {
1613            class: ErrorClass::Unsupported,
1614            origin: ErrorOrigin::Store,
1615            detail: Some(ErrorDetail::Store(
1616                StoreError::SchemaDdlRewriteRequiresMigration,
1617            )),
1618        }
1619    }
1620
1621    /// Construct the fail-closed journal mutation-revision exhaustion error.
1622    pub(crate) fn journal_mutation_revision_exhausted() -> Self {
1623        Self {
1624            class: ErrorClass::Unsupported,
1625            origin: ErrorOrigin::Store,
1626            detail: Some(ErrorDetail::Store(
1627                StoreError::JournalMutationRevisionExhausted,
1628            )),
1629        }
1630    }
1631
1632    /// Construct a bounded schema-transition resource rejection.
1633    pub(crate) fn schema_transition_budget_exceeded(
1634        resource: SchemaTransitionBudgetResource,
1635    ) -> Self {
1636        Self {
1637            class: ErrorClass::Unsupported,
1638            origin: ErrorOrigin::Store,
1639            detail: Some(ErrorDetail::Store(
1640                StoreError::SchemaTransitionBudgetExceeded { resource },
1641            )),
1642        }
1643    }
1644
1645    /// Construct an index-origin unsupported error.
1646    pub(crate) fn index_unsupported() -> Self {
1647        Self::new(ErrorClass::Unsupported, ErrorOrigin::Index)
1648    }
1649
1650    /// Reject a complete encoded index key with bounded accepted-index evidence.
1651    pub(crate) fn index_key_exceeds_max_size_at(
1652        entity_tag: u64,
1653        physical_generation: u64,
1654        actual_length: usize,
1655        limit: usize,
1656    ) -> Self {
1657        Self::with_diagnostic_facts(
1658            ErrorClass::Unsupported,
1659            ErrorOrigin::Index,
1660            None,
1661            vec![
1662                (diagnostic_code::DiagnosticFactTag::EntityTag, entity_tag),
1663                (
1664                    diagnostic_code::DiagnosticFactTag::PhysicalGeneration,
1665                    physical_generation,
1666                ),
1667                (
1668                    diagnostic_code::DiagnosticFactTag::ComponentKind,
1669                    diagnostic_code::DiagnosticComponentKind::IndexKey.raw(),
1670                ),
1671                (
1672                    diagnostic_code::DiagnosticFactTag::ActualLength,
1673                    actual_length as u64,
1674                ),
1675                (diagnostic_code::DiagnosticFactTag::Limit, limit as u64),
1676            ],
1677        )
1678    }
1679
1680    /// Construct the canonical index-key component size-limit unsupported error.
1681    pub(crate) fn index_component_exceeds_max_size_at(
1682        entity_tag: u64,
1683        physical_generation: u64,
1684        component_index: usize,
1685        actual_length: usize,
1686        limit: usize,
1687    ) -> Self {
1688        Self::with_diagnostic_facts(
1689            ErrorClass::Unsupported,
1690            ErrorOrigin::Index,
1691            None,
1692            vec![
1693                (diagnostic_code::DiagnosticFactTag::EntityTag, entity_tag),
1694                (
1695                    diagnostic_code::DiagnosticFactTag::PhysicalGeneration,
1696                    physical_generation,
1697                ),
1698                (
1699                    diagnostic_code::DiagnosticFactTag::ComponentIndex,
1700                    component_index as u64,
1701                ),
1702                (
1703                    diagnostic_code::DiagnosticFactTag::ComponentKind,
1704                    diagnostic_code::DiagnosticComponentKind::IndexKeyComponent.raw(),
1705                ),
1706                (
1707                    diagnostic_code::DiagnosticFactTag::ActualLength,
1708                    actual_length as u64,
1709                ),
1710                (diagnostic_code::DiagnosticFactTag::Limit, limit as u64),
1711            ],
1712        )
1713    }
1714
1715    /// Construct the canonical index-key component size-limit error when the
1716    /// generic caller has not retained one accepted index identity.
1717    pub(crate) fn index_component_exceeds_max_size() -> Self {
1718        Self::index_unsupported()
1719    }
1720
1721    /// Construct a serialize-origin unsupported error.
1722    pub(crate) fn serialize_unsupported() -> Self {
1723        Self::new(ErrorClass::Unsupported, ErrorOrigin::Serialize)
1724    }
1725
1726    /// Construct a cursor-origin invalid-continuation error.
1727    pub(crate) fn cursor_invalid_continuation() -> Self {
1728        Self::new(ErrorClass::Unsupported, ErrorOrigin::Cursor)
1729    }
1730
1731    /// Construct a serialize-origin incompatible persisted-format error.
1732    pub(crate) fn serialize_incompatible_persisted_format() -> Self {
1733        Self::new(
1734            ErrorClass::IncompatiblePersistedFormat,
1735            ErrorOrigin::Serialize,
1736        )
1737    }
1738
1739    /// Construct a query-origin unsupported error preserving one SQL parser
1740    /// unsupported-feature code in structured error detail.
1741    #[cfg(feature = "sql")]
1742    pub(crate) fn query_unsupported_sql_feature(feature: diagnostic_code::SqlFeatureCode) -> Self {
1743        Self {
1744            class: ErrorClass::Unsupported,
1745            origin: ErrorOrigin::Query,
1746            detail: Some(ErrorDetail::Query(
1747                QueryErrorDetail::UnsupportedSqlFeature { feature },
1748            )),
1749        }
1750    }
1751
1752    /// Construct a query-origin unsupported SQL lowering error preserving one
1753    /// compact lowering reason in structured error detail.
1754    #[cfg(feature = "sql")]
1755    pub(crate) fn query_sql_lowering(reason: diagnostic_code::SqlLoweringCode) -> Self {
1756        Self {
1757            class: ErrorClass::Unsupported,
1758            origin: ErrorOrigin::Query,
1759            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlLowering { reason })),
1760        }
1761    }
1762
1763    /// Construct one query-origin SQL lowering error with bounded numeric context.
1764    #[cfg(feature = "sql")]
1765    pub(crate) fn query_sql_lowering_with_facts(
1766        reason: diagnostic_code::SqlLoweringCode,
1767        facts: Vec<(diagnostic_code::DiagnosticFactTag, u64)>,
1768    ) -> Self {
1769        Self::with_diagnostic_facts(
1770            ErrorClass::Unsupported,
1771            ErrorOrigin::Query,
1772            Some(diagnostic_code::DiagnosticDetail::SqlLowering { reason }),
1773            facts,
1774        )
1775    }
1776
1777    /// Construct a query-origin unsupported projection error preserving one
1778    /// compact projection reason in structured error detail.
1779    pub(crate) fn query_unsupported_projection(
1780        reason: diagnostic_code::QueryProjectionCode,
1781    ) -> Self {
1782        Self {
1783            class: ErrorClass::Unsupported,
1784            origin: ErrorOrigin::Query,
1785            detail: Some(ErrorDetail::Query(
1786                QueryErrorDetail::UnsupportedProjection { reason },
1787            )),
1788        }
1789    }
1790
1791    /// Construct a query-origin unsupported error preserving one SQL endpoint
1792    /// surface mismatch in structured error detail.
1793    #[cfg(feature = "sql")]
1794    pub(crate) fn query_sql_surface_mismatch(
1795        mismatch: diagnostic_code::SqlSurfaceMismatchCode,
1796    ) -> Self {
1797        Self {
1798            class: ErrorClass::Unsupported,
1799            origin: ErrorOrigin::Query,
1800            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlSurfaceMismatch {
1801                mismatch,
1802            })),
1803        }
1804    }
1805
1806    /// Construct a query-origin unsupported SQL write boundary error.
1807    pub(crate) fn query_sql_write_boundary(
1808        boundary: diagnostic_code::SqlWriteBoundaryCode,
1809    ) -> Self {
1810        Self {
1811            class: ErrorClass::Unsupported,
1812            origin: ErrorOrigin::Query,
1813            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlWriteBoundary {
1814                boundary,
1815            })),
1816        }
1817    }
1818
1819    /// Construct one query-origin SQL write-boundary error with bounded numeric context.
1820    pub(crate) fn query_sql_write_boundary_with_facts(
1821        boundary: diagnostic_code::SqlWriteBoundaryCode,
1822        facts: Vec<(diagnostic_code::DiagnosticFactTag, u64)>,
1823    ) -> Self {
1824        Self::with_diagnostic_facts(
1825            ErrorClass::Unsupported,
1826            ErrorOrigin::Query,
1827            Some(diagnostic_code::DiagnosticDetail::SqlWriteBoundary { boundary }),
1828            facts,
1829        )
1830    }
1831
1832    pub fn store_not_found(_key: impl Sized) -> Self {
1833        Self {
1834            class: ErrorClass::NotFound,
1835            origin: ErrorOrigin::Store,
1836            detail: Some(ErrorDetail::Store(StoreError::NotFound)),
1837        }
1838    }
1839
1840    /// Construct a standardized unsupported-entity-path error.
1841    pub fn unsupported_entity_path(_path: impl Sized) -> Self {
1842        Self::store_unsupported()
1843    }
1844
1845    /// Construct an index-plan corruption error with a canonical prefix.
1846    #[cold]
1847    #[inline(never)]
1848    pub(crate) fn index_plan_corruption(origin: ErrorOrigin) -> Self {
1849        Self::new(ErrorClass::Corruption, origin)
1850    }
1851
1852    /// Construct an index-plan corruption error for index-origin failures.
1853    #[cold]
1854    #[inline(never)]
1855    pub(crate) fn index_plan_index_corruption() -> Self {
1856        Self::index_plan_corruption(ErrorOrigin::Index)
1857    }
1858
1859    /// Construct an index-plan corruption error for store-origin failures.
1860    #[cold]
1861    #[inline(never)]
1862    pub(crate) fn index_plan_store_corruption() -> Self {
1863        Self::index_plan_corruption(ErrorOrigin::Store)
1864    }
1865
1866    /// Construct an index-plan corruption error for serialize-origin failures.
1867    #[cold]
1868    #[inline(never)]
1869    pub(crate) fn index_plan_serialize_corruption() -> Self {
1870        Self::index_plan_corruption(ErrorOrigin::Serialize)
1871    }
1872
1873    /// Construct an index-plan invariant violation error with a canonical prefix.
1874    #[cfg(test)]
1875    pub(crate) fn index_plan_invariant(origin: ErrorOrigin) -> Self {
1876        Self::new(ErrorClass::InvariantViolation, origin)
1877    }
1878
1879    /// Construct an index-plan invariant violation error for store-origin failures.
1880    #[cfg(test)]
1881    pub(crate) fn index_plan_store_invariant() -> Self {
1882        Self::index_plan_invariant(ErrorOrigin::Store)
1883    }
1884
1885    /// Construct an index-origin conflict without claiming accepted identity.
1886    ///
1887    /// Live accepted uniqueness violations use compact accepted-constraint facts.
1888    /// Schema-domain staging and activation findings use this compact
1889    /// classification before an accepted write-admission diagnostic exists.
1890    pub(crate) fn index_conflict() -> Self {
1891        Self::new(ErrorClass::Conflict, ErrorOrigin::Index)
1892    }
1893}
1894
1895impl From<diagnostic_code::QueryReadAdmissionCode> for InternalError {
1896    fn from(reason: diagnostic_code::QueryReadAdmissionCode) -> Self {
1897        Self {
1898            class: ErrorClass::Unsupported,
1899            origin: ErrorOrigin::Query,
1900            detail: Some(ErrorDetail::Query(QueryErrorDetail::QueryReadAdmission {
1901                reason,
1902            })),
1903        }
1904    }
1905}
1906
1907impl fmt::Debug for InternalError {
1908    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1909        fmt_compact_diagnostic(
1910            f,
1911            self.diagnostic_code(),
1912            self.detail
1913                .as_ref()
1914                .and_then(ErrorDetail::diagnostic_detail),
1915        )
1916    }
1917}
1918
1919impl fmt::Display for InternalError {
1920    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1921        f.write_str(self.message())
1922    }
1923}
1924
1925impl std::error::Error for InternalError {}
1926
1927///
1928/// ConstraintValuePathComponent
1929///
1930/// Stable accepted identity or finite-value coordinate in one targeted-rule
1931/// violation. Display names are deliberately absent so renames cannot change
1932/// the diagnostic identity.
1933///
1934
1935#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, serde::Serialize)]
1936pub enum ConstraintValuePathComponent {
1937    /// Persisted root field whose admitted value was traversed.
1938    RootField { field_id: u32 },
1939
1940    /// Accepted record member selected by immutable composite/member identity.
1941    RecordMember {
1942        composite_type_id: u32,
1943        member_id: u32,
1944    },
1945
1946    /// Tuple element selected by accepted composite identity and ordinal.
1947    TupleElement {
1948        composite_type_id: u32,
1949        ordinal: u32,
1950    },
1951
1952    /// Transparent accepted newtype boundary.
1953    Newtype { composite_type_id: u32 },
1954
1955    /// Selected accepted enum variant.
1956    EnumVariant { enum_type_id: u32, variant_id: u32 },
1957
1958    /// List element in admitted order.
1959    ListElement { index: u32 },
1960
1961    /// Set element in canonical admitted order.
1962    SetElement { index: u32 },
1963
1964    /// Map key in canonical entry order.
1965    MapEntryKey { index: u32 },
1966
1967    /// Map value in canonical entry order.
1968    MapEntryValue { index: u32 },
1969}
1970
1971impl fmt::Display for ConstraintValuePathComponent {
1972    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1973        match self {
1974            Self::RootField { field_id } => write!(f, "field#{field_id}"),
1975            Self::RecordMember {
1976                composite_type_id,
1977                member_id,
1978            } => write!(f, "record#{composite_type_id}.member#{member_id}"),
1979            Self::TupleElement {
1980                composite_type_id,
1981                ordinal,
1982            } => write!(f, "tuple#{composite_type_id}[{ordinal}]"),
1983            Self::Newtype { composite_type_id } => write!(f, "newtype#{composite_type_id}"),
1984            Self::EnumVariant {
1985                enum_type_id,
1986                variant_id,
1987            } => write!(f, "enum#{enum_type_id}.variant#{variant_id}"),
1988            Self::ListElement { index } => write!(f, "list[{index}]"),
1989            Self::SetElement { index } => write!(f, "set[{index}]"),
1990            Self::MapEntryKey { index } => write!(f, "map[{index}].key"),
1991            Self::MapEntryValue { index } => write!(f, "map[{index}].value"),
1992        }
1993    }
1994}
1995
1996///
1997/// ConstraintValuePath
1998///
1999/// Bounded typed path to the first deterministic failing value occurrence.
2000///
2001
2002#[derive(Clone, Debug, Deserialize, Eq, PartialEq, serde::Serialize)]
2003pub struct ConstraintValuePath {
2004    components: Vec<ConstraintValuePathComponent>,
2005}
2006
2007impl ConstraintValuePath {
2008    /// Build one already-bounded accepted occurrence path.
2009    #[must_use]
2010    pub(crate) const fn new(components: Vec<ConstraintValuePathComponent>) -> Self {
2011        Self { components }
2012    }
2013
2014    /// Borrow the stable accepted components.
2015    #[must_use]
2016    pub const fn components(&self) -> &[ConstraintValuePathComponent] {
2017        self.components.as_slice()
2018    }
2019}
2020
2021impl fmt::Display for ConstraintValuePath {
2022    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2023        for (ordinal, component) in self.components.iter().enumerate() {
2024            if ordinal != 0 {
2025                f.write_str("/")?;
2026            }
2027            component.fmt(f)?;
2028        }
2029        Ok(())
2030    }
2031}
2032
2033///
2034/// ConstraintValidationFindingOutput
2035///
2036/// Bounded historical validation evidence returned only by explicit schema
2037/// validation operations. Names are resolved by host tooling from the exact
2038/// accepted fingerprint and immutable numeric identities.
2039///
2040
2041#[derive(Clone, Debug, Deserialize, Eq, PartialEq, serde::Serialize)]
2042pub struct ConstraintValidationFindingOutput {
2043    accepted_schema_fingerprint: [u8; 16],
2044    entity_tag: u64,
2045    constraint_id: u32,
2046    primary_key: Vec<u8>,
2047    field_ids: Vec<u32>,
2048    value_path: Option<ConstraintValuePath>,
2049    error_code: u16,
2050}
2051
2052impl ConstraintValidationFindingOutput {
2053    /// Build one already-bounded historical validation finding.
2054    #[must_use]
2055    pub(crate) const fn new(
2056        accepted_schema_fingerprint: [u8; 16],
2057        entity_tag: u64,
2058        constraint_id: u32,
2059        primary_key: Vec<u8>,
2060        field_ids: Vec<u32>,
2061        value_path: Option<ConstraintValuePath>,
2062        error_code: u16,
2063    ) -> Self {
2064        Self {
2065            accepted_schema_fingerprint,
2066            entity_tag,
2067            constraint_id,
2068            primary_key,
2069            field_ids,
2070            value_path,
2071            error_code,
2072        }
2073    }
2074
2075    /// Return the exact accepted-schema fingerprint that binds every numeric identity.
2076    #[must_use]
2077    pub const fn accepted_schema_fingerprint(&self) -> [u8; 16] {
2078        self.accepted_schema_fingerprint
2079    }
2080
2081    /// Return the stable accepted entity identity.
2082    #[must_use]
2083    pub const fn entity_tag(&self) -> u64 {
2084        self.entity_tag
2085    }
2086
2087    /// Return the stable accepted constraint identity.
2088    #[must_use]
2089    pub const fn constraint_id(&self) -> u32 {
2090        self.constraint_id
2091    }
2092
2093    /// Borrow the bounded canonical persisted primary-key locator.
2094    #[must_use]
2095    pub const fn primary_key(&self) -> &[u8] {
2096        self.primary_key.as_slice()
2097    }
2098
2099    /// Borrow immutable accepted field identities implicated by the finding.
2100    #[must_use]
2101    pub const fn field_ids(&self) -> &[u32] {
2102        self.field_ids.as_slice()
2103    }
2104
2105    /// Borrow the typed concrete value path for a targeted-rule violation.
2106    #[must_use]
2107    pub const fn value_path(&self) -> Option<&ConstraintValuePath> {
2108        self.value_path.as_ref()
2109    }
2110
2111    /// Return the compact stable error code for this exact failure.
2112    #[must_use]
2113    pub const fn error_code(&self) -> diagnostic_code::ErrorCode {
2114        diagnostic_code::ErrorCode::from_raw(self.error_code)
2115    }
2116
2117    /// Return the broad public error class derived from the compact code.
2118    #[must_use]
2119    pub const fn error_class(&self) -> diagnostic_code::ErrorClass {
2120        self.error_code().class()
2121    }
2122}
2123
2124/// Complete bounded numeric authority needed to publish E210 or E212 facts.
2125#[derive(Clone)]
2126pub(crate) struct AcceptedConstraintFactContext {
2127    fingerprint_method: u8,
2128    accepted_schema_fingerprint: [u8; 16],
2129    entity_tag: u64,
2130    constraint_id: u32,
2131    constraint_kind: diagnostic_code::DiagnosticConstraintKind,
2132    mutation: Option<MutationDiagnosticContext>,
2133    value_path: Option<ConstraintValuePath>,
2134}
2135
2136impl AcceptedConstraintFactContext {
2137    #[must_use]
2138    pub(crate) fn write_admission(
2139        fingerprint_method: u8,
2140        accepted_schema_fingerprint: [u8; 16],
2141        entity_tag: u64,
2142        constraint_id: u32,
2143        constraint_kind: diagnostic_code::DiagnosticConstraintKind,
2144        mutation: Option<MutationDiagnosticContext>,
2145        value_path: Option<ConstraintValuePath>,
2146    ) -> Self {
2147        debug_assert!(mutation.is_none_or(|context| context.entity_tag() == entity_tag));
2148        Self {
2149            fingerprint_method,
2150            accepted_schema_fingerprint,
2151            entity_tag,
2152            constraint_id,
2153            constraint_kind,
2154            mutation,
2155            value_path,
2156        }
2157    }
2158
2159    fn facts(self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
2160        let path_len = self
2161            .value_path
2162            .as_ref()
2163            .map_or(0, |path| path.components().len());
2164        let mutation_fact_count = self.mutation.map_or(0, |mutation| {
2165            1 + usize::from(mutation.batch_position.is_some())
2166        });
2167        let mut facts = Vec::with_capacity(7 + mutation_fact_count + path_len);
2168        append_accepted_schema_facts(
2169            &mut facts,
2170            self.fingerprint_method,
2171            self.accepted_schema_fingerprint,
2172        );
2173        facts.push((
2174            diagnostic_code::DiagnosticFactTag::EntityTag,
2175            self.entity_tag,
2176        ));
2177        facts.push((
2178            diagnostic_code::DiagnosticFactTag::ConstraintId,
2179            u64::from(self.constraint_id),
2180        ));
2181        facts.push((
2182            diagnostic_code::DiagnosticFactTag::ConstraintKind,
2183            self.constraint_kind.raw(),
2184        ));
2185        facts.push((
2186            diagnostic_code::DiagnosticFactTag::ConstraintContext,
2187            diagnostic_code::DiagnosticConstraintContext::WriteAdmission.raw(),
2188        ));
2189        if let Some(mutation) = self.mutation {
2190            mutation.append_operation_facts(&mut facts);
2191        }
2192        if let Some(path) = self.value_path {
2193            for component in path.components {
2194                facts.push(constraint_value_path_fact(component));
2195            }
2196        }
2197        debug_assert!(facts.len() <= diagnostic_code::MAX_PUBLIC_DIAGNOSTIC_FACTS);
2198        facts
2199    }
2200}
2201
2202/// Mutation and constraint errors use the same lossless accepted-schema identity.
2203fn append_accepted_schema_facts(
2204    facts: &mut Vec<(diagnostic_code::DiagnosticFactTag, u64)>,
2205    method: u8,
2206    fingerprint: [u8; 16],
2207) {
2208    facts.extend([
2209        (
2210            diagnostic_code::DiagnosticFactTag::AcceptedSchemaFingerprintMethod,
2211            u64::from(method),
2212        ),
2213        (
2214            diagnostic_code::DiagnosticFactTag::AcceptedSchemaFingerprintHigh,
2215            u64::from_be_bytes([
2216                fingerprint[0],
2217                fingerprint[1],
2218                fingerprint[2],
2219                fingerprint[3],
2220                fingerprint[4],
2221                fingerprint[5],
2222                fingerprint[6],
2223                fingerprint[7],
2224            ]),
2225        ),
2226        (
2227            diagnostic_code::DiagnosticFactTag::AcceptedSchemaFingerprintLow,
2228            u64::from_be_bytes([
2229                fingerprint[8],
2230                fingerprint[9],
2231                fingerprint[10],
2232                fingerprint[11],
2233                fingerprint[12],
2234                fingerprint[13],
2235                fingerprint[14],
2236                fingerprint[15],
2237            ]),
2238        ),
2239    ]);
2240}
2241
2242fn constraint_value_path_fact(
2243    component: ConstraintValuePathComponent,
2244) -> (diagnostic_code::DiagnosticFactTag, u64) {
2245    use diagnostic_code::DiagnosticFactTag;
2246    match component {
2247        ConstraintValuePathComponent::RootField { field_id } => {
2248            (DiagnosticFactTag::RootField, u64::from(field_id))
2249        }
2250        ConstraintValuePathComponent::RecordMember {
2251            composite_type_id,
2252            member_id,
2253        } => (
2254            DiagnosticFactTag::RecordMember,
2255            diagnostic_code::pack_u32_pair(composite_type_id, member_id),
2256        ),
2257        ConstraintValuePathComponent::TupleElement {
2258            composite_type_id,
2259            ordinal,
2260        } => (
2261            DiagnosticFactTag::TupleElement,
2262            diagnostic_code::pack_u32_pair(composite_type_id, ordinal),
2263        ),
2264        ConstraintValuePathComponent::Newtype { composite_type_id } => {
2265            (DiagnosticFactTag::Newtype, u64::from(composite_type_id))
2266        }
2267        ConstraintValuePathComponent::EnumVariant {
2268            enum_type_id,
2269            variant_id,
2270        } => (
2271            DiagnosticFactTag::EnumVariant,
2272            diagnostic_code::pack_u32_pair(enum_type_id, variant_id),
2273        ),
2274        ConstraintValuePathComponent::ListElement { index } => {
2275            (DiagnosticFactTag::ListElement, u64::from(index))
2276        }
2277        ConstraintValuePathComponent::SetElement { index } => {
2278            (DiagnosticFactTag::SetElement, u64::from(index))
2279        }
2280        ConstraintValuePathComponent::MapEntryKey { index } => {
2281            (DiagnosticFactTag::MapEntryKey, u64::from(index))
2282        }
2283        ConstraintValuePathComponent::MapEntryValue { index } => {
2284            (DiagnosticFactTag::MapEntryValue, u64::from(index))
2285        }
2286    }
2287}
2288
2289///
2290/// ErrorDetail
2291///
2292/// Structured, origin-specific error detail carried by [`InternalError`].
2293/// This enum is intentionally extensible.
2294///
2295
2296pub enum ErrorDetail {
2297    /// Compact code/detail plus safe numeric context for one public failure.
2298    DiagnosticFacts(Box<DiagnosticFactDetail>),
2299    /// Executor-owned mutation and query execution details.
2300    Executor(ExecutorErrorDetail),
2301    Store(StoreError),
2302    Query(QueryErrorDetail),
2303    Recovery(RecoveryErrorDetail),
2304    // Future-proofing:
2305    // Index(IndexError),
2306}
2307
2308/// Executor-specific structured error detail.
2309pub enum ExecutorErrorDetail {
2310    /// A complete insert or replacement omitted one or more required fields.
2311    MutationRequiredFieldMissing,
2312    /// A logical mutation would move accepted managed time backward.
2313    MutationManagedTimestampRegression,
2314    /// A caller explicitly authored a field owned by accepted database policy.
2315    MutationDatabaseOwnedFieldExplicit,
2316    /// A mixed structural mutation batch contained no operations.
2317    MutationBatchEmpty,
2318    /// A mixed structural mutation batch exceeded its operation-count bound.
2319    MutationBatchTooManyItems,
2320    /// A mixed structural mutation batch exceeded its staged-byte bound.
2321    MutationBatchStagedBytesExceeded,
2322    /// A mixed structural mutation result exceeded its encoded response bound.
2323    MutationBatchResultBytesExceeded,
2324    /// A mixed structural mutation batch crossed an accepted store boundary.
2325    MutationBatchStoreMismatch,
2326    /// A mixed structural mutation batch exceeded its distinct-entity bound.
2327    MutationBatchTooManyEntities,
2328    /// More than one mixed structural operation targeted the same accepted key.
2329    MutationBatchDuplicateKey,
2330    /// Accepted row-constraint metadata or compiled state was inconsistent.
2331    AcceptedRowConstraintProgramCorrupt,
2332}
2333
2334///
2335/// RecoveryErrorDetail
2336///
2337/// Recovery-origin structured error detail payload.
2338///
2339
2340pub enum RecoveryErrorDetail {
2341    UnsupportedFormatVersion { found: Option<u16>, required: u16 },
2342
2343    MalformedFormatMarker { reason: RecoveryFormatMarkerError },
2344}
2345
2346/// Store boot-marker corruption classification.
2347#[derive(Clone, Copy, Eq, PartialEq)]
2348pub enum RecoveryFormatMarkerError {
2349    Magic,
2350    Checksum,
2351    State,
2352}
2353
2354impl RecoveryFormatMarkerError {
2355    const fn diagnostic_decode_reason(self) -> diagnostic_code::DiagnosticDecodeReason {
2356        match self {
2357            Self::Magic => diagnostic_code::DiagnosticDecodeReason::RecoveryMarkerMagic,
2358            Self::Checksum => diagnostic_code::DiagnosticDecodeReason::RecoveryMarkerChecksum,
2359            Self::State => diagnostic_code::DiagnosticDecodeReason::RecoveryMarkerState,
2360        }
2361    }
2362}
2363
2364///
2365/// StoreError
2366///
2367/// Store-specific structured error detail.
2368/// Never returned directly; always wrapped in [`ErrorDetail::Store`].
2369///
2370
2371pub enum StoreError {
2372    NotFound,
2373
2374    Corrupt,
2375
2376    InvariantViolation,
2377
2378    SchemaDdlPublicationRaceLost,
2379
2380    SchemaDdlRewriteRequiresMigration,
2381
2382    SchemaMigration {
2383        reason: diagnostic_code::SchemaMigrationCode,
2384    },
2385
2386    SchemaRowLayoutVersionExhausted,
2387
2388    JournalMutationRevisionExhausted,
2389
2390    SchemaTransitionBudgetExceeded {
2391        resource: SchemaTransitionBudgetResource,
2392    },
2393
2394    /// A generated field would collide with an accepted DDL-owned slot.
2395    SchemaGeneratedFieldAfterDdlField,
2396
2397    /// A live generated constraint activation no longer matches its proposal.
2398    SchemaGeneratedConstraintActivationStale,
2399}
2400
2401///
2402/// QueryErrorDetail
2403///
2404/// Query-origin structured error detail payload.
2405///
2406
2407pub enum QueryErrorDetail {
2408    /// The exact-count shape is supported, but its metadata is unavailable.
2409    ExactCountMetadataUnavailable,
2410
2411    NumericOverflow,
2412
2413    NumericNotRepresentable,
2414
2415    UnsupportedSqlFeature {
2416        feature: diagnostic_code::SqlFeatureCode,
2417    },
2418
2419    SqlLowering {
2420        reason: diagnostic_code::SqlLoweringCode,
2421    },
2422
2423    UnsupportedProjection {
2424        reason: diagnostic_code::QueryProjectionCode,
2425    },
2426
2427    UnknownAggregateTargetField,
2428
2429    QueryReadAdmission {
2430        reason: diagnostic_code::QueryReadAdmissionCode,
2431    },
2432
2433    SqlSurfaceMismatch {
2434        mismatch: diagnostic_code::SqlSurfaceMismatchCode,
2435    },
2436
2437    SqlWriteBoundary {
2438        boundary: diagnostic_code::SqlWriteBoundaryCode,
2439    },
2440
2441    SchemaDdlAdmission {
2442        error: SchemaDdlAdmissionError,
2443    },
2444
2445    StaleSchemaRevision,
2446}
2447
2448impl fmt::Display for QueryErrorDetail {
2449    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2450        f.write_str(COMPACT_QUERY_DIAGNOSTIC_MESSAGE)
2451    }
2452}
2453
2454impl std::error::Error for QueryErrorDetail {}
2455
2456///
2457/// SchemaTransitionBudgetResource
2458///
2459/// Query-visible identity of the exact schema-transition resource cap that
2460/// rejected a complete validation or derived-state stage.
2461///
2462
2463#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2464pub enum SchemaTransitionBudgetResource {
2465    /// Number of physical deletion keys retained for replacement.
2466    DeletionKeys,
2467    /// Number of row-derived projection entries retained for validation.
2468    ProjectionEntries,
2469    /// Deterministic projection and physical-classification work units.
2470    ProjectionWorkUnits,
2471    /// Number of authoritative source rows.
2472    SourceRows,
2473    /// Cumulative bytes of authoritative source rows.
2474    SourceRowBytes,
2475    /// Retained raw payloads plus deterministic-sort workspace bytes.
2476    StagedRawBytes,
2477}
2478
2479///
2480/// SchemaDdlAdmissionError
2481///
2482/// Stable query-visible SQL DDL admission reason. Human diagnostics may carry
2483/// extra version, fingerprint, and target facts beside this machine-readable
2484/// variant.
2485///
2486
2487#[derive(Clone, Copy, Eq, PartialEq)]
2488pub enum SchemaDdlAdmissionError {
2489    MissingExpectedSchemaVersion,
2490
2491    MissingNextSchemaVersion,
2492
2493    StaleExpectedSchemaVersion,
2494
2495    InvalidExpectedSchemaVersion,
2496
2497    InvalidNextSchemaVersion,
2498
2499    AcceptedSchemaChangeWithoutVersionBump,
2500
2501    EmptyVersionBump,
2502
2503    VersionGap,
2504
2505    VersionRollback,
2506
2507    FingerprintMethodMismatch,
2508
2509    UnsupportedTransitionClass,
2510
2511    PhysicalRunnerMissing,
2512
2513    ValidationFailed,
2514
2515    PublicationRaceLost,
2516
2517    InvalidAddColumnDefault,
2518
2519    InvalidAlterColumnDefault,
2520
2521    RowLayoutVersionExhausted,
2522
2523    GeneratedIndexDropRejected,
2524
2525    SchemaRewriteRequiresMigration,
2526
2527    SchemaTransitionBudgetExceeded {
2528        resource: SchemaTransitionBudgetResource,
2529    },
2530
2531    GeneratedFieldDefaultChangeRejected,
2532
2533    GeneratedFieldNullabilityChangeRejected,
2534}
2535
2536impl fmt::Display for SchemaDdlAdmissionError {
2537    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2538        f.write_str(COMPACT_QUERY_DIAGNOSTIC_MESSAGE)
2539    }
2540}
2541
2542impl std::error::Error for SchemaDdlAdmissionError {}
2543
2544impl fmt::Debug for ErrorDetail {
2545    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2546        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2547    }
2548}
2549
2550impl fmt::Debug for ExecutorErrorDetail {
2551    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2552        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2553    }
2554}
2555
2556impl fmt::Debug for StoreError {
2557    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2558        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2559    }
2560}
2561
2562impl fmt::Debug for QueryErrorDetail {
2563    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2564        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2565    }
2566}
2567
2568impl fmt::Debug for RecoveryErrorDetail {
2569    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2570        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2571    }
2572}
2573
2574impl fmt::Debug for RecoveryFormatMarkerError {
2575    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2576        fmt_compact_diagnostic(
2577            f,
2578            diagnostic_code::DiagnosticCode::RuntimeCorruption,
2579            Some(diagnostic_code::DiagnosticDetail::RuntimeKind {
2580                kind: diagnostic_code::RuntimeErrorKind::Corruption,
2581            }),
2582        )
2583    }
2584}
2585
2586impl fmt::Debug for SchemaDdlAdmissionError {
2587    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2588        fmt_compact_diagnostic(
2589            f,
2590            diagnostic_code::DiagnosticCode::SchemaDdlAdmission,
2591            Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2592                reason: self.diagnostic_code(),
2593            }),
2594        )
2595    }
2596}
2597
2598fn fmt_compact_diagnostic(
2599    f: &mut fmt::Formatter<'_>,
2600    code: diagnostic_code::DiagnosticCode,
2601    detail: Option<diagnostic_code::DiagnosticDetail>,
2602) -> fmt::Result {
2603    write!(
2604        f,
2605        "{}",
2606        diagnostic_code::ErrorCode::from_parts(code, detail).raw()
2607    )
2608}
2609
2610impl ErrorDetail {
2611    /// Return the compact diagnostic code for this structured detail.
2612    #[must_use]
2613    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2614        match self {
2615            Self::DiagnosticFacts(detail) => detail.diagnostic.code(),
2616            Self::Executor(error) => error.diagnostic_code(),
2617            Self::Store(error) => error.diagnostic_code(),
2618            Self::Query(error) => error.diagnostic_code(),
2619            Self::Recovery(error) => error.diagnostic_code(),
2620        }
2621    }
2622
2623    /// Return compact structured diagnostic detail when the payload carries one.
2624    #[must_use]
2625    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2626        match self {
2627            Self::DiagnosticFacts(detail) => detail.diagnostic.detail().copied(),
2628            Self::Executor(error) => error.diagnostic_detail(),
2629            Self::Store(error) => error.diagnostic_detail(),
2630            Self::Query(error) => error.diagnostic_detail(),
2631            Self::Recovery(error) => error.diagnostic_detail(),
2632        }
2633    }
2634
2635    /// Project safe typed detail into canonical public numeric facts.
2636    #[must_use]
2637    #[cold]
2638    #[inline(never)]
2639    pub fn diagnostic_facts(&self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
2640        match self {
2641            Self::DiagnosticFacts(detail) => detail.facts.clone(),
2642            Self::Executor(error) => error.diagnostic_facts(),
2643            Self::Query(error) => error.diagnostic_facts(),
2644            Self::Recovery(error) => error.diagnostic_facts(),
2645            Self::Store(_) => Vec::new(),
2646        }
2647    }
2648}
2649
2650impl ExecutorErrorDetail {
2651    /// Return the compact diagnostic code for this executor detail.
2652    #[must_use]
2653    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2654        match self {
2655            Self::MutationRequiredFieldMissing
2656            | Self::MutationDatabaseOwnedFieldExplicit
2657            | Self::MutationBatchEmpty
2658            | Self::MutationBatchTooManyItems
2659            | Self::MutationBatchTooManyEntities
2660            | Self::MutationBatchStagedBytesExceeded
2661            | Self::MutationBatchResultBytesExceeded => {
2662                diagnostic_code::DiagnosticCode::RuntimeUnsupported
2663            }
2664            Self::MutationBatchStoreMismatch | Self::MutationBatchDuplicateKey => {
2665                diagnostic_code::DiagnosticCode::RuntimeConflict
2666            }
2667            Self::MutationManagedTimestampRegression => {
2668                diagnostic_code::DiagnosticCode::RuntimeInvariantViolation
2669            }
2670            Self::AcceptedRowConstraintProgramCorrupt => {
2671                diagnostic_code::DiagnosticCode::RuntimeCorruption
2672            }
2673        }
2674    }
2675
2676    /// Return compact structured diagnostic detail for this executor detail.
2677    #[must_use]
2678    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2679        match self {
2680            Self::MutationRequiredFieldMissing => {
2681                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2682                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationRequiredFieldMissing,
2683                })
2684            }
2685            Self::MutationDatabaseOwnedFieldExplicit => {
2686                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2687                    boundary:
2688                        diagnostic_code::RuntimeBoundaryCode::MutationDatabaseOwnedFieldExplicit,
2689                })
2690            }
2691            Self::MutationBatchEmpty => Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2692                boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchEmpty,
2693            }),
2694            Self::MutationBatchTooManyItems => {
2695                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2696                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchTooManyItems,
2697                })
2698            }
2699            Self::MutationBatchStagedBytesExceeded => {
2700                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2701                    boundary:
2702                        diagnostic_code::RuntimeBoundaryCode::MutationBatchStagedBytesExceeded,
2703                })
2704            }
2705            Self::MutationBatchResultBytesExceeded => {
2706                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2707                    boundary:
2708                        diagnostic_code::RuntimeBoundaryCode::MutationBatchResultBytesExceeded,
2709                })
2710            }
2711            Self::MutationBatchStoreMismatch => {
2712                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2713                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchStoreMismatch,
2714                })
2715            }
2716            Self::MutationBatchTooManyEntities => {
2717                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2718                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchTooManyEntities,
2719                })
2720            }
2721            Self::MutationBatchDuplicateKey => {
2722                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2723                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationBatchDuplicateKey,
2724                })
2725            }
2726            Self::MutationManagedTimestampRegression => {
2727                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2728                    boundary:
2729                        diagnostic_code::RuntimeBoundaryCode::MutationManagedTimestampRegression,
2730                })
2731            }
2732            Self::AcceptedRowConstraintProgramCorrupt => {
2733                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2734                    boundary:
2735                        diagnostic_code::RuntimeBoundaryCode::AcceptedRowConstraintProgramCorrupt,
2736                })
2737            }
2738        }
2739    }
2740
2741    /// Project safe mutation detail into canonical public numeric facts.
2742    #[must_use]
2743    #[cold]
2744    #[inline(never)]
2745    pub const fn diagnostic_facts(&self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
2746        Vec::new()
2747    }
2748}
2749
2750impl RecoveryErrorDetail {
2751    /// Return the compact diagnostic code for this recovery detail.
2752    #[must_use]
2753    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2754        match self {
2755            Self::UnsupportedFormatVersion { .. } => {
2756                diagnostic_code::DiagnosticCode::RuntimeIncompatiblePersistedFormat
2757            }
2758            Self::MalformedFormatMarker { .. } => {
2759                diagnostic_code::DiagnosticCode::RuntimeCorruption
2760            }
2761        }
2762    }
2763
2764    /// Return compact structured diagnostic detail for this recovery detail.
2765    #[must_use]
2766    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2767        let kind = match self {
2768            Self::UnsupportedFormatVersion { .. } => {
2769                diagnostic_code::RuntimeErrorKind::IncompatiblePersistedFormat
2770            }
2771            Self::MalformedFormatMarker { .. } => diagnostic_code::RuntimeErrorKind::Corruption,
2772        };
2773
2774        Some(diagnostic_code::DiagnosticDetail::RuntimeKind { kind })
2775    }
2776
2777    /// Project database-format recovery context without retaining marker bytes.
2778    #[must_use]
2779    pub fn diagnostic_facts(&self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
2780        match self {
2781            Self::UnsupportedFormatVersion { found, required } => {
2782                let mut facts = Vec::with_capacity(usize::from(found.is_some()) + 1);
2783                facts.push((
2784                    diagnostic_code::DiagnosticFactTag::ExpectedVersion,
2785                    u64::from(*required),
2786                ));
2787                if let Some(found) = found {
2788                    facts.push((
2789                        diagnostic_code::DiagnosticFactTag::ActualVersion,
2790                        u64::from(*found),
2791                    ));
2792                }
2793                facts
2794            }
2795            Self::MalformedFormatMarker { reason } => vec![(
2796                diagnostic_code::DiagnosticFactTag::DecodeReason,
2797                reason.diagnostic_decode_reason().raw(),
2798            )],
2799        }
2800    }
2801}
2802
2803impl StoreError {
2804    /// Return the compact diagnostic code for this store detail.
2805    #[must_use]
2806    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2807        match self {
2808            Self::NotFound => diagnostic_code::DiagnosticCode::StoreNotFound,
2809            Self::Corrupt => diagnostic_code::DiagnosticCode::StoreCorruption,
2810            Self::InvariantViolation => diagnostic_code::DiagnosticCode::StoreInvariantViolation,
2811            Self::SchemaDdlPublicationRaceLost
2812            | Self::SchemaDdlRewriteRequiresMigration
2813            | Self::SchemaRowLayoutVersionExhausted
2814            | Self::SchemaTransitionBudgetExceeded { .. } => {
2815                diagnostic_code::DiagnosticCode::SchemaDdlAdmission
2816            }
2817            Self::JournalMutationRevisionExhausted | Self::SchemaGeneratedFieldAfterDdlField => {
2818                diagnostic_code::DiagnosticCode::RuntimeUnsupported
2819            }
2820            Self::SchemaGeneratedConstraintActivationStale => {
2821                diagnostic_code::DiagnosticCode::RuntimeConflict
2822            }
2823            Self::SchemaMigration { reason } => reason.diagnostic_code(),
2824        }
2825    }
2826
2827    /// Return compact structured diagnostic detail when the store error has one.
2828    #[must_use]
2829    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2830        match self {
2831            Self::SchemaDdlPublicationRaceLost => {
2832                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2833                    reason: diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost,
2834                })
2835            }
2836            Self::SchemaDdlRewriteRequiresMigration => {
2837                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2838                    reason: diagnostic_code::SchemaDdlAdmissionCode::SchemaRewriteRequiresMigration,
2839                })
2840            }
2841            Self::SchemaMigration { reason } => {
2842                Some(diagnostic_code::DiagnosticDetail::SchemaMigration { reason: *reason })
2843            }
2844            Self::SchemaRowLayoutVersionExhausted => {
2845                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2846                    reason: diagnostic_code::SchemaDdlAdmissionCode::RowLayoutVersionExhausted,
2847                })
2848            }
2849            Self::JournalMutationRevisionExhausted => {
2850                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2851                    boundary:
2852                        diagnostic_code::RuntimeBoundaryCode::JournalMutationRevisionExhausted,
2853                })
2854            }
2855            Self::SchemaTransitionBudgetExceeded { .. } => {
2856                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2857                    reason: diagnostic_code::SchemaDdlAdmissionCode::SchemaTransitionBudgetExceeded,
2858                })
2859            }
2860            Self::SchemaGeneratedFieldAfterDdlField => {
2861                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2862                    boundary: diagnostic_code::RuntimeBoundaryCode::GeneratedFieldAfterDdlField,
2863                })
2864            }
2865            Self::SchemaGeneratedConstraintActivationStale => {
2866                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2867                    boundary:
2868                        diagnostic_code::RuntimeBoundaryCode::GeneratedConstraintActivationStale,
2869                })
2870            }
2871            Self::NotFound | Self::Corrupt | Self::InvariantViolation => None,
2872        }
2873    }
2874}
2875
2876impl QueryErrorDetail {
2877    /// Return the compact diagnostic code for this query detail.
2878    #[must_use]
2879    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2880        match self {
2881            Self::ExactCountMetadataUnavailable => {
2882                diagnostic_code::DiagnosticCode::QueryExactCountMetadataUnavailable
2883            }
2884            Self::NumericOverflow => diagnostic_code::DiagnosticCode::QueryNumericOverflow,
2885            Self::NumericNotRepresentable => {
2886                diagnostic_code::DiagnosticCode::QueryNumericNotRepresentable
2887            }
2888            Self::UnsupportedSqlFeature { .. } => {
2889                diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature
2890            }
2891            Self::SqlLowering { .. } => diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature,
2892            Self::UnsupportedProjection { .. } => {
2893                diagnostic_code::DiagnosticCode::QueryUnsupportedProjection
2894            }
2895            Self::UnknownAggregateTargetField => {
2896                diagnostic_code::DiagnosticCode::QueryUnknownAggregateTargetField
2897            }
2898            Self::QueryReadAdmission { .. } => diagnostic_code::DiagnosticCode::QueryReadAdmission,
2899            Self::SqlSurfaceMismatch { .. } => {
2900                diagnostic_code::DiagnosticCode::QuerySqlSurfaceMismatch
2901            }
2902            Self::SqlWriteBoundary { .. } => diagnostic_code::DiagnosticCode::QuerySqlWriteBoundary,
2903            Self::SchemaDdlAdmission { .. } => diagnostic_code::DiagnosticCode::SchemaDdlAdmission,
2904            Self::StaleSchemaRevision => diagnostic_code::DiagnosticCode::RuntimeConflict,
2905        }
2906    }
2907
2908    /// Return compact structured diagnostic detail when the query detail has one.
2909    #[must_use]
2910    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2911        match self {
2912            Self::UnsupportedSqlFeature { feature } => {
2913                Some(diagnostic_code::DiagnosticDetail::UnsupportedSqlFeature { feature: *feature })
2914            }
2915            Self::SqlLowering { reason } => {
2916                Some(diagnostic_code::DiagnosticDetail::SqlLowering { reason: *reason })
2917            }
2918            Self::UnsupportedProjection { reason } => {
2919                Some(diagnostic_code::DiagnosticDetail::QueryProjection { reason: *reason })
2920            }
2921            Self::QueryReadAdmission { reason } => {
2922                Some(diagnostic_code::DiagnosticDetail::QueryReadAdmission { reason: *reason })
2923            }
2924            Self::SqlSurfaceMismatch { mismatch } => {
2925                Some(diagnostic_code::DiagnosticDetail::SqlSurfaceMismatch {
2926                    mismatch: *mismatch,
2927                })
2928            }
2929            Self::SqlWriteBoundary { boundary } => {
2930                Some(diagnostic_code::DiagnosticDetail::SqlWriteBoundary {
2931                    boundary: *boundary,
2932                })
2933            }
2934            Self::SchemaDdlAdmission { error } => {
2935                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2936                    reason: error.diagnostic_code(),
2937                })
2938            }
2939            Self::ExactCountMetadataUnavailable
2940            | Self::NumericOverflow
2941            | Self::NumericNotRepresentable
2942            | Self::UnknownAggregateTargetField
2943            | Self::StaleSchemaRevision => None,
2944        }
2945    }
2946
2947    /// Project safe query detail into canonical public numeric facts.
2948    #[must_use]
2949    #[cold]
2950    #[inline(never)]
2951    pub const fn diagnostic_facts(&self) -> Vec<(diagnostic_code::DiagnosticFactTag, u64)> {
2952        Vec::new()
2953    }
2954}
2955
2956impl SchemaDdlAdmissionError {
2957    /// Return the compact diagnostic code for this SQL DDL admission reason.
2958    #[must_use]
2959    pub const fn diagnostic_code(&self) -> diagnostic_code::SchemaDdlAdmissionCode {
2960        match self {
2961            Self::MissingExpectedSchemaVersion => {
2962                diagnostic_code::SchemaDdlAdmissionCode::MissingExpectedSchemaVersion
2963            }
2964            Self::MissingNextSchemaVersion => {
2965                diagnostic_code::SchemaDdlAdmissionCode::MissingNextSchemaVersion
2966            }
2967            Self::StaleExpectedSchemaVersion => {
2968                diagnostic_code::SchemaDdlAdmissionCode::StaleExpectedSchemaVersion
2969            }
2970            Self::InvalidExpectedSchemaVersion => {
2971                diagnostic_code::SchemaDdlAdmissionCode::InvalidExpectedSchemaVersion
2972            }
2973            Self::InvalidNextSchemaVersion => {
2974                diagnostic_code::SchemaDdlAdmissionCode::InvalidNextSchemaVersion
2975            }
2976            Self::AcceptedSchemaChangeWithoutVersionBump => {
2977                diagnostic_code::SchemaDdlAdmissionCode::AcceptedSchemaChangeWithoutVersionBump
2978            }
2979            Self::EmptyVersionBump => diagnostic_code::SchemaDdlAdmissionCode::EmptyVersionBump,
2980            Self::VersionGap => diagnostic_code::SchemaDdlAdmissionCode::VersionGap,
2981            Self::VersionRollback => diagnostic_code::SchemaDdlAdmissionCode::VersionRollback,
2982            Self::FingerprintMethodMismatch => {
2983                diagnostic_code::SchemaDdlAdmissionCode::FingerprintMethodMismatch
2984            }
2985            Self::UnsupportedTransitionClass => {
2986                diagnostic_code::SchemaDdlAdmissionCode::UnsupportedTransitionClass
2987            }
2988            Self::PhysicalRunnerMissing => {
2989                diagnostic_code::SchemaDdlAdmissionCode::PhysicalRunnerMissing
2990            }
2991            Self::ValidationFailed => diagnostic_code::SchemaDdlAdmissionCode::ValidationFailed,
2992            Self::PublicationRaceLost => {
2993                diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost
2994            }
2995            Self::InvalidAddColumnDefault => {
2996                diagnostic_code::SchemaDdlAdmissionCode::InvalidAddColumnDefault
2997            }
2998            Self::InvalidAlterColumnDefault => {
2999                diagnostic_code::SchemaDdlAdmissionCode::InvalidAlterColumnDefault
3000            }
3001            Self::GeneratedIndexDropRejected => {
3002                diagnostic_code::SchemaDdlAdmissionCode::GeneratedIndexDropRejected
3003            }
3004            Self::SchemaRewriteRequiresMigration => {
3005                diagnostic_code::SchemaDdlAdmissionCode::SchemaRewriteRequiresMigration
3006            }
3007            Self::SchemaTransitionBudgetExceeded { .. } => {
3008                diagnostic_code::SchemaDdlAdmissionCode::SchemaTransitionBudgetExceeded
3009            }
3010            Self::GeneratedFieldDefaultChangeRejected => {
3011                diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldDefaultChangeRejected
3012            }
3013            Self::GeneratedFieldNullabilityChangeRejected => {
3014                diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldNullabilityChangeRejected
3015            }
3016            Self::RowLayoutVersionExhausted => {
3017                diagnostic_code::SchemaDdlAdmissionCode::RowLayoutVersionExhausted
3018            }
3019        }
3020    }
3021}
3022
3023///
3024/// ErrorClass
3025/// Internal error taxonomy for runtime classification.
3026/// Not a stable API; may change without notice.
3027///
3028
3029#[repr(u8)]
3030#[derive(Clone, Copy, Eq, PartialEq)]
3031pub enum ErrorClass {
3032    Corruption,
3033    IncompatiblePersistedFormat,
3034    NotFound,
3035    Internal,
3036    Conflict,
3037    Unsupported,
3038    InvariantViolation,
3039}
3040
3041impl ErrorClass {
3042    /// Return a compact diagnostic code for this broad class and origin pair.
3043    #[must_use]
3044    pub const fn diagnostic_code(self, origin: ErrorOrigin) -> diagnostic_code::DiagnosticCode {
3045        match self {
3046            Self::Corruption if matches!(origin, ErrorOrigin::Store) => {
3047                diagnostic_code::DiagnosticCode::StoreCorruption
3048            }
3049            Self::Corruption => diagnostic_code::DiagnosticCode::RuntimeCorruption,
3050            Self::IncompatiblePersistedFormat => {
3051                diagnostic_code::DiagnosticCode::RuntimeIncompatiblePersistedFormat
3052            }
3053            Self::NotFound if matches!(origin, ErrorOrigin::Store) => {
3054                diagnostic_code::DiagnosticCode::StoreNotFound
3055            }
3056            Self::NotFound => diagnostic_code::DiagnosticCode::RuntimeNotFound,
3057            Self::Internal => diagnostic_code::DiagnosticCode::RuntimeInternal,
3058            Self::Conflict => diagnostic_code::DiagnosticCode::RuntimeConflict,
3059            Self::Unsupported if matches!(origin, ErrorOrigin::Cursor) => {
3060                diagnostic_code::DiagnosticCode::QueryInvalidContinuationCursor
3061            }
3062            Self::Unsupported => diagnostic_code::DiagnosticCode::RuntimeUnsupported,
3063            Self::InvariantViolation if matches!(origin, ErrorOrigin::Store) => {
3064                diagnostic_code::DiagnosticCode::StoreInvariantViolation
3065            }
3066            Self::InvariantViolation => diagnostic_code::DiagnosticCode::RuntimeInvariantViolation,
3067        }
3068    }
3069}
3070
3071impl fmt::Debug for ErrorClass {
3072    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3073        write!(f, "{}", *self as u8)
3074    }
3075}
3076
3077///
3078/// ErrorOrigin
3079/// Internal origin taxonomy for runtime classification.
3080/// Not a stable API; may change without notice.
3081///
3082
3083#[repr(u8)]
3084#[derive(Clone, Copy, Eq, PartialEq)]
3085pub enum ErrorOrigin {
3086    Serialize,
3087    Store,
3088    Index,
3089    Identity,
3090    Query,
3091    Planner,
3092    Cursor,
3093    Recovery,
3094    Response,
3095    Executor,
3096    Interface,
3097}
3098
3099impl ErrorOrigin {
3100    /// Return the compact diagnostic origin for this internal origin.
3101    #[must_use]
3102    pub const fn diagnostic_origin(self) -> diagnostic_code::ErrorOrigin {
3103        match self {
3104            Self::Serialize => diagnostic_code::ErrorOrigin::Serialize,
3105            Self::Store => diagnostic_code::ErrorOrigin::Store,
3106            Self::Index => diagnostic_code::ErrorOrigin::Index,
3107            Self::Identity => diagnostic_code::ErrorOrigin::Identity,
3108            Self::Query => diagnostic_code::ErrorOrigin::Query,
3109            Self::Planner => diagnostic_code::ErrorOrigin::Planner,
3110            Self::Cursor => diagnostic_code::ErrorOrigin::Cursor,
3111            Self::Recovery => diagnostic_code::ErrorOrigin::Recovery,
3112            Self::Response => diagnostic_code::ErrorOrigin::Response,
3113            Self::Executor => diagnostic_code::ErrorOrigin::Executor,
3114            Self::Interface => diagnostic_code::ErrorOrigin::Interface,
3115        }
3116    }
3117}
3118
3119impl fmt::Debug for ErrorOrigin {
3120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3121        write!(f, "{}", *self as u8)
3122    }
3123}