Skip to main content

ralph_core/event_loop/
loop_state.rs

1//! Loop state tracking for the event loop.
2//!
3//! This module contains the `LoopState` struct that tracks the current
4//! state of the orchestration loop including iteration count, failures,
5//! timing, and hat activation tracking.
6
7use ralph_proto::HatId;
8use std::collections::{HashMap, HashSet};
9use std::time::{Duration, Instant};
10
11/// Current state of the event loop.
12#[derive(Debug)]
13pub struct LoopState {
14    /// Current iteration number (1-indexed).
15    pub iteration: u32,
16    /// Number of consecutive failures.
17    pub consecutive_failures: u32,
18    /// Cumulative cost in USD (if tracked).
19    pub cumulative_cost: f64,
20    /// When the loop started.
21    pub started_at: Instant,
22    /// The last hat that executed.
23    pub last_hat: Option<HatId>,
24    /// Consecutive blocked events from the same hat.
25    pub consecutive_blocked: u32,
26    /// Hat that emitted the last blocked event.
27    pub last_blocked_hat: Option<HatId>,
28    /// Per-task block counts for task-level thrashing detection.
29    pub task_block_counts: HashMap<String, u32>,
30    /// Tasks that have been abandoned after 3+ blocks.
31    pub abandoned_tasks: Vec<String>,
32    /// Count of times planner dispatched an already-abandoned task.
33    pub abandoned_task_redispatches: u32,
34    /// Consecutive malformed JSONL lines encountered (for validation backpressure).
35    pub consecutive_malformed_events: u32,
36
37    /// Per-hat activation counts (used for max_activations).
38    pub hat_activation_counts: HashMap<HatId, u32>,
39
40    /// Hats for which `<hat_id>.exhausted` has been emitted.
41    pub exhausted_hats: HashSet<HatId>,
42
43    /// When the last Telegram check-in message was sent.
44    /// `None` means no check-in has been sent yet.
45    pub last_checkin_at: Option<Instant>,
46}
47
48impl Default for LoopState {
49    fn default() -> Self {
50        Self {
51            iteration: 0,
52            consecutive_failures: 0,
53            cumulative_cost: 0.0,
54            started_at: Instant::now(),
55            last_hat: None,
56            consecutive_blocked: 0,
57            last_blocked_hat: None,
58            task_block_counts: HashMap::new(),
59            abandoned_tasks: Vec::new(),
60            abandoned_task_redispatches: 0,
61            consecutive_malformed_events: 0,
62            hat_activation_counts: HashMap::new(),
63            exhausted_hats: HashSet::new(),
64            last_checkin_at: None,
65        }
66    }
67}
68
69impl LoopState {
70    /// Creates a new loop state.
71    pub fn new() -> Self {
72        Self::default()
73    }
74
75    /// Returns the elapsed time since the loop started.
76    pub fn elapsed(&self) -> Duration {
77        self.started_at.elapsed()
78    }
79}