Skip to main content

xlog_cuda/
semantic_training_view.rs

1use std::mem::size_of;
2use std::sync::Arc;
3
4use sha2::{Digest, Sha256};
5
6use crate::launch::LaunchEnqueueError;
7use crate::memory::{DeviceMemoryView, TrackedCudaSlice};
8use crate::provider::resident_schedule::{validate_execution_domain, ResidentExecutionDomain};
9use crate::semantic_transition::{
10    Identity256, SemanticPublishedIdentity, SemanticRngBinding, SemanticTaskContentIdentity,
11    SemanticTransitionError, SemanticTransitionKind,
12};
13use crate::{
14    CudaFunction, CudaKernelProvider, DeviceRepr, LaunchAsync, LaunchConfig, SemanticTruth,
15};
16
17type TrainingViewPort = (DeviceMemoryView<u8>, Vec<i64>, Vec<i64>, (u8, u8));
18type ValidatedTrainingObjective = (
19    SemanticTrainingObjectiveRecord,
20    Vec<SemanticTrainingObjectiveGroupRecord>,
21    Vec<u64>,
22    Vec<SemanticTrainingCanaryRecord>,
23    Vec<u64>,
24);
25
26const MODULE: &str = "xlog_semantic_training_view";
27const SELECT_KERNEL: &str = "semantic_training_view_select";
28const GATHER_KERNEL: &str = "semantic_training_view_gather";
29const TRAINING_VIEW_HEADER_BYTES: usize = 264;
30const TRAINING_VIEW_ROW_BYTES: usize = 84;
31const PROPOSAL_TRANSITION: u64 = 1;
32pub const SEMANTIC_TRAINING_CANARY_EVALUATOR_ABI: u64 = 3;
33
34/// Validated geometry of the bytes consumed by native training-view selection.
35#[derive(Clone, Copy, Debug)]
36pub struct SemanticTrainingViewLayout {
37    pub window: usize,
38    pub source_length: u64,
39    pub block_size: u64,
40    pub prefix_extent: u64,
41    pub answer_start: u64,
42    pub branch_words: [u64; 16],
43}
44
45/// Origin of one authentic replay training view.
46#[repr(u64)]
47#[derive(Clone, Copy, Debug, Eq, PartialEq)]
48pub enum SemanticTrainingViewBasis {
49    Episode = 1,
50    CorpusLanguageAnchor = 2,
51    CorpusSymbolicAnchor = 3,
52}
53
54/// Authenticated native execution that produced one episode training view.
55/// Corpus anchors have no execution origin and use their independent graph.
56#[derive(Clone, Copy, Debug, Eq, PartialEq)]
57pub struct SemanticTrainingViewOrigin {
58    pub transition: SemanticTransitionKind,
59    pub predecessor: SemanticPublishedIdentity,
60    pub successor: SemanticPublishedIdentity,
61    pub invocation: SemanticRngBinding,
62    pub model_geometry_digest: Identity256,
63    pub model_numerical_digest: Identity256,
64}
65
66/// Exact sealed input identity and its four admission caps, in source order:
67/// entries, total keys, records per input, bytes per record.
68#[derive(Clone, Debug, PartialEq, Eq)]
69pub struct SemanticTrainingManifest {
70    pub identity: Identity256,
71    pub caps: [u64; 4],
72}
73
74/// Prospective training domain fixed before any action, not a selected roster
75/// or a numerical certificate. The application supplies the original sealed
76/// manifests and complete loader geometry; native admission retains them.
77#[derive(Clone, Debug, PartialEq, Eq)]
78pub struct SemanticTrainingDomain {
79    /// Strictly increasing by identity; no duplicate manifest or omitted cap.
80    pub manifests: Vec<SemanticTrainingManifest>,
81    pub window: u64,
82    pub pad_id: u64,
83    pub mask_id: u64,
84    pub block_size: u64,
85    pub batch_size: u64,
86    pub mask_permille: u64,
87    /// Canonical nonnegative decimal, preserving arbitrary-width source seeds.
88    pub training_seed: String,
89    pub mask_policy: Identity256,
90}
91
92impl SemanticTrainingDomain {
93    pub(crate) fn validate(&self) -> Result<(), SemanticTransitionError> {
94        let seed = self.training_seed.as_bytes();
95        if self.manifests.is_empty()
96            || self.manifests.iter().any(|manifest| {
97                manifest.identity == Identity256::default() || manifest.caps.contains(&0)
98            })
99            || self
100                .manifests
101                .windows(2)
102                .any(|pair| pair[0].identity.as_bytes() >= pair[1].identity.as_bytes())
103            || self.window == 0
104            || self.block_size == 0
105            || self.batch_size == 0
106            || !(1..=1000).contains(&self.mask_permille)
107            || self.mask_policy == Identity256::default()
108            || !(seed == b"0"
109                || (matches!(seed.first(), Some(b'1'..=b'9'))
110                    && seed.iter().all(u8::is_ascii_digit)))
111        {
112            return Err(input_error(
113                "training domain requires exact manifests and loader geometry",
114            ));
115        }
116        Ok(())
117    }
118
119    /// Each original episodes/anchors input is independently bounded by this
120    /// minimum. It is not derived from arena storage or byte capacities.
121    pub fn record_limit(&self) -> u64 {
122        self.manifests
123            .iter()
124            .map(|manifest| manifest.caps[2])
125            .min()
126            .unwrap_or(0)
127    }
128
129    /// Cold metadata identity retained by task/checkpoint ownership, not a grant.
130    pub fn identity(&self) -> Identity256 {
131        let mut hash = Sha256::new();
132        hash.update(b"xlog.semantic.training-domain.v1\0");
133        hash.update((self.manifests.len() as u64).to_le_bytes());
134        for manifest in &self.manifests {
135            hash.update(manifest.identity.as_bytes());
136            for cap in manifest.caps {
137                hash.update(cap.to_le_bytes());
138            }
139        }
140        for word in [
141            self.window,
142            self.pad_id,
143            self.mask_id,
144            self.block_size,
145            self.batch_size,
146            self.mask_permille,
147        ] {
148            hash.update(word.to_le_bytes());
149        }
150        hash.update((self.training_seed.len() as u64).to_le_bytes());
151        hash.update(self.training_seed.as_bytes());
152        hash.update(self.mask_policy.as_bytes());
153        Identity256::from_bytes(hash.finalize().into())
154    }
155}
156
157/// One already-admitted training view retained for cold device selection.
158pub struct SemanticTrainingViewRow {
159    pub basis: SemanticTrainingViewBasis,
160    pub identity: Identity256,
161    pub bytes_identity: Identity256,
162    pub content_identity: Identity256,
163    /// Original record's training-view identity, validated by its cold owner.
164    pub data_manifest: Identity256,
165    pub mask_policy: Identity256,
166    pub training_seed: String,
167    /// Present only for a symbolic corpus anchor. The three identities must
168    /// equal the native task content executed during this cold binding.
169    pub task_content: Option<SemanticTaskContentIdentity>,
170    pub origin: Option<SemanticTrainingViewOrigin>,
171    pub bytes: Vec<u8>,
172}
173
174/// Frozen evaluator term attached to the complete device-resident training roster.
175#[repr(u64)]
176#[derive(Clone, Copy, Debug, Eq, PartialEq)]
177pub enum SemanticTrainingObjectiveGroupKind {
178    MaskedLanguage = 1,
179    AutoregressiveLanguage = 2,
180    Semantic = 3,
181    Edit = 4,
182    Execution = 5,
183    RetentionLanguage = 6,
184    RetentionSymbolic = 7,
185    ActorCriticCost = 8,
186}
187
188/// One nonempty reduction group in the frozen training objective.
189#[derive(Clone, Debug, Eq, PartialEq)]
190pub struct SemanticTrainingObjectiveGroup {
191    pub kind: SemanticTrainingObjectiveGroupKind,
192    pub denominator: u64,
193    pub row_ordinals: Vec<u64>,
194}
195
196/// Mandatory acceptance gate evaluated from original full-vocabulary logits.
197#[repr(u64)]
198#[derive(Clone, Copy, Debug, Eq, PartialEq)]
199pub enum SemanticTrainingCanaryKind {
200    SymbolicUtility = 1,
201    RetainedBehavior = 2,
202    LogitDrift = 3,
203    GoalChain = 4,
204    ResourceLimits = 5,
205}
206
207/// Frozen bounds and resource ceilings for one candidate-update canary.
208#[derive(Clone, Debug, PartialEq)]
209pub struct SemanticTrainingCanary {
210    pub kind: SemanticTrainingCanaryKind,
211    pub row_ordinal: u64,
212    pub lower_bound: f64,
213    pub upper_bound: f64,
214    pub memory_limit: u64,
215    pub work_limit: u64,
216    /// Model-logit positions whose next-token predictions must equal the
217    /// canonical truth token for each native task result, in query order. Only
218    /// the symbolic-utility and goal-chain canaries use these coordinates;
219    /// every other kind uses `[u64::MAX; 3]`.
220    pub obligation_positions: [u64; 3],
221    /// Explicit frozen members whose individual retention is uncompensated.
222    /// Only the retained-behavior canary carries this set. Aggregate retention
223    /// labels remain the metric denominator and do not imply protection.
224    pub protected_positions: Vec<u64>,
225}
226
227/// Complete frozen objective carried by the canonical replay-roster owner.
228#[derive(Clone, Debug, PartialEq)]
229pub struct SemanticTrainingObjective {
230    pub evaluator_min: f64,
231    pub evaluator_max: f64,
232    /// Masked-language, autoregressive, semantic, edit, execution, retention,
233    /// actor, critic and cost coefficients, in that order.
234    pub coefficients: [f32; 9],
235    pub cost_unit: Identity256,
236    pub cost_cap: u64,
237    /// Vocabulary token representing NEITHER, TRUE, FALSE and BOTH.
238    pub truth_tokens: [u64; 4],
239    pub groups: Vec<SemanticTrainingObjectiveGroup>,
240    pub canaries: Vec<SemanticTrainingCanary>,
241}
242
243/// One coefficient law shared by cold task admission and full arena validation.
244pub(crate) fn frozen_training_coefficients(evaluator_min: f64, evaluator_max: f64) -> [f32; 9] {
245    let return_scale = evaluator_min.abs().max(evaluator_max.abs()).max(1.0);
246    let mut coefficients = [1.0f32; 9];
247    coefficients[6] = (1.0 / return_scale) as f32;
248    coefficients[7] = (1.0 / (return_scale * return_scale)) as f32;
249    coefficients
250}
251
252#[repr(C)]
253#[derive(Clone, Copy, Debug, Default)]
254pub struct SemanticTrainingObjectiveRecord {
255    pub evaluator_abi: u64,
256    pub identity: [u64; 4],
257    pub task_identity: [u64; 4],
258    pub row_count: u64,
259    pub capacity: u64,
260    pub group_count: u64,
261    pub group_member_count: u64,
262    pub canary_count: u64,
263    pub protected_member_count: u64,
264    pub evaluator_min_bits: u64,
265    pub evaluator_max_bits: u64,
266    pub coefficient_bits: [u64; 9],
267    pub cost_unit: [u64; 4],
268    pub cost_cap: u64,
269    pub truth_tokens: [u64; 4],
270}
271
272// SAFETY: the fixed CUDA ABI contains only u64 words.
273unsafe impl DeviceRepr for SemanticTrainingObjectiveRecord {}
274
275#[repr(C)]
276#[derive(Clone, Copy, Debug, Default)]
277pub struct SemanticTrainingObjectiveGroupRecord {
278    pub kind: u64,
279    pub denominator: u64,
280    pub member_offset: u64,
281    pub member_count: u64,
282}
283
284// SAFETY: the fixed CUDA ABI contains only u64 words.
285unsafe impl DeviceRepr for SemanticTrainingObjectiveGroupRecord {}
286
287#[repr(C)]
288#[derive(Clone, Copy, Debug, Default)]
289pub struct SemanticTrainingCanaryRecord {
290    pub evaluator_abi: u64,
291    pub kind: u64,
292    pub row_ordinal: u64,
293    pub lower_bound_bits: u64,
294    pub upper_bound_bits: u64,
295    pub memory_limit: u64,
296    pub work_limit: u64,
297    pub obligation_positions: [u64; 3],
298    pub protected_member_offset: u64,
299    pub protected_member_count: u64,
300    pub row_identity: [u64; 4],
301    pub row_content_identity: [u64; 4],
302    pub task_identity: [u64; 4],
303    pub identity: [u64; 4],
304}
305
306// SAFETY: the fixed CUDA ABI contains only u64 words.
307unsafe impl DeviceRepr for SemanticTrainingCanaryRecord {}
308
309/// Native device-produced measurement for one frozen candidate-update canary.
310///
311/// The result repeats the frozen kind, row and identity so the native update
312/// gate can reject a measurement produced for any other roster entry. The
313/// measurement is carried as raw FP64 bits; memory and work are exact integer
314/// tallies checked against the frozen ceilings.
315#[repr(C)]
316#[derive(Clone, Copy, Debug, Default)]
317pub(crate) struct SemanticTrainingCanaryResultRecord {
318    pub abi: u64,
319    pub kind: u64,
320    pub row_ordinal: u64,
321    /// Zero: not evaluated; one: evaluation started without a complete metric;
322    /// two: the complete original metric was obtained, including refused values.
323    pub availability: u64,
324    pub reason: u64,
325    pub measurement_bits: u64,
326    pub memory_used: u64,
327    pub work_used: u64,
328    pub identity: [u64; 4],
329}
330
331// SAFETY: the fixed CUDA ABI contains only u64 words.
332unsafe impl DeviceRepr for SemanticTrainingCanaryResultRecord {}
333
334const _: () = assert!(size_of::<SemanticTrainingCanaryResultRecord>() == 96);
335
336/// Closed device reason for refusing a candidate model update canary.
337#[repr(u64)]
338#[derive(Clone, Copy, Debug, PartialEq, Eq)]
339pub enum SemanticTrainingCanaryRefusalReason {
340    NonFiniteMeasurement = 1,
341    OutsideBounds = 2,
342    MemoryLimitExceeded = 3,
343    WorkLimitExceeded = 4,
344    IncompleteOperands = 5,
345    ProtectedRetentionLost = 6,
346    GoalWitnessInvalid = 7,
347    WorkOverflow = 8,
348    GenerationMismatch = 9,
349}
350
351/// Device-authored evidence for the highest-precedence failed update canary.
352///
353/// A successful canary join has ABI one and every other word zero. A refusal
354/// retains the exact measured value, frozen comparison bounds, resource use and
355/// limits, and both the canary and selected-view identities.
356#[repr(C)]
357#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
358pub struct SemanticTrainingCanaryRefusalRecord {
359    pub abi: u64,
360    pub reason: u64,
361    pub kind: u64,
362    pub row_ordinal: u64,
363    pub measurement_bits: u64,
364    pub lower_bound_bits: u64,
365    pub upper_bound_bits: u64,
366    pub memory_used: u64,
367    pub memory_limit: u64,
368    pub work_used: u64,
369    pub work_limit: u64,
370    pub identity: [u64; 4],
371    pub selection_identity: [u64; 4],
372}
373
374impl SemanticTrainingCanaryRefusalRecord {
375    pub fn reason(&self) -> Option<SemanticTrainingCanaryRefusalReason> {
376        match self.reason {
377            1 => Some(SemanticTrainingCanaryRefusalReason::NonFiniteMeasurement),
378            2 => Some(SemanticTrainingCanaryRefusalReason::OutsideBounds),
379            3 => Some(SemanticTrainingCanaryRefusalReason::MemoryLimitExceeded),
380            4 => Some(SemanticTrainingCanaryRefusalReason::WorkLimitExceeded),
381            5 => Some(SemanticTrainingCanaryRefusalReason::IncompleteOperands),
382            6 => Some(SemanticTrainingCanaryRefusalReason::ProtectedRetentionLost),
383            7 => Some(SemanticTrainingCanaryRefusalReason::GoalWitnessInvalid),
384            8 => Some(SemanticTrainingCanaryRefusalReason::WorkOverflow),
385            9 => Some(SemanticTrainingCanaryRefusalReason::GenerationMismatch),
386            _ => None,
387        }
388    }
389}
390
391// SAFETY: the fixed CUDA ABI contains only u64 words.
392unsafe impl DeviceRepr for SemanticTrainingCanaryRefusalRecord {}
393
394#[repr(C)]
395#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
396pub struct SemanticTrainingViewOriginRecord {
397    pub present: u64,
398    pub transition: u64,
399    /// Stable historical predecessor identity. Historical rows use their
400    /// original instance; a fresh replay candidate derives it from the restored
401    /// predecessor's recovered-instance seal.
402    pub lineage_instance: [u64; 4],
403    pub predecessor_instance: [u64; 4],
404    pub predecessor_word: u64,
405    pub predecessor_logical: [u64; 4],
406    pub predecessor_state: [u64; 4],
407    pub successor_instance: [u64; 4],
408    pub successor_word: u64,
409    pub successor_logical: [u64; 4],
410    pub successor_state: [u64; 4],
411    pub model_generation: u64,
412    pub stream_serial: u64,
413    pub family_id: u64,
414    pub proposal: u64,
415    pub model_geometry_digest: [u64; 4],
416    pub model_numerical_digest: [u64; 4],
417}
418
419// SAFETY: the fixed CUDA ABI contains only u64 words.
420unsafe impl DeviceRepr for SemanticTrainingViewOriginRecord {}
421
422#[repr(C)]
423#[derive(Clone, Copy, Default)]
424struct TrainingViewRowDescriptor {
425    ordinal: u64,
426    basis: u64,
427    raw_offset: u64,
428    raw_bytes: u64,
429    window: u64,
430    source_length: u64,
431    block_size: u64,
432    prefix_extent: u64,
433    answer_start: u64,
434    identity: [u64; 4],
435    source_identity: [u64; 4],
436    content_identity: [u64; 4],
437    task_query_identity: [u64; 4],
438    task_theory_program_identity: [u64; 4],
439    task_result_identity: [u64; 4],
440    origin: SemanticTrainingViewOriginRecord,
441}
442
443// SAFETY: the fixed CUDA ABI contains only u64 words.
444unsafe impl DeviceRepr for TrainingViewRowDescriptor {}
445
446/// Device-written metadata for every row in the selected objective roster.
447#[repr(C)]
448#[derive(Clone, Copy, Debug, Default)]
449pub struct SemanticTrainingRosterRow {
450    pub ordinal: u64,
451    pub basis: u64,
452    pub window: u64,
453    pub source_length: u64,
454    pub block_size: u64,
455    pub prefix_extent: u64,
456    pub answer_start: u64,
457    pub origin_candidate: u64,
458    pub identity: [u64; 4],
459    pub source_identity: [u64; 4],
460    pub content_identity: [u64; 4],
461    /// Authenticated historical execution origin for episode rows. The separate
462    /// candidate ordinal, when present, names a successful fresh re-execution.
463    pub origin: SemanticTrainingViewOriginRecord,
464}
465
466// SAFETY: the fixed CUDA ABI contains only u64 words.
467unsafe impl DeviceRepr for SemanticTrainingRosterRow {}
468
469/// Device-written selection coordinates. Status zero is the only admissible
470/// result; downstream Update work must predicate on this resident word.
471#[repr(C)]
472#[derive(Clone, Copy, Debug, Default)]
473pub struct SemanticTrainingViewSelection {
474    pub status: u64,
475    pub row_count: u64,
476    pub capacity: u64,
477    pub ordinal: u64,
478    pub basis: u64,
479    pub window: u64,
480    pub source_length: u64,
481    pub block_size: u64,
482    pub prefix_extent: u64,
483    pub answer_start: u64,
484    pub identity: [u64; 4],
485    pub source_identity: [u64; 4],
486    pub content_identity: [u64; 4],
487    pub origin: SemanticTrainingViewOriginRecord,
488    pub origin_candidate: u64,
489    pub training_rng: [u64; 4],
490}
491
492// SAFETY: the fixed CUDA ABI contains only u64 words.
493unsafe impl DeviceRepr for SemanticTrainingViewSelection {}
494
495#[repr(C)]
496#[derive(Clone, Copy, Default)]
497struct TrainingViewLaunch {
498    descriptors: u64,
499    raw: u64,
500    row_count: u64,
501    selected_view: u64,
502    selected_view_bytes: u64,
503    cursor: u64,
504    training_rng: [u64; 4],
505    coordinates: u64,
506    origin_candidates: u64,
507    origin_candidate_count: u64,
508    selection: u64,
509    roster_rows: u64,
510    capacity: u64,
511    token_ids: u64,
512    mask_labels: u64,
513    mask_weights: u64,
514    ar_labels: u64,
515    retention_labels: u64,
516    branch_labels: u64,
517    branch_ids: u64,
518    source_slots: u64,
519    logical_positions: u64,
520    kinds: u64,
521    parents: u64,
522}
523
524// SAFETY: the fixed CUDA ABI contains only u64 words.
525unsafe impl DeviceRepr for TrainingViewLaunch {}
526
527struct TrainingViewLaunchParam(TrainingViewLaunch);
528
529impl crate::cuda_compat::KernelParamStorage for TrainingViewLaunchParam {
530    fn as_kernel_param(&self) -> *mut std::ffi::c_void {
531        (&self.0 as *const TrainingViewLaunch).cast_mut().cast()
532    }
533}
534
535impl crate::cuda_compat::IntoKernelParamStorage for TrainingViewLaunch {
536    type Storage = TrainingViewLaunchParam;
537
538    fn into_kernel_param_storage(self) -> Self::Storage {
539        TrainingViewLaunchParam(self)
540    }
541}
542
543struct SelectedTrainingViewStorage {
544    selection: TrackedCudaSlice<SemanticTrainingViewSelection>,
545    roster_rows: TrackedCudaSlice<SemanticTrainingRosterRow>,
546    token_ids: TrackedCudaSlice<i64>,
547    mask_labels: TrackedCudaSlice<i64>,
548    mask_weights: TrackedCudaSlice<f32>,
549    ar_labels: TrackedCudaSlice<i64>,
550    retention_labels: TrackedCudaSlice<i64>,
551    branch_labels: TrackedCudaSlice<i64>,
552    branch_ids: TrackedCudaSlice<i64>,
553    source_slots: TrackedCudaSlice<i64>,
554    logical_positions: TrackedCudaSlice<i64>,
555    kinds: TrackedCudaSlice<i64>,
556    parents: TrackedCudaSlice<i64>,
557    #[cfg(feature = "semantic-policy")]
558    critic_terms: TrackedCudaSlice<f32>,
559    #[cfg(feature = "semantic-policy")]
560    critic_total: TrackedCudaSlice<f32>,
561}
562
563/// Retained native result of device selection. The views are fixed-capacity
564/// CUDA ports; the resident selection record supplies their logical extent.
565pub struct SemanticSelectedTrainingView {
566    arena: Arc<SemanticTrainingViewArena>,
567    _origin_candidates: Option<Arc<TrackedCudaSlice<SemanticTrainingViewOriginRecord>>>,
568    storage: SelectedTrainingViewStorage,
569}
570
571#[cfg(feature = "semantic-policy")]
572#[derive(Clone, Copy)]
573pub(crate) struct FrozenPolicyGroupMember {
574    pub ordinal: u64,
575    pub identity: [u64; 4],
576    pub source_identity: [u64; 4],
577    pub bytes_identity: Identity256,
578    pub content_identity: [u64; 4],
579    pub origin: SemanticTrainingViewOriginRecord,
580}
581
582/// Fixed device port of the native-selected complete training roster.
583#[derive(Clone, Copy, Debug, Eq, PartialEq)]
584pub enum SemanticTrainingViewPort {
585    Selection,
586    RosterRows,
587    Objective,
588    ObjectiveGroups,
589    ObjectiveGroupMembers,
590    Canaries,
591    TokenIds,
592    MaskLabels,
593    MaskWeights,
594    AutoregressiveLabels,
595    RetentionLabels,
596    BranchLabels,
597    BranchIds,
598    SourceSlots,
599    LogicalPositions,
600    Kinds,
601    Parents,
602}
603
604impl SemanticTrainingViewPort {
605    /// Canonical public order of the complete selected roster.
606    pub const ALL: [Self; 17] = [
607        Self::Selection,
608        Self::RosterRows,
609        Self::Objective,
610        Self::ObjectiveGroups,
611        Self::ObjectiveGroupMembers,
612        Self::Canaries,
613        Self::TokenIds,
614        Self::MaskLabels,
615        Self::MaskWeights,
616        Self::AutoregressiveLabels,
617        Self::RetentionLabels,
618        Self::BranchLabels,
619        Self::BranchIds,
620        Self::SourceSlots,
621        Self::LogicalPositions,
622        Self::Kinds,
623        Self::Parents,
624    ];
625}
626
627impl SemanticSelectedTrainingView {
628    pub fn capacity(&self) -> usize {
629        self.arena.capacity
630    }
631
632    #[cfg(feature = "semantic-policy")]
633    pub(crate) fn accounted_allocation_bytes(&self) -> Result<u64, SemanticTransitionError> {
634        let mut allocations: Vec<crate::memory::DeviceAllocationProvenance> = Vec::new();
635        macro_rules! account {
636            ($slice:expr) => {{
637                let provenance = $slice
638                    .view()
639                    .allocation_provenance()
640                    .ok_or(SemanticTransitionError::ObservationMismatch)?;
641                if !allocations
642                    .iter()
643                    .any(|known| provenance.same_allocation(known))
644                {
645                    allocations.push(provenance);
646                }
647            }};
648        }
649        account!(self.arena.descriptors);
650        account!(self.arena.raw);
651        account!(self.arena.objective);
652        account!(self.arena.groups);
653        account!(self.arena.group_members);
654        account!(self.arena.canaries);
655        account!(self.arena.protected_members);
656        account!(self.storage.selection);
657        account!(self.storage.roster_rows);
658        account!(self.storage.token_ids);
659        account!(self.storage.mask_labels);
660        account!(self.storage.mask_weights);
661        account!(self.storage.ar_labels);
662        account!(self.storage.retention_labels);
663        account!(self.storage.branch_labels);
664        account!(self.storage.branch_ids);
665        account!(self.storage.source_slots);
666        account!(self.storage.logical_positions);
667        account!(self.storage.kinds);
668        account!(self.storage.parents);
669        #[cfg(feature = "semantic-policy")]
670        account!(self.storage.critic_terms);
671        #[cfg(feature = "semantic-policy")]
672        account!(self.storage.critic_total);
673        allocations.into_iter().try_fold(0u64, |total, allocation| {
674            total
675                .checked_add(allocation.allocation_bytes())
676                .ok_or(SemanticTransitionError::GenerationExhausted)
677        })
678    }
679
680    pub fn row_count(&self) -> usize {
681        self.arena.row_count
682    }
683
684    pub fn selection(&self) -> DeviceMemoryView<SemanticTrainingViewSelection> {
685        self.storage.selection.view()
686    }
687
688    pub fn roster_rows(&self) -> DeviceMemoryView<SemanticTrainingRosterRow> {
689        self.storage.roster_rows.view()
690    }
691
692    #[cfg(feature = "semantic-policy")]
693    pub(crate) fn objective(&self) -> DeviceMemoryView<SemanticTrainingObjectiveRecord> {
694        self.arena.objective.view()
695    }
696
697    #[cfg(feature = "semantic-policy")]
698    pub(crate) fn objective_groups(
699        &self,
700    ) -> DeviceMemoryView<SemanticTrainingObjectiveGroupRecord> {
701        self.arena.groups.view()
702    }
703
704    #[cfg(feature = "semantic-policy")]
705    pub(crate) fn objective_group_members(&self) -> DeviceMemoryView<u64> {
706        self.arena.group_members.view()
707    }
708
709    #[cfg(feature = "semantic-policy")]
710    pub(crate) fn actor_group_member_count(&self) -> u64 {
711        self.arena.actor_group_member_count
712    }
713
714    #[cfg(feature = "semantic-policy")]
715    pub(crate) fn actor_group_members(&self) -> &[FrozenPolicyGroupMember] {
716        &self.arena.actor_group_members
717    }
718
719    #[cfg(feature = "semantic-policy")]
720    pub(crate) fn edit_group_members(&self) -> &[FrozenPolicyGroupMember] {
721        &self.arena.edit_group_members
722    }
723
724    #[cfg(feature = "semantic-policy")]
725    pub(crate) fn actor_group_member_index(
726        &self,
727        ordinal: u64,
728        row: &SemanticTrainingViewRow,
729    ) -> Result<usize, SemanticTransitionError> {
730        let descriptor = validate_row(
731            usize::try_from(ordinal)
732                .map_err(|_| input_error("actor group ordinal exceeds host address space"))?,
733            row,
734            0,
735            &self.arena.training_domain,
736        )?;
737        self.actor_group_members()
738            .iter()
739            .position(|member| {
740                member.ordinal == ordinal
741                    && member.identity == descriptor.identity
742                    && member.source_identity == descriptor.source_identity
743                    && member.bytes_identity == row.bytes_identity
744                    && member.content_identity == descriptor.content_identity
745                    && member.origin == descriptor.origin
746            })
747            .ok_or_else(|| input_error("original replay is not an exact frozen actor group member"))
748    }
749
750    #[cfg(feature = "semantic-policy")]
751    pub(crate) fn edit_group_member_index(
752        &self,
753        ordinal: u64,
754        row: &SemanticTrainingViewRow,
755    ) -> Result<Option<usize>, SemanticTransitionError> {
756        let descriptor = validate_row(
757            usize::try_from(ordinal)
758                .map_err(|_| input_error("edit group ordinal exceeds host address space"))?,
759            row,
760            0,
761            &self.arena.training_domain,
762        )?;
763        Ok(self.edit_group_members().iter().position(|member| {
764            member.ordinal == ordinal
765                && member.identity == descriptor.identity
766                && member.source_identity == descriptor.source_identity
767                && member.bytes_identity == row.bytes_identity
768                && member.content_identity == descriptor.content_identity
769                && member.origin == descriptor.origin
770        }))
771    }
772
773    #[cfg(feature = "semantic-policy")]
774    pub(crate) fn critic_term(&self, index: usize) -> DeviceMemoryView<f32> {
775        self.storage.critic_terms.slice(index..index + 1)
776    }
777
778    #[cfg(feature = "semantic-policy")]
779    pub(crate) fn critic_terms(&self) -> DeviceMemoryView<f32> {
780        self.storage.critic_terms.view()
781    }
782
783    #[cfg(feature = "semantic-policy")]
784    pub(crate) fn critic_total(&self) -> DeviceMemoryView<f32> {
785        self.storage.critic_total.view()
786    }
787
788    #[cfg(feature = "semantic-policy")]
789    pub(crate) fn canaries(&self) -> DeviceMemoryView<SemanticTrainingCanaryRecord> {
790        self.arena.canaries.view()
791    }
792
793    #[cfg(feature = "semantic-policy")]
794    pub(crate) fn protected_members(&self) -> DeviceMemoryView<u64> {
795        self.arena.protected_members.view()
796    }
797
798    pub fn token_ids(&self) -> DeviceMemoryView<i64> {
799        self.storage.token_ids.view()
800    }
801
802    pub fn mask_labels(&self) -> DeviceMemoryView<i64> {
803        self.storage.mask_labels.view()
804    }
805
806    pub fn mask_weights(&self) -> DeviceMemoryView<f32> {
807        self.storage.mask_weights.view()
808    }
809
810    pub fn ar_labels(&self) -> DeviceMemoryView<i64> {
811        self.storage.ar_labels.view()
812    }
813
814    pub fn retention_labels(&self) -> DeviceMemoryView<i64> {
815        self.storage.retention_labels.view()
816    }
817
818    pub fn branch_labels(&self) -> DeviceMemoryView<i64> {
819        self.storage.branch_labels.view()
820    }
821
822    pub fn branch_ids(&self) -> DeviceMemoryView<i64> {
823        self.storage.branch_ids.view()
824    }
825
826    pub fn source_slots(&self) -> DeviceMemoryView<i64> {
827        self.storage.source_slots.view()
828    }
829
830    pub fn logical_positions(&self) -> DeviceMemoryView<i64> {
831        self.storage.logical_positions.view()
832    }
833
834    pub fn kinds(&self) -> DeviceMemoryView<i64> {
835        self.storage.kinds.view()
836    }
837
838    pub fn parents(&self) -> DeviceMemoryView<i64> {
839        self.storage.parents.view()
840    }
841
842    pub(crate) fn port(
843        &self,
844        port: SemanticTrainingViewPort,
845    ) -> Result<TrainingViewPort, SemanticTransitionError> {
846        let words = |bytes: usize| {
847            i64::try_from(bytes / size_of::<u64>())
848                .map_err(|_| SemanticTransitionError::GenerationExhausted)
849        };
850        let rows = i64::try_from(self.row_count())
851            .map_err(|_| SemanticTransitionError::GenerationExhausted)?;
852        let capacity = i64::try_from(self.capacity())
853            .map_err(|_| SemanticTransitionError::GenerationExhausted)?;
854        let extent = |length: usize| {
855            i64::try_from(length).map_err(|_| SemanticTransitionError::GenerationExhausted)
856        };
857        let matrix = |view, dtype| (view, vec![rows, capacity], vec![capacity, 1], dtype);
858        let result = match port {
859            SemanticTrainingViewPort::Selection => (
860                unsafe { self.storage.selection.view().cast::<u8>() }
861                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
862                vec![words(size_of::<SemanticTrainingViewSelection>())?],
863                vec![1],
864                (1, 64),
865            ),
866            SemanticTrainingViewPort::RosterRows => (
867                unsafe { self.storage.roster_rows.view().cast::<u8>() }
868                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
869                vec![rows, words(size_of::<SemanticTrainingRosterRow>())?],
870                vec![words(size_of::<SemanticTrainingRosterRow>())?, 1],
871                (1, 64),
872            ),
873            SemanticTrainingViewPort::Objective => (
874                unsafe { self.arena.objective.view().cast::<u8>() }
875                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
876                vec![words(size_of::<SemanticTrainingObjectiveRecord>())?],
877                vec![1],
878                (1, 64),
879            ),
880            SemanticTrainingViewPort::ObjectiveGroups => {
881                let width = words(size_of::<SemanticTrainingObjectiveGroupRecord>())?;
882                (
883                    unsafe { self.arena.groups.view().cast::<u8>() }
884                        .ok_or(SemanticTransitionError::ObservationMismatch)?,
885                    vec![extent(self.arena.groups.len())?, width],
886                    vec![width, 1],
887                    (1, 64),
888                )
889            }
890            SemanticTrainingViewPort::ObjectiveGroupMembers => (
891                unsafe { self.arena.group_members.view().cast::<u8>() }
892                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
893                vec![extent(self.arena.group_members.len())?],
894                vec![1],
895                (1, 64),
896            ),
897            SemanticTrainingViewPort::Canaries => {
898                let width = words(size_of::<SemanticTrainingCanaryRecord>())?;
899                (
900                    unsafe { self.arena.canaries.view().cast::<u8>() }
901                        .ok_or(SemanticTransitionError::ObservationMismatch)?,
902                    vec![extent(self.arena.canaries.len())?, width],
903                    vec![width, 1],
904                    (1, 64),
905                )
906            }
907            SemanticTrainingViewPort::TokenIds => (
908                unsafe { self.storage.token_ids.view().cast::<u8>() }
909                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
910                vec![],
911                vec![],
912                (0, 64),
913            ),
914            SemanticTrainingViewPort::MaskLabels => (
915                unsafe { self.storage.mask_labels.view().cast::<u8>() }
916                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
917                vec![],
918                vec![],
919                (0, 64),
920            ),
921            SemanticTrainingViewPort::MaskWeights => (
922                unsafe { self.storage.mask_weights.view().cast::<u8>() }
923                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
924                vec![],
925                vec![],
926                (2, 32),
927            ),
928            SemanticTrainingViewPort::AutoregressiveLabels => (
929                unsafe { self.storage.ar_labels.view().cast::<u8>() }
930                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
931                vec![],
932                vec![],
933                (0, 64),
934            ),
935            SemanticTrainingViewPort::RetentionLabels => (
936                unsafe { self.storage.retention_labels.view().cast::<u8>() }
937                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
938                vec![],
939                vec![],
940                (0, 64),
941            ),
942            SemanticTrainingViewPort::BranchLabels => (
943                unsafe { self.storage.branch_labels.view().cast::<u8>() }
944                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
945                vec![],
946                vec![],
947                (0, 64),
948            ),
949            SemanticTrainingViewPort::BranchIds => (
950                unsafe { self.storage.branch_ids.view().cast::<u8>() }
951                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
952                vec![],
953                vec![],
954                (0, 64),
955            ),
956            SemanticTrainingViewPort::SourceSlots => (
957                unsafe { self.storage.source_slots.view().cast::<u8>() }
958                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
959                vec![],
960                vec![],
961                (0, 64),
962            ),
963            SemanticTrainingViewPort::LogicalPositions => (
964                unsafe { self.storage.logical_positions.view().cast::<u8>() }
965                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
966                vec![],
967                vec![],
968                (0, 64),
969            ),
970            SemanticTrainingViewPort::Kinds => (
971                unsafe { self.storage.kinds.view().cast::<u8>() }
972                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
973                vec![],
974                vec![],
975                (0, 64),
976            ),
977            SemanticTrainingViewPort::Parents => (
978                unsafe { self.storage.parents.view().cast::<u8>() }
979                    .ok_or(SemanticTransitionError::ObservationMismatch)?,
980                vec![],
981                vec![],
982                (0, 64),
983            ),
984        };
985        Ok(match port {
986            SemanticTrainingViewPort::TokenIds
987            | SemanticTrainingViewPort::MaskLabels
988            | SemanticTrainingViewPort::MaskWeights
989            | SemanticTrainingViewPort::AutoregressiveLabels
990            | SemanticTrainingViewPort::RetentionLabels
991            | SemanticTrainingViewPort::BranchLabels
992            | SemanticTrainingViewPort::BranchIds
993            | SemanticTrainingViewPort::SourceSlots
994            | SemanticTrainingViewPort::LogicalPositions
995            | SemanticTrainingViewPort::Kinds
996            | SemanticTrainingViewPort::Parents => matrix(result.0, result.3),
997            _ => result,
998        })
999    }
1000
1001    pub(crate) fn enqueue_device_selection(
1002        &self,
1003        selected_view: DeviceMemoryView<u8>,
1004        coordinates: DeviceMemoryView<u64>,
1005    ) -> Result<(), SemanticTransitionError> {
1006        if coordinates.len() != 5 {
1007            return Err(input_error(
1008                "prepared training-view coordinates require cursor and four RNG words",
1009            ));
1010        }
1011        self.enqueue(selected_view, 0, [0; 4], Some(coordinates))
1012    }
1013
1014    fn enqueue(
1015        &self,
1016        selected_view: DeviceMemoryView<u8>,
1017        cursor: u64,
1018        training_rng: [u64; 4],
1019        coordinates: Option<DeviceMemoryView<u64>>,
1020    ) -> Result<(), SemanticTransitionError> {
1021        let (origin_candidate_ptr, origin_candidate_count) =
1022            if let Some(candidates) = self._origin_candidates.as_ref() {
1023                (
1024                    candidates.device_ptr_value(),
1025                    u64::try_from(candidates.len())
1026                        .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1027                )
1028            } else {
1029                (0, 0)
1030            };
1031        let launch = TrainingViewLaunch {
1032            descriptors: self.arena.descriptors.device_ptr_value(),
1033            raw: self.arena.raw.device_ptr_value(),
1034            row_count: u64::try_from(self.arena.row_count)
1035                .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1036            selected_view: *selected_view.device_ptr(),
1037            selected_view_bytes: u64::try_from(selected_view.len())
1038                .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1039            cursor,
1040            training_rng,
1041            coordinates: coordinates.as_ref().map_or(0, |view| *view.device_ptr()),
1042            origin_candidates: origin_candidate_ptr,
1043            origin_candidate_count,
1044            selection: self.storage.selection.device_ptr_value(),
1045            roster_rows: self.storage.roster_rows.device_ptr_value(),
1046            capacity: u64::try_from(self.arena.capacity)
1047                .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1048            token_ids: self.storage.token_ids.device_ptr_value(),
1049            mask_labels: self.storage.mask_labels.device_ptr_value(),
1050            mask_weights: self.storage.mask_weights.device_ptr_value(),
1051            ar_labels: self.storage.ar_labels.device_ptr_value(),
1052            retention_labels: self.storage.retention_labels.device_ptr_value(),
1053            branch_labels: self.storage.branch_labels.device_ptr_value(),
1054            branch_ids: self.storage.branch_ids.device_ptr_value(),
1055            source_slots: self.storage.source_slots.device_ptr_value(),
1056            logical_positions: self.storage.logical_positions.device_ptr_value(),
1057            kinds: self.storage.kinds.device_ptr_value(),
1058            parents: self.storage.parents.device_ptr_value(),
1059        };
1060        let mut recorder = self.arena.domain.new_strict_recorder();
1061        recorder.read(&self.arena.descriptors);
1062        recorder.read(&self.arena.raw);
1063        recorder.read(&selected_view);
1064        if let Some(candidates) = &self._origin_candidates {
1065            recorder.read(candidates.as_ref());
1066        }
1067        if let Some(coordinates) = &coordinates {
1068            recorder.read(coordinates);
1069        }
1070        recorder.write(&self.storage.selection);
1071        recorder.write(&self.storage.roster_rows);
1072        recorder.write(&self.storage.token_ids);
1073        recorder.write(&self.storage.mask_labels);
1074        recorder.write(&self.storage.mask_weights);
1075        recorder.write(&self.storage.ar_labels);
1076        recorder.write(&self.storage.retention_labels);
1077        recorder.write(&self.storage.branch_labels);
1078        recorder.write(&self.storage.branch_ids);
1079        recorder.write(&self.storage.source_slots);
1080        recorder.write(&self.storage.logical_positions);
1081        recorder.write(&self.storage.kinds);
1082        recorder.write(&self.storage.parents);
1083        let select = self.arena.select.clone();
1084        let gather = self.arena.gather.clone();
1085        let gather_grid = u32::try_from(self.arena.row_count)
1086            .map_err(|_| SemanticTransitionError::GenerationExhausted)?;
1087        let enqueued = unsafe {
1088            self.arena.domain.enqueue(recorder, |stream| {
1089                select.clone().launch_in(
1090                    stream,
1091                    LaunchConfig {
1092                        grid_dim: (1, 1, 1),
1093                        block_dim: (256, 1, 1),
1094                        shared_mem_bytes: 0,
1095                    },
1096                    (launch,),
1097                )?;
1098                gather.clone().launch_in(
1099                    stream,
1100                    LaunchConfig {
1101                        grid_dim: (gather_grid, 1, 1),
1102                        block_dim: (256, 1, 1),
1103                        shared_mem_bytes: 0,
1104                    },
1105                    (launch,),
1106                )
1107            })
1108        }
1109        .map_err(map_enqueue_error)?;
1110        enqueued
1111            .commit()
1112            .map_err(|error| runtime_error("training-view launch commit", error))
1113    }
1114}
1115
1116/// Immutable cold roster used by the native Update selector.
1117pub(crate) struct SemanticTrainingViewArena {
1118    #[cfg(feature = "semantic-policy")]
1119    training_domain: SemanticTrainingDomain,
1120    provider: Arc<CudaKernelProvider>,
1121    domain: ResidentExecutionDomain,
1122    select: CudaFunction,
1123    gather: CudaFunction,
1124    descriptors: TrackedCudaSlice<TrainingViewRowDescriptor>,
1125    raw: TrackedCudaSlice<u8>,
1126    objective: TrackedCudaSlice<SemanticTrainingObjectiveRecord>,
1127    groups: TrackedCudaSlice<SemanticTrainingObjectiveGroupRecord>,
1128    group_members: TrackedCudaSlice<u64>,
1129    canaries: TrackedCudaSlice<SemanticTrainingCanaryRecord>,
1130    #[cfg(feature = "semantic-policy")]
1131    protected_members: TrackedCudaSlice<u64>,
1132    #[cfg(feature = "semantic-policy")]
1133    actor_group_member_count: u64,
1134    #[cfg(feature = "semantic-policy")]
1135    actor_group_members: Box<[FrozenPolicyGroupMember]>,
1136    #[cfg(feature = "semantic-policy")]
1137    edit_group_members: Box<[FrozenPolicyGroupMember]>,
1138    row_count: usize,
1139    capacity: usize,
1140}
1141
1142impl SemanticTrainingViewArena {
1143    #[cfg(feature = "semantic-policy")]
1144    pub(crate) fn training_domain_identity(&self) -> Identity256 {
1145        self.training_domain.identity()
1146    }
1147
1148    pub(crate) fn selection_bytes(&self) -> Result<usize, SemanticTransitionError> {
1149        let roster_bytes = self
1150            .row_count
1151            .checked_mul(size_of::<SemanticTrainingRosterRow>())
1152            .ok_or(SemanticTransitionError::GenerationExhausted)?;
1153        let port_bytes = self
1154            .capacity
1155            .checked_mul(self.row_count)
1156            .and_then(|cells| cells.checked_mul(TRAINING_VIEW_ROW_BYTES))
1157            .ok_or(SemanticTransitionError::GenerationExhausted)?;
1158        let bytes = size_of::<SemanticTrainingViewSelection>()
1159            .checked_add(roster_bytes)
1160            .and_then(|bytes| bytes.checked_add(port_bytes))
1161            .ok_or(SemanticTransitionError::GenerationExhausted)?;
1162        #[cfg(feature = "semantic-policy")]
1163        let bytes = bytes
1164            .checked_add(
1165                (self.actor_group_members.len() + 1)
1166                    .checked_mul(size_of::<f32>())
1167                    .ok_or(SemanticTransitionError::GenerationExhausted)?,
1168            )
1169            .ok_or(SemanticTransitionError::GenerationExhausted)?;
1170        Ok(bytes)
1171    }
1172
1173    pub(crate) fn allocate_selection_reserved(
1174        self: &Arc<Self>,
1175        reservation: &mut crate::memory::GpuMemoryReservation,
1176        origin_candidates: Option<Arc<TrackedCudaSlice<SemanticTrainingViewOriginRecord>>>,
1177    ) -> Result<SemanticSelectedTrainingView, SemanticTransitionError> {
1178        let storage = SelectedTrainingViewStorage {
1179            selection: reservation
1180                .alloc::<SemanticTrainingViewSelection>(1)
1181                .map_err(|error| runtime_error("training-view selection allocation", error))?,
1182            roster_rows: reservation
1183                .alloc::<SemanticTrainingRosterRow>(self.row_count)
1184                .map_err(|error| runtime_error("training-view roster allocation", error))?,
1185            token_ids: allocate_port(reservation, self.row_count, self.capacity)?,
1186            mask_labels: allocate_port(reservation, self.row_count, self.capacity)?,
1187            mask_weights: allocate_port(reservation, self.row_count, self.capacity)?,
1188            ar_labels: allocate_port(reservation, self.row_count, self.capacity)?,
1189            retention_labels: allocate_port(reservation, self.row_count, self.capacity)?,
1190            branch_labels: allocate_port(reservation, self.row_count, self.capacity)?,
1191            branch_ids: allocate_port(reservation, self.row_count, self.capacity)?,
1192            source_slots: allocate_port(reservation, self.row_count, self.capacity)?,
1193            logical_positions: allocate_port(reservation, self.row_count, self.capacity)?,
1194            kinds: allocate_port(reservation, self.row_count, self.capacity)?,
1195            parents: allocate_port(reservation, self.row_count, self.capacity)?,
1196            #[cfg(feature = "semantic-policy")]
1197            critic_terms: reservation
1198                .alloc::<f32>(self.actor_group_members.len())
1199                .map_err(|error| runtime_error("group critic term allocation", error))?,
1200            #[cfg(feature = "semantic-policy")]
1201            critic_total: reservation
1202                .alloc::<f32>(1)
1203                .map_err(|error| runtime_error("group critic scalar allocation", error))?,
1204        };
1205        Ok(SemanticSelectedTrainingView {
1206            arena: Arc::clone(self),
1207            _origin_candidates: origin_candidates,
1208            storage,
1209        })
1210    }
1211
1212    #[expect(
1213        clippy::too_many_arguments,
1214        reason = "arena retains the task, numerical law and original input domain"
1215    )]
1216    pub(crate) fn allocate(
1217        provider: &Arc<CudaKernelProvider>,
1218        domain: &ResidentExecutionDomain,
1219        rows: Vec<SemanticTrainingViewRow>,
1220        objective: SemanticTrainingObjective,
1221        training_domain: &SemanticTrainingDomain,
1222        task_identity: Identity256,
1223        task_content: SemanticTaskContentIdentity,
1224        expected_truth: [SemanticTruth; 3],
1225    ) -> Result<Arc<Self>, SemanticTransitionError> {
1226        validate_execution_domain(provider, domain)
1227            .map_err(|error| runtime_error("training-view domain validation", error))?;
1228        if rows.is_empty() {
1229            return Err(input_error(
1230                "training-view arena requires at least one admitted row",
1231            ));
1232        }
1233        training_domain.validate()?;
1234        let record_limit = training_domain.record_limit();
1235        let mut episode_count = 0u64;
1236        let mut anchor_count = 0u64;
1237        let mut content_identities = std::collections::BTreeSet::new();
1238        for row in &rows {
1239            let count = if row.basis == SemanticTrainingViewBasis::Episode {
1240                &mut episode_count
1241            } else {
1242                &mut anchor_count
1243            };
1244            *count = count
1245                .checked_add(1)
1246                .ok_or(SemanticTransitionError::GenerationExhausted)?;
1247            if *count > record_limit || !content_identities.insert(*row.content_identity.as_bytes())
1248            {
1249                return Err(input_error(
1250                    "training rows exceed the original input bound or repeat content",
1251                ));
1252            }
1253        }
1254        let select = provider
1255            .device()
1256            .inner()
1257            .get_func(MODULE, SELECT_KERNEL)
1258            .ok_or_else(|| runtime_error("training-view kernel lookup", "selector unavailable"))?;
1259        let gather = provider
1260            .device()
1261            .inner()
1262            .get_func(MODULE, GATHER_KERNEL)
1263            .ok_or_else(|| runtime_error("training-view kernel lookup", "gather unavailable"))?;
1264        let mut raw = Vec::new();
1265        let mut descriptors = Vec::with_capacity(rows.len());
1266        #[cfg(feature = "semantic-policy")]
1267        let mut row_bytes_identities = Vec::with_capacity(rows.len());
1268        let mut capacity = 0usize;
1269        for (ordinal, row) in rows.into_iter().enumerate() {
1270            let descriptor = validate_row(ordinal, &row, raw.len(), training_domain)?;
1271            #[cfg(feature = "semantic-policy")]
1272            row_bytes_identities.push(row.bytes_identity);
1273            capacity = capacity.max(descriptor.window as usize);
1274            raw.extend_from_slice(&row.bytes);
1275            descriptors.push(descriptor);
1276        }
1277        #[cfg(feature = "semantic-policy")]
1278        let (objective, groups, group_members, canaries, protected_members) = validate_objective(
1279            objective,
1280            &descriptors,
1281            &raw,
1282            capacity,
1283            task_identity,
1284            task_content,
1285            expected_truth,
1286            record_limit,
1287        )?;
1288        #[cfg(not(feature = "semantic-policy"))]
1289        let (objective, groups, group_members, canaries, _) = validate_objective(
1290            objective,
1291            &descriptors,
1292            &raw,
1293            capacity,
1294            task_identity,
1295            task_content,
1296            expected_truth,
1297            record_limit,
1298        )?;
1299        #[cfg(feature = "semantic-policy")]
1300        let actor_group = groups
1301            .iter()
1302            .find(|group| group.kind == SemanticTrainingObjectiveGroupKind::ActorCriticCost as u64)
1303            .ok_or_else(|| input_error("training objective has no actor-critic-cost group"))?;
1304        #[cfg(feature = "semantic-policy")]
1305        let actor_group_member_count = actor_group.member_count;
1306        #[cfg(feature = "semantic-policy")]
1307        let actor_group_members = {
1308            let start = usize::try_from(actor_group.member_offset)
1309                .map_err(|_| SemanticTransitionError::GenerationExhausted)?;
1310            let count = usize::try_from(actor_group.member_count)
1311                .map_err(|_| SemanticTransitionError::GenerationExhausted)?;
1312            group_members[start..start + count]
1313                .iter()
1314                .map(|&ordinal| {
1315                    let row = &descriptors[ordinal as usize];
1316                    FrozenPolicyGroupMember {
1317                        ordinal,
1318                        identity: row.identity,
1319                        source_identity: row.source_identity,
1320                        bytes_identity: row_bytes_identities[ordinal as usize],
1321                        content_identity: row.content_identity,
1322                        origin: row.origin,
1323                    }
1324                })
1325                .collect::<Vec<_>>()
1326                .into_boxed_slice()
1327        };
1328        #[cfg(feature = "semantic-policy")]
1329        let edit_group_members = {
1330            let group = groups
1331                .iter()
1332                .find(|group| group.kind == SemanticTrainingObjectiveGroupKind::Edit as u64)
1333                .ok_or_else(|| input_error("training objective has no supervised-edit group"))?;
1334            let start = usize::try_from(group.member_offset)
1335                .map_err(|_| SemanticTransitionError::GenerationExhausted)?;
1336            let count = usize::try_from(group.member_count)
1337                .map_err(|_| SemanticTransitionError::GenerationExhausted)?;
1338            group_members[start..start + count]
1339                .iter()
1340                .map(|&ordinal| {
1341                    let row = &descriptors[ordinal as usize];
1342                    FrozenPolicyGroupMember {
1343                        ordinal,
1344                        identity: row.identity,
1345                        source_identity: row.source_identity,
1346                        bytes_identity: row_bytes_identities[ordinal as usize],
1347                        content_identity: row.content_identity,
1348                        origin: row.origin,
1349                    }
1350                })
1351                .collect::<Vec<_>>()
1352                .into_boxed_slice()
1353        };
1354        let bytes = descriptors
1355            .len()
1356            .checked_mul(size_of::<TrainingViewRowDescriptor>())
1357            .and_then(|bytes| bytes.checked_add(raw.len()))
1358            .and_then(|bytes| bytes.checked_add(size_of::<SemanticTrainingObjectiveRecord>()))
1359            .and_then(|bytes| {
1360                groups
1361                    .len()
1362                    .checked_mul(size_of::<SemanticTrainingObjectiveGroupRecord>())
1363                    .and_then(|group_bytes| bytes.checked_add(group_bytes))
1364            })
1365            .and_then(|bytes| {
1366                group_members
1367                    .len()
1368                    .checked_mul(size_of::<u64>())
1369                    .and_then(|member_bytes| bytes.checked_add(member_bytes))
1370            })
1371            .and_then(|bytes| {
1372                canaries
1373                    .len()
1374                    .checked_mul(size_of::<SemanticTrainingCanaryRecord>())
1375                    .and_then(|canary_bytes| bytes.checked_add(canary_bytes))
1376            });
1377        #[cfg(feature = "semantic-policy")]
1378        let bytes = bytes.and_then(|bytes| {
1379            protected_members
1380                .len()
1381                .checked_mul(size_of::<u64>())
1382                .and_then(|member_bytes| bytes.checked_add(member_bytes))
1383        });
1384        let bytes = bytes.ok_or(SemanticTransitionError::GenerationExhausted)?;
1385        let mut reservation = provider
1386            .memory()
1387            .reserve_bytes(
1388                u64::try_from(bytes).map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1389            )
1390            .map_err(|error| runtime_error("training-view reservation", error))?;
1391        let mut device_descriptors = reservation
1392            .alloc::<TrainingViewRowDescriptor>(descriptors.len())
1393            .map_err(|error| runtime_error("training-view descriptor allocation", error))?;
1394        let mut device_raw = reservation
1395            .alloc::<u8>(raw.len())
1396            .map_err(|error| runtime_error("training-view byte allocation", error))?;
1397        let mut device_objective = reservation
1398            .alloc::<SemanticTrainingObjectiveRecord>(1)
1399            .map_err(|error| runtime_error("training objective allocation", error))?;
1400        let mut device_groups = reservation
1401            .alloc::<SemanticTrainingObjectiveGroupRecord>(groups.len())
1402            .map_err(|error| runtime_error("training objective group allocation", error))?;
1403        let mut device_group_members = reservation
1404            .alloc::<u64>(group_members.len())
1405            .map_err(|error| runtime_error("training objective member allocation", error))?;
1406        let mut device_canaries = reservation
1407            .alloc::<SemanticTrainingCanaryRecord>(canaries.len())
1408            .map_err(|error| runtime_error("training canary allocation", error))?;
1409        #[cfg(feature = "semantic-policy")]
1410        let mut device_protected_members = reservation
1411            .alloc::<u64>(protected_members.len())
1412            .map_err(|error| runtime_error("protected retention member allocation", error))?;
1413        if reservation.remaining_bytes() != 0 {
1414            return Err(SemanticTransitionError::ObservationMismatch);
1415        }
1416        provider
1417            .htod_sync_copy_into_tracked(&descriptors, &mut device_descriptors)
1418            .map_err(|error| runtime_error("training-view descriptor upload", error))?;
1419        provider
1420            .htod_sync_copy_into_tracked(&raw, &mut device_raw)
1421            .map_err(|error| runtime_error("training-view byte upload", error))?;
1422        provider
1423            .htod_sync_copy_into_tracked(&[objective], &mut device_objective)
1424            .map_err(|error| runtime_error("training objective upload", error))?;
1425        provider
1426            .htod_sync_copy_into_tracked(&groups, &mut device_groups)
1427            .map_err(|error| runtime_error("training objective group upload", error))?;
1428        provider
1429            .htod_sync_copy_into_tracked(&group_members, &mut device_group_members)
1430            .map_err(|error| runtime_error("training objective member upload", error))?;
1431        provider
1432            .htod_sync_copy_into_tracked(&canaries, &mut device_canaries)
1433            .map_err(|error| runtime_error("training canary upload", error))?;
1434        #[cfg(feature = "semantic-policy")]
1435        provider
1436            .htod_sync_copy_into_tracked(&protected_members, &mut device_protected_members)
1437            .map_err(|error| runtime_error("protected retention member upload", error))?;
1438        Ok(Arc::new(Self {
1439            #[cfg(feature = "semantic-policy")]
1440            training_domain: training_domain.clone(),
1441            provider: Arc::clone(provider),
1442            domain: domain.clone(),
1443            select,
1444            gather,
1445            descriptors: device_descriptors,
1446            raw: device_raw,
1447            objective: device_objective,
1448            groups: device_groups,
1449            group_members: device_group_members,
1450            canaries: device_canaries,
1451            #[cfg(feature = "semantic-policy")]
1452            protected_members: device_protected_members,
1453            #[cfg(feature = "semantic-policy")]
1454            actor_group_member_count,
1455            #[cfg(feature = "semantic-policy")]
1456            actor_group_members,
1457            #[cfg(feature = "semantic-policy")]
1458            edit_group_members,
1459            row_count: descriptors.len(),
1460            capacity,
1461        }))
1462    }
1463
1464    pub(crate) fn enqueue_selection(
1465        self: &Arc<Self>,
1466        selected_view: DeviceMemoryView<u8>,
1467        cursor: u64,
1468        training_rng: [u64; 4],
1469        origin_candidates: Option<Arc<TrackedCudaSlice<SemanticTrainingViewOriginRecord>>>,
1470    ) -> Result<SemanticSelectedTrainingView, SemanticTransitionError> {
1471        let output_bytes = self.selection_bytes()?;
1472        let mut reservation = self
1473            .provider
1474            .memory()
1475            .reserve_bytes(
1476                u64::try_from(output_bytes)
1477                    .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1478            )
1479            .map_err(|error| runtime_error("selected training-view reservation", error))?;
1480        let selected = self.allocate_selection_reserved(&mut reservation, origin_candidates)?;
1481        if reservation.remaining_bytes() != 0 {
1482            return Err(SemanticTransitionError::ObservationMismatch);
1483        }
1484        selected.enqueue(selected_view, cursor, training_rng, None)?;
1485        Ok(selected)
1486    }
1487}
1488
1489fn allocate_port<T: DeviceRepr>(
1490    reservation: &mut crate::memory::GpuMemoryReservation,
1491    row_count: usize,
1492    capacity: usize,
1493) -> Result<TrackedCudaSlice<T>, SemanticTransitionError> {
1494    reservation
1495        .alloc::<T>(
1496            row_count
1497                .checked_mul(capacity)
1498                .ok_or(SemanticTransitionError::GenerationExhausted)?,
1499        )
1500        .map_err(|error| runtime_error("selected training-view port allocation", error))
1501}
1502
1503#[expect(
1504    clippy::too_many_arguments,
1505    reason = "objective admission checks task, truth and original input bound"
1506)]
1507fn validate_objective(
1508    objective: SemanticTrainingObjective,
1509    rows: &[TrainingViewRowDescriptor],
1510    raw: &[u8],
1511    capacity: usize,
1512    task_identity: Identity256,
1513    task_content: SemanticTaskContentIdentity,
1514    expected_truth: [SemanticTruth; 3],
1515    record_limit: u64,
1516) -> Result<ValidatedTrainingObjective, SemanticTransitionError> {
1517    if objective.groups.len() != 8 || objective.canaries.len() != 5 {
1518        return Err(input_error(
1519            "training objective requires all eight reduction groups and five canaries",
1520        ));
1521    }
1522    if !objective.evaluator_min.is_finite()
1523        || !objective.evaluator_max.is_finite()
1524        || objective.evaluator_min > objective.evaluator_max
1525        || objective
1526            .coefficients
1527            .iter()
1528            .any(|value| !value.is_finite() || *value <= 0.0)
1529        || objective.cost_unit == Identity256::default()
1530        || objective.cost_cap == 0
1531        || task_identity == Identity256::default()
1532        || objective
1533            .truth_tokens
1534            .iter()
1535            .copied()
1536            .collect::<std::collections::BTreeSet<_>>()
1537            .len()
1538            != objective.truth_tokens.len()
1539    {
1540        return Err(input_error(
1541            "training objective has invalid evaluator, coefficient or cost bounds",
1542        ));
1543    }
1544    let expected_query = identity_words(task_content.query);
1545    let expected_theory_program = identity_words(task_content.theory_program);
1546    let expected_result = identity_words(task_content.result);
1547    for row in rows {
1548        let symbolic = row.basis == SemanticTrainingViewBasis::CorpusSymbolicAnchor as u64;
1549        let bound = row.task_query_identity == expected_query
1550            && row.task_theory_program_identity == expected_theory_program
1551            && row.task_result_identity == expected_result;
1552        let absent = row.task_query_identity == [0; 4]
1553            && row.task_theory_program_identity == [0; 4]
1554            && row.task_result_identity == [0; 4];
1555        if (symbolic && !bound) || (!symbolic && !absent) {
1556            return Err(input_error(
1557                "symbolic training view differs from the native query, theory/program or result binding",
1558            ));
1559        }
1560    }
1561    let mut seen_groups = [false; 8];
1562    let mut referenced_rows = vec![false; rows.len()];
1563    let mut groups = Vec::with_capacity(objective.groups.len());
1564    let mut group_members = Vec::new();
1565    for group in &objective.groups {
1566        let index = group.kind as usize - 1;
1567        if seen_groups[index]
1568            || group.row_ordinals.is_empty()
1569            || group.denominator > record_limit
1570            || group.denominator
1571                != u64::try_from(group.row_ordinals.len())
1572                    .map_err(|_| SemanticTransitionError::GenerationExhausted)?
1573        {
1574            return Err(input_error(
1575                "training objective groups must be unique, nonempty and use their exact member count as denominator",
1576            ));
1577        }
1578        seen_groups[index] = true;
1579        let member_offset = group_members.len();
1580        let mut previous = None;
1581        for &ordinal in &group.row_ordinals {
1582            let row_ordinal = usize::try_from(ordinal)
1583                .map_err(|_| input_error("training objective row exceeds host address space"))?;
1584            let row = rows
1585                .get(row_ordinal)
1586                .ok_or_else(|| input_error("training objective names an unknown replay row"))?;
1587            if previous.is_some_and(|value| value >= ordinal) {
1588                return Err(input_error(
1589                    "training objective row ordinals must be strictly increasing",
1590                ));
1591            }
1592            let policy_group = group.kind == SemanticTrainingObjectiveGroupKind::ActorCriticCost;
1593            let expected_anchor = match group.kind {
1594                SemanticTrainingObjectiveGroupKind::RetentionLanguage => {
1595                    Some(SemanticTrainingViewBasis::CorpusLanguageAnchor)
1596                }
1597                SemanticTrainingObjectiveGroupKind::RetentionSymbolic => {
1598                    Some(SemanticTrainingViewBasis::CorpusSymbolicAnchor)
1599                }
1600                _ => None,
1601            };
1602            if expected_anchor.map_or(
1603                row.basis != SemanticTrainingViewBasis::Episode as u64,
1604                |basis| row.basis != basis as u64,
1605            ) || (policy_group
1606                && (row.basis != SemanticTrainingViewBasis::Episode as u64
1607                    || row.origin.transition != PROPOSAL_TRANSITION))
1608            {
1609                return Err(input_error(
1610                    "training objective group refers to an incompatible replay basis",
1611                ));
1612            }
1613            referenced_rows[row_ordinal] = true;
1614            group_members.push(ordinal);
1615            previous = Some(ordinal);
1616        }
1617        groups.push(SemanticTrainingObjectiveGroupRecord {
1618            kind: group.kind as u64,
1619            denominator: group.denominator,
1620            member_offset: u64::try_from(member_offset)
1621                .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1622            member_count: u64::try_from(group.row_ordinals.len())
1623                .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1624        });
1625    }
1626    if seen_groups.iter().any(|seen| !seen) || referenced_rows.iter().any(|seen| !seen) {
1627        return Err(input_error(
1628            "training objective must cover every mandatory group and replay row",
1629        ));
1630    }
1631    if objective.coefficients.map(f32::to_bits)
1632        != frozen_training_coefficients(objective.evaluator_min, objective.evaluator_max)
1633            .map(f32::to_bits)
1634    {
1635        return Err(input_error(
1636            "training coefficients differ from the frozen primary and evaluator-scaled law",
1637        ));
1638    }
1639    let mut seen_canaries = [false; 5];
1640    let mut canaries = Vec::with_capacity(objective.canaries.len());
1641    let mut protected_members = Vec::new();
1642    for canary in &objective.canaries {
1643        let index = canary.kind as usize - 1;
1644        let row = usize::try_from(canary.row_ordinal)
1645            .ok()
1646            .and_then(|ordinal| rows.get(ordinal));
1647        let obligation_kind = matches!(
1648            canary.kind,
1649            SemanticTrainingCanaryKind::SymbolicUtility | SemanticTrainingCanaryKind::GoalChain
1650        );
1651        let positions_valid = if obligation_kind {
1652            row.is_some_and(|row| {
1653                row.basis == SemanticTrainingViewBasis::CorpusSymbolicAnchor as u64
1654                    && canary
1655                        .obligation_positions
1656                        .windows(2)
1657                        .all(|pair| pair[0] < pair[1])
1658                    && canary
1659                        .obligation_positions
1660                        .iter()
1661                        .all(|position| *position < row.window)
1662                    && canary.obligation_positions.iter().zip(expected_truth).all(
1663                        |(position, truth)| {
1664                            position
1665                                .checked_add(1)
1666                                .filter(|target| {
1667                                    *target >= row.answer_start
1668                                        && *target < row.window
1669                                        && *target < row.source_length
1670                                })
1671                                .and_then(|target| {
1672                                    usize::try_from(row.raw_offset).ok().and_then(|base| {
1673                                        usize::try_from(target).ok().and_then(|target| {
1674                                            base.checked_add(TRAINING_VIEW_HEADER_BYTES).and_then(
1675                                                |offset| {
1676                                                    target
1677                                                        .checked_mul(size_of::<i64>())
1678                                                        .and_then(|bytes| offset.checked_add(bytes))
1679                                                },
1680                                            )
1681                                        })
1682                                    })
1683                                })
1684                                .and_then(|offset| {
1685                                    offset
1686                                        .checked_add(size_of::<i64>())
1687                                        .and_then(|end| raw.get(offset..end))
1688                                })
1689                                .map(|bytes| {
1690                                    u64::from_le_bytes(
1691                                        bytes.try_into().expect("bounded truth token"),
1692                                    ) == objective.truth_tokens[truth as usize]
1693                                })
1694                                .unwrap_or(false)
1695                        },
1696                    )
1697            })
1698        } else {
1699            canary.obligation_positions == [u64::MAX; 3]
1700        };
1701        let protected_valid = if canary.kind == SemanticTrainingCanaryKind::RetainedBehavior {
1702            canary
1703                .protected_positions
1704                .windows(2)
1705                .all(|pair| pair[0] < pair[1])
1706                && row.is_some_and(|row| {
1707                    canary
1708                        .protected_positions
1709                        .iter()
1710                        .all(|position| *position < row.window)
1711                })
1712        } else {
1713            canary.protected_positions.is_empty()
1714        };
1715        if index != canaries.len()
1716            || seen_canaries[index]
1717            || !canary.lower_bound.is_finite()
1718            || !canary.upper_bound.is_finite()
1719            || canary.lower_bound > canary.upper_bound
1720            || canary.memory_limit == 0
1721            || canary.work_limit == 0
1722            || row.is_none()
1723            || !positions_valid
1724            || !protected_valid
1725            || (canary.kind == SemanticTrainingCanaryKind::GoalChain
1726                && (canary.lower_bound.to_bits() != 0.0f64.to_bits()
1727                    || canary.upper_bound.to_bits() != 0.0f64.to_bits()))
1728        {
1729            return Err(input_error(
1730                "training canaries require canonical kind order, valid rows, bounds and resource ceilings",
1731            ));
1732        }
1733        seen_canaries[index] = true;
1734        let row = row.expect("checked canary row");
1735        let identity = canary_identity(&objective, canary, row, task_identity);
1736        let protected_member_offset = protected_members.len();
1737        protected_members.extend_from_slice(&canary.protected_positions);
1738        canaries.push(SemanticTrainingCanaryRecord {
1739            evaluator_abi: SEMANTIC_TRAINING_CANARY_EVALUATOR_ABI,
1740            kind: canary.kind as u64,
1741            row_ordinal: canary.row_ordinal,
1742            lower_bound_bits: canary.lower_bound.to_bits(),
1743            upper_bound_bits: canary.upper_bound.to_bits(),
1744            memory_limit: canary.memory_limit,
1745            work_limit: canary.work_limit,
1746            obligation_positions: canary.obligation_positions,
1747            protected_member_offset: u64::try_from(protected_member_offset)
1748                .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1749            protected_member_count: u64::try_from(canary.protected_positions.len())
1750                .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1751            row_identity: row.identity,
1752            row_content_identity: row.content_identity,
1753            task_identity: identity_words(task_identity),
1754            identity: identity_words(identity),
1755        });
1756    }
1757    if seen_canaries.iter().any(|seen| !seen) {
1758        return Err(input_error("training objective is incomplete"));
1759    }
1760    let identity = objective_identity(&objective, rows, task_identity, &canaries);
1761    Ok((
1762        SemanticTrainingObjectiveRecord {
1763            evaluator_abi: SEMANTIC_TRAINING_CANARY_EVALUATOR_ABI,
1764            identity: identity_words(identity),
1765            task_identity: identity_words(task_identity),
1766            row_count: u64::try_from(rows.len())
1767                .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1768            capacity: u64::try_from(capacity)
1769                .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1770            group_count: u64::try_from(groups.len())
1771                .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1772            group_member_count: u64::try_from(group_members.len())
1773                .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1774            canary_count: u64::try_from(canaries.len())
1775                .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1776            protected_member_count: u64::try_from(protected_members.len())
1777                .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1778            evaluator_min_bits: objective.evaluator_min.to_bits(),
1779            evaluator_max_bits: objective.evaluator_max.to_bits(),
1780            coefficient_bits: objective
1781                .coefficients
1782                .map(|value| u64::from(value.to_bits())),
1783            cost_unit: identity_words(objective.cost_unit),
1784            cost_cap: objective.cost_cap,
1785            truth_tokens: objective.truth_tokens,
1786        },
1787        groups,
1788        group_members,
1789        canaries,
1790        protected_members,
1791    ))
1792}
1793
1794fn objective_identity(
1795    objective: &SemanticTrainingObjective,
1796    rows: &[TrainingViewRowDescriptor],
1797    task_identity: Identity256,
1798    canaries: &[SemanticTrainingCanaryRecord],
1799) -> Identity256 {
1800    let mut hasher = Sha256::new();
1801    hasher.update(b"xlog.semantic.training-objective.v3\0");
1802    hasher.update(SEMANTIC_TRAINING_CANARY_EVALUATOR_ABI.to_le_bytes());
1803    hasher.update(task_identity.as_bytes());
1804    hasher.update(objective.evaluator_min.to_bits().to_le_bytes());
1805    hasher.update(objective.evaluator_max.to_bits().to_le_bytes());
1806    for coefficient in objective.coefficients {
1807        hasher.update(coefficient.to_bits().to_le_bytes());
1808    }
1809    hasher.update(objective.cost_unit.as_bytes());
1810    hasher.update(objective.cost_cap.to_le_bytes());
1811    for token in objective.truth_tokens {
1812        hasher.update(token.to_le_bytes());
1813    }
1814    hasher.update(
1815        u64::try_from(objective.groups.len())
1816            .expect("validated training objective group count")
1817            .to_le_bytes(),
1818    );
1819    for group in &objective.groups {
1820        hasher.update((group.kind as u64).to_le_bytes());
1821        hasher.update(group.denominator.to_le_bytes());
1822        hasher.update(
1823            u64::try_from(group.row_ordinals.len())
1824                .expect("validated training objective group extent")
1825                .to_le_bytes(),
1826        );
1827        for ordinal in &group.row_ordinals {
1828            hasher.update(ordinal.to_le_bytes());
1829            let row = &rows[*ordinal as usize];
1830            for word in row.identity {
1831                hasher.update(word.to_le_bytes());
1832            }
1833            for word in row.content_identity {
1834                hasher.update(word.to_le_bytes());
1835            }
1836        }
1837    }
1838    hasher.update(
1839        u64::try_from(objective.canaries.len())
1840            .expect("validated training canary count")
1841            .to_le_bytes(),
1842    );
1843    for canary in canaries {
1844        for word in canary.identity {
1845            hasher.update(word.to_le_bytes());
1846        }
1847    }
1848    Identity256::from_bytes(hasher.finalize().into())
1849}
1850
1851fn canary_identity(
1852    objective: &SemanticTrainingObjective,
1853    canary: &SemanticTrainingCanary,
1854    row: &TrainingViewRowDescriptor,
1855    task_identity: Identity256,
1856) -> Identity256 {
1857    let mut hasher = Sha256::new();
1858    hasher.update(b"xlog.semantic.training-canary.v3\0");
1859    hasher.update(SEMANTIC_TRAINING_CANARY_EVALUATOR_ABI.to_le_bytes());
1860    hasher.update((canary.kind as u64).to_le_bytes());
1861    hasher.update(canary.row_ordinal.to_le_bytes());
1862    hasher.update(canary.lower_bound.to_bits().to_le_bytes());
1863    hasher.update(canary.upper_bound.to_bits().to_le_bytes());
1864    hasher.update(canary.memory_limit.to_le_bytes());
1865    hasher.update(canary.work_limit.to_le_bytes());
1866    for position in canary.obligation_positions {
1867        hasher.update(position.to_le_bytes());
1868    }
1869    hasher.update(
1870        u64::try_from(canary.protected_positions.len())
1871            .expect("validated protected retention member count")
1872            .to_le_bytes(),
1873    );
1874    for position in &canary.protected_positions {
1875        hasher.update(position.to_le_bytes());
1876    }
1877    for word in row.identity {
1878        hasher.update(word.to_le_bytes());
1879    }
1880    for word in row.content_identity {
1881        hasher.update(word.to_le_bytes());
1882    }
1883    hasher.update(task_identity.as_bytes());
1884    for token in objective.truth_tokens {
1885        hasher.update(token.to_le_bytes());
1886    }
1887    Identity256::from_bytes(hasher.finalize().into())
1888}
1889
1890/// Check the original v2 byte layout before either cold admission or device allocation.
1891pub fn validate_training_view_layout(
1892    bytes: &[u8],
1893) -> Result<SemanticTrainingViewLayout, SemanticTransitionError> {
1894    let mut schema = [0u8; 32];
1895    let tag = b"dlm-new/training-view/v2";
1896    schema[..tag.len()].copy_from_slice(tag);
1897    if bytes.len() < TRAINING_VIEW_HEADER_BYTES || bytes[..32] != schema {
1898        return Err(input_error("training-view row has another schema"));
1899    }
1900    let word = |offset: usize| {
1901        u64::from_le_bytes(
1902            bytes[offset..offset + 8]
1903                .try_into()
1904                .expect("bounded training-view header"),
1905        )
1906    };
1907    let window = usize::try_from(word(96))
1908        .map_err(|_| input_error("training-view window exceeds host address space"))?;
1909    let expected = window
1910        .checked_mul(TRAINING_VIEW_ROW_BYTES)
1911        .and_then(|length| length.checked_add(TRAINING_VIEW_HEADER_BYTES + (window % 2) * 4));
1912    if window == 0
1913        || word(104) == 0
1914        || word(112) == 0
1915        || word(104) > word(96)
1916        || word(120) > word(104)
1917        || word(128) == 0
1918        || word(128) > word(104)
1919        || expected != Some(bytes.len())
1920    {
1921        return Err(input_error(
1922            "training-view row has invalid extent or geometry",
1923        ));
1924    }
1925    let branch_words = std::array::from_fn(|index| word(136 + index * 8));
1926    let shared_context_end = branch_words[0];
1927    if shared_context_end == 0 {
1928        if branch_words[1..].iter().any(|&value| value != 0) {
1929            return Err(input_error(
1930                "training-view row has incomplete branch geometry",
1931            ));
1932        }
1933    } else {
1934        let mut next_begin = word(104)
1935            .checked_mul(2)
1936            .ok_or_else(|| input_error("training-view branch extent exceeds address space"))?;
1937        if shared_context_end > word(128) || next_begin > word(96) {
1938            return Err(input_error("training-view row has invalid branch geometry"));
1939        }
1940        for branch in 0..3 {
1941            let offset = 1 + branch * 5;
1942            let [tail_begin, tail_end, answer_begin, answer_end, answer_start] = branch_words
1943                [offset..offset + 5]
1944                .try_into()
1945                .expect("bounded branch words");
1946            if tail_begin != next_begin
1947                || tail_end <= tail_begin
1948                || answer_begin != tail_end
1949                || answer_end <= answer_begin
1950                || answer_end > word(96)
1951                || shared_context_end.checked_add(tail_end - tail_begin) != Some(answer_start)
1952                || answer_start
1953                    .checked_add(answer_end - answer_begin)
1954                    .is_none_or(|end| end > word(96))
1955            {
1956                return Err(input_error("training-view row has invalid branch geometry"));
1957            }
1958            next_begin = answer_end;
1959        }
1960    }
1961    let padding = TRAINING_VIEW_HEADER_BYTES + window * 20;
1962    if bytes[padding..padding + (window % 2) * 4]
1963        .iter()
1964        .any(|&byte| byte != 0)
1965    {
1966        return Err(input_error(
1967            "training-view row has noncanonical alignment padding",
1968        ));
1969    }
1970    Ok(SemanticTrainingViewLayout {
1971        window,
1972        source_length: word(104),
1973        block_size: word(112),
1974        prefix_extent: word(120),
1975        answer_start: word(128),
1976        branch_words,
1977    })
1978}
1979
1980fn validate_row(
1981    ordinal: usize,
1982    row: &SemanticTrainingViewRow,
1983    raw_offset: usize,
1984    training_domain: &SemanticTrainingDomain,
1985) -> Result<TrainingViewRowDescriptor, SemanticTransitionError> {
1986    let bytes = &row.bytes;
1987    let layout = validate_training_view_layout(bytes)?;
1988    if layout.window as u64 != training_domain.window
1989        || layout.block_size != training_domain.block_size
1990        || layout
1991            .source_length
1992            .checked_mul(2)
1993            .is_none_or(|extent| extent > training_domain.window)
1994        || row.mask_policy != training_domain.mask_policy
1995        || row.training_seed != training_domain.training_seed
1996        || training_domain
1997            .manifests
1998            .binary_search_by(|manifest| {
1999                manifest
2000                    .identity
2001                    .as_bytes()
2002                    .cmp(row.data_manifest.as_bytes())
2003            })
2004            .is_err()
2005    {
2006        return Err(input_error(
2007            "training view lies outside its original manifest or loader domain",
2008        ));
2009    }
2010    let kinds_offset = TRAINING_VIEW_HEADER_BYTES + layout.window * 68 + (layout.window % 2) * 4;
2011    for slot in 0..layout.window {
2012        let token_offset = TRAINING_VIEW_HEADER_BYTES + slot * 8;
2013        let kind_offset = kinds_offset + slot * 8;
2014        let token = i64::from_le_bytes(bytes[token_offset..token_offset + 8].try_into().unwrap());
2015        let kind = i64::from_le_bytes(bytes[kind_offset..kind_offset + 8].try_into().unwrap());
2016        if (kind == 0 && u64::try_from(token).ok() != Some(training_domain.pad_id))
2017            || (kind == 2 && u64::try_from(token).ok() != Some(training_domain.mask_id))
2018        {
2019            return Err(input_error(
2020                "training view changed the original padding or mask token",
2021            ));
2022        }
2023    }
2024    if !raw_offset.is_multiple_of(8) {
2025        return Err(input_error("training-view row has invalid raw alignment"));
2026    }
2027    let identity: [u8; 32] = bytes[32..64].try_into().expect("bounded identity");
2028    let source_identity: [u8; 32] = bytes[64..96].try_into().expect("bounded identity");
2029    if matches!(row.basis, SemanticTrainingViewBasis::Episode) != row.origin.is_some() {
2030        return Err(input_error(
2031            "only an episode training view has an authentic execution origin",
2032        ));
2033    }
2034    if matches!(row.basis, SemanticTrainingViewBasis::CorpusSymbolicAnchor)
2035        != row.task_content.is_some()
2036    {
2037        return Err(input_error(
2038            "only a symbolic corpus training view carries a native task-content binding",
2039        ));
2040    }
2041    if identity != *row.identity.as_bytes()
2042        || Sha256::digest(bytes).as_slice() != row.bytes_identity.as_bytes()
2043        || source_identity == [0; 32]
2044        || row.content_identity == Identity256::default()
2045    {
2046        return Err(input_error(
2047            "training-view row differs from its logical identity, material bytes, source or replay content",
2048        ));
2049    }
2050    Ok(TrainingViewRowDescriptor {
2051        ordinal: u64::try_from(ordinal)
2052            .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
2053        basis: row.basis as u64,
2054        raw_offset: u64::try_from(raw_offset)
2055            .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
2056        raw_bytes: u64::try_from(bytes.len())
2057            .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
2058        window: u64::try_from(layout.window)
2059            .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
2060        source_length: layout.source_length,
2061        block_size: layout.block_size,
2062        prefix_extent: layout.prefix_extent,
2063        answer_start: layout.answer_start,
2064        identity: identity_words(Identity256::from_bytes(identity)),
2065        source_identity: identity_words(Identity256::from_bytes(source_identity)),
2066        content_identity: identity_words(row.content_identity),
2067        task_query_identity: row
2068            .task_content
2069            .map(|binding| identity_words(binding.query))
2070            .unwrap_or_default(),
2071        task_theory_program_identity: row
2072            .task_content
2073            .map(|binding| identity_words(binding.theory_program))
2074            .unwrap_or_default(),
2075        task_result_identity: row
2076            .task_content
2077            .map(|binding| identity_words(binding.result))
2078            .unwrap_or_default(),
2079        origin: row.origin.map(origin_record).unwrap_or_default(),
2080    })
2081}
2082
2083fn origin_record(origin: SemanticTrainingViewOrigin) -> SemanticTrainingViewOriginRecord {
2084    SemanticTrainingViewOriginRecord {
2085        present: 1,
2086        transition: match origin.transition {
2087            SemanticTransitionKind::Proposal => 1,
2088            SemanticTransitionKind::Recompute => 2,
2089            SemanticTransitionKind::Drain => 3,
2090            SemanticTransitionKind::Update => 4,
2091        },
2092        lineage_instance: identity_words(origin.predecessor.instance),
2093        predecessor_instance: identity_words(origin.predecessor.instance),
2094        predecessor_word: origin.predecessor.word,
2095        predecessor_logical: identity_words(origin.predecessor.logical_digest),
2096        predecessor_state: identity_words(origin.predecessor.state_digest),
2097        successor_instance: identity_words(origin.successor.instance),
2098        successor_word: origin.successor.word,
2099        successor_logical: identity_words(origin.successor.logical_digest),
2100        successor_state: identity_words(origin.successor.state_digest),
2101        model_generation: u64::from(origin.invocation.model_generation),
2102        stream_serial: origin.invocation.stream_serial,
2103        family_id: u64::from(origin.invocation.family_id),
2104        proposal: u64::from(origin.invocation.proposal),
2105        model_geometry_digest: identity_words(origin.model_geometry_digest),
2106        model_numerical_digest: identity_words(origin.model_numerical_digest),
2107    }
2108}
2109
2110fn identity_words(identity: Identity256) -> [u64; 4] {
2111    std::array::from_fn(|index| {
2112        let begin = index * 8;
2113        u64::from_le_bytes(
2114            identity.as_bytes()[begin..begin + 8]
2115                .try_into()
2116                .expect("identity word"),
2117        )
2118    })
2119}
2120
2121fn input_error(detail: impl Into<String>) -> SemanticTransitionError {
2122    SemanticTransitionError::InvalidInput {
2123        detail: detail.into(),
2124    }
2125}
2126
2127fn runtime_error(
2128    operation: &'static str,
2129    error: impl std::fmt::Display,
2130) -> SemanticTransitionError {
2131    SemanticTransitionError::Runtime {
2132        operation,
2133        detail: error.to_string(),
2134    }
2135}
2136
2137fn map_enqueue_error<E: std::fmt::Display>(
2138    error: LaunchEnqueueError<E>,
2139) -> SemanticTransitionError {
2140    runtime_error("training-view device selection", error)
2141}