Skip to main content

clawft_kernel/
view_resolver.rs

1//! View resolution seam for the multiplexed [`TalkModeLoop`](crate::talk_loop).
2//!
3//! M2 D4: the loop stops holding a single `Arc<SessionView>` and instead holds
4//! an injected [`ViewResolver`], so one daemon-hosted loop can multiplex all
5//! conversations — committing each turn onto the *same* per-conversation
6//! `SessionView` that `SessionTier` grafts from. `SessionTier` implements
7//! `ViewResolver` (production), while [`SingleViewResolver`] keeps voice's
8//! single-conversation `TalkForest` and the unit tests green.
9
10use std::sync::Arc;
11
12use crate::context_graft::SessionView;
13
14/// Resolve the per-conversation [`SessionView`] the loop must commit onto.
15///
16/// Returns `None` when the conversation has been reaped (its view dropped); the
17/// loop treats that as a logged no-op rather than committing onto a stale view.
18pub trait ViewResolver: Send + Sync {
19    /// The live view for `conv_id`, or `None` if the conversation is gone.
20    fn view_for(&self, conv_id: &str) -> Option<Arc<SessionView>>;
21}
22
23/// A trivial resolver that returns one fixed view for any `conv_id`.
24///
25/// Used by voice's single-conversation `TalkForest` (Decision 4 "voice stays
26/// green" adapter) and by kernel unit tests that exercise a single view.
27pub struct SingleViewResolver(Arc<SessionView>);
28
29impl SingleViewResolver {
30    /// Wrap a single view; every `view_for` call returns it.
31    pub fn new(view: Arc<SessionView>) -> Self {
32        Self(view)
33    }
34}
35
36impl ViewResolver for SingleViewResolver {
37    fn view_for(&self, _conv_id: &str) -> Option<Arc<SessionView>> {
38        Some(self.0.clone())
39    }
40}