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