Skip to main content

icydb_core/error/
mod.rs

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