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