Skip to main content

trellis_runner/engine/
event.rs

1use crate::progress::Progress;
2use crate::Termination;
3
4/// A batch of progress signals emitted during a single solver iteration.
5///
6/// The engine aggregates low-level `Progress` signals produced by the
7/// procedure and convergence subsystem into a single batch.
8///
9/// Policies consume this batch to make decisions about:
10/// - convergence
11/// - stagnation
12/// - checkpointing
13/// - termination
14#[derive(Debug)]
15pub struct EventBatch<F> {
16    /// All progress signals emitted during the current iteration.
17    pub events: Vec<Progress<F>>,
18}
19
20impl<F> Default for EventBatch<F> {
21    fn default() -> Self {
22        Self::new()
23    }
24}
25
26impl<F> EventBatch<F> {
27    /// Generate and empty batch
28    pub fn new() -> Self {
29        Self { events: vec![] }
30    }
31    /// Adds a progress event to the batch.
32    pub fn add(mut self, event: Progress<F>) -> Self {
33        self.events.push(event);
34        self
35    }
36}
37
38/// Action returned by the policy layer after evaluating an [`EventBatch`]
39/// and the current engine context.
40///
41/// This is the *only control signal* that influences the engine loop.
42///
43/// The engine reacts deterministically to this value.
44#[derive(Clone, Debug, PartialEq)]
45pub enum EngineAction {
46    /// Continue normal execution with no side effects.
47    Continue,
48
49    /// Request that the engine persists a checkpoint of the current state.
50    ///
51    /// This does not stop execution; it is a side-effect request.
52    EmitCheckpoint(CheckpointReason),
53
54    /// Request termination of the solver.
55    ///
56    /// This immediately ends execution and propagates the termination reason
57    /// to the final [`EngineOutput`].
58    Stop(Termination),
59}
60
61/// Reason for emitting a checkpoint.
62///
63/// This distinguishes between scheduled persistence and semantic triggers
64/// (e.g. stagnation recovery or user-driven requests).
65#[derive(Clone, Debug, PartialEq)]
66pub enum CheckpointReason {
67    /// Checkpoint triggered on a fixed schedule (e.g. every N iterations).
68    Scheduled,
69
70    /// Checkpoint triggered due to stagnation detection.
71    ///
72    /// Typically used for recovery or restart strategies.
73    Stagnation,
74
75    /// Checkpoint triggered by an external user or system request.
76    UserRequest,
77}
78
79/// High-level lifecycle events emitted by the engine.
80///
81/// These events are used for:
82/// - observers / logging
83/// - external monitoring
84/// - UI updates
85///
86/// They are distinct from [`Progress`], which represents *numerical solver signals*.
87#[derive(Debug)]
88pub enum EngineSignal<F> {
89    /// Engine has completed initialisation and is ready to iterate.
90    Initialised,
91
92    /// A single progress signal emitted during iteration.
93    Progress(Progress<F>),
94
95    /// Engine has completed a single iteration
96    Iterated,
97
98    /// A checkpoint has been successfully persisted.
99    CheckpointSaved,
100
101    /// A checkpoint has been requested.
102    CheckpointRequested(CheckpointReason),
103
104    /// Engine has terminated for any reason.
105    Termination(Termination),
106}
107
108impl<F> EngineSignal<F> {
109    /// Returns a stable string tag identifying the event kind.
110    pub fn as_tag(&self) -> &'static str {
111        match self {
112            Self::Initialised => "initialised",
113            Self::Progress(_) => "progress",
114            Self::Iterated => "iterated",
115            Self::CheckpointSaved => "checkpoint_saved",
116            Self::Termination(_) => "termination",
117            Self::CheckpointRequested(_) => "checkpoint_requested",
118        }
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125    #[test]
126    fn event_batch_accumulates_events() {
127        let batch = EventBatch::new()
128            .add(Progress::Measure(1.0))
129            .add(Progress::Measure(2.0));
130
131        assert_eq!(batch.events.len(), 2);
132    }
133}