Skip to content

Slot

Slot<C, O> is the container that parent modalities use to hold child components in one of two states: Cold (cached output) or Hot (live, fully operational). It implements Component, so the parent’s compilation pipeline treats both states uniformly.

crates/core/src/template/slot.rs [21:26]
pub enum Slot<C, O> {
Cold(O),
Hot(Arc<Mutex<C>>),
}
VariantContentsBehavior
Cold(O)Cached compiled outputReturns the cached value on compile(). Zero cost, no recompilation.
Hot(Arc<Mutex<C>>)Live child behind Arc<Mutex>Delegates compile() to the inner component. Arc<Mutex> allows independent locking for recursive generation (each child runs its own agent thread).

Slot implements Component by branching on the variant. Hot locks the Arc<Mutex> before delegating:

crates/core/src/template/slot.rs [59:84]
impl<C, O> Component for Slot<C, O>
where
C: Component<Output = O>,
O: Clone + Send + Sync + 'static,
{
type Metadata = C::Metadata;
type Output = O;
fn properties(&self) -> Vec<PropertySchema> {
match self {
Slot::Hot(c) => c.lock().properties(),
Slot::Cold(_) => Vec::new(),
}
}
fn compile(
&self,
metadata: &Self::Metadata,
values: &HashMap<String, PropertyValue>,
) -> Result<Self::Output, CompileError> {
match self {
Slot::Hot(c) => c.lock().compile(metadata, values),
Slot::Cold(o) => Ok(o.clone()),
}
}
}
  • Cold returns the cached output directly. No metadata or values are consulted.
  • Hot locks the mutex, then delegates to the inner component’s compile(). For Session<Whiteboard, ...>, this returns the session’s already-compiled output.

Slot does not implement Reducer. The parent Session’s ReduceIntent impl locks the Hot child’s Arc<Mutex<Session>> and calls dispatch() directly. Dispatching to a Cold slot is an error — it must be warmed first.

LessonPlan holds its whiteboard children as a HashMap of Slots:

pub struct LessonPlan {
rt: Runtime<Self>,
children: HashMap<String, Slot<Session<Whiteboard>, Vec<WhiteboardElement>>>,
}

The concrete type is Slot<Session<Whiteboard>, Vec<WhiteboardElement>> (Session<Whiteboard> uses the default I = SessionIntent<WhiteboardIntent>):

  • C = full child session with its own Doc, cache, effects, and AI subagent
  • O = Vec<WhiteboardElement>, the whiteboard’s compiled output
stateDiagram-v2
[*] --> Cold: Session opens
Cold --> Hot: WarmChild
Hot --> Cold: Session persists on close
Hot --> Hot: Child dispatch
state Cold {
[*] --> cached_output: Cached from parent synced state
}
state Hot {
[*] --> child_session: Full Session with Doc + cache
}

When a parent session opens, all children begin as Cold with cached output from the parent’s synced state (or empty output for new placements). This is the zero-cost default.

When Dart navigates to a slide, it dispatches ParentSessionIntent::WarmChild { id }:

WarmChild { id } => {
fx.spawn(move |svc, sender| {
let bytes = svc.backend.load(&id).unwrap();
sender.send(Command::Feedback(
ParentSessionFeedback::ChildWarmed { id, bytes }
));
});
}

The async load completes, and the feedback handler creates a full Hot session from the loaded bytes.

3. Dispatch — interact with Hot children

Section titled “3. Dispatch — interact with Hot children”

Child intents route through ParentSessionIntent::Child { id, intent }:

Child { id, intent } => {
let child_id = id.unwrap_or_else(|| {
state.modality.selected_child_id().unwrap().to_string()
});
match state.modality.children_mut().get_mut(&child_id) {
Some(Slot::Hot(child_session)) => {
child_session.dispatch(Command::Intent(intent));
}
Some(Slot::Cold(_)) => { /* error: must warm first */ }
None => { /* error: unknown child */ }
}
}

The Hot child runs its own full reduce cycle (drain, reduce, send, lifecycle, spawn). The parent then invalidates the cache for that placement and runs its own lifecycle (recompile, emit).

On session close, Hot sessions export their docs. The parent can store the compiled output back as Cold for the next open.

During parent compilation, Slot’s Component::compile() is called for every child placement:

Child StateCompilation CostWhat Happens
ColdO(1) cloneReturns cached output. No hash check needed.
HotDelegates to SessionSession’s compile() returns its already-compiled output (computed during dispatch lifecycle).

For a LessonPlan with 20 slides where 1 is Hot and 19 are Cold: the Hot slide returns its cached output from the last dispatch, and the 19 Cold slides each return their stored output. Total recompilation: zero (unless the Hot slide’s output changed).

  • Component — the trait Slot implements
  • Modality — parent modalities use Slot for child management
  • Reducer — Hot children run their own reduce cycles via dispatch