Skip to main content

ferrum_interfaces/vnext/resource/
plan_runtime.rs

1use super::{
2    core_resource_failure, deferred_device_cleanup_status, invalid_resource,
3    maintain_deferred_device_cleanups, new_deferred_device_cleanup_domain,
4    retire_deferred_device_cleanup_domain, watch, AdmissionDeferred, AdmissionDemand,
5    AdmissionFitPolicy, AdmissionPressureAction, AllocationLifetime, Arc, AtomicU8,
6    BackingPrepareDecision, CapacityAvailabilityEpoch, CapacityEntry, CapacityEpochs,
7    CapacityUnits, CapacityVector, CapacityWaitCondition, CapacityWaitRecheck,
8    DeferredDeviceCleanupDomainId, DeferredDeviceCleanupMaintenanceReceipt,
9    DeferredDeviceCleanupStatus, DeviceCapacityClaim, DeviceCapacitySignal, DeviceId,
10    DeviceRuntime, DynamicBackingDeferred, DynamicDeferredMaintenanceOutcome,
11    DynamicPoolMaintenanceController, DynamicPoolMaintenanceStatus, DynamicPoolSet,
12    DynamicResourceShape, EvaluatedBackingProjection, EvaluatedBackingRequest, ExecutionLane,
13    ExecutionLaneCreationError, FailureEnvelope, InvocationLivenessMode,
14    LaneBackingPrepareDecision, LogicalAdmissionCoordinator, LogicalAdmissionCoordinatorId, Mutex,
15    NoStatic, NodeId, Ordering, PhysicalBackingClaimIdentity, PlanHash, PlanId, PlanNode,
16    ResourceAbandonSignal, ResourceActionCursor, ResourceDriverFailure,
17    ResourceLedgerEntrySnapshot, ResourceOwnershipReason, ResourceOwnershipTransferFailure,
18    ResourcePoolIdentity, ResourcePoolOwnership, ResourceReservation, ResourceReservationBatch,
19    ResourceTransactionAction, ResourceTransactionContext, ResourceTransactionDriver,
20    ResourceTransactionIdentity, ResourceTransactionState, RwLock, RwLockReadGuard, Serialize,
21    StaticProvisioningBinding, StaticProvisioningLease, VNextError,
22};
23use crate::vnext::{
24    ResolvedReusableExecutionBucket, ReusableExecutionBucketId, ReusableExecutionBucketSpec,
25};
26
27pub(super) const PLAN_RUNTIME_OPEN: u8 = 0;
28const PLAN_RUNTIME_CLOSING: u8 = 1;
29
30pub(super) trait ErasedPlanStaticDriver<R>: Send
31where
32    R: DeviceRuntime,
33{
34    fn release_resource(
35        &mut self,
36        context: &ResourceTransactionContext<'_, R>,
37        reservation: &ResourceReservation,
38        buffer: &R::Buffer,
39    ) -> Result<(), ResourceDriverFailure>;
40
41    fn quarantine_transaction(
42        &mut self,
43        context: &ResourceTransactionContext<'_, R>,
44        ownership: ResourcePoolOwnership<R>,
45    ) -> Result<(), ResourceOwnershipTransferFailure<R>>;
46
47    fn abandon_transaction(&mut self, ownership: ResourcePoolOwnership<R>);
48}
49
50impl<D> ErasedPlanStaticDriver<D::Runtime> for D
51where
52    D: ResourceTransactionDriver,
53{
54    fn release_resource(
55        &mut self,
56        context: &ResourceTransactionContext<'_, D::Runtime>,
57        reservation: &ResourceReservation,
58        buffer: &D::Buffer,
59    ) -> Result<(), ResourceDriverFailure> {
60        ResourceTransactionDriver::release_resource(self, context, reservation, buffer)
61    }
62
63    fn quarantine_transaction(
64        &mut self,
65        context: &ResourceTransactionContext<'_, D::Runtime>,
66        ownership: ResourcePoolOwnership<D::Runtime>,
67    ) -> Result<(), ResourceOwnershipTransferFailure<D::Runtime>> {
68        ResourceTransactionDriver::quarantine_transaction(self, context, ownership)
69    }
70
71    fn abandon_transaction(&mut self, ownership: ResourcePoolOwnership<D::Runtime>) {
72        ResourceTransactionDriver::abandon_transaction(self, ownership);
73    }
74}
75
76pub(super) struct PlanStaticResources<R>
77where
78    R: DeviceRuntime,
79{
80    pub(super) driver: Mutex<Option<Box<dyn ErasedPlanStaticDriver<R>>>>,
81    pub(super) identity: ResourceTransactionIdentity,
82    pub(super) admission: StaticProvisioningBinding,
83    pub(super) reservations: ResourceReservationBatch,
84    pub(super) states: Vec<ResourceTransactionState>,
85    pub(super) capacity_claim: Option<DeviceCapacityClaim>,
86    pub(super) lease: Option<StaticProvisioningLease<R>>,
87    pub(super) finalized: bool,
88}
89
90impl<R> PlanStaticResources<R>
91where
92    R: DeviceRuntime,
93{
94    fn ledger_snapshot_entries(&self) -> Vec<ResourceLedgerEntrySnapshot> {
95        self.lease
96            .as_ref()
97            .expect("open plan runtime owns its static lease")
98            .slots
99            .iter()
100            .zip(&self.states)
101            .map(|(slot, &transaction_state)| ResourceLedgerEntrySnapshot {
102                entry: slot.entry.clone(),
103                transaction_state,
104                buffer_present: slot.buffer.is_some(),
105                actual_resource_id: slot.actual_resource_id.clone(),
106                actual_generation: slot.actual_generation,
107                actual_descriptor: slot.descriptor.clone(),
108            })
109            .collect()
110    }
111
112    fn release_all(&mut self) -> Result<usize, ResourceDriverFailure> {
113        let mut released = 0;
114        for order in 0..self.states.len() {
115            if self.states[order] == ResourceTransactionState::Released {
116                continue;
117            }
118            if self.states[order] != ResourceTransactionState::Committed {
119                return Err(ResourceDriverFailure::new(core_resource_failure(
120                    "plan_runtime_close_ledger_diverged",
121                    "plan runtime close found a non-committed static resource",
122                    false,
123                ))
124                .expect("core failure has resource domain"));
125            }
126            let lease = self
127                .lease
128                .as_ref()
129                .expect("open plan runtime owns its static lease");
130            let reservation = self.reservations.reservations[order].clone();
131            let buffer = lease
132                .buffer(order)
133                .expect("committed plan runtime owns its static buffer");
134            let context = ResourceTransactionContext {
135                runtime: &lease.runtime,
136                identity: &self.identity,
137                binding: &self.admission,
138                reservations: &self.reservations,
139                cursor: Some(ResourceActionCursor {
140                    order,
141                    action: ResourceTransactionAction::Release,
142                    before: self.states[order],
143                    allocation_authorized: false,
144                }),
145                allocation_authority: None,
146                pending_allocation: None,
147            };
148            let driver = match self.driver.get_mut() {
149                Ok(driver) => driver,
150                Err(poisoned) => poisoned.into_inner(),
151            };
152            driver
153                .as_mut()
154                .expect("open plan runtime owns its static driver")
155                .release_resource(&context, &reservation, buffer)?;
156            self.lease
157                .as_mut()
158                .expect("open plan runtime owns its static lease")
159                .clear(order);
160            self.states[order] = ResourceTransactionState::Released;
161            self.capacity_claim
162                .as_mut()
163                .expect("open plan runtime owns its static capacity claim")
164                .release_bytes(reservation.size_bytes());
165            released += 1;
166        }
167        if let Some(mut claim) = self.capacity_claim.take() {
168            claim.release();
169        }
170        self.finalized = true;
171        Ok(released)
172    }
173
174    fn quarantine_remaining(&mut self) -> Result<usize, ResourceDriverFailure> {
175        let quarantined = self
176            .states
177            .iter()
178            .filter(|state| **state == ResourceTransactionState::Committed)
179            .count();
180        if self.states.iter().any(|state| {
181            !matches!(
182                state,
183                ResourceTransactionState::Committed | ResourceTransactionState::Released
184            )
185        }) {
186            return Err(ResourceDriverFailure::new(core_resource_failure(
187                "plan_runtime_quarantine_ledger_diverged",
188                "plan runtime quarantine found an invalid static resource state",
189                false,
190            ))
191            .expect("core failure has resource domain"));
192        }
193        if quarantined == 0 {
194            self.finalized = true;
195            return Ok(0);
196        }
197        let lease = self
198            .lease
199            .as_mut()
200            .expect("failed plan runtime close owns its static lease");
201        let buffers = lease.take_owned_buffers(&self.reservations);
202        let ownership = ResourcePoolOwnership {
203            runtime: Arc::clone(&lease.runtime),
204            pool_identity: self.admission.pool_identity.clone(),
205            reason: ResourceOwnershipReason::Quarantine,
206            signal: None,
207            buffers,
208            capacity_claim: self.capacity_claim.take(),
209        };
210        let result = {
211            let context = ResourceTransactionContext {
212                runtime: &lease.runtime,
213                identity: &self.identity,
214                binding: &self.admission,
215                reservations: &self.reservations,
216                cursor: None,
217                allocation_authority: None,
218                pending_allocation: None,
219            };
220            let driver = match self.driver.get_mut() {
221                Ok(driver) => driver,
222                Err(poisoned) => poisoned.into_inner(),
223            };
224            driver
225                .as_mut()
226                .expect("failed plan runtime close owns its static driver")
227                .quarantine_transaction(&context, ownership)
228        };
229        if let Err(failure) = result {
230            let (failure, mut ownership) = failure.into_parts();
231            let expected_claimed_bytes = self
232                .states
233                .iter()
234                .zip(self.reservations.reservations())
235                .filter(|(state, _)| state.is_live())
236                .map(|(_, reservation)| reservation.size_bytes())
237                .sum::<u64>();
238            if ownership.pool_identity != self.admission.pool_identity
239                || ownership.claimed_bytes() != expected_claimed_bytes
240            {
241                std::mem::forget(ownership);
242                return Err(ResourceDriverFailure::new(core_resource_failure(
243                    "ownership_transfer_identity_mismatch",
244                    "plan runtime quarantine failure returned foreign ownership",
245                    false,
246                ))
247                .expect("core failure has resource domain"));
248            }
249            self.lease
250                .as_mut()
251                .expect("failed plan runtime close owns its static lease")
252                .restore_owned_buffers(std::mem::take(&mut ownership.buffers));
253            self.capacity_claim = ownership.capacity_claim.take();
254            return Err(failure);
255        }
256        for state in &mut self.states {
257            if *state == ResourceTransactionState::Committed {
258                *state = ResourceTransactionState::Quarantined;
259            }
260        }
261        self.finalized = true;
262        Ok(quarantined)
263    }
264}
265
266impl<R> Drop for PlanStaticResources<R>
267where
268    R: DeviceRuntime,
269{
270    fn drop(&mut self) {
271        if self.finalized {
272            return;
273        }
274        let signal = ResourceAbandonSignal {
275            identity: self.identity.clone(),
276            admission: self.admission.clone(),
277            state: if self
278                .states
279                .iter()
280                .all(|state| *state == ResourceTransactionState::Committed)
281            {
282                ResourceTransactionState::Committed
283            } else {
284                ResourceTransactionState::Released
285            },
286            pending_action: None,
287            ledger: self.ledger_snapshot_entries(),
288            active_sequence_slots: Vec::new(),
289            poisoned_sequence_slots: Vec::new(),
290            undrained_sequence_slots: Vec::new(),
291            failure: None,
292        };
293        let mut lease = self
294            .lease
295            .take()
296            .expect("open plan runtime owns its static lease");
297        let buffers = lease.take_owned_buffers(&self.reservations);
298        let StaticProvisioningLease {
299            slots: _,
300            identity: _,
301            admission: _,
302            runtime,
303        } = lease;
304        let ownership = ResourcePoolOwnership {
305            runtime,
306            pool_identity: self.admission.pool_identity.clone(),
307            reason: ResourceOwnershipReason::Abandon,
308            signal: Some(signal),
309            buffers,
310            capacity_claim: self.capacity_claim.take(),
311        };
312        let driver = match self.driver.get_mut() {
313            Ok(driver) => driver,
314            Err(poisoned) => poisoned.into_inner(),
315        };
316        if let Some(driver) = driver.as_mut() {
317            let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
318                driver.abandon_transaction(ownership);
319            }));
320        } else {
321            std::mem::forget(ownership);
322        }
323        self.finalized = true;
324    }
325}
326
327pub(super) enum PlanRuntimeStatic<R>
328where
329    R: DeviceRuntime,
330{
331    NoStatic { binding: StaticProvisioningBinding },
332    Static(PlanStaticResources<R>),
333}
334
335/// Unique plan-lifetime owner of the runtime, dynamic pools, maintenance
336/// authority, static buffers, capacity claims, and cleanup authority.
337#[must_use = "plan runtime resources must be explicitly closed or safely abandoned"]
338pub struct PlanRuntimeResources<R>
339where
340    R: DeviceRuntime,
341{
342    pub(super) lifecycle: RwLock<()>,
343    pub(super) phase: AtomicU8,
344    pub(super) lifecycle_tx: watch::Sender<u8>,
345    pub(super) maintenance_controller: DynamicPoolMaintenanceController<R>,
346    pub(super) dynamic_pools: Arc<DynamicPoolSet<R>>,
347    pub(super) static_resources: PlanRuntimeStatic<R>,
348    pub(super) runtime: Arc<R>,
349    pub(super) deferred_cleanup_domain: DeferredDeviceCleanupDomainId,
350}
351
352/// Sealed owning proof that one exact plan, runtime instance, provisioning
353/// outcome, and admission coordinator belong together. Every durable child
354/// authority holds the same root `Arc`.
355#[must_use = "a trusted plan/runtime binding must be consumed by logical admission"]
356pub struct TrustedPlanRuntimeBinding<R>
357where
358    R: DeviceRuntime,
359{
360    pub(super) resources: Arc<PlanRuntimeResources<R>>,
361}
362
363/// Internal owning capability that binds non-authoritative backing evidence to
364/// the one plan runtime allowed to revalidate it. Public scope-specific handles
365/// embed this value and retain any additional request/session/step parent.
366pub(super) struct PlanBackingDeferral<R>
367where
368    R: DeviceRuntime,
369{
370    evidence: DynamicBackingDeferred,
371    resources: Arc<PlanRuntimeResources<R>>,
372}
373
374impl<R> PlanBackingDeferral<R>
375where
376    R: DeviceRuntime,
377{
378    pub(super) fn new(
379        resources: Arc<PlanRuntimeResources<R>>,
380        evidence: DynamicBackingDeferred,
381    ) -> Result<Self, VNextError> {
382        {
383            let _lifecycle = resources.read_lifecycle("bind deferred backing to its plan")?;
384            if evidence.wait_condition().coordinator_id()
385                != resources.dynamic_pools.logical_admission.id()
386            {
387                return Err(invalid_resource(
388                    "deferred backing belongs to another plan coordinator",
389                ));
390            }
391        }
392        Ok(Self {
393            evidence,
394            resources,
395        })
396    }
397
398    pub(super) fn evidence(&self) -> &DynamicBackingDeferred {
399        &self.evidence
400    }
401
402    pub(super) fn maintain(&self) -> Result<DynamicDeferredMaintenanceOutcome, VNextError> {
403        let _lifecycle = self
404            .resources
405            .read_lifecycle("maintain plan-owned deferred backing")?;
406        // Stable lane slots own real residency. Reclaim a provably idle slot
407        // before asking the pool to grow, otherwise a reclaimable cache entry
408        // can make growth look like a terminal resident-ceiling violation.
409        if self
410            .resources
411            .dynamic_pools
412            .try_reclaim_expired_lane_slots()?
413        {
414            return self.retry_admission();
415        }
416        let outcome = self
417            .resources
418            .maintenance_controller
419            .maintain_for_live_deferred(&self.evidence)?;
420        if matches!(
421            &outcome,
422            DynamicDeferredMaintenanceOutcome::WaitForRelease { .. }
423        ) && self
424            .resources
425            .dynamic_pools
426            .try_reclaim_one_idle_lane_slot()?
427        {
428            return self.retry_admission();
429        }
430        Ok(outcome)
431    }
432
433    pub(super) fn retry_admission(&self) -> Result<DynamicDeferredMaintenanceOutcome, VNextError> {
434        let mut availability = Vec::with_capacity(self.resources.dynamic_pools.domains.len() + 3);
435        let current_epochs = self
436            .resources
437            .dynamic_pools
438            .write_capacity_availability(&mut availability)?;
439        Ok(DynamicDeferredMaintenanceOutcome::RetryAdmission { current_epochs })
440    }
441
442    pub(super) fn register_waiter(&self) -> Result<PlanCapacityWaitRegistration<R>, VNextError> {
443        self.resources
444            .register_capacity_waiter(self.evidence.wait_condition())
445    }
446}
447
448/// A capacity wait registration that keeps its exact plan runtime alive until
449/// the waiter either observes a retry epoch or is cancelled by being dropped.
450#[must_use = "capacity wait registrations must be awaited, rechecked, or dropped"]
451pub struct PlanCapacityWaitRegistration<R>
452where
453    R: DeviceRuntime,
454{
455    observed: CapacityWaitCondition,
456    registered: CapacityWaitCondition,
457    logical_rx: watch::Receiver<CapacityEpochs>,
458    plan_capacity_rx: watch::Receiver<DeviceCapacitySignal>,
459    process_capacity_rx: watch::Receiver<DeviceCapacitySignal>,
460    lifecycle_rx: watch::Receiver<u8>,
461    resources: Arc<PlanRuntimeResources<R>>,
462}
463
464impl<R> PlanCapacityWaitRegistration<R>
465where
466    R: DeviceRuntime,
467{
468    pub fn recheck(&self) -> Result<CapacityWaitRecheck, VNextError> {
469        let _lifecycle = self.resources.read_lifecycle("recheck a capacity waiter")?;
470        let mut availability = Vec::with_capacity(self.resources.dynamic_pools.domains.len() + 3);
471        let current = self
472            .resources
473            .dynamic_pools
474            .write_capacity_availability(&mut availability)?;
475        Ok(CapacityWaitRecheck::new(
476            current,
477            self.observed.changed_since(&availability)?,
478            self.registered.changed_since(&availability)?,
479        ))
480    }
481
482    pub async fn wait_for_change(self) -> Result<CapacityEpochs, VNextError> {
483        let Self {
484            observed,
485            registered,
486            mut logical_rx,
487            mut plan_capacity_rx,
488            mut process_capacity_rx,
489            mut lifecycle_rx,
490            resources,
491        } = self;
492        loop {
493            let recheck = {
494                let _lifecycle = match resources.read_lifecycle("recheck a capacity waiter") {
495                    Ok(lifecycle) => lifecycle,
496                    Err(_) if resources.is_closing() => {
497                        return Err(invalid_resource(
498                            "closing plan runtime cancelled its capacity waiter",
499                        ));
500                    }
501                    Err(error) => return Err(error),
502                };
503                let mut availability =
504                    Vec::with_capacity(resources.dynamic_pools.domains.len() + 3);
505                let current = resources
506                    .dynamic_pools
507                    .write_capacity_availability(&mut availability)?;
508                CapacityWaitRecheck::new(
509                    current,
510                    observed.changed_since(&availability)?,
511                    registered.changed_since(&availability)?,
512                )
513            };
514            if recheck.should_retry() {
515                return Ok(recheck.current());
516            }
517            tokio::select! {
518                biased;
519                changed = lifecycle_rx.changed() => {
520                    changed.map_err(|_| invalid_resource(
521                        "plan runtime lifecycle signal closed while a capacity waiter was live",
522                    ))?;
523                    if *lifecycle_rx.borrow_and_update() == PLAN_RUNTIME_CLOSING {
524                        return Err(invalid_resource(
525                            "closing plan runtime cancelled its capacity waiter",
526                        ));
527                    }
528                    return Err(invalid_resource(
529                        "capacity waiter observed an invalid plan runtime lifecycle transition",
530                    ));
531                }
532                changed = logical_rx.changed() => {
533                    changed.map_err(|_| invalid_resource(
534                        "logical capacity signal closed while a plan waiter was live",
535                    ))?;
536                    logical_rx.borrow_and_update();
537                }
538                changed = plan_capacity_rx.changed() => {
539                    changed.map_err(|_| invalid_resource(
540                        "plan device-capacity signal closed while a waiter was live",
541                    ))?;
542                    plan_capacity_rx.borrow_and_update();
543                }
544                changed = process_capacity_rx.changed() => {
545                    changed.map_err(|_| invalid_resource(
546                        "process device-capacity signal closed while a waiter was live",
547                    ))?;
548                    process_capacity_rx.borrow_and_update();
549                }
550            }
551        }
552    }
553}
554
555#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
556pub struct PlanRuntimeCloseReceipt {
557    evidence: TrustedPlanRuntimeEvidence,
558    released_static_resources: usize,
559}
560
561#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
562pub struct PlanRuntimeQuarantineReceipt {
563    evidence: TrustedPlanRuntimeEvidence,
564    released_static_resources: usize,
565    quarantined_static_resources: usize,
566}
567
568impl PlanRuntimeQuarantineReceipt {
569    pub fn evidence(&self) -> &TrustedPlanRuntimeEvidence {
570        &self.evidence
571    }
572
573    pub const fn released_static_resources(&self) -> usize {
574        self.released_static_resources
575    }
576
577    pub const fn quarantined_static_resources(&self) -> usize {
578        self.quarantined_static_resources
579    }
580}
581
582impl PlanRuntimeCloseReceipt {
583    pub fn evidence(&self) -> &TrustedPlanRuntimeEvidence {
584        &self.evidence
585    }
586
587    pub const fn released_static_resources(&self) -> usize {
588        self.released_static_resources
589    }
590}
591
592pub enum PlanRuntimeCloseOutcome<R>
593where
594    R: DeviceRuntime,
595{
596    Closed(PlanRuntimeCloseReceipt),
597    Referenced {
598        resources: Arc<PlanRuntimeResources<R>>,
599        strong_count: usize,
600        deferred_cleanup: DeferredDeviceCleanupStatus,
601    },
602}
603
604#[must_use = "failed plan runtime close retains static cleanup ownership"]
605pub struct PlanRuntimeCloseFailure<R>
606where
607    R: DeviceRuntime,
608{
609    failure: FailureEnvelope,
610    evidence: TrustedPlanRuntimeEvidence,
611    static_resources: Option<PlanStaticResources<R>>,
612}
613
614impl<R> PlanRuntimeCloseFailure<R>
615where
616    R: DeviceRuntime,
617{
618    pub fn failure(&self) -> &FailureEnvelope {
619        &self.failure
620    }
621
622    pub fn retry(mut self) -> Result<PlanRuntimeCloseReceipt, Self> {
623        let static_resources = self
624            .static_resources
625            .as_mut()
626            .expect("plan runtime close failure owns static cleanup authority");
627        let total_static_resources = static_resources.states.len();
628        match static_resources.release_all() {
629            Ok(_) => {
630                self.static_resources.take();
631                Ok(PlanRuntimeCloseReceipt {
632                    evidence: self.evidence,
633                    released_static_resources: total_static_resources,
634                })
635            }
636            Err(failure) => {
637                self.failure = failure.into_failure();
638                Err(self)
639            }
640        }
641    }
642
643    pub fn quarantine(mut self) -> Result<PlanRuntimeQuarantineReceipt, Self> {
644        let static_resources = self
645            .static_resources
646            .as_mut()
647            .expect("plan runtime close failure owns static cleanup authority");
648        let released_static_resources = static_resources
649            .states
650            .iter()
651            .filter(|state| **state == ResourceTransactionState::Released)
652            .count();
653        match static_resources.quarantine_remaining() {
654            Ok(quarantined_static_resources) => {
655                self.static_resources.take();
656                Ok(PlanRuntimeQuarantineReceipt {
657                    evidence: self.evidence,
658                    released_static_resources,
659                    quarantined_static_resources,
660                })
661            }
662            Err(failure) => {
663                self.failure = failure.into_failure();
664                Err(self)
665            }
666        }
667    }
668}
669
670#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
671pub struct TrustedPlanRuntimeEvidence {
672    plan_id: PlanId,
673    plan_hash: PlanHash,
674    device_id: DeviceId,
675    runtime_implementation_fingerprint: String,
676    coordinator_id: LogicalAdmissionCoordinatorId,
677    static_provisioning_binding: Option<StaticProvisioningBinding>,
678    static_pool_identity: Option<ResourcePoolIdentity>,
679    static_provisioning_identity: Option<ResourceTransactionIdentity>,
680}
681
682impl TrustedPlanRuntimeEvidence {
683    pub fn plan_id(&self) -> &PlanId {
684        &self.plan_id
685    }
686
687    pub fn plan_hash(&self) -> &PlanHash {
688        &self.plan_hash
689    }
690
691    pub fn device_id(&self) -> &DeviceId {
692        &self.device_id
693    }
694
695    pub fn runtime_implementation_fingerprint(&self) -> &str {
696        &self.runtime_implementation_fingerprint
697    }
698
699    pub const fn coordinator_id(&self) -> LogicalAdmissionCoordinatorId {
700        self.coordinator_id
701    }
702
703    pub fn static_pool_identity(&self) -> Option<&ResourcePoolIdentity> {
704        self.static_pool_identity.as_ref()
705    }
706
707    pub fn static_provisioning_binding(&self) -> Option<&StaticProvisioningBinding> {
708        self.static_provisioning_binding.as_ref()
709    }
710
711    pub fn static_provisioning_identity(&self) -> Option<&ResourceTransactionIdentity> {
712        self.static_provisioning_identity.as_ref()
713    }
714}
715
716impl<R> NoStatic<R>
717where
718    R: DeviceRuntime,
719{
720    pub fn into_plan_runtime(self) -> Arc<PlanRuntimeResources<R>> {
721        let Self {
722            maintenance_controller,
723            dynamic_pools,
724            binding,
725            runtime,
726        } = self;
727        let (lifecycle_tx, _) = watch::channel(PLAN_RUNTIME_OPEN);
728        Arc::new(PlanRuntimeResources {
729            lifecycle: RwLock::new(()),
730            phase: AtomicU8::new(PLAN_RUNTIME_OPEN),
731            lifecycle_tx,
732            maintenance_controller,
733            dynamic_pools,
734            static_resources: PlanRuntimeStatic::NoStatic { binding },
735            runtime,
736            deferred_cleanup_domain: new_deferred_device_cleanup_domain(),
737        })
738    }
739}
740
741impl<R> PlanRuntimeResources<R>
742where
743    R: DeviceRuntime,
744{
745    fn evidence(&self) -> TrustedPlanRuntimeEvidence {
746        let (binding, identity) = match &self.static_resources {
747            PlanRuntimeStatic::NoStatic { binding } => (binding, None),
748            PlanRuntimeStatic::Static(source) => (&source.admission, Some(&source.identity)),
749        };
750        let has_static = matches!(&self.static_resources, PlanRuntimeStatic::Static(_));
751        TrustedPlanRuntimeEvidence {
752            plan_id: binding.plan_id().clone(),
753            plan_hash: binding.plan_hash().clone(),
754            device_id: binding.device_id().clone(),
755            runtime_implementation_fingerprint: binding
756                .device_runtime_implementation_fingerprint()
757                .to_owned(),
758            coordinator_id: self.dynamic_pools.logical_admission.id(),
759            static_provisioning_binding: has_static.then(|| binding.clone()),
760            static_pool_identity: has_static.then(|| binding.pool_identity().clone()),
761            static_provisioning_identity: identity.cloned(),
762        }
763    }
764
765    pub(super) fn read_lifecycle(
766        &self,
767        action: &'static str,
768    ) -> Result<RwLockReadGuard<'_, ()>, VNextError> {
769        let lifecycle = self
770            .lifecycle
771            .read()
772            .map_err(|_| invalid_resource("plan runtime lifecycle gate is poisoned"))?;
773        if self.phase.load(Ordering::Acquire) != PLAN_RUNTIME_OPEN {
774            return Err(invalid_resource(format!(
775                "closing plan runtime cannot {action}"
776            )));
777        }
778        let cleanup = self.deferred_cleanup_status();
779        if cleanup.is_saturated() {
780            return Err(invalid_resource(format!(
781                "plan runtime cannot {action} while {} deferred device cleanup owners await recovery",
782                cleanup.pending()
783            )));
784        }
785        Ok(lifecycle)
786    }
787
788    pub fn deferred_cleanup_status(&self) -> DeferredDeviceCleanupStatus {
789        deferred_device_cleanup_status(self.deferred_cleanup_domain)
790    }
791
792    pub fn create_execution_lane(
793        &self,
794    ) -> Result<Arc<ExecutionLane<R>>, ExecutionLaneCreationError<R::Error>> {
795        let _lifecycle = self
796            .read_lifecycle("create an execution lane")
797            .map_err(ExecutionLaneCreationError::Contract)?;
798        ExecutionLane::create(Arc::clone(&self.runtime))
799    }
800
801    /// Attempts each selected cleanup owner at most once. This may block in a
802    /// backend recovery call and must therefore run on a scheduler recovery
803    /// thread, never on a request, admission, or destructor path.
804    pub fn maintain_deferred_cleanups(
805        &self,
806        maximum_tasks: usize,
807    ) -> Result<DeferredDeviceCleanupMaintenanceReceipt, VNextError> {
808        if maximum_tasks == 0
809            || maximum_tasks > super::MAX_DEFERRED_DEVICE_CLEANUP_MAINTENANCE_TASKS
810        {
811            return Err(invalid_resource(format!(
812                "deferred device cleanup maintenance size must be in 1..={}",
813                super::MAX_DEFERRED_DEVICE_CLEANUP_MAINTENANCE_TASKS
814            )));
815        }
816        Ok(maintain_deferred_device_cleanups(
817            self.deferred_cleanup_domain,
818            maximum_tasks,
819        ))
820    }
821
822    pub fn trusted_runtime_binding(
823        self: &Arc<Self>,
824    ) -> Result<TrustedPlanRuntimeBinding<R>, VNextError> {
825        let _lifecycle = self.read_lifecycle("mint a trusted runtime binding")?;
826        Ok(TrustedPlanRuntimeBinding {
827            resources: Arc::clone(self),
828        })
829    }
830
831    pub fn maintain_for_admission_deferred(
832        self: &Arc<Self>,
833        deferred: &AdmissionDeferred,
834    ) -> Result<DynamicDeferredMaintenanceOutcome, VNextError> {
835        let _lifecycle = self.read_lifecycle("maintain deferred logical backing growth")?;
836        self.maintenance_controller
837            .maintain_for_admission_deferred(deferred)
838    }
839
840    /// Returns a point-in-time view of the exact dynamic pools owned by this
841    /// plan. Product telemetry consumes this instead of maintaining a second
842    /// allocator ledger that can drift from admission decisions.
843    pub fn dynamic_pool_status(&self) -> Result<DynamicPoolMaintenanceStatus, VNextError> {
844        let _lifecycle = self.read_lifecycle("observe dynamic pool status")?;
845        self.maintenance_controller.status()
846    }
847
848    pub fn write_dynamic_capacity_availability(
849        &self,
850        out: &mut Vec<CapacityAvailabilityEpoch>,
851    ) -> Result<CapacityEpochs, VNextError> {
852        let _lifecycle = self.read_lifecycle("observe dynamic capacity availability")?;
853        self.dynamic_pools.write_capacity_availability(out)
854    }
855
856    pub fn register_capacity_waiter(
857        self: &Arc<Self>,
858        observed: &CapacityWaitCondition,
859    ) -> Result<PlanCapacityWaitRegistration<R>, VNextError> {
860        let _lifecycle = self.read_lifecycle("register a capacity waiter")?;
861        if observed.coordinator_id() != self.dynamic_pools.logical_admission.id() {
862            return Err(invalid_resource(
863                "capacity wait condition belongs to another plan coordinator",
864            ));
865        }
866        let logical_rx = self.dynamic_pools.logical_admission.subscribe_epochs();
867        let plan_capacity_rx = self.dynamic_pools.budget.subscribe_plan_availability();
868        let process_capacity_rx = self.dynamic_pools.budget.subscribe_process_availability();
869        let lifecycle_rx = self.lifecycle_tx.subscribe();
870        let mut availability = Vec::with_capacity(self.dynamic_pools.domains.len() + 3);
871        self.dynamic_pools
872            .write_capacity_availability(&mut availability)?;
873        let registered = observed.refreshed_from(&availability)?;
874        Ok(PlanCapacityWaitRegistration {
875            observed: observed.clone(),
876            registered,
877            logical_rx,
878            plan_capacity_rx,
879            process_capacity_rx,
880            lifecycle_rx,
881            resources: Arc::clone(self),
882        })
883    }
884
885    pub fn is_closing(&self) -> bool {
886        self.phase.load(Ordering::Acquire) == PLAN_RUNTIME_CLOSING
887    }
888
889    pub fn close(
890        resources: Arc<Self>,
891    ) -> Result<PlanRuntimeCloseOutcome<R>, PlanRuntimeCloseFailure<R>> {
892        {
893            let _lifecycle = resources
894                .lifecycle
895                .write()
896                .unwrap_or_else(std::sync::PoisonError::into_inner);
897            match resources.phase.compare_exchange(
898                PLAN_RUNTIME_OPEN,
899                PLAN_RUNTIME_CLOSING,
900                Ordering::AcqRel,
901                Ordering::Acquire,
902            ) {
903                Ok(_) => {
904                    resources.lifecycle_tx.send_replace(PLAN_RUNTIME_CLOSING);
905                }
906                Err(PLAN_RUNTIME_CLOSING) => {}
907                Err(_) => unreachable!("plan runtime phase is privately bounded"),
908            }
909        }
910        let resources = match Arc::try_unwrap(resources) {
911            Ok(resources) => resources,
912            Err(resources) => {
913                let strong_count = Arc::strong_count(&resources);
914                let deferred_cleanup = resources.deferred_cleanup_status();
915                return Ok(PlanRuntimeCloseOutcome::Referenced {
916                    resources,
917                    strong_count,
918                    deferred_cleanup,
919                });
920            }
921        };
922        if resources.deferred_cleanup_status().pending() != 0 {
923            let resources = Arc::new(resources);
924            let deferred_cleanup = resources.deferred_cleanup_status();
925            return Ok(PlanRuntimeCloseOutcome::Referenced {
926                resources,
927                strong_count: 1,
928                deferred_cleanup,
929            });
930        }
931        let evidence = resources.evidence();
932        let Self {
933            lifecycle: _,
934            phase: _,
935            lifecycle_tx,
936            maintenance_controller,
937            dynamic_pools,
938            static_resources,
939            runtime,
940            deferred_cleanup_domain,
941        } = resources;
942        debug_assert!(retire_deferred_device_cleanup_domain(
943            deferred_cleanup_domain
944        ));
945        drop(lifecycle_tx);
946        drop(maintenance_controller);
947        drop(dynamic_pools);
948        match static_resources {
949            PlanRuntimeStatic::NoStatic { .. } => {
950                drop(runtime);
951                Ok(PlanRuntimeCloseOutcome::Closed(PlanRuntimeCloseReceipt {
952                    evidence,
953                    released_static_resources: 0,
954                }))
955            }
956            PlanRuntimeStatic::Static(mut static_resources) => {
957                drop(runtime);
958                let total_static_resources = static_resources.states.len();
959                match static_resources.release_all() {
960                    Ok(_) => {
961                        drop(static_resources);
962                        Ok(PlanRuntimeCloseOutcome::Closed(PlanRuntimeCloseReceipt {
963                            evidence,
964                            released_static_resources: total_static_resources,
965                        }))
966                    }
967                    Err(failure) => Err(PlanRuntimeCloseFailure {
968                        failure: failure.into_failure(),
969                        evidence,
970                        static_resources: Some(static_resources),
971                    }),
972                }
973            }
974        }
975    }
976}
977
978impl<R> TrustedPlanRuntimeBinding<R>
979where
980    R: DeviceRuntime,
981{
982    pub(super) fn runtime(&self) -> &Arc<R> {
983        &self.resources.runtime
984    }
985
986    pub(super) fn logical_admission(&self) -> &LogicalAdmissionCoordinator {
987        &self.dynamic_pools().logical_admission
988    }
989
990    pub(super) fn dynamic_pools(&self) -> &Arc<DynamicPoolSet<R>> {
991        &self.resources.dynamic_pools
992    }
993
994    pub(super) fn nodes(&self) -> &[PlanNode] {
995        &self.dynamic_pools().nodes
996    }
997
998    pub(super) fn reusable_execution_bucket(
999        &self,
1000        bucket_id: &ReusableExecutionBucketId,
1001    ) -> Option<&ResolvedReusableExecutionBucket> {
1002        self.dynamic_pools()
1003            .reusable_execution
1004            .as_ref()
1005            .and_then(|plan| plan.bucket(bucket_id))
1006    }
1007
1008    pub fn plan_id(&self) -> &PlanId {
1009        match &self.resources.static_resources {
1010            PlanRuntimeStatic::NoStatic { binding } => binding.plan_id(),
1011            PlanRuntimeStatic::Static(source) => source.admission.plan_id(),
1012        }
1013    }
1014
1015    pub fn plan_hash(&self) -> &PlanHash {
1016        match &self.resources.static_resources {
1017            PlanRuntimeStatic::NoStatic { binding } => binding.plan_hash(),
1018            PlanRuntimeStatic::Static(source) => source.admission.plan_hash(),
1019        }
1020    }
1021
1022    pub fn device_id(&self) -> &DeviceId {
1023        match &self.resources.static_resources {
1024            PlanRuntimeStatic::NoStatic { binding } => binding.device_id(),
1025            PlanRuntimeStatic::Static(source) => source.admission.device_id(),
1026        }
1027    }
1028
1029    pub fn runtime_implementation_fingerprint(&self) -> &str {
1030        match &self.resources.static_resources {
1031            PlanRuntimeStatic::NoStatic { binding } => {
1032                binding.device_runtime_implementation_fingerprint()
1033            }
1034            PlanRuntimeStatic::Static(source) => {
1035                source.admission.device_runtime_implementation_fingerprint()
1036            }
1037        }
1038    }
1039
1040    pub fn coordinator_id(&self) -> LogicalAdmissionCoordinatorId {
1041        self.logical_admission().id()
1042    }
1043
1044    pub fn static_provisioning(&self) -> Option<&StaticProvisioningLease<R>> {
1045        match &self.resources.static_resources {
1046            PlanRuntimeStatic::NoStatic { .. } => None,
1047            PlanRuntimeStatic::Static(source) => source.lease.as_ref(),
1048        }
1049    }
1050
1051    pub fn evidence(&self) -> TrustedPlanRuntimeEvidence {
1052        self.resources.evidence()
1053    }
1054
1055    pub(super) fn scoped_demand(
1056        &self,
1057        lifetime: AllocationLifetime,
1058        node_id: Option<&NodeId>,
1059        immediate_shape: DynamicResourceShape,
1060        fit_shape: DynamicResourceShape,
1061        reusable_execution_bucket: Option<&ReusableExecutionBucketSpec>,
1062        fit_policy: AdmissionFitPolicy,
1063        pressure_action: AdmissionPressureAction,
1064    ) -> Result<(AdmissionDemand, Vec<EvaluatedBackingRequest<'_>>), VNextError> {
1065        if (lifetime == AllocationLifetime::Invocation) != node_id.is_some() {
1066            return Err(invalid_resource(
1067                "invocation resource demand requires one exact node identity",
1068            ));
1069        }
1070        let capacity_shape = match reusable_execution_bucket {
1071            Some(bucket)
1072                if matches!(
1073                    lifetime,
1074                    AllocationLifetime::Step | AllocationLifetime::Invocation
1075                ) && bucket.capacity().covers(
1076                    immediate_shape.sequences(),
1077                    immediate_shape.tokens(),
1078                    immediate_shape.pages(),
1079                ) && bucket.capacity().covers(
1080                    fit_shape.sequences(),
1081                    fit_shape.tokens(),
1082                    fit_shape.pages(),
1083                ) =>
1084            {
1085                DynamicResourceShape::from_validated(
1086                    bucket.capacity().maximum_sequences(),
1087                    bucket.capacity().maximum_tokens(),
1088                    bucket.capacity().maximum_pages(),
1089                )
1090            }
1091            Some(_) => {
1092                return Err(invalid_resource(
1093                    "reusable execution bucket does not cover this Step or Invocation demand",
1094                ));
1095            }
1096            None => immediate_shape,
1097        };
1098        let node_resources = node_id
1099            .map(|node_id| {
1100                self.nodes()
1101                    .iter()
1102                    .find(|node| node.id() == node_id)
1103                    .map(PlanNode::resources)
1104                    .ok_or_else(|| {
1105                        invalid_resource("resource admission references an unknown node")
1106                    })
1107            })
1108            .transpose()?;
1109        let mut immediate_entries = Vec::new();
1110        let mut fit_entries = Vec::new();
1111        let mut requested_slices = Vec::new();
1112        for domain in &self.dynamic_pools().domains {
1113            let mut immediate_pool_bytes = 0_u64;
1114            let mut fit_pool_bytes = 0_u64;
1115            let mut matched = false;
1116            if lifetime == AllocationLifetime::Step {
1117                for slot in domain.pool.step_resource_slots() {
1118                    let mut projections = Vec::with_capacity(slot.resource_ids().len());
1119                    let mut immediate_slot_bytes = 0_u64;
1120                    let mut fit_slot_bytes = 0_u64;
1121                    let mut capacity_slot_bytes = 0_u64;
1122                    for resource_id in slot.resource_ids() {
1123                        let descriptor = domain
1124                            .descriptors
1125                            .iter()
1126                            .find(|descriptor| descriptor.base_resource_id() == resource_id)
1127                            .ok_or_else(|| {
1128                                invalid_resource(
1129                                    "step physical slot references a descriptor outside its pool",
1130                                )
1131                            })?;
1132                        if descriptor.lifetime() != AllocationLifetime::Step {
1133                            return Err(invalid_resource(
1134                                "step physical slot references a non-Step descriptor",
1135                            ));
1136                        }
1137                        let logical_size_bytes =
1138                            descriptor.evaluate_request_bytes_for_shape(immediate_shape)?;
1139                        let fit_bytes = descriptor.evaluate_request_bytes_for_shape(fit_shape)?;
1140                        let capacity_size_bytes =
1141                            descriptor.evaluate_request_bytes_for_shape(capacity_shape)?;
1142                        immediate_slot_bytes = immediate_slot_bytes.max(logical_size_bytes);
1143                        fit_slot_bytes = fit_slot_bytes.max(fit_bytes);
1144                        capacity_slot_bytes = capacity_slot_bytes.max(capacity_size_bytes);
1145                        projections.push(EvaluatedBackingProjection {
1146                            descriptor,
1147                            physical_offset_bytes: 0,
1148                            logical_size_bytes,
1149                            capacity_size_bytes,
1150                        });
1151                    }
1152                    immediate_pool_bytes = immediate_pool_bytes
1153                        .checked_add(immediate_slot_bytes)
1154                        .ok_or_else(|| {
1155                        invalid_resource("dynamic pool immediate demand overflows u64")
1156                    })?;
1157                    fit_pool_bytes = fit_pool_bytes
1158                        .checked_add(fit_slot_bytes)
1159                        .ok_or_else(|| invalid_resource("dynamic pool fit demand overflows u64"))?;
1160                    requested_slices.push(EvaluatedBackingRequest {
1161                        domain,
1162                        claim_identity: PhysicalBackingClaimIdentity::new(
1163                            domain.pool_id().clone(),
1164                            slot.resource_ids().to_vec(),
1165                        )?,
1166                        capacity_size_bytes: capacity_slot_bytes,
1167                        reusable_execution_bucket_id: reusable_execution_bucket
1168                            .map(|bucket| bucket.bucket_id().clone()),
1169                        projections,
1170                    });
1171                    matched = true;
1172                }
1173            } else {
1174                for descriptor in &domain.descriptors {
1175                    if descriptor.lifetime() != lifetime
1176                        || node_resources.is_some_and(|resources| {
1177                            !resources.contains(descriptor.base_resource_id())
1178                        })
1179                    {
1180                        continue;
1181                    }
1182                    matched = true;
1183                    let logical_size_bytes =
1184                        descriptor.evaluate_request_bytes_for_shape(immediate_shape)?;
1185                    let fit_bytes = descriptor.evaluate_request_bytes_for_shape(fit_shape)?;
1186                    let capacity_size_bytes =
1187                        descriptor.evaluate_request_bytes_for_shape(capacity_shape)?;
1188                    immediate_pool_bytes = immediate_pool_bytes
1189                        .checked_add(logical_size_bytes)
1190                        .ok_or_else(|| {
1191                            invalid_resource("dynamic pool immediate demand overflows u64")
1192                        })?;
1193                    fit_pool_bytes = fit_pool_bytes
1194                        .checked_add(fit_bytes)
1195                        .ok_or_else(|| invalid_resource("dynamic pool fit demand overflows u64"))?;
1196                    requested_slices.push(EvaluatedBackingRequest {
1197                        domain,
1198                        claim_identity: PhysicalBackingClaimIdentity::new(
1199                            domain.pool_id().clone(),
1200                            vec![descriptor.base_resource_id().clone()],
1201                        )?,
1202                        capacity_size_bytes,
1203                        reusable_execution_bucket_id: reusable_execution_bucket
1204                            .map(|bucket| bucket.bucket_id().clone()),
1205                        projections: vec![EvaluatedBackingProjection {
1206                            descriptor,
1207                            physical_offset_bytes: 0,
1208                            logical_size_bytes,
1209                            capacity_size_bytes,
1210                        }],
1211                    });
1212                }
1213            }
1214            if matched {
1215                immediate_entries.push(CapacityEntry::new(
1216                    domain.domain_id(),
1217                    CapacityUnits::new(immediate_pool_bytes),
1218                )?);
1219                fit_entries.push(CapacityEntry::new(
1220                    domain.domain_id(),
1221                    CapacityUnits::new(fit_pool_bytes),
1222                )?);
1223            }
1224        }
1225        let immediate = if immediate_entries.is_empty() {
1226            CapacityVector::empty()
1227        } else {
1228            CapacityVector::new(immediate_entries)?
1229        };
1230        let fit = if fit_entries.is_empty() {
1231            CapacityVector::empty()
1232        } else {
1233            CapacityVector::new(fit_entries)?
1234        };
1235        Ok((
1236            AdmissionDemand::from_plan(immediate, fit, fit_policy, pressure_action)?,
1237            requested_slices,
1238        ))
1239    }
1240
1241    /// Derives the exact additional physical/logical claim needed to advance
1242    /// one sequence's committed frontier. Existing extents remain owned by the
1243    /// prior snapshot; only paged storage can append disjoint extents.
1244    pub(super) fn sequence_extension_demand(
1245        &self,
1246        committed: DynamicResourceShape,
1247        target: DynamicResourceShape,
1248        pressure_action: AdmissionPressureAction,
1249    ) -> Result<(AdmissionDemand, Vec<EvaluatedBackingRequest<'_>>), VNextError> {
1250        if committed.sequences() != 1
1251            || target.sequences() != 1
1252            || target.tokens() < committed.tokens()
1253            || target.pages() < committed.pages()
1254        {
1255            return Err(invalid_resource(
1256                "sequence extension target must monotonically advance one committed sequence",
1257            ));
1258        }
1259
1260        let mut entries = Vec::new();
1261        let mut requested_slices = Vec::new();
1262        for domain in &self.dynamic_pools().domains {
1263            let mut pool_delta = 0_u64;
1264            for descriptor in &domain.descriptors {
1265                if descriptor.lifetime() != AllocationLifetime::Sequence {
1266                    continue;
1267                }
1268                let committed_bytes = descriptor.evaluate_request_bytes_for_shape(committed)?;
1269                let target_bytes = descriptor.evaluate_request_bytes_for_shape(target)?;
1270                let delta_bytes = target_bytes.checked_sub(committed_bytes).ok_or_else(|| {
1271                    invalid_resource("sequence extension descriptor capacity regressed")
1272                })?;
1273                if delta_bytes == 0 {
1274                    continue;
1275                }
1276                if !matches!(
1277                    descriptor.storage().profile().view(),
1278                    super::DynamicStorageView::PagedRegions { .. }
1279                ) {
1280                    return Err(invalid_resource(
1281                        "sequence backing extension requires a paged storage profile",
1282                    ));
1283                }
1284                pool_delta = pool_delta.checked_add(delta_bytes).ok_or_else(|| {
1285                    invalid_resource("sequence extension pool demand overflows u64")
1286                })?;
1287                requested_slices.push(EvaluatedBackingRequest {
1288                    domain,
1289                    claim_identity: PhysicalBackingClaimIdentity::new(
1290                        domain.pool_id().clone(),
1291                        vec![descriptor.base_resource_id().clone()],
1292                    )?,
1293                    capacity_size_bytes: delta_bytes,
1294                    reusable_execution_bucket_id: None,
1295                    projections: vec![EvaluatedBackingProjection {
1296                        descriptor,
1297                        physical_offset_bytes: 0,
1298                        logical_size_bytes: delta_bytes,
1299                        capacity_size_bytes: delta_bytes,
1300                    }],
1301                });
1302            }
1303            if pool_delta != 0 {
1304                entries.push(CapacityEntry::new(
1305                    domain.domain_id(),
1306                    CapacityUnits::new(pool_delta),
1307                )?);
1308            }
1309        }
1310        let delta = if entries.is_empty() {
1311            CapacityVector::empty()
1312        } else {
1313            CapacityVector::new(entries)?
1314        };
1315        Ok((
1316            AdmissionDemand::from_plan(
1317                delta.clone(),
1318                delta,
1319                AdmissionFitPolicy::ImmediateOnly,
1320                pressure_action,
1321            )?,
1322            requested_slices,
1323        ))
1324    }
1325
1326    /// Evaluates all Invocation-scoped resources for one immutable-plan
1327    /// submission wave. A total-order pool reuses one physical extent across
1328    /// node rows, while a conservative pool retains disjoint row ranges.
1329    pub(super) fn submission_wave_demand(
1330        &self,
1331        immediate_shape: DynamicResourceShape,
1332        fit_shape: DynamicResourceShape,
1333        reusable_execution_bucket: Option<&ReusableExecutionBucketSpec>,
1334        fit_policy: AdmissionFitPolicy,
1335        pressure_action: AdmissionPressureAction,
1336    ) -> Result<(AdmissionDemand, Vec<EvaluatedBackingRequest<'_>>), VNextError> {
1337        match reusable_execution_bucket {
1338            Some(bucket)
1339                if bucket.capacity().covers(
1340                    immediate_shape.sequences(),
1341                    immediate_shape.tokens(),
1342                    immediate_shape.pages(),
1343                ) && bucket.capacity().covers(
1344                    fit_shape.sequences(),
1345                    fit_shape.tokens(),
1346                    fit_shape.pages(),
1347                ) =>
1348            {
1349                // Physical reusable capacity is compiled once with the immutable
1350                // plan. Per-wave logical and fit demand remain dynamic below.
1351            }
1352            Some(_) => {
1353                return Err(invalid_resource(
1354                    "reusable execution bucket does not cover the submission-wave work shape",
1355                ));
1356            }
1357            None => {}
1358        }
1359
1360        let mut immediate_entries = Vec::new();
1361        let mut fit_entries = Vec::new();
1362        let mut requested_slices = Vec::new();
1363        let pools = self.dynamic_pools();
1364        if pools.domains.len() != pools.submission_wave_layouts.len() {
1365            return Err(invalid_resource(
1366                "submission wave layout count differs from immutable plan domains",
1367            ));
1368        }
1369        let reusable_capacity_layouts = reusable_execution_bucket
1370            .map(|bucket| {
1371                pools
1372                    .submission_wave_reusable_capacity_layouts
1373                    .get(bucket.bucket_id())
1374                    .ok_or_else(|| {
1375                        invalid_resource(
1376                            "reusable execution bucket has no compiled submission-wave capacity layout",
1377                        )
1378                    })
1379            })
1380            .transpose()?;
1381        if reusable_capacity_layouts.is_some_and(|layouts| layouts.len() != pools.domains.len()) {
1382            return Err(invalid_resource(
1383                "compiled reusable submission-wave capacity layout count differs from immutable plan domains",
1384            ));
1385        }
1386        for (domain_index, (domain, layout)) in pools
1387            .domains
1388            .iter()
1389            .zip(&pools.submission_wave_layouts)
1390            .enumerate()
1391        {
1392            let reusable_capacity_layout = reusable_capacity_layouts
1393                .map(|layouts| {
1394                    layouts.get(domain_index).ok_or_else(|| {
1395                        invalid_resource(
1396                            "compiled reusable submission-wave capacity layout is incomplete",
1397                        )
1398                    })
1399                })
1400                .transpose()?
1401                .and_then(Option::as_ref);
1402            let Some(layout) = layout else {
1403                if reusable_capacity_layout.is_some() {
1404                    return Err(invalid_resource(
1405                        "compiled reusable submission-wave capacity exists without a domain layout",
1406                    ));
1407                }
1408                continue;
1409            };
1410            if reusable_execution_bucket.is_some() && reusable_capacity_layout.is_none() {
1411                return Err(invalid_resource(
1412                    "compiled reusable submission-wave capacity is missing for a domain layout",
1413                ));
1414            }
1415            if reusable_capacity_layout
1416                .is_some_and(|capacity| capacity.projections.len() != layout.projection_count)
1417            {
1418                return Err(invalid_resource(
1419                    "compiled reusable submission-wave projection count differs from its domain layout",
1420                ));
1421            }
1422            let mode = domain.pool.invocation_liveness_mode();
1423
1424            let mut projections = vec![None; layout.projection_count];
1425            let mut immediate_pool_bytes = 0_u64;
1426            let mut fit_pool_bytes = 0_u64;
1427            let mut capacity_pool_bytes = 0_u64;
1428            for row in &layout.rows {
1429                let row_base = match mode {
1430                    InvocationLivenessMode::TotalOrderReuse => 0,
1431                    InvocationLivenessMode::ConservativeConcurrent => capacity_pool_bytes,
1432                    InvocationLivenessMode::NoInvocationResources => unreachable!(),
1433                };
1434                let mut immediate_row_bytes = 0_u64;
1435                let mut fit_row_bytes = 0_u64;
1436                let mut capacity_row_bytes = 0_u64;
1437                for projection_layout in &row.projections {
1438                    let descriptor = domain
1439                        .descriptors
1440                        .get(projection_layout.descriptor_index)
1441                        .ok_or_else(|| {
1442                            invalid_resource(
1443                                "submission wave layout references a descriptor outside its pool",
1444                            )
1445                        })?;
1446                    if descriptor.lifetime() != AllocationLifetime::Invocation {
1447                        return Err(invalid_resource(
1448                            "invocation liveness row references a non-Invocation descriptor",
1449                        ));
1450                    }
1451                    let logical_size_bytes =
1452                        descriptor.evaluate_request_bytes_for_shape(immediate_shape)?;
1453                    let fit_bytes = if fit_shape == immediate_shape {
1454                        logical_size_bytes
1455                    } else {
1456                        descriptor.evaluate_request_bytes_for_shape(fit_shape)?
1457                    };
1458                    let (physical_offset_bytes, capacity_size_bytes) =
1459                        match reusable_capacity_layout {
1460                            Some(capacity_layout) => {
1461                                let capacity_projection = capacity_layout
1462                                    .projections
1463                                    .get(projection_layout.projection_index)
1464                                    .ok_or_else(|| {
1465                                        invalid_resource(
1466                                            "compiled reusable submission-wave projection is missing",
1467                                        )
1468                                    })?;
1469                                (
1470                                    capacity_projection.physical_offset_bytes,
1471                                    capacity_projection.capacity_size_bytes,
1472                                )
1473                            }
1474                            None => {
1475                                let physical_offset_bytes =
1476                                    row_base.checked_add(capacity_row_bytes).ok_or_else(|| {
1477                                        invalid_resource(
1478                                            "invocation wave projection offset overflows u64",
1479                                        )
1480                                    })?;
1481                                (physical_offset_bytes, logical_size_bytes)
1482                            }
1483                        };
1484                    immediate_row_bytes = immediate_row_bytes
1485                        .checked_add(logical_size_bytes)
1486                        .ok_or_else(|| {
1487                            invalid_resource("invocation wave row demand overflows u64")
1488                        })?;
1489                    fit_row_bytes = fit_row_bytes.checked_add(fit_bytes).ok_or_else(|| {
1490                        invalid_resource("invocation wave fit row demand overflows u64")
1491                    })?;
1492                    if reusable_capacity_layout.is_none() {
1493                        capacity_row_bytes = capacity_row_bytes
1494                            .checked_add(capacity_size_bytes)
1495                            .ok_or_else(|| {
1496                                invalid_resource("invocation wave capacity row overflows u64")
1497                            })?;
1498                    }
1499                    if projections[projection_layout.projection_index]
1500                        .replace(EvaluatedBackingProjection {
1501                            descriptor,
1502                            physical_offset_bytes,
1503                            logical_size_bytes,
1504                            capacity_size_bytes,
1505                        })
1506                        .is_some()
1507                    {
1508                        return Err(invalid_resource(
1509                            "submission wave layout repeated a canonical projection",
1510                        ));
1511                    }
1512                }
1513                match mode {
1514                    InvocationLivenessMode::TotalOrderReuse => {
1515                        immediate_pool_bytes = immediate_pool_bytes.max(immediate_row_bytes);
1516                        fit_pool_bytes = fit_pool_bytes.max(fit_row_bytes);
1517                        if reusable_capacity_layout.is_none() {
1518                            capacity_pool_bytes = capacity_pool_bytes.max(capacity_row_bytes);
1519                        }
1520                    }
1521                    InvocationLivenessMode::ConservativeConcurrent => {
1522                        immediate_pool_bytes = immediate_pool_bytes
1523                            .checked_add(immediate_row_bytes)
1524                            .ok_or_else(|| {
1525                                invalid_resource("invocation wave pool demand overflows u64")
1526                            })?;
1527                        fit_pool_bytes =
1528                            fit_pool_bytes.checked_add(fit_row_bytes).ok_or_else(|| {
1529                                invalid_resource("invocation wave pool fit demand overflows u64")
1530                            })?;
1531                        if reusable_capacity_layout.is_none() {
1532                            capacity_pool_bytes = capacity_pool_bytes
1533                                .checked_add(capacity_row_bytes)
1534                                .ok_or_else(|| {
1535                                    invalid_resource(
1536                                        "invocation wave capacity pool demand overflows u64",
1537                                    )
1538                                })?;
1539                        }
1540                    }
1541                    InvocationLivenessMode::NoInvocationResources => unreachable!(),
1542                }
1543            }
1544            if let Some(capacity_layout) = reusable_capacity_layout {
1545                capacity_pool_bytes = capacity_layout.physical_size_bytes;
1546            }
1547            let projections = projections
1548                .into_iter()
1549                .collect::<Option<Vec<_>>>()
1550                .ok_or_else(|| {
1551                    invalid_resource("submission wave layout left a projection unevaluated")
1552                })?;
1553            if projections.is_empty() || immediate_pool_bytes == 0 {
1554                return Err(invalid_resource(
1555                    "submission wave layout evaluated to empty invocation demand",
1556                ));
1557            }
1558
1559            requested_slices.push(EvaluatedBackingRequest {
1560                domain,
1561                claim_identity: layout.claim_identity.clone(),
1562                capacity_size_bytes: capacity_pool_bytes,
1563                reusable_execution_bucket_id: reusable_execution_bucket
1564                    .map(|bucket| bucket.bucket_id().clone()),
1565                projections,
1566            });
1567            immediate_entries.push(CapacityEntry::new(
1568                domain.domain_id(),
1569                CapacityUnits::new(immediate_pool_bytes),
1570            )?);
1571            fit_entries.push(CapacityEntry::new(
1572                domain.domain_id(),
1573                CapacityUnits::new(fit_pool_bytes),
1574            )?);
1575        }
1576
1577        let immediate = if immediate_entries.is_empty() {
1578            CapacityVector::empty()
1579        } else {
1580            CapacityVector::new(immediate_entries)?
1581        };
1582        let fit = if fit_entries.is_empty() {
1583            CapacityVector::empty()
1584        } else {
1585            CapacityVector::new(fit_entries)?
1586        };
1587        Ok((
1588            AdmissionDemand::from_plan(immediate, fit, fit_policy, pressure_action)?,
1589            requested_slices,
1590        ))
1591    }
1592
1593    pub(super) fn prepare_backing_slices(
1594        &self,
1595        requested_slices: Vec<EvaluatedBackingRequest<'_>>,
1596    ) -> Result<BackingPrepareDecision<R>, VNextError> {
1597        self.dynamic_pools().prepare_claim(&requested_slices)
1598    }
1599
1600    pub(super) fn prepare_lane_stable_backing_slices(
1601        &self,
1602        lane: &Arc<ExecutionLane<R>>,
1603        requested_slices: Vec<EvaluatedBackingRequest<'_>>,
1604    ) -> Result<LaneBackingPrepareDecision, VNextError> {
1605        self.dynamic_pools()
1606            .prepare_lane_stable_claim(lane, &requested_slices)
1607    }
1608
1609    pub(super) fn prepare_initial_sequence_backing_slices(
1610        &self,
1611        requested_slices: &[EvaluatedBackingRequest<'_>],
1612    ) -> Result<BackingPrepareDecision<R>, VNextError> {
1613        self.dynamic_pools()
1614            .prepare_initial_sequence_claim(requested_slices)
1615    }
1616
1617    pub(super) fn register_backing_waiter(
1618        &self,
1619        deferred: &DynamicBackingDeferred,
1620    ) -> Result<PlanCapacityWaitRegistration<R>, VNextError> {
1621        self.resources
1622            .register_capacity_waiter(deferred.wait_condition())
1623    }
1624
1625    pub fn register_admission_waiter(
1626        &self,
1627        deferred: &AdmissionDeferred,
1628    ) -> Result<PlanCapacityWaitRegistration<R>, VNextError> {
1629        self.resources
1630            .register_capacity_waiter(deferred.wait_condition())
1631    }
1632}