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}