Skip to main content

icydb_core/error/
mod.rs

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