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    CursorOddLength,
160    CursorInvalidHex,
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        DiagnosticSteps = 18;
251        ResultRows = 19;
252        ResultBytes = 20;
253        InstructionUnits = 21;
254    }
255}
256
257impl DiagnosticExecutionBudgetResource {
258    /// Every maintained hard-budget resource in stable numeric order.
259    pub const ALL: [Self; 21] = [
260        Self::QueryExecutions,
261        Self::PlanningSteps,
262        Self::PlanCompilations,
263        Self::KeyIndexEntriesVisited,
264        Self::RowsVisited,
265        Self::StoredBytesRead,
266        Self::PredicateExpressionSteps,
267        Self::NestedValueSteps,
268        Self::DecodedBytes,
269        Self::MaterializedBytes,
270        Self::SortEntries,
271        Self::SortComparisons,
272        Self::SortTemporaryBytes,
273        Self::GroupDistinctEntries,
274        Self::GroupDistinctStateBytes,
275        Self::CursorSteps,
276        Self::TemporaryBytes,
277        Self::DiagnosticSteps,
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::CursorOddLength => 3,
439            Self::CursorInvalidHex => 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        }
447    }
448
449    /// Recover a known compact decode reason.
450    #[must_use]
451    pub const fn known(raw: u64) -> Option<Self> {
452        match raw {
453            1 => Some(Self::CursorEmpty),
454            2 => Some(Self::CursorTooLong),
455            3 => Some(Self::CursorOddLength),
456            4 => Some(Self::CursorInvalidHex),
457            5 => Some(Self::CursorGroupedDirectionMismatch),
458            6 => Some(Self::CursorTokenEncode),
459            7 => Some(Self::CursorTokenDecode),
460            8 => Some(Self::RecoveryMarkerMagic),
461            9 => Some(Self::RecoveryMarkerChecksum),
462            10 => Some(Self::RecoveryMarkerState),
463            _ => None,
464        }
465    }
466}
467
468impl DiagnosticMutationOperation {
469    /// Return the fixed numeric fact value.
470    #[must_use]
471    pub const fn raw(self) -> u64 {
472        match self {
473            Self::Insert => 1,
474            Self::Replace => 2,
475            Self::Update => 3,
476            Self::Delete => 4,
477        }
478    }
479
480    /// Recover a known compact mutation operation.
481    #[must_use]
482    pub const fn known(raw: u64) -> Option<Self> {
483        match raw {
484            1 => Some(Self::Insert),
485            2 => Some(Self::Replace),
486            3 => Some(Self::Update),
487            4 => Some(Self::Delete),
488            _ => None,
489        }
490    }
491}
492
493impl fmt::Debug for DiagnosticFactTag {
494    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
495        write!(f, "{}", self.raw())
496    }
497}
498
499impl fmt::Debug for DiagnosticDecodeReason {
500    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
501        write!(f, "{}", self.raw())
502    }
503}
504
505impl fmt::Debug for DiagnosticMutationOperation {
506    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
507        write!(f, "{}", self.raw())
508    }
509}
510
511/// Pack two accepted `u32` identities into one fact value without narrowing.
512#[must_use]
513pub const fn pack_u32_pair(high: u32, low: u32) -> u64 {
514    (high as u64) << 32 | low as u64
515}
516
517/// Recover the two accepted identities from one packed fact value.
518#[must_use]
519pub const fn unpack_u32_pair(value: u64) -> (u32, u32) {
520    let bytes = value.to_be_bytes();
521    (
522        u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
523        u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
524    )
525}
526
527/// Why one numeric fact sequence does not satisfy its owning E-code schema.
528///
529/// This taxonomy intentionally carries no prose. Host tooling owns rendering.
530#[derive(Clone, Copy, Debug, Eq, PartialEq)]
531pub enum DiagnosticFactSchemaMismatch {
532    /// The sequence exceeds the global public fact ceiling.
533    GlobalMaximumExceeded,
534    /// The sequence exceeds the tighter ceiling for its E-code.
535    CodeMaximumExceeded,
536    /// Required, allowed, repeated, or ordered tags do not match the E-code.
537    InvalidSequence,
538    /// One known compact tag carries a value outside its frozen numeric registry.
539    InvalidValue,
540}
541
542/// Validate facts already expressed with known production tag identities.
543///
544/// The validator allocates nothing. It is the runtime-side entry point used
545/// immediately before facts cross the public facade.
546pub fn validate_known_diagnostic_fact_schema(
547    code: ErrorCode,
548    facts: &[(DiagnosticFactTag, u64)],
549) -> Result<(), DiagnosticFactSchemaMismatch> {
550    validate_diagnostic_fact_schema(code, facts.len(), |index| {
551        let (tag, value) = facts[index];
552        (tag.raw(), value)
553    })
554}
555
556/// Validate raw host/tooling facts against their owning E-code schema.
557///
558/// Unknown tags remain renderable by callers, but make a known E-code context
559/// invalid instead of being heuristically reinterpreted.
560pub fn validate_raw_diagnostic_fact_schema(
561    code: ErrorCode,
562    facts: &[(u8, u64)],
563) -> Result<(), DiagnosticFactSchemaMismatch> {
564    validate_diagnostic_fact_schema(code, facts.len(), |index| facts[index])
565}
566
567#[expect(
568    clippy::too_many_lines,
569    reason = "the frozen E-code schema registry keeps every numeric owner visible in one exhaustive dispatch"
570)]
571fn validate_diagnostic_fact_schema(
572    code: ErrorCode,
573    fact_count: usize,
574    fact_at: impl Fn(usize) -> (u8, u64),
575) -> Result<(), DiagnosticFactSchemaMismatch> {
576    if fact_count > MAX_PUBLIC_DIAGNOSTIC_FACTS {
577        return Err(DiagnosticFactSchemaMismatch::GlobalMaximumExceeded);
578    }
579
580    let maximum = diagnostic_fact_maximum(code);
581    if fact_count > maximum {
582        return Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded);
583    }
584
585    let valid_sequence = match code.raw() {
586        3 => query_plan_schema(fact_count, &fact_at),
587        6 => cursor_schema(fact_count, &fact_at),
588        16 => store_corruption_schema(fact_count, &fact_at),
589        18 => runtime_corruption_schema(fact_count, &fact_at),
590        19 => incompatible_format_schema(fact_count, &fact_at),
591        20 => runtime_invariant_schema(fact_count, &fact_at),
592        21 => runtime_conflict_schema(fact_count, &fact_at),
593        23 => runtime_unsupported_schema(fact_count, &fact_at),
594        24 => runtime_internal_schema(fact_count, &fact_at),
595        138 => tags_match(
596            fact_count,
597            &fact_at,
598            &[
599                DiagnosticFactTag::ExpectedArity,
600                DiagnosticFactTag::ActualArity,
601            ],
602        ),
603        141 | 142 | 169 | 170 => {
604            tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ProjectionIndex])
605        }
606        175 => tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ParameterIndex]),
607        177 | 237 => {
608            tags_match(fact_count, &fact_at, &[DiagnosticFactTag::Limit])
609                || tags_match(
610                    fact_count,
611                    &fact_at,
612                    &[DiagnosticFactTag::ActualLength, DiagnosticFactTag::Limit],
613                )
614        }
615        178 | 180 | 202 | 203 | 205 | 236 | 269 => tags_match(
616            fact_count,
617            &fact_at,
618            &[DiagnosticFactTag::ActualCount, DiagnosticFactTag::Limit],
619        ),
620        283 => {
621            tags_match(fact_count, &fact_at, &[DiagnosticFactTag::Limit])
622                || tags_match(
623                    fact_count,
624                    &fact_at,
625                    &[DiagnosticFactTag::ActualCount, DiagnosticFactTag::Limit],
626                )
627        }
628        284 => tags_match(
629            fact_count,
630            &fact_at,
631            &[
632                DiagnosticFactTag::BacklogResource,
633                DiagnosticFactTag::CurrentCount,
634                DiagnosticFactTag::ProposedCount,
635                DiagnosticFactTag::Limit,
636            ],
637        ),
638        196 | 234 => tags_match(
639            fact_count,
640            &fact_at,
641            &[
642                DiagnosticFactTag::EntityTag,
643                DiagnosticFactTag::FieldId,
644                DiagnosticFactTag::MutationOperation,
645                DiagnosticFactTag::BatchPosition,
646            ],
647        ),
648        197 => tags_match(
649            fact_count,
650            &fact_at,
651            &[
652                DiagnosticFactTag::RowLayout,
653                DiagnosticFactTag::HistoryFloor,
654                DiagnosticFactTag::CurrentLayout,
655            ],
656        ),
657        198 => tags_match(
658            fact_count,
659            &fact_at,
660            &[
661                DiagnosticFactTag::RowLayout,
662                DiagnosticFactTag::ExpectedSlotCount,
663                DiagnosticFactTag::ActualSlotCount,
664            ],
665        ),
666        201 => tags_match(
667            fact_count,
668            &fact_at,
669            &[DiagnosticFactTag::ActualCount, DiagnosticFactTag::Minimum],
670        ),
671        223 => constraint_schema(fact_count, &fact_at, true),
672        225 => constraint_schema(fact_count, &fact_at, false),
673        233 => tags_match(
674            fact_count,
675            &fact_at,
676            &[
677                DiagnosticFactTag::EntityTag,
678                DiagnosticFactTag::MutationOperation,
679                DiagnosticFactTag::BatchPosition,
680            ],
681        ),
682        235 => {
683            tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ActualCount]) && fact_at(0).1 == 0
684        }
685        238 | 270 | 271 | 272 => tags_match(
686            fact_count,
687            &fact_at,
688            &[DiagnosticFactTag::ActualLength, DiagnosticFactTag::Limit],
689        ),
690        273 => tags_match(
691            fact_count,
692            &fact_at,
693            &[
694                DiagnosticFactTag::BudgetResource,
695                DiagnosticFactTag::Limit,
696                DiagnosticFactTag::Actual,
697                DiagnosticFactTag::ExecutionBudgetScope,
698                DiagnosticFactTag::ExecutionLane,
699                DiagnosticFactTag::QueryShapeFingerprintPrefix,
700            ],
701        ),
702        274 => tags_match(
703            fact_count,
704            &fact_at,
705            &[
706                DiagnosticFactTag::BudgetResource,
707                DiagnosticFactTag::Limit,
708                DiagnosticFactTag::Actual,
709            ],
710        ),
711        239 => tags_match(
712            fact_count,
713            &fact_at,
714            &[
715                DiagnosticFactTag::BatchPosition,
716                DiagnosticFactTag::ExpectedEntityTag,
717                DiagnosticFactTag::ActualEntityTag,
718            ],
719        ),
720        240 => tags_match(
721            fact_count,
722            &fact_at,
723            &[
724                DiagnosticFactTag::EntityTag,
725                DiagnosticFactTag::FirstBatchPosition,
726                DiagnosticFactTag::DuplicateBatchPosition,
727            ],
728        ),
729        _ => fact_count == 0,
730    };
731    if !valid_sequence {
732        return Err(DiagnosticFactSchemaMismatch::InvalidSequence);
733    }
734
735    for index in 0..fact_count {
736        let (raw_tag, value) = fact_at(index);
737        let Some(tag) = DiagnosticFactTag::known(raw_tag) else {
738            return Err(DiagnosticFactSchemaMismatch::InvalidSequence);
739        };
740        if !diagnostic_fact_value_is_valid(tag, value) {
741            return Err(DiagnosticFactSchemaMismatch::InvalidValue);
742        }
743    }
744    Ok(())
745}
746
747const fn diagnostic_fact_maximum(code: ErrorCode) -> usize {
748    match code.raw() {
749        6 | 16 | 18 | 24 | 197 | 198 | 233 | 239 | 240 | 274 => 3,
750        141 | 142 | 169 | 170 | 175 | 235 => 1,
751        19 | 21 | 138 | 177 | 178 | 180 | 201 | 202 | 203 | 205 | 236 | 237 | 238 | 269 | 270
752        | 271 | 272 | 283 => 2,
753        3 | 20 => 5,
754        23 | 273 => 6,
755        196 | 234 | 284 => 4,
756        223 => 73,
757        225 => 9,
758        _ => 0,
759    }
760}
761
762#[expect(
763    clippy::too_many_lines,
764    reason = "the query-plan E-code deliberately owns several exact finite fact sequences"
765)]
766fn query_plan_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
767    fact_count == 0
768        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::TermIndex])
769        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ComponentIndex])
770        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::GroupIndex])
771        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ClauseIndex])
772        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::AggregateIndex])
773        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::AggregateKind])
774        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ProjectionIndex])
775        || tags_match(
776            fact_count,
777            fact_at,
778            &[
779                DiagnosticFactTag::FirstTermIndex,
780                DiagnosticFactTag::DuplicateTermIndex,
781            ],
782        )
783        || tags_match(
784            fact_count,
785            fact_at,
786            &[
787                DiagnosticFactTag::ClauseIndex,
788                DiagnosticFactTag::OperatorKind,
789            ],
790        )
791        || tags_match(
792            fact_count,
793            fact_at,
794            &[
795                DiagnosticFactTag::AggregateIndex,
796                DiagnosticFactTag::AggregateKind,
797            ],
798        )
799        || tags_match(
800            fact_count,
801            fact_at,
802            &[
803                DiagnosticFactTag::AggregateKind,
804                DiagnosticFactTag::TypeFamily,
805            ],
806        )
807        || tags_match(
808            fact_count,
809            fact_at,
810            &[
811                DiagnosticFactTag::OperatorKind,
812                DiagnosticFactTag::TypeFamily,
813            ],
814        )
815        || tags_match(
816            fact_count,
817            fact_at,
818            &[
819                DiagnosticFactTag::BranchIndex,
820                DiagnosticFactTag::TypeFamily,
821            ],
822        )
823        || tags_match(
824            fact_count,
825            fact_at,
826            &[DiagnosticFactTag::TypeFamily, DiagnosticFactTag::TypeFamily],
827        )
828        || tags_match(
829            fact_count,
830            fact_at,
831            &[
832                DiagnosticFactTag::ClauseIndex,
833                DiagnosticFactTag::AggregateIndex,
834                DiagnosticFactTag::ActualCount,
835            ],
836        )
837        || tags_match(
838            fact_count,
839            fact_at,
840            &[
841                DiagnosticFactTag::FunctionKind,
842                DiagnosticFactTag::ExpectedArity,
843                DiagnosticFactTag::ActualArity,
844            ],
845        )
846        || tags_match(
847            fact_count,
848            fact_at,
849            &[
850                DiagnosticFactTag::FunctionKind,
851                DiagnosticFactTag::ArgumentIndex,
852                DiagnosticFactTag::TypeFamily,
853            ],
854        )
855        || tags_match(
856            fact_count,
857            fact_at,
858            &[
859                DiagnosticFactTag::OperatorKind,
860                DiagnosticFactTag::TypeFamily,
861                DiagnosticFactTag::TypeFamily,
862            ],
863        )
864        || tags_match(
865            fact_count,
866            fact_at,
867            &[
868                DiagnosticFactTag::BranchIndex,
869                DiagnosticFactTag::TypeFamily,
870                DiagnosticFactTag::TypeFamily,
871            ],
872        )
873        || tags_match(
874            fact_count,
875            fact_at,
876            &[
877                DiagnosticFactTag::TypeFamily,
878                DiagnosticFactTag::BranchIndex,
879                DiagnosticFactTag::TypeFamily,
880            ],
881        )
882        || tags_match(
883            fact_count,
884            fact_at,
885            &[
886                DiagnosticFactTag::BranchIndex,
887                DiagnosticFactTag::TypeFamily,
888                DiagnosticFactTag::BranchIndex,
889                DiagnosticFactTag::TypeFamily,
890            ],
891        )
892        || tags_match(
893            fact_count,
894            fact_at,
895            &[
896                DiagnosticFactTag::FunctionKind,
897                DiagnosticFactTag::ArgumentIndex,
898                DiagnosticFactTag::TypeFamily,
899                DiagnosticFactTag::ArgumentIndex,
900                DiagnosticFactTag::TypeFamily,
901            ],
902        )
903}
904
905fn cursor_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
906    if fact_count == 0 {
907        return true;
908    }
909    if tags_match(fact_count, fact_at, &[DiagnosticFactTag::DecodeReason]) {
910        return matches!(fact_at(0).1, 1 | 3 | 5 | 6 | 7);
911    }
912    if tags_match(
913        fact_count,
914        fact_at,
915        &[
916            DiagnosticFactTag::ActualLength,
917            DiagnosticFactTag::Maximum,
918            DiagnosticFactTag::DecodeReason,
919        ],
920    ) {
921        return fact_at(2).1 == DiagnosticDecodeReason::CursorTooLong.raw();
922    }
923    if tags_match(
924        fact_count,
925        fact_at,
926        &[
927            DiagnosticFactTag::ComponentIndex,
928            DiagnosticFactTag::DecodeReason,
929        ],
930    ) {
931        return matches!(fact_at(1).1, 4 | 6 | 7);
932    }
933    tags_match(
934        fact_count,
935        fact_at,
936        &[
937            DiagnosticFactTag::ExpectedSignaturePrefix,
938            DiagnosticFactTag::ActualSignaturePrefix,
939        ],
940    ) || tags_match(
941        fact_count,
942        fact_at,
943        &[
944            DiagnosticFactTag::ExpectedOffset,
945            DiagnosticFactTag::ActualOffset,
946        ],
947    )
948}
949
950fn store_corruption_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
951    fact_count == 0
952        || (tags_match(
953            fact_count,
954            fact_at,
955            &[
956                DiagnosticFactTag::ComponentKind,
957                DiagnosticFactTag::ActualLength,
958                DiagnosticFactTag::Limit,
959            ],
960        ) && fact_at(0).1 == DiagnosticComponentKind::CommitDataKey.raw())
961        || tags_match(
962            fact_count,
963            fact_at,
964            &[
965                DiagnosticFactTag::ExpectedEntityTag,
966                DiagnosticFactTag::ActualEntityTag,
967            ],
968        )
969}
970
971fn runtime_corruption_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
972    store_corruption_schema(fact_count, fact_at)
973        || (tags_match(fact_count, fact_at, &[DiagnosticFactTag::DecodeReason])
974            && matches!(fact_at(0).1, 8..=10))
975}
976
977fn incompatible_format_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
978    fact_count == 0
979        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ExpectedVersion])
980        || tags_match(
981            fact_count,
982            fact_at,
983            &[
984                DiagnosticFactTag::ExpectedVersion,
985                DiagnosticFactTag::ActualVersion,
986            ],
987        )
988}
989
990fn runtime_invariant_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
991    fact_count == 0
992        || (tags_match(
993            fact_count,
994            fact_at,
995            &[
996                DiagnosticFactTag::EntityTag,
997                DiagnosticFactTag::PhysicalGeneration,
998                DiagnosticFactTag::ComponentKind,
999                DiagnosticFactTag::ActualArity,
1000                DiagnosticFactTag::Maximum,
1001            ],
1002        ) && fact_at(2).1 == DiagnosticComponentKind::IndexKey.raw())
1003}
1004
1005fn runtime_conflict_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1006    fact_count == 0
1007        || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ExpectedRevision])
1008        || tags_match(
1009            fact_count,
1010            fact_at,
1011            &[
1012                DiagnosticFactTag::ExpectedRevision,
1013                DiagnosticFactTag::CurrentRevision,
1014            ],
1015        )
1016}
1017
1018fn runtime_unsupported_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1019    fact_count == 0
1020        || (tags_match(
1021            fact_count,
1022            fact_at,
1023            &[
1024                DiagnosticFactTag::EntityTag,
1025                DiagnosticFactTag::PhysicalGeneration,
1026                DiagnosticFactTag::ComponentIndex,
1027                DiagnosticFactTag::ComponentKind,
1028                DiagnosticFactTag::ActualLength,
1029                DiagnosticFactTag::Limit,
1030            ],
1031        ) && fact_at(3).1 == DiagnosticComponentKind::IndexKeyComponent.raw())
1032}
1033
1034fn runtime_internal_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::ExpectedMemoryId,
1041                DiagnosticFactTag::ActualMemoryId,
1042            ],
1043        )
1044        || (tags_match(
1045            fact_count,
1046            fact_at,
1047            &[
1048                DiagnosticFactTag::ComponentKind,
1049                DiagnosticFactTag::ExpectedArity,
1050                DiagnosticFactTag::ActualArity,
1051            ],
1052        ) && fact_at(0).1 == DiagnosticComponentKind::RelationTargetPrimaryKey.raw())
1053}
1054
1055fn constraint_schema(
1056    fact_count: usize,
1057    fact_at: &impl Fn(usize) -> (u8, u64),
1058    allow_targeted_path: bool,
1059) -> bool {
1060    const COMMON: &[DiagnosticFactTag] = &[
1061        DiagnosticFactTag::AcceptedSchemaFingerprintMethod,
1062        DiagnosticFactTag::AcceptedSchemaFingerprintHigh,
1063        DiagnosticFactTag::AcceptedSchemaFingerprintLow,
1064        DiagnosticFactTag::EntityTag,
1065        DiagnosticFactTag::ConstraintId,
1066        DiagnosticFactTag::ConstraintKind,
1067        DiagnosticFactTag::ConstraintContext,
1068    ];
1069    if fact_count < COMMON.len()
1070        || !tags_prefix_matches(fact_count, fact_at, COMMON)
1071        || fact_at(6).1 != DiagnosticConstraintContext::WriteAdmission.raw()
1072    {
1073        return false;
1074    }
1075
1076    let constraint_kind = fact_at(5).1;
1077    if DiagnosticConstraintKind::known(constraint_kind).is_none() {
1078        return false;
1079    }
1080    let mut index = COMMON.len();
1081    if index < fact_count && fact_at(index).0 == DiagnosticFactTag::MutationOperation.raw() {
1082        index += 1;
1083        if index < fact_count && fact_at(index).0 == DiagnosticFactTag::BatchPosition.raw() {
1084            index += 1;
1085        }
1086    }
1087
1088    let path_len = fact_count - index;
1089    if path_len == 0 {
1090        return !allow_targeted_path
1091            || constraint_kind != DiagnosticConstraintKind::TargetedRule.raw();
1092    }
1093    allow_targeted_path
1094        && constraint_kind == DiagnosticConstraintKind::TargetedRule.raw()
1095        && path_len <= 64
1096        && (index..fact_count).all(|position| {
1097            DiagnosticFactTag::known(fact_at(position).0).is_some_and(is_value_path_tag)
1098        })
1099}
1100
1101fn tags_match(
1102    fact_count: usize,
1103    fact_at: &impl Fn(usize) -> (u8, u64),
1104    expected: &[DiagnosticFactTag],
1105) -> bool {
1106    fact_count == expected.len() && tags_prefix_matches(fact_count, fact_at, expected)
1107}
1108
1109fn tags_prefix_matches(
1110    fact_count: usize,
1111    fact_at: &impl Fn(usize) -> (u8, u64),
1112    expected: &[DiagnosticFactTag],
1113) -> bool {
1114    fact_count >= expected.len()
1115        && expected
1116            .iter()
1117            .enumerate()
1118            .all(|(index, tag)| fact_at(index).0 == tag.raw())
1119}
1120
1121const fn is_value_path_tag(tag: DiagnosticFactTag) -> bool {
1122    matches!(
1123        tag,
1124        DiagnosticFactTag::RootField
1125            | DiagnosticFactTag::RecordMember
1126            | DiagnosticFactTag::TupleElement
1127            | DiagnosticFactTag::Newtype
1128            | DiagnosticFactTag::EnumVariant
1129            | DiagnosticFactTag::ListElement
1130            | DiagnosticFactTag::SetElement
1131            | DiagnosticFactTag::MapEntryKey
1132            | DiagnosticFactTag::MapEntryValue
1133    )
1134}
1135
1136const fn diagnostic_fact_value_is_valid(tag: DiagnosticFactTag, value: u64) -> bool {
1137    match tag {
1138        DiagnosticFactTag::AcceptedSchemaFingerprintMethod
1139        | DiagnosticFactTag::ExpectedMemoryId
1140        | DiagnosticFactTag::ActualMemoryId => value <= u8::MAX as u64,
1141        DiagnosticFactTag::ConstraintId
1142        | DiagnosticFactTag::FieldId
1143        | DiagnosticFactTag::IndexId
1144        | DiagnosticFactTag::RelationId
1145        | DiagnosticFactTag::BatchPosition
1146        | DiagnosticFactTag::FirstBatchPosition
1147        | DiagnosticFactTag::DuplicateBatchPosition
1148        | DiagnosticFactTag::RowLayout
1149        | DiagnosticFactTag::HistoryFloor
1150        | DiagnosticFactTag::CurrentLayout
1151        | DiagnosticFactTag::RootField
1152        | DiagnosticFactTag::Newtype
1153        | DiagnosticFactTag::ListElement
1154        | DiagnosticFactTag::SetElement
1155        | DiagnosticFactTag::MapEntryKey
1156        | DiagnosticFactTag::MapEntryValue => value <= u32::MAX as u64,
1157        DiagnosticFactTag::ConstraintKind => DiagnosticConstraintKind::known(value).is_some(),
1158        DiagnosticFactTag::BacklogResource => DiagnosticBacklogResource::known(value).is_some(),
1159        DiagnosticFactTag::ConstraintContext => DiagnosticConstraintContext::known(value).is_some(),
1160        DiagnosticFactTag::TypeFamily => DiagnosticTypeFamily::known(value).is_some(),
1161        DiagnosticFactTag::FunctionKind => DiagnosticFunctionKind::known(value).is_some(),
1162        DiagnosticFactTag::OperatorKind => DiagnosticOperatorKind::known(value).is_some(),
1163        DiagnosticFactTag::AggregateKind => DiagnosticAggregateKind::known(value).is_some(),
1164        DiagnosticFactTag::ComponentKind => DiagnosticComponentKind::known(value).is_some(),
1165        DiagnosticFactTag::DecodeReason => DiagnosticDecodeReason::known(value).is_some(),
1166        DiagnosticFactTag::BudgetResource => {
1167            DiagnosticExecutionBudgetResource::known(value).is_some()
1168        }
1169        DiagnosticFactTag::ExecutionBudgetScope => {
1170            DiagnosticExecutionBudgetScope::known(value).is_some()
1171        }
1172        DiagnosticFactTag::ExecutionLane => DiagnosticExecutionLane::known(value).is_some(),
1173        DiagnosticFactTag::MutationOperation => DiagnosticMutationOperation::known(value).is_some(),
1174        _ => true,
1175    }
1176}
1177
1178#[cfg(test)]
1179mod tests {
1180    use super::{
1181        DiagnosticAggregateKind, DiagnosticBacklogResource, DiagnosticComponentKind,
1182        DiagnosticConstraintContext, DiagnosticConstraintKind, DiagnosticDecodeReason,
1183        DiagnosticExecutionBudgetResource, DiagnosticExecutionBudgetScope, DiagnosticExecutionLane,
1184        DiagnosticFactSchemaMismatch, DiagnosticFactTag, DiagnosticFunctionKind,
1185        DiagnosticMutationOperation, DiagnosticOperatorKind, DiagnosticTypeFamily,
1186        ORDERED_FACT_TAGS, pack_u32_pair, unpack_u32_pair, validate_known_diagnostic_fact_schema,
1187        validate_raw_diagnostic_fact_schema,
1188    };
1189    use crate::ErrorCode;
1190
1191    #[test]
1192    fn fact_tag_registry_is_fixed_unique_and_contiguous() {
1193        for (index, tag) in ORDERED_FACT_TAGS.iter().copied().enumerate() {
1194            let expected = u8::try_from(index + 1).expect("fact-tag index fits u8");
1195            assert_eq!(tag.raw(), expected);
1196            assert_eq!(DiagnosticFactTag::known(expected), Some(tag));
1197        }
1198
1199        assert_eq!(DiagnosticFactTag::known(0), None);
1200        assert_eq!(DiagnosticFactTag::known(96), None);
1201        assert_eq!(DiagnosticFactTag::known(u8::MAX), None);
1202    }
1203
1204    #[test]
1205    fn execution_budget_fact_value_registries_are_fixed() {
1206        assert_eq!(DiagnosticBacklogResource::Batches.raw(), 1);
1207        assert_eq!(DiagnosticBacklogResource::Records.raw(), 2);
1208        assert_eq!(DiagnosticBacklogResource::EncodedBytes.raw(), 3);
1209        assert_eq!(DiagnosticBacklogResource::known(4), None);
1210
1211        for (index, resource) in DiagnosticExecutionBudgetResource::ALL
1212            .iter()
1213            .copied()
1214            .enumerate()
1215        {
1216            let expected = u64::try_from(index + 1).expect("resource index fits u64");
1217            assert_eq!(resource.raw(), expected);
1218            assert_eq!(
1219                DiagnosticExecutionBudgetResource::known(expected),
1220                Some(resource)
1221            );
1222        }
1223        assert_eq!(DiagnosticExecutionBudgetResource::known(0), None);
1224        assert_eq!(DiagnosticExecutionBudgetResource::known(22), None);
1225
1226        assert_eq!(DiagnosticExecutionBudgetScope::Execution.raw(), 1);
1227        assert_eq!(DiagnosticExecutionBudgetScope::Request.raw(), 2);
1228        assert_eq!(DiagnosticExecutionBudgetScope::known(3), None);
1229
1230        assert_eq!(DiagnosticExecutionLane::PublicRead.raw(), 1);
1231        assert_eq!(DiagnosticExecutionLane::TrustedRead.raw(), 2);
1232        assert_eq!(DiagnosticExecutionLane::Diagnostic.raw(), 3);
1233        assert_eq!(DiagnosticExecutionLane::Mutation.raw(), 4);
1234        assert_eq!(DiagnosticExecutionLane::Recovery.raw(), 5);
1235        assert_eq!(DiagnosticExecutionLane::known(6), None);
1236    }
1237
1238    #[test]
1239    fn accepted_identity_pair_packing_is_exact() {
1240        for pair in [
1241            (0, 0),
1242            (1, 2),
1243            (u32::MAX, 0),
1244            (0, u32::MAX),
1245            (u32::MAX, u32::MAX),
1246        ] {
1247            assert_eq!(unpack_u32_pair(pack_u32_pair(pair.0, pair.1)), pair);
1248        }
1249    }
1250
1251    #[test]
1252    fn constraint_fact_value_registries_are_fixed() {
1253        assert_eq!(DiagnosticConstraintKind::Check.raw(), 1);
1254        assert_eq!(DiagnosticConstraintKind::NotNull.raw(), 2);
1255        assert_eq!(DiagnosticConstraintKind::Relation.raw(), 3);
1256        assert_eq!(DiagnosticConstraintKind::TargetedRule.raw(), 4);
1257        assert_eq!(DiagnosticConstraintKind::Unique.raw(), 5);
1258        assert_eq!(DiagnosticConstraintKind::known(0), None);
1259        assert_eq!(DiagnosticConstraintKind::known(6), None);
1260
1261        assert_eq!(DiagnosticConstraintContext::Integrity.raw(), 1);
1262        assert_eq!(DiagnosticConstraintContext::MigrationValidation.raw(), 2);
1263        assert_eq!(DiagnosticConstraintContext::WriteAdmission.raw(), 3);
1264        assert_eq!(DiagnosticConstraintContext::known(0), None);
1265        assert_eq!(DiagnosticConstraintContext::known(4), None);
1266    }
1267
1268    #[test]
1269    fn component_kind_registry_is_fixed_and_numeric() {
1270        let kinds = [
1271            DiagnosticComponentKind::CommitDataKey,
1272            DiagnosticComponentKind::IndexKey,
1273            DiagnosticComponentKind::IndexKeyComponent,
1274            DiagnosticComponentKind::RelationTargetPrimaryKey,
1275        ];
1276
1277        for (index, kind) in kinds.iter().copied().enumerate() {
1278            let expected = (index + 1) as u64;
1279            assert_eq!(kind.raw(), expected);
1280            assert_eq!(DiagnosticComponentKind::known(expected), Some(kind));
1281            assert_eq!(format!("{kind:?}"), expected.to_string());
1282        }
1283        assert_eq!(DiagnosticComponentKind::known(0), None);
1284        assert_eq!(DiagnosticComponentKind::known(5), None);
1285    }
1286
1287    #[test]
1288    fn decode_reason_registry_is_fixed_and_numeric() {
1289        let reasons = [
1290            DiagnosticDecodeReason::CursorEmpty,
1291            DiagnosticDecodeReason::CursorTooLong,
1292            DiagnosticDecodeReason::CursorOddLength,
1293            DiagnosticDecodeReason::CursorInvalidHex,
1294            DiagnosticDecodeReason::CursorGroupedDirectionMismatch,
1295            DiagnosticDecodeReason::CursorTokenEncode,
1296            DiagnosticDecodeReason::CursorTokenDecode,
1297            DiagnosticDecodeReason::RecoveryMarkerMagic,
1298            DiagnosticDecodeReason::RecoveryMarkerChecksum,
1299            DiagnosticDecodeReason::RecoveryMarkerState,
1300        ];
1301
1302        for (index, reason) in reasons.iter().copied().enumerate() {
1303            let expected = (index + 1) as u64;
1304            assert_eq!(reason.raw(), expected);
1305            assert_eq!(DiagnosticDecodeReason::known(expected), Some(reason));
1306            assert_eq!(format!("{reason:?}"), expected.to_string());
1307        }
1308
1309        assert_eq!(DiagnosticDecodeReason::known(0), None);
1310        assert_eq!(DiagnosticDecodeReason::known(11), None);
1311    }
1312
1313    #[test]
1314    fn mutation_operation_registry_is_fixed_and_numeric() {
1315        let operations = [
1316            DiagnosticMutationOperation::Insert,
1317            DiagnosticMutationOperation::Replace,
1318            DiagnosticMutationOperation::Update,
1319            DiagnosticMutationOperation::Delete,
1320        ];
1321
1322        for (index, operation) in operations.iter().copied().enumerate() {
1323            let expected = (index + 1) as u64;
1324            assert_eq!(operation.raw(), expected);
1325            assert_eq!(
1326                DiagnosticMutationOperation::known(expected),
1327                Some(operation)
1328            );
1329            assert_eq!(format!("{operation:?}"), expected.to_string());
1330        }
1331
1332        assert_eq!(DiagnosticMutationOperation::known(0), None);
1333        assert_eq!(DiagnosticMutationOperation::known(5), None);
1334    }
1335
1336    #[test]
1337    fn query_kind_registries_are_fixed_contiguous_and_numeric() {
1338        for raw in 1..=9 {
1339            let value = DiagnosticTypeFamily::known(raw).expect("type family should be known");
1340            assert_eq!(value.raw(), raw);
1341            assert_eq!(format!("{value:?}"), raw.to_string());
1342        }
1343        assert_eq!(DiagnosticTypeFamily::known(0), None);
1344        assert_eq!(DiagnosticTypeFamily::known(10), None);
1345
1346        for raw in 1..=39 {
1347            let value = DiagnosticFunctionKind::known(raw).expect("function kind should be known");
1348            assert_eq!(value.raw(), raw);
1349            assert_eq!(format!("{value:?}"), raw.to_string());
1350        }
1351        assert_eq!(DiagnosticFunctionKind::known(0), None);
1352        assert_eq!(DiagnosticFunctionKind::known(40), None);
1353
1354        for raw in 1..=18 {
1355            let value = DiagnosticOperatorKind::known(raw).expect("operator kind should be known");
1356            assert_eq!(value.raw(), raw);
1357            assert_eq!(format!("{value:?}"), raw.to_string());
1358        }
1359        assert_eq!(DiagnosticOperatorKind::known(0), None);
1360        assert_eq!(DiagnosticOperatorKind::known(19), None);
1361
1362        for raw in 1..=8 {
1363            let value =
1364                DiagnosticAggregateKind::known(raw).expect("aggregate kind should be known");
1365            assert_eq!(value.raw(), raw);
1366            assert_eq!(format!("{value:?}"), raw.to_string());
1367        }
1368        assert_eq!(DiagnosticAggregateKind::known(0), None);
1369        assert_eq!(DiagnosticAggregateKind::known(9), None);
1370    }
1371
1372    #[test]
1373    fn per_code_schema_rejects_missing_disallowed_and_noncanonical_tags() {
1374        let valid = [
1375            (DiagnosticFactTag::ActualCount, 5),
1376            (DiagnosticFactTag::Limit, 4),
1377        ];
1378        assert_eq!(
1379            validate_known_diagnostic_fact_schema(
1380                ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1381                &valid,
1382            ),
1383            Ok(())
1384        );
1385        assert_eq!(
1386            validate_known_diagnostic_fact_schema(
1387                ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1388                &valid[..1],
1389            ),
1390            Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1391        );
1392        assert_eq!(
1393            validate_known_diagnostic_fact_schema(
1394                ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1395                &[valid[1], valid[0]],
1396            ),
1397            Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1398        );
1399        assert_eq!(
1400            validate_known_diagnostic_fact_schema(ErrorCode::QUERY_VALIDATE, &valid),
1401            Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded)
1402        );
1403
1404        assert_eq!(
1405            validate_known_diagnostic_fact_schema(
1406                ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_COMMIT_WORK_EXCEEDED,
1407                &valid,
1408            ),
1409            Ok(())
1410        );
1411        assert_eq!(
1412            validate_known_diagnostic_fact_schema(
1413                ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_COMMIT_WORK_EXCEEDED,
1414                &valid[1..],
1415            ),
1416            Ok(())
1417        );
1418    }
1419
1420    #[test]
1421    fn execution_budget_schema_requires_complete_typed_attribution() {
1422        let valid = [
1423            (
1424                DiagnosticFactTag::BudgetResource,
1425                DiagnosticExecutionBudgetResource::StoredBytesRead.raw(),
1426            ),
1427            (DiagnosticFactTag::Limit, 4_096),
1428            (DiagnosticFactTag::Actual, 4_097),
1429            (
1430                DiagnosticFactTag::ExecutionBudgetScope,
1431                DiagnosticExecutionBudgetScope::Request.raw(),
1432            ),
1433            (
1434                DiagnosticFactTag::ExecutionLane,
1435                DiagnosticExecutionLane::TrustedRead.raw(),
1436            ),
1437            (DiagnosticFactTag::QueryShapeFingerprintPrefix, 17),
1438        ];
1439        assert_eq!(
1440            validate_known_diagnostic_fact_schema(
1441                ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1442                &valid,
1443            ),
1444            Ok(())
1445        );
1446        assert_eq!(
1447            validate_known_diagnostic_fact_schema(
1448                ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1449                &valid[..5],
1450            ),
1451            Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1452        );
1453
1454        let mut invalid_resource = valid;
1455        invalid_resource[0].1 = 0;
1456        assert_eq!(
1457            validate_known_diagnostic_fact_schema(
1458                ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1459                &invalid_resource,
1460            ),
1461            Err(DiagnosticFactSchemaMismatch::InvalidValue)
1462        );
1463    }
1464
1465    #[test]
1466    fn raw_schema_keeps_unknown_context_numeric_but_marks_it_invalid() {
1467        assert_eq!(
1468            validate_raw_diagnostic_fact_schema(
1469                ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
1470                &[(u8::MAX, 17)],
1471            ),
1472            Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1473        );
1474        assert_eq!(
1475            validate_raw_diagnostic_fact_schema(
1476                ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
1477                &[(DiagnosticFactTag::DecodeReason.raw(), u64::MAX)],
1478            ),
1479            Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1480        );
1481    }
1482
1483    #[test]
1484    fn constraint_schema_enforces_authority_operation_and_bounded_path_suffix() {
1485        let mut targeted = vec![
1486            (DiagnosticFactTag::AcceptedSchemaFingerprintMethod, 1),
1487            (DiagnosticFactTag::AcceptedSchemaFingerprintHigh, 2),
1488            (DiagnosticFactTag::AcceptedSchemaFingerprintLow, 3),
1489            (DiagnosticFactTag::EntityTag, 17),
1490            (DiagnosticFactTag::ConstraintId, 4),
1491            (
1492                DiagnosticFactTag::ConstraintKind,
1493                DiagnosticConstraintKind::TargetedRule.raw(),
1494            ),
1495            (
1496                DiagnosticFactTag::ConstraintContext,
1497                DiagnosticConstraintContext::WriteAdmission.raw(),
1498            ),
1499            (
1500                DiagnosticFactTag::MutationOperation,
1501                DiagnosticMutationOperation::Insert.raw(),
1502            ),
1503            (DiagnosticFactTag::BatchPosition, 0),
1504        ];
1505        targeted.extend((0..64).map(|index| (DiagnosticFactTag::ListElement, index)));
1506        assert_eq!(targeted.len(), 73);
1507        assert_eq!(
1508            validate_known_diagnostic_fact_schema(
1509                ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1510                targeted.as_slice(),
1511            ),
1512            Ok(())
1513        );
1514
1515        let mut overlong = targeted.clone();
1516        overlong.push((DiagnosticFactTag::ListElement, 64));
1517        assert_eq!(
1518            validate_known_diagnostic_fact_schema(
1519                ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1520                overlong.as_slice(),
1521            ),
1522            Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded)
1523        );
1524
1525        let mut non_targeted_path = targeted;
1526        non_targeted_path[5].1 = DiagnosticConstraintKind::Unique.raw();
1527        assert_eq!(
1528            validate_known_diagnostic_fact_schema(
1529                ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1530                non_targeted_path.as_slice(),
1531            ),
1532            Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1533        );
1534    }
1535
1536    #[test]
1537    fn schema_enforces_global_ceiling_before_per_code_ceiling() {
1538        let facts = vec![(DiagnosticFactTag::ActualCount.raw(), 0); 81];
1539        assert_eq!(
1540            validate_raw_diagnostic_fact_schema(ErrorCode::QUERY_PLAN, facts.as_slice()),
1541            Err(DiagnosticFactSchemaMismatch::GlobalMaximumExceeded)
1542        );
1543    }
1544}