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