Skip to main content

behavior/effects/
actions.rs

1//! The explicit result of one actor behavior transition.
2
3use super::sending::{
4    ClassifySettlement, InterpretItem, InterpretSends, Interpretation, InterpreterFault,
5    ItemSettlement, SendEffects, SendInput, SendSettlements, SettledItem, SettlementStatus,
6    SourceAdmission, SourceCustody, SourceSettlementCustody, offer_source_in_order,
7};
8use crate::actor::{
9    Address, BirthMode, Births, ChildCreationProduct, ChildHead, ChildNamespaceExhausted,
10    CreateChild, CreationRejection, Creations, DispatchBirth, NoBirths, RoutedCreation,
11};
12use crate::next::{Never, Step, Stopped};
13use crate::transition::{Behavior, BehaviorAddr};
14
15pub type Become<Ph = Never> = Step<Ph, Stopped>;
16
17/// Complete owned settlement of one [`Actions`] value.
18///
19/// Creations retain their declared vector order, sends retain their named
20/// product shape, and `become_` is the exact verdict already committed by the
21/// pure behavior fold. The enclosing [`Interpretation`] records whether the
22/// interpreter completed or corrupted while retaining this entire shape.
23#[derive(Clone, Debug, Eq, PartialEq)]
24pub struct ActionSettlement<Creations, Sends, Ph> {
25    pub creations: Creations,
26    pub sends: Sends,
27    pub become_: Become<Ph>,
28}
29
30/// Final settlement of the creation leg of one [`Actions`] value.
31#[doc(hidden)]
32#[derive(Clone, Debug, Eq, PartialEq)]
33pub enum CreationSettlement<Requests, Settlements> {
34    /// The whole batch was routed and every child has an exact settlement.
35    Settled(Settlements),
36    /// Route preparation rejected the complete unchanged batch.
37    Rejected {
38        creations: Requests,
39        reason: ChildNamespaceExhausted,
40    },
41    /// Route preparation corrupted before transferring any request.
42    Corrupt {
43        creations: Requests,
44        fault: InterpreterFault,
45    },
46}
47
48/// Static complete-settlement product selected by one concrete action type.
49///
50/// This projection performs no interpretation and names no runtime. It lets a
51/// lifecycle host retain the exact result type of an action without rebuilding
52/// its creation or send products.
53pub trait ActionSettlements {
54    type Settlements;
55}
56
57impl<A, Ph, Sends, Birth> ActionSettlements for Actions<A, Ph, Sends, Birth>
58where
59    A: Address,
60    Sends: SendSettlements,
61    Birth: CreationSettlements<A>,
62{
63    type Settlements = ActionSettlement<Birth::Settlements, Sends::Settlements, Ph>;
64}
65
66/// One exact action-settlement product selected by a concrete behavior.
67///
68/// Runtime lifecycle types use this blanket projection instead of repeating
69/// the behavior's internal send- and birth-product bounds through every parent
70/// and application owner. The associated value remains fully concrete and is
71/// never erased or reclassified.
72pub trait BehaviorSettlements: Behavior {
73    type Settlements;
74}
75
76impl<B> BehaviorSettlements for B
77where
78    B: Behavior,
79    B::Sends: SendSettlements,
80    B::Birth: CreationSettlements<BehaviorAddr<B>>,
81{
82    type Settlements =
83        <Actions<BehaviorAddr<B>, B::Ph, B::Sends, B::Birth> as ActionSettlements>::Settlements;
84}
85
86impl<Requests, Settlements> ClassifySettlement for CreationSettlement<Requests, Settlements>
87where
88    Settlements: ClassifySettlement,
89{
90    fn settlement_status(&self) -> SettlementStatus {
91        match self {
92            Self::Settled(settlements) => settlements.settlement_status(),
93            Self::Rejected { .. } => SettlementStatus::Rejected,
94            Self::Corrupt { .. } => SettlementStatus::Corrupt,
95        }
96    }
97}
98
99impl<Item> ClassifySettlement for Creations<Item>
100where
101    Item: ClassifySettlement,
102{
103    fn settlement_status(&self) -> SettlementStatus {
104        let mut status = SettlementStatus::Accepted;
105        for item in self.iter() {
106            status = status.combine(item.settlement_status());
107        }
108        status
109    }
110}
111
112/// Static creation-settlement product selected by one birth mode.
113#[doc(hidden)]
114pub trait CreationSettlements<A: Address>: BirthMode {
115    type Settlements: ClassifySettlement;
116}
117
118impl<A: Address> CreationSettlements<A> for NoBirths {
119    type Settlements = Creations<Never>;
120}
121
122impl<A, C> CreationSettlements<A> for Births<C>
123where
124    A: Address,
125    C: ChildCreationProduct<A, ChildHead>,
126{
127    type Settlements = CreationSettlement<
128        Creations<CreateChild<A, C>>,
129        Creations<
130            SettledItem<
131                RoutedCreation<A, C>,
132                ItemSettlement<
133                    RoutedCreation<A, C>,
134                    <C as ChildCreationProduct<A, ChildHead>>::Result,
135                    CreationRejection,
136                    Never,
137                >,
138            >,
139        >,
140    >;
141}
142
143/// One complete creation batch returned to its live creator.
144///
145/// The value retains either every routed child settlement or the entire
146/// unchanged batch rejected during route preparation. It is interpreter-facing
147/// custody, not an application protocol or another creation operation.
148#[doc(hidden)]
149#[must_use = "a returned creation batch must be admitted or retained"]
150pub struct CreationsSettled<A, C>
151where
152    A: Address,
153    C: ChildCreationProduct<A, ChildHead>,
154{
155    settlement: <Births<C> as CreationSettlements<A>>::Settlements,
156}
157
158impl<A, C> CreationsSettled<A, C>
159where
160    A: Address,
161    C: ChildCreationProduct<A, ChildHead>,
162{
163    #[must_use]
164    pub const fn new(settlement: <Births<C> as CreationSettlements<A>>::Settlements) -> Self {
165        Self { settlement }
166    }
167
168    #[must_use]
169    pub fn into_settlement(self) -> <Births<C> as CreationSettlements<A>>::Settlements {
170        self.settlement
171    }
172}
173
174impl<Host, RootEvent> SourceSettlementCustody<Host, RootEvent> for Creations<Never> {
175    fn offer_next_to_source(
176        self,
177        _: &mut Host,
178    ) -> impl core::future::Future<Output = SourceCustody<Self>> + Send {
179        core::future::ready(SourceCustody::Exhausted(self))
180    }
181}
182
183impl<A, C, Host, RootEvent> SourceSettlementCustody<Host, RootEvent>
184    for CreationSettlement<
185        Creations<CreateChild<A, C>>,
186        Creations<
187            SettledItem<
188                RoutedCreation<A, C>,
189                ItemSettlement<
190                    RoutedCreation<A, C>,
191                    <C as ChildCreationProduct<A, ChildHead>>::Result,
192                    CreationRejection,
193                    Never,
194                >,
195            >,
196        >,
197    >
198where
199    A: Address,
200    A::Nonce: Send,
201    C: ChildCreationProduct<A, ChildHead> + Send,
202    <C as ChildCreationProduct<A, ChildHead>>::Result: Send,
203    Host: SourceAdmission<RootEvent, Births<C>, CreationsSettled<A, C>>,
204    RootEvent: crate::EventIngress<Births<C>, CreationsSettled<A, C>>,
205{
206    fn offer_next_to_source(
207        self,
208        host: &mut Host,
209    ) -> impl core::future::Future<Output = SourceCustody<Self>> + Send {
210        async move {
211            if matches!(&self, CreationSettlement::Settled(settlements) if settlements.is_empty()) {
212                return SourceCustody::Exhausted(self);
213            }
214            match host.admit_source(CreationsSettled::new(self)).await {
215                Ok(()) => SourceCustody::Admitted(CreationSettlement::Settled(Creations::empty())),
216                Err(returned) => SourceCustody::Closed(returned.into_settlement()),
217            }
218        }
219    }
220}
221
222/// Generic interpretation of the one creation leg selected by a birth mode.
223///
224/// `NoBirths` needs no runtime capability. `Births<C>` performs one real batch
225/// route attempt followed by independent child establishment in declared order.
226#[doc(hidden)]
227pub trait InterpretCreations<A, Interpreter, RootEvent, Path>: CreationSettlements<A>
228where
229    A: Address,
230{
231    fn interpret_creations(
232        creations: Creations<CreateChild<A, Self::Child>>,
233        interpreter: &mut Interpreter,
234    ) -> impl core::future::Future<Output = Interpretation<Self::Settlements>> + Send;
235}
236
237impl<A, Interpreter, RootEvent, Path> InterpretCreations<A, Interpreter, RootEvent, Path>
238    for NoBirths
239where
240    A: Address,
241{
242    fn interpret_creations(
243        _: Creations<CreateChild<A, Never>>,
244        _: &mut Interpreter,
245    ) -> impl core::future::Future<Output = Interpretation<Self::Settlements>> + Send {
246        core::future::ready(Interpretation::Complete(Creations::empty()))
247    }
248}
249
250impl<A, C, Interpreter, RootEvent, Path> InterpretCreations<A, Interpreter, RootEvent, Path>
251    for Births<C>
252where
253    A: Address,
254    A::Nonce: Send,
255    C: ChildCreationProduct<A, ChildHead> + DispatchBirth<A, Interpreter> + Send,
256    <C as ChildCreationProduct<A, ChildHead>>::Result: Send,
257    Interpreter: InterpretItem<Creations<CreateChild<A, C>>, RootEvent, Path> + Send,
258{
259    fn interpret_creations(
260        creations: Creations<CreateChild<A, C>>,
261        interpreter: &mut Interpreter,
262    ) -> impl core::future::Future<Output = Interpretation<Self::Settlements>> + Send {
263        async move {
264            let routed = match interpreter.interpret_item(creations).await {
265                ItemSettlement::Accepted(routed) => routed,
266                ItemSettlement::Rejected { item, reason } => {
267                    return Interpretation::Complete(CreationSettlement::Rejected {
268                        creations: item,
269                        reason,
270                    });
271                }
272                ItemSettlement::Blocked { prerequisite, .. } => match prerequisite {},
273                ItemSettlement::Corrupt { item, fault } => {
274                    return Interpretation::Corrupt(CreationSettlement::Corrupt {
275                        creations: item,
276                        fault,
277                    });
278                }
279            };
280
281            let mut remaining = routed.into_iter();
282            let mut settled = Vec::with_capacity(remaining.len());
283            while let Some(creation) = remaining.next() {
284                let (creation, route) = creation.into_parts();
285                let (id, child, kind) = creation.into_parts();
286                let settlement = child.dispatch_birth(id, route, kind, interpreter).await;
287                match settlement {
288                    corrupt @ ItemSettlement::Corrupt { .. } => {
289                        settled.push(SettledItem::Attempted(corrupt));
290                        settled.extend(remaining.map(SettledItem::Unattempted));
291                        return Interpretation::Corrupt(CreationSettlement::Settled(
292                            Creations::from_items(settled),
293                        ));
294                    }
295                    creation => settled.push(SettledItem::Attempted(creation)),
296                }
297            }
298            Interpretation::Complete(CreationSettlement::Settled(Creations::from_items(settled)))
299        }
300    }
301}
302
303impl<Creations, Sends, Ph> ClassifySettlement for ActionSettlement<Creations, Sends, Ph>
304where
305    Creations: ClassifySettlement,
306    Sends: ClassifySettlement,
307{
308    fn settlement_status(&self) -> SettlementStatus {
309        self.creations
310            .settlement_status()
311            .combine(self.sends.settlement_status())
312    }
313}
314
315impl<Host, RootEvent, Creations, Sends, Ph> SourceSettlementCustody<Host, RootEvent>
316    for ActionSettlement<Creations, Sends, Ph>
317where
318    Host: Send,
319    Creations: SourceSettlementCustody<Host, RootEvent> + Send,
320    Sends: SourceSettlementCustody<Host, RootEvent> + Send,
321    Ph: Send,
322{
323    fn offer_next_to_source(
324        self,
325        host: &mut Host,
326    ) -> impl core::future::Future<Output = SourceCustody<Self>> + Send {
327        async move {
328            let Self {
329                creations,
330                sends,
331                become_,
332            } = self;
333            offer_source_in_order(creations, sends, host)
334                .await
335                .map(|(creations, sends)| Self {
336                    creations,
337                    sends,
338                    become_,
339                })
340        }
341    }
342}
343
344/// Capability to append one communication at a statically selected lane while
345/// preserving every other actor-transition effect.
346pub trait AppendSend<Input, Path>: Sized {
347    /// Append `input` exactly once and preserve creations and the next verdict.
348    #[must_use]
349    fn append_send(self, input: Input) -> Self;
350}
351
352/// Bombay's typed realization of the actor transition effects: communications,
353/// fresh actor creation, and next behavior or termination.
354///
355/// [`Actions::interpret`] resolves every fresh creation in `creates` before the
356/// named `sends` product. A committed resolution installs and binds the child;
357/// a semantic rejection binds nothing but remains an accepted interpretation
358/// receipt. This ordering lets a same-action [`crate::ChildDelivery`] or typed
359/// creation-observation request use the authoritative result rather than
360/// Behavior's intent. Each child-operation interpreter returns `Blocked` only
361/// when its exact binding prerequisite rejected, while independent later items
362/// are still attempted. Interpreter corruption retains the factual prefix and
363/// every exact remaining value. No later creation may inherit a failed binding.
364/// Creation order is vector order, and named send products declare their own
365/// stable order. Constructing a value remains pure.
366#[must_use = "actor transition effects must be interpreted, inspected, or retained"]
367pub struct Actions<A: Address, Ph, Sends, Birth: BirthMode> {
368    pub sends: Sends,
369    pub creates: Creations<CreateChild<A, Birth::Child>>,
370    pub become_: Become<Ph>,
371}
372
373impl<A, Ph, Sends, Birth> core::fmt::Debug for Actions<A, Ph, Sends, Birth>
374where
375    A: Address + core::fmt::Debug,
376    A::Nonce: core::fmt::Debug,
377    Ph: core::fmt::Debug,
378    Sends: core::fmt::Debug,
379    Birth: BirthMode,
380    Birth::Child: core::fmt::Debug,
381{
382    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
383        formatter
384            .debug_struct("Actions")
385            .field("sends", &self.sends)
386            .field("creates", &self.creates)
387            .field("become", &self.become_)
388            .finish()
389    }
390}
391
392impl<A, Ph, Sends, Birth> PartialEq for Actions<A, Ph, Sends, Birth>
393where
394    A: Address + PartialEq,
395    A::Nonce: PartialEq,
396    Ph: PartialEq,
397    Sends: PartialEq,
398    Birth: BirthMode,
399    Birth::Child: PartialEq,
400{
401    fn eq(&self, other: &Self) -> bool {
402        self.sends == other.sends && self.creates == other.creates && self.become_ == other.become_
403    }
404}
405
406impl<A, Ph, Sends, Birth> Eq for Actions<A, Ph, Sends, Birth>
407where
408    A: Address + Eq,
409    A::Nonce: Eq,
410    Ph: Eq,
411    Sends: Eq,
412    Birth: BirthMode,
413    Birth::Child: Eq,
414{
415}
416
417impl<A: Address, Ph, Sends, Birth: BirthMode> Actions<A, Ph, Sends, Birth> {
418    /// Transform only the send effects, preserving creation order and the
419    /// next-behavior verdict exactly.
420    #[must_use]
421    pub fn map_sends<Mapped>(
422        self,
423        map: impl FnOnce(Sends) -> Mapped,
424    ) -> Actions<A, Ph, Mapped, Birth> {
425        Actions {
426            sends: map(self.sends),
427            creates: self.creates,
428            become_: self.become_,
429        }
430    }
431
432    /// Transform only the next-behavior verdict, preserving sends and
433    /// creation order exactly.
434    #[must_use]
435    pub fn map_become<NextPh>(
436        self,
437        map: impl FnOnce(Become<Ph>) -> Become<NextPh>,
438    ) -> Actions<A, NextPh, Sends, Birth> {
439        Actions {
440            sends: self.sends,
441            creates: self.creates,
442            become_: map(self.become_),
443        }
444    }
445
446    /// Append one communication to its statically selected send lane.
447    ///
448    /// This is a pure transformation of the send leg. It preserves creation
449    /// order and the exact continue, phase-change, or termination verdict.
450    /// `Path` is compile-time lane evidence; no runtime lookup is performed.
451    #[must_use]
452    pub fn with_send<Input, Path>(mut self, input: Input) -> Self
453    where
454        Sends: SendInput<Input, Path>,
455    {
456        <Sends as SendInput<Input, Path>>::emit(&mut self.sends, input);
457        self
458    }
459
460    /// Interpret this complete action value using one statically typed runtime.
461    ///
462    /// Creation items are attempted in vector order before the named send
463    /// product. Lawful creation rejection or blocking is retained and does not
464    /// suppress sends; concrete child-operation interpreters decide whether an
465    /// exact binding prerequisite was satisfied. Interpreter corruption stops
466    /// traversal and retains every later creation and the complete send product
467    /// as unattempted. The next-behavior verdict is never reconstructed.
468    pub async fn interpret<Interpreter, RootEvent, Path>(
469        self,
470        interpreter: &mut Interpreter,
471    ) -> Interpretation<
472        ActionSettlement<Birth::Settlements, <Sends as SendSettlements>::Settlements, Ph>,
473    >
474    where
475        Ph: Send,
476        Sends: InterpretSends<Interpreter, RootEvent, Path>,
477        Birth: InterpretCreations<A, Interpreter, RootEvent, Path>,
478        Interpreter: Send,
479    {
480        let Actions {
481            sends,
482            creates,
483            become_,
484        } = self;
485        let creations = match Birth::interpret_creations(creates, interpreter).await {
486            Interpretation::Complete(creations) => creations,
487            Interpretation::Corrupt(creations) => {
488                return Interpretation::Corrupt(ActionSettlement {
489                    creations,
490                    sends: Sends::unattempted(sends),
491                    become_,
492                });
493            }
494        };
495
496        match Sends::interpret(sends, interpreter).await {
497            Interpretation::Complete(sends) => Interpretation::Complete(ActionSettlement {
498                creations,
499                sends,
500                become_,
501            }),
502            Interpretation::Corrupt(sends) => Interpretation::Corrupt(ActionSettlement {
503                creations,
504                sends,
505                become_,
506            }),
507        }
508    }
509}
510
511impl<A, Ph, Sends, Birth, Input, Path> AppendSend<Input, Path> for Actions<A, Ph, Sends, Birth>
512where
513    A: Address,
514    Birth: BirthMode,
515    Sends: SendInput<Input, Path>,
516{
517    fn append_send(self, input: Input) -> Self {
518        self.with_send::<Input, Path>(input)
519    }
520}
521
522impl<A: Address, Ph, Sends: SendEffects, Birth: BirthMode> Actions<A, Ph, Sends, Birth> {
523    #[must_use]
524    pub const fn new(
525        sends: Sends,
526        creates: Creations<CreateChild<A, Birth::Child>>,
527        become_: Become<Ph>,
528    ) -> Self {
529        Self {
530            sends,
531            creates,
532            become_,
533        }
534    }
535
536    #[must_use]
537    pub fn just(become_: Become<Ph>) -> Self {
538        Self {
539            sends: Sends::empty(),
540            creates: Creations::empty(),
541            become_,
542        }
543    }
544
545    #[must_use]
546    pub fn cont() -> Self {
547        Self::just(Step::Continue)
548    }
549    #[must_use]
550    pub fn stop() -> Self {
551        Self::just(Step::Stop(Stopped))
552    }
553    #[must_use]
554    pub fn goto(phase: Ph) -> Self {
555        Self::just(Step::Goto(phase))
556    }
557
558    /// Continue after emitting the complete declared send product.
559    #[must_use]
560    pub fn send(sends: Sends) -> Self {
561        Self::new(sends, Creations::empty(), Step::Continue)
562    }
563
564    /// Continue after staging the complete declared creation product.
565    #[must_use]
566    pub fn create(creates: Creations<CreateChild<A, Birth::Child>>) -> Self {
567        Self::new(Sends::empty(), creates, Step::Continue)
568    }
569}
570
571impl<A: Address, Ph, Sends, Birth: BirthMode>
572    From<(Sends, Creations<CreateChild<A, Birth::Child>>, Become<Ph>)>
573    for Actions<A, Ph, Sends, Birth>
574{
575    fn from(
576        (sends, creates, become_): (Sends, Creations<CreateChild<A, Birth::Child>>, Become<Ph>),
577    ) -> Self {
578        Self {
579            sends,
580            creates,
581            become_,
582        }
583    }
584}
585
586pub type Acted<A, Ph, Sends, Birth, E> = Result<Actions<A, Ph, Sends, Birth>, E>;
587
588#[cfg(test)]
589mod tests {
590    use super::*;
591    use crate::{Births, CreationSequence, MailAddr, NoBirths, Own};
592
593    fn two_ids() -> (crate::CreationId, crate::CreationId) {
594        let mut sequence = CreationSequence::new();
595        let first = sequence.issue().expect("the first creation ID exists");
596        let second = sequence.issue().expect("the second creation ID exists");
597        (first, second)
598    }
599
600    #[test]
601    fn equality_and_debug_cover_every_named_effect_leg() {
602        type Plain = Actions<MailAddr, u8, Vec<u8>, NoBirths>;
603        type Creating = Actions<MailAddr, Never, Vec<u8>, Births<u8>>;
604
605        let value = Plain::new(vec![1], Creations::empty(), Step::Goto(3));
606        assert_eq!(
607            value,
608            Plain::new(vec![1], Creations::empty(), Step::Goto(3))
609        );
610        assert_ne!(
611            value,
612            Plain::new(vec![2], Creations::empty(), Step::Goto(3))
613        );
614        assert_ne!(
615            value,
616            Plain::new(vec![1], Creations::empty(), Step::Goto(4))
617        );
618        assert_ne!(
619            value,
620            Plain::new(vec![1], Creations::empty(), Step::Continue)
621        );
622        assert_eq!(
623            format!("{value:?}"),
624            "Actions { sends: [1], creates: Creations { items: [] }, become: Goto(3) }"
625        );
626
627        let (first, second) = two_ids();
628        let created = Creating::new(
629            Vec::new(),
630            Creations::one(CreateChild::birth(first, 9)),
631            Step::Continue,
632        );
633        let other_creation = Creating::new(
634            Vec::new(),
635            Creations::one(CreateChild::birth(second, 9)),
636            Step::Continue,
637        );
638        assert_ne!(created, other_creation);
639    }
640
641    #[test]
642    fn mapping_sends_preserves_creation_order_and_verdict() {
643        let (first, second) = two_ids();
644        let actions: Actions<MailAddr, u8, Vec<u8>, Births<()>> = Actions::new(
645            vec![1, 2],
646            Creations::one(CreateChild::birth(first, ())).and(CreateChild::replacement(
647                second,
648                first,
649                (),
650            )),
651            Step::Goto(7),
652        );
653
654        let mapped = actions.map_sends(|sends| sends.len());
655        assert_eq!(mapped.sends, 2);
656        assert_eq!(
657            mapped
658                .creates
659                .iter()
660                .map(CreateChild::id)
661                .collect::<Vec<_>>(),
662            [first, second]
663        );
664        assert!(matches!(mapped.become_, Step::Goto(7)));
665    }
666
667    #[test]
668    fn mapping_become_preserves_sends_and_creation_order() {
669        let (first, _) = two_ids();
670        let actions: Actions<MailAddr, u8, Vec<u8>, Births<()>> = Actions::new(
671            vec![1, 2],
672            Creations::one(CreateChild::birth(first, ())),
673            Step::Goto(7),
674        );
675
676        let mapped: Actions<MailAddr, Never, Vec<u8>, Births<()>> =
677            actions.map_become(|_| Step::Stop(Stopped));
678        assert_eq!(mapped.sends, [1, 2]);
679        assert_eq!(
680            mapped.creates.iter().next().map(CreateChild::id),
681            Some(first)
682        );
683        assert!(matches!(mapped.become_, Step::Stop(Stopped)));
684    }
685
686    #[test]
687    fn fluent_send_changes_only_the_selected_effect_leg() {
688        let (first, second) = two_ids();
689        let actions: Actions<MailAddr, u8, Vec<u8>, Births<()>> = Actions::new(
690            vec![1],
691            Creations::one(CreateChild::birth(first, ())).and(CreateChild::replacement(
692                second,
693                first,
694                (),
695            )),
696            Step::Goto(7),
697        )
698        .with_send::<_, Own>(2)
699        .with_send::<_, Own>(3);
700
701        assert_eq!(actions.sends, [1, 2, 3]);
702        assert_eq!(
703            actions
704                .creates
705                .iter()
706                .map(CreateChild::id)
707                .collect::<Vec<_>>(),
708            [first, second]
709        );
710        assert!(matches!(actions.become_, Step::Goto(7)));
711
712        let stopped: Actions<MailAddr, Never, Vec<u8>, NoBirths> =
713            Actions::stop().with_send::<_, Own>(5);
714        assert_eq!(stopped.sends, [5]);
715        assert!(matches!(stopped.become_, Step::Stop(Stopped)));
716    }
717}