vtcode_commons/ui_protocol/
activity.rs1#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
4pub enum ActivityState {
5 #[default]
6 Idle,
7 Planning,
9 PreparingFreshExecutionThread,
10 RestoringApprovedPlan,
11 Building,
13 StartingBuild,
14 Recovery,
17 Blocked,
21}
22
23impl ActivityState {
24 pub const fn is_busy(self) -> bool {
26 matches!(self, Self::PreparingFreshExecutionThread | Self::RestoringApprovedPlan | Self::StartingBuild)
27 }
28
29 pub const fn locks_mode_switch(self) -> bool {
33 matches!(
34 self,
35 Self::PreparingFreshExecutionThread
36 | Self::RestoringApprovedPlan
37 | Self::Building
38 | Self::StartingBuild
39 | Self::Recovery
40 | Self::Blocked
41 )
42 }
43
44 pub const fn is_stage(self) -> bool {
46 matches!(self, Self::Planning | Self::Building | Self::Recovery)
47 }
48
49 pub const fn status(self) -> Option<&'static str> {
50 match self {
51 Self::Idle => None,
52 Self::Planning => Some("Planning..."),
53 Self::PreparingFreshExecutionThread => Some("Preparing fresh execution thread..."),
54 Self::RestoringApprovedPlan => Some("Restoring approved plan..."),
55 Self::Building => Some("Building..."),
56 Self::StartingBuild => Some("Starting build..."),
57 Self::Recovery => Some("Recovery: tools disabled..."),
58 Self::Blocked => Some("Blocked — Type 'continue' to retry..."),
59 }
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::ActivityState;
66
67 #[test]
68 fn idle_has_no_status_and_is_not_busy_or_stage() {
69 assert_eq!(ActivityState::Idle.status(), None);
70 assert!(!ActivityState::Idle.is_busy());
71 assert!(!ActivityState::Idle.is_stage());
72 }
73
74 #[test]
75 fn stages_are_not_busy_but_keep_a_status() {
76 for state in [ActivityState::Planning, ActivityState::Building] {
77 assert!(!state.is_busy());
78 assert!(state.is_stage());
79 assert!(state.status().is_some());
80 }
81 assert_eq!(ActivityState::Planning.status(), Some("Planning..."));
82 assert_eq!(ActivityState::Building.status(), Some("Building..."));
83 }
84
85 #[test]
86 fn transient_handoffs_are_busy_but_not_stages() {
87 for state in [
88 ActivityState::PreparingFreshExecutionThread,
89 ActivityState::RestoringApprovedPlan,
90 ActivityState::StartingBuild,
91 ] {
92 assert!(state.is_busy());
93 assert!(!state.is_stage());
94 assert!(state.status().is_some());
95 }
96 }
97
98 #[test]
99 fn mode_switch_is_locked_for_active_build_recovery_and_blocked_states() {
100 for state in [
101 ActivityState::PreparingFreshExecutionThread,
102 ActivityState::RestoringApprovedPlan,
103 ActivityState::Building,
104 ActivityState::StartingBuild,
105 ActivityState::Recovery,
106 ActivityState::Blocked,
107 ] {
108 assert!(state.locks_mode_switch(), "{state:?} must lock mode switching");
109 }
110 assert!(!ActivityState::Idle.locks_mode_switch());
111 assert!(!ActivityState::Planning.locks_mode_switch());
112 }
113}