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