@team-agent/installer 0.5.42 → 0.5.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +129 -54
  4. package/crates/team-agent/src/cli/diagnose.rs +1 -2
  5. package/crates/team-agent/src/cli/emit.rs +1 -7
  6. package/crates/team-agent/src/cli/helpers.rs +3 -1
  7. package/crates/team-agent/src/cli/leader.rs +2 -1
  8. package/crates/team-agent/src/cli/mod.rs +126 -16
  9. package/crates/team-agent/src/cli/named_address.rs +14 -5
  10. package/crates/team-agent/src/cli/profile.rs +19 -7
  11. package/crates/team-agent/src/cli/send.rs +9 -3
  12. package/crates/team-agent/src/cli/status.rs +8 -30
  13. package/crates/team-agent/src/cli/status_port.rs +1341 -1272
  14. package/crates/team-agent/src/cli/tests/base.rs +738 -660
  15. package/crates/team-agent/src/cli/tests/compile.rs +45 -18
  16. package/crates/team-agent/src/cli/tests/divergence.rs +462 -444
  17. package/crates/team-agent/src/cli/tests/lane_c.rs +365 -282
  18. package/crates/team-agent/src/cli/tests/leader_watch.rs +356 -329
  19. package/crates/team-agent/src/cli/tests/main_preserved.rs +672 -564
  20. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +284 -224
  21. package/crates/team-agent/src/cli/tests/mod.rs +17 -7
  22. package/crates/team-agent/src/cli/tests/named_address.rs +8 -2
  23. package/crates/team-agent/src/cli/tests/peer_allow.rs +10 -2
  24. package/crates/team-agent/src/cli/tests/run_delegation.rs +314 -273
  25. package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +26 -15
  26. package/crates/team-agent/src/cli/tests/status_send.rs +707 -629
  27. package/crates/team-agent/src/cli/tests/verb_install_skill.rs +20 -4
  28. package/crates/team-agent/src/cli/tests/verb_profile.rs +46 -17
  29. package/crates/team-agent/src/cli/tests/verb_validate.rs +15 -3
  30. package/crates/team-agent/src/codex_app_server.rs +2 -5
  31. package/crates/team-agent/src/compiler/tests.rs +139 -33
  32. package/crates/team-agent/src/compiler.rs +55 -22
  33. package/crates/team-agent/src/conpty/backend.rs +23 -33
  34. package/crates/team-agent/src/coordinator/backoff.rs +2 -7
  35. package/crates/team-agent/src/coordinator/conpty_shim.rs +55 -67
  36. package/crates/team-agent/src/coordinator/health.rs +46 -31
  37. package/crates/team-agent/src/coordinator/mod.rs +3 -3
  38. package/crates/team-agent/src/coordinator/orphan.rs +22 -10
  39. package/crates/team-agent/src/coordinator/steps/abnormal.rs +51 -56
  40. package/crates/team-agent/src/coordinator/tests/abnormal.rs +55 -19
  41. package/crates/team-agent/src/coordinator/tests/basics.rs +179 -41
  42. package/crates/team-agent/src/coordinator/tests/daemon.rs +53 -13
  43. package/crates/team-agent/src/coordinator/tests/health_sync.rs +78 -19
  44. package/crates/team-agent/src/coordinator/tests/main_preserved.rs +61 -11
  45. package/crates/team-agent/src/coordinator/tests/mod.rs +33 -39
  46. package/crates/team-agent/src/coordinator/tests/spine.rs +52 -12
  47. package/crates/team-agent/src/coordinator/tests/takeover.rs +73 -15
  48. package/crates/team-agent/src/coordinator/tests/tick_core.rs +50 -15
  49. package/crates/team-agent/src/coordinator/tests/watch.rs +74 -20
  50. package/crates/team-agent/src/db/message_store.rs +138 -30
  51. package/crates/team-agent/src/db/migration.rs +249 -61
  52. package/crates/team-agent/src/db/schema.rs +303 -82
  53. package/crates/team-agent/src/diagnose/comms.rs +9 -2
  54. package/crates/team-agent/src/diagnose/mod.rs +1 -3
  55. package/crates/team-agent/src/diagnose/orphans.rs +79 -61
  56. package/crates/team-agent/src/event_log.rs +70 -16
  57. package/crates/team-agent/src/layout/manager.rs +15 -4
  58. package/crates/team-agent/src/layout/mod.rs +4 -4
  59. package/crates/team-agent/src/layout/overlay.rs +10 -3
  60. package/crates/team-agent/src/layout/placement.rs +5 -1
  61. package/crates/team-agent/src/layout/recovery.rs +4 -2
  62. package/crates/team-agent/src/layout/runtime_sessions.rs +7 -7
  63. package/crates/team-agent/src/layout/sessions.rs +17 -9
  64. package/crates/team-agent/src/layout/tmux_endpoint.rs +1 -1
  65. package/crates/team-agent/src/layout/worker_env.rs +87 -19
  66. package/crates/team-agent/src/leader/helpers.rs +7 -1
  67. package/crates/team-agent/src/leader/lease.rs +199 -89
  68. package/crates/team-agent/src/leader/owner_bind.rs +55 -22
  69. package/crates/team-agent/src/leader/provider_attribution.rs +25 -6
  70. package/crates/team-agent/src/leader/rediscover/tests.rs +88 -24
  71. package/crates/team-agent/src/leader/rediscover.rs +74 -25
  72. package/crates/team-agent/src/leader/registry.rs +1 -1
  73. package/crates/team-agent/src/leader/start.rs +75 -54
  74. package/crates/team-agent/src/leader/takeover.rs +46 -11
  75. package/crates/team-agent/src/leader/tests/basics.rs +320 -167
  76. package/crates/team-agent/src/leader/tests/byte_findings.rs +361 -219
  77. package/crates/team-agent/src/leader/tests/identity.rs +428 -356
  78. package/crates/team-agent/src/leader/tests/idle.rs +285 -254
  79. package/crates/team-agent/src/leader/tests/lease_api.rs +338 -274
  80. package/crates/team-agent/src/leader/tests/lease_claim.rs +643 -593
  81. package/crates/team-agent/src/leader/tests/mod.rs +115 -99
  82. package/crates/team-agent/src/leader/tests/rediscover.rs +74 -22
  83. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +237 -211
  84. package/crates/team-agent/src/lib.rs +4 -4
  85. package/crates/team-agent/src/lifecycle/display.rs +7 -3
  86. package/crates/team-agent/src/lifecycle/launch.rs +55 -15
  87. package/crates/team-agent/src/lifecycle/mod.rs +9 -1
  88. package/crates/team-agent/src/lifecycle/profile_launch.rs +77 -34
  89. package/crates/team-agent/src/lifecycle/profile_smoke.rs +3 -1
  90. package/crates/team-agent/src/lifecycle/restart/agent.rs +1 -6
  91. package/crates/team-agent/src/lifecycle/restart/common.rs +6 -2
  92. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +1 -4
  93. package/crates/team-agent/src/lifecycle/restart/preflight.rs +6 -5
  94. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +26 -22
  95. package/crates/team-agent/src/lifecycle/restart/remove.rs +45 -35
  96. package/crates/team-agent/src/lifecycle/restart/team_state.rs +63 -17
  97. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +251 -84
  98. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +198 -48
  99. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +2 -1
  100. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +152 -32
  101. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +1 -5
  102. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +3 -0
  103. package/crates/team-agent/src/lifecycle/tests.rs +2 -2
  104. package/crates/team-agent/src/main.rs +4 -4
  105. package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +47 -20
  106. package/crates/team-agent/src/mcp_server/mod.rs +11 -2
  107. package/crates/team-agent/src/mcp_server/types.rs +14 -3
  108. package/crates/team-agent/src/mcp_server/wire.rs +230 -68
  109. package/crates/team-agent/src/messaging/delivery.rs +20 -18
  110. package/crates/team-agent/src/messaging/helpers.rs +25 -4
  111. package/crates/team-agent/src/messaging/leader_receiver.rs +4 -5
  112. package/crates/team-agent/src/messaging/mod.rs +2 -3
  113. package/crates/team-agent/src/messaging/selftest.rs +46 -26
  114. package/crates/team-agent/src/messaging/tests/main_preserved.rs +47 -12
  115. package/crates/team-agent/src/messaging/tests/runtime.rs +57 -22
  116. package/crates/team-agent/src/messaging/tests/spine.rs +154 -40
  117. package/crates/team-agent/src/messaging/tests/wave2.rs +31 -32
  118. package/crates/team-agent/src/messaging/trust.rs +25 -2
  119. package/crates/team-agent/src/messaging/watchers.rs +37 -9
  120. package/crates/team-agent/src/model/enums.rs +65 -16
  121. package/crates/team-agent/src/model/ids.rs +12 -3
  122. package/crates/team-agent/src/model/paths.rs +28 -7
  123. package/crates/team-agent/src/model/permissions.rs +176 -33
  124. package/crates/team-agent/src/model/routing.rs +66 -20
  125. package/crates/team-agent/src/model/spec.rs +365 -69
  126. package/crates/team-agent/src/model/task_graph.rs +36 -9
  127. package/crates/team-agent/src/model/yaml/tests.rs +24 -6
  128. package/crates/team-agent/src/model/yaml.rs +7 -6
  129. package/crates/team-agent/src/packaging/install.rs +23 -9
  130. package/crates/team-agent/src/packaging/migrate.rs +5 -7
  131. package/crates/team-agent/src/packaging/mod.rs +9 -1
  132. package/crates/team-agent/src/packaging/repair.rs +13 -6
  133. package/crates/team-agent/src/packaging/tests.rs +63 -16
  134. package/crates/team-agent/src/packaging/types.rs +22 -7
  135. package/crates/team-agent/src/platform/argv.rs +4 -1
  136. package/crates/team-agent/src/platform/file_lock.rs +22 -8
  137. package/crates/team-agent/src/platform/process.rs +54 -24
  138. package/crates/team-agent/src/provider/adapters/claude.rs +1 -3
  139. package/crates/team-agent/src/provider/approvals/parsing.rs +134 -26
  140. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +13 -3
  141. package/crates/team-agent/src/provider/classify.rs +127 -42
  142. package/crates/team-agent/src/provider/faults.rs +17 -5
  143. package/crates/team-agent/src/provider/helpers.rs +6 -5
  144. package/crates/team-agent/src/provider/startup_prompt.rs +75 -27
  145. package/crates/team-agent/src/state/persist.rs +28 -0
  146. package/crates/team-agent/src/state/repository.rs +8 -3
  147. package/crates/team-agent/src/tmux_backend/tests.rs +1637 -1398
  148. package/crates/team-agent/src/tmux_backend.rs +80 -44
  149. package/crates/team-agent/src/topology.rs +40 -20
  150. package/crates/team-agent/src/transport/test_support.rs +20 -23
  151. package/crates/team-agent/src/transport/tests/behavior.rs +292 -293
  152. package/crates/team-agent/src/transport/tests/mod.rs +178 -187
  153. package/crates/team-agent/src/transport/tests/wire.rs +561 -525
  154. package/crates/team-agent/src/transport.rs +12 -17
  155. package/crates/team-agent/src/transport_factory.rs +29 -14
  156. package/package.json +4 -4
@@ -19,18 +19,34 @@ fn p2_trust_refuses_subdirectory_and_sibling_of_workspace() {
19
19
 
20
20
  let subdir = format!("Allow Codex to write to {canon}/subproject ?");
21
21
  let s = attempt_trust_auto_answer(
22
- &canonical, &t, Some(&pane), &subdir, &PaneWidthQuery::Ok { pane_width: 200 }, &log,
22
+ &canonical,
23
+ &t,
24
+ Some(&pane),
25
+ &subdir,
26
+ &PaneWidthQuery::Ok { pane_width: 200 },
27
+ &log,
23
28
  )
24
29
  .unwrap();
25
- assert!(!s.answered, "a SUBDIRECTORY of the workspace must NOT auto-answer trust");
30
+ assert!(
31
+ !s.answered,
32
+ "a SUBDIRECTORY of the workspace must NOT auto-answer trust"
33
+ );
26
34
  assert_eq!(s.reason, "workspace_dir_mismatch");
27
35
 
28
36
  let sibling = format!("Allow Codex to write to {canon}-backup ?");
29
37
  let sib = attempt_trust_auto_answer(
30
- &canonical, &t, Some(&pane), &sibling, &PaneWidthQuery::Ok { pane_width: 200 }, &log,
38
+ &canonical,
39
+ &t,
40
+ Some(&pane),
41
+ &sibling,
42
+ &PaneWidthQuery::Ok { pane_width: 200 },
43
+ &log,
31
44
  )
32
45
  .unwrap();
33
- assert!(!sib.answered, "a SIBLING (<ws>-backup) must NOT auto-answer trust");
46
+ assert!(
47
+ !sib.answered,
48
+ "a SIBLING (<ws>-backup) must NOT auto-answer trust"
49
+ );
34
50
  assert_eq!(sib.reason, "workspace_dir_mismatch");
35
51
  }
36
52
 
@@ -106,14 +122,12 @@ fn p2_classify_activity_reads_command_stale_and_prompts() {
106
122
  assert_eq!((e.status, e.confidence), (ActivityStatus::Uncertain, 0.5));
107
123
  // (5b) E47 structural Working: a real codex live spinner in the bottom
108
124
  // active region must still classify Working 0.9.
109
- let e_struct = classify_agent_activity(
110
- &st,
111
- "• Working (12s · esc to interrupt)",
112
- false,
113
- None,
114
- None,
115
- );
116
- assert_eq!((e_struct.status, e_struct.confidence), (ActivityStatus::Working, 0.9));
125
+ let e_struct =
126
+ classify_agent_activity(&st, "• Working (12s · esc to interrupt)", false, None, None);
127
+ assert_eq!(
128
+ (e_struct.status, e_struct.confidence),
129
+ (ActivityStatus::Working, 0.9)
130
+ );
117
131
  }
118
132
 
119
133
  // P1 — scheduler dispatch must be exhaustive: an unknown kind surfaces an error, not a
@@ -160,17 +174,38 @@ impl Transport for DeliverOkTransport {
160
174
  fn kind(&self) -> BackendKind {
161
175
  BackendKind::Tmux
162
176
  }
163
- fn spawn_first(&self, _s: &SessionName, _w: &WindowName, _a: &[String], _c: &Path, _e: &BTreeMap<String, String>) -> Result<SpawnResult, TransportError> {
177
+ fn spawn_first(
178
+ &self,
179
+ _s: &SessionName,
180
+ _w: &WindowName,
181
+ _a: &[String],
182
+ _c: &Path,
183
+ _e: &BTreeMap<String, String>,
184
+ ) -> Result<SpawnResult, TransportError> {
164
185
  unimplemented!("not reached in delivery")
165
186
  }
166
- fn spawn_into(&self, _s: &SessionName, _w: &WindowName, _a: &[String], _c: &Path, _e: &BTreeMap<String, String>) -> Result<SpawnResult, TransportError> {
187
+ fn spawn_into(
188
+ &self,
189
+ _s: &SessionName,
190
+ _w: &WindowName,
191
+ _a: &[String],
192
+ _c: &Path,
193
+ _e: &BTreeMap<String, String>,
194
+ ) -> Result<SpawnResult, TransportError> {
167
195
  unimplemented!("not reached in delivery")
168
196
  }
169
- fn inject(&self, _t: &Target, _p: &InjectPayload, _s: Key, _b: bool) -> Result<InjectReport, TransportError> {
197
+ fn inject(
198
+ &self,
199
+ _t: &Target,
200
+ _p: &InjectPayload,
201
+ _s: Key,
202
+ _b: bool,
203
+ ) -> Result<InjectReport, TransportError> {
170
204
  Ok(InjectReport {
171
205
  stage_reached: crate::transport::InjectStage::Submit,
172
206
  inject_verification: crate::transport::InjectVerification::CaptureContainsToken,
173
- submit_verification: crate::transport::SubmitVerification::EnterSentWithoutPlaceholderCheck,
207
+ submit_verification:
208
+ crate::transport::SubmitVerification::EnterSentWithoutPlaceholderCheck,
174
209
  turn_verification: crate::transport::TurnVerification::NotYetObserved,
175
210
  attempts: 1,
176
211
  submit_diagnostics: None,
@@ -180,7 +215,10 @@ impl Transport for DeliverOkTransport {
180
215
  Ok(())
181
216
  }
182
217
  fn capture(&self, _t: &Target, range: CaptureRange) -> Result<CapturedText, TransportError> {
183
- Ok(CapturedText { text: String::new(), range })
218
+ Ok(CapturedText {
219
+ text: String::new(),
220
+ range,
221
+ })
184
222
  }
185
223
  fn query(&self, _t: &Target, _f: PaneField) -> Result<Option<String>, TransportError> {
186
224
  Ok(None)
@@ -197,7 +235,12 @@ impl Transport for DeliverOkTransport {
197
235
  fn list_windows(&self, _s: &SessionName) -> Result<Vec<WindowName>, TransportError> {
198
236
  Ok(Vec::new())
199
237
  }
200
- fn set_session_env(&self, _s: &SessionName, _k: &str, _v: &str) -> Result<SetEnvOutcome, TransportError> {
238
+ fn set_session_env(
239
+ &self,
240
+ _s: &SessionName,
241
+ _k: &str,
242
+ _v: &str,
243
+ ) -> Result<SetEnvOutcome, TransportError> {
201
244
  Ok(SetEnvOutcome::Applied)
202
245
  }
203
246
  fn kill_session(&self, _s: &SessionName) -> Result<(), TransportError> {
@@ -213,15 +256,29 @@ impl Transport for DeliverOkTransport {
213
256
 
214
257
  fn set_message_status(store: &MessageStore, message_id: &str, status: &str) {
215
258
  let conn = crate::db::schema::open_db(store.db_path()).unwrap();
216
- conn.execute("update messages set status = ?2 where message_id = ?1", sql_params![message_id, status]).unwrap();
259
+ conn.execute(
260
+ "update messages set status = ?2 where message_id = ?1",
261
+ sql_params![message_id, status],
262
+ )
263
+ .unwrap();
217
264
  }
218
265
  fn message_status_of(store: &MessageStore, message_id: &str) -> String {
219
266
  let conn = crate::db::schema::open_db(store.db_path()).unwrap();
220
- conn.query_row("select status from messages where message_id = ?1", sql_params![message_id], |r| r.get::<_, String>(0)).unwrap()
267
+ conn.query_row(
268
+ "select status from messages where message_id = ?1",
269
+ sql_params![message_id],
270
+ |r| r.get::<_, String>(0),
271
+ )
272
+ .unwrap()
221
273
  }
222
274
  fn scheduled_status_of(store: &MessageStore, id: i64) -> String {
223
275
  let conn = crate::db::schema::open_db(store.db_path()).unwrap();
224
- conn.query_row("select status from scheduled_events where id = ?1", [id], |r| r.get::<_, String>(0)).unwrap()
276
+ conn.query_row(
277
+ "select status from scheduled_events where id = ?1",
278
+ [id],
279
+ |r| r.get::<_, String>(0),
280
+ )
281
+ .unwrap()
225
282
  }
226
283
  fn seed_agent_health(store: &MessageStore, agent_id: &str, status: &str) {
227
284
  let conn = crate::db::schema::open_db(store.db_path()).unwrap();
@@ -245,7 +302,10 @@ fn seed_event_due(store: &MessageStore, kind: &str, due_at: &str, payload: &str)
245
302
  fn read_event_log(ws: &Path) -> Vec<serde_json::Value> {
246
303
  let path = crate::model::paths::logs_dir(ws).join("events.jsonl");
247
304
  match std::fs::read_to_string(&path) {
248
- Ok(text) => text.lines().filter_map(|l| serde_json::from_str(l).ok()).collect(),
305
+ Ok(text) => text
306
+ .lines()
307
+ .filter_map(|l| serde_json::from_str(l).ok())
308
+ .collect(),
249
309
  Err(_) => Vec::new(),
250
310
  }
251
311
  }
@@ -257,17 +317,31 @@ fn spine_delivery_skips_queued_statuses() {
257
317
  let ws = tmp_ws("deliver-queued");
258
318
  let store = store_for(&ws);
259
319
  let log = EventLog::new(&ws);
260
- let _acc = store.create_message(Some("t"), "leader", "w1", "hi", None, true, None).unwrap();
261
- let qidle = store.create_message(Some("t"), "leader", "w1", "hi", None, true, None).unwrap();
262
- let qstart = store.create_message(Some("t"), "leader", "w1", "hi", None, true, None).unwrap();
320
+ let _acc = store
321
+ .create_message(Some("t"), "leader", "w1", "hi", None, true, None)
322
+ .unwrap();
323
+ let qidle = store
324
+ .create_message(Some("t"), "leader", "w1", "hi", None, true, None)
325
+ .unwrap();
326
+ let qstart = store
327
+ .create_message(Some("t"), "leader", "w1", "hi", None, true, None)
328
+ .unwrap();
263
329
  set_message_status(&store, &qidle, "queued_until_idle");
264
330
  set_message_status(&store, &qstart, "queued_until_start");
265
331
  let state = serde_json::json!({"agents": {"w1": {}}});
266
332
 
267
333
  let _ = deliver_pending_messages(&ws, &state, &DeliverOkTransport, &log).unwrap();
268
334
 
269
- assert_eq!(message_status_of(&store, &qidle), "queued_until_idle", "queued_until_idle must NOT be claimed/injected/marked by deliver_pending");
270
- assert_eq!(message_status_of(&store, &qstart), "queued_until_start", "queued_until_start must NOT be touched by deliver_pending");
335
+ assert_eq!(
336
+ message_status_of(&store, &qidle),
337
+ "queued_until_idle",
338
+ "queued_until_idle must NOT be claimed/injected/marked by deliver_pending"
339
+ );
340
+ assert_eq!(
341
+ message_status_of(&store, &qstart),
342
+ "queued_until_start",
343
+ "queued_until_start must NOT be touched by deliver_pending"
344
+ );
271
345
  }
272
346
 
273
347
  // #2 (CONTRACT, corrected) — busy recipient = LIFECYCLE status=="busy" → emit ONE send.deferred_busy
@@ -280,21 +354,40 @@ fn spine_delivery_busy_recipient_defers_with_event() {
280
354
  let ws = tmp_ws("deliver-busy");
281
355
  let store = store_for(&ws);
282
356
  let log = EventLog::new(&ws);
283
- let mid = store.create_message(Some("t"), "leader", "w1", "hi", None, true, None).unwrap();
357
+ let mid = store
358
+ .create_message(Some("t"), "leader", "w1", "hi", None, true, None)
359
+ .unwrap();
284
360
  let state = serde_json::json!({"agents": {"w1": {"status": "busy"}}});
285
361
 
286
362
  let delivered = deliver_pending_messages(&ws, &state, &DeliverOkTransport, &log).unwrap();
287
363
 
288
- assert!(!delivered.contains(&mid), "a busy recipient's message must NOT be delivered");
289
- assert_eq!(message_status_of(&store, &mid), "accepted", "the row must stay 'accepted' (no-drop, left for a later tick)");
364
+ assert!(
365
+ !delivered.contains(&mid),
366
+ "a busy recipient's message must NOT be delivered"
367
+ );
368
+ assert_eq!(
369
+ message_status_of(&store, &mid),
370
+ "accepted",
371
+ "the row must stay 'accepted' (no-drop, left for a later tick)"
372
+ );
290
373
  let events = read_event_log(&ws);
291
374
  let deferred: Vec<_> = events
292
375
  .iter()
293
376
  .filter(|e| e.get("event").and_then(|v| v.as_str()) == Some("send.deferred_busy"))
294
377
  .collect();
295
- assert_eq!(deferred.len(), 1, "exactly one send.deferred_busy event expected; got {events:?}");
296
- assert_eq!(deferred[0].get("recipient").and_then(|v| v.as_str()), Some("w1"));
297
- assert_eq!(deferred[0].get("reason").and_then(|v| v.as_str()), Some("recipient_busy"));
378
+ assert_eq!(
379
+ deferred.len(),
380
+ 1,
381
+ "exactly one send.deferred_busy event expected; got {events:?}"
382
+ );
383
+ assert_eq!(
384
+ deferred[0].get("recipient").and_then(|v| v.as_str()),
385
+ Some("w1")
386
+ );
387
+ assert_eq!(
388
+ deferred[0].get("reason").and_then(|v| v.as_str()),
389
+ Some("recipient_busy")
390
+ );
298
391
  }
299
392
 
300
393
  // CONTRACT (shared-root, real-machine-driven; golden = correct-behavior baseline): an ALIVE worker
@@ -309,7 +402,9 @@ fn contract_alive_worker_with_working_health_is_deliverable_not_deferred() {
309
402
  let ws = tmp_ws("deliver-alive-working");
310
403
  let store = store_for(&ws);
311
404
  let log = EventLog::new(&ws);
312
- let mid = store.create_message(Some("t"), "leader", "w1", "hi", None, true, None).unwrap();
405
+ let mid = store
406
+ .create_message(Some("t"), "leader", "w1", "hi", None, true, None)
407
+ .unwrap();
313
408
  seed_agent_health(&store, "w1", "WORKING"); // turn-level state — must NOT gate delivery
314
409
  let state = serde_json::json!({"agents": {"w1": {"status": "running"}}}); // lifecycle: alive
315
410
 
@@ -360,7 +455,12 @@ fn spine_scheduler_poison_event_does_not_halt_batch() {
360
455
  let store = store_for(&ws);
361
456
  let log = EventLog::new(&ws);
362
457
  // earlier due_at → selected first; malformed JSON payload errors at from_str.
363
- let poison = seed_event_due(&store, "send", "2000-01-01T00:00:00+00:00", "{not valid json");
458
+ let poison = seed_event_due(
459
+ &store,
460
+ "send",
461
+ "2000-01-01T00:00:00+00:00",
462
+ "{not valid json",
463
+ );
364
464
  // later due_at → selected after poison; healthy.
365
465
  let healthy = seed_event_due(&store, "health_ping", "2000-01-02T00:00:00+00:00", "{}");
366
466
 
@@ -381,7 +481,9 @@ fn spine_scheduler_poison_event_does_not_halt_batch() {
381
481
  // failure is loud: scheduler.event_failed event emitted for the poison.
382
482
  let events = read_event_log(&ws);
383
483
  assert!(
384
- events.iter().any(|e| e.get("event").and_then(|v| v.as_str()) == Some("scheduler.event_failed")),
484
+ events
485
+ .iter()
486
+ .any(|e| e.get("event").and_then(|v| v.as_str()) == Some("scheduler.event_failed")),
385
487
  "U1 #5: a poison event must emit scheduler.event_failed (failure must be loud, not silent)"
386
488
  );
387
489
  }
@@ -423,7 +525,9 @@ fn spine_delivery_injects_resolvable_target_not_bare_agent_pane() {
423
525
  let ws = tmp_ws("deliver-target");
424
526
  let store = store_for(&ws);
425
527
  let log = EventLog::new(&ws);
426
- let _mid = store.create_message(Some("t"), "leader", "w1", "hi", None, true, None).unwrap();
528
+ let _mid = store
529
+ .create_message(Some("t"), "leader", "w1", "hi", None, true, None)
530
+ .unwrap();
427
531
  // in-team, non-busy agent -> deliver proceeds; the session-qualified resolution uses session_name + window.
428
532
  let state = serde_json::json!({
429
533
  "session_name": "team-x",
@@ -434,7 +538,11 @@ fn spine_delivery_injects_resolvable_target_not_bare_agent_pane() {
434
538
  let _ = deliver_pending_messages(&ws, &state, &transport, &log).unwrap();
435
539
 
436
540
  let recorded = transport.inject_targets();
437
- assert_eq!(recorded.len(), 1, "deliver must inject the one pending message exactly once; got {recorded:?}");
541
+ assert_eq!(
542
+ recorded.len(),
543
+ 1,
544
+ "deliver must inject the one pending message exactly once; got {recorded:?}"
545
+ );
438
546
  let target = recorded[0].clone();
439
547
  // THE BUG (rt-host-a #3): deliver builds Target::Pane(PaneId(message.recipient)) — the agent id as a
440
548
  // BARE pane, which a non-attached coordinator can't resolve ("can't find pane: w1").
@@ -473,7 +581,9 @@ fn spine_delivery_injects_rendered_protocol_block_not_raw_content() {
473
581
  let ws = tmp_ws("deliver-render");
474
582
  let store = store_for(&ws);
475
583
  let log = EventLog::new(&ws);
476
- let mid = store.create_message(Some("t1"), "leader", "w1", "do the thing", None, true, None).unwrap();
584
+ let mid = store
585
+ .create_message(Some("t1"), "leader", "w1", "do the thing", None, true, None)
586
+ .unwrap();
477
587
  let state = serde_json::json!({
478
588
  "session_name": "team-x",
479
589
  "agents": {"w1": {"status": "idle", "window": "w1"}}
@@ -483,7 +593,11 @@ fn spine_delivery_injects_rendered_protocol_block_not_raw_content() {
483
593
  let _ = deliver_pending_messages(&ws, &state, &transport, &log).unwrap();
484
594
 
485
595
  let recorded = transport.inject_payloads();
486
- assert_eq!(recorded.len(), 1, "deliver must inject the one pending message exactly once; got {recorded:?}");
596
+ assert_eq!(
597
+ recorded.len(),
598
+ 1,
599
+ "deliver must inject the one pending message exactly once; got {recorded:?}"
600
+ );
487
601
  let payload = recorded[0].clone();
488
602
  // THE BUG (rt-host-a #4): deliver injects the RAW content -> the worker never reports (it only builds
489
603
  // a result_envelope on the rendered block with the token).
@@ -34,7 +34,7 @@
34
34
  use super::*;
35
35
  use crate::messaging::deliver_pending_message;
36
36
  use crate::transport::test_support::OfflineTransport;
37
- use crate::transport::{PaneLiveness, PaneId, SessionName, WindowName};
37
+ use crate::transport::{PaneId, PaneLiveness, SessionName, WindowName};
38
38
 
39
39
  fn pane_info_w(pane_id: &str, session: &str, window: &str) -> PaneInfo {
40
40
  PaneInfo {
@@ -97,8 +97,7 @@ fn u1_a_leader_pane_drift_falls_back_to_live_session_window_not_failed() {
97
97
  .with_liveness("%old", PaneLiveness::Dead)
98
98
  .with_liveness("%new", PaneLiveness::Live);
99
99
 
100
- let out = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state)
101
- .unwrap();
100
+ let out = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state).unwrap();
102
101
 
103
102
  assert!(
104
103
  out.reason != Some(DeliveryRefusal::LeaderNotAttached),
@@ -108,14 +107,15 @@ fn u1_a_leader_pane_drift_falls_back_to_live_session_window_not_failed() {
108
107
  out.message_status.0
109
108
  );
110
109
  assert_ne!(
111
- out.message_status.0,
112
- "failed",
110
+ out.message_status.0, "failed",
113
111
  "U1-A: drift must not be terminal; got status={}",
114
112
  out.message_status.0
115
113
  );
116
114
  let inject_targets = transport.inject_targets();
117
115
  assert!(
118
- inject_targets.iter().any(|t| matches!(t, Target::Pane(p) if p.as_str() == "%new")),
116
+ inject_targets
117
+ .iter()
118
+ .any(|t| matches!(t, Target::Pane(p) if p.as_str() == "%new")),
119
119
  "U1-A: fallback chain must inject into the LIVE pane (%new), not the stale \
120
120
  %old; injected targets={inject_targets:?}"
121
121
  );
@@ -147,8 +147,7 @@ fn u1_a_leader_truly_not_attached_still_leader_not_attached_not_synthetic_target
147
147
  .with_targets(vec![])
148
148
  .with_session_present(false);
149
149
 
150
- let out = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state)
151
- .unwrap();
150
+ let out = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state).unwrap();
152
151
 
153
152
  assert_eq!(
154
153
  out.reason,
@@ -205,22 +204,19 @@ fn u1_b_list_targets_err_defers_does_not_reuse_unvalidated_cached_pane() {
205
204
  .with_session_present(true)
206
205
  .with_default_liveness(PaneLiveness::Live); // cached looks alive in isolation
207
206
 
208
- let out = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state)
209
- .unwrap();
207
+ let out = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state).unwrap();
210
208
 
211
209
  assert!(
212
210
  !out.ok,
213
211
  "U1-B: jitter must not declare delivered (false-green guard)"
214
212
  );
215
213
  assert_ne!(
216
- out.message_status.0,
217
- "delivered",
214
+ out.message_status.0, "delivered",
218
215
  "U1-B: jitter must not mark delivered; got status={}",
219
216
  out.message_status.0
220
217
  );
221
218
  assert_ne!(
222
- out.message_status.0,
223
- "failed",
219
+ out.message_status.0, "failed",
224
220
  "U1-B: jitter must not terminal-fail this tick (defer); got status={}",
225
221
  out.message_status.0
226
222
  );
@@ -233,8 +229,14 @@ fn u1_b_list_targets_err_defers_does_not_reuse_unvalidated_cached_pane() {
233
229
  let events = log.tail(0).unwrap();
234
230
  assert!(
235
231
  events.iter().any(|event| {
236
- let reason = event.get("reason").and_then(serde_json::Value::as_str).unwrap_or("");
237
- let verif = event.get("verification").and_then(serde_json::Value::as_str).unwrap_or("");
232
+ let reason = event
233
+ .get("reason")
234
+ .and_then(serde_json::Value::as_str)
235
+ .unwrap_or("");
236
+ let verif = event
237
+ .get("verification")
238
+ .and_then(serde_json::Value::as_str)
239
+ .unwrap_or("");
238
240
  reason.contains("list_targets")
239
241
  || verif.contains("list_targets")
240
242
  || reason.contains("server_jitter")
@@ -280,8 +282,7 @@ fn u1_b_list_targets_ok_empty_is_authoritative_not_deferred() {
280
282
  .with_session_present(true)
281
283
  .with_default_liveness(PaneLiveness::Unknown);
282
284
 
283
- let out = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state)
284
- .unwrap();
285
+ let out = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state).unwrap();
285
286
 
286
287
  // We don't assert ok=true (the fake provider may or may not roundtrip);
287
288
  // we assert the pre-existing inject ATTEMPT happened (i.e. NOT deferred
@@ -353,8 +354,7 @@ fn u1_c_delivery_peek_uses_tail_eighty_not_full() {
353
354
  .with_default_liveness(PaneLiveness::Live)
354
355
  .with_capture_for_pane("%cdx", "OpenAI Codex\ncodex>\n");
355
356
 
356
- let _ = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state)
357
- .unwrap();
357
+ let _ = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state).unwrap();
358
358
 
359
359
  let ranges = transport.capture_ranges();
360
360
  assert!(
@@ -363,7 +363,9 @@ fn u1_c_delivery_peek_uses_tail_eighty_not_full() {
363
363
  startup_prompts; got no capture calls"
364
364
  );
365
365
  assert!(
366
- ranges.iter().any(|r| matches!(r, CaptureRange::Tail(n) if *n == 80)),
366
+ ranges
367
+ .iter()
368
+ .any(|r| matches!(r, CaptureRange::Tail(n) if *n == 80)),
367
369
  "U1-C: delivery peek site must request Tail(80), not Full. ranges={ranges:?}. \
368
370
  Full was the pre-wave-2 default and caused scrolled-off answered-trust \
369
371
  residue to be classified as actionable, parking idle codex panes forever."
@@ -415,16 +417,13 @@ Do you trust the contents of this directory?
415
417
  .with_default_liveness(PaneLiveness::Live)
416
418
  .with_capture_for_pane("%cdx", live_trust);
417
419
 
418
- let out = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state)
419
- .unwrap();
420
+ let out = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state).unwrap();
420
421
 
421
422
  assert_eq!(
422
- out.message_status.0,
423
- "queued_until_trust",
423
+ out.message_status.0, "queued_until_trust",
424
424
  "U1-C regression guard: a live trust modal in Tail(80) must still park as \
425
425
  queued_until_trust; got status={} reason={:?}",
426
- out.message_status.0,
427
- out.reason
426
+ out.message_status.0, out.reason
428
427
  );
429
428
  }
430
429
 
@@ -553,8 +552,7 @@ fn u1_a_projected_team_state_with_null_session_still_finds_drifted_leader_pane()
553
552
  .with_liveness("%old", PaneLiveness::Dead)
554
553
  .with_liveness("%new", PaneLiveness::Live);
555
554
 
556
- let out = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state)
557
- .unwrap();
555
+ let out = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state).unwrap();
558
556
 
559
557
  assert!(
560
558
  out.reason != Some(DeliveryRefusal::LeaderNotAttached),
@@ -568,15 +566,16 @@ fn u1_a_projected_team_state_with_null_session_still_finds_drifted_leader_pane()
568
566
  out.message_status.0
569
567
  );
570
568
  assert_ne!(
571
- out.message_status.0,
572
- "failed",
569
+ out.message_status.0, "failed",
573
570
  "U1-A projection-null escape: drift via projected state must not be \
574
571
  terminal; got status={}",
575
572
  out.message_status.0
576
573
  );
577
574
  let inject_targets = transport.inject_targets();
578
575
  assert!(
579
- inject_targets.iter().any(|t| matches!(t, Target::Pane(p) if p.as_str() == "%new")),
576
+ inject_targets
577
+ .iter()
578
+ .any(|t| matches!(t, Target::Pane(p) if p.as_str() == "%new")),
580
579
  "U1-A projection-null escape: fallback chain must inject into the LIVE \
581
580
  pane (%new) reachable via session+window probe; injected={inject_targets:?}"
582
581
  );
@@ -135,8 +135,31 @@ fn path_tokens(text: &str) -> Vec<String> {
135
135
  fn token_trim_char(c: char) -> bool {
136
136
  matches!(
137
137
  c,
138
- '?' | '!' | ',' | ';' | ':' | '"' | '\'' | '`' | '[' | ']' | '(' | ')' | '{' | '}'
139
- | '<' | '>' | '│' | '┃' | '┆' | '┊' | '┌' | '┐' | '└' | '┘' | '─' | '═'
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
+ | '═'
140
163
  )
141
164
  }
142
165
 
@@ -29,7 +29,10 @@ pub fn notify_result_watchers(
29
29
  let conn = crate::db::schema::open_db(store.db_path())?;
30
30
  let result_task = result.get("task_id").and_then(|v| v.as_str());
31
31
  let result_agent = result.get("agent_id").and_then(|v| v.as_str());
32
- let result_id = result.get("result_id").and_then(|v| v.as_str()).map(ToString::to_string);
32
+ let result_id = result
33
+ .get("result_id")
34
+ .and_then(|v| v.as_str())
35
+ .map(ToString::to_string);
33
36
  let mut matched: Vec<&serde_json::Value> = watchers
34
37
  .iter()
35
38
  .filter(|w| watcher_matches(w, result_task, result_agent))
@@ -145,7 +148,13 @@ fn deliver_primary_watcher(
145
148
  .get("message_id")
146
149
  .and_then(|v| v.as_str())
147
150
  .map(ToString::to_string);
148
- mark_watcher_notified(conn, event_log, watcher_id, result_id, message_id.as_deref())?;
151
+ mark_watcher_notified(
152
+ conn,
153
+ event_log,
154
+ watcher_id,
155
+ result_id,
156
+ message_id.as_deref(),
157
+ )?;
149
158
  return Ok(WatcherNotice {
150
159
  watcher_id: watcher_id.to_string(),
151
160
  result_id: Some(result_id.to_string()),
@@ -171,7 +180,10 @@ fn deliver_primary_watcher(
171
180
  let content = format_result_watcher_notification(result);
172
181
  let message_id = store.create_message(
173
182
  result_task,
174
- watcher.get("leader_id").and_then(|v| v.as_str()).unwrap_or("team-agent"),
183
+ watcher
184
+ .get("leader_id")
185
+ .and_then(|v| v.as_str())
186
+ .unwrap_or("team-agent"),
175
187
  "leader",
176
188
  &content,
177
189
  None,
@@ -596,7 +608,9 @@ pub fn delivered_result_message(
596
608
  for row in rows.into_iter().rev() {
597
609
  let (message_id, content) = row;
598
610
  if result_id_from_text(&content).as_deref() == Some(result_id) {
599
- return Ok(Some(serde_json::json!({"message_id": message_id, "content": content})));
611
+ return Ok(Some(
612
+ serde_json::json!({"message_id": message_id, "content": content}),
613
+ ));
600
614
  }
601
615
  }
602
616
  Ok(None)
@@ -624,11 +638,25 @@ pub fn result_id_from_text(content: &str) -> Option<String> {
624
638
  /// `format_result_watcher_notification` (`result_delivery.py:521`):拼 watcher 通知文案 +
625
639
  /// `Result id: <id>` 行。**格式必须字节级稳定** (golden fixture,与 [`result_id_from_text`] 对偶)。
626
640
  pub fn format_result_watcher_notification(result: &serde_json::Value) -> String {
627
- let task_id = result.get("task_id").and_then(|v| v.as_str()).unwrap_or("unknown task");
628
- let agent_id = result.get("agent_id").and_then(|v| v.as_str()).unwrap_or("unknown agent");
629
- let status = result.get("status").and_then(|v| v.as_str()).unwrap_or("unknown");
630
- let summary = result.get("summary").and_then(|v| v.as_str()).unwrap_or("completed");
631
- let mut lines = vec![format!("Task {task_id} reported {status} from {agent_id}: {summary}")];
641
+ let task_id = result
642
+ .get("task_id")
643
+ .and_then(|v| v.as_str())
644
+ .unwrap_or("unknown task");
645
+ let agent_id = result
646
+ .get("agent_id")
647
+ .and_then(|v| v.as_str())
648
+ .unwrap_or("unknown agent");
649
+ let status = result
650
+ .get("status")
651
+ .and_then(|v| v.as_str())
652
+ .unwrap_or("unknown");
653
+ let summary = result
654
+ .get("summary")
655
+ .and_then(|v| v.as_str())
656
+ .unwrap_or("completed");
657
+ let mut lines = vec![format!(
658
+ "Task {task_id} reported {status} from {agent_id}: {summary}"
659
+ )];
632
660
  if let Some(tests) = result.get("tests").and_then(|v| v.as_array()) {
633
661
  let rendered: Vec<String> = tests
634
662
  .iter()