Skip to main content

icydb_diagnostic_code/
fact.rs

1//! Module: fact
2//!
3//! Responsibility: production-safe numeric diagnostic-fact identities.
4//! Does not own: Candid records, rich labels, prose, or subsystem projections.
5//! Boundary: freezes the numeric vocabulary shared by public errors and host tooling.
6
7use std::fmt;
8
9use crate::ErrorCode;
10
11/// Maximum number of numeric facts carried by one public error.
12pub const MAX_PUBLIC_DIAGNOSTIC_FACTS: usize = 80;
13
14macro_rules! define_fact_tag_registry {
15    ($($name:ident = $raw:literal;)+) => {
16        /// Stable semantic identity for one numeric public diagnostic fact.
17        #[derive(Clone, Copy, Eq, Hash, PartialEq)]
18        pub enum DiagnosticFactTag {
19            $(
20                #[doc = concat!("Public fact tag ", stringify!($raw), ".")]
21                $name,
22            )+
23        }
24
25        impl DiagnosticFactTag {
26            /// Return the fixed public wire value.
27            #[must_use]
28            pub const fn raw(self) -> u8 {
29                match self {
30                    $(Self::$name => $raw,)+
31                }
32            }
33
34            /// Recover a known tag from its public wire value.
35            #[must_use]
36            pub const fn known(raw: u8) -> Option<Self> {
37                match raw {
38                    $($raw => Some(Self::$name),)+
39                    _ => None,
40                }
41            }
42        }
43
44        #[cfg(test)]
45        const ORDERED_FACT_TAGS: &[DiagnosticFactTag] = &[
46            $(DiagnosticFactTag::$name,)+
47        ];
48    };
49}
50
51// This table is a public numeric registry. Append only within a released
52// major-version contract; do not reuse or reinterpret an assigned value.
53define_fact_tag_registry! {
54    AcceptedSchemaFingerprintMethod = 1;
55    AcceptedSchemaFingerprintHigh = 2;
56    AcceptedSchemaFingerprintLow = 3;
57    ExpectedFingerprintPrefix = 4;
58    ActualFingerprintPrefix = 5;
59    EntityTag = 6;
60    ExpectedEntityTag = 7;
61    ActualEntityTag = 8;
62    ConstraintId = 9;
63    FieldId = 10;
64    IndexId = 11;
65    RelationId = 12;
66    MutationOperation = 13;
67    RowOperation = 14;
68    BatchPosition = 15;
69    FirstBatchPosition = 16;
70    DuplicateBatchPosition = 17;
71    ClauseIndex = 18;
72    TermIndex = 19;
73    FirstTermIndex = 20;
74    DuplicateTermIndex = 21;
75    ProjectionIndex = 22;
76    GroupIndex = 23;
77    AggregateIndex = 24;
78    ArgumentIndex = 25;
79    BranchIndex = 26;
80    ComponentIndex = 27;
81    ParameterIndex = 28;
82    SourceSpanStart = 29;
83    SourceSpanEnd = 30;
84    Expected = 31;
85    Actual = 32;
86    Minimum = 33;
87    Maximum = 34;
88    Limit = 35;
89    ExpectedCount = 36;
90    ActualCount = 37;
91    ExpectedRevision = 38;
92    ActualRevision = 39;
93    CurrentRevision = 40;
94    RequestedRevision = 41;
95    ExpectedVersion = 42;
96    ActualVersion = 43;
97    CurrentVersion = 44;
98    RequestedVersion = 45;
99    ExpectedOffset = 46;
100    ActualOffset = 47;
101    ExpectedArity = 48;
102    ActualArity = 49;
103    ExpectedLength = 50;
104    ActualLength = 51;
105    ExpectedSlotCount = 52;
106    ActualSlotCount = 53;
107    RowLayout = 54;
108    HistoryFloor = 55;
109    CurrentLayout = 56;
110    PhysicalSlot = 57;
111    PhysicalGeneration = 58;
112    ExpectedMemoryId = 59;
113    ActualMemoryId = 60;
114    ConstraintKind = 61;
115    ConstraintContext = 62;
116    FieldKind = 63;
117    ValueKind = 64;
118    TypeFamily = 65;
119    FunctionKind = 66;
120    OperatorKind = 67;
121    AggregateKind = 68;
122    KeyNamespaceKind = 69;
123    ComponentKind = 70;
124    MismatchKind = 71;
125    DecodeReason = 72;
126    BudgetResource = 73;
127    MigrationPhase = 74;
128    DatabaseControlRecordKind = 75;
129    StateKind = 76;
130    PayloadComponent = 77;
131    ExpectedSignaturePrefix = 78;
132    ActualSignaturePrefix = 79;
133    FindingPosition = 80;
134    RootField = 81;
135    RecordMember = 82;
136    TupleElement = 83;
137    Newtype = 84;
138    EnumVariant = 85;
139    ListElement = 86;
140    SetElement = 87;
141    MapEntryKey = 88;
142    MapEntryValue = 89;
143    ExecutionBudgetScope = 90;
144    ExecutionLane = 91;
145    QueryShapeFingerprintPrefix = 92;
146    BacklogResource = 93;
147    CurrentCount = 94;
148    ProposedCount = 95;
149}
150
151/// Compact reason carried by [`DiagnosticFactTag::DecodeReason`].
152///
153/// Values are global within that fact tag. They identify only bounded decode
154/// or validation categories and never retain rejected payload bytes.
155#[derive(Clone, Copy, Eq, Hash, PartialEq)]
156pub enum DiagnosticDecodeReason {
157    CursorEmpty,
158    CursorTooLong,
159    CursorInvalidLength,
160    CursorInvalidBase64,
161    CursorGroupedDirectionMismatch,
162    CursorTokenEncode,
163    CursorTokenDecode,
164    RecoveryMarkerMagic,
165    RecoveryMarkerChecksum,
166    RecoveryMarkerState,
167}
168
169/// Compact operation carried by [`DiagnosticFactTag::MutationOperation`].
170#[derive(Clone, Copy, Eq, Hash, PartialEq)]
171pub enum DiagnosticMutationOperation {
172    Insert,
173    Replace,
174    Update,
175    Delete,
176}
177
178macro_rules! define_numeric_fact_value_registry {
179    (
180        $(#[$enum_meta:meta])*
181        pub enum $name:ident {
182            $($variant:ident = $raw:literal;)+
183        }
184    ) => {
185        $(#[$enum_meta])*
186        #[derive(Clone, Copy, Eq, Hash, PartialEq)]
187        pub enum $name {
188            $(
189                #[doc = concat!("Compact diagnostic value ", stringify!($raw), ".")]
190                $variant,
191            )+
192        }
193
194        impl $name {
195            /// Return the fixed numeric fact value.
196            #[must_use]
197            pub const fn raw(self) -> u64 {
198                match self {
199                    $(Self::$variant => $raw,)+
200                }
201            }
202
203            /// Recover a known compact value from its public numeric identity.
204            #[must_use]
205            pub const fn known(raw: u64) -> Option<Self> {
206                match raw {
207                    $($raw => Some(Self::$variant),)+
208                    _ => None,
209                }
210            }
211        }
212
213        impl fmt::Debug for $name {
214            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215                write!(f, "{}", self.raw())
216            }
217        }
218    };
219}
220
221define_numeric_fact_value_registry! {
222    /// Compact cumulative journal resource carried by [`DiagnosticFactTag::BacklogResource`].
223    pub enum DiagnosticBacklogResource {
224        Batches = 1;
225        Records = 2;
226        EncodedBytes = 3;
227    }
228}
229
230define_numeric_fact_value_registry! {
231    /// Compact hard-budget resource carried by [`DiagnosticFactTag::BudgetResource`].
232    pub enum DiagnosticExecutionBudgetResource {
233        QueryExecutions = 1;
234        PlanningSteps = 2;
235        PlanCompilations = 3;
236        KeyIndexEntriesVisited = 4;
237        RowsVisited = 5;
238        StoredBytesRead = 6;
239        PredicateExpressionSteps = 7;
240        NestedValueSteps = 8;
241        DecodedBytes = 9;
242        MaterializedBytes = 10;
243        SortEntries = 11;
244        SortComparisons = 12;
245        SortTemporaryBytes = 13;
246        GroupDistinctEntries = 14;
247        GroupDistinctStateBytes = 15;
248        CursorSteps = 16;
249        TemporaryBytes = 17;
250        ResultRows = 18;
251        ResultBytes = 19;
252        InstructionUnits = 20;
253    }
254}
255
256impl DiagnosticExecutionBudgetResource {
257    /// Every maintained hard-budget resource in stable numeric order.
258    pub const ALL: [Self; 20] = [
259        Self::QueryExecutions,
260        Self::PlanningSteps,
261        Self::PlanCompilations,
262        Self::KeyIndexEntriesVisited,
263        Self::RowsVisited,
264        Self::StoredBytesRead,
265        Self::PredicateExpressionSteps,
266        Self::NestedValueSteps,
267        Self::DecodedBytes,
268        Self::MaterializedBytes,
269        Self::SortEntries,
270        Self::SortComparisons,
271        Self::SortTemporaryBytes,
272        Self::GroupDistinctEntries,
273        Self::GroupDistinctStateBytes,
274        Self::CursorSteps,
275        Self::TemporaryBytes,
276        Self::ResultRows,
277        Self::ResultBytes,
278        Self::InstructionUnits,
279    ];
280}
281
282define_numeric_fact_value_registry! {
283    /// Compact counter owner carried by [`DiagnosticFactTag::ExecutionBudgetScope`].
284    pub enum DiagnosticExecutionBudgetScope {
285        Execution = 1;
286        Request = 2;
287    }
288}
289
290define_numeric_fact_value_registry! {
291    /// Compact execution lane carried by [`DiagnosticFactTag::ExecutionLane`].
292    pub enum DiagnosticExecutionLane {
293        PublicRead = 1;
294        TrustedRead = 2;
295        Diagnostic = 3;
296        Mutation = 4;
297        Recovery = 5;
298    }
299}
300
301define_numeric_fact_value_registry! {
302    /// Compact accepted constraint family carried by [`DiagnosticFactTag::ConstraintKind`].
303    pub enum DiagnosticConstraintKind {
304        Check = 1;
305        NotNull = 2;
306        Relation = 3;
307        TargetedRule = 4;
308        Unique = 5;
309    }
310}
311
312define_numeric_fact_value_registry! {
313    /// Compact enforcement boundary carried by [`DiagnosticFactTag::ConstraintContext`].
314    pub enum DiagnosticConstraintContext {
315        Integrity = 1;
316        MigrationValidation = 2;
317        WriteAdmission = 3;
318    }
319}
320
321define_numeric_fact_value_registry! {
322    /// Compact storage component carried by [`DiagnosticFactTag::ComponentKind`].
323    pub enum DiagnosticComponentKind {
324        CommitDataKey = 1;
325        IndexKey = 2;
326        IndexKeyComponent = 3;
327        RelationTargetPrimaryKey = 4;
328    }
329}
330
331define_numeric_fact_value_registry! {
332    /// Compact semantic family carried by [`DiagnosticFactTag::TypeFamily`].
333    pub enum DiagnosticTypeFamily {
334        Blob = 1;
335        Bool = 2;
336        Collection = 3;
337        Null = 4;
338        Numeric = 5;
339        Opaque = 6;
340        Structured = 7;
341        Text = 8;
342        Unknown = 9;
343    }
344}
345
346define_numeric_fact_value_registry! {
347    /// Compact function identity carried by [`DiagnosticFactTag::FunctionKind`].
348    pub enum DiagnosticFunctionKind {
349        Abs = 1;
350        Cbrt = 2;
351        Ceiling = 3;
352        Coalesce = 4;
353        CollectionContains = 5;
354        Contains = 6;
355        EndsWith = 7;
356        Exp = 8;
357        Floor = 9;
358        IsEmpty = 10;
359        IsMissing = 11;
360        IsNotEmpty = 12;
361        IsNotNull = 13;
362        IsNull = 14;
363        Left = 15;
364        Length = 16;
365        Ln = 17;
366        Log = 18;
367        Log2 = 19;
368        Log10 = 20;
369        Lower = 21;
370        Ltrim = 22;
371        Mod = 23;
372        NullIf = 24;
373        OctetLength = 25;
374        Position = 26;
375        Power = 27;
376        Replace = 28;
377        Right = 29;
378        Round = 30;
379        Rtrim = 31;
380        Sign = 32;
381        Sqrt = 33;
382        StartsWith = 34;
383        Substring = 35;
384        Trim = 36;
385        Trunc = 37;
386        Upper = 38;
387        InList = 39;
388    }
389}
390
391define_numeric_fact_value_registry! {
392    /// Compact operator identity carried by [`DiagnosticFactTag::OperatorKind`].
393    pub enum DiagnosticOperatorKind {
394        Not = 1;
395        Add = 2;
396        And = 3;
397        Div = 4;
398        Eq = 5;
399        Gt = 6;
400        Gte = 7;
401        Lt = 8;
402        Lte = 9;
403        Mul = 10;
404        Ne = 11;
405        Or = 12;
406        Sub = 13;
407        In = 14;
408        NotIn = 15;
409        Contains = 16;
410        StartsWith = 17;
411        EndsWith = 18;
412    }
413}
414
415define_numeric_fact_value_registry! {
416    /// Compact aggregate identity carried by [`DiagnosticFactTag::AggregateKind`].
417    pub enum DiagnosticAggregateKind {
418        Count = 1;
419        Sum = 2;
420        Avg = 3;
421        Exists = 4;
422        Min = 5;
423        Max = 6;
424        First = 7;
425        Last = 8;
426    }
427}
428
429impl DiagnosticDecodeReason {
430    /// Return the fixed numeric fact value.
431    #[must_use]
432    pub const fn raw(self) -> u64 {
433        match self {
434            Self::CursorEmpty => 1,
435            Self::CursorTooLong => 2,
436            Self::CursorInvalidLength => 3,
437            Self::CursorInvalidBase64 => 4,
438            Self::CursorGroupedDirectionMismatch => 5,
439            Self::CursorTokenEncode => 6,
440            Self::CursorTokenDecode => 7,
441            Self::RecoveryMarkerMagic => 8,
442            Self::RecoveryMarkerChecksum => 9,
443            Self::RecoveryMarkerState => 10,
444        }
445    }
446
447    /// Recover a known compact decode reason.
448    #[must_use]
449    pub const fn known(raw: u64) -> Option<Self> {
450        match raw {
451            1 => Some(Self::CursorEmpty),
452            2 => Some(Self::CursorTooLong),
453            3 => Some(Self::CursorInvalidLength),
454            4 => Some(Self::CursorInvalidBase64),
455            5 => Some(Self::CursorGroupedDirectionMismatch),
456            6 => Some(Self::CursorTokenEncode),
457            7 => Some(Self::CursorTokenDecode),
458            8 => Some(Self::RecoveryMarkerMagic),
459            9 => Some(Self::RecoveryMarkerChecksum),
460            10 => Some(Self::RecoveryMarkerState),
461            _ => None,
462        }
463    }
464}
465
466impl DiagnosticMutationOperation {
467    /// Return the fixed numeric fact value.
468    #[must_use]
469    pub const fn raw(self) -> u64 {
470        match self {
471            Self::Insert => 1,
472            Self::Replace => 2,
473            Self::Update => 3,
474            Self::Delete => 4,
475        }
476    }
477
478    /// Recover a known compact mutation operation.
479    #[must_use]
480    pub const fn known(raw: u64) -> Option<Self> {
481        match raw {
482            1 => Some(Self::Insert),
483            2 => Some(Self::Replace),
484            3 => Some(Self::Update),
485            4 => Some(Self::Delete),
486            _ => None,
487        }
488    }
489}
490
491impl fmt::Debug for DiagnosticFactTag {
492    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493        write!(f, "{}", self.raw())
494    }
495}
496
497impl fmt::Debug for DiagnosticDecodeReason {
498    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
499        write!(f, "{}", self.raw())
500    }
501}
502
503impl fmt::Debug for DiagnosticMutationOperation {
504    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505        write!(f, "{}", self.raw())
506    }
507}
508
509/// Pack two accepted `u32` identities into one fact value without narrowing.
510#[must_use]
511pub const fn pack_u32_pair(high: u32, low: u32) -> u64 {
512    (high as u64) << 32 | low as u64
513}
514
515/// Recover the two accepted identities from one packed fact value.
516#[must_use]
517pub const fn unpack_u32_pair(value: u64) -> (u32, u32) {
518    let bytes = value.to_be_bytes();
519    (
520        u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
521        u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
522    )
523}
524
525/// Why one numeric fact sequence does not satisfy its owning E-code schema.
526///
527/// This taxonomy intentionally carries no prose. Host tooling owns rendering.
528#[derive(Clone, Copy, Debug, Eq, PartialEq)]
529pub enum DiagnosticFactSchemaMismatch {
530    /// The sequence exceeds the global public fact ceiling.
531    GlobalMaximumExceeded,
532    /// The sequence exceeds the tighter ceiling for its E-code.
533    CodeMaximumExceeded,
534    /// Required, allowed, repeated, or ordered tags do not match the E-code.
535    InvalidSequence,
536    /// One known compact tag carries a value outside its frozen numeric registry.
537    InvalidValue,
538}
539
540/// Validate facts already expressed with known production tag identities.
541///
542/// The validator allocates nothing. It is the runtime-side entry point used
543/// immediately before facts cross the public facade.
544pub fn validate_known_diagnostic_fact_schema(
545    code: ErrorCode,
546    facts: &[(DiagnosticFactTag, u64)],
547) -> Result<(), DiagnosticFactSchemaMismatch> {
548    validate_diagnostic_fact_schema(code, facts.len(), |index| {
549        let (tag, value) = facts[index];
550        (tag.raw(), value)
551    })
552}
553
554/// Validate raw host/tooling facts against their owning E-code schema.
555///
556/// Unknown tags remain renderable by callers, but make a known E-code context
557/// invalid instead of being heuristically reinterpreted.
558pub fn validate_raw_diagnostic_fact_schema(
559    code: ErrorCode,
560    facts: &[(u8, u64)],
561) -> Result<(), DiagnosticFactSchemaMismatch> {
562    validate_diagnostic_fact_schema(code, facts.len(), |index| facts[index])
563}
564
565#[expect(
566    clippy::too_many_lines,
567    reason = "the frozen E-code schema registry keeps every numeric owner visible in one exhaustive dispatch"
568)]
569fn validate_diagnostic_fact_schema(
570    code: ErrorCode,
571    fact_count: usize,
572    fact_at: impl Fn(usize) -> (u8, u64),
573) -> Result<(), DiagnosticFactSchemaMismatch> {
574    if fact_count > MAX_PUBLIC_DIAGNOSTIC_FACTS {
575        return Err(DiagnosticFactSchemaMismatch::GlobalMaximumExceeded);
576    }
577
578    let maximum = diagnostic_fact_maximum(code);
579    if fact_count > maximum {
580        return Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded);
581    }
582
583    let valid_sequence = match code.raw() {
584        3 => query_plan_schema(fact_count, &fact_at),
585        5 => cursor_schema(fact_count, &fact_at),
586        15 => store_corruption_schema(fact_count, &fact_at),
587        17 => runtime_corruption_schema(fact_count, &fact_at),
588        18 => incompatible_format_schema(fact_count, &fact_at),
589        19 => runtime_invariant_schema(fact_count, &fact_at),
590        20 => runtime_conflict_schema(fact_count, &fact_at),
591        22 => runtime_unsupported_schema(fact_count, &fact_at),
592        23 => runtime_internal_schema(fact_count, &fact_at),
593        130 => tags_match(
594            fact_count,
595            &fact_at,
596            &[
597                DiagnosticFactTag::ExpectedArity,
598                DiagnosticFactTag::ActualArity,
599            ],
600        ),
601        133 | 134 | 158 | 159 => {
602            tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ProjectionIndex])
603        }
604        164 => tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ParameterIndex]),
605        166 | 224 => {
606            tags_match(fact_count, &fact_at, &[DiagnosticFactTag::Limit])
607                || tags_match(
608                    fact_count,
609                    &fact_at,
610                    &[DiagnosticFactTag::ActualLength, DiagnosticFactTag::Limit],
611                )
612        }
613        167 | 169 | 191 | 192 | 194 | 223 | 248 | 264 => tags_match(
614            fact_count,
615            &fact_at,
616            &[DiagnosticFactTag::ActualCount, DiagnosticFactTag::Limit],
617        ),
618        262 => {
619            tags_match(fact_count, &fact_at, &[DiagnosticFactTag::Limit])
620                || tags_match(
621                    fact_count,
622                    &fact_at,
623                    &[DiagnosticFactTag::ActualCount, DiagnosticFactTag::Limit],
624                )
625        }
626        263 => tags_match(
627            fact_count,
628            &fact_at,
629            &[
630                DiagnosticFactTag::BacklogResource,
631                DiagnosticFactTag::CurrentCount,
632                DiagnosticFactTag::ProposedCount,
633                DiagnosticFactTag::Limit,
634            ],
635        ),
636        185 | 221 => mutation_schema(
637            fact_count,
638            &fact_at,
639            &[
640                DiagnosticFactTag::AcceptedSchemaFingerprintMethod,
641                DiagnosticFactTag::AcceptedSchemaFingerprintHigh,
642                DiagnosticFactTag::AcceptedSchemaFingerprintLow,
643                DiagnosticFactTag::EntityTag,
644                DiagnosticFactTag::FieldId,
645                DiagnosticFactTag::MutationOperation,
646            ],
647        ),
648        186 => tags_match(
649            fact_count,
650            &fact_at,
651            &[
652                DiagnosticFactTag::RowLayout,
653                DiagnosticFactTag::HistoryFloor,
654                DiagnosticFactTag::CurrentLayout,
655            ],
656        ),
657        187 => tags_match(
658            fact_count,
659            &fact_at,
660            &[
661                DiagnosticFactTag::RowLayout,
662                DiagnosticFactTag::ExpectedSlotCount,
663                DiagnosticFactTag::ActualSlotCount,
664            ],
665        ),
666        190 => tags_match(
667            fact_count,
668            &fact_at,
669            &[DiagnosticFactTag::ActualCount, DiagnosticFactTag::Minimum],
670        ),
671        210 => constraint_schema(fact_count, &fact_at, true),
672        212 => constraint_schema(fact_count, &fact_at, false),
673        220 => mutation_schema(
674            fact_count,
675            &fact_at,
676            &[
677                DiagnosticFactTag::AcceptedSchemaFingerprintMethod,
678                DiagnosticFactTag::AcceptedSchemaFingerprintHigh,
679                DiagnosticFactTag::AcceptedSchemaFingerprintLow,
680                DiagnosticFactTag::EntityTag,
681                DiagnosticFactTag::MutationOperation,
682            ],
683        ),
684        222 => {
685            tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ActualCount]) && fact_at(0).1 == 0
686        }
687        225 | 249 | 250 | 251 => tags_match(
688            fact_count,
689            &fact_at,
690            &[DiagnosticFactTag::ActualLength, DiagnosticFactTag::Limit],
691        ),
692        252 => tags_match(
693            fact_count,
694            &fact_at,
695            &[
696                DiagnosticFactTag::BudgetResource,
697                DiagnosticFactTag::Limit,
698                DiagnosticFactTag::Actual,
699                DiagnosticFactTag::ExecutionBudgetScope,
700                DiagnosticFactTag::ExecutionLane,
701                DiagnosticFactTag::QueryShapeFingerprintPrefix,
702            ],
703        ),
704        253 => tags_match(
705            fact_count,
706            &fact_at,
707            &[
708                DiagnosticFactTag::BudgetResource,
709                DiagnosticFactTag::Limit,
710                DiagnosticFactTag::Actual,
711            ],
712        ),
713        271 | 272 => tags_match(
714            fact_count,
715            &fact_at,
716            &[DiagnosticFactTag::Limit, DiagnosticFactTag::Actual],
717        ),
718        274 => {
719            tags_match(
720                fact_count,
721                &fact_at,
722                &[DiagnosticFactTag::Expected, DiagnosticFactTag::Actual],
723            ) && (0..2).all(|index| (1..=u64::from(u16::MAX)).contains(&fact_at(index).1))
724                && fact_at(0).1 != fact_at(1).1
725        }
726        226 => tags_match(
727            fact_count,
728            &fact_at,
729            &[
730                DiagnosticFactTag::BatchPosition,
731                DiagnosticFactTag::ExpectedEntityTag,
732                DiagnosticFactTag::ActualEntityTag,
733            ],
734        ),
735        227 => tags_match(
736            fact_count,
737            &fact_at,
738            &[
739                DiagnosticFactTag::EntityTag,
740                DiagnosticFactTag::FirstBatchPosition,
741                DiagnosticFactTag::DuplicateBatchPosition,
742            ],
743        ),
744        _ => fact_count == 0,
745    };
746    if !valid_sequence {
747        return Err(DiagnosticFactSchemaMismatch::InvalidSequence);
748    }
749
750    for index in 0..fact_count {
751        let (raw_tag, value) = fact_at(index);
752        let Some(tag) = DiagnosticFactTag::known(raw_tag) else {
753            return Err(DiagnosticFactSchemaMismatch::InvalidSequence);
754        };
755        if !diagnostic_fact_value_is_valid(tag, value) {
756            return Err(DiagnosticFactSchemaMismatch::InvalidValue);
757        }
758    }
759    Ok(())
760}
761
762const fn diagnostic_fact_maximum(code: ErrorCode) -> usize {
763    match code.raw() {
764        5 | 15 | 17 | 186 | 187 | 226 | 227 | 253 => 3,
765        133 | 134 | 158 | 159 | 164 | 222 => 1,
766        18 | 20 | 130 | 166 | 167 | 169 | 190 | 191 | 192 | 194 | 223 | 224 | 225 | 248 | 249
767        | 250 | 251 | 262 | 264 | 271 | 272 | 274 => 2,
768        3 | 19 | 23 => 5,
769        22 | 220 | 252 => 6,
770        185 | 221 => 7,
771        263 => 4,
772        210 => 73,
773        212 => 9,
774        _ => 0,
775    }
776}
777
778#[expect(
779    clippy::too_many_lines,
780    reason = "the query-plan E-code deliberately owns several exact finite fact sequences"
781)]
782fn query_plan_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
783    fact_count == 0
784        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::TermIndex])
785        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ComponentIndex])
786        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::GroupIndex])
787        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ClauseIndex])
788        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::AggregateIndex])
789        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::AggregateKind])
790        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ProjectionIndex])
791        || tags_match(
792            fact_count,
793            fact_at,
794            &[
795                DiagnosticFactTag::FirstTermIndex,
796                DiagnosticFactTag::DuplicateTermIndex,
797            ],
798        )
799        || tags_match(
800            fact_count,
801            fact_at,
802            &[
803                DiagnosticFactTag::ClauseIndex,
804                DiagnosticFactTag::OperatorKind,
805            ],
806        )
807        || tags_match(
808            fact_count,
809            fact_at,
810            &[
811                DiagnosticFactTag::AggregateIndex,
812                DiagnosticFactTag::AggregateKind,
813            ],
814        )
815        || tags_match(
816            fact_count,
817            fact_at,
818            &[
819                DiagnosticFactTag::AggregateKind,
820                DiagnosticFactTag::TypeFamily,
821            ],
822        )
823        || tags_match(
824            fact_count,
825            fact_at,
826            &[
827                DiagnosticFactTag::OperatorKind,
828                DiagnosticFactTag::TypeFamily,
829            ],
830        )
831        || tags_match(
832            fact_count,
833            fact_at,
834            &[
835                DiagnosticFactTag::BranchIndex,
836                DiagnosticFactTag::TypeFamily,
837            ],
838        )
839        || tags_match(
840            fact_count,
841            fact_at,
842            &[DiagnosticFactTag::TypeFamily, DiagnosticFactTag::TypeFamily],
843        )
844        || tags_match(
845            fact_count,
846            fact_at,
847            &[
848                DiagnosticFactTag::ClauseIndex,
849                DiagnosticFactTag::AggregateIndex,
850                DiagnosticFactTag::ActualCount,
851            ],
852        )
853        || tags_match(
854            fact_count,
855            fact_at,
856            &[
857                DiagnosticFactTag::FunctionKind,
858                DiagnosticFactTag::ExpectedArity,
859                DiagnosticFactTag::ActualArity,
860            ],
861        )
862        || tags_match(
863            fact_count,
864            fact_at,
865            &[
866                DiagnosticFactTag::FunctionKind,
867                DiagnosticFactTag::ArgumentIndex,
868                DiagnosticFactTag::TypeFamily,
869            ],
870        )
871        || tags_match(
872            fact_count,
873            fact_at,
874            &[
875                DiagnosticFactTag::OperatorKind,
876                DiagnosticFactTag::TypeFamily,
877                DiagnosticFactTag::TypeFamily,
878            ],
879        )
880        || tags_match(
881            fact_count,
882            fact_at,
883            &[
884                DiagnosticFactTag::BranchIndex,
885                DiagnosticFactTag::TypeFamily,
886                DiagnosticFactTag::TypeFamily,
887            ],
888        )
889        || tags_match(
890            fact_count,
891            fact_at,
892            &[
893                DiagnosticFactTag::TypeFamily,
894                DiagnosticFactTag::BranchIndex,
895                DiagnosticFactTag::TypeFamily,
896            ],
897        )
898        || tags_match(
899            fact_count,
900            fact_at,
901            &[
902                DiagnosticFactTag::BranchIndex,
903                DiagnosticFactTag::TypeFamily,
904                DiagnosticFactTag::BranchIndex,
905                DiagnosticFactTag::TypeFamily,
906            ],
907        )
908        || tags_match(
909            fact_count,
910            fact_at,
911            &[
912                DiagnosticFactTag::FunctionKind,
913                DiagnosticFactTag::ArgumentIndex,
914                DiagnosticFactTag::TypeFamily,
915                DiagnosticFactTag::ArgumentIndex,
916                DiagnosticFactTag::TypeFamily,
917            ],
918        )
919}
920
921fn cursor_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
922    if fact_count == 0 {
923        return true;
924    }
925    if tags_match(fact_count, fact_at, &[DiagnosticFactTag::DecodeReason]) {
926        return matches!(fact_at(0).1, 1 | 3 | 5 | 6 | 7);
927    }
928    if tags_match(
929        fact_count,
930        fact_at,
931        &[
932            DiagnosticFactTag::ActualLength,
933            DiagnosticFactTag::Maximum,
934            DiagnosticFactTag::DecodeReason,
935        ],
936    ) {
937        return fact_at(2).1 == DiagnosticDecodeReason::CursorTooLong.raw();
938    }
939    if tags_match(
940        fact_count,
941        fact_at,
942        &[
943            DiagnosticFactTag::ComponentIndex,
944            DiagnosticFactTag::DecodeReason,
945        ],
946    ) {
947        return matches!(fact_at(1).1, 4 | 6 | 7);
948    }
949    tags_match(
950        fact_count,
951        fact_at,
952        &[
953            DiagnosticFactTag::ExpectedSignaturePrefix,
954            DiagnosticFactTag::ActualSignaturePrefix,
955        ],
956    ) || tags_match(
957        fact_count,
958        fact_at,
959        &[
960            DiagnosticFactTag::ExpectedOffset,
961            DiagnosticFactTag::ActualOffset,
962        ],
963    )
964}
965
966fn store_corruption_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
967    fact_count == 0
968        || (tags_match(
969            fact_count,
970            fact_at,
971            &[
972                DiagnosticFactTag::ComponentKind,
973                DiagnosticFactTag::ActualLength,
974                DiagnosticFactTag::Limit,
975            ],
976        ) && fact_at(0).1 == DiagnosticComponentKind::CommitDataKey.raw())
977        || tags_match(
978            fact_count,
979            fact_at,
980            &[
981                DiagnosticFactTag::ExpectedEntityTag,
982                DiagnosticFactTag::ActualEntityTag,
983            ],
984        )
985}
986
987fn runtime_corruption_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
988    store_corruption_schema(fact_count, fact_at)
989        || (tags_match(fact_count, fact_at, &[DiagnosticFactTag::DecodeReason])
990            && matches!(fact_at(0).1, 8..=10))
991}
992
993fn incompatible_format_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
994    fact_count == 0
995        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ExpectedVersion])
996        || tags_match(
997            fact_count,
998            fact_at,
999            &[
1000                DiagnosticFactTag::ExpectedVersion,
1001                DiagnosticFactTag::ActualVersion,
1002            ],
1003        )
1004}
1005
1006fn runtime_invariant_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1007    fact_count == 0
1008        || (tags_match(
1009            fact_count,
1010            fact_at,
1011            &[
1012                DiagnosticFactTag::EntityTag,
1013                DiagnosticFactTag::PhysicalGeneration,
1014                DiagnosticFactTag::ComponentKind,
1015                DiagnosticFactTag::ActualArity,
1016                DiagnosticFactTag::Maximum,
1017            ],
1018        ) && fact_at(2).1 == DiagnosticComponentKind::IndexKey.raw())
1019}
1020
1021fn runtime_conflict_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1022    fact_count == 0
1023        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ExpectedRevision])
1024        || tags_match(
1025            fact_count,
1026            fact_at,
1027            &[
1028                DiagnosticFactTag::ExpectedRevision,
1029                DiagnosticFactTag::CurrentRevision,
1030            ],
1031        )
1032}
1033
1034fn runtime_unsupported_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1035    fact_count == 0
1036        || (tags_match(
1037            fact_count,
1038            fact_at,
1039            &[
1040                DiagnosticFactTag::EntityTag,
1041                DiagnosticFactTag::PhysicalGeneration,
1042                DiagnosticFactTag::ComponentIndex,
1043                DiagnosticFactTag::ComponentKind,
1044                DiagnosticFactTag::ActualLength,
1045                DiagnosticFactTag::Limit,
1046            ],
1047        ) && fact_at(3).1 == DiagnosticComponentKind::IndexKeyComponent.raw())
1048}
1049
1050fn runtime_internal_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1051    // Accepted relation compilation adds source identity ahead of the existing
1052    // bounded cause facts; identity must not hide or relax the cause schema.
1053    if fact_count >= 2
1054        && fact_at(0).0 == DiagnosticFactTag::EntityTag.raw()
1055        && fact_at(1).0 == DiagnosticFactTag::RelationId.raw()
1056    {
1057        return runtime_internal_detail_schema(fact_count - 2, &|index| fact_at(index + 2));
1058    }
1059    runtime_internal_detail_schema(fact_count, fact_at)
1060}
1061
1062fn runtime_internal_detail_schema(
1063    fact_count: usize,
1064    fact_at: &impl Fn(usize) -> (u8, u64),
1065) -> bool {
1066    fact_count == 0
1067        || tags_match(
1068            fact_count,
1069            fact_at,
1070            &[
1071                DiagnosticFactTag::ExpectedMemoryId,
1072                DiagnosticFactTag::ActualMemoryId,
1073            ],
1074        )
1075        || (tags_match(
1076            fact_count,
1077            fact_at,
1078            &[
1079                DiagnosticFactTag::ComponentKind,
1080                DiagnosticFactTag::ExpectedArity,
1081                DiagnosticFactTag::ActualArity,
1082            ],
1083        ) && fact_at(0).1 == DiagnosticComponentKind::RelationTargetPrimaryKey.raw())
1084}
1085
1086fn constraint_schema(
1087    fact_count: usize,
1088    fact_at: &impl Fn(usize) -> (u8, u64),
1089    allow_targeted_path: bool,
1090) -> bool {
1091    const COMMON: &[DiagnosticFactTag] = &[
1092        DiagnosticFactTag::AcceptedSchemaFingerprintMethod,
1093        DiagnosticFactTag::AcceptedSchemaFingerprintHigh,
1094        DiagnosticFactTag::AcceptedSchemaFingerprintLow,
1095        DiagnosticFactTag::EntityTag,
1096        DiagnosticFactTag::ConstraintId,
1097        DiagnosticFactTag::ConstraintKind,
1098        DiagnosticFactTag::ConstraintContext,
1099    ];
1100    if fact_count < COMMON.len()
1101        || !tags_prefix_matches(fact_count, fact_at, COMMON)
1102        || fact_at(6).1 != DiagnosticConstraintContext::WriteAdmission.raw()
1103    {
1104        return false;
1105    }
1106
1107    let constraint_kind = fact_at(5).1;
1108    if DiagnosticConstraintKind::known(constraint_kind).is_none() {
1109        return false;
1110    }
1111    let mut index = COMMON.len();
1112    if index < fact_count && fact_at(index).0 == DiagnosticFactTag::MutationOperation.raw() {
1113        index += 1;
1114        if index < fact_count && fact_at(index).0 == DiagnosticFactTag::BatchPosition.raw() {
1115            index += 1;
1116        }
1117    }
1118
1119    let path_len = fact_count - index;
1120    if path_len == 0 {
1121        return !allow_targeted_path
1122            || constraint_kind != DiagnosticConstraintKind::TargetedRule.raw();
1123    }
1124    allow_targeted_path
1125        && constraint_kind == DiagnosticConstraintKind::TargetedRule.raw()
1126        && path_len <= 64
1127        && (index..fact_count).all(|position| {
1128            DiagnosticFactTag::known(fact_at(position).0).is_some_and(is_value_path_tag)
1129        })
1130}
1131
1132// Input position exists only once a concrete row has been selected. SQL fixed
1133// assignment admission reports the same schema identity before selecting rows.
1134fn mutation_schema(
1135    fact_count: usize,
1136    fact_at: &impl Fn(usize) -> (u8, u64),
1137    required: &[DiagnosticFactTag],
1138) -> bool {
1139    tags_prefix_matches(fact_count, fact_at, required)
1140        && (fact_count == required.len()
1141            || (fact_count == required.len() + 1
1142                && fact_at(required.len()).0 == DiagnosticFactTag::BatchPosition.raw()))
1143}
1144
1145fn tags_match(
1146    fact_count: usize,
1147    fact_at: &impl Fn(usize) -> (u8, u64),
1148    expected: &[DiagnosticFactTag],
1149) -> bool {
1150    fact_count == expected.len() && tags_prefix_matches(fact_count, fact_at, expected)
1151}
1152
1153fn tags_prefix_matches(
1154    fact_count: usize,
1155    fact_at: &impl Fn(usize) -> (u8, u64),
1156    expected: &[DiagnosticFactTag],
1157) -> bool {
1158    fact_count >= expected.len()
1159        && expected
1160            .iter()
1161            .enumerate()
1162            .all(|(index, tag)| fact_at(index).0 == tag.raw())
1163}
1164
1165const fn is_value_path_tag(tag: DiagnosticFactTag) -> bool {
1166    matches!(
1167        tag,
1168        DiagnosticFactTag::RootField
1169            | DiagnosticFactTag::RecordMember
1170            | DiagnosticFactTag::TupleElement
1171            | DiagnosticFactTag::Newtype
1172            | DiagnosticFactTag::EnumVariant
1173            | DiagnosticFactTag::ListElement
1174            | DiagnosticFactTag::SetElement
1175            | DiagnosticFactTag::MapEntryKey
1176            | DiagnosticFactTag::MapEntryValue
1177    )
1178}
1179
1180const fn diagnostic_fact_value_is_valid(tag: DiagnosticFactTag, value: u64) -> bool {
1181    match tag {
1182        DiagnosticFactTag::AcceptedSchemaFingerprintMethod
1183        | DiagnosticFactTag::ExpectedMemoryId
1184        | DiagnosticFactTag::ActualMemoryId => value <= u8::MAX as u64,
1185        DiagnosticFactTag::ConstraintId
1186        | DiagnosticFactTag::FieldId
1187        | DiagnosticFactTag::IndexId
1188        | DiagnosticFactTag::RelationId
1189        | DiagnosticFactTag::BatchPosition
1190        | DiagnosticFactTag::FirstBatchPosition
1191        | DiagnosticFactTag::DuplicateBatchPosition
1192        | DiagnosticFactTag::RowLayout
1193        | DiagnosticFactTag::HistoryFloor
1194        | DiagnosticFactTag::CurrentLayout
1195        | DiagnosticFactTag::RootField
1196        | DiagnosticFactTag::Newtype
1197        | DiagnosticFactTag::ListElement
1198        | DiagnosticFactTag::SetElement
1199        | DiagnosticFactTag::MapEntryKey
1200        | DiagnosticFactTag::MapEntryValue => value <= u32::MAX as u64,
1201        DiagnosticFactTag::ConstraintKind => DiagnosticConstraintKind::known(value).is_some(),
1202        DiagnosticFactTag::BacklogResource => DiagnosticBacklogResource::known(value).is_some(),
1203        DiagnosticFactTag::ConstraintContext => DiagnosticConstraintContext::known(value).is_some(),
1204        DiagnosticFactTag::TypeFamily => DiagnosticTypeFamily::known(value).is_some(),
1205        DiagnosticFactTag::FunctionKind => DiagnosticFunctionKind::known(value).is_some(),
1206        DiagnosticFactTag::OperatorKind => DiagnosticOperatorKind::known(value).is_some(),
1207        DiagnosticFactTag::AggregateKind => DiagnosticAggregateKind::known(value).is_some(),
1208        DiagnosticFactTag::ComponentKind => DiagnosticComponentKind::known(value).is_some(),
1209        DiagnosticFactTag::DecodeReason => DiagnosticDecodeReason::known(value).is_some(),
1210        DiagnosticFactTag::BudgetResource => {
1211            DiagnosticExecutionBudgetResource::known(value).is_some()
1212        }
1213        DiagnosticFactTag::ExecutionBudgetScope => {
1214            DiagnosticExecutionBudgetScope::known(value).is_some()
1215        }
1216        DiagnosticFactTag::ExecutionLane => DiagnosticExecutionLane::known(value).is_some(),
1217        DiagnosticFactTag::MutationOperation => DiagnosticMutationOperation::known(value).is_some(),
1218        _ => true,
1219    }
1220}
1221
1222#[cfg(test)]
1223mod tests {
1224    use super::{
1225        DiagnosticAggregateKind, DiagnosticBacklogResource, DiagnosticComponentKind,
1226        DiagnosticConstraintContext, DiagnosticConstraintKind, DiagnosticDecodeReason,
1227        DiagnosticExecutionBudgetResource, DiagnosticExecutionBudgetScope, DiagnosticExecutionLane,
1228        DiagnosticFactSchemaMismatch, DiagnosticFactTag, DiagnosticFunctionKind,
1229        DiagnosticMutationOperation, DiagnosticOperatorKind, DiagnosticTypeFamily,
1230        ORDERED_FACT_TAGS, pack_u32_pair, unpack_u32_pair, validate_known_diagnostic_fact_schema,
1231        validate_raw_diagnostic_fact_schema,
1232    };
1233    use crate::ErrorCode;
1234
1235    #[test]
1236    fn bucket_mismatch_requires_two_distinct_nonzero_page_counts() {
1237        let code = ErrorCode::RUNTIME_BOUNDARY_MEMORY_BUCKET_SIZE_MISMATCH;
1238        let expected = DiagnosticFactTag::Expected;
1239        let actual = DiagnosticFactTag::Actual;
1240        assert_eq!(
1241            validate_known_diagnostic_fact_schema(code, &[(expected, 16), (actual, 128)]),
1242            Ok(()),
1243        );
1244        for facts in [
1245            vec![],
1246            vec![(expected, 16)],
1247            vec![(actual, 128), (expected, 16)],
1248            vec![(expected, 16), (expected, 128)],
1249            vec![(expected, 16), (actual, 16)],
1250            vec![(expected, 0), (actual, 128)],
1251            vec![(expected, 16), (actual, 0)],
1252            vec![(expected, 65_536), (actual, 128)],
1253            vec![(expected, 16), (actual, 65_536)],
1254        ] {
1255            assert_eq!(
1256                validate_known_diagnostic_fact_schema(code, &facts),
1257                Err(DiagnosticFactSchemaMismatch::InvalidSequence),
1258            );
1259        }
1260        assert_eq!(
1261            validate_known_diagnostic_fact_schema(
1262                code,
1263                &[(expected, 16), (actual, 128), (actual, 128)],
1264            ),
1265            Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded),
1266        );
1267    }
1268
1269    #[test]
1270    fn fact_tag_registry_is_fixed_unique_and_contiguous() {
1271        for (index, tag) in ORDERED_FACT_TAGS.iter().copied().enumerate() {
1272            let expected = u8::try_from(index + 1).expect("fact-tag index fits u8");
1273            assert_eq!(tag.raw(), expected);
1274            assert_eq!(DiagnosticFactTag::known(expected), Some(tag));
1275        }
1276
1277        assert_eq!(DiagnosticFactTag::known(0), None);
1278        assert_eq!(DiagnosticFactTag::known(96), None);
1279        assert_eq!(DiagnosticFactTag::known(u8::MAX), None);
1280    }
1281
1282    #[test]
1283    fn execution_budget_fact_value_registries_are_fixed() {
1284        assert_eq!(DiagnosticBacklogResource::Batches.raw(), 1);
1285        assert_eq!(DiagnosticBacklogResource::Records.raw(), 2);
1286        assert_eq!(DiagnosticBacklogResource::EncodedBytes.raw(), 3);
1287        assert_eq!(DiagnosticBacklogResource::known(4), None);
1288
1289        for (index, resource) in DiagnosticExecutionBudgetResource::ALL
1290            .iter()
1291            .copied()
1292            .enumerate()
1293        {
1294            let expected = u64::try_from(index + 1).expect("resource index fits u64");
1295            assert_eq!(resource.raw(), expected);
1296            assert_eq!(
1297                DiagnosticExecutionBudgetResource::known(expected),
1298                Some(resource)
1299            );
1300        }
1301        assert_eq!(DiagnosticExecutionBudgetResource::known(0), None);
1302        assert_eq!(DiagnosticExecutionBudgetResource::known(21), None);
1303
1304        assert_eq!(DiagnosticExecutionBudgetScope::Execution.raw(), 1);
1305        assert_eq!(DiagnosticExecutionBudgetScope::Request.raw(), 2);
1306        assert_eq!(DiagnosticExecutionBudgetScope::known(3), None);
1307
1308        assert_eq!(DiagnosticExecutionLane::PublicRead.raw(), 1);
1309        assert_eq!(DiagnosticExecutionLane::TrustedRead.raw(), 2);
1310        assert_eq!(DiagnosticExecutionLane::Diagnostic.raw(), 3);
1311        assert_eq!(DiagnosticExecutionLane::Mutation.raw(), 4);
1312        assert_eq!(DiagnosticExecutionLane::Recovery.raw(), 5);
1313        assert_eq!(DiagnosticExecutionLane::known(6), None);
1314    }
1315
1316    #[test]
1317    fn accepted_identity_pair_packing_is_exact() {
1318        for pair in [
1319            (0, 0),
1320            (1, 2),
1321            (u32::MAX, 0),
1322            (0, u32::MAX),
1323            (u32::MAX, u32::MAX),
1324        ] {
1325            assert_eq!(unpack_u32_pair(pack_u32_pair(pair.0, pair.1)), pair);
1326        }
1327    }
1328
1329    #[test]
1330    fn constraint_fact_value_registries_are_fixed() {
1331        assert_eq!(DiagnosticConstraintKind::Check.raw(), 1);
1332        assert_eq!(DiagnosticConstraintKind::NotNull.raw(), 2);
1333        assert_eq!(DiagnosticConstraintKind::Relation.raw(), 3);
1334        assert_eq!(DiagnosticConstraintKind::TargetedRule.raw(), 4);
1335        assert_eq!(DiagnosticConstraintKind::Unique.raw(), 5);
1336        assert_eq!(DiagnosticConstraintKind::known(0), None);
1337        assert_eq!(DiagnosticConstraintKind::known(6), None);
1338
1339        assert_eq!(DiagnosticConstraintContext::Integrity.raw(), 1);
1340        assert_eq!(DiagnosticConstraintContext::MigrationValidation.raw(), 2);
1341        assert_eq!(DiagnosticConstraintContext::WriteAdmission.raw(), 3);
1342        assert_eq!(DiagnosticConstraintContext::known(0), None);
1343        assert_eq!(DiagnosticConstraintContext::known(4), None);
1344    }
1345
1346    #[test]
1347    fn component_kind_registry_is_fixed_and_numeric() {
1348        let kinds = [
1349            DiagnosticComponentKind::CommitDataKey,
1350            DiagnosticComponentKind::IndexKey,
1351            DiagnosticComponentKind::IndexKeyComponent,
1352            DiagnosticComponentKind::RelationTargetPrimaryKey,
1353        ];
1354
1355        for (index, kind) in kinds.iter().copied().enumerate() {
1356            let expected = (index + 1) as u64;
1357            assert_eq!(kind.raw(), expected);
1358            assert_eq!(DiagnosticComponentKind::known(expected), Some(kind));
1359            assert_eq!(format!("{kind:?}"), expected.to_string());
1360        }
1361        assert_eq!(DiagnosticComponentKind::known(0), None);
1362        assert_eq!(DiagnosticComponentKind::known(5), None);
1363    }
1364
1365    #[test]
1366    fn decode_reason_registry_is_fixed_and_numeric() {
1367        let reasons = [
1368            DiagnosticDecodeReason::CursorEmpty,
1369            DiagnosticDecodeReason::CursorTooLong,
1370            DiagnosticDecodeReason::CursorInvalidLength,
1371            DiagnosticDecodeReason::CursorInvalidBase64,
1372            DiagnosticDecodeReason::CursorGroupedDirectionMismatch,
1373            DiagnosticDecodeReason::CursorTokenEncode,
1374            DiagnosticDecodeReason::CursorTokenDecode,
1375            DiagnosticDecodeReason::RecoveryMarkerMagic,
1376            DiagnosticDecodeReason::RecoveryMarkerChecksum,
1377            DiagnosticDecodeReason::RecoveryMarkerState,
1378        ];
1379
1380        for (index, reason) in reasons.iter().copied().enumerate() {
1381            let expected = (index + 1) as u64;
1382            assert_eq!(reason.raw(), expected);
1383            assert_eq!(DiagnosticDecodeReason::known(expected), Some(reason));
1384            assert_eq!(format!("{reason:?}"), expected.to_string());
1385        }
1386
1387        assert_eq!(DiagnosticDecodeReason::known(0), None);
1388        assert_eq!(DiagnosticDecodeReason::known(11), None);
1389    }
1390
1391    #[test]
1392    fn mutation_operation_registry_is_fixed_and_numeric() {
1393        let operations = [
1394            DiagnosticMutationOperation::Insert,
1395            DiagnosticMutationOperation::Replace,
1396            DiagnosticMutationOperation::Update,
1397            DiagnosticMutationOperation::Delete,
1398        ];
1399
1400        for (index, operation) in operations.iter().copied().enumerate() {
1401            let expected = (index + 1) as u64;
1402            assert_eq!(operation.raw(), expected);
1403            assert_eq!(
1404                DiagnosticMutationOperation::known(expected),
1405                Some(operation)
1406            );
1407            assert_eq!(format!("{operation:?}"), expected.to_string());
1408        }
1409
1410        assert_eq!(DiagnosticMutationOperation::known(0), None);
1411        assert_eq!(DiagnosticMutationOperation::known(5), None);
1412    }
1413
1414    #[test]
1415    fn query_kind_registries_are_fixed_contiguous_and_numeric() {
1416        for raw in 1..=9 {
1417            let value = DiagnosticTypeFamily::known(raw).expect("type family should be known");
1418            assert_eq!(value.raw(), raw);
1419            assert_eq!(format!("{value:?}"), raw.to_string());
1420        }
1421        assert_eq!(DiagnosticTypeFamily::known(0), None);
1422        assert_eq!(DiagnosticTypeFamily::known(10), None);
1423
1424        for raw in 1..=39 {
1425            let value = DiagnosticFunctionKind::known(raw).expect("function kind should be known");
1426            assert_eq!(value.raw(), raw);
1427            assert_eq!(format!("{value:?}"), raw.to_string());
1428        }
1429        assert_eq!(DiagnosticFunctionKind::known(0), None);
1430        assert_eq!(DiagnosticFunctionKind::known(40), None);
1431
1432        for raw in 1..=18 {
1433            let value = DiagnosticOperatorKind::known(raw).expect("operator kind should be known");
1434            assert_eq!(value.raw(), raw);
1435            assert_eq!(format!("{value:?}"), raw.to_string());
1436        }
1437        assert_eq!(DiagnosticOperatorKind::known(0), None);
1438        assert_eq!(DiagnosticOperatorKind::known(19), None);
1439
1440        for raw in 1..=8 {
1441            let value =
1442                DiagnosticAggregateKind::known(raw).expect("aggregate kind should be known");
1443            assert_eq!(value.raw(), raw);
1444            assert_eq!(format!("{value:?}"), raw.to_string());
1445        }
1446        assert_eq!(DiagnosticAggregateKind::known(0), None);
1447        assert_eq!(DiagnosticAggregateKind::known(9), None);
1448    }
1449
1450    #[test]
1451    fn per_code_schema_rejects_missing_disallowed_and_noncanonical_tags() {
1452        let valid = [
1453            (DiagnosticFactTag::ActualCount, 5),
1454            (DiagnosticFactTag::Limit, 4),
1455        ];
1456        assert_eq!(
1457            validate_known_diagnostic_fact_schema(
1458                ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1459                &valid,
1460            ),
1461            Ok(())
1462        );
1463        assert_eq!(
1464            validate_known_diagnostic_fact_schema(
1465                ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1466                &valid[..1],
1467            ),
1468            Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1469        );
1470        assert_eq!(
1471            validate_known_diagnostic_fact_schema(
1472                ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1473                &[valid[1], valid[0]],
1474            ),
1475            Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1476        );
1477        assert_eq!(
1478            validate_known_diagnostic_fact_schema(ErrorCode::QUERY_VALIDATE, &valid),
1479            Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded)
1480        );
1481
1482        assert_eq!(
1483            validate_known_diagnostic_fact_schema(
1484                ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_COMMIT_WORK_EXCEEDED,
1485                &valid,
1486            ),
1487            Ok(())
1488        );
1489        assert_eq!(
1490            validate_known_diagnostic_fact_schema(
1491                ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_COMMIT_WORK_EXCEEDED,
1492                &valid[1..],
1493            ),
1494            Ok(())
1495        );
1496    }
1497
1498    #[test]
1499    fn execution_budget_schema_requires_complete_typed_attribution() {
1500        let valid = [
1501            (
1502                DiagnosticFactTag::BudgetResource,
1503                DiagnosticExecutionBudgetResource::StoredBytesRead.raw(),
1504            ),
1505            (DiagnosticFactTag::Limit, 4_096),
1506            (DiagnosticFactTag::Actual, 4_097),
1507            (
1508                DiagnosticFactTag::ExecutionBudgetScope,
1509                DiagnosticExecutionBudgetScope::Request.raw(),
1510            ),
1511            (
1512                DiagnosticFactTag::ExecutionLane,
1513                DiagnosticExecutionLane::TrustedRead.raw(),
1514            ),
1515            (DiagnosticFactTag::QueryShapeFingerprintPrefix, 17),
1516        ];
1517        assert_eq!(
1518            validate_known_diagnostic_fact_schema(
1519                ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1520                &valid,
1521            ),
1522            Ok(())
1523        );
1524        assert_eq!(
1525            validate_known_diagnostic_fact_schema(
1526                ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1527                &valid[..5],
1528            ),
1529            Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1530        );
1531
1532        let mut invalid_resource = valid;
1533        invalid_resource[0].1 = 0;
1534        assert_eq!(
1535            validate_known_diagnostic_fact_schema(
1536                ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1537                &invalid_resource,
1538            ),
1539            Err(DiagnosticFactSchemaMismatch::InvalidValue)
1540        );
1541    }
1542
1543    #[test]
1544    fn raw_schema_keeps_unknown_context_numeric_but_marks_it_invalid() {
1545        assert_eq!(
1546            validate_raw_diagnostic_fact_schema(
1547                ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
1548                &[(u8::MAX, 17)],
1549            ),
1550            Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1551        );
1552        assert_eq!(
1553            validate_raw_diagnostic_fact_schema(
1554                ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
1555                &[(DiagnosticFactTag::DecodeReason.raw(), u64::MAX)],
1556            ),
1557            Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1558        );
1559    }
1560
1561    #[test]
1562    fn constraint_schema_enforces_authority_operation_and_bounded_path_suffix() {
1563        let mut targeted = vec![
1564            (DiagnosticFactTag::AcceptedSchemaFingerprintMethod, 1),
1565            (DiagnosticFactTag::AcceptedSchemaFingerprintHigh, 2),
1566            (DiagnosticFactTag::AcceptedSchemaFingerprintLow, 3),
1567            (DiagnosticFactTag::EntityTag, 17),
1568            (DiagnosticFactTag::ConstraintId, 4),
1569            (
1570                DiagnosticFactTag::ConstraintKind,
1571                DiagnosticConstraintKind::TargetedRule.raw(),
1572            ),
1573            (
1574                DiagnosticFactTag::ConstraintContext,
1575                DiagnosticConstraintContext::WriteAdmission.raw(),
1576            ),
1577            (
1578                DiagnosticFactTag::MutationOperation,
1579                DiagnosticMutationOperation::Insert.raw(),
1580            ),
1581            (DiagnosticFactTag::BatchPosition, 0),
1582        ];
1583        targeted.extend((0..64).map(|index| (DiagnosticFactTag::ListElement, index)));
1584        assert_eq!(targeted.len(), 73);
1585        assert_eq!(
1586            validate_known_diagnostic_fact_schema(
1587                ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1588                targeted.as_slice(),
1589            ),
1590            Ok(())
1591        );
1592
1593        let mut overlong = targeted.clone();
1594        overlong.push((DiagnosticFactTag::ListElement, 64));
1595        assert_eq!(
1596            validate_known_diagnostic_fact_schema(
1597                ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1598                overlong.as_slice(),
1599            ),
1600            Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded)
1601        );
1602
1603        let mut non_targeted_path = targeted;
1604        non_targeted_path[5].1 = DiagnosticConstraintKind::Unique.raw();
1605        assert_eq!(
1606            validate_known_diagnostic_fact_schema(
1607                ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1608                non_targeted_path.as_slice(),
1609            ),
1610            Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1611        );
1612    }
1613
1614    #[test]
1615    fn schema_enforces_global_ceiling_before_per_code_ceiling() {
1616        let facts = vec![(DiagnosticFactTag::ActualCount.raw(), 0); 81];
1617        assert_eq!(
1618            validate_raw_diagnostic_fact_schema(ErrorCode::QUERY_PLAN, facts.as_slice()),
1619            Err(DiagnosticFactSchemaMismatch::GlobalMaximumExceeded)
1620        );
1621    }
1622}