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