Skip to main content

batty_cli/team/daemon/
tick_report.rs

1//! Structured observability for one daemon poll iteration.
2//!
3//! `TickReport` is the return value of [`TeamDaemon::tick`]. It captures the
4//! side effects produced during a single iteration of the daemon's poll loop
5//! so callers (tests, the future `batty debug tick` subcommand, operators)
6//! can assert against them without scraping logs.
7//!
8//! Phase 1 ships with `cycle` and `subsystem_errors` populated. The remaining
9//! fields are intentionally `Default`-shaped placeholders so the contract is
10//! stable for callers; later phases of the scenario framework will fill them
11//! in by snapshotting state around the tick.
12
13use crate::team::events::TeamEvent;
14use crate::team::standup::MemberState;
15
16/// Observable side effects produced by one [`TeamDaemon::tick`] call.
17#[derive(Debug, Default, Clone)]
18pub struct TickReport {
19    /// Daemon poll cycle counter at the end of the tick.
20    pub cycle: u64,
21
22    /// Subsystem failures recorded during the tick. Each entry is
23    /// `(step_name, error_text)` and represents one call to
24    /// `record_loop_step_error`. Both transient (recoverable) and
25    /// fatal-this-tick (loop_step) errors land here.
26    pub subsystem_errors: Vec<(String, String)>,
27
28    /// Events appended to `events.jsonl` during the tick. Empty in phase 1
29    /// (placeholder for the scenario framework's diff-based snapshotting).
30    pub events_emitted: Vec<TeamEvent>,
31
32    /// Member state transitions observed during the tick.
33    /// Empty in phase 1 (placeholder).
34    pub state_transitions: Vec<(String, MemberState, MemberState)>,
35
36    /// New `main` HEAD SHA if the tick advanced the main branch.
37    /// `None` in phase 1 (placeholder).
38    pub main_advanced_to: Option<String>,
39
40    /// Inbox messages delivered during the tick, as `(recipient, message_id)`.
41    /// Empty in phase 1 (placeholder).
42    pub inbox_delivered: Vec<(String, String)>,
43
44    /// Task status transitions observed during the tick, as
45    /// `(task_id, from_status, to_status)`. Empty in phase 1 (placeholder).
46    pub tasks_transitioned: Vec<(u32, String, String)>,
47}
48
49impl TickReport {
50    /// Convenience constructor used by `tick()` to start a fresh report.
51    pub(crate) fn new(cycle: u64) -> Self {
52        Self {
53            cycle,
54            ..Self::default()
55        }
56    }
57
58    /// True if the tick recorded no subsystem errors.
59    pub fn ok(&self) -> bool {
60        self.subsystem_errors.is_empty()
61    }
62}