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