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}