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