Skip to main content

xlog_runtime/executor/
resident.rs

1use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
2use std::marker::PhantomData;
3use std::mem::ManuallyDrop;
4use std::ops::{Deref, DerefMut};
5use std::sync::Arc;
6use std::time::Instant;
7
8use cudarc::driver::{sys::CUevent_flags, CudaEvent};
9use xlog_core::{resolve_bool, symbol, RelId, Result, ScalarType, Schema, XlogError};
10use xlog_cuda::cuda_graph::{
11    reserve_uncaptured_stream, retire_after_stream_captures, CapturedCudaGraph,
12    ConditionalCudaGraphSequenceBuilder, CudaConditionalGraphUnavailable, CudaGraphNodeKind,
13};
14use xlog_cuda::device_runtime::{BlockId, ResidentCompletionEvent, XlogDeviceRuntime};
15use xlog_cuda::launch::LaunchRecorder;
16use xlog_cuda::memory::GpuMemoryReservation;
17use xlog_cuda::provider::resident_filter_project::{
18    resident_filter_scratch_device_bytes, ResidentFilterScratch,
19};
20use xlog_cuda::provider::resident_relational::{
21    resident_control_device_bytes, resident_device_trace_bytes,
22    resident_join_workspace_device_bytes, resident_packed_receipt_with_schema_winners_device_bytes,
23    resident_relation_device_bytes, resident_schema_winners_device_bytes,
24    resident_set_workspace_device_bytes, ResidentConvergenceControl, ResidentDeviceTrace,
25    ResidentJoinKind, ResidentJoinWorkspace, ResidentPackedReceipt, ResidentPinnedReceipt,
26    ResidentRelation, ResidentResourceCode, ResidentSchemaWinners, ResidentSetWorkspace,
27    ResidentTerminalCode, ResidentTerminalStatus,
28};
29use xlog_cuda::provider::resident_schedule::{
30    resident_schedule_metadata_device_bytes, ResidentExecutionDomain,
31    ResidentFilterComparisonDescriptor, ResidentOpDescriptor, ResidentProjectExpressionDescriptor,
32    ResidentRegionDescriptor, ResidentScheduleDeviceProgram, ResidentScheduleExternalBindings,
33    ResidentScheduleOpKind, ResidentScheduleSlotBinding, ResidentWaveDescriptor,
34    RESIDENT_SCHEDULE_OP_MARK_NOVELTY, RESIDENT_SCHEDULE_OP_MARK_SCHEMA_WINNER,
35    RESIDENT_SCHEDULE_REGION_FINALIZE, RESIDENT_SCHEDULE_REGION_INITIALIZE,
36    RESIDENT_SCHEDULE_REGION_RECURSIVE, RESIDENT_SCHEDULE_REGION_SCC_BEGIN,
37};
38use xlog_cuda::{
39    CompareOp as CudaCompareOp, CudaBuffer, CudaColumn, CudaKernelProvider, CudaStream,
40};
41use xlog_ir::{
42    CompareOp as RirCompareOp, CompiledRule, ConstValue, ExecutionPlan, Expr, JoinType,
43    ProjectExpr, RirNode,
44};
45
46use super::Executor;
47use crate::resident_graph::{
48    ResidentGraphCertifiedPlan, ResidentGraphDeclineReason, ResidentGraphDeviceStatus,
49    ResidentGraphExecutionError, ResidentGraphPrepareDiagnosticSnapshot,
50    ResidentGraphPrepareOptions, ResidentGraphRouteCertificate,
51};
52
53const MAX_RESIDENT_CAPACITY: u32 = 65_536;
54const RESIDENT_DYNAMIC_SCHEMA_ID: u32 = u32::MAX;
55
56fn resident_schemas_type_compatible(left: &Schema, right: &Schema) -> bool {
57    left.arity() == right.arity()
58        && (0..left.arity()).all(|column| left.column_type(column) == right.column_type(column))
59}
60
61fn resident_intern_schema(candidates: &mut Vec<Schema>, schema: Schema) -> Result<u32> {
62    let index = candidates
63        .iter()
64        .position(|candidate| candidate == &schema)
65        .unwrap_or_else(|| {
66            candidates.push(schema);
67            candidates.len() - 1
68        });
69    u32::try_from(index)
70        .map_err(|_| XlogError::Execution("resident schema candidate count exceeds u32".into()))
71}
72#[derive(Debug, Clone)]
73pub(super) struct ResidentWorkspaceAdmission {
74    pub(super) relation_capacity: u32,
75    pub(super) head_schemas: BTreeMap<String, Schema>,
76    head_schema_choices: BTreeMap<String, Vec<Schema>>,
77    rule_schema_ids: Vec<Vec<Option<u32>>>,
78    head_schema_selections: BTreeMap<String, ResidentHeadSchemaSelection>,
79}
80
81#[derive(Debug, Clone)]
82struct ResidentHeadSchemaSelection {
83    source_head: String,
84    output_schemas_by_source_winner: Vec<Schema>,
85}
86
87#[derive(Debug, Clone)]
88enum ResidentSchemaVariants {
89    Fixed(Schema),
90    Dynamic {
91        source_head: String,
92        schemas: Vec<Schema>,
93    },
94}
95
96fn resident_schema_variants_from_source(
97    source_head: String,
98    schemas: Vec<Schema>,
99) -> std::result::Result<ResidentSchemaVariants, ResidentGraphDeclineReason> {
100    let Some(first) = schemas.first() else {
101        return Err(resident_workspace_decline(format!(
102            "resident schema source {source_head} has no admitted candidate"
103        )));
104    };
105    if schemas.iter().all(|schema| schema == first) {
106        Ok(ResidentSchemaVariants::Fixed(first.clone()))
107    } else {
108        Ok(ResidentSchemaVariants::Dynamic {
109            source_head,
110            schemas,
111        })
112    }
113}
114
115fn resident_register_schema_selection(
116    target_head: &str,
117    selection: ResidentHeadSchemaSelection,
118    head_schema_choices: &BTreeMap<String, Vec<Schema>>,
119    head_schema_selections: &mut BTreeMap<String, ResidentHeadSchemaSelection>,
120) -> std::result::Result<(), ResidentGraphDeclineReason> {
121    if selection.source_head == target_head {
122        return Err(resident_workspace_decline(format!(
123            "resident schema lineage for {target_head} contains a cycle"
124        )));
125    }
126    let source_candidates = head_schema_choices
127        .get(&selection.source_head)
128        .filter(|candidates| !candidates.is_empty())
129        .ok_or_else(|| {
130            resident_workspace_decline(format!(
131                "resident schema source {} for {target_head} has no admitted candidate",
132                selection.source_head
133            ))
134        })?;
135    if source_candidates.len() != selection.output_schemas_by_source_winner.len() {
136        return Err(resident_workspace_decline(format!(
137            "resident schema source {} for {target_head} has {} candidates but {} mappings",
138            selection.source_head,
139            source_candidates.len(),
140            selection.output_schemas_by_source_winner.len()
141        )));
142    }
143
144    let mut source = selection.source_head.as_str();
145    let mut seen = HashSet::new();
146    while let Some(parent) = head_schema_selections.get(source) {
147        if source == target_head || !seen.insert(source) {
148            return Err(resident_workspace_decline(format!(
149                "resident schema lineage for {target_head} contains a cycle"
150            )));
151        }
152        source = parent.source_head.as_str();
153    }
154    if source == target_head {
155        return Err(resident_workspace_decline(format!(
156            "resident schema lineage for {target_head} contains a cycle"
157        )));
158    }
159
160    if let Some(existing) = head_schema_selections.get(target_head) {
161        if existing.source_head != selection.source_head
162            || existing.output_schemas_by_source_winner != selection.output_schemas_by_source_winner
163        {
164            return Err(resident_workspace_decline(format!(
165                "resident head {target_head} has multiple schema sources or mappings"
166            )));
167        }
168        return Ok(());
169    }
170    head_schema_selections.insert(target_head.to_string(), selection);
171    Ok(())
172}
173
174#[derive(Debug, Clone)]
175enum ResidentOutputSchemaSelection {
176    OwnWinner,
177    SourceWinner {
178        source_output: usize,
179        schemas: Vec<Schema>,
180    },
181}
182
183#[derive(Debug, Clone)]
184struct ResidentOutputSchemaPlan {
185    candidates: Vec<Schema>,
186    selection: ResidentOutputSchemaSelection,
187}
188
189fn resident_resolve_output_schemas(
190    plans: &[ResidentOutputSchemaPlan],
191    winner_ids: &[u32],
192) -> Result<Vec<Schema>> {
193    if plans.len() != winner_ids.len() {
194        return Err(XlogError::Execution(format!(
195            "resident schema plan count {} does not match winner count {}",
196            plans.len(),
197            winner_ids.len()
198        )));
199    }
200    (0..plans.len())
201        .map(|output| {
202            resident_resolve_output_schema(plans, winner_ids, output, &mut HashSet::new())
203        })
204        .collect()
205}
206
207fn resident_resolve_output_schema(
208    plans: &[ResidentOutputSchemaPlan],
209    winner_ids: &[u32],
210    output: usize,
211    resolving: &mut HashSet<usize>,
212) -> Result<Schema> {
213    if !resolving.insert(output) {
214        return Err(XlogError::Execution(
215            "resident schema source lineage contains a cycle".into(),
216        ));
217    }
218    let result = (|| {
219        let plan = plans.get(output).ok_or_else(|| {
220            XlogError::Execution(format!("resident schema output {output} is missing"))
221        })?;
222        let winner = *winner_ids.get(output).ok_or_else(|| {
223            XlogError::Execution(format!("resident schema winner {output} is missing"))
224        })?;
225        if winner != RESIDENT_DYNAMIC_SCHEMA_ID {
226            return plan
227                .candidates
228                .get(winner as usize)
229                .cloned()
230                .ok_or_else(|| {
231                    XlogError::Execution(format!(
232                        "resident schema winner {winner} exceeds {} candidates",
233                        plan.candidates.len()
234                    ))
235                });
236        }
237        let ResidentOutputSchemaSelection::SourceWinner {
238            source_output,
239            schemas,
240        } = &plan.selection
241        else {
242            return Err(XlogError::Execution(
243                "resident dynamic schema winner has no source lineage".into(),
244            ));
245        };
246        let source_schema =
247            resident_resolve_output_schema(plans, winner_ids, *source_output, resolving)?;
248        let source_plan = plans
249            .get(*source_output)
250            .ok_or_else(|| XlogError::Execution("resident schema source plan is missing".into()))?;
251        let source_winner = source_plan
252            .candidates
253            .iter()
254            .position(|candidate| candidate == &source_schema)
255            .ok_or_else(|| {
256                XlogError::Execution(
257                    "resident resolved source schema has no candidate index".into(),
258                )
259            })?;
260        schemas.get(source_winner).cloned().ok_or_else(|| {
261            XlogError::Execution(format!(
262                "resident schema source winner {source_winner} exceeds {} mappings",
263                schemas.len()
264            ))
265        })
266    })();
267    resolving.remove(&output);
268    result
269}
270
271struct ResidentRunOwners {
272    provider: Arc<CudaKernelProvider>,
273    runtime: Arc<XlogDeviceRuntime>,
274    stream: Arc<CudaStream>,
275    // Field drop order is intentional: destroy the graph before its compact
276    // program/domain and every externally owned device allocation.
277    graph: CapturedCudaGraph,
278    execution_domain: ResidentExecutionDomain,
279    _schedule_program: ResidentScheduleDeviceProgram,
280    recorder: LaunchRecorder,
281    relations: Vec<Option<ResidentRelation>>,
282    source_binding_names: Vec<String>,
283    output_indices: Vec<(String, usize)>,
284    _filter_scratch: Option<ResidentFilterScratch>,
285    _set_workspace: ResidentSetWorkspace,
286    _join_workspace: ResidentJoinWorkspace,
287    _control: ResidentConvergenceControl,
288    _device_trace: ResidentDeviceTrace,
289    _schema_winners: ResidentSchemaWinners,
290    receipt: ResidentPackedReceipt,
291    pinned_receipt: ResidentPinnedReceipt,
292    source_epoch: u64,
293    relation_registration: Vec<(RelId, String)>,
294    transaction_identity: Arc<()>,
295    output_schema_plans: Vec<ResidentOutputSchemaPlan>,
296}
297
298/// Holds actual foreign-resource owners across an operation that may enqueue.
299/// Allocation reservations remain governed by the existing runtime registry.
300/// This boundary retains the enclosing graph, host bank, streams and events too.
301struct PendingResidentOwners<T: Send + 'static> {
302    value: Option<T>,
303    wait: fn(&mut T) -> bool,
304}
305
306impl<T: Send + 'static> PendingResidentOwners<T> {
307    fn new(owners: T, wait: fn(&mut T) -> bool) -> Self {
308        Self {
309            value: Some(owners),
310            wait,
311        }
312    }
313
314    /// The caller has proved completion or transferred this pending owner.
315    fn into_inner(mut self) -> T {
316        self.value
317            .take()
318            .expect("resident execution owners missing")
319    }
320}
321
322impl<T: Send + 'static> PendingResidentOwners<T> {
323    fn with_admission<A, R, E>(
324        mut self,
325        admit: impl FnOnce(&T) -> std::result::Result<A, E>,
326        operation: impl FnOnce(&mut T) -> std::result::Result<R, E>,
327    ) -> std::result::Result<(Self, R), E> {
328        let admission = admit(&self)?;
329        // This local drops before the owning self parameter on error or unwind,
330        // so retirement can run immediately without a later lifecycle entry.
331        let result = operation(&mut self)?;
332        drop(admission);
333        Ok((self, result))
334    }
335}
336
337impl<T: Send + 'static> Deref for PendingResidentOwners<T> {
338    type Target = T;
339    fn deref(&self) -> &T {
340        self.value
341            .as_ref()
342            .expect("resident execution owners missing")
343    }
344}
345
346impl<T: Send + 'static> DerefMut for PendingResidentOwners<T> {
347    fn deref_mut(&mut self) -> &mut T {
348        self.value
349            .as_mut()
350            .expect("resident execution owners missing")
351    }
352}
353
354impl<T: Send + 'static> Drop for PendingResidentOwners<T> {
355    fn drop(&mut self) {
356        let Some(owners) = self.value.take() else {
357            return;
358        };
359        let wait = self.wait;
360        retire_after_stream_captures(move || {
361            // Preserve the full dependency chain even if waiting or reporting
362            // its failure unwinds. No registry lock is held during this wait.
363            let mut owners = ManuallyDrop::new(owners);
364            if !wait(&mut owners) {
365                eprintln!("CUDA resident completion remains unknown; retaining execution owners");
366                return;
367            }
368            // SAFETY: the exact terminal event or stream wait succeeded. This
369            // is the only release of the manually retained owner.
370            unsafe { ManuallyDrop::drop(&mut owners) };
371        });
372    }
373}
374
375#[derive(Clone, Debug, PartialEq, Eq)]
376struct ResidentSourceSetSnapshot {
377    name: String,
378    version: u64,
379    schema: Schema,
380    row_capacity: u64,
381    column_blocks: Vec<Option<BlockId>>,
382    row_count_block: BlockId,
383}
384
385#[derive(Clone)]
386enum ResidentBufferRef {
387    Source(String),
388    Private(usize),
389}
390
391enum ResidentRecordedOp {
392    Unit {
393        output: usize,
394        op_id: u32,
395    },
396    Scan {
397        relation: ResidentBufferRef,
398        op_id: u32,
399    },
400    TraceDelta {
401        scan_delta: u32,
402        filter_delta: u32,
403        semantic_guard: Option<ResidentBufferRef>,
404    },
405    #[cfg(test)]
406    Clear {
407        output: usize,
408    },
409    Filter {
410        input: ResidentBufferRef,
411        output: usize,
412        workspace: usize,
413        op_id: u32,
414    },
415    Project {
416        input: ResidentBufferRef,
417        output: usize,
418        workspace: usize,
419        op_id: u32,
420    },
421    Union {
422        left: ResidentBufferRef,
423        right: ResidentBufferRef,
424        output: usize,
425        op_id: u32,
426    },
427    Diff {
428        left: ResidentBufferRef,
429        right: ResidentBufferRef,
430        output: usize,
431        op_id: u32,
432    },
433    Join {
434        kind: ResidentJoinKind,
435        left: ResidentBufferRef,
436        left_key: usize,
437        right: ResidentBufferRef,
438        right_key: usize,
439        output: usize,
440        op_id: u32,
441    },
442    ChangedReset,
443    ChangedMark {
444        relation: usize,
445    },
446    TestStatus(ResidentTerminalStatus),
447    SchemaWinnerMark {
448        contribution: ResidentBufferRef,
449        head_index: u32,
450        schema_id: u32,
451    },
452}
453
454#[cfg(test)]
455fn resident_record_unit_leaf(
456    output: usize,
457    op_id: u32,
458    ops: &mut Vec<ResidentRecordedOp>,
459    push_physical: impl FnOnce(&mut Vec<ResidentRecordedOp>, ResidentRecordedOp, u32),
460) -> ResidentBufferRef {
461    push_physical(ops, ResidentRecordedOp::Unit { output, op_id }, op_id);
462    ResidentBufferRef::Private(output)
463}
464
465fn resident_new_phase_unit(
466    relations: &mut Vec<ResidentLogicalRelation>,
467    op_id: u32,
468) -> Result<(ResidentBufferRef, ResidentRecordedOp)> {
469    let output = relations.len();
470    relations.push(ResidentLogicalRelation {
471        schema: Schema::new(Vec::new()),
472        initial_count: 0,
473        permanent: false,
474    });
475    Ok((
476        ResidentBufferRef::Private(output),
477        ResidentRecordedOp::Unit { output, op_id },
478    ))
479}
480
481fn resident_record_scan_leaf(
482    relation: ResidentBufferRef,
483    op_id: u32,
484    semantic_guard: Option<ResidentBufferRef>,
485    ops: &mut Vec<ResidentRecordedOp>,
486    push_physical: impl FnOnce(&mut Vec<ResidentRecordedOp>, ResidentRecordedOp, u32),
487) -> ResidentBufferRef {
488    push_physical(
489        ops,
490        ResidentRecordedOp::Scan {
491            relation: relation.clone(),
492            op_id,
493        },
494        op_id,
495    );
496    ops.push(ResidentRecordedOp::TraceDelta {
497        scan_delta: 1,
498        filter_delta: 0,
499        semantic_guard,
500    });
501    relation
502}
503
504fn resident_semantic_trace_guard(
505    override_scan: Option<(RelId, usize, usize)>,
506) -> Option<ResidentBufferRef> {
507    override_scan.map(|(_, _, delta)| ResidentBufferRef::Private(delta))
508}
509
510fn resident_record_schema_winner_mark(
511    ops: &mut Vec<ResidentRecordedOp>,
512    contribution: ResidentBufferRef,
513    head_index: u32,
514    schema_id: u32,
515) {
516    ops.push(ResidentRecordedOp::SchemaWinnerMark {
517        contribution,
518        head_index,
519        schema_id,
520    });
521}
522
523#[derive(Debug, Clone, Copy, PartialEq, Eq)]
524enum ResidentUnionFoldMode {
525    SelfUnion,
526    LeftAssociated,
527}
528
529fn resident_union_fold_mode(input_count: usize) -> Result<ResidentUnionFoldMode> {
530    match input_count {
531        0 => Err(XlogError::Execution("resident union has no inputs".into())),
532        1 => Ok(ResidentUnionFoldMode::SelfUnion),
533        _ => Ok(ResidentUnionFoldMode::LeftAssociated),
534    }
535}
536
537enum ResidentPhaseMergeStep<T> {
538    Deduplicate(T),
539    Union(T, T),
540}
541
542fn resident_phase_merge<T, E>(
543    current: Option<T>,
544    contribution: T,
545    mut execute: impl FnMut(ResidentPhaseMergeStep<T>) -> std::result::Result<T, E>,
546) -> std::result::Result<T, E> {
547    let contribution = execute(ResidentPhaseMergeStep::Deduplicate(contribution))?;
548    match current {
549        Some(current) => execute(ResidentPhaseMergeStep::Union(current, contribution)),
550        None => Ok(contribution),
551    }
552}
553
554enum ResidentCapturePhase {
555    Segment {
556        ops: Vec<ResidentRecordedOp>,
557        scc_begin: Option<(u32, u32)>,
558    },
559    ConditionalWhile {
560        ops: Vec<ResidentRecordedOp>,
561        iteration_limit: u32,
562        convergence_op_id: u32,
563    },
564}
565
566struct ResidentCompactLogicalRegion {
567    ops: Vec<ResidentRecordedOp>,
568    iteration_limit: u32,
569    op_id: u32,
570    flags: u32,
571}
572
573struct ResidentCompactSchedulePlan {
574    source_slots: BTreeMap<String, u32>,
575    ops: Vec<ResidentOpDescriptor>,
576    waves: Vec<ResidentWaveDescriptor>,
577    regions: Vec<ResidentRegionDescriptor>,
578    generation_bases: Vec<u32>,
579    filter_comparisons: Vec<ResidentFilterComparisonDescriptor>,
580    project_expressions: Vec<ResidentProjectExpressionDescriptor>,
581}
582
583#[derive(Debug, Clone, Copy, PartialEq, Eq)]
584enum ResidentCaptureParentKind {
585    Kernel,
586    Conditional,
587}
588
589struct ResidentCompactTopology {
590    parent_kinds: Vec<ResidentCaptureParentKind>,
591    conditional_body_kernel_counts: Vec<usize>,
592    hierarchical_node_count: usize,
593}
594
595trait ResidentRegionFlags {
596    fn region_flags(&self) -> u32;
597}
598
599impl ResidentRegionFlags for ResidentCompactLogicalRegion {
600    fn region_flags(&self) -> u32 {
601        self.flags
602    }
603}
604
605impl ResidentRegionFlags for ResidentRegionDescriptor {
606    fn region_flags(&self) -> u32 {
607        self.flags
608    }
609}
610
611fn resident_compact_topology<R: ResidentRegionFlags>(
612    regions: &[R],
613) -> Result<ResidentCompactTopology> {
614    let parent_kinds = regions
615        .iter()
616        .map(|region| {
617            if region.region_flags() == RESIDENT_SCHEDULE_REGION_RECURSIVE {
618                ResidentCaptureParentKind::Conditional
619            } else {
620                ResidentCaptureParentKind::Kernel
621            }
622        })
623        .collect::<Vec<_>>();
624    let conditional_count = parent_kinds
625        .iter()
626        .filter(|kind| **kind == ResidentCaptureParentKind::Conditional)
627        .count();
628    let hierarchical_node_count = parent_kinds
629        .len()
630        .checked_add(conditional_count)
631        .ok_or_else(|| XlogError::Execution("resident topology node count overflow".into()))?;
632    Ok(ResidentCompactTopology {
633        parent_kinds,
634        conditional_body_kernel_counts: vec![1; conditional_count],
635        hierarchical_node_count,
636    })
637}
638
639fn resident_validate_parent_graph_kinds(
640    actual: &[CudaGraphNodeKind],
641    expected: &[ResidentCaptureParentKind],
642) -> Result<()> {
643    if actual.len() != expected.len()
644        || actual.iter().zip(expected).any(|(actual, expected)| {
645            !matches!(
646                (actual, expected),
647                (CudaGraphNodeKind::Kernel, ResidentCaptureParentKind::Kernel)
648                    | (
649                        CudaGraphNodeKind::Conditional,
650                        ResidentCaptureParentKind::Conditional
651                    )
652            )
653        })
654    {
655        return Err(XlogError::Execution(
656            "resident parent graph topology differs from the compact schedule".into(),
657        ));
658    }
659    Ok(())
660}
661
662fn resident_validate_conditional_body_node_kinds(
663    actual: &[Vec<CudaGraphNodeKind>],
664    expected_conditional_count: usize,
665) -> Result<Vec<usize>> {
666    if actual.len() != expected_conditional_count {
667        return Err(XlogError::Execution(format!(
668            "resident conditional body inventory has {} bodies, expected {expected_conditional_count}",
669            actual.len()
670        )));
671    }
672    let mut kernel_counts = Vec::with_capacity(actual.len());
673    for (index, kinds) in actual.iter().enumerate() {
674        if kinds.as_slice() != [CudaGraphNodeKind::Kernel] {
675            return Err(XlogError::Execution(format!(
676                "resident conditional body {index} must contain exactly one kernel node, found {kinds:?}"
677            )));
678        }
679        kernel_counts.push(1);
680    }
681    Ok(kernel_counts)
682}
683
684#[derive(Default)]
685struct ResidentCompactDescriptorTables {
686    filter_comparisons: Vec<ResidentFilterComparisonDescriptor>,
687    filter_ranges: Vec<(u32, u32)>,
688    project_expressions: Vec<ResidentProjectExpressionDescriptor>,
689    project_ranges: Vec<(u32, u32)>,
690}
691
692#[expect(
693    clippy::too_many_arguments,
694    reason = "schedule sizing accounts for each independently bounded descriptor table before any device allocation"
695)]
696fn resident_compact_schedule_metadata_bytes(
697    slot_count: usize,
698    op_count: usize,
699    wave_count: usize,
700    region_count: usize,
701    generation_base_count: usize,
702    head_count: usize,
703    filter_comparison_count: usize,
704    project_expression_count: usize,
705) -> Result<u64> {
706    let generation_metadata_count =
707        generation_base_count
708            .checked_add(head_count)
709            .ok_or_else(|| {
710                XlogError::Execution("resident generation metadata count overflow".into())
711            })?;
712    resident_schedule_metadata_device_bytes(
713        slot_count,
714        op_count,
715        wave_count,
716        region_count,
717        generation_metadata_count,
718        filter_comparison_count,
719        project_expression_count,
720    )
721}
722
723fn resident_compact_schema_defaults(
724    ops: &[ResidentOpDescriptor],
725    head_count: usize,
726) -> Result<Vec<u32>> {
727    let mut defaults = vec![None; head_count];
728    for op in ops {
729        if op.flags & RESIDENT_SCHEDULE_OP_MARK_SCHEMA_WINNER == 0 {
730            continue;
731        }
732        let head = usize::try_from(op.schema_winner_head).map_err(|_| {
733            XlogError::Execution("resident schema winner head index overflow".into())
734        })?;
735        let default = defaults.get_mut(head).ok_or_else(|| {
736            XlogError::Execution("resident schema winner head is out of range".into())
737        })?;
738        if default.is_none() {
739            *default = Some(op.schema_winner_id);
740        }
741    }
742    defaults
743        .into_iter()
744        .enumerate()
745        .map(|(head, default)| {
746            default.ok_or_else(|| {
747                XlogError::Execution(format!(
748                    "resident head {head} has no schema winner candidate"
749                ))
750            })
751        })
752        .collect()
753}
754
755fn resident_compact_allocation_bytes(
756    relation_bytes: u64,
757    filter_scratch_bytes: u64,
758    fixed_workspace_bytes: u64,
759    private_slot_count: usize,
760    head_count: usize,
761    plan: &ResidentCompactSchedulePlan,
762) -> Result<(u64, u64)> {
763    let slot_count = private_slot_count
764        .checked_add(plan.source_slots.len())
765        .ok_or_else(|| XlogError::Execution("resident compact slot count overflow".into()))?;
766    let metadata_bytes = resident_compact_schedule_metadata_bytes(
767        slot_count,
768        plan.ops.len(),
769        plan.waves.len(),
770        plan.regions.len(),
771        plan.generation_bases.len(),
772        head_count,
773        plan.filter_comparisons.len(),
774        plan.project_expressions.len(),
775    )?;
776    let required_bytes = relation_bytes
777        .checked_add(filter_scratch_bytes)
778        .and_then(|bytes| bytes.checked_add(fixed_workspace_bytes))
779        .and_then(|bytes| bytes.checked_add(metadata_bytes))
780        .ok_or_else(|| XlogError::Execution("resident compact manifest overflow".into()))?;
781    Ok((required_bytes, metadata_bytes))
782}
783
784fn resident_compact_preflight_device_bytes(
785    manifest: &ResidentAllocationManifest,
786    plan: &ResidentCompactSchedulePlan,
787) -> Result<(u64, u64, u64)> {
788    let filter_count = u64::try_from(plan.filter_comparisons.len().max(1)).map_err(|_| {
789        XlogError::Execution("resident compact filter descriptor count exceeds u64".into())
790    })?;
791    let project_count = u64::try_from(plan.project_expressions.len().max(1)).map_err(|_| {
792        XlogError::Execution("resident compact project descriptor count exceeds u64".into())
793    })?;
794    let filter_bytes = u64::try_from(std::mem::size_of::<ResidentFilterComparisonDescriptor>())
795        .ok()
796        .and_then(|size| size.checked_mul(filter_count))
797        .ok_or_else(|| {
798            XlogError::Execution("resident compact filter descriptor bytes overflow".into())
799        })?;
800    let project_bytes = u64::try_from(std::mem::size_of::<ResidentProjectExpressionDescriptor>())
801        .ok()
802        .and_then(|size| size.checked_mul(project_count))
803        .ok_or_else(|| {
804            XlogError::Execution("resident compact project descriptor bytes overflow".into())
805        })?;
806    let descriptor_bytes = filter_bytes
807        .checked_add(project_bytes)
808        .ok_or_else(|| XlogError::Execution("resident compact descriptor bytes overflow".into()))?;
809    let remaining_metadata_bytes = manifest
810        .schedule_metadata_bytes
811        .checked_sub(descriptor_bytes)
812        .ok_or_else(|| {
813            XlogError::Execution(
814                "resident compact descriptor bytes exceed schedule metadata".into(),
815            )
816        })?;
817    let fixed_bytes = manifest
818        .fixed_workspace_bytes
819        .checked_add(remaining_metadata_bytes)
820        .ok_or_else(|| XlogError::Execution("resident compact fixed bytes overflow".into()))?;
821    let reported_total = manifest
822        .relation_bytes
823        .checked_add(manifest.filter_scratch_bytes)
824        .and_then(|bytes| bytes.checked_add(filter_bytes))
825        .and_then(|bytes| bytes.checked_add(project_bytes))
826        .and_then(|bytes| bytes.checked_add(fixed_bytes))
827        .ok_or_else(|| XlogError::Execution("resident compact preflight bytes overflow".into()))?;
828    if reported_total != manifest.required_bytes {
829        return Err(XlogError::Execution(format!(
830            "resident compact preflight component mismatch: reported {reported_total}, required {}",
831            manifest.required_bytes
832        )));
833    }
834    Ok((filter_bytes, project_bytes, fixed_bytes))
835}
836
837fn resident_compact_tables(
838    filter_workspaces: &[ResidentFilterPlan],
839    project_workspaces: &[ResidentProjectPlan],
840) -> Result<ResidentCompactDescriptorTables> {
841    let mut tables = ResidentCompactDescriptorTables::default();
842    for workspace in filter_workspaces {
843        let offset = u32::try_from(tables.filter_comparisons.len()).map_err(|_| {
844            XlogError::Execution("resident filter comparison offset exceeds u32".into())
845        })?;
846        let count = u32::try_from(workspace.compact_comparisons.len()).map_err(|_| {
847            XlogError::Execution("resident filter comparison count exceeds u32".into())
848        })?;
849        tables
850            .filter_comparisons
851            .extend_from_slice(&workspace.compact_comparisons);
852        tables.filter_ranges.push((offset, count));
853    }
854    for workspace in project_workspaces {
855        let offset = u32::try_from(tables.project_expressions.len()).map_err(|_| {
856            XlogError::Execution("resident project expression offset exceeds u32".into())
857        })?;
858        let count = u32::try_from(workspace.compact_expressions.len()).map_err(|_| {
859            XlogError::Execution("resident project expression count exceeds u32".into())
860        })?;
861        tables
862            .project_expressions
863            .extend_from_slice(&workspace.compact_expressions);
864        tables.project_ranges.push((offset, count));
865    }
866    Ok(tables)
867}
868
869#[derive(Debug, Clone, Copy, PartialEq, Eq)]
870struct ResidentCompactSlotRef {
871    slot: u32,
872    generation: u32,
873}
874
875fn resident_compact_slot_ref(
876    reference: &ResidentBufferRef,
877    assignments: &[ResidentSlotAssignment],
878    source_slots: &BTreeMap<String, u32>,
879) -> Result<ResidentCompactSlotRef> {
880    match reference {
881        ResidentBufferRef::Private(logical) => {
882            let assignment = assignments.get(*logical).ok_or_else(|| {
883                XlogError::Execution(format!(
884                    "resident logical relation {logical} has no compact slot assignment"
885                ))
886            })?;
887            Ok(ResidentCompactSlotRef {
888                slot: u32::try_from(assignment.slot).map_err(|_| {
889                    XlogError::Execution("resident compact slot index exceeds u32".into())
890                })?,
891                generation: assignment.generation,
892            })
893        }
894        ResidentBufferRef::Source(name) => Ok(ResidentCompactSlotRef {
895            slot: *source_slots.get(name).ok_or_else(|| {
896                XlogError::Execution(format!(
897                    "resident source {name} has no compact slot assignment"
898                ))
899            })?,
900            generation: 0,
901        }),
902    }
903}
904
905fn resident_lower_compact_regions<'a>(
906    logical_regions: Vec<ResidentCompactLogicalRegion>,
907    physical_slots: &[ResidentPhysicalSlotPlan],
908    assignments: &[ResidentSlotAssignment],
909    source_names: impl IntoIterator<Item = &'a str>,
910    tables: ResidentCompactDescriptorTables,
911) -> Result<ResidentCompactSchedulePlan> {
912    let source_slots = resident_source_slot_map(physical_slots.len(), source_names)?;
913    let slot_count = physical_slots
914        .len()
915        .checked_add(source_slots.len())
916        .ok_or_else(|| XlogError::Execution("resident compact slot count overflow".into()))?;
917    let slot_count_u32 = u32::try_from(slot_count)
918        .map_err(|_| XlogError::Execution("resident compact slot count exceeds u32".into()))?;
919    let mut ops = Vec::new();
920    let mut waves = Vec::new();
921    let mut regions = Vec::with_capacity(logical_regions.len());
922    let filter_total = u32::try_from(tables.filter_comparisons.len())
923        .map_err(|_| XlogError::Execution("resident filter comparison count exceeds u32".into()))?;
924    let project_total = u32::try_from(tables.project_expressions.len()).map_err(|_| {
925        XlogError::Execution("resident project expression count exceeds u32".into())
926    })?;
927    let mut filter_cursor = 0_u32;
928    let mut project_cursor = 0_u32;
929    let mut generation_bases = Vec::with_capacity(
930        logical_regions
931            .len()
932            .checked_mul(slot_count)
933            .ok_or_else(|| {
934                XlogError::Execution("resident generation baseline count overflow".into())
935            })?,
936    );
937
938    for logical_region in logical_regions {
939        let region_flags = logical_region.flags;
940        let first_wave = u32::try_from(waves.len())
941            .map_err(|_| XlogError::Execution("resident wave count exceeds u32".into()))?;
942        let first_op = u32::try_from(ops.len())
943            .map_err(|_| XlogError::Execution("resident compact op count exceeds u32".into()))?;
944        let generation_offset = u32::try_from(generation_bases.len()).map_err(|_| {
945            XlogError::Execution("resident generation baseline offset exceeds u32".into())
946        })?;
947        let mut first_generations = vec![None; slot_count];
948        let mut last_relation_descriptor = None::<(usize, ResidentCompactSlotRef)>;
949
950        for (logical_op_index, logical_op) in logical_region.ops.into_iter().enumerate() {
951            let mut emit = |descriptor: ResidentOpDescriptor,
952                            output: ResidentCompactSlotRef,
953                            references: Vec<ResidentCompactSlotRef>|
954             -> Result<()> {
955                for reference in &references {
956                    let generation = first_generations
957                        .get_mut(reference.slot as usize)
958                        .ok_or_else(|| {
959                            XlogError::Execution(
960                                "resident compact operation slot is out of range".into(),
961                            )
962                        })?;
963                    if generation.is_none() {
964                        *generation = Some(reference.generation);
965                    }
966                }
967                let op_index = ops.len();
968                ops.push(descriptor);
969                last_relation_descriptor = Some((op_index, output));
970                Ok(())
971            };
972
973            match logical_op {
974                ResidentRecordedOp::Unit { output, op_id } => {
975                    let output = resident_compact_slot_ref(
976                        &ResidentBufferRef::Private(output),
977                        assignments,
978                        &source_slots,
979                    )?;
980                    emit(
981                        ResidentOpDescriptor::unit(op_id, output.slot, output.generation),
982                        output,
983                        vec![output],
984                    )?;
985                }
986                ResidentRecordedOp::Scan { relation, op_id } => {
987                    let source = resident_compact_slot_ref(&relation, assignments, &source_slots)?;
988                    emit(
989                        ResidentOpDescriptor::scan(op_id, source.slot, source.generation),
990                        source,
991                        vec![source],
992                    )?;
993                }
994                op @ (ResidentRecordedOp::Filter { .. } | ResidentRecordedOp::Project { .. }) => {
995                    let (is_filter, input, output, workspace, op_id) = match op {
996                        ResidentRecordedOp::Filter {
997                            input,
998                            output,
999                            workspace,
1000                            op_id,
1001                        } => (true, input, output, workspace, op_id),
1002                        ResidentRecordedOp::Project {
1003                            input,
1004                            output,
1005                            workspace,
1006                            op_id,
1007                        } => (false, input, output, workspace, op_id),
1008                        _ => unreachable!("compact filter/project arm"),
1009                    };
1010                    let input = resident_compact_slot_ref(&input, assignments, &source_slots)?;
1011                    let output = resident_compact_slot_ref(
1012                        &ResidentBufferRef::Private(output),
1013                        assignments,
1014                        &source_slots,
1015                    )?;
1016                    let (aux_offset, aux_count) = if is_filter {
1017                        *tables.filter_ranges.get(workspace).ok_or_else(|| {
1018                            XlogError::Execution(
1019                                "resident compact filter workspace is out of range".into(),
1020                            )
1021                        })?
1022                    } else {
1023                        *tables.project_ranges.get(workspace).ok_or_else(|| {
1024                            XlogError::Execution(
1025                                "resident compact project workspace is out of range".into(),
1026                            )
1027                        })?
1028                    };
1029                    let (cursor, total, table_name) = if is_filter {
1030                        (&mut filter_cursor, filter_total, "filter")
1031                    } else {
1032                        (&mut project_cursor, project_total, "project")
1033                    };
1034                    if aux_offset != *cursor || aux_offset > total || aux_count > total - aux_offset
1035                    {
1036                        return Err(XlogError::Execution(format!(
1037                            "resident compact {table_name} descriptor range is not contiguous"
1038                        )));
1039                    }
1040                    *cursor = aux_offset + aux_count;
1041                    emit(
1042                        ResidentOpDescriptor {
1043                            kind: if is_filter {
1044                                ResidentScheduleOpKind::Filter
1045                            } else {
1046                                ResidentScheduleOpKind::Project
1047                            },
1048                            op_id,
1049                            out: output.slot,
1050                            in0: input.slot,
1051                            in0_generation: input.generation,
1052                            out_generation: output.generation,
1053                            aux_offset,
1054                            aux_count,
1055                            ..Default::default()
1056                        },
1057                        output,
1058                        vec![input, output],
1059                    )?;
1060                }
1061                op @ (ResidentRecordedOp::Union { .. } | ResidentRecordedOp::Diff { .. }) => {
1062                    let (is_union, left, right, output, op_id) = match op {
1063                        ResidentRecordedOp::Union {
1064                            left,
1065                            right,
1066                            output,
1067                            op_id,
1068                        } => (true, left, right, output, op_id),
1069                        ResidentRecordedOp::Diff {
1070                            left,
1071                            right,
1072                            output,
1073                            op_id,
1074                        } => (false, left, right, output, op_id),
1075                        _ => unreachable!("compact set arm"),
1076                    };
1077                    let left = resident_compact_slot_ref(&left, assignments, &source_slots)?;
1078                    let right = resident_compact_slot_ref(&right, assignments, &source_slots)?;
1079                    let output = resident_compact_slot_ref(
1080                        &ResidentBufferRef::Private(output),
1081                        assignments,
1082                        &source_slots,
1083                    )?;
1084                    emit(
1085                        ResidentOpDescriptor {
1086                            kind: if is_union {
1087                                ResidentScheduleOpKind::Union
1088                            } else {
1089                                ResidentScheduleOpKind::Diff
1090                            },
1091                            op_id,
1092                            out: output.slot,
1093                            in0: left.slot,
1094                            in1: right.slot,
1095                            in0_generation: left.generation,
1096                            in1_generation: right.generation,
1097                            out_generation: output.generation,
1098                            ..Default::default()
1099                        },
1100                        output,
1101                        vec![left, right, output],
1102                    )?;
1103                }
1104                ResidentRecordedOp::Join {
1105                    kind,
1106                    left,
1107                    left_key,
1108                    right,
1109                    right_key,
1110                    output,
1111                    op_id,
1112                } => {
1113                    let left = resident_compact_slot_ref(&left, assignments, &source_slots)?;
1114                    let right = resident_compact_slot_ref(&right, assignments, &source_slots)?;
1115                    let output = resident_compact_slot_ref(
1116                        &ResidentBufferRef::Private(output),
1117                        assignments,
1118                        &source_slots,
1119                    )?;
1120                    emit(
1121                        ResidentOpDescriptor {
1122                            kind: match kind {
1123                                ResidentJoinKind::Inner => ResidentScheduleOpKind::JoinInner,
1124                                ResidentJoinKind::Semi => ResidentScheduleOpKind::JoinSemi,
1125                            },
1126                            op_id,
1127                            out: output.slot,
1128                            in0: left.slot,
1129                            in1: right.slot,
1130                            in0_generation: left.generation,
1131                            in1_generation: right.generation,
1132                            out_generation: output.generation,
1133                            left_key: u32::try_from(left_key).map_err(|_| {
1134                                XlogError::Execution(
1135                                    "resident compact left join key exceeds u32".into(),
1136                                )
1137                            })?,
1138                            right_key: u32::try_from(right_key).map_err(|_| {
1139                                XlogError::Execution(
1140                                    "resident compact right join key exceeds u32".into(),
1141                                )
1142                            })?,
1143                            ..Default::default()
1144                        },
1145                        output,
1146                        vec![left, right, output],
1147                    )?;
1148                }
1149                ResidentRecordedOp::TraceDelta {
1150                    scan_delta,
1151                    filter_delta,
1152                    semantic_guard,
1153                } => {
1154                    let semantic_guard = semantic_guard
1155                        .as_ref()
1156                        .map(|reference| {
1157                            resident_compact_slot_ref(reference, assignments, &source_slots)
1158                        })
1159                        .transpose()?;
1160                    if let Some(reference) = semantic_guard {
1161                        let generation = first_generations
1162                            .get_mut(reference.slot as usize)
1163                            .ok_or_else(|| {
1164                                XlogError::Execution(
1165                                    "resident compact trace guard slot is out of range".into(),
1166                                )
1167                            })?;
1168                        if generation.is_none() {
1169                            *generation = Some(reference.generation);
1170                        }
1171                    }
1172                    ops.push(ResidentOpDescriptor::trace_delta(
1173                        scan_delta,
1174                        filter_delta,
1175                        semantic_guard.map(|reference| (reference.slot, reference.generation)),
1176                    ));
1177                }
1178                ResidentRecordedOp::TestStatus(status) => {
1179                    ops.push(ResidentOpDescriptor::test_status(status)?);
1180                }
1181                ResidentRecordedOp::SchemaWinnerMark {
1182                    contribution,
1183                    head_index,
1184                    schema_id,
1185                } => {
1186                    let contribution =
1187                        resident_compact_slot_ref(&contribution, assignments, &source_slots)?;
1188                    let (index, output) = last_relation_descriptor.as_ref().ok_or_else(|| {
1189                        XlogError::Execution(
1190                            "resident schema marker has no contribution operation".into(),
1191                        )
1192                    })?;
1193                    if *output != contribution {
1194                        return Err(XlogError::Execution(
1195                            "resident schema marker does not match its contribution operation"
1196                                .into(),
1197                        ));
1198                    }
1199                    ops[*index] = ops[*index].with_schema_winner(head_index, schema_id);
1200                }
1201                ResidentRecordedOp::ChangedMark { relation } => {
1202                    let relation = resident_compact_slot_ref(
1203                        &ResidentBufferRef::Private(relation),
1204                        assignments,
1205                        &source_slots,
1206                    )?;
1207                    let (index, output) = last_relation_descriptor.as_ref().ok_or_else(|| {
1208                        XlogError::Execution(
1209                            "resident novelty marker has no completed delta copy".into(),
1210                        )
1211                    })?;
1212                    if *output != relation || ops[*index].kind != ResidentScheduleOpKind::Project {
1213                        return Err(XlogError::Execution(
1214                            "resident novelty marker does not follow its completed delta copy"
1215                                .into(),
1216                        ));
1217                    }
1218                    ops[*index].flags |= RESIDENT_SCHEDULE_OP_MARK_NOVELTY;
1219                }
1220                ResidentRecordedOp::ChangedReset => {
1221                    if region_flags != RESIDENT_SCHEDULE_REGION_RECURSIVE || logical_op_index != 0 {
1222                        return Err(XlogError::Execution(
1223                            "resident changed reset is not the recursive region entry".into(),
1224                        ));
1225                    }
1226                }
1227                #[cfg(test)]
1228                ResidentRecordedOp::Clear { .. } => {
1229                    return Err(XlogError::Execution(
1230                        "resident compact SSA still contains a Clear operation".into(),
1231                    ));
1232                }
1233            }
1234        }
1235
1236        let last_op = u32::try_from(ops.len())
1237            .map_err(|_| XlogError::Execution("resident compact op count exceeds u32".into()))?;
1238        if last_op != first_op {
1239            // The device executes operations serially within a wave; extra waves add grid barriers.
1240            waves.push(ResidentWaveDescriptor {
1241                first_op,
1242                op_count: last_op - first_op,
1243                flags: 0,
1244                reserved: 0,
1245            });
1246        }
1247
1248        for (slot, generation) in first_generations.into_iter().enumerate() {
1249            let baseline = if slot < physical_slots.len() && physical_slots[slot].permanent {
1250                0
1251            } else {
1252                generation.unwrap_or(0)
1253            };
1254            generation_bases.push(baseline);
1255        }
1256        let wave_count = u32::try_from(waves.len())
1257            .map_err(|_| XlogError::Execution("resident wave count exceeds u32".into()))?
1258            .checked_sub(first_wave)
1259            .ok_or_else(|| XlogError::Execution("resident wave range underflow".into()))?;
1260        regions.push(ResidentRegionDescriptor {
1261            first_wave,
1262            wave_count,
1263            iteration_limit: logical_region.iteration_limit,
1264            op_id: logical_region.op_id,
1265            flags: region_flags,
1266            first_slot: 0,
1267            slot_count: slot_count_u32,
1268            generation_offset,
1269        });
1270    }
1271
1272    if filter_cursor != filter_total || project_cursor != project_total {
1273        return Err(XlogError::Execution(
1274            "resident compact descriptor tables are not exactly covered".into(),
1275        ));
1276    }
1277
1278    Ok(ResidentCompactSchedulePlan {
1279        source_slots,
1280        ops,
1281        waves,
1282        regions,
1283        generation_bases,
1284        filter_comparisons: tables.filter_comparisons,
1285        project_expressions: tables.project_expressions,
1286    })
1287}
1288
1289#[cfg(test)]
1290impl ResidentCompactLogicalRegion {
1291    fn initializes(&self) -> bool {
1292        self.flags & RESIDENT_SCHEDULE_REGION_INITIALIZE != 0
1293    }
1294
1295    fn begins_scc(&self) -> bool {
1296        self.flags & RESIDENT_SCHEDULE_REGION_SCC_BEGIN != 0
1297    }
1298
1299    fn recursive(&self) -> bool {
1300        self.flags == RESIDENT_SCHEDULE_REGION_RECURSIVE
1301    }
1302
1303    fn finalizes(&self) -> bool {
1304        self.flags & RESIDENT_SCHEDULE_REGION_FINALIZE != 0
1305    }
1306}
1307
1308fn resident_compact_regions(
1309    initial_ops: Vec<ResidentRecordedOp>,
1310    phases: Vec<ResidentCapturePhase>,
1311    success_op_id: u32,
1312) -> Result<Vec<ResidentCompactLogicalRegion>> {
1313    let mut regions = Vec::new();
1314    let mut pending = initial_ops;
1315    let mut pending_seed_region = None;
1316
1317    for phase in phases {
1318        match phase {
1319            ResidentCapturePhase::Segment {
1320                mut ops,
1321                scc_begin: None,
1322            } => {
1323                if pending_seed_region.is_some() {
1324                    return Err(XlogError::Execution(
1325                        "resident SCC seed is not followed by its recursive body".into(),
1326                    ));
1327                }
1328                pending.append(&mut ops);
1329            }
1330            ResidentCapturePhase::Segment {
1331                mut ops,
1332                scc_begin: Some((iteration_limit, op_id)),
1333            } => {
1334                if pending_seed_region.is_some() {
1335                    return Err(XlogError::Execution(
1336                        "resident SCC seed is not followed by its recursive body".into(),
1337                    ));
1338                }
1339                pending.append(&mut ops);
1340                let mut flags = RESIDENT_SCHEDULE_REGION_SCC_BEGIN;
1341                if regions.is_empty() {
1342                    flags |= RESIDENT_SCHEDULE_REGION_INITIALIZE;
1343                }
1344                regions.push(ResidentCompactLogicalRegion {
1345                    ops: std::mem::take(&mut pending),
1346                    iteration_limit,
1347                    op_id,
1348                    flags,
1349                });
1350                pending_seed_region = Some(regions.len() - 1);
1351            }
1352            ResidentCapturePhase::ConditionalWhile {
1353                ops,
1354                iteration_limit,
1355                convergence_op_id,
1356            } => {
1357                let seed_region = pending_seed_region.take().ok_or_else(|| {
1358                    XlogError::Execution("resident recursive body has no preceding SCC seed".into())
1359                })?;
1360                if regions[seed_region].iteration_limit != iteration_limit {
1361                    return Err(XlogError::Execution(
1362                        "resident SCC seed and recursive body iteration limits differ".into(),
1363                    ));
1364                }
1365                regions[seed_region].op_id = convergence_op_id;
1366                regions.push(ResidentCompactLogicalRegion {
1367                    ops,
1368                    iteration_limit,
1369                    op_id: convergence_op_id,
1370                    flags: RESIDENT_SCHEDULE_REGION_RECURSIVE,
1371                });
1372            }
1373        }
1374    }
1375    if pending_seed_region.is_some() {
1376        return Err(XlogError::Execution(
1377            "resident SCC seed is missing its recursive body".into(),
1378        ));
1379    }
1380
1381    let mut flags = RESIDENT_SCHEDULE_REGION_FINALIZE;
1382    if regions.is_empty() {
1383        flags |= RESIDENT_SCHEDULE_REGION_INITIALIZE;
1384    }
1385    regions.push(ResidentCompactLogicalRegion {
1386        ops: pending,
1387        iteration_limit: 1,
1388        op_id: success_op_id,
1389        flags,
1390    });
1391    Ok(regions)
1392}
1393
1394#[cfg(test)]
1395fn coalesce_resident_capture_phases(
1396    phases: Vec<ResidentCapturePhase>,
1397) -> Vec<ResidentCapturePhase> {
1398    let mut coalesced = Vec::new();
1399    let mut ordinary_ops = Vec::new();
1400    let flush_ordinary = |coalesced: &mut Vec<ResidentCapturePhase>,
1401                          ordinary_ops: &mut Vec<ResidentRecordedOp>| {
1402        if !ordinary_ops.is_empty() {
1403            coalesced.push(ResidentCapturePhase::Segment {
1404                ops: std::mem::take(ordinary_ops),
1405                scc_begin: None,
1406            });
1407        }
1408    };
1409
1410    for phase in phases {
1411        match phase {
1412            ResidentCapturePhase::Segment {
1413                mut ops,
1414                scc_begin: None,
1415            } => ordinary_ops.append(&mut ops),
1416            boundary => {
1417                flush_ordinary(&mut coalesced, &mut ordinary_ops);
1418                coalesced.push(boundary);
1419            }
1420        }
1421    }
1422    flush_ordinary(&mut coalesced, &mut ordinary_ops);
1423    coalesced
1424}
1425
1426#[cfg(test)]
1427mod capture_phase_tests {
1428    use super::{coalesce_resident_capture_phases, ResidentCapturePhase, ResidentRecordedOp};
1429
1430    fn segment(output: usize, scc_begin: Option<(u32, u32)>) -> ResidentCapturePhase {
1431        ResidentCapturePhase::Segment {
1432            ops: vec![ResidentRecordedOp::Clear { output }],
1433            scc_begin,
1434        }
1435    }
1436
1437    fn assert_clear_range(phase: &ResidentCapturePhase, expected: std::ops::Range<usize>) {
1438        let ResidentCapturePhase::Segment {
1439            ops,
1440            scc_begin: None,
1441        } = phase
1442        else {
1443            panic!("expected an ordinary capture segment");
1444        };
1445        assert_eq!(ops.len(), expected.len());
1446        for (op, output) in ops.iter().zip(expected) {
1447            assert!(
1448                matches!(op, ResidentRecordedOp::Clear { output: actual } if *actual == output)
1449            );
1450        }
1451    }
1452
1453    #[test]
1454    fn coalesces_only_maximal_ordinary_phase_runs() {
1455        let mut phases = (0..1_000)
1456            .map(|output| segment(output, None))
1457            .collect::<Vec<_>>();
1458        phases.push(segment(10_000, Some((64, 41))));
1459        phases.extend((1_000..1_500).map(|output| segment(output, None)));
1460        phases.push(ResidentCapturePhase::ConditionalWhile {
1461            ops: vec![ResidentRecordedOp::Clear { output: 20_000 }],
1462            iteration_limit: 64,
1463            convergence_op_id: 42,
1464        });
1465        phases.extend((1_500..1_800).map(|output| segment(output, None)));
1466
1467        let coalesced = coalesce_resident_capture_phases(phases);
1468        assert_eq!(coalesced.len(), 5);
1469        assert_clear_range(&coalesced[0], 0..1_000);
1470        assert!(matches!(
1471            &coalesced[1],
1472            ResidentCapturePhase::Segment {
1473                ops,
1474                scc_begin: Some((64, 41)),
1475            } if matches!(ops.as_slice(), [ResidentRecordedOp::Clear { output: 10_000 }])
1476        ));
1477        assert_clear_range(&coalesced[2], 1_000..1_500);
1478        assert!(matches!(
1479            &coalesced[3],
1480            ResidentCapturePhase::ConditionalWhile {
1481                ops,
1482                iteration_limit: 64,
1483                convergence_op_id: 42,
1484            } if matches!(ops.as_slice(), [ResidentRecordedOp::Clear { output: 20_000 }])
1485        ));
1486        assert_clear_range(&coalesced[4], 1_500..1_800);
1487    }
1488}
1489
1490struct ResidentBuild<'executor> {
1491    executor: &'executor Executor,
1492    certificate: &'executor ResidentGraphRouteCertificate,
1493    capacity: u32,
1494    relations: Vec<ResidentLogicalRelation>,
1495    filter_workspaces: Vec<ResidentFilterPlan>,
1496    project_workspaces: Vec<ResidentProjectPlan>,
1497    heads: BTreeMap<String, usize>,
1498    head_winner_indices: BTreeMap<String, u32>,
1499    source_names: HashSet<String>,
1500    source_aliases: HashMap<String, usize>,
1501    next_op_id: u32,
1502    injection: Option<crate::resident_graph::ResidentGraphDeviceStatusTestInjection>,
1503    injection_recorded: bool,
1504}
1505
1506#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1507enum ResidentSourceBindingRoute {
1508    Direct,
1509    NormalizeEmpty,
1510}
1511
1512fn resident_source_logical_count(cached: Option<u32>) -> Result<u64> {
1513    cached.map(u64::from).ok_or_else(|| {
1514        XlogError::Execution("resident source requires a cold-path cached logical row count".into())
1515    })
1516}
1517
1518fn resident_source_binding_route(
1519    row_count: u64,
1520    count_tracked: bool,
1521    columns_tracked: bool,
1522) -> Result<ResidentSourceBindingRoute> {
1523    if count_tracked && columns_tracked {
1524        return Ok(ResidentSourceBindingRoute::Direct);
1525    }
1526    if row_count == 0 {
1527        return Ok(ResidentSourceBindingRoute::NormalizeEmpty);
1528    }
1529    Err(XlogError::Execution(
1530        "nonempty resident source is not fully runtime tracked".into(),
1531    ))
1532}
1533
1534#[derive(Debug, Clone)]
1535struct ResidentLogicalRelation {
1536    schema: Schema,
1537    initial_count: u32,
1538    permanent: bool,
1539}
1540
1541#[derive(Debug, Clone)]
1542struct ResidentFilterPlan {
1543    compact_comparisons: Vec<ResidentFilterComparisonDescriptor>,
1544}
1545
1546#[derive(Debug, Clone)]
1547struct ResidentProjectPlan {
1548    compact_expressions: Vec<ResidentProjectExpressionDescriptor>,
1549}
1550
1551#[derive(Debug, Clone, Copy)]
1552struct ResidentSlotAssignment {
1553    slot: usize,
1554    generation: u32,
1555}
1556
1557#[derive(Debug, Clone)]
1558struct ResidentPhysicalSlotPlan {
1559    schema: Schema,
1560    initial_count: u32,
1561    permanent: bool,
1562}
1563
1564#[derive(Debug, Clone)]
1565struct ResidentAllocationManifest {
1566    slots: Vec<ResidentPhysicalSlotPlan>,
1567    logical_to_slot: Vec<ResidentSlotAssignment>,
1568    required_bytes: u64,
1569    relation_bytes: u64,
1570    filter_scratch_bytes: u64,
1571    schedule_metadata_bytes: u64,
1572    fixed_workspace_bytes: u64,
1573    logical_relation_values: usize,
1574    permanent_slots: u32,
1575    scratch_slots: u32,
1576    filter_scratch_allocations: u32,
1577    max_row_bytes: u64,
1578}
1579
1580struct ResidentPhysicalBuild {
1581    relations: Vec<Option<ResidentRelation>>,
1582    filter_scratch: Option<ResidentFilterScratch>,
1583    set_workspace: ResidentSetWorkspace,
1584    join_workspace: ResidentJoinWorkspace,
1585    control: ResidentConvergenceControl,
1586}
1587
1588/// Raw fixed-cost counters retained until outer latency sampling is complete.
1589#[doc(hidden)]
1590#[derive(Default)]
1591pub struct ResidentPrepareDiagnostics {
1592    sample: u64,
1593    total_ns: u64,
1594    required_reservation_bytes: u64,
1595    logical_relation_values: usize,
1596    physical_relation_slots: usize,
1597    relation_device_allocation_calls: u64,
1598    compact_ops: usize,
1599    compact_waves: usize,
1600    compact_regions: usize,
1601    conditional_regions: usize,
1602    parent_graph_nodes: usize,
1603    conditional_body_nodes: usize,
1604    admission_and_source_snapshot_ns: u64,
1605    execution_domain_and_build_setup_ns: u64,
1606    logical_schedule_planning_ns: u64,
1607    manifest_compact_construction_ns: u64,
1608    schedule_lowering_ns: u64,
1609    reservation_ns: u64,
1610    relation_slot_allocation_ns: u64,
1611    relation_slot_allocation_ns_max: u64,
1612    relation_slot_allocation_bytes: u64,
1613    count_initialization_ns: u64,
1614    count_initialization_ns_max: u64,
1615    count_memset_calls: u64,
1616    workspace_provider_calls: u64,
1617    filter_scratch_allocation_ns: u64,
1618    filter_scratch_allocation_bytes: u64,
1619    set_workspace_allocation_ns: u64,
1620    set_workspace_allocation_bytes: u64,
1621    join_workspace_allocation_ns: u64,
1622    join_workspace_allocation_bytes: u64,
1623    control_allocation_ns: u64,
1624    control_allocation_bytes: u64,
1625    metadata_binding_construction_ns: u64,
1626    metadata_provider_calls: u64,
1627    device_trace_preparation_ns: u64,
1628    device_trace_reserved_bytes: u64,
1629    device_trace_initial_htod_calls: u64,
1630    device_trace_initial_htod_bytes: u64,
1631    schema_winners_preparation_ns: u64,
1632    schema_winners_reserved_bytes: u64,
1633    schema_winners_initial_htod_calls: u64,
1634    schema_winners_initial_htod_bytes: u64,
1635    receipt_preparation_ns: u64,
1636    receipt_reserved_bytes: u64,
1637    receipt_initial_htod_calls: u64,
1638    receipt_initial_htod_bytes: u64,
1639    schedule_program_preparation_ns: u64,
1640    schedule_program_reserved_bytes: u64,
1641    schedule_program_initial_htod_calls: u64,
1642    schedule_program_initial_htod_bytes: u64,
1643    reservation_validation_and_release_ns: u64,
1644    pinned_receipt_ns: u64,
1645    graph_body_capture_ns: u64,
1646    graph_instantiate_ns: u64,
1647    validation_owner_assembly_ns: u64,
1648}
1649
1650fn resident_prepare_diagnostics_for_sample(
1651    sample: Option<u64>,
1652) -> Option<ResidentPrepareDiagnostics> {
1653    sample.map(|sample| ResidentPrepareDiagnostics {
1654        sample,
1655        ..ResidentPrepareDiagnostics::default()
1656    })
1657}
1658
1659fn resident_prepare_elapsed_ns(started: std::time::Instant) -> u64 {
1660    u64::try_from(started.elapsed().as_nanos()).unwrap_or(u64::MAX)
1661}
1662
1663fn resident_schema_winners_initial_htod(default_count: usize) -> (u64, u64) {
1664    if default_count == 0 {
1665        return (0, 0);
1666    }
1667    (
1668        1,
1669        u64::try_from(default_count)
1670            .unwrap_or(u64::MAX)
1671            .saturating_mul(std::mem::size_of::<u32>() as u64),
1672    )
1673}
1674
1675fn resident_receipt_initial_htod(output_count: usize) -> (u64, u64) {
1676    let pointee_count = output_count.saturating_mul(2).saturating_add(4);
1677    if pointee_count == 0 {
1678        return (0, 0);
1679    }
1680    (
1681        1,
1682        u64::try_from(pointee_count)
1683            .unwrap_or(u64::MAX)
1684            .saturating_mul(std::mem::size_of::<u64>() as u64),
1685    )
1686}
1687
1688fn resident_schedule_initial_htod(reserved_bytes: u64) -> (u64, u64) {
1689    (8, reserved_bytes)
1690}
1691
1692impl ResidentPrepareDiagnostics {
1693    pub fn into_snapshot(self) -> ResidentGraphPrepareDiagnosticSnapshot {
1694        let workspace_allocation_ns = self
1695            .filter_scratch_allocation_ns
1696            .saturating_add(self.set_workspace_allocation_ns)
1697            .saturating_add(self.join_workspace_allocation_ns)
1698            .saturating_add(self.control_allocation_ns);
1699        let workspace_allocation_bytes = self
1700            .filter_scratch_allocation_bytes
1701            .saturating_add(self.set_workspace_allocation_bytes)
1702            .saturating_add(self.join_workspace_allocation_bytes)
1703            .saturating_add(self.control_allocation_bytes);
1704        let metadata_preparation_ns = self
1705            .device_trace_preparation_ns
1706            .saturating_add(self.schema_winners_preparation_ns)
1707            .saturating_add(self.receipt_preparation_ns)
1708            .saturating_add(self.schedule_program_preparation_ns);
1709        let metadata_reserved_bytes = self
1710            .device_trace_reserved_bytes
1711            .saturating_add(self.schema_winners_reserved_bytes)
1712            .saturating_add(self.receipt_reserved_bytes)
1713            .saturating_add(self.schedule_program_reserved_bytes);
1714        let additive = [
1715            self.admission_and_source_snapshot_ns,
1716            self.execution_domain_and_build_setup_ns,
1717            self.logical_schedule_planning_ns,
1718            self.manifest_compact_construction_ns,
1719            self.schedule_lowering_ns,
1720            self.reservation_ns,
1721            self.relation_slot_allocation_ns,
1722            self.count_initialization_ns,
1723            workspace_allocation_ns,
1724            self.metadata_binding_construction_ns,
1725            metadata_preparation_ns,
1726            self.reservation_validation_and_release_ns,
1727            self.pinned_receipt_ns,
1728            self.graph_body_capture_ns,
1729            self.graph_instantiate_ns,
1730            self.validation_owner_assembly_ns,
1731        ];
1732        let attributed_ns = additive.iter().copied().fold(0u64, u64::saturating_add);
1733        let unattributed_ns = self.total_ns.saturating_sub(attributed_ns);
1734        let metadata_initial_htod_calls = self
1735            .device_trace_initial_htod_calls
1736            .saturating_add(self.schema_winners_initial_htod_calls)
1737            .saturating_add(self.receipt_initial_htod_calls)
1738            .saturating_add(self.schedule_program_initial_htod_calls);
1739        let metadata_initial_htod_bytes = self
1740            .device_trace_initial_htod_bytes
1741            .saturating_add(self.schema_winners_initial_htod_bytes)
1742            .saturating_add(self.receipt_initial_htod_bytes)
1743            .saturating_add(self.schedule_program_initial_htod_bytes);
1744        let to_u64 = |value: usize| u64::try_from(value).unwrap_or(u64::MAX);
1745        ResidentGraphPrepareDiagnosticSnapshot {
1746            sample: self.sample,
1747            total_ns: self.total_ns,
1748            admission_and_source_snapshot_ns: self.admission_and_source_snapshot_ns,
1749            execution_domain_and_build_setup_ns: self.execution_domain_and_build_setup_ns,
1750            logical_schedule_planning_ns: self.logical_schedule_planning_ns,
1751            manifest_compact_construction_ns: self.manifest_compact_construction_ns,
1752            schedule_lowering_ns: self.schedule_lowering_ns,
1753            reservation_ns: self.reservation_ns,
1754            relation_preparation_ns: self.relation_slot_allocation_ns,
1755            count_initialization_ns: self.count_initialization_ns,
1756            workspace_preparation_ns: workspace_allocation_ns,
1757            metadata_binding_construction_ns: self.metadata_binding_construction_ns,
1758            metadata_preparation_ns,
1759            reservation_validation_and_release_ns: self.reservation_validation_and_release_ns,
1760            pinned_receipt_ns: self.pinned_receipt_ns,
1761            graph_body_capture_ns: self.graph_body_capture_ns,
1762            graph_instantiate_ns: self.graph_instantiate_ns,
1763            validation_owner_assembly_ns: self.validation_owner_assembly_ns,
1764            unattributed_ns,
1765            required_reservation_bytes: self.required_reservation_bytes,
1766            logical_relation_values: to_u64(self.logical_relation_values),
1767            physical_relation_slots: to_u64(self.physical_relation_slots),
1768            relation_device_allocation_calls: self.relation_device_allocation_calls,
1769            relation_reserved_bytes: self.relation_slot_allocation_bytes,
1770            relation_slot_preparation_ns_max: self.relation_slot_allocation_ns_max,
1771            count_memset_calls: self.count_memset_calls,
1772            count_memset_bytes: self
1773                .count_memset_calls
1774                .saturating_mul(std::mem::size_of::<u32>() as u64),
1775            count_initialization_ns_max: self.count_initialization_ns_max,
1776            workspace_provider_calls: self.workspace_provider_calls,
1777            workspace_reserved_bytes: workspace_allocation_bytes,
1778            filter_scratch_preparation_ns: self.filter_scratch_allocation_ns,
1779            filter_scratch_reserved_bytes: self.filter_scratch_allocation_bytes,
1780            set_workspace_preparation_ns: self.set_workspace_allocation_ns,
1781            set_workspace_reserved_bytes: self.set_workspace_allocation_bytes,
1782            join_workspace_preparation_ns: self.join_workspace_allocation_ns,
1783            join_workspace_reserved_bytes: self.join_workspace_allocation_bytes,
1784            control_preparation_ns: self.control_allocation_ns,
1785            control_reserved_bytes: self.control_allocation_bytes,
1786            metadata_provider_calls: self.metadata_provider_calls,
1787            metadata_reserved_bytes,
1788            metadata_initial_htod_calls,
1789            metadata_initial_htod_bytes,
1790            device_trace_preparation_ns: self.device_trace_preparation_ns,
1791            device_trace_reserved_bytes: self.device_trace_reserved_bytes,
1792            device_trace_initial_htod_calls: self.device_trace_initial_htod_calls,
1793            device_trace_initial_htod_bytes: self.device_trace_initial_htod_bytes,
1794            schema_winners_preparation_ns: self.schema_winners_preparation_ns,
1795            schema_winners_reserved_bytes: self.schema_winners_reserved_bytes,
1796            schema_winners_initial_htod_calls: self.schema_winners_initial_htod_calls,
1797            schema_winners_initial_htod_bytes: self.schema_winners_initial_htod_bytes,
1798            receipt_preparation_ns: self.receipt_preparation_ns,
1799            receipt_reserved_bytes: self.receipt_reserved_bytes,
1800            receipt_initial_htod_calls: self.receipt_initial_htod_calls,
1801            receipt_initial_htod_bytes: self.receipt_initial_htod_bytes,
1802            schedule_program_preparation_ns: self.schedule_program_preparation_ns,
1803            schedule_program_reserved_bytes: self.schedule_program_reserved_bytes,
1804            schedule_program_initial_htod_calls: self.schedule_program_initial_htod_calls,
1805            schedule_program_initial_htod_bytes: self.schedule_program_initial_htod_bytes,
1806            compact_ops: to_u64(self.compact_ops),
1807            compact_waves: to_u64(self.compact_waves),
1808            compact_regions: to_u64(self.compact_regions),
1809            conditional_regions: to_u64(self.conditional_regions),
1810            parent_graph_nodes: to_u64(self.parent_graph_nodes),
1811            conditional_body_nodes: to_u64(self.conditional_body_nodes),
1812        }
1813    }
1814}
1815
1816#[cfg(test)]
1817mod prepare_diagnostic_tests {
1818    use super::{
1819        resident_prepare_diagnostics_for_sample, resident_receipt_initial_htod,
1820        resident_schedule_initial_htod, resident_schema_winners_initial_htod,
1821        ResidentPrepareDiagnostics,
1822    };
1823
1824    #[test]
1825    fn prepare_diagnostic_counts_only_source_proven_initial_transfers() {
1826        assert_eq!(resident_schema_winners_initial_htod(0), (0, 0));
1827        assert_eq!(resident_schema_winners_initial_htod(3), (1, 12));
1828        assert_eq!(resident_receipt_initial_htod(0), (1, 32));
1829        assert_eq!(resident_receipt_initial_htod(3), (1, 80));
1830        assert_eq!(resident_schedule_initial_htod(4096), (8, 4096));
1831
1832        let (schema_calls, schema_bytes) = resident_schema_winners_initial_htod(3);
1833        let (receipt_calls, receipt_bytes) = resident_receipt_initial_htod(0);
1834        let (schedule_calls, schedule_bytes) = resident_schedule_initial_htod(4096);
1835        let diagnostics = ResidentPrepareDiagnostics {
1836            sample: 9,
1837            total_ns: 100,
1838            relation_slot_allocation_ns: 11,
1839            relation_slot_allocation_ns_max: 7,
1840            relation_slot_allocation_bytes: 2048,
1841            workspace_provider_calls: 4,
1842            count_initialization_ns: 10,
1843            count_initialization_ns_max: 5,
1844            count_memset_calls: 3,
1845            metadata_provider_calls: 4,
1846            schema_winners_initial_htod_calls: schema_calls,
1847            schema_winners_initial_htod_bytes: schema_bytes,
1848            receipt_initial_htod_calls: receipt_calls,
1849            receipt_initial_htod_bytes: receipt_bytes,
1850            schedule_program_initial_htod_calls: schedule_calls,
1851            schedule_program_initial_htod_bytes: schedule_bytes,
1852            ..ResidentPrepareDiagnostics::default()
1853        };
1854        let snapshot = diagnostics.into_snapshot();
1855        assert_eq!(snapshot.sample, 9);
1856        assert_eq!(snapshot.total_ns, 100);
1857        assert_eq!(snapshot.relation_preparation_ns, 11);
1858        assert_eq!(snapshot.relation_reserved_bytes, 2048);
1859        assert_eq!(snapshot.relation_slot_preparation_ns_max, 7);
1860        assert_eq!(snapshot.workspace_provider_calls, 4);
1861        assert_eq!(snapshot.count_memset_calls, 3);
1862        assert_eq!(snapshot.count_initialization_ns, 10);
1863        assert_eq!(snapshot.count_initialization_ns_max, 5);
1864        assert_eq!(
1865            snapshot.count_memset_bytes,
1866            3 * std::mem::size_of::<u32>() as u64
1867        );
1868        assert_eq!(snapshot.metadata_provider_calls, 4);
1869        assert_eq!(snapshot.device_trace_initial_htod_calls, 0);
1870        assert_eq!(snapshot.device_trace_initial_htod_bytes, 0);
1871        assert_eq!(snapshot.schema_winners_initial_htod_calls, 1);
1872        assert_eq!(snapshot.schema_winners_initial_htod_bytes, 12);
1873        assert_eq!(snapshot.receipt_initial_htod_calls, 1);
1874        assert_eq!(snapshot.receipt_initial_htod_bytes, 32);
1875        assert_eq!(snapshot.schedule_program_initial_htod_calls, 8);
1876        assert_eq!(snapshot.schedule_program_initial_htod_bytes, 4096);
1877        assert_eq!(snapshot.metadata_initial_htod_calls, 10);
1878        assert_eq!(snapshot.metadata_initial_htod_bytes, 4140);
1879    }
1880
1881    #[test]
1882    fn prepare_diagnostic_is_absent_when_sampling_is_disabled() {
1883        assert!(resident_prepare_diagnostics_for_sample(None).is_none());
1884        assert_eq!(
1885            resident_prepare_diagnostics_for_sample(Some(23))
1886                .expect("enabled diagnostics create raw timing state")
1887                .sample,
1888            23
1889        );
1890    }
1891
1892    #[test]
1893    fn prepare_diagnostic_keeps_setup_and_reservation_validation_separate() {
1894        let snapshot = ResidentPrepareDiagnostics {
1895            sample: 5,
1896            total_ns: 100,
1897            admission_and_source_snapshot_ns: 11,
1898            execution_domain_and_build_setup_ns: 13,
1899            metadata_binding_construction_ns: 17,
1900            reservation_validation_and_release_ns: 19,
1901            ..ResidentPrepareDiagnostics::default()
1902        }
1903        .into_snapshot();
1904
1905        assert_eq!(snapshot.admission_and_source_snapshot_ns, 11);
1906        assert_eq!(snapshot.execution_domain_and_build_setup_ns, 13);
1907        assert_eq!(snapshot.metadata_binding_construction_ns, 17);
1908        assert_eq!(snapshot.reservation_validation_and_release_ns, 19);
1909        assert_eq!(snapshot.unattributed_ns, 40);
1910    }
1911}
1912
1913impl ResidentAllocationManifest {
1914    fn finalize_compact_schedule(
1915        &mut self,
1916        schedule: &ResidentCompactSchedulePlan,
1917        head_count: usize,
1918    ) -> Result<()> {
1919        let (required_bytes, schedule_metadata_bytes) = resident_compact_allocation_bytes(
1920            self.relation_bytes,
1921            self.filter_scratch_bytes,
1922            self.fixed_workspace_bytes,
1923            self.slots.len(),
1924            head_count,
1925            schedule,
1926        )?;
1927        self.required_bytes = required_bytes;
1928        self.schedule_metadata_bytes = schedule_metadata_bytes;
1929        Ok(())
1930    }
1931}
1932
1933fn resident_output_indices(
1934    heads: &BTreeMap<String, usize>,
1935    assignments: &[ResidentSlotAssignment],
1936    slots: &[ResidentPhysicalSlotPlan],
1937) -> Result<Vec<(String, usize)>> {
1938    heads
1939        .iter()
1940        .map(|(name, logical)| {
1941            let assignment = assignments.get(*logical).ok_or_else(|| {
1942                XlogError::Execution("resident head has no physical slot assignment".into())
1943            })?;
1944            if !slots
1945                .get(assignment.slot)
1946                .is_some_and(|slot| slot.permanent)
1947            {
1948                return Err(XlogError::Execution(
1949                    "resident head is not assigned to a permanent physical slot".into(),
1950                ));
1951            }
1952            Ok((name.clone(), assignment.slot))
1953        })
1954        .collect()
1955}
1956
1957fn resident_validate_exact_reservation(required: u64, used: u64, remaining: u64) -> Result<()> {
1958    if used == required && remaining == 0 {
1959        return Ok(());
1960    }
1961    Err(XlogError::Execution(format!(
1962        "resident allocation manifest accounted for {required} manager-tracked bytes but materialization consumed {used} and left {remaining} reserved bytes"
1963    )))
1964}
1965
1966#[derive(Debug)]
1967struct ResidentScratchSlotState {
1968    slot: usize,
1969    layout: Vec<ScalarType>,
1970    last_use: usize,
1971    generation: u32,
1972}
1973
1974fn resident_schema_layout(schema: &Schema) -> Vec<ScalarType> {
1975    schema.columns.iter().map(|(_, scalar)| *scalar).collect()
1976}
1977
1978fn resident_private_inputs(op: &ResidentRecordedOp, mut visit: impl FnMut(usize)) {
1979    let mut visit_ref = |reference: &ResidentBufferRef| {
1980        if let ResidentBufferRef::Private(index) = reference {
1981            visit(*index);
1982        }
1983    };
1984    match op {
1985        ResidentRecordedOp::Scan {
1986            relation: input, ..
1987        }
1988        | ResidentRecordedOp::Filter { input, .. }
1989        | ResidentRecordedOp::Project { input, .. } => visit_ref(input),
1990        ResidentRecordedOp::Union { left, right, .. }
1991        | ResidentRecordedOp::Diff { left, right, .. }
1992        | ResidentRecordedOp::Join { left, right, .. } => {
1993            visit_ref(left);
1994            visit_ref(right);
1995        }
1996        ResidentRecordedOp::ChangedMark { relation } => visit(*relation),
1997        ResidentRecordedOp::TraceDelta { semantic_guard, .. } => {
1998            if let Some(semantic_guard) = semantic_guard {
1999                visit_ref(semantic_guard);
2000            }
2001        }
2002        ResidentRecordedOp::SchemaWinnerMark { contribution, .. } => visit_ref(contribution),
2003        ResidentRecordedOp::Unit { .. }
2004        | ResidentRecordedOp::ChangedReset
2005        | ResidentRecordedOp::TestStatus(_) => {}
2006        #[cfg(test)]
2007        ResidentRecordedOp::Clear { .. } => {}
2008    }
2009}
2010
2011fn resident_private_output(op: &ResidentRecordedOp) -> Option<usize> {
2012    match op {
2013        ResidentRecordedOp::Unit { output, .. }
2014        | ResidentRecordedOp::Filter { output, .. }
2015        | ResidentRecordedOp::Project { output, .. }
2016        | ResidentRecordedOp::Union { output, .. }
2017        | ResidentRecordedOp::Diff { output, .. }
2018        | ResidentRecordedOp::Join { output, .. } => Some(*output),
2019        #[cfg(test)]
2020        ResidentRecordedOp::Clear { output } => Some(*output),
2021        ResidentRecordedOp::Scan { .. }
2022        | ResidentRecordedOp::TraceDelta { .. }
2023        | ResidentRecordedOp::ChangedReset
2024        | ResidentRecordedOp::ChangedMark { .. }
2025        | ResidentRecordedOp::SchemaWinnerMark { .. }
2026        | ResidentRecordedOp::TestStatus(_) => None,
2027    }
2028}
2029
2030fn resident_source_slot_map<'a>(
2031    private_slot_count: usize,
2032    sources: impl IntoIterator<Item = &'a str>,
2033) -> Result<BTreeMap<String, u32>> {
2034    let first_source_slot = u32::try_from(private_slot_count)
2035        .map_err(|_| XlogError::Execution("resident private slot count exceeds u32".into()))?;
2036    let names = sources
2037        .into_iter()
2038        .map(str::to_owned)
2039        .collect::<BTreeSet<_>>();
2040    names
2041        .into_iter()
2042        .enumerate()
2043        .map(|(offset, name)| {
2044            let offset = u32::try_from(offset).map_err(|_| {
2045                XlogError::Execution("resident source slot count exceeds u32".into())
2046            })?;
2047            let slot = first_source_slot.checked_add(offset).ok_or_else(|| {
2048                XlogError::Execution("resident source slot index overflow".into())
2049            })?;
2050            Ok((name, slot))
2051        })
2052        .collect()
2053}
2054
2055fn resident_record_lifetimes(
2056    ops: &[ResidentRecordedOp],
2057    relations: &[ResidentLogicalRelation],
2058    definitions: &mut [Option<usize>],
2059    last_uses: &mut [Option<usize>],
2060    ordinal: &mut usize,
2061) -> Result<(usize, usize)> {
2062    let start = *ordinal;
2063    for op in ops {
2064        let current = *ordinal;
2065        let mut invalid_input = None;
2066        let mut input_before_definition = None;
2067        resident_private_inputs(op, |logical| {
2068            let Some(relation) = relations.get(logical) else {
2069                invalid_input.get_or_insert(logical);
2070                return;
2071            };
2072            if !relation.permanent {
2073                if definitions[logical].is_none() {
2074                    input_before_definition.get_or_insert(logical);
2075                } else {
2076                    last_uses[logical] = Some(last_uses[logical].unwrap_or(current).max(current));
2077                }
2078            }
2079        });
2080        if let Some(logical) = invalid_input {
2081            return Err(XlogError::Execution(format!(
2082                "resident logical input relation {logical} is missing"
2083            )));
2084        }
2085        if let Some(logical) = input_before_definition {
2086            return Err(XlogError::Execution(format!(
2087                "resident scratch relation {logical} is used before its definition"
2088            )));
2089        }
2090        if let Some(logical) = resident_private_output(op) {
2091            let relation = relations.get(logical).ok_or_else(|| {
2092                XlogError::Execution(format!(
2093                    "resident logical output relation {logical} is missing"
2094                ))
2095            })?;
2096            if !relation.permanent {
2097                if definitions[logical].replace(current).is_some() {
2098                    return Err(XlogError::Execution(format!(
2099                        "resident scratch relation {logical} has multiple definitions"
2100                    )));
2101                }
2102                last_uses[logical] = Some(last_uses[logical].unwrap_or(current).max(current));
2103            }
2104        }
2105        *ordinal = ordinal
2106            .checked_add(1)
2107            .ok_or_else(|| XlogError::Execution("resident operation ordinal overflow".into()))?;
2108    }
2109    Ok((start, *ordinal))
2110}
2111
2112fn resident_validate_slot_assignments(
2113    relations: &[ResidentLogicalRelation],
2114    definitions: &[Option<usize>],
2115    last_uses: &[Option<usize>],
2116    slots: &[ResidentPhysicalSlotPlan],
2117    assignments: &[ResidentSlotAssignment],
2118) -> Result<()> {
2119    if definitions.len() != relations.len()
2120        || last_uses.len() != relations.len()
2121        || assignments.len() != relations.len()
2122    {
2123        return Err(XlogError::Execution(
2124            "resident slot validation vector length mismatch".into(),
2125        ));
2126    }
2127
2128    let mut occupied_permanent_slots = HashSet::new();
2129    let mut scratch_by_slot = BTreeMap::<usize, Vec<(usize, usize, usize, u32)>>::new();
2130    let mut assigned_slots = HashSet::new();
2131    for (logical, relation) in relations.iter().enumerate() {
2132        let assignment = assignments[logical];
2133        let slot = slots.get(assignment.slot).ok_or_else(|| {
2134            XlogError::Execution(format!(
2135                "resident logical relation {logical} maps to missing physical slot {}",
2136                assignment.slot
2137            ))
2138        })?;
2139        assigned_slots.insert(assignment.slot);
2140        if relation.permanent {
2141            if assignment.generation != 0 {
2142                return Err(XlogError::Execution(format!(
2143                    "resident permanent relation {logical} has nonzero generation {}",
2144                    assignment.generation
2145                )));
2146            }
2147            if !slot.permanent
2148                || slot.schema != relation.schema
2149                || slot.initial_count != relation.initial_count
2150            {
2151                return Err(XlogError::Execution(format!(
2152                    "resident permanent relation {logical} has an incompatible physical slot"
2153                )));
2154            }
2155            if !occupied_permanent_slots.insert(assignment.slot) {
2156                return Err(XlogError::Execution(format!(
2157                    "resident permanent physical slot {} has multiple owners",
2158                    assignment.slot
2159                )));
2160            }
2161            continue;
2162        }
2163
2164        if slot.permanent
2165            || resident_schema_layout(&slot.schema) != resident_schema_layout(&relation.schema)
2166        {
2167            return Err(XlogError::Execution(format!(
2168                "resident scratch relation {logical} has an incompatible physical slot"
2169            )));
2170        }
2171        let definition = definitions[logical].ok_or_else(|| {
2172            XlogError::Execution(format!(
2173                "resident scratch relation {logical} is never defined"
2174            ))
2175        })?;
2176        let last_use = last_uses[logical].unwrap_or(definition);
2177        if last_use < definition {
2178            return Err(XlogError::Execution(format!(
2179                "resident scratch relation {logical} ends before its definition"
2180            )));
2181        }
2182        scratch_by_slot.entry(assignment.slot).or_default().push((
2183            definition,
2184            last_use,
2185            logical,
2186            assignment.generation,
2187        ));
2188    }
2189
2190    if assigned_slots.len() != slots.len() {
2191        return Err(XlogError::Execution(
2192            "resident allocation manifest contains an unassigned physical slot".into(),
2193        ));
2194    }
2195    for (slot, mut generations) in scratch_by_slot {
2196        generations.sort_by_key(|(definition, _, logical, _)| (*definition, *logical));
2197        let mut previous_last_use = None;
2198        for (expected_generation, (definition, last_use, logical, generation)) in
2199            generations.into_iter().enumerate()
2200        {
2201            let expected_generation = u32::try_from(expected_generation).map_err(|_| {
2202                XlogError::Execution("resident scratch generation exceeds u32".into())
2203            })?;
2204            if generation != expected_generation {
2205                return Err(XlogError::Execution(format!(
2206                    "resident scratch relation {logical} in slot {slot} has generation {generation} but expected {expected_generation}"
2207                )));
2208            }
2209            if previous_last_use.is_some_and(|previous| previous >= definition) {
2210                return Err(XlogError::Execution(format!(
2211                    "resident scratch generations overlap in physical slot {slot}"
2212                )));
2213            }
2214            previous_last_use = Some(last_use);
2215        }
2216    }
2217    Ok(())
2218}
2219
2220fn resident_checked_add(total: &mut u64, bytes: u64, label: &str) -> Result<()> {
2221    *total = total.checked_add(bytes).ok_or_else(|| {
2222        XlogError::Execution(format!("resident {label} allocation byte overflow"))
2223    })?;
2224    Ok(())
2225}
2226
2227/// A fully allocated, instantiated resident transaction that has not launched.
2228pub struct PreparedResidentGraph {
2229    owners: ResidentRunOwners,
2230    preflight_report: ResidentGraphPreflightReport,
2231    has_device_status_writer: bool,
2232    source_set_snapshots: Vec<ResidentSourceSetSnapshot>,
2233    source_buffers_to_restore: Vec<(String, CudaBuffer)>,
2234    prepare_diagnostic: Option<ResidentPrepareDiagnostics>,
2235}
2236
2237/// Read-only topology and memory facts captured after preparation and before launch.
2238#[derive(Debug, Clone, PartialEq, Eq)]
2239pub struct ResidentGraphPreflightReport {
2240    /// Fixed logical row capacity used for every staged relation.
2241    pub relation_capacity: u32,
2242    /// Exact manager-tracked bytes reserved before physical materialization.
2243    pub estimated_required_bytes: u64,
2244    /// Caller budget remaining when admission ran.
2245    pub available_bytes_at_admission: u64,
2246    /// Manager-tracked live allocation growth caused by this prepared transaction.
2247    pub tracked_device_allocation_bytes: u64,
2248    /// Exact manager-tracked relation data and logical-count bytes.
2249    pub relation_device_bytes: u64,
2250    /// Exact immutable filter descriptor bytes retained by the graph.
2251    pub filter_descriptor_device_bytes: u64,
2252    /// Exact mutable filter scratch bytes shared by all sequential filters.
2253    pub filter_scratch_device_bytes: u64,
2254    /// Exact immutable project and copy descriptor bytes.
2255    pub project_descriptor_device_bytes: u64,
2256    /// Exact set, join, control, trace, packed-receipt, and remaining compact schedule metadata bytes.
2257    pub fixed_workspace_device_bytes: u64,
2258    /// Nodes in the instantiated parent graph, excluding conditional body children.
2259    pub parent_graph_nodes: usize,
2260    /// Conditional-WHILE nodes in the instantiated parent graph.
2261    pub conditional_while_nodes: usize,
2262    /// Actual node kinds in root-to-leaf dependency-chain order.
2263    pub parent_graph_node_kinds: Vec<CudaGraphNodeKind>,
2264    /// Actual node kinds in each conditional-WHILE body, in parent order.
2265    pub conditional_body_node_kinds: Vec<Vec<CudaGraphNodeKind>>,
2266    /// Actual kernel-node count in each conditional-WHILE body.
2267    pub conditional_body_kernel_counts: Vec<usize>,
2268    /// Parent nodes plus every conditional-body child node.
2269    pub hierarchical_graph_nodes: usize,
2270    /// Private fixed-capacity relation slots retained by the graph.
2271    pub private_relation_slots: usize,
2272    /// Logical relation values before interval coloring.
2273    pub logical_relation_values: usize,
2274    /// Permanent unit, staged-head, raw, and delta slots.
2275    pub permanent_relation_slots: u32,
2276    /// Final staged relation count included in the terminal receipt.
2277    pub staged_output_relations: usize,
2278    /// Layout-compatible scratch slots selected by interval coloring.
2279    pub scratch_slots: u32,
2280    /// Number of mutable filter scratch allocations retained by this graph.
2281    pub filter_scratch_allocations: u32,
2282    /// Widest allocated permanent or intermediate row in bytes.
2283    pub max_row_bytes: u64,
2284}
2285
2286/// A resident transaction whose one graph launch is in flight.
2287pub struct ResidentGraphInFlight<'executor> {
2288    execution: PendingResidentOwners<ResidentLaunchOwners>,
2289    replay_metadata: ResidentGraphReplayMetadata,
2290    _executor: PhantomData<&'executor Executor>,
2291}
2292
2293struct ResidentGraphReplayMetadata {
2294    preflight_report: ResidentGraphPreflightReport,
2295    has_device_status_writer: bool,
2296    source_set_snapshots: Vec<ResidentSourceSetSnapshot>,
2297    prepare_diagnostic: Option<ResidentPrepareDiagnostics>,
2298}
2299
2300struct ResidentLaunchOwners {
2301    // Keep every handle beside its allocations through partial enqueue as well
2302    // as terminal event-record and wait errors.
2303    completion: Option<ResidentCompletionEvent>,
2304    timing_start: Option<CudaEvent>,
2305    timing_end: Option<CudaEvent>,
2306    owners: ResidentRunOwners,
2307}
2308
2309/// A resident transaction after its one terminal completion wait.
2310pub struct ResidentGraphSynchronized<'executor> {
2311    device_elapsed_ns: u64,
2312    owners: ResidentRunOwners,
2313    replay_metadata: ResidentGraphReplayMetadata,
2314    _executor: PhantomData<&'executor Executor>,
2315}
2316
2317struct StagedResidentOutput {
2318    name: String,
2319    buffer: CudaBuffer,
2320}
2321
2322/// Borrow-free decoded receipt. Store mutation happens only in [`Self::commit`].
2323pub struct ObservedResidentGraphReceipt {
2324    encoded_len: usize,
2325    device_elapsed_ns: u64,
2326    device_scan_invocations: u64,
2327    device_filter_invocations: u64,
2328    semantic_scan_invocations: u64,
2329    semantic_filter_invocations: u64,
2330    iterations: u32,
2331    terminal: std::result::Result<(), ResidentGraphExecutionError>,
2332    outputs: Vec<StagedResidentOutput>,
2333    source_epoch: u64,
2334    relation_registration: Vec<(RelId, String)>,
2335    transaction_identity: Arc<()>,
2336    provider: Arc<CudaKernelProvider>,
2337    phase_timings: Option<ResidentFinalObservationPhaseTimings>,
2338    prepared_for_reuse: Option<PreparedResidentGraph>,
2339}
2340
2341/// Opt-in wall-clock breakdown of final resident receipt observation.
2342#[doc(hidden)]
2343#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2344pub struct ResidentFinalObservationPhaseTimings {
2345    /// Time spent in the sole pinned device-to-host receipt transfer.
2346    pub receipt_d2h_ns: u64,
2347    /// Time spent decoding the receipt, resolving schemas, and staging outputs.
2348    pub decode_schema_staging_ns: u64,
2349}
2350
2351fn resident_source_set_snapshot(
2352    provider: &CudaKernelProvider,
2353    name: &str,
2354    version: u64,
2355    buffer: &CudaBuffer,
2356) -> std::result::Result<ResidentSourceSetSnapshot, ResidentGraphDeclineReason> {
2357    if !buffer.canonical_full_row_set_certified() {
2358        return Err(ResidentGraphDeclineReason::SourceSetUncertified {
2359            relation: name.to_owned(),
2360        });
2361    }
2362    let manager_ptr = Arc::as_ptr(provider.memory()) as usize;
2363    let runtime = provider.memory().runtime().ok_or_else(|| {
2364        ResidentGraphDeclineReason::SourceSetUncertified {
2365            relation: format!("{name} memory manager has no device runtime"),
2366        }
2367    })?;
2368    if !Arc::ptr_eq(provider.device(), provider.memory().device())
2369        || !Arc::ptr_eq(provider.device(), runtime.device())
2370        || u32::try_from(provider.device().ordinal()).ok() != Some(runtime.device_ordinal())
2371    {
2372        return Err(ResidentGraphDeclineReason::SourceSetUncertified {
2373            relation: format!("{name} memory manager does not match the resident provider"),
2374        });
2375    }
2376    let column_blocks = buffer
2377        .columns()
2378        .iter()
2379        .enumerate()
2380        .map(|(column_index, column)| {
2381            let CudaColumn::Owned(column) = column else {
2382                return Err(ResidentGraphDeclineReason::SourceSetUncertified {
2383                    relation: format!("{name} column {column_index} is externally owned"),
2384                });
2385            };
2386            if column.memory_manager_ptr_value() != manager_ptr {
2387                return Err(ResidentGraphDeclineReason::SourceSetUncertified {
2388                    relation: format!(
2389                        "{name} column {column_index} belongs to another memory manager"
2390                    ),
2391                });
2392            }
2393            let block = column.runtime_block().map(BlockId::from_block);
2394            if block.is_none() && buffer.num_rows() != 0 {
2395                return Err(ResidentGraphDeclineReason::SourceSetUncertified {
2396                    relation: format!("{name} column {column_index} is not runtime tracked"),
2397                });
2398            }
2399            Ok(block)
2400        })
2401        .collect::<std::result::Result<Vec<_>, _>>()?;
2402    let row_count = buffer.num_rows_device();
2403    if row_count.memory_manager_ptr_value() != manager_ptr {
2404        return Err(ResidentGraphDeclineReason::SourceSetUncertified {
2405            relation: format!("{name} logical count belongs to another memory manager"),
2406        });
2407    }
2408    let row_count_block = row_count
2409        .runtime_block()
2410        .map(BlockId::from_block)
2411        .ok_or_else(|| ResidentGraphDeclineReason::SourceSetUncertified {
2412            relation: format!("{name} logical count is not runtime tracked"),
2413        })?;
2414    Ok(ResidentSourceSetSnapshot {
2415        name: name.to_owned(),
2416        version,
2417        schema: buffer.schema().clone(),
2418        row_capacity: buffer.num_rows(),
2419        column_blocks,
2420        row_count_block,
2421    })
2422}
2423
2424fn validate_resident_source_set_snapshots(
2425    executor: &Executor,
2426    source_epoch: u64,
2427    snapshots: &[ResidentSourceSetSnapshot],
2428) -> std::result::Result<(), ResidentGraphExecutionError> {
2429    if executor.store.mutation_epoch() != source_epoch {
2430        return Err(resident_decline_error(
2431            ResidentGraphDeclineReason::SourceSetUncertified {
2432                relation: "relation store changed after resident preparation".to_owned(),
2433            },
2434        ));
2435    }
2436    for snapshot in snapshots {
2437        let Some((buffer, version)) = executor.store.get_with_version(&snapshot.name) else {
2438            return Err(resident_decline_error(
2439                ResidentGraphDeclineReason::SourceSetUncertified {
2440                    relation: snapshot.name.clone(),
2441                },
2442            ));
2443        };
2444        let current =
2445            resident_source_set_snapshot(&executor.provider, &snapshot.name, version, buffer)
2446                .map_err(resident_decline_error)?;
2447        if &current != snapshot {
2448            return Err(resident_decline_error(
2449                ResidentGraphDeclineReason::SourceSetUncertified {
2450                    relation: snapshot.name.clone(),
2451                },
2452            ));
2453        }
2454    }
2455    Ok(())
2456}
2457
2458impl PreparedResidentGraph {
2459    /// Return immutable prelaunch topology and memory diagnostics.
2460    pub fn preflight_report(&self) -> &ResidentGraphPreflightReport {
2461        &self.preflight_report
2462    }
2463
2464    /// Move out the opt-in raw prepare diagnostic without deriving or emitting it.
2465    #[doc(hidden)]
2466    pub fn take_prepare_diagnostic(&mut self) -> Option<ResidentPrepareDiagnostics> {
2467        self.prepare_diagnostic.take()
2468    }
2469
2470    /// Rebind source versions after an in-place device copy without changing
2471    /// any address or captured source geometry.
2472    pub fn refresh_source_set_snapshots(
2473        &mut self,
2474        executor: &Executor,
2475    ) -> std::result::Result<(), ResidentGraphExecutionError> {
2476        for snapshot in &mut self.source_set_snapshots {
2477            let Some((buffer, version)) = executor.store.get_with_version(&snapshot.name) else {
2478                return Err(resident_decline_error(
2479                    ResidentGraphDeclineReason::SourceSetUncertified {
2480                        relation: snapshot.name.clone(),
2481                    },
2482                ));
2483            };
2484            let current =
2485                resident_source_set_snapshot(&executor.provider, &snapshot.name, version, buffer)
2486                    .map_err(resident_decline_error)?;
2487            let mut expected = snapshot.clone();
2488            expected.version = version;
2489            if current != expected {
2490                return Err(resident_decline_error(
2491                    ResidentGraphDeclineReason::SourceSetUncertified {
2492                        relation: snapshot.name.clone(),
2493                    },
2494                ));
2495            }
2496            snapshot.version = version;
2497        }
2498        self.owners.source_epoch = executor.store.mutation_epoch();
2499        Ok(())
2500    }
2501
2502    /// Return staged output owners to their captured slots after the caller
2503    /// has finished inspecting them. The caller must discard this graph on error.
2504    pub fn restore_outputs(
2505        &mut self,
2506        executor: &mut Executor,
2507    ) -> std::result::Result<(), ResidentGraphExecutionError> {
2508        for (name, index) in &self.owners.output_indices {
2509            let slot = self
2510                .owners
2511                .relations
2512                .get_mut(*index)
2513                .ok_or_else(|| runtime_error("resident output slot index is invalid"))?;
2514            if slot.is_some() {
2515                return Err(runtime_error("resident output slot was not staged"));
2516            }
2517            let buffer = executor.store.remove(name).ok_or_else(|| {
2518                runtime_error(format!("resident output {name} was not published"))
2519            })?;
2520            *slot = Some(ResidentRelation::from_buffer(buffer));
2521        }
2522        for (name, buffer) in self.source_buffers_to_restore.drain(..) {
2523            executor.store.put_owned(name, buffer);
2524        }
2525        Ok(())
2526    }
2527
2528    #[cfg(all(feature = "resident-graph-tests", test))]
2529    pub(crate) fn invalidate_expected_source_epoch(&mut self) {
2530        self.owners.source_epoch = self.owners.source_epoch.wrapping_add(1);
2531    }
2532
2533    /// Launch the already-instantiated graph exactly once.
2534    pub fn launch<'executor>(
2535        self,
2536        source_guard: &'executor Executor,
2537    ) -> std::result::Result<ResidentGraphInFlight<'executor>, ResidentGraphExecutionError> {
2538        if !self.source_buffers_to_restore.is_empty() {
2539            return Err(runtime_error(
2540                "resident source buffers were not restored before graph launch",
2541            ));
2542        }
2543        validate_resident_source_set_snapshots(
2544            source_guard,
2545            self.owners.source_epoch,
2546            &self.source_set_snapshots,
2547        )?;
2548        let next_recorder = resident_replay_recorder(&self.owners, source_guard)?;
2549        for (_, index) in &self.owners.output_indices {
2550            private_relation(&self.owners.relations, *index)
2551                .map_err(runtime_error)?
2552                .invalidate_observed_row_count();
2553        }
2554        let replay_metadata = ResidentGraphReplayMetadata {
2555            preflight_report: self.preflight_report,
2556            has_device_status_writer: self.has_device_status_writer,
2557            source_set_snapshots: self.source_set_snapshots,
2558            prepare_diagnostic: self.prepare_diagnostic,
2559        };
2560        // Arm before the first API that may enqueue. The recorder protects
2561        // tracked allocations; this owner also protects the graph, host bank,
2562        // provider, exact stream/context and timing/completion handles.
2563        let execution = PendingResidentOwners::new(
2564            ResidentLaunchOwners {
2565                completion: None,
2566                timing_start: None,
2567                timing_end: None,
2568                owners: self.owners,
2569            },
2570            |execution| match execution.completion.as_mut() {
2571                Some(completion) => completion.synchronize().is_ok(),
2572                None => execution.owners.stream.synchronize().is_ok(),
2573            },
2574        );
2575        let (execution, ()) = execution.with_admission(
2576            |execution| reserve_uncaptured_stream(&execution.owners.stream).map_err(runtime_error),
2577            |execution| {
2578                let execution_domain = execution.owners.execution_domain.clone();
2579                let recorder = std::mem::replace(&mut execution.owners.recorder, next_recorder);
2580                // SAFETY: graph preparation registered every runtime allocation touched
2581                // by this graph on `recorder`. The closure queues the timing events and
2582                // graph launch synchronously and uses only the domain-supplied stream.
2583                let enqueued = unsafe {
2584                    execution_domain.enqueue(recorder, |stream| {
2585                        execution.timing_start = Some(
2586                            stream
2587                                .stream()
2588                                .record_event(Some(CUevent_flags::CU_EVENT_DEFAULT))
2589                                .map_err(|error| {
2590                                    runtime_error(format!(
2591                                        "resident timing-start event failed: {error}"
2592                                    ))
2593                                })?,
2594                        );
2595                        execution
2596                            .owners
2597                            .graph
2598                            .launch_in(stream)
2599                            .map_err(runtime_error)?;
2600                        execution.timing_end = Some(
2601                            stream
2602                                .stream()
2603                                .record_event(Some(CUevent_flags::CU_EVENT_DEFAULT))
2604                                .map_err(|error| {
2605                                    runtime_error(format!(
2606                                "resident timing-end event failed after graph launch: {error}"
2607                            ))
2608                                })?,
2609                        );
2610                        execution.owners.runtime.record_conditional_graph_launch(
2611                            replay_metadata.has_device_status_writer,
2612                        );
2613                        Ok::<(), ResidentGraphExecutionError>(())
2614                    })
2615                }
2616                .map_err(runtime_error)?;
2617                enqueued.commit().map_err(runtime_error)?;
2618                execution.completion = Some(
2619                    execution
2620                        .owners
2621                        .runtime
2622                        .record_resident_completion_event(&execution.owners.stream)
2623                        .map_err(runtime_error)?,
2624                );
2625                Ok::<(), ResidentGraphExecutionError>(())
2626            },
2627        )?;
2628        Ok(ResidentGraphInFlight {
2629            execution,
2630            replay_metadata,
2631            _executor: PhantomData,
2632        })
2633    }
2634}
2635
2636fn resident_replay_recorder(
2637    owners: &ResidentRunOwners,
2638    executor: &Executor,
2639) -> std::result::Result<LaunchRecorder, ResidentGraphExecutionError> {
2640    let mut recorder = owners.execution_domain.new_strict_recorder();
2641    for relation in &owners.relations {
2642        let relation = relation
2643            .as_ref()
2644            .ok_or_else(|| runtime_error("resident graph relation was not restored"))?;
2645        ResidentScheduleSlotBinding::permanent(relation.buffer(), 0).record_uses(&mut recorder);
2646    }
2647    for name in &owners.source_binding_names {
2648        let source = executor
2649            .store
2650            .get(name)
2651            .ok_or_else(|| runtime_error(format!("resident source {name} was not restored")))?;
2652        for column in source.columns() {
2653            recorder.read_column(column);
2654        }
2655        recorder.read(source.num_rows_device());
2656    }
2657    ResidentScheduleExternalBindings::new(
2658        owners._filter_scratch.as_ref(),
2659        &owners._set_workspace,
2660        &owners._join_workspace,
2661        &owners._control,
2662        &owners._device_trace,
2663        &owners._schema_winners,
2664        &owners.receipt,
2665    )
2666    .record_uses(&mut recorder);
2667    owners._schedule_program.record_uses(&mut recorder);
2668    Ok(recorder)
2669}
2670
2671impl<'executor> ResidentGraphInFlight<'executor> {
2672    /// Wait for the one real completion event and retain every graph owner.
2673    pub fn synchronize_core(
2674        mut self,
2675    ) -> std::result::Result<ResidentGraphSynchronized<'executor>, ResidentGraphExecutionError>
2676    {
2677        let capture_exclusion =
2678            reserve_uncaptured_stream(&self.execution.owners.stream).map_err(runtime_error)?;
2679        self.execution
2680            .completion
2681            .as_mut()
2682            .expect("resident completion event missing after launch")
2683            .synchronize()
2684            .map_err(runtime_error)?;
2685        drop(capture_exclusion);
2686        let elapsed_ms = self
2687            .execution
2688            .timing_start
2689            .as_ref()
2690            .expect("resident timing-start event missing after launch")
2691            .elapsed_ms(
2692                self.execution
2693                    .timing_end
2694                    .as_ref()
2695                    .expect("resident timing-end event missing after launch"),
2696            )
2697            .map_err(|error| {
2698                runtime_error(format!("resident CUDA-event timing failed: {error}"))
2699            })?;
2700        if !elapsed_ms.is_finite() || elapsed_ms < 0.0 {
2701            return Err(runtime_error("resident CUDA-event timing was not finite"));
2702        }
2703        let device_elapsed_ns = (f64::from(elapsed_ms) * 1_000_000.0)
2704            .round()
2705            .clamp(0.0, u64::MAX as f64) as u64;
2706        Ok(ResidentGraphSynchronized {
2707            device_elapsed_ns,
2708            owners: self.execution.into_inner().owners,
2709            replay_metadata: self.replay_metadata,
2710            _executor: PhantomData,
2711        })
2712    }
2713}
2714
2715impl<'executor> ResidentGraphSynchronized<'executor> {
2716    /// Perform the transaction's only device-to-host observation and decode it.
2717    pub fn observe_final_receipt(
2718        self,
2719    ) -> std::result::Result<ObservedResidentGraphReceipt, ResidentGraphExecutionError> {
2720        let phase_diagnostics = resolve_bool(None, "XLOG_RESIDENT_LATENCY_DIAGNOSTICS", false)
2721            .map_err(runtime_error)?;
2722        let receipt_d2h_started = phase_diagnostics.then(Instant::now);
2723        let encoded_len = self.owners.receipt.len_bytes();
2724        // The earlier graph event precedes this D2H. Keep both the pinned
2725        // destination and device source until this exact stream completes.
2726        let pending =
2727            PendingResidentOwners::new(self.owners, |owners| owners.stream.synchronize().is_ok());
2728        let (pending, bytes) = pending.with_admission(
2729            |owners| reserve_uncaptured_stream(&owners.stream).map_err(runtime_error),
2730            |owners| {
2731                owners
2732                    .provider
2733                    .observe_resident_packed_receipt(
2734                        &owners.receipt,
2735                        &mut owners.pinned_receipt,
2736                        &owners.stream,
2737                    )
2738                    .map_err(runtime_error)
2739            },
2740        )?;
2741        let mut owners = pending.into_inner();
2742        let receipt_d2h_ns = receipt_d2h_started
2743            .map(|started| u64::try_from(started.elapsed().as_nanos()).unwrap_or(u64::MAX))
2744            .unwrap_or(0);
2745        let decode_started = phase_diagnostics.then(Instant::now);
2746        let relation_count_len = owners.output_indices.len();
2747        let schema_winner_count = owners.output_schema_plans.len();
2748        let expected_count_fields = relation_count_len + 4 + schema_winner_count;
2749        if owners.receipt.relation_count_len() as usize != relation_count_len
2750            || owners.receipt.device_trace_field_count() != 4
2751            || owners.receipt.schema_winner_count() as usize != schema_winner_count
2752            || owners.receipt.total_count_field_len() as usize != expected_count_fields
2753            || bytes.len() != encoded_len
2754            || encoded_len != 44 + 4 * expected_count_fields
2755        {
2756            return Err(runtime_error(format!(
2757                "malformed resident receipt length: got {}, expected {}",
2758                bytes.len(),
2759                44 + 4 * expected_count_fields
2760            )));
2761        }
2762        let field_u32 = |offset| {
2763            read_receipt_u32(&bytes, offset)
2764                .ok_or_else(|| runtime_error("truncated resident receipt u32 field"))
2765        };
2766        let field_u64 = |offset| {
2767            read_receipt_u64(&bytes, offset)
2768                .ok_or_else(|| runtime_error("truncated resident receipt u64 field"))
2769        };
2770        let code = field_u32(0)?;
2771        let op_id = field_u32(4)?;
2772        let resource_code = field_u32(8)?;
2773        let iterations = field_u32(12)?;
2774        let limit = field_u32(16)?;
2775        let reserved = field_u32(20)?;
2776        let required = field_u64(24)?;
2777        let capacity = field_u64(32)?;
2778        let changed = field_u32(40)?;
2779        if reserved != 0 || changed > 1 {
2780            return Err(runtime_error(
2781                "malformed resident terminal status reserved/changed field",
2782            ));
2783        }
2784        let terminal = match code {
2785            value if value == ResidentTerminalCode::Success as u32 => {
2786                if resource_code != ResidentResourceCode::None as u32
2787                    || limit != 0
2788                    || required != 0
2789                    || capacity != 0
2790                {
2791                    return Err(runtime_error("malformed resident success payload"));
2792                }
2793                Ok(())
2794            }
2795            value if value == ResidentTerminalCode::IterationLimit as u32 => {
2796                if resource_code != ResidentResourceCode::None as u32
2797                    || required != 0
2798                    || capacity != 0
2799                    || iterations > limit
2800                {
2801                    return Err(runtime_error("malformed resident iteration-limit payload"));
2802                }
2803                Err(ResidentGraphExecutionError::IterationLimit {
2804                    limit,
2805                    completed: iterations,
2806                })
2807            }
2808            value if value == ResidentTerminalCode::CapacityOverflow as u32 => {
2809                if resource_code != ResidentResourceCode::OutputRows as u32 || required <= capacity
2810                {
2811                    return Err(runtime_error(
2812                        "malformed resident capacity-overflow payload",
2813                    ));
2814                }
2815                Err(ResidentGraphExecutionError::CapacityOverflow {
2816                    op_id,
2817                    required,
2818                    capacity,
2819                })
2820            }
2821            value if value == ResidentTerminalCode::ResourceExhausted as u32 => {
2822                let resource = match resource_code {
2823                    value
2824                        if value == ResidentResourceCode::SetHashSlots as u32
2825                            || value == ResidentResourceCode::JoinBuckets as u32
2826                            || value == ResidentResourceCode::JoinChains as u32 =>
2827                    {
2828                        "workspace_slots"
2829                    }
2830                    value if value == ResidentResourceCode::InputRows as u32 => "input_rows",
2831                    value if value == ResidentResourceCode::OutputRows as u32 => "output_rows",
2832                    _ => return Err(runtime_error("unknown resident resource code")),
2833                };
2834                if required <= capacity {
2835                    return Err(runtime_error("malformed resident resource payload"));
2836                }
2837                Err(ResidentGraphExecutionError::ResourceExhausted {
2838                    op_id,
2839                    resource,
2840                    required,
2841                    capacity,
2842                })
2843            }
2844            _ => {
2845                return Err(runtime_error(format!(
2846                    "unknown resident terminal code {code}"
2847                )))
2848            }
2849        };
2850
2851        let mut counts = Vec::with_capacity(owners.output_indices.len());
2852        for index in 0..owners.output_indices.len() {
2853            counts.push(field_u32(44 + index * 4)?);
2854        }
2855        let device_scan_invocations = u64::from(field_u32(44 + relation_count_len * 4)?);
2856        let device_filter_invocations = u64::from(field_u32(48 + relation_count_len * 4)?);
2857        let semantic_scan_invocations = u64::from(field_u32(52 + relation_count_len * 4)?);
2858        let semantic_filter_invocations = u64::from(field_u32(56 + relation_count_len * 4)?);
2859        let schema_winner_offset = 60 + relation_count_len * 4;
2860        let mut schema_winner_ids = Vec::with_capacity(schema_winner_count);
2861        for index in 0..schema_winner_count {
2862            schema_winner_ids.push(field_u32(schema_winner_offset + index * 4)?);
2863        }
2864        let mut outputs = Vec::new();
2865        if terminal.is_ok() {
2866            let selected_schemas =
2867                resident_resolve_output_schemas(&owners.output_schema_plans, &schema_winner_ids)
2868                    .map_err(runtime_error)?;
2869            let mut cache_entries = Vec::with_capacity(counts.len());
2870            for ((_, relation_index), count) in
2871                owners.output_indices.iter().zip(counts.iter().copied())
2872            {
2873                let relation =
2874                    private_relation(&owners.relations, *relation_index).map_err(runtime_error)?;
2875                if count > relation.capacity() {
2876                    return Err(runtime_error(format!(
2877                        "resident receipt count {count} exceeds output capacity {}",
2878                        relation.capacity()
2879                    )));
2880                }
2881                cache_entries.push((relation.buffer(), count));
2882            }
2883            owners
2884                .provider
2885                .finalize_resident_logical_counts(&cache_entries)
2886                .map_err(runtime_error)?;
2887            for ((name, relation_index), schema) in
2888                owners.output_indices.iter().zip(selected_schemas)
2889            {
2890                let relation = owners
2891                    .relations
2892                    .get_mut(*relation_index)
2893                    .and_then(Option::take)
2894                    .ok_or_else(|| runtime_error("resident output relation owner missing"))?;
2895                outputs.push(StagedResidentOutput {
2896                    name: name.clone(),
2897                    buffer: relation
2898                        .into_buffer_with_observed_schema(schema)
2899                        .map_err(runtime_error)?,
2900                });
2901            }
2902        }
2903
2904        let decode_schema_staging_ns = decode_started
2905            .map(|started| u64::try_from(started.elapsed().as_nanos()).unwrap_or(u64::MAX))
2906            .unwrap_or(0);
2907        let source_epoch = owners.source_epoch;
2908        let relation_registration = owners.relation_registration.clone();
2909        let transaction_identity = Arc::clone(&owners.transaction_identity);
2910        let provider = Arc::clone(&owners.provider);
2911        let prepared_for_reuse = terminal.is_ok().then_some(PreparedResidentGraph {
2912            owners,
2913            preflight_report: self.replay_metadata.preflight_report,
2914            has_device_status_writer: self.replay_metadata.has_device_status_writer,
2915            source_set_snapshots: self.replay_metadata.source_set_snapshots,
2916            source_buffers_to_restore: Vec::new(),
2917            prepare_diagnostic: self.replay_metadata.prepare_diagnostic,
2918        });
2919        Ok(ObservedResidentGraphReceipt {
2920            encoded_len,
2921            device_elapsed_ns: self.device_elapsed_ns,
2922            device_scan_invocations,
2923            device_filter_invocations,
2924            semantic_scan_invocations,
2925            semantic_filter_invocations,
2926            iterations,
2927            terminal,
2928            outputs,
2929            source_epoch,
2930            relation_registration,
2931            transaction_identity,
2932            provider,
2933            phase_timings: phase_diagnostics.then_some(ResidentFinalObservationPhaseTimings {
2934                receipt_d2h_ns,
2935                decode_schema_staging_ns,
2936            }),
2937            prepared_for_reuse,
2938        })
2939    }
2940}
2941
2942impl ObservedResidentGraphReceipt {
2943    /// Exact bytes transferred in the sole final observation.
2944    pub fn encoded_len(&self) -> usize {
2945        self.encoded_len
2946    }
2947
2948    /// Return opt-in final-observation phase timings when diagnostics were enabled.
2949    #[doc(hidden)]
2950    pub fn phase_timings(&self) -> Option<ResidentFinalObservationPhaseTimings> {
2951        self.phase_timings
2952    }
2953
2954    /// Number of relation registrations validated by commit.
2955    #[doc(hidden)]
2956    pub fn relation_registration_count(&self) -> usize {
2957        self.relation_registration.len()
2958    }
2959
2960    /// Device execution duration measured between CUDA events around the graph launch.
2961    pub fn device_elapsed_ns(&self) -> u64 {
2962        self.device_elapsed_ns
2963    }
2964
2965    /// Actual scan invocations counted by device kernels, including WHILE replays.
2966    pub fn device_scan_invocations(&self) -> u64 {
2967        self.device_scan_invocations
2968    }
2969
2970    /// Actual filter invocations counted by device kernels, including WHILE replays.
2971    pub fn device_filter_invocations(&self) -> u64 {
2972        self.device_filter_invocations
2973    }
2974
2975    /// Legacy-semantic scan count, excluding recursive variants whose selected delta was empty.
2976    pub fn semantic_scan_invocations(&self) -> u64 {
2977        self.semantic_scan_invocations
2978    }
2979
2980    /// Legacy-semantic filter count, excluding recursive variants whose selected delta was empty.
2981    pub fn semantic_filter_invocations(&self) -> u64 {
2982        self.semantic_filter_invocations
2983    }
2984
2985    /// Number of staged relation heads published by a successful commit.
2986    pub fn staged_output_count(&self) -> u64 {
2987        self.outputs.len() as u64
2988    }
2989
2990    /// Aggregate recursive iterations reported by the device.
2991    pub fn iterations(&self) -> u32 {
2992        self.iterations
2993    }
2994
2995    /// Atomically publish staged outputs after optimistic validation.
2996    pub fn commit(
2997        self,
2998        executor: &mut Executor,
2999    ) -> std::result::Result<(), ResidentGraphExecutionError> {
3000        self.commit_inner(executor).map(|_| ())
3001    }
3002
3003    /// Publish outputs while retaining the graph for the next invocation.
3004    pub fn commit_for_reuse(
3005        self,
3006        executor: &mut Executor,
3007    ) -> std::result::Result<PreparedResidentGraph, ResidentGraphExecutionError> {
3008        self.commit_inner(executor)?.ok_or_else(|| {
3009            runtime_error("successful resident receipt did not retain its graph owner")
3010        })
3011    }
3012
3013    fn commit_inner(
3014        mut self,
3015        executor: &mut Executor,
3016    ) -> std::result::Result<Option<PreparedResidentGraph>, ResidentGraphExecutionError> {
3017        self.terminal?;
3018        if !Arc::ptr_eq(&self.transaction_identity, &executor.transaction_identity)
3019            || !Arc::ptr_eq(&self.provider, &executor.provider)
3020            || self.source_epoch != executor.store.mutation_epoch()
3021        {
3022            return Err(ResidentGraphExecutionError::Runtime(
3023                "resident transaction became stale before commit".into(),
3024            ));
3025        }
3026        let mut current_registration = executor
3027            .rel_names
3028            .iter()
3029            .map(|(rel, name)| (*rel, name.clone()))
3030            .collect::<Vec<_>>();
3031        current_registration.sort_by_key(|(rel, name)| (rel.0, name.clone()));
3032        if current_registration != self.relation_registration {
3033            return Err(ResidentGraphExecutionError::Runtime(
3034                "resident relation registration changed before commit".into(),
3035            ));
3036        }
3037        let additional = self
3038            .outputs
3039            .iter()
3040            .filter(|output| !executor.store.contains(&output.name))
3041            .count();
3042        executor
3043            .store
3044            .try_reserve_relations(additional)
3045            .map_err(runtime_error)?;
3046        executor.common_subexpression_cache.clear();
3047        for output in self.outputs.drain(..) {
3048            if let Some(&rel) = executor.name_to_rel.get(&output.name) {
3049                executor.join_index_cache.invalidate_rel(rel);
3050            }
3051            if let Some(prepared) = self.prepared_for_reuse.as_mut() {
3052                if prepared
3053                    .source_set_snapshots
3054                    .iter()
3055                    .any(|source| source.name == output.name)
3056                {
3057                    let source = executor.store.remove(&output.name).ok_or_else(|| {
3058                        runtime_error(format!(
3059                            "resident source {} disappeared before output publication",
3060                            output.name
3061                        ))
3062                    })?;
3063                    prepared
3064                        .source_buffers_to_restore
3065                        .push((output.name.clone(), source));
3066                }
3067            }
3068            executor.store.put_owned(output.name, output.buffer);
3069        }
3070        Ok(self.prepared_for_reuse.take())
3071    }
3072}
3073
3074fn runtime_error(error: impl std::fmt::Display) -> ResidentGraphExecutionError {
3075    ResidentGraphExecutionError::Runtime(error.to_string())
3076}
3077
3078impl ResidentBuild<'_> {
3079    fn source_reference(&mut self, name: &str) -> Result<ResidentBufferRef> {
3080        if let Some(index) = self.source_aliases.get(name) {
3081            return Ok(ResidentBufferRef::Private(*index));
3082        }
3083        let (row_count, count_tracked, columns_tracked, schema) = {
3084            let source = self.executor.store.get(name).ok_or_else(|| {
3085                XlogError::Execution(format!(
3086                    "resident source {name} disappeared during planning"
3087                ))
3088            })?;
3089            (
3090                resident_source_logical_count(source.cached_row_count())?,
3091                source.num_rows_device().runtime_block().is_some(),
3092                source
3093                    .columns()
3094                    .iter()
3095                    .all(|column| column.runtime_block().is_some()),
3096                source.schema().clone(),
3097            )
3098        };
3099        match resident_source_binding_route(row_count, count_tracked, columns_tracked)? {
3100            ResidentSourceBindingRoute::Direct => {
3101                self.source_names.insert(name.to_owned());
3102                Ok(ResidentBufferRef::Source(name.to_owned()))
3103            }
3104            ResidentSourceBindingRoute::NormalizeEmpty => {
3105                let index = self.allocate_permanent_relation(schema, 0)?;
3106                self.source_aliases.insert(name.to_owned(), index);
3107                Ok(ResidentBufferRef::Private(index))
3108            }
3109        }
3110    }
3111
3112    fn private(&self, index: usize) -> &ResidentLogicalRelation {
3113        &self.relations[index]
3114    }
3115
3116    fn schema(&self, reference: &ResidentBufferRef) -> Result<&Schema> {
3117        match reference {
3118            ResidentBufferRef::Source(name) => self
3119                .executor
3120                .store
3121                .get(name)
3122                .map(CudaBuffer::schema)
3123                .ok_or_else(|| {
3124                    XlogError::Execution(format!("missing resident source relation {name}"))
3125                }),
3126            ResidentBufferRef::Private(index) => Ok(&self.private(*index).schema),
3127        }
3128    }
3129
3130    fn allocate_relation(
3131        &mut self,
3132        schema: Schema,
3133        initial_count: u32,
3134        permanent: bool,
3135    ) -> Result<usize> {
3136        if initial_count > 1 {
3137            return Err(XlogError::Execution(
3138                "resident logical initial count must be zero or one".into(),
3139            ));
3140        }
3141        let index = self.relations.len();
3142        self.relations.push(ResidentLogicalRelation {
3143            schema,
3144            initial_count,
3145            permanent,
3146        });
3147        Ok(index)
3148    }
3149
3150    fn allocate_permanent_relation(&mut self, schema: Schema, initial_count: u32) -> Result<usize> {
3151        self.allocate_relation(schema, initial_count, true)
3152    }
3153
3154    fn allocate_scratch_relation(&mut self, schema: Schema) -> Result<usize> {
3155        self.allocate_relation(schema, 0, false)
3156    }
3157
3158    fn allocation_manifest(
3159        &self,
3160        initial_ops: &[ResidentRecordedOp],
3161        phases: &[ResidentCapturePhase],
3162    ) -> Result<ResidentAllocationManifest> {
3163        let mut definitions = vec![None; self.relations.len()];
3164        let mut last_uses = vec![None; self.relations.len()];
3165        let mut ordinal = 0usize;
3166        let mut phase_ranges = Vec::with_capacity(phases.len() + 1);
3167        phase_ranges.push(resident_record_lifetimes(
3168            initial_ops,
3169            &self.relations,
3170            &mut definitions,
3171            &mut last_uses,
3172            &mut ordinal,
3173        )?);
3174        for phase in phases {
3175            let ops = match phase {
3176                ResidentCapturePhase::Segment { ops, .. }
3177                | ResidentCapturePhase::ConditionalWhile { ops, .. } => ops,
3178            };
3179            phase_ranges.push(resident_record_lifetimes(
3180                ops,
3181                &self.relations,
3182                &mut definitions,
3183                &mut last_uses,
3184                &mut ordinal,
3185            )?);
3186        }
3187
3188        let mut slots = Vec::<ResidentPhysicalSlotPlan>::new();
3189        let mut logical_to_slot = vec![
3190            ResidentSlotAssignment {
3191                slot: usize::MAX,
3192                generation: 0,
3193            };
3194            self.relations.len()
3195        ];
3196        for (logical, relation) in self.relations.iter().enumerate() {
3197            if relation.permanent {
3198                let slot = slots.len();
3199                slots.push(ResidentPhysicalSlotPlan {
3200                    schema: relation.schema.clone(),
3201                    initial_count: relation.initial_count,
3202                    permanent: true,
3203                });
3204                logical_to_slot[logical] = ResidentSlotAssignment {
3205                    slot,
3206                    generation: 0,
3207                };
3208            }
3209        }
3210
3211        let mut scratch_relations = self
3212            .relations
3213            .iter()
3214            .enumerate()
3215            .filter(|(_, relation)| !relation.permanent)
3216            .map(|(logical, relation)| {
3217                let definition = definitions[logical].ok_or_else(|| {
3218                    XlogError::Execution(format!(
3219                        "resident scratch relation {logical} is never defined"
3220                    ))
3221                })?;
3222                let last_use = last_uses[logical].unwrap_or(definition);
3223                if !phase_ranges
3224                    .iter()
3225                    .any(|(start, end)| *start <= definition && last_use < *end)
3226                {
3227                    return Err(XlogError::Execution(format!(
3228                        "resident scratch relation {logical} crosses a capture phase boundary"
3229                    )));
3230                }
3231                Ok((
3232                    logical,
3233                    definition,
3234                    last_use,
3235                    resident_schema_layout(&relation.schema),
3236                ))
3237            })
3238            .collect::<Result<Vec<_>>>()?;
3239        scratch_relations.sort_by_key(|(logical, definition, _, _)| (*definition, *logical));
3240
3241        let mut scratch_slots = Vec::<ResidentScratchSlotState>::new();
3242        for (logical, definition, last_use, layout) in scratch_relations {
3243            let reusable = scratch_slots
3244                .iter_mut()
3245                .filter(|slot| slot.layout == layout && slot.last_use < definition)
3246                .min_by_key(|slot| slot.last_use);
3247            let assignment = if let Some(slot) = reusable {
3248                slot.generation = slot.generation.checked_add(1).ok_or_else(|| {
3249                    XlogError::Execution("resident scratch generation overflow".into())
3250                })?;
3251                slot.last_use = last_use;
3252                ResidentSlotAssignment {
3253                    slot: slot.slot,
3254                    generation: slot.generation,
3255                }
3256            } else {
3257                let slot_index = slots.len();
3258                slots.push(ResidentPhysicalSlotPlan {
3259                    schema: self.relations[logical].schema.clone(),
3260                    initial_count: 0,
3261                    permanent: false,
3262                });
3263                scratch_slots.push(ResidentScratchSlotState {
3264                    slot: slot_index,
3265                    layout,
3266                    last_use,
3267                    generation: 0,
3268                });
3269                ResidentSlotAssignment {
3270                    slot: slot_index,
3271                    generation: 0,
3272                }
3273            };
3274            logical_to_slot[logical] = assignment;
3275        }
3276        if logical_to_slot
3277            .iter()
3278            .any(|assignment| assignment.slot == usize::MAX)
3279        {
3280            return Err(XlogError::Execution(
3281                "resident logical relation has no physical slot assignment".into(),
3282            ));
3283        }
3284        resident_validate_slot_assignments(
3285            &self.relations,
3286            &definitions,
3287            &last_uses,
3288            &slots,
3289            &logical_to_slot,
3290        )?;
3291
3292        let capacity = u64::from(self.capacity);
3293        let mut relation_bytes = 0u64;
3294        let mut max_row_bytes = 1u64;
3295        for slot in &slots {
3296            max_row_bytes = max_row_bytes.max(slot.schema.row_size_bytes() as u64);
3297            resident_checked_add(
3298                &mut relation_bytes,
3299                resident_relation_device_bytes(&slot.schema, capacity)?,
3300                "relation",
3301            )?;
3302        }
3303        let filter_scratch_bytes = if self.filter_workspaces.is_empty() {
3304            0
3305        } else {
3306            resident_filter_scratch_device_bytes(capacity)?
3307        };
3308        let set_candidate_capacity = capacity.checked_mul(2).ok_or_else(|| {
3309            XlogError::Execution("resident set candidate capacity overflow".into())
3310        })?;
3311        let mut fixed_workspace_bytes = 0u64;
3312        for bytes in [
3313            resident_set_workspace_device_bytes(set_candidate_capacity)?,
3314            resident_join_workspace_device_bytes(capacity)?,
3315            resident_control_device_bytes(),
3316            resident_device_trace_bytes(),
3317            resident_schema_winners_device_bytes(self.heads.len())?,
3318            resident_packed_receipt_with_schema_winners_device_bytes(self.heads.len())?,
3319        ] {
3320            resident_checked_add(&mut fixed_workspace_bytes, bytes, "fixed workspace")?;
3321        }
3322        let mut required_bytes = 0u64;
3323        for bytes in [relation_bytes, filter_scratch_bytes, fixed_workspace_bytes] {
3324            resident_checked_add(&mut required_bytes, bytes, "manifest")?;
3325        }
3326
3327        let permanent_slots = u32::try_from(slots.iter().filter(|slot| slot.permanent).count())
3328            .map_err(|_| {
3329                XlogError::Execution("resident permanent slot count exceeds u32".into())
3330            })?;
3331        Ok(ResidentAllocationManifest {
3332            slots,
3333            logical_to_slot,
3334            required_bytes,
3335            relation_bytes,
3336            filter_scratch_bytes,
3337            schedule_metadata_bytes: 0,
3338            fixed_workspace_bytes,
3339            logical_relation_values: self.relations.len(),
3340            permanent_slots,
3341            scratch_slots: u32::try_from(scratch_slots.len()).map_err(|_| {
3342                XlogError::Execution("resident scratch slot count exceeds u32".into())
3343            })?,
3344            filter_scratch_allocations: u32::from(!self.filter_workspaces.is_empty()),
3345            max_row_bytes,
3346        })
3347    }
3348
3349    fn materialize(
3350        &self,
3351        manifest: &ResidentAllocationManifest,
3352        reservation: &mut GpuMemoryReservation,
3353        mut diagnostics: Option<&mut ResidentPrepareDiagnostics>,
3354    ) -> Result<ResidentPhysicalBuild> {
3355        let capacity = u64::from(self.capacity);
3356        let allocation_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
3357        let allocation_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
3358        let relations = self
3359            .executor
3360            .provider
3361            .prepare_resident_relations_in_reservation(
3362                manifest.slots.iter().map(|slot| &slot.schema),
3363                capacity,
3364                reservation,
3365            )?
3366            .into_iter()
3367            .map(Some)
3368            .collect();
3369        if let Some(diagnostics) = diagnostics.as_deref_mut() {
3370            let allocation_ns = resident_prepare_elapsed_ns(
3371                allocation_started.expect("diagnostic timer exists when enabled"),
3372            );
3373            let allocation_bytes = reservation.used_bytes().saturating_sub(
3374                allocation_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
3375            );
3376            diagnostics.relation_slot_allocation_ns = diagnostics
3377                .relation_slot_allocation_ns
3378                .saturating_add(allocation_ns);
3379            diagnostics.relation_slot_allocation_ns_max = diagnostics
3380                .relation_slot_allocation_ns_max
3381                .max(allocation_ns);
3382            diagnostics.relation_slot_allocation_bytes = diagnostics
3383                .relation_slot_allocation_bytes
3384                .saturating_add(allocation_bytes);
3385            diagnostics.relation_device_allocation_calls = diagnostics
3386                .relation_device_allocation_calls
3387                .saturating_add(u64::from(!manifest.slots.is_empty()));
3388        }
3389        let filter_scratch = if self.filter_workspaces.is_empty() {
3390            None
3391        } else {
3392            let bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
3393            let started = diagnostics.as_ref().map(|_| std::time::Instant::now());
3394            let scratch = self
3395                .executor
3396                .provider
3397                .prepare_resident_filter_scratch_in_reservation(capacity, reservation)?;
3398            if let Some(diagnostics) = diagnostics.as_deref_mut() {
3399                let elapsed_ns = resident_prepare_elapsed_ns(
3400                    started.expect("diagnostic timer exists when enabled"),
3401                );
3402                let reserved_bytes = reservation.used_bytes().saturating_sub(
3403                    bytes_before.expect("diagnostic byte snapshot exists when enabled"),
3404                );
3405                diagnostics.workspace_provider_calls =
3406                    diagnostics.workspace_provider_calls.saturating_add(1);
3407                diagnostics.filter_scratch_allocation_ns = elapsed_ns;
3408                diagnostics.filter_scratch_allocation_bytes = reserved_bytes;
3409            }
3410            Some(scratch)
3411        };
3412        let set_candidate_capacity = capacity.checked_mul(2).ok_or_else(|| {
3413            XlogError::Execution("resident set candidate capacity overflow".into())
3414        })?;
3415        let set_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
3416        let set_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
3417        let set_workspace = self
3418            .executor
3419            .provider
3420            .prepare_resident_set_workspace_in_reservation(set_candidate_capacity, reservation)?;
3421        if let Some(diagnostics) = diagnostics.as_mut() {
3422            let set_ns = resident_prepare_elapsed_ns(
3423                set_started.expect("diagnostic timer exists when enabled"),
3424            );
3425            let set_reserved_bytes = reservation.used_bytes().saturating_sub(
3426                set_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
3427            );
3428            diagnostics.workspace_provider_calls =
3429                diagnostics.workspace_provider_calls.saturating_add(1);
3430            diagnostics.set_workspace_allocation_ns = set_ns;
3431            diagnostics.set_workspace_allocation_bytes = set_reserved_bytes;
3432        }
3433        let join_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
3434        let join_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
3435        let join_workspace = self
3436            .executor
3437            .provider
3438            .prepare_resident_join_workspace_in_reservation(capacity, reservation)?;
3439        if let Some(diagnostics) = diagnostics.as_deref_mut() {
3440            let join_ns = resident_prepare_elapsed_ns(
3441                join_started.expect("diagnostic timer exists when enabled"),
3442            );
3443            let join_reserved_bytes = reservation.used_bytes().saturating_sub(
3444                join_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
3445            );
3446            diagnostics.workspace_provider_calls =
3447                diagnostics.workspace_provider_calls.saturating_add(1);
3448            diagnostics.join_workspace_allocation_ns = join_ns;
3449            diagnostics.join_workspace_allocation_bytes = join_reserved_bytes;
3450        }
3451        let control_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
3452        let control_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
3453        let control = self
3454            .executor
3455            .provider
3456            .prepare_resident_convergence_control_in_reservation(reservation)?;
3457        if let Some(diagnostics) = diagnostics {
3458            let control_ns = resident_prepare_elapsed_ns(
3459                control_started.expect("diagnostic timer exists when enabled"),
3460            );
3461            let control_reserved_bytes = reservation.used_bytes().saturating_sub(
3462                control_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
3463            );
3464            diagnostics.workspace_provider_calls =
3465                diagnostics.workspace_provider_calls.saturating_add(1);
3466            diagnostics.control_allocation_ns = control_ns;
3467            diagnostics.control_allocation_bytes = control_reserved_bytes;
3468        }
3469        Ok(ResidentPhysicalBuild {
3470            relations,
3471            filter_scratch,
3472            set_workspace,
3473            join_workspace,
3474            control,
3475        })
3476    }
3477
3478    fn next_op_id(&mut self) -> Result<u32> {
3479        let op_id = self.next_op_id;
3480        self.next_op_id = self
3481            .next_op_id
3482            .checked_add(1)
3483            .ok_or_else(|| XlogError::Execution("resident physical op id overflow".into()))?;
3484        Ok(op_id)
3485    }
3486
3487    fn push_physical_op(
3488        &mut self,
3489        ops: &mut Vec<ResidentRecordedOp>,
3490        op: ResidentRecordedOp,
3491        op_id: u32,
3492    ) {
3493        ops.push(op);
3494        if !self.injection_recorded
3495            && self
3496                .injection
3497                .as_ref()
3498                .is_some_and(|injection| injection.after_op == op_id)
3499        {
3500            let status =
3501                terminal_status_for_injection(&self.injection.as_ref().expect("checked").status);
3502            ops.push(ResidentRecordedOp::TestStatus(status));
3503            self.injection_recorded = true;
3504        }
3505    }
3506
3507    fn scan_reference(
3508        &mut self,
3509        rel: RelId,
3510        override_scan: Option<(RelId, usize, usize)>,
3511        occurrences: &mut HashMap<RelId, usize>,
3512    ) -> Result<ResidentBufferRef> {
3513        let occurrence = occurrences.entry(rel).or_insert(0);
3514        let current = *occurrence;
3515        *occurrence += 1;
3516        if let Some((target, target_occurrence, delta)) = override_scan {
3517            if rel == target && current == target_occurrence {
3518                return Ok(ResidentBufferRef::Private(delta));
3519            }
3520        }
3521        let name = self
3522            .executor
3523            .rel_names
3524            .get(&rel)
3525            .ok_or_else(|| XlogError::Execution(format!("unknown resident relation id {rel:?}")))?
3526            .clone();
3527        if let Some(index) = self.heads.get(&name) {
3528            Ok(ResidentBufferRef::Private(*index))
3529        } else {
3530            self.source_reference(&name)
3531        }
3532    }
3533
3534    fn plan_node(
3535        &mut self,
3536        node: &RirNode,
3537        override_scan: Option<(RelId, usize, usize)>,
3538        occurrences: &mut HashMap<RelId, usize>,
3539        ops: &mut Vec<ResidentRecordedOp>,
3540    ) -> Result<ResidentBufferRef> {
3541        match node {
3542            RirNode::Unit => {
3543                let op_id = self.next_op_id()?;
3544                let (reference, op) = resident_new_phase_unit(&mut self.relations, op_id)?;
3545                self.push_physical_op(ops, op, op_id);
3546                Ok(reference)
3547            }
3548            RirNode::Scan { rel } => {
3549                let reference = self.scan_reference(*rel, override_scan, occurrences)?;
3550                let op_id = self.next_op_id()?;
3551                Ok(resident_record_scan_leaf(
3552                    reference,
3553                    op_id,
3554                    resident_semantic_trace_guard(override_scan),
3555                    ops,
3556                    |ops, op, op_id| self.push_physical_op(ops, op, op_id),
3557                ))
3558            }
3559            RirNode::Filter { input, predicate } => {
3560                let input_ref = self.plan_node(input, override_scan, occurrences, ops)?;
3561                let input_schema = self.schema(&input_ref)?.clone();
3562                let compact_comparisons =
3563                    resident_compact_filter_descriptors(predicate, &input_schema)?;
3564                let workspace = ResidentFilterPlan {
3565                    compact_comparisons,
3566                };
3567                let workspace_index = self.filter_workspaces.len();
3568                self.filter_workspaces.push(workspace);
3569                let schema = self
3570                    .certificate
3571                    .node_schema(node)
3572                    .ok_or_else(|| XlogError::Execution("resident filter schema missing".into()))?;
3573                let output = self.allocate_scratch_relation(schema)?;
3574                let op_id = self.next_op_id()?;
3575                self.push_physical_op(
3576                    ops,
3577                    ResidentRecordedOp::Filter {
3578                        input: input_ref,
3579                        output,
3580                        workspace: workspace_index,
3581                        op_id,
3582                    },
3583                    op_id,
3584                );
3585                ops.push(ResidentRecordedOp::TraceDelta {
3586                    scan_delta: 0,
3587                    filter_delta: 1,
3588                    semantic_guard: resident_semantic_trace_guard(override_scan),
3589                });
3590                Ok(ResidentBufferRef::Private(output))
3591            }
3592            RirNode::Project { input, columns } => {
3593                let input_ref = self.plan_node(input, override_scan, occurrences, ops)?;
3594                let input_schema = self.schema(&input_ref)?.clone();
3595                let certified_schema = self.certificate.node_schema(node).ok_or_else(|| {
3596                    XlogError::Execution("resident project schema missing".into())
3597                })?;
3598                let schema = self.executor.project_schema(&input_schema, columns)?;
3599                if schema.arity() != certified_schema.arity()
3600                    || (0..schema.arity()).any(|column| {
3601                        schema.column_type(column) != certified_schema.column_type(column)
3602                    })
3603                {
3604                    return Err(XlogError::Execution(
3605                        "resident project physical schema differs from certificate".into(),
3606                    ));
3607                }
3608                let compact_expressions =
3609                    resident_compact_project_descriptors(columns, &input_schema, &schema)?;
3610                let workspace = ResidentProjectPlan {
3611                    compact_expressions,
3612                };
3613                let workspace_index = self.project_workspaces.len();
3614                self.project_workspaces.push(workspace);
3615                let output = self.allocate_scratch_relation(schema)?;
3616                let op_id = self.next_op_id()?;
3617                self.push_physical_op(
3618                    ops,
3619                    ResidentRecordedOp::Project {
3620                        input: input_ref,
3621                        output,
3622                        workspace: workspace_index,
3623                        op_id,
3624                    },
3625                    op_id,
3626                );
3627                Ok(ResidentBufferRef::Private(output))
3628            }
3629            RirNode::Join {
3630                left,
3631                right,
3632                left_keys,
3633                right_keys,
3634                join_type,
3635            } => {
3636                let left_ref = self.plan_node(left, override_scan, occurrences, ops)?;
3637                let right_ref = self.plan_node(right, override_scan, occurrences, ops)?;
3638                let certified_schema = self
3639                    .certificate
3640                    .node_schema(node)
3641                    .ok_or_else(|| XlogError::Execution("resident join schema missing".into()))?;
3642                let kind = match join_type {
3643                    JoinType::Inner => ResidentJoinKind::Inner,
3644                    JoinType::Semi => ResidentJoinKind::Semi,
3645                    _ => return Err(XlogError::Execution("uncertified resident join".into())),
3646                };
3647                // The provider preserves operand column names. The route
3648                // certificate canonicalizes those names and binds the same
3649                // physical column types, so construct the provider's exact
3650                // schema here and normalize names at the next projection.
3651                let left_schema = self.schema(&left_ref)?.clone();
3652                let right_schema = self.schema(&right_ref)?.clone();
3653                let schema = match kind {
3654                    ResidentJoinKind::Semi => left_schema,
3655                    ResidentJoinKind::Inner => {
3656                        let mut columns = left_schema.columns;
3657                        columns.extend(right_schema.columns);
3658                        Schema::new(columns)
3659                    }
3660                };
3661                if schema.arity() != certified_schema.arity()
3662                    || (0..schema.arity()).any(|column| {
3663                        schema.column_type(column) != certified_schema.column_type(column)
3664                    })
3665                {
3666                    return Err(XlogError::Execution(
3667                        "resident join physical schema differs from certificate".into(),
3668                    ));
3669                }
3670                let output = self.allocate_scratch_relation(schema).map_err(|error| {
3671                    XlogError::Execution(format!(
3672                        "resident join intermediate arity {} at {node:?} could not be allocated: {error}",
3673                        certified_schema.arity()
3674                    ))
3675                })?;
3676                let op_id = self.next_op_id()?;
3677                self.push_physical_op(
3678                    ops,
3679                    ResidentRecordedOp::Join {
3680                        kind,
3681                        left: left_ref,
3682                        left_key: left_keys[0],
3683                        right: right_ref,
3684                        right_key: right_keys[0],
3685                        output,
3686                        op_id,
3687                    },
3688                    op_id,
3689                );
3690                Ok(ResidentBufferRef::Private(output))
3691            }
3692            RirNode::ChainJoin { fallback, .. } | RirNode::MultiWayJoin { fallback, .. } => {
3693                self.plan_node(fallback, override_scan, occurrences, ops)
3694            }
3695            RirNode::Distinct { input, .. } => {
3696                let input_ref = self.plan_node(input, override_scan, occurrences, ops)?;
3697                self.plan_dedup(input_ref, node, ops)
3698            }
3699            RirNode::Diff { left, right } => {
3700                let left_ref = self.plan_node(left, override_scan, occurrences, ops)?;
3701                let right_ref = self.plan_node(right, override_scan, occurrences, ops)?;
3702                self.plan_diff(left_ref, right_ref, node, ops)
3703            }
3704            RirNode::Union { inputs } => {
3705                let fold_mode = resident_union_fold_mode(inputs.len())?;
3706                let mut iter = inputs.iter();
3707                let first = iter
3708                    .next()
3709                    .expect("union fold mode rejected the empty case");
3710                let mut result = self.plan_node(first, override_scan, occurrences, ops)?;
3711                if fold_mode == ResidentUnionFoldMode::SelfUnion {
3712                    return self.plan_union(result.clone(), result, node, ops);
3713                }
3714                for input in iter {
3715                    let right = self.plan_node(input, override_scan, occurrences, ops)?;
3716                    result = self.plan_union(result, right, node, ops)?;
3717                }
3718                Ok(result)
3719            }
3720            RirNode::Fixpoint { .. }
3721            | RirNode::GroupBy { .. }
3722            | RirNode::TensorMaskedJoin { .. } => Err(XlogError::Execution(
3723                "uncertified node reached resident planner".into(),
3724            )),
3725        }
3726    }
3727
3728    fn plan_dedup(
3729        &mut self,
3730        input: ResidentBufferRef,
3731        node: &RirNode,
3732        ops: &mut Vec<ResidentRecordedOp>,
3733    ) -> Result<ResidentBufferRef> {
3734        let schema = self
3735            .certificate
3736            .node_schema(node)
3737            .ok_or_else(|| XlogError::Execution("resident dedup schema missing".into()))?;
3738        let output = self.allocate_scratch_relation(schema)?;
3739        // Dedup is union against itself in the exact full-row set primitive.
3740        let op_id = self.next_op_id()?;
3741        self.push_physical_op(
3742            ops,
3743            ResidentRecordedOp::Union {
3744                left: input.clone(),
3745                right: input,
3746                output,
3747                op_id,
3748            },
3749            op_id,
3750        );
3751        Ok(ResidentBufferRef::Private(output))
3752    }
3753
3754    fn plan_union(
3755        &mut self,
3756        left: ResidentBufferRef,
3757        right: ResidentBufferRef,
3758        schema_node: &RirNode,
3759        ops: &mut Vec<ResidentRecordedOp>,
3760    ) -> Result<ResidentBufferRef> {
3761        let schema = self
3762            .certificate
3763            .node_schema(schema_node)
3764            .ok_or_else(|| XlogError::Execution("resident union schema missing".into()))?;
3765        self.plan_union_schema(left, right, schema, ops)
3766    }
3767
3768    fn plan_union_schema(
3769        &mut self,
3770        left: ResidentBufferRef,
3771        right: ResidentBufferRef,
3772        schema: Schema,
3773        ops: &mut Vec<ResidentRecordedOp>,
3774    ) -> Result<ResidentBufferRef> {
3775        let output = self.allocate_scratch_relation(schema)?;
3776        let op_id = self.next_op_id()?;
3777        self.push_physical_op(
3778            ops,
3779            ResidentRecordedOp::Union {
3780                left,
3781                right,
3782                output,
3783                op_id,
3784            },
3785            op_id,
3786        );
3787        Ok(ResidentBufferRef::Private(output))
3788    }
3789
3790    fn plan_diff(
3791        &mut self,
3792        left: ResidentBufferRef,
3793        right: ResidentBufferRef,
3794        schema_node: &RirNode,
3795        ops: &mut Vec<ResidentRecordedOp>,
3796    ) -> Result<ResidentBufferRef> {
3797        let schema = self
3798            .certificate
3799            .node_schema(schema_node)
3800            .ok_or_else(|| XlogError::Execution("resident diff schema missing".into()))?;
3801        self.plan_diff_schema(left, right, schema, ops)
3802    }
3803
3804    fn plan_diff_schema(
3805        &mut self,
3806        left: ResidentBufferRef,
3807        right: ResidentBufferRef,
3808        schema: Schema,
3809        ops: &mut Vec<ResidentRecordedOp>,
3810    ) -> Result<ResidentBufferRef> {
3811        let output = self.allocate_scratch_relation(schema)?;
3812        let op_id = self.next_op_id()?;
3813        self.push_physical_op(
3814            ops,
3815            ResidentRecordedOp::Diff {
3816                left,
3817                right,
3818                output,
3819                op_id,
3820            },
3821            op_id,
3822        );
3823        Ok(ResidentBufferRef::Private(output))
3824    }
3825
3826    fn plan_copy(
3827        &mut self,
3828        input: ResidentBufferRef,
3829        output: usize,
3830        schema: &Schema,
3831        ops: &mut Vec<ResidentRecordedOp>,
3832    ) -> Result<()> {
3833        let compact_expressions = (0..schema.arity())
3834            .map(|column| {
3835                let scalar = schema.column_type(column).ok_or_else(|| {
3836                    XlogError::Execution("resident identity project column is missing".into())
3837                })?;
3838                Ok(ResidentProjectExpressionDescriptor::column(
3839                    u32::try_from(column).map_err(|_| {
3840                        XlogError::Execution("resident project column exceeds u32".into())
3841                    })?,
3842                    u32::try_from(scalar.size_bytes()).map_err(|_| {
3843                        XlogError::Execution("resident project scalar width exceeds u32".into())
3844                    })?,
3845                ))
3846            })
3847            .collect::<Result<Vec<_>>>()?;
3848        let workspace = ResidentProjectPlan {
3849            compact_expressions,
3850        };
3851        let workspace_index = self.project_workspaces.len();
3852        self.project_workspaces.push(workspace);
3853        let op_id = self.next_op_id()?;
3854        self.push_physical_op(
3855            ops,
3856            ResidentRecordedOp::Project {
3857                input,
3858                output,
3859                workspace: workspace_index,
3860                op_id,
3861            },
3862            op_id,
3863        );
3864        Ok(())
3865    }
3866
3867    fn normalize_to_schema(
3868        &mut self,
3869        input: ResidentBufferRef,
3870        schema: &Schema,
3871        ops: &mut Vec<ResidentRecordedOp>,
3872    ) -> Result<ResidentBufferRef> {
3873        let input_schema = self.schema(&input)?;
3874        if input_schema == schema {
3875            return Ok(input);
3876        }
3877        if input_schema.arity() != schema.arity()
3878            || input_schema
3879                .columns
3880                .iter()
3881                .zip(&schema.columns)
3882                .any(|(left, right)| left.1 != right.1)
3883        {
3884            return Err(XlogError::Execution(format!(
3885                "resident rule output schema {:?} cannot be normalized to head schema {:?}",
3886                input_schema, schema
3887            )));
3888        }
3889        let output = self.allocate_scratch_relation(schema.clone())?;
3890        self.plan_copy(input, output, schema, ops)?;
3891        Ok(ResidentBufferRef::Private(output))
3892    }
3893
3894    fn merge_phase_contribution(
3895        &mut self,
3896        current: Option<ResidentBufferRef>,
3897        contribution: ResidentBufferRef,
3898        schema: &Schema,
3899        ops: &mut Vec<ResidentRecordedOp>,
3900    ) -> Result<ResidentBufferRef> {
3901        let contribution = self.normalize_to_schema(contribution, schema, ops)?;
3902        resident_phase_merge(current, contribution, |step| match step {
3903            ResidentPhaseMergeStep::Deduplicate(input) => {
3904                self.plan_union_schema(input.clone(), input, schema.clone(), ops)
3905            }
3906            ResidentPhaseMergeStep::Union(left, right) => {
3907                self.plan_union_schema(left, right, schema.clone(), ops)
3908            }
3909        })
3910    }
3911}
3912
3913fn private_relation(
3914    relations: &[Option<ResidentRelation>],
3915    index: usize,
3916) -> Result<&ResidentRelation> {
3917    relations
3918        .get(index)
3919        .and_then(Option::as_ref)
3920        .ok_or_else(|| XlogError::Execution(format!("resident relation slot {index} is missing")))
3921}
3922
3923fn collect_recorded_scans(node: &RirNode, output: &mut Vec<RelId>) {
3924    match node {
3925        RirNode::Unit | RirNode::TensorMaskedJoin { .. } => {}
3926        RirNode::Scan { rel } => output.push(*rel),
3927        RirNode::Filter { input, .. }
3928        | RirNode::Project { input, .. }
3929        | RirNode::GroupBy { input, .. }
3930        | RirNode::Distinct { input, .. } => collect_recorded_scans(input, output),
3931        RirNode::Join { left, right, .. } | RirNode::Diff { left, right } => {
3932            collect_recorded_scans(left, output);
3933            collect_recorded_scans(right, output);
3934        }
3935        RirNode::ChainJoin { fallback, .. } | RirNode::MultiWayJoin { fallback, .. } => {
3936            collect_recorded_scans(fallback, output);
3937        }
3938        RirNode::Union { inputs } => {
3939            for input in inputs {
3940                collect_recorded_scans(input, output);
3941            }
3942        }
3943        RirNode::Fixpoint {
3944            base, recursive, ..
3945        } => {
3946            collect_recorded_scans(base, output);
3947            collect_recorded_scans(recursive, output);
3948        }
3949    }
3950}
3951
3952fn resident_decline_error(reason: ResidentGraphDeclineReason) -> ResidentGraphExecutionError {
3953    ResidentGraphExecutionError::Declined(reason)
3954}
3955
3956fn conditional_graph_error(error: CudaConditionalGraphUnavailable) -> ResidentGraphExecutionError {
3957    if error.is_unsupported() {
3958        resident_decline_error(ResidentGraphDeclineReason::ConditionalGraphUnavailable {
3959            detail: error.decline_detail(),
3960        })
3961    } else {
3962        runtime_error(error.decline_detail())
3963    }
3964}
3965
3966fn read_receipt_u32(bytes: &[u8], offset: usize) -> Option<u32> {
3967    bytes
3968        .get(offset..offset.checked_add(4)?)?
3969        .try_into()
3970        .ok()
3971        .map(u32::from_le_bytes)
3972}
3973
3974fn read_receipt_u64(bytes: &[u8], offset: usize) -> Option<u64> {
3975    bytes
3976        .get(offset..offset.checked_add(8)?)?
3977        .try_into()
3978        .ok()
3979        .map(u64::from_le_bytes)
3980}
3981
3982fn resident_compact_scalar(value: &ConstValue) -> Result<(ScalarType, u64)> {
3983    match value {
3984        ConstValue::Symbol(value) => Ok((ScalarType::Symbol, u64::from(symbol::intern(value)))),
3985        ConstValue::U32(value) => Ok((ScalarType::U32, u64::from(*value))),
3986        ConstValue::U64(value) => Ok((ScalarType::U64, *value)),
3987        _ => Err(XlogError::Execution(
3988            "uncertified resident constant type".into(),
3989        )),
3990    }
3991}
3992
3993fn resident_compact_filter_descriptors(
3994    expression: &Expr,
3995    input_schema: &Schema,
3996) -> Result<Vec<ResidentFilterComparisonDescriptor>> {
3997    fn operand(expression: &Expr, input_schema: &Schema) -> Result<(u32, u32, ScalarType, u64)> {
3998        match expression {
3999            Expr::Column(column) => {
4000                let scalar = input_schema.column_type(*column).ok_or_else(|| {
4001                    XlogError::Execution("resident filter column is out of range".into())
4002                })?;
4003                Ok((
4004                    0,
4005                    u32::try_from(*column).map_err(|_| {
4006                        XlogError::Execution("resident filter column exceeds u32".into())
4007                    })?,
4008                    scalar,
4009                    0,
4010                ))
4011            }
4012            Expr::Const(value) => {
4013                let (scalar, bits) = resident_compact_scalar(value)?;
4014                Ok((1, 0, scalar, bits))
4015            }
4016            _ => Err(XlogError::Execution(
4017                "uncertified resident filter operand".into(),
4018            )),
4019        }
4020    }
4021
4022    fn append(
4023        expression: &Expr,
4024        input_schema: &Schema,
4025        output: &mut Vec<ResidentFilterComparisonDescriptor>,
4026    ) -> Result<()> {
4027        match expression {
4028            Expr::And(parts) => {
4029                for part in parts {
4030                    append(part, input_schema, output)?;
4031                }
4032                Ok(())
4033            }
4034            Expr::Compare { left, op, right } => {
4035                let (left_kind, left_column, left_type, left_constant) =
4036                    operand(left, input_schema)?;
4037                let (right_kind, right_column, right_type, right_constant) =
4038                    operand(right, input_schema)?;
4039                if left_type != right_type {
4040                    return Err(XlogError::Execution(
4041                        "resident filter operand scalar types differ".into(),
4042                    ));
4043                }
4044                let width = u32::try_from(left_type.size_bytes()).map_err(|_| {
4045                    XlogError::Execution("resident filter scalar width exceeds u32".into())
4046                })?;
4047                if !matches!(width, 4 | 8) {
4048                    return Err(XlogError::Execution(
4049                        "resident filter scalar width is unsupported".into(),
4050                    ));
4051                }
4052                output.push(ResidentFilterComparisonDescriptor {
4053                    left_kind,
4054                    left_column,
4055                    right_kind,
4056                    right_column,
4057                    op: u32::from(resident_compare_op(*op) as u8),
4058                    width,
4059                    reserved_zero: 0,
4060                    reserved_one: 0,
4061                    left_constant,
4062                    right_constant,
4063                });
4064                Ok(())
4065            }
4066            _ => Err(XlogError::Execution(
4067                "uncertified resident filter expression".into(),
4068            )),
4069        }
4070    }
4071
4072    let mut output = Vec::new();
4073    append(expression, input_schema, &mut output)?;
4074    Ok(output)
4075}
4076
4077fn resident_compact_project_descriptors(
4078    columns: &[ProjectExpr],
4079    input_schema: &Schema,
4080    output_schema: &Schema,
4081) -> Result<Vec<ResidentProjectExpressionDescriptor>> {
4082    if columns.len() != output_schema.arity() {
4083        return Err(XlogError::Execution(
4084            "resident project expression count differs from output arity".into(),
4085        ));
4086    }
4087    columns
4088        .iter()
4089        .enumerate()
4090        .map(|(output_column, expression)| {
4091            let output_type = output_schema.column_type(output_column).ok_or_else(|| {
4092                XlogError::Execution("resident project output column is out of range".into())
4093            })?;
4094            let width = u32::try_from(output_type.size_bytes()).map_err(|_| {
4095                XlogError::Execution("resident project scalar width exceeds u32".into())
4096            })?;
4097            if !matches!(width, 4 | 8) {
4098                return Err(XlogError::Execution(
4099                    "resident project scalar width is unsupported".into(),
4100                ));
4101            }
4102            match expression {
4103                ProjectExpr::Column(input_column) => {
4104                    if input_schema.column_type(*input_column) != Some(output_type) {
4105                        return Err(XlogError::Execution(
4106                            "resident project column scalar type differs from output".into(),
4107                        ));
4108                    }
4109                    Ok(ResidentProjectExpressionDescriptor::column(
4110                        u32::try_from(*input_column).map_err(|_| {
4111                            XlogError::Execution("resident project column exceeds u32".into())
4112                        })?,
4113                        width,
4114                    ))
4115                }
4116                ProjectExpr::Computed(Expr::Const(value), declared_type) => {
4117                    let (constant_type, bits) = resident_compact_scalar(value)?;
4118                    if constant_type != *declared_type || constant_type != output_type {
4119                        return Err(XlogError::Execution(
4120                            "resident project constant scalar type differs from output".into(),
4121                        ));
4122                    }
4123                    Ok(ResidentProjectExpressionDescriptor::constant(width, bits))
4124                }
4125                _ => Err(XlogError::Execution(
4126                    "uncertified resident projection expression".into(),
4127                )),
4128            }
4129        })
4130        .collect()
4131}
4132
4133fn resident_compare_op(op: RirCompareOp) -> CudaCompareOp {
4134    match op {
4135        RirCompareOp::Eq => CudaCompareOp::Eq,
4136        RirCompareOp::Ne => CudaCompareOp::Ne,
4137        RirCompareOp::Lt => CudaCompareOp::Lt,
4138        RirCompareOp::Le => CudaCompareOp::Le,
4139        RirCompareOp::Gt => CudaCompareOp::Gt,
4140        RirCompareOp::Ge => CudaCompareOp::Ge,
4141    }
4142}
4143
4144fn terminal_status_for_injection(status: &ResidentGraphDeviceStatus) -> ResidentTerminalStatus {
4145    match status {
4146        ResidentGraphDeviceStatus::Success { iterations } => ResidentTerminalStatus {
4147            code: ResidentTerminalCode::Success as u32,
4148            iterations: *iterations,
4149            ..ResidentTerminalStatus::default()
4150        },
4151        ResidentGraphDeviceStatus::IterationLimit { limit, completed } => ResidentTerminalStatus {
4152            code: ResidentTerminalCode::IterationLimit as u32,
4153            iterations: *completed,
4154            limit: *limit,
4155            ..ResidentTerminalStatus::default()
4156        },
4157        ResidentGraphDeviceStatus::CapacityOverflow {
4158            op_id,
4159            required,
4160            capacity,
4161        } => ResidentTerminalStatus {
4162            code: ResidentTerminalCode::CapacityOverflow as u32,
4163            op_id: *op_id,
4164            resource_code: ResidentResourceCode::OutputRows as u32,
4165            required: *required,
4166            capacity: *capacity,
4167            ..ResidentTerminalStatus::default()
4168        },
4169        ResidentGraphDeviceStatus::ResourceExhausted {
4170            op_id,
4171            required,
4172            capacity,
4173            ..
4174        } => ResidentTerminalStatus {
4175            code: ResidentTerminalCode::ResourceExhausted as u32,
4176            op_id: *op_id,
4177            resource_code: ResidentResourceCode::SetHashSlots as u32,
4178            required: *required,
4179            capacity: *capacity,
4180            ..ResidentTerminalStatus::default()
4181        },
4182    }
4183}
4184
4185fn validate_compact_resident_node_envelope(
4186    node: &RirNode,
4187    schema_for: &impl Fn(&RirNode) -> std::result::Result<Schema, ResidentGraphDeclineReason>,
4188) -> std::result::Result<(), ResidentGraphDeclineReason> {
4189    match node {
4190        RirNode::Unit | RirNode::Scan { .. } => Ok(()),
4191        RirNode::Filter { input, .. } | RirNode::Project { input, .. } => {
4192            validate_compact_resident_node_envelope(input, schema_for)
4193        }
4194        RirNode::Distinct { input, key_cols } => {
4195            validate_compact_resident_node_envelope(input, schema_for)?;
4196            let arity = schema_for(input)?.arity();
4197            if key_cols.len() != arity
4198                || key_cols
4199                    .iter()
4200                    .copied()
4201                    .enumerate()
4202                    .any(|(column, key)| column != key)
4203            {
4204                return Err(resident_workspace_decline(format!(
4205                    "compact resident Distinct requires canonical full-row key columns 0..{arity}"
4206                )));
4207            }
4208            Ok(())
4209        }
4210        RirNode::Union { inputs } => {
4211            if inputs.is_empty() {
4212                return Err(resident_workspace_decline(
4213                    "compact resident Union requires at least one input",
4214                ));
4215            }
4216            for input in inputs {
4217                validate_compact_resident_node_envelope(input, schema_for)?;
4218            }
4219            Ok(())
4220        }
4221        RirNode::Diff { left, right } => {
4222            validate_compact_resident_node_envelope(left, schema_for)?;
4223            validate_compact_resident_node_envelope(right, schema_for)
4224        }
4225        RirNode::Join {
4226            left,
4227            right,
4228            left_keys,
4229            right_keys,
4230            join_type,
4231        } => {
4232            if !matches!(join_type, JoinType::Inner | JoinType::Semi) {
4233                return Err(resident_workspace_decline(format!(
4234                    "compact resident Join does not support {join_type:?}"
4235                )));
4236            }
4237            if left_keys.len() != 1 || right_keys.len() != 1 {
4238                return Err(resident_workspace_decline(
4239                    "compact resident Join requires exactly one key per input",
4240                ));
4241            }
4242            validate_compact_resident_node_envelope(left, schema_for)?;
4243            validate_compact_resident_node_envelope(right, schema_for)?;
4244            let left_schema = schema_for(left)?;
4245            let right_schema = schema_for(right)?;
4246            let left_key = left_keys[0];
4247            let right_key = right_keys[0];
4248            if left_key >= left_schema.arity() || right_key >= right_schema.arity() {
4249                return Err(resident_workspace_decline(
4250                    "compact resident Join key is outside its input schema",
4251                ));
4252            }
4253            let left_key_type = left_schema
4254                .column_type(left_key)
4255                .expect("left key bounds checked");
4256            let right_key_type = right_schema
4257                .column_type(right_key)
4258                .expect("right key bounds checked");
4259            if left_key_type != right_key_type
4260                || !matches!(
4261                    left_key_type,
4262                    ScalarType::U32 | ScalarType::U64 | ScalarType::Symbol
4263                )
4264            {
4265                return Err(resident_workspace_decline(
4266                    "compact resident Join requires matching U32, U64, or Symbol key types",
4267                ));
4268            }
4269            if left_key_type.size_bytes() != right_key_type.size_bytes() {
4270                return Err(resident_workspace_decline(
4271                    "compact resident Join key widths differ",
4272                ));
4273            }
4274            let output_arity = match join_type {
4275                JoinType::Inner => left_schema
4276                    .arity()
4277                    .checked_add(right_schema.arity())
4278                    .ok_or_else(|| {
4279                        resident_workspace_decline("compact resident Join arity overflow")
4280                    })?,
4281                JoinType::Semi => left_schema.arity(),
4282                _ => unreachable!("join kind checked above"),
4283            };
4284            if output_arity > 17 {
4285                return Err(resident_workspace_decline(
4286                    "compact resident Join output arity exceeds 17",
4287                ));
4288            }
4289            Ok(())
4290        }
4291        RirNode::ChainJoin { fallback, .. } | RirNode::MultiWayJoin { fallback, .. } => {
4292            validate_compact_resident_node_envelope(fallback, schema_for)
4293        }
4294        RirNode::Fixpoint { .. } | RirNode::GroupBy { .. } | RirNode::TensorMaskedJoin { .. } => {
4295            Err(resident_workspace_decline(
4296                "compact resident route contains an unsupported node",
4297            ))
4298        }
4299    }
4300}
4301
4302fn resident_generated_query_heads(
4303    plan: &ExecutionPlan,
4304) -> std::result::Result<BTreeSet<String>, ResidentGraphDeclineReason> {
4305    let mut heads = BTreeSet::new();
4306    let mut rule_positions = HashSet::new();
4307    for (position, provenance) in plan.generated_query_rules.iter().enumerate() {
4308        if provenance.query_index != position {
4309            return Err(resident_workspace_decline(format!(
4310                "generated query provenance position {position} carries query index {}",
4311                provenance.query_index
4312            )));
4313        }
4314        if !rule_positions.insert((provenance.scc_index, provenance.rule_index)) {
4315            return Err(resident_workspace_decline(format!(
4316                "generated query provenance {} reuses compiled rule scc={} rule={}",
4317                provenance.query_index, provenance.scc_index, provenance.rule_index
4318            )));
4319        }
4320        let expected_head = format!("__xlog_query_{}", provenance.query_index);
4321        let rule = plan
4322            .rules_by_scc
4323            .get(provenance.scc_index)
4324            .and_then(|rules| rules.get(provenance.rule_index))
4325            .ok_or_else(|| {
4326                resident_workspace_decline(format!(
4327                    "generated query provenance {} references missing compiled rule scc={} rule={}",
4328                    provenance.query_index, provenance.scc_index, provenance.rule_index
4329                ))
4330            })?;
4331        if rule.head != expected_head {
4332            return Err(resident_workspace_decline(format!(
4333                "generated query provenance {} expects head {expected_head} but references authored head {}",
4334                provenance.query_index, rule.head
4335            )));
4336        }
4337        let occurrence_count = plan
4338            .rules_by_scc
4339            .iter()
4340            .flatten()
4341            .filter(|candidate| candidate.head == expected_head)
4342            .count();
4343        if occurrence_count != 1 {
4344            return Err(resident_workspace_decline(format!(
4345                "generated query head {expected_head} must have exactly one compiled rule, found {occurrence_count}"
4346            )));
4347        }
4348        heads.insert(expected_head);
4349    }
4350    Ok(heads)
4351}
4352
4353impl Executor {
4354    /// Prepare one immutable, fixed-capacity conditional graph transaction.
4355    pub fn prepare_resident_graph(
4356        &self,
4357        plan: &ExecutionPlan,
4358        certificate: &ResidentGraphRouteCertificate,
4359        options: ResidentGraphPrepareOptions,
4360    ) -> std::result::Result<PreparedResidentGraph, ResidentGraphExecutionError> {
4361        if !certificate.matches_plan(plan).map_err(runtime_error)? {
4362            return Err(resident_decline_error(
4363                ResidentGraphDeclineReason::WorkspaceUnbounded {
4364                    detail: "route certificate does not match the plan being prepared".into(),
4365                },
4366            ));
4367        }
4368        if !certificate.is_supported() {
4369            return Err(resident_decline_error(
4370                certificate.declines().first().cloned().unwrap_or_else(|| {
4371                    ResidentGraphDeclineReason::WorkspaceUnbounded {
4372                        detail: "route certificate is not resident-capable".into(),
4373                    }
4374                }),
4375            ));
4376        }
4377
4378        self.prepare_resident_graph_after_certification(plan, certificate, options)
4379    }
4380
4381    /// Prepare a transaction from a certificate sealed to its exact immutable plan.
4382    pub fn prepare_certified_resident_graph(
4383        &self,
4384        certified: &ResidentGraphCertifiedPlan,
4385        options: ResidentGraphPrepareOptions,
4386    ) -> std::result::Result<PreparedResidentGraph, ResidentGraphExecutionError> {
4387        let certificate = certified.certificate();
4388        if !certificate.is_supported() {
4389            return Err(resident_decline_error(
4390                certificate.declines().first().cloned().unwrap_or_else(|| {
4391                    ResidentGraphDeclineReason::WorkspaceUnbounded {
4392                        detail: "route certificate is not resident-capable".into(),
4393                    }
4394                }),
4395            ));
4396        }
4397        self.prepare_resident_graph_after_certification(certified.plan(), certificate, options)
4398    }
4399
4400    fn prepare_resident_graph_after_certification(
4401        &self,
4402        plan: &ExecutionPlan,
4403        certificate: &ResidentGraphRouteCertificate,
4404        options: ResidentGraphPrepareOptions,
4405    ) -> std::result::Result<PreparedResidentGraph, ResidentGraphExecutionError> {
4406        let mut diagnostics =
4407            resident_prepare_diagnostics_for_sample(options.latency_diagnostic_sample);
4408        let total_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4409        let admission_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4410        let source_epoch = self.store.mutation_epoch();
4411        let resident_head_names = plan
4412            .rules_by_scc
4413            .iter()
4414            .flat_map(|rules| rules.iter().map(|rule| rule.head.clone()))
4415            .collect::<HashSet<_>>();
4416        let mut source_set_snapshots = Vec::new();
4417        let mut scans = Vec::new();
4418        for rules in &plan.rules_by_scc {
4419            for rule in rules {
4420                collect_recorded_scans(&rule.body, &mut scans);
4421            }
4422        }
4423        scans.sort_by_key(|rel| rel.0);
4424        scans.dedup();
4425        for rel in scans {
4426            let name = self
4427                .rel_names
4428                .get(&rel)
4429                .ok_or_else(|| runtime_error(format!("unknown resident relation id {rel:?}")))?;
4430            match self.store.get_with_version(name) {
4431                Some((buffer, version)) => {
4432                    let schema = certificate.schema_for(rel).ok_or_else(|| {
4433                        runtime_error(format!("resident scan {name} has no certified schema"))
4434                    })?;
4435                    if buffer.schema() != schema {
4436                        return Err(runtime_error(format!(
4437                            "resident scan {name} store schema differs from its certificate"
4438                        )));
4439                    }
4440                    source_set_snapshots.push(
4441                        resident_source_set_snapshot(&self.provider, name, version, buffer)
4442                            .map_err(resident_decline_error)?,
4443                    );
4444                }
4445                None if resident_head_names.contains(name) => {}
4446                None => {
4447                    return Err(runtime_error(format!(
4448                        "missing resident source relation {name}"
4449                    )))
4450                }
4451            }
4452        }
4453        let admission = self
4454            .resident_workspace_admission_after_certification(plan, certificate)
4455            .map_err(resident_decline_error)?;
4456        for (name, schema) in &admission.head_schemas {
4457            if let Some(existing) = self.store.get(name) {
4458                if !resident_schemas_type_compatible(existing.schema(), schema) {
4459                    return Err(runtime_error(format!(
4460                        "existing resident head {name} has a physically incompatible schema: existing={:?}, staged={schema:?}",
4461                        existing.schema()
4462                    )));
4463                }
4464            }
4465        }
4466        if let Some(diagnostics) = diagnostics.as_mut() {
4467            diagnostics.admission_and_source_snapshot_ns = resident_prepare_elapsed_ns(
4468                admission_started.expect("diagnostic timer exists when enabled"),
4469            );
4470        }
4471
4472        let execution_setup_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4473        let runtime =
4474            Arc::clone(self.provider.memory().runtime().ok_or_else(|| {
4475                runtime_error("resident graph requires the async device runtime")
4476            })?);
4477        let stream_id = if let Some(stream_id) = self.resident_graph_stream.get() {
4478            *stream_id
4479        } else {
4480            let acquired = runtime.stream_pool().acquire().map_err(runtime_error)?;
4481            match self.resident_graph_stream.set(acquired) {
4482                Ok(()) => acquired,
4483                Err(_) => *self
4484                    .resident_graph_stream
4485                    .get()
4486                    .ok_or_else(|| runtime_error("resident stream initialization raced"))?,
4487            }
4488        };
4489        let stream = runtime
4490            .stream_pool()
4491            .resolve(stream_id)
4492            .ok_or_else(|| runtime_error("resident graph stream is no longer live"))?;
4493        let execution_domain = self
4494            .provider
4495            .bind_resident_execution_domain(Arc::clone(&runtime), stream_id, Arc::clone(&stream))
4496            .map_err(runtime_error)?;
4497
4498        let mut relation_registration = self
4499            .rel_names
4500            .iter()
4501            .map(|(rel, name)| (*rel, name.clone()))
4502            .collect::<Vec<_>>();
4503        relation_registration.sort_by_key(|(rel, name)| (rel.0, name.clone()));
4504
4505        let mut build = ResidentBuild {
4506            executor: self,
4507            certificate,
4508            capacity: admission.relation_capacity,
4509            relations: Vec::new(),
4510            filter_workspaces: Vec::new(),
4511            project_workspaces: Vec::new(),
4512            heads: BTreeMap::new(),
4513            head_winner_indices: BTreeMap::new(),
4514            source_names: HashSet::new(),
4515            source_aliases: HashMap::new(),
4516            next_op_id: 0,
4517            injection: options.test_device_status,
4518            injection_recorded: false,
4519        };
4520        if let Some(diagnostics) = diagnostics.as_mut() {
4521            diagnostics.execution_domain_and_build_setup_ns = resident_prepare_elapsed_ns(
4522                execution_setup_started.expect("diagnostic timer exists when enabled"),
4523            );
4524        }
4525        let logical_planning_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4526        for (winner_index, (name, schema)) in admission.head_schemas.iter().enumerate() {
4527            let index = build
4528                .allocate_permanent_relation(schema.clone(), 0)
4529                .map_err(runtime_error)?;
4530            build.heads.insert(name.clone(), index);
4531            build.head_winner_indices.insert(
4532                name.clone(),
4533                u32::try_from(winner_index)
4534                    .map_err(|_| runtime_error("resident head count exceeds u32"))?,
4535            );
4536        }
4537
4538        let mut initial_ops = Vec::new();
4539        for (name, schema) in &admission.head_schemas {
4540            if self.store.contains(name) {
4541                let source = build.source_reference(name).map_err(runtime_error)?;
4542                build
4543                    .plan_copy(source.clone(), build.heads[name], schema, &mut initial_ops)
4544                    .map_err(runtime_error)?;
4545                resident_record_schema_winner_mark(
4546                    &mut initial_ops,
4547                    ResidentBufferRef::Private(build.heads[name]),
4548                    build.head_winner_indices[name],
4549                    0,
4550                );
4551            }
4552        }
4553
4554        let mut scc_by_id = HashMap::new();
4555        for (index, scc) in plan.sccs.iter().enumerate() {
4556            if scc_by_id.insert(scc.id, index).is_some() {
4557                return Err(runtime_error(format!(
4558                    "duplicate resident SCC id {}",
4559                    scc.id
4560                )));
4561            }
4562        }
4563        let ordered_sccs = if plan.strata.is_empty() {
4564            (0..plan.sccs.len()).collect::<Vec<_>>()
4565        } else {
4566            let mut ordered = Vec::new();
4567            for stratum in &plan.strata {
4568                for scc_id in &stratum.sccs {
4569                    ordered.push(*scc_by_id.get(scc_id).ok_or_else(|| {
4570                        runtime_error(format!(
4571                            "stratum {} references unknown SCC {scc_id}",
4572                            stratum.id
4573                        ))
4574                    })?);
4575                }
4576            }
4577            ordered
4578        };
4579        let mut seen_sccs = HashSet::new();
4580        for index in &ordered_sccs {
4581            if !seen_sccs.insert(*index) {
4582                return Err(runtime_error(format!(
4583                    "resident execution schedule repeats SCC {}",
4584                    plan.sccs[*index].id
4585                )));
4586            }
4587        }
4588        if seen_sccs.len() != plan.sccs.len() {
4589            return Err(runtime_error(
4590                "resident execution schedule omits one or more SCCs",
4591            ));
4592        }
4593
4594        let mut phases = Vec::new();
4595        for scc_index in ordered_sccs {
4596            let scc = &plan.sccs[scc_index];
4597            let rules = plan.rules_by_scc.get(scc_index).ok_or_else(|| {
4598                runtime_error(format!("resident SCC {scc_index} has no rule vector"))
4599            })?;
4600            if rules.is_empty() {
4601                continue;
4602            }
4603            if !scc.is_recursive {
4604                let mut ops = Vec::new();
4605                let mut phase_heads = BTreeMap::<String, ResidentBufferRef>::new();
4606                for (rule_index, rule) in rules.iter().enumerate() {
4607                    let mut occurrences = HashMap::new();
4608                    let contribution = build
4609                        .plan_node(&rule.body, None, &mut occurrences, &mut ops)
4610                        .map_err(runtime_error)?;
4611                    let schema_id =
4612                        admission.rule_schema_ids[scc_index][rule_index].ok_or_else(|| {
4613                            runtime_error(format!(
4614                                "resident rule {} has no schema winner id",
4615                                rule.head
4616                            ))
4617                        })?;
4618                    ops.push(ResidentRecordedOp::SchemaWinnerMark {
4619                        contribution: contribution.clone(),
4620                        head_index: build.head_winner_indices[&rule.head],
4621                        schema_id,
4622                    });
4623                    let target = *build.heads.get(&rule.head).ok_or_else(|| {
4624                        runtime_error(format!("resident head {} was not staged", rule.head))
4625                    })?;
4626                    let target_schema = build
4627                        .schema(&ResidentBufferRef::Private(target))
4628                        .map_err(runtime_error)?
4629                        .clone();
4630                    let current = phase_heads
4631                        .remove(&rule.head)
4632                        .unwrap_or(ResidentBufferRef::Private(target));
4633                    let merged = build
4634                        .merge_phase_contribution(
4635                            Some(current),
4636                            contribution,
4637                            &target_schema,
4638                            &mut ops,
4639                        )
4640                        .map_err(runtime_error)?;
4641                    phase_heads.insert(rule.head.clone(), merged);
4642                }
4643                for (head, value) in phase_heads {
4644                    let target = build.heads[&head];
4645                    let target_schema = build
4646                        .schema(&ResidentBufferRef::Private(target))
4647                        .map_err(runtime_error)?
4648                        .clone();
4649                    build
4650                        .plan_copy(value, target, &target_schema, &mut ops)
4651                        .map_err(runtime_error)?;
4652                }
4653                phases.push(ResidentCapturePhase::Segment {
4654                    ops,
4655                    scc_begin: None,
4656                });
4657                continue;
4658            }
4659
4660            let mut recursive_heads = BTreeMap::new();
4661            for rule in rules {
4662                recursive_heads
4663                    .entry(rule.head.clone())
4664                    .or_insert_with(|| rule.meta.schema.clone());
4665            }
4666            let mut delta_heads = BTreeMap::new();
4667            for (name, schema) in &recursive_heads {
4668                delta_heads.insert(
4669                    name.clone(),
4670                    build
4671                        .allocate_permanent_relation(schema.clone(), 0)
4672                        .map_err(runtime_error)?,
4673                );
4674            }
4675
4676            let mut seed_ops = Vec::new();
4677            let mut seed_values = BTreeMap::<String, ResidentBufferRef>::new();
4678            for (rule_index, rule) in rules.iter().enumerate() {
4679                let mut occurrences = HashMap::new();
4680                let contribution = build
4681                    .plan_node(&rule.body, None, &mut occurrences, &mut seed_ops)
4682                    .map_err(runtime_error)?;
4683                let schema_id =
4684                    admission.rule_schema_ids[scc_index][rule_index].ok_or_else(|| {
4685                        runtime_error(format!(
4686                            "resident rule {} has no schema winner id",
4687                            rule.head
4688                        ))
4689                    })?;
4690                seed_ops.push(ResidentRecordedOp::SchemaWinnerMark {
4691                    contribution: contribution.clone(),
4692                    head_index: build.head_winner_indices[&rule.head],
4693                    schema_id,
4694                });
4695                let current = seed_values.remove(&rule.head);
4696                let merged = build
4697                    .merge_phase_contribution(
4698                        current,
4699                        contribution,
4700                        &rule.meta.schema,
4701                        &mut seed_ops,
4702                    )
4703                    .map_err(runtime_error)?;
4704                seed_values.insert(rule.head.clone(), merged);
4705            }
4706            for (name, schema) in &recursive_heads {
4707                let full = build.heads[name];
4708                let seed = seed_values.remove(name).ok_or_else(|| {
4709                    runtime_error(format!(
4710                        "recursive resident seed has no contribution for {name}"
4711                    ))
4712                })?;
4713                let candidate = build
4714                    .plan_union_schema(
4715                        ResidentBufferRef::Private(full),
4716                        seed,
4717                        schema.clone(),
4718                        &mut seed_ops,
4719                    )
4720                    .map_err(runtime_error)?;
4721                let novel = build
4722                    .plan_diff_schema(
4723                        candidate.clone(),
4724                        ResidentBufferRef::Private(full),
4725                        schema.clone(),
4726                        &mut seed_ops,
4727                    )
4728                    .map_err(runtime_error)?;
4729                build
4730                    .plan_copy(candidate, full, schema, &mut seed_ops)
4731                    .map_err(runtime_error)?;
4732                build
4733                    .plan_copy(novel, delta_heads[name], schema, &mut seed_ops)
4734                    .map_err(runtime_error)?;
4735            }
4736            let begin_op_id = build.next_op_id().map_err(runtime_error)?;
4737            phases.push(ResidentCapturePhase::Segment {
4738                ops: seed_ops,
4739                scc_begin: Some((self.config.max_iterations, begin_op_id)),
4740            });
4741
4742            let mut body_ops = vec![ResidentRecordedOp::ChangedReset];
4743            let mut body_values = BTreeMap::<String, ResidentBufferRef>::new();
4744            let recursive_names = recursive_heads.keys().cloned().collect::<HashSet<_>>();
4745            for (rule_index, rule) in rules.iter().enumerate() {
4746                let mut rule_scans = Vec::new();
4747                collect_recorded_scans(&rule.body, &mut rule_scans);
4748                let mut per_relation_occurrence = HashMap::<RelId, usize>::new();
4749                for rel in rule_scans {
4750                    let occurrence = per_relation_occurrence.entry(rel).or_insert(0);
4751                    let current = *occurrence;
4752                    *occurrence += 1;
4753                    let Some(name) = self.rel_names.get(&rel) else {
4754                        return Err(runtime_error(format!(
4755                            "recursive resident scan has unknown relation id {rel:?}"
4756                        )));
4757                    };
4758                    if !recursive_names.contains(name) {
4759                        continue;
4760                    }
4761                    let mut planning_occurrences = HashMap::new();
4762                    let contribution = build
4763                        .plan_node(
4764                            &rule.body,
4765                            Some((rel, current, delta_heads[name])),
4766                            &mut planning_occurrences,
4767                            &mut body_ops,
4768                        )
4769                        .map_err(runtime_error)?;
4770                    let schema_id =
4771                        admission.rule_schema_ids[scc_index][rule_index].ok_or_else(|| {
4772                            runtime_error(format!(
4773                                "resident rule {} has no schema winner id",
4774                                rule.head
4775                            ))
4776                        })?;
4777                    body_ops.push(ResidentRecordedOp::SchemaWinnerMark {
4778                        contribution: contribution.clone(),
4779                        head_index: build.head_winner_indices[&rule.head],
4780                        schema_id,
4781                    });
4782                    let current = body_values.remove(&rule.head);
4783                    let merged = build
4784                        .merge_phase_contribution(
4785                            current,
4786                            contribution,
4787                            &rule.meta.schema,
4788                            &mut body_ops,
4789                        )
4790                        .map_err(runtime_error)?;
4791                    body_values.insert(rule.head.clone(), merged);
4792                }
4793            }
4794            for (name, schema) in &recursive_heads {
4795                let full = build.heads[name];
4796                let accumulated = match body_values.remove(name) {
4797                    Some(value) => value,
4798                    None => build
4799                        .plan_diff_schema(
4800                            ResidentBufferRef::Private(full),
4801                            ResidentBufferRef::Private(full),
4802                            schema.clone(),
4803                            &mut body_ops,
4804                        )
4805                        .map_err(runtime_error)?,
4806                };
4807                let novel = build
4808                    .plan_diff_schema(
4809                        accumulated,
4810                        ResidentBufferRef::Private(full),
4811                        schema.clone(),
4812                        &mut body_ops,
4813                    )
4814                    .map_err(runtime_error)?;
4815                let next_full = build
4816                    .plan_union_schema(
4817                        ResidentBufferRef::Private(full),
4818                        novel.clone(),
4819                        schema.clone(),
4820                        &mut body_ops,
4821                    )
4822                    .map_err(runtime_error)?;
4823                build
4824                    .plan_copy(next_full, full, schema, &mut body_ops)
4825                    .map_err(runtime_error)?;
4826                build
4827                    .plan_copy(novel, delta_heads[name], schema, &mut body_ops)
4828                    .map_err(runtime_error)?;
4829                // The transient Diff slot can be reused by later body operations. Drive
4830                // convergence from the completed permanent delta instead.
4831                body_ops.push(ResidentRecordedOp::ChangedMark {
4832                    relation: delta_heads[name],
4833                });
4834            }
4835            let convergence_op_id = build.next_op_id().map_err(runtime_error)?;
4836            phases.push(ResidentCapturePhase::ConditionalWhile {
4837                ops: body_ops,
4838                iteration_limit: self.config.max_iterations,
4839                convergence_op_id,
4840            });
4841        }
4842
4843        let success_op_id = build.next_op_id().map_err(runtime_error)?;
4844        if let Some(diagnostics) = diagnostics.as_mut() {
4845            diagnostics.logical_schedule_planning_ns = resident_prepare_elapsed_ns(
4846                logical_planning_started.expect("diagnostic timer exists when enabled"),
4847            );
4848        }
4849        let manifest_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4850        let mut manifest = build
4851            .allocation_manifest(&initial_ops, &phases)
4852            .map_err(runtime_error)?;
4853        let compact_tables =
4854            resident_compact_tables(&build.filter_workspaces, &build.project_workspaces)
4855                .map_err(runtime_error)?;
4856        let compact_regions =
4857            resident_compact_regions(initial_ops, phases, success_op_id).map_err(runtime_error)?;
4858        if let Some(diagnostics) = diagnostics.as_mut() {
4859            diagnostics.manifest_compact_construction_ns = resident_prepare_elapsed_ns(
4860                manifest_started.expect("diagnostic timer exists when enabled"),
4861            );
4862            diagnostics.compact_regions = compact_regions.len();
4863            diagnostics.conditional_regions = compact_regions
4864                .iter()
4865                .filter(|region| region.iteration_limit != 0)
4866                .count();
4867        }
4868        let schedule_lowering_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4869        let compact_schedule = resident_lower_compact_regions(
4870            compact_regions,
4871            &manifest.slots,
4872            &manifest.logical_to_slot,
4873            build.source_names.iter().map(String::as_str),
4874            compact_tables,
4875        )
4876        .map_err(runtime_error)?;
4877        manifest
4878            .finalize_compact_schedule(&compact_schedule, build.heads.len())
4879            .map_err(runtime_error)?;
4880        if let Some(diagnostics) = diagnostics.as_mut() {
4881            diagnostics.schedule_lowering_ns = resident_prepare_elapsed_ns(
4882                schedule_lowering_started.expect("diagnostic timer exists when enabled"),
4883            );
4884            diagnostics.required_reservation_bytes = manifest.required_bytes;
4885            diagnostics.logical_relation_values = manifest.logical_relation_values;
4886            diagnostics.physical_relation_slots = manifest.slots.len();
4887            diagnostics.compact_ops = compact_schedule.ops.len();
4888            diagnostics.compact_waves = compact_schedule.waves.len();
4889        }
4890        let reservation_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4891        let manifest_available_bytes = self.provider.memory().remaining_bytes();
4892        let mut reservation = self
4893            .provider
4894            .memory()
4895            .reserve_bytes(manifest.required_bytes)
4896            .map_err(|error| {
4897                resident_decline_error(ResidentGraphDeclineReason::WorkspaceUnbounded {
4898                    detail: format!(
4899                        "resident allocation manifest reservation of {} bytes failed: {error}",
4900                        manifest.required_bytes
4901                    ),
4902                })
4903            })?;
4904        if let Some(diagnostics) = diagnostics.as_mut() {
4905            diagnostics.reservation_ns = resident_prepare_elapsed_ns(
4906                reservation_started.expect("diagnostic timer exists when enabled"),
4907            );
4908        }
4909        let physical = build
4910            .materialize(&manifest, &mut reservation, diagnostics.as_mut())
4911            .map_err(runtime_error)?;
4912        let count_initialization_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4913        let mut count_initialization = execution_domain.new_strict_recorder();
4914        for slot_index in 0..manifest.slots.len() {
4915            let relation =
4916                private_relation(&physical.relations, slot_index).map_err(runtime_error)?;
4917            count_initialization.write(relation.num_rows_device());
4918        }
4919        let count_initialization = unsafe {
4920            execution_domain.enqueue(count_initialization, |enqueue| {
4921                for (slot_index, slot) in manifest.slots.iter().enumerate() {
4922                    let relation = private_relation(&physical.relations, slot_index)?;
4923                    self.provider
4924                        .record_resident_relation_count_initialize_on_stream(
4925                            relation,
4926                            slot.initial_count,
4927                            enqueue,
4928                        )?;
4929                }
4930                Ok::<(), XlogError>(())
4931            })
4932        }
4933        .map_err(runtime_error)?;
4934        count_initialization.commit().map_err(runtime_error)?;
4935        if let Some(diagnostics) = diagnostics.as_mut() {
4936            diagnostics.count_initialization_ns = resident_prepare_elapsed_ns(
4937                count_initialization_started.expect("diagnostic timer exists when enabled"),
4938            );
4939            diagnostics.count_initialization_ns_max = diagnostics.count_initialization_ns;
4940            diagnostics.count_memset_calls =
4941                u64::try_from(manifest.slots.len()).unwrap_or(u64::MAX);
4942        }
4943
4944        if build.injection.is_some() && !build.injection_recorded {
4945            return Err(runtime_error(
4946                "test device status requested after a nonexistent physical op",
4947            ));
4948        }
4949        let metadata_binding_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4950        let output_indices =
4951            resident_output_indices(&build.heads, &manifest.logical_to_slot, &manifest.slots)
4952                .map_err(runtime_error)?;
4953        let output_relations = output_indices
4954            .iter()
4955            .map(|(_, index)| private_relation(&physical.relations, *index))
4956            .collect::<Result<Vec<_>>>()
4957            .map_err(runtime_error)?;
4958        let output_schema_plans = output_indices
4959            .iter()
4960            .map(|(name, _)| {
4961                let candidates = admission
4962                    .head_schema_choices
4963                    .get(name)
4964                    .cloned()
4965                    .ok_or_else(|| {
4966                        runtime_error(format!("resident head {name} has no schema plan"))
4967                    })?;
4968                let selection = if let Some(selection) = admission.head_schema_selections.get(name)
4969                {
4970                    let source_output = usize::try_from(
4971                        *build
4972                            .head_winner_indices
4973                            .get(&selection.source_head)
4974                            .ok_or_else(|| {
4975                                runtime_error(format!(
4976                                    "resident schema source {} is not staged",
4977                                    selection.source_head
4978                                ))
4979                            })?,
4980                    )
4981                    .map_err(|_| runtime_error("resident schema source index overflow"))?;
4982                    let source_candidates = admission
4983                        .head_schema_choices
4984                        .get(&selection.source_head)
4985                        .ok_or_else(|| runtime_error("resident schema source is missing"))?;
4986                    if source_candidates.len() != selection.output_schemas_by_source_winner.len() {
4987                        return Err(runtime_error(
4988                            "resident schema source mapping has the wrong length",
4989                        ));
4990                    }
4991                    ResidentOutputSchemaSelection::SourceWinner {
4992                        source_output,
4993                        schemas: selection.output_schemas_by_source_winner.clone(),
4994                    }
4995                } else {
4996                    ResidentOutputSchemaSelection::OwnWinner
4997                };
4998                Ok(ResidentOutputSchemaPlan {
4999                    candidates,
5000                    selection,
5001                })
5002            })
5003            .collect::<std::result::Result<Vec<_>, ResidentGraphExecutionError>>()?;
5004        if let Some(diagnostics) = diagnostics.as_mut() {
5005            let metadata_binding_ns = resident_prepare_elapsed_ns(
5006                metadata_binding_started.expect("diagnostic timer exists when enabled"),
5007            );
5008            diagnostics.metadata_binding_construction_ns = diagnostics
5009                .metadata_binding_construction_ns
5010                .saturating_add(metadata_binding_ns);
5011        }
5012        let device_trace_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
5013        let device_trace_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5014        let device_trace = self
5015            .provider
5016            .prepare_resident_device_trace_in_reservation(&mut reservation)
5017            .map_err(runtime_error)?;
5018        if let Some(diagnostics) = diagnostics.as_mut() {
5019            let device_trace_ns = resident_prepare_elapsed_ns(
5020                device_trace_started.expect("diagnostic timer exists when enabled"),
5021            );
5022            let device_trace_reserved_bytes = reservation.used_bytes().saturating_sub(
5023                device_trace_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
5024            );
5025            diagnostics.metadata_provider_calls =
5026                diagnostics.metadata_provider_calls.saturating_add(1);
5027            diagnostics.device_trace_preparation_ns = device_trace_ns;
5028            diagnostics.device_trace_reserved_bytes = device_trace_reserved_bytes;
5029        }
5030        let schema_defaults_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5031        let default_schema_ids =
5032            resident_compact_schema_defaults(&compact_schedule.ops, output_relations.len())
5033                .map_err(runtime_error)?;
5034        if let Some(diagnostics) = diagnostics.as_mut() {
5035            let schema_defaults_ns = resident_prepare_elapsed_ns(
5036                schema_defaults_started.expect("diagnostic timer exists when enabled"),
5037            );
5038            diagnostics.metadata_binding_construction_ns = diagnostics
5039                .metadata_binding_construction_ns
5040                .saturating_add(schema_defaults_ns);
5041        }
5042        let schema_winners_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
5043        let schema_winners_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5044        let schema_winners = self
5045            .provider
5046            .prepare_resident_schema_winners_in_reservation(&default_schema_ids, &mut reservation)
5047            .map_err(runtime_error)?;
5048        if let Some(diagnostics) = diagnostics.as_mut() {
5049            let schema_winners_ns = resident_prepare_elapsed_ns(
5050                schema_winners_started.expect("diagnostic timer exists when enabled"),
5051            );
5052            let schema_winners_reserved_bytes = reservation.used_bytes().saturating_sub(
5053                schema_winners_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
5054            );
5055            diagnostics.metadata_provider_calls =
5056                diagnostics.metadata_provider_calls.saturating_add(1);
5057            diagnostics.schema_winners_preparation_ns = schema_winners_ns;
5058            diagnostics.schema_winners_reserved_bytes = schema_winners_reserved_bytes;
5059            (
5060                diagnostics.schema_winners_initial_htod_calls,
5061                diagnostics.schema_winners_initial_htod_bytes,
5062            ) = resident_schema_winners_initial_htod(default_schema_ids.len());
5063        }
5064        let receipt_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
5065        let receipt_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5066        let receipt = self
5067            .provider
5068            .prepare_resident_packed_receipt_with_trace_and_schema_winners_in_reservation(
5069                &output_relations,
5070                &device_trace,
5071                &schema_winners,
5072                &mut reservation,
5073            )
5074            .map_err(runtime_error)?;
5075        if let Some(diagnostics) = diagnostics.as_mut() {
5076            let receipt_ns = resident_prepare_elapsed_ns(
5077                receipt_started.expect("diagnostic timer exists when enabled"),
5078            );
5079            let receipt_reserved_bytes = reservation.used_bytes().saturating_sub(
5080                receipt_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
5081            );
5082            diagnostics.metadata_provider_calls =
5083                diagnostics.metadata_provider_calls.saturating_add(1);
5084            diagnostics.receipt_preparation_ns = receipt_ns;
5085            diagnostics.receipt_reserved_bytes = receipt_reserved_bytes;
5086            (
5087                diagnostics.receipt_initial_htod_calls,
5088                diagnostics.receipt_initial_htod_bytes,
5089            ) = resident_receipt_initial_htod(output_relations.len());
5090        }
5091        let schedule_bindings_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5092        let mut schedule_bindings = Vec::with_capacity(
5093            manifest
5094                .slots
5095                .len()
5096                .checked_add(compact_schedule.source_slots.len())
5097                .ok_or_else(|| runtime_error("resident compact slot binding count overflow"))?,
5098        );
5099        for (slot_index, slot) in manifest.slots.iter().enumerate() {
5100            let relation =
5101                private_relation(&physical.relations, slot_index).map_err(runtime_error)?;
5102            schedule_bindings.push(if slot.permanent {
5103                ResidentScheduleSlotBinding::permanent(relation.buffer(), 0)
5104            } else {
5105                ResidentScheduleSlotBinding::scratch(relation.buffer(), 0)
5106            });
5107        }
5108        for (name, slot) in &compact_schedule.source_slots {
5109            let expected_slot = u32::try_from(schedule_bindings.len())
5110                .map_err(|_| runtime_error("resident compact slot binding count exceeds u32"))?;
5111            if *slot != expected_slot {
5112                return Err(runtime_error(
5113                    "resident compact source bindings are not contiguous",
5114                ));
5115            }
5116            let source = self.store.get(name).ok_or_else(|| {
5117                runtime_error(format!("resident source {name} disappeared during prepare"))
5118            })?;
5119            schedule_bindings
5120                .push(ResidentScheduleSlotBinding::source(source, 0).map_err(runtime_error)?);
5121        }
5122        let receipt_slots = output_indices
5123            .iter()
5124            .map(|(_, slot)| {
5125                u32::try_from(*slot)
5126                    .map_err(|_| runtime_error("resident receipt slot index exceeds u32"))
5127            })
5128            .collect::<std::result::Result<Vec<_>, _>>()?;
5129        if let Some(diagnostics) = diagnostics.as_mut() {
5130            let schedule_bindings_ns = resident_prepare_elapsed_ns(
5131                schedule_bindings_started.expect("diagnostic timer exists when enabled"),
5132            );
5133            diagnostics.metadata_binding_construction_ns = diagnostics
5134                .metadata_binding_construction_ns
5135                .saturating_add(schedule_bindings_ns);
5136        }
5137        let schedule_program_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
5138        let schedule_program_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5139        let schedule_program = self
5140            .provider
5141            .prepare_resident_schedule_program_in_reservation(
5142                &execution_domain,
5143                &schedule_bindings,
5144                &compact_schedule.ops,
5145                &compact_schedule.waves,
5146                &compact_schedule.regions,
5147                &compact_schedule.generation_bases,
5148                &compact_schedule.filter_comparisons,
5149                &compact_schedule.project_expressions,
5150                &receipt_slots,
5151                ResidentScheduleExternalBindings::new(
5152                    physical.filter_scratch.as_ref(),
5153                    &physical.set_workspace,
5154                    &physical.join_workspace,
5155                    &physical.control,
5156                    &device_trace,
5157                    &schema_winners,
5158                    &receipt,
5159                ),
5160                &mut reservation,
5161            )
5162            .map_err(runtime_error)?;
5163        if let Some(diagnostics) = diagnostics.as_mut() {
5164            let schedule_program_ns = resident_prepare_elapsed_ns(
5165                schedule_program_started.expect("diagnostic timer exists when enabled"),
5166            );
5167            let schedule_program_reserved_bytes = reservation.used_bytes().saturating_sub(
5168                schedule_program_bytes_before
5169                    .expect("diagnostic byte snapshot exists when enabled"),
5170            );
5171            diagnostics.metadata_provider_calls =
5172                diagnostics.metadata_provider_calls.saturating_add(1);
5173            diagnostics.schedule_program_preparation_ns = schedule_program_ns;
5174            diagnostics.schedule_program_reserved_bytes = schedule_program_reserved_bytes;
5175            (
5176                diagnostics.schedule_program_initial_htod_calls,
5177                diagnostics.schedule_program_initial_htod_bytes,
5178            ) = resident_schedule_initial_htod(diagnostics.schedule_program_reserved_bytes);
5179        }
5180        let reservation_validation_started =
5181            diagnostics.as_ref().map(|_| std::time::Instant::now());
5182        let tracked_device_allocation_bytes = reservation.used_bytes();
5183        resident_validate_exact_reservation(
5184            manifest.required_bytes,
5185            tracked_device_allocation_bytes,
5186            reservation.remaining_bytes(),
5187        )
5188        .map_err(runtime_error)?;
5189        drop(reservation);
5190        if let Some(diagnostics) = diagnostics.as_mut() {
5191            diagnostics.reservation_validation_and_release_ns = resident_prepare_elapsed_ns(
5192                reservation_validation_started.expect("diagnostic timer exists when enabled"),
5193            );
5194        }
5195        let pinned_receipt_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5196        let pinned_receipt = self
5197            .provider
5198            .prepare_resident_pinned_receipt(&receipt)
5199            .map_err(runtime_error)?;
5200        if let Some(diagnostics) = diagnostics.as_mut() {
5201            diagnostics.pinned_receipt_ns = resident_prepare_elapsed_ns(
5202                pinned_receipt_started.expect("diagnostic timer exists when enabled"),
5203            );
5204        }
5205
5206        // Build the same complete owner set inside capture and before replay.
5207        // No live admission may cross BeginCapture.
5208        let record_launch = || {
5209            let mut recorder = execution_domain.new_strict_recorder();
5210            for binding in &schedule_bindings {
5211                binding.record_uses(&mut recorder);
5212            }
5213            ResidentScheduleExternalBindings::new(
5214                physical.filter_scratch.as_ref(),
5215                &physical.set_workspace,
5216                &physical.join_workspace,
5217                &physical.control,
5218                &device_trace,
5219                &schema_winners,
5220                &receipt,
5221            )
5222            .record_uses(&mut recorder);
5223            schedule_program.record_uses(&mut recorder);
5224            recorder
5225        };
5226        let graph_capture_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5227        let mut sequence =
5228            ConditionalCudaGraphSequenceBuilder::new(&stream).map_err(conditional_graph_error)?;
5229        let capture_topology =
5230            resident_compact_topology(&compact_schedule.regions).map_err(runtime_error)?;
5231        let mut conditional_body_node_kinds =
5232            Vec::with_capacity(capture_topology.conditional_body_kernel_counts.len());
5233        for ((region_index, region), parent_kind) in compact_schedule
5234            .regions
5235            .iter()
5236            .enumerate()
5237            .zip(capture_topology.parent_kinds.iter().copied())
5238        {
5239            let region_index = u32::try_from(region_index)
5240                .map_err(|_| runtime_error("resident compact region index exceeds u32"))?;
5241            if parent_kind == ResidentCaptureParentKind::Conditional {
5242                sequence
5243                    .add_conditional_while(u32::from(region.iteration_limit != 0), true, |body| {
5244                        body.capture_on_stream(&stream, || unsafe {
5245                            execution_domain
5246                                .enqueue(record_launch(), |enqueue| {
5247                                    self.provider.record_resident_schedule_region_on_stream(
5248                                        &schedule_program,
5249                                        region_index,
5250                                        Some(body),
5251                                        enqueue,
5252                                    )
5253                                })
5254                                .map_err(|error| xlog_core::XlogError::Kernel(error.to_string()))?
5255                                .commit()
5256                        })?;
5257                        conditional_body_node_kinds.push(body.linear_chain_node_kinds()?);
5258                        Ok(())
5259                    })
5260                    .map_err(conditional_graph_error)?;
5261            } else {
5262                sequence
5263                    .capture_segment_on_stream(&stream, || unsafe {
5264                        execution_domain
5265                            .enqueue(record_launch(), |enqueue| {
5266                                self.provider.record_resident_schedule_region_on_stream(
5267                                    &schedule_program,
5268                                    region_index,
5269                                    None,
5270                                    enqueue,
5271                                )
5272                            })
5273                            .map_err(|error| xlog_core::XlogError::Kernel(error.to_string()))?
5274                            .commit()
5275                    })
5276                    .map_err(conditional_graph_error)?;
5277            }
5278        }
5279        if let Some(diagnostics) = diagnostics.as_mut() {
5280            diagnostics.graph_body_capture_ns = resident_prepare_elapsed_ns(
5281                graph_capture_started.expect("diagnostic timer exists when enabled"),
5282            );
5283        }
5284        let graph_instantiate_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5285        let graph = sequence
5286            .instantiate()
5287            .map_err(conditional_graph_error)?
5288            .bind_resident_lifecycle(&runtime);
5289        if let Some(diagnostics) = diagnostics.as_mut() {
5290            diagnostics.graph_instantiate_ns = resident_prepare_elapsed_ns(
5291                graph_instantiate_started.expect("diagnostic timer exists when enabled"),
5292            );
5293        }
5294
5295        let validation_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5296        let recorder = record_launch();
5297
5298        let graph_node_kinds = graph.linear_chain_node_kinds().map_err(runtime_error)?;
5299        resident_validate_parent_graph_kinds(&graph_node_kinds, &capture_topology.parent_kinds)
5300            .map_err(runtime_error)?;
5301        let conditional_body_kernel_counts = resident_validate_conditional_body_node_kinds(
5302            &conditional_body_node_kinds,
5303            capture_topology.conditional_body_kernel_counts.len(),
5304        )
5305        .map_err(runtime_error)?;
5306        if conditional_body_kernel_counts != capture_topology.conditional_body_kernel_counts {
5307            return Err(runtime_error(
5308                "resident conditional body topology differs from the compact schedule",
5309            ));
5310        }
5311        let body_node_count =
5312            conditional_body_node_kinds
5313                .iter()
5314                .try_fold(0usize, |total, kinds| {
5315                    total.checked_add(kinds.len()).ok_or_else(|| {
5316                        runtime_error("resident hierarchical graph node count overflow")
5317                    })
5318                })?;
5319        let hierarchical_graph_nodes = graph_node_kinds
5320            .len()
5321            .checked_add(body_node_count)
5322            .ok_or_else(|| runtime_error("resident hierarchical graph node count overflow"))?;
5323        if hierarchical_graph_nodes != capture_topology.hierarchical_node_count {
5324            return Err(runtime_error(
5325                "resident hierarchical graph topology differs from the compact schedule",
5326            ));
5327        }
5328        let (
5329            filter_descriptor_device_bytes,
5330            project_descriptor_device_bytes,
5331            fixed_workspace_device_bytes,
5332        ) = resident_compact_preflight_device_bytes(&manifest, &compact_schedule)
5333            .map_err(runtime_error)?;
5334        let parent_graph_node_count = graph_node_kinds.len();
5335        let preflight_report = ResidentGraphPreflightReport {
5336            relation_capacity: admission.relation_capacity,
5337            estimated_required_bytes: manifest.required_bytes,
5338            available_bytes_at_admission: manifest_available_bytes,
5339            tracked_device_allocation_bytes,
5340            relation_device_bytes: manifest.relation_bytes,
5341            filter_descriptor_device_bytes,
5342            filter_scratch_device_bytes: manifest.filter_scratch_bytes,
5343            project_descriptor_device_bytes,
5344            fixed_workspace_device_bytes,
5345            parent_graph_nodes: graph_node_kinds.len(),
5346            conditional_while_nodes: graph_node_kinds
5347                .iter()
5348                .filter(|kind| **kind == CudaGraphNodeKind::Conditional)
5349                .count(),
5350            parent_graph_node_kinds: graph_node_kinds,
5351            conditional_body_node_kinds,
5352            conditional_body_kernel_counts,
5353            hierarchical_graph_nodes,
5354            private_relation_slots: physical.relations.len(),
5355            logical_relation_values: manifest.logical_relation_values,
5356            permanent_relation_slots: manifest.permanent_slots,
5357            staged_output_relations: output_indices.len(),
5358            scratch_slots: manifest.scratch_slots,
5359            filter_scratch_allocations: manifest.filter_scratch_allocations,
5360            max_row_bytes: manifest.max_row_bytes,
5361        };
5362        let has_device_status_writer = build.injection_recorded;
5363        let owners = ResidentRunOwners {
5364            provider: Arc::clone(&self.provider),
5365            runtime,
5366            stream,
5367            graph,
5368            execution_domain,
5369            _schedule_program: schedule_program,
5370            recorder,
5371            relations: physical.relations,
5372            source_binding_names: compact_schedule.source_slots.keys().cloned().collect(),
5373            output_indices,
5374            _filter_scratch: physical.filter_scratch,
5375            _set_workspace: physical.set_workspace,
5376            _join_workspace: physical.join_workspace,
5377            _control: physical.control,
5378            _device_trace: device_trace,
5379            _schema_winners: schema_winners,
5380            receipt,
5381            pinned_receipt,
5382            source_epoch,
5383            relation_registration,
5384            transaction_identity: Arc::clone(&self.transaction_identity),
5385            output_schema_plans,
5386        };
5387        let validation_owner_assembly_ns = diagnostics.as_ref().map(|_| {
5388            resident_prepare_elapsed_ns(
5389                validation_started.expect("diagnostic timer exists when enabled"),
5390            )
5391        });
5392        let prepare_total_ns = diagnostics.as_ref().map(|_| {
5393            resident_prepare_elapsed_ns(
5394                total_started.expect("diagnostic total timer exists when enabled"),
5395            )
5396        });
5397        let prepare_diagnostic = diagnostics.map(|mut diagnostics| {
5398            diagnostics.total_ns =
5399                prepare_total_ns.expect("diagnostic total sample exists when enabled");
5400            diagnostics.validation_owner_assembly_ns = validation_owner_assembly_ns
5401                .expect("diagnostic validation sample exists when enabled");
5402            diagnostics.parent_graph_nodes = parent_graph_node_count;
5403            diagnostics.conditional_body_nodes = body_node_count;
5404            diagnostics
5405        });
5406        Ok(PreparedResidentGraph {
5407            owners,
5408            preflight_report,
5409            has_device_status_writer,
5410            source_set_snapshots,
5411            source_buffers_to_restore: Vec::new(),
5412            prepare_diagnostic,
5413        })
5414    }
5415
5416    fn resident_schema_variants(
5417        &self,
5418        node: &RirNode,
5419        certificate: &ResidentGraphRouteCertificate,
5420        head_schema_choices: &BTreeMap<String, Vec<Schema>>,
5421    ) -> std::result::Result<ResidentSchemaVariants, ResidentGraphDeclineReason> {
5422        match node {
5423            RirNode::Unit => certificate
5424                .node_schema(node)
5425                .map(ResidentSchemaVariants::Fixed)
5426                .ok_or_else(|| resident_workspace_decline("resident node schema missing")),
5427            RirNode::Scan { rel } => {
5428                let name = self.rel_names.get(rel).ok_or_else(|| {
5429                    resident_workspace_decline(format!(
5430                        "resident scan relation {rel:?} is not registered"
5431                    ))
5432                })?;
5433                if let Some(schemas) = head_schema_choices.get(name) {
5434                    return resident_schema_variants_from_source(name.clone(), schemas.clone());
5435                }
5436                let schema = self
5437                    .store
5438                    .get(name)
5439                    .map(|buffer| buffer.schema().clone())
5440                    .or_else(|| certificate.schema_for(*rel).cloned())
5441                    .ok_or_else(|| {
5442                        resident_workspace_decline(format!("resident scan {name} has no schema"))
5443                    })?;
5444                Ok(ResidentSchemaVariants::Fixed(schema))
5445            }
5446            RirNode::Filter { input, .. } | RirNode::Distinct { input, .. } => {
5447                self.resident_schema_variants(input, certificate, head_schema_choices)
5448            }
5449            RirNode::Project { input, columns } => {
5450                match self.resident_schema_variants(input, certificate, head_schema_choices)? {
5451                    ResidentSchemaVariants::Fixed(schema) => self
5452                        .project_schema(&schema, columns)
5453                        .map(ResidentSchemaVariants::Fixed)
5454                        .map_err(|error| resident_workspace_decline(error.to_string())),
5455                    ResidentSchemaVariants::Dynamic {
5456                        source_head,
5457                        schemas,
5458                    } => {
5459                        let schemas = schemas
5460                            .iter()
5461                            .map(|schema| self.project_schema(schema, columns))
5462                            .collect::<Result<Vec<_>>>()
5463                            .map_err(|error| resident_workspace_decline(error.to_string()))?;
5464                        resident_schema_variants_from_source(source_head, schemas)
5465                    }
5466                }
5467            }
5468            RirNode::Join {
5469                left,
5470                right,
5471                join_type,
5472                ..
5473            } => {
5474                let left = self.resident_schema_variants(left, certificate, head_schema_choices)?;
5475                match join_type {
5476                    JoinType::Semi => Ok(left),
5477                    JoinType::Inner => {
5478                        let right =
5479                            self.resident_schema_variants(right, certificate, head_schema_choices)?;
5480                        match (left, right) {
5481                            (
5482                                ResidentSchemaVariants::Fixed(left),
5483                                ResidentSchemaVariants::Fixed(right),
5484                            ) => {
5485                                let mut columns = left.columns;
5486                                columns.extend(right.columns);
5487                                Ok(ResidentSchemaVariants::Fixed(Schema::new(columns)))
5488                            }
5489                            (
5490                                ResidentSchemaVariants::Dynamic {
5491                                    source_head,
5492                                    schemas,
5493                                },
5494                                ResidentSchemaVariants::Fixed(right),
5495                            ) => {
5496                                let schemas = schemas
5497                                    .into_iter()
5498                                    .map(|left| {
5499                                        let mut columns = left.columns;
5500                                        columns.extend(right.columns.iter().cloned());
5501                                        Schema::new(columns)
5502                                    })
5503                                    .collect();
5504                                resident_schema_variants_from_source(source_head, schemas)
5505                            }
5506                            (
5507                                ResidentSchemaVariants::Fixed(left),
5508                                ResidentSchemaVariants::Dynamic {
5509                                    source_head,
5510                                    schemas,
5511                                },
5512                            ) => {
5513                                let schemas = schemas
5514                                    .into_iter()
5515                                    .map(|right| {
5516                                        let mut columns = left.columns.clone();
5517                                        columns.extend(right.columns);
5518                                        Schema::new(columns)
5519                                    })
5520                                    .collect();
5521                                resident_schema_variants_from_source(source_head, schemas)
5522                            }
5523                            (
5524                                ResidentSchemaVariants::Dynamic {
5525                                    source_head: left_source,
5526                                    schemas: left_schemas,
5527                                },
5528                                ResidentSchemaVariants::Dynamic {
5529                                    source_head: right_source,
5530                                    schemas: right_schemas,
5531                                },
5532                            ) => {
5533                                if left_source != right_source
5534                                    || left_schemas.len() != right_schemas.len()
5535                                {
5536                                    return Err(resident_workspace_decline(
5537                                        "resident join has multiple schema sources",
5538                                    ));
5539                                }
5540                                let schemas = left_schemas
5541                                    .into_iter()
5542                                    .zip(right_schemas)
5543                                    .map(|(left, right)| {
5544                                        let mut columns = left.columns;
5545                                        columns.extend(right.columns);
5546                                        Schema::new(columns)
5547                                    })
5548                                    .collect();
5549                                resident_schema_variants_from_source(left_source, schemas)
5550                            }
5551                        }
5552                    }
5553                    _ => Err(resident_workspace_decline(
5554                        "uncertified join reached resident schema planning",
5555                    )),
5556                }
5557            }
5558            RirNode::ChainJoin { fallback, .. } | RirNode::MultiWayJoin { fallback, .. } => {
5559                self.resident_schema_variants(fallback, certificate, head_schema_choices)
5560            }
5561            RirNode::Diff { left, .. } => {
5562                self.resident_schema_variants(left, certificate, head_schema_choices)
5563            }
5564            RirNode::Union { inputs } => {
5565                if inputs.is_empty() {
5566                    return Err(resident_workspace_decline(
5567                        "resident schema union has no inputs",
5568                    ));
5569                }
5570                let mut fixed = None;
5571                for input in inputs {
5572                    match self.resident_schema_variants(input, certificate, head_schema_choices)? {
5573                        ResidentSchemaVariants::Fixed(schema) => match &fixed {
5574                            Some(existing) if existing != &schema => {
5575                                return Err(resident_workspace_decline(
5576                                    "resident schema union has ambiguous input schemas",
5577                                ));
5578                            }
5579                            Some(_) => {}
5580                            None => fixed = Some(schema),
5581                        },
5582                        ResidentSchemaVariants::Dynamic { .. } => {
5583                            return Err(resident_workspace_decline(
5584                                "resident schema union has ambiguous dynamic lineage",
5585                            ));
5586                        }
5587                    }
5588                }
5589                fixed.map(ResidentSchemaVariants::Fixed).ok_or_else(|| {
5590                    resident_workspace_decline("resident schema union has no inputs")
5591                })
5592            }
5593            RirNode::Fixpoint { .. }
5594            | RirNode::GroupBy { .. }
5595            | RirNode::TensorMaskedJoin { .. } => Err(resident_workspace_decline(
5596                "unsupported node reached resident schema planning",
5597            )),
5598        }
5599    }
5600
5601    fn resident_workspace_admission_after_certification(
5602        &self,
5603        plan: &ExecutionPlan,
5604        certificate: &ResidentGraphRouteCertificate,
5605    ) -> std::result::Result<ResidentWorkspaceAdmission, ResidentGraphDeclineReason> {
5606        for rule in plan.rules_by_scc.iter().flatten() {
5607            validate_compact_resident_node_envelope(&rule.body, &|node| {
5608                certificate.node_schema(node).ok_or_else(|| {
5609                    resident_workspace_decline("compact resident node schema is missing")
5610                })
5611            })?;
5612        }
5613
5614        let generated_query_heads = resident_generated_query_heads(plan)?;
5615
5616        let mut generated_query_occurrences = generated_query_heads
5617            .iter()
5618            .map(|head| (head.as_str(), 0usize))
5619            .collect::<BTreeMap<_, _>>();
5620        for rule in plan.rules_by_scc.iter().flatten() {
5621            if let Some(count) = generated_query_occurrences.get_mut(rule.head.as_str()) {
5622                *count = count.checked_add(1).ok_or_else(|| {
5623                    resident_workspace_decline(format!(
5624                        "generated query head {} rule count overflowed",
5625                        rule.head
5626                    ))
5627                })?;
5628            }
5629        }
5630        for (head, count) in generated_query_occurrences {
5631            if count != 1 {
5632                return Err(resident_workspace_decline(format!(
5633                    "generated query head {head} must have exactly one compiled rule, found {count}"
5634                )));
5635            }
5636        }
5637
5638        let (required_row_capacity, capacity_witness) =
5639            self.resident_required_row_capacity(plan, certificate)?;
5640        let required_row_capacity = required_row_capacity.max(1);
5641        let required_row_capacity = u32::try_from(required_row_capacity).map_err(|_| {
5642            ResidentGraphDeclineReason::WorkspaceUnbounded {
5643                detail: format!(
5644                    "certified resident row bound {required_row_capacity} exceeds the u32 count ABI; witness: {capacity_witness}"
5645                ),
5646            }
5647        })?;
5648        let relation_capacity = checked_capacity_class(required_row_capacity).map_err(|error| {
5649            ResidentGraphDeclineReason::WorkspaceUnbounded {
5650                detail: format!(
5651                    "{error}; certified_required_rows={required_row_capacity}; witness: {capacity_witness}"
5652                ),
5653            }
5654        })?;
5655
5656        let mut head_schemas = BTreeMap::<String, Schema>::new();
5657        for rules in &plan.rules_by_scc {
5658            for rule in rules {
5659                match head_schemas.get(&rule.head) {
5660                    Some(existing) if existing != &rule.meta.schema => {
5661                        return Err(ResidentGraphDeclineReason::WorkspaceUnbounded {
5662                            detail: format!(
5663                                "relation {} has inconsistent compiled head schemas",
5664                                rule.head
5665                            ),
5666                        });
5667                    }
5668                    Some(_) => {}
5669                    None => {
5670                        head_schemas.insert(rule.head.clone(), rule.meta.schema.clone());
5671                    }
5672                }
5673            }
5674        }
5675
5676        let mut head_schema_choices = head_schemas
5677            .keys()
5678            .map(|name| {
5679                let mut candidates = Vec::new();
5680                if let Some(existing) = self.store.get(name) {
5681                    let physical = &head_schemas[name];
5682                    if !resident_schemas_type_compatible(existing.schema(), physical) {
5683                        return Err(resident_workspace_decline(format!(
5684                            "existing resident head {name} is physically incompatible with its compiled schema"
5685                        )));
5686                    }
5687                    if !(generated_query_heads.contains(name)
5688                        && existing.cached_row_count() == Some(0))
5689                    {
5690                        candidates.push(existing.schema().clone());
5691                    }
5692                }
5693                Ok((name.clone(), candidates))
5694            })
5695            .collect::<std::result::Result<BTreeMap<_, _>, _>>()?;
5696        let mut rule_schema_ids = plan
5697            .rules_by_scc
5698            .iter()
5699            .map(|rules| vec![None; rules.len()])
5700            .collect::<Vec<_>>();
5701        let mut head_schema_selections = BTreeMap::new();
5702
5703        for (scc_index, rules) in plan.rules_by_scc.iter().enumerate() {
5704            let recursive = plan.sccs.get(scc_index).is_some_and(|scc| scc.is_recursive);
5705            for (rule_index, rule) in rules.iter().enumerate() {
5706                if generated_query_heads.contains(&rule.head) {
5707                    continue;
5708                }
5709                let variants =
5710                    self.resident_schema_variants(&rule.body, certificate, &head_schema_choices)?;
5711                let physical = &head_schemas[&rule.head];
5712                let validate_schema = |schema: &Schema| {
5713                    if !resident_schemas_type_compatible(schema, physical) {
5714                        return Err(resident_workspace_decline(format!(
5715                            "resident rule contribution for {} is physically incompatible with its compiled head",
5716                            rule.head
5717                        )));
5718                    }
5719                    if recursive {
5720                        let rel = self.name_to_rel.get(&rule.head).ok_or_else(|| {
5721                            resident_workspace_decline(format!(
5722                                "recursive resident head {} is not registered",
5723                                rule.head
5724                            ))
5725                        })?;
5726                        let candidate = certificate.schema_for(*rel).ok_or_else(|| {
5727                            resident_workspace_decline(format!(
5728                                "recursive resident head {} has no catalog schema candidate",
5729                                rule.head
5730                            ))
5731                        })?;
5732                        if !resident_schemas_type_compatible(schema, candidate) {
5733                            return Err(resident_workspace_decline(format!(
5734                                "recursive resident head {} contribution schema is physically incompatible with its catalog candidate: contribution={schema:?}, catalog={candidate:?}",
5735                                rule.head
5736                            )));
5737                        }
5738                        if schema.key_columns != candidate.key_columns {
5739                            return Err(resident_workspace_decline(format!(
5740                                "recursive resident head {} contribution key columns do not equal its catalog candidate: contribution={:?}, catalog={:?}",
5741                                rule.head, schema.key_columns, candidate.key_columns
5742                            )));
5743                        }
5744                    }
5745                    Ok(())
5746                };
5747                match &variants {
5748                    ResidentSchemaVariants::Fixed(schema) => validate_schema(schema)?,
5749                    ResidentSchemaVariants::Dynamic { schemas, .. } => {
5750                        for schema in schemas {
5751                            validate_schema(schema)?;
5752                        }
5753                    }
5754                }
5755                match variants {
5756                    ResidentSchemaVariants::Fixed(schema) => {
5757                        let schema_id = resident_intern_schema(
5758                            head_schema_choices
5759                                .get_mut(&rule.head)
5760                                .expect("compiled head choice exists"),
5761                            schema,
5762                        )
5763                        .map_err(|error| resident_workspace_decline(error.to_string()))?;
5764                        rule_schema_ids[scc_index][rule_index] = Some(schema_id);
5765                    }
5766                    ResidentSchemaVariants::Dynamic {
5767                        source_head,
5768                        schemas,
5769                    } => {
5770                        resident_register_schema_selection(
5771                            &rule.head,
5772                            ResidentHeadSchemaSelection {
5773                                source_head,
5774                                output_schemas_by_source_winner: schemas.clone(),
5775                            },
5776                            &head_schema_choices,
5777                            &mut head_schema_selections,
5778                        )?;
5779                        for schema in schemas {
5780                            resident_intern_schema(
5781                                head_schema_choices
5782                                    .get_mut(&rule.head)
5783                                    .expect("compiled head choice exists"),
5784                                schema,
5785                            )
5786                            .map_err(|error| resident_workspace_decline(error.to_string()))?;
5787                        }
5788                        rule_schema_ids[scc_index][rule_index] = Some(RESIDENT_DYNAMIC_SCHEMA_ID);
5789                    }
5790                }
5791            }
5792        }
5793
5794        let mut seen_query_heads = HashSet::new();
5795        for (scc_index, rules) in plan.rules_by_scc.iter().enumerate() {
5796            for (rule_index, rule) in rules.iter().enumerate() {
5797                if !generated_query_heads.contains(&rule.head) {
5798                    continue;
5799                }
5800                if !seen_query_heads.insert(rule.head.clone()) {
5801                    return Err(resident_workspace_decline(format!(
5802                        "synthetic query head {} has more than one compiled rule",
5803                        rule.head
5804                    )));
5805                }
5806                let variants =
5807                    self.resident_schema_variants(&rule.body, certificate, &head_schema_choices)?;
5808                let physical = &head_schemas[&rule.head];
5809                match variants {
5810                    ResidentSchemaVariants::Fixed(schema) => {
5811                        if !resident_schemas_type_compatible(&schema, physical) {
5812                            return Err(resident_workspace_decline(format!(
5813                                "synthetic query head {} is physically incompatible with its compiled head",
5814                                rule.head
5815                            )));
5816                        }
5817                        let schema_id = resident_intern_schema(
5818                            head_schema_choices
5819                                .get_mut(&rule.head)
5820                                .expect("compiled query head choice exists"),
5821                            schema,
5822                        )
5823                        .map_err(|error| resident_workspace_decline(error.to_string()))?;
5824                        rule_schema_ids[scc_index][rule_index] = Some(schema_id);
5825                    }
5826                    ResidentSchemaVariants::Dynamic {
5827                        source_head,
5828                        schemas,
5829                    } => {
5830                        if schemas
5831                            .iter()
5832                            .any(|schema| !resident_schemas_type_compatible(schema, physical))
5833                        {
5834                            return Err(resident_workspace_decline(format!(
5835                                "synthetic query head {} has a physically incompatible dynamic schema",
5836                                rule.head
5837                            )));
5838                        }
5839                        for schema in &schemas {
5840                            resident_intern_schema(
5841                                head_schema_choices
5842                                    .get_mut(&rule.head)
5843                                    .expect("compiled query head choice exists"),
5844                                schema.clone(),
5845                            )
5846                            .map_err(|error| resident_workspace_decline(error.to_string()))?;
5847                        }
5848                        rule_schema_ids[scc_index][rule_index] = Some(RESIDENT_DYNAMIC_SCHEMA_ID);
5849                        resident_register_schema_selection(
5850                            &rule.head,
5851                            ResidentHeadSchemaSelection {
5852                                source_head,
5853                                output_schemas_by_source_winner: schemas,
5854                            },
5855                            &head_schema_choices,
5856                            &mut head_schema_selections,
5857                        )?;
5858                    }
5859                }
5860            }
5861        }
5862
5863        for (name, choices) in &head_schema_choices {
5864            if choices.is_empty() {
5865                return Err(resident_workspace_decline(format!(
5866                    "resident head {name} has no schema candidate"
5867                )));
5868            }
5869        }
5870        Ok(ResidentWorkspaceAdmission {
5871            relation_capacity,
5872            head_schemas,
5873            head_schema_choices,
5874            rule_schema_ids,
5875            head_schema_selections,
5876        })
5877    }
5878
5879    fn resident_required_row_capacity(
5880        &self,
5881        plan: &ExecutionPlan,
5882        certificate: &ResidentGraphRouteCertificate,
5883    ) -> std::result::Result<(u64, String), ResidentGraphDeclineReason> {
5884        let mut relation_domains = HashMap::<RelId, ResidentColumnDomains>::new();
5885        let mut synthetic_domains = HashMap::<String, ResidentColumnDomains>::new();
5886        let mut source_rows = HashMap::<RelId, u64>::new();
5887        let plan_relations =
5888            resident_plan_relation_ids(plan.rules_by_scc.iter().flatten(), &self.name_to_rel);
5889        for (relation, name) in &self.rel_names {
5890            if !plan_relations.contains(relation) {
5891                continue;
5892            }
5893            let schema = certificate.schema_for(*relation).ok_or_else(|| {
5894                resident_workspace_decline(format!(
5895                    "relation {name} has no schema in the resident certificate"
5896                ))
5897            })?;
5898            let mut columns = vec![BTreeMap::new(); schema.arity()];
5899            let rows = self.store.get(name).map(CudaBuffer::num_rows).unwrap_or(0);
5900            if rows != 0 {
5901                for (column, domain) in columns.iter_mut().enumerate() {
5902                    domain.insert(format!("source:{}:{column}", relation.0), rows);
5903                }
5904            }
5905            relation_domains.insert(*relation, columns);
5906            source_rows.insert(*relation, rows);
5907        }
5908
5909        loop {
5910            let mut changed = false;
5911            for rules in &plan.rules_by_scc {
5912                for rule in rules {
5913                    let contribution = resident_node_domains(&rule.body, &relation_domains)?;
5914                    if contribution.len() != rule.meta.schema.arity() {
5915                        return Err(resident_workspace_decline(format!(
5916                            "resident rule head {} has {} certified domains for schema arity {}",
5917                            rule.head,
5918                            contribution.len(),
5919                            rule.meta.schema.arity()
5920                        )));
5921                    }
5922                    let target = if let Some(head) = self.name_to_rel.get(&rule.head) {
5923                        relation_domains.get_mut(head).ok_or_else(|| {
5924                            resident_workspace_decline(format!(
5925                                "resident rule head {} has no domain slot",
5926                                rule.head
5927                            ))
5928                        })?
5929                    } else {
5930                        synthetic_domains
5931                            .entry(rule.head.clone())
5932                            .or_insert_with(|| vec![BTreeMap::new(); rule.meta.schema.arity()])
5933                    };
5934                    changed |= merge_resident_domains(&rule.head, target, contribution)?;
5935                }
5936            }
5937            if !changed {
5938                break;
5939            }
5940        }
5941
5942        let mut relation_set_bounds = source_rows.clone();
5943        let mut synthetic_set_bounds = HashMap::<String, u64>::new();
5944        let mut required = source_rows.values().copied().max().unwrap_or(1).max(1);
5945        let mut witness = source_rows
5946            .iter()
5947            .max_by_key(|(_, rows)| *rows)
5948            .map(|(relation, rows)| {
5949                let name = self
5950                    .rel_names
5951                    .get(relation)
5952                    .map(String::as_str)
5953                    .unwrap_or("<unregistered>");
5954                format!("source relation {name} ({relation:?}) rows={rows}")
5955            })
5956            .unwrap_or_else(|| "unit relation bound=1".to_string());
5957
5958        let mut scc_by_id = HashMap::new();
5959        for (index, scc) in plan.sccs.iter().enumerate() {
5960            if scc_by_id.insert(scc.id, index).is_some() {
5961                return Err(resident_workspace_decline(format!(
5962                    "resident row proof found duplicate SCC id {}",
5963                    scc.id
5964                )));
5965            }
5966        }
5967        let ordered_sccs = if plan.strata.is_empty() {
5968            (0..plan.sccs.len()).collect::<Vec<_>>()
5969        } else {
5970            let mut ordered = Vec::new();
5971            for stratum in &plan.strata {
5972                for scc_id in &stratum.sccs {
5973                    ordered.push(*scc_by_id.get(scc_id).ok_or_else(|| {
5974                        resident_workspace_decline(format!(
5975                            "resident row proof stratum {} references unknown SCC {scc_id}",
5976                            stratum.id
5977                        ))
5978                    })?);
5979                }
5980            }
5981            ordered
5982        };
5983        let mut seen_sccs = HashSet::new();
5984        for scc_index in ordered_sccs {
5985            if !seen_sccs.insert(scc_index) {
5986                return Err(resident_workspace_decline(format!(
5987                    "resident row proof schedule repeats SCC {}",
5988                    plan.sccs[scc_index].id
5989                )));
5990            }
5991            let scc = &plan.sccs[scc_index];
5992            let rules = plan.rules_by_scc.get(scc_index).ok_or_else(|| {
5993                resident_workspace_decline(format!(
5994                    "resident row proof SCC {scc_index} has no rule vector"
5995                ))
5996            })?;
5997
5998            if scc.is_recursive {
5999                for name in &scc.predicates {
6000                    let relation = self.name_to_rel.get(name).ok_or_else(|| {
6001                        resident_workspace_decline(format!(
6002                            "recursive relation {name} has no registered relation id"
6003                        ))
6004                    })?;
6005                    let columns = relation_domains.get(relation).ok_or_else(|| {
6006                        resident_workspace_decline(format!(
6007                            "recursive relation {relation:?} has no active-domain proof"
6008                        ))
6009                    })?;
6010                    let context = format!("recursive relation {name} ({relation:?})");
6011                    let finite_cap = resident_domain_product(columns, &context)?;
6012                    relation_set_bounds.insert(*relation, finite_cap);
6013                    if finite_cap > required {
6014                        required = finite_cap;
6015                        witness = format!(
6016                            "{context} finite_domain_cap={finite_cap} domains={}",
6017                            resident_domain_description(columns)
6018                        );
6019                    }
6020                }
6021            }
6022
6023            let mut additions = BTreeMap::<String, u64>::new();
6024            let mut raw_additions = BTreeMap::<String, u64>::new();
6025            let mut addition_witnesses = BTreeMap::<String, String>::new();
6026            for (rule_index, rule) in rules.iter().enumerate() {
6027                let path = format!("scc[{scc_index}].rule[{rule_index}] head={}", rule.head);
6028                let proof = resident_node_row_bound(
6029                    &rule.body,
6030                    &source_rows,
6031                    &relation_set_bounds,
6032                    &relation_domains,
6033                    &path,
6034                )?;
6035                let rows = proof.rows;
6036                let transient = proof.peak;
6037                let rule_domains = resident_node_domains(&rule.body, &relation_domains)?;
6038                let set_rows = resident_domain_product_capped(
6039                    &rule_domains,
6040                    rows,
6041                    &format!("{path} projected set"),
6042                )?;
6043                if transient > required {
6044                    required = transient;
6045                    witness = format!(
6046                        "{path} transient_bound={transient} proof={} body={:?}",
6047                        proof.peak_detail, rule.body
6048                    );
6049                }
6050                let total = additions.entry(rule.head.clone()).or_default();
6051                *total = total.checked_add(set_rows).ok_or_else(|| {
6052                    resident_workspace_decline(format!(
6053                        "{path} rule-output addition overflow: partial={total} addition={set_rows} raw_rows={rows}"
6054                    ))
6055                })?;
6056                let raw_total = raw_additions.entry(rule.head.clone()).or_default();
6057                *raw_total = raw_total.checked_add(rows).ok_or_else(|| {
6058                    resident_workspace_decline(format!(
6059                        "{path} raw rule-output addition overflow: partial={raw_total} addition={rows}"
6060                    ))
6061                })?;
6062                addition_witnesses.insert(
6063                    rule.head.clone(),
6064                    format!(
6065                        "{path} body={:?} raw_rows={rows} per_clause_set_cap={set_rows} domains={}",
6066                        rule.body,
6067                        resident_domain_description(&rule_domains)
6068                    ),
6069                );
6070            }
6071
6072            for (head, addition) in additions {
6073                let (current, relation) = if let Some(relation) = self.name_to_rel.get(&head) {
6074                    (
6075                        relation_set_bounds.get(relation).copied().unwrap_or(0),
6076                        Some(*relation),
6077                    )
6078                } else {
6079                    (synthetic_set_bounds.get(&head).copied().unwrap_or(0), None)
6080                };
6081                let path = addition_witnesses
6082                    .get(&head)
6083                    .map(String::as_str)
6084                    .unwrap_or("unknown rule");
6085                let raw_addition = raw_additions.get(&head).copied().unwrap_or(0);
6086                let raw_candidate = current.checked_add(raw_addition).ok_or_else(|| {
6087                    resident_workspace_decline(format!(
6088                        "{path} staged candidate addition overflow: prior={current} raw_rule_rows={raw_addition}"
6089                    ))
6090                })?;
6091                if raw_candidate > required {
6092                    required = raw_candidate;
6093                    witness = format!(
6094                        "{path} staged_candidate_bound={raw_candidate} prior={current} raw_rule_rows={raw_addition}"
6095                    );
6096                }
6097                if scc.is_recursive {
6098                    let candidate = current.checked_add(addition).ok_or_else(|| {
6099                        resident_workspace_decline(format!(
6100                            "{path} recursive set candidate addition overflow: finite_cap={current} set_rule_rows={addition}"
6101                        ))
6102                    })?;
6103                    if candidate > required {
6104                        required = candidate;
6105                        witness = format!(
6106                            "{path} recursive_candidate_bound={candidate} finite_cap={current} raw_rule_rows={addition}"
6107                        );
6108                    }
6109                } else {
6110                    let summed_bound = current.checked_add(addition).ok_or_else(|| {
6111                        resident_workspace_decline(format!(
6112                            "{path} nonrecursive set addition overflow: prior={current} rule_rows={addition}"
6113                        ))
6114                    })?;
6115                    let columns = if let Some(relation) = relation {
6116                        relation_domains.get(&relation)
6117                    } else {
6118                        synthetic_domains.get(&head)
6119                    }
6120                    .ok_or_else(|| {
6121                        resident_workspace_decline(format!(
6122                            "{path} nonrecursive head {head} has no active-domain proof"
6123                        ))
6124                    })?;
6125                    let set_bound = resident_domain_product_capped(
6126                        columns,
6127                        summed_bound,
6128                        &format!("{path} merged set {head}"),
6129                    )?;
6130                    if let Some(relation) = relation {
6131                        relation_set_bounds.insert(relation, set_bound);
6132                    } else {
6133                        synthetic_set_bounds.insert(head.clone(), set_bound);
6134                    }
6135                    if set_bound > required {
6136                        required = set_bound;
6137                        witness = format!(
6138                            "{path} nonrecursive_set_bound={set_bound} prior={current} rule_rows={addition}"
6139                        );
6140                    }
6141                }
6142            }
6143        }
6144        if seen_sccs.len() != plan.sccs.len() {
6145            return Err(resident_workspace_decline(
6146                "resident row proof schedule omits one or more SCCs",
6147            ));
6148        }
6149        Ok((required, witness))
6150    }
6151}
6152
6153type ResidentDomain = BTreeMap<String, u64>;
6154type ResidentColumnDomains = Vec<ResidentDomain>;
6155
6156fn resident_plan_relation_ids<'a>(
6157    rules: impl Iterator<Item = &'a CompiledRule>,
6158    name_to_rel: &HashMap<String, RelId>,
6159) -> HashSet<RelId> {
6160    let mut relations = HashSet::new();
6161    for rule in rules {
6162        relations.extend(rule.body.referenced_relations());
6163        if let Some(head) = name_to_rel.get(&rule.head) {
6164            relations.insert(*head);
6165        }
6166    }
6167    relations
6168}
6169
6170fn resident_workspace_decline(detail: impl Into<String>) -> ResidentGraphDeclineReason {
6171    ResidentGraphDeclineReason::WorkspaceUnbounded {
6172        detail: detail.into(),
6173    }
6174}
6175
6176fn merge_resident_domains(
6177    relation: &str,
6178    target: &mut ResidentColumnDomains,
6179    contribution: ResidentColumnDomains,
6180) -> std::result::Result<bool, ResidentGraphDeclineReason> {
6181    if target.len() != contribution.len() {
6182        return Err(resident_workspace_decline(format!(
6183            "resident rule head {relation} domain arity changed"
6184        )));
6185    }
6186    let mut changed = false;
6187    for (target_domain, contribution_domain) in target.iter_mut().zip(contribution) {
6188        for (source, bound) in contribution_domain {
6189            match target_domain.entry(source) {
6190                std::collections::btree_map::Entry::Vacant(entry) => {
6191                    entry.insert(bound);
6192                    changed = true;
6193                }
6194                std::collections::btree_map::Entry::Occupied(mut entry) if *entry.get() < bound => {
6195                    entry.insert(bound);
6196                    changed = true;
6197                }
6198                std::collections::btree_map::Entry::Occupied(_) => {}
6199            }
6200        }
6201    }
6202    Ok(changed)
6203}
6204
6205fn resident_node_domains(
6206    node: &RirNode,
6207    relations: &HashMap<RelId, ResidentColumnDomains>,
6208) -> std::result::Result<ResidentColumnDomains, ResidentGraphDeclineReason> {
6209    match node {
6210        RirNode::Unit => Ok(Vec::new()),
6211        RirNode::Scan { rel } => relations.get(rel).cloned().ok_or_else(|| {
6212            resident_workspace_decline(format!(
6213                "resident scan {rel:?} has no source-domain certificate"
6214            ))
6215        }),
6216        RirNode::Filter { input, predicate } => {
6217            let mut domains = resident_node_domains(input, relations)?;
6218            resident_refine_filter_domains(&mut domains, predicate)?;
6219            Ok(domains)
6220        }
6221        RirNode::Distinct { input, .. } => resident_node_domains(input, relations),
6222        RirNode::Project { input, columns } => {
6223            let input_domains = resident_node_domains(input, relations)?;
6224            resident_project_domains(&input_domains, columns)
6225        }
6226        RirNode::Join {
6227            left,
6228            right,
6229            join_type,
6230            ..
6231        } => {
6232            let mut left_domains = resident_node_domains(left, relations)?;
6233            match join_type {
6234                JoinType::Inner => {
6235                    left_domains.extend(resident_node_domains(right, relations)?);
6236                    Ok(left_domains)
6237                }
6238                JoinType::Semi => Ok(left_domains),
6239                other => Err(resident_workspace_decline(format!(
6240                    "resident domain proof does not support {other:?} joins"
6241                ))),
6242            }
6243        }
6244        RirNode::ChainJoin { fallback, .. } | RirNode::MultiWayJoin { fallback, .. } => {
6245            resident_node_domains(fallback, relations)
6246        }
6247        RirNode::Union { inputs } => {
6248            let mut merged: Option<ResidentColumnDomains> = None;
6249            for input in inputs {
6250                let contribution = resident_node_domains(input, relations)?;
6251                if let Some(domains) = merged.as_mut() {
6252                    if domains.len() != contribution.len() {
6253                        return Err(resident_workspace_decline(
6254                            "resident union domain arity mismatch",
6255                        ));
6256                    }
6257                    for (domain, addition) in domains.iter_mut().zip(contribution) {
6258                        for (source, bound) in addition {
6259                            domain
6260                                .entry(source)
6261                                .and_modify(|current| *current = (*current).max(bound))
6262                                .or_insert(bound);
6263                        }
6264                    }
6265                } else {
6266                    merged = Some(contribution);
6267                }
6268            }
6269            merged.ok_or_else(|| resident_workspace_decline("resident union has no inputs"))
6270        }
6271        RirNode::Diff { left, .. } => resident_node_domains(left, relations),
6272        RirNode::GroupBy { .. } | RirNode::Fixpoint { .. } | RirNode::TensorMaskedJoin { .. } => {
6273            Err(resident_workspace_decline(
6274                "resident domain proof encountered an uncertified operator",
6275            ))
6276        }
6277    }
6278}
6279
6280fn resident_refine_filter_domains(
6281    domains: &mut ResidentColumnDomains,
6282    predicate: &Expr,
6283) -> std::result::Result<(), ResidentGraphDeclineReason> {
6284    match predicate {
6285        Expr::And(parts) => {
6286            for part in parts {
6287                resident_refine_filter_domains(domains, part)?;
6288            }
6289        }
6290        Expr::Compare {
6291            left,
6292            op: RirCompareOp::Eq,
6293            right,
6294        } => {
6295            let (column, value) = match (&**left, &**right) {
6296                (Expr::Column(column), Expr::Const(value))
6297                | (Expr::Const(value), Expr::Column(column)) => (*column, value),
6298                _ => return Ok(()),
6299            };
6300            let arity = domains.len();
6301            let domain = domains.get_mut(column).ok_or_else(|| {
6302                resident_workspace_decline(format!(
6303                    "resident equality filter column {column} exceeds input arity {}",
6304                    arity
6305                ))
6306            })?;
6307            *domain = BTreeMap::from([(format!("constant:{value:?}"), 1)]);
6308        }
6309        _ => {}
6310    }
6311    Ok(())
6312}
6313
6314fn resident_project_domains(
6315    input: &[ResidentDomain],
6316    columns: &[ProjectExpr],
6317) -> std::result::Result<ResidentColumnDomains, ResidentGraphDeclineReason> {
6318    columns
6319        .iter()
6320        .map(|column| match column {
6321            ProjectExpr::Column(index) => input.get(*index).cloned().ok_or_else(|| {
6322                resident_workspace_decline(format!(
6323                    "resident projection column {index} exceeds input arity {}",
6324                    input.len()
6325                ))
6326            }),
6327            ProjectExpr::Computed(Expr::Const(value), scalar) => Ok(BTreeMap::from([(
6328                format!("constant:{scalar:?}:{value:?}"),
6329                1,
6330            )])),
6331            _ => Err(resident_workspace_decline(
6332                "resident projection domain is not a column or constant",
6333            )),
6334        })
6335        .collect()
6336}
6337
6338fn resident_domain_product(
6339    columns: &[ResidentDomain],
6340    context: &str,
6341) -> std::result::Result<u64, ResidentGraphDeclineReason> {
6342    if columns.is_empty() {
6343        return Ok(1);
6344    }
6345    columns
6346        .iter()
6347        .enumerate()
6348        .try_fold(1u64, |product, (column_index, domain)| {
6349            let values = domain.values().try_fold(0u64, |total, bound| {
6350                total.checked_add(*bound).ok_or_else(|| {
6351                    resident_workspace_decline(format!(
6352                        "{context} active-domain sum overflow at column {column_index}: partial={total} addition={bound} lineage={domain:?}"
6353                    ))
6354                })
6355            })?;
6356            product.checked_mul(values).ok_or_else(|| {
6357                resident_workspace_decline(format!(
6358                    "{context} active-domain product overflow at column {column_index}: partial={product} factor={values} lineage={domain:?}"
6359                ))
6360            })
6361        })
6362}
6363
6364fn resident_domain_product_capped(
6365    columns: &[ResidentDomain],
6366    cap: u64,
6367    context: &str,
6368) -> std::result::Result<u64, ResidentGraphDeclineReason> {
6369    if cap == 0 {
6370        return Ok(0);
6371    }
6372    if columns.is_empty() {
6373        return Ok(1);
6374    }
6375    let mut product = 1u64;
6376    for (column_index, domain) in columns.iter().enumerate() {
6377        let mut values = 0u64;
6378        for bound in domain.values() {
6379            let addition = (*bound).min(cap.saturating_sub(values));
6380            values = values.checked_add(addition).ok_or_else(|| {
6381                resident_workspace_decline(format!(
6382                    "{context} capped active-domain sum overflow at column {column_index}: partial={values} addition={addition}"
6383                ))
6384            })?;
6385            if values == cap {
6386                break;
6387            }
6388        }
6389        if values == 0 {
6390            return Ok(0);
6391        }
6392        if product > cap / values {
6393            return Ok(cap);
6394        }
6395        product = product.checked_mul(values).ok_or_else(|| {
6396            resident_workspace_decline(format!(
6397                "{context} capped active-domain product overflow at column {column_index}: partial={product} factor={values}"
6398            ))
6399        })?;
6400    }
6401    Ok(product.min(cap))
6402}
6403
6404fn resident_domain_description(columns: &[ResidentDomain]) -> String {
6405    columns
6406        .iter()
6407        .enumerate()
6408        .map(|(column, domain)| {
6409            let sum = domain.values().copied().fold(0u64, u64::saturating_add);
6410            format!("column[{column}] sum={sum} lineage={domain:?}")
6411        })
6412        .collect::<Vec<_>>()
6413        .join("; ")
6414}
6415
6416#[derive(Clone, Debug, PartialEq, Eq)]
6417enum ResidentCapacityBound {
6418    Finite { rows: u64, proof: String },
6419    AboveResidentLimit { proof: String },
6420}
6421
6422fn resident_capacity_product_bound(proof: &'static str, factors: &[u64]) -> ResidentCapacityBound {
6423    if factors.contains(&0) {
6424        return ResidentCapacityBound::Finite {
6425            rows: 0,
6426            proof: proof.to_owned(),
6427        };
6428    }
6429
6430    let limit = u64::from(MAX_RESIDENT_CAPACITY);
6431    let mut rows = 1u64;
6432    for factor in factors {
6433        if rows > limit / factor {
6434            return ResidentCapacityBound::AboveResidentLimit {
6435                proof: format!("{proof} factors={factors:?}"),
6436            };
6437        }
6438        rows *= factor;
6439    }
6440    ResidentCapacityBound::Finite {
6441        rows,
6442        proof: proof.to_owned(),
6443    }
6444}
6445
6446fn resident_join_capacity_bound(
6447    left_rows: u64,
6448    right_rows: u64,
6449    left_fanout: u64,
6450    right_fanout: u64,
6451    matching_key_values: u64,
6452) -> ResidentCapacityBound {
6453    let candidates = [
6454        resident_capacity_product_bound("left_rows*right_rows", &[left_rows, right_rows]),
6455        resident_capacity_product_bound("left_rows*right_fanout", &[left_rows, right_fanout]),
6456        resident_capacity_product_bound("right_rows*left_fanout", &[right_rows, left_fanout]),
6457        resident_capacity_product_bound(
6458            "matching_keys*left_fanout*right_fanout",
6459            &[matching_key_values, left_fanout, right_fanout],
6460        ),
6461    ];
6462
6463    let mut tightest: Option<(u64, String)> = None;
6464    let mut above = Vec::new();
6465    for candidate in candidates {
6466        match candidate {
6467            ResidentCapacityBound::Finite { rows, proof } => {
6468                if tightest.as_ref().is_none_or(|(current, _)| rows < *current) {
6469                    tightest = Some((rows, proof));
6470                }
6471            }
6472            ResidentCapacityBound::AboveResidentLimit { proof } => above.push(proof),
6473        }
6474    }
6475    if let Some((rows, proof)) = tightest {
6476        ResidentCapacityBound::Finite { rows, proof }
6477    } else {
6478        ResidentCapacityBound::AboveResidentLimit {
6479            proof: above.join("; "),
6480        }
6481    }
6482}
6483
6484struct ResidentRowProof {
6485    rows: u64,
6486    peak: u64,
6487    domains: ResidentColumnDomains,
6488    full_row_unique: bool,
6489    peak_detail: String,
6490}
6491
6492fn resident_node_row_bound(
6493    node: &RirNode,
6494    source_rows: &HashMap<RelId, u64>,
6495    relation_set_bounds: &HashMap<RelId, u64>,
6496    relation_domains: &HashMap<RelId, ResidentColumnDomains>,
6497    path: &str,
6498) -> std::result::Result<ResidentRowProof, ResidentGraphDeclineReason> {
6499    match node {
6500        RirNode::Unit => Ok(ResidentRowProof {
6501            rows: 1,
6502            peak: 1,
6503            domains: Vec::new(),
6504            full_row_unique: true,
6505            peak_detail: format!("{path}.unit rows=1"),
6506        }),
6507        RirNode::Scan { rel } => {
6508            let rows = source_rows
6509                .get(rel)
6510                .copied()
6511                .unwrap_or(0)
6512                .max(relation_set_bounds.get(rel).copied().unwrap_or(0));
6513            let domains = relation_domains.get(rel).cloned().ok_or_else(|| {
6514                resident_workspace_decline(format!(
6515                    "{path}.scan {rel:?} has no active-domain proof"
6516                ))
6517            })?;
6518            Ok(ResidentRowProof {
6519                rows,
6520                peak: rows,
6521                domains,
6522                full_row_unique: true,
6523                peak_detail: format!("{path}.scan rel={rel:?} rows={rows}"),
6524            })
6525        }
6526        RirNode::Filter { input, predicate } => {
6527            let mut proof = resident_node_row_bound(
6528                input,
6529                source_rows,
6530                relation_set_bounds,
6531                relation_domains,
6532                &format!("{path}.filter.input"),
6533            )?;
6534            resident_refine_filter_domains(&mut proof.domains, predicate)?;
6535            if proof.full_row_unique {
6536                proof.rows = resident_domain_product_capped(
6537                    &proof.domains,
6538                    proof.rows,
6539                    &format!("{path}.filter"),
6540                )?;
6541            }
6542            Ok(proof)
6543        }
6544        RirNode::Project { input, columns } => {
6545            let input = resident_node_row_bound(
6546                input,
6547                source_rows,
6548                relation_set_bounds,
6549                relation_domains,
6550                &format!("{path}.project.input"),
6551            )?;
6552            let domains = resident_project_domains(&input.domains, columns)?;
6553            let full_row_unique = input.full_row_unique
6554                && resident_projection_is_injective(&input.domains, columns, path)?;
6555            Ok(ResidentRowProof {
6556                rows: input.rows,
6557                peak: input.peak.max(input.rows),
6558                domains,
6559                full_row_unique,
6560                peak_detail: input.peak_detail,
6561            })
6562        }
6563        RirNode::Distinct { input, .. } => {
6564            let mut proof = resident_node_row_bound(
6565                input,
6566                source_rows,
6567                relation_set_bounds,
6568                relation_domains,
6569                &format!("{path}.distinct.input"),
6570            )?;
6571            proof.rows = resident_domain_product_capped(
6572                &proof.domains,
6573                proof.rows,
6574                &format!("{path}.distinct"),
6575            )?;
6576            proof.full_row_unique = true;
6577            Ok(proof)
6578        }
6579        RirNode::Join {
6580            left,
6581            right,
6582            left_keys,
6583            right_keys,
6584            join_type,
6585        } => {
6586            let left = resident_node_row_bound(
6587                left,
6588                source_rows,
6589                relation_set_bounds,
6590                relation_domains,
6591                &format!("{path}.join.left"),
6592            )?;
6593            let right = resident_node_row_bound(
6594                right,
6595                source_rows,
6596                relation_set_bounds,
6597                relation_domains,
6598                &format!("{path}.join.right"),
6599            )?;
6600            match join_type {
6601                JoinType::Semi => Ok(ResidentRowProof {
6602                    rows: left.rows,
6603                    peak: left.peak.max(right.peak).max(left.rows),
6604                    domains: left.domains,
6605                    full_row_unique: left.full_row_unique,
6606                    peak_detail: if left.peak >= right.peak {
6607                        left.peak_detail
6608                    } else {
6609                        right.peak_detail
6610                    },
6611                }),
6612                JoinType::Inner => {
6613                    if left_keys.len() != 1 || right_keys.len() != 1 {
6614                        return Err(resident_workspace_decline(format!(
6615                            "{path} resident row proof requires exactly one join key per side"
6616                        )));
6617                    }
6618                    let left_key = left_keys[0];
6619                    let right_key = right_keys[0];
6620                    let left_fanout = resident_key_fanout_bound(
6621                        &left.domains,
6622                        left_key,
6623                        left.rows,
6624                        left.full_row_unique,
6625                        &format!("{path}.join.left"),
6626                    )?;
6627                    let right_fanout = resident_key_fanout_bound(
6628                        &right.domains,
6629                        right_key,
6630                        right.rows,
6631                        right.full_row_unique,
6632                        &format!("{path}.join.right"),
6633                    )?;
6634                    let left_key_values = resident_domain_cardinality_capped(
6635                        left.domains.get(left_key).ok_or_else(|| {
6636                            resident_workspace_decline(format!(
6637                                "{path} left join key {left_key} exceeds arity {}",
6638                                left.domains.len()
6639                            ))
6640                        })?,
6641                        left.rows,
6642                        &format!("{path}.join.left_key"),
6643                    )?;
6644                    let right_key_values = resident_domain_cardinality_capped(
6645                        right.domains.get(right_key).ok_or_else(|| {
6646                            resident_workspace_decline(format!(
6647                                "{path} right join key {right_key} exceeds arity {}",
6648                                right.domains.len()
6649                            ))
6650                        })?,
6651                        right.rows,
6652                        &format!("{path}.join.right_key"),
6653                    )?;
6654                    let matching_key_values = left_key_values.min(right_key_values);
6655                    let (rows, bound_proof) = match resident_join_capacity_bound(
6656                        left.rows,
6657                        right.rows,
6658                        left_fanout,
6659                        right_fanout,
6660                        matching_key_values,
6661                    ) {
6662                        ResidentCapacityBound::Finite { rows, proof } => (rows, proof),
6663                        ResidentCapacityBound::AboveResidentLimit { proof } => {
6664                            return Err(resident_workspace_decline(format!(
6665                                "{path} inner join exceeds the fixed resident capacity limit: left_rows={} right_rows={} left_fanout={left_fanout} right_fanout={right_fanout} matching_keys={matching_key_values} products={proof}",
6666                                left.rows, right.rows
6667                            )));
6668                        }
6669                    };
6670                    let join_detail = format!(
6671                        "{path}.join left_rows={} right_rows={} left_fanout={left_fanout} right_fanout={right_fanout} matching_keys={matching_key_values} bound={rows} proof={bound_proof}",
6672                        left.rows, right.rows
6673                    );
6674                    let (peak, peak_detail) = if rows >= left.peak && rows >= right.peak {
6675                        (rows, join_detail)
6676                    } else if left.peak >= right.peak {
6677                        (left.peak, left.peak_detail)
6678                    } else {
6679                        (right.peak, right.peak_detail)
6680                    };
6681                    let mut domains = left.domains;
6682                    domains.extend(right.domains);
6683                    Ok(ResidentRowProof {
6684                        rows,
6685                        peak,
6686                        domains,
6687                        full_row_unique: left.full_row_unique && right.full_row_unique,
6688                        peak_detail,
6689                    })
6690                }
6691                other => Err(resident_workspace_decline(format!(
6692                    "resident row proof does not support {other:?} joins"
6693                ))),
6694            }
6695        }
6696        RirNode::ChainJoin { fallback, .. } | RirNode::MultiWayJoin { fallback, .. } => {
6697            resident_node_row_bound(
6698                fallback,
6699                source_rows,
6700                relation_set_bounds,
6701                relation_domains,
6702                &format!("{path}.fallback"),
6703            )
6704        }
6705        RirNode::Union { inputs } => {
6706            let mut rows = 0u64;
6707            let mut peak = 0u64;
6708            let mut peak_detail = format!("{path}.union empty");
6709            let mut domains: Option<ResidentColumnDomains> = None;
6710            for (index, input) in inputs.iter().enumerate() {
6711                let proof = resident_node_row_bound(
6712                    input,
6713                    source_rows,
6714                    relation_set_bounds,
6715                    relation_domains,
6716                    &format!("{path}.union[{index}]"),
6717                )?;
6718                rows = rows.checked_add(proof.rows).ok_or_else(|| {
6719                    resident_workspace_decline(format!(
6720                        "{path} union addition overflow: partial={rows} addition={}",
6721                        proof.rows
6722                    ))
6723                })?;
6724                if proof.peak > peak {
6725                    peak = proof.peak;
6726                    peak_detail = proof.peak_detail;
6727                }
6728                if let Some(merged) = domains.as_mut() {
6729                    merge_domain_columns(merged, proof.domains, path)?;
6730                } else {
6731                    domains = Some(proof.domains);
6732                }
6733            }
6734            if rows > peak {
6735                peak = rows;
6736                peak_detail = format!("{path}.union raw_rows={rows}");
6737            }
6738            Ok(ResidentRowProof {
6739                rows,
6740                peak,
6741                domains: domains
6742                    .ok_or_else(|| resident_workspace_decline("resident union has no inputs"))?,
6743                full_row_unique: false,
6744                peak_detail,
6745            })
6746        }
6747        RirNode::Diff { left, right } => {
6748            let left = resident_node_row_bound(
6749                left,
6750                source_rows,
6751                relation_set_bounds,
6752                relation_domains,
6753                &format!("{path}.diff.left"),
6754            )?;
6755            let right = resident_node_row_bound(
6756                right,
6757                source_rows,
6758                relation_set_bounds,
6759                relation_domains,
6760                &format!("{path}.diff.right"),
6761            )?;
6762            let (peak, peak_detail) = if left.peak >= right.peak {
6763                (left.peak.max(left.rows), left.peak_detail)
6764            } else {
6765                (right.peak.max(left.rows), right.peak_detail)
6766            };
6767            Ok(ResidentRowProof {
6768                rows: left.rows,
6769                peak,
6770                domains: left.domains,
6771                full_row_unique: left.full_row_unique,
6772                peak_detail,
6773            })
6774        }
6775        RirNode::GroupBy { .. } | RirNode::Fixpoint { .. } | RirNode::TensorMaskedJoin { .. } => {
6776            Err(resident_workspace_decline(
6777                "resident row proof encountered an uncertified operator",
6778            ))
6779        }
6780    }
6781}
6782
6783fn resident_projection_is_injective(
6784    input_domains: &[ResidentDomain],
6785    columns: &[ProjectExpr],
6786    path: &str,
6787) -> std::result::Result<bool, ResidentGraphDeclineReason> {
6788    let mut retained = vec![false; input_domains.len()];
6789    for column in columns {
6790        if let ProjectExpr::Column(index) = column {
6791            let slot = retained.get_mut(*index).ok_or_else(|| {
6792                resident_workspace_decline(format!(
6793                    "{path} projection column {index} exceeds input arity {}",
6794                    input_domains.len()
6795                ))
6796            })?;
6797            *slot = true;
6798        }
6799    }
6800    for (index, (retained, domain)) in retained.iter().zip(input_domains).enumerate() {
6801        if !retained
6802            && resident_domain_cardinality_capped(
6803                domain,
6804                2,
6805                &format!("{path}.project.omitted[{index}]"),
6806            )? > 1
6807        {
6808            return Ok(false);
6809        }
6810    }
6811    Ok(true)
6812}
6813
6814fn resident_domain_cardinality_capped(
6815    domain: &ResidentDomain,
6816    cap: u64,
6817    context: &str,
6818) -> std::result::Result<u64, ResidentGraphDeclineReason> {
6819    let mut total = 0u64;
6820    for bound in domain.values() {
6821        let addition = (*bound).min(cap.saturating_sub(total));
6822        total = total.checked_add(addition).ok_or_else(|| {
6823            resident_workspace_decline(format!(
6824                "{context} domain-cardinality addition overflow: partial={total} addition={addition}"
6825            ))
6826        })?;
6827        if total == cap {
6828            break;
6829        }
6830    }
6831    Ok(total)
6832}
6833
6834fn resident_key_fanout_bound(
6835    domains: &[ResidentDomain],
6836    key: usize,
6837    rows: u64,
6838    full_row_unique: bool,
6839    context: &str,
6840) -> std::result::Result<u64, ResidentGraphDeclineReason> {
6841    if key >= domains.len() {
6842        return Err(resident_workspace_decline(format!(
6843            "{context} join key {key} exceeds input arity {}",
6844            domains.len()
6845        )));
6846    }
6847    if !full_row_unique {
6848        return Ok(rows);
6849    }
6850    let non_key = domains
6851        .iter()
6852        .enumerate()
6853        .filter_map(|(index, domain)| (index != key).then_some(domain.clone()))
6854        .collect::<Vec<_>>();
6855    resident_domain_product_capped(&non_key, rows, &format!("{context}.fanout"))
6856}
6857
6858fn merge_domain_columns(
6859    target: &mut ResidentColumnDomains,
6860    addition: ResidentColumnDomains,
6861    path: &str,
6862) -> std::result::Result<(), ResidentGraphDeclineReason> {
6863    if target.len() != addition.len() {
6864        return Err(resident_workspace_decline(format!(
6865            "{path} union domain arity mismatch: left={} right={}",
6866            target.len(),
6867            addition.len()
6868        )));
6869    }
6870    for (target, addition) in target.iter_mut().zip(addition) {
6871        for (source, bound) in addition {
6872            target
6873                .entry(source)
6874                .and_modify(|current| *current = (*current).max(bound))
6875                .or_insert(bound);
6876        }
6877    }
6878    Ok(())
6879}
6880
6881fn checked_capacity_class(source_capacity: u32) -> Result<u32> {
6882    let capacity = source_capacity
6883        .max(1)
6884        .checked_next_power_of_two()
6885        .ok_or_else(|| XlogError::Execution("resident capacity class overflow".to_string()))?;
6886    if capacity > MAX_RESIDENT_CAPACITY {
6887        return Err(XlogError::Execution(format!(
6888            "resident capacity class {capacity} exceeds the fixed scan envelope {MAX_RESIDENT_CAPACITY}"
6889        )));
6890    }
6891    Ok(capacity)
6892}
6893
6894#[cfg(test)]
6895mod tests {
6896    // These scenarios drive one process-wide CUDA retirement queue. Isolate
6897    // their injected waits/panics; production queue workers remain concurrent.
6898    fn resident_retirement_test_guard() -> std::sync::MutexGuard<'static, ()> {
6899        static TEST_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
6900        TEST_LOCK.lock().unwrap_or_else(|error| error.into_inner())
6901    }
6902
6903    #[test]
6904    fn pending_resident_owners_release_admission_before_error_retirement() {
6905        let _retirement_test = resident_retirement_test_guard();
6906        use std::sync::{
6907            atomic::{AtomicBool, Ordering},
6908            Arc,
6909        };
6910        struct Admission(Arc<AtomicBool>);
6911        impl Drop for Admission {
6912            fn drop(&mut self) {
6913                self.0.store(false, Ordering::SeqCst);
6914            }
6915        }
6916        for unwind in [false, true] {
6917            let excluded = Arc::new(AtomicBool::new(false));
6918            let owner = Arc::new(());
6919            let observer = Arc::downgrade(&owner);
6920            let pending = super::PendingResidentOwners::new(
6921                (owner, Arc::clone(&excluded)),
6922                |(_, excluded)| {
6923                    assert!(
6924                        !excluded.load(Ordering::SeqCst),
6925                        "admission must end before retirement waits"
6926                    );
6927                    true
6928                },
6929            );
6930            let outcome = std::panic::catch_unwind(move || {
6931                pending.with_admission(
6932                    |(_, excluded)| {
6933                        excluded.store(true, Ordering::SeqCst);
6934                        Ok::<_, ()>(Admission(Arc::clone(excluded)))
6935                    },
6936                    |_| -> std::result::Result<(), ()> {
6937                        if unwind {
6938                            panic!("controlled partial operation unwind");
6939                        }
6940                        Err(())
6941                    },
6942                )
6943            });
6944            assert_eq!(outcome.is_err(), unwind);
6945            if let Ok(result) = outcome {
6946                assert!(result.is_err());
6947            }
6948            assert!(!excluded.load(Ordering::SeqCst));
6949            assert!(
6950                observer.upgrade().is_none(),
6951                "completed owners must retire without another lifecycle entry"
6952            );
6953        }
6954    }
6955
6956    #[test]
6957    fn pending_resident_owners_rearm_for_late_receipt_copy() {
6958        let _retirement_test = resident_retirement_test_guard();
6959        let owner = std::sync::Arc::new(());
6960        let observer = std::sync::Arc::downgrade(&owner);
6961        let completed = super::PendingResidentOwners::new(owner, |_| {
6962            panic!("successful completion transfers ownership without another wait")
6963        });
6964        let synchronized_owner = completed.into_inner();
6965        assert!(observer.upgrade().is_some());
6966        // A graph's completed event cannot release the subsequent D2H source
6967        // and destination: the later operation needs its own completion proof.
6968        let copy = super::PendingResidentOwners::new(synchronized_owner, |_| false);
6969        drop(copy);
6970        assert!(observer.upgrade().is_some());
6971    }
6972
6973    #[test]
6974    fn pending_resident_owners_remain_retained_if_wait_unwinds() {
6975        let _retirement_test = resident_retirement_test_guard();
6976        let owner = std::sync::Arc::new(());
6977        let observer = std::sync::Arc::downgrade(&owner);
6978        let result = std::panic::catch_unwind(move || {
6979            drop(super::PendingResidentOwners::new(owner, |_| {
6980                panic!("controlled completion wait failure")
6981            }));
6982        });
6983        assert!(result.is_err());
6984        assert!(observer.upgrade().is_some());
6985    }
6986
6987    #[test]
6988    fn pending_resident_owners_keep_full_chain_after_failed_completion() {
6989        let _retirement_test = resident_retirement_test_guard();
6990        use std::sync::{
6991            atomic::{AtomicUsize, Ordering},
6992            Arc,
6993        };
6994
6995        struct Owner(Arc<AtomicUsize>);
6996        impl Drop for Owner {
6997            fn drop(&mut self) {
6998                self.0.fetch_add(1, Ordering::SeqCst);
6999            }
7000        }
7001        // The same owner boundary encloses graph enqueue, completion recording,
7002        // terminal waiting and the later device-to-host receipt operation.
7003        for completion in [false, true] {
7004            let drops = Arc::new(AtomicUsize::new(0));
7005            let owners: Vec<_> = (0..5)
7006                .map(|_| Arc::new(Owner(Arc::clone(&drops))))
7007                .collect();
7008            let observers: Vec<_> = owners.iter().map(Arc::downgrade).collect();
7009            let pending =
7010                super::PendingResidentOwners::new((owners, completion), |(_, completed)| {
7011                    *completed
7012                });
7013            drop(pending);
7014            assert_eq!(drops.load(Ordering::SeqCst), if completion { 5 } else { 0 });
7015            assert!(observers
7016                .iter()
7017                .all(|owner| owner.upgrade().is_some() != completion));
7018        }
7019    }
7020
7021    use super::{
7022        checked_capacity_class, resident_compact_allocation_bytes,
7023        resident_compact_filter_descriptors, resident_compact_preflight_device_bytes,
7024        resident_compact_project_descriptors, resident_compact_regions,
7025        resident_compact_schedule_metadata_bytes, resident_compact_schema_defaults,
7026        resident_compact_tables, resident_compact_topology, resident_join_capacity_bound,
7027        resident_lower_compact_regions, resident_new_phase_unit, resident_output_indices,
7028        resident_phase_merge, resident_plan_relation_ids, resident_record_lifetimes,
7029        resident_record_scan_leaf, resident_record_schema_winner_mark, resident_record_unit_leaf,
7030        resident_register_schema_selection, resident_resolve_output_schema,
7031        resident_semantic_trace_guard, resident_source_binding_route,
7032        resident_source_logical_count, resident_source_slot_map, resident_union_fold_mode,
7033        resident_validate_conditional_body_node_kinds, resident_validate_exact_reservation,
7034        resident_validate_parent_graph_kinds, resident_validate_slot_assignments,
7035        validate_compact_resident_node_envelope, CudaGraphNodeKind, ResidentAllocationManifest,
7036        ResidentBufferRef, ResidentCapacityBound, ResidentCaptureParentKind, ResidentCapturePhase,
7037        ResidentCompactDescriptorTables, ResidentCompactLogicalRegion, ResidentFilterPlan,
7038        ResidentGraphDeclineReason, ResidentHeadSchemaSelection, ResidentLogicalRelation,
7039        ResidentOpDescriptor, ResidentOutputSchemaPlan, ResidentOutputSchemaSelection,
7040        ResidentPhaseMergeStep, ResidentPhysicalSlotPlan, ResidentProjectPlan, ResidentRecordedOp,
7041        ResidentSlotAssignment, ResidentUnionFoldMode, ScalarType, Schema,
7042        RESIDENT_DYNAMIC_SCHEMA_ID,
7043    };
7044    use std::collections::{BTreeMap, HashMap, HashSet};
7045    use xlog_core::RelId;
7046    use xlog_ir::{
7047        CompareOp, CompiledRule, ConstValue, Expr, JoinType, ProjectExpr, RirMeta, RirNode,
7048    };
7049
7050    #[test]
7051    fn resident_plan_relations_exclude_registered_but_unreferenced_relations() {
7052        let source = RelId(1);
7053        let head = RelId(2);
7054        let unrelated = RelId(3);
7055        let rules = [CompiledRule {
7056            head: "reachable".to_owned(),
7057            body: RirNode::Scan { rel: source },
7058            meta: RirMeta::default(),
7059        }];
7060        let names = HashMap::from([
7061            ("source".to_owned(), source),
7062            ("reachable".to_owned(), head),
7063            ("unrelated".to_owned(), unrelated),
7064        ]);
7065
7066        let relations = resident_plan_relation_ids(rules.iter(), &names);
7067
7068        assert_eq!(relations, HashSet::from([source, head]));
7069    }
7070
7071    #[test]
7072    fn capacity_class_is_source_bounded_and_checked() {
7073        assert_eq!(checked_capacity_class(0).unwrap(), 1);
7074        assert_eq!(checked_capacity_class(4_393).unwrap(), 8_192);
7075        assert_eq!(checked_capacity_class(65_536).unwrap(), 65_536);
7076        assert!(checked_capacity_class(65_537).is_err());
7077    }
7078
7079    #[test]
7080    fn schema_lineage_fails_closed_for_cycles_and_missing_sources() {
7081        let schema = Schema::new(vec![("value".to_owned(), ScalarType::U32)]);
7082        let selection = |source: &str| ResidentHeadSchemaSelection {
7083            source_head: source.to_owned(),
7084            output_schemas_by_source_winner: vec![schema.clone()],
7085        };
7086        let choices = BTreeMap::from([
7087            ("a".to_owned(), vec![schema.clone()]),
7088            ("b".to_owned(), vec![schema.clone()]),
7089            ("c".to_owned(), vec![schema.clone()]),
7090            ("empty".to_owned(), Vec::new()),
7091        ]);
7092
7093        let direct_cycle =
7094            resident_register_schema_selection("a", selection("a"), &choices, &mut BTreeMap::new())
7095                .expect_err("direct schema lineage cycle must decline");
7096        assert!(matches!(
7097            direct_cycle,
7098            ResidentGraphDeclineReason::WorkspaceUnbounded { detail }
7099                if detail.contains("contains a cycle")
7100        ));
7101
7102        let mut long_cycle = BTreeMap::new();
7103        resident_register_schema_selection("a", selection("b"), &choices, &mut long_cycle)
7104            .expect("first acyclic lineage edge");
7105        resident_register_schema_selection("b", selection("c"), &choices, &mut long_cycle)
7106            .expect("second acyclic lineage edge");
7107        let long_cycle =
7108            resident_register_schema_selection("c", selection("a"), &choices, &mut long_cycle)
7109                .expect_err("transitive schema lineage cycle must decline");
7110        assert!(matches!(
7111            long_cycle,
7112            ResidentGraphDeclineReason::WorkspaceUnbounded { detail }
7113                if detail.contains("contains a cycle")
7114        ));
7115
7116        for source in ["missing", "empty"] {
7117            let missing = resident_register_schema_selection(
7118                "target",
7119                selection(source),
7120                &choices,
7121                &mut BTreeMap::new(),
7122            )
7123            .expect_err("missing schema source must decline");
7124            assert!(matches!(
7125                missing,
7126                ResidentGraphDeclineReason::WorkspaceUnbounded { detail }
7127                    if detail.contains("has no admitted candidate")
7128            ));
7129        }
7130
7131        let dynamic_plan = |source_output| ResidentOutputSchemaPlan {
7132            candidates: vec![schema.clone()],
7133            selection: ResidentOutputSchemaSelection::SourceWinner {
7134                source_output,
7135                schemas: vec![schema.clone()],
7136            },
7137        };
7138        let resolver_cycle = resident_resolve_output_schema(
7139            &[dynamic_plan(1), dynamic_plan(0)],
7140            &[RESIDENT_DYNAMIC_SCHEMA_ID, RESIDENT_DYNAMIC_SCHEMA_ID],
7141            0,
7142            &mut HashSet::new(),
7143        )
7144        .expect_err("receipt schema resolver cycle must fail");
7145        assert!(resolver_cycle.to_string().contains("contains a cycle"));
7146
7147        let missing_plan = resident_resolve_output_schema(
7148            &[dynamic_plan(1)],
7149            &[RESIDENT_DYNAMIC_SCHEMA_ID],
7150            0,
7151            &mut HashSet::new(),
7152        )
7153        .expect_err("missing receipt schema source plan must fail");
7154        assert!(missing_plan.to_string().contains("output 1 is missing"));
7155    }
7156
7157    #[test]
7158    fn join_capacity_uses_a_finite_tight_bound_when_loose_products_overflow() {
7159        let bound = resident_join_capacity_bound(u64::MAX, u64::MAX, 1, 1, 1);
7160        assert_eq!(
7161            bound,
7162            ResidentCapacityBound::Finite {
7163                rows: 1,
7164                proof: "matching_keys*left_fanout*right_fanout".to_owned(),
7165            }
7166        );
7167    }
7168
7169    #[test]
7170    fn true_cartesian_join_remains_above_the_resident_limit() {
7171        let bound = resident_join_capacity_bound(257, 257, 257, 257, 1);
7172        assert!(matches!(
7173            bound,
7174            ResidentCapacityBound::AboveResidentLimit { .. }
7175        ));
7176    }
7177
7178    #[test]
7179    fn lifetime_scan_rejects_an_out_of_range_logical_input() {
7180        let relations = vec![ResidentLogicalRelation {
7181            schema: Schema::new(vec![("value".to_string(), ScalarType::U32)]),
7182            initial_count: 0,
7183            permanent: false,
7184        }];
7185        let mut definitions = vec![None];
7186        let mut last_uses = vec![None];
7187        let mut ordinal = 0;
7188        let error = resident_record_lifetimes(
7189            &[ResidentRecordedOp::Filter {
7190                input: ResidentBufferRef::Private(1),
7191                output: 0,
7192                workspace: 0,
7193                op_id: 0,
7194            }],
7195            &relations,
7196            &mut definitions,
7197            &mut last_uses,
7198            &mut ordinal,
7199        )
7200        .expect_err("an invalid logical input must fail before allocation");
7201        assert!(error
7202            .to_string()
7203            .contains("logical input relation 1 is missing"));
7204    }
7205
7206    #[test]
7207    fn stale_scratch_generation_is_rejected_before_materialization() {
7208        let schema = Schema::new(vec![("value".to_string(), ScalarType::U32)]);
7209        let relations = vec![
7210            ResidentLogicalRelation {
7211                schema: schema.clone(),
7212                initial_count: 0,
7213                permanent: false,
7214            },
7215            ResidentLogicalRelation {
7216                schema: schema.clone(),
7217                initial_count: 0,
7218                permanent: false,
7219            },
7220        ];
7221        let slots = vec![ResidentPhysicalSlotPlan {
7222            schema,
7223            initial_count: 0,
7224            permanent: false,
7225        }];
7226        let assignments = vec![
7227            ResidentSlotAssignment {
7228                slot: 0,
7229                generation: 0,
7230            },
7231            ResidentSlotAssignment {
7232                slot: 0,
7233                generation: 0,
7234            },
7235        ];
7236        let error = resident_validate_slot_assignments(
7237            &relations,
7238            &[Some(0), Some(2)],
7239            &[Some(0), Some(2)],
7240            &slots,
7241            &assignments,
7242        )
7243        .expect_err("a stale scratch generation must fail before allocation");
7244        assert!(error.to_string().contains("generation 0 but expected 1"));
7245    }
7246
7247    #[test]
7248    fn authored_scan_and_unit_are_explicit_lifetime_operations() {
7249        let relations = vec![ResidentLogicalRelation {
7250            schema: Schema::new(Vec::<(String, ScalarType)>::new()),
7251            initial_count: 0,
7252            permanent: false,
7253        }];
7254        let mut definitions = vec![None];
7255        let mut last_uses = vec![None];
7256        let mut ordinal = 0;
7257        let operations = vec![
7258            ResidentRecordedOp::Unit {
7259                output: 0,
7260                op_id: 17,
7261            },
7262            ResidentRecordedOp::Scan {
7263                relation: ResidentBufferRef::Private(0),
7264                op_id: 18,
7265            },
7266            ResidentRecordedOp::TraceDelta {
7267                scan_delta: 1,
7268                filter_delta: 0,
7269                semantic_guard: None,
7270            },
7271        ];
7272
7273        resident_record_lifetimes(
7274            &operations,
7275            &relations,
7276            &mut definitions,
7277            &mut last_uses,
7278            &mut ordinal,
7279        )
7280        .expect("an emitted Unit defines the relation consumed by the emitted Scan");
7281
7282        assert_eq!(definitions, vec![Some(0)]);
7283        assert_eq!(last_uses, vec![Some(1)]);
7284        assert_eq!(ordinal, operations.len());
7285    }
7286
7287    #[test]
7288    fn authored_leaf_emitters_preserve_scan_identity_and_trace_order() {
7289        let mut operations = Vec::new();
7290        let push = |ops: &mut Vec<_>, op, _op_id| ops.push(op);
7291
7292        let unit = resident_record_unit_leaf(3, 17, &mut operations, push);
7293        let first = resident_record_scan_leaf(
7294            ResidentBufferRef::Private(4),
7295            18,
7296            None,
7297            &mut operations,
7298            push,
7299        );
7300        let second = resident_record_scan_leaf(
7301            ResidentBufferRef::Private(5),
7302            19,
7303            None,
7304            &mut operations,
7305            push,
7306        );
7307
7308        assert!(matches!(unit, ResidentBufferRef::Private(3)));
7309        assert!(matches!(first, ResidentBufferRef::Private(4)));
7310        assert!(matches!(second, ResidentBufferRef::Private(5)));
7311        assert!(matches!(
7312            operations.as_slice(),
7313            [
7314                ResidentRecordedOp::Unit {
7315                    output: 3,
7316                    op_id: 17
7317                },
7318                ResidentRecordedOp::Scan {
7319                    relation: ResidentBufferRef::Private(4),
7320                    op_id: 18
7321                },
7322                ResidentRecordedOp::TraceDelta {
7323                    scan_delta: 1,
7324                    filter_delta: 0,
7325                    semantic_guard: None,
7326                },
7327                ResidentRecordedOp::Scan {
7328                    relation: ResidentBufferRef::Private(5),
7329                    op_id: 19
7330                },
7331                ResidentRecordedOp::TraceDelta {
7332                    scan_delta: 1,
7333                    filter_delta: 0,
7334                    semantic_guard: None,
7335                },
7336            ]
7337        ));
7338    }
7339
7340    #[test]
7341    fn recursive_trace_semantics_use_the_selected_delta_and_leave_seed_unguarded() {
7342        let selected_delta = ResidentBufferRef::Private(9);
7343        assert!(resident_semantic_trace_guard(None).is_none());
7344        assert!(matches!(
7345            resident_semantic_trace_guard(Some((RelId(4), 2, 9))),
7346            Some(ResidentBufferRef::Private(9))
7347        ));
7348
7349        let mut operations = Vec::new();
7350        let push = |ops: &mut Vec<_>, op, _op_id| ops.push(op);
7351        resident_record_scan_leaf(
7352            ResidentBufferRef::Private(4),
7353            18,
7354            None,
7355            &mut operations,
7356            push,
7357        );
7358        resident_record_scan_leaf(
7359            ResidentBufferRef::Private(5),
7360            19,
7361            Some(selected_delta.clone()),
7362            &mut operations,
7363            push,
7364        );
7365
7366        assert!(matches!(
7367            operations.as_slice(),
7368            [
7369                ResidentRecordedOp::Scan { op_id: 18, .. },
7370                ResidentRecordedOp::TraceDelta {
7371                    semantic_guard: None,
7372                    ..
7373                },
7374                ResidentRecordedOp::Scan { op_id: 19, .. },
7375                ResidentRecordedOp::TraceDelta {
7376                    semantic_guard: Some(ResidentBufferRef::Private(9)),
7377                    ..
7378                },
7379            ]
7380        ));
7381    }
7382
7383    #[test]
7384    fn phase_unit_allocator_creates_fresh_scratch_values() {
7385        let mut relations = Vec::new();
7386
7387        let (first, first_op) = resident_new_phase_unit(&mut relations, 17).unwrap();
7388        let (second, second_op) = resident_new_phase_unit(&mut relations, 18).unwrap();
7389
7390        assert!(matches!(first, ResidentBufferRef::Private(0)));
7391        assert!(matches!(second, ResidentBufferRef::Private(1)));
7392        assert!(matches!(
7393            first_op,
7394            ResidentRecordedOp::Unit {
7395                output: 0,
7396                op_id: 17
7397            }
7398        ));
7399        assert!(matches!(
7400            second_op,
7401            ResidentRecordedOp::Unit {
7402                output: 1,
7403                op_id: 18
7404            }
7405        ));
7406        assert!(relations
7407            .iter()
7408            .all(|relation| !relation.permanent && relation.initial_count == 0));
7409    }
7410
7411    #[test]
7412    fn phase_local_merge_deduplicates_each_contribution_before_ordered_union() {
7413        let mut steps = Vec::new();
7414        let first = resident_phase_merge(None, 2_u32, |step| match step {
7415            ResidentPhaseMergeStep::Deduplicate(value) => {
7416                steps.push(("dedup", value, 0));
7417                Ok::<_, &'static str>(value * 10)
7418            }
7419            ResidentPhaseMergeStep::Union(left, right) => {
7420                steps.push(("union", left, right));
7421                Ok(left + right)
7422            }
7423        })
7424        .unwrap();
7425        let second = resident_phase_merge(Some(first), 3_u32, |step| match step {
7426            ResidentPhaseMergeStep::Deduplicate(value) => {
7427                steps.push(("dedup", value, 0));
7428                Ok::<_, &'static str>(value * 10)
7429            }
7430            ResidentPhaseMergeStep::Union(left, right) => {
7431                steps.push(("union", left, right));
7432                Ok(left + right)
7433            }
7434        })
7435        .unwrap();
7436
7437        assert_eq!(first, 20);
7438        assert_eq!(second, 50);
7439        assert_eq!(steps, [("dedup", 2, 0), ("dedup", 3, 0), ("union", 20, 30)]);
7440    }
7441
7442    #[test]
7443    fn initial_copy_schema_marker_is_ordered_after_its_count_producer() {
7444        let source = ResidentBufferRef::Source("head".to_owned());
7445        let mut operations = vec![ResidentRecordedOp::Project {
7446            input: source.clone(),
7447            output: 0,
7448            workspace: 0,
7449            op_id: 7,
7450        }];
7451
7452        resident_record_schema_winner_mark(&mut operations, ResidentBufferRef::Private(0), 0, 3);
7453
7454        assert!(matches!(
7455            operations.as_slice(),
7456            [
7457                ResidentRecordedOp::Project { op_id: 7, .. },
7458                ResidentRecordedOp::SchemaWinnerMark {
7459                    contribution: ResidentBufferRef::Private(0),
7460                    head_index: 0,
7461                    schema_id: 3,
7462                }
7463            ]
7464        ));
7465    }
7466
7467    #[test]
7468    fn source_slots_are_deduplicated_sorted_and_distinct_from_private_targets() {
7469        let slots =
7470            resident_source_slot_map(3, ["zeta", "head", "zeta", "head"]).expect("source slots");
7471
7472        assert_eq!(slots.get("head"), Some(&3));
7473        assert_eq!(slots.get("zeta"), Some(&4));
7474        assert_eq!(slots.len(), 2);
7475        assert_ne!(slots["head"], 0, "stored source and staged target differ");
7476    }
7477
7478    #[test]
7479    fn empty_untracked_sources_are_normalized_before_slot_binding() {
7480        let logical_count = resident_source_logical_count(Some(0)).unwrap();
7481        assert_eq!(
7482            resident_source_binding_route(logical_count, false, false).unwrap(),
7483            super::ResidentSourceBindingRoute::NormalizeEmpty
7484        );
7485        assert_eq!(
7486            resident_source_binding_route(0, true, true).unwrap(),
7487            super::ResidentSourceBindingRoute::Direct
7488        );
7489        assert!(resident_source_binding_route(1, false, true).is_err());
7490        assert!(resident_source_binding_route(1, true, false).is_err());
7491        assert!(resident_source_logical_count(None).is_err());
7492    }
7493
7494    #[test]
7495    fn compact_regions_preserve_phase_order_and_form_five_parent_nodes() {
7496        let unit = |output, op_id| ResidentRecordedOp::Unit { output, op_id };
7497        let phases = vec![
7498            ResidentCapturePhase::Segment {
7499                ops: vec![unit(10, 10)],
7500                scc_begin: None,
7501            },
7502            ResidentCapturePhase::Segment {
7503                ops: vec![unit(11, 11)],
7504                scc_begin: Some((64, 101)),
7505            },
7506            ResidentCapturePhase::ConditionalWhile {
7507                ops: vec![unit(12, 12)],
7508                iteration_limit: 64,
7509                convergence_op_id: 102,
7510            },
7511            ResidentCapturePhase::Segment {
7512                ops: vec![unit(20, 20)],
7513                scc_begin: None,
7514            },
7515            ResidentCapturePhase::Segment {
7516                ops: vec![ResidentRecordedOp::Scan {
7517                    relation: ResidentBufferRef::Private(20),
7518                    op_id: 21,
7519                }],
7520                scc_begin: Some((32, 201)),
7521            },
7522            ResidentCapturePhase::ConditionalWhile {
7523                ops: vec![unit(22, 22)],
7524                iteration_limit: 32,
7525                convergence_op_id: 202,
7526            },
7527            ResidentCapturePhase::Segment {
7528                ops: vec![unit(30, 30)],
7529                scc_begin: None,
7530            },
7531        ];
7532
7533        let regions = resident_compact_regions(vec![unit(0, 0)], phases, 999).expect("regions");
7534
7535        assert_eq!(regions.len(), 5);
7536        assert_eq!(
7537            regions.iter().filter(|region| region.recursive()).count(),
7538            2
7539        );
7540        assert_eq!(regions.len() + 2, 7, "hierarchical node inventory");
7541        let topology = resident_compact_topology(&regions).unwrap();
7542        assert_eq!(
7543            topology.parent_kinds,
7544            vec![
7545                ResidentCaptureParentKind::Kernel,
7546                ResidentCaptureParentKind::Conditional,
7547                ResidentCaptureParentKind::Kernel,
7548                ResidentCaptureParentKind::Conditional,
7549                ResidentCaptureParentKind::Kernel,
7550            ]
7551        );
7552        assert_eq!(topology.conditional_body_kernel_counts, vec![1, 1]);
7553        assert_eq!(topology.hierarchical_node_count, 7);
7554        assert!(regions[0].initializes());
7555        assert!(regions[0].begins_scc());
7556        assert_eq!(regions[0].op_id, regions[1].op_id);
7557        assert_eq!(regions[0].iteration_limit, regions[1].iteration_limit);
7558        assert_eq!(regions[2].op_id, regions[3].op_id);
7559        assert_eq!(regions[2].iteration_limit, regions[3].iteration_limit);
7560        assert!(regions[4].finalizes());
7561        assert_eq!(regions[4].op_id, 999);
7562        assert!(matches!(
7563            regions[2].ops.as_slice(),
7564            [
7565                ResidentRecordedOp::Unit { output: 20, .. },
7566                ResidentRecordedOp::Scan {
7567                    relation: ResidentBufferRef::Private(20),
7568                    ..
7569                }
7570            ]
7571        ));
7572    }
7573
7574    #[test]
7575    fn conditional_body_inventory_requires_one_actual_kernel_per_body() {
7576        let exact = vec![
7577            vec![CudaGraphNodeKind::Kernel],
7578            vec![CudaGraphNodeKind::Kernel],
7579        ];
7580        assert_eq!(
7581            resident_validate_conditional_body_node_kinds(&exact, 2).unwrap(),
7582            vec![1, 1]
7583        );
7584        assert!(resident_validate_conditional_body_node_kinds(&exact[..1], 2).is_err());
7585        assert!(resident_validate_conditional_body_node_kinds(&[Vec::new()], 1).is_err());
7586        assert!(resident_validate_conditional_body_node_kinds(
7587            &[vec![CudaGraphNodeKind::Kernel, CudaGraphNodeKind::Kernel]],
7588            1,
7589        )
7590        .is_err());
7591        assert!(resident_validate_conditional_body_node_kinds(
7592            &[vec![CudaGraphNodeKind::Memcpy]],
7593            1,
7594        )
7595        .is_err());
7596    }
7597
7598    #[test]
7599    fn compact_parent_graph_kind_validation_is_exact() {
7600        let expected = vec![
7601            ResidentCaptureParentKind::Kernel,
7602            ResidentCaptureParentKind::Conditional,
7603            ResidentCaptureParentKind::Kernel,
7604        ];
7605        assert!(resident_validate_parent_graph_kinds(
7606            &[
7607                CudaGraphNodeKind::Kernel,
7608                CudaGraphNodeKind::Conditional,
7609                CudaGraphNodeKind::Kernel
7610            ],
7611            &expected,
7612        )
7613        .is_ok());
7614        assert!(resident_validate_parent_graph_kinds(
7615            &[
7616                CudaGraphNodeKind::Kernel,
7617                CudaGraphNodeKind::Kernel,
7618                CudaGraphNodeKind::Conditional
7619            ],
7620            &expected,
7621        )
7622        .is_err());
7623        assert!(resident_validate_parent_graph_kinds(
7624            &[CudaGraphNodeKind::Kernel, CudaGraphNodeKind::Conditional],
7625            &expected,
7626        )
7627        .is_err());
7628    }
7629
7630    #[test]
7631    fn compact_descriptors_preserve_physical_generations_and_source_slots() {
7632        let regions = vec![ResidentCompactLogicalRegion {
7633            ops: vec![
7634                ResidentRecordedOp::Unit {
7635                    output: 0,
7636                    op_id: 7,
7637                },
7638                ResidentRecordedOp::SchemaWinnerMark {
7639                    contribution: ResidentBufferRef::Private(0),
7640                    head_index: 0,
7641                    schema_id: 3,
7642                },
7643                ResidentRecordedOp::TraceDelta {
7644                    scan_delta: 0,
7645                    filter_delta: 1,
7646                    semantic_guard: None,
7647                },
7648                ResidentRecordedOp::Scan {
7649                    relation: ResidentBufferRef::Source("source".to_owned()),
7650                    op_id: 8,
7651                },
7652                ResidentRecordedOp::TraceDelta {
7653                    scan_delta: 1,
7654                    filter_delta: 0,
7655                    semantic_guard: None,
7656                },
7657            ],
7658            iteration_limit: 1,
7659            op_id: 0,
7660            flags: super::RESIDENT_SCHEDULE_REGION_INITIALIZE
7661                | super::RESIDENT_SCHEDULE_REGION_FINALIZE,
7662        }];
7663        let slots = vec![
7664            ResidentPhysicalSlotPlan {
7665                schema: Schema::new(Vec::new()),
7666                initial_count: 0,
7667                permanent: true,
7668            },
7669            ResidentPhysicalSlotPlan {
7670                schema: Schema::new(Vec::new()),
7671                initial_count: 0,
7672                permanent: false,
7673            },
7674        ];
7675        let assignments = vec![ResidentSlotAssignment {
7676            slot: 1,
7677            generation: 4,
7678        }];
7679
7680        let plan = resident_lower_compact_regions(
7681            regions,
7682            &slots,
7683            &assignments,
7684            ["source"],
7685            Default::default(),
7686        )
7687        .expect("compact descriptors");
7688
7689        assert_eq!(plan.source_slots["source"], 2);
7690        assert_eq!(plan.ops.len(), 4);
7691        assert_eq!(plan.ops[0].kind, super::ResidentScheduleOpKind::Unit);
7692        assert_eq!(plan.ops[0].out, 1);
7693        assert_eq!(plan.ops[0].out_generation, 4);
7694        assert_eq!(plan.ops[0].schema_winner_head, 0);
7695        assert_eq!(plan.ops[0].schema_winner_id, 3);
7696        assert_eq!(plan.ops[2].kind, super::ResidentScheduleOpKind::Scan);
7697        assert_eq!(plan.ops[2].out, 2);
7698        assert_eq!(plan.ops[2].in0_generation, 0);
7699        assert_eq!(plan.waves.len(), 1);
7700        assert_eq!(plan.waves[0].first_op, 0);
7701        assert_eq!(plan.waves[0].op_count, 4);
7702        assert_eq!(plan.regions.len(), 1);
7703        assert_eq!(plan.generation_bases, vec![0, 4, 0]);
7704    }
7705
7706    #[test]
7707    fn compact_novelty_marker_observes_the_completed_delta_copy() {
7708        let regions = vec![ResidentCompactLogicalRegion {
7709            ops: vec![
7710                ResidentRecordedOp::ChangedReset,
7711                ResidentRecordedOp::Diff {
7712                    left: ResidentBufferRef::Private(0),
7713                    right: ResidentBufferRef::Private(1),
7714                    output: 2,
7715                    op_id: 10,
7716                },
7717                ResidentRecordedOp::Project {
7718                    input: ResidentBufferRef::Private(2),
7719                    output: 3,
7720                    workspace: 0,
7721                    op_id: 11,
7722                },
7723                ResidentRecordedOp::ChangedMark { relation: 3 },
7724            ],
7725            iteration_limit: 3,
7726            op_id: 12,
7727            flags: super::RESIDENT_SCHEDULE_REGION_RECURSIVE,
7728        }];
7729        let slots = (0..4)
7730            .map(|slot| ResidentPhysicalSlotPlan {
7731                schema: Schema::new(Vec::new()),
7732                initial_count: 0,
7733                permanent: slot != 2,
7734            })
7735            .collect::<Vec<_>>();
7736        let assignments = (0..4)
7737            .map(|slot| ResidentSlotAssignment {
7738                slot,
7739                generation: 0,
7740            })
7741            .collect::<Vec<_>>();
7742        let tables = ResidentCompactDescriptorTables {
7743            project_expressions: vec![super::ResidentProjectExpressionDescriptor::column(0, 4)],
7744            project_ranges: vec![(0, 1)],
7745            ..Default::default()
7746        };
7747
7748        let plan = resident_lower_compact_regions(
7749            regions,
7750            &slots,
7751            &assignments,
7752            std::iter::empty(),
7753            tables,
7754        )
7755        .expect("the final delta copy must carry the novelty marker");
7756
7757        assert_eq!(plan.ops.len(), 2);
7758        assert_eq!(plan.ops[0].kind, super::ResidentScheduleOpKind::Diff);
7759        assert_eq!(plan.ops[0].flags, 0);
7760        assert_eq!(plan.ops[1].kind, super::ResidentScheduleOpKind::Project);
7761        assert_eq!(plan.ops[1].flags, super::RESIDENT_SCHEDULE_OP_MARK_NOVELTY);
7762        assert_eq!(plan.ops[1].out, 3);
7763    }
7764
7765    #[test]
7766    fn compact_schema_marker_must_name_the_producer_output() {
7767        let regions = vec![ResidentCompactLogicalRegion {
7768            ops: vec![
7769                ResidentRecordedOp::Project {
7770                    input: ResidentBufferRef::Source("source".to_owned()),
7771                    output: 0,
7772                    workspace: 0,
7773                    op_id: 7,
7774                },
7775                ResidentRecordedOp::SchemaWinnerMark {
7776                    contribution: ResidentBufferRef::Source("source".to_owned()),
7777                    head_index: 0,
7778                    schema_id: 0,
7779                },
7780            ],
7781            iteration_limit: 1,
7782            op_id: 9,
7783            flags: super::RESIDENT_SCHEDULE_REGION_INITIALIZE
7784                | super::RESIDENT_SCHEDULE_REGION_FINALIZE,
7785        }];
7786        let slots = vec![ResidentPhysicalSlotPlan {
7787            schema: Schema::new(Vec::new()),
7788            initial_count: 0,
7789            permanent: true,
7790        }];
7791        let assignments = vec![ResidentSlotAssignment {
7792            slot: 0,
7793            generation: 0,
7794        }];
7795        let tables = ResidentCompactDescriptorTables {
7796            project_ranges: vec![(0, 0)],
7797            ..Default::default()
7798        };
7799
7800        assert!(resident_lower_compact_regions(
7801            regions,
7802            &slots,
7803            &assignments,
7804            ["source"].into_iter(),
7805            tables,
7806        )
7807        .is_err());
7808    }
7809
7810    #[test]
7811    fn compact_schema_defaults_follow_first_marker_per_head() {
7812        let ops = vec![
7813            ResidentOpDescriptor::unit(1, 0, 0).with_schema_winner(1, u32::MAX),
7814            ResidentOpDescriptor::unit(2, 1, 0).with_schema_winner(0, 7),
7815            ResidentOpDescriptor::unit(3, 2, 0).with_schema_winner(1, 9),
7816        ];
7817        assert_eq!(
7818            resident_compact_schema_defaults(&ops, 2).unwrap(),
7819            [7, u32::MAX]
7820        );
7821        assert!(resident_compact_schema_defaults(&ops[..1], 2).is_err());
7822    }
7823
7824    #[test]
7825    fn compact_changed_reset_is_only_absorbed_at_recursive_region_entry() {
7826        let slot = ResidentPhysicalSlotPlan {
7827            schema: Schema::new(Vec::new()),
7828            initial_count: 0,
7829            permanent: false,
7830        };
7831        let assignment = ResidentSlotAssignment {
7832            slot: 0,
7833            generation: 0,
7834        };
7835        let region = |ops, flags| ResidentCompactLogicalRegion {
7836            ops,
7837            iteration_limit: 4,
7838            op_id: 9,
7839            flags,
7840        };
7841
7842        assert!(resident_lower_compact_regions(
7843            vec![region(vec![ResidentRecordedOp::ChangedReset], 0)],
7844            std::slice::from_ref(&slot),
7845            std::slice::from_ref(&assignment),
7846            std::iter::empty(),
7847            Default::default(),
7848        )
7849        .is_err());
7850        assert!(resident_lower_compact_regions(
7851            vec![region(
7852                vec![
7853                    ResidentRecordedOp::Unit {
7854                        output: 0,
7855                        op_id: 1,
7856                    },
7857                    ResidentRecordedOp::ChangedReset,
7858                ],
7859                super::RESIDENT_SCHEDULE_REGION_RECURSIVE,
7860            )],
7861            std::slice::from_ref(&slot),
7862            std::slice::from_ref(&assignment),
7863            std::iter::empty(),
7864            Default::default(),
7865        )
7866        .is_err());
7867        assert!(resident_lower_compact_regions(
7868            vec![region(
7869                vec![
7870                    ResidentRecordedOp::ChangedReset,
7871                    ResidentRecordedOp::Unit {
7872                        output: 0,
7873                        op_id: 1,
7874                    },
7875                ],
7876                super::RESIDENT_SCHEDULE_REGION_RECURSIVE,
7877            )],
7878            &[slot],
7879            &[assignment],
7880            std::iter::empty(),
7881            Default::default(),
7882        )
7883        .is_ok());
7884    }
7885
7886    #[test]
7887    fn compact_filter_project_tables_preserve_types_widths_and_order() {
7888        let input = Schema::new(vec![
7889            ("symbol".to_owned(), ScalarType::Symbol),
7890            ("number".to_owned(), ScalarType::U64),
7891        ]);
7892        let predicate = Expr::And(vec![
7893            Expr::Compare {
7894                left: Box::new(Expr::Column(1)),
7895                op: CompareOp::Ge,
7896                right: Box::new(Expr::Const(ConstValue::U64(9))),
7897            },
7898            Expr::Compare {
7899                left: Box::new(Expr::Column(0)),
7900                op: CompareOp::Eq,
7901                right: Box::new(Expr::Const(ConstValue::Symbol("x".to_owned()))),
7902            },
7903        ]);
7904        let comparisons =
7905            resident_compact_filter_descriptors(&predicate, &input).expect("filter table");
7906        assert_eq!(comparisons.len(), 2);
7907        assert_eq!(comparisons[0].left_column, 1);
7908        assert_eq!(comparisons[0].width, 8);
7909        assert_eq!(comparisons[0].right_constant, 9);
7910        assert_eq!(comparisons[1].left_column, 0);
7911        assert_eq!(comparisons[1].width, 4);
7912
7913        let output = Schema::new(vec![
7914            ("number".to_owned(), ScalarType::U64),
7915            ("symbol".to_owned(), ScalarType::Symbol),
7916        ]);
7917        let expressions = resident_compact_project_descriptors(
7918            &[
7919                ProjectExpr::Column(1),
7920                ProjectExpr::Computed(
7921                    Expr::Const(ConstValue::Symbol("y".to_owned())),
7922                    ScalarType::Symbol,
7923                ),
7924            ],
7925            &input,
7926            &output,
7927        )
7928        .expect("project table");
7929        assert_eq!(expressions.len(), output.arity());
7930        assert_eq!(expressions[0].column, 1);
7931        assert_eq!(expressions[0].width, 8);
7932        assert_eq!(expressions[1].kind, 1);
7933        assert_eq!(expressions[1].width, 4);
7934
7935        let mismatch = Expr::Compare {
7936            left: Box::new(Expr::Column(1)),
7937            op: CompareOp::Eq,
7938            right: Box::new(Expr::Const(ConstValue::Symbol("wrong".to_owned()))),
7939        };
7940        assert!(resident_compact_filter_descriptors(&mismatch, &input).is_err());
7941    }
7942
7943    #[test]
7944    fn compact_tables_cover_multiple_empty_occurrences_without_duplicate_payloads() {
7945        let filters = vec![
7946            ResidentFilterPlan {
7947                compact_comparisons: Vec::new(),
7948            },
7949            ResidentFilterPlan {
7950                compact_comparisons: Vec::new(),
7951            },
7952        ];
7953        let projects = vec![ResidentProjectPlan {
7954            compact_expressions: Vec::new(),
7955        }];
7956
7957        let tables = resident_compact_tables(&filters, &projects).expect("compact tables");
7958
7959        assert!(tables.filter_comparisons.is_empty());
7960        assert_eq!(tables.filter_ranges, [(0, 0), (0, 0)]);
7961        assert!(tables.project_expressions.is_empty());
7962        assert_eq!(tables.project_ranges, [(0, 0)]);
7963    }
7964
7965    #[test]
7966    fn compact_descriptor_ranges_follow_operation_order_exactly() {
7967        let regions = vec![ResidentCompactLogicalRegion {
7968            ops: vec![
7969                ResidentRecordedOp::Project {
7970                    input: ResidentBufferRef::Source("source".to_owned()),
7971                    output: 0,
7972                    workspace: 1,
7973                    op_id: 1,
7974                },
7975                ResidentRecordedOp::Project {
7976                    input: ResidentBufferRef::Source("source".to_owned()),
7977                    output: 1,
7978                    workspace: 0,
7979                    op_id: 2,
7980                },
7981            ],
7982            iteration_limit: 1,
7983            op_id: 3,
7984            flags: super::RESIDENT_SCHEDULE_REGION_INITIALIZE
7985                | super::RESIDENT_SCHEDULE_REGION_FINALIZE,
7986        }];
7987        let slots = vec![
7988            ResidentPhysicalSlotPlan {
7989                schema: Schema::new(Vec::new()),
7990                initial_count: 0,
7991                permanent: false,
7992            },
7993            ResidentPhysicalSlotPlan {
7994                schema: Schema::new(Vec::new()),
7995                initial_count: 0,
7996                permanent: false,
7997            },
7998        ];
7999        let assignments = vec![
8000            ResidentSlotAssignment {
8001                slot: 0,
8002                generation: 0,
8003            },
8004            ResidentSlotAssignment {
8005                slot: 1,
8006                generation: 0,
8007            },
8008        ];
8009        let tables = ResidentCompactDescriptorTables {
8010            project_expressions: vec![Default::default(), Default::default()],
8011            project_ranges: vec![(0, 1), (1, 1)],
8012            ..Default::default()
8013        };
8014
8015        assert!(resident_lower_compact_regions(
8016            regions,
8017            &slots,
8018            &assignments,
8019            ["source"].into_iter(),
8020            tables,
8021        )
8022        .is_err());
8023    }
8024
8025    #[test]
8026    fn compact_metadata_reservation_counts_generation_bases_and_schema_defaults() {
8027        let actual = resident_compact_schedule_metadata_bytes(3, 5, 5, 2, 6, 4, 0, 0)
8028            .expect("metadata bytes");
8029        let expected = super::resident_schedule_metadata_device_bytes(3, 5, 5, 2, 10, 0, 0)
8030            .expect("expected metadata bytes");
8031        let missing_defaults = super::resident_schedule_metadata_device_bytes(3, 5, 5, 2, 6, 0, 0)
8032            .expect("generation-only bytes");
8033
8034        assert_eq!(actual, expected);
8035        assert_ne!(actual, missing_defaults);
8036    }
8037
8038    #[test]
8039    fn compact_manifest_replaces_per_occurrence_descriptor_allocations() {
8040        let plan = super::ResidentCompactSchedulePlan {
8041            source_slots: [("source".to_owned(), 2)].into_iter().collect(),
8042            ops: Vec::new(),
8043            waves: Vec::new(),
8044            regions: Vec::new(),
8045            generation_bases: Vec::new(),
8046            filter_comparisons: Vec::new(),
8047            project_expressions: Vec::new(),
8048        };
8049        let (required, metadata) =
8050            resident_compact_allocation_bytes(1_000, 2_000, 3_000, 2, 2, &plan).unwrap();
8051        let expected_metadata =
8052            resident_compact_schedule_metadata_bytes(3, 0, 0, 0, 0, 2, 0, 0).unwrap();
8053        assert_eq!(metadata, expected_metadata);
8054        assert_eq!(required, 1_000 + 2_000 + 3_000 + expected_metadata);
8055        assert_ne!(
8056            required,
8057            1_000 + 48 + 2_000 + 24 + 3_000 + expected_metadata
8058        );
8059    }
8060
8061    #[test]
8062    fn compact_preflight_bytes_report_flattened_tables_without_double_counting() {
8063        let assert_components = |filter_count: usize, project_count: usize| {
8064            let plan = super::ResidentCompactSchedulePlan {
8065                source_slots: [("source".to_owned(), 2)].into_iter().collect(),
8066                ops: Vec::new(),
8067                waves: Vec::new(),
8068                regions: Vec::new(),
8069                generation_bases: Vec::new(),
8070                filter_comparisons: vec![
8071                    super::ResidentFilterComparisonDescriptor::default();
8072                    filter_count
8073                ],
8074                project_expressions: vec![
8075                    super::ResidentProjectExpressionDescriptor::default();
8076                    project_count
8077                ],
8078            };
8079            let mut manifest = ResidentAllocationManifest {
8080                slots: Vec::new(),
8081                logical_to_slot: Vec::new(),
8082                required_bytes: 0,
8083                relation_bytes: 1_000,
8084                filter_scratch_bytes: 2_000,
8085                schedule_metadata_bytes: 0,
8086                fixed_workspace_bytes: 3_000,
8087                logical_relation_values: 0,
8088                permanent_slots: 0,
8089                scratch_slots: 0,
8090                filter_scratch_allocations: 1,
8091                max_row_bytes: 0,
8092            };
8093            manifest.finalize_compact_schedule(&plan, 2).unwrap();
8094
8095            let (filter_bytes, project_bytes, fixed_bytes) =
8096                resident_compact_preflight_device_bytes(&manifest, &plan).unwrap();
8097            assert_eq!(
8098                filter_bytes,
8099                48 * u64::try_from(filter_count.max(1)).unwrap()
8100            );
8101            assert_eq!(
8102                project_bytes,
8103                24 * u64::try_from(project_count.max(1)).unwrap()
8104            );
8105            assert_eq!(
8106                manifest.relation_bytes
8107                    + manifest.filter_scratch_bytes
8108                    + filter_bytes
8109                    + project_bytes
8110                    + fixed_bytes,
8111                manifest.required_bytes
8112            );
8113        };
8114
8115        assert_components(0, 0);
8116        assert_components(3, 2);
8117    }
8118
8119    #[test]
8120    fn compact_manifest_is_exact_and_preserves_permanent_head_mapping() {
8121        let mut manifest = ResidentAllocationManifest {
8122            slots: vec![
8123                ResidentPhysicalSlotPlan {
8124                    schema: Schema::new(Vec::new()),
8125                    initial_count: 0,
8126                    permanent: true,
8127                },
8128                ResidentPhysicalSlotPlan {
8129                    schema: Schema::new(Vec::new()),
8130                    initial_count: 0,
8131                    permanent: true,
8132                },
8133            ],
8134            logical_to_slot: vec![
8135                ResidentSlotAssignment {
8136                    slot: 0,
8137                    generation: 0,
8138                },
8139                ResidentSlotAssignment {
8140                    slot: 1,
8141                    generation: 0,
8142                },
8143            ],
8144            required_bytes: 0,
8145            relation_bytes: 1_000,
8146            filter_scratch_bytes: 2_000,
8147            schedule_metadata_bytes: 0,
8148            fixed_workspace_bytes: 3_000,
8149            logical_relation_values: 2,
8150            permanent_slots: 2,
8151            scratch_slots: 0,
8152            filter_scratch_allocations: 1,
8153            max_row_bytes: 1,
8154        };
8155        let plan = super::ResidentCompactSchedulePlan {
8156            source_slots: [("external".to_owned(), 2)].into_iter().collect(),
8157            ops: Vec::new(),
8158            waves: Vec::new(),
8159            regions: Vec::new(),
8160            generation_bases: Vec::new(),
8161            filter_comparisons: Vec::new(),
8162            project_expressions: Vec::new(),
8163        };
8164        manifest.finalize_compact_schedule(&plan, 1).unwrap();
8165        let without_source =
8166            resident_compact_schedule_metadata_bytes(2, 0, 0, 0, 0, 1, 0, 0).unwrap();
8167        assert_eq!(manifest.schedule_metadata_bytes - without_source, 240);
8168        assert_eq!(
8169            manifest.required_bytes,
8170            manifest.relation_bytes
8171                + manifest.filter_scratch_bytes
8172                + manifest.fixed_workspace_bytes
8173                + manifest.schedule_metadata_bytes
8174        );
8175        resident_validate_exact_reservation(manifest.required_bytes, manifest.required_bytes, 0)
8176            .unwrap();
8177        assert!(resident_validate_exact_reservation(manifest.required_bytes, 5_999, 1).is_err());
8178        let heads = [("head".to_owned(), 0_usize)].into_iter().collect();
8179        assert_eq!(
8180            resident_output_indices(&heads, &manifest.logical_to_slot, &manifest.slots).unwrap(),
8181            vec![("head".to_owned(), 0)]
8182        );
8183        let mut scratch_slots = manifest.slots.clone();
8184        scratch_slots[0].permanent = false;
8185        assert!(
8186            resident_output_indices(&heads, &manifest.logical_to_slot, &scratch_slots,).is_err()
8187        );
8188    }
8189
8190    #[test]
8191    fn compact_distinct_declines_partial_keys_before_planning() {
8192        let node = RirNode::Distinct {
8193            input: Box::new(RirNode::Scan { rel: RelId(1) }),
8194            key_cols: vec![0],
8195        };
8196        let schema = Schema::new(vec![
8197            ("key".to_owned(), ScalarType::U32),
8198            ("value".to_owned(), ScalarType::U32),
8199        ]);
8200
8201        let error = validate_compact_resident_node_envelope(&node, &|_| Ok(schema.clone()))
8202            .expect_err("a partial-key distinct must decline before allocation");
8203
8204        let crate::resident_graph::ResidentGraphDeclineReason::WorkspaceUnbounded { detail } =
8205            error
8206        else {
8207            panic!("expected a workspace-envelope decline");
8208        };
8209        assert!(detail.contains("canonical full-row key columns"));
8210    }
8211
8212    #[test]
8213    fn compact_distinct_accepts_empty_keys_for_nullary_rows() {
8214        let node = RirNode::Distinct {
8215            input: Box::new(RirNode::Unit),
8216            key_cols: vec![],
8217        };
8218
8219        validate_compact_resident_node_envelope(&node, &|_| Ok(Schema::new(Vec::new())))
8220            .expect("the canonical full-row key for an arity-zero relation is empty");
8221    }
8222
8223    #[test]
8224    fn compact_join_declines_same_width_different_key_types() {
8225        let node = RirNode::Join {
8226            left: Box::new(RirNode::Scan { rel: RelId(1) }),
8227            right: Box::new(RirNode::Scan { rel: RelId(2) }),
8228            left_keys: vec![0],
8229            right_keys: vec![0],
8230            join_type: JoinType::Inner,
8231        };
8232
8233        let error = validate_compact_resident_node_envelope(&node, &|node| match node {
8234            RirNode::Scan { rel: RelId(1) } => {
8235                Ok(Schema::new(vec![("key".to_owned(), ScalarType::U32)]))
8236            }
8237            RirNode::Scan { rel: RelId(2) } => {
8238                Ok(Schema::new(vec![("key".to_owned(), ScalarType::Symbol)]))
8239            }
8240            _ => unreachable!("the envelope asks only for operand schemas"),
8241        })
8242        .expect_err("same-width but different key types must decline");
8243
8244        let crate::resident_graph::ResidentGraphDeclineReason::WorkspaceUnbounded { detail } =
8245            error
8246        else {
8247            panic!("expected a workspace-envelope decline");
8248        };
8249        assert!(detail.contains("matching U32, U64, or Symbol key types"));
8250    }
8251
8252    #[test]
8253    fn compact_union_shape_requires_unary_self_union() {
8254        assert!(resident_union_fold_mode(0).is_err());
8255        assert_eq!(
8256            resident_union_fold_mode(1).unwrap(),
8257            ResidentUnionFoldMode::SelfUnion
8258        );
8259        assert_eq!(
8260            resident_union_fold_mode(4).unwrap(),
8261            ResidentUnionFoldMode::LeftAssociated
8262        );
8263    }
8264}