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