Skip to main content

orcs_runtime/channel/
world.rs

1//! World - manages Channel lifecycle.
2//!
3//! The [`World`] is the central manager for all [`Channel`]s in the ORCS
4//! system. It handles spawning, killing, and state transitions.
5//!
6//! # Responsibilities
7//!
8//! - **Create**: Create root channels (no parent)
9//! - **Spawn**: Create child channels with parent relationships
10//! - **Kill**: Remove channels and cascade to descendants
11//! - **Complete**: Transition channels to completed state
12//! - **Query**: Look up channels by ID
13//!
14//! # Example
15//!
16//! ```
17//! use orcs_runtime::{World, ChannelConfig};
18//!
19//! let mut world = World::new();
20//!
21//! // Create a root channel (no parent)
22//! let root = world.create_channel(ChannelConfig::interactive());
23//!
24//! // Spawn child channels
25//! let agent = world.spawn(root).expect("parent exists");
26//! let tool = world.spawn(agent).expect("parent exists");
27//!
28//! // Kill cascades to descendants
29//! world.kill(agent, "cancelled".into());
30//! assert!(world.get(&tool).is_none());
31//! ```
32
33use super::channel::Channel;
34use super::config::ChannelConfig;
35use super::traits::{ChannelCore, ChannelMut};
36use orcs_types::{ChannelId, Principal};
37use std::collections::HashMap;
38
39/// Central manager for all [`Channel`]s.
40///
41/// The `World` maintains the channel tree and provides methods for
42/// channel lifecycle management.
43///
44/// # Channel Tree
45///
46/// ```text
47/// World
48///   │
49///   ├── IO Channel (interactive)
50///   │     ├── Agent Channel
51///   │     │     ├── Tool Channel 1
52///   │     │     └── Tool Channel 2
53///   │     │
54///   │     └── Background Channel
55///   │
56///   └── Other Root Channels...
57/// ```
58///
59/// # Thread Safety
60///
61/// `World` is not thread-safe by itself. In a concurrent environment,
62/// wrap it in appropriate synchronization primitives (e.g., `Mutex`).
63///
64/// # Example
65///
66/// ```
67/// use orcs_runtime::{World, ChannelConfig, ChannelCore, ChannelState};
68///
69/// let mut world = World::new();
70///
71/// // Create a root channel
72/// let root = world.create_channel(ChannelConfig::interactive());
73///
74/// // Spawn and complete a child
75/// let child = world.spawn(root).expect("parent exists");
76/// world.complete(child);
77///
78/// assert_eq!(
79///     world.get(&child).expect("child channel exists").state(),
80///     &ChannelState::Completed
81/// );
82/// ```
83#[derive(Debug)]
84pub struct World {
85    channels: HashMap<ChannelId, Channel>,
86}
87
88impl World {
89    /// Creates an empty World with no channels.
90    ///
91    /// Use [`create_channel()`](Self::create_channel) to create root channels.
92    ///
93    /// # Example
94    ///
95    /// ```
96    /// use orcs_runtime::World;
97    ///
98    /// let world = World::new();
99    /// assert_eq!(world.channel_count(), 0);
100    /// ```
101    #[must_use]
102    pub fn new() -> Self {
103        Self {
104            channels: HashMap::new(),
105        }
106    }
107
108    /// Creates a root channel (no parent).
109    ///
110    /// Root channels have no parent and serve as entry points to the
111    /// channel tree. Multiple root channels are allowed.
112    ///
113    /// # Arguments
114    ///
115    /// * `config` - Configuration for the new channel
116    ///
117    /// # Returns
118    ///
119    /// The ID of the newly created channel.
120    ///
121    /// # Example
122    ///
123    /// ```
124    /// use orcs_runtime::{World, ChannelConfig, ChannelCore};
125    ///
126    /// let mut world = World::new();
127    ///
128    /// // Create an interactive (IO) channel
129    /// let io = world.create_channel(ChannelConfig::interactive());
130    /// assert!(world.get(&io).expect("channel exists").parent().is_none());
131    /// assert_eq!(world.get(&io).expect("channel exists").priority(), 255);
132    ///
133    /// // Create a background root channel
134    /// let bg = world.create_channel(ChannelConfig::background());
135    /// assert_eq!(world.channel_count(), 2);
136    /// ```
137    #[must_use]
138    pub fn create_channel(&mut self, config: ChannelConfig) -> ChannelId {
139        self.create_channel_with_principal(config, Principal::System)
140    }
141
142    /// Creates a root channel with a specific principal.
143    ///
144    /// # Arguments
145    ///
146    /// * `config` - Configuration for the new channel
147    /// * `principal` - Principal for scope resolution
148    #[must_use]
149    pub fn create_channel_with_principal(
150        &mut self,
151        config: ChannelConfig,
152        principal: Principal,
153    ) -> ChannelId {
154        let id = ChannelId::new();
155        let channel = Channel::new(id, None, config, principal);
156        self.channels.insert(id, channel);
157        id
158    }
159
160    /// Spawns a new child channel under the given parent with default config.
161    ///
162    /// The new channel starts in [`Running`](crate::ChannelState::Running)
163    /// state and is registered as a child of the parent.
164    ///
165    /// Uses [`ChannelConfig::default()`] for the new channel.
166    /// For explicit configuration, use [`spawn_with()`](Self::spawn_with).
167    ///
168    /// # Arguments
169    ///
170    /// * `parent` - ID of the parent channel
171    ///
172    /// # Returns
173    ///
174    /// - `Some(ChannelId)` if the spawn succeeded
175    /// - `None` if the parent channel does not exist
176    ///
177    /// # Example
178    ///
179    /// ```
180    /// use orcs_runtime::{World, ChannelConfig, ChannelCore};
181    ///
182    /// let mut world = World::new();
183    /// let root = world.create_channel(ChannelConfig::interactive());
184    ///
185    /// let child = world.spawn(root).expect("parent exists");
186    /// assert_eq!(world.get(&child).expect("child channel exists").parent(), Some(root));
187    /// ```
188    pub fn spawn(&mut self, parent: ChannelId) -> Option<ChannelId> {
189        self.spawn_with(parent, ChannelConfig::default())
190    }
191
192    /// Spawns a new child channel with explicit configuration.
193    ///
194    /// The new channel starts in [`Running`](crate::ChannelState::Running)
195    /// state and is registered as a child of the parent.
196    ///
197    /// # Arguments
198    ///
199    /// * `parent` - ID of the parent channel
200    /// * `config` - Configuration for the new channel
201    ///
202    /// # Returns
203    ///
204    /// - `Some(ChannelId)` if the spawn succeeded
205    /// - `None` if the parent channel does not exist
206    ///
207    /// # Privilege Inheritance
208    ///
209    /// The child's `max_privilege` is automatically capped by the parent's level.
210    /// This ensures privilege reduction propagates down the channel tree.
211    ///
212    /// # Example
213    ///
214    /// ```
215    /// use orcs_runtime::{World, ChannelConfig, ChannelCore, MaxPrivilege};
216    ///
217    /// let mut world = World::new();
218    /// let root = world.create_channel(ChannelConfig::interactive());
219    ///
220    /// // Spawn a background channel
221    /// let bg = world.spawn_with(root, ChannelConfig::background())
222    ///     .expect("parent exists");
223    /// assert_eq!(world.get(&bg).expect("channel exists").priority(), 10);
224    /// assert!(!world.get(&bg).expect("channel exists").can_spawn());
225    ///
226    /// // Spawn a tool channel (inherits elevated from interactive parent)
227    /// let tool = world.spawn_with(root, ChannelConfig::tool())
228    ///     .expect("parent exists");
229    /// assert_eq!(world.get(&tool).expect("channel exists").priority(), 100);
230    /// assert_eq!(world.get(&tool).expect("channel exists").config().max_privilege(), MaxPrivilege::Elevated);
231    ///
232    /// // Spawn from background parent (max_privilege capped to Standard)
233    /// let child = world.spawn_with(bg, ChannelConfig::tool())
234    ///     .expect("parent exists");
235    /// assert_eq!(world.get(&child).expect("channel exists").config().max_privilege(), MaxPrivilege::Standard);
236    /// ```
237    pub fn spawn_with(&mut self, parent: ChannelId, config: ChannelConfig) -> Option<ChannelId> {
238        let id = ChannelId::new();
239        self.spawn_with_id(parent, id, config)
240    }
241
242    /// Spawns a new child channel with a pre-determined [`ChannelId`].
243    ///
244    /// Same as [`spawn_with()`](Self::spawn_with), but the caller provides the
245    /// [`ChannelId`] instead of having one generated.  This is required when the
246    /// caller needs to know the ID before the World is updated (e.g. to pass
247    /// the ID into a `tokio::spawn` closure).
248    ///
249    /// Returns `Some(id)` on success, `None` if the parent does not exist.
250    pub fn spawn_with_id(
251        &mut self,
252        parent: ChannelId,
253        id: ChannelId,
254        config: ChannelConfig,
255    ) -> Option<ChannelId> {
256        // Get parent channel for inheritance
257        let parent_channel = self.channels.get(&parent)?;
258        let parent_config = *parent_channel.config();
259        let parent_principal = parent_channel.principal().clone();
260
261        // Build ancestor path: [parent, ...parent's ancestors]
262        let mut ancestor_path = Vec::with_capacity(parent_channel.depth() + 1);
263        ancestor_path.push(parent);
264        ancestor_path.extend_from_slice(parent_channel.ancestor_path());
265
266        // Apply privilege inheritance
267        let inherited_config = config.inherit_from(&parent_config);
268
269        let channel = Channel::new_with_ancestors(
270            id,
271            parent,
272            inherited_config,
273            parent_principal,
274            ancestor_path,
275        );
276        self.channels.insert(id, channel);
277
278        // Register with parent
279        if let Some(parent_channel) = self.channels.get_mut(&parent) {
280            parent_channel.add_child(id);
281        }
282
283        Some(id)
284    }
285
286    /// Kills a channel and all its descendants.
287    ///
288    /// This method:
289    /// 1. Recursively kills all child channels
290    /// 2. Removes the channel from its parent's child set
291    /// 3. Removes the channel from the World
292    ///
293    /// # Arguments
294    ///
295    /// * `id` - ID of the channel to kill
296    /// * `reason` - Explanation for why the channel was killed
297    ///
298    /// # Note
299    ///
300    /// Unlike [`complete()`](Self::complete), `kill()` removes the
301    /// channel entirely from the World rather than keeping it in
302    /// a terminal state.
303    ///
304    /// # Example
305    ///
306    /// ```
307    /// use orcs_runtime::{World, ChannelConfig};
308    ///
309    /// let mut world = World::new();
310    /// let root = world.create_channel(ChannelConfig::interactive());
311    /// let agent = world.spawn(root).expect("parent exists");
312    /// let tool = world.spawn(agent).expect("parent exists");
313    ///
314    /// // Killing agent also removes tool
315    /// world.kill(agent, "task cancelled".into());
316    ///
317    /// assert!(world.get(&agent).is_none());
318    /// assert!(world.get(&tool).is_none());
319    /// assert_eq!(world.channel_count(), 1); // only root
320    /// ```
321    pub fn kill(&mut self, id: ChannelId, reason: String) {
322        // Collect children first to avoid borrow issues
323        let children: Vec<ChannelId> = self
324            .channels
325            .get(&id)
326            .map(|ch| ch.children().iter().copied().collect())
327            .unwrap_or_default();
328
329        // Kill children recursively
330        for child_id in children {
331            self.kill(child_id, reason.clone());
332        }
333
334        // Remove from parent
335        if let Some(channel) = self.channels.get(&id) {
336            if let Some(parent_id) = channel.parent() {
337                if let Some(parent) = self.channels.get_mut(&parent_id) {
338                    parent.remove_child(&id);
339                }
340            }
341        }
342
343        self.channels.remove(&id);
344    }
345
346    /// Completes a channel, transitioning it to [`Completed`](crate::ChannelState::Completed).
347    ///
348    /// Unlike [`kill()`](Self::kill), the channel remains in the World
349    /// with its completed state preserved.
350    ///
351    /// # Arguments
352    ///
353    /// * `id` - ID of the channel to complete
354    ///
355    /// # Returns
356    ///
357    /// - `true` if the channel was successfully completed
358    /// - `false` if the channel doesn't exist or is already in a terminal state
359    ///
360    /// # Example
361    ///
362    /// ```
363    /// use orcs_runtime::{World, ChannelConfig, ChannelState};
364    ///
365    /// let mut world = World::new();
366    /// let root = world.create_channel(ChannelConfig::interactive());
367    ///
368    /// assert!(world.complete(root));
369    /// assert!(!world.complete(root)); // Already completed
370    /// ```
371    pub fn complete(&mut self, id: ChannelId) -> bool {
372        self.channels
373            .get_mut(&id)
374            .map(|ch| ch.complete())
375            .unwrap_or(false)
376    }
377
378    /// Returns a reference to the channel with the given ID.
379    ///
380    /// # Returns
381    ///
382    /// - `Some(&Channel)` if the channel exists
383    /// - `None` if no channel has this ID
384    #[must_use]
385    pub fn get(&self, id: &ChannelId) -> Option<&Channel> {
386        self.channels.get(id)
387    }
388
389    /// Returns a mutable reference to the channel with the given ID.
390    ///
391    /// # Returns
392    ///
393    /// - `Some(&mut Channel)` if the channel exists
394    /// - `None` if no channel has this ID
395    pub fn get_mut(&mut self, id: &ChannelId) -> Option<&mut Channel> {
396        self.channels.get_mut(id)
397    }
398
399    /// Returns all channel IDs in the World.
400    ///
401    /// The order is not guaranteed.
402    #[must_use]
403    pub fn channel_ids(&self) -> Vec<ChannelId> {
404        self.channels.keys().copied().collect()
405    }
406
407    /// Returns the total number of channels.
408    #[must_use]
409    pub fn channel_count(&self) -> usize {
410        self.channels.len()
411    }
412
413    /// Checks if `child` is a descendant of `ancestor`.
414    ///
415    /// A channel is a descendant if there's a path from it to the ancestor
416    /// through parent relationships. A channel is NOT its own descendant.
417    ///
418    /// # Performance
419    ///
420    /// Uses cached ancestor path for O(depth) lookup with cache-friendly
421    /// sequential memory access (DOD optimization).
422    ///
423    /// # Arguments
424    ///
425    /// * `child` - ID of the potential descendant
426    /// * `ancestor` - ID of the potential ancestor
427    ///
428    /// # Returns
429    ///
430    /// `true` if `child` is a descendant of `ancestor`, `false` otherwise.
431    ///
432    /// # Example
433    ///
434    /// ```
435    /// use orcs_runtime::{World, ChannelConfig};
436    ///
437    /// let mut world = World::new();
438    /// let root = world.create_channel(ChannelConfig::interactive());
439    /// let child = world.spawn(root).expect("parent exists");
440    /// let grandchild = world.spawn(child).expect("parent exists");
441    ///
442    /// assert!(world.is_descendant_of(child, root));
443    /// assert!(world.is_descendant_of(grandchild, root));
444    /// assert!(world.is_descendant_of(grandchild, child));
445    /// assert!(!world.is_descendant_of(root, child)); // parent is not descendant
446    /// assert!(!world.is_descendant_of(root, root));  // not own descendant
447    /// ```
448    #[must_use]
449    pub fn is_descendant_of(&self, child: ChannelId, ancestor: ChannelId) -> bool {
450        if child == ancestor {
451            return false;
452        }
453
454        self.channels
455            .get(&child)
456            .map(|ch| ch.is_descendant_of(ancestor))
457            .unwrap_or(false)
458    }
459}
460
461impl Default for World {
462    fn default() -> Self {
463        Self::new()
464    }
465}
466
467#[cfg(test)]
468mod tests {
469    use super::*;
470    use crate::ChannelState;
471
472    #[test]
473    fn world_creation() {
474        let world = World::new();
475        assert_eq!(world.channel_count(), 0);
476    }
477
478    #[test]
479    fn create_channel_interactive() {
480        let mut world = World::new();
481        let io = world.create_channel(ChannelConfig::interactive());
482
483        assert_eq!(world.channel_count(), 1);
484        assert!(world.get(&io).is_some());
485        assert!(world
486            .get(&io)
487            .expect("interactive channel should exist")
488            .parent()
489            .is_none());
490        assert_eq!(
491            world
492                .get(&io)
493                .expect("interactive channel should exist for priority check")
494                .priority(),
495            255
496        );
497    }
498
499    #[test]
500    fn create_multiple_root_channels() {
501        let mut world = World::new();
502        let io = world.create_channel(ChannelConfig::interactive());
503        let bg = world.create_channel(ChannelConfig::background());
504
505        assert_eq!(world.channel_count(), 2);
506        assert!(world
507            .get(&io)
508            .expect("IO channel should exist")
509            .parent()
510            .is_none());
511        assert!(world
512            .get(&bg)
513            .expect("background channel should exist")
514            .parent()
515            .is_none());
516    }
517
518    #[test]
519    fn spawn_child() {
520        let mut world = World::new();
521        let root = world.create_channel(ChannelConfig::interactive());
522        let child = world.spawn(root).expect("spawn child from root");
523
524        assert_eq!(world.channel_count(), 2);
525        assert!(world.get(&child).is_some());
526        assert_eq!(
527            world
528                .get(&child)
529                .expect("child channel should exist")
530                .parent(),
531            Some(root)
532        );
533        assert!(world
534            .get(&root)
535            .expect("root channel should exist")
536            .children()
537            .contains(&child));
538    }
539
540    #[test]
541    fn spawn_invalid_parent() {
542        let mut world = World::new();
543        let invalid = ChannelId::new();
544        let result = world.spawn(invalid);
545
546        assert!(result.is_none());
547    }
548
549    #[test]
550    fn kill_channel() {
551        let mut world = World::new();
552        let root = world.create_channel(ChannelConfig::interactive());
553        let child = world.spawn(root).expect("spawn child for kill test");
554
555        world.kill(child, "test".into());
556
557        assert_eq!(world.channel_count(), 1);
558        assert!(world.get(&child).is_none());
559        assert!(!world
560            .get(&root)
561            .expect("root should exist after killing child")
562            .children()
563            .contains(&child));
564    }
565
566    #[test]
567    fn kill_with_children() {
568        let mut world = World::new();
569        let root = world.create_channel(ChannelConfig::interactive());
570        let child = world
571            .spawn(root)
572            .expect("spawn child for cascade kill test");
573        let grandchild = world
574            .spawn(child)
575            .expect("spawn grandchild for cascade kill test");
576
577        assert_eq!(world.channel_count(), 3);
578
579        world.kill(child, "cascade".into());
580
581        assert_eq!(world.channel_count(), 1);
582        assert!(world.get(&child).is_none());
583        assert!(world.get(&grandchild).is_none());
584    }
585
586    #[test]
587    fn complete_channel() {
588        let mut world = World::new();
589        let root = world.create_channel(ChannelConfig::interactive());
590
591        assert!(world.complete(root));
592
593        assert_eq!(
594            world
595                .get(&root)
596                .expect("root channel should exist after complete")
597                .state(),
598            &ChannelState::Completed
599        );
600
601        // Cannot complete twice
602        assert!(!world.complete(root));
603    }
604
605    #[test]
606    fn complete_nonexistent() {
607        let mut world = World::new();
608        let invalid = ChannelId::new();
609        assert!(!world.complete(invalid));
610    }
611
612    #[test]
613    fn interactive_has_high_priority() {
614        let mut world = World::new();
615        let io = world.create_channel(ChannelConfig::interactive());
616
617        let channel = world
618            .get(&io)
619            .expect("IO channel should exist for priority check");
620        assert_eq!(channel.priority(), 255);
621        assert!(channel.can_spawn());
622    }
623
624    #[test]
625    fn spawn_with_background_config() {
626        let mut world = World::new();
627        let root = world.create_channel(ChannelConfig::interactive());
628
629        let bg = world
630            .spawn_with(root, ChannelConfig::background())
631            .expect("spawn background channel from root");
632
633        let channel = world.get(&bg).expect("background channel should exist");
634        assert_eq!(channel.priority(), 10);
635        assert!(!channel.can_spawn());
636    }
637
638    #[test]
639    fn spawn_with_tool_config() {
640        let mut world = World::new();
641        let root = world.create_channel(ChannelConfig::interactive());
642
643        let tool = world
644            .spawn_with(root, ChannelConfig::tool())
645            .expect("spawn tool channel from root");
646
647        let channel = world.get(&tool).expect("tool channel should exist");
648        assert_eq!(channel.priority(), 100);
649        assert!(channel.can_spawn());
650    }
651
652    #[test]
653    fn spawn_with_custom_config() {
654        let mut world = World::new();
655        let root = world.create_channel(ChannelConfig::interactive());
656
657        let config = ChannelConfig::new(200, false);
658        let custom = world
659            .spawn_with(root, config)
660            .expect("spawn custom config channel from root");
661
662        let channel = world.get(&custom).expect("custom channel should exist");
663        assert_eq!(channel.priority(), 200);
664        assert!(!channel.can_spawn());
665    }
666
667    #[test]
668    fn spawn_uses_default_config() {
669        let mut world = World::new();
670        let root = world.create_channel(ChannelConfig::interactive());
671
672        let child = world.spawn(root).expect("spawn child with default config");
673
674        let channel = world
675            .get(&child)
676            .expect("child channel should exist for default config check");
677        // Default config: NORMAL priority (50), can_spawn = true
678        assert_eq!(channel.priority(), 50);
679        assert!(channel.can_spawn());
680    }
681
682    #[test]
683    fn is_descendant_direct_child() {
684        let mut world = World::new();
685        let root = world.create_channel(ChannelConfig::interactive());
686        let child = world.spawn(root).expect("spawn child for descendant test");
687
688        assert!(world.is_descendant_of(child, root));
689        assert!(!world.is_descendant_of(root, child));
690    }
691
692    #[test]
693    fn is_descendant_grandchild() {
694        let mut world = World::new();
695        let root = world.create_channel(ChannelConfig::interactive());
696        let child = world
697            .spawn(root)
698            .expect("spawn child for grandchild descendant test");
699        let grandchild = world
700            .spawn(child)
701            .expect("spawn grandchild for descendant test");
702
703        assert!(world.is_descendant_of(grandchild, root));
704        assert!(world.is_descendant_of(grandchild, child));
705        assert!(world.is_descendant_of(child, root));
706    }
707
708    #[test]
709    fn is_descendant_not_self() {
710        let mut world = World::new();
711        let root = world.create_channel(ChannelConfig::interactive());
712
713        assert!(!world.is_descendant_of(root, root));
714    }
715
716    #[test]
717    fn is_descendant_siblings_not_related() {
718        let mut world = World::new();
719        let root = world.create_channel(ChannelConfig::interactive());
720        let child1 = world.spawn(root).expect("spawn first sibling");
721        let child2 = world.spawn(root).expect("spawn second sibling");
722
723        assert!(!world.is_descendant_of(child1, child2));
724        assert!(!world.is_descendant_of(child2, child1));
725    }
726
727    #[test]
728    fn is_descendant_nonexistent() {
729        let mut world = World::new();
730        let root = world.create_channel(ChannelConfig::interactive());
731        let nonexistent = ChannelId::new();
732
733        assert!(!world.is_descendant_of(nonexistent, root));
734        assert!(!world.is_descendant_of(root, nonexistent));
735    }
736}