1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
//! Agent-state classifier (ARCH §3.5 / §7.1, terminal rules §4.4).
//!
//! The four live-view states are derived from the executor lock, the
//! agent's latest-step `response.json`, and nothing stored (PRINCIPLES
//! "Single source of truth"):
//!
//! - [`AgentState::Live`] — a driver holds the agent's inbox-directory lock
//! (§2.11): someone is stepping the branch.
//! - [`AgentState::InFlight`] — the `live` sub-state where a model call is
//! in flight *right now*: the latest step's `response.json` fd is still
//! open (§3.5, §4.4). The harness holds that fd across every retry
//! attempt and backoff sleep, so a mid-retry `end` segment is still
//! in_flight, never stopped.
//! - [`AgentState::Quiescent`] — no lock held and the latest step's
//! `response.json` is *complete and whole* (§4.4, [`Settled::whole`]): last
//! line `end`, last segment a `finish` with no `error`, and that `finish`
//! naming a reason the model reached rather than ran out of room at. A
//! finished-for-now agent awaiting a message (§2.4).
//! - [`AgentState::Stopped`] — no lock held and the latest step is *failed*
//! (last segment carries an `error`, §2.10) or *killed* (closed with no
//! trailing `end`, §2.9), or ended at the **output limit** (§4.4
//! [`Ending::OutputLimit`], bl-fb87), or no step has run. Kill, crash, and
//! explicit stop are indistinguishable on disk (§2.9); a failed step renders
//! here too, per §3.5.
//!
//! The output-limit arm is bl-fb87's correction, and it is a §3.5 reading, not
//! a fifth state: transport completion is not task completion, so a tail that
//! framed cleanly around a turn the request's `max_tokens` cut off is a
//! conversation **stopped mid-utterance**, not one at rest. The coarse badge
//! vocabulary is unchanged (the bl-d816 ruling: the badge answers "needs me?",
//! the workspace pane answers "why"), and the *why* rides beside it as
//! [`Liveness::truncated`] — the fact §8.2's Nudge gate reads, because linked
//! litany derives `NothingDue` from exactly this shape and a control that
//! fires and does nothing is QUALITY H4's theater.
//!
//! The two observations are deliberately not collapsed (§2.11): the lock is
//! *is-anyone-driving*; the open `response.json` fd is
//! *is-a-model-call-in-flight-right-now*.
//!
//! Each observation is a tri-state [`Probe`] (DESIGN §10): a backend that
//! cannot look (macOS `lsof` missing) returns `Unknown`. On `Unknown`,
//! [`classify`] degrades to the framing-only reading (quiescent/stopped) and
//! returns an **uncertainty flag** so the renderer marks it "live?" — never a
//! false definite. The Linux procfs backends never return `Unknown`, so the
//! flag is always `false` here.
use ;
use failure;
use ;
use latest_step_dir;
use ;
use ;
pub const RESPONSE_FILE: &str = "response.json";
/// One agent's §3.5 classification: the three readings the two liveness
/// observations plus the latest step's settled tail yield, gathered in one
/// pass so the response file is read once.
pub
/// Classify `agent_id` in `workspace` from the two liveness observations
/// plus the latest step's settled tail.
pub
/// Under the lock: `InFlight` iff a writer still holds the latest step's
/// `response.json` open (a model call right now), else plain `Live`. A writer
/// that cannot observe ([`Probe::Unknown`]) leaves the state `Live` (the lock
/// is definitely held) but flags uncertainty — the in_flight refinement is
/// undetectable.
/// No (observable) lock: the §4.4 terminal-only rules settle the file — a
/// complete-and-whole latest `response.json` is `Quiescent`, anything else
/// `Stopped`, and an output-limited tail says so beside the state.
/// The latest step's §4.4 settled reading **and why its model call failed**
/// (bl-b43b, widened bl-9b88) — two readings off one read, for the reason this
/// module already gathers three: reading the file twice could catch two
/// different mid-write states of it.
///
/// Absence of a step tree is the *killed* tail it honestly is, with no failure:
/// nothing on disk says a call was even attempted. An **unreadable** response
/// is not the same answer — the step exists and its adapter may have said why
/// beside it — so it falls through to [`failure`], which is the whole of the
/// out-of-band half.