@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
@@ -23,19 +23,27 @@ pub(crate) fn attribute_pane_provider(pane: &PaneInfo) -> Option<Provider> {
23
23
  attribute_pane_provider_with_process(pane, provider_from_pid_env, provider_from_pid_argv)
24
24
  }
25
25
 
26
- fn attribute_pane_provider_with_process(
26
+ fn attribute_pane_provider_with_process<FEnv, FArg>(
27
27
  pane: &PaneInfo,
28
- provider_from_env_pid: impl Fn(u32) -> Option<Provider>,
29
- provider_from_argv_pid: impl Fn(u32) -> Option<Provider>,
30
- ) -> Option<Provider> {
28
+ provider_from_env_pid: FEnv,
29
+ provider_from_argv_pid: FArg,
30
+ ) -> Option<Provider>
31
+ where
32
+ FEnv: Fn(u32) -> Option<Provider>,
33
+ FArg: Fn(u32) -> Option<Provider>,
34
+ {
31
35
  provider_from_env(&pane.leader_env)
32
- .or_else(|| pane.pane_pid.and_then(provider_from_env_pid))
36
+ .or_else(|| pane.pane_pid.and_then(|pid| provider_from_env_pid(pid)))
33
37
  .or_else(|| {
34
38
  pane.current_command
35
39
  .as_deref()
36
40
  .and_then(attribute_command_provider)
37
41
  })
38
- .or_else(|| pane.pane_pid.and_then(provider_from_argv_pid))
42
+ .or_else(|| pane.pane_pid.and_then(|pid| provider_from_argv_pid(pid)))
43
+ .or_else(|| {
44
+ pane.pane_pid
45
+ .and_then(|pid| provider_from_process_tree_argv(pid, &provider_from_argv_pid))
46
+ })
39
47
  }
40
48
 
41
49
  pub(crate) fn attribute_command_provider(command: &str) -> Option<Provider> {
@@ -63,6 +71,17 @@ fn provider_from_pid_argv(pid: u32) -> Option<Provider> {
63
71
  .and_then(provider_from_command_text)
64
72
  }
65
73
 
74
+ fn provider_from_process_tree_argv(
75
+ root_pid: u32,
76
+ provider_from_argv_pid: &impl Fn(u32) -> Option<Provider>,
77
+ ) -> Option<Provider> {
78
+ crate::platform::process::process_tree(root_pid)
79
+ .ok()?
80
+ .into_iter()
81
+ .filter(|pid| *pid != root_pid)
82
+ .find_map(provider_from_argv_pid)
83
+ }
84
+
66
85
  fn provider_from_pid_env(pid: u32) -> Option<Provider> {
67
86
  process_environment(pid)
68
87
  .as_deref()
@@ -206,8 +206,14 @@ fn rediscover_updates_unique_env_triple_match() {
206
206
  let ws = ws("rediscover");
207
207
  let mut env = BTreeMap::new();
208
208
  env.insert("TEAM_AGENT_LEADER_PANE_ID".to_string(), "%r".to_string());
209
- env.insert("TEAM_AGENT_LEADER_PROVIDER".to_string(), "codex".to_string());
210
- env.insert("TEAM_AGENT_MACHINE_FINGERPRINT".to_string(), "fp".to_string());
209
+ env.insert(
210
+ "TEAM_AGENT_LEADER_PROVIDER".to_string(),
211
+ "codex".to_string(),
212
+ );
213
+ env.insert(
214
+ "TEAM_AGENT_MACHINE_FINGERPRINT".to_string(),
215
+ "fp".to_string(),
216
+ );
211
217
  let target = PaneInfo {
212
218
  pane_id: PaneId::new("%r"),
213
219
  session: SessionName::new("sess"),
@@ -235,10 +241,14 @@ fn rediscover_updates_unique_env_triple_match() {
235
241
  });
236
242
  let event_log = crate::event_log::EventLog::new(&ws);
237
243
 
238
- let out = rediscover_leader_receiver_from_targets(&ws, &mut state, &[target], &event_log).unwrap();
244
+ let out =
245
+ rediscover_leader_receiver_from_targets(&ws, &mut state, &[target], &event_log).unwrap();
239
246
 
240
247
  assert_eq!(out["status"], json!("updated"));
241
- assert!(out.get("ok").is_none(), "golden rediscover result has no ok wrapper");
248
+ assert!(
249
+ out.get("ok").is_none(),
250
+ "golden rediscover result has no ok wrapper"
251
+ );
242
252
  assert_eq!(out["owner_identity"]["pane_id"], json!("%r"));
243
253
  // Stage 3d: receiver lives at canonical teams.<team_key>.leader_receiver.
244
254
  let team_key = crate::state::projection::team_state_key(&state);
@@ -255,9 +265,15 @@ fn rediscover_updates_unique_env_triple_match() {
255
265
  assert!(rebound.get("reason").is_some_and(Value::is_null));
256
266
  assert_eq!(rebound["old_pane_id"], json!("%r"));
257
267
  assert_eq!(rebound["new_pane_id"], json!("%r"));
258
- assert_eq!(rebound["owner_identity"]["machine_fingerprint"], json!("fp"));
268
+ assert_eq!(
269
+ rebound["owner_identity"]["machine_fingerprint"],
270
+ json!("fp")
271
+ );
259
272
  assert_eq!(rebound["uuid_prefix"], json!("OWNERUUI"));
260
- assert_eq!(receiver["discovery"], json!("stale_rediscovery_unique_candidate"));
273
+ assert_eq!(
274
+ receiver["discovery"],
275
+ json!("stale_rediscovery_unique_candidate")
276
+ );
261
277
  }
262
278
 
263
279
  #[test]
@@ -276,8 +292,14 @@ fn rediscover_owner_identity_passthrough_sets_discovery_and_reason() {
276
292
  pane_pid: None,
277
293
  leader_env: BTreeMap::from([
278
294
  ("TEAM_AGENT_LEADER_PANE_ID".to_string(), "%raw".to_string()),
279
- ("TEAM_AGENT_LEADER_PROVIDER".to_string(), "codex".to_string()),
280
- ("TEAM_AGENT_MACHINE_FINGERPRINT".to_string(), "fp".to_string()),
295
+ (
296
+ "TEAM_AGENT_LEADER_PROVIDER".to_string(),
297
+ "codex".to_string(),
298
+ ),
299
+ (
300
+ "TEAM_AGENT_MACHINE_FINGERPRINT".to_string(),
301
+ "fp".to_string(),
302
+ ),
281
303
  ]),
282
304
  };
283
305
  let raw_identity = json!({
@@ -305,8 +327,14 @@ fn rediscover_owner_identity_passthrough_sets_discovery_and_reason() {
305
327
  // Stage 3d: receiver lives at canonical teams.<team_key>.leader_receiver.
306
328
  let team_key = crate::state::projection::team_state_key(&state);
307
329
  let receiver = &state["teams"][&team_key]["leader_receiver"];
308
- assert_eq!(receiver["discovery"], json!("stale_rediscovery_owner_identity"));
309
- let rebound = event_named(&event_log.tail(10).unwrap(), "leader_receiver.rebind_applied");
330
+ assert_eq!(
331
+ receiver["discovery"],
332
+ json!("stale_rediscovery_owner_identity")
333
+ );
334
+ let rebound = event_named(
335
+ &event_log.tail(10).unwrap(),
336
+ "leader_receiver.rebind_applied",
337
+ );
310
338
  assert_eq!(rebound["reason"], json!("stale_receiver"));
311
339
  assert_eq!(rebound["owner_identity"], raw_identity);
312
340
  assert_eq!(rebound["uuid_prefix"], json!("RAWUUID1"));
@@ -328,16 +356,23 @@ fn rediscover_missing_and_ambiguous_use_golden_status_names() {
328
356
  "claimed_via": "claim-leader"
329
357
  }
330
358
  });
331
- let missing = rediscover_leader_receiver_from_targets(&ws, &mut state, &[], &event_log).unwrap();
359
+ let missing =
360
+ rediscover_leader_receiver_from_targets(&ws, &mut state, &[], &event_log).unwrap();
332
361
  assert_eq!(missing["status"], json!("missing"));
333
362
  assert!(missing.get("ok").is_none());
334
363
  assert_eq!(missing["owner_identity"]["pane_id"], json!("%owner"));
335
- let missing_event = event_named(&event_log.tail(10).unwrap(), "leader_receiver.rediscover_missing");
364
+ let missing_event = event_named(
365
+ &event_log.tail(10).unwrap(),
366
+ "leader_receiver.rediscover_missing",
367
+ );
336
368
  assert_eq!(missing_event["owner_identity"]["pane_id"], json!("%owner"));
337
369
  assert_eq!(missing_event["provider"], json!("codex"));
338
370
  assert_eq!(missing_event["old_target"], json!("%owner"));
339
371
  assert_eq!(missing_event["candidate_count"], json!(0));
340
- let missing_rebind = last_event_named(&event_log.tail(10).unwrap(), "leader_receiver.rebind_required");
372
+ let missing_rebind = last_event_named(
373
+ &event_log.tail(10).unwrap(),
374
+ "leader_receiver.rebind_required",
375
+ );
341
376
  assert!(missing_rebind.get("reason").is_some_and(Value::is_null));
342
377
  assert_eq!(missing_rebind["provider"], json!("codex"));
343
378
  assert_eq!(missing_rebind["team_id"], json!("sess"));
@@ -350,9 +385,18 @@ fn rediscover_missing_and_ambiguous_use_golden_status_names() {
350
385
  assert!(missing_rebind.get("invalidation_reason").is_none());
351
386
 
352
387
  let mut env = BTreeMap::new();
353
- env.insert("TEAM_AGENT_LEADER_PANE_ID".to_string(), "%owner".to_string());
354
- env.insert("TEAM_AGENT_LEADER_PROVIDER".to_string(), "codex".to_string());
355
- env.insert("TEAM_AGENT_MACHINE_FINGERPRINT".to_string(), "fp".to_string());
388
+ env.insert(
389
+ "TEAM_AGENT_LEADER_PANE_ID".to_string(),
390
+ "%owner".to_string(),
391
+ );
392
+ env.insert(
393
+ "TEAM_AGENT_LEADER_PROVIDER".to_string(),
394
+ "codex".to_string(),
395
+ );
396
+ env.insert(
397
+ "TEAM_AGENT_MACHINE_FINGERPRINT".to_string(),
398
+ "fp".to_string(),
399
+ );
356
400
  let mk = |pane: &str, window: u32, pane_index: u32, tty: &str| PaneInfo {
357
401
  pane_id: PaneId::new(pane),
358
402
  session: SessionName::new("sess"),
@@ -375,18 +419,29 @@ fn rediscover_missing_and_ambiguous_use_golden_status_names() {
375
419
  .unwrap();
376
420
  assert_eq!(ambiguous["status"], json!("ambiguous"));
377
421
  assert!(ambiguous.get("ok").is_none());
378
- assert_eq!(ambiguous["owner_candidates"].as_array().map(Vec::len), Some(2));
422
+ assert_eq!(
423
+ ambiguous["owner_candidates"].as_array().map(Vec::len),
424
+ Some(2)
425
+ );
379
426
  assert_eq!(ambiguous["owner_candidates"][0]["pane_id"], json!("%a"));
380
427
  assert_eq!(ambiguous["owner_candidates"][0]["window_index"], json!(1));
381
428
  assert_eq!(ambiguous["owner_candidates"][0]["pane_index"], json!(0));
382
- assert_eq!(ambiguous["owner_candidates"][0]["pane_tty"], json!("/ttys001"));
383
- assert_eq!(ambiguous["owner_candidates"][0]["current_command"], json!("codex"));
429
+ assert_eq!(
430
+ ambiguous["owner_candidates"][0]["pane_tty"],
431
+ json!("/ttys001")
432
+ );
433
+ assert_eq!(
434
+ ambiguous["owner_candidates"][0]["current_command"],
435
+ json!("codex")
436
+ );
384
437
  assert_eq!(ambiguous["owner_candidates"][0]["fingerprint"], json!("fp"));
385
438
  assert!(ambiguous["owner_candidates"][0]["current_path"]
386
439
  .as_str()
387
440
  .is_some_and(|path| path.contains("rediscover-%a")));
388
441
  assert!(ambiguous.get("incident").is_none());
389
- assert!(ambiguous["incident_id"].as_str().is_some_and(|id| id.starts_with("rediscover_")));
442
+ assert!(ambiguous["incident_id"]
443
+ .as_str()
444
+ .is_some_and(|id| id.starts_with("rediscover_")));
390
445
  assert_eq!(ambiguous["deduped"], json!(false));
391
446
  let events = event_log.tail(20).unwrap();
392
447
  let ambiguous_event = event_named(&events, "leader_receiver.rediscover_ambiguous");
@@ -398,7 +453,10 @@ fn rediscover_missing_and_ambiguous_use_golden_status_names() {
398
453
  assert_eq!(candidates_event["provider"], json!("codex"));
399
454
  assert_eq!(candidates_event["team_id"], json!("sess"));
400
455
  assert_eq!(candidates_event["uuid_prefix"], json!("OWNERUUI"));
401
- assert_eq!(candidates_event["debounce_bucket"], ambiguous["incident_id"]);
456
+ assert_eq!(
457
+ candidates_event["debounce_bucket"],
458
+ ambiguous["incident_id"]
459
+ );
402
460
  assert_eq!(candidates_event["reason"], json!("force_confirm_required"));
403
461
  assert_eq!(candidates_event["queued"].as_array().map(Vec::len), Some(2));
404
462
  }
@@ -538,19 +596,25 @@ fn rediscover_failed_uses_golden_status_and_rebind_shape() {
538
596
  .unwrap();
539
597
 
540
598
  assert_eq!(out["status"], json!("failed"));
541
- assert!(out["error"].as_str().is_some_and(|error| error.contains("scan failed")));
599
+ assert!(out["error"]
600
+ .as_str()
601
+ .is_some_and(|error| error.contains("scan failed")));
542
602
  assert!(out.get("ok").is_none());
543
603
  let events = event_log.tail(10).unwrap();
544
604
  let failed = event_named(&events, "leader_receiver.rediscover_failed");
545
605
  assert_eq!(failed["provider"], json!("codex"));
546
- assert!(failed["error"].as_str().is_some_and(|error| error.contains("scan failed")));
606
+ assert!(failed["error"]
607
+ .as_str()
608
+ .is_some_and(|error| error.contains("scan failed")));
547
609
  let rebind = event_named(&events, "leader_receiver.rebind_required");
548
610
  assert_eq!(rebind["old_pane_id"], json!("%old"));
549
611
  assert_eq!(rebind["reason"], json!("stale_receiver"));
550
612
  assert_eq!(rebind["provider"], json!("codex"));
551
613
  assert_eq!(rebind["team_id"], json!("sess"));
552
614
  assert_eq!(rebind["rediscovery_status"], json!("failed"));
553
- assert!(rebind["error"].as_str().is_some_and(|error| error.contains("scan failed")));
615
+ assert!(rebind["error"]
616
+ .as_str()
617
+ .is_some_and(|error| error.contains("scan failed")));
554
618
  assert!(rebind.get("owner_identity").is_none());
555
619
  assert!(rebind.get("uuid_prefix").is_none());
556
620
  assert!(rebind.get("invalidation_reason").is_none());
@@ -6,11 +6,13 @@ use std::path::{Path, PathBuf};
6
6
  use serde_json::{json, Value};
7
7
 
8
8
  use crate::model::ids::{LeaderSessionUuid, OwnerEpoch};
9
- use crate::provider::Provider;
10
9
  use crate::provider::wire::provider_wire;
10
+ use crate::provider::Provider;
11
11
  use crate::transport::{PaneId, PaneInfo, SessionName, Transport, WindowName};
12
12
 
13
- use super::helpers::{get_path_str, now_ts, parse_provider, prefix, resolve_workspace_for_hash, sha1_hex_prefix};
13
+ use super::helpers::{
14
+ get_path_str, now_ts, parse_provider, prefix, resolve_workspace_for_hash, sha1_hex_prefix,
15
+ };
14
16
  use super::{
15
17
  ClaimedVia, Discovery, LeaderError, LeaderEvent, LeaderReceiver, LeaseSource, ReceiverMode,
16
18
  ReceiverStatus, TeamOwner,
@@ -56,8 +58,21 @@ pub fn try_readopt_leader_pane(
56
58
  .or_else(|| owner_record.and_then(|owner| owner.leader_session_uuid.clone()))
57
59
  .or_else(|| receiver.leader_session_uuid.clone());
58
60
  let provider = candidate.provider.unwrap_or(receiver_provider);
59
- let rebound = receiver_from_candidate(&candidate, receiver, provider, uuid.clone(), owner_epoch, Discovery::AttachReadopt);
60
- let owner = owner_from_candidate(&candidate, provider, uuid, owner_epoch, ClaimedVia::AttachLeader);
61
+ let rebound = receiver_from_candidate(
62
+ &candidate,
63
+ receiver,
64
+ provider,
65
+ uuid.clone(),
66
+ owner_epoch,
67
+ Discovery::AttachReadopt,
68
+ );
69
+ let owner = owner_from_candidate(
70
+ &candidate,
71
+ provider,
72
+ uuid,
73
+ owner_epoch,
74
+ ClaimedVia::AttachLeader,
75
+ );
61
76
  let mut owner_identity = OwnerIdentity::from_state(workspace, state)?;
62
77
  if let Some(record) = owner_record {
63
78
  owner_identity.pane_id = Some(record.pane_id.as_str().to_string());
@@ -123,7 +138,9 @@ pub fn rediscover_leader_receiver(
123
138
  event_log: &crate::event_log::EventLog,
124
139
  ) -> Result<Value, LeaderError> {
125
140
  let identity = OwnerIdentity::from_state(workspace, state)?;
126
- rediscover_leader_receiver_with_identity(workspace, state, transport, event_log, &identity, None)
141
+ rediscover_leader_receiver_with_identity(
142
+ workspace, state, transport, event_log, &identity, None,
143
+ )
127
144
  }
128
145
 
129
146
  pub fn rediscover_leader_receiver_with_owner_identity(
@@ -164,7 +181,13 @@ fn rediscover_leader_receiver_with_identity(
164
181
  "error": error.as_str(),
165
182
  }),
166
183
  )?;
167
- emit_failed_rebind_required(event_log, state, identity, invalidation_reason, error.as_str())?;
184
+ emit_failed_rebind_required(
185
+ event_log,
186
+ state,
187
+ identity,
188
+ invalidation_reason,
189
+ error.as_str(),
190
+ )?;
168
191
  return Ok(json!({
169
192
  "status": "failed",
170
193
  "error": error,
@@ -189,12 +212,7 @@ pub fn rediscover_leader_receiver_from_targets(
189
212
  ) -> Result<Value, LeaderError> {
190
213
  let identity = OwnerIdentity::from_state(workspace, state)?;
191
214
  rediscover_leader_receiver_from_targets_with_identity(
192
- workspace,
193
- state,
194
- targets,
195
- event_log,
196
- &identity,
197
- None,
215
+ workspace, state, targets, event_log, &identity, None,
198
216
  )
199
217
  }
200
218
 
@@ -268,7 +286,14 @@ fn rediscover_leader_receiver_from_targets_with_identity(
268
286
  )?;
269
287
  }
270
288
  if has_owner_identity {
271
- emit_rebind_required(event_log, "ambiguous", state, identity, invalidation_reason, "confirm rediscover leader receiver")?;
289
+ emit_rebind_required(
290
+ event_log,
291
+ "ambiguous",
292
+ state,
293
+ identity,
294
+ invalidation_reason,
295
+ "confirm rediscover leader receiver",
296
+ )?;
272
297
  return Ok(json!({
273
298
  "status": "ambiguous",
274
299
  "owner_candidates": candidate_values(&candidates, identity.from_caller),
@@ -504,13 +529,16 @@ impl OwnerIdentity {
504
529
  }
505
530
 
506
531
  fn has_match_identity(&self) -> bool {
507
- self.pane_id.is_some() || self.leader_session_uuid.is_some() || !self.machine_fingerprint.is_empty()
532
+ self.pane_id.is_some()
533
+ || self.leader_session_uuid.is_some()
534
+ || !self.machine_fingerprint.is_empty()
508
535
  }
509
536
  }
510
537
 
511
538
  fn target_from_value(value: &Value) -> Option<LeaderTarget> {
512
539
  let pane_id = get_str(value, "pane_id").or_else(|| get_str(value, "pane"))?;
513
- let current_command = get_str(value, "pane_current_command").or_else(|| get_str(value, "current_command"));
540
+ let current_command =
541
+ get_str(value, "pane_current_command").or_else(|| get_str(value, "current_command"));
514
542
  let leader_env = map_env(value.get("leader_env").or_else(|| value.get("env")));
515
543
  let pane_pid = get_u32(value, "pane_pid");
516
544
  let provider = super::attribute_pane_provider(&PaneInfo {
@@ -553,7 +581,8 @@ fn target_from_value(value: &Value) -> Option<LeaderTarget> {
553
581
  leader_env,
554
582
  provider,
555
583
  leader_session_uuid,
556
- fingerprint: get_str(value, "fingerprint").or_else(|| get_str(value, "machine_fingerprint")),
584
+ fingerprint: get_str(value, "fingerprint")
585
+ .or_else(|| get_str(value, "machine_fingerprint")),
557
586
  })
558
587
  }
559
588
 
@@ -648,7 +677,13 @@ fn candidate_pane_ids(candidates: &[LeaderTarget]) -> Vec<String> {
648
677
 
649
678
  fn ambiguous_incident_id(identity: &OwnerIdentity, panes: &[String]) -> String {
650
679
  let mut bytes = Vec::new();
651
- bytes.extend_from_slice(identity.provider.map(provider_wire).unwrap_or("").as_bytes());
680
+ bytes.extend_from_slice(
681
+ identity
682
+ .provider
683
+ .map(provider_wire)
684
+ .unwrap_or("")
685
+ .as_bytes(),
686
+ );
652
687
  bytes.push(0);
653
688
  bytes.extend_from_slice(identity.team_id.as_bytes());
654
689
  bytes.push(0);
@@ -752,7 +787,8 @@ fn ambiguous_candidates_already_broadcast(
752
787
  incident_id: &str,
753
788
  ) -> Result<bool, LeaderError> {
754
789
  Ok(event_log.tail(200)?.iter().any(|event| {
755
- event.get("event").and_then(Value::as_str) == Some(LeaderEvent::ReceiverAmbiguousCandidates.name())
790
+ event.get("event").and_then(Value::as_str)
791
+ == Some(LeaderEvent::ReceiverAmbiguousCandidates.name())
756
792
  && event.get("incident_id").and_then(Value::as_str) == Some(incident_id)
757
793
  }))
758
794
  }
@@ -915,11 +951,15 @@ fn env_triple_matches(target: &LeaderTarget, identity: &OwnerIdentity) -> bool {
915
951
  && target
916
952
  .leader_env
917
953
  .get("TEAM_AGENT_LEADER_PROVIDER")
918
- .is_some_and(|value| identity.provider.is_some_and(|provider| value == provider_wire(provider)))
954
+ .is_some_and(|value| {
955
+ identity
956
+ .provider
957
+ .is_some_and(|provider| value == provider_wire(provider))
958
+ })
919
959
  && target
920
- .leader_env
921
- .get("TEAM_AGENT_MACHINE_FINGERPRINT")
922
- .is_some_and(|value| value == &identity.machine_fingerprint)
960
+ .leader_env
961
+ .get("TEAM_AGENT_MACHINE_FINGERPRINT")
962
+ .is_some_and(|value| value == &identity.machine_fingerprint)
923
963
  }
924
964
 
925
965
  fn different_live_owner_uuid_mismatch(
@@ -984,7 +1024,12 @@ fn owner_from_candidate(
984
1024
  machine_fingerprint: candidate
985
1025
  .fingerprint
986
1026
  .clone()
987
- .or_else(|| candidate.leader_env.get("TEAM_AGENT_MACHINE_FINGERPRINT").cloned())
1027
+ .or_else(|| {
1028
+ candidate
1029
+ .leader_env
1030
+ .get("TEAM_AGENT_MACHINE_FINGERPRINT")
1031
+ .cloned()
1032
+ })
988
1033
  .unwrap_or_default(),
989
1034
  leader_session_uuid: uuid,
990
1035
  owner_epoch: epoch,
@@ -1067,7 +1112,9 @@ fn write_readopt_state(
1067
1112
  *state = json!({});
1068
1113
  }
1069
1114
  if !state.is_object() {
1070
- return Err(LeaderError::Validation("state root is not an object".to_string()));
1115
+ return Err(LeaderError::Validation(
1116
+ "state root is not an object".to_string(),
1117
+ ));
1071
1118
  }
1072
1119
  let team_key = crate::state::projection::team_state_key(state);
1073
1120
  let record = crate::state::ownership::OwnershipWrite::new()
@@ -1086,7 +1133,9 @@ fn write_receiver_state(
1086
1133
  *state = json!({});
1087
1134
  }
1088
1135
  if !state.is_object() {
1089
- return Err(LeaderError::Validation("state root is not an object".to_string()));
1136
+ return Err(LeaderError::Validation(
1137
+ "state root is not an object".to_string(),
1138
+ ));
1090
1139
  }
1091
1140
  let team_key = crate::state::projection::team_state_key(state);
1092
1141
  let record = crate::state::ownership::OwnershipWrite::new()
@@ -200,7 +200,7 @@ pub fn register_binding_from_state_best_effort(
200
200
  team: Option<&str>,
201
201
  source: &str,
202
202
  ) -> Option<RegistryWriteOutcome> {
203
- let Ok(state) = crate::state::persist::load_runtime_state(workspace) else {
203
+ let Ok(state) = crate::state::persist::load_runtime_state_without_migrations(workspace) else {
204
204
  return None;
205
205
  };
206
206
  let team_key = match team.filter(|team| !team.is_empty()) {