Skip to main content

behavior/actor/
creation.rs

1//! Staged fresh-actor creation capabilities.
2
3use core::future::Future;
4use core::marker::PhantomData;
5use core::num::NonZeroU64;
6
7use super::addressing::{Address, EndpointAddress, EstablishedActor, EstablishedRecipient};
8use crate::next::Never;
9use crate::{
10    ActionItem, Actions, Behavior, BehaviorAddr, BehaviorBase, InterpretationProgress,
11    ItemSettlement, Protocol, SettledItem, finish_item, prepare_item,
12};
13
14/// Correlation for one staged creation within a static child occurrence.
15///
16/// This value is neither an actor address nor a runtime route. Its constructor
17/// is private so only a [`CreationSequence`] can issue it.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub struct CreationId(NonZeroU64);
20
21impl CreationId {
22    /// Return the occurrence-local numeric value for protocols that derive
23    /// another correlation from this creation.
24    ///
25    /// This number is neither an actor identity nor evidence that the runtime
26    /// established a fresh child. Only a committed creation settlement proves
27    /// establishment.
28    #[must_use]
29    pub const fn get(self) -> u64 {
30        self.0.get()
31    }
32}
33
34/// Checked source of IDs that are not reused within one child occurrence.
35#[derive(Debug, PartialEq, Eq)]
36pub struct CreationSequence {
37    next: Option<NonZeroU64>,
38}
39
40impl CreationSequence {
41    /// Begin a fresh sequence for one child occurrence.
42    #[must_use]
43    pub const fn new() -> Self {
44        Self {
45            next: Some(NonZeroU64::MIN),
46        }
47    }
48
49    /// Issue the next ID, or return absence after the finite sequence is
50    /// exhausted. Exhaustion leaves the sequence unchanged.
51    pub fn issue(&mut self) -> Option<CreationId> {
52        let value = self.next?;
53        self.next = value.checked_add(1);
54        Some(CreationId(value))
55    }
56}
57
58impl Default for CreationSequence {
59    fn default() -> Self {
60        Self::new()
61    }
62}
63
64/// Behavior-owned provenance for a staged fresh actor creation request.
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum CreationKind {
67    /// An initial or ordinary later birth.
68    Birth,
69    /// A fresh successor requested by a replacement protocol.
70    Replacement {
71        /// Creator-local ID of the exact child this request supersedes.
72        previous: CreationId,
73    },
74}
75
76impl CreationKind {
77    #[must_use]
78    pub const fn replacement(previous: CreationId) -> Self {
79        Self::Replacement { previous }
80    }
81}
82
83/// A staged request to establish one fresh child.
84///
85/// The ID is correlation within the request's static child occurrence, not an
86/// address, route, actor identity, or proof of freshness. The kind is
87/// Behavior-owned intent. Replacement at an existing address is deliberately
88/// absent; stable identity is derived with a proxy actor.
89///
90/// ```compile_fail
91/// let mut creations = behavior::CreationSequence::new();
92/// let id = creations.issue().expect("the first child ID exists");
93/// let _ = behavior::CreateChild::<behavior::MailAddr, ()>::new(
94///     id,
95///     (),
96///     behavior::CreationKind::Birth,
97/// );
98/// ```
99#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct CreateChild<A: Address, New> {
101    id: CreationId,
102    child: New,
103    kind: CreationKind,
104    address: PhantomData<fn() -> A>,
105}
106
107impl<A: Address, New> CreateChild<A, New> {
108    #[must_use]
109    pub(crate) const fn from_parts(id: CreationId, child: New, kind: CreationKind) -> Self {
110        Self {
111            id,
112            child,
113            kind,
114            address: PhantomData,
115        }
116    }
117
118    #[must_use]
119    pub const fn birth(id: CreationId, child: New) -> Self {
120        Self::from_parts(id, child, CreationKind::Birth)
121    }
122
123    #[must_use]
124    pub const fn replacement(id: CreationId, previous: CreationId, child: New) -> Self {
125        Self::from_parts(id, child, CreationKind::replacement(previous))
126    }
127
128    #[must_use]
129    pub const fn id(&self) -> CreationId {
130        self.id
131    }
132
133    #[must_use]
134    pub const fn kind(&self) -> CreationKind {
135        self.kind
136    }
137
138    #[must_use]
139    pub const fn child(&self) -> &New {
140        &self.child
141    }
142
143    /// Consume the staged request, returning its exact correlation, child,
144    /// and creation provenance to the interpreter.
145    ///
146    /// Taking these parts does not establish a child actor. The interpreter
147    /// remains responsible for fresh creation and for returning owned work on
148    /// rejection.
149    #[must_use]
150    pub fn into_parts(self) -> (CreationId, New, CreationKind) {
151        (self.id, self.child, self.kind)
152    }
153}
154
155/// One ordered creation batch.
156///
157/// The batch is the all-or-none unit for runtime route preparation. After
158/// routing succeeds, each child still settles independently in this declared
159/// order.
160#[derive(Debug, Clone, PartialEq, Eq)]
161pub struct Creations<Item> {
162    items: Vec<Item>,
163}
164
165impl<Item> Creations<Item> {
166    /// Construct an empty creation batch.
167    #[must_use]
168    pub const fn empty() -> Self {
169        Self { items: Vec::new() }
170    }
171
172    #[must_use]
173    pub fn one(item: Item) -> Self {
174        Self { items: vec![item] }
175    }
176
177    /// Consume a batch containing exactly one item.
178    ///
179    /// # Errors
180    /// Returns the complete ordered batch when it is empty or contains more
181    /// than one item.
182    pub fn into_one(self) -> Result<Item, Self> {
183        let one: Result<[Item; 1], Vec<Item>> = self.items.try_into();
184        match one {
185            Ok([item]) => Ok(item),
186            Err(items) => Err(Self { items }),
187        }
188    }
189
190    #[must_use]
191    pub fn and(mut self, item: Item) -> Self {
192        self.items.push(item);
193        self
194    }
195
196    /// Transform every item while preserving declared order and cardinality.
197    #[must_use]
198    pub fn map<Mapped>(self, mut map: impl FnMut(Item) -> Mapped) -> Creations<Mapped> {
199        Creations::from_items(self.items.into_iter().map(&mut map).collect())
200    }
201
202    #[must_use]
203    pub fn len(&self) -> usize {
204        self.items.len()
205    }
206
207    #[must_use]
208    pub fn is_empty(&self) -> bool {
209        self.items.is_empty()
210    }
211
212    pub(crate) fn from_items(items: Vec<Item>) -> Self {
213        Self { items }
214    }
215
216    pub fn iter(&self) -> core::slice::Iter<'_, Item> {
217        self.items.iter()
218    }
219}
220
221impl<Item> Default for Creations<Item> {
222    fn default() -> Self {
223        Self::empty()
224    }
225}
226
227impl<Item> Extend<Item> for Creations<Item> {
228    fn extend<Items: IntoIterator<Item = Item>>(&mut self, items: Items) {
229        self.items.extend(items);
230    }
231}
232
233impl<Item> FromIterator<Item> for Creations<Item> {
234    fn from_iter<Items: IntoIterator<Item = Item>>(items: Items) -> Self {
235        Self::from_items(items.into_iter().collect())
236    }
237}
238
239impl<'a, Item> IntoIterator for &'a Creations<Item> {
240    type Item = &'a Item;
241    type IntoIter = core::slice::Iter<'a, Item>;
242
243    fn into_iter(self) -> Self::IntoIter {
244        self.iter()
245    }
246}
247
248impl<Item> IntoIterator for Creations<Item> {
249    type Item = Item;
250    type IntoIter = std::vec::IntoIter<Item>;
251
252    fn into_iter(self) -> Self::IntoIter {
253        self.items.into_iter()
254    }
255}
256
257/// Static proof that `Role` names one exact direct child of `Parent`.
258///
259/// Behavior authoring owns this relationship. A runtime may use the proof to
260/// build application topology, but the role itself allocates nothing and is
261/// not evidence that the child was created or installed.
262pub trait ChildRole<Parent: Behavior> {
263    /// The only child behavior accepted at this role.
264    type Child: Behavior;
265
266    /// Structural position of this role in `Parent`'s closed child sum.
267    type Position: ChildPosition<<Parent::Birth as BirthMode>::Child, Self::Child>;
268}
269
270/// Declares how one effect occurrence is resolved from an authored parent.
271///
272/// This is topology metadata, not actor identity or a runtime capability.
273/// Generated nominal roles implement it with their declared parent, child,
274/// and structural position. [`ChildHead`] and [`ChildTail`] implement it as
275/// raw structural positions. Consumers normally use
276/// [`ResolveChildOccurrence`] rather than inspecting `Resolution`.
277///
278/// Manually authored roles may implement this trait as the power-user path by
279/// selecting [`DeclaredChildOccurrence`] with the same relationship expressed
280/// by their [`ChildRole`] implementation. The actual resolution contract is
281/// sealed, so downstream code cannot redefine wrapper transparency or replace
282/// structural resolution with a runtime lookup.
283pub trait ChildOccurrence<Parent: Behavior>: Sized {
284    /// Sealed descriptor interpreted by [`ResolveChildOccurrence`].
285    type Resolution: ChildOccurrenceResolution<Parent, Self>;
286}
287
288/// Sealed descriptor for one nominal child occurrence declared by `Parent`.
289///
290/// This type exists so generated and manually authored roles can carry their
291/// static declaration into the sealed resolver. It has no values or runtime
292/// behavior.
293pub struct DeclaredChildOccurrence;
294
295/// Sealed descriptor for a raw structural child position.
296///
297/// This type has no values or runtime behavior.
298#[doc(hidden)]
299pub struct StructuralChildOccurrence<Position>(PhantomData<fn() -> Position>);
300
301/// Resolve an effect's nominal or structural occurrence against the concrete
302/// behavior currently being interpreted.
303///
304/// This is a sealed, type-level derived construction. It performs no lookup,
305/// allocates no actor, and introduces no second identity: the resolved child's
306/// [`Behavior::Protocol`] remains canonical identity, while `Position` is only
307/// navigation evidence into the emitter's direct birth algebra.
308///
309/// A nominal role follows [`BehaviorBase`] through a wrapper only when the
310/// wrapper preserves the exact protocol and the role's declared child at its
311/// exact structural position. A wrapper may append births after that position,
312/// but it cannot replace, reorder, or insert births before it. Raw
313/// [`ChildHead`] and [`ChildTail`] positions instead resolve directly against
314/// the running emitter's own birth algebra.
315///
316/// A wrapper that replaces its base role's child cannot silently reuse that
317/// role:
318///
319/// ```compile_fail
320///
321/// struct ActorProtocol;
322/// impl behavior::Protocol for ActorProtocol {
323///     type Addr = behavior::MailAddr;
324///     type Msg = behavior::Never;
325/// }
326///
327/// macro_rules! inert {
328///     ($actor:ident, $birth:ty) => {
329///         struct $actor;
330///         impl behavior::Behavior for $actor {
331///             type Protocol = ActorProtocol;
332///             type Event = behavior::Never;
333///             type Sends = behavior::NoSends;
334///             type Ph = behavior::Never;
335///             type Error = behavior::Never;
336///             type Birth = $birth;
337///             fn transition(
338///                 &mut self,
339///                 _: behavior::ActiveTurn,
340///                 event: behavior::Never,
341///             ) -> behavior::BehaviorActed<Self> {
342///                 match event {}
343///             }
344///         }
345///     };
346/// }
347/// inert!(Child, behavior::NoBirths);
348/// inert!(Proxy, behavior::NoBirths);
349/// inert!(Parent, behavior::Births<Child>);
350/// inert!(ChangedTopology, behavior::Births<Proxy>);
351///
352/// impl behavior::BehaviorBase for Parent {
353///     type Base = Self;
354///     fn base(&self) -> &Self { self }
355/// }
356/// impl behavior::BehaviorBase for ChangedTopology {
357///     type Base = Parent;
358///     fn base(&self) -> &Parent { unreachable!() }
359/// }
360///
361/// struct WorkerRole;
362/// impl behavior::ChildRole<Parent> for WorkerRole {
363///     type Child = Child;
364///     type Position = behavior::ChildHead;
365/// }
366/// impl behavior::ChildOccurrence<Parent> for WorkerRole {
367///     type Resolution = behavior::DeclaredChildOccurrence;
368/// }
369///
370/// fn require<T: behavior::ResolveChildOccurrence<WorkerRole>>() {}
371/// require::<ChangedTopology>();
372/// ```
373pub trait ResolveChildOccurrence<Occurrence>:
374    Behavior + sealed::ResolveChildOccurrence<Occurrence>
375{
376    /// Exact concrete child behavior at this occurrence.
377    type Child: Behavior;
378
379    /// Exact structural position in this emitter's direct birth algebra.
380    type Position: ChildPosition<<Self::Birth as BirthMode>::Child, Self::Child>;
381}
382
383impl<Emitter, Occurrence> ResolveChildOccurrence<Occurrence> for Emitter
384where
385    Emitter: Behavior + BehaviorBase + sealed::ResolveChildOccurrence<Occurrence>,
386    Emitter::Base: Behavior,
387    Occurrence: ChildOccurrence<Emitter::Base>,
388    Occurrence::Resolution: ResolveChildOccurrenceDescriptor<Emitter, Occurrence>,
389{
390    type Child =
391        <Occurrence::Resolution as ResolveChildOccurrenceDescriptor<Emitter, Occurrence>>::Child;
392    type Position =
393        <Occurrence::Resolution as ResolveChildOccurrenceDescriptor<Emitter, Occurrence>>::Position;
394}
395
396/// Child behavior resolved from `Occurrence` for the running `Emitter`.
397pub type ResolvedChild<Emitter, Occurrence> =
398    <Emitter as ResolveChildOccurrence<Occurrence>>::Child;
399
400/// Structural birth position resolved from `Occurrence` for the running
401/// `Emitter`.
402pub type ResolvedChildPosition<Emitter, Occurrence> =
403    <Emitter as ResolveChildOccurrence<Occurrence>>::Position;
404
405/// Child behavior selected by one named role.
406pub type RoleChild<Parent, Role> = <Role as ChildRole<Parent>>::Child;
407
408/// Canonical protocol selected by one named role.
409pub type RoleProtocol<Parent, Role> = <RoleChild<Parent, Role> as Behavior>::Protocol;
410
411/// One complete creation paired with a route selected by the interpreter.
412///
413/// Behavior never constructs or stores this value. It exists only between the
414/// generic batch-routing step and the concrete child host, and it returns
415/// complete on rejection or corruption.
416#[derive(Debug, Clone, PartialEq, Eq)]
417pub struct RoutedCreation<A: Address, New> {
418    creation: CreateChild<A, New>,
419    route: A::Nonce,
420}
421
422impl<A: Address, New> RoutedCreation<A, New> {
423    /// Pair one creation with the route selected by the current runtime.
424    #[must_use]
425    pub const fn new(creation: CreateChild<A, New>, route: A::Nonce) -> Self {
426        Self { creation, route }
427    }
428
429    #[must_use]
430    pub const fn id(&self) -> CreationId {
431        self.creation.id()
432    }
433
434    #[must_use]
435    pub const fn kind(&self) -> CreationKind {
436        self.creation.kind()
437    }
438
439    #[must_use]
440    pub const fn route(&self) -> A::Nonce {
441        self.route
442    }
443
444    /// Borrow the staged child for its pure initialization fold while the
445    /// complete routed request remains owned for a possible rejection.
446    #[must_use]
447    pub fn child_mut(&mut self) -> &mut New {
448        &mut self.creation.child
449    }
450
451    #[must_use]
452    pub fn into_parts(self) -> (CreateChild<A, New>, A::Nonce) {
453        (self.creation, self.route)
454    }
455}
456
457/// Failure to claim an address fresh with respect to the current actor
458/// configuration.
459#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
460pub enum AllocationRejection {
461    /// The allocator has no address it can presently claim.
462    #[error("fresh actor-address allocation is exhausted")]
463    Exhausted,
464    /// The proposed address was already claimed; accepting it would violate
465    /// actor-name freshness.
466    #[error("the proposed actor address is already claimed")]
467    AddressAlreadyClaimed,
468}
469
470/// Complete semantic rejection of one staged fresh creation.
471#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
472pub enum CreationRejection {
473    /// Fresh address allocation failed.
474    #[error("fresh allocation failed: {0}")]
475    Allocation(AllocationRejection),
476    /// The child's initialization fold did not complete successfully.
477    #[error("child initialization failed")]
478    InitializationFailed,
479    /// Installation or commit failed after allocation.
480    #[error("the interpreter could not install and commit the child")]
481    EnvironmentFailed,
482}
483
484/// The creator namespace cannot route an entire declared creation batch.
485#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
486#[error("the creator child namespace is exhausted")]
487pub struct ChildNamespaceExhausted;
488
489impl<A, New> ActionItem for Creations<CreateChild<A, New>>
490where
491    A: Address,
492    A::Nonce: Send,
493    New: Send,
494{
495    type Custody = (Option<Self>, Option<Self::Reply>);
496    type Input<'a>
497        = &'a mut Option<Self>
498    where
499        Self: 'a;
500    type Reply = ItemSettlement<Self, Self::Accepted, Self::Rejection, Self::Prerequisite>;
501
502    fn prepare_interpretation(
503        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
504    ) {
505        prepare_item::<Self>(progress);
506    }
507    fn interpretation_input<'a>(
508        custody: &'a mut Self::Custody,
509    ) -> Option<(Self::Input<'a>, &'a mut Option<Self::Reply>)>
510    where
511        Self: 'a,
512    {
513        let (input, received) = custody;
514        if input.is_some() && received.is_none() {
515            Some((input, received))
516        } else {
517            None
518        }
519    }
520    fn finish_interpretation(
521        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
522    ) {
523        finish_item::<Self>(progress);
524    }
525
526    type Accepted = Creations<RoutedCreation<A, New>>;
527    type Rejection = ChildNamespaceExhausted;
528    type Prerequisite = Never;
529}
530
531/// One committed fresh child with its creator-local provenance and exact
532/// installed actor. The occurrence distinguishes equal child behaviors in
533/// distinct static positions; it is not an actor identity or runtime route.
534pub struct CommittedChild<C, Occurrence>
535where
536    C: Behavior,
537    BehaviorAddr<C>: EndpointAddress,
538{
539    id: CreationId,
540    kind: CreationKind,
541    actor: EstablishedActor<C>,
542    occurrence: PhantomData<fn() -> Occurrence>,
543}
544
545impl<C, Occurrence> CommittedChild<C, Occurrence>
546where
547    C: Behavior,
548    BehaviorAddr<C>: EndpointAddress,
549{
550    /// Record an already committed fresh installation. This does not install
551    /// an actor; only a successful interpreter installation may issue its
552    /// runtime-owned actor value.
553    #[must_use]
554    pub const fn new(id: CreationId, kind: CreationKind, actor: EstablishedActor<C>) -> Self {
555        Self {
556            id,
557            kind,
558            actor,
559            occurrence: PhantomData,
560        }
561    }
562
563    #[must_use]
564    pub const fn id(&self) -> CreationId {
565        self.id
566    }
567
568    #[must_use]
569    pub const fn kind(&self) -> CreationKind {
570        self.kind
571    }
572
573    #[must_use]
574    pub fn actor(&self) -> EstablishedActor<C> {
575        self.actor.clone()
576    }
577
578    #[must_use]
579    pub fn into_parts(self) -> (CreationId, CreationKind, EstablishedActor<C>) {
580        (self.id, self.kind, self.actor)
581    }
582}
583
584/// Committed or rejected result for one exact concrete child occurrence.
585///
586/// `Installed` is constructed only after fresh allocation, successful
587/// initialization, endpoint establishment, and creator-local binding. It returns the
588/// exact installed actor. `Rejected` carries no capability, so a
589/// failed request cannot be used as an established destination. Both variants
590/// preserve Behavior-authored creation provenance.
591///
592/// `Occurrence` is topology navigation evidence authored by the parent. It
593/// distinguishes duplicate occurrences without becoming another protocol
594/// identity or runtime key. A consumer that only sends messages may project
595/// the protocol recipient without retaining the installed actor.
596///
597/// A rejected named report has no actor capability to extract:
598///
599/// ```compile_fail,E0026
600/// fn rejected_actor<C, Occurrence>(report: behavior::EstablishedCreation<C, Occurrence>)
601/// where
602///     C: behavior::Behavior,
603///     behavior::BehaviorAddr<C>: behavior::EndpointAddress,
604/// {
605///     let behavior::EstablishedCreation::Rejected { actor, .. } = report else { return; };
606///     let _: behavior::EstablishedActor<C> = actor;
607/// }
608/// ```
609///
610/// Duplicate occurrences remain incompatible even when their protocols and
611/// endpoint representations match:
612///
613/// ```compile_fail,E0308
614/// fn wrong_occurrence<C, Primary, Backup>(backup: behavior::EstablishedCreation<C, Backup>)
615/// where
616///     C: behavior::Behavior,
617///     behavior::BehaviorAddr<C>: behavior::EndpointAddress,
618/// {
619///     let _: behavior::EstablishedCreation<C, Primary> = backup;
620/// }
621/// ```
622pub enum EstablishedCreation<C, Occurrence>
623where
624    C: Behavior,
625    BehaviorAddr<C>: EndpointAddress,
626{
627    Installed(CommittedChild<C, Occurrence>),
628    Rejected {
629        id: CreationId,
630        kind: CreationKind,
631        reason: CreationRejection,
632        occurrence: PhantomData<fn() -> Occurrence>,
633    },
634}
635
636impl<C, Occurrence> EstablishedCreation<C, Occurrence>
637where
638    C: Behavior,
639    BehaviorAddr<C>: EndpointAddress,
640{
641    #[must_use]
642    pub const fn installed(child: CommittedChild<C, Occurrence>) -> Self {
643        Self::Installed(child)
644    }
645
646    #[must_use]
647    pub const fn rejected(id: CreationId, kind: CreationKind, reason: CreationRejection) -> Self {
648        Self::Rejected {
649            id,
650            kind,
651            reason,
652            occurrence: PhantomData,
653        }
654    }
655
656    #[must_use]
657    pub const fn id(&self) -> CreationId {
658        match self {
659            Self::Installed(child) => child.id(),
660            Self::Rejected { id, .. } => *id,
661        }
662    }
663
664    #[must_use]
665    pub const fn kind(&self) -> CreationKind {
666        match self {
667            Self::Installed(child) => child.kind(),
668            Self::Rejected { kind, .. } => *kind,
669        }
670    }
671
672    /// Consume the report and return its exact endpoint capability.
673    ///
674    /// # Errors
675    /// Returns the original [`CreationRejection`] when child creation did not
676    /// commit.
677    pub fn into_recipient(self) -> Result<EstablishedRecipient<C::Protocol>, CreationRejection> {
678        match self {
679            Self::Installed(child) => Ok(child.into_parts().2.into_recipient()),
680            Self::Rejected { reason, .. } => Err(reason),
681        }
682    }
683
684    /// Transfer the concrete committed child without recreating authority
685    /// from a protocol recipient.
686    ///
687    /// # Errors
688    /// Returns the original [`CreationRejection`] when child creation did not
689    /// commit.
690    pub fn into_committed(self) -> Result<CommittedChild<C, Occurrence>, CreationRejection> {
691        match self {
692            Self::Installed(child) => Ok(child),
693            Self::Rejected { reason, .. } => Err(reason),
694        }
695    }
696}
697
698/// Complete result after a runtime accepts one child definition.
699///
700/// A created child leaves only its exact established capability. Rejection
701/// during the pure initialization transition returns the current child and its
702/// exact error. A caught panic returns the extant current child without
703/// claiming an error or successful actions. Rejection while establishing the
704/// host returns the current child and the still-uninterpreted initialization
705/// actions. No variant reconstructs a pre-initialization value or silently
706/// discards an affine action.
707///
708/// A failed child installation returns current work and no installed actor:
709///
710/// ```compile_fail,E0026
711/// fn rejected_actor<C, Occurrence>(outcome: behavior::ChildCreationOutcome<C, Occurrence>)
712/// where
713///     C: behavior::Behavior,
714///     behavior::BehaviorAddr<C>: behavior::EndpointAddress,
715/// {
716///     if let behavior::ChildCreationOutcome::HostRejected { actor, .. } = outcome {
717///         let _: behavior::EstablishedActor<C> = actor;
718///     }
719/// }
720/// ```
721pub enum ChildCreationOutcome<C, Occurrence>
722where
723    C: Behavior,
724    BehaviorAddr<C>: EndpointAddress,
725{
726    /// Fresh child creation committed successfully.
727    Established(CommittedChild<C, Occurrence>),
728    /// The child's pure initialization transition rejected before host commit.
729    InitializationRejected {
730        /// Current child value with its creator correlation and private route.
731        creation: RoutedCreation<BehaviorAddr<C>, C>,
732        /// Exact error returned by the child's initialization transition.
733        error: C::Error,
734    },
735    /// The child's pure initialization panicked before any host commitment.
736    ///
737    /// Only the outer routed child that survived Rust unwinding is returned;
738    /// this does not promise recovery of values destroyed inside user code.
739    InitializationPanicked {
740        /// Current child value with its creator correlation and private route.
741        creation: RoutedCreation<BehaviorAddr<C>, C>,
742    },
743    /// Host establishment rejected after initialization produced actions.
744    HostRejected {
745        /// Current child value with its creator correlation and private route.
746        creation: RoutedCreation<BehaviorAddr<C>, C>,
747        /// Complete initialization actions that were never interpreted.
748        initialization: Actions<BehaviorAddr<C>, C::Ph, C::Sends, C::Birth>,
749        /// Exact reason the host could not commit this child.
750        reason: CreationRejection,
751    },
752}
753
754impl<C, Occurrence> ChildCreationOutcome<C, Occurrence>
755where
756    C: Behavior,
757    BehaviorAddr<C>: EndpointAddress,
758{
759    /// Consume a committed child result into its exact concrete actor
760    /// capability.
761    ///
762    /// The committed variant directly owns the actor, ID, kind, and
763    /// occurrence; no nested rejection can appear under success.
764    ///
765    /// # Errors
766    ///
767    /// Returns the complete original result when creation did not commit.
768    pub fn into_committed(self) -> Result<CommittedChild<C, Occurrence>, Self> {
769        match self {
770            Self::Established(child) => Ok(child),
771            other => Err(other),
772        }
773    }
774
775    /// Consume a committed child into its exact actor.
776    ///
777    /// # Errors
778    /// Returns the complete original result when creation did not commit.
779    pub fn into_actor(self) -> Result<EstablishedActor<C>, Self> {
780        self.into_committed().map(|child| child.into_parts().2)
781    }
782}
783
784/// One complete child-creation settlement returned to its creator.
785///
786/// This product moves the existing settlement without reclassifying it. In
787/// particular, initialization and host rejection retain the current child and
788/// exact initialization value. A runtime that cannot admit this value to the
789/// live creator must recover it from the returned event and keep it in host
790/// custody.
791#[must_use = "a child-creation settlement must be admitted or retained"]
792pub struct ChildCreationSettled<C, Occurrence>
793where
794    C: Behavior,
795    BehaviorAddr<C>: EndpointAddress,
796{
797    settlement: SettledItem<
798        RoutedCreation<BehaviorAddr<C>, C>,
799        ItemSettlement<
800            RoutedCreation<BehaviorAddr<C>, C>,
801            ChildCreationOutcome<C, Occurrence>,
802            CreationRejection,
803            Never,
804        >,
805    >,
806}
807
808impl<C, Occurrence> ChildCreationSettled<C, Occurrence>
809where
810    C: Behavior,
811    BehaviorAddr<C>: EndpointAddress,
812{
813    #[must_use]
814    pub const fn new(
815        settlement: SettledItem<
816            RoutedCreation<BehaviorAddr<C>, C>,
817            ItemSettlement<
818                RoutedCreation<BehaviorAddr<C>, C>,
819                ChildCreationOutcome<C, Occurrence>,
820                CreationRejection,
821                Never,
822            >,
823        >,
824    ) -> Self {
825        Self { settlement }
826    }
827
828    /// Recover the exact generic settlement without cloning any owned value.
829    #[must_use]
830    pub fn into_settlement(
831        self,
832    ) -> SettledItem<
833        RoutedCreation<BehaviorAddr<C>, C>,
834        ItemSettlement<
835            RoutedCreation<BehaviorAddr<C>, C>,
836            ChildCreationOutcome<C, Occurrence>,
837            CreationRejection,
838            Never,
839        >,
840    > {
841        self.settlement
842    }
843}
844
845impl<C, Occurrence> core::fmt::Debug for ChildCreationSettled<C, Occurrence>
846where
847    C: Behavior,
848    BehaviorAddr<C>: EndpointAddress,
849{
850    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
851        formatter
852            .debug_struct("ChildCreationSettled")
853            .finish_non_exhaustive()
854    }
855}
856
857/// Non-authoritative correlation to one creation result in the current action.
858///
859/// `P` and `Occurrence` select the exact creator-local entry statically. The
860/// value carries no recipient, actor identity, or child-hosting authority; the
861/// corresponding creation settlement remains its sole authoritative owner.
862///
863/// Equal ID representations at different occurrences cannot be
864/// substituted:
865///
866/// ```compile_fail,E0308
867/// struct Worker;
868/// impl behavior::Protocol for Worker {
869///     type Addr = behavior::MailAddr;
870///     type Msg = ();
871/// }
872/// struct Primary;
873/// struct Backup;
874/// let mut sequence = behavior::CreationSequence::new();
875/// let id = sequence.issue().expect("fixture creation ID");
876/// let backup = behavior::CreationCorrelation::<Worker, Backup>::new(id);
877/// let _: behavior::CreationCorrelation<Worker, Primary> = backup;
878/// ```
879pub struct CreationCorrelation<P, Occurrence>
880where
881    P: Protocol,
882{
883    id: CreationId,
884    occurrence: PhantomData<fn() -> (P, Occurrence)>,
885}
886
887impl<P, Occurrence> CreationCorrelation<P, Occurrence>
888where
889    P: Protocol,
890{
891    #[must_use]
892    pub const fn new(id: CreationId) -> Self {
893        Self {
894            id,
895            occurrence: PhantomData,
896        }
897    }
898
899    #[must_use]
900    pub const fn id(self) -> CreationId {
901        self.id
902    }
903}
904
905impl<P, Occurrence> Copy for CreationCorrelation<P, Occurrence> where P: Protocol {}
906
907impl<P, Occurrence> Clone for CreationCorrelation<P, Occurrence>
908where
909    P: Protocol,
910{
911    fn clone(&self) -> Self {
912        *self
913    }
914}
915
916impl<P, Occurrence> core::fmt::Debug for CreationCorrelation<P, Occurrence>
917where
918    P: Protocol,
919{
920    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
921        formatter
922            .debug_tuple("CreationCorrelation")
923            .field(&self.id)
924            .finish()
925    }
926}
927
928impl<P, Occurrence> PartialEq for CreationCorrelation<P, Occurrence>
929where
930    P: Protocol,
931{
932    fn eq(&self, other: &Self) -> bool {
933        self.id == other.id
934    }
935}
936
937impl<P, Occurrence> Eq for CreationCorrelation<P, Occurrence> where P: Protocol {}
938
939/// Same-action communication to one declared creator-local role.
940///
941/// The interpreter resolves this route only after all creations in the same
942/// [`crate::Actions`] have committed. A rejected or absent binding must produce
943/// a typed interpreter outcome; it can never be converted into a logical
944/// address by nonce arithmetic.
945pub struct ChildDelivery<P, Occurrence>
946where
947    P: Protocol,
948{
949    pub creation: CreationId,
950    pub message: P::Msg,
951    occurrence: PhantomData<fn() -> Occurrence>,
952}
953
954/// Private typed communication to one declared creator-local child role.
955///
956/// `ChildDelivery` addresses the child's public protocol. `ChildInput`
957/// instead selects one owner-defined member of the concrete child's event
958/// algebra through `Source`. This is the static boundary used for lifecycle
959/// coordination between an owner and a composed child: it retains the exact
960/// child behavior, occurrence, input, and ingress owner without exposing the
961/// input through the child's public protocol or performing a runtime lookup.
962///
963/// This is a derived Bombay communication form. Like `ChildDelivery`, its
964/// creator-local route is interpreted only after same-action creations have
965/// committed; constructing it performs no delivery.
966pub struct ChildInput<Child, Source, Input, Occurrence>
967where
968    Child: Behavior,
969{
970    /// Creator-local creation ID of the concrete child receiving the input.
971    pub creation: CreationId,
972    /// Complete private input transferred to the child.
973    pub input: Input,
974    marker: PhantomData<fn() -> (Child, Source, Occurrence)>,
975}
976
977/// Exact reason one public child delivery was not accepted.
978#[derive(Clone, Copy, Debug, Eq, PartialEq)]
979pub enum ChildDeliveryReason {
980    MissingBinding,
981    ClosedRecipient,
982}
983
984/// Exact reason one private child input was not accepted.
985#[derive(Clone, Copy, Debug, Eq, PartialEq)]
986pub enum ChildInputReason {
987    MissingBinding,
988    ClosedControlLane,
989}
990
991/// One report emitted through an established creator/child relationship.
992///
993/// The interpreter attaches `child` from its exact local binding; the
994/// emitting behavior supplies only `report`. `EventIngress` separately keeps
995/// the concrete child behavior and occurrence in the parent's event type, so
996/// equal nonce representations cannot confuse different child roles.
997#[derive(Debug, Clone, Copy, PartialEq, Eq)]
998pub struct ChildReport<R> {
999    /// Creator-local ID of the child that emitted the report.
1000    pub child: CreationId,
1001    /// Complete report value transferred by that child.
1002    pub report: R,
1003}
1004
1005impl<R> ChildReport<R> {
1006    /// Attach an established creator-local creation ID to one report.
1007    #[must_use]
1008    pub const fn new(child: CreationId, report: R) -> Self {
1009        Self { child, report }
1010    }
1011}
1012
1013impl<R> From<(CreationId, crate::ReportToParent<R>)> for ChildReport<R> {
1014    fn from((child, request): (CreationId, crate::ReportToParent<R>)) -> Self {
1015        Self::new(child, request.into_inner())
1016    }
1017}
1018
1019impl<Child, Source, Input, Occurrence> ChildInput<Child, Source, Input, Occurrence>
1020where
1021    Child: Behavior,
1022    Child::Event: crate::ChildInputIngress<Source, Input>,
1023{
1024    /// Construct a private input for one exact creator-local child creation.
1025    #[must_use]
1026    pub const fn after(creation: CreationId, input: Input) -> Self {
1027        Self {
1028            creation,
1029            input,
1030            marker: PhantomData,
1031        }
1032    }
1033}
1034
1035impl<Child, Source, Input, Occurrence> Clone for ChildInput<Child, Source, Input, Occurrence>
1036where
1037    Child: Behavior,
1038    Input: Clone,
1039{
1040    fn clone(&self) -> Self {
1041        Self {
1042            creation: self.creation,
1043            input: self.input.clone(),
1044            marker: PhantomData,
1045        }
1046    }
1047}
1048
1049impl<P, Occurrence> ChildDelivery<P, Occurrence>
1050where
1051    P: Protocol,
1052{
1053    #[must_use]
1054    pub const fn after(creation: CreationId, message: P::Msg) -> Self {
1055        Self {
1056            creation,
1057            message,
1058            occurrence: PhantomData,
1059        }
1060    }
1061}
1062
1063impl<P, Occurrence> Clone for ChildDelivery<P, Occurrence>
1064where
1065    P: Protocol,
1066    P::Msg: Clone,
1067{
1068    fn clone(&self) -> Self {
1069        Self {
1070            creation: self.creation,
1071            message: self.message.clone(),
1072            occurrence: PhantomData,
1073        }
1074    }
1075}
1076
1077impl<P, Occurrence> ActionItem for ChildDelivery<P, Occurrence>
1078where
1079    P: Protocol,
1080    P::Msg: Send,
1081{
1082    type Custody = (Option<Self>, Option<Self::Reply>);
1083    type Input<'a>
1084        = &'a mut Option<Self>
1085    where
1086        Self: 'a;
1087    type Reply = ItemSettlement<Self, Self::Accepted, Self::Rejection, Self::Prerequisite>;
1088
1089    fn prepare_interpretation(
1090        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
1091    ) {
1092        prepare_item::<Self>(progress);
1093    }
1094    fn interpretation_input<'a>(
1095        custody: &'a mut Self::Custody,
1096    ) -> Option<(Self::Input<'a>, &'a mut Option<Self::Reply>)>
1097    where
1098        Self: 'a,
1099    {
1100        let (input, received) = custody;
1101        if input.is_some() && received.is_none() {
1102            Some((input, received))
1103        } else {
1104            None
1105        }
1106    }
1107    fn finish_interpretation(
1108        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
1109    ) {
1110        finish_item::<Self>(progress);
1111    }
1112
1113    type Accepted = ();
1114    type Rejection = ChildDeliveryReason;
1115    type Prerequisite = CreationCorrelation<P, Occurrence>;
1116}
1117
1118impl<Child, Source, Input, Occurrence> ActionItem for ChildInput<Child, Source, Input, Occurrence>
1119where
1120    Child: Behavior,
1121    Input: Send,
1122{
1123    type Custody = (Option<Self>, Option<Self::Reply>);
1124    type Input<'a>
1125        = &'a mut Option<Self>
1126    where
1127        Self: 'a;
1128    type Reply = ItemSettlement<Self, Self::Accepted, Self::Rejection, Self::Prerequisite>;
1129
1130    fn prepare_interpretation(
1131        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
1132    ) {
1133        prepare_item::<Self>(progress);
1134    }
1135    fn interpretation_input<'a>(
1136        custody: &'a mut Self::Custody,
1137    ) -> Option<(Self::Input<'a>, &'a mut Option<Self::Reply>)>
1138    where
1139        Self: 'a,
1140    {
1141        let (input, received) = custody;
1142        if input.is_some() && received.is_none() {
1143            Some((input, received))
1144        } else {
1145            None
1146        }
1147    }
1148    fn finish_interpretation(
1149        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
1150    ) {
1151        finish_item::<Self>(progress);
1152    }
1153
1154    type Accepted = ();
1155    type Rejection = ChildInputReason;
1156    type Prerequisite = CreationCorrelation<Child::Protocol, Occurrence>;
1157}
1158
1159/// Runtime ownership port for establishing one concrete child behavior.
1160///
1161/// The result vocabulary belongs to the creation request, never the runtime.
1162/// Accepting ownership consumes the child definition and returns its one
1163/// authoritative created-or-rejected result. Refusal before ownership transfer
1164/// returns the complete request. Child creation has no prerequisite, while
1165/// interpreter corruption uses the shared [`crate::InterpreterFault`] sum.
1166///
1167/// A runtime cannot substitute a private receipt or rejection type:
1168///
1169/// ```compile_fail,E0271
1170/// #[derive(Clone, Copy, Eq, PartialEq)]
1171/// struct RuntimeAddr;
1172/// impl behavior::Address for RuntimeAddr { type Nonce = u8; }
1173/// #[derive(Clone)]
1174/// struct Endpoint;
1175/// struct Installed<B: behavior::Behavior>(Endpoint, std::sync::mpsc::Sender<B::Event>);
1176/// impl<B: behavior::Behavior> Clone for Installed<B> {
1177///     fn clone(&self) -> Self { Self(self.0.clone(), self.1.clone()) }
1178/// }
1179/// impl behavior::EndpointAddress for RuntimeAddr {
1180///     type Established<P> = Endpoint where P: behavior::Protocol<Addr = Self>;
1181///     type Installed<B> = Installed<B>
1182///         where B: behavior::Behavior<Protocol: behavior::Protocol<Addr = Self>>;
1183///     fn recipient<B>(installed: &Self::Installed<B>) -> Endpoint
1184///     where B: behavior::Behavior<Protocol: behavior::Protocol<Addr = Self>> {
1185///         installed.0.clone()
1186///     }
1187/// }
1188/// struct Child;
1189/// impl behavior::Protocol for Child {
1190///     type Addr = RuntimeAddr;
1191///     type Msg = behavior::Never;
1192/// }
1193/// impl behavior::Behavior for Child {
1194///     type Protocol = Self;
1195///     type Event = behavior::User<RuntimeAddr, behavior::Never>;
1196///     type Sends = behavior::NoSends;
1197///     type Ph = behavior::Never;
1198///     type Error = behavior::Never;
1199///     type Birth = behavior::NoBirths;
1200///     fn transition(
1201///         &mut self,
1202///         _: behavior::ActiveTurn,
1203///         event: Self::Event,
1204///     ) -> behavior::BehaviorActed<Self> {
1205///         match event.message {}
1206///     }
1207/// }
1208/// struct Runtime;
1209///
1210/// impl behavior::EstablishChild<behavior::ChildHead, Child> for Runtime {
1211///     fn establish_child(
1212///         &mut self,
1213///         creation: behavior::RoutedCreation<behavior::BehaviorAddr<Child>, Child>,
1214///     ) -> impl core::future::Future<Output = behavior::ItemSettlement<
1215///         behavior::RoutedCreation<behavior::BehaviorAddr<Child>, Child>,
1216///         (),
1217///         &'static str,
1218///         behavior::Never,
1219///     >> + Send {
1220///         async move { behavior::ItemSettlement::Rejected { item: creation, reason: "no" } }
1221///     }
1222/// }
1223/// ```
1224pub trait EstablishChild<Occurrence, C>
1225where
1226    C: Behavior,
1227    BehaviorAddr<C>: EndpointAddress,
1228{
1229    /// Establish exactly the supplied child or return its complete settlement.
1230    fn establish_child(
1231        &mut self,
1232        creation: RoutedCreation<BehaviorAddr<C>, C>,
1233    ) -> impl Future<
1234        Output = ItemSettlement<
1235            RoutedCreation<BehaviorAddr<C>, C>,
1236            ChildCreationOutcome<C, Occurrence>,
1237            CreationRejection,
1238            Never,
1239        >,
1240    > + Send;
1241}
1242
1243/// Authoritative result shape for one closed child choice.
1244///
1245/// This type-level product depends only on the declared child protocols and
1246/// their occurrences. It contains no runtime-selected type.
1247#[doc(hidden)]
1248pub trait ChildCreationProduct<A: Address, Occurrence>: Sized {
1249    type Result;
1250}
1251
1252/// Exhaustive static dispatch of one creation-only child sum.
1253///
1254/// This is an interpreter-facing derived construction, not another actor-model
1255/// operation. Implementations must preserve the ID, route, and creation kind and
1256/// call exactly one concrete [`EstablishChild`] implementation. [`ChildChoice`]
1257/// provides the closed recursive heterogeneous sum. Dispatch futures are
1258/// sendable; heterogeneous sums therefore require sendable alternatives,
1259/// runtime routes, and child hosts.
1260pub trait DispatchBirth<A: Address, Host>: ChildCreationProduct<A, ChildHead> + Sized {
1261    /// Select exactly one concrete child host while preserving creation data
1262    /// in every non-accepted settlement.
1263    fn dispatch_birth(
1264        self,
1265        id: CreationId,
1266        route: A::Nonce,
1267        kind: CreationKind,
1268        host: &mut Host,
1269    ) -> impl Future<
1270        Output = ItemSettlement<
1271            RoutedCreation<A, Self>,
1272            <Self as ChildCreationProduct<A, ChildHead>>::Result,
1273            CreationRejection,
1274            Never,
1275        >,
1276    > + Send;
1277}
1278
1279trait CreateSelectedChild<A: Address, Occurrence, Host>:
1280    ChildCreationProduct<A, Occurrence> + Sized
1281{
1282    fn dispatch_birth_at(
1283        self,
1284        id: CreationId,
1285        route: A::Nonce,
1286        kind: CreationKind,
1287        host: &mut Host,
1288    ) -> impl Future<
1289        Output = ItemSettlement<
1290            RoutedCreation<A, Self>,
1291            <Self as ChildCreationProduct<A, Occurrence>>::Result,
1292            CreationRejection,
1293            Never,
1294        >,
1295    > + Send;
1296}
1297
1298impl<A, Child, Host> DispatchBirth<A, Host> for Child
1299where
1300    A: Address,
1301    Child: CreateSelectedChild<A, ChildHead, Host>,
1302{
1303    fn dispatch_birth(
1304        self,
1305        id: CreationId,
1306        route: A::Nonce,
1307        kind: CreationKind,
1308        host: &mut Host,
1309    ) -> impl Future<
1310        Output = ItemSettlement<
1311            RoutedCreation<A, Self>,
1312            <Self as ChildCreationProduct<A, ChildHead>>::Result,
1313            CreationRejection,
1314            Never,
1315        >,
1316    > + Send {
1317        self.dispatch_birth_at(id, route, kind, host)
1318    }
1319}
1320
1321/// One alternative in a closed, recursively composed child-creation sum.
1322///
1323/// `Head` is one concrete child behavior and `Tail` is the remaining closed
1324/// sum. This is a creation choice only: it is not a behavior, message
1325/// envelope, registry, or runtime dispatch mechanism.
1326///
1327/// Every alternative requires a concrete child host; incomplete interpreter
1328/// support is rejected statically:
1329///
1330/// ```compile_fail,E0277
1331/// #[derive(Clone, Copy, Eq, PartialEq)]
1332/// struct RuntimeAddr;
1333/// impl behavior::Address for RuntimeAddr { type Nonce = u8; }
1334/// #[derive(Clone)]
1335/// struct Endpoint;
1336/// struct Installed<B: behavior::Behavior>(Endpoint, std::sync::mpsc::Sender<B::Event>);
1337/// impl<B: behavior::Behavior> Clone for Installed<B> {
1338///     fn clone(&self) -> Self { Self(self.0.clone(), self.1.clone()) }
1339/// }
1340/// impl behavior::EndpointAddress for RuntimeAddr {
1341///     type Established<P> = Endpoint where P: behavior::Protocol<Addr = Self>;
1342///     type Installed<B> = Installed<B>
1343///         where B: behavior::Behavior<Protocol: behavior::Protocol<Addr = Self>>;
1344///     fn recipient<B>(installed: &Self::Installed<B>) -> Endpoint
1345///     where B: behavior::Behavior<Protocol: behavior::Protocol<Addr = Self>> {
1346///         installed.0.clone()
1347///     }
1348/// }
1349/// struct CacheWorker;
1350/// struct QueueWorker;
1351///
1352/// macro_rules! inert {
1353///     ($child:ty) => {
1354///         impl behavior::Protocol for $child {
1355///             type Addr = RuntimeAddr;
1356///             type Msg = behavior::Never;
1357///         }
1358///         impl behavior::Behavior for $child {
1359///             type Protocol = Self;
1360///             type Event = behavior::User<RuntimeAddr, behavior::Never>;
1361///             type Sends = behavior::NoSends;
1362///             type Ph = behavior::Never;
1363///             type Error = behavior::Never;
1364///             type Birth = behavior::NoBirths;
1365///
1366///             fn transition(
1367///                 &mut self,
1368///                 _: behavior::ActiveTurn,
1369///                 event: Self::Event,
1370///             ) -> behavior::BehaviorActed<Self> {
1371///                 match event.message {}
1372///             }
1373///         }
1374///     };
1375/// }
1376/// inert!(CacheWorker);
1377/// inert!(QueueWorker);
1378///
1379/// struct Incomplete;
1380/// impl behavior::EstablishChild<behavior::ChildHead, CacheWorker> for Incomplete {
1381///     fn establish_child(
1382///         &mut self,
1383///         creation: behavior::RoutedCreation<RuntimeAddr, CacheWorker>,
1384///     ) -> impl core::future::Future<Output = behavior::ItemSettlement<
1385///         behavior::RoutedCreation<RuntimeAddr, CacheWorker>,
1386///         behavior::ChildCreationOutcome<CacheWorker, behavior::ChildHead>,
1387///         behavior::CreationRejection,
1388///         behavior::Never,
1389///     >> + Send {
1390///         async move { behavior::ItemSettlement::Rejected {
1391///             item: creation,
1392///             reason: behavior::CreationRejection::EnvironmentFailed,
1393///         } }
1394///     }
1395/// }
1396///
1397/// type WorkerChoices = behavior::ChildChoice<
1398///     CacheWorker,
1399///     behavior::ChildChoice<QueueWorker, behavior::Never>,
1400/// >;
1401/// fn require_complete<T: behavior::DispatchBirth<RuntimeAddr, Incomplete>>() {}
1402/// require_complete::<WorkerChoices>();
1403/// ```
1404#[derive(Debug, Clone, PartialEq, Eq)]
1405pub enum ChildChoice<Head, Tail> {
1406    /// Select the concrete child at this position.
1407    Head(Head),
1408    /// Select one concrete child from the remaining alternatives.
1409    Tail(Tail),
1410}
1411
1412/// Position selecting the head of a closed [`ChildChoice`] sum.
1413#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1414pub struct ChildHead;
1415
1416/// Position selecting inside the tail of a closed [`ChildChoice`] sum.
1417#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1418pub struct ChildTail<Position>(PhantomData<fn() -> Position>);
1419
1420/// Static proof that `Child` occupies `Position` in a closed child sum.
1421///
1422/// This trait provides structural evidence only. It does not construct a
1423/// choice, perform creation, or select a child through runtime inspection.
1424/// A role cannot claim a position occupied by a different child:
1425///
1426/// ```compile_fail
1427/// struct CacheWorker;
1428/// struct QueueWorker;
1429/// struct Parent;
1430/// macro_rules! inert {
1431///     ($actor:ty) => {
1432///         impl behavior::Protocol for $actor {
1433///             type Addr = behavior::MailAddr;
1434///             type Msg = behavior::Never;
1435///         }
1436///         impl behavior::Behavior for $actor {
1437///             type Protocol = Self;
1438///             type Event = behavior::User<behavior::MailAddr, behavior::Never>;
1439///             type Sends = Vec<behavior::Never>;
1440///             type Ph = behavior::Never;
1441///             type Error = behavior::Never;
1442///             type Birth = behavior::NoBirths;
1443///             fn transition(&mut self, _: behavior::ActiveTurn, event: Self::Event) -> behavior::BehaviorActed<Self> {
1444///                 match event.message {}
1445///             }
1446///         }
1447///     };
1448/// }
1449/// inert!(CacheWorker);
1450/// inert!(QueueWorker);
1451/// impl behavior::Protocol for Parent {
1452///     type Addr = behavior::MailAddr;
1453///     type Msg = behavior::Never;
1454/// }
1455/// impl behavior::Behavior for Parent {
1456///     type Protocol = Self;
1457///     type Event = behavior::User<behavior::MailAddr, behavior::Never>;
1458///     type Sends = Vec<behavior::Never>;
1459///     type Ph = behavior::Never;
1460///     type Error = behavior::Never;
1461///     type Birth = behavior::Births<behavior::ChildChoice<QueueWorker, behavior::ChildChoice<CacheWorker, behavior::Never>>>;
1462///     fn transition(&mut self, _: behavior::ActiveTurn, event: Self::Event) -> behavior::BehaviorActed<Self> {
1463///         match event.message {}
1464///     }
1465/// }
1466/// struct ForgedCacheRole;
1467/// impl behavior::ChildRole<Parent> for ForgedCacheRole {
1468///     type Child = CacheWorker;
1469///     type Position = behavior::ChildHead;
1470/// }
1471/// ```
1472pub trait ChildPosition<Children, Child: Behavior>: sealed::ChildPosition {}
1473
1474impl sealed::ChildPosition for ChildHead {}
1475
1476impl<Child: Behavior> ChildPosition<Child, Child> for ChildHead {}
1477
1478impl<Head: Behavior, Tail> ChildPosition<ChildChoice<Head, Tail>, Head> for ChildHead {}
1479
1480impl<Position> sealed::ChildPosition for ChildTail<Position> {}
1481
1482impl<Head, Tail, Position, Child> ChildPosition<ChildChoice<Head, Tail>, Child>
1483    for ChildTail<Position>
1484where
1485    Child: Behavior,
1486    Position: ChildPosition<Tail, Child>,
1487{
1488}
1489
1490/// Append one closed direct-child algebra after another.
1491///
1492/// `Self` remains the structural prefix, so every child occurrence already
1493/// valid in that prefix retains both its child type and its position. `Tail`
1494/// begins only after the prefix's final position. The two injection functions
1495/// change only the closed sum containing a child; [`append_creations`](Self::append_creations)
1496/// additionally preserves every creation's ID, kind, and batch order while
1497/// placing all prefix creations before all appended creations.
1498///
1499/// This is a static composition of Bombay's existing staged-creation
1500/// capability, not another actor effect and not an allocation operation. The
1501/// interpreter remains solely responsible for establishing and binding the child.
1502///
1503/// A generic topology owner can therefore retain an inner behavior's exact
1504/// creation effects and append children whose concrete types were inferred
1505/// from value construction:
1506///
1507/// ```
1508/// type Combined = <behavior::Never as behavior::BirthNodeAppend<behavior::Never>>::Output;
1509/// let _: core::marker::PhantomData<Combined> = core::marker::PhantomData;
1510/// ```
1511pub trait BirthNodeAppend<Tail>: sealed::BirthNode + Sized
1512where
1513    Tail: sealed::BirthNode,
1514{
1515    /// Closed child algebra containing the complete prefix followed by the
1516    /// complete appended tail.
1517    type Output: sealed::BirthNode;
1518
1519    /// Inject one child from the existing prefix without changing its
1520    /// structural occurrence.
1521    fn append_prefix(self) -> Self::Output;
1522
1523    /// Inject one child from the appended tail after every prefix occurrence.
1524    fn append_tail(tail: Tail) -> Self::Output;
1525
1526    /// Preserve and concatenate two ordered creation batches.
1527    #[must_use]
1528    fn append_creations<A: Address>(
1529        prefix: Creations<CreateChild<A, Self>>,
1530        tail: Creations<CreateChild<A, Tail>>,
1531    ) -> Creations<CreateChild<A, Self::Output>> {
1532        let mut combined = Vec::with_capacity(prefix.len() + tail.len());
1533        combined.extend(prefix.into_iter().map(|creation| {
1534            let (id, child, kind) = creation.into_parts();
1535            CreateChild::from_parts(id, Self::append_prefix(child), kind)
1536        }));
1537        combined.extend(tail.into_iter().map(|creation| {
1538            let (id, child, kind) = creation.into_parts();
1539            CreateChild::from_parts(id, Self::append_tail(child), kind)
1540        }));
1541        Creations::from_items(combined)
1542    }
1543}
1544
1545impl<Tail> BirthNodeAppend<Tail> for Never
1546where
1547    Tail: sealed::BirthNode,
1548{
1549    type Output = Tail;
1550
1551    fn append_prefix(self) -> Self::Output {
1552        match self {}
1553    }
1554
1555    fn append_tail(tail: Tail) -> Self::Output {
1556        tail
1557    }
1558}
1559
1560impl<Node> BirthNodeAppend<Never> for Node
1561where
1562    Node: sealed::NonEmptyBirthNode,
1563{
1564    type Output = Node;
1565
1566    fn append_prefix(self) -> Self::Output {
1567        self
1568    }
1569
1570    fn append_tail(tail: Never) -> Self::Output {
1571        match tail {}
1572    }
1573}
1574
1575impl<Child, Tail> BirthNodeAppend<Tail> for Child
1576where
1577    Child: Behavior,
1578    Tail: sealed::NonEmptyBirthNode,
1579{
1580    type Output = ChildChoice<Child, Tail>;
1581
1582    fn append_prefix(self) -> Self::Output {
1583        ChildChoice::Head(self)
1584    }
1585
1586    fn append_tail(tail: Tail) -> Self::Output {
1587        ChildChoice::Tail(tail)
1588    }
1589}
1590
1591impl<Head, Rest, Tail> BirthNodeAppend<Tail> for ChildChoice<Head, Rest>
1592where
1593    Head: Behavior,
1594    Rest: sealed::BirthNode + BirthNodeAppend<Tail>,
1595    Tail: sealed::NonEmptyBirthNode,
1596    Rest::Output: sealed::BirthNode,
1597{
1598    type Output = ChildChoice<Head, Rest::Output>;
1599
1600    fn append_prefix(self) -> Self::Output {
1601        match self {
1602            ChildChoice::Head(head) => ChildChoice::Head(head),
1603            ChildChoice::Tail(rest) => ChildChoice::Tail(rest.append_prefix()),
1604        }
1605    }
1606
1607    fn append_tail(tail: Tail) -> Self::Output {
1608        ChildChoice::Tail(Rest::append_tail(tail))
1609    }
1610}
1611
1612impl<Parent: Behavior> ChildOccurrence<Parent> for ChildHead {
1613    type Resolution = StructuralChildOccurrence<Self>;
1614}
1615
1616impl<Parent: Behavior, Position> ChildOccurrence<Parent> for ChildTail<Position> {
1617    type Resolution = StructuralChildOccurrence<Self>;
1618}
1619
1620/// Sealed proof that an occurrence may select one resolver descriptor.
1621///
1622/// Nominal occurrences can select [`DeclaredChildOccurrence`] only when their
1623/// existing [`ChildRole`] implementation supplies the child and position. Raw
1624/// structural descriptors are available only to the identical
1625/// [`ChildHead`] or [`ChildTail`] occurrence.
1626#[doc(hidden)]
1627pub trait ChildOccurrenceResolution<Parent: Behavior, Occurrence>:
1628    sealed::ChildOccurrenceDescriptor + sealed::OccurrenceResolution<Parent, Occurrence>
1629{
1630}
1631
1632impl<Parent, Occurrence> ChildOccurrenceResolution<Parent, Occurrence> for DeclaredChildOccurrence
1633where
1634    Parent: Behavior,
1635    Occurrence: ChildRole<Parent>,
1636{
1637}
1638
1639impl<Parent: Behavior> ChildOccurrenceResolution<Parent, ChildHead>
1640    for StructuralChildOccurrence<ChildHead>
1641{
1642}
1643
1644impl<Parent: Behavior, Position> ChildOccurrenceResolution<Parent, ChildTail<Position>>
1645    for StructuralChildOccurrence<ChildTail<Position>>
1646{
1647}
1648
1649/// Sealed implementation detail for resolving occurrence descriptors.
1650#[doc(hidden)]
1651pub trait ResolveChildOccurrenceDescriptor<Emitter: Behavior, Occurrence>:
1652    sealed::ChildOccurrenceDescriptor + sealed::ResolveDescriptor<Emitter, Occurrence>
1653{
1654    type Child: Behavior;
1655    type Position: ChildPosition<<Emitter::Birth as BirthMode>::Child, Self::Child>;
1656}
1657
1658impl<Emitter, Occurrence, Position> ResolveChildOccurrenceDescriptor<Emitter, Occurrence>
1659    for StructuralChildOccurrence<Position>
1660where
1661    Emitter: Behavior,
1662    <Emitter::Birth as BirthMode>::Child: BirthNodeAt<Position>,
1663    Position: ChildPosition<
1664            <Emitter::Birth as BirthMode>::Child,
1665            <<Emitter::Birth as BirthMode>::Child as BirthNodeAt<Position>>::Child,
1666        >,
1667{
1668    type Child = <<Emitter::Birth as BirthMode>::Child as BirthNodeAt<Position>>::Child;
1669    type Position = Position;
1670}
1671
1672impl<Emitter, Occurrence> ResolveChildOccurrenceDescriptor<Emitter, Occurrence>
1673    for DeclaredChildOccurrence
1674where
1675    Emitter:
1676        Behavior<Protocol = <<Emitter as BehaviorBase>::Base as Behavior>::Protocol> + BehaviorBase,
1677    Emitter::Base: Behavior,
1678    Occurrence: ChildRole<Emitter::Base>,
1679    <Emitter::Birth as BirthMode>::Child:
1680        BirthNodeAt<Occurrence::Position, Child = Occurrence::Child>,
1681    Occurrence::Position: ChildPosition<<Emitter::Birth as BirthMode>::Child, Occurrence::Child>,
1682{
1683    type Child = Occurrence::Child;
1684    type Position = Occurrence::Position;
1685}
1686
1687/// Sealed inverse projection from one structural position to its child.
1688#[doc(hidden)]
1689pub trait BirthNodeAt<Position>: sealed::BirthNode {
1690    type Child: Behavior;
1691}
1692
1693impl<Child: Behavior> BirthNodeAt<ChildHead> for Child {
1694    type Child = Child;
1695}
1696
1697impl<Head: Behavior, Tail> BirthNodeAt<ChildHead> for ChildChoice<Head, Tail>
1698where
1699    Tail: sealed::BirthNode,
1700{
1701    type Child = Head;
1702}
1703
1704impl<Head, Tail, Position> BirthNodeAt<ChildTail<Position>> for ChildChoice<Head, Tail>
1705where
1706    Head: Behavior,
1707    Tail: BirthNodeAt<Position>,
1708{
1709    type Child = <Tail as BirthNodeAt<Position>>::Child;
1710}
1711
1712/// Downstream shape of one direct-child occurrence product.
1713///
1714/// Behavior owns the closed node algebra: a concrete [`Behavior`] leaf,
1715/// [`ChildChoice`], or [`Never`]. A runtime owns the representation associated
1716/// with each leaf. `Empty` supplies its terminal representation and `Member`
1717/// receives one concrete child, its structural occurrence, and the remaining
1718/// child product.
1719///
1720/// This is a type-level derived construction. It creates no value, allocates
1721/// no actor, interprets no effect, and introduces no protocol identity or
1722/// runtime key. A shape that builds a heterogeneous product should retain
1723/// `Tail`; the product includes every declared child exactly once.
1724///
1725/// ```
1726/// struct NoChildBindings;
1727/// struct ChildBinding<Position, Child, Tail>(core::marker::PhantomData<fn() -> (Position, Child, Tail)>);
1728/// struct RuntimeStorage;
1729///
1730/// impl behavior::ChildOccurrenceShape for RuntimeStorage {
1731///     type Empty = NoChildBindings;
1732///     type Member<Occurrence, Child: behavior::Behavior, Tail> =
1733///         ChildBinding<Occurrence, Child, Tail>;
1734/// }
1735///
1736/// type ChildBindings<Node> = behavior::ChildOccurrences<Node, RuntimeStorage>;
1737/// ```
1738pub trait ChildOccurrenceShape {
1739    /// Representation of the empty [`Never`] node.
1740    type Empty;
1741
1742    /// Representation of one concrete child followed by the remaining product.
1743    type Member<Occurrence, Child: Behavior, Tail>;
1744}
1745
1746/// Sealed occurrence product of a closed direct-child birth node.
1747///
1748/// The product starts at [`ChildHead`] and advances through
1749/// [`ChildTail<Position>`] in exactly the same way as [`DispatchBirth`] and
1750/// [`ChildPosition`]. A downstream runtime selects only the result shape
1751/// through [`ChildOccurrenceShape`]; it cannot reclassify a foreign type as a
1752/// birth node or replace the recursion.
1753///
1754/// This product is intentionally direct rather than transitive. Each installed
1755/// actor owns the bindings for its own `Behavior::Birth`; when a concrete child
1756/// is installed, the same product law applies to that child's birth algebra.
1757///
1758/// Foreign types cannot extend the closed node algebra:
1759///
1760/// ```compile_fail
1761/// struct RuntimeShape;
1762/// impl behavior::ChildOccurrenceShape for RuntimeShape {
1763///     type Empty = ();
1764///     type Member<Occurrence, Child: behavior::Behavior, Tail> = ();
1765/// }
1766///
1767/// struct ForeignNode;
1768/// impl behavior::ChildOccurrenceProduct<RuntimeShape> for ForeignNode {
1769///     type Product = ();
1770/// }
1771/// ```
1772pub trait ChildOccurrenceProduct<Shape>: sealed::BirthNode
1773where
1774    Shape: ChildOccurrenceShape,
1775{
1776    /// Complete shape-owned representation of this closed birth node.
1777    type Product;
1778}
1779
1780/// Occurrence-carrying recursion for [`ChildOccurrenceProduct`]. Consumers
1781/// should name [`ChildOccurrenceProduct`] or [`ChildOccurrences`] instead.
1782#[doc(hidden)]
1783pub trait ChildOccurrenceProductAt<Occurrence, Shape>: sealed::BirthNode
1784where
1785    Shape: ChildOccurrenceShape,
1786{
1787    type Product;
1788}
1789
1790impl<Node, Shape> ChildOccurrenceProduct<Shape> for Node
1791where
1792    Node: ChildOccurrenceProductAt<ChildHead, Shape>,
1793    Shape: ChildOccurrenceShape,
1794{
1795    type Product = <Node as ChildOccurrenceProductAt<ChildHead, Shape>>::Product;
1796}
1797
1798impl<Occurrence, Shape, Child> ChildOccurrenceProductAt<Occurrence, Shape> for Child
1799where
1800    Shape: ChildOccurrenceShape,
1801    Child: Behavior,
1802{
1803    type Product = Shape::Member<Occurrence, Child, Shape::Empty>;
1804}
1805
1806impl<Occurrence, Shape, Head, Tail> ChildOccurrenceProductAt<Occurrence, Shape>
1807    for ChildChoice<Head, Tail>
1808where
1809    Shape: ChildOccurrenceShape,
1810    Head: Behavior,
1811    Tail: ChildOccurrenceProductAt<ChildTail<Occurrence>, Shape>,
1812{
1813    type Product = Shape::Member<
1814        Occurrence,
1815        Head,
1816        <Tail as ChildOccurrenceProductAt<ChildTail<Occurrence>, Shape>>::Product,
1817    >;
1818}
1819
1820impl<Occurrence, Shape> ChildOccurrenceProductAt<Occurrence, Shape> for Never
1821where
1822    Shape: ChildOccurrenceShape,
1823{
1824    type Product = Shape::Empty;
1825}
1826
1827/// Occurrence-preserving representation selected by one downstream shape.
1828pub type ChildOccurrences<Node, Shape> = <Node as ChildOccurrenceProduct<Shape>>::Product;
1829
1830mod sealed {
1831    use super::{
1832        Behavior, BehaviorBase, BirthMode, BirthNodeAt, ChildChoice, ChildHead, ChildOccurrence,
1833        ChildRole, ChildTail, DeclaredChildOccurrence, Never, ResolveChildOccurrenceDescriptor,
1834        StructuralChildOccurrence,
1835    };
1836
1837    pub trait BirthNode {}
1838
1839    pub trait NonEmptyBirthNode: BirthNode {}
1840
1841    impl<Child: Behavior> BirthNode for Child {}
1842    impl<Child: Behavior> NonEmptyBirthNode for Child {}
1843
1844    impl<Head, Tail> BirthNode for ChildChoice<Head, Tail>
1845    where
1846        Head: Behavior,
1847        Tail: BirthNode,
1848    {
1849    }
1850
1851    impl<Head, Tail> NonEmptyBirthNode for ChildChoice<Head, Tail>
1852    where
1853        Head: Behavior,
1854        Tail: BirthNode,
1855    {
1856    }
1857
1858    impl BirthNode for Never {}
1859
1860    pub trait ChildPosition {}
1861    pub trait ChildProduct {}
1862
1863    pub trait ChildOccurrenceDescriptor {}
1864
1865    impl ChildOccurrenceDescriptor for DeclaredChildOccurrence {}
1866
1867    impl<Position> ChildOccurrenceDescriptor for StructuralChildOccurrence<Position> {}
1868
1869    pub trait OccurrenceResolution<Parent: Behavior, Occurrence> {}
1870
1871    impl<Parent, Occurrence> OccurrenceResolution<Parent, Occurrence> for DeclaredChildOccurrence
1872    where
1873        Parent: Behavior,
1874        Occurrence: ChildRole<Parent>,
1875    {
1876    }
1877
1878    impl<Parent: Behavior> OccurrenceResolution<Parent, ChildHead>
1879        for StructuralChildOccurrence<ChildHead>
1880    {
1881    }
1882
1883    impl<Parent: Behavior, Position> OccurrenceResolution<Parent, ChildTail<Position>>
1884        for StructuralChildOccurrence<ChildTail<Position>>
1885    {
1886    }
1887
1888    pub trait ResolveDescriptor<Emitter: Behavior, Occurrence> {}
1889
1890    impl<Emitter, Occurrence, Position> ResolveDescriptor<Emitter, Occurrence>
1891        for StructuralChildOccurrence<Position>
1892    where
1893        Emitter: Behavior,
1894        <Emitter::Birth as BirthMode>::Child: BirthNodeAt<Position>,
1895    {
1896    }
1897
1898    impl<Emitter, Occurrence> ResolveDescriptor<Emitter, Occurrence> for DeclaredChildOccurrence
1899    where
1900        Emitter: Behavior<Protocol = <<Emitter as BehaviorBase>::Base as Behavior>::Protocol>
1901            + BehaviorBase,
1902        Emitter::Base: Behavior,
1903        Occurrence: ChildRole<Emitter::Base>,
1904        <Emitter::Birth as BirthMode>::Child:
1905            BirthNodeAt<Occurrence::Position, Child = Occurrence::Child>,
1906        Occurrence::Position:
1907            super::ChildPosition<<Emitter::Birth as BirthMode>::Child, Occurrence::Child>,
1908    {
1909    }
1910
1911    pub trait ResolveChildOccurrence<Occurrence> {}
1912
1913    impl<Emitter, Occurrence> ResolveChildOccurrence<Occurrence> for Emitter
1914    where
1915        Emitter: Behavior + BehaviorBase,
1916        Emitter::Base: Behavior,
1917        Occurrence: ChildOccurrence<Emitter::Base>,
1918        Occurrence::Resolution: ResolveChildOccurrenceDescriptor<Emitter, Occurrence>,
1919    {
1920    }
1921}
1922
1923/// The empty heterogeneous creation product.
1924#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1925pub struct NoChildren;
1926
1927impl sealed::ChildProduct for NoChildren {}
1928
1929/// One creation appended to an ordered heterogeneous child product.
1930pub struct ChildCons<A: Address, C, Earlier> {
1931    creation: CreateChild<A, C>,
1932    earlier: Earlier,
1933}
1934
1935impl<A: Address, C, Earlier> sealed::ChildProduct for ChildCons<A, C, Earlier> {}
1936
1937/// A pure, ordered heterogeneous product of staged direct-child creations.
1938///
1939/// This value owns no mailbox, address, runtime actor, or lifecycle. Every
1940/// product position is a distinct static child occurrence, so equal numeric IDs
1941/// in different positions remain distinct correlations.
1942pub struct Children<A: Address, Product = NoChildren> {
1943    product: Product,
1944    address: PhantomData<fn() -> A>,
1945}
1946
1947/// Closed recursive conversion implemented only by Bombay child products.
1948pub trait ChildProduct<A: Address>: sealed::ChildProduct + Sized {
1949    /// Closed sum containing exactly the concrete child behavior types.
1950    type Choice: BirthNodeAppend<Never>;
1951
1952    #[doc(hidden)]
1953    fn stage(self) -> Vec<CreateChild<A, Self::Choice>>;
1954}
1955
1956impl<A: Address> ChildProduct<A> for NoChildren {
1957    type Choice = Never;
1958
1959    fn stage(self) -> Vec<CreateChild<A, Self::Choice>> {
1960        Vec::new()
1961    }
1962}
1963
1964impl<A, C, Earlier> ChildProduct<A> for ChildCons<A, C, Earlier>
1965where
1966    A: Address,
1967    C: Behavior,
1968    C::Protocol: Protocol<Addr = A>,
1969    Earlier: ChildProduct<A>,
1970{
1971    type Choice = ChildChoice<C, Earlier::Choice>;
1972
1973    fn stage(self) -> Vec<CreateChild<A, Self::Choice>> {
1974        let earlier = self.earlier.stage();
1975        let mut creates = earlier
1976            .into_iter()
1977            .map(|creation| {
1978                let (id, child, kind) = creation.into_parts();
1979                CreateChild::from_parts(id, ChildChoice::Tail(child), kind)
1980            })
1981            .collect::<Vec<_>>();
1982        let (id, child, kind) = self.creation.into_parts();
1983        creates.push(CreateChild::from_parts(id, ChildChoice::Head(child), kind));
1984        creates
1985    }
1986}
1987
1988impl<A: Address> Children<A, NoChildren> {
1989    /// Start an empty heterogeneous creation product.
1990    #[must_use]
1991    pub const fn new() -> Self {
1992        Self {
1993            product: NoChildren,
1994            address: PhantomData,
1995        }
1996    }
1997}
1998
1999impl<A: Address> Default for Children<A, NoChildren> {
2000    fn default() -> Self {
2001        Self::new()
2002    }
2003}
2004
2005impl<A: Address, Product> Children<A, Product> {
2006    /// Append one complete staged creation, preserving its provenance.
2007    #[must_use]
2008    pub fn create<C>(self, creation: CreateChild<A, C>) -> Children<A, ChildCons<A, C, Product>>
2009    where
2010        C: Behavior,
2011        C::Protocol: Protocol<Addr = A>,
2012    {
2013        Children {
2014            product: ChildCons {
2015                creation,
2016                earlier: self.product,
2017            },
2018            address: PhantomData,
2019        }
2020    }
2021
2022    /// Append one ordinary fresh-birth request.
2023    #[must_use]
2024    pub fn child<C>(self, id: CreationId, child: C) -> Children<A, ChildCons<A, C, Product>>
2025    where
2026        C: Behavior,
2027        C::Protocol: Protocol<Addr = A>,
2028    {
2029        self.create(CreateChild::birth(id, child))
2030    }
2031}
2032
2033impl<A, Product> Children<A, Product>
2034where
2035    A: Address,
2036    Product: ChildProduct<A>,
2037{
2038    /// Produce one ordered creation batch.
2039    #[must_use]
2040    pub fn into_creates(self) -> Creations<CreateChild<A, Product::Choice>> {
2041        Creations::from_items(self.product.stage())
2042    }
2043}
2044
2045impl<A, Occurrence, Head, Tail> ChildCreationProduct<A, Occurrence> for ChildChoice<Head, Tail>
2046where
2047    A: EndpointAddress,
2048    Head: Behavior,
2049    Head::Protocol: Protocol<Addr = A>,
2050    Tail: ChildCreationProduct<A, ChildTail<Occurrence>>,
2051{
2052    type Result = ChildChoice<
2053        ChildCreationOutcome<Head, Occurrence>,
2054        <Tail as ChildCreationProduct<A, ChildTail<Occurrence>>>::Result,
2055    >;
2056}
2057
2058impl<A, Occurrence, Head, Tail, Host> CreateSelectedChild<A, Occurrence, Host>
2059    for ChildChoice<Head, Tail>
2060where
2061    A: EndpointAddress,
2062    A::Nonce: Send,
2063    Head: Behavior + Send,
2064    Head::Protocol: Protocol<Addr = A>,
2065    Tail: CreateSelectedChild<A, ChildTail<Occurrence>, Host> + Send,
2066    Host: EstablishChild<Occurrence, Head> + Send,
2067{
2068    async fn dispatch_birth_at(
2069        self,
2070        id: CreationId,
2071        route: A::Nonce,
2072        kind: CreationKind,
2073        host: &mut Host,
2074    ) -> ItemSettlement<
2075        RoutedCreation<A, Self>,
2076        <Self as ChildCreationProduct<A, Occurrence>>::Result,
2077        CreationRejection,
2078        Never,
2079    > {
2080        match self {
2081            Self::Head(child) => {
2082                let creation = RoutedCreation::new(CreateChild::from_parts(id, child, kind), route);
2083                match host.establish_child(creation).await {
2084                    ItemSettlement::Accepted(receipt) => {
2085                        ItemSettlement::Accepted(ChildChoice::Head(receipt))
2086                    }
2087                    ItemSettlement::Rejected { item, reason } => {
2088                        let (creation, route) = item.into_parts();
2089                        let (id, child, kind) = creation.into_parts();
2090                        ItemSettlement::Rejected {
2091                            item: RoutedCreation::new(
2092                                CreateChild::from_parts(id, Self::Head(child), kind),
2093                                route,
2094                            ),
2095                            reason,
2096                        }
2097                    }
2098                    ItemSettlement::Blocked { prerequisite, .. } => match prerequisite {},
2099                    ItemSettlement::Corrupt { item, fault } => {
2100                        let (creation, route) = item.into_parts();
2101                        let (id, child, kind) = creation.into_parts();
2102                        ItemSettlement::Corrupt {
2103                            item: RoutedCreation::new(
2104                                CreateChild::from_parts(id, Self::Head(child), kind),
2105                                route,
2106                            ),
2107                            fault,
2108                        }
2109                    }
2110                }
2111            }
2112            Self::Tail(tail) => match tail.dispatch_birth_at(id, route, kind, host).await {
2113                ItemSettlement::Accepted(receipt) => {
2114                    ItemSettlement::Accepted(ChildChoice::Tail(receipt))
2115                }
2116                ItemSettlement::Rejected { item, reason } => {
2117                    let (creation, route) = item.into_parts();
2118                    let (id, child, kind) = creation.into_parts();
2119                    ItemSettlement::Rejected {
2120                        item: RoutedCreation::new(
2121                            CreateChild::from_parts(id, Self::Tail(child), kind),
2122                            route,
2123                        ),
2124                        reason,
2125                    }
2126                }
2127                ItemSettlement::Blocked { prerequisite, .. } => match prerequisite {},
2128                ItemSettlement::Corrupt { item, fault } => {
2129                    let (creation, route) = item.into_parts();
2130                    let (id, child, kind) = creation.into_parts();
2131                    ItemSettlement::Corrupt {
2132                        item: RoutedCreation::new(
2133                            CreateChild::from_parts(id, Self::Tail(child), kind),
2134                            route,
2135                        ),
2136                        fault,
2137                    }
2138                }
2139            },
2140        }
2141    }
2142}
2143
2144impl<A, Occurrence> ChildCreationProduct<A, Occurrence> for Never
2145where
2146    A: Address,
2147{
2148    type Result = Never;
2149}
2150
2151impl<A, Occurrence, Host> CreateSelectedChild<A, Occurrence, Host> for Never
2152where
2153    A: Address,
2154{
2155    fn dispatch_birth_at(
2156        self,
2157        _id: CreationId,
2158        _route: A::Nonce,
2159        _kind: CreationKind,
2160        _host: &mut Host,
2161    ) -> impl Future<
2162        Output = ItemSettlement<
2163            RoutedCreation<A, Self>,
2164            <Self as ChildCreationProduct<A, Occurrence>>::Result,
2165            CreationRejection,
2166            Never,
2167        >,
2168    > + Send {
2169        async move { match self {} }
2170    }
2171}
2172
2173impl<A, Occurrence, C> ChildCreationProduct<A, Occurrence> for C
2174where
2175    A: EndpointAddress,
2176    C: Behavior,
2177    C::Protocol: Protocol<Addr = A>,
2178{
2179    type Result = ChildCreationOutcome<C, Occurrence>;
2180}
2181
2182impl<A, Occurrence, C, Host> CreateSelectedChild<A, Occurrence, Host> for C
2183where
2184    A: EndpointAddress,
2185    C: Behavior,
2186    C::Protocol: Protocol<Addr = A>,
2187    Host: EstablishChild<Occurrence, C>,
2188{
2189    fn dispatch_birth_at(
2190        self,
2191        id: CreationId,
2192        route: A::Nonce,
2193        kind: CreationKind,
2194        host: &mut Host,
2195    ) -> impl Future<
2196        Output = ItemSettlement<
2197            RoutedCreation<A, Self>,
2198            <Self as ChildCreationProduct<A, Occurrence>>::Result,
2199            CreationRejection,
2200            Never,
2201        >,
2202    > + Send {
2203        host.establish_child(RoutedCreation::new(
2204            CreateChild::from_parts(id, self, kind),
2205            route,
2206        ))
2207    }
2208}
2209
2210/// A type-level description of a behavior's creation capability.
2211pub trait BirthMode {
2212    type Child;
2213}
2214
2215/// Empty protocol projection of a closed behavior-birth algebra.
2216pub struct NoBirthProtocols;
2217
2218/// One behavior protocol followed by the remaining closed birth projection.
2219pub struct BirthProtocol<P: Protocol, Tail> {
2220    protocol: PhantomData<fn() -> P>,
2221    tail: PhantomData<fn() -> Tail>,
2222}
2223
2224/// Structural position selecting the current projected birth protocol.
2225pub struct BirthProtocolHead;
2226
2227/// Structural position selecting inside the remaining protocol projection.
2228pub struct BirthProtocolTail<Position>(PhantomData<fn() -> Position>);
2229
2230/// Static membership evidence for one occurrence in a birth-protocol product.
2231pub trait BirthProtocolAt<P: Protocol, Position> {}
2232
2233impl<P: Protocol, Tail> BirthProtocolAt<P, BirthProtocolHead> for BirthProtocol<P, Tail> {}
2234
2235impl<Head, Tail, P, Position> BirthProtocolAt<P, BirthProtocolTail<Position>>
2236    for BirthProtocol<Head, Tail>
2237where
2238    Head: Protocol,
2239    P: Protocol,
2240    Tail: BirthProtocolAt<P, Position>,
2241{
2242}
2243
2244/// Closed product operation used by the structural birth projection.
2245pub trait BirthProtocolProduct {
2246    type Append<Tail: BirthProtocolProduct>: BirthProtocolProduct;
2247}
2248
2249impl BirthProtocolProduct for NoBirthProtocols {
2250    type Append<Tail: BirthProtocolProduct> = Tail;
2251}
2252
2253impl<P, Rest> BirthProtocolProduct for BirthProtocol<P, Rest>
2254where
2255    P: Protocol,
2256    Rest: BirthProtocolProduct,
2257{
2258    type Append<Tail: BirthProtocolProduct> = BirthProtocol<P, Rest::Append<Tail>>;
2259}
2260
2261/// Closed static projection of a behavior's own protocol and every protocol
2262/// reachable through its transitive staged-birth algebra.
2263///
2264/// This is structural information derived from [`Behavior::Birth`]. It makes
2265/// no hosting or allocation decision and does not inspect send destinations.
2266pub trait BirthProtocols: Behavior {
2267    type Protocols: BirthProtocolProduct;
2268}
2269
2270impl<B> BirthProtocols for B
2271where
2272    B: Behavior,
2273    B::Birth: BirthModeProtocols,
2274{
2275    type Protocols = BirthProtocol<B::Protocol, <B::Birth as BirthModeProtocols>::Protocols>;
2276}
2277
2278#[doc(hidden)]
2279pub trait BirthModeProtocols {
2280    type Protocols: BirthProtocolProduct;
2281}
2282
2283impl<M> BirthModeProtocols for M
2284where
2285    M: BirthMode,
2286    M::Child: BirthNodeProtocols,
2287{
2288    type Protocols = <M::Child as BirthNodeProtocols>::Protocols;
2289}
2290
2291#[doc(hidden)]
2292pub trait BirthNodeProtocols {
2293    type Protocols: BirthProtocolProduct;
2294}
2295
2296impl<B> BirthNodeProtocols for B
2297where
2298    B: BirthProtocols,
2299{
2300    type Protocols = B::Protocols;
2301}
2302
2303impl<Head, Tail> BirthNodeProtocols for ChildChoice<Head, Tail>
2304where
2305    Head: BirthNodeProtocols,
2306    Tail: BirthNodeProtocols,
2307{
2308    type Protocols = <Head::Protocols as BirthProtocolProduct>::Append<Tail::Protocols>;
2309}
2310
2311impl BirthNodeProtocols for Never {
2312    type Protocols = NoBirthProtocols;
2313}
2314
2315/// Structural logical-destination projection for one closed birth node.
2316///
2317/// This implementation detail is public only because its associated product
2318/// participates in the blanket [`crate::LogicalHostRequirements`] interface.
2319#[doc(hidden)]
2320pub trait BirthNodeLogicalHosts {
2321    type LogicalHosts: BirthProtocolProduct;
2322}
2323
2324impl<B> BirthNodeLogicalHosts for B
2325where
2326    B: crate::LogicalHostRequirements,
2327{
2328    type LogicalHosts = B::LogicalHosts;
2329}
2330
2331impl<Head, Tail> BirthNodeLogicalHosts for ChildChoice<Head, Tail>
2332where
2333    Head: BirthNodeLogicalHosts,
2334    Tail: BirthNodeLogicalHosts,
2335{
2336    type LogicalHosts = <Head::LogicalHosts as BirthProtocolProduct>::Append<Tail::LogicalHosts>;
2337}
2338
2339impl BirthNodeLogicalHosts for Never {
2340    type LogicalHosts = NoBirthProtocols;
2341}
2342
2343/// This behavior cannot emit child births.
2344#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
2345pub struct NoBirths;
2346
2347impl BirthMode for NoBirths {
2348    type Child = Never;
2349}
2350
2351/// This behavior may emit births of `C`.
2352#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
2353pub struct Births<C>(PhantomData<fn() -> C>);
2354
2355impl<C> BirthMode for Births<C> {
2356    type Child = C;
2357}
2358
2359/// This behavior may emit births of `C` whose exact settlements remain in
2360/// runtime custody until actor retirement.
2361///
2362/// Unlike [`Births`], this mode does not return a creation settlement as a
2363/// later input to the live creator. It is the deliberate policy for actors
2364/// whose terminal owner, rather than another behavior transition, must retain
2365/// every authoritative creation result.
2366#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
2367pub struct RetirementBirths<C>(PhantomData<fn() -> C>);
2368
2369impl<C> BirthMode for RetirementBirths<C> {
2370    type Child = C;
2371}
2372
2373#[cfg(test)]
2374mod tests {
2375    use super::*;
2376    use crate::{Actions, BehaviorActed, ChildInputIngress, Interpretation, NoBirths, User};
2377
2378    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
2379    struct TestAddr(u64);
2380
2381    impl Address for TestAddr {
2382        type Nonce = u64;
2383    }
2384
2385    struct TestEndpoint<P>(PhantomData<fn() -> P>);
2386
2387    impl<P> Clone for TestEndpoint<P> {
2388        fn clone(&self) -> Self {
2389            *self
2390        }
2391    }
2392
2393    impl<P> Copy for TestEndpoint<P> {}
2394
2395    struct TestInstalled<B: Behavior> {
2396        endpoint: TestEndpoint<B::Protocol>,
2397        control: std::sync::mpsc::Sender<B::Event>,
2398    }
2399
2400    impl<B: Behavior> Clone for TestInstalled<B> {
2401        fn clone(&self) -> Self {
2402            Self {
2403                endpoint: self.endpoint,
2404                control: self.control.clone(),
2405            }
2406        }
2407    }
2408
2409    impl EndpointAddress for TestAddr {
2410        type Established<P>
2411            = TestEndpoint<P>
2412        where
2413            P: Protocol<Addr = Self>;
2414
2415        type Installed<B>
2416            = TestInstalled<B>
2417        where
2418            B: Behavior<Protocol: Protocol<Addr = Self>>;
2419
2420        fn recipient<B>(installed: &Self::Installed<B>) -> Self::Established<B::Protocol>
2421        where
2422            B: Behavior<Protocol: Protocol<Addr = Self>>,
2423        {
2424            installed.endpoint
2425        }
2426    }
2427
2428    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
2429    struct Child;
2430
2431    struct SharedProtocol;
2432
2433    impl behavior::Protocol for SharedProtocol {
2434        type Addr = TestAddr;
2435        type Msg = u8;
2436    }
2437
2438    struct Primary;
2439    struct Fallback;
2440
2441    macro_rules! shared_protocol_behavior {
2442        ($behavior:ty, $birth:ty) => {
2443            impl Behavior for $behavior {
2444                type Protocol = SharedProtocol;
2445                type Event = User<TestAddr, u8>;
2446                type Sends = Vec<Never>;
2447                type Ph = Never;
2448                type Error = Never;
2449                type Birth = $birth;
2450
2451                fn transition(
2452                    &mut self,
2453                    _: crate::ActiveTurn,
2454                    _: Self::Event,
2455                ) -> BehaviorActed<Self> {
2456                    Ok(Actions::cont())
2457                }
2458            }
2459        };
2460    }
2461
2462    shared_protocol_behavior!(Primary, Births<Child>);
2463    shared_protocol_behavior!(Fallback, NoBirths);
2464
2465    impl behavior::Protocol for Child {
2466        type Addr = TestAddr;
2467        type Msg = u8;
2468    }
2469
2470    impl Behavior for Child {
2471        type Protocol = Self;
2472        type Event = User<TestAddr, u8>;
2473        type Sends = Vec<Never>;
2474        type Ph = Never;
2475        type Error = Never;
2476        type Birth = NoBirths;
2477
2478        fn transition(&mut self, _: crate::ActiveTurn, _: Self::Event) -> BehaviorActed<Self> {
2479            Ok(Actions::cont())
2480        }
2481    }
2482
2483    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
2484    enum ChildPlan {
2485        CreateChild,
2486        RejectBeforeTransfer,
2487    }
2488
2489    struct RecordingHost {
2490        calls: usize,
2491        observed: Vec<(CreationId, u64, CreationKind)>,
2492        plan: ChildPlan,
2493        control_receivers: Vec<std::sync::mpsc::Receiver<User<TestAddr, u8>>>,
2494    }
2495
2496    #[derive(Debug, PartialEq, Eq)]
2497    enum SharedCreation {
2498        Primary(CreationId, u64),
2499        Fallback(CreationId, u64),
2500    }
2501
2502    #[derive(Default)]
2503    struct SharedProtocolHost(
2504        Vec<SharedCreation>,
2505        Vec<std::sync::mpsc::Receiver<User<TestAddr, u8>>>,
2506    );
2507
2508    impl EstablishChild<ChildHead, Primary> for SharedProtocolHost {
2509        async fn establish_child(
2510            &mut self,
2511            creation: RoutedCreation<TestAddr, Primary>,
2512        ) -> ItemSettlement<
2513            RoutedCreation<TestAddr, Primary>,
2514            ChildCreationOutcome<Primary, ChildHead>,
2515            CreationRejection,
2516            Never,
2517        > {
2518            let id = creation.id();
2519            let kind = creation.kind();
2520            let route = creation.route();
2521            let (creation, _) = creation.into_parts();
2522            let (_, _child, _) = creation.into_parts();
2523            self.0.push(SharedCreation::Primary(id, route));
2524            let (control, receiver) = std::sync::mpsc::channel();
2525            self.1.push(receiver);
2526            ItemSettlement::Accepted(ChildCreationOutcome::Established(CommittedChild::new(
2527                id,
2528                kind,
2529                EstablishedActor::issued(TestInstalled {
2530                    endpoint: TestEndpoint(PhantomData),
2531                    control,
2532                }),
2533            )))
2534        }
2535    }
2536
2537    impl EstablishChild<ChildTail<ChildHead>, Fallback> for SharedProtocolHost {
2538        async fn establish_child(
2539            &mut self,
2540            creation: RoutedCreation<TestAddr, Fallback>,
2541        ) -> ItemSettlement<
2542            RoutedCreation<TestAddr, Fallback>,
2543            ChildCreationOutcome<Fallback, ChildTail<ChildHead>>,
2544            CreationRejection,
2545            Never,
2546        > {
2547            let id = creation.id();
2548            let kind = creation.kind();
2549            let route = creation.route();
2550            let (creation, _) = creation.into_parts();
2551            let (_, _child, _) = creation.into_parts();
2552            self.0.push(SharedCreation::Fallback(id, route));
2553            let (control, receiver) = std::sync::mpsc::channel();
2554            self.1.push(receiver);
2555            ItemSettlement::Accepted(ChildCreationOutcome::Established(CommittedChild::new(
2556                id,
2557                kind,
2558                EstablishedActor::issued(TestInstalled {
2559                    endpoint: TestEndpoint(PhantomData),
2560                    control,
2561                }),
2562            )))
2563        }
2564    }
2565
2566    impl EstablishChild<ChildHead, Child> for RecordingHost {
2567        async fn establish_child(
2568            &mut self,
2569            creation: RoutedCreation<TestAddr, Child>,
2570        ) -> ItemSettlement<
2571            RoutedCreation<TestAddr, Child>,
2572            ChildCreationOutcome<Child, ChildHead>,
2573            CreationRejection,
2574            Never,
2575        > {
2576            self.calls += 1;
2577            self.observed
2578                .push((creation.id(), creation.route(), creation.kind()));
2579            match self.plan {
2580                ChildPlan::CreateChild => {
2581                    let id = creation.id();
2582                    let kind = creation.kind();
2583                    let (creation, _) = creation.into_parts();
2584                    let (_, _child, _) = creation.into_parts();
2585                    let (control, receiver) = std::sync::mpsc::channel();
2586                    self.control_receivers.push(receiver);
2587                    ItemSettlement::Accepted(ChildCreationOutcome::Established(
2588                        CommittedChild::new(
2589                            id,
2590                            kind,
2591                            EstablishedActor::issued(TestInstalled {
2592                                endpoint: TestEndpoint(PhantomData),
2593                                control,
2594                            }),
2595                        ),
2596                    ))
2597                }
2598                ChildPlan::RejectBeforeTransfer => ItemSettlement::Rejected {
2599                    item: creation,
2600                    reason: CreationRejection::EnvironmentFailed,
2601                },
2602            }
2603        }
2604    }
2605
2606    fn host(plan: ChildPlan) -> RecordingHost {
2607        RecordingHost {
2608            calls: 0,
2609            observed: Vec::new(),
2610            plan,
2611            control_receivers: Vec::new(),
2612        }
2613    }
2614
2615    fn assert_send<T: Send>(_: &T) {}
2616
2617    #[test]
2618    fn empty_child_product_stages_no_creations() {
2619        let creations = <NoChildren as ChildProduct<TestAddr>>::stage(NoChildren);
2620        assert!(creations.is_empty());
2621    }
2622
2623    #[test]
2624    fn creation_values_preserve_ids_order_and_debug_identity() {
2625        let mut sequence = CreationSequence::new();
2626        let first = sequence.issue().expect("the first creation ID exists");
2627        let second = sequence.issue().expect("the second creation ID exists");
2628        assert_eq!(first.get(), 1);
2629        assert_eq!(second.get(), 2);
2630
2631        let first_correlation = CreationCorrelation::<SharedProtocol, ChildHead>::new(first);
2632        let same_correlation = CreationCorrelation::<SharedProtocol, ChildHead>::new(first);
2633        let second_correlation = CreationCorrelation::<SharedProtocol, ChildHead>::new(second);
2634        assert_eq!(first_correlation, same_correlation);
2635        assert_ne!(first_correlation, second_correlation);
2636        assert_eq!(
2637            format!("{first_correlation:?}"),
2638            "CreationCorrelation(CreationId(1))"
2639        );
2640
2641        let mut creations = Creations::one(1_u8);
2642        creations.extend([2, 3]);
2643        assert_eq!(creations.len(), 3);
2644        assert!(!creations.is_empty());
2645        assert_eq!(creations.iter().copied().collect::<Vec<_>>(), [1, 2, 3]);
2646        let borrowed = (&creations).into_iter().copied().collect::<Vec<_>>();
2647        assert_eq!(borrowed, [1, 2, 3]);
2648
2649        let collected: Creations<_> = [4_u8, 5].into_iter().collect();
2650        assert_eq!(collected.len(), 2);
2651        let collected_values = collected.into_iter().collect::<Vec<_>>();
2652        assert_eq!(collected_values, [4, 5]);
2653
2654        let returned = ChildCreationSettled::<Child, ChildHead>::new(SettledItem::Unattempted(
2655            RoutedCreation::new(CreateChild::birth(first, Child), 17),
2656        ));
2657        assert_eq!(format!("{returned:?}"), "ChildCreationSettled { .. }");
2658    }
2659
2660    #[tokio::test]
2661    async fn concrete_child_dispatches_once_with_exact_creation_and_route() {
2662        let mut sequence = CreationSequence::new();
2663        let previous = sequence.issue().expect("the previous ID exists");
2664        let id = sequence.issue().expect("the replacement ID exists");
2665        let mut host = host(ChildPlan::CreateChild);
2666        let kind = CreationKind::replacement(previous);
2667        let future = Child.dispatch_birth(id, 17, kind, &mut host);
2668        assert_send(&future);
2669        let result = future.await;
2670
2671        let ItemSettlement::Accepted(ChildCreationOutcome::Established(established)) = result
2672        else {
2673            panic!("expected the child to be created");
2674        };
2675        assert_eq!(established.id(), id);
2676        assert_eq!(host.calls, 1);
2677        assert_eq!(host.observed, [(id, 17, kind)]);
2678    }
2679
2680    #[tokio::test]
2681    async fn concrete_child_returns_the_exact_rejected_creation_without_retry() {
2682        let mut sequence = CreationSequence::new();
2683        let previous = sequence.issue().expect("the previous ID exists");
2684        let id = sequence.issue().expect("the replacement ID exists");
2685        let mut host = host(ChildPlan::RejectBeforeTransfer);
2686        let kind = CreationKind::replacement(previous);
2687        let future = Child.dispatch_birth(id, 23, kind, &mut host);
2688        assert_send(&future);
2689        let result = future.await;
2690
2691        let ItemSettlement::Rejected { item, reason } = result else {
2692            panic!("expected refusal before child ownership transfer");
2693        };
2694        assert_eq!(
2695            item,
2696            RoutedCreation::new(CreateChild::replacement(id, previous, Child), 23)
2697        );
2698        assert_eq!(reason, CreationRejection::EnvironmentFailed);
2699        assert_eq!(host.calls, 1);
2700        assert_eq!(host.observed, [(id, 23, kind)]);
2701    }
2702
2703    #[tokio::test]
2704    async fn distinct_child_alternatives_preserve_one_canonical_protocol_identity() {
2705        type Alternatives = ChildChoice<Primary, ChildChoice<Fallback, Never>>;
2706
2707        fn requires_shared_protocol<C: Behavior<Protocol = SharedProtocol>>() {}
2708        requires_shared_protocol::<Primary>();
2709        requires_shared_protocol::<Fallback>();
2710
2711        let mut sequence = CreationSequence::new();
2712        let primary_id = sequence.issue().expect("the primary ID exists");
2713        let fallback_id = sequence.issue().expect("the fallback ID exists");
2714        let mut host = SharedProtocolHost::default();
2715        let primary = Alternatives::Head(Primary)
2716            .dispatch_birth(primary_id, 11, CreationKind::Birth, &mut host)
2717            .await;
2718        let fallback = Alternatives::Tail(ChildChoice::Head(Fallback))
2719            .dispatch_birth(fallback_id, 17, CreationKind::Birth, &mut host)
2720            .await;
2721
2722        let ItemSettlement::Accepted(ChildChoice::Head(ChildCreationOutcome::Established(primary))) =
2723            primary
2724        else {
2725            panic!("expected the primary child result");
2726        };
2727        assert_eq!(primary.id(), primary_id);
2728        let ItemSettlement::Accepted(ChildChoice::Tail(ChildChoice::Head(
2729            ChildCreationOutcome::Established(fallback),
2730        ))) = fallback
2731        else {
2732            panic!("expected the fallback child result");
2733        };
2734        assert_eq!(fallback.id(), fallback_id);
2735
2736        assert_eq!(
2737            host.0,
2738            [
2739                SharedCreation::Primary(primary_id, 11),
2740                SharedCreation::Fallback(fallback_id, 17),
2741            ]
2742        );
2743    }
2744
2745    #[test]
2746    fn birth_protocol_projection_recurses_without_inspecting_send_lanes() {
2747        type Protocols = <Primary as BirthProtocols>::Protocols;
2748        type Expected = BirthProtocol<SharedProtocol, BirthProtocol<Child, NoBirthProtocols>>;
2749
2750        trait Same<T> {}
2751        impl<T> Same<T> for T {}
2752        fn exact<T: Same<Expected>>() {}
2753        exact::<Protocols>();
2754    }
2755    fn child_request_loan_is_affine<Item, Observation>(
2756        request: Item,
2757        duplicate: Item,
2758        reason: Item::Rejection,
2759        observe: impl Fn(&Item) -> Observation,
2760    ) where
2761        Item: ActionItem<
2762                Custody = (Option<Item>, Option<<Item as ActionItem>::Reply>),
2763                Reply = ItemSettlement<
2764                    Item,
2765                    <Item as ActionItem>::Accepted,
2766                    <Item as ActionItem>::Rejection,
2767                    <Item as ActionItem>::Prerequisite,
2768                >,
2769            > + 'static,
2770        for<'a> Item: ActionItem<Input<'a> = &'a mut Option<Item>>,
2771        Item::Rejection: Copy + PartialEq + core::fmt::Debug,
2772        Observation: PartialEq + core::fmt::Debug,
2773    {
2774        let expected = observe(&request);
2775        let mut progress = Some(InterpretationProgress::Original(request));
2776        Item::prepare_interpretation(&mut progress);
2777        Item::prepare_interpretation(&mut progress);
2778        let Some(InterpretationProgress::Interpreting(custody)) = &mut progress else {
2779            panic!("preparation must preserve the real request in outside custody");
2780        };
2781        let (input, reply) = Item::interpretation_input(custody)
2782            .expect("one unreplied request must loan its complete input");
2783        let request = input.take().expect("the loan owns the complete request");
2784        let observed = observe(&request);
2785        assert_eq!(
2786            observed, expected,
2787            "the host loan must preserve the complete request"
2788        );
2789        *reply = Some(ItemSettlement::Rejected {
2790            item: request,
2791            reason,
2792        });
2793        let replied = Item::interpretation_input(custody);
2794        assert!(
2795            replied.is_none(),
2796            "a replied request must not be loaned again"
2797        );
2798        custody.0 = Some(duplicate);
2799        let duplicate_loan = Item::interpretation_input(custody);
2800        assert!(
2801            duplicate_loan.is_none(),
2802            "a reply denies reentry even if input is retained"
2803        );
2804        let duplicate = custody
2805            .0
2806            .take()
2807            .expect("the duplicate remains outside-owned");
2808        drop(duplicate);
2809        Item::finish_interpretation(&mut progress);
2810        Item::prepare_interpretation(&mut progress);
2811        Item::finish_interpretation(&mut progress);
2812        match progress {
2813            Some(InterpretationProgress::Completed(Interpretation::Complete(
2814                ItemSettlement::Rejected {
2815                    item,
2816                    reason: returned_reason,
2817                },
2818            ))) => {
2819                let observed = observe(&item);
2820                assert_eq!(
2821                    (observed, returned_reason),
2822                    (expected, reason),
2823                    "replay must preserve the complete rejected request and reason"
2824                );
2825            }
2826            _ => panic!("completed ownership must survive preparation and finalization replay"),
2827        }
2828        let mut empty = (None, None);
2829        let missing = Item::interpretation_input(&mut empty);
2830        assert!(
2831            missing.is_none(),
2832            "missing input must never expose a host loan"
2833        );
2834    }
2835
2836    impl ChildInputIngress<ChildHead, u8> for User<TestAddr, u8> {
2837        fn child_input(input: u8) -> Self {
2838            User::new(TestAddr(7), input)
2839        }
2840    }
2841
2842    #[test]
2843    fn child_delivery_custody_allows_one_complete_request_and_denies_replay() {
2844        let id = CreationSequence::new()
2845            .issue()
2846            .expect("the creation ID exists");
2847        let request = ChildDelivery::<SharedProtocol, ChildHead>::after(id, 41);
2848        child_request_loan_is_affine(
2849            request,
2850            ChildDelivery::<SharedProtocol, ChildHead>::after(id, 47),
2851            ChildDeliveryReason::ClosedRecipient,
2852            |request| (request.creation, request.message),
2853        );
2854    }
2855
2856    #[test]
2857    fn private_child_input_custody_allows_one_complete_request_and_denies_replay() {
2858        let id = CreationSequence::new()
2859            .issue()
2860            .expect("the creation ID exists");
2861        let request = ChildInput::<Child, ChildHead, u8, ChildHead>::after(id, 43);
2862        child_request_loan_is_affine(
2863            request,
2864            ChildInput::<Child, ChildHead, u8, ChildHead>::after(id, 47),
2865            ChildInputReason::ClosedControlLane,
2866            |request| (request.creation, request.input),
2867        );
2868    }
2869
2870    #[test]
2871    fn creation_route_custody_allows_one_complete_batch_and_denies_replay() {
2872        let id = CreationSequence::new()
2873            .issue()
2874            .expect("the creation ID exists");
2875        let request = Creations::one(CreateChild::<TestAddr, Child>::birth(id, Child));
2876        child_request_loan_is_affine(
2877            request,
2878            Creations::one(CreateChild::<TestAddr, Child>::birth(id, Child)),
2879            ChildNamespaceExhausted,
2880            |request| {
2881                request
2882                    .iter()
2883                    .map(|creation| (creation.id(), creation.kind(), *creation.child()))
2884                    .collect::<Vec<_>>()
2885            },
2886        );
2887    }
2888}