Skip to main content

xlog_cuda/
semantic_hypergraph.rs

1use std::fmt;
2use std::sync::atomic::{AtomicU64, Ordering};
3use std::sync::Arc;
4
5use sha2::{Digest, Sha256};
6use xlog_core::{symbol, RelId, ScalarType, Schema, XlogError};
7
8use crate::launch::LaunchEnqueueError;
9use crate::memory::TrackedCudaSlice;
10use crate::provider::resident_schedule::{validate_execution_domain, ResidentExecutionDomain};
11use crate::semantic_transition::Identity256;
12use crate::{
13    CudaFunction, CudaKernelProvider, CudaStream, DeviceRepr, DriverError, LaunchAsync,
14    LaunchConfig,
15};
16
17const MODULE: &str = "xlog_semantic_hypergraph";
18const KERNEL: &str = "semantic_hypergraph_execute";
19const COMMAND_WORDS: usize = 32;
20const RECEIPT_WORDS: usize = 42;
21// Version word 13 retains root-local insertion order across physical slot reuse.
22const HYPERGRAPH_ABI_GENERATION: u64 = 7;
23
24const CONTROL_WORDS: u64 = 16;
25const ROOT_WORDS: u64 = 16;
26const CANDIDATE_WORDS: u64 = 16;
27const STATEMENT_WORDS: u64 = 8;
28const SUPPORT_WORDS: u64 = 17;
29const VERSION_WORDS: u64 = 16;
30
31const OP_INITIALIZE: u64 = 1;
32const OP_FORK: u64 = 2;
33const OP_INSERT_SUPPORT: u64 = 3;
34const OP_DISCARD: u64 = 4;
35const OP_SEAL: u64 = 5;
36const OP_SNAPSHOT: u64 = 6;
37const OP_TRUTH: u64 = 7;
38const OP_INSPECT_STATEMENT: u64 = 8;
39const OP_INSPECT_SUPPORT: u64 = 9;
40const OP_INSPECT_VERSION: u64 = 10;
41#[cfg(test)]
42const OP_PREFLIGHT_TRANSITION: u64 = 11;
43
44const HOST_COMMAND_ADMISSION: u64 = 0;
45const RESIDENT_EMPTY_ROOT_HANDLE_ADMISSION: u64 = 1;
46const RESIDENT_FORK_ADMISSION: u64 = 2;
47const RESIDENT_INSERT_SUPPORT_ADMISSION: u64 = 3;
48const RESIDENT_FORK_TRUTH_ADMISSION: u64 = 4;
49const RESIDENT_SEAL_ADMISSION: u64 = 5;
50const RESIDENT_CONSUME_TRUTH_ADMISSION: u64 = 6;
51const RESIDENT_MATERIALIZE_DECODED_ADMISSION: u64 = 7;
52const RESIDENT_PREFLIGHT_TRANSITION_ADMISSION: u64 = 8;
53const RESIDENT_ROOT_TRUTH_ADMISSION: u64 = 9;
54
55const STATUS_OK: u64 = 0;
56const STATUS_FOREIGN_OWNER: u64 = 1;
57const STATUS_SLOT_OUT_OF_RANGE: u64 = 2;
58const STATUS_STALE_GENERATION: u64 = 3;
59const STATUS_INACTIVE: u64 = 4;
60const STATUS_NOT_REACHABLE: u64 = 5;
61const STATUS_STATEMENT_CAPACITY: u64 = 6;
62const STATUS_SUPPORT_CAPACITY: u64 = 7;
63const STATUS_VERSION_CAPACITY: u64 = 8;
64const STATUS_ROOT_CAPACITY: u64 = 9;
65const STATUS_GENERATION_EXHAUSTED: u64 = 10;
66const STATUS_CORRUPT_LINEAGE: u64 = 11;
67const STATUS_INVALID_COMMAND: u64 = 12;
68const STATUS_ARENA_MISMATCH: u64 = 13;
69
70const OUTCOME_INSERTED: u64 = 1;
71const OUTCOME_UNCHANGED: u64 = 2;
72const INSERT_NEW_STATEMENT: u64 = 1;
73const INSERT_PREVIOUS_TRUTH_SHIFT: u32 = 1;
74const INSERT_PREVIOUS_TRUTH_MASK: u64 = 3 << INSERT_PREVIOUS_TRUTH_SHIFT;
75
76static NEXT_OWNER_ID: AtomicU64 = AtomicU64::new(1);
77
78/// The four-valued semantic state derived from reachable support events.
79#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
80pub enum SemanticTruth {
81    /// Neither positive nor negative support is reachable.
82    Neither,
83    /// Positive support, and no negative support, is reachable.
84    True,
85    /// Negative support, and no positive support, is reachable.
86    False,
87    /// Both positive and negative support are reachable.
88    Both,
89}
90
91impl SemanticTruth {
92    /// Maps exact positive/negative presence bits to the four-valued state.
93    pub const fn from_presence(pro: bool, contra: bool) -> Self {
94        match (pro, contra) {
95            (false, false) => Self::Neither,
96            (true, false) => Self::True,
97            (false, true) => Self::False,
98            (true, true) => Self::Both,
99        }
100    }
101
102    /// Returns exact positive/negative presence bits.
103    pub const fn presence(self) -> (bool, bool) {
104        match self {
105            Self::Neither => (false, false),
106            Self::True => (true, false),
107            Self::False => (false, true),
108            Self::Both => (true, true),
109        }
110    }
111
112    pub(crate) fn from_bits(bits: u64) -> Result<Self, SemanticHypergraphError> {
113        match bits {
114            0 => Ok(Self::Neither),
115            1 => Ok(Self::True),
116            2 => Ok(Self::False),
117            3 => Ok(Self::Both),
118            _ => Err(SemanticHypergraphError::CorruptLineage {
119                detail: format!("device returned invalid semantic bits {bits}"),
120            }),
121        }
122    }
123}
124
125/// Polarity of one exact support event.
126#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
127pub enum SemanticPolarity {
128    /// Positive support.
129    Pro,
130    /// Negative support.
131    Contra,
132}
133
134impl SemanticPolarity {
135    const fn code(self) -> u64 {
136        match self {
137            Self::Pro => 1,
138            Self::Contra => 2,
139        }
140    }
141}
142
143/// One typed argument in a canonical statement identity.
144#[derive(Clone, Copy, Debug, Eq, PartialEq)]
145pub enum SemanticArgument {
146    U32(u32),
147    U64(u64),
148    I32(i32),
149    I64(i64),
150    F32Bits(u32),
151    F64Bits(u64),
152    Bool(bool),
153    Symbol(u32),
154}
155
156/// Schema-declared role of a retained typed record.
157#[derive(Clone, Copy, Debug, Eq, PartialEq)]
158pub enum SemanticRecordRole {
159    Statement,
160    Qualifier,
161    Provenance,
162    Source,
163    Context,
164    Scope,
165}
166
167impl SemanticRecordRole {
168    fn code(self) -> u8 {
169        match self {
170            Self::Statement => 1,
171            Self::Qualifier => 2,
172            Self::Provenance => 3,
173            Self::Source => 4,
174            Self::Context => 5,
175            Self::Scope => 6,
176        }
177    }
178}
179
180/// A predicate and its complete existing XLOG schema, not a caller-supplied digest.
181#[derive(Clone, Debug, Eq, PartialEq)]
182pub struct SemanticPredicateRecord {
183    pub predicate: RelId,
184    pub role: SemanticRecordRole,
185    pub schema: Schema,
186}
187
188/// Original typed content. Qualifier indices address this admission's records.
189///
190/// Only statements carry ordered identity-bearing qualifiers. The referenced
191/// records must have a qualifier schema; provenance is carried by support events.
192#[derive(Clone, Debug, Eq, PartialEq)]
193pub struct SemanticTypedRecord {
194    pub predicate: RelId,
195    pub arguments: Vec<SemanticArgument>,
196    pub qualifiers: Vec<u32>,
197}
198
199/// One unit of support and its complete, role-checked record references.
200#[derive(Clone, Debug, Eq, PartialEq)]
201pub struct SemanticSupportRecord {
202    pub statement: u32,
203    pub polarity: SemanticPolarity,
204    pub provenance: u32,
205    pub source: u32,
206    pub context: u32,
207    pub scope: u32,
208}
209
210/// Cold input transferred into the semantic owner's immutable admission.
211#[derive(Clone, Debug, Eq, PartialEq)]
212pub struct SemanticAdmissionRecords {
213    pub predicates: Vec<SemanticPredicateRecord>,
214    pub records: Vec<SemanticTypedRecord>,
215    pub supports: Vec<SemanticSupportRecord>,
216}
217
218/// Aggregate cold-input limits, checked before copying symbols or allocating views.
219#[derive(Clone, Copy, Debug)]
220pub struct SemanticAdmissionLimits {
221    /// Total predicate, typed-record and support-record count.
222    pub max_records: u32,
223    /// Total schema columns, schema key columns and typed arguments.
224    pub max_terms: u32,
225    /// Total qualifier and support-to-record references.
226    pub max_references: u32,
227    /// Total column names, sort labels and copied symbol text, including duplicates.
228    pub max_utf8_bytes: usize,
229}
230
231/// Complete immutable admission and the selected root's chronological insertions.
232/// Symbol arguments index `symbols` in occurrence order, never a process registry.
233#[derive(Clone, Debug, Eq, PartialEq)]
234pub(crate) struct SemanticRootMaterial {
235    pub(crate) records: SemanticAdmissionRecords,
236    pub(crate) symbols: Vec<String>,
237    pub(crate) insertions: Vec<SemanticRootInsertion>,
238    pub(crate) digest: [u8; 32],
239    pub(crate) extents: [u32; 3],
240    pub(crate) admission_base_digest: [u8; 32],
241    pub(crate) admission_base_extents: [u32; 3],
242}
243
244#[derive(Clone, Debug, Eq, PartialEq)]
245pub(crate) struct SemanticRootInsertion {
246    /// A genuine original occurrence, independently of equal reconstructed bytes.
247    pub(crate) statement: Option<u32>,
248    /// Derived predicate, four (record, argument) pairs, and qualifier owner.
249    /// The complete typed values remain in this material's immutable admission.
250    pub(crate) reconstruction: [u32; 10],
251    pub(crate) support: u32,
252    pub(crate) version: [u8; 32],
253}
254
255/// Bounded little-endian reader shared by native execution material payloads.
256pub(crate) struct SemanticMaterialReader<'a> {
257    remaining: &'a [u8],
258}
259
260impl<'a> SemanticMaterialReader<'a> {
261    pub(crate) fn new(bytes: &'a [u8]) -> Self {
262        Self { remaining: bytes }
263    }
264
265    pub(crate) fn take(&mut self, len: usize) -> Result<&'a [u8], SemanticHypergraphError> {
266        if len > self.remaining.len() {
267            return Err(admission_error("truncated execution material"));
268        }
269        let (value, rest) = self.remaining.split_at(len);
270        self.remaining = rest;
271        Ok(value)
272    }
273
274    pub(crate) fn u8(&mut self) -> Result<u8, SemanticHypergraphError> {
275        Ok(self.take(1)?[0])
276    }
277    pub(crate) fn u32(&mut self) -> Result<u32, SemanticHypergraphError> {
278        Ok(u32::from_le_bytes(self.take(4)?.try_into().unwrap()))
279    }
280    pub(crate) fn u64(&mut self) -> Result<u64, SemanticHypergraphError> {
281        Ok(u64::from_le_bytes(self.take(8)?.try_into().unwrap()))
282    }
283    pub(crate) fn count(
284        &mut self,
285        min_bytes_per_item: usize,
286    ) -> Result<usize, SemanticHypergraphError> {
287        let count = self.u32()? as usize;
288        if min_bytes_per_item == 0 || count > self.remaining.len() / min_bytes_per_item {
289            return Err(admission_error(
290                "execution material count exceeds remaining bytes",
291            ));
292        }
293        Ok(count)
294    }
295    pub(crate) fn bytes(&mut self) -> Result<&'a [u8], SemanticHypergraphError> {
296        let len = self.count(1)?;
297        self.take(len)
298    }
299    pub(crate) fn take_rest(&mut self) -> &'a [u8] {
300        let value = self.remaining;
301        self.remaining = &[];
302        value
303    }
304    pub(crate) fn finish(self) -> Result<(), SemanticHypergraphError> {
305        if self.remaining.is_empty() {
306            Ok(())
307        } else {
308            Err(admission_error("trailing execution material bytes"))
309        }
310    }
311}
312
313pub(crate) fn material_u32(output: &mut Vec<u8>, value: u32) {
314    output.extend_from_slice(&value.to_le_bytes());
315}
316pub(crate) fn material_u64(output: &mut Vec<u8>, value: u64) {
317    output.extend_from_slice(&value.to_le_bytes());
318}
319pub(crate) fn material_bytes(
320    output: &mut Vec<u8>,
321    bytes: &[u8],
322) -> Result<(), SemanticHypergraphError> {
323    material_count(output, bytes.len())?;
324    output.extend_from_slice(bytes);
325    Ok(())
326}
327fn material_count(output: &mut Vec<u8>, count: usize) -> Result<(), SemanticHypergraphError> {
328    material_u32(
329        output,
330        u32::try_from(count).map_err(|_| admission_error("material count exceeds u32"))?,
331    );
332    Ok(())
333}
334fn material_budget(remaining: &mut usize, count: usize) -> Result<(), SemanticHypergraphError> {
335    *remaining = remaining
336        .checked_sub(count)
337        .ok_or_else(|| admission_error("execution material exceeds admission bound"))?;
338    Ok(())
339}
340fn material_string(
341    reader: &mut SemanticMaterialReader<'_>,
342    budget: &mut usize,
343) -> Result<String, SemanticHypergraphError> {
344    let bytes = reader.bytes()?;
345    material_budget(budget, bytes.len())?;
346    Ok(std::str::from_utf8(bytes)
347        .map_err(|_| admission_error("execution material text is not UTF-8"))?
348        .to_owned())
349}
350
351impl SemanticRootMaterial {
352    fn validate_symbol_indices(&self) -> Result<(), SemanticHypergraphError> {
353        let mut occurrence = 0usize;
354        for record in &self.records.records {
355            for argument in &record.arguments {
356                if let SemanticArgument::Symbol(index) = argument {
357                    if *index as usize != occurrence || occurrence >= self.symbols.len() {
358                        return Err(admission_error(
359                            "material symbols are not in canonical occurrence order",
360                        ));
361                    }
362                    occurrence += 1;
363                }
364            }
365        }
366        if occurrence != self.symbols.len() {
367            return Err(admission_error(
368                "material contains unreferenced symbol text",
369            ));
370        }
371        Ok(())
372    }
373
374    /// Resolves retained text once for the existing cold typed-admission path.
375    pub(crate) fn admission_records(
376        &self,
377    ) -> Result<SemanticAdmissionRecords, SemanticHypergraphError> {
378        self.validate_symbol_indices()?;
379        let mut records = self.records.clone();
380        for record in &mut records.records {
381            for argument in &mut record.arguments {
382                if let SemanticArgument::Symbol(index) = argument {
383                    *index = symbol::intern(&self.symbols[*index as usize]);
384                }
385            }
386        }
387        Ok(records)
388    }
389
390    pub(crate) fn encode(&self) -> Result<Vec<u8>, SemanticHypergraphError> {
391        self.validate_symbol_indices()?;
392        let mut out = Self::encode_admission(&self.records, &self.symbols)?;
393        material_count(&mut out, self.insertions.len())?;
394        for insertion in &self.insertions {
395            if insertion.statement.is_some() && insertion.reconstruction != [0; 10] {
396                return Err(admission_error(
397                    "original material target has derived reconstruction references",
398                ));
399            }
400            out.push(u8::from(insertion.statement.is_some()));
401            if let Some(statement) = insertion.statement {
402                material_u32(&mut out, statement);
403            }
404            for reference in insertion.reconstruction {
405                material_u32(&mut out, reference);
406            }
407            material_u32(&mut out, insertion.support);
408            out.extend_from_slice(&insertion.version);
409        }
410        out.extend_from_slice(&self.digest);
411        for extent in self.extents {
412            material_u32(&mut out, extent);
413        }
414        out.extend_from_slice(&self.admission_base_digest);
415        for extent in self.admission_base_extents {
416            material_u32(&mut out, extent);
417        }
418        Ok(out)
419    }
420
421    fn encode_admission(
422        records: &SemanticAdmissionRecords,
423        symbols: &[String],
424    ) -> Result<Vec<u8>, SemanticHypergraphError> {
425        let mut out = b"XLOGROOT".to_vec();
426        material_u32(&mut out, 2);
427        material_count(&mut out, records.predicates.len())?;
428        for predicate in &records.predicates {
429            material_u32(&mut out, predicate.predicate.0);
430            out.push(predicate.role.code());
431            material_count(&mut out, predicate.schema.columns.len())?;
432            for ((name, scalar), label) in predicate
433                .schema
434                .columns
435                .iter()
436                .zip(predicate.schema.sort_labels())
437            {
438                material_bytes(&mut out, name.as_bytes())?;
439                out.push(scalar.to_code());
440                material_bytes(&mut out, label.as_bytes())?;
441            }
442            material_count(&mut out, predicate.schema.key_columns.len())?;
443            for &column in &predicate.schema.key_columns {
444                material_count(&mut out, column)?;
445            }
446        }
447        material_count(&mut out, records.records.len())?;
448        for record in &records.records {
449            material_u32(&mut out, record.predicate.0);
450            material_count(&mut out, record.arguments.len())?;
451            for argument in &record.arguments {
452                out.push(argument_type(*argument).to_code());
453                match argument {
454                    SemanticArgument::U32(value)
455                    | SemanticArgument::F32Bits(value)
456                    | SemanticArgument::Symbol(value) => material_u32(&mut out, *value),
457                    SemanticArgument::U64(value) | SemanticArgument::F64Bits(value) => {
458                        material_u64(&mut out, *value)
459                    }
460                    SemanticArgument::I32(value) => material_u32(&mut out, *value as u32),
461                    SemanticArgument::I64(value) => material_u64(&mut out, *value as u64),
462                    SemanticArgument::Bool(value) => out.push(u8::from(*value)),
463                }
464            }
465            material_count(&mut out, record.qualifiers.len())?;
466            for &qualifier in &record.qualifiers {
467                material_u32(&mut out, qualifier);
468            }
469        }
470        material_count(&mut out, records.supports.len())?;
471        for support in &records.supports {
472            material_u32(&mut out, support.statement);
473            out.push(support.polarity.code() as u8);
474            for value in [
475                support.provenance,
476                support.source,
477                support.context,
478                support.scope,
479            ] {
480                material_u32(&mut out, value);
481            }
482        }
483        material_count(&mut out, symbols.len())?;
484        for symbol in symbols {
485            material_bytes(&mut out, symbol.as_bytes())?;
486        }
487        Ok(out)
488    }
489
490    pub(crate) fn decode(
491        bytes: &[u8],
492        limits: SemanticAdmissionLimits,
493    ) -> Result<Self, SemanticHypergraphError> {
494        let mut reader = SemanticMaterialReader::new(bytes);
495        if reader.take(8)? != b"XLOGROOT" || reader.u32()? != 2 {
496            return Err(admission_error(
497                "unsupported semantic root material encoding",
498            ));
499        }
500        let mut record_budget = limits.max_records as usize;
501        let mut term_budget = limits.max_terms as usize;
502        let mut reference_budget = limits.max_references as usize;
503        let mut text_budget = limits.max_utf8_bytes;
504        let count = reader.count(13)?;
505        material_budget(&mut record_budget, count)?;
506        let mut predicates = Vec::with_capacity(count);
507        for _ in 0..count {
508            let predicate = RelId(reader.u32()?);
509            let role = match reader.u8()? {
510                1 => SemanticRecordRole::Statement,
511                2 => SemanticRecordRole::Qualifier,
512                3 => SemanticRecordRole::Provenance,
513                4 => SemanticRecordRole::Source,
514                5 => SemanticRecordRole::Context,
515                6 => SemanticRecordRole::Scope,
516                _ => return Err(admission_error("invalid material record role")),
517            };
518            let count = reader.count(9)?;
519            material_budget(&mut term_budget, count)?;
520            let mut columns = Vec::with_capacity(count);
521            let mut labels = Vec::with_capacity(count);
522            for _ in 0..count {
523                let name = material_string(&mut reader, &mut text_budget)?;
524                let scalar = ScalarType::from_code(reader.u8()?)
525                    .ok_or_else(|| admission_error("invalid material scalar code"))?;
526                let label = material_string(&mut reader, &mut text_budget)?;
527                if label.trim().is_empty() {
528                    return Err(admission_error("empty material sort label"));
529                }
530                columns.push((name, scalar));
531                labels.push(label);
532            }
533            let mut schema = Schema::new(columns)
534                .with_sort_labels(labels)
535                .map_err(admission_error)?;
536            let count = reader.count(4)?;
537            material_budget(&mut term_budget, count)?;
538            schema.key_columns.clear();
539            for _ in 0..count {
540                schema.key_columns.push(reader.u32()? as usize);
541            }
542            predicates.push(SemanticPredicateRecord {
543                predicate,
544                role,
545                schema,
546            });
547        }
548        let count = reader.count(12)?;
549        material_budget(&mut record_budget, count)?;
550        let mut records = Vec::with_capacity(count);
551        for _ in 0..count {
552            let predicate = RelId(reader.u32()?);
553            let count = reader.count(2)?;
554            material_budget(&mut term_budget, count)?;
555            let mut arguments = Vec::with_capacity(count);
556            for _ in 0..count {
557                let scalar = ScalarType::from_code(reader.u8()?)
558                    .ok_or_else(|| admission_error("invalid material scalar code"))?;
559                arguments.push(match scalar {
560                    ScalarType::U32 => SemanticArgument::U32(reader.u32()?),
561                    ScalarType::U64 => SemanticArgument::U64(reader.u64()?),
562                    ScalarType::I32 => SemanticArgument::I32(reader.u32()? as i32),
563                    ScalarType::I64 => SemanticArgument::I64(reader.u64()? as i64),
564                    ScalarType::F32 => SemanticArgument::F32Bits(reader.u32()?),
565                    ScalarType::F64 => SemanticArgument::F64Bits(reader.u64()?),
566                    ScalarType::Symbol => SemanticArgument::Symbol(reader.u32()?),
567                    ScalarType::Bool => SemanticArgument::Bool(match reader.u8()? {
568                        0 => false,
569                        1 => true,
570                        _ => return Err(admission_error("noncanonical material boolean")),
571                    }),
572                });
573            }
574            let count = reader.count(4)?;
575            material_budget(&mut reference_budget, count)?;
576            let mut qualifiers = Vec::with_capacity(count);
577            for _ in 0..count {
578                qualifiers.push(reader.u32()?);
579            }
580            records.push(SemanticTypedRecord {
581                predicate,
582                arguments,
583                qualifiers,
584            });
585        }
586        let count = reader.count(21)?;
587        material_budget(&mut record_budget, count)?;
588        material_budget(
589            &mut reference_budget,
590            count.checked_mul(5).ok_or_else(size_overflow)?,
591        )?;
592        let mut supports = Vec::with_capacity(count);
593        for _ in 0..count {
594            let statement = reader.u32()?;
595            let polarity = match reader.u8()? {
596                1 => SemanticPolarity::Pro,
597                2 => SemanticPolarity::Contra,
598                _ => return Err(admission_error("invalid material polarity")),
599            };
600            supports.push(SemanticSupportRecord {
601                statement,
602                polarity,
603                provenance: reader.u32()?,
604                source: reader.u32()?,
605                context: reader.u32()?,
606                scope: reader.u32()?,
607            });
608        }
609        let count = reader.count(4)?;
610        if count > limits.max_terms as usize {
611            return Err(admission_error(
612                "material symbol count exceeds admission bound",
613            ));
614        }
615        let mut symbols = Vec::with_capacity(count);
616        for _ in 0..count {
617            symbols.push(material_string(&mut reader, &mut text_budget)?);
618        }
619        let count = reader.count(77)?;
620        let mut insertions = Vec::with_capacity(count);
621        for _ in 0..count {
622            let statement = match reader.u8()? {
623                0 => None,
624                1 => Some(reader.u32()?),
625                _ => return Err(admission_error("invalid material target origin")),
626            };
627            let mut reconstruction = [0; 10];
628            for reference in &mut reconstruction {
629                *reference = reader.u32()?;
630            }
631            if statement.is_some() && reconstruction != [0; 10] {
632                return Err(admission_error(
633                    "original material target has derived reconstruction references",
634                ));
635            }
636            insertions.push(SemanticRootInsertion {
637                statement,
638                reconstruction,
639                support: reader.u32()?,
640                version: reader.take(32)?.try_into().unwrap(),
641            });
642        }
643        let digest = reader.take(32)?.try_into().unwrap();
644        let extents = [reader.u32()?, reader.u32()?, reader.u32()?];
645        let admission_base_digest = reader.take(32)?.try_into().unwrap();
646        let admission_base_extents = [reader.u32()?, reader.u32()?, reader.u32()?];
647        reader.finish()?;
648        let material = Self {
649            records: SemanticAdmissionRecords {
650                predicates,
651                records,
652                supports,
653            },
654            symbols,
655            insertions,
656            digest,
657            extents,
658            admission_base_digest,
659            admission_base_extents,
660        };
661        material.validate_symbol_indices()?;
662        Ok(material)
663    }
664}
665
666fn normalized_material_admission(
667    admission: &SemanticAdmission,
668) -> Result<(SemanticAdmissionRecords, Vec<String>), SemanticHypergraphError> {
669    let mut records = admission.records.clone();
670    let mut symbols = Vec::with_capacity(admission.symbols.entries().len());
671    for record in &mut records.records {
672        for argument in &mut record.arguments {
673            if let SemanticArgument::Symbol(index) = argument {
674                let (accepted, text) = admission
675                    .symbols
676                    .entries()
677                    .get(symbols.len())
678                    .ok_or_else(|| admission_error("retained admission symbol is missing"))?;
679                if *index != *accepted {
680                    return Err(admission_error("retained admission symbol order differs"));
681                }
682                *index = u32::try_from(symbols.len()).map_err(|_| size_overflow())?;
683                symbols.push(text.to_string());
684            }
685        }
686    }
687    if symbols.len() != admission.symbols.entries().len() {
688        return Err(admission_error("retained admission has excess symbols"));
689    }
690    Ok((records, symbols))
691}
692
693fn material_root_digest(previous: [u8; 32], version: [u8; 32], extents: [u32; 3]) -> [u8; 32] {
694    let mut bytes = [0u8; 108];
695    bytes[..22].copy_from_slice(b"xlog.semantic.root.v1\0");
696    bytes[32..64].copy_from_slice(&previous);
697    bytes[64..96].copy_from_slice(&version);
698    for (chunk, extent) in bytes[96..].as_chunks_mut::<4>().0.iter_mut().zip(extents) {
699        chunk.copy_from_slice(&extent.to_le_bytes());
700    }
701    Sha256::digest(bytes).into()
702}
703
704fn material_version_digest(previous: [u8; 32], support: [u8; 32], truth: u64) -> [u8; 32] {
705    let mut bytes = [0u8; 104];
706    bytes[..25].copy_from_slice(b"xlog.semantic.version.v1\0");
707    bytes[32..64].copy_from_slice(&previous);
708    bytes[64..96].copy_from_slice(&support);
709    bytes[96..].copy_from_slice(&truth.to_le_bytes());
710    Sha256::digest(bytes).into()
711}
712
713fn record_encoding_prefix(schema: Identity256, predicate: RelId, arity: usize) -> Vec<u8> {
714    let mut bytes = b"xlog.semantic.record.v2\0".to_vec();
715    bytes.extend_from_slice(schema.as_bytes());
716    bytes.extend_from_slice(&predicate.0.to_le_bytes());
717    bytes.extend_from_slice(&(arity as u32).to_le_bytes());
718    bytes
719}
720
721fn qualified_statement_identity(
722    atom: [u8; 32],
723    qualifiers: impl ExactSizeIterator<Item = [u8; 32]>,
724) -> SemanticStatementIdentity {
725    let mut hash = Sha256::new();
726    hash.update(b"xlog.semantic.statement.v2\0");
727    hash.update(atom);
728    hash.update((qualifiers.len() as u32).to_le_bytes());
729    for qualifier in qualifiers {
730        hash.update(qualifier);
731    }
732    SemanticStatementIdentity(hash.finalize().into())
733}
734
735/// Reconstructs a decoder-selected target from the same retained typed input.
736/// Source references select values only; they never create an original target.
737pub(crate) fn material_statement_key(
738    admission: &SemanticAdmission,
739    original: Option<u32>,
740    reconstruction: &[u32; 10],
741) -> Result<SemanticStatementKey, SemanticHypergraphError> {
742    if let Some(record) = original {
743        if *reconstruction != [0; 10] {
744            return Err(admission_error(
745                "original material target has derived reconstruction references",
746            ));
747        }
748        return admission.statement_key(record);
749    }
750    let predicates = &admission.records.predicates;
751    let target = predicates
752        .iter()
753        .find(|entry| entry.predicate.0 == reconstruction[0])
754        .ok_or_else(|| {
755            admission_error("derived target predicate is outside the retained admission")
756        })?;
757    let arity = target.schema.arity();
758    if target.role != SemanticRecordRole::Statement || arity > 4 {
759        return Err(admission_error(
760            "derived target requires an admitted statement schema of at most four arguments",
761        ));
762    }
763    let mut bytes = record_encoding_prefix(admission.schema_generation, target.predicate, arity);
764    for argument in 0..4 {
765        let record_index = reconstruction[1 + 2 * argument] as usize;
766        let argument_index = reconstruction[2 + 2 * argument] as usize;
767        if argument >= arity {
768            if record_index != 0 || argument_index != 0 {
769                return Err(admission_error(
770                    "derived target has nonzero inactive argument references",
771                ));
772            }
773            continue;
774        }
775        let record = admission.records.records.get(record_index).ok_or_else(|| {
776            admission_error("derived argument record is outside the retained admission")
777        })?;
778        let source = predicates
779            .iter()
780            .find(|entry| entry.predicate == record.predicate)
781            .ok_or_else(|| {
782                admission_error("derived argument predicate is outside the retained admission")
783            })?;
784        let column = source
785            .schema
786            .columns
787            .get(argument_index)
788            .ok_or_else(|| admission_error("derived argument is outside its source schema"))?;
789        if column.1 != target.schema.columns[argument].1
790            || source.schema.sort_labels().get(argument_index)
791                != target.schema.sort_labels().get(argument)
792        {
793            return Err(admission_error(
794                "derived argument type or sort differs from its target schema",
795            ));
796        }
797        let encoding = &admission.encoded_records[record_index];
798        let span = encoding.arguments.get(argument_index).ok_or_else(|| {
799            admission_error("derived argument lacks its retained canonical bytes")
800        })?;
801        bytes.extend_from_slice(&encoding.bytes[span.clone()]);
802    }
803    let qualifiers = if reconstruction[9] == u32::MAX {
804        &[][..]
805    } else {
806        admission.statement_key(reconstruction[9])?;
807        &admission.records.records[reconstruction[9] as usize].qualifiers
808    };
809    let identity = qualified_statement_identity(
810        Sha256::digest(bytes).into(),
811        qualifiers
812            .iter()
813            .map(|&index| Sha256::digest(&admission.encoded_records[index as usize].bytes).into()),
814    );
815    Ok(SemanticStatementKey {
816        identity,
817        owner: admission.base.owner,
818        record: u32::MAX,
819    })
820}
821
822/// Packs an already-validated insertion identically for original and restored
823/// targets. Runtime ownership and the typed reconstruction are checked by callers.
824fn encode_support_insertion(
825    command: &mut DeviceCommand,
826    fork: SemanticForkHandle,
827    statement: &SemanticStatementKey,
828    event: &SemanticSupportEvent,
829    reconstruction: &[u32; 10],
830) {
831    command.words[0] = OP_INSERT_SUPPORT;
832    command.words[1] = fork.owner;
833    command.words[8] = u64::from(fork.slot);
834    command.words[9] = fork.generation;
835    command.words[12] = event.polarity.code();
836    command.words[13] = u64::from(statement.record);
837    command.words[14] = u64::from(event.record);
838    command.words[16..20].copy_from_slice(&identity_words(statement.identity.0));
839    command.words[20..24].copy_from_slice(&identity_words(event.identity(statement.identity).0));
840    for word in 0..5 {
841        command.words[24 + word] =
842            u64::from(reconstruction[2 * word]) | (u64::from(reconstruction[2 * word + 1]) << 32);
843    }
844}
845
846impl SemanticRootMaterial {
847    /// Project only contributors to the selected heads from this complete,
848    /// owner-validated insertion history. Value equality selects a head, never
849    /// replaces an insertion's original target or support occurrence.
850    pub(crate) fn task_observation_roots(
851        &self,
852        admission: &SemanticAdmission,
853        query_records: [u32; 3],
854    ) -> Result<crate::semantic_transition::SemanticTaskObservationRoots, SemanticHypergraphError>
855    {
856        self.validate_lineage(admission)?;
857        let queries = query_records.map(|record| admission.statement_key(record));
858        let mut contributors = Vec::new();
859        for (ordinal, query) in queries.into_iter().enumerate() {
860            let query = query?;
861            for insertion in &self.insertions {
862                let key = material_statement_key(
863                    admission,
864                    insertion.statement,
865                    &insertion.reconstruction,
866                )?;
867                if key.identity == query.identity {
868                    contributors.push((ordinal as u32, insertion.statement, insertion.support));
869                }
870            }
871        }
872        Ok(crate::semantic_transition::SemanticTaskObservationRoots {
873            root_digest: crate::semantic_transition::Identity256::from_bytes(self.digest),
874            root_extents: self.extents,
875            query_records,
876            contributors,
877        })
878    }
879
880    fn validate_lineage(
881        &self,
882        admission: &SemanticAdmission,
883    ) -> Result<(), SemanticHypergraphError> {
884        let (records, symbols) = normalized_material_admission(admission)?;
885        if self.records != records || self.symbols != symbols {
886            return Err(admission_error(
887                "root material differs from the owner's complete typed admission",
888            ));
889        }
890        let mut heads = std::collections::BTreeMap::<[u8; 32], ([u8; 32], u64)>::new();
891        let mut supports = std::collections::BTreeSet::new();
892        let mut digest = material_root_digest([0; 32], [0; 32], [0; 3]);
893        let mut extents = [0u32; 3];
894        let mut found_base =
895            self.admission_base_extents == extents && self.admission_base_digest == digest;
896        for insertion in &self.insertions {
897            let key =
898                material_statement_key(admission, insertion.statement, &insertion.reconstruction)?;
899            let event = admission.support_event(insertion.support)?;
900            let support = event.identity(key.identity).0;
901            if !supports.insert((key.identity.0, support)) {
902                return Err(admission_error(
903                    "material repeats an unchanged support insertion",
904                ));
905            }
906            let previous = heads.get(&key.identity.0).copied().unwrap_or(([0; 32], 0));
907            let truth = previous.1 | event.polarity.code();
908            let version = material_version_digest(previous.0, support, truth);
909            if version != insertion.version {
910                return Err(admission_error(
911                    "material version identity does not match typed insertion history",
912                ));
913            }
914            heads.insert(key.identity.0, (version, truth));
915            extents = [
916                u32::try_from(heads.len()).map_err(|_| size_overflow())?,
917                extents[1].checked_add(1).ok_or_else(size_overflow)?,
918                extents[2].checked_add(1).ok_or_else(size_overflow)?,
919            ];
920            digest = material_root_digest(digest, version, extents);
921            if extents == self.admission_base_extents && digest == self.admission_base_digest {
922                found_base = true;
923            }
924        }
925        if digest != self.digest || extents != self.extents || !found_base {
926            return Err(admission_error(
927                "material root or original admission base differs from insertion history",
928            ));
929        }
930        Ok(())
931    }
932}
933
934/// Extracts only a sealed root's generation-valid reachable records. The transient
935/// arena copy is not itself material: candidate state and other roots never escape.
936fn material_from_arena(
937    arena: &[u64],
938    capacities: SemanticHypergraphCapacities,
939    root: SemanticRootHandle,
940    snapshot: SemanticRootSnapshot,
941    admission: &SemanticAdmission,
942) -> Result<SemanticRootMaterial, SemanticHypergraphError> {
943    let corrupt = || SemanticHypergraphError::CorruptLineage {
944        detail: "root material contains invalid native reachability or generation".into(),
945    };
946    if arena.len() as u64 != checked_arena_words(capacities)? || root.slot >= capacities.roots {
947        return Err(corrupt());
948    }
949    let statements = CONTROL_WORDS as usize
950        + capacities.roots as usize * ROOT_WORDS as usize
951        + CANDIDATE_WORDS as usize;
952    let supports = statements + capacities.statements as usize * STATEMENT_WORDS as usize;
953    let versions = supports + capacities.supports as usize * SUPPORT_WORDS as usize;
954    let heads = versions
955        + capacities.versions as usize * VERSION_WORDS as usize
956        + root.slot as usize * capacities.statements as usize;
957    let root_offset = CONTROL_WORDS as usize + root.slot as usize * ROOT_WORDS as usize;
958    let native_root = &arena[root_offset..root_offset + ROOT_WORDS as usize];
959    if arena[1] != root.owner
960        || native_root[0] != 3
961        || native_root[1] != root.generation
962        || root.generation == 0
963    {
964        return Err(corrupt());
965    }
966    let identity = |words: &[u64]| -> [u8; 32] {
967        let mut bytes = [0; 32];
968        for (chunk, word) in bytes.as_chunks_mut::<8>().0.iter_mut().zip(words) {
969            chunk.copy_from_slice(&word.to_le_bytes());
970        }
971        bytes
972    };
973    if identity(&native_root[6..10]) != snapshot.digest.0
974        || native_root[3..6]
975            != [
976                u64::from(snapshot.extents.statements),
977                u64::from(snapshot.extents.supports),
978                u64::from(snapshot.extents.versions),
979            ]
980    {
981        return Err(corrupt());
982    }
983    let mut seen_versions = std::collections::BTreeSet::new();
984    let mut seen_supports = std::collections::BTreeSet::new();
985    let mut ordered = Vec::new();
986    let mut statement_count = 0u32;
987    for statement_slot in 0..capacities.statements as usize {
988        let mut encoded = arena[heads + statement_slot];
989        if encoded == 0 {
990            continue;
991        }
992        statement_count += 1;
993        let offset = statements + statement_slot * STATEMENT_WORDS as usize;
994        let statement = &arena[offset..offset + STATEMENT_WORDS as usize];
995        if statement[0] != 3 || statement[1] == 0 {
996            return Err(corrupt());
997        }
998        let statement_identity = identity(&statement[3..7]);
999        let mut newer_ordinal = u64::MAX;
1000        while encoded != 0 {
1001            let version_slot = usize::try_from(encoded - 1).map_err(|_| corrupt())?;
1002            if version_slot >= capacities.versions as usize || !seen_versions.insert(version_slot) {
1003                return Err(corrupt());
1004            }
1005            let offset = versions + version_slot * VERSION_WORDS as usize;
1006            let version = &arena[offset..offset + VERSION_WORDS as usize];
1007            if version[0] != 3
1008                || version[1] == 0
1009                || version[3] != statement_slot as u64
1010                || version[4] != statement[1]
1011                || version[13] == 0
1012                || version[13] >= newer_ordinal
1013            {
1014                return Err(corrupt());
1015            }
1016            newer_ordinal = version[13];
1017            let support_slot = usize::try_from(version[5]).map_err(|_| corrupt())?;
1018            if support_slot >= capacities.supports as usize || !seen_supports.insert(support_slot) {
1019                return Err(corrupt());
1020            }
1021            let offset = supports + support_slot * SUPPORT_WORDS as usize;
1022            let support = &arena[offset..offset + SUPPORT_WORDS as usize];
1023            if support[0] != 3
1024                || support[1] == 0
1025                || support[1] != version[6]
1026                || support[3] != statement_slot as u64
1027                || support[4] != statement[1]
1028                || !matches!(support[5], 1 | 2)
1029            {
1030                return Err(corrupt());
1031            }
1032            let support_identity = identity(&support[6..10]);
1033            let statement_index = u32::try_from(support[10]).map_err(|_| corrupt())?;
1034            let statement_index = (statement_index != u32::MAX).then_some(statement_index);
1035            let support_index = u32::try_from(support[11]).map_err(|_| corrupt())?;
1036            let reconstruction =
1037                std::array::from_fn(|word| (support[12 + word / 2] >> (32 * (word % 2))) as u32);
1038            let key = material_statement_key(admission, statement_index, &reconstruction)?;
1039            let event = admission.support_event(support_index)?;
1040            if key.identity.0 != statement_identity
1041                || event.polarity.code() != support[5]
1042                || event.identity(key.identity).0 != support_identity
1043            {
1044                return Err(admission_error(
1045                    "reachable insertion differs from its original typed occurrences",
1046                ));
1047            }
1048            let (previous, previous_truth) = if version[7] == 0 {
1049                ([0; 32], 0)
1050            } else {
1051                let slot = usize::try_from(version[7] - 1).map_err(|_| corrupt())?;
1052                if slot >= capacities.versions as usize {
1053                    return Err(corrupt());
1054                }
1055                let offset = versions + slot * VERSION_WORDS as usize;
1056                (identity(&arena[offset + 9..offset + 13]), arena[offset + 8])
1057            };
1058            let digest = identity(&version[9..13]);
1059            if previous_truth > 3
1060                || version[8] != previous_truth | support[5]
1061                || digest != material_version_digest(previous, support_identity, version[8])
1062            {
1063                return Err(corrupt());
1064            }
1065            ordered.push((
1066                version[13],
1067                SemanticRootInsertion {
1068                    statement: statement_index,
1069                    reconstruction,
1070                    support: support_index,
1071                    version: digest,
1072                },
1073            ));
1074            encoded = version[7];
1075        }
1076    }
1077    ordered.sort_unstable_by_key(|(ordinal, _)| *ordinal);
1078    if ordered
1079        .iter()
1080        .enumerate()
1081        .any(|(index, (ordinal, _))| *ordinal != index as u64 + 1)
1082        || statement_count != snapshot.extents.statements
1083        || seen_supports.len() != snapshot.extents.supports as usize
1084        || seen_versions.len() != snapshot.extents.versions as usize
1085    {
1086        return Err(corrupt());
1087    }
1088    let (records, symbols) = normalized_material_admission(admission)?;
1089    let material = SemanticRootMaterial {
1090        records,
1091        symbols,
1092        insertions: ordered
1093            .into_iter()
1094            .map(|(_, insertion)| insertion)
1095            .collect(),
1096        digest: snapshot.digest.0,
1097        extents: [
1098            snapshot.extents.statements,
1099            snapshot.extents.supports,
1100            snapshot.extents.versions,
1101        ],
1102        admission_base_digest: admission.base_snapshot.digest.0,
1103        admission_base_extents: [
1104            admission.base_snapshot.extents.statements,
1105            admission.base_snapshot.extents.supports,
1106            admission.base_snapshot.extents.versions,
1107        ],
1108    };
1109    material.validate_lineage(admission)?;
1110    Ok(material)
1111}
1112
1113/// Immutable typed content and identities held by one graph for one acquired base.
1114///
1115/// Registry IDs are diagnostic references only. Consumers use `symbols()` for
1116/// accepted meanings and must not resolve them again through the live registry.
1117/// Admission validates general semantic records; it does not qualify targets for
1118/// an action catalogue's operand limits or its completion-aware draw mask.
1119pub struct SemanticAdmission {
1120    records: SemanticAdmissionRecords,
1121    symbols: symbol::SymbolSnapshot,
1122    schema_bytes: Vec<u8>,
1123    schema_generation: Identity256,
1124    identity: Identity256,
1125    base: SemanticRootHandle,
1126    base_snapshot: SemanticRootSnapshot,
1127    statement_keys: Vec<Option<SemanticStatementKey>>,
1128    support_events: Vec<SemanticSupportEvent>,
1129    pub(crate) encoded_records: Vec<SemanticRecordEncoding>,
1130}
1131
1132/// The exact admitted atom preimage and its typed argument ranges. Derived device
1133/// views borrow these bytes; symbols are never looked up a second time.
1134pub(crate) struct SemanticRecordEncoding {
1135    pub bytes: Vec<u8>,
1136    pub arguments: Vec<std::ops::Range<usize>>,
1137}
1138
1139impl SemanticAdmission {
1140    /// Typed record and symbol-content encoding, distinct from the kernel launch ABI.
1141    pub const fn encoding_generation(&self) -> u32 {
1142        2
1143    }
1144    pub fn records(&self) -> &SemanticAdmissionRecords {
1145        &self.records
1146    }
1147    pub fn symbols(&self) -> &symbol::SymbolSnapshot {
1148        &self.symbols
1149    }
1150    pub const fn schema_generation(&self) -> Identity256 {
1151        self.schema_generation
1152    }
1153    /// Exact canonical preimage of the admitted schema generation.
1154    pub fn schema_bytes(&self) -> &[u8] {
1155        &self.schema_bytes
1156    }
1157    pub const fn identity(&self) -> Identity256 {
1158        self.identity
1159    }
1160    pub const fn base(&self) -> SemanticRootHandle {
1161        self.base
1162    }
1163    pub const fn base_snapshot(&self) -> &SemanticRootSnapshot {
1164        &self.base_snapshot
1165    }
1166
1167    /// Selects a validated statement record; metadata records cannot become statements.
1168    pub fn statement_key(
1169        &self,
1170        record: u32,
1171    ) -> Result<SemanticStatementKey, SemanticHypergraphError> {
1172        self.statement_keys
1173            .get(record as usize)
1174            .copied()
1175            .flatten()
1176            .ok_or_else(|| admission_error("record is not an admitted statement"))
1177    }
1178
1179    /// Selects owner-bound support metadata whose source references were checked together.
1180    /// The original statement link remains in `records()`; insertion derives a
1181    /// support identity for the selected admitted target statement.
1182    pub fn support_event(
1183        &self,
1184        index: u32,
1185    ) -> Result<SemanticSupportEvent, SemanticHypergraphError> {
1186        self.support_events
1187            .get(index as usize)
1188            .copied()
1189            .ok_or_else(|| admission_error("support event is outside the admitted records"))
1190    }
1191}
1192
1193fn admission_error(detail: impl Into<String>) -> SemanticHypergraphError {
1194    SemanticHypergraphError::InvalidInput {
1195        detail: detail.into(),
1196    }
1197}
1198
1199fn consume_admission_bound(
1200    remaining: &mut usize,
1201    count: usize,
1202    name: &str,
1203) -> Result<(), SemanticHypergraphError> {
1204    *remaining = remaining
1205        .checked_sub(count)
1206        .ok_or_else(|| admission_error(format!("semantic admission exceeds {name} bound")))?;
1207    Ok(())
1208}
1209
1210fn argument_type(argument: SemanticArgument) -> ScalarType {
1211    match argument {
1212        SemanticArgument::U32(_) => ScalarType::U32,
1213        SemanticArgument::U64(_) => ScalarType::U64,
1214        SemanticArgument::I32(_) => ScalarType::I32,
1215        SemanticArgument::I64(_) => ScalarType::I64,
1216        SemanticArgument::F32Bits(_) => ScalarType::F32,
1217        SemanticArgument::F64Bits(_) => ScalarType::F64,
1218        SemanticArgument::Bool(_) => ScalarType::Bool,
1219        SemanticArgument::Symbol(_) => ScalarType::Symbol,
1220    }
1221}
1222
1223// One private admission path. It owns the input, validates the complete closure
1224// before symbol copying, and derives all identities from that retained content.
1225fn admit_semantic_records(
1226    records: SemanticAdmissionRecords,
1227    limits: SemanticAdmissionLimits,
1228    base: SemanticRootHandle,
1229    observe_base: impl FnOnce() -> Result<SemanticRootSnapshot, SemanticHypergraphError>,
1230) -> Result<SemanticAdmission, SemanticHypergraphError> {
1231    let mut record_budget = limits.max_records as usize;
1232    for count in [
1233        records.predicates.len(),
1234        records.records.len(),
1235        records.supports.len(),
1236    ] {
1237        consume_admission_bound(&mut record_budget, count, "record count")?;
1238    }
1239    let mut term_budget = limits.max_terms as usize;
1240    let mut reference_budget = limits.max_references as usize;
1241    let mut byte_budget = limits.max_utf8_bytes;
1242    for predicate in &records.predicates {
1243        let schema = &predicate.schema;
1244        consume_admission_bound(&mut term_budget, schema.columns.len(), "term count")?;
1245        consume_admission_bound(&mut term_budget, schema.key_columns.len(), "term count")?;
1246        if !schema.has_authoritative_sort_labels() {
1247            return Err(admission_error("schema has missing or invalid sort labels"));
1248        }
1249        for (name, _) in &schema.columns {
1250            consume_admission_bound(&mut byte_budget, name.len(), "UTF-8 bytes")?;
1251        }
1252        for label in schema.sort_labels() {
1253            consume_admission_bound(&mut byte_budget, label.len(), "UTF-8 bytes")?;
1254        }
1255        let mut keys = std::collections::BTreeSet::new();
1256        for &key in &schema.key_columns {
1257            if key >= schema.arity() || !keys.insert(key) {
1258                return Err(admission_error(
1259                    "schema key column is out of range or repeated",
1260                ));
1261            }
1262        }
1263    }
1264    for record in &records.records {
1265        consume_admission_bound(&mut term_budget, record.arguments.len(), "term count")?;
1266        consume_admission_bound(
1267            &mut reference_budget,
1268            record.qualifiers.len(),
1269            "reference count",
1270        )?;
1271    }
1272    for _ in &records.supports {
1273        consume_admission_bound(&mut reference_budget, 5, "reference count")?;
1274    }
1275    let mut predicates = std::collections::BTreeMap::new();
1276    for predicate in &records.predicates {
1277        if predicates
1278            .insert(predicate.predicate.0, predicate)
1279            .is_some()
1280        {
1281            return Err(admission_error("predicate schema is repeated"));
1282        }
1283    }
1284    let mut roles = Vec::with_capacity(records.records.len());
1285    let mut symbol_ids = Vec::new();
1286    for record in &records.records {
1287        let predicate = predicates
1288            .get(&record.predicate.0)
1289            .ok_or_else(|| admission_error("typed record references an unknown predicate"))?;
1290        if record.arguments.len() != predicate.schema.arity() {
1291            return Err(admission_error(
1292                "typed record arity differs from its predicate schema",
1293            ));
1294        }
1295        for (argument, (_, expected)) in record.arguments.iter().zip(&predicate.schema.columns) {
1296            if argument_type(*argument) != *expected {
1297                return Err(admission_error(
1298                    "typed argument differs from its schema column type",
1299                ));
1300            }
1301            if let SemanticArgument::Symbol(id) = argument {
1302                symbol_ids.push(*id);
1303            }
1304        }
1305        if predicate.role != SemanticRecordRole::Statement && !record.qualifiers.is_empty() {
1306            return Err(admission_error(
1307                "only statements may carry identity-bearing qualifiers",
1308            ));
1309        }
1310        roles.push(predicate.role);
1311    }
1312    let require_role = |index: u32, role| {
1313        if roles.get(index as usize) == Some(&role) {
1314            Ok(())
1315        } else {
1316            Err(admission_error(format!(
1317                "record reference {index} is not an admitted {role:?}"
1318            )))
1319        }
1320    };
1321    for record in &records.records {
1322        for &index in &record.qualifiers {
1323            require_role(index, SemanticRecordRole::Qualifier)?;
1324        }
1325    }
1326    for support in &records.supports {
1327        for (index, role) in [
1328            (support.statement, SemanticRecordRole::Statement),
1329            (support.provenance, SemanticRecordRole::Provenance),
1330            (support.source, SemanticRecordRole::Source),
1331            (support.context, SemanticRecordRole::Context),
1332            (support.scope, SemanticRecordRole::Scope),
1333        ] {
1334            require_role(index, role)?;
1335        }
1336    }
1337    let symbols = symbol::snapshot_checked(&symbol_ids, limits.max_terms as usize, byte_budget)
1338        .map_err(|error| admission_error(error.to_string()))?;
1339    let mut schema_bytes = Vec::new();
1340    schema_bytes.extend_from_slice(b"xlog.semantic.schema.v2\0");
1341    schema_bytes.extend_from_slice(&(predicates.len() as u32).to_le_bytes());
1342    for predicate in predicates.values() {
1343        schema_bytes.extend_from_slice(&predicate.predicate.0.to_le_bytes());
1344        schema_bytes.push(predicate.role.code());
1345        schema_bytes.extend_from_slice(&(predicate.schema.arity() as u32).to_le_bytes());
1346        for (name, ty) in &predicate.schema.columns {
1347            schema_bytes.extend_from_slice(&(name.len() as u64).to_le_bytes());
1348            schema_bytes.extend_from_slice(name.as_bytes());
1349            schema_bytes.push(ty.to_code());
1350        }
1351        schema_bytes.extend_from_slice(&(predicate.schema.key_columns.len() as u32).to_le_bytes());
1352        for &key in &predicate.schema.key_columns {
1353            schema_bytes.extend_from_slice(&(key as u32).to_le_bytes());
1354        }
1355        for label in predicate.schema.sort_labels() {
1356            schema_bytes.extend_from_slice(&(label.len() as u64).to_le_bytes());
1357            schema_bytes.extend_from_slice(label.as_bytes());
1358        }
1359    }
1360    let schema_generation = Identity256::from_bytes(Sha256::digest(&schema_bytes).into());
1361    let mut symbol_text = symbols.entries().iter();
1362    let mut atoms = Vec::<[u8; 32]>::with_capacity(records.records.len());
1363    let mut encoded_records = Vec::with_capacity(records.records.len());
1364    for record in &records.records {
1365        let mut bytes =
1366            record_encoding_prefix(schema_generation, record.predicate, record.arguments.len());
1367        let mut arguments = Vec::with_capacity(record.arguments.len());
1368        for &argument in &record.arguments {
1369            let start = bytes.len();
1370            bytes.push(argument_type(argument).to_code() + 1);
1371            match argument {
1372                SemanticArgument::U32(value) | SemanticArgument::F32Bits(value) => {
1373                    bytes.extend_from_slice(&value.to_le_bytes())
1374                }
1375                SemanticArgument::U64(value) | SemanticArgument::F64Bits(value) => {
1376                    bytes.extend_from_slice(&value.to_le_bytes())
1377                }
1378                SemanticArgument::I32(value) => bytes.extend_from_slice(&value.to_le_bytes()),
1379                SemanticArgument::I64(value) => bytes.extend_from_slice(&value.to_le_bytes()),
1380                SemanticArgument::Bool(value) => bytes.push(u8::from(value)),
1381                SemanticArgument::Symbol(id) => {
1382                    let (accepted_id, text) = symbol_text
1383                        .next()
1384                        .expect("validated complete symbol snapshot");
1385                    debug_assert_eq!(*accepted_id, id);
1386                    // Encoding two binds UTF-8 content, not a process-local ID.
1387                    bytes.extend_from_slice(&(text.len() as u64).to_le_bytes());
1388                    bytes.extend_from_slice(text.as_bytes());
1389                }
1390            }
1391            arguments.push(start..bytes.len());
1392        }
1393        atoms.push(Sha256::digest(&bytes).into());
1394        encoded_records.push(SemanticRecordEncoding { bytes, arguments });
1395    }
1396    let statement_keys: Vec<_> = records
1397        .records
1398        .iter()
1399        .enumerate()
1400        .map(|(index, record)| {
1401            if roles[index] != SemanticRecordRole::Statement {
1402                return None;
1403            }
1404            Some(SemanticStatementKey {
1405                identity: qualified_statement_identity(
1406                    atoms[index],
1407                    record
1408                        .qualifiers
1409                        .iter()
1410                        .map(|&qualifier| atoms[qualifier as usize]),
1411                ),
1412                owner: base.owner,
1413                record: index as u32,
1414            })
1415        })
1416        .collect();
1417    let support_events: Vec<_> = records
1418        .supports
1419        .iter()
1420        .enumerate()
1421        .map(|(index, record)| SemanticSupportEvent {
1422            owner: base.owner,
1423            record: index as u32,
1424            polarity: record.polarity,
1425            provenance: Identity256::from_bytes(atoms[record.provenance as usize]),
1426            source: Identity256::from_bytes(atoms[record.source as usize]),
1427            context: Identity256::from_bytes(atoms[record.context as usize]),
1428            scope: Identity256::from_bytes(atoms[record.scope as usize]),
1429        })
1430        .collect();
1431    let base_snapshot = observe_base()?;
1432    let identity = derive_admission_identity(
1433        &records,
1434        schema_generation,
1435        &encoded_records,
1436        &statement_keys,
1437        &support_events,
1438        &base_snapshot,
1439    );
1440    Ok(SemanticAdmission {
1441        records,
1442        symbols,
1443        schema_bytes,
1444        schema_generation,
1445        identity,
1446        base,
1447        base_snapshot,
1448        statement_keys,
1449        support_events,
1450        encoded_records,
1451    })
1452}
1453
1454fn derive_admission_identity(
1455    records: &SemanticAdmissionRecords,
1456    schema_generation: Identity256,
1457    encoded_records: &[SemanticRecordEncoding],
1458    statement_keys: &[Option<SemanticStatementKey>],
1459    support_events: &[SemanticSupportEvent],
1460    base_snapshot: &SemanticRootSnapshot,
1461) -> Identity256 {
1462    let mut hash = Sha256::new();
1463    hash.update(b"xlog.semantic.admission.v2\0");
1464    hash.update(base_snapshot.canonical_bytes());
1465    hash.update(schema_generation.as_bytes());
1466    for predicate in &records.predicates {
1467        hash.update(predicate.predicate.0.to_le_bytes());
1468    }
1469    hash.update((encoded_records.len() as u32).to_le_bytes());
1470    for ((encoded, key), record) in encoded_records
1471        .iter()
1472        .zip(statement_keys)
1473        .zip(&records.records)
1474    {
1475        hash.update(Sha256::digest(&encoded.bytes));
1476        hash.update((record.qualifiers.len() as u32).to_le_bytes());
1477        for &reference in &record.qualifiers {
1478            hash.update(reference.to_le_bytes());
1479        }
1480        hash.update([u8::from(key.is_some())]);
1481        if let Some(key) = key {
1482            hash.update(key.identity.as_bytes());
1483        }
1484    }
1485    let symbol_ids: Vec<_> = records
1486        .records
1487        .iter()
1488        .flat_map(|record| &record.arguments)
1489        .filter_map(|argument| match argument {
1490            SemanticArgument::Symbol(id) => Some(*id),
1491            _ => None,
1492        })
1493        .collect();
1494    hash.update((symbol_ids.len() as u32).to_le_bytes());
1495    for id in symbol_ids {
1496        hash.update(id.to_le_bytes());
1497    }
1498    hash.update((support_events.len() as u32).to_le_bytes());
1499    for (event, record) in support_events.iter().zip(&records.supports) {
1500        for index in [
1501            record.statement,
1502            record.provenance,
1503            record.source,
1504            record.context,
1505            record.scope,
1506        ] {
1507            hash.update(index.to_le_bytes());
1508        }
1509        let source_statement = statement_keys[record.statement as usize]
1510            .expect("validated source statement reference")
1511            .identity;
1512        hash.update(event.identity(source_statement).as_bytes());
1513    }
1514    Identity256::from_bytes(hash.finalize().into())
1515}
1516
1517/// Content identity of a semantic statement.
1518#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1519pub struct SemanticStatementIdentity([u8; 32]);
1520
1521impl SemanticStatementIdentity {
1522    /// Returns the canonical digest bytes.
1523    pub const fn as_bytes(&self) -> &[u8; 32] {
1524        &self.0
1525    }
1526}
1527
1528/// Content identity of one exact support event.
1529#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1530pub struct SemanticSupportIdentity([u8; 32]);
1531
1532impl SemanticSupportIdentity {
1533    /// Returns the canonical digest bytes.
1534    pub const fn as_bytes(&self) -> &[u8; 32] {
1535        &self.0
1536    }
1537}
1538
1539/// Content identity of an immutable statement version.
1540#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1541pub struct SemanticVersionIdentity([u8; 32]);
1542
1543impl SemanticVersionIdentity {
1544    /// Returns the canonical digest bytes.
1545    pub const fn as_bytes(&self) -> &[u8; 32] {
1546        &self.0
1547    }
1548}
1549
1550/// Content digest of an immutable semantic root.
1551#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1552pub struct SemanticRootDigest([u8; 32]);
1553
1554impl SemanticRootDigest {
1555    /// Returns the canonical digest bytes.
1556    pub const fn as_bytes(&self) -> &[u8; 32] {
1557        &self.0
1558    }
1559}
1560
1561/// Canonical, identity-bearing statement key.
1562#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1563pub struct SemanticStatementKey {
1564    identity: SemanticStatementIdentity,
1565    owner: u64,
1566    record: u32,
1567}
1568
1569impl SemanticStatementKey {
1570    /// Returns the derived statement identity.
1571    pub const fn identity(&self) -> SemanticStatementIdentity {
1572        self.identity
1573    }
1574}
1575
1576/// One provenance-bearing support event. Presence is exactly one.
1577#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1578pub struct SemanticSupportEvent {
1579    owner: u64,
1580    record: u32,
1581    polarity: SemanticPolarity,
1582    provenance: Identity256,
1583    source: Identity256,
1584    context: Identity256,
1585    scope: Identity256,
1586}
1587
1588impl SemanticSupportEvent {
1589    pub(crate) fn identity(self, statement: SemanticStatementIdentity) -> SemanticSupportIdentity {
1590        let mut hash = Sha256::new();
1591        hash.update(b"xlog.semantic.support.v1\0");
1592        hash.update(statement.as_bytes());
1593        hash.update((self.polarity.code() as u32).to_le_bytes());
1594        hash.update(1u32.to_le_bytes());
1595        hash.update(self.provenance.as_bytes());
1596        hash.update(self.source.as_bytes());
1597        hash.update(self.context.as_bytes());
1598        hash.update(self.scope.as_bytes());
1599        SemanticSupportIdentity(hash.finalize().into())
1600    }
1601}
1602
1603/// Kind of opaque semantic handle used in typed diagnostics.
1604#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1605pub enum SemanticHandleKind {
1606    Root,
1607    Fork,
1608    Statement,
1609    Support,
1610    Version,
1611}
1612
1613macro_rules! semantic_handle {
1614    ($name:ident) => {
1615        #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1616        pub struct $name {
1617            owner: u64,
1618            slot: u32,
1619            generation: u64,
1620        }
1621
1622        impl $name {
1623            const fn new(owner: u64, slot: u32, generation: u64) -> Self {
1624                Self {
1625                    owner,
1626                    slot,
1627                    generation,
1628                }
1629            }
1630
1631            /// Returns the opaque physical slot for diagnostics.
1632            pub const fn slot(self) -> u32 {
1633                self.slot
1634            }
1635
1636            /// Returns the exact physical generation.
1637            pub const fn generation(self) -> u64 {
1638                self.generation
1639            }
1640        }
1641    };
1642}
1643
1644semantic_handle!(SemanticRootHandle);
1645semantic_handle!(SemanticForkHandle);
1646semantic_handle!(SemanticStatementHandle);
1647semantic_handle!(SemanticSupportHandle);
1648semantic_handle!(SemanticVersionHandle);
1649
1650/// A sealed root or one live fork used for device observation.
1651#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1652pub enum SemanticView {
1653    Root(SemanticRootHandle),
1654    Fork(SemanticForkHandle),
1655}
1656
1657/// Exact reachable logical extents.
1658#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
1659pub struct SemanticExtents {
1660    statements: u32,
1661    supports: u32,
1662    versions: u32,
1663}
1664
1665impl SemanticExtents {
1666    pub const fn new(statements: u32, supports: u32, versions: u32) -> Self {
1667        Self {
1668            statements,
1669            supports,
1670            versions,
1671        }
1672    }
1673
1674    pub const fn statements(self) -> u32 {
1675        self.statements
1676    }
1677
1678    pub const fn supports(self) -> u32 {
1679        self.supports
1680    }
1681
1682    pub const fn versions(self) -> u32 {
1683        self.versions
1684    }
1685}
1686
1687/// Immutable device-derived root receipt.
1688#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1689pub struct SemanticRootSnapshot {
1690    digest: SemanticRootDigest,
1691    extents: SemanticExtents,
1692    canonical_bytes: [u8; 64],
1693}
1694
1695impl SemanticRootSnapshot {
1696    fn new(digest: SemanticRootDigest, extents: SemanticExtents) -> Self {
1697        let mut canonical_bytes = [0u8; 64];
1698        canonical_bytes[..8].copy_from_slice(b"XLOGSHG1");
1699        canonical_bytes[8..12].copy_from_slice(&1u32.to_le_bytes());
1700        canonical_bytes[12..16].copy_from_slice(&extents.statements.to_le_bytes());
1701        canonical_bytes[16..20].copy_from_slice(&extents.supports.to_le_bytes());
1702        canonical_bytes[20..24].copy_from_slice(&extents.versions.to_le_bytes());
1703        canonical_bytes[24..56].copy_from_slice(digest.as_bytes());
1704        Self {
1705            digest,
1706            extents,
1707            canonical_bytes,
1708        }
1709    }
1710
1711    pub const fn digest(&self) -> &SemanticRootDigest {
1712        &self.digest
1713    }
1714
1715    pub const fn extents(&self) -> SemanticExtents {
1716        self.extents
1717    }
1718
1719    pub const fn canonical_bytes(&self) -> &[u8; 64] {
1720        &self.canonical_bytes
1721    }
1722}
1723
1724/// Checked fixed capacities for one resident semantic graph.
1725#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1726pub struct SemanticHypergraphCapacities {
1727    roots: u32,
1728    statements: u32,
1729    supports: u32,
1730    versions: u32,
1731}
1732
1733impl SemanticHypergraphCapacities {
1734    pub fn try_new(
1735        roots: u32,
1736        statements: u32,
1737        supports: u32,
1738        versions: u32,
1739    ) -> Result<Self, SemanticHypergraphError> {
1740        for (name, value) in [
1741            ("root", roots),
1742            ("statement", statements),
1743            ("support", supports),
1744            ("version", versions),
1745        ] {
1746            if value == 0 {
1747                return Err(SemanticHypergraphError::InvalidCapacity { kind: name, value });
1748            }
1749        }
1750        Ok(Self {
1751            roots,
1752            statements,
1753            supports,
1754            versions,
1755        })
1756    }
1757}
1758
1759/// Statement identity paired with its opaque physical handle.
1760#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1761pub struct SemanticStatementRef {
1762    handle: SemanticStatementHandle,
1763    identity: SemanticStatementIdentity,
1764}
1765
1766impl SemanticStatementRef {
1767    pub const fn handle(self) -> SemanticStatementHandle {
1768        self.handle
1769    }
1770
1771    pub const fn identity(self) -> SemanticStatementIdentity {
1772        self.identity
1773    }
1774}
1775
1776/// Support identity paired with its opaque physical handle.
1777#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1778pub struct SemanticSupportRef {
1779    handle: SemanticSupportHandle,
1780    identity: SemanticSupportIdentity,
1781}
1782
1783impl SemanticSupportRef {
1784    pub const fn handle(self) -> SemanticSupportHandle {
1785        self.handle
1786    }
1787
1788    pub const fn identity(self) -> SemanticSupportIdentity {
1789        self.identity
1790    }
1791}
1792
1793/// Immutable statement-version identity and device-derived truth.
1794#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1795pub struct SemanticVersionRef {
1796    handle: SemanticVersionHandle,
1797    identity: SemanticVersionIdentity,
1798    truth: SemanticTruth,
1799}
1800
1801impl SemanticVersionRef {
1802    pub const fn handle(self) -> SemanticVersionHandle {
1803        self.handle
1804    }
1805
1806    pub const fn identity(self) -> SemanticVersionIdentity {
1807        self.identity
1808    }
1809
1810    pub const fn truth(self) -> SemanticTruth {
1811        self.truth
1812    }
1813}
1814
1815/// Complete result of inserting a new support event.
1816#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1817pub struct SemanticInsertedSupport {
1818    statement: SemanticStatementRef,
1819    support: SemanticSupportRef,
1820    version: SemanticVersionRef,
1821    previous_truth: SemanticTruth,
1822}
1823
1824impl SemanticInsertedSupport {
1825    pub const fn statement(self) -> SemanticStatementRef {
1826        self.statement
1827    }
1828
1829    pub const fn support(self) -> SemanticSupportRef {
1830        self.support
1831    }
1832
1833    pub const fn version(self) -> SemanticVersionRef {
1834        self.version
1835    }
1836
1837    /// Truth immediately before this support was attached, retained by the insertion.
1838    pub const fn previous_truth(self) -> SemanticTruth {
1839        self.previous_truth
1840    }
1841
1842    /// Whether the attachment changed a truth that already had reachable support.
1843    /// A first support and a new event with unchanged truth both return false.
1844    pub fn changes_defined_truth(self) -> bool {
1845        self.previous_truth != SemanticTruth::Neither && self.previous_truth != self.version.truth
1846    }
1847}
1848
1849/// Result of exact support insertion.
1850#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1851pub enum SemanticInsertOutcome {
1852    Inserted(SemanticInsertedSupport),
1853    Unchanged(SemanticVersionRef),
1854}
1855
1856/// Production-path execution counters.
1857#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1858pub struct SemanticHypergraphExecutionStats {
1859    cuda_kernel_launches: u64,
1860}
1861
1862impl SemanticHypergraphExecutionStats {
1863    pub const fn cuda_kernel_launches(self) -> u64 {
1864        self.cuda_kernel_launches
1865    }
1866}
1867
1868/// Typed failures from semantic owner and device lifecycle validation.
1869#[non_exhaustive]
1870#[derive(Clone, Debug, PartialEq, Eq)]
1871pub enum SemanticHypergraphError {
1872    InvalidCapacity {
1873        kind: &'static str,
1874        value: u32,
1875    },
1876    InvalidInput {
1877        detail: String,
1878    },
1879    ForeignHandle {
1880        kind: SemanticHandleKind,
1881    },
1882    SlotOutOfRange {
1883        kind: SemanticHandleKind,
1884        slot: u32,
1885        capacity: u32,
1886    },
1887    StaleGeneration {
1888        kind: SemanticHandleKind,
1889        slot: u32,
1890        presented: u64,
1891        current: u64,
1892    },
1893    InactiveHandle {
1894        kind: SemanticHandleKind,
1895        slot: u32,
1896    },
1897    NotReachable {
1898        kind: SemanticHandleKind,
1899        slot: u32,
1900    },
1901    CapacityExceeded {
1902        kind: SemanticHandleKind,
1903        capacity: u32,
1904    },
1905    GenerationExhausted {
1906        kind: SemanticHandleKind,
1907        slot: u32,
1908    },
1909    CorruptLineage {
1910        detail: String,
1911    },
1912    KernelUnavailable,
1913    Runtime {
1914        operation: &'static str,
1915        detail: String,
1916    },
1917    DeviceControlled,
1918    Poisoned,
1919}
1920
1921impl fmt::Display for SemanticHypergraphError {
1922    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1923        match self {
1924            Self::InvalidCapacity { kind, value } => {
1925                write!(
1926                    formatter,
1927                    "semantic {kind} capacity must be positive, got {value}"
1928                )
1929            }
1930            Self::InvalidInput { detail } => write!(formatter, "invalid semantic input: {detail}"),
1931            Self::ForeignHandle { kind } => write!(formatter, "foreign {kind:?} handle"),
1932            Self::SlotOutOfRange {
1933                kind,
1934                slot,
1935                capacity,
1936            } => write!(
1937                formatter,
1938                "semantic {kind:?} slot {slot} exceeds capacity {capacity}"
1939            ),
1940            Self::StaleGeneration {
1941                kind,
1942                slot,
1943                presented,
1944                current,
1945            } => write!(
1946                formatter,
1947                "stale semantic {kind:?} generation at slot {slot}: {presented}, current {current}"
1948            ),
1949            Self::InactiveHandle { kind, slot } => {
1950                write!(formatter, "inactive semantic {kind:?} slot {slot}")
1951            }
1952            Self::NotReachable { kind, slot } => {
1953                write!(formatter, "semantic {kind:?} slot {slot} is not reachable")
1954            }
1955            Self::CapacityExceeded { kind, capacity } => {
1956                write!(
1957                    formatter,
1958                    "semantic {kind:?} capacity {capacity} is exhausted"
1959                )
1960            }
1961            Self::GenerationExhausted { kind, slot } => {
1962                write!(
1963                    formatter,
1964                    "semantic {kind:?} generation exhausted at slot {slot}"
1965                )
1966            }
1967            Self::CorruptLineage { detail } => {
1968                write!(formatter, "corrupt semantic device lineage: {detail}")
1969            }
1970            Self::KernelUnavailable => {
1971                write!(formatter, "semantic hypergraph CUDA kernel unavailable")
1972            }
1973            Self::Runtime { operation, detail } => {
1974                write!(formatter, "semantic {operation} failed: {detail}")
1975            }
1976            Self::DeviceControlled => write!(
1977                formatter,
1978                "semantic hypergraph is device-controlled after resident admission"
1979            ),
1980            Self::Poisoned => write!(
1981                formatter,
1982                "semantic hypergraph is poisoned after runtime or integrity failure"
1983            ),
1984        }
1985    }
1986}
1987
1988impl std::error::Error for SemanticHypergraphError {}
1989
1990#[repr(C)]
1991#[derive(Clone, Copy)]
1992pub(crate) struct DeviceCommand {
1993    words: [u64; COMMAND_WORDS],
1994}
1995
1996impl Default for DeviceCommand {
1997    fn default() -> Self {
1998        Self {
1999            words: [0; COMMAND_WORDS],
2000        }
2001    }
2002}
2003
2004// SAFETY: this fixed-size C-layout value contains only `u64` words and no references.
2005unsafe impl DeviceRepr for DeviceCommand {}
2006
2007#[repr(C)]
2008#[derive(Clone, Copy)]
2009pub(crate) struct SemanticResidentReceiptRecord {
2010    // Successful insertion word 32 holds new-statement bit 0 and the actual
2011    // previous truth in bits 1..2. Outcome word 1 distinguishes new attachment
2012    // from duplicate; the returned duplicate version can predate the current head.
2013    // The final two words preserve the successfully acquired candidate slot and
2014    // generation through refusal diagnostics. A failed fork never grants them.
2015    words: [u64; RECEIPT_WORDS],
2016}
2017
2018impl Default for SemanticResidentReceiptRecord {
2019    fn default() -> Self {
2020        Self {
2021            words: [0; RECEIPT_WORDS],
2022        }
2023    }
2024}
2025
2026// SAFETY: this fixed-size C-layout value contains only `u64` words and no references.
2027unsafe impl DeviceRepr for SemanticResidentReceiptRecord {}
2028
2029type DeviceReceipt = SemanticResidentReceiptRecord;
2030
2031/// Canonical statement identity decoded into a resident semantic input bank.
2032#[repr(C)]
2033#[derive(Clone, Copy)]
2034pub(crate) struct SemanticResidentDecodedStatement {
2035    identity_words: [u32; 8],
2036    record: u32,
2037    reconstruction: [u32; 10],
2038}
2039
2040// SAFETY: this fixed-size C-layout value contains only `u32` words and no references.
2041unsafe impl DeviceRepr for SemanticResidentDecodedStatement {}
2042
2043/// Canonical support event components decoded into a resident semantic input bank.
2044#[repr(C)]
2045#[derive(Clone, Copy)]
2046pub(crate) struct SemanticResidentDecodedSupport {
2047    polarity: u32,
2048    provenance_words: [u32; 8],
2049    source_words: [u32; 8],
2050    context_words: [u32; 8],
2051    scope_words: [u32; 8],
2052    record: u32,
2053}
2054
2055// SAFETY: this fixed-size C-layout value contains only `u32` words and no references.
2056unsafe impl DeviceRepr for SemanticResidentDecodedSupport {}
2057
2058/// One decoder-owned semantic record before it is split into owner input banks.
2059#[repr(C)]
2060#[derive(Clone, Copy)]
2061pub(crate) struct SemanticResidentDecodedInput {
2062    statement_identity_words: [u32; 8],
2063    polarity: u32,
2064    provenance_words: [u32; 8],
2065    source_words: [u32; 8],
2066    context_words: [u32; 8],
2067    scope_words: [u32; 8],
2068    statement_record: u32,
2069    support_record: u32,
2070    reconstruction: [u32; 10],
2071}
2072
2073// SAFETY: this fixed-size C-layout value contains only `u32` words and no references.
2074unsafe impl DeviceRepr for SemanticResidentDecodedInput {}
2075
2076#[repr(C)]
2077#[derive(Clone, Copy, Default)]
2078pub(crate) struct SemanticResidentTruthValue {
2079    status: u64,
2080    truth: u64,
2081    owner: u64,
2082    reserved: u64,
2083}
2084
2085// SAFETY: this type is a fixed-width C-layout POD shared with the CUDA kernel.
2086unsafe impl DeviceRepr for SemanticResidentTruthValue {}
2087
2088/// Generation-bound semantic handle stored in a resident device bank.
2089#[repr(C)]
2090#[derive(Clone, Copy)]
2091pub(crate) struct SemanticResidentHandleRecord {
2092    owner: u64,
2093    kind: u64,
2094    slot: u64,
2095    generation: u64,
2096}
2097
2098// SAFETY: this fixed-size C-layout value contains only `u64` words and no references.
2099unsafe impl DeviceRepr for SemanticResidentHandleRecord {}
2100
2101const _: () = assert!(std::mem::size_of::<DeviceCommand>() == COMMAND_WORDS * 8);
2102const _: () = assert!(std::mem::align_of::<DeviceCommand>() == 8);
2103const _: () = assert!(std::mem::size_of::<DeviceReceipt>() == RECEIPT_WORDS * 8);
2104const _: () = assert!(std::mem::align_of::<DeviceReceipt>() == 8);
2105const _: () = assert!(std::mem::size_of::<SemanticResidentDecodedStatement>() == 76);
2106const _: () = assert!(std::mem::align_of::<SemanticResidentDecodedStatement>() == 4);
2107const _: () = assert!(std::mem::size_of::<SemanticResidentDecodedSupport>() == 136);
2108const _: () = assert!(std::mem::align_of::<SemanticResidentDecodedSupport>() == 4);
2109const _: () = assert!(std::mem::size_of::<SemanticResidentDecodedInput>() == 212);
2110const _: () = assert!(std::mem::align_of::<SemanticResidentDecodedInput>() == 4);
2111const _: () = assert!(std::mem::size_of::<SemanticResidentTruthValue>() == 32);
2112const _: () = assert!(std::mem::align_of::<SemanticResidentTruthValue>() == 8);
2113const _: () = assert!(std::mem::size_of::<SemanticResidentHandleRecord>() == 32);
2114const _: () = assert!(std::mem::align_of::<SemanticResidentHandleRecord>() == 8);
2115
2116/// One writable slot in a resident generation-bound handle bank.
2117pub(crate) struct SemanticResidentHandleSlot<'a> {
2118    allocation: &'a TrackedCudaSlice<SemanticResidentHandleRecord>,
2119    index: u32,
2120}
2121
2122impl<'a> SemanticResidentHandleSlot<'a> {
2123    #[cfg_attr(
2124        not(test),
2125        expect(
2126            dead_code,
2127            reason = "constructed by crate-internal device-resident pipelines"
2128        )
2129    )]
2130    pub(crate) fn new(
2131        allocation: &'a TrackedCudaSlice<SemanticResidentHandleRecord>,
2132        index: u32,
2133    ) -> Result<Self, SemanticHypergraphError> {
2134        validate_resident_bank_index(index, allocation.len(), "handle output")?;
2135        Ok(Self { allocation, index })
2136    }
2137}
2138
2139/// One immutable-root handle selected from an already-resident handle bank.
2140pub(crate) struct SemanticResidentRootHandleBank<'a> {
2141    allocation: &'a TrackedCudaSlice<SemanticResidentHandleRecord>,
2142    index: u32,
2143}
2144
2145impl SemanticResidentRootHandleBank<'_> {
2146    #[cfg_attr(
2147        not(test),
2148        expect(
2149            dead_code,
2150            reason = "consumed by crate-internal device-resident pipelines"
2151        )
2152    )]
2153    pub(crate) fn handle(&self) -> SemanticResidentRootHandle<'_> {
2154        SemanticResidentRootHandle::Bank {
2155            allocation: self.allocation,
2156            index: self.index,
2157        }
2158    }
2159}
2160
2161/// One canonical statement identity selected from an already-resident decoded bank.
2162pub(crate) struct SemanticResidentStatementBank<'a> {
2163    allocation: &'a TrackedCudaSlice<SemanticResidentDecodedStatement>,
2164    index: u32,
2165}
2166
2167/// One record selected from a decoder-owned resident semantic input bank.
2168pub(crate) struct SemanticResidentDecodedInputBank<'a> {
2169    allocation: &'a TrackedCudaSlice<SemanticResidentDecodedInput>,
2170    index: u32,
2171}
2172
2173impl<'a> SemanticResidentDecodedInputBank<'a> {
2174    #[cfg_attr(
2175        not(test),
2176        expect(
2177            dead_code,
2178            reason = "constructed by crate-internal device-resident pipelines"
2179        )
2180    )]
2181    pub(crate) fn new(
2182        allocation: &'a TrackedCudaSlice<SemanticResidentDecodedInput>,
2183        index: u32,
2184    ) -> Result<Self, SemanticHypergraphError> {
2185        validate_resident_bank_index(index, allocation.len(), "decoded input")?;
2186        Ok(Self { allocation, index })
2187    }
2188}
2189
2190impl<'a> SemanticResidentStatementBank<'a> {
2191    #[cfg_attr(
2192        not(test),
2193        expect(
2194            dead_code,
2195            reason = "constructed by crate-internal device-resident pipelines"
2196        )
2197    )]
2198    pub(crate) fn new(
2199        allocation: &'a TrackedCudaSlice<SemanticResidentDecodedStatement>,
2200        index: u32,
2201    ) -> Result<Self, SemanticHypergraphError> {
2202        validate_resident_bank_index(index, allocation.len(), "decoded statement")?;
2203        Ok(Self { allocation, index })
2204    }
2205}
2206
2207/// One canonical support event selected from an already-resident decoded bank.
2208pub(crate) struct SemanticResidentSupportBank<'a> {
2209    allocation: &'a TrackedCudaSlice<SemanticResidentDecodedSupport>,
2210    index: u32,
2211}
2212
2213impl<'a> SemanticResidentSupportBank<'a> {
2214    #[cfg_attr(
2215        not(test),
2216        expect(
2217            dead_code,
2218            reason = "constructed by crate-internal device-resident pipelines"
2219        )
2220    )]
2221    pub(crate) fn new(
2222        allocation: &'a TrackedCudaSlice<SemanticResidentDecodedSupport>,
2223        index: u32,
2224    ) -> Result<Self, SemanticHypergraphError> {
2225        validate_resident_bank_index(index, allocation.len(), "decoded support")?;
2226        Ok(Self { allocation, index })
2227    }
2228}
2229
2230/// One writable receipt slot selected from a private device bank.
2231pub(crate) struct SemanticResidentReceiptSlot<'a> {
2232    allocation: &'a TrackedCudaSlice<SemanticResidentReceiptRecord>,
2233    index: u32,
2234}
2235
2236impl<'a> SemanticResidentReceiptSlot<'a> {
2237    #[cfg_attr(
2238        not(test),
2239        expect(
2240            dead_code,
2241            reason = "constructed by crate-internal device-resident pipelines"
2242        )
2243    )]
2244    pub(crate) fn new(
2245        allocation: &'a TrackedCudaSlice<SemanticResidentReceiptRecord>,
2246        index: u32,
2247    ) -> Result<Self, SemanticHypergraphError> {
2248        validate_resident_bank_index(index, allocation.len(), "receipt")?;
2249        Ok(Self { allocation, index })
2250    }
2251}
2252
2253/// Device pointer and dependency view for one typed resident semantic receipt.
2254pub(crate) struct SemanticResidentReceiptView<'a> {
2255    allocation: &'a TrackedCudaSlice<SemanticResidentReceiptRecord>,
2256    index: u32,
2257}
2258
2259/// A typed, device-resident four-valued truth receipt.
2260pub(crate) struct SemanticResidentTruthView<'a> {
2261    receipt: SemanticResidentReceiptView<'a>,
2262}
2263
2264/// One writable output selected from a resident truth-value bank.
2265pub(crate) struct SemanticResidentTruthSlot<'a> {
2266    allocation: &'a TrackedCudaSlice<SemanticResidentTruthValue>,
2267    index: u32,
2268}
2269
2270impl<'a> SemanticResidentTruthSlot<'a> {
2271    #[cfg_attr(
2272        not(test),
2273        expect(
2274            dead_code,
2275            reason = "constructed by crate-internal device-resident pipelines"
2276        )
2277    )]
2278    pub(crate) fn new(
2279        allocation: &'a TrackedCudaSlice<SemanticResidentTruthValue>,
2280        index: u32,
2281    ) -> Result<Self, SemanticHypergraphError> {
2282        validate_resident_bank_index(index, allocation.len(), "truth output")?;
2283        Ok(Self { allocation, index })
2284    }
2285}
2286
2287impl SemanticResidentReceiptView<'_> {
2288    pub(crate) fn record_read(&self, recorder: &mut crate::launch::LaunchRecorder) {
2289        recorder.read(self.allocation);
2290    }
2291}
2292
2293/// A generation-bound immutable-root handle carried by a typed bank or prior receipt.
2294pub(crate) enum SemanticResidentRootHandle<'a> {
2295    Bank {
2296        allocation: &'a TrackedCudaSlice<SemanticResidentHandleRecord>,
2297        index: u32,
2298    },
2299    Receipt(SemanticResidentReceiptView<'a>),
2300}
2301
2302impl SemanticResidentRootHandle<'_> {
2303    fn record_input(
2304        self,
2305        descriptor: &mut DeviceLaunchDescriptor,
2306        recorder: &mut crate::launch::LaunchRecorder,
2307    ) {
2308        match self {
2309            Self::Bank { allocation, index } => {
2310                recorder.read(allocation);
2311                descriptor.handle_ptr = allocation.device_ptr_value();
2312                descriptor.handle_index = u64::from(index);
2313            }
2314            Self::Receipt(receipt) => {
2315                receipt.record_read(recorder);
2316                descriptor.source_receipt_ptr = receipt.allocation.device_ptr_value();
2317                descriptor.source_receipt_index = u64::from(receipt.index);
2318            }
2319        }
2320    }
2321}
2322
2323/// A generation-bound candidate handle carried by a pending device receipt.
2324pub(crate) struct SemanticResidentCandidateHandle<'a> {
2325    receipt: SemanticResidentReceiptView<'a>,
2326}
2327
2328/// Explicit query source; roots never require acquiring a mutable candidate.
2329#[cfg_attr(
2330    not(test),
2331    expect(
2332        dead_code,
2333        reason = "root and fork construction is exercised by the CUDA resident-query qualification"
2334    )
2335)]
2336pub(crate) enum SemanticResidentView<'a> {
2337    Root(SemanticResidentRootHandle<'a>),
2338    Fork(SemanticResidentCandidateHandle<'a>),
2339}
2340
2341/// A pending fork or insertion result that keeps its candidate handle resident.
2342pub(crate) struct SemanticResidentMutationReceipt<'a> {
2343    receipt: SemanticResidentReceiptView<'a>,
2344}
2345
2346impl SemanticResidentMutationReceipt<'_> {
2347    #[cfg_attr(
2348        not(test),
2349        expect(
2350            dead_code,
2351            reason = "consumed by crate-internal device-resident pipelines"
2352        )
2353    )]
2354    pub(crate) fn candidate_handle(&self) -> SemanticResidentCandidateHandle<'_> {
2355        SemanticResidentCandidateHandle {
2356            receipt: SemanticResidentReceiptView {
2357                allocation: self.receipt.allocation,
2358                index: self.receipt.index,
2359            },
2360        }
2361    }
2362}
2363
2364/// A sealed immutable-root result that remains device resident.
2365pub(crate) struct SemanticResidentSealReceipt<'a> {
2366    receipt: SemanticResidentReceiptView<'a>,
2367}
2368
2369impl SemanticResidentSealReceipt<'_> {
2370    #[cfg_attr(
2371        not(test),
2372        expect(
2373            dead_code,
2374            reason = "consumed by crate-internal device-resident pipelines"
2375        )
2376    )]
2377    pub(crate) fn root_handle(&self) -> SemanticResidentRootHandle<'_> {
2378        SemanticResidentRootHandle::Receipt(SemanticResidentReceiptView {
2379            allocation: self.receipt.allocation,
2380            index: self.receipt.index,
2381        })
2382    }
2383}
2384
2385/// Four-valued truth result produced and retained on the resident device path.
2386pub(crate) struct SemanticResidentTruthReceipt<'a> {
2387    receipt: SemanticResidentReceiptView<'a>,
2388}
2389
2390impl SemanticResidentTruthReceipt<'_> {
2391    #[cfg_attr(
2392        not(test),
2393        expect(
2394            dead_code,
2395            reason = "consumed by crate-internal device-resident pipelines"
2396        )
2397    )]
2398    pub(crate) fn device_view(&self) -> SemanticResidentTruthView<'_> {
2399        SemanticResidentTruthView {
2400            receipt: SemanticResidentReceiptView {
2401                allocation: self.receipt.allocation,
2402                index: self.receipt.index,
2403            },
2404        }
2405    }
2406}
2407
2408#[derive(Clone, Copy)]
2409struct SlotLedger {
2410    generation: u64,
2411    live: bool,
2412}
2413
2414impl SlotLedger {
2415    const FREE: Self = Self {
2416        generation: 1,
2417        live: false,
2418    };
2419}
2420
2421struct CurrentFork {
2422    handle: SemanticForkHandle,
2423    staged_statements: Vec<u32>,
2424    staged_supports: Vec<u32>,
2425    staged_versions: Vec<u32>,
2426}
2427
2428#[derive(Clone, Copy)]
2429enum ArenaAccess {
2430    Read,
2431    ReadWrite,
2432}
2433
2434struct SemanticKernelLaunchSpec<'a> {
2435    domain: &'a ResidentExecutionDomain,
2436    execute: &'a CudaFunction,
2437    owner: u64,
2438    capacities: SemanticHypergraphCapacities,
2439    arena_words: u64,
2440}
2441
2442#[repr(C)]
2443#[derive(Clone, Copy)]
2444struct DeviceLaunchDescriptor {
2445    expected_owner: u64,
2446    root_capacity: u64,
2447    statement_capacity: u64,
2448    support_capacity: u64,
2449    version_capacity: u64,
2450    arena_words: u64,
2451    command_ptr: u64,
2452    command_index: u64,
2453    handle_ptr: u64,
2454    handle_index: u64,
2455    source_receipt_ptr: u64,
2456    source_receipt_index: u64,
2457    decoded_input_ptr: u64,
2458    decoded_input_index: u64,
2459    decoded_statement_ptr: u64,
2460    decoded_statement_index: u64,
2461    decoded_support_ptr: u64,
2462    decoded_support_index: u64,
2463    output_ptr: u64,
2464    output_index: u64,
2465    receipt_ptr: u64,
2466    receipt_index: u64,
2467    admission: u64,
2468    abi_generation: u64,
2469}
2470
2471const _: () = assert!(std::mem::size_of::<DeviceLaunchDescriptor>() == 192);
2472const _: () = assert!(std::mem::align_of::<DeviceLaunchDescriptor>() == 8);
2473
2474// SAFETY: this fixed-size C-layout value contains only `u64` words and no references.
2475unsafe impl DeviceRepr for DeviceLaunchDescriptor {}
2476
2477struct DeviceLaunchDescriptorParam(DeviceLaunchDescriptor);
2478
2479impl crate::cuda_compat::KernelParamStorage for DeviceLaunchDescriptorParam {
2480    fn as_kernel_param(&self) -> *mut std::ffi::c_void {
2481        (&self.0 as *const DeviceLaunchDescriptor).cast_mut().cast()
2482    }
2483}
2484
2485impl crate::cuda_compat::IntoKernelParamStorage for DeviceLaunchDescriptor {
2486    type Storage = DeviceLaunchDescriptorParam;
2487
2488    fn into_kernel_param_storage(self) -> Self::Storage {
2489        DeviceLaunchDescriptorParam(self)
2490    }
2491}
2492
2493enum SemanticKernelInput<'a> {
2494    HostCommand {
2495        commands: &'a TrackedCudaSlice<DeviceCommand>,
2496        index: u32,
2497    },
2498    ResidentEmptyRootHandle {
2499        handle: &'a TrackedCudaSlice<SemanticResidentHandleRecord>,
2500        index: u32,
2501    },
2502    ResidentFork {
2503        root: SemanticResidentRootHandle<'a>,
2504    },
2505    ResidentPreflightTransition {
2506        root: SemanticResidentRootHandle<'a>,
2507    },
2508    ResidentInsertSupport {
2509        candidate: SemanticResidentCandidateHandle<'a>,
2510        statement: SemanticResidentStatementBank<'a>,
2511        support: SemanticResidentSupportBank<'a>,
2512    },
2513    ResidentSeal {
2514        candidate: SemanticResidentCandidateHandle<'a>,
2515    },
2516    ResidentTruth {
2517        view: SemanticResidentView<'a>,
2518        statement: SemanticResidentStatementBank<'a>,
2519    },
2520    ResidentConsumeTruth {
2521        truth: SemanticResidentTruthView<'a>,
2522        output: SemanticResidentTruthSlot<'a>,
2523    },
2524    ResidentMaterializeDecoded {
2525        input: SemanticResidentDecodedInputBank<'a>,
2526        statement: SemanticResidentStatementBank<'a>,
2527        support: SemanticResidentSupportBank<'a>,
2528    },
2529}
2530
2531struct SemanticKernelLaunchIo<'a> {
2532    arena: &'a mut TrackedCudaSlice<u64>,
2533    arena_access: ArenaAccess,
2534    input: SemanticKernelInput<'a>,
2535    receipts: &'a TrackedCudaSlice<DeviceReceipt>,
2536    receipt_index: u32,
2537}
2538
2539/// Device-resident immutable-root semantic owner retaining its provider.
2540pub struct SemanticHypergraph {
2541    domain: ResidentExecutionDomain,
2542    stream: Arc<CudaStream>,
2543    execute: CudaFunction,
2544    arena: TrackedCudaSlice<u64>,
2545    command: TrackedCudaSlice<DeviceCommand>,
2546    receipt: TrackedCudaSlice<DeviceReceipt>,
2547    arena_words: u64,
2548    owner: u64,
2549    capacities: SemanticHypergraphCapacities,
2550    roots: Vec<SlotLedger>,
2551    fork: SlotLedger,
2552    statements: Vec<SlotLedger>,
2553    supports: Vec<SlotLedger>,
2554    versions: Vec<SlotLedger>,
2555    current_fork: Option<CurrentFork>,
2556    empty_root: SemanticRootHandle,
2557    admission: Option<SemanticAdmission>,
2558    stats: SemanticHypergraphExecutionStats,
2559    device_controlled: bool,
2560    poisoned: bool,
2561    // Retire device state before releasing its allocation and driver owner.
2562    provider: Arc<CudaKernelProvider>,
2563}
2564
2565impl SemanticHypergraph {
2566    pub(crate) fn transition_owner(
2567        &self,
2568    ) -> Result<(Arc<CudaKernelProvider>, ResidentExecutionDomain), SemanticHypergraphError> {
2569        self.ensure_host_facade_available()?;
2570        if self.current_fork.is_some() || self.admission.is_none() {
2571            return Err(admission_error(
2572                "transition requires admitted records and no live candidate",
2573            ));
2574        }
2575        Ok((Arc::clone(&self.provider), self.domain.clone()))
2576    }
2577
2578    /// Registers the canonical arena in the enclosing captured transaction. The
2579    /// consuming session owns this graph exclusively until all lane terminals.
2580    pub(crate) fn record_transition(&self, recorder: &mut crate::launch::LaunchRecorder) {
2581        recorder.read_write(&self.arena);
2582    }
2583
2584    pub(crate) fn transition_arena(&self) -> [u64; 7] {
2585        [
2586            self.arena.device_ptr_value(),
2587            self.owner,
2588            self.capacities.roots as u64,
2589            self.capacities.statements as u64,
2590            self.capacities.supports as u64,
2591            self.capacities.versions as u64,
2592            self.arena_words,
2593        ]
2594    }
2595
2596    pub(crate) fn transition_arena_view(&self) -> crate::memory::DeviceMemoryView<u64> {
2597        self.arena.view()
2598    }
2599
2600    /// Decode the original root from its private, completed device snapshot.
2601    /// No current arena slot is consulted after a later step can retire it.
2602    #[cfg(feature = "semantic-policy")]
2603    pub(crate) fn export_retained_transition_root(
2604        &self,
2605        arena: &[u64],
2606        owner: u64,
2607        slot: u64,
2608        generation: u64,
2609        digest: [u8; 32],
2610        extents: [u64; 3],
2611    ) -> Result<SemanticRootMaterial, SemanticHypergraphError> {
2612        self.ensure_not_poisoned()?;
2613        if owner != self.owner {
2614            return Err(SemanticHypergraphError::ForeignHandle {
2615                kind: SemanticHandleKind::Root,
2616            });
2617        }
2618        let extents = extents.map(u32::try_from);
2619        let [statements, supports, versions] = extents;
2620        let snapshot = SemanticRootSnapshot::new(
2621            SemanticRootDigest(digest),
2622            SemanticExtents::new(
2623                statements.map_err(|_| size_overflow())?,
2624                supports.map_err(|_| size_overflow())?,
2625                versions.map_err(|_| size_overflow())?,
2626            ),
2627        );
2628        let slot = checked_receipt_slot(slot, SemanticHandleKind::Root, self.capacities.roots)?;
2629        material_from_arena(
2630            arena,
2631            self.capacities,
2632            SemanticRootHandle::new(owner, slot, generation),
2633            snapshot,
2634            self.admission
2635                .as_ref()
2636                .ok_or_else(|| admission_error("retained root requires typed admission"))?,
2637        )
2638    }
2639
2640    /// The canonical admission prefix plus the maximum reachable insertion roster.
2641    /// This inspects immutable admitted meanings and capacities, never a device value.
2642    #[cfg(feature = "semantic-policy")]
2643    pub(crate) fn transition_root_material_capacity(
2644        &self,
2645    ) -> Result<usize, SemanticHypergraphError> {
2646        let admission = self
2647            .admission
2648            .as_ref()
2649            .ok_or_else(|| admission_error("root capacity requires typed admission"))?;
2650        let (records, symbols) = normalized_material_admission(admission)?;
2651        let prefix = SemanticRootMaterial::encode_admission(&records, &symbols)?.len();
2652        (self.capacities.versions as usize)
2653            .checked_mul(1 + 4 + 10 * 4 + 4 + 32)
2654            .and_then(|bytes| bytes.checked_add(prefix))
2655            .and_then(|bytes| bytes.checked_add(4 + 2 * 32 + 2 * 3 * 4))
2656            .ok_or_else(size_overflow)
2657    }
2658
2659    pub(crate) fn enter_transition(&mut self) {
2660        self.device_controlled = true;
2661    }
2662
2663    pub(crate) fn observe_transition_root(
2664        &mut self,
2665        words: [u64; 42],
2666    ) -> Result<(SemanticRootHandle, SemanticRootSnapshot), SemanticHypergraphError> {
2667        let receipt = DeviceReceipt { words };
2668        self.expect_success(&receipt)?;
2669        let result = (|| {
2670            let slot =
2671                checked_receipt_slot(words[3], SemanticHandleKind::Root, self.capacities.roots)?;
2672            if words[39] != self.owner || words[4] == 0 {
2673                return self.corrupt("transition root receipt has invalid owner or generation");
2674            }
2675            Ok((
2676                SemanticRootHandle::new(self.owner, slot, words[4]),
2677                SemanticRootSnapshot::new(
2678                    SemanticRootDigest(receipt_identity(&receipt, 16)),
2679                    receipt_extents(&receipt)?,
2680                ),
2681            ))
2682        })();
2683        poison_after_reconciliation_error(&mut self.poisoned, result)
2684    }
2685
2686    pub(crate) fn observe_transition_edit(
2687        &mut self,
2688        words: [u64; 42],
2689    ) -> Result<Option<SemanticInsertOutcome>, SemanticHypergraphError> {
2690        let receipt = DeviceReceipt { words };
2691        self.expect_success(&receipt)?;
2692        let result = (|| {
2693            if words[39] != self.owner
2694                || (words[1] == OUTCOME_INSERTED
2695                    && (words[8] == 0 || words[10] == 0 || words[12] == 0))
2696                || (words[1] == OUTCOME_UNCHANGED && words[12] == 0)
2697            {
2698                return self.corrupt("transition edit receipt has invalid owner or generation");
2699            }
2700            match words[1] {
2701                0 => Ok(None),
2702                OUTCOME_UNCHANGED => Ok(Some(SemanticInsertOutcome::Unchanged(
2703                    self.version_ref(&receipt)?,
2704                ))),
2705                OUTCOME_INSERTED => Ok(Some(SemanticInsertOutcome::Inserted(
2706                    SemanticInsertedSupport {
2707                        statement: self.statement_ref(&receipt)?,
2708                        support: self.support_ref(&receipt)?,
2709                        version: self.version_ref(&receipt)?,
2710                        previous_truth: inserted_support_previous_truth(&receipt)?,
2711                    },
2712                ))),
2713                _ => self.corrupt("invalid transition edit outcome"),
2714            }
2715        })();
2716        poison_after_reconciliation_error(&mut self.poisoned, result)
2717    }
2718}
2719
2720impl CudaKernelProvider {
2721    /// Allocates one fixed-capacity semantic graph and initializes its empty root on CUDA.
2722    pub fn allocate_semantic_hypergraph(
2723        self: &Arc<Self>,
2724        domain: &ResidentExecutionDomain,
2725        capacities: SemanticHypergraphCapacities,
2726    ) -> Result<SemanticHypergraph, SemanticHypergraphError> {
2727        validate_execution_domain(self, domain)
2728            .map_err(|error| runtime_error("domain validation", error))?;
2729        let execute = self
2730            .device()
2731            .inner()
2732            .get_func(MODULE, KERNEL)
2733            .ok_or(SemanticHypergraphError::KernelUnavailable)?;
2734        let arena_words = checked_arena_words(capacities)?;
2735        let arena_bytes =
2736            arena_words
2737                .checked_mul(8)
2738                .ok_or_else(|| SemanticHypergraphError::InvalidInput {
2739                    detail: "semantic arena byte size overflow".into(),
2740                })?;
2741        let total_bytes = arena_bytes
2742            .checked_add(std::mem::size_of::<DeviceCommand>() as u64)
2743            .and_then(|bytes| bytes.checked_add(std::mem::size_of::<DeviceReceipt>() as u64))
2744            .ok_or_else(|| SemanticHypergraphError::InvalidInput {
2745                detail: "semantic allocation byte size overflow".into(),
2746            })?;
2747        let arena_len =
2748            usize::try_from(arena_words).map_err(|_| SemanticHypergraphError::InvalidInput {
2749                detail: "semantic arena exceeds platform usize".into(),
2750            })?;
2751        let mut reservation = self
2752            .memory()
2753            .reserve_bytes(total_bytes)
2754            .map_err(|error| runtime_error("reservation", error))?;
2755        let arena = reservation
2756            .alloc::<u64>(arena_len)
2757            .map_err(|error| runtime_error("arena allocation", error))?;
2758        let command = reservation
2759            .alloc::<DeviceCommand>(1)
2760            .map_err(|error| runtime_error("command allocation", error))?;
2761        let receipt = reservation
2762            .alloc::<DeviceReceipt>(1)
2763            .map_err(|error| runtime_error("receipt allocation", error))?;
2764        if reservation.remaining_bytes() != 0 {
2765            return Err(SemanticHypergraphError::CorruptLineage {
2766                detail: format!(
2767                    "{} reserved semantic bytes were not materialized",
2768                    reservation.remaining_bytes()
2769                ),
2770            });
2771        }
2772        let runtime = self
2773            .memory()
2774            .runtime()
2775            .ok_or_else(|| SemanticHypergraphError::Runtime {
2776                operation: "stream resolution",
2777                detail: "provider has no device runtime".into(),
2778            })?;
2779        let stream = runtime
2780            .stream_pool()
2781            .resolve(domain.stream_id())
2782            .ok_or_else(|| SemanticHypergraphError::Runtime {
2783                operation: "stream resolution",
2784                detail: "resident stream id is not live in provider runtime".into(),
2785            })?;
2786        let owner = NEXT_OWNER_ID
2787            .try_update(Ordering::Relaxed, Ordering::Relaxed, |value| {
2788                value.checked_add(1).filter(|next| *next != 0)
2789            })
2790            .map_err(|_| SemanticHypergraphError::GenerationExhausted {
2791                kind: SemanticHandleKind::Root,
2792                slot: 0,
2793            })?;
2794        let mut graph = SemanticHypergraph {
2795            provider: Arc::clone(self),
2796            domain: domain.clone(),
2797            stream,
2798            execute,
2799            arena,
2800            command,
2801            receipt,
2802            arena_words,
2803            owner,
2804            capacities,
2805            roots: vec![SlotLedger::FREE; capacities.roots as usize],
2806            fork: SlotLedger::FREE,
2807            statements: vec![SlotLedger::FREE; capacities.statements as usize],
2808            supports: vec![SlotLedger::FREE; capacities.supports as usize],
2809            versions: vec![SlotLedger::FREE; capacities.versions as usize],
2810            current_fork: None,
2811            empty_root: SemanticRootHandle::new(owner, 0, 1),
2812            admission: None,
2813            stats: SemanticHypergraphExecutionStats::default(),
2814            device_controlled: false,
2815            poisoned: false,
2816        };
2817        let command = graph.command_for(OP_INITIALIZE);
2818        let receipt = graph.run(command, ArenaAccess::ReadWrite)?;
2819        graph.expect_success(&receipt)?;
2820        if receipt.words[3] != 0 || receipt.words[4] != 1 {
2821            return Err(SemanticHypergraphError::CorruptLineage {
2822                detail: "CUDA initialization did not publish empty root slot 0 generation 1".into(),
2823            });
2824        }
2825        graph.roots[0].live = true;
2826        Ok(graph)
2827    }
2828}
2829
2830impl SemanticHypergraph {
2831    /// Consumes a cold graph and imports the explicitly selected support assertions.
2832    ///
2833    /// Each index selects an admitted support record with its declared statement
2834    /// link. Order and duplicate-insertion semantics are preserved; unselected
2835    /// events remain declarations available to future proposals, not initial truth.
2836    /// The assertion count is bounded by `limits.max_records`.
2837    ///
2838    /// Initialization uses the existing device mutation path, then binds admission
2839    /// to the sealed populated root without re-resolving accepted symbol content.
2840    /// Errors return no partially initialized owner. This cold import does not
2841    /// certify external truth or provenance, or publish joint neural/text state.
2842    pub fn admit_initial_records(
2843        mut self,
2844        records: SemanticAdmissionRecords,
2845        initial_supports: &[u32],
2846        limits: SemanticAdmissionLimits,
2847    ) -> Result<Self, SemanticHypergraphError> {
2848        self.ensure_host_facade_available()?;
2849        if initial_supports.len() > limits.max_records as usize {
2850            return Err(admission_error(
2851                "initial support count exceeds admission bound",
2852            ));
2853        }
2854        if initial_supports
2855            .iter()
2856            .any(|&index| records.supports.get(index as usize).is_none())
2857        {
2858            return Err(admission_error(
2859                "initial support is outside the declared records",
2860            ));
2861        }
2862        let empty = self.empty_root();
2863        self.admit_records(empty, records, limits)?;
2864        if initial_supports.is_empty() {
2865            return Ok(self);
2866        }
2867        let fork = self.fork(empty)?;
2868        for &index in initial_supports {
2869            let admission = self.admission.as_ref().expect("validated cold admission");
2870            let statement = admission.records.supports[index as usize].statement;
2871            let key = admission.statement_key(statement)?;
2872            let event = admission.support_event(index)?;
2873            self.insert_support(fork, &key, &event)?;
2874        }
2875        let base = self.seal(fork)?;
2876        let base_snapshot = self.snapshot(SemanticView::Root(base))?;
2877        let admission = self.admission.as_mut().expect("validated cold admission");
2878        let identity = derive_admission_identity(
2879            &admission.records,
2880            admission.schema_generation,
2881            &admission.encoded_records,
2882            &admission.statement_keys,
2883            &admission.support_events,
2884            &base_snapshot,
2885        );
2886        admission.base = base;
2887        admission.base_snapshot = base_snapshot;
2888        admission.identity = identity;
2889        Ok(self)
2890    }
2891
2892    /// Admits and owns typed content for one exact sealed base before resident execution.
2893    ///
2894    /// This is not an import of a separate relation store, and does not assert the
2895    /// external truth of provenance. Invalid input is rejected before CUDA work.
2896    /// The accepted snapshot cannot be replaced or mutated by the caller.
2897    pub fn admit_records(
2898        &mut self,
2899        base: SemanticRootHandle,
2900        records: SemanticAdmissionRecords,
2901        limits: SemanticAdmissionLimits,
2902    ) -> Result<&SemanticAdmission, SemanticHypergraphError> {
2903        self.ensure_host_facade_available()?;
2904        self.validate_root(base)?;
2905        if self.admission.is_some() || self.current_fork.is_some() {
2906            return Err(admission_error(
2907                "typed admission requires an unbound owner with no live fork",
2908            ));
2909        }
2910        let admission = admit_semantic_records(records, limits, base, || {
2911            self.snapshot(SemanticView::Root(base))
2912        })?;
2913        self.admission = Some(admission);
2914        Ok(self
2915            .admission
2916            .as_ref()
2917            .expect("just published complete admission"))
2918    }
2919
2920    /// Returns the retained cold snapshot, not a re-resolution of live inputs.
2921    pub fn admission(&self) -> Option<&SemanticAdmission> {
2922        self.admission.as_ref()
2923    }
2924
2925    /// Cold export under the transition owner's exclusive, quiescent acquisition.
2926    /// Native validation is intentional: resident execution invalidates host ledgers.
2927    pub(crate) fn export_transition_root_parts(
2928        &mut self,
2929        owner: u64,
2930        slot: u64,
2931        generation: u64,
2932    ) -> Result<SemanticRootMaterial, SemanticHypergraphError> {
2933        self.ensure_not_poisoned()?;
2934        if owner != self.owner {
2935            return Err(SemanticHypergraphError::ForeignHandle {
2936                kind: SemanticHandleKind::Root,
2937            });
2938        }
2939        let slot = checked_receipt_slot(slot, SemanticHandleKind::Root, self.capacities.roots)?;
2940        self.export_transition_root(SemanticRootHandle::new(owner, slot, generation))
2941    }
2942
2943    /// Exports an existing opaque root through native generation validation.
2944    pub(crate) fn export_transition_root(
2945        &mut self,
2946        root: SemanticRootHandle,
2947    ) -> Result<SemanticRootMaterial, SemanticHypergraphError> {
2948        self.ensure_not_poisoned()?;
2949        if root.owner != self.owner {
2950            return Err(SemanticHypergraphError::ForeignHandle {
2951                kind: SemanticHandleKind::Root,
2952            });
2953        }
2954        if self.admission.is_none() {
2955            return Err(admission_error(
2956                "root material export requires typed admission",
2957            ));
2958        }
2959        let mut command = self.command_for(OP_SNAPSHOT);
2960        write_view(&mut command, SemanticView::Root(root));
2961        let receipt = self.run(command, ArenaAccess::Read)?;
2962        self.expect_success(&receipt)?;
2963        let result = (|| {
2964            let snapshot = SemanticRootSnapshot::new(
2965                SemanticRootDigest(receipt_identity(&receipt, 16)),
2966                receipt_extents(&receipt)?,
2967            );
2968            let arena_words = usize::try_from(self.arena_words).map_err(|_| size_overflow())?;
2969            let mut arena = vec![0; arena_words];
2970            self.provider
2971                .dtoh_sync_copy_into_tracked(&self.arena, &mut arena)
2972                .map_err(|error| runtime_error("root material arena read", error))?;
2973            material_from_arena(
2974                &arena,
2975                self.capacities,
2976                root,
2977                snapshot,
2978                self.admission.as_ref().expect("checked typed admission"),
2979            )
2980        })();
2981        poison_after_reconciliation_error(&mut self.poisoned, result)
2982    }
2983
2984    /// Rematerializes a complete root on a fresh, already-admitted native owner.
2985    /// No original physical slot, generation, or encoded embedding is imported.
2986    pub(crate) fn restore_root(
2987        &mut self,
2988        material: &SemanticRootMaterial,
2989    ) -> Result<SemanticRootHandle, SemanticHypergraphError> {
2990        self.ensure_host_facade_available()?;
2991        let admission = self
2992            .admission
2993            .as_ref()
2994            .ok_or_else(|| admission_error("root restoration requires typed admission"))?;
2995        if admission.base != self.empty_root
2996            || self.current_fork.is_some()
2997            || self.fork.generation != 1
2998            || self.roots.iter().filter(|slot| slot.live).count() != 1
2999            || self.statements.iter().any(|slot| slot.live)
3000            || self.supports.iter().any(|slot| slot.live)
3001            || self.versions.iter().any(|slot| slot.live)
3002        {
3003            return Err(admission_error(
3004                "root restoration requires a fresh empty native owner",
3005            ));
3006        }
3007        material.validate_lineage(admission)?;
3008        let base_versions = material.admission_base_extents[2] as usize;
3009        let needed_roots = 1
3010            + u32::from(!material.insertions.is_empty())
3011            + u32::from(base_versions > 0 && base_versions < material.insertions.len());
3012        if material.extents[0] > self.capacities.statements
3013            || material.extents[1] > self.capacities.supports
3014            || material.extents[2] > self.capacities.versions
3015            || self.capacities.roots < needed_roots
3016        {
3017            return Err(admission_error(
3018                "root material exceeds fresh native capacities",
3019            ));
3020        }
3021        let result = (|| {
3022            let mut admission_root = self.empty_root;
3023            let root = if material.insertions.is_empty() {
3024                self.empty_root
3025            } else {
3026                let mut fork = self.fork(self.empty_root)?;
3027                for (index, insertion) in material.insertions.iter().enumerate() {
3028                    let admission = self.admission.as_ref().expect("checked typed admission");
3029                    let key = material_statement_key(
3030                        admission,
3031                        insertion.statement,
3032                        &insertion.reconstruction,
3033                    )?;
3034                    let event = admission.support_event(insertion.support)?;
3035                    match self.insert_support_reconstructed(
3036                        fork,
3037                        &key,
3038                        &event,
3039                        &insertion.reconstruction,
3040                    )? {
3041                        SemanticInsertOutcome::Inserted(inserted)
3042                            if inserted.version.identity.0 == insertion.version => {}
3043                        _ => {
3044                            return self
3045                                .corrupt("restored insertion differs from canonical root material")
3046                        }
3047                    }
3048                    if index + 1 == base_versions && base_versions < material.insertions.len() {
3049                        admission_root = self.seal(fork)?;
3050                        fork = self.fork(admission_root)?;
3051                    }
3052                }
3053                self.seal(fork)?
3054            };
3055            if base_versions == material.insertions.len() {
3056                admission_root = root;
3057            }
3058            let snapshot = self.snapshot(SemanticView::Root(root))?;
3059            if snapshot.digest.0 != material.digest
3060                || snapshot.extents
3061                    != SemanticExtents::new(
3062                        material.extents[0],
3063                        material.extents[1],
3064                        material.extents[2],
3065                    )
3066            {
3067                return self.corrupt(
3068                    "fresh native root differs from restored digest or reachable extents",
3069                );
3070            }
3071            let base_snapshot = if admission_root == root {
3072                snapshot
3073            } else {
3074                self.snapshot(SemanticView::Root(admission_root))?
3075            };
3076            if base_snapshot.digest.0 != material.admission_base_digest
3077                || base_snapshot.extents
3078                    != SemanticExtents::new(
3079                        material.admission_base_extents[0],
3080                        material.admission_base_extents[1],
3081                        material.admission_base_extents[2],
3082                    )
3083            {
3084                return self.corrupt(
3085                    "fresh native admission base differs from its retained original binding",
3086                );
3087            }
3088            let admission = self.admission.as_mut().expect("checked typed admission");
3089            admission.base = admission_root;
3090            admission.base_snapshot = base_snapshot;
3091            admission.identity = derive_admission_identity(
3092                &admission.records,
3093                admission.schema_generation,
3094                &admission.encoded_records,
3095                &admission.statement_keys,
3096                &admission.support_events,
3097                &base_snapshot,
3098            );
3099            Ok(root)
3100        })();
3101        poison_after_reconciliation_error(&mut self.poisoned, result)
3102    }
3103
3104    fn validate_statement_key(
3105        &self,
3106        key: &SemanticStatementKey,
3107    ) -> Result<(), SemanticHypergraphError> {
3108        if key.owner != self.owner
3109            || self
3110                .admission
3111                .as_ref()
3112                .and_then(|admission| admission.statement_keys.get(key.record as usize))
3113                != Some(&Some(*key))
3114        {
3115            return Err(admission_error(
3116                "statement key is not from this owner's typed admission",
3117            ));
3118        }
3119        Ok(())
3120    }
3121
3122    pub const fn empty_root(&self) -> SemanticRootHandle {
3123        self.empty_root
3124    }
3125
3126    pub const fn execution_stats(&self) -> SemanticHypergraphExecutionStats {
3127        self.stats
3128    }
3129
3130    pub fn fork(
3131        &mut self,
3132        base: SemanticRootHandle,
3133    ) -> Result<SemanticForkHandle, SemanticHypergraphError> {
3134        self.ensure_host_facade_available()?;
3135        self.validate_root(base)?;
3136        if self.current_fork.is_some() {
3137            return Err(SemanticHypergraphError::InactiveHandle {
3138                kind: SemanticHandleKind::Fork,
3139                slot: 0,
3140            });
3141        }
3142        let mut command = self.command_for(OP_FORK);
3143        command.words[1] = base.owner;
3144        command.words[8] = u64::from(base.slot);
3145        command.words[9] = base.generation;
3146        let receipt = self.run(command, ArenaAccess::ReadWrite)?;
3147        self.expect_success(&receipt)?;
3148        let result = (|| {
3149            let slot = checked_receipt_slot(receipt.words[5], SemanticHandleKind::Fork, 1)?;
3150            let generation = receipt.words[6];
3151            if slot != 0 || generation != self.fork.generation {
3152                return self.corrupt("fork receipt disagrees with host physical generation");
3153            }
3154            self.fork.live = true;
3155            let handle = SemanticForkHandle::new(self.owner, slot, generation);
3156            self.current_fork = Some(CurrentFork {
3157                handle,
3158                staged_statements: Vec::new(),
3159                staged_supports: Vec::new(),
3160                staged_versions: Vec::new(),
3161            });
3162            Ok(handle)
3163        })();
3164        poison_after_reconciliation_error(&mut self.poisoned, result)
3165    }
3166
3167    pub fn insert_support(
3168        &mut self,
3169        fork: SemanticForkHandle,
3170        statement: &SemanticStatementKey,
3171        event: &SemanticSupportEvent,
3172    ) -> Result<SemanticInsertOutcome, SemanticHypergraphError> {
3173        self.insert_support_reconstructed(fork, statement, event, &[0; 10])
3174    }
3175
3176    fn insert_support_reconstructed(
3177        &mut self,
3178        fork: SemanticForkHandle,
3179        statement: &SemanticStatementKey,
3180        event: &SemanticSupportEvent,
3181        reconstruction: &[u32; 10],
3182    ) -> Result<SemanticInsertOutcome, SemanticHypergraphError> {
3183        self.ensure_host_facade_available()?;
3184        self.validate_fork(fork)?;
3185        if statement.record == u32::MAX {
3186            let admission = self.admission.as_ref().ok_or_else(|| {
3187                admission_error("derived insertion requires retained typed admission")
3188            })?;
3189            if material_statement_key(admission, None, reconstruction)? != *statement {
3190                return Err(admission_error(
3191                    "derived insertion key differs from its typed reconstruction",
3192                ));
3193            }
3194        } else {
3195            if *reconstruction != [0; 10] {
3196                return Err(admission_error(
3197                    "original insertion has derived reconstruction references",
3198                ));
3199            }
3200            self.validate_statement_key(statement)?;
3201        }
3202        if event.owner != self.owner {
3203            return Err(admission_error(
3204                "support event does not belong to this owner's typed admission",
3205            ));
3206        }
3207        let mut command = self.command_for(OP_INSERT_SUPPORT);
3208        encode_support_insertion(&mut command, fork, statement, event, reconstruction);
3209        let receipt = self.run(command, ArenaAccess::ReadWrite)?;
3210        self.expect_success(&receipt)?;
3211        let result = (|| {
3212            let statement_ref = self.statement_ref(&receipt)?;
3213            let version_ref = self.version_ref(&receipt)?;
3214            match receipt.words[1] {
3215                OUTCOME_UNCHANGED => Ok(SemanticInsertOutcome::Unchanged(version_ref)),
3216                OUTCOME_INSERTED => {
3217                    let previous_truth = inserted_support_previous_truth(&receipt)?;
3218                    let support_ref = self.support_ref(&receipt)?;
3219                    let flags = receipt.words[32];
3220                    if flags & INSERT_NEW_STATEMENT != 0 {
3221                        self.publish_statement(statement_ref.handle)?;
3222                        self.current_fork
3223                            .as_mut()
3224                            .expect("validated live fork")
3225                            .staged_statements
3226                            .push(statement_ref.handle.slot);
3227                    } else {
3228                        self.validate_statement(statement_ref.handle)?;
3229                    }
3230                    self.publish_support(support_ref.handle)?;
3231                    self.publish_version(version_ref.handle)?;
3232                    let current = self.current_fork.as_mut().expect("validated live fork");
3233                    current.staged_supports.push(support_ref.handle.slot);
3234                    current.staged_versions.push(version_ref.handle.slot);
3235                    Ok(SemanticInsertOutcome::Inserted(SemanticInsertedSupport {
3236                        statement: statement_ref,
3237                        support: support_ref,
3238                        version: version_ref,
3239                        previous_truth,
3240                    }))
3241                }
3242                other => self.corrupt(format!("invalid insertion outcome {other}")),
3243            }
3244        })();
3245        poison_after_reconciliation_error(&mut self.poisoned, result)
3246    }
3247
3248    pub fn discard(&mut self, fork: SemanticForkHandle) -> Result<(), SemanticHypergraphError> {
3249        self.ensure_host_facade_available()?;
3250        self.validate_fork(fork)?;
3251        let mut command = self.command_for(OP_DISCARD);
3252        command.words[1] = fork.owner;
3253        command.words[8] = u64::from(fork.slot);
3254        command.words[9] = fork.generation;
3255        let receipt = self.run(command, ArenaAccess::ReadWrite)?;
3256        self.expect_success(&receipt)?;
3257        let result = (|| {
3258            let current = self.current_fork.take().expect("validated live fork");
3259            for slot in current.staged_statements {
3260                retire_slot(
3261                    &mut self.statements[slot as usize],
3262                    SemanticHandleKind::Statement,
3263                    slot,
3264                )?;
3265            }
3266            for slot in current.staged_supports {
3267                retire_slot(
3268                    &mut self.supports[slot as usize],
3269                    SemanticHandleKind::Support,
3270                    slot,
3271                )?;
3272            }
3273            for slot in current.staged_versions {
3274                retire_slot(
3275                    &mut self.versions[slot as usize],
3276                    SemanticHandleKind::Version,
3277                    slot,
3278                )?;
3279            }
3280            retire_slot(&mut self.fork, SemanticHandleKind::Fork, 0)?;
3281            if receipt.words[6] != self.fork.generation {
3282                return self.corrupt("discard receipt disagrees with advanced fork generation");
3283            }
3284            Ok(())
3285        })();
3286        poison_after_reconciliation_error(&mut self.poisoned, result)
3287    }
3288
3289    pub fn seal(
3290        &mut self,
3291        fork: SemanticForkHandle,
3292    ) -> Result<SemanticRootHandle, SemanticHypergraphError> {
3293        self.ensure_host_facade_available()?;
3294        self.validate_fork(fork)?;
3295        let mut command = self.command_for(OP_SEAL);
3296        command.words[1] = fork.owner;
3297        command.words[8] = u64::from(fork.slot);
3298        command.words[9] = fork.generation;
3299        let receipt = self.run(command, ArenaAccess::ReadWrite)?;
3300        self.expect_success(&receipt)?;
3301        let result = (|| {
3302            let slot = checked_receipt_slot(
3303                receipt.words[3],
3304                SemanticHandleKind::Root,
3305                self.capacities.roots,
3306            )?;
3307            let generation = receipt.words[4];
3308            let unchanged = match receipt.words[1] {
3309                OUTCOME_INSERTED => false,
3310                OUTCOME_UNCHANGED => true,
3311                _ => return self.corrupt("seal receipt has an invalid outcome"),
3312            };
3313            if unchanged {
3314                let current = self.current_fork.as_ref().expect("validated live fork");
3315                if !current.staged_statements.is_empty()
3316                    || !current.staged_supports.is_empty()
3317                    || !current.staged_versions.is_empty()
3318                {
3319                    return self.corrupt("unchanged seal discarded staged insertions");
3320                }
3321            }
3322            let root_ledger = &mut self.roots[slot as usize];
3323            if root_ledger.live != unchanged || root_ledger.generation != generation {
3324                return self.corrupt("seal receipt disagrees with root physical generation");
3325            }
3326            root_ledger.live = true;
3327            self.current_fork.take().expect("validated live fork");
3328            retire_slot(&mut self.fork, SemanticHandleKind::Fork, 0)?;
3329            if receipt.words[6] != self.fork.generation {
3330                return self.corrupt("seal receipt disagrees with advanced fork generation");
3331            }
3332            Ok(SemanticRootHandle::new(self.owner, slot, generation))
3333        })();
3334        poison_after_reconciliation_error(&mut self.poisoned, result)
3335    }
3336
3337    pub fn snapshot(
3338        &mut self,
3339        view: SemanticView,
3340    ) -> Result<SemanticRootSnapshot, SemanticHypergraphError> {
3341        self.ensure_host_facade_available()?;
3342        self.validate_view(view)?;
3343        let mut command = self.command_for(OP_SNAPSHOT);
3344        write_view(&mut command, view);
3345        let receipt = self.run(command, ArenaAccess::Read)?;
3346        self.expect_success(&receipt)?;
3347        let result = (|| {
3348            let extents = receipt_extents(&receipt)?;
3349            let digest = SemanticRootDigest(receipt_identity(&receipt, 16));
3350            Ok(SemanticRootSnapshot::new(digest, extents))
3351        })();
3352        poison_after_reconciliation_error(&mut self.poisoned, result)
3353    }
3354
3355    pub fn truth(
3356        &mut self,
3357        view: SemanticView,
3358        statement: &SemanticStatementKey,
3359    ) -> Result<SemanticTruth, SemanticHypergraphError> {
3360        self.ensure_host_facade_available()?;
3361        self.validate_view(view)?;
3362        self.validate_statement_key(statement)?;
3363        let mut command = self.command_for(OP_TRUTH);
3364        write_view(&mut command, view);
3365        command.words[16..20].copy_from_slice(&identity_words(statement.identity.0));
3366        let receipt = self.run(command, ArenaAccess::Read)?;
3367        self.expect_success(&receipt)?;
3368        let result = SemanticTruth::from_bits(receipt.words[2]);
3369        poison_after_reconciliation_error(&mut self.poisoned, result)
3370    }
3371
3372    pub fn inspect_statement(
3373        &mut self,
3374        view: SemanticView,
3375        handle: SemanticStatementHandle,
3376    ) -> Result<SemanticStatementRef, SemanticHypergraphError> {
3377        self.ensure_host_facade_available()?;
3378        self.validate_view(view)?;
3379        self.validate_statement(handle)?;
3380        let mut command = self.command_for(OP_INSPECT_STATEMENT);
3381        write_view(&mut command, view);
3382        command.words[10] = u64::from(handle.slot);
3383        command.words[11] = handle.generation;
3384        let receipt = self.run(command, ArenaAccess::Read)?;
3385        self.expect_success(&receipt)?;
3386        let result = self.statement_ref(&receipt);
3387        poison_after_reconciliation_error(&mut self.poisoned, result)
3388    }
3389
3390    pub fn inspect_support(
3391        &mut self,
3392        view: SemanticView,
3393        handle: SemanticSupportHandle,
3394    ) -> Result<SemanticSupportRef, SemanticHypergraphError> {
3395        self.ensure_host_facade_available()?;
3396        self.validate_view(view)?;
3397        self.validate_support(handle)?;
3398        let mut command = self.command_for(OP_INSPECT_SUPPORT);
3399        write_view(&mut command, view);
3400        command.words[10] = u64::from(handle.slot);
3401        command.words[11] = handle.generation;
3402        let receipt = self.run(command, ArenaAccess::Read)?;
3403        self.expect_success(&receipt)?;
3404        let result = self.support_ref(&receipt);
3405        poison_after_reconciliation_error(&mut self.poisoned, result)
3406    }
3407
3408    pub fn inspect_version(
3409        &mut self,
3410        view: SemanticView,
3411        handle: SemanticVersionHandle,
3412    ) -> Result<SemanticVersionRef, SemanticHypergraphError> {
3413        self.ensure_host_facade_available()?;
3414        self.validate_view(view)?;
3415        self.validate_version(handle)?;
3416        let mut command = self.command_for(OP_INSPECT_VERSION);
3417        write_view(&mut command, view);
3418        command.words[10] = u64::from(handle.slot);
3419        command.words[11] = handle.generation;
3420        let receipt = self.run(command, ArenaAccess::Read)?;
3421        self.expect_success(&receipt)?;
3422        let result = self.version_ref(&receipt);
3423        poison_after_reconciliation_error(&mut self.poisoned, result)
3424    }
3425
3426    fn command_for(&self, operation: u64) -> DeviceCommand {
3427        let mut command = DeviceCommand::default();
3428        command.words[0] = operation;
3429        command.words[1] = self.owner;
3430        command.words[2] = u64::from(self.capacities.roots);
3431        command.words[3] = u64::from(self.capacities.statements);
3432        command.words[4] = u64::from(self.capacities.supports);
3433        command.words[5] = u64::from(self.capacities.versions);
3434        command.words[6] = self.arena_words;
3435        command
3436    }
3437
3438    /// Materializes the immutable empty-root handle into a resident typed bank.
3439    #[cfg_attr(
3440        not(test),
3441        expect(
3442            dead_code,
3443            reason = "reserved for crate-internal device-resident pipelines"
3444        )
3445    )]
3446    pub(crate) fn enqueue_resident_empty_root_handle<'handle, 'receipt>(
3447        &mut self,
3448        handle: SemanticResidentHandleSlot<'handle>,
3449        receipt: SemanticResidentReceiptSlot<'receipt>,
3450    ) -> Result<SemanticResidentRootHandleBank<'handle>, SemanticHypergraphError> {
3451        let SemanticResidentHandleSlot {
3452            allocation: handles,
3453            index: handle_index,
3454        } = handle;
3455        self.enqueue_resident_input(
3456            SemanticKernelInput::ResidentEmptyRootHandle {
3457                handle: handles,
3458                index: handle_index,
3459            },
3460            ArenaAccess::Read,
3461            false,
3462            receipt,
3463        )?;
3464        Ok(SemanticResidentRootHandleBank {
3465            allocation: handles,
3466            index: handle_index,
3467        })
3468    }
3469
3470    /// Checks both lanes' full worst-case reserve without acquiring scratch.
3471    /// The owner must remain exclusive through both lane terminals; the returned
3472    /// root receipt propagates refusal, but is not a reusable allocation permit.
3473    #[cfg_attr(
3474        not(test),
3475        expect(dead_code, reason = "consumed by the resident transition owner")
3476    )]
3477    pub(crate) fn enqueue_resident_preflight_transition<'input, 'receipt>(
3478        &mut self,
3479        root: SemanticResidentRootHandle<'input>,
3480        receipt: SemanticResidentReceiptSlot<'receipt>,
3481    ) -> Result<SemanticResidentRootHandle<'receipt>, SemanticHypergraphError> {
3482        self.enqueue_resident_input(
3483            SemanticKernelInput::ResidentPreflightTransition { root },
3484            ArenaAccess::Read,
3485            false,
3486            receipt,
3487        )
3488        .map(SemanticResidentRootHandle::Receipt)
3489    }
3490
3491    /// Forks one immutable root selected from a resident generation-bound handle bank.
3492    #[cfg_attr(
3493        not(test),
3494        expect(
3495            dead_code,
3496            reason = "reserved for crate-internal device-resident pipelines"
3497        )
3498    )]
3499    pub(crate) fn enqueue_resident_fork<'input, 'receipt>(
3500        &mut self,
3501        root: SemanticResidentRootHandle<'input>,
3502        receipt: SemanticResidentReceiptSlot<'receipt>,
3503    ) -> Result<SemanticResidentMutationReceipt<'receipt>, SemanticHypergraphError> {
3504        let receipt = self.enqueue_resident_input(
3505            SemanticKernelInput::ResidentFork { root },
3506            ArenaAccess::ReadWrite,
3507            true,
3508            receipt,
3509        )?;
3510        Ok(SemanticResidentMutationReceipt { receipt })
3511    }
3512
3513    /// Inserts one support from typed resident decoded banks and a resident candidate handle.
3514    #[cfg_attr(
3515        not(test),
3516        expect(
3517            dead_code,
3518            reason = "reserved for crate-internal device-resident pipelines"
3519        )
3520    )]
3521    pub(crate) fn enqueue_resident_insert_support<'input, 'receipt>(
3522        &mut self,
3523        candidate: SemanticResidentCandidateHandle<'input>,
3524        statement: SemanticResidentStatementBank<'input>,
3525        support: SemanticResidentSupportBank<'input>,
3526        receipt: SemanticResidentReceiptSlot<'receipt>,
3527    ) -> Result<SemanticResidentMutationReceipt<'receipt>, SemanticHypergraphError> {
3528        let receipt = self.enqueue_resident_input(
3529            SemanticKernelInput::ResidentInsertSupport {
3530                candidate,
3531                statement,
3532                support,
3533            },
3534            ArenaAccess::ReadWrite,
3535            true,
3536            receipt,
3537        )?;
3538        Ok(SemanticResidentMutationReceipt { receipt })
3539    }
3540
3541    /// Seals a resident candidate whose generation is carried by a prior device receipt.
3542    #[cfg_attr(
3543        not(test),
3544        expect(
3545            dead_code,
3546            reason = "reserved for crate-internal device-resident pipelines"
3547        )
3548    )]
3549    pub(crate) fn enqueue_resident_seal<'input, 'receipt>(
3550        &mut self,
3551        candidate: SemanticResidentCandidateHandle<'input>,
3552        receipt: SemanticResidentReceiptSlot<'receipt>,
3553    ) -> Result<SemanticResidentSealReceipt<'receipt>, SemanticHypergraphError> {
3554        let receipt = self.enqueue_resident_input(
3555            SemanticKernelInput::ResidentSeal { candidate },
3556            ArenaAccess::ReadWrite,
3557            true,
3558            receipt,
3559        )?;
3560        Ok(SemanticResidentSealReceipt { receipt })
3561    }
3562
3563    /// Derives truth from an immutable root or live fork and a decoded statement identity.
3564    #[cfg_attr(
3565        not(test),
3566        expect(
3567            dead_code,
3568            reason = "reserved for crate-internal device-resident pipelines"
3569        )
3570    )]
3571    pub(crate) fn enqueue_resident_truth<'input, 'receipt>(
3572        &mut self,
3573        view: SemanticResidentView<'input>,
3574        statement: SemanticResidentStatementBank<'input>,
3575        receipt: SemanticResidentReceiptSlot<'receipt>,
3576    ) -> Result<SemanticResidentTruthReceipt<'receipt>, SemanticHypergraphError> {
3577        let receipt = self.enqueue_resident_input(
3578            SemanticKernelInput::ResidentTruth { view, statement },
3579            ArenaAccess::Read,
3580            false,
3581            receipt,
3582        )?;
3583        Ok(SemanticResidentTruthReceipt { receipt })
3584    }
3585
3586    /// Validates a pending truth receipt on device and projects its semantic value.
3587    #[cfg_attr(
3588        not(test),
3589        expect(
3590            dead_code,
3591            reason = "reserved for crate-internal device-resident pipelines"
3592        )
3593    )]
3594    pub(crate) fn enqueue_resident_truth_consumer<'input, 'output, 'receipt>(
3595        &mut self,
3596        truth: SemanticResidentTruthView<'input>,
3597        output: SemanticResidentTruthSlot<'output>,
3598        receipt: SemanticResidentReceiptSlot<'receipt>,
3599    ) -> Result<SemanticResidentReceiptView<'receipt>, SemanticHypergraphError> {
3600        self.enqueue_resident_input(
3601            SemanticKernelInput::ResidentConsumeTruth { truth, output },
3602            ArenaAccess::Read,
3603            false,
3604            receipt,
3605        )
3606    }
3607
3608    /// Splits a decoder-owned resident record into canonical semantic input banks.
3609    #[cfg_attr(
3610        not(test),
3611        expect(
3612            dead_code,
3613            reason = "reserved for crate-internal device-resident pipelines"
3614        )
3615    )]
3616    pub(crate) fn enqueue_resident_materialize_decoded<'input, 'receipt>(
3617        &mut self,
3618        input: SemanticResidentDecodedInputBank<'input>,
3619        statement: SemanticResidentStatementBank<'input>,
3620        support: SemanticResidentSupportBank<'input>,
3621        receipt: SemanticResidentReceiptSlot<'receipt>,
3622    ) -> Result<SemanticResidentReceiptView<'receipt>, SemanticHypergraphError> {
3623        self.enqueue_resident_input(
3624            SemanticKernelInput::ResidentMaterializeDecoded {
3625                input,
3626                statement,
3627                support,
3628            },
3629            ArenaAccess::Read,
3630            false,
3631            receipt,
3632        )
3633    }
3634
3635    fn enqueue_resident_input<'receipt>(
3636        &mut self,
3637        input: SemanticKernelInput<'_>,
3638        arena_access: ArenaAccess,
3639        device_mutation: bool,
3640        receipt: SemanticResidentReceiptSlot<'receipt>,
3641    ) -> Result<SemanticResidentReceiptView<'receipt>, SemanticHypergraphError> {
3642        self.ensure_not_poisoned()?;
3643        let next_launches = self.next_launch_count()?;
3644        let SemanticResidentReceiptSlot {
3645            allocation: receipts,
3646            index: receipt_index,
3647        } = receipt;
3648        enqueue_device_command(
3649            SemanticKernelLaunchSpec {
3650                domain: &self.domain,
3651                execute: &self.execute,
3652                owner: self.owner,
3653                capacities: self.capacities,
3654                arena_words: self.arena_words,
3655            },
3656            SemanticKernelLaunchIo {
3657                arena: &mut self.arena,
3658                arena_access,
3659                input,
3660                receipts,
3661                receipt_index,
3662            },
3663            &mut self.poisoned,
3664        )?;
3665        self.stats.cuda_kernel_launches = next_launches;
3666        self.device_controlled |= device_mutation;
3667        Ok(SemanticResidentReceiptView {
3668            allocation: receipts,
3669            index: receipt_index,
3670        })
3671    }
3672
3673    fn run(
3674        &mut self,
3675        command: DeviceCommand,
3676        arena_access: ArenaAccess,
3677    ) -> Result<DeviceReceipt, SemanticHypergraphError> {
3678        let next_launches = self.next_launch_count()?;
3679        self.provider
3680            .htod_launch_metadata_sync_copy_into(&[command], &mut self.command)
3681            .map_err(|error| runtime_error("command upload", error))?;
3682        enqueue_device_command(
3683            SemanticKernelLaunchSpec {
3684                domain: &self.domain,
3685                execute: &self.execute,
3686                owner: self.owner,
3687                capacities: self.capacities,
3688                arena_words: self.arena_words,
3689            },
3690            SemanticKernelLaunchIo {
3691                arena: &mut self.arena,
3692                arena_access,
3693                input: SemanticKernelInput::HostCommand {
3694                    commands: &self.command,
3695                    index: 0,
3696                },
3697                receipts: &mut self.receipt,
3698                receipt_index: 0,
3699            },
3700            &mut self.poisoned,
3701        )?;
3702        self.stats.cuda_kernel_launches = next_launches;
3703        if let Err(error) = self.stream.synchronize() {
3704            self.poisoned = true;
3705            return Err(SemanticHypergraphError::Runtime {
3706                operation: "stream synchronization",
3707                detail: error.to_string(),
3708            });
3709        }
3710        let mut receipts = match self
3711            .provider
3712            .dtoh_small_metadata_untracked(&self.receipt, 1)
3713        {
3714            Ok(receipts) => receipts,
3715            Err(error) => {
3716                self.poisoned = true;
3717                return Err(runtime_error("receipt read", error));
3718            }
3719        };
3720        receipts.pop().ok_or_else(|| {
3721            self.poisoned = true;
3722            SemanticHypergraphError::CorruptLineage {
3723                detail: "CUDA receipt read returned no record".into(),
3724            }
3725        })
3726    }
3727
3728    fn next_launch_count(&self) -> Result<u64, SemanticHypergraphError> {
3729        self.stats.cuda_kernel_launches.checked_add(1).ok_or(
3730            SemanticHypergraphError::GenerationExhausted {
3731                kind: SemanticHandleKind::Root,
3732                slot: 0,
3733            },
3734        )
3735    }
3736
3737    fn expect_success(&mut self, receipt: &DeviceReceipt) -> Result<(), SemanticHypergraphError> {
3738        let status = receipt.words[0];
3739        if status == STATUS_OK {
3740            return Ok(());
3741        }
3742        let kind = decode_kind(receipt.words[33]);
3743        let slot = u32::try_from(receipt.words[34]).unwrap_or(u32::MAX);
3744        let result = match status {
3745            STATUS_FOREIGN_OWNER => Err(SemanticHypergraphError::ForeignHandle { kind }),
3746            STATUS_SLOT_OUT_OF_RANGE => Err(SemanticHypergraphError::SlotOutOfRange {
3747                kind,
3748                slot,
3749                capacity: u32::try_from(receipt.words[37]).unwrap_or(u32::MAX),
3750            }),
3751            STATUS_STALE_GENERATION => Err(SemanticHypergraphError::StaleGeneration {
3752                kind,
3753                slot,
3754                presented: receipt.words[35],
3755                current: receipt.words[36],
3756            }),
3757            STATUS_INACTIVE => Err(SemanticHypergraphError::InactiveHandle { kind, slot }),
3758            STATUS_NOT_REACHABLE => Err(SemanticHypergraphError::NotReachable { kind, slot }),
3759            STATUS_STATEMENT_CAPACITY => Err(SemanticHypergraphError::CapacityExceeded {
3760                kind: SemanticHandleKind::Statement,
3761                capacity: self.capacities.statements,
3762            }),
3763            STATUS_SUPPORT_CAPACITY => Err(SemanticHypergraphError::CapacityExceeded {
3764                kind: SemanticHandleKind::Support,
3765                capacity: self.capacities.supports,
3766            }),
3767            STATUS_VERSION_CAPACITY => Err(SemanticHypergraphError::CapacityExceeded {
3768                kind: SemanticHandleKind::Version,
3769                capacity: self.capacities.versions,
3770            }),
3771            STATUS_ROOT_CAPACITY => Err(SemanticHypergraphError::CapacityExceeded {
3772                kind: SemanticHandleKind::Root,
3773                capacity: self.capacities.roots,
3774            }),
3775            STATUS_GENERATION_EXHAUSTED => {
3776                Err(SemanticHypergraphError::GenerationExhausted { kind, slot })
3777            }
3778            STATUS_CORRUPT_LINEAGE => Err(SemanticHypergraphError::CorruptLineage {
3779                detail: format!("device validation code {}", receipt.words[38]),
3780            }),
3781            STATUS_INVALID_COMMAND => Err(SemanticHypergraphError::CorruptLineage {
3782                detail: "device rejected an internal operation code".into(),
3783            }),
3784            STATUS_ARENA_MISMATCH => Err(SemanticHypergraphError::CorruptLineage {
3785                detail: "device and host semantic arena layouts disagree".into(),
3786            }),
3787            _ => Err(SemanticHypergraphError::CorruptLineage {
3788                detail: format!("unknown device status {status}"),
3789            }),
3790        };
3791        if device_status_is_integrity_failure(status) {
3792            self.poisoned = true;
3793        }
3794        result
3795    }
3796
3797    fn statement_ref(
3798        &self,
3799        receipt: &DeviceReceipt,
3800    ) -> Result<SemanticStatementRef, SemanticHypergraphError> {
3801        let slot = checked_receipt_slot(
3802            receipt.words[7],
3803            SemanticHandleKind::Statement,
3804            self.capacities.statements,
3805        )?;
3806        Ok(SemanticStatementRef {
3807            handle: SemanticStatementHandle::new(self.owner, slot, receipt.words[8]),
3808            identity: SemanticStatementIdentity(receipt_identity(receipt, 20)),
3809        })
3810    }
3811
3812    fn support_ref(
3813        &self,
3814        receipt: &DeviceReceipt,
3815    ) -> Result<SemanticSupportRef, SemanticHypergraphError> {
3816        let slot = checked_receipt_slot(
3817            receipt.words[9],
3818            SemanticHandleKind::Support,
3819            self.capacities.supports,
3820        )?;
3821        Ok(SemanticSupportRef {
3822            handle: SemanticSupportHandle::new(self.owner, slot, receipt.words[10]),
3823            identity: SemanticSupportIdentity(receipt_identity(receipt, 24)),
3824        })
3825    }
3826
3827    fn version_ref(
3828        &self,
3829        receipt: &DeviceReceipt,
3830    ) -> Result<SemanticVersionRef, SemanticHypergraphError> {
3831        let slot = checked_receipt_slot(
3832            receipt.words[11],
3833            SemanticHandleKind::Version,
3834            self.capacities.versions,
3835        )?;
3836        Ok(SemanticVersionRef {
3837            handle: SemanticVersionHandle::new(self.owner, slot, receipt.words[12]),
3838            identity: SemanticVersionIdentity(receipt_identity(receipt, 28)),
3839            truth: SemanticTruth::from_bits(receipt.words[2])?,
3840        })
3841    }
3842
3843    fn publish_statement(
3844        &mut self,
3845        handle: SemanticStatementHandle,
3846    ) -> Result<(), SemanticHypergraphError> {
3847        publish_slot(
3848            &mut self.statements,
3849            handle.slot,
3850            handle.generation,
3851            SemanticHandleKind::Statement,
3852        )
3853    }
3854
3855    fn publish_support(
3856        &mut self,
3857        handle: SemanticSupportHandle,
3858    ) -> Result<(), SemanticHypergraphError> {
3859        publish_slot(
3860            &mut self.supports,
3861            handle.slot,
3862            handle.generation,
3863            SemanticHandleKind::Support,
3864        )
3865    }
3866
3867    fn publish_version(
3868        &mut self,
3869        handle: SemanticVersionHandle,
3870    ) -> Result<(), SemanticHypergraphError> {
3871        publish_slot(
3872            &mut self.versions,
3873            handle.slot,
3874            handle.generation,
3875            SemanticHandleKind::Version,
3876        )
3877    }
3878
3879    fn validate_view(&self, view: SemanticView) -> Result<(), SemanticHypergraphError> {
3880        match view {
3881            SemanticView::Root(handle) => self.validate_root(handle),
3882            SemanticView::Fork(handle) => self.validate_fork(handle),
3883        }
3884    }
3885
3886    fn validate_root(&self, handle: SemanticRootHandle) -> Result<(), SemanticHypergraphError> {
3887        validate_slot(
3888            self.owner,
3889            handle.owner,
3890            handle.slot,
3891            handle.generation,
3892            &self.roots,
3893            SemanticHandleKind::Root,
3894        )
3895    }
3896
3897    fn validate_fork(&self, handle: SemanticForkHandle) -> Result<(), SemanticHypergraphError> {
3898        if handle.owner != self.owner {
3899            return Err(SemanticHypergraphError::ForeignHandle {
3900                kind: SemanticHandleKind::Fork,
3901            });
3902        }
3903        if handle.slot != 0 {
3904            return Err(SemanticHypergraphError::SlotOutOfRange {
3905                kind: SemanticHandleKind::Fork,
3906                slot: handle.slot,
3907                capacity: 1,
3908            });
3909        }
3910        if handle.generation != self.fork.generation {
3911            return Err(SemanticHypergraphError::StaleGeneration {
3912                kind: SemanticHandleKind::Fork,
3913                slot: 0,
3914                presented: handle.generation,
3915                current: self.fork.generation,
3916            });
3917        }
3918        if !self.fork.live
3919            || self
3920                .current_fork
3921                .as_ref()
3922                .is_none_or(|current| current.handle != handle)
3923        {
3924            return Err(SemanticHypergraphError::InactiveHandle {
3925                kind: SemanticHandleKind::Fork,
3926                slot: 0,
3927            });
3928        }
3929        Ok(())
3930    }
3931
3932    fn validate_statement(
3933        &self,
3934        handle: SemanticStatementHandle,
3935    ) -> Result<(), SemanticHypergraphError> {
3936        validate_slot(
3937            self.owner,
3938            handle.owner,
3939            handle.slot,
3940            handle.generation,
3941            &self.statements,
3942            SemanticHandleKind::Statement,
3943        )
3944    }
3945
3946    fn validate_support(
3947        &self,
3948        handle: SemanticSupportHandle,
3949    ) -> Result<(), SemanticHypergraphError> {
3950        validate_slot(
3951            self.owner,
3952            handle.owner,
3953            handle.slot,
3954            handle.generation,
3955            &self.supports,
3956            SemanticHandleKind::Support,
3957        )
3958    }
3959
3960    fn validate_version(
3961        &self,
3962        handle: SemanticVersionHandle,
3963    ) -> Result<(), SemanticHypergraphError> {
3964        validate_slot(
3965            self.owner,
3966            handle.owner,
3967            handle.slot,
3968            handle.generation,
3969            &self.versions,
3970            SemanticHandleKind::Version,
3971        )
3972    }
3973
3974    pub(crate) fn ensure_not_poisoned(&self) -> Result<(), SemanticHypergraphError> {
3975        if self.poisoned {
3976            Err(SemanticHypergraphError::Poisoned)
3977        } else {
3978            Ok(())
3979        }
3980    }
3981
3982    fn ensure_host_facade_available(&self) -> Result<(), SemanticHypergraphError> {
3983        self.ensure_not_poisoned()?;
3984        if self.device_controlled {
3985            return Err(SemanticHypergraphError::DeviceControlled);
3986        }
3987        Ok(())
3988    }
3989
3990    fn corrupt<T>(&self, detail: impl Into<String>) -> Result<T, SemanticHypergraphError> {
3991        Err(SemanticHypergraphError::CorruptLineage {
3992            detail: detail.into(),
3993        })
3994    }
3995}
3996
3997fn device_status_is_integrity_failure(status: u64) -> bool {
3998    !matches!(
3999        status,
4000        STATUS_OK
4001            | STATUS_NOT_REACHABLE
4002            | STATUS_STATEMENT_CAPACITY
4003            | STATUS_SUPPORT_CAPACITY
4004            | STATUS_VERSION_CAPACITY
4005            | STATUS_ROOT_CAPACITY
4006            | STATUS_GENERATION_EXHAUSTED
4007    )
4008}
4009
4010fn poison_after_reconciliation_error<T>(
4011    poisoned: &mut bool,
4012    result: Result<T, SemanticHypergraphError>,
4013) -> Result<T, SemanticHypergraphError> {
4014    if result.is_err() {
4015        *poisoned = true;
4016    }
4017    result
4018}
4019
4020fn checked_arena_words(
4021    capacities: SemanticHypergraphCapacities,
4022) -> Result<u64, SemanticHypergraphError> {
4023    let roots = u64::from(capacities.roots);
4024    let statements = u64::from(capacities.statements);
4025    let supports = u64::from(capacities.supports);
4026    let versions = u64::from(capacities.versions);
4027    CONTROL_WORDS
4028        .checked_add(roots.checked_mul(ROOT_WORDS).ok_or_else(size_overflow)?)
4029        .and_then(|words| words.checked_add(CANDIDATE_WORDS))
4030        .and_then(|words| words.checked_add(statements.checked_mul(STATEMENT_WORDS)?))
4031        .and_then(|words| words.checked_add(supports.checked_mul(SUPPORT_WORDS)?))
4032        .and_then(|words| words.checked_add(versions.checked_mul(VERSION_WORDS)?))
4033        .and_then(|words| words.checked_add(roots.checked_mul(statements)?))
4034        .and_then(|words| words.checked_add(statements))
4035        .ok_or_else(size_overflow)
4036}
4037
4038fn size_overflow() -> SemanticHypergraphError {
4039    SemanticHypergraphError::InvalidInput {
4040        detail: "semantic arena size overflow".into(),
4041    }
4042}
4043
4044fn validate_slot(
4045    expected_owner: u64,
4046    presented_owner: u64,
4047    slot: u32,
4048    generation: u64,
4049    ledger: &[SlotLedger],
4050    kind: SemanticHandleKind,
4051) -> Result<(), SemanticHypergraphError> {
4052    if presented_owner != expected_owner {
4053        return Err(SemanticHypergraphError::ForeignHandle { kind });
4054    }
4055    let entry = ledger
4056        .get(slot as usize)
4057        .ok_or(SemanticHypergraphError::SlotOutOfRange {
4058            kind,
4059            slot,
4060            capacity: u32::try_from(ledger.len()).unwrap_or(u32::MAX),
4061        })?;
4062    if generation != entry.generation {
4063        return Err(SemanticHypergraphError::StaleGeneration {
4064            kind,
4065            slot,
4066            presented: generation,
4067            current: entry.generation,
4068        });
4069    }
4070    if !entry.live {
4071        return Err(SemanticHypergraphError::InactiveHandle { kind, slot });
4072    }
4073    Ok(())
4074}
4075
4076fn publish_slot(
4077    ledger: &mut [SlotLedger],
4078    slot: u32,
4079    generation: u64,
4080    kind: SemanticHandleKind,
4081) -> Result<(), SemanticHypergraphError> {
4082    let capacity = u32::try_from(ledger.len()).unwrap_or(u32::MAX);
4083    let entry = ledger
4084        .get_mut(slot as usize)
4085        .ok_or(SemanticHypergraphError::SlotOutOfRange {
4086            kind,
4087            slot,
4088            capacity,
4089        })?;
4090    if entry.live || entry.generation != generation {
4091        return Err(SemanticHypergraphError::CorruptLineage {
4092            detail: format!("device published inconsistent {kind:?} slot {slot}"),
4093        });
4094    }
4095    entry.live = true;
4096    Ok(())
4097}
4098
4099fn retire_slot(
4100    slot: &mut SlotLedger,
4101    kind: SemanticHandleKind,
4102    index: u32,
4103) -> Result<(), SemanticHypergraphError> {
4104    slot.generation = slot
4105        .generation
4106        .checked_add(1)
4107        .ok_or(SemanticHypergraphError::GenerationExhausted { kind, slot: index })?;
4108    slot.live = false;
4109    Ok(())
4110}
4111
4112fn enqueue_device_command(
4113    spec: SemanticKernelLaunchSpec<'_>,
4114    io: SemanticKernelLaunchIo<'_>,
4115    poisoned: &mut bool,
4116) -> Result<(), SemanticHypergraphError> {
4117    let SemanticKernelLaunchIo {
4118        arena,
4119        arena_access,
4120        input,
4121        receipts,
4122        receipt_index,
4123    } = io;
4124    let mut recorder = spec.domain.new_strict_recorder();
4125    match arena_access {
4126        ArenaAccess::Read => {
4127            recorder.read(arena);
4128        }
4129        ArenaAccess::ReadWrite => {
4130            recorder.read_write(arena);
4131        }
4132    }
4133    recorder.write(receipts);
4134    let mut descriptor = DeviceLaunchDescriptor {
4135        expected_owner: spec.owner,
4136        root_capacity: u64::from(spec.capacities.roots),
4137        statement_capacity: u64::from(spec.capacities.statements),
4138        support_capacity: u64::from(spec.capacities.supports),
4139        version_capacity: u64::from(spec.capacities.versions),
4140        arena_words: spec.arena_words,
4141        command_ptr: 0,
4142        command_index: 0,
4143        handle_ptr: 0,
4144        handle_index: 0,
4145        source_receipt_ptr: 0,
4146        source_receipt_index: 0,
4147        decoded_input_ptr: 0,
4148        decoded_input_index: 0,
4149        decoded_statement_ptr: 0,
4150        decoded_statement_index: 0,
4151        decoded_support_ptr: 0,
4152        decoded_support_index: 0,
4153        output_ptr: 0,
4154        output_index: 0,
4155        receipt_ptr: receipts.device_ptr_value(),
4156        receipt_index: u64::from(receipt_index),
4157        admission: HOST_COMMAND_ADMISSION,
4158        abi_generation: HYPERGRAPH_ABI_GENERATION,
4159    };
4160    let preflight = matches!(
4161        &input,
4162        SemanticKernelInput::ResidentPreflightTransition { .. }
4163    );
4164    match input {
4165        SemanticKernelInput::HostCommand { commands, index } => {
4166            recorder.read(commands);
4167            descriptor.command_ptr = commands.device_ptr_value();
4168            descriptor.command_index = u64::from(index);
4169        }
4170        SemanticKernelInput::ResidentEmptyRootHandle { handle, index } => {
4171            recorder.write(handle);
4172            descriptor.handle_ptr = handle.device_ptr_value();
4173            descriptor.handle_index = u64::from(index);
4174            descriptor.admission = RESIDENT_EMPTY_ROOT_HANDLE_ADMISSION;
4175        }
4176        SemanticKernelInput::ResidentFork { root }
4177        | SemanticKernelInput::ResidentPreflightTransition { root } => {
4178            root.record_input(&mut descriptor, &mut recorder);
4179            descriptor.admission = if preflight {
4180                RESIDENT_PREFLIGHT_TRANSITION_ADMISSION
4181            } else {
4182                RESIDENT_FORK_ADMISSION
4183            };
4184        }
4185        SemanticKernelInput::ResidentInsertSupport {
4186            candidate,
4187            statement,
4188            support,
4189        } => {
4190            candidate.receipt.record_read(&mut recorder);
4191            descriptor.source_receipt_ptr = candidate.receipt.allocation.device_ptr_value();
4192            descriptor.source_receipt_index = u64::from(candidate.receipt.index);
4193            recorder.read(statement.allocation);
4194            recorder.read(support.allocation);
4195            descriptor.decoded_statement_ptr = statement.allocation.device_ptr_value();
4196            descriptor.decoded_statement_index = u64::from(statement.index);
4197            descriptor.decoded_support_ptr = support.allocation.device_ptr_value();
4198            descriptor.decoded_support_index = u64::from(support.index);
4199            descriptor.admission = RESIDENT_INSERT_SUPPORT_ADMISSION;
4200        }
4201        SemanticKernelInput::ResidentSeal { candidate } => {
4202            candidate.receipt.record_read(&mut recorder);
4203            descriptor.source_receipt_ptr = candidate.receipt.allocation.device_ptr_value();
4204            descriptor.source_receipt_index = u64::from(candidate.receipt.index);
4205            descriptor.admission = RESIDENT_SEAL_ADMISSION;
4206        }
4207        SemanticKernelInput::ResidentTruth { view, statement } => {
4208            match view {
4209                SemanticResidentView::Root(root) => {
4210                    root.record_input(&mut descriptor, &mut recorder);
4211                    descriptor.admission = RESIDENT_ROOT_TRUTH_ADMISSION;
4212                }
4213                SemanticResidentView::Fork(candidate) => {
4214                    candidate.receipt.record_read(&mut recorder);
4215                    descriptor.source_receipt_ptr = candidate.receipt.allocation.device_ptr_value();
4216                    descriptor.source_receipt_index = u64::from(candidate.receipt.index);
4217                    descriptor.admission = RESIDENT_FORK_TRUTH_ADMISSION;
4218                }
4219            }
4220            recorder.read(statement.allocation);
4221            descriptor.decoded_statement_ptr = statement.allocation.device_ptr_value();
4222            descriptor.decoded_statement_index = u64::from(statement.index);
4223        }
4224        SemanticKernelInput::ResidentConsumeTruth { truth, output } => {
4225            truth.receipt.record_read(&mut recorder);
4226            recorder.write(output.allocation);
4227            descriptor.source_receipt_ptr = truth.receipt.allocation.device_ptr_value();
4228            descriptor.source_receipt_index = u64::from(truth.receipt.index);
4229            descriptor.output_ptr = output.allocation.device_ptr_value();
4230            descriptor.output_index = u64::from(output.index);
4231            descriptor.admission = RESIDENT_CONSUME_TRUTH_ADMISSION;
4232        }
4233        SemanticKernelInput::ResidentMaterializeDecoded {
4234            input,
4235            statement,
4236            support,
4237        } => {
4238            recorder.read(input.allocation);
4239            recorder.write(statement.allocation);
4240            recorder.write(support.allocation);
4241            descriptor.decoded_input_ptr = input.allocation.device_ptr_value();
4242            descriptor.decoded_input_index = u64::from(input.index);
4243            descriptor.decoded_statement_ptr = statement.allocation.device_ptr_value();
4244            descriptor.decoded_statement_index = u64::from(statement.index);
4245            descriptor.decoded_support_ptr = support.allocation.device_ptr_value();
4246            descriptor.decoded_support_index = u64::from(support.index);
4247            descriptor.admission = RESIDENT_MATERIALIZE_DECODED_ADMISSION;
4248        }
4249    }
4250    let execute = spec.execute.clone();
4251    let enqueued = match unsafe {
4252        spec.domain.enqueue(recorder, |stream| {
4253            execute.clone().launch_in(
4254                stream,
4255                LaunchConfig {
4256                    grid_dim: (1, 1, 1),
4257                    block_dim: (1, 1, 1),
4258                    shared_mem_bytes: 0,
4259                },
4260                (arena, descriptor),
4261            )
4262        })
4263    } {
4264        Ok(enqueued) => enqueued,
4265        Err(error) => {
4266            if matches!(
4267                &error,
4268                LaunchEnqueueError::Operation(_) | LaunchEnqueueError::OperationAndCleanup { .. }
4269            ) {
4270                *poisoned = true;
4271            }
4272            return Err(map_enqueue_error(error));
4273        }
4274    };
4275    if let Err(error) = enqueued.commit() {
4276        *poisoned = true;
4277        return Err(runtime_error("launch commit", error));
4278    }
4279    Ok(())
4280}
4281
4282fn runtime_error(operation: &'static str, error: XlogError) -> SemanticHypergraphError {
4283    SemanticHypergraphError::Runtime {
4284        operation,
4285        detail: error.to_string(),
4286    }
4287}
4288
4289fn validate_resident_bank_index(
4290    index: u32,
4291    len: usize,
4292    role: &'static str,
4293) -> Result<(), SemanticHypergraphError> {
4294    if (index as usize) < len {
4295        return Ok(());
4296    }
4297    Err(SemanticHypergraphError::InvalidInput {
4298        detail: format!("resident semantic {role} index {index} exceeds bank length {len}"),
4299    })
4300}
4301
4302fn map_enqueue_error(error: LaunchEnqueueError<DriverError>) -> SemanticHypergraphError {
4303    let operation = match &error {
4304        LaunchEnqueueError::Preparation(_) => "launch preparation",
4305        LaunchEnqueueError::PreparationAndCleanup { .. } => "launch preparation and cleanup",
4306        LaunchEnqueueError::Operation(_) => "kernel enqueue",
4307        LaunchEnqueueError::OperationAndCleanup { .. } => "kernel enqueue and cleanup",
4308    };
4309    SemanticHypergraphError::Runtime {
4310        operation,
4311        detail: error.to_string(),
4312    }
4313}
4314
4315fn identity_words(bytes: [u8; 32]) -> [u64; 4] {
4316    core::array::from_fn(|index| {
4317        let start = index * 8;
4318        u64::from_le_bytes(
4319            bytes[start..start + 8]
4320                .try_into()
4321                .expect("fixed identity chunk"),
4322        )
4323    })
4324}
4325
4326fn receipt_identity(receipt: &DeviceReceipt, start: usize) -> [u8; 32] {
4327    let mut bytes = [0u8; 32];
4328    for (index, word) in receipt.words[start..start + 4].iter().enumerate() {
4329        bytes[index * 8..index * 8 + 8].copy_from_slice(&word.to_le_bytes());
4330    }
4331    bytes
4332}
4333
4334fn inserted_support_previous_truth(
4335    receipt: &DeviceReceipt,
4336) -> Result<SemanticTruth, SemanticHypergraphError> {
4337    let metadata = receipt.words[32];
4338    let previous = (metadata & INSERT_PREVIOUS_TRUTH_MASK) >> INSERT_PREVIOUS_TRUTH_SHIFT;
4339    let resulting = receipt.words[2];
4340    let new_statement = metadata & INSERT_NEW_STATEMENT != 0;
4341    if receipt.words[0] != STATUS_OK
4342        || receipt.words[1] != OUTCOME_INSERTED
4343        || metadata & !(INSERT_NEW_STATEMENT | INSERT_PREVIOUS_TRUTH_MASK) != 0
4344        || new_statement != (previous == 0)
4345        || !(1..=3).contains(&resulting)
4346        || previous & resulting != previous
4347        || (previous ^ resulting).count_ones() > 1
4348    {
4349        return Err(SemanticHypergraphError::CorruptLineage {
4350            detail: "insertion receipt has invalid previous or resulting truth".into(),
4351        });
4352    }
4353    SemanticTruth::from_bits(previous)
4354}
4355
4356fn receipt_extents(receipt: &DeviceReceipt) -> Result<SemanticExtents, SemanticHypergraphError> {
4357    Ok(SemanticExtents::new(
4358        u32::try_from(receipt.words[13]).map_err(|_| SemanticHypergraphError::CorruptLineage {
4359            detail: "statement extent exceeds u32".into(),
4360        })?,
4361        u32::try_from(receipt.words[14]).map_err(|_| SemanticHypergraphError::CorruptLineage {
4362            detail: "support extent exceeds u32".into(),
4363        })?,
4364        u32::try_from(receipt.words[15]).map_err(|_| SemanticHypergraphError::CorruptLineage {
4365            detail: "version extent exceeds u32".into(),
4366        })?,
4367    ))
4368}
4369
4370fn checked_receipt_slot(
4371    value: u64,
4372    kind: SemanticHandleKind,
4373    capacity: u32,
4374) -> Result<u32, SemanticHypergraphError> {
4375    let slot = u32::try_from(value).map_err(|_| SemanticHypergraphError::CorruptLineage {
4376        detail: format!("device returned non-u32 {kind:?} slot {value}"),
4377    })?;
4378    if slot >= capacity {
4379        return Err(SemanticHypergraphError::CorruptLineage {
4380            detail: format!("device returned out-of-range {kind:?} slot {slot}"),
4381        });
4382    }
4383    Ok(slot)
4384}
4385
4386fn write_view(command: &mut DeviceCommand, view: SemanticView) {
4387    match view {
4388        SemanticView::Root(handle) => {
4389            command.words[1] = handle.owner;
4390            command.words[7] = 1;
4391            command.words[8] = u64::from(handle.slot);
4392            command.words[9] = handle.generation;
4393        }
4394        SemanticView::Fork(handle) => {
4395            command.words[1] = handle.owner;
4396            command.words[7] = 2;
4397            command.words[8] = u64::from(handle.slot);
4398            command.words[9] = handle.generation;
4399        }
4400    }
4401}
4402
4403fn decode_kind(code: u64) -> SemanticHandleKind {
4404    match code {
4405        1 => SemanticHandleKind::Root,
4406        2 => SemanticHandleKind::Fork,
4407        3 => SemanticHandleKind::Statement,
4408        4 => SemanticHandleKind::Support,
4409        5 => SemanticHandleKind::Version,
4410        _ => SemanticHandleKind::Root,
4411    }
4412}
4413
4414#[cfg(test)]
4415pub(crate) mod tests {
4416    use super::*;
4417    use crate::cuda_graph::CapturedCudaGraph;
4418    use crate::CudaProviderBuilder;
4419    use xlog_core::MemoryBudget;
4420
4421    const OP_RETIRE_ROOT: u64 = 12;
4422
4423    fn kernel_error(error: impl fmt::Display) -> XlogError {
4424        XlogError::Kernel(error.to_string())
4425    }
4426
4427    fn download_test_arena(provider: &CudaKernelProvider, graph: &SemanticHypergraph) -> Vec<u64> {
4428        let mut arena = vec![0; graph.arena_words as usize];
4429        provider
4430            .dtoh_sync_copy_into_tracked(&graph.arena, &mut arena)
4431            .unwrap();
4432        arena
4433    }
4434
4435    fn retirement_test_graph() -> Option<SemanticHypergraph> {
4436        if std::env::var("XLOG_REQUIRE_CUDA").as_deref() != Ok("1") {
4437            eprintln!("Skipping: set XLOG_REQUIRE_CUDA=1 to run this real-CUDA contract");
4438            return None;
4439        }
4440        let provider = Arc::new(
4441            CudaProviderBuilder::new(0, MemoryBudget::with_limit(64 * 1024 * 1024))
4442                .with_stream_capacity(1)
4443                .build()
4444                .unwrap(),
4445        );
4446        let runtime = Arc::clone(provider.memory().runtime().unwrap());
4447        let stream_id = runtime.stream_pool().acquire().unwrap();
4448        let stream = runtime.stream_pool().resolve(stream_id).unwrap();
4449        let domain = provider
4450            .bind_resident_execution_domain(runtime, stream_id, stream)
4451            .unwrap();
4452        let mut graph = provider
4453            .allocate_semantic_hypergraph(
4454                &domain,
4455                SemanticHypergraphCapacities::try_new(8, 8, 16, 16).unwrap(),
4456            )
4457            .unwrap();
4458        graph.enter_transition();
4459        Some(graph)
4460    }
4461
4462    fn retirement_command(
4463        graph: &mut SemanticHypergraph,
4464        operation: u64,
4465        fields: &[(usize, u64)],
4466    ) -> DeviceReceipt {
4467        let mut command = graph.command_for(operation);
4468        for &(index, value) in fields {
4469            command.words[index] = value;
4470        }
4471        graph.run(command, ArenaAccess::ReadWrite).unwrap()
4472    }
4473
4474    fn retirement_root(
4475        graph: &mut SemanticHypergraph,
4476        root: SemanticRootHandle,
4477        protected_base: SemanticRootHandle,
4478    ) -> DeviceReceipt {
4479        retirement_command(
4480            graph,
4481            OP_RETIRE_ROOT,
4482            &[
4483                (8, root.slot as u64),
4484                (9, root.generation),
4485                (10, protected_base.slot as u64),
4486                (11, protected_base.generation),
4487            ],
4488        )
4489    }
4490
4491    fn retirement_insert_root(
4492        graph: &mut SemanticHypergraph,
4493        base: SemanticRootHandle,
4494        statement: u64,
4495        support: u64,
4496    ) -> (SemanticRootHandle, DeviceReceipt) {
4497        let fork = retirement_command(
4498            graph,
4499            OP_FORK,
4500            &[(8, base.slot as u64), (9, base.generation)],
4501        );
4502        assert_eq!(fork.words[0], STATUS_OK);
4503        let edit = retirement_command(
4504            graph,
4505            OP_INSERT_SUPPORT,
4506            &[(9, fork.words[6]), (12, 1), (16, statement), (20, support)],
4507        );
4508        assert_eq!(edit.words[0], STATUS_OK);
4509        let sealed = retirement_command(graph, OP_SEAL, &[(9, fork.words[6])]);
4510        assert_eq!(sealed.words[0], STATUS_OK);
4511        (
4512            SemanticRootHandle::new(graph.owner, sealed.words[3] as u32, sealed.words[4]),
4513            edit,
4514        )
4515    }
4516
4517    #[test]
4518    fn sealed_root_retirement_preserves_descendants_and_reuses_generations() {
4519        let Some(mut graph) = retirement_test_graph() else {
4520            return;
4521        };
4522        let provider = Arc::clone(&graph.provider);
4523        let empty = graph.empty_root();
4524        let (parent, inherited) = retirement_insert_root(&mut graph, empty, 100, 200);
4525        let (loser, exclusive) = retirement_insert_root(&mut graph, parent, 300, 400);
4526        let (descendant, _) = retirement_insert_root(&mut graph, parent, 100, 201);
4527        let parent_before = retirement_command(
4528            &mut graph,
4529            OP_SNAPSHOT,
4530            &[(7, 1), (8, parent.slot as u64), (9, parent.generation)],
4531        );
4532        let retired = retirement_root(&mut graph, loser, parent);
4533        assert_eq!(retired.words[0], STATUS_OK);
4534        assert_eq!(retired.words[3], loser.slot as u64);
4535        assert_eq!(retired.words[4], loser.generation + 1);
4536        assert_eq!(retired.words[39], graph.owner);
4537        let parent_after = retirement_command(
4538            &mut graph,
4539            OP_SNAPSHOT,
4540            &[(7, 1), (8, parent.slot as u64), (9, parent.generation)],
4541        );
4542        assert_eq!(&parent_after.words[13..20], &parent_before.words[13..20]);
4543        let retained = download_test_arena(&provider, &graph);
4544        let repeated = retirement_command(
4545            &mut graph,
4546            OP_RETIRE_ROOT,
4547            &[
4548                (8, loser.slot as u64),
4549                (9, loser.generation),
4550                (10, parent.slot as u64),
4551                (11, parent.generation),
4552            ],
4553        );
4554        assert_eq!(repeated.words[0], STATUS_STALE_GENERATION);
4555        assert_eq!(download_test_arena(&provider, &graph), retained);
4556        let (reused, replacement) = retirement_insert_root(&mut graph, parent, 301, 401);
4557        assert_eq!(reused.slot, loser.slot);
4558        assert_eq!(reused.generation, loser.generation + 1);
4559        for (slot, generation) in [(7, 8), (9, 10), (11, 12)] {
4560            assert_eq!(replacement.words[slot], exclusive.words[slot]);
4561            assert_eq!(
4562                replacement.words[generation],
4563                exclusive.words[generation] + 1
4564            );
4565        }
4566        // An unpublished ancestor's root slot can retire while descendant roots
4567        // retain its exact statement, support and version lineage.
4568        let candidate = retirement_command(
4569            &mut graph,
4570            OP_FORK,
4571            &[(8, descendant.slot as u64), (9, descendant.generation)],
4572        );
4573        assert_eq!(candidate.words[0], STATUS_OK);
4574        let staged = retirement_command(
4575            &mut graph,
4576            OP_INSERT_SUPPORT,
4577            &[(9, candidate.words[6]), (12, 2), (16, 100), (20, 202)],
4578        );
4579        assert_eq!(staged.words[0], STATUS_OK);
4580        assert_eq!(
4581            retirement_root(&mut graph, parent, empty).words[0],
4582            STATUS_OK
4583        );
4584        let candidate_truth = retirement_command(
4585            &mut graph,
4586            OP_TRUTH,
4587            &[(7, 2), (9, candidate.words[6]), (16, 100)],
4588        );
4589        assert_eq!(candidate_truth.words[0], STATUS_OK);
4590        assert_eq!(candidate_truth.words[2], 3);
4591        assert_eq!(
4592            retirement_command(&mut graph, OP_SEAL, &[(9, candidate.words[6])]).words[0],
4593            STATUS_OK
4594        );
4595        for (operation, slot, generation) in [
4596            (OP_INSPECT_STATEMENT, 7, 8),
4597            (OP_INSPECT_SUPPORT, 9, 10),
4598            (OP_INSPECT_VERSION, 11, 12),
4599        ] {
4600            let inspected = retirement_command(
4601                &mut graph,
4602                operation,
4603                &[
4604                    (7, 1),
4605                    (8, descendant.slot as u64),
4606                    (9, descendant.generation),
4607                    (10, inherited.words[slot]),
4608                    (11, inherited.words[generation]),
4609                ],
4610            );
4611            assert_eq!(inspected.words[0], STATUS_OK);
4612            assert_eq!(inspected.words[slot], inherited.words[slot]);
4613            assert_eq!(inspected.words[generation], inherited.words[generation]);
4614        }
4615        let truth = retirement_command(
4616            &mut graph,
4617            OP_TRUTH,
4618            &[
4619                (7, 1),
4620                (8, descendant.slot as u64),
4621                (9, descendant.generation),
4622                (16, 100),
4623            ],
4624        );
4625        assert_eq!(truth.words[0], STATUS_OK);
4626        assert_eq!(truth.words[2], 1);
4627        let stale = retirement_root(&mut graph, loser, empty);
4628        assert_eq!(stale.words[0], STATUS_STALE_GENERATION);
4629    }
4630
4631    #[test]
4632    fn sealed_root_retirement_refuses_without_partial_reclamation() {
4633        let Some(mut graph) = retirement_test_graph() else {
4634            return;
4635        };
4636        let provider = Arc::clone(&graph.provider);
4637        let empty = graph.empty_root();
4638        let (target, target_edit) = retirement_insert_root(&mut graph, empty, 100, 200);
4639        let (other, other_edit) = retirement_insert_root(&mut graph, empty, 300, 400);
4640        let read_arena = |graph: &SemanticHypergraph| download_test_arena(&provider, graph);
4641        for (root, base) in [(empty, empty), (target, target)] {
4642            let before = read_arena(&graph);
4643            let refused = retirement_command(
4644                &mut graph,
4645                OP_RETIRE_ROOT,
4646                &[
4647                    (8, root.slot as u64),
4648                    (9, root.generation),
4649                    (10, base.slot as u64),
4650                    (11, base.generation),
4651                ],
4652            );
4653            assert_eq!(refused.words[0], STATUS_INACTIVE);
4654            assert_eq!(read_arena(&graph), before);
4655        }
4656        let fork = retirement_command(
4657            &mut graph,
4658            OP_FORK,
4659            &[(8, target.slot as u64), (9, target.generation)],
4660        );
4661        assert_eq!(fork.words[0], STATUS_OK);
4662        let before = read_arena(&graph);
4663        let refused = retirement_command(
4664            &mut graph,
4665            OP_RETIRE_ROOT,
4666            &[
4667                (8, target.slot as u64),
4668                (9, target.generation),
4669                (11, empty.generation),
4670            ],
4671        );
4672        assert_eq!(refused.words[0], STATUS_INACTIVE);
4673        assert_eq!(read_arena(&graph), before);
4674        assert_eq!(
4675            retirement_command(&mut graph, OP_DISCARD, &[(9, fork.words[6])]).words[0],
4676            STATUS_OK
4677        );
4678        let version_start = CONTROL_WORDS
4679            + 8 * ROOT_WORDS
4680            + CANDIDATE_WORDS
4681            + 8 * STATEMENT_WORDS
4682            + 16 * SUPPORT_WORDS;
4683        let clean = read_arena(&graph);
4684        for (offset, value, expected_status) in [
4685            (
4686                version_start + target_edit.words[11] * VERSION_WORDS + 1,
4687                u64::MAX,
4688                STATUS_GENERATION_EXHAUSTED,
4689            ),
4690            (
4691                version_start + other_edit.words[11] * VERSION_WORDS + 7,
4692                17,
4693                STATUS_CORRUPT_LINEAGE,
4694            ),
4695        ] {
4696            let mut arena = clean.clone();
4697            arena[offset as usize] = value;
4698            provider
4699                .htod_sync_copy_into_tracked(&arena, &mut graph.arena)
4700                .unwrap();
4701            let refused = retirement_command(
4702                &mut graph,
4703                OP_RETIRE_ROOT,
4704                &[
4705                    (8, target.slot as u64),
4706                    (9, target.generation),
4707                    (11, empty.generation),
4708                ],
4709            );
4710            assert_eq!(refused.words[0], expected_status);
4711            assert_eq!(read_arena(&graph), arena);
4712        }
4713        provider
4714            .htod_sync_copy_into_tracked(&clean, &mut graph.arena)
4715            .unwrap();
4716        assert_eq!(
4717            retirement_root(&mut graph, target, empty).words[0],
4718            STATUS_OK
4719        );
4720        let truth = retirement_command(
4721            &mut graph,
4722            OP_TRUTH,
4723            &[
4724                (7, 1),
4725                (8, other.slot as u64),
4726                (9, other.generation),
4727                (16, 300),
4728            ],
4729        );
4730        assert_eq!(truth.words[0], STATUS_OK);
4731        assert_eq!(truth.words[2], 1);
4732    }
4733
4734    #[test]
4735    fn insertion_receipt_retains_previous_truth_for_actual_support_effects() {
4736        for (previous, resulting, new_statement) in [
4737            (0, 1, true),
4738            (0, 2, true),
4739            (1, 1, false),
4740            (2, 2, false),
4741            (1, 3, false),
4742            (2, 3, false),
4743            (3, 3, false),
4744        ] {
4745            let mut receipt = DeviceReceipt::default();
4746            receipt.words[1] = OUTCOME_INSERTED;
4747            receipt.words[2] = resulting;
4748            receipt.words[32] = u64::from(new_statement) | (previous << 1);
4749            assert_eq!(
4750                inserted_support_previous_truth(&receipt).unwrap(),
4751                SemanticTruth::from_bits(previous).unwrap(),
4752            );
4753        }
4754    }
4755
4756    #[test]
4757    fn resident_discard_retires_refused_acquisition_and_rejects_stale_replay() {
4758        let Some(mut graph) = retirement_test_graph() else {
4759            return;
4760        };
4761        let provider = Arc::clone(&graph.provider);
4762        let bytes = std::mem::size_of::<SemanticResidentDecodedStatement>()
4763            + 2 * std::mem::size_of::<SemanticResidentDecodedSupport>()
4764            + std::mem::size_of::<SemanticResidentHandleRecord>()
4765            + 5 * std::mem::size_of::<SemanticResidentReceiptRecord>();
4766        let mut reservation = provider.memory().reserve_bytes(bytes as u64).unwrap();
4767        let mut statements = reservation
4768            .alloc::<SemanticResidentDecodedStatement>(1)
4769            .unwrap();
4770        let mut supports = reservation
4771            .alloc::<SemanticResidentDecodedSupport>(2)
4772            .unwrap();
4773        let handles = reservation
4774            .alloc::<SemanticResidentHandleRecord>(1)
4775            .unwrap();
4776        let receipts = reservation
4777            .alloc::<SemanticResidentReceiptRecord>(5)
4778            .unwrap();
4779        provider
4780            .htod_sync_copy_into_tracked(
4781                &[SemanticResidentDecodedStatement {
4782                    identity_words: [17; 8],
4783                    record: 0,
4784                    reconstruction: [0; 10],
4785                }],
4786                &mut statements,
4787            )
4788            .unwrap();
4789        provider
4790            .htod_sync_copy_into_tracked(
4791                &[1, 0].map(|polarity| SemanticResidentDecodedSupport {
4792                    polarity,
4793                    provenance_words: [polarity; 8],
4794                    source_words: [3; 8],
4795                    context_words: [4; 8],
4796                    scope_words: [5; 8],
4797                    record: polarity,
4798                }),
4799                &mut supports,
4800            )
4801            .unwrap();
4802        let mut setup = graph.domain.new_strict_recorder();
4803        setup.read_write(&statements);
4804        setup.read_write(&supports);
4805        setup.write(&handles);
4806        setup.write(&receipts);
4807        unsafe { graph.domain.enqueue(setup, |_| Ok::<(), XlogError>(())) }
4808            .unwrap()
4809            .commit()
4810            .unwrap();
4811        let slot = |index| SemanticResidentReceiptSlot::new(&receipts, index).unwrap();
4812        let statement = || SemanticResidentStatementBank::new(&statements, 0).unwrap();
4813        let support = |index| SemanticResidentSupportBank::new(&supports, index).unwrap();
4814        graph
4815            .enqueue_resident_empty_root_handle(
4816                SemanticResidentHandleSlot::new(&handles, 0).unwrap(),
4817                slot(0),
4818            )
4819            .unwrap();
4820        let base = || SemanticResidentRootHandle::Bank {
4821            allocation: &handles,
4822            index: 0,
4823        };
4824        let fork = graph.enqueue_resident_fork(base(), slot(1)).unwrap();
4825        let inserted = graph
4826            .enqueue_resident_insert_support(
4827                fork.candidate_handle(),
4828                statement(),
4829                support(0),
4830                slot(2),
4831            )
4832            .unwrap();
4833        graph
4834            .enqueue_resident_insert_support(
4835                inserted.candidate_handle(),
4836                statement(),
4837                support(1),
4838                slot(3),
4839            )
4840            .unwrap();
4841        graph.stream.synchronize().unwrap();
4842        let before = provider
4843            .dtoh_small_metadata_untracked(&receipts, receipts.len())
4844            .unwrap();
4845        assert_eq!(before[2].words[1], OUTCOME_INSERTED);
4846        assert_eq!(before[3].words[0], STATUS_INVALID_COMMAND);
4847        assert_ne!(before[3].words[41], 0);
4848
4849        let discard = |graph: &SemanticHypergraph| {
4850            let descriptor = DeviceLaunchDescriptor {
4851                expected_owner: graph.owner,
4852                root_capacity: graph.capacities.roots as u64,
4853                statement_capacity: graph.capacities.statements as u64,
4854                support_capacity: graph.capacities.supports as u64,
4855                version_capacity: graph.capacities.versions as u64,
4856                arena_words: graph.arena_words,
4857                command_ptr: 0,
4858                command_index: 0,
4859                handle_ptr: 0,
4860                handle_index: 0,
4861                source_receipt_ptr: receipts.device_ptr_value(),
4862                source_receipt_index: 3,
4863                decoded_input_ptr: 0,
4864                decoded_input_index: 0,
4865                decoded_statement_ptr: 0,
4866                decoded_statement_index: 0,
4867                decoded_support_ptr: 0,
4868                decoded_support_index: 0,
4869                output_ptr: 0,
4870                output_index: 0,
4871                receipt_ptr: receipts.device_ptr_value(),
4872                receipt_index: 4,
4873                admission: 10,
4874                abi_generation: HYPERGRAPH_ABI_GENERATION,
4875            };
4876            let mut recorder = graph.domain.new_strict_recorder();
4877            recorder.read_write(&graph.arena);
4878            recorder.read_write(&receipts);
4879            unsafe {
4880                graph.domain.enqueue(recorder, |stream| {
4881                    graph.execute.clone().launch_in(
4882                        stream,
4883                        LaunchConfig {
4884                            grid_dim: (1, 1, 1),
4885                            block_dim: (1, 1, 1),
4886                            shared_mem_bytes: 0,
4887                        },
4888                        (graph.arena.device_ptr_value(), descriptor),
4889                    )
4890                })
4891            }
4892            .unwrap()
4893            .commit()
4894            .unwrap();
4895            graph.stream.synchronize().unwrap();
4896            provider
4897                .dtoh_small_metadata_untracked(&receipts, receipts.len())
4898                .unwrap()
4899        };
4900        let cleaned = discard(&graph);
4901        assert_eq!(cleaned[4].words[0], STATUS_OK);
4902        assert_eq!(&cleaned[4].words[40..42], &[0, 0]);
4903        assert_eq!(cleaned[3].words, before[3].words);
4904        let next = graph.enqueue_resident_fork(base(), slot(1)).unwrap();
4905        graph.stream.synchronize().unwrap();
4906        let arena = download_test_arena(&provider, &graph);
4907        let stale = discard(&graph);
4908        assert_eq!(stale[4].words[0], STATUS_STALE_GENERATION);
4909        assert_eq!(stale[3].words, before[3].words);
4910        assert_eq!(download_test_arena(&provider, &graph), arena);
4911        let reused = graph
4912            .enqueue_resident_insert_support(
4913                next.candidate_handle(),
4914                statement(),
4915                support(0),
4916                slot(2),
4917            )
4918            .unwrap();
4919        graph
4920            .enqueue_resident_seal(reused.candidate_handle(), slot(3))
4921            .unwrap();
4922        graph.stream.synchronize().unwrap();
4923        let sealed_arena = download_test_arena(&provider, &graph);
4924        let sealed = discard(&graph);
4925        assert_eq!(sealed[3].words[0], STATUS_OK);
4926        assert_eq!(sealed[2].words[1], OUTCOME_INSERTED);
4927        assert_eq!(sealed[2].words[9], before[2].words[9]);
4928        assert_eq!(sealed[2].words[10], before[2].words[10] + 1);
4929        assert_eq!(sealed[4].words[0], STATUS_INVALID_COMMAND);
4930        assert_eq!(download_test_arena(&provider, &graph), sealed_arena);
4931    }
4932
4933    #[test]
4934    fn insertion_receipt_rejects_refused_duplicate_and_impossible_truth_effects() {
4935        for (status, outcome, resulting, metadata) in [
4936            (STATUS_SUPPORT_CAPACITY, OUTCOME_INSERTED, 1, 1),
4937            (STATUS_OK, OUTCOME_UNCHANGED, 1, 2),
4938            (STATUS_OK, 0, 1, 1),
4939            (STATUS_OK, OUTCOME_INSERTED, 0, 1),
4940            (STATUS_OK, OUTCOME_INSERTED, 4, 1),
4941            (STATUS_OK, OUTCOME_INSERTED, 3, 1),
4942            (STATUS_OK, OUTCOME_INSERTED, 1, 0),
4943            (STATUS_OK, OUTCOME_INSERTED, 1, 3),
4944            (STATUS_OK, OUTCOME_INSERTED, 1, 4),
4945            (STATUS_OK, OUTCOME_INSERTED, 1, 6),
4946            (STATUS_OK, OUTCOME_INSERTED, 1, 9),
4947        ] {
4948            let mut receipt = DeviceReceipt::default();
4949            receipt.words[0] = status;
4950            receipt.words[1] = outcome;
4951            receipt.words[2] = resulting;
4952            receipt.words[32] = metadata;
4953            assert!(
4954                matches!(
4955                    inserted_support_previous_truth(&receipt),
4956                    Err(SemanticHypergraphError::CorruptLineage { .. }),
4957                ),
4958                "accepted invalid insertion receipt: {status}/{outcome}/{resulting}/{metadata}",
4959            );
4960        }
4961    }
4962
4963    fn identity_bytes_from_dwords(words: [u32; 8]) -> [u8; 32] {
4964        let mut bytes = [0; 32];
4965        for (chunk, word) in bytes.as_chunks_mut::<4>().0.iter_mut().zip(words) {
4966            chunk.copy_from_slice(&word.to_le_bytes());
4967        }
4968        bytes
4969    }
4970
4971    #[test]
4972    fn root_material_codec_preserves_typed_bits_and_rejects_truncation() {
4973        let material = SemanticRootMaterial {
4974            records: numeric_records(),
4975            symbols: vec![],
4976            insertions: vec![],
4977            digest: [0; 32],
4978            extents: [0; 3],
4979            admission_base_digest: material_root_digest([0; 32], [0; 32], [0; 3]),
4980            admission_base_extents: [0; 3],
4981        };
4982        let limits = SemanticAdmissionLimits {
4983            max_records: 5,
4984            max_terms: 25,
4985            max_references: 2,
4986            max_utf8_bytes: 1024,
4987        };
4988        let encoded = material.encode().unwrap();
4989        assert_eq!(
4990            SemanticRootMaterial::decode(&encoded, limits).unwrap(),
4991            material
4992        );
4993        for len in 0..encoded.len() {
4994            assert!(SemanticRootMaterial::decode(&encoded[..len], limits).is_err());
4995        }
4996        let mut trailing = encoded.clone();
4997        trailing.push(0);
4998        assert!(SemanticRootMaterial::decode(&trailing, limits).is_err());
4999        let tight = SemanticAdmissionLimits {
5000            max_terms: 24,
5001            ..limits
5002        };
5003        assert!(SemanticRootMaterial::decode(&encoded, tight).is_err());
5004        let mut reader = SemanticMaterialReader::new(&[255; 4]);
5005        assert!(reader.count(1).is_err());
5006    }
5007
5008    pub(crate) fn root_material_records() -> SemanticAdmissionRecords {
5009        let roles = [
5010            SemanticRecordRole::Statement,
5011            SemanticRecordRole::Provenance,
5012            SemanticRecordRole::Source,
5013            SemanticRecordRole::Context,
5014            SemanticRecordRole::Scope,
5015        ];
5016        let predicates = roles
5017            .into_iter()
5018            .enumerate()
5019            .map(|(index, role)| SemanticPredicateRecord {
5020                predicate: RelId(index as u32),
5021                role,
5022                schema: Schema::new(vec![("value".into(), ScalarType::U32)]),
5023            })
5024            .collect();
5025        let records = [0, 0, 1, 2, 3, 4, 2]
5026            .into_iter()
5027            .enumerate()
5028            .map(|(index, predicate)| SemanticTypedRecord {
5029                predicate: RelId(predicate),
5030                arguments: vec![SemanticArgument::U32(index as u32)],
5031                qualifiers: vec![],
5032            })
5033            .collect();
5034        let mut supports: Vec<_> = [SemanticPolarity::Pro, SemanticPolarity::Contra]
5035            .into_iter()
5036            .map(|polarity| SemanticSupportRecord {
5037                statement: 0,
5038                polarity,
5039                provenance: 2,
5040                source: 3,
5041                context: 4,
5042                scope: 5,
5043            })
5044            .collect();
5045        supports.push(SemanticSupportRecord {
5046            statement: 0,
5047            polarity: SemanticPolarity::Pro,
5048            provenance: 2,
5049            source: 6,
5050            context: 4,
5051            scope: 5,
5052        });
5053        SemanticAdmissionRecords {
5054            predicates,
5055            records,
5056            supports,
5057        }
5058    }
5059
5060    pub(crate) fn root_material_limits() -> SemanticAdmissionLimits {
5061        SemanticAdmissionLimits {
5062            max_records: 32,
5063            max_terms: 64,
5064            max_references: 32,
5065            max_utf8_bytes: 1024,
5066        }
5067    }
5068
5069    pub(crate) fn admit_material_records(records: SemanticAdmissionRecords) -> SemanticAdmission {
5070        admit_semantic_records(
5071            records,
5072            root_material_limits(),
5073            SemanticRootHandle::new(1, 0, 1),
5074            || {
5075                Ok(SemanticRootSnapshot::new(
5076                    SemanticRootDigest(material_root_digest([0; 32], [0; 32], [0; 3])),
5077                    SemanticExtents::default(),
5078                ))
5079            },
5080        )
5081        .unwrap()
5082    }
5083
5084    pub(crate) fn reconstructed_material_statement(
5085        admission: &SemanticAdmission,
5086        original: Option<u32>,
5087        reconstruction: &[u32; 10],
5088    ) -> Result<SemanticStatementKey, SemanticHypergraphError> {
5089        material_statement_key(admission, original, reconstruction)
5090    }
5091
5092    fn verify_native_material_reconstruction(
5093        executable: &std::path::Path,
5094        directory: &std::path::Path,
5095        run: &impl Fn(&mut std::process::Command),
5096    ) {
5097        let mut records = root_material_records();
5098        records.predicates[0].schema = Schema::new(vec![
5099            ("left".into(), ScalarType::U32),
5100            ("right".into(), ScalarType::U32),
5101        ])
5102        .with_sort_labels(vec!["bit".into(), "bit".into()])
5103        .unwrap();
5104        records.records[0].arguments = vec![SemanticArgument::U32(0), SemanticArgument::U32(0)];
5105        records.records[1].arguments = vec![SemanticArgument::U32(1), SemanticArgument::U32(1)];
5106        records.supports.push(records.supports[0].clone());
5107        let empty_digest = material_root_digest([0; 32], [0; 32], [0; 3]);
5108        let admission = admit_semantic_records(
5109            records,
5110            root_material_limits(),
5111            SemanticRootHandle::new(91, 0, 1),
5112            || {
5113                Ok(SemanticRootSnapshot::new(
5114                    SemanticRootDigest(empty_digest),
5115                    SemanticExtents::default(),
5116                ))
5117            },
5118        )
5119        .unwrap();
5120        let (records, symbols) = normalized_material_admission(&admission).unwrap();
5121        let derived_equal = [0, 0, 0, 0, 1, 0, 0, 0, 0, u32::MAX];
5122        let derived_new = [0, 0, 0, 1, 1, 0, 0, 0, 0, u32::MAX];
5123        let original = material_statement_key(&admission, Some(0), &[0; 10]).unwrap();
5124        assert_eq!(
5125            original.identity,
5126            material_statement_key(&admission, None, &derived_equal)
5127                .unwrap()
5128                .identity
5129        );
5130        let new_key = material_statement_key(&admission, None, &derived_new).unwrap();
5131        assert!(admission
5132            .statement_keys
5133            .iter()
5134            .flatten()
5135            .all(|key| key.identity != new_key.identity));
5136        let mut encodings = Vec::new();
5137        for (case, (statement, reconstruction)) in [
5138            (Some(0), [0; 10]),
5139            (None, derived_equal),
5140            (None, derived_new),
5141        ]
5142        .into_iter()
5143        .enumerate()
5144        {
5145            let key = material_statement_key(&admission, statement, &reconstruction).unwrap();
5146            let event = admission.support_event(3).unwrap();
5147            let version = material_version_digest(
5148                [0; 32],
5149                event.identity(key.identity).0,
5150                event.polarity.code(),
5151            );
5152            let material = SemanticRootMaterial {
5153                records: records.clone(),
5154                symbols: symbols.clone(),
5155                insertions: vec![SemanticRootInsertion {
5156                    statement,
5157                    reconstruction,
5158                    support: 3,
5159                    version,
5160                }],
5161                digest: material_root_digest(empty_digest, version, [1; 3]),
5162                extents: [1; 3],
5163                admission_base_digest: empty_digest,
5164                admission_base_extents: [0; 3],
5165            };
5166            material.validate_lineage(&admission).unwrap();
5167            let encoded = material.encode().unwrap();
5168            let decoded = SemanticRootMaterial::decode(&encoded, root_material_limits()).unwrap();
5169            assert_eq!(decoded, material);
5170            let mut old_encoding = encoded.clone();
5171            old_encoding[8..12].copy_from_slice(&1u32.to_le_bytes());
5172            assert!(SemanticRootMaterial::decode(&old_encoding, root_material_limits()).is_err());
5173            encodings.push(encoded);
5174            for reuse in [false, true] {
5175                let mut commands = vec![DeviceCommand::default()];
5176                commands[0].words[0] = OP_INITIALIZE;
5177                let fork = || {
5178                    let mut command = DeviceCommand::default();
5179                    command.words[0] = OP_FORK;
5180                    command.words[9] = 1;
5181                    command
5182                };
5183                if reuse {
5184                    commands.push(fork());
5185                    let mut insert = DeviceCommand::default();
5186                    encode_support_insertion(
5187                        &mut insert,
5188                        SemanticForkHandle::new(91, 0, 1),
5189                        &key,
5190                        &event,
5191                        &reconstruction,
5192                    );
5193                    commands.push(insert);
5194                    let mut discard = DeviceCommand::default();
5195                    discard.words[0] = OP_DISCARD;
5196                    discard.words[9] = 1;
5197                    commands.push(discard);
5198                }
5199                commands.push(fork());
5200                let insertion = &decoded.insertions[0];
5201                let restored_key = material_statement_key(
5202                    &admission,
5203                    insertion.statement,
5204                    &insertion.reconstruction,
5205                )
5206                .unwrap();
5207                let mut insert = DeviceCommand::default();
5208                encode_support_insertion(
5209                    &mut insert,
5210                    SemanticForkHandle::new(91, 0, 1 + u64::from(reuse)),
5211                    &restored_key,
5212                    &admission.support_event(insertion.support).unwrap(),
5213                    &insertion.reconstruction,
5214                );
5215                commands.push(insert);
5216                let mut seal = DeviceCommand::default();
5217                seal.words[0] = OP_SEAL;
5218                seal.words[9] = 1 + u64::from(reuse);
5219                commands.push(seal);
5220                let command_path = directory.join(format!("material-{case}-{reuse}.commands"));
5221                let output_path = directory.join(format!("material-{case}-{reuse}.arena"));
5222                let bytes: Vec<_> = commands
5223                    .iter()
5224                    .flat_map(|command| command.words.iter().flat_map(|word| word.to_ne_bytes()))
5225                    .collect();
5226                std::fs::write(&command_path, bytes).unwrap();
5227                run(std::process::Command::new(executable)
5228                    .arg("--commands")
5229                    .arg(&command_path)
5230                    .arg(&output_path));
5231                let bytes = std::fs::read(&output_path).unwrap();
5232                assert_eq!(bytes.len() % 8, 0);
5233                let words: Vec<_> = bytes
5234                    .as_chunks::<8>()
5235                    .0
5236                    .iter()
5237                    .map(|word| u64::from_ne_bytes(*word))
5238                    .collect();
5239                let root = SemanticRootHandle::new(91, words[3] as u32, words[4]);
5240                let digest =
5241                    std::array::from_fn(|byte| (words[16 + byte / 8] >> (8 * (byte % 8))) as u8);
5242                let snapshot = SemanticRootSnapshot::new(
5243                    SemanticRootDigest(digest),
5244                    SemanticExtents::new(words[13] as u32, words[14] as u32, words[15] as u32),
5245                );
5246                let capacities = SemanticHypergraphCapacities::try_new(4, 4, 8, 8).unwrap();
5247                let arena = &words[RECEIPT_WORDS..];
5248                let observed =
5249                    material_from_arena(arena, capacities, root, snapshot, &admission).unwrap();
5250                assert_eq!(
5251                    observed, decoded,
5252                    "native restoration changed original/derived target or support occurrence"
5253                );
5254                let support =
5255                    (CONTROL_WORDS + 4 * ROOT_WORDS + CANDIDATE_WORDS + 4 * STATEMENT_WORDS)
5256                        as usize;
5257                assert_eq!(arena[support + 1], 1 + u64::from(reuse));
5258                std::fs::remove_file(command_path).unwrap();
5259                std::fs::remove_file(output_path).unwrap();
5260            }
5261            let mut changed = material.clone();
5262            changed.insertions[0].support = 2;
5263            assert!(changed.validate_lineage(&admission).is_err());
5264            if statement.is_none() {
5265                for (field, value) in [(0, 1), (1, u32::MAX), (1, 2), (5, 1), (9, 2)] {
5266                    let mut changed = material.clone();
5267                    changed.insertions[0].reconstruction[field] = value;
5268                    assert!(changed.validate_lineage(&admission).is_err());
5269                }
5270                changed = material.clone();
5271                changed.insertions[0].statement = Some(0);
5272                assert!(changed.validate_lineage(&admission).is_err());
5273                assert!(changed.encode().is_err());
5274            }
5275        }
5276        assert_ne!(
5277            encodings[0], encodings[1],
5278            "derived bytes acquired original record zero"
5279        );
5280        assert_ne!(Sha256::digest(&encodings[0]), Sha256::digest(&encodings[1]));
5281    }
5282
5283    #[test]
5284    fn native_truth_receipt_producer_preserves_view_and_head_fields() {
5285        use std::process::Command;
5286        use std::time::{Duration, Instant};
5287        let mut nonce = [0; 8];
5288        getrandom::fill(&mut nonce).unwrap();
5289        let directory = std::env::temp_dir().join(format!(
5290            "xlog-native-truth-{}-{}",
5291            std::process::id(),
5292            u64::from_le_bytes(nonce)
5293        ));
5294        std::fs::create_dir(&directory).unwrap();
5295        let deadline = Instant::now() + Duration::from_secs(120);
5296        let run = |command: &mut Command| {
5297            let invocation = format!("{command:?}");
5298            let mut child = command
5299                .current_dir(&directory)
5300                .spawn()
5301                .expect("native truth regression command must start; no silent compiler skip");
5302            loop {
5303                if let Some(status) = child
5304                    .try_wait()
5305                    .expect("native truth regression process status")
5306                {
5307                    #[cfg(unix)]
5308                    let termination = {
5309                        use std::os::unix::process::ExitStatusExt;
5310                        format!(
5311                            "code={:?}, signal={:?}, core_dumped={}",
5312                            status.code(),
5313                            status.signal(),
5314                            status.core_dumped()
5315                        )
5316                    };
5317                    #[cfg(not(unix))]
5318                    let termination = format!("code={:?}", status.code());
5319                    assert!(
5320                        status.success(),
5321                        "native truth regression failed: {termination}; command {invocation}; output retained in {}",
5322                        directory.display()
5323                    );
5324                    break;
5325                }
5326                if Instant::now() >= deadline {
5327                    child
5328                        .kill()
5329                        .expect("stop timed-out native truth regression");
5330                    child
5331                        .wait()
5332                        .expect("reap timed-out native truth regression");
5333                    panic!(
5334                        "native truth regression exceeded two minutes; output retained in {}",
5335                        directory.display()
5336                    );
5337                }
5338                std::thread::sleep(Duration::from_millis(10));
5339            }
5340        };
5341        for name in ["semantic_truth_receipt", "semantic_feedback_lineage"] {
5342            let executable = directory.join(name);
5343            let source =
5344                std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join(format!("tests/{name}.cpp"));
5345            let mut compiler =
5346                Command::new(std::env::var_os("CXX").unwrap_or_else(|| "c++".into()));
5347            compiler.args([
5348                "-std=c++20",
5349                "-O0",
5350                "-g",
5351                "-fno-omit-frame-pointer",
5352                "-rdynamic",
5353                "-Wall",
5354                "-Wextra",
5355                "-I",
5356                env!("OUT_DIR"),
5357            ]);
5358            if cfg!(feature = "semantic-policy") {
5359                compiler.arg("-DXLOG_SEMANTIC_POLICY");
5360            }
5361            run(compiler.arg(source).arg("-o").arg(&executable));
5362            run(&mut Command::new(&executable));
5363            if name == "semantic_truth_receipt" {
5364                verify_native_material_reconstruction(&executable, &directory, &run);
5365            } else {
5366                crate::semantic_transition::task_binding_tests::verify_native_decoded_reconstruction(
5367                    &executable, &directory, &run,
5368                );
5369            }
5370            std::fs::remove_file(&executable).unwrap();
5371        }
5372        std::fs::remove_dir(&directory).unwrap();
5373    }
5374
5375    #[test]
5376    fn task_selection_identity_retains_exact_admitted_occurrences() {
5377        use crate::semantic_transition::{
5378            task_binding_tests::{arithmetic_observation, task_spec},
5379            TaskEvaluationBinding,
5380        };
5381        let mut records = root_material_records();
5382        records.predicates[0].schema = Schema::new(vec![
5383            ("left".into(), ScalarType::U32),
5384            ("right".into(), ScalarType::U32),
5385        ])
5386        .with_sort_labels(vec!["bit".into(), "bit".into()])
5387        .unwrap();
5388        records.records[0].arguments = vec![SemanticArgument::U32(1), SemanticArgument::U32(1)];
5389        records.records[1].arguments = vec![SemanticArgument::U32(0), SemanticArgument::U32(1)];
5390        records.records.push(records.records[0].clone());
5391        records.supports.push(records.supports[0].clone());
5392        let admission = admit_material_records(records);
5393        let bind = |statement_records, allowed_support_records: Vec<u32>| {
5394            let binding = TaskEvaluationBinding::bind(
5395                &admission,
5396                task_spec(statement_records, allowed_support_records.clone()),
5397                arithmetic_observation(),
5398            )
5399            .unwrap();
5400            assert_eq!(binding.spec().statement_records, statement_records);
5401            assert_eq!(
5402                binding.spec().allowed_support_records,
5403                allowed_support_records
5404            );
5405            binding
5406        };
5407        let first = bind([0, 1, 1], vec![0]).words(1);
5408        let equal_occurrence = bind([0, 1, 1], vec![3]).words(1);
5409        assert_ne!(
5410            first[6..],
5411            equal_occurrence[6..],
5412            "resident task bank collapsed equal support occurrences"
5413        );
5414        assert_eq!(first[22..25], [0, 1, 1]);
5415        assert_eq!(first[34], 0);
5416        assert_eq!(equal_occurrence[34], 3);
5417        let both = bind([0, 1, 1], vec![0, 3]).words(1);
5418        assert_eq!(both[21], 2);
5419        assert_eq!([both[34], both[39]], [0, 3]);
5420        for (left, right) in [
5421            (bind([0, 1, 1], vec![]), bind([7, 1, 1], vec![])),
5422            (bind([0, 1, 1], vec![0]), bind([0, 1, 1], vec![3])),
5423            (bind([0, 1, 1], vec![0, 3]), bind([0, 1, 1], vec![3, 0])),
5424            (bind([0, 1, 1], vec![0, 0]), bind([0, 1, 1], vec![0])),
5425        ] {
5426            // Same semantic query/support operands are not the same original
5427            // admitted source selections for restoration and read provenance.
5428            assert_ne!(
5429                left.identity(),
5430                right.identity(),
5431                "task identity lost original record selections"
5432            );
5433        }
5434    }
5435
5436    #[test]
5437    fn root_material_lineage_binds_cross_statement_order_and_original_support() {
5438        let admission = admit_semantic_records(
5439            root_material_records(),
5440            root_material_limits(),
5441            SemanticRootHandle::new(1, 0, 1),
5442            || {
5443                Ok(SemanticRootSnapshot::new(
5444                    SemanticRootDigest(material_root_digest([0; 32], [0; 32], [0; 3])),
5445                    SemanticExtents::default(),
5446                ))
5447            },
5448        )
5449        .unwrap();
5450        let (records, symbols) = normalized_material_admission(&admission).unwrap();
5451        let mut material = SemanticRootMaterial {
5452            records,
5453            symbols,
5454            insertions: vec![],
5455            digest: material_root_digest([0; 32], [0; 32], [0; 3]),
5456            extents: [0; 3],
5457            admission_base_digest: material_root_digest([0; 32], [0; 32], [0; 3]),
5458            admission_base_extents: [0; 3],
5459        };
5460        for (ordinal, (statement, support)) in [(1, 0), (0, 1)].into_iter().enumerate() {
5461            let key = admission.statement_key(statement).unwrap();
5462            let event = admission.support_event(support).unwrap();
5463            let version = material_version_digest(
5464                [0; 32],
5465                event.identity(key.identity).0,
5466                event.polarity.code(),
5467            );
5468            material.insertions.push(SemanticRootInsertion {
5469                statement: Some(statement),
5470                reconstruction: [0; 10],
5471                support,
5472                version,
5473            });
5474            material.extents = [(ordinal + 1) as u32; 3];
5475            material.digest = material_root_digest(material.digest, version, material.extents);
5476        }
5477        material.validate_lineage(&admission).unwrap();
5478        // The immutable admission binding may name a proper prefix of the
5479        // acquired root, even after that original physical root is retired.
5480        let first = &material.insertions[0];
5481        material.admission_base_digest = material_root_digest(
5482            material_root_digest([0; 32], [0; 32], [0; 3]),
5483            first.version,
5484            [1; 3],
5485        );
5486        material.admission_base_extents = [1; 3];
5487        material.validate_lineage(&admission).unwrap();
5488        let mut wrong_base = material.clone();
5489        wrong_base.admission_base_digest[0] ^= 1;
5490        assert!(wrong_base.validate_lineage(&admission).is_err());
5491        let decoded =
5492            SemanticRootMaterial::decode(&material.encode().unwrap(), root_material_limits())
5493                .unwrap();
5494        assert_eq!(decoded, material);
5495        assert_eq!(decoded.records.supports[0].statement, 0);
5496        assert_eq!(decoded.insertions[0].statement, Some(1));
5497        let mut reversed = material.clone();
5498        reversed.insertions.reverse();
5499        assert!(reversed.validate_lineage(&admission).is_err());
5500        let mut duplicate = material.clone();
5501        duplicate.insertions.push(duplicate.insertions[0].clone());
5502        assert!(duplicate.validate_lineage(&admission).is_err());
5503        let mut altered = material;
5504        altered.insertions[0].version[0] ^= 1;
5505        assert!(altered.validate_lineage(&admission).is_err());
5506    }
5507
5508    #[test]
5509    fn root_material_observation_roots_preserve_actual_aliases_and_both_polarities() {
5510        let mut declared = root_material_records();
5511        declared.records.push(declared.records[0].clone());
5512        let mut other_query = declared.records[1].clone();
5513        other_query.arguments = vec![SemanticArgument::U32(8)];
5514        declared.records.push(other_query);
5515        let admission = admit_material_records(declared);
5516        let (records, symbols) = normalized_material_admission(&admission).unwrap();
5517        let empty = material_root_digest([0; 32], [0; 32], [0; 3]);
5518        let mut material = SemanticRootMaterial {
5519            records,
5520            symbols,
5521            insertions: vec![],
5522            digest: empty,
5523            extents: [0; 3],
5524            admission_base_digest: empty,
5525            admission_base_extents: [0; 3],
5526        };
5527        let mut previous = [0; 32];
5528        for (statement, support, extents, truth) in [
5529            (7, 0, [1, 1, 1], 1),
5530            (7, 1, [1, 2, 2], 3),
5531            (1, 2, [2, 3, 3], 1),
5532        ] {
5533            let key = admission.statement_key(statement).unwrap();
5534            let event = admission.support_event(support).unwrap();
5535            let version = material_version_digest(
5536                if statement == 7 { previous } else { [0; 32] },
5537                event.identity(key.identity).0,
5538                truth,
5539            );
5540            material.insertions.push(SemanticRootInsertion {
5541                statement: Some(statement),
5542                reconstruction: [0; 10],
5543                support,
5544                version,
5545            });
5546            material.extents = extents;
5547            material.digest = material_root_digest(material.digest, version, extents);
5548            previous = version;
5549            if support == 0 {
5550                material.admission_base_digest = material.digest;
5551                material.admission_base_extents = extents;
5552            }
5553        }
5554        let material =
5555            SemanticRootMaterial::decode(&material.encode().unwrap(), root_material_limits())
5556                .unwrap();
5557        let roots = material
5558            .task_observation_roots(&admission, [0, 8, 8])
5559            .unwrap();
5560        assert_eq!(roots.query_records, [0, 8, 8]);
5561        assert_eq!(roots.root_digest.as_bytes(), &material.digest);
5562        assert_eq!(roots.root_extents, [2, 3, 3]);
5563        assert_eq!(roots.contributors, [(0, Some(7), 0), (0, Some(7), 1)]);
5564        let reverse = material
5565            .task_observation_roots(&admission, [8, 0, 8])
5566            .unwrap();
5567        assert_eq!(reverse.contributors, [(1, Some(7), 0), (1, Some(7), 1)]);
5568        let mut corrupt = material.clone();
5569        corrupt.insertions[0].version[0] ^= 1;
5570        assert!(corrupt
5571            .task_observation_roots(&admission, [0, 8, 8])
5572            .is_err());
5573        assert!(material
5574            .task_observation_roots(&admission, [0, 2, 2])
5575            .is_err());
5576        let mut derived = material;
5577        derived.insertions[0].statement = None;
5578        derived.insertions[0].reconstruction = [0; 10];
5579        derived.insertions[0].reconstruction[9] = u32::MAX;
5580        let roots = derived
5581            .task_observation_roots(&admission, [0, 8, 8])
5582            .unwrap();
5583        assert_eq!(roots.contributors, [(0, None, 0), (0, Some(7), 1)]);
5584    }
5585
5586    #[test]
5587    fn root_material_symbols_retain_occurrence_order_and_enforce_byte_budget() {
5588        let mut records = root_material_records();
5589        records.predicates[0].schema = Schema::new(vec![("value".into(), ScalarType::Symbol)]);
5590        records.records[0].arguments =
5591            vec![SemanticArgument::Symbol(symbol::intern("root-material-a"))];
5592        records.records[1].arguments =
5593            vec![SemanticArgument::Symbol(symbol::intern("root-material-b"))];
5594        let admission = admit_semantic_records(
5595            records,
5596            root_material_limits(),
5597            SemanticRootHandle::new(1, 0, 1),
5598            || {
5599                Ok(SemanticRootSnapshot::new(
5600                    SemanticRootDigest([0; 32]),
5601                    SemanticExtents::default(),
5602                ))
5603            },
5604        )
5605        .unwrap();
5606        let (records, symbols) = normalized_material_admission(&admission).unwrap();
5607        let mut material = SemanticRootMaterial {
5608            records,
5609            symbols,
5610            insertions: vec![],
5611            digest: [0; 32],
5612            extents: [0; 3],
5613            admission_base_digest: material_root_digest([0; 32], [0; 32], [0; 3]),
5614            admission_base_extents: [0; 3],
5615        };
5616        assert_eq!(
5617            material.records.records[0].arguments,
5618            [SemanticArgument::Symbol(0)]
5619        );
5620        assert_eq!(
5621            material.records.records[1].arguments,
5622            [SemanticArgument::Symbol(1)]
5623        );
5624        let bytes = material.encode().unwrap();
5625        assert_eq!(
5626            SemanticRootMaterial::decode(&bytes, root_material_limits()).unwrap(),
5627            material
5628        );
5629        let records = material.admission_records().unwrap();
5630        assert_eq!(records, admission.records);
5631        assert!(SemanticRootMaterial::decode(
5632            &bytes,
5633            SemanticAdmissionLimits {
5634                max_utf8_bytes: 1,
5635                ..root_material_limits()
5636            }
5637        )
5638        .is_err());
5639        material.records.records[1].arguments = vec![SemanticArgument::Symbol(0)];
5640        assert!(material.encode().is_err());
5641    }
5642
5643    #[test]
5644    fn root_material_capture_uses_ordinals_and_rejects_invalid_reachable_generations() {
5645        let root = SemanticRootHandle::new(17, 1, 4);
5646        let mut records = root_material_records();
5647        records.records.push(records.records[0].clone());
5648        records.supports.push(records.supports[1].clone());
5649        let admission = admit_semantic_records(
5650            records,
5651            root_material_limits(),
5652            SemanticRootHandle::new(17, 0, 1),
5653            || {
5654                Ok(SemanticRootSnapshot::new(
5655                    SemanticRootDigest(material_root_digest([0; 32], [0; 32], [0; 3])),
5656                    SemanticExtents::default(),
5657                ))
5658            },
5659        )
5660        .unwrap();
5661        let capacities = SemanticHypergraphCapacities::try_new(2, 3, 3, 3).unwrap();
5662        // Native ABI input to the same extraction function used after the cold
5663        // device copy. This checks the CPU decoder, not CUDA execution.
5664        let mut arena = vec![0u64; checked_arena_words(capacities).unwrap() as usize];
5665        arena[1] = root.owner;
5666        let statements = (CONTROL_WORDS + 2 * ROOT_WORDS + CANDIDATE_WORDS) as usize;
5667        let supports = statements + 3 * STATEMENT_WORDS as usize;
5668        let versions = supports + 3 * SUPPORT_WORDS as usize;
5669        let heads = versions + 3 * VERSION_WORDS as usize + 3;
5670        let root_offset = (CONTROL_WORDS + ROOT_WORDS) as usize;
5671        arena[root_offset] = 3;
5672        arena[root_offset + 1] = root.generation;
5673        arena[root_offset + 3..root_offset + 6].copy_from_slice(&[2, 2, 2]);
5674        // Unreachable records and a pending candidate are deliberately not valid
5675        // admissions. They must never enter the selected root's material.
5676        arena[CONTROL_WORDS as usize] = 2;
5677        arena[(CONTROL_WORDS + 2 * ROOT_WORDS) as usize] = 1;
5678        arena[versions + VERSION_WORDS as usize] = 99;
5679        let mut digest = material_root_digest([0; 32], [0; 32], [0; 3]);
5680        for (index, (statement_index, support_index, statement_slot, slot)) in
5681            [(1u32, 0u32, 1usize, 2usize), (7, 3, 0, 0)]
5682                .into_iter()
5683                .enumerate()
5684        {
5685            let key = admission.statement_key(statement_index).unwrap();
5686            let event = admission.support_event(support_index).unwrap();
5687            let support_digest = event.identity(key.identity).0;
5688            let version_digest =
5689                material_version_digest([0; 32], support_digest, event.polarity.code());
5690            let statement = statements + statement_slot * STATEMENT_WORDS as usize;
5691            arena[statement] = 3;
5692            arena[statement + 1] = 8;
5693            arena[statement + 3..statement + 7].copy_from_slice(&identity_words(key.identity.0));
5694            let support = supports + slot * SUPPORT_WORDS as usize;
5695            arena[support..support + 6].copy_from_slice(&[
5696                3,
5697                9,
5698                0,
5699                statement_slot as u64,
5700                8,
5701                event.polarity.code(),
5702            ]);
5703            arena[support + 6..support + 10].copy_from_slice(&identity_words(support_digest));
5704            arena[support + 10] = u64::from(statement_index);
5705            arena[support + 11] = u64::from(support_index);
5706            let version = versions + slot * VERSION_WORDS as usize;
5707            arena[version..version + 9].copy_from_slice(&[
5708                3,
5709                10,
5710                0,
5711                statement_slot as u64,
5712                8,
5713                slot as u64,
5714                9,
5715                0,
5716                event.polarity.code(),
5717            ]);
5718            arena[version + 9..version + 13].copy_from_slice(&identity_words(version_digest));
5719            arena[version + 13] = index as u64 + 1;
5720            arena[heads + statement_slot] = slot as u64 + 1;
5721            digest = material_root_digest(digest, version_digest, [(index + 1) as u32; 3]);
5722        }
5723        arena[root_offset + 6..root_offset + 10].copy_from_slice(&identity_words(digest));
5724        let snapshot =
5725            SemanticRootSnapshot::new(SemanticRootDigest(digest), SemanticExtents::new(2, 2, 2));
5726        let material = material_from_arena(&arena, capacities, root, snapshot, &admission).unwrap();
5727        assert_eq!(
5728            material
5729                .insertions
5730                .iter()
5731                .map(|insertion| insertion.statement)
5732                .collect::<Vec<_>>(),
5733            [Some(1), Some(7)]
5734        );
5735        assert_eq!(
5736            material
5737                .insertions
5738                .iter()
5739                .map(|insertion| insertion.support)
5740                .collect::<Vec<_>>(),
5741            [0, 3]
5742        );
5743        assert_eq!(material.extents, [2; 3]);
5744        for (offset, value) in [
5745            (versions + 13, 0),
5746            (versions + 13, 1),
5747            (versions + 6, 8),
5748            (versions + 7, 1),
5749            (heads, 4),
5750            (root_offset + 1, 3),
5751            (supports + 10, 1),
5752            (supports + 10, u64::MAX),
5753            (supports + 11, 0),
5754            (supports + 11, u64::MAX),
5755        ] {
5756            let mut damaged = arena.clone();
5757            damaged[offset] = value;
5758            assert!(material_from_arena(&damaged, capacities, root, snapshot, &admission).is_err());
5759        }
5760    }
5761
5762    #[test]
5763    fn root_material_codec_rejects_noncanonical_boolean_and_unknown_scalar() {
5764        let mut records = root_material_records();
5765        records.predicates[0].schema = Schema::new(vec![("value".into(), ScalarType::Bool)]);
5766        records.records[0].arguments = vec![SemanticArgument::Bool(false)];
5767        records.records[1].arguments = vec![SemanticArgument::Bool(true)];
5768        let material = SemanticRootMaterial {
5769            records,
5770            symbols: vec![],
5771            insertions: vec![],
5772            digest: [0; 32],
5773            extents: [0; 3],
5774            admission_base_digest: material_root_digest([0; 32], [0; 32], [0; 3]),
5775            admission_base_extents: [0; 3],
5776        };
5777        let encoded = material.encode().unwrap();
5778        let mut reader = SemanticMaterialReader::new(&encoded);
5779        reader.take(12).unwrap();
5780        for _ in 0..reader.u32().unwrap() {
5781            reader.take(5).unwrap();
5782            for _ in 0..reader.u32().unwrap() {
5783                reader.bytes().unwrap();
5784                reader.u8().unwrap();
5785                reader.bytes().unwrap();
5786            }
5787            let keys = reader.u32().unwrap() as usize;
5788            reader.take(keys * 4).unwrap();
5789        }
5790        assert_eq!(reader.u32().unwrap(), material.records.records.len() as u32);
5791        reader.u32().unwrap();
5792        assert_eq!(reader.u32().unwrap(), 1);
5793        let scalar_offset = encoded.len() - reader.remaining.len();
5794        assert_eq!(reader.u8().unwrap(), ScalarType::Bool.to_code());
5795        let mut invalid = encoded.clone();
5796        invalid[scalar_offset + 1] = 2;
5797        assert!(SemanticRootMaterial::decode(&invalid, root_material_limits()).is_err());
5798        invalid = encoded;
5799        invalid[scalar_offset] = u8::MAX;
5800        assert!(SemanticRootMaterial::decode(&invalid, root_material_limits()).is_err());
5801    }
5802
5803    #[test]
5804    #[ignore = "requires explicitly authorized CUDA execution"]
5805    fn root_material_native_restore_preserves_history_after_retirement_and_slot_reuse() {
5806        let mut graph = retirement_test_graph().expect("XLOG_REQUIRE_CUDA=1 is required");
5807        graph.device_controlled = false;
5808        graph = graph
5809            .admit_initial_records(root_material_records(), &[0], root_material_limits())
5810            .unwrap();
5811        graph.enter_transition();
5812        let insert = |graph: &mut SemanticHypergraph,
5813                      base: SemanticRootHandle,
5814                      statement: u32,
5815                      support: u32| {
5816            let admission = graph.admission.as_ref().unwrap();
5817            let key = admission.statement_key(statement).unwrap();
5818            let event = admission.support_event(support).unwrap();
5819            let fork = retirement_command(
5820                graph,
5821                OP_FORK,
5822                &[(8, base.slot as u64), (9, base.generation)],
5823            );
5824            assert_eq!(fork.words[0], STATUS_OK);
5825            let mut command = graph.command_for(OP_INSERT_SUPPORT);
5826            command.words[9] = fork.words[6];
5827            command.words[12] = event.polarity.code();
5828            command.words[13] = u64::from(key.record);
5829            command.words[14] = u64::from(event.record);
5830            command.words[16..20].copy_from_slice(&identity_words(key.identity.0));
5831            command.words[20..24].copy_from_slice(&identity_words(event.identity(key.identity).0));
5832            assert_eq!(
5833                graph.run(command, ArenaAccess::ReadWrite).unwrap().words[0],
5834                STATUS_OK
5835            );
5836            let sealed = retirement_command(graph, OP_SEAL, &[(9, fork.words[6])]);
5837            assert_eq!(sealed.words[0], STATUS_OK);
5838            SemanticRootHandle::new(graph.owner, sealed.words[3] as u32, sealed.words[4])
5839        };
5840        let original_base = graph.admission().unwrap().base();
5841        let original_base_snapshot = *graph.admission().unwrap().base_snapshot();
5842        let unrelated = insert(&mut graph, original_base, 1, 1);
5843        let first = insert(&mut graph, original_base, 1, 0);
5844        assert_eq!(
5845            retirement_root(&mut graph, unrelated, first).words[0],
5846            STATUS_OK
5847        );
5848        let second = insert(&mut graph, first, 0, 1);
5849        let root = insert(&mut graph, second, 1, 1);
5850        assert_eq!(
5851            retirement_root(&mut graph, original_base, root).words[0],
5852            STATUS_OK
5853        );
5854        let material = graph.export_transition_root(root).unwrap();
5855        assert_eq!(
5856            material
5857                .insertions
5858                .iter()
5859                .map(|insertion| (insertion.statement, insertion.support))
5860                .collect::<Vec<_>>(),
5861            [(Some(0), 0), (Some(1), 0), (Some(0), 1), (Some(1), 1)]
5862        );
5863        assert_eq!(
5864            material.admission_base_digest,
5865            original_base_snapshot.digest.0
5866        );
5867        assert_eq!(material.admission_base_extents, [1; 3]);
5868        let pending = retirement_command(
5869            &mut graph,
5870            OP_FORK,
5871            &[(8, root.slot as u64), (9, root.generation)],
5872        );
5873        assert_eq!(pending.words[0], STATUS_OK);
5874        let key = graph.admission().unwrap().statement_key(0).unwrap();
5875        let event = graph.admission().unwrap().support_event(2).unwrap();
5876        let mut pending_insert = graph.command_for(OP_INSERT_SUPPORT);
5877        pending_insert.words[9] = pending.words[6];
5878        pending_insert.words[12] = event.polarity.code();
5879        pending_insert.words[13] = u64::from(key.record);
5880        pending_insert.words[14] = u64::from(event.record);
5881        pending_insert.words[16..20].copy_from_slice(&identity_words(key.identity.0));
5882        pending_insert.words[20..24]
5883            .copy_from_slice(&identity_words(event.identity(key.identity).0));
5884        let pending_inserted = graph.run(pending_insert, ArenaAccess::ReadWrite).unwrap();
5885        assert_eq!(pending_inserted.words[0], STATUS_OK);
5886        assert_eq!(pending_inserted.words[1], OUTCOME_INSERTED);
5887        assert_eq!(graph.export_transition_root(root).unwrap(), material);
5888        let decoded =
5889            SemanticRootMaterial::decode(&material.encode().unwrap(), root_material_limits())
5890                .unwrap();
5891        let mut too_small = graph
5892            .provider
5893            .allocate_semantic_hypergraph(
5894                &graph.domain,
5895                SemanticHypergraphCapacities::try_new(2, 8, 16, 16).unwrap(),
5896            )
5897            .unwrap();
5898        too_small
5899            .admit_records(
5900                too_small.empty_root,
5901                decoded.admission_records().unwrap(),
5902                root_material_limits(),
5903            )
5904            .unwrap();
5905        let before = too_small.execution_stats();
5906        assert!(too_small.restore_root(&decoded).is_err());
5907        assert_eq!(too_small.execution_stats(), before);
5908        let mut restored = graph
5909            .provider
5910            .allocate_semantic_hypergraph(&graph.domain, graph.capacities)
5911            .unwrap();
5912        restored
5913            .admit_records(
5914                restored.empty_root,
5915                decoded.admission_records().unwrap(),
5916                root_material_limits(),
5917            )
5918            .unwrap();
5919        let before = restored.execution_stats();
5920        let mut damaged = decoded.clone();
5921        damaged.digest[0] ^= 1;
5922        assert!(restored.restore_root(&damaged).is_err());
5923        assert_eq!(restored.execution_stats(), before);
5924        let fresh = restored.restore_root(&decoded).unwrap();
5925        assert_ne!(fresh.owner, root.owner);
5926        assert_ne!(restored.admission().unwrap().base(), fresh);
5927        assert_eq!(
5928            *restored.admission().unwrap().base_snapshot(),
5929            original_base_snapshot
5930        );
5931        assert_eq!(restored.export_transition_root(fresh).unwrap(), decoded);
5932        assert!(restored.restore_root(&decoded).is_err());
5933        assert!(matches!(
5934            restored.export_transition_root(root),
5935            Err(SemanticHypergraphError::ForeignHandle { .. })
5936        ));
5937        assert!(graph.export_transition_root(unrelated).is_err());
5938    }
5939
5940    fn numeric_records() -> SemanticAdmissionRecords {
5941        let types = [
5942            ScalarType::U32,
5943            ScalarType::U64,
5944            ScalarType::I32,
5945            ScalarType::I64,
5946            ScalarType::F32,
5947            ScalarType::F64,
5948            ScalarType::Bool,
5949        ];
5950        SemanticAdmissionRecords {
5951            predicates: vec![
5952                SemanticPredicateRecord {
5953                    predicate: RelId(7),
5954                    role: SemanticRecordRole::Statement,
5955                    schema: Schema::new(
5956                        types
5957                            .into_iter()
5958                            .enumerate()
5959                            .map(|(i, ty)| (format!("column_{i}"), ty))
5960                            .collect(),
5961                    ),
5962                },
5963                SemanticPredicateRecord {
5964                    predicate: RelId(8),
5965                    role: SemanticRecordRole::Qualifier,
5966                    schema: Schema::new(vec![("qualifier".into(), ScalarType::U64)]),
5967                },
5968            ],
5969            records: vec![
5970                SemanticTypedRecord {
5971                    predicate: RelId(7),
5972                    arguments: vec![
5973                        SemanticArgument::U32(u32::MAX),
5974                        SemanticArgument::U64(u64::MAX),
5975                        SemanticArgument::I32(i32::MIN),
5976                        SemanticArgument::I64(i64::MIN),
5977                        SemanticArgument::F32Bits(0x7fc0_0001),
5978                        SemanticArgument::F64Bits(0x8000_0000_0000_0000),
5979                        SemanticArgument::Bool(false),
5980                    ],
5981                    qualifiers: vec![1, 2],
5982                },
5983                SemanticTypedRecord {
5984                    predicate: RelId(8),
5985                    arguments: vec![SemanticArgument::U64(1)],
5986                    qualifiers: vec![],
5987                },
5988                SemanticTypedRecord {
5989                    predicate: RelId(8),
5990                    arguments: vec![SemanticArgument::U64(2)],
5991                    qualifiers: vec![],
5992                },
5993            ],
5994            supports: vec![],
5995        }
5996    }
5997
5998    pub(crate) fn admit_numeric(records: SemanticAdmissionRecords) -> SemanticAdmission {
5999        admit_semantic_records(
6000            records,
6001            SemanticAdmissionLimits {
6002                max_records: 5,
6003                max_terms: 25,
6004                max_references: 2,
6005                max_utf8_bytes: 1024,
6006            },
6007            SemanticRootHandle::new(1, 0, 1),
6008            || {
6009                Ok(SemanticRootSnapshot::new(
6010                    SemanticRootDigest([0; 32]),
6011                    SemanticExtents::default(),
6012                ))
6013            },
6014        )
6015        .unwrap()
6016    }
6017
6018    #[test]
6019    fn admitted_schema_bytes_are_the_generation_preimage() {
6020        let records = numeric_records();
6021        let baseline = admit_numeric(records.clone());
6022        assert!(!baseline.schema_bytes().is_empty());
6023        assert_eq!(
6024            Sha256::digest(baseline.schema_bytes()).as_slice(),
6025            baseline.schema_generation().as_bytes()
6026        );
6027
6028        let mut different_fact = records.clone();
6029        different_fact.records[0].arguments[0] = SemanticArgument::U32(0);
6030        assert_eq!(
6031            admit_numeric(different_fact).schema_bytes(),
6032            baseline.schema_bytes()
6033        );
6034
6035        let mut different_schema = records;
6036        different_schema.predicates[0].schema.key_columns.reverse();
6037        assert_ne!(
6038            admit_numeric(different_schema).schema_bytes(),
6039            baseline.schema_bytes()
6040        );
6041    }
6042
6043    #[test]
6044    fn typed_admission_identity_preserves_numeric_bits_schema_and_qualifier_order() {
6045        let records = numeric_records();
6046        let baseline = admit_numeric(records.clone());
6047        let original = baseline.statement_key(0).unwrap().identity();
6048        for (column, value) in [
6049            (0, SemanticArgument::U32(0)),
6050            (1, SemanticArgument::U64(0)),
6051            (2, SemanticArgument::I32(-1)),
6052            (3, SemanticArgument::I64(-1)),
6053            (4, SemanticArgument::F32Bits(0x7fc0_0002)), // retain NaN payload
6054            (5, SemanticArgument::F64Bits(0)),           // distinguish negative zero
6055            (6, SemanticArgument::Bool(true)),
6056        ] {
6057            let mut changed = records.clone();
6058            changed.records[0].arguments[column] = value;
6059            assert_ne!(
6060                admit_numeric(changed).statement_key(0).unwrap().identity(),
6061                original
6062            );
6063        }
6064        let mut changed = records.clone();
6065        changed.records[0].qualifiers.reverse();
6066        assert_ne!(
6067            admit_numeric(changed).statement_key(0).unwrap().identity(),
6068            original
6069        );
6070        let mut changed = records.clone();
6071        changed.records[0].qualifiers.clear();
6072        assert_ne!(
6073            admit_numeric(changed).statement_key(0).unwrap().identity(),
6074            original
6075        );
6076        let mut changed = records.clone();
6077        changed.predicates[0].schema.key_columns.reverse();
6078        let changed = admit_numeric(changed);
6079        assert_ne!(changed.schema_generation(), baseline.schema_generation());
6080        assert_ne!(changed.statement_key(0).unwrap().identity(), original);
6081        let mut changed = records.clone();
6082        changed.predicates[0].schema = changed.predicates[0]
6083            .schema
6084            .clone()
6085            .with_sort_labels((0..7).map(|i| format!("sort_{i}")).collect())
6086            .unwrap();
6087        assert_ne!(
6088            admit_numeric(changed).statement_key(0).unwrap().identity(),
6089            original
6090        );
6091        let mut changed = records.clone();
6092        changed.predicates[0].schema.columns[0].0 = "renamed column".into();
6093        assert_ne!(
6094            admit_numeric(changed).statement_key(0).unwrap().identity(),
6095            original
6096        );
6097        assert_eq!(
6098            baseline.records(),
6099            &records,
6100            "the accepted schema and typed bytes are retained"
6101        );
6102    }
6103
6104    #[test]
6105    fn typed_admission_checks_complete_input_before_observing_base() {
6106        let mut records = numeric_records();
6107        records.records[0].arguments[0] = SemanticArgument::I32(0);
6108        let result = admit_semantic_records(
6109            records,
6110            SemanticAdmissionLimits {
6111                max_records: 5,
6112                max_terms: 25,
6113                max_references: 2,
6114                max_utf8_bytes: 1024,
6115            },
6116            SemanticRootHandle::new(1, 0, 1),
6117            || panic!("invalid input must not observe CUDA"),
6118        );
6119        assert!(matches!(
6120            result,
6121            Err(SemanticHypergraphError::InvalidInput { .. })
6122        ));
6123    }
6124
6125    #[test]
6126    fn typed_admission_identity_rebinds_the_base_without_reencoding_records() {
6127        let admission = admit_numeric(numeric_records());
6128        let identity_for = |snapshot| {
6129            derive_admission_identity(
6130                &admission.records,
6131                admission.schema_generation,
6132                &admission.encoded_records,
6133                &admission.statement_keys,
6134                &admission.support_events,
6135                snapshot,
6136            )
6137        };
6138        assert_eq!(identity_for(&admission.base_snapshot), admission.identity);
6139        let populated =
6140            SemanticRootSnapshot::new(SemanticRootDigest([17; 32]), SemanticExtents::new(1, 2, 2));
6141        assert_ne!(identity_for(&populated), admission.identity);
6142        let changed_extents =
6143            SemanticRootSnapshot::new(*populated.digest(), SemanticExtents::new(1, 2, 3));
6144        assert_ne!(identity_for(&populated), identity_for(&changed_extents));
6145        let changed_digest =
6146            SemanticRootSnapshot::new(SemanticRootDigest([18; 32]), populated.extents());
6147        assert_ne!(identity_for(&populated), identity_for(&changed_digest));
6148        assert_eq!(identity_for(&admission.base_snapshot), admission.identity);
6149    }
6150
6151    #[test]
6152    fn host_device_divergence_statuses_are_integrity_failures() {
6153        for status in [
6154            STATUS_FOREIGN_OWNER,
6155            STATUS_SLOT_OUT_OF_RANGE,
6156            STATUS_STALE_GENERATION,
6157            STATUS_INACTIVE,
6158            STATUS_CORRUPT_LINEAGE,
6159            STATUS_INVALID_COMMAND,
6160            STATUS_ARENA_MISMATCH,
6161            u64::MAX,
6162        ] {
6163            assert!(device_status_is_integrity_failure(status));
6164        }
6165        for status in [
6166            STATUS_OK,
6167            STATUS_NOT_REACHABLE,
6168            STATUS_STATEMENT_CAPACITY,
6169            STATUS_SUPPORT_CAPACITY,
6170            STATUS_VERSION_CAPACITY,
6171            STATUS_ROOT_CAPACITY,
6172            STATUS_GENERATION_EXHAUSTED,
6173        ] {
6174            assert!(!device_status_is_integrity_failure(status));
6175        }
6176    }
6177
6178    #[test]
6179    fn reconciliation_failure_poisoning_is_sticky() {
6180        let mut poisoned = false;
6181        let failure = Err::<(), _>(SemanticHypergraphError::CorruptLineage {
6182            detail: "test reconciliation failure".into(),
6183        });
6184        assert!(poison_after_reconciliation_error(&mut poisoned, failure).is_err());
6185        assert!(poisoned);
6186        assert!(poison_after_reconciliation_error(&mut poisoned, Ok(())).is_ok());
6187        assert!(poisoned);
6188    }
6189
6190    #[test]
6191    fn resident_refusal_retires_only_its_acquired_candidate() {
6192        if std::env::var("XLOG_REQUIRE_CUDA").as_deref() != Ok("1") {
6193            eprintln!("Skipping: set XLOG_REQUIRE_CUDA=1 to run this real-CUDA contract");
6194            return;
6195        }
6196        let provider = CudaProviderBuilder::new(0, MemoryBudget::with_limit(64 * 1024 * 1024))
6197            .with_stream_capacity(1)
6198            .build()
6199            .unwrap();
6200        let provider = Arc::new(provider);
6201        let runtime = Arc::clone(provider.memory().runtime().unwrap());
6202        let stream_id = runtime.stream_pool().acquire().unwrap();
6203        let stream = runtime.stream_pool().resolve(stream_id).unwrap();
6204        let domain = provider
6205            .bind_resident_execution_domain(runtime, stream_id, Arc::clone(&stream))
6206            .unwrap();
6207        let mut graph = provider
6208            .allocate_semantic_hypergraph(
6209                &domain,
6210                SemanticHypergraphCapacities::try_new(2, 1, 1, 1).unwrap(),
6211            )
6212            .unwrap();
6213        let bytes = std::mem::size_of::<SemanticResidentDecodedStatement>()
6214            + 2 * std::mem::size_of::<SemanticResidentDecodedSupport>()
6215            + std::mem::size_of::<SemanticResidentHandleRecord>()
6216            + 11 * std::mem::size_of::<SemanticResidentReceiptRecord>();
6217        let mut reservation = provider.memory().reserve_bytes(bytes as u64).unwrap();
6218        let mut statements = reservation
6219            .alloc::<SemanticResidentDecodedStatement>(1)
6220            .unwrap();
6221        let mut supports = reservation
6222            .alloc::<SemanticResidentDecodedSupport>(2)
6223            .unwrap();
6224        let handles = reservation
6225            .alloc::<SemanticResidentHandleRecord>(1)
6226            .unwrap();
6227        let receipts = reservation
6228            .alloc::<SemanticResidentReceiptRecord>(11)
6229            .unwrap();
6230        assert_eq!(reservation.remaining_bytes(), 0);
6231        provider
6232            .htod_sync_copy_into_tracked(
6233                &[SemanticResidentDecodedStatement {
6234                    identity_words: [17; 8],
6235                    record: 0,
6236                    reconstruction: [0; 10],
6237                }],
6238                &mut statements,
6239            )
6240            .unwrap();
6241        let events = [1, 2].map(|value| SemanticResidentDecodedSupport {
6242            polarity: 1,
6243            provenance_words: [value; 8],
6244            source_words: [3; 8],
6245            context_words: [4; 8],
6246            scope_words: [5; 8],
6247            record: value - 1,
6248        });
6249        provider
6250            .htod_sync_copy_into_tracked(&events, &mut supports)
6251            .unwrap();
6252        let mut setup = domain.new_strict_recorder();
6253        setup.read_write(&statements);
6254        setup.read_write(&supports);
6255        setup.write(&handles);
6256        setup.write(&receipts);
6257        unsafe { domain.enqueue(setup, |_| Ok::<(), XlogError>(())) }
6258            .unwrap()
6259            .commit()
6260            .unwrap();
6261        stream.synchronize().unwrap();
6262        let before = provider.host_transfer_stats();
6263        let metadata_before = provider.host_launch_metadata_transfer_stats();
6264        let untracked_before = provider.untracked_metadata_dtoh_count();
6265        let captured = CapturedCudaGraph::capture_on_stream(&stream, || {
6266            let slot =
6267                |index| SemanticResidentReceiptSlot::new(&receipts, index).map_err(kernel_error);
6268            let statement =
6269                || SemanticResidentStatementBank::new(&statements, 0).map_err(kernel_error);
6270            let support =
6271                |index| SemanticResidentSupportBank::new(&supports, index).map_err(kernel_error);
6272            let base = graph
6273                .enqueue_resident_empty_root_handle(
6274                    SemanticResidentHandleSlot::new(&handles, 0).map_err(kernel_error)?,
6275                    slot(0)?,
6276                )
6277                .map_err(kernel_error)?;
6278            let first = graph
6279                .enqueue_resident_fork(base.handle(), slot(1)?)
6280                .map_err(kernel_error)?;
6281            let inserted = graph
6282                .enqueue_resident_insert_support(
6283                    first.candidate_handle(),
6284                    statement()?,
6285                    support(0)?,
6286                    slot(2)?,
6287                )
6288                .map_err(kernel_error)?;
6289            let refused = graph
6290                .enqueue_resident_insert_support(
6291                    inserted.candidate_handle(),
6292                    statement()?,
6293                    support(1)?,
6294                    slot(3)?,
6295                )
6296                .map_err(kernel_error)?;
6297            graph
6298                .enqueue_resident_seal(refused.candidate_handle(), slot(4)?)
6299                .map_err(kernel_error)?;
6300            let second = graph
6301                .enqueue_resident_fork(base.handle(), slot(5)?)
6302                .map_err(kernel_error)?;
6303            let denied = graph
6304                .enqueue_resident_fork(base.handle(), slot(6)?)
6305                .map_err(kernel_error)?;
6306            graph
6307                .enqueue_resident_seal(denied.candidate_handle(), slot(7)?)
6308                .map_err(kernel_error)?;
6309            let inserted = graph
6310                .enqueue_resident_insert_support(
6311                    second.candidate_handle(),
6312                    statement()?,
6313                    support(1)?,
6314                    slot(8)?,
6315                )
6316                .map_err(kernel_error)?;
6317            graph
6318                .enqueue_resident_seal(inserted.candidate_handle(), slot(9)?)
6319                .map_err(kernel_error)?;
6320            graph
6321                .enqueue_resident_insert_support(
6322                    first.candidate_handle(),
6323                    statement()?,
6324                    support(0)?,
6325                    slot(10)?,
6326                )
6327                .map_err(kernel_error)?;
6328            Ok(())
6329        })
6330        .unwrap();
6331        captured.launch(&stream).unwrap();
6332        let after = provider.host_transfer_stats();
6333        let metadata_after = provider.host_launch_metadata_transfer_stats();
6334        assert_eq!(
6335            (
6336                after.htod_calls,
6337                after.htod_bytes,
6338                after.dtoh_calls,
6339                after.dtoh_bytes
6340            ),
6341            (
6342                before.htod_calls,
6343                before.htod_bytes,
6344                before.dtoh_calls,
6345                before.dtoh_bytes
6346            )
6347        );
6348        assert_eq!(
6349            (metadata_after.htod_calls, metadata_after.htod_bytes),
6350            (metadata_before.htod_calls, metadata_before.htod_bytes)
6351        );
6352        assert_eq!(provider.untracked_metadata_dtoh_count(), untracked_before);
6353        stream.synchronize().unwrap();
6354        let observed = provider
6355            .dtoh_small_metadata_untracked(&receipts, 11)
6356            .unwrap();
6357        assert_eq!(observed[2].words[0], STATUS_OK);
6358        assert_eq!(observed[3].words[0], STATUS_SUPPORT_CAPACITY);
6359        assert_eq!(observed[4].words[0], STATUS_SUPPORT_CAPACITY);
6360        assert_eq!(
6361            observed[5].words[0], STATUS_OK,
6362            "the refused candidate must be retired"
6363        );
6364        assert_eq!(observed[6].words[0], STATUS_INACTIVE);
6365        assert_eq!(observed[7].words[0], STATUS_INACTIVE);
6366        assert_eq!(
6367            observed[8].words[0], STATUS_OK,
6368            "a failed fork must not retire another candidate"
6369        );
6370        assert_eq!(observed[9].words[0], STATUS_OK);
6371        assert_eq!(&observed[9].words[13..16], &[1, 1, 1]);
6372        assert_eq!(observed[10].words[0], STATUS_STALE_GENERATION);
6373        assert_eq!(observed[8].words[8], observed[2].words[8] + 1);
6374        assert_eq!(observed[8].words[10], observed[2].words[10] + 1);
6375        assert_eq!(observed[8].words[12], observed[2].words[12] + 1);
6376    }
6377
6378    #[test]
6379    fn transition_reserve_checks_both_lanes_without_mutating_the_arena() {
6380        if std::env::var("XLOG_REQUIRE_CUDA").as_deref() != Ok("1") {
6381            return;
6382        }
6383        let provider = CudaProviderBuilder::new(0, MemoryBudget::with_limit(64 * 1024 * 1024))
6384            .with_stream_capacity(1)
6385            .build()
6386            .unwrap();
6387        let provider = Arc::new(provider);
6388        let runtime = Arc::clone(provider.memory().runtime().unwrap());
6389        let stream_id = runtime.stream_pool().acquire().unwrap();
6390        let stream = runtime.stream_pool().resolve(stream_id).unwrap();
6391        let domain = provider
6392            .bind_resident_execution_domain(runtime, stream_id, stream)
6393            .unwrap();
6394        let check = |caps: [u32; 4], changes: &[(usize, u64)], expected: u64| {
6395            let mut graph = provider
6396                .allocate_semantic_hypergraph(
6397                    &domain,
6398                    SemanticHypergraphCapacities::try_new(caps[0], caps[1], caps[2], caps[3])
6399                        .unwrap(),
6400                )
6401                .unwrap();
6402            let mut before = download_test_arena(&provider, &graph);
6403            for &(index, value) in changes {
6404                before[index] = value;
6405            }
6406            provider
6407                .htod_sync_copy_into_tracked(&before, &mut graph.arena)
6408                .unwrap();
6409            // The private command is the same read-only pre-draw operation used
6410            // by the resident dispatcher; it does not mint a reusable host permit.
6411            let mut command = graph.command_for(OP_PREFLIGHT_TRANSITION);
6412            command.words[8] = 0;
6413            command.words[9] = 1;
6414            let receipt = graph.run(command, ArenaAccess::Read).unwrap();
6415            assert_eq!(
6416                receipt.words[0], expected,
6417                "capacities={caps:?}, changes={changes:?}"
6418            );
6419            assert_eq!(
6420                receipt.words[41], 0,
6421                "preflight must not acquire a candidate"
6422            );
6423            let after = download_test_arena(&provider, &graph);
6424            assert_eq!(
6425                after, before,
6426                "preflight, including refusal, must be read-only"
6427            );
6428        };
6429        check([3, 4, 4, 4], &[], STATUS_OK);
6430        check([3, 4, 4, 4], &[(1, 0)], STATUS_FOREIGN_OWNER);
6431        check(
6432            [3, 4, 4, 4],
6433            &[(CONTROL_WORDS as usize + 1, 2)],
6434            STATUS_STALE_GENERATION,
6435        );
6436        check(
6437            [3, 4, 4, 4],
6438            &[(CONTROL_WORDS as usize, 0)],
6439            STATUS_INACTIVE,
6440        );
6441        check(
6442            [3, 4, 4, 4],
6443            &[((CONTROL_WORDS + ROOT_WORDS) as usize, 3)],
6444            STATUS_ROOT_CAPACITY,
6445        );
6446        check([2, 4, 4, 4], &[], STATUS_ROOT_CAPACITY);
6447        check([3, 3, 4, 4], &[], STATUS_STATEMENT_CAPACITY);
6448        check([3, 4, 3, 4], &[], STATUS_SUPPORT_CAPACITY);
6449        check([3, 4, 4, 3], &[], STATUS_VERSION_CAPACITY);
6450        let candidate = (CONTROL_WORDS + 3 * ROOT_WORDS) as usize;
6451        check([3, 4, 4, 4], &[(candidate, 1)], STATUS_INACTIVE);
6452        check([3, 4, 4, 4], &[(candidate + 1, u64::MAX - 2)], STATUS_OK);
6453        check(
6454            [3, 4, 4, 4],
6455            &[(candidate + 1, u64::MAX - 1)],
6456            STATUS_GENERATION_EXHAUSTED,
6457        );
6458        check(
6459            [3, 4, 4, 4],
6460            &[(candidate + 1, u64::MAX)],
6461            STATUS_GENERATION_EXHAUSTED,
6462        );
6463        let statements = candidate + CANDIDATE_WORDS as usize;
6464        let supports = statements + 4 * STATEMENT_WORDS as usize;
6465        let versions = supports + 4 * SUPPORT_WORDS as usize;
6466        // Count allocator order, not physical slot ordinal: a retained slot can
6467        // precede the first available record and need not have a reusable gen.
6468        check(
6469            [3, 5, 4, 4],
6470            &[(statements, 3), (statements + 1, u64::MAX)],
6471            STATUS_OK,
6472        );
6473        check(
6474            [3, 5, 4, 4],
6475            &[
6476                (statements, 3),
6477                (statements + STATEMENT_WORDS as usize + 1, u64::MAX - 1),
6478            ],
6479            STATUS_GENERATION_EXHAUSTED,
6480        );
6481        check(
6482            [3, 5, 4, 4],
6483            &[
6484                (statements, 3),
6485                (statements + 3 * STATEMENT_WORDS as usize + 1, u64::MAX - 1),
6486            ],
6487            STATUS_OK,
6488        );
6489        for (start, width, exhausted) in [
6490            (
6491                statements,
6492                STATEMENT_WORDS as usize,
6493                STATUS_STATEMENT_CAPACITY,
6494            ),
6495            (supports, SUPPORT_WORDS as usize, STATUS_SUPPORT_CAPACITY),
6496            (versions, VERSION_WORDS as usize, STATUS_VERSION_CAPACITY),
6497        ] {
6498            // A record retained by a different root is not available merely
6499            // because the acquired empty base has zero extents.
6500            check([3, 4, 4, 4], &[(start, 3)], exhausted);
6501            for slot in 0..4 {
6502                let generation = start + slot * width + 1;
6503                let maximum = u64::MAX - if slot < 2 { 2 } else { 1 };
6504                check([3, 4, 4, 4], &[(generation, maximum)], STATUS_OK);
6505                check(
6506                    [3, 4, 4, 4],
6507                    &[(generation, maximum + 1)],
6508                    STATUS_GENERATION_EXHAUSTED,
6509                );
6510            }
6511        }
6512        for extent in 3..6 {
6513            check(
6514                [3, 4, 4, 4],
6515                &[(CONTROL_WORDS as usize + extent, u32::MAX as u64 - 1)],
6516                STATUS_CORRUPT_LINEAGE,
6517            );
6518        }
6519        // Reachable statements need insertion headroom even though they are not
6520        // free. Keep the version/support generation references coherent so the
6521        // failure is generation exhaustion, not an earlier broken-link check.
6522        let statement = (CONTROL_WORDS + 4 * ROOT_WORDS + CANDIDATE_WORDS) as usize;
6523        let support = statement + 5 * STATEMENT_WORDS as usize;
6524        let version = support + 5 * SUPPORT_WORDS as usize;
6525        let heads = version + 5 * VERSION_WORDS as usize;
6526        let mut reachable = vec![
6527            (CONTROL_WORDS as usize + 3, 1),
6528            (CONTROL_WORDS as usize + 4, 1),
6529            (CONTROL_WORDS as usize + 5, 1),
6530            (statement, 3),
6531            (support, 3),
6532            (version, 3),
6533            (heads, 1),
6534            (version + 6, 1),
6535        ];
6536        for generation in [u64::MAX - 1, u64::MAX] {
6537            reachable.extend([
6538                (statement + 1, generation),
6539                (support + 4, generation),
6540                (version + 4, generation),
6541            ]);
6542            check(
6543                [4, 5, 5, 5],
6544                &reachable,
6545                if generation == u64::MAX {
6546                    STATUS_GENERATION_EXHAUSTED
6547                } else {
6548                    STATUS_OK
6549                },
6550            );
6551            reachable.truncate(8);
6552        }
6553    }
6554
6555    #[test]
6556    fn resident_transition_reserve_precedes_two_lanes_and_survives_refusal() {
6557        if std::env::var("XLOG_REQUIRE_CUDA").as_deref() != Ok("1") {
6558            return;
6559        }
6560        let provider = CudaProviderBuilder::new(0, MemoryBudget::with_limit(64 * 1024 * 1024))
6561            .with_stream_capacity(1)
6562            .build()
6563            .unwrap();
6564        let provider = Arc::new(provider);
6565        let runtime = Arc::clone(provider.memory().runtime().unwrap());
6566        let stream_id = runtime.stream_pool().acquire().unwrap();
6567        let stream = runtime.stream_pool().resolve(stream_id).unwrap();
6568        let domain = provider
6569            .bind_resident_execution_domain(runtime, stream_id, Arc::clone(&stream))
6570            .unwrap();
6571        for refuse_first in [false, true] {
6572            let mut graph = provider
6573                .allocate_semantic_hypergraph(
6574                    &domain,
6575                    SemanticHypergraphCapacities::try_new(3, 4, 4, 4).unwrap(),
6576                )
6577                .unwrap();
6578            assert_eq!(graph.arena_words * 8, 2080);
6579            let candidate = (CONTROL_WORDS + 3 * ROOT_WORDS) as usize;
6580            let statement_start = candidate + CANDIDATE_WORDS as usize;
6581            let support_start = statement_start + 4 * STATEMENT_WORDS as usize;
6582            let version_start = support_start + 4 * SUPPORT_WORDS as usize;
6583            let mut arena = download_test_arena(&provider, &graph);
6584            arena[candidate + 1] = u64::MAX - 2;
6585            for (start, width) in [
6586                (statement_start, STATEMENT_WORDS as usize),
6587                (support_start, SUPPORT_WORDS as usize),
6588                (version_start, VERSION_WORDS as usize),
6589            ] {
6590                for slot in 0..4 {
6591                    arena[start + slot * width + 1] = u64::MAX - if slot < 2 { 2 } else { 1 };
6592                }
6593            }
6594            provider
6595                .htod_sync_copy_into_tracked(&arena, &mut graph.arena)
6596                .unwrap();
6597            let bytes = 4 * std::mem::size_of::<SemanticResidentDecodedStatement>()
6598                + 4 * std::mem::size_of::<SemanticResidentDecodedSupport>()
6599                + std::mem::size_of::<SemanticResidentHandleRecord>()
6600                + 11 * std::mem::size_of::<SemanticResidentReceiptRecord>();
6601            let mut reservation = provider.memory().reserve_bytes(bytes as u64).unwrap();
6602            let mut statements = reservation
6603                .alloc::<SemanticResidentDecodedStatement>(4)
6604                .unwrap();
6605            let mut supports = reservation
6606                .alloc::<SemanticResidentDecodedSupport>(4)
6607                .unwrap();
6608            let handles = reservation
6609                .alloc::<SemanticResidentHandleRecord>(1)
6610                .unwrap();
6611            let receipts = reservation
6612                .alloc::<SemanticResidentReceiptRecord>(11)
6613                .unwrap();
6614            assert_eq!(reservation.remaining_bytes(), 0);
6615            provider
6616                .htod_sync_copy_into_tracked(
6617                    &[17, 18, 19, 20].map(|value| SemanticResidentDecodedStatement {
6618                        identity_words: [value; 8],
6619                        record: value - 17,
6620                        reconstruction: [0; 10],
6621                    }),
6622                    &mut statements,
6623                )
6624                .unwrap();
6625            provider
6626                .htod_sync_copy_into_tracked(
6627                    &[0, 1, 2, 3].map(|index| SemanticResidentDecodedSupport {
6628                        polarity: if refuse_first && index == 1 { 0 } else { 1 },
6629                        provenance_words: [index + 1; 8],
6630                        source_words: [3; 8],
6631                        context_words: [4; 8],
6632                        scope_words: [5; 8],
6633                        record: index,
6634                    }),
6635                    &mut supports,
6636                )
6637                .unwrap();
6638            let mut setup = domain.new_strict_recorder();
6639            setup.read_write(&graph.arena);
6640            setup.read_write(&statements);
6641            setup.read_write(&supports);
6642            setup.write(&handles);
6643            setup.write(&receipts);
6644            unsafe { domain.enqueue(setup, |_| Ok::<(), XlogError>(())) }
6645                .unwrap()
6646                .commit()
6647                .unwrap();
6648            stream.synchronize().unwrap();
6649            let captured = CapturedCudaGraph::capture_on_stream(&stream, || {
6650                let slot = |index| {
6651                    SemanticResidentReceiptSlot::new(&receipts, index).map_err(kernel_error)
6652                };
6653                let base = graph
6654                    .enqueue_resident_empty_root_handle(
6655                        SemanticResidentHandleSlot::new(&handles, 0).map_err(kernel_error)?,
6656                        slot(0)?,
6657                    )
6658                    .map_err(kernel_error)?;
6659                graph
6660                    .enqueue_resident_preflight_transition(base.handle(), slot(1)?)
6661                    .map_err(kernel_error)?;
6662                for lane in 0..2 {
6663                    let base = SemanticResidentRootHandle::Receipt(SemanticResidentReceiptView {
6664                        allocation: &receipts,
6665                        index: 1,
6666                    });
6667                    let first = 2 + lane * 4;
6668                    let fork = graph
6669                        .enqueue_resident_fork(base, slot(first)?)
6670                        .map_err(kernel_error)?;
6671                    let inserted = graph
6672                        .enqueue_resident_insert_support(
6673                            fork.candidate_handle(),
6674                            SemanticResidentStatementBank::new(&statements, lane * 2)
6675                                .map_err(kernel_error)?,
6676                            SemanticResidentSupportBank::new(&supports, lane * 2)
6677                                .map_err(kernel_error)?,
6678                            slot(first + 1)?,
6679                        )
6680                        .map_err(kernel_error)?;
6681                    let inserted = graph
6682                        .enqueue_resident_insert_support(
6683                            inserted.candidate_handle(),
6684                            SemanticResidentStatementBank::new(&statements, lane * 2 + 1)
6685                                .map_err(kernel_error)?,
6686                            SemanticResidentSupportBank::new(&supports, lane * 2 + 1)
6687                                .map_err(kernel_error)?,
6688                            slot(first + 2)?,
6689                        )
6690                        .map_err(kernel_error)?;
6691                    graph
6692                        .enqueue_resident_seal(inserted.candidate_handle(), slot(first + 3)?)
6693                        .map_err(kernel_error)?;
6694                }
6695                // Both terminals consumed the two reserved candidate generations.
6696                graph
6697                    .enqueue_resident_preflight_transition(base.handle(), slot(10)?)
6698                    .map_err(kernel_error)?;
6699                Ok(())
6700            })
6701            .unwrap();
6702            let before = provider.host_transfer_stats();
6703            let metadata_before = provider.host_launch_metadata_transfer_stats();
6704            let untracked_before = provider.untracked_metadata_dtoh_count();
6705            captured.launch(&stream).unwrap();
6706            let after = provider.host_transfer_stats();
6707            let metadata_after = provider.host_launch_metadata_transfer_stats();
6708            assert_eq!(
6709                (
6710                    before.htod_calls,
6711                    before.htod_bytes,
6712                    before.dtoh_calls,
6713                    before.dtoh_bytes
6714                ),
6715                (
6716                    after.htod_calls,
6717                    after.htod_bytes,
6718                    after.dtoh_calls,
6719                    after.dtoh_bytes
6720                )
6721            );
6722            assert_eq!(
6723                (metadata_before.htod_calls, metadata_before.htod_bytes),
6724                (metadata_after.htod_calls, metadata_after.htod_bytes)
6725            );
6726            assert_eq!(untracked_before, provider.untracked_metadata_dtoh_count());
6727            stream.synchronize().unwrap();
6728            let observed = provider
6729                .dtoh_small_metadata_untracked(&receipts, 11)
6730                .unwrap();
6731            assert_eq!(observed[1].words[0], STATUS_OK);
6732            assert_eq!(&observed[1].words[33..36], &[1, 0, 1]);
6733            assert_eq!(observed[1].words[41], 0);
6734            assert_eq!(observed[2].words[0], STATUS_OK);
6735            assert_eq!(
6736                observed[5].words[0],
6737                if refuse_first {
6738                    STATUS_INVALID_COMMAND
6739                } else {
6740                    STATUS_OK
6741                }
6742            );
6743            assert_eq!(
6744                observed[6].words[0], STATUS_OK,
6745                "second lane must start after first retirement"
6746            );
6747            assert_eq!(observed[9].words[0], STATUS_OK);
6748            assert_eq!(
6749                &observed[9].words[13..16],
6750                &[2, 2, 2],
6751                "both lanes start from the same empty base"
6752            );
6753            assert_eq!(observed[10].words[0], STATUS_GENERATION_EXHAUSTED);
6754            if refuse_first {
6755                assert_eq!(
6756                    observed[7].words[8],
6757                    u64::MAX - 1,
6758                    "discarded statement slot is reused"
6759                );
6760                assert_eq!(observed[7].words[10], u64::MAX - 1);
6761                assert_eq!(observed[7].words[12], u64::MAX - 1);
6762            } else {
6763                assert_eq!(&observed[5].words[13..16], &[2, 2, 2]);
6764                assert_ne!(observed[5].words[3], observed[9].words[3]);
6765            }
6766        }
6767    }
6768
6769    #[test]
6770    fn exhausted_candidate_generation_is_rejected_before_fork() {
6771        if std::env::var("XLOG_REQUIRE_CUDA").as_deref() != Ok("1") {
6772            eprintln!("Skipping: set XLOG_REQUIRE_CUDA=1 to run this real-CUDA contract");
6773            return;
6774        }
6775        let provider = CudaProviderBuilder::new(0, MemoryBudget::with_limit(64 * 1024 * 1024))
6776            .with_stream_capacity(1)
6777            .build()
6778            .unwrap();
6779        let provider = Arc::new(provider);
6780        let runtime = Arc::clone(provider.memory().runtime().unwrap());
6781        let stream_id = runtime.stream_pool().acquire().unwrap();
6782        let stream = runtime.stream_pool().resolve(stream_id).unwrap();
6783        let domain = provider
6784            .bind_resident_execution_domain(runtime, stream_id, stream)
6785            .unwrap();
6786        let mut graph = provider
6787            .allocate_semantic_hypergraph(
6788                &domain,
6789                SemanticHypergraphCapacities::try_new(2, 1, 1, 1).unwrap(),
6790            )
6791            .unwrap();
6792        // Establish a reachable cold boundary without iterating 2^64 retirements.
6793        let mut arena = download_test_arena(&provider, &graph);
6794        let candidate_offset = (CONTROL_WORDS + ROOT_WORDS * 2) as usize;
6795        arena[candidate_offset + 1] = u64::MAX - 1;
6796        provider
6797            .htod_sync_copy_into_tracked(&arena, &mut graph.arena)
6798            .unwrap();
6799        graph.fork.generation = u64::MAX - 1;
6800        let last = graph.fork(graph.empty_root()).unwrap();
6801        graph.discard(last).unwrap();
6802        let arena = download_test_arena(&provider, &graph);
6803        assert_eq!(arena[candidate_offset + 1], u64::MAX);
6804        let error = graph.fork(graph.empty_root()).unwrap_err();
6805        assert!(matches!(
6806            error,
6807            SemanticHypergraphError::GenerationExhausted {
6808                kind: SemanticHandleKind::Fork,
6809                slot: 0,
6810            }
6811        ));
6812        let after = download_test_arena(&provider, &graph);
6813        assert_eq!(
6814            after, arena,
6815            "an unretirable fork must not acquire or modify scratch"
6816        );
6817    }
6818
6819    #[test]
6820    fn mismatched_launch_abi_is_rejected_without_arena_mutation() {
6821        if std::env::var("XLOG_REQUIRE_CUDA").as_deref() != Ok("1") {
6822            eprintln!("Skipping: set XLOG_REQUIRE_CUDA=1 to run this real-CUDA contract");
6823            return;
6824        }
6825        let provider = CudaProviderBuilder::new(0, MemoryBudget::with_limit(64 * 1024 * 1024))
6826            .with_stream_capacity(1)
6827            .build()
6828            .unwrap();
6829        let provider = Arc::new(provider);
6830        let runtime = Arc::clone(provider.memory().runtime().unwrap());
6831        let stream_id = runtime.stream_pool().acquire().unwrap();
6832        let stream = runtime.stream_pool().resolve(stream_id).unwrap();
6833        let domain = provider
6834            .bind_resident_execution_domain(runtime, stream_id, Arc::clone(&stream))
6835            .unwrap();
6836        let graph = provider
6837            .allocate_semantic_hypergraph(
6838                &domain,
6839                SemanticHypergraphCapacities::try_new(2, 1, 1, 1).unwrap(),
6840            )
6841            .unwrap();
6842        let before = download_test_arena(&provider, &graph);
6843        let descriptor = DeviceLaunchDescriptor {
6844            expected_owner: graph.owner,
6845            root_capacity: 2,
6846            statement_capacity: 1,
6847            support_capacity: 1,
6848            version_capacity: 1,
6849            arena_words: graph.arena_words,
6850            command_ptr: graph.command.device_ptr_value(),
6851            command_index: 0,
6852            handle_ptr: 0,
6853            handle_index: 0,
6854            source_receipt_ptr: 0,
6855            source_receipt_index: 0,
6856            decoded_input_ptr: 0,
6857            decoded_input_index: 0,
6858            decoded_statement_ptr: 0,
6859            decoded_statement_index: 0,
6860            decoded_support_ptr: 0,
6861            decoded_support_index: 0,
6862            output_ptr: 0,
6863            output_index: 0,
6864            receipt_ptr: graph.receipt.device_ptr_value(),
6865            receipt_index: 0,
6866            admission: HOST_COMMAND_ADMISSION,
6867            abi_generation: HYPERGRAPH_ABI_GENERATION - 1,
6868        };
6869        let mut recorder = domain.new_strict_recorder();
6870        recorder.read_write(&graph.arena);
6871        recorder.read(&graph.command);
6872        recorder.write(&graph.receipt);
6873        let enqueued = unsafe {
6874            domain.enqueue(recorder, |stream| {
6875                graph.execute.clone().launch_in(
6876                    stream,
6877                    LaunchConfig {
6878                        grid_dim: (1, 1, 1),
6879                        block_dim: (1, 1, 1),
6880                        shared_mem_bytes: 0,
6881                    },
6882                    (graph.arena.device_ptr_value(), descriptor),
6883                )
6884            })
6885        }
6886        .unwrap();
6887        enqueued.commit().unwrap();
6888        stream.synchronize().unwrap();
6889        let receipt = provider
6890            .dtoh_small_metadata_untracked(&graph.receipt, 1)
6891            .unwrap();
6892        assert_eq!(receipt[0].words[0], STATUS_ARENA_MISMATCH);
6893        let after = download_test_arena(&provider, &graph);
6894        assert_eq!(after, before);
6895    }
6896
6897    #[test]
6898    fn resident_failed_seal_rolls_back_without_transferring_acquisition() {
6899        if std::env::var("XLOG_REQUIRE_CUDA").as_deref() != Ok("1") {
6900            eprintln!("Skipping: set XLOG_REQUIRE_CUDA=1 to run this real-CUDA contract");
6901            return;
6902        }
6903        let provider = CudaProviderBuilder::new(0, MemoryBudget::with_limit(64 * 1024 * 1024))
6904            .with_stream_capacity(1)
6905            .build()
6906            .unwrap();
6907        let provider = Arc::new(provider);
6908        let runtime = Arc::clone(provider.memory().runtime().unwrap());
6909        let stream_id = runtime.stream_pool().acquire().unwrap();
6910        let stream = runtime.stream_pool().resolve(stream_id).unwrap();
6911        let domain = provider
6912            .bind_resident_execution_domain(runtime, stream_id, Arc::clone(&stream))
6913            .unwrap();
6914        let mut graph = provider
6915            .allocate_semantic_hypergraph(
6916                &domain,
6917                SemanticHypergraphCapacities::try_new(1, 1, 1, 1).unwrap(),
6918            )
6919            .unwrap();
6920        let bytes = std::mem::size_of::<SemanticResidentDecodedStatement>()
6921            + std::mem::size_of::<SemanticResidentDecodedSupport>()
6922            + std::mem::size_of::<SemanticResidentHandleRecord>()
6923            + 9 * std::mem::size_of::<SemanticResidentReceiptRecord>();
6924        let mut reservation = provider.memory().reserve_bytes(bytes as u64).unwrap();
6925        let mut statements = reservation
6926            .alloc::<SemanticResidentDecodedStatement>(1)
6927            .unwrap();
6928        let mut supports = reservation
6929            .alloc::<SemanticResidentDecodedSupport>(1)
6930            .unwrap();
6931        let handles = reservation
6932            .alloc::<SemanticResidentHandleRecord>(1)
6933            .unwrap();
6934        let receipts = reservation
6935            .alloc::<SemanticResidentReceiptRecord>(9)
6936            .unwrap();
6937        assert_eq!(reservation.remaining_bytes(), 0);
6938        provider
6939            .htod_sync_copy_into_tracked(
6940                &[SemanticResidentDecodedStatement {
6941                    identity_words: [17; 8],
6942                    record: 0,
6943                    reconstruction: [0; 10],
6944                }],
6945                &mut statements,
6946            )
6947            .unwrap();
6948        provider
6949            .htod_sync_copy_into_tracked(
6950                &[SemanticResidentDecodedSupport {
6951                    polarity: 1,
6952                    provenance_words: [2; 8],
6953                    source_words: [3; 8],
6954                    context_words: [4; 8],
6955                    scope_words: [5; 8],
6956                    record: 0,
6957                }],
6958                &mut supports,
6959            )
6960            .unwrap();
6961        let mut setup = domain.new_strict_recorder();
6962        setup.read_write(&statements);
6963        setup.read_write(&supports);
6964        setup.write(&handles);
6965        setup.write(&receipts);
6966        unsafe { domain.enqueue(setup, |_| Ok::<(), XlogError>(())) }
6967            .unwrap()
6968            .commit()
6969            .unwrap();
6970        stream.synchronize().unwrap();
6971        let captured = CapturedCudaGraph::capture_on_stream(&stream, || {
6972            let slot =
6973                |index| SemanticResidentReceiptSlot::new(&receipts, index).map_err(kernel_error);
6974            let statement =
6975                || SemanticResidentStatementBank::new(&statements, 0).map_err(kernel_error);
6976            let base = graph
6977                .enqueue_resident_empty_root_handle(
6978                    SemanticResidentHandleSlot::new(&handles, 0).map_err(kernel_error)?,
6979                    slot(0)?,
6980                )
6981                .map_err(kernel_error)?;
6982            let acquired = graph
6983                .enqueue_resident_fork(base.handle(), slot(1)?)
6984                .map_err(kernel_error)?;
6985            let inserted = graph
6986                .enqueue_resident_insert_support(
6987                    acquired.candidate_handle(),
6988                    statement()?,
6989                    SemanticResidentSupportBank::new(&supports, 0).map_err(kernel_error)?,
6990                    slot(2)?,
6991                )
6992                .map_err(kernel_error)?;
6993            let refused_seal = graph
6994                .enqueue_resident_seal(inserted.candidate_handle(), slot(3)?)
6995                .map_err(kernel_error)?;
6996            let failed_fork = graph
6997                .enqueue_resident_fork(refused_seal.root_handle(), slot(4)?)
6998                .map_err(kernel_error)?;
6999            graph
7000                .enqueue_resident_seal(failed_fork.candidate_handle(), slot(5)?)
7001                .map_err(kernel_error)?;
7002            let next = graph
7003                .enqueue_resident_fork(base.handle(), slot(6)?)
7004                .map_err(kernel_error)?;
7005            graph
7006                .enqueue_resident_truth(
7007                    SemanticResidentView::Fork(next.candidate_handle()),
7008                    statement()?,
7009                    slot(7)?,
7010                )
7011                .map_err(kernel_error)?;
7012            graph
7013                .enqueue_resident_truth(
7014                    SemanticResidentView::Fork(acquired.candidate_handle()),
7015                    statement()?,
7016                    slot(8)?,
7017                )
7018                .map_err(kernel_error)?;
7019            Ok(())
7020        })
7021        .unwrap();
7022        captured.launch(&stream).unwrap();
7023        stream.synchronize().unwrap();
7024        let observed = provider
7025            .dtoh_small_metadata_untracked(&receipts, 9)
7026            .unwrap();
7027        assert_eq!(observed[3].words[0], STATUS_ROOT_CAPACITY);
7028        assert_eq!(observed[4].words[0], STATUS_ROOT_CAPACITY);
7029        assert_eq!(observed[5].words[0], STATUS_ROOT_CAPACITY);
7030        assert_eq!(
7031            observed[6].words[0], STATUS_OK,
7032            "a refused seal must roll back its candidate"
7033        );
7034        assert_eq!(observed[7].words[0], STATUS_OK);
7035        assert_eq!(
7036            observed[7].words[2], 0,
7037            "a refused lane must leave no partial semantic result"
7038        );
7039        assert_eq!(observed[8].words[0], STATUS_STALE_GENERATION);
7040        assert_eq!(&observed[4].words[40..42], &[0, 0]);
7041    }
7042
7043    #[test]
7044    fn resident_admission_is_capture_safe_and_zero_host_io() {
7045        if std::env::var("XLOG_REQUIRE_CUDA").as_deref() != Ok("1") {
7046            eprintln!("Skipping: set XLOG_REQUIRE_CUDA=1 to run this real-CUDA contract");
7047            return;
7048        }
7049
7050        let provider = CudaProviderBuilder::new(0, MemoryBudget::with_limit(64 * 1024 * 1024))
7051            .with_stream_capacity(1)
7052            .build()
7053            .expect("CUDA provider must be available when XLOG_REQUIRE_CUDA=1");
7054        let provider = Arc::new(provider);
7055        let runtime = Arc::clone(
7056            provider
7057                .memory()
7058                .runtime()
7059                .expect("canonical provider must own a runtime"),
7060        );
7061        let stream_id = runtime
7062            .stream_pool()
7063            .acquire()
7064            .expect("semantic hypergraph must acquire its resident stream");
7065        let stream = runtime
7066            .stream_pool()
7067            .resolve(stream_id)
7068            .expect("resident stream id must resolve through its runtime");
7069        let domain = provider
7070            .bind_resident_execution_domain(Arc::clone(&runtime), stream_id, Arc::clone(&stream))
7071            .expect("provider must bind its exact resident execution domain");
7072        let mut graph = provider
7073            .allocate_semantic_hypergraph(
7074                &domain,
7075                SemanticHypergraphCapacities::try_new(2, 1, 1, 1).unwrap(),
7076            )
7077            .expect("semantic storage initialization must execute on CUDA");
7078
7079        let resident_bytes = (std::mem::size_of::<SemanticResidentDecodedInput>()
7080            + std::mem::size_of::<SemanticResidentHandleRecord>()
7081            + std::mem::size_of::<SemanticResidentDecodedStatement>()
7082            + std::mem::size_of::<SemanticResidentDecodedSupport>()
7083            + 12 * std::mem::size_of::<SemanticResidentReceiptRecord>()
7084            + 3 * std::mem::size_of::<SemanticResidentTruthValue>())
7085            as u64;
7086        let mut reservation = provider
7087            .memory()
7088            .reserve_bytes(resident_bytes)
7089            .expect("resident decoded banks and receipts reservation must succeed");
7090        let mut decoded_inputs = reservation
7091            .alloc::<SemanticResidentDecodedInput>(1)
7092            .expect("decoder-owned input allocation must succeed");
7093        let empty_root_handles = reservation
7094            .alloc::<SemanticResidentHandleRecord>(1)
7095            .expect("resident empty-root handle allocation must succeed");
7096        let decoded_statements = reservation
7097            .alloc::<SemanticResidentDecodedStatement>(1)
7098            .expect("resident decoded statement allocation must succeed");
7099        let decoded_supports = reservation
7100            .alloc::<SemanticResidentDecodedSupport>(1)
7101            .expect("resident decoded support allocation must succeed");
7102        let receipts = reservation
7103            .alloc::<SemanticResidentReceiptRecord>(12)
7104            .expect("resident receipt bank allocation must succeed");
7105        let truth_values = reservation
7106            .alloc::<SemanticResidentTruthValue>(3)
7107            .expect("resident truth-value output allocation must succeed");
7108        assert_eq!(reservation.remaining_bytes(), 0);
7109
7110        let decoded_input = SemanticResidentDecodedInput {
7111            statement_identity_words: [
7112                0x1020_3040,
7113                0x5060_7080,
7114                0x90a0_b0c0,
7115                0xd0e0_f001,
7116                0x1234_5678,
7117                0x9abc_def0,
7118                0x0fed_cba9,
7119                0x8765_4321,
7120            ],
7121            polarity: 1,
7122            provenance_words: [1, 2, 3, 4, 5, 6, 7, 8],
7123            source_words: [11, 12, 13, 14, 15, 16, 17, 18],
7124            context_words: [21, 22, 23, 24, 25, 26, 27, 28],
7125            scope_words: [31, 32, 33, 34, 35, 36, 37, 38],
7126            statement_record: 0,
7127            support_record: 0,
7128            reconstruction: [0; 10],
7129        };
7130        provider
7131            .htod_sync_copy_into_tracked(&[decoded_input], &mut decoded_inputs)
7132            .expect("decoder-owned typed input upload must succeed before measurement");
7133
7134        // Move allocation ownership to the exact resident stream without
7135        // initializing the output payloads. The captured producer below is
7136        // the first writer of every statement/support output field.
7137        let mut setup_recorder = domain.new_strict_recorder();
7138        setup_recorder.read_write(&decoded_inputs);
7139        setup_recorder.write(&empty_root_handles);
7140        setup_recorder.write(&decoded_statements);
7141        setup_recorder.write(&decoded_supports);
7142        setup_recorder.write(&receipts);
7143        setup_recorder.write(&truth_values);
7144        let setup_enqueued = unsafe { domain.enqueue(setup_recorder, |_| Ok::<(), XlogError>(())) }
7145            .expect("test setup must adopt resident allocations on the selected stream");
7146        setup_enqueued
7147            .commit()
7148            .expect("test setup must publish resident bank dependencies");
7149        stream
7150            .synchronize()
7151            .expect("test setup allocation adoption must complete before capture");
7152
7153        let data_plane_before = provider.host_transfer_stats();
7154        let launch_metadata_before = provider.host_launch_metadata_transfer_stats();
7155        let tracked_dtoh_before = provider.d2h_transfer_count();
7156        let untracked_dtoh_before = provider.untracked_metadata_dtoh_count();
7157        let final_observation_before = provider.final_observation_transfer_stats();
7158        let semantic_launches_before = graph.execution_stats().cuda_kernel_launches();
7159
7160        let captured = CapturedCudaGraph::capture_on_stream(&stream, || {
7161            let input =
7162                SemanticResidentDecodedInputBank::new(&decoded_inputs, 0).map_err(kernel_error)?;
7163            let statement =
7164                SemanticResidentStatementBank::new(&decoded_statements, 0).map_err(kernel_error)?;
7165            let support =
7166                SemanticResidentSupportBank::new(&decoded_supports, 0).map_err(kernel_error)?;
7167            let materialize_slot =
7168                SemanticResidentReceiptSlot::new(&receipts, 0).map_err(kernel_error)?;
7169            graph
7170                .enqueue_resident_materialize_decoded(input, statement, support, materialize_slot)
7171                .map_err(kernel_error)?;
7172
7173            let handle_slot =
7174                SemanticResidentHandleSlot::new(&empty_root_handles, 0).map_err(kernel_error)?;
7175            let handle_receipt_slot =
7176                SemanticResidentReceiptSlot::new(&receipts, 1).map_err(kernel_error)?;
7177            let empty_root = graph
7178                .enqueue_resident_empty_root_handle(handle_slot, handle_receipt_slot)
7179                .map_err(kernel_error)?;
7180
7181            let statement =
7182                SemanticResidentStatementBank::new(&decoded_statements, 0).map_err(kernel_error)?;
7183            let truth_slot =
7184                SemanticResidentReceiptSlot::new(&receipts, 8).map_err(kernel_error)?;
7185            let base_truth = graph
7186                .enqueue_resident_truth(
7187                    SemanticResidentView::Root(empty_root.handle()),
7188                    statement,
7189                    truth_slot,
7190                )
7191                .map_err(kernel_error)?;
7192            let output = SemanticResidentTruthSlot::new(&truth_values, 1).map_err(kernel_error)?;
7193            let consumer = SemanticResidentReceiptSlot::new(&receipts, 9).map_err(kernel_error)?;
7194            graph
7195                .enqueue_resident_truth_consumer(base_truth.device_view(), output, consumer)
7196                .map_err(kernel_error)?;
7197
7198            let fork_slot = SemanticResidentReceiptSlot::new(&receipts, 2).map_err(kernel_error)?;
7199            let fork_receipt = graph
7200                .enqueue_resident_fork(empty_root.handle(), fork_slot)
7201                .map_err(kernel_error)?;
7202
7203            let statement =
7204                SemanticResidentStatementBank::new(&decoded_statements, 0).map_err(kernel_error)?;
7205            let support =
7206                SemanticResidentSupportBank::new(&decoded_supports, 0).map_err(kernel_error)?;
7207            let insert_slot =
7208                SemanticResidentReceiptSlot::new(&receipts, 3).map_err(kernel_error)?;
7209            let insert_receipt = graph
7210                .enqueue_resident_insert_support(
7211                    fork_receipt.candidate_handle(),
7212                    statement,
7213                    support,
7214                    insert_slot,
7215                )
7216                .map_err(kernel_error)?;
7217
7218            let seal_slot = SemanticResidentReceiptSlot::new(&receipts, 4).map_err(kernel_error)?;
7219            let sealed = graph
7220                .enqueue_resident_seal(insert_receipt.candidate_handle(), seal_slot)
7221                .map_err(kernel_error)?;
7222
7223            let statement =
7224                SemanticResidentStatementBank::new(&decoded_statements, 0).map_err(kernel_error)?;
7225            let truth_slot =
7226                SemanticResidentReceiptSlot::new(&receipts, 10).map_err(kernel_error)?;
7227            let sealed_truth = graph
7228                .enqueue_resident_truth(
7229                    SemanticResidentView::Root(sealed.root_handle()),
7230                    statement,
7231                    truth_slot,
7232                )
7233                .map_err(kernel_error)?;
7234            let output = SemanticResidentTruthSlot::new(&truth_values, 2).map_err(kernel_error)?;
7235            let consumer = SemanticResidentReceiptSlot::new(&receipts, 11).map_err(kernel_error)?;
7236            graph
7237                .enqueue_resident_truth_consumer(sealed_truth.device_view(), output, consumer)
7238                .map_err(kernel_error)?;
7239
7240            let refork_slot =
7241                SemanticResidentReceiptSlot::new(&receipts, 5).map_err(kernel_error)?;
7242            let refork = graph
7243                .enqueue_resident_fork(sealed.root_handle(), refork_slot)
7244                .map_err(kernel_error)?;
7245
7246            let truth_slot =
7247                SemanticResidentReceiptSlot::new(&receipts, 6).map_err(kernel_error)?;
7248            let statement =
7249                SemanticResidentStatementBank::new(&decoded_statements, 0).map_err(kernel_error)?;
7250            let truth_receipt = graph
7251                .enqueue_resident_truth(
7252                    SemanticResidentView::Fork(refork.candidate_handle()),
7253                    statement,
7254                    truth_slot,
7255                )
7256                .map_err(kernel_error)?;
7257
7258            let output = SemanticResidentTruthSlot::new(&truth_values, 0).map_err(kernel_error)?;
7259            let consumer_slot =
7260                SemanticResidentReceiptSlot::new(&receipts, 7).map_err(kernel_error)?;
7261            graph
7262                .enqueue_resident_truth_consumer(truth_receipt.device_view(), output, consumer_slot)
7263                .map_err(kernel_error)?;
7264            Ok(())
7265        })
7266        .expect("typed resident semantic chain must be CUDA-capture safe");
7267        assert_eq!(
7268            graph.execution_stats().cuda_kernel_launches(),
7269            semantic_launches_before + 12
7270        );
7271        captured
7272            .launch(&stream)
7273            .expect("captured resident semantic chain must launch");
7274
7275        let data_plane_after = provider.host_transfer_stats();
7276        assert_eq!(data_plane_after.dtoh_bytes, data_plane_before.dtoh_bytes);
7277        assert_eq!(data_plane_after.htod_bytes, data_plane_before.htod_bytes);
7278        assert_eq!(data_plane_after.dtoh_calls, data_plane_before.dtoh_calls);
7279        assert_eq!(data_plane_after.htod_calls, data_plane_before.htod_calls);
7280        let launch_metadata_after = provider.host_launch_metadata_transfer_stats();
7281        assert_eq!(
7282            launch_metadata_after.htod_bytes,
7283            launch_metadata_before.htod_bytes
7284        );
7285        assert_eq!(
7286            launch_metadata_after.htod_calls,
7287            launch_metadata_before.htod_calls
7288        );
7289        assert_eq!(provider.d2h_transfer_count(), tracked_dtoh_before);
7290        assert_eq!(
7291            provider.untracked_metadata_dtoh_count(),
7292            untracked_dtoh_before
7293        );
7294        let final_observation_after = provider.final_observation_transfer_stats();
7295        assert_eq!(
7296            final_observation_after.dtoh_bytes,
7297            final_observation_before.dtoh_bytes
7298        );
7299        assert_eq!(
7300            final_observation_after.dtoh_calls,
7301            final_observation_before.dtoh_calls
7302        );
7303        assert_eq!(
7304            final_observation_after.pinned_receipts,
7305            final_observation_before.pinned_receipts
7306        );
7307
7308        stream
7309            .synchronize()
7310            .expect("the test may observe only after the measured resident interval");
7311        let observed_truths = provider
7312            .dtoh_small_metadata_untracked(&truth_values, 3)
7313            .expect("the test may observe only the typed truth output after synchronization");
7314        for (truth_value, expected) in observed_truths.iter().zip([1, 0, 1]) {
7315            assert_eq!(truth_value.status, STATUS_OK);
7316            assert_eq!(truth_value.truth, expected);
7317            assert_eq!(truth_value.owner, graph.owner);
7318            assert_eq!(truth_value.reserved, 0);
7319        }
7320        let statement_identity = SemanticStatementIdentity(identity_bytes_from_dwords(
7321            decoded_input.statement_identity_words,
7322        ));
7323        let mut expected = Sha256::new();
7324        expected.update(b"xlog.semantic.support.v1\0");
7325        expected.update(statement_identity.as_bytes());
7326        expected.update(1u32.to_le_bytes()); // positive polarity
7327        expected.update(1u32.to_le_bytes()); // unit presence
7328        for words in [
7329            decoded_input.provenance_words,
7330            decoded_input.source_words,
7331            decoded_input.context_words,
7332            decoded_input.scope_words,
7333        ] {
7334            expected.update(identity_bytes_from_dwords(words));
7335        }
7336        let expected_support_identity = SemanticSupportIdentity(expected.finalize().into());
7337        assert_eq!(
7338            provider.untracked_metadata_dtoh_count(),
7339            untracked_dtoh_before + 1
7340        );
7341        let observed_receipts = provider
7342            .dtoh_small_metadata_untracked(&receipts, 4)
7343            .expect("the test may observe bounded receipts only after synchronization");
7344        let insert_receipt = &observed_receipts[3];
7345        assert_eq!(insert_receipt.words[0], STATUS_OK);
7346        assert_eq!(insert_receipt.words[1], OUTCOME_INSERTED);
7347        assert_eq!(
7348            receipt_identity(insert_receipt, 24),
7349            *expected_support_identity.as_bytes()
7350        );
7351        assert_eq!(
7352            provider.untracked_metadata_dtoh_count(),
7353            untracked_dtoh_before + 2
7354        );
7355
7356        let replay_data_plane_before = provider.host_transfer_stats();
7357        let replay_launch_metadata_before = provider.host_launch_metadata_transfer_stats();
7358        let replay_tracked_dtoh_before = provider.d2h_transfer_count();
7359        let replay_untracked_dtoh_before = provider.untracked_metadata_dtoh_count();
7360        let replay_final_observation_before = provider.final_observation_transfer_stats();
7361        captured
7362            .launch(&stream)
7363            .expect("captured resident semantic chain must be replayable");
7364        let replay_data_plane_after = provider.host_transfer_stats();
7365        assert_eq!(
7366            replay_data_plane_after.dtoh_bytes,
7367            replay_data_plane_before.dtoh_bytes
7368        );
7369        assert_eq!(
7370            replay_data_plane_after.htod_bytes,
7371            replay_data_plane_before.htod_bytes
7372        );
7373        assert_eq!(
7374            replay_data_plane_after.dtoh_calls,
7375            replay_data_plane_before.dtoh_calls
7376        );
7377        assert_eq!(
7378            replay_data_plane_after.htod_calls,
7379            replay_data_plane_before.htod_calls
7380        );
7381        let replay_launch_metadata_after = provider.host_launch_metadata_transfer_stats();
7382        assert_eq!(
7383            replay_launch_metadata_after.htod_bytes,
7384            replay_launch_metadata_before.htod_bytes
7385        );
7386        assert_eq!(
7387            replay_launch_metadata_after.htod_calls,
7388            replay_launch_metadata_before.htod_calls
7389        );
7390        assert_eq!(provider.d2h_transfer_count(), replay_tracked_dtoh_before);
7391        assert_eq!(
7392            provider.untracked_metadata_dtoh_count(),
7393            replay_untracked_dtoh_before
7394        );
7395        let replay_final_observation_after = provider.final_observation_transfer_stats();
7396        assert_eq!(
7397            replay_final_observation_after.dtoh_bytes,
7398            replay_final_observation_before.dtoh_bytes
7399        );
7400        assert_eq!(
7401            replay_final_observation_after.dtoh_calls,
7402            replay_final_observation_before.dtoh_calls
7403        );
7404        assert_eq!(
7405            replay_final_observation_after.pinned_receipts,
7406            replay_final_observation_before.pinned_receipts
7407        );
7408        stream
7409            .synchronize()
7410            .expect("replayed resident semantic chain must complete");
7411        let replayed_truths = provider
7412            .dtoh_small_metadata_untracked(&truth_values, 3)
7413            .expect("the test may observe the replayed typed output after synchronization");
7414        for (truth_value, status) in
7415            replayed_truths
7416                .iter()
7417                .zip([STATUS_INACTIVE, STATUS_OK, STATUS_INACTIVE])
7418        {
7419            assert_eq!(truth_value.status, status);
7420            assert_eq!(truth_value.truth, 0);
7421            assert_eq!(truth_value.owner, graph.owner);
7422            assert_eq!(truth_value.reserved, 0);
7423        }
7424        let replayed_receipts = provider
7425            .dtoh_small_metadata_untracked(&receipts, 12)
7426            .expect("the test may observe bounded replay receipts only after synchronization");
7427        assert_eq!(replayed_receipts[0].words[0], STATUS_OK);
7428        assert_eq!(replayed_receipts[1].words[0], STATUS_OK);
7429        assert_eq!(replayed_receipts[2].words[0], STATUS_INACTIVE);
7430        assert_eq!(
7431            decode_kind(replayed_receipts[2].words[33]),
7432            SemanticHandleKind::Fork
7433        );
7434        for propagated in replayed_receipts[3..8]
7435            .iter()
7436            .chain(&replayed_receipts[10..])
7437        {
7438            assert_eq!(propagated.words, replayed_receipts[2].words);
7439        }
7440        assert_eq!(replayed_receipts[8].words[0], STATUS_OK);
7441        assert_eq!(replayed_receipts[8].words[2], 0);
7442        assert_eq!(replayed_receipts[9].words[0], STATUS_OK);
7443        assert_eq!(
7444            provider.untracked_metadata_dtoh_count(),
7445            untracked_dtoh_before + 4
7446        );
7447
7448        let launches_before_host_rejection = graph.execution_stats().cuda_kernel_launches();
7449        let launch_metadata_before_host_rejection = provider.host_launch_metadata_transfer_stats();
7450        let empty_root = graph.empty_root();
7451        assert!(matches!(
7452            graph.snapshot(SemanticView::Root(empty_root)),
7453            Err(SemanticHypergraphError::DeviceControlled)
7454        ));
7455        assert_eq!(
7456            graph.execution_stats().cuda_kernel_launches(),
7457            launches_before_host_rejection
7458        );
7459        let launch_metadata_after_host_rejection = provider.host_launch_metadata_transfer_stats();
7460        assert_eq!(
7461            launch_metadata_after_host_rejection.htod_bytes,
7462            launch_metadata_before_host_rejection.htod_bytes
7463        );
7464        assert_eq!(
7465            launch_metadata_after_host_rejection.htod_calls,
7466            launch_metadata_before_host_rejection.htod_calls
7467        );
7468    }
7469}