odem_rs_core/
agent.rs

1//! This module provides the `Agent` structure, representing an isolated
2//! execution context within the simulation, along with supporting traits
3//! and types.
4//!
5//! # Overview
6//!
7//! An `Agent` acts as the root of a potential tree of [jobs](Job), managing its
8//! own isolated state. This isolation ensures that an agent's internal
9//! execution is independent of the lexical context where it was created and
10//! prevents side effects from influencing its behavior, except through
11//! controlled simulation interactions (like scheduling events or waiting).
12//!
13//! Agent isolation is primarily achieved through the [`Actions`] trait and its
14//! use of Higher-Ranked Trait Bounds (HRTBs). This mechanism prevents the
15//! agent's core asynchronous logic (its "lifecycle") from capturing references
16//! to non-static data from its creation environment.
17//!
18//! # Key Parts
19//!
20//!  - **Agent:** The [`Agent`] struct encapsulates an isolated execution
21//!    context. It holds the agent's state and manages its lifecycle via an
22//!    internal state machine. It automatically dereferences to its internal
23//!    state, but mutable access is only possible *before* the agent is
24//!    activated (enforced at compile-time via borrowing rules around
25//!    [`Sim::activate`]).
26//!
27//!  - **Behavior:** The [`Behavior`] trait offers a user-friendly way to define
28//!    an agent's actions. Implementing this trait for a type allows instances
29//!    of that type to be easily converted into an [`Agent`]. It specifies the
30//!    agent's return type (`Output`) and provides the core `async fn actions`.
31//!    A blanket implementation exists for tuples `(I, A)` where `I` is the
32//!    state and `A` is a compatible async function, allowing agent definition
33//!    on foreign types.
34//!
35//!  - **Actions:** The [`Actions`] trait is the core abstraction defining an
36//!    agent's lifecycle logic. It uses HRTBs (`for<'p>`) over the private
37//!    `Action` trait to ensure isolation. It's parameterized on the simulation
38//!    configuration and the agent's state type. Users cannot implement this
39//!    directly but rely on implementations generated via the [`Behavior`] trait
40//!    or the `(I, A)` tuple blanket implementation.
41//!
42//!  - **Builder:** The [`Builder`] provides a fluent API for constructing
43//!    [`Agent`]s with detailed configuration options, such as a custom name,
44//!    initial scheduling rank, source code location tracking, and custom
45//!    finalization logic ([`Settle`]).
46//!
47//!  - **Puck:** An [`agent::Puck`](Puck) is the handle returned when an
48//!    [`Agent`] is activated via [`Sim::activate`]. It allows interaction with
49//!    the running agent, such as awaiting its completion, checking its status,
50//!    accessing its shared state, or aborting it.
51//!
52//! # Examples
53//!
54//! ## Simple agent using `Behavior`
55//!
56//! ```
57//! # use odem_rs_core::{agent::{Agent, Behavior}, simulator::Sim};
58//! # use core::pin::pin;
59//! struct MyData(usize);
60//!
61//! impl Behavior for MyData {
62//!     type Output = (); // This agent doesn't return a meaningful value
63//!
64//!     async fn actions(&self, sim: &Sim) {
65//!         println!("Agent starting with data: {}", self.0);
66//!         sim.advance(10.0).await; // Simulate work
67//!         println!("Agent finished.");
68//!     }
69//! }
70//!
71//! async fn sim_main(sim: &Sim) {
72//!     // Create the agent state
73//!     let data = MyData(42);
74//!     // Create the agent using Agent::new, which leverages the Behavior impl
75//!     let agent = pin!(Agent::new(data));
76//!     // Activate the agent and get a handle (Puck)
77//!     let handle = sim.activate(agent);
78//!     // We can wait for the agent to finish (though it returns ())
79//!     handle.await;
80//! }
81//! ```
82//!
83//! ## Agent returning a value
84//!
85//! ```
86//! # use odem_rs_core::{agent::{Agent, Behavior}, simulator::Sim};
87//! # use core::pin::pin;
88//! struct Calculator(i32, i32);
89//!
90//! impl Behavior for Calculator {
91//!     type Output = i32; // This agent returns a calculation result
92//!
93//!     async fn actions(&self, sim: &Sim) -> Self::Output {
94//!         sim.advance(1.0).await; // Simulate calculation time
95//!         self.0 + self.1
96//!     }
97//! }
98//!
99//! async fn sim_main(sim: &Sim) {
100//!     let agent = pin!(Agent::new(Calculator(10, 5)));
101//!     // Activate and await the result directly
102//!     let result: i32 = sim.activate(agent).await;
103//!     assert_eq!(result, 15);
104//!     println!("Calculation result: {}", result);
105//! }
106//! ```
107//!
108//! ## Agent defined using a tuple `(State, AsyncFn)`
109//!
110//! This uses the blanket implementation `Behavior for (I, A)`.
111//!
112//! ```
113//! # use odem_rs_core::{agent::Agent, simulator::Sim};
114//! # use core::pin::pin;
115//! struct SharedState { id: u32 }
116//!
117//! // Define the async logic as a standalone function or inherent method
118//! async fn agent_logic(state: &SharedState, sim: &Sim) -> u32 {
119//!     println!("Agent {} starting", state.id);
120//!     sim.advance(5.0).await;
121//!     println!("Agent {} finished", state.id);
122//!     state.id * 2
123//! }
124//!
125//! async fn sim_main(sim: &Sim) {
126//!     let state = SharedState { id: 101 };
127//!     // Create agent by pairing state and the async function
128//!     let agent = pin!(Agent::new((state, agent_logic)));
129//!     let result: u32 = sim.activate(agent).await;
130//!     assert_eq!(result, 202);
131//! }
132//! ```
133//!
134//! ## Agent configured with the Builder
135//!
136//! ```
137//! # use odem_rs_core::{agent::{Agent, Behavior}, config::Config, simulator::Sim, job::Checked};
138//! # use core::pin::pin;
139//! # #[derive(Config)] struct MyConfig { #[rank] rank: i32 }
140//! struct Task { data: &'static str }
141//!
142//! impl Task {
143//!     async fn actions(&self, sim: &Sim<MyConfig>) -> Result<String, &'static str> {
144//!         if self.data.is_empty() {
145//!             Err("Data cannot be empty")
146//!         } else {
147//!             sim.advance(2.0).await;
148//!             Ok(format!("Processed: {}", self.data))
149//!         }
150//!     }
151//! }
152//!
153//! async fn sim_main(sim: &Sim<MyConfig>) {
154//!     let task = Task { data: "Valid Data" };
155//!     
156//!     let agent = Agent::build()
157//!         .with_subject(task)          // Does not require `impl Behavior`
158//!         .with_actions(Task::actions) // Provide the async fn
159//!         .with_name("MyTask")         // Custom name
160//!         .with_rank(10)               // Custom rank (C::Rank=i32)
161//!         .checked()                   // Use checked finalization (for Result/Option/bool)
162//!         .finish();                   // Get the Lease<'p, Agent<...>>
163//!
164//!     let pinned_agent = pin!(agent);
165//!     let result = sim.activate(pinned_agent).await;
166//!
167//!     match result {
168//!         Ok(output) => println!("Task succeeded: {}", output),
169//!         Err(e) => println!("Task failed: {}", e),
170//!     }
171//!     // Note: .checked() ensures the ExitStatus reflects the Ok/Err outcome.
172//!     // Accessing the result requires the Agent's Puck or awaiting it.
173//! }
174//! ```
175
176use core::{
177	any::{Any, type_name},
178	future::{Future, IntoFuture, Pending, pending},
179	marker::PhantomData,
180	ops::{Deref, DerefMut},
181	panic::Location,
182	pin::Pin,
183	task::{Context, Poll},
184};
185
186use crate::{
187	Active, Dispatch, ExitStatus,
188	config::Config,
189	continuation::{Continuation, Label, Share, erased::State as ContState},
190	error,
191	job::{Builder as JobBuilder, Checked, Job, Puck as JobPuck, Settle, Unchecked},
192	ptr::{AsIrc, Irc, Lease, LeasedMut},
193	simulator::{Prec, Sim},
194};
195
196/* *************************************************** Internal Actions-Trait */
197
198/// Defines the primary asynchronous behavior and output type for an [`Agent`].
199///
200/// This trait provides a convenient way to specify what an agent does. By
201/// implementing `Behavior` for a type `T`, you allow `Agent::new(T)` to
202/// automatically construct an agent using the provided `actions` method.
203///
204/// The `name` method determines the default base name for the agent's label,
205/// defaulting to the type name of `T`.
206///
207/// A blanket implementation `Behavior<C> for (I, A)` exists, allowing you to
208/// define agents by simply pairing a state type `I` with an appropriate
209/// `async fn(&I, &Sim<C>) -> R` function, without needing a separate `impl`
210/// block or a newtype wrapper.
211///
212/// # Examples
213///
214/// ```
215/// # use odem_rs_core::{agent::{Agent, Behavior}, simulator::Sim};
216/// # use core::pin::pin;
217/// struct MyAgentState(f64);
218///
219/// impl Behavior for MyAgentState {
220///     type Output = (); // No return value
221///
222///     async fn actions(&self, sim: &Sim) {
223///         println!("Waiting for {} time units.", self.0);
224///         sim.advance(self.0).await;
225///         println!("Done waiting.");
226///     }
227/// }
228/// # async fn run(sim: &Sim) {
229/// // Use Agent::new thanks to the Behavior implementation
230/// let agent = pin!(Agent::new(MyAgentState(5.0)));
231/// sim.activate(agent).await;
232/// # }
233/// ```
234///
235/// Using the tuple blanket implementation:
236/// ```
237/// # use odem_rs_core::{agent::Agent, simulator::Sim};
238/// # use core::pin::pin;
239/// struct MyState(u32);
240/// async fn my_async_fn(state: &MyState, sim: &Sim) { /* ... */ }
241/// # async fn run(sim: &Sim) {
242/// // Use Agent::new with a tuple (state, function)
243/// let agent = pin!(Agent::new((MyState(10), my_async_fn)));
244/// sim.activate(agent);
245/// # }
246/// ```
247pub trait Behavior<C: ?Sized + Config = ()> {
248	/// The type returned by the agent's `actions` method upon successful
249	/// completion.
250	type Output;
251
252	/// The core asynchronous logic of the agent.
253	///
254	/// This method receives an immutable reference to the agent's state and the
255	/// simulation context. It defines the sequence of operations and simulation
256	/// interactions (like `sim.advance()`) the agent performs.
257	///
258	/// The returned `Future` cannot capture non-`'static` data from the
259	/// surrounding environment, enforced implicitly by how [`Agent`] constructs
260	/// the underlying task. `Send` is not required as simulations are
261	/// single-threaded.
262	#[allow(async_fn_in_trait)]
263	async fn actions(&self, sim: &Sim<C>) -> Self::Output;
264
265	/// Returns the base name for this agent type.
266	///
267	/// Defaults to the [`type_name`] of `Self`. Override this if a different
268	/// base name is desired for logging or identification.
269	fn name(&self) -> &'static str {
270		type_name::<Self>()
271	}
272}
273
274/// Abstracts over the asynchronous lifecycle logic of an [`Agent`].
275///
276/// This trait is the core mechanism for ensuring agent isolation. It uses a
277/// Higher-Ranked Trait Bound (HRTB) over the sealed `Action` trait.
278/// This HRTB (`for<'p> ...`) ensures that the future produced by the agent's
279/// logic (`bind` method in `Action`) can only borrow data with lifetime `'p`
280/// (namely, the agent's state `&'p I` and the sim context `&'p Sim<C>`),
281/// preventing it from capturing references from the surrounding lexical scope
282/// where the agent was created.
283///
284/// Users cannot implement this trait directly. Instead, they use types that
285/// implement [`Behavior`], or tuples `(I, async_fn)`, which have blanket
286/// implementations providing the necessary `Actions` implementation.
287///
288/// The main purpose of this trait is to act as a type parameter bound for
289/// [`Agent`], allowing the agent's lifecycle (an unnameable `Future` type)
290/// to be handled generically without needing to explicitly name it.
291pub trait Actions<C: ?Sized + Config, I: ?Sized + Any>:
292	// The implementation must satisfy `private::Action` for *any* lifetime 'p.
293	for<'p> private::Action<'p, C, I, Future: Future<Output = Self::Output>>
294{
295	/// The result type produced by the agent's lifecycle future when it
296	/// completes.
297	type Output;
298}
299
300mod private {
301	use super::*;
302
303	/// **(Private)** Defines the function signature for creating an agent's
304	/// lifecycle future.
305	///
306	/// This trait is an internal detail used by the [`Actions`] trait's HRTB.
307	/// It represents a function that can be called once with temporarily
308	/// borrowed references to the agent's state and the simulation context.
309	/// It returns a [`Future`] that is allowed to borrow these inputs for the
310	/// duration of lifetime `'p`.
311	///
312	/// The HRTB in [`Actions`] ensures that this `'p` lifetime cannot be tied
313	/// to anything outside the scope of the `bind` call itself, thus enforcing
314	/// agent isolation.
315	///
316	/// [HRTB]: https://doc.rust-lang.org/nomicon/hrtb.html
317	pub trait Action<'p, C: ?Sized + Config, I: ?Sized + Any> {
318		/// The type of `Future` returned by `bind`. Must live at least as long
319		/// as `'p`.
320		type Future: Future + 'p;
321
322		/// Creates the agent's lifecycle `Future`.
323		///
324		/// This method consumes the action provider (`self`) and uses the
325		/// provided borrowed state (`item`) and simulation context (`sim`)
326		/// to construct the `Future` that represents the agent's execution.
327		fn bind(self, item: &'p I, sim: &'p Sim<C>) -> Self::Future;
328	}
329}
330
331/* ****************************************************************** Agent */
332
333/// Represents an isolated execution context (an *agent*) within the simulation.
334///
335/// An `Agent` encapsulates:
336///   - Subject: The data associated with this agent.
337///   - Lifecycle: The asynchronous logic defining its behavior.
338///   - Execution State: Manages whether the agent is ready, running, or finished.
339///
340/// Agents provide isolation: their internal logic, defined via the [`Actions`]
341/// (and typically [`Behavior`]) trait, cannot directly access or be influenced
342/// by the lexical scope where the `Agent` struct was created, due to the HRTB
343/// mechanism employed by [`Actions`]. Communication happens through simulation
344/// primitives (scheduling, waiting) and the agent's final `Output`.
345///
346/// `Agent` implements [`Deref`] and [`DerefMut`] to its subject.
347/// However, mutable access is only practically possible *before* the agent is
348/// passed to [`Sim::activate`], as activation blocks the access path. This is
349/// enforced by Rust's borrowing rules at compile time.
350///
351/// Agents are typically created using [`Agent::new`] (for types implementing
352/// [`Behavior`]) or [`Agent::build`] for more configuration options. They must
353/// be pinned (e.g., using [`core::pin::pin!`]) before being passed to
354/// [`Sim::activate`].
355///
356/// The generic parameter `S` defines the [`Settle`] strategy, controlling how
357/// the agent's `Output` is translated into an [`ExitStatus`]. [`Unchecked`] is
358/// the default, treating all outcomes as success. [`Checked`] interprets
359/// `Result`, `Option`, and `bool` as success/failure indicators.
360///
361/// # Type Parameters
362///
363/// - `C`: The simulation configuration type ([`Config`]).
364/// - `I`: The type of the agent's internal state (the "item" or "subject").
365/// - `A`: The type implementing [`Actions`], defining the agent's lifecycle logic.
366/// - `S`: The [`Settle`] strategy (defaults to [`Unchecked`]).
367///
368/// # Safety Warning
369///
370/// The order of fields `inner` and `item` **must not** be changed. `inner` must
371/// come *before* `item`. Reversing this order will lead to use-after-free and
372/// undefined behavior because the `Share` within `Inner::Live` internally holds
373/// a pointer derived from `item`, and drop order relies on `inner` being
374/// dropped first.
375#[pin_project::pin_project(!Unpin)]
376pub struct Agent<C: ?Sized + Config, I: ?Sized + Any, A: Actions<C, I>, S = Unchecked> {
377	/// Internal state machine (`Born`, `Bust`, `Live`). Must be declared before
378	/// `item`.
379	#[pin]
380	inner: Inner<C, I, A, S>,
381
382	/// The user-provided state associated with this agent.
383	#[pin]
384	item: I,
385}
386
387/// Internal runtime states of an [`Agent`].
388///
389/// - `Born`: The initial state before activation. Holds the configuration
390///   needed to construct the agent's lifecycle future.
391/// - `Bust`: A transient state entered if the `action.bind()` call panics
392///   during activation. The agent is effectively dead if observed in this state.
393/// - `Live`: The state after successful activation. Contains the running root
394///   [`Job`] and the shared context ([`Share`]) for the agent's job tree.
395///
396/// # Safety Warning
397///
398/// - Field Order (within `Live` variant): The order of fields `job` and `share`
399///   **must not** be changed. `job` must come *before* `share`. Reversing this
400///   order can lead to undefined behavior because the `job`'s future may hold
401///   references derived from data pointed to by `share` (specifically, the
402///   agent's `item` and `Sim` context). Drop order guarantees that `job` is
403///   dropped before `share`, ensuring these references are not dangling.
404#[pin_project::pin_project(
405	project = StateProject,
406	project_ref = StateProjectRef,
407	project_replace = StateOwn
408)]
409enum Inner<C: ?Sized + Config, I: ?Sized + Any, A: Actions<C, I>, S> {
410	/// Initial state before activation.
411	Born {
412		/// The action provider that will create the agent's lifecycle future.
413		action: A,
414		/// The base name used for the agent's label.
415		name: &'static str,
416		/// Optional initial scheduling rank. Defaults according to `C`.
417		rank: Option<C::Rank>,
418		/// Partially configured builder for the root job, awaiting the future.
419		builder: JobBuilder<true, (), S>,
420	},
421	/// Transient state if `action.bind()` panics during activation.
422	Bust,
423	/// State after successful activation, agent is running or completed.
424	Live {
425		/// The root job executing the agent's lifecycle future. Must be
426		/// declared before `share`.
427		#[pin]
428		job: RootJob<'static, C, I, A, S>,
429		/// Shared context data (sim, subject ptr, rank, label) for this agent
430		/// and its jobs.
431		#[pin]
432		share: Share<C>,
433	},
434}
435
436/// Type alias for the `Future` created by the agent's [`Actions`].
437type RootFuture<'l, C, I, A> = <A as private::Action<'l, C, I>>::Future;
438
439/// Type alias for the root [`Job`] wrapping the agent's `RootFuture`.
440type RootJob<'l, C, I, A, S> = Job<C, RootFuture<'l, C, I, A>, S>;
441
442/// Type alias for the [`JobPuck`] associated with the agent's `RootJob`.
443type RootPuck<'l, C, I, A, S> = JobPuck<'l, C, RootFuture<'l, C, I, A>, S>;
444
445impl Agent<(), (), Pending<()>> {
446	/// Creates a new [`Agent`] using a type that implements [`Behavior`].
447	///
448	/// This is the simplest way to create an agent. It infers the agent's
449	/// actions, output type, and name from the `Behavior` implementation of
450	/// `I`. The agent uses the default [`Unchecked`] settle strategy, meaning
451	/// its `ExitStatus` will always be `Ok` unless it panics or is aborted,
452	/// regardless of the `Output` type (even if it's a `Result::Err`).
453	///
454	/// The provided `subject` (of type `I`) becomes the agent's internal state.
455	///
456	/// Returns a [`Lease`], which must be pinned before activation.
457	///
458	/// # Example
459	///
460	/// ```
461	/// # use odem_rs_core::{agent::{Agent, Behavior}, simulator::Sim};
462	/// # use core::pin::pin;
463	/// struct MyAgent;
464	/// impl Behavior for MyAgent {
465	///     type Output = ();
466	///     async fn actions(&self, sim: &Sim) { /* ... */ }
467	/// }
468	/// # fn make_agent() {
469	/// let agent_lease = Agent::new(MyAgent);
470	/// let pinned_agent = pin!(agent_lease);
471	/// // Pass pinned_agent to sim.activate(...)
472	/// # }
473	/// ```
474	#[track_caller]
475	pub fn new<'p, C, I>(
476		subject: I,
477	) -> Lease<'p, Agent<C, I, impl Actions<C, I, Output = I::Output> + use<C, I>>>
478	where
479		C: ?Sized + Config,
480		I: Any + Behavior<C>,
481	{
482		Self::build()
483			.with_name(subject.name())
484			.with_subject(subject)
485			.with_actions(I::actions)
486			.finish()
487	}
488
489	/// Creates a [`Builder`] for configuring an [`Agent`] instance.
490	///
491	/// Use the builder when you need to customize options like the agent's name,
492	/// initial rank, settle strategy ([`Checked`]), or source code location,
493	/// or when defining the agent using a state/function tuple instead of a
494	/// dedicated `Behavior` implementation.
495	///
496	/// # Example
497	///
498	/// ```
499	/// # use odem_rs_core::{agent::Agent, simulator::Sim, config::Config};
500	/// # #[derive(Config)] struct MyConfig { #[rank] rank: i32 }
501	/// # struct MyState;
502	/// # async fn my_actions(_s: &MyState, _sim: &Sim<MyConfig>) {}
503	/// # fn build_agent() {
504	/// let agent = Agent::build()
505	///     .with_subject(MyState)
506	///     .with_actions(my_actions)
507	///     .with_name("CustomAgent")
508	///     .finish();
509	/// // Pin and activate agent...
510	/// # }
511	/// ```
512	pub const fn build<C: ?Sized + Config>() -> Builder<C> {
513		Builder::new()
514	}
515}
516
517impl<C, I, A, S> Deref for Agent<C, I, A, S>
518where
519	C: ?Sized + Config,
520	I: ?Sized + Any,
521	A: Actions<C, I>,
522{
523	type Target = I;
524
525	fn deref(&self) -> &Self::Target {
526		&self.item
527	}
528}
529
530impl<C, I, A, S> DerefMut for Agent<C, I, A, S>
531where
532	C: ?Sized + Config,
533	I: ?Sized + Any,
534	A: Actions<C, I>,
535{
536	fn deref_mut(&mut self) -> &mut Self::Target {
537		&mut self.item
538	}
539}
540
541impl<C, I, A, S> Dispatch for Agent<C, I, A, S>
542where
543	C: ?Sized + Config,
544	I: ?Sized + Any,
545	A: Actions<C, I>,
546	S: Settle<A::Output>,
547{
548	fn poll(self: Pin<&Self>, cx: &mut Context<'_>) -> Poll<ExitStatus> {
549		// Project to the 'inner' field
550		match self.project_ref().inner.project_ref() {
551			// If the agent is live, poll its root job
552			StateProjectRef::Live { job, .. } => job.poll(cx),
553			// Should not be polled in Born or Bust state.
554			StateProjectRef::Born { .. } => {
555				unreachable!("Agent::poll called before activation (state is Born)")
556			}
557			StateProjectRef::Bust => {
558				unreachable!("Agent::poll called after activation panicked (state is Bust)")
559			}
560		}
561	}
562}
563
564impl<C, I, A, S> Active<C> for Agent<C, I, A, S>
565where
566	C: ?Sized + Config,
567	I: Any,
568	A: Actions<C, I>,
569	S: Settle<A::Output>,
570{
571	type Output = A::Output;
572	type Puck<'p>
573		= Puck<'p, C, I, A, S>
574	where
575		Self: 'p;
576
577	/// Binds the agent to the simulation context, transitioning it from `Born`
578	/// to `Live`.
579	///
580	/// This method is called internally by [`Sim::activate`]. It performs the
581	/// critical steps:
582	/// 1. Temporarily replaces the `Inner::Born` state with `Inner::Bust`.
583	/// 2. Calls the `action.bind()` method (from `private::Action`) to create
584	///    the agent's lifecycle future (`RootFuture`).
585	/// 3. If `bind` succeeds, creates the `Share` context and the `RootJob`.
586	/// 4. Replaces the `Inner::Bust` state with `Inner::Live`, containing the
587	///    `RootJob` and `Share`.
588	/// 5. Returns the agent's [`Puck`] handle.
589	///
590	/// If `action.bind()` panics, the agent remains in the `Inner::Bust` state.
591	///
592	/// # Safety
593	/// Relies on `unsafe` blocks for lifetime transmutation (`'p` to `'static`
594	/// and back) and creating the `Share` context. These are justified because:
595	/// - The `'static` lifetime on `RootJob` within `Inner::Live` is an
596	///   internal implementation detail; the actual future only borrows data
597	///   for `'p`. The lifetime is transmuted back to `'p` when creating the
598	///   `Puck`.
599	/// - `Share::new` requires ensuring the `item` pointer outlives `Share`.
600	///   This is guaranteed by the struct field ordering (`inner` before
601	///   `item`), and Rust's drop order guarantees (`inner` is dropped first).
602	fn bind<'p>(this: Pin<LeasedMut<'p, Self>>, sctx: &'p Share<C>) -> Self::Puck<'p> {
603		let mut this = this.project().project();
604
605		// replace the `Born` state by `Bust` for the transition
606		match this.inner.as_mut().project_replace(Inner::Bust) {
607			StateOwn::Born {
608				action,
609				name,
610				rank,
611				builder,
612			} => {
613				use core::mem::transmute;
614
615				// Get references needed for `action.bind` and `Share::new`.
616				let item_ref: &'p I = this.item.into_ref().get_ref();
617				let sim: &'p Irc<Sim<C>> = sctx.sim();
618				let pid = sim.pid_gen::<I>();
619
620				// Optional: Tracing span for the agent's lifetime
621				#[cfg(feature = "tracing")]
622				let _span = tracing::error_span!(
623					parent: None, "Agent",
624					label = %Label { name, pid: Some(pid) }
625				)
626				.entered();
627
628				// *** Critical Section Start ***
629				// Call the action's bind method to create the actual lifecycle
630				// future. This is the point that might panic, leaving the agent
631				// in `Bust` state.
632				let lifecycle = action.bind(item_ref, sim);
633				// *** Critical Section End ***
634
635				// Complete the root job builder with the created future
636				let root_job = builder.with_actions(lifecycle).finish();
637
638				// Now, transition from Bust to Live state.
639				this.inner.set(Inner::Live {
640					// SAFETY: Transmuting lifetime from 'p to 'static for storage.
641					// The actual future inside `RootJob` correctly captures 'p.
642					// We transmute back to 'p when returning the Puck.
643					job: unsafe {
644						transmute::<RootJob<'p, C, I, A, S>, RootJob<'static, C, I, A, S>>(
645							root_job.into_inner(),
646						)
647					},
648					// SAFETY: `Share::new` takes a pointer to `item`. This is
649					// safe because `item` (in the `Agent` struct) outlives
650					// `share` (in the Inner struct) due to field declaration
651					// order and drop order guarantees.
652					// `inner` is dropped before `item`.
653					share: unsafe {
654						Share::new(
655							sim.clone(),
656							item_ref,
657							rank.unwrap_or(sim.config().default_rank()),
658							name,
659							pid,
660						)
661					},
662				});
663
664				// Project the now guaranteed `Live` state to get Pin<&mut Job>
665				// and Pin<&mut Share>
666				match this.inner.project() {
667					StateProject::Live { job, share } => {
668						// Bind the root job to its share context
669
670						// SAFETY: Transmuting the 'static lifetime back to 'p
671						// is safe because the underlying future only borrows
672						// for 'p. Adding LeasedMut wrapper is safe as this
673						// specific `job` reference is consumed here.
674						let job_pin = unsafe {
675							transmute::<
676								Pin<&mut RootJob<'static, C, I, A, S>>,
677								Pin<LeasedMut<'p, RootJob<'p, C, I, A, S>>>,
678							>(job)
679						};
680
681						let share_ref = share.into_ref().get_ref();
682
683						// Create the Job's Puck by binding it
684						let job_puck = Active::bind(job_pin, share_ref);
685
686						// Wrap the JobPuck in our Agent::Puck
687						Puck(job_puck)
688					}
689					_ => unreachable!("State should be Live after successful transition"),
690				}
691			}
692			StateOwn::Live { .. } | StateOwn::Bust => {
693				unreachable!("Agent::bind called on an already bound or busted agent")
694			}
695		}
696	}
697}
698
699/* ************************************************************* Agent Puck */
700
701/// A handle to an activated [`Agent`], returned by [`Sim::activate`].
702///
703/// This handle allows interaction with the running (or completed) agent. It
704/// wraps the [`JobPuck`] of the agent's root job, providing access to
705/// agent-specific information and actions.
706///
707/// The `Puck` implements [`IntoFuture`], allowing you to `.await` it to get the
708/// agent's final `Output`. It also implements [`crate::Puck`], providing common
709/// methods for interacting with active simulation entities (checking state,
710/// time, etc.).
711pub struct Puck<'p, C: ?Sized + Config, I: ?Sized + Any, A: Actions<C, I>, S>(
712	RootPuck<'p, C, I, A, S>,
713);
714
715impl<C, I, A, S> Puck<'_, C, I, A, S>
716where
717	C: ?Sized + Config,
718	I: ?Sized + Any,
719	A: Actions<C, I>,
720	S: Settle<A::Output>,
721{
722	/// Sets the rank of the root job.
723	///
724	/// The new rank takes immediate effect and causes the rearrangement
725	/// of all jobs currently scheduled, both in the present and future.
726	///
727	/// Lowering the rank of the active `Agent` can lead to another `Agent`
728	/// gaining control if one with a higher rank after the change has jobs
729	/// scheduled at the current model time. This change takes effect once the
730	/// currently active agent suspends.
731	pub fn update_rank(&self, rank: C::Rank) {
732		self.0.share().update_rank(rank);
733	}
734
735	/// Returns an immutable reference to the agent's internal state (`item`).
736	///
737	/// This allows observing the agent's state while it's running or after it
738	/// has completed. Mutable access is not possible via the `Puck`.
739	pub fn subject(&self) -> &I
740	where
741		I: Sized,
742	{
743		use crate::Puck;
744
745		self.0.subject().downcast_ref::<I>().unwrap()
746	}
747
748	/// Aborts the agent's execution prematurely.
749	///
750	/// This terminates the agent's root job and any descendant jobs.
751	/// The agent's `ExitStatus` will reflect the abortion. The `Output` value
752	/// will not be produced.
753	///
754	/// Consumes the `Puck` to prevent further interaction after aborting.
755	pub fn abort(self) {
756		self.0.abort();
757	}
758}
759
760// Allow awaiting the Agent::Puck to get the final result.
761impl<C, I, A, S> IntoFuture for Puck<'_, C, I, A, S>
762where
763	C: ?Sized + Config,
764	I: ?Sized + Any,
765	A: Actions<C, I>,
766	S: Settle<A::Output>,
767{
768	type Output = A::Output;
769	type IntoFuture = crate::ops::Join<C, Self>;
770
771	fn into_future(self) -> Self::IntoFuture {
772		crate::ops::join(self)
773	}
774}
775
776// Implement the base Puck trait for Agent::Puck by delegating.
777impl<C, I, A, S> crate::Puck<C> for Puck<'_, C, I, A, S>
778where
779	C: ?Sized + Config,
780	I: ?Sized + Any,
781	A: Actions<C, I>,
782	S: Settle<A::Output>,
783{
784	fn result(&mut self) -> Option<Self::Output> {
785		self.0.result()
786	}
787
788	fn wake(&mut self) -> Result<(), error::NotIdle> {
789		self.0.wake()
790	}
791
792	fn subject(&self) -> &dyn Any {
793		self.0.subject()
794	}
795
796	fn sim(&self) -> &Sim<C> {
797		self.0.sim()
798	}
799
800	fn label(&self) -> Label {
801		self.0.label()
802	}
803
804	fn time(&self) -> Option<C::Time> {
805		self.0.time()
806	}
807
808	fn rank(&self) -> C::Rank {
809		self.0.rank()
810	}
811
812	fn prec(&self) -> Prec {
813		self.0.prec()
814	}
815
816	fn state(&self) -> ContState {
817		self.0.state()
818	}
819
820	fn location(&self) -> &'static Location<'static> {
821		self.0.location()
822	}
823}
824
825// Allow getting a reference to the underlying Continuation.
826impl<C, I, A, S> AsRef<Continuation<'static, C>> for Puck<'_, C, I, A, S>
827where
828	C: ?Sized + Config,
829	I: ?Sized + Any,
830	A: Actions<C, I>,
831{
832	fn as_ref(&self) -> &Continuation<'static, C> {
833		self.0.as_ref()
834	}
835}
836
837// Allow getting an intrusive reference counter (Irc) to the Continuation.
838impl<C, I, A, S> AsIrc<Continuation<'static, C>> for Puck<'_, C, I, A, S>
839where
840	C: ?Sized + Config,
841	I: ?Sized + Any,
842	A: Actions<C, I>,
843{
844	fn as_irc(&self) -> Irc<Continuation<'static, C>> {
845		self.0.as_irc()
846	}
847}
848
849/* ********************************************************** Agent Builder */
850
851/// A builder pattern for configuring and creating [`Agent`] instances.
852///
853/// Use the builder when you need more control over the agent's properties than
854/// [`Agent::new`] provides, such as setting a custom name, initial rank,
855/// source location, or finalization strategy ([`Settle`]).
856///
857/// Start with [`Agent::build()`] and chain `with_*` methods to configure,
858/// finally calling [`finish()`](Builder::finish) to create the [`Agent`]
859/// wrapped in a [`Lease`].
860///
861/// # Type Parameters
862///
863/// - `C`: The simulation configuration ([`Config`]).
864/// - `I`: The type of the agent's state (`item`). Initially `()`.
865/// - `A`: The type implementing [`Actions`]. Initially `()`.
866/// - `S`: The [`Settle`] strategy. Initially [`Unchecked`].
867pub struct Builder<C: ?Sized + Config, I = (), A = (), S = Unchecked> {
868	/// The agent's state (subject). Set via `with_subject`.
869	subject: I,
870	/// The agent's lifecycle logic provider. Set via `with_actions`.
871	actions: A,
872	/// Optional custom base name for the agent's label. Set via `with_name`.
873	name: Option<&'static str>,
874	/// Optional initial scheduling rank. Set via `with_rank`.
875	rank: Option<C::Rank>,
876	/// The finalization strategy. Set via `with_finalizer` or `checked`.
877	settle: S,
878	/// Optional source code location.
879	/// Set via `with_location` or implicitly by `with_actions`.
880	location: Option<&'static Location<'static>>,
881	/// Marker for the configuration type `C`.
882	_config: PhantomData<C>,
883}
884
885impl<C: ?Sized + Config> Builder<C, (), ()> {
886	/// Creates a new, empty agent builder.
887	/// Called via [`Agent::build()`].
888	const fn new() -> Self {
889		Builder {
890			subject: (),
891			actions: (),
892			name: None,
893			rank: None,
894			settle: Unchecked,
895			location: None,
896			_config: PhantomData,
897		}
898	}
899}
900
901// Methods to configure the Builder
902impl<C: ?Sized + Config, I: Any, A, S> Builder<C, I, A, S> {
903	/// Sets the state (`item`) for the agent being built.
904	pub fn with_subject<X: Any>(self, object: X) -> Builder<C, X, A, S> {
905		Builder {
906			subject: object,
907			actions: self.actions,
908			name: self.name,
909			rank: self.rank,
910			settle: self.settle,
911			location: self.location,
912			_config: self._config,
913		}
914	}
915
916	/// Sets the lifecycle logic (actions) for the agent.
917	///
918	/// This accepts any type `X` that implements [`Actions<C, I>`], where `I`
919	/// is the type set by `with_subject`. Typically, this is an `async fn`
920	/// reference compatible with the state `I`, or the `actions` method from a
921	/// `Behavior` impl.
922	///
923	/// This method also captures the caller's source code location using
924	/// `#[track_caller]` as the default location for the agent, unless
925	/// explicitly overridden by `with_location`.
926	#[track_caller]
927	pub fn with_actions<X>(self, actions: X) -> Builder<C, I, X, S>
928	where
929		X: Actions<C, I>,
930	{
931		Builder {
932			subject: self.subject,
933			actions,
934			name: self.name,
935			rank: self.rank,
936			settle: self.settle,
937			location: Some(self.location.unwrap_or_else(Location::caller)),
938			_config: self._config,
939		}
940	}
941
942	/// Explicitly sets the source code [`Location`] associated with the agent.
943	///
944	/// Overrides the location captured by `with_actions`. Useful if the agent's
945	/// definition site is different from where the builder is called.
946	pub const fn with_location(mut self, location: &'static Location<'static>) -> Self {
947		self.location = Some(location);
948		self
949	}
950
951	/// Sets a custom base name for the agent's [`Label`].
952	///
953	/// If not set, the name defaults to the type name of the subject (`I`)
954	/// when [`finish()`](Self::finish) is called.
955	pub const fn with_name(mut self, name: &'static str) -> Self {
956		self.name = Some(name);
957		self
958	}
959
960	/// Sets a custom initial scheduling rank for the agent.
961	///
962	/// If not set, the rank defaults to `sim.config().default_rank()`
963	/// during activation.
964	pub fn with_rank(self, rank: C::Rank) -> Self {
965		Builder {
966			rank: Some(rank),
967			..self
968		}
969	}
970
971	/// Sets a custom [`Settle`] strategy for the agent's finalization.
972	///
973	/// The `Settle` trait determines how the agent's `Output` value (of type
974	/// `R`) is converted into an [`ExitStatus`].
975	pub fn with_finalizer<X>(self, finalizer: X) -> Builder<C, I, A, X> {
976		Builder {
977			subject: self.subject,
978			actions: self.actions,
979			name: self.name,
980			rank: self.rank,
981			settle: finalizer,
982			location: self.location,
983			_config: self._config,
984		}
985	}
986
987	/// Sets the [`Settle`] strategy to [`Checked`].
988	///
989	/// This is a shortcut for `with_finalizer(Checked)`. The `Checked` strategy
990	/// interprets `bool`, `Option`, and `Result` outputs to determine the
991	/// agent's [`ExitStatus`] (`Success` vs. `Failure`).
992	pub fn checked(self) -> Builder<C, I, A, Checked> {
993		self.with_finalizer(Checked)
994	}
995}
996
997impl<C: ?Sized + Config, I: Any, R, A, S> Builder<C, I, A, S>
998where
999	A: Actions<C, I, Output = R>,
1000	S: Settle<R>,
1001{
1002	/// Constructs the [`Agent`] instance from the builder configuration.
1003	///
1004	/// This method consumes the builder and returns the configured `Agent`
1005	/// wrapped in a [`Lease`]. The `Lease` provides ownership transfer
1006	/// semantics for mutable borrows and requires pinning before activation.
1007	///
1008	/// Requires that `with_subject` and `with_actions` have been called
1009	/// previously. It uses the type name of `I` as the default agent name if
1010	/// `with_name` was not called.
1011	pub fn finish<'p>(self) -> Lease<'p, Agent<C, I, A, S>> {
1012		// Create the Agent in the Born state
1013		Lease::new(Agent {
1014			item: self.subject,
1015			inner: Inner::Born {
1016				action: self.actions,
1017				name: self.name.unwrap_or_else(|| type_name::<I>()),
1018				rank: self.rank,
1019				// Create the root job builder with finalizer and location
1020				builder: JobBuilder::root() // Mark as the root job
1021					.with_finalizer(self.settle)
1022					.with_location(self.location.unwrap()),
1023			},
1024		})
1025	}
1026}
1027
1028/* **************************************************** Trait Implementations */
1029
1030impl<C, I, A, R> Behavior<C> for (I, A)
1031where
1032	C: ?Sized + Config,
1033	I: Any,
1034	A: AsyncFn(&I, &Sim<C>) -> R,
1035{
1036	type Output = R;
1037
1038	fn actions(&self, sim: &Sim<C>) -> impl Future<Output = R> {
1039		(self.1)(&self.0, sim)
1040	}
1041
1042	fn name(&self) -> &'static str {
1043		type_name::<I>()
1044	}
1045}
1046
1047// Blanket implementation forwarding `Actions` to `private::Action`.
1048// This connects the public `Actions` trait to the private HRTB machinery.
1049impl<A, C, I, R> Actions<C, I> for A
1050where
1051	A: for<'p> private::Action<'p, C, I, Future: Future<Output = R>>,
1052	C: ?Sized + Config,
1053	I: ?Sized + Any,
1054{
1055	type Output = R;
1056}
1057
1058// Implement `private::Action` for `Pending<()>` used in the empty builder.
1059impl<'p, C, I> private::Action<'p, C, I> for Pending<()>
1060where
1061	C: ?Sized + Config,
1062	I: ?Sized + Any,
1063{
1064	type Future = Pending<()>;
1065
1066	fn bind(self, _item: &'p I, _sim: &'p Sim<C>) -> Self::Future {
1067		pending()
1068	}
1069}
1070
1071// Implement `private::Action` for function pointers and closures that match the
1072// signature. This is the core implementation that allows `async fn` references
1073// to be used as actions.
1074impl<'p, C, I, F, R> private::Action<'p, C, I> for F
1075where
1076	C: ?Sized + Config,
1077	I: ?Sized + Any,
1078	F: FnOnce(&'p I, &'p Sim<C>) -> R,
1079	R: Future + 'p,
1080{
1081	type Future = R;
1082
1083	fn bind(self, item: &'p I, sim: &'p Sim<C>) -> Self::Future {
1084		self(item, sim)
1085	}
1086}