@team-agent/installer 0.5.41 → 0.5.43

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 (152) 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 +27 -7
  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 +47 -52
  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/coordinator/tick.rs +19 -1
  51. package/crates/team-agent/src/db/message_store.rs +138 -30
  52. package/crates/team-agent/src/db/migration.rs +249 -61
  53. package/crates/team-agent/src/db/schema.rs +303 -82
  54. package/crates/team-agent/src/diagnose/comms.rs +9 -2
  55. package/crates/team-agent/src/diagnose/mod.rs +1 -3
  56. package/crates/team-agent/src/diagnose/orphans.rs +79 -61
  57. package/crates/team-agent/src/event_log.rs +70 -16
  58. package/crates/team-agent/src/layout/manager.rs +15 -4
  59. package/crates/team-agent/src/layout/mod.rs +4 -4
  60. package/crates/team-agent/src/layout/overlay.rs +10 -3
  61. package/crates/team-agent/src/layout/placement.rs +5 -1
  62. package/crates/team-agent/src/layout/recovery.rs +4 -2
  63. package/crates/team-agent/src/layout/runtime_sessions.rs +7 -7
  64. package/crates/team-agent/src/layout/sessions.rs +17 -9
  65. package/crates/team-agent/src/layout/tmux_endpoint.rs +1 -1
  66. package/crates/team-agent/src/layout/worker_env.rs +52 -19
  67. package/crates/team-agent/src/leader/helpers.rs +7 -1
  68. package/crates/team-agent/src/leader/lease.rs +195 -82
  69. package/crates/team-agent/src/leader/owner_bind.rs +55 -22
  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/start.rs +75 -54
  73. package/crates/team-agent/src/leader/takeover.rs +46 -11
  74. package/crates/team-agent/src/leader/tests/basics.rs +320 -167
  75. package/crates/team-agent/src/leader/tests/byte_findings.rs +361 -219
  76. package/crates/team-agent/src/leader/tests/identity.rs +428 -356
  77. package/crates/team-agent/src/leader/tests/idle.rs +285 -254
  78. package/crates/team-agent/src/leader/tests/lease_api.rs +338 -274
  79. package/crates/team-agent/src/leader/tests/lease_claim.rs +643 -593
  80. package/crates/team-agent/src/leader/tests/mod.rs +115 -99
  81. package/crates/team-agent/src/leader/tests/rediscover.rs +74 -22
  82. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +237 -211
  83. package/crates/team-agent/src/lib.rs +4 -4
  84. package/crates/team-agent/src/lifecycle/display.rs +7 -3
  85. package/crates/team-agent/src/lifecycle/launch.rs +6 -1
  86. package/crates/team-agent/src/lifecycle/mod.rs +9 -1
  87. package/crates/team-agent/src/lifecycle/profile_launch.rs +77 -34
  88. package/crates/team-agent/src/lifecycle/profile_smoke.rs +3 -1
  89. package/crates/team-agent/src/lifecycle/restart/agent.rs +1 -6
  90. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +1 -4
  91. package/crates/team-agent/src/lifecycle/restart/preflight.rs +6 -5
  92. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +26 -22
  93. package/crates/team-agent/src/lifecycle/restart/remove.rs +45 -35
  94. package/crates/team-agent/src/lifecycle/restart/team_state.rs +63 -17
  95. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +251 -84
  96. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +198 -48
  97. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +2 -1
  98. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +152 -32
  99. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +1 -5
  100. package/crates/team-agent/src/lifecycle/tests.rs +2 -2
  101. package/crates/team-agent/src/main.rs +4 -4
  102. package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +47 -20
  103. package/crates/team-agent/src/mcp_server/mod.rs +11 -2
  104. package/crates/team-agent/src/mcp_server/types.rs +14 -3
  105. package/crates/team-agent/src/mcp_server/wire.rs +230 -68
  106. package/crates/team-agent/src/messaging/delivery.rs +20 -18
  107. package/crates/team-agent/src/messaging/helpers.rs +25 -4
  108. package/crates/team-agent/src/messaging/leader_receiver.rs +4 -5
  109. package/crates/team-agent/src/messaging/mod.rs +2 -3
  110. package/crates/team-agent/src/messaging/selftest.rs +46 -26
  111. package/crates/team-agent/src/messaging/tests/main_preserved.rs +47 -12
  112. package/crates/team-agent/src/messaging/tests/runtime.rs +57 -22
  113. package/crates/team-agent/src/messaging/tests/spine.rs +154 -40
  114. package/crates/team-agent/src/messaging/tests/wave2.rs +31 -32
  115. package/crates/team-agent/src/messaging/trust.rs +25 -2
  116. package/crates/team-agent/src/messaging/watchers.rs +37 -9
  117. package/crates/team-agent/src/model/enums.rs +65 -16
  118. package/crates/team-agent/src/model/ids.rs +12 -3
  119. package/crates/team-agent/src/model/paths.rs +28 -7
  120. package/crates/team-agent/src/model/permissions.rs +176 -33
  121. package/crates/team-agent/src/model/routing.rs +66 -20
  122. package/crates/team-agent/src/model/spec.rs +365 -69
  123. package/crates/team-agent/src/model/task_graph.rs +36 -9
  124. package/crates/team-agent/src/model/yaml/tests.rs +24 -6
  125. package/crates/team-agent/src/model/yaml.rs +7 -6
  126. package/crates/team-agent/src/packaging/install.rs +23 -9
  127. package/crates/team-agent/src/packaging/migrate.rs +5 -7
  128. package/crates/team-agent/src/packaging/mod.rs +9 -1
  129. package/crates/team-agent/src/packaging/repair.rs +13 -6
  130. package/crates/team-agent/src/packaging/tests.rs +63 -16
  131. package/crates/team-agent/src/packaging/types.rs +22 -7
  132. package/crates/team-agent/src/platform/argv.rs +4 -1
  133. package/crates/team-agent/src/platform/file_lock.rs +22 -8
  134. package/crates/team-agent/src/platform/process.rs +21 -22
  135. package/crates/team-agent/src/provider/adapters/claude.rs +1 -3
  136. package/crates/team-agent/src/provider/approvals/parsing.rs +134 -26
  137. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +13 -3
  138. package/crates/team-agent/src/provider/classify.rs +127 -42
  139. package/crates/team-agent/src/provider/faults.rs +17 -5
  140. package/crates/team-agent/src/provider/helpers.rs +6 -5
  141. package/crates/team-agent/src/provider/startup_prompt.rs +75 -27
  142. package/crates/team-agent/src/state/repository.rs +27 -14
  143. package/crates/team-agent/src/tmux_backend/tests.rs +1637 -1398
  144. package/crates/team-agent/src/tmux_backend.rs +80 -44
  145. package/crates/team-agent/src/topology.rs +27 -20
  146. package/crates/team-agent/src/transport/test_support.rs +20 -23
  147. package/crates/team-agent/src/transport/tests/behavior.rs +292 -293
  148. package/crates/team-agent/src/transport/tests/mod.rs +178 -187
  149. package/crates/team-agent/src/transport/tests/wire.rs +561 -525
  150. package/crates/team-agent/src/transport.rs +12 -17
  151. package/crates/team-agent/src/transport_factory.rs +29 -14
  152. package/package.json +4 -4
@@ -1,125 +1,141 @@
1
- #![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
2
- use super::*;
3
- use std::cell::Cell;
1
+ #![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
2
+ use super::*;
3
+ use std::cell::Cell;
4
4
 
5
- // ── helpers ────────────────────────────────────────────────────────────
6
- /// derive 一个真 LeaderSessionUuid(骨架无公开裸构造器,仅 `derive`)。
7
- fn uuid(fp: &str, ws: &str, user: &str, team: &str) -> LeaderSessionUuid {
8
- LeaderSessionUuid::derive(fp, ws, user, team).unwrap()
9
- }
5
+ // ── helpers ────────────────────────────────────────────────────────────
6
+ /// derive 一个真 LeaderSessionUuid(骨架无公开裸构造器,仅 `derive`)。
7
+ fn uuid(fp: &str, ws: &str, user: &str, team: &str) -> LeaderSessionUuid {
8
+ LeaderSessionUuid::derive(fp, ws, user, team).unwrap()
9
+ }
10
10
 
11
- /// 注入式 turn-state 分类器:MUST-NOT-13 命门 —— 统计 classify 调用次数,
12
- /// 据此断言 idle 面经此 trait 分类、绝不直连任何 provider client。
13
- struct CountingClassifier {
14
- calls: Cell<usize>,
15
- result: TurnState,
16
- }
17
- impl CountingClassifier {
18
- fn new(result: TurnState) -> Self {
19
- Self { calls: Cell::new(0), result }
11
+ /// 注入式 turn-state 分类器:MUST-NOT-13 命门 —— 统计 classify 调用次数,
12
+ /// 据此断言 idle 面经此 trait 分类、绝不直连任何 provider client。
13
+ struct CountingClassifier {
14
+ calls: Cell<usize>,
15
+ result: TurnState,
16
+ }
17
+ impl CountingClassifier {
18
+ fn new(result: TurnState) -> Self {
19
+ Self {
20
+ calls: Cell::new(0),
21
+ result,
20
22
  }
21
23
  }
22
- impl TurnStateClassifier for CountingClassifier {
23
- fn classify(
24
- &self,
25
- _provider: Provider,
26
- session_log_text: &str,
27
- ) -> Result<TurnClassification, LeaderError> {
28
- self.calls.set(self.calls.get() + 1);
29
- // 空 session-log 文本 → unknown(bug-085:None rollout_path 漏穿后读到空串)
30
- let state = if session_log_text.is_empty() { TurnState::Unknown } else { self.result };
31
- Ok(TurnClassification {
32
- state,
33
- turn_id: None,
34
- annotations: vec![],
35
- reason: if state == TurnState::Unknown { Some("empty_session_log".into()) } else { None },
36
- })
37
- }
24
+ }
25
+ impl TurnStateClassifier for CountingClassifier {
26
+ fn classify(
27
+ &self,
28
+ _provider: Provider,
29
+ session_log_text: &str,
30
+ ) -> Result<TurnClassification, LeaderError> {
31
+ self.calls.set(self.calls.get() + 1);
32
+ // session-log 文本 → unknown(bug-085:None rollout_path 漏穿后读到空串)。
33
+ let state = if session_log_text.is_empty() {
34
+ TurnState::Unknown
35
+ } else {
36
+ self.result
37
+ };
38
+ Ok(TurnClassification {
39
+ state,
40
+ turn_id: None,
41
+ annotations: vec![],
42
+ reason: if state == TurnState::Unknown {
43
+ Some("empty_session_log".into())
44
+ } else {
45
+ None
46
+ },
47
+ })
38
48
  }
49
+ }
39
50
 
40
- static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
51
+ static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
41
52
 
42
- /// RAII env override: restores prior values on drop (incl. panic unwind), so a
43
- /// RED assertion-panic cannot leak a dirty env into other tests.
44
- struct EnvGuard {
45
- saved: Vec<(String, Option<String>)>,
46
- }
47
- impl EnvGuard {
48
- fn apply(vars: &[(&str, Option<&str>)]) -> Self {
49
- let saved = vars.iter().map(|(k, _)| ((*k).to_string(), std::env::var(k).ok())).collect();
50
- for (k, v) in vars {
51
- match v {
52
- Some(val) => std::env::set_var(k, val),
53
- None => std::env::remove_var(k),
54
- }
53
+ /// RAII env override: restores prior values on drop (incl. panic unwind), so a
54
+ /// RED assertion-panic cannot leak a dirty env into other tests.
55
+ struct EnvGuard {
56
+ saved: Vec<(String, Option<String>)>,
57
+ }
58
+ impl EnvGuard {
59
+ fn apply(vars: &[(&str, Option<&str>)]) -> Self {
60
+ let saved = vars
61
+ .iter()
62
+ .map(|(k, _)| ((*k).to_string(), std::env::var(k).ok()))
63
+ .collect();
64
+ for (k, v) in vars {
65
+ match v {
66
+ Some(val) => std::env::set_var(k, val),
67
+ None => std::env::remove_var(k),
55
68
  }
56
- Self { saved }
57
69
  }
70
+ Self { saved }
58
71
  }
59
- impl Drop for EnvGuard {
60
- fn drop(&mut self) {
61
- for (k, v) in &self.saved {
62
- match v {
63
- Some(val) => std::env::set_var(k, val),
64
- None => std::env::remove_var(k),
65
- }
72
+ }
73
+ impl Drop for EnvGuard {
74
+ fn drop(&mut self) {
75
+ for (k, v) in &self.saved {
76
+ match v {
77
+ Some(val) => std::env::set_var(k, val),
78
+ None => std::env::remove_var(k),
66
79
  }
67
80
  }
68
81
  }
82
+ }
69
83
 
70
- fn p2_temp_ws(tag: &str) -> PathBuf {
71
- let ws = std::env::temp_dir().join(format!("ta_rs_p2leader_{}_{}", tag, std::process::id()));
72
- std::fs::create_dir_all(&ws).unwrap();
73
- ws
74
- }
84
+ fn p2_temp_ws(tag: &str) -> PathBuf {
85
+ let ws = std::env::temp_dir().join(format!("ta_rs_p2leader_{}_{}", tag, std::process::id()));
86
+ std::fs::create_dir_all(&ws).unwrap();
87
+ ws
88
+ }
75
89
 
76
- // P1 — state-supplied leader_session_uuid → source 'derived' (NOT 'env'); Python's
77
- // source is binary override-vs-derived (leader/__init__.py:206).
78
- struct SeededLiveness {
79
- live_panes: std::collections::BTreeSet<String>,
80
- }
81
- impl crate::state::owner_gate::PaneLivenessProbe for SeededLiveness {
82
- fn liveness(&self, pane_id: &str) -> crate::model::enums::PaneLiveness {
83
- if self.live_panes.contains(pane_id) {
84
- crate::model::enums::PaneLiveness::Live
85
- } else {
86
- crate::model::enums::PaneLiveness::Dead
87
- }
90
+ // P1 — state-supplied leader_session_uuid → source 'derived' (NOT 'env'); Python's
91
+ // source is binary override-vs-derived (leader/__init__.py:206).
92
+ struct SeededLiveness {
93
+ live_panes: std::collections::BTreeSet<String>,
94
+ }
95
+ impl crate::state::owner_gate::PaneLivenessProbe for SeededLiveness {
96
+ fn liveness(&self, pane_id: &str) -> crate::model::enums::PaneLiveness {
97
+ if self.live_panes.contains(pane_id) {
98
+ crate::model::enums::PaneLiveness::Live
99
+ } else {
100
+ crate::model::enums::PaneLiveness::Dead
88
101
  }
89
102
  }
103
+ }
90
104
 
91
- fn seeded_liveness(panes: &[&str]) -> SeededLiveness {
92
- SeededLiveness { live_panes: panes.iter().map(|p| (*p).to_string()).collect() }
105
+ fn seeded_liveness(panes: &[&str]) -> SeededLiveness {
106
+ SeededLiveness {
107
+ live_panes: panes.iter().map(|p| (*p).to_string()).collect(),
93
108
  }
109
+ }
94
110
 
95
- // RED — VACANT ACQUIRE: empty state + eligible caller → status=Claimed,
96
- // reason=vacant_acquired, owner_epoch 0→1, owner+receiver bound to caller,
97
- // claimed_via=claim-leader, BOTH state files written (dual-state).
98
- // golden /tmp/probe_claimed.py: epoch 1, claimed_via "claim-leader",
99
- // discovery "claim_leader", reason "vacant_acquired".
100
- /// Event names from the workspace event log (events.jsonl), in write order.
101
- fn event_names(ws: &std::path::Path) -> Vec<String> {
102
- let path = crate::model::paths::logs_dir(ws).join("events.jsonl");
103
- std::fs::read_to_string(&path)
104
- .map(|t| {
105
- t.lines()
106
- .filter_map(|l| serde_json::from_str::<serde_json::Value>(l).ok())
107
- .filter_map(|e| e.get("event").and_then(|v| v.as_str()).map(String::from))
108
- .collect()
109
- })
110
- .unwrap_or_default()
111
- }
111
+ // RED — VACANT ACQUIRE: empty state + eligible caller → status=Claimed,
112
+ // reason=vacant_acquired, owner_epoch 0→1, owner+receiver bound to caller,
113
+ // claimed_via=claim-leader, BOTH state files written (dual-state).
114
+ // golden /tmp/probe_claimed.py: epoch 1, claimed_via "claim-leader",
115
+ // discovery "claim_leader", reason "vacant_acquired".
116
+ /// Event names from the workspace event log (events.jsonl), in write order.
117
+ fn event_names(ws: &std::path::Path) -> Vec<String> {
118
+ let path = crate::model::paths::logs_dir(ws).join("events.jsonl");
119
+ std::fs::read_to_string(&path)
120
+ .map(|t| {
121
+ t.lines()
122
+ .filter_map(|l| serde_json::from_str::<serde_json::Value>(l).ok())
123
+ .filter_map(|e| e.get("event").and_then(|v| v.as_str()).map(String::from))
124
+ .collect()
125
+ })
126
+ .unwrap_or_default()
127
+ }
112
128
 
113
- // D1 [BLOCK] — claim-path team_owner is EXACTLY golden's 7 keys (NO os_user). Golden new_owner
114
- // (__init__.py:686-694) = pane_id,provider,machine_fingerprint,leader_session_uuid,owner_epoch,
115
- // claimed_at,claimed_via; only Family-A leader_binding writes os_user, NEVER the claim path. Rust
116
- // make_owner sets os_user:Some(..) + TeamOwner has no skip_serializing_if -> 8 keys. RED.
129
+ // D1 [BLOCK] — claim-path team_owner is EXACTLY golden's 7 keys (NO os_user). Golden new_owner
130
+ // (__init__.py:686-694) = pane_id,provider,machine_fingerprint,leader_session_uuid,owner_epoch,
131
+ // claimed_at,claimed_via; only Family-A leader_binding writes os_user, NEVER the claim path. Rust
132
+ // make_owner sets os_user:Some(..) + TeamOwner has no skip_serializing_if -> 8 keys. RED.
117
133
 
118
134
  mod basics;
119
- mod wake_start_owner;
120
- mod lease_api;
121
- mod idle;
135
+ mod byte_findings;
122
136
  mod identity;
137
+ mod idle;
138
+ mod lease_api;
123
139
  mod lease_claim;
124
- mod byte_findings;
125
140
  mod rediscover;
141
+ mod wake_start_owner;
@@ -8,7 +8,10 @@ fn r12_ws(tag: &str) -> std::path::PathBuf {
8
8
  "ta-r12-{}-{}-{}",
9
9
  tag,
10
10
  std::process::id(),
11
- std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos()
11
+ std::time::SystemTime::now()
12
+ .duration_since(std::time::UNIX_EPOCH)
13
+ .unwrap()
14
+ .as_nanos()
12
15
  ));
13
16
  std::fs::create_dir_all(&ws).unwrap();
14
17
  ws
@@ -31,14 +34,23 @@ fn usable_target(pane: &str, cmd: &str) -> crate::transport::PaneInfo {
31
34
  }
32
35
 
33
36
  fn r12_events(ws: &std::path::Path) -> Vec<serde_json::Value> {
34
- crate::event_log::EventLog::new(ws).tail(0).unwrap_or_default()
37
+ crate::event_log::EventLog::new(ws)
38
+ .tail(0)
39
+ .unwrap_or_default()
35
40
  }
36
41
  fn find_ev<'a>(evs: &'a [serde_json::Value], name: &str) -> Option<&'a serde_json::Value> {
37
- evs.iter().rev().find(|e| e.get("event").and_then(|v| v.as_str()) == Some(name))
42
+ evs.iter()
43
+ .rev()
44
+ .find(|e| e.get("event").and_then(|v| v.as_str()) == Some(name))
38
45
  }
39
46
  fn r12_keys(e: &serde_json::Value) -> std::collections::BTreeSet<String> {
40
47
  e.as_object()
41
- .map(|o| o.keys().filter(|k| *k != "ts" && *k != "event").cloned().collect())
48
+ .map(|o| {
49
+ o.keys()
50
+ .filter(|k| *k != "ts" && *k != "event")
51
+ .cloned()
52
+ .collect()
53
+ })
42
54
  .unwrap_or_default()
43
55
  }
44
56
 
@@ -46,12 +58,16 @@ fn r12_keys(e: &serde_json::Value) -> std::collections::BTreeSet<String> {
46
58
  // path D3: golden _rediscover_leader_receiver:133-145, owner present + >1 owner_candidates → broadcast).
47
59
  fn owner_matching_target(pane: &str, cmd: &str, uuid: &str) -> crate::transport::PaneInfo {
48
60
  let mut t = usable_target(pane, cmd);
49
- t.leader_env
50
- .insert("TEAM_AGENT_LEADER_SESSION_UUID".to_string(), uuid.to_string());
61
+ t.leader_env.insert(
62
+ "TEAM_AGENT_LEADER_SESSION_UUID".to_string(),
63
+ uuid.to_string(),
64
+ );
51
65
  t
52
66
  }
53
67
  fn count_ev(evs: &[serde_json::Value], name: &str) -> usize {
54
- evs.iter().filter(|e| e.get("event").and_then(|v| v.as_str()) == Some(name)).count()
68
+ evs.iter()
69
+ .filter(|e| e.get("event").and_then(|v| v.as_str()) == Some(name))
70
+ .count()
55
71
  }
56
72
 
57
73
  // R12-0 + R12-3: NO owner_identity + 2 command-usable targets -> golden two-tier AMBIGUOUS (command-usable
@@ -61,10 +77,16 @@ fn count_ev(evs: &[serde_json::Value], name: &str) -> usize {
61
77
  fn r12_no_owner_two_usable_targets_is_ambiguous_not_missing() {
62
78
  let ws = r12_ws("noownerambig");
63
79
  let log = crate::event_log::EventLog::new(&ws);
64
- let mut state = serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
80
+ let mut state =
81
+ serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
65
82
  let targets = vec![usable_target("%a", "codex"), usable_target("%b", "codex")];
66
83
  let out = crate::leader::rediscover_leader_receiver_from_targets_with_owner_identity(
67
- &ws, &mut state, &targets, &log, &serde_json::json!({}), None,
84
+ &ws,
85
+ &mut state,
86
+ &targets,
87
+ &log,
88
+ &serde_json::json!({}),
89
+ None,
68
90
  )
69
91
  .unwrap();
70
92
  assert_eq!(
@@ -86,10 +108,16 @@ fn r12_no_owner_two_usable_targets_is_ambiguous_not_missing() {
86
108
  fn r12_no_owner_zero_usable_missing_is_lean_payload() {
87
109
  let ws = r12_ws("noownermiss");
88
110
  let log = crate::event_log::EventLog::new(&ws);
89
- let mut state = serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
111
+ let mut state =
112
+ serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
90
113
  let targets: Vec<crate::transport::PaneInfo> = vec![];
91
114
  let out = crate::leader::rediscover_leader_receiver_from_targets_with_owner_identity(
92
- &ws, &mut state, &targets, &log, &serde_json::json!({}), None,
115
+ &ws,
116
+ &mut state,
117
+ &targets,
118
+ &log,
119
+ &serde_json::json!({}),
120
+ None,
93
121
  )
94
122
  .unwrap();
95
123
  assert_eq!(out.get("status").and_then(|v| v.as_str()), Some("missing"));
@@ -108,7 +136,10 @@ fn r12_no_owner_zero_usable_missing_is_lean_payload() {
108
136
  "R12-1: no-owner D7 rebind_required (golden leader_panes.py:186) has rediscovery_status only, NO \
109
137
  recovery_action/owner_identity; got {rk:?}"
110
138
  );
111
- assert_eq!(rebind.get("rediscovery_status").and_then(|v| v.as_str()), Some("missing"));
139
+ assert_eq!(
140
+ rebind.get("rediscovery_status").and_then(|v| v.as_str()),
141
+ Some("missing")
142
+ );
112
143
  }
113
144
 
114
145
  // R12-1/R12-2: owner_identity PRESENT + non-matching + 2 command-usable -> golden owner-MISSING (D4):
@@ -119,7 +150,8 @@ fn r12_no_owner_zero_usable_missing_is_lean_payload() {
119
150
  fn r12_owner_missing_candidate_count_is_command_usable_len_and_rebind_shape() {
120
151
  let ws = r12_ws("ownermiss");
121
152
  let log = crate::event_log::EventLog::new(&ws);
122
- let mut state = serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
153
+ let mut state =
154
+ serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
123
155
  let owner = serde_json::json!({
124
156
  "pane_id": "%owner", "leader_session_uuid": "U-abcdef01",
125
157
  "machine_fingerprint": "F", "provider": "codex", "team_id": "team-a"
@@ -161,7 +193,8 @@ fn r12_owner_missing_candidate_count_is_command_usable_len_and_rebind_shape() {
161
193
  fn r12_5_ambiguous_candidates_event_is_full_golden_candidates_key() {
162
194
  let ws = r12_ws("ambigbroadcast");
163
195
  let log = crate::event_log::EventLog::new(&ws);
164
- let mut state = serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
196
+ let mut state =
197
+ serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
165
198
  let owner = serde_json::json!({
166
199
  "pane_id": "%owner", "leader_session_uuid": "U-abcdef01",
167
200
  "machine_fingerprint": "F", "provider": "codex", "team_id": "team-a"
@@ -174,7 +207,10 @@ fn r12_5_ambiguous_candidates_event_is_full_golden_candidates_key() {
174
207
  &ws, &mut state, &targets, &log, &owner, None,
175
208
  )
176
209
  .unwrap();
177
- assert_eq!(out.get("status").and_then(|v| v.as_str()), Some("ambiguous"));
210
+ assert_eq!(
211
+ out.get("status").and_then(|v| v.as_str()),
212
+ Some("ambiguous")
213
+ );
178
214
  let evs = r12_events(&ws);
179
215
  let bc = find_ev(&evs, "leader_receiver.ambiguous_candidates")
180
216
  .expect("OWNER-ambiguous must broadcast leader_receiver.ambiguous_candidates");
@@ -193,7 +229,10 @@ fn r12_5_ambiguous_candidates_event_is_full_golden_candidates_key() {
193
229
  Some(serde_json::json!(["%a", "%b"])),
194
230
  "R12-5: candidates == sorted pane_ids (golden candidate_ids = sorted); bc={bc:?}"
195
231
  );
196
- assert_eq!(bc.get("reason").and_then(|v| v.as_str()), Some("force_confirm_required"));
232
+ assert_eq!(
233
+ bc.get("reason").and_then(|v| v.as_str()),
234
+ Some("force_confirm_required")
235
+ );
197
236
  }
198
237
 
199
238
  // R12-5 dedup: golden _broadcast_ambiguous_candidates:263 skips re-broadcast when a prior
@@ -204,7 +243,8 @@ fn r12_5_ambiguous_candidates_event_is_full_golden_candidates_key() {
204
243
  fn r12_5_ambiguous_candidates_dedups_on_repeat_incident() {
205
244
  let ws = r12_ws("ambigdedup");
206
245
  let log = crate::event_log::EventLog::new(&ws);
207
- let mut state = serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
246
+ let mut state =
247
+ serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
208
248
  let owner = serde_json::json!({
209
249
  "pane_id": "%owner", "leader_session_uuid": "U-abcdef01",
210
250
  "machine_fingerprint": "F", "provider": "codex", "team_id": "team-a"
@@ -240,7 +280,8 @@ fn r12_5_ambiguous_candidates_dedups_on_repeat_incident() {
240
280
  fn r12_6_per_candidate_emits_separate_ambiguous_candidate_queued_events() {
241
281
  let ws = r12_ws("percand");
242
282
  let log = crate::event_log::EventLog::new(&ws);
243
- let mut state = serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
283
+ let mut state =
284
+ serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
244
285
  let owner = serde_json::json!({
245
286
  "pane_id": "%owner", "leader_session_uuid": "U-abcdef01",
246
287
  "machine_fingerprint": "F", "provider": "codex", "team_id": "team-a"
@@ -263,8 +304,15 @@ fn r12_6_per_candidate_emits_separate_ambiguous_candidate_queued_events() {
263
304
  );
264
305
  let qpanes: std::collections::BTreeSet<String> = evs
265
306
  .iter()
266
- .filter(|e| e.get("event").and_then(|v| v.as_str()) == Some("leader_receiver.ambiguous_candidate_queued"))
267
- .filter_map(|e| e.get("pane_id").and_then(|v| v.as_str()).map(str::to_string))
307
+ .filter(|e| {
308
+ e.get("event").and_then(|v| v.as_str())
309
+ == Some("leader_receiver.ambiguous_candidate_queued")
310
+ })
311
+ .filter_map(|e| {
312
+ e.get("pane_id")
313
+ .and_then(|v| v.as_str())
314
+ .map(str::to_string)
315
+ })
268
316
  .collect();
269
317
  assert_eq!(
270
318
  qpanes,
@@ -296,7 +344,8 @@ fn r12_return_candidates(out: &serde_json::Value) -> &Vec<serde_json::Value> {
296
344
  fn r12_7_candidate_raw_uses_golden_pane_current_spelling() {
297
345
  let ws = r12_ws("rawkeys");
298
346
  let log = crate::event_log::EventLog::new(&ws);
299
- let mut state = serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
347
+ let mut state =
348
+ serde_json::json!({"leader_receiver": {"pane_id": "%old", "provider": "codex"}});
300
349
  let owner = serde_json::json!({
301
350
  "pane_id": "%owner", "leader_session_uuid": "U-abcdef01",
302
351
  "machine_fingerprint": "F", "provider": "codex", "team_id": "team-a"
@@ -339,7 +388,10 @@ fn r12_7_event_provider_defaults_to_codex_when_absent() {
339
388
  &ws, &mut state, &targets, &log, &owner, None,
340
389
  )
341
390
  .unwrap();
342
- assert_eq!(out.get("status").and_then(|v| v.as_str()), Some("ambiguous"));
391
+ assert_eq!(
392
+ out.get("status").and_then(|v| v.as_str()),
393
+ Some("ambiguous")
394
+ );
343
395
  let evs = r12_events(&ws);
344
396
  let amb = find_ev(&evs, "leader_receiver.rediscover_ambiguous").expect("rediscover_ambiguous");
345
397
  assert_eq!(