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