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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
//! Supervisor configuration: poll cadence, auto-resume policy, idle classification.
//!
//! Why: the unattended supervisor must run with no live caller for hours, so its
//! behavior has to be driven entirely by *seed configuration* an operator sets
//! once (env vars / a struct) rather than by interactive prompts. Centralizing
//! the knobs in one struct keeps the env-var contract (`TRUSTY_MPM_AUTO_RESUME`,
//! `TRUSTY_MPM_SUPERVISOR_INTERVAL`, …) auditable and gives the loop a single
//! immutable config value to read each tick.
//! What: defines [`SupervisorConfig`] with the poll interval, the auto-resume
//! gate, and the idle-classification toggle; plus [`SupervisorConfig::from_env`]
//! which reads the documented env vars with safe defaults. #6288 removed the
//! metrics bind address: the supervisor publishes to a file instead of binding
//! `127.0.0.1:7881`.
//! Test: `config_defaults`, `auto_resume_env_parsing`, `interval_env_parsing`,
//! `classify_idle_env_parsing` in `super::tests`.
use Duration;
/// Environment variable that gates auto-resume of `stopped` sessions.
///
/// Why: auto-resume is the supervisor's most consequential action — it brings a
/// runtime back without a human present — so it MUST be opt-in via an explicit
/// env var, matching the existing reconcile-on-boot gate in `DaemonState`.
/// What: the canonical name an operator sets to `1` / `true` to enable resume.
/// Test: `auto_resume_env_parsing`.
pub const ENV_AUTO_RESUME: &str = "TRUSTY_MPM_AUTO_RESUME";
/// Environment variable that overrides the poll interval (in seconds).
///
/// Why: overnight fleets want a slow cadence (cheap); active debugging wants a
/// fast one. An env override lets operators tune without recompiling.
/// What: parsed as `u64` seconds; invalid / absent values fall back to the default.
/// Test: `interval_env_parsing`.
pub const ENV_INTERVAL_SECS: &str = "TRUSTY_MPM_SUPERVISOR_INTERVAL";
/// Environment variable that selects the LLM model for idle classification.
///
/// Why: idle-session activity classification calls an LLM through OpenRouter;
/// the model is operator-tunable (cost vs. accuracy) and must be read from one
/// named constant so the env contract is auditable alongside the other `ENV_*`
/// knobs rather than buried as a string literal at the read site.
/// What: the variable an operator sets to override the classification model;
/// when absent the supervisor falls back to [`DEFAULT_LLM_MODEL`].
/// Test: `default_llm_model_is_documented` asserts the constant + default pair.
/// #4427: aliased to the classifier's own key so the supervisor and the daemon
/// can never disagree about which variable configures the model.
pub const ENV_LLM_MODEL: &str = crateCLASSIFIER_MODEL_ENV;
/// Default LLM model used for idle classification when [`ENV_LLM_MODEL`] is unset.
///
/// Why: a cheap, capable default keeps token spend low for the common case while
/// still letting operators opt into a stronger model via [`ENV_LLM_MODEL`].
/// What: the fallback model id passed to the classifier when the env var is absent.
/// Test: `default_llm_model_is_documented`.
/// #4427: aliased to the classifier's own default so the supervisor cannot label
/// its metrics with a model the classifier never requests.
pub const DEFAULT_LLM_MODEL: &str = crateDEFAULT_CLASSIFIER_MODEL;
/// Environment variable that toggles idle-session activity classification.
///
/// Why: classification calls the activity monitor (which may call an LLM). On a
/// host with no `OPENROUTER_API_KEY`, or when an operator wants zero token spend,
/// this lets them turn classification off while keeping auto-resume + metrics.
/// What: set to `0` / `false` to disable; defaults to enabled.
/// Test: `classify_idle_env_parsing`.
pub const ENV_CLASSIFY_IDLE: &str = "TRUSTY_MPM_SUPERVISOR_CLASSIFY";
/// Default poll interval when [`ENV_INTERVAL_SECS`] is unset: 30 seconds.
///
/// Why: 30s balances responsiveness (a stopped session resumes within half a
/// minute) against load (the sweep lists sessions and may classify panes).
/// What: the fallback `Duration` used by [`SupervisorConfig::from_env`].
/// Test: `config_defaults`.
pub const DEFAULT_INTERVAL_SECS: u64 = 30;
/// Environment variable that overrides the post-merge cleanup cadence, in
/// seconds (#7275).
///
/// Why: the cleanup sweep costs one `gh pr view` per pending pull request, so
/// it must run far less often than the fleet sweep. A separate variable lets an
/// operator slow it down (or, with `0`, turn it off) without touching the
/// auto-resume cadence the fleet depends on.
/// What: parsed as `u64` seconds; `0` disables the sweep entirely; an absent or
/// unparsable value falls back to [`DEFAULT_PR_CLEANUP_SECS`].
/// Test: `pr_cleanup_interval_env_parsing`.
pub const ENV_PR_CLEANUP_SECS: &str = "TRUSTY_MPM_PR_CLEANUP_INTERVAL";
/// Default post-merge cleanup cadence when [`ENV_PR_CLEANUP_SECS`] is unset:
/// 5 minutes (#7275, owner amendment 2026-09-09).
///
/// Why: a merge is not time-critical to clean up after — the worktree and
/// branches cost only disk until they go — and five minutes bounds the sweep's
/// `gh` traffic to a rate GitHub's API budget never notices.
pub const DEFAULT_PR_CLEANUP_SECS: u64 = 300;
/// Upper bound on one whole sweep before the loop abandons it (#8335).
///
/// Why: a sweep awaiting a future that never wakes (an LLM reply that never
/// comes, a lock nobody releases), or blocked in a `gh`, `git` or tmux call,
/// stopped the loop for hours with no log line. Ten minutes is far past any
/// healthy sweep and past the default 300 s staleness window, so the heartbeat
/// watchdog reports the wedge first.
/// What: the `tick_timeout` default in [`SupervisorConfig::default`].
/// Test: `config_defaults`.
pub const DEFAULT_TICK_TIMEOUT_SECS: u64 = 600;
/// Upper bound on one per-session step of a fleet sweep (#8335).
///
/// Why: one session's hung resume or classification must not use up the whole
/// sweep's bound and starve every session after it. Two minutes covers a slow
/// LLM reply and a pane rebuild.
/// What: the `step_timeout` default in [`SupervisorConfig::default`].
/// Test: `config_defaults`.
pub const DEFAULT_STEP_TIMEOUT_SECS: u64 = 120;
/// Immutable configuration for one supervisor run.
///
/// Why: the loop reads its policy from one value per tick; bundling the knobs in
/// a small `Clone` struct makes the supervisor trivially testable — a test
/// constructs a config directly rather than mutating process-wide env vars.
/// What: carries the poll [`Duration`], the `auto_resume` gate, and the
/// `classify_idle` toggle.
/// Test: `config_defaults` and every loop test constructs one of these.
/// Parse a boolean-ish environment variable via a resolver.
///
/// Why: the env contract accepts `1`/`true`/`yes`/`on` (and their negatives) so
/// operators are not surprised by which spelling works; sharing one parser keeps
/// every boolean flag consistent. Taking the value through a resolver (rather than
/// reading `std::env` directly) keeps [`SupervisorConfig::from_env_with`] testable
/// without process-wide mutation.
/// What: returns `Some(true)` for truthy spellings, `Some(false)` for falsy ones,
/// and `None` when the var is absent or unrecognized (so the caller keeps its default).
/// Test: `env_bool_recognizes_truthy_and_falsy`, `auto_resume_env_parsing`.