@team-agent/installer 0.5.61 → 0.5.63

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 (250) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +5 -2
  4. package/crates/team-agent/src/cli/attach_app_server_leader.rs +1 -0
  5. package/crates/team-agent/src/cli/diagnose.rs +1 -0
  6. package/crates/team-agent/src/cli/emit.rs +41 -7
  7. package/crates/team-agent/src/cli/helpers.rs +1 -0
  8. package/crates/team-agent/src/cli/leader.rs +6 -1
  9. package/crates/team-agent/src/cli/leaders.rs +2 -0
  10. package/crates/team-agent/src/cli/mod.rs +33 -3
  11. package/crates/team-agent/src/cli/named_address.rs +8 -1
  12. package/crates/team-agent/src/cli/profile.rs +2 -1
  13. package/crates/team-agent/src/cli/send/coordinator.rs +1 -0
  14. package/crates/team-agent/src/cli/send/mailbox.rs +3 -0
  15. package/crates/team-agent/src/cli/send/persist.rs +1 -0
  16. package/crates/team-agent/src/cli/send/presentation.rs +1 -0
  17. package/crates/team-agent/src/cli/send/resolve.rs +206 -1
  18. package/crates/team-agent/src/cli/send.rs +2 -1
  19. package/crates/team-agent/src/cli/spec.rs +1 -0
  20. package/crates/team-agent/src/cli/status.rs +2 -1
  21. package/crates/team-agent/src/cli/status_port/compact.rs +1 -0
  22. package/crates/team-agent/src/cli/status_port/format.rs +1 -0
  23. package/crates/team-agent/src/cli/status_port/inbox.rs +1 -0
  24. package/crates/team-agent/src/cli/tests/verb_profile.rs +38 -0
  25. package/crates/team-agent/src/cli/types.rs +3 -2
  26. package/crates/team-agent/src/communication_mode/mod.rs +1 -0
  27. package/crates/team-agent/src/compiler.rs +1 -0
  28. package/crates/team-agent/src/conpty/backend.rs +1 -0
  29. package/crates/team-agent/src/conpty/mod.rs +1 -0
  30. package/crates/team-agent/src/coordinator/backoff.rs +5 -8
  31. package/crates/team-agent/src/coordinator/conpty_shim.rs +3 -2
  32. package/crates/team-agent/src/coordinator/health.rs +1 -0
  33. package/crates/team-agent/src/coordinator/mod.rs +2 -2
  34. package/crates/team-agent/src/coordinator/orphan.rs +4 -116
  35. package/crates/team-agent/src/coordinator/runtime_detectors.rs +1 -0
  36. package/crates/team-agent/src/coordinator/runtime_observation.rs +1 -0
  37. package/crates/team-agent/src/coordinator/steps/abnormal.rs +15 -866
  38. package/crates/team-agent/src/coordinator/steps/delivery.rs +1 -0
  39. package/crates/team-agent/src/coordinator/steps/health_sync.rs +1 -0
  40. package/crates/team-agent/src/coordinator/steps/mod.rs +2 -0
  41. package/crates/team-agent/src/coordinator/steps/persist.rs +1 -0
  42. package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +1 -0
  43. package/crates/team-agent/src/coordinator/steps/session_gate.rs +1 -0
  44. package/crates/team-agent/src/coordinator/tests/abnormal.rs +0 -174
  45. package/crates/team-agent/src/coordinator/tests/api_error_recovery.rs +139 -375
  46. package/crates/team-agent/src/coordinator/tests/basics.rs +0 -184
  47. package/crates/team-agent/src/coordinator/tests/mod.rs +2 -17
  48. package/crates/team-agent/src/coordinator/tests/tick_core.rs +0 -49
  49. package/crates/team-agent/src/coordinator/tick.rs +23 -40
  50. package/crates/team-agent/src/coordinator/types.rs +3 -176
  51. package/crates/team-agent/src/db/agent_health_capture.rs +1 -0
  52. package/crates/team-agent/src/db/message_store.rs +2 -1
  53. package/crates/team-agent/src/db/migration.rs +5 -4
  54. package/crates/team-agent/src/db/mod.rs +1 -0
  55. package/crates/team-agent/src/db/schema.rs +1 -0
  56. package/crates/team-agent/src/diagnose/comms.rs +2 -1
  57. package/crates/team-agent/src/diagnose/mod.rs +1 -0
  58. package/crates/team-agent/src/diagnose/orphans.rs +1 -0
  59. package/crates/team-agent/src/layout/manager.rs +3 -7
  60. package/crates/team-agent/src/layout/mod.rs +2 -2
  61. package/crates/team-agent/src/layout/overlay.rs +6 -1
  62. package/crates/team-agent/src/layout/placement.rs +1 -0
  63. package/crates/team-agent/src/layout/recovery.rs +3 -0
  64. package/crates/team-agent/src/layout/runtime_sessions.rs +8 -3
  65. package/crates/team-agent/src/layout/sessions.rs +8 -1
  66. package/crates/team-agent/src/layout/tmux_endpoint.rs +3 -0
  67. package/crates/team-agent/src/layout/worker_env.rs +3 -0
  68. package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
  69. package/crates/team-agent/src/leader/helpers.rs +1 -0
  70. package/crates/team-agent/src/leader/incident.rs +1 -0
  71. package/crates/team-agent/src/leader/lease.rs +27 -22
  72. package/crates/team-agent/src/leader/mod.rs +2 -3
  73. package/crates/team-agent/src/leader/owner_bind.rs +13 -3
  74. package/crates/team-agent/src/leader/provider_attribution.rs +1 -0
  75. package/crates/team-agent/src/leader/rediscover.rs +4 -17
  76. package/crates/team-agent/src/leader/registry.rs +3 -15
  77. package/crates/team-agent/src/leader/start.rs +373 -125
  78. package/crates/team-agent/src/leader/takeover.rs +10 -232
  79. package/crates/team-agent/src/leader/tests/claim_leader_mailbox_flush_contract.rs +544 -0
  80. package/crates/team-agent/src/leader/tests/historical_inventory_upgrade_gate_red.rs +624 -0
  81. package/crates/team-agent/src/leader/tests/identity.rs +7 -2
  82. package/crates/team-agent/src/leader/tests/idle.rs +0 -193
  83. package/crates/team-agent/src/leader/tests/lease_api.rs +0 -45
  84. package/crates/team-agent/src/leader/tests/mod.rs +4 -41
  85. package/crates/team-agent/src/leader/tests/rehomed_env.rs +83 -0
  86. package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1273 -0
  87. package/crates/team-agent/src/leader/tests/terminal_and_replay_vs_rerender_invariants_red.rs +417 -0
  88. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +22 -103
  89. package/crates/team-agent/src/leader/types.rs +1 -28
  90. package/crates/team-agent/src/lib.rs +3 -0
  91. package/crates/team-agent/src/lifecycle/display.rs +7 -1
  92. package/crates/team-agent/src/lifecycle/helpers.rs +3 -1
  93. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +2 -2
  94. package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +1 -0
  95. package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +1 -0
  96. package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +1 -0
  97. package/crates/team-agent/src/lifecycle/launch/plan.rs +2 -2
  98. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +4 -26
  99. package/crates/team-agent/src/lifecycle/launch/readiness.rs +0 -27
  100. package/crates/team-agent/src/lifecycle/launch/spawn.rs +1 -27
  101. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +4 -32
  102. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +1 -0
  103. package/crates/team-agent/src/lifecycle/launch.rs +11 -12
  104. package/crates/team-agent/src/lifecycle/mod.rs +2 -1
  105. package/crates/team-agent/src/lifecycle/restart/agent.rs +3 -66
  106. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +2 -2
  107. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +13 -5
  108. package/crates/team-agent/src/lifecycle/restart/remove.rs +1 -1
  109. package/crates/team-agent/src/lifecycle/restart/selection.rs +3 -3
  110. package/crates/team-agent/src/lifecycle/restart.rs +13 -15
  111. package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1007 -0
  112. package/crates/team-agent/src/lifecycle/tests/b0_legacy_snapshot_nonauthority_contract.rs +265 -0
  113. package/crates/team-agent/src/lifecycle/tests/b0_reader_hideone_audit_contract.rs +459 -0
  114. package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +849 -0
  115. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +323 -0
  116. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +423 -0
  117. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +737 -0
  118. package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +1187 -0
  119. package/crates/team-agent/src/lifecycle/tests/codex_weak_window_attribution_red.rs +683 -0
  120. package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +394 -0
  121. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +1856 -0
  122. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +917 -0
  123. package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +481 -0
  124. package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +301 -0
  125. package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +516 -0
  126. package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +986 -0
  127. package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +651 -0
  128. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +2 -0
  129. package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +332 -0
  130. package/crates/team-agent/src/lifecycle/tests/realmachine_clusters_1_6_red.rs +846 -0
  131. package/crates/team-agent/src/lifecycle/tests/realmachine_residual_g1_g4_red.rs +648 -0
  132. package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +910 -0
  133. package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +630 -0
  134. package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +1083 -0
  135. package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +1677 -0
  136. package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +410 -0
  137. package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +428 -0
  138. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +1032 -0
  139. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +869 -0
  140. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1249 -0
  141. package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +342 -0
  142. package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +542 -0
  143. package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1103 -0
  144. package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1276 -0
  145. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_roster_red.rs +145 -0
  146. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +603 -0
  147. package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +837 -0
  148. package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +928 -0
  149. package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +951 -0
  150. package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +327 -0
  151. package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +760 -0
  152. package/crates/team-agent/src/lifecycle/tests.rs +59 -0
  153. package/crates/team-agent/src/lifecycle/types.rs +0 -9
  154. package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
  155. package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +1 -0
  156. package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +1 -0
  157. package/crates/team-agent/src/mcp_server/mod.rs +1 -0
  158. package/crates/team-agent/src/mcp_server/normalize.rs +4 -3
  159. package/crates/team-agent/src/mcp_server/tools.rs +1 -0
  160. package/crates/team-agent/src/mcp_server/types.rs +1 -18
  161. package/crates/team-agent/src/mcp_server/wire.rs +5 -5
  162. package/crates/team-agent/src/messaging/activity.rs +1 -0
  163. package/crates/team-agent/src/messaging/address.rs +1 -0
  164. package/crates/team-agent/src/messaging/delivery.rs +4 -3
  165. package/crates/team-agent/src/messaging/helpers.rs +2 -1
  166. package/crates/team-agent/src/messaging/leader_channel.rs +1 -0
  167. package/crates/team-agent/src/messaging/leader_receiver.rs +2 -1
  168. package/crates/team-agent/src/messaging/mod.rs +10 -14
  169. package/crates/team-agent/src/messaging/peers.rs +2 -0
  170. package/crates/team-agent/src/messaging/persist.rs +3 -1
  171. package/crates/team-agent/src/messaging/presentation.rs +2 -1
  172. package/crates/team-agent/src/messaging/results.rs +1 -0
  173. package/crates/team-agent/src/messaging/scheduler.rs +1 -0
  174. package/crates/team-agent/src/messaging/selftest.rs +1 -1
  175. package/crates/team-agent/src/messaging/send.rs +41 -8
  176. package/crates/team-agent/src/messaging/tests/mod.rs +5 -0
  177. package/crates/team-agent/src/messaging/types.rs +2 -2
  178. package/crates/team-agent/src/messaging/watchers.rs +19 -13
  179. package/crates/team-agent/src/model/enums.rs +1 -0
  180. package/crates/team-agent/src/model/errors.rs +1 -0
  181. package/crates/team-agent/src/model/ids.rs +1 -0
  182. package/crates/team-agent/src/model/mod.rs +1 -0
  183. package/crates/team-agent/src/model/name_similarity.rs +1 -0
  184. package/crates/team-agent/src/model/pane_authority_refusal.rs +1 -0
  185. package/crates/team-agent/src/model/paths.rs +1 -0
  186. package/crates/team-agent/src/model/permissions.rs +3 -2
  187. package/crates/team-agent/src/model/routing.rs +1 -0
  188. package/crates/team-agent/src/model/spec.rs +1 -0
  189. package/crates/team-agent/src/model/task_graph.rs +1 -0
  190. package/crates/team-agent/src/model/yaml/tests.rs +1 -0
  191. package/crates/team-agent/src/model/yaml.rs +1 -0
  192. package/crates/team-agent/src/packaging/install.rs +9 -147
  193. package/crates/team-agent/src/packaging/migrate.rs +2 -0
  194. package/crates/team-agent/src/packaging/mod.rs +1 -0
  195. package/crates/team-agent/src/packaging/repair.rs +2 -0
  196. package/crates/team-agent/src/packaging/tests.rs +29 -218
  197. package/crates/team-agent/src/packaging/types.rs +8 -15
  198. package/crates/team-agent/src/platform/argv.rs +4 -0
  199. package/crates/team-agent/src/platform/errors.rs +10 -1
  200. package/crates/team-agent/src/platform/file_lock.rs +4 -157
  201. package/crates/team-agent/src/platform/mod.rs +0 -61
  202. package/crates/team-agent/src/platform/process.rs +1 -0
  203. package/crates/team-agent/src/provider/adapter.rs +1 -0
  204. package/crates/team-agent/src/provider/adapters/claude.rs +1 -0
  205. package/crates/team-agent/src/provider/adapters/claude_fork.rs +1 -0
  206. package/crates/team-agent/src/provider/adapters/codex.rs +1 -0
  207. package/crates/team-agent/src/provider/adapters/copilot.rs +1 -0
  208. package/crates/team-agent/src/provider/adapters/copilot_fork.rs +1 -0
  209. package/crates/team-agent/src/provider/adapters/fake.rs +1 -0
  210. package/crates/team-agent/src/provider/adapters/mod.rs +1 -0
  211. package/crates/team-agent/src/provider/approvals/mod.rs +1 -0
  212. package/crates/team-agent/src/provider/approvals/parsing.rs +2 -5
  213. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +6 -5
  214. package/crates/team-agent/src/provider/classify.rs +1 -0
  215. package/crates/team-agent/src/provider/faults.rs +1 -26
  216. package/crates/team-agent/src/provider/helpers.rs +1 -0
  217. package/crates/team-agent/src/provider/mod.rs +1 -1
  218. package/crates/team-agent/src/provider/session/capture.rs +1 -4
  219. package/crates/team-agent/src/provider/session/context_fork/claude.rs +1 -0
  220. package/crates/team-agent/src/provider/session/context_fork/codex.rs +1 -0
  221. package/crates/team-agent/src/provider/session/context_fork/outcome.rs +1 -0
  222. package/crates/team-agent/src/provider/session/context_fork.rs +1 -0
  223. package/crates/team-agent/src/provider/session/mod.rs +2 -4
  224. package/crates/team-agent/src/provider/session/resume.rs +2 -209
  225. package/crates/team-agent/src/provider/session_scan/claude.rs +1 -0
  226. package/crates/team-agent/src/provider/session_scan/codex.rs +1 -0
  227. package/crates/team-agent/src/provider/session_scan/common.rs +1 -0
  228. package/crates/team-agent/src/provider/session_scan/copilot.rs +1 -0
  229. package/crates/team-agent/src/provider/session_scan.rs +1 -42
  230. package/crates/team-agent/src/provider/startup_prompt.rs +1 -0
  231. package/crates/team-agent/src/provider/types.rs +1 -0
  232. package/crates/team-agent/src/provider/wire.rs +1 -0
  233. package/crates/team-agent/src/state/identity.rs +16 -5
  234. package/crates/team-agent/src/state/identity_keys.rs +1 -0
  235. package/crates/team-agent/src/state/mod.rs +2 -0
  236. package/crates/team-agent/src/state/owner_gate.rs +5 -0
  237. package/crates/team-agent/src/state/ownership.rs +10 -4
  238. package/crates/team-agent/src/state/paths.rs +12 -0
  239. package/crates/team-agent/src/state/persist.rs +9 -3
  240. package/crates/team-agent/src/state/projection.rs +15 -2
  241. package/crates/team-agent/src/state/repository/intent.rs +1 -0
  242. package/crates/team-agent/src/state/repository/tests.rs +1 -0
  243. package/crates/team-agent/src/state/repository.rs +7 -13
  244. package/crates/team-agent/src/state/selector.rs +1 -0
  245. package/crates/team-agent/src/tmux_backend/tests.rs +39 -0
  246. package/crates/team-agent/src/tmux_backend.rs +63 -0
  247. package/crates/team-agent/src/transport.rs +15 -0
  248. package/package.json +4 -4
  249. package/crates/team-agent/src/leader/inject.rs +0 -33
  250. package/crates/team-agent/src/provider/command.rs +0 -80
@@ -1,198 +1,5 @@
1
1
  use super::*;
2
2
 
3
- // =====================================================================
4
- // 8. idle-takeover 接线 — build_idle_nodes / leader_node(unimplemented → RED)
5
- // 命门:rollout_path=None → Unknown → never idle;leader path/provider 缺 → 省略;
6
- // MUST-NOT-13:经 TurnStateClassifier mock,断言零 provider client 直连。
7
- // =====================================================================
8
-
9
- // build_idle_nodes:一个 worker 有 rollout_path(可读)→ classifier.classify 被调一次;
10
- // 经注入分类器(零 provider client)。stopped/paused 跳过。
11
- #[test]
12
- fn build_idle_nodes_uses_injected_classifier_no_provider_client() {
13
- // 真实 session 文件,使 _read_session_tail 读到非空 → classifier 返回注入 state。
14
- let dir = std::env::temp_dir().join(format!("ta_rs_idle_{}", std::process::id()));
15
- std::fs::create_dir_all(&dir).unwrap();
16
- let log = dir.join("w1.jsonl");
17
- std::fs::write(&log, b"{\"type\":\"turn_complete\"}\n").unwrap();
18
- let state = serde_json::json!({
19
- "agents": {
20
- "w1": {"provider": "codex", "rollout_path": log.to_string_lossy(), "status": "running"},
21
- "w_stopped": {"provider": "codex", "rollout_path": log.to_string_lossy(), "status": "stopped"},
22
- }
23
- });
24
- let clf = CountingClassifier::new(TurnState::Idle);
25
- let nodes = build_idle_nodes(&state, &clf).unwrap();
26
- // stopped 被跳过(__init__ wiring:29)→ 仅 w1。
27
- assert_eq!(nodes.len(), 1);
28
- assert_eq!(nodes[0].node_id, "w1");
29
- assert_eq!(nodes[0].role, NodeRole::Worker);
30
- assert_eq!(nodes[0].state, TurnState::Idle);
31
- // MUST-NOT-13:分类只经注入 classifier(此 mock 计数==1),零 provider client 直连。
32
- assert_eq!(clf.calls.get(), 1, "每个 live node 恰调一次注入 classify");
33
- }
34
-
35
- // bug-085:rollout_path=None → 读到空串 → Unknown(不当 idle)。
36
- #[test]
37
- fn build_idle_nodes_none_rollout_path_yields_unknown_not_idle() {
38
- let state = serde_json::json!({
39
- "agents": {
40
- "w1": {"provider": "codex", "status": "running"} // 无 rollout_path。
41
- }
42
- });
43
- // 即使 mock 默认想返 Idle,空 session-log → classifier 返 Unknown(见 mock 逻辑)。
44
- let clf = CountingClassifier::new(TurnState::Idle);
45
- let nodes = build_idle_nodes(&state, &clf).unwrap();
46
- assert_eq!(nodes.len(), 1);
47
- assert_eq!(
48
- nodes[0].state,
49
- TurnState::Unknown,
50
- "None rollout_path → Unknown,绝不 idle"
51
- );
52
- assert!(
53
- !nodes[0].state.is_idle_for_takeover(),
54
- "unknown ≠ idle:不得对 takeover 放行"
55
- );
56
- }
57
-
58
- // _leader_node:leader path 或 provider 缺 → None(省略而非猜 idle)。
59
- #[test]
60
- fn leader_node_omitted_when_path_or_provider_missing() {
61
- let clf = CountingClassifier::new(TurnState::Idle);
62
- // 既无 leader.rollout_path 也无 receiver.rollout_path → None。
63
- let state_no_path = serde_json::json!({
64
- "leader": {"id": "leader", "provider": "codex"},
65
- "leader_receiver": {"provider": "codex"}
66
- });
67
- assert!(
68
- leader_node(&state_no_path, &clf).unwrap().is_none(),
69
- "缺 path → 省略 leader 节点"
70
- );
71
- // 有 path 但无 provider → None。
72
- let dir = std::env::temp_dir().join(format!("ta_rs_lnode_{}", std::process::id()));
73
- std::fs::create_dir_all(&dir).unwrap();
74
- let log = dir.join("leader.jsonl");
75
- std::fs::write(&log, b"{\"type\":\"turn_open\"}\n").unwrap();
76
- let state_no_provider = serde_json::json!({
77
- "leader": {"id": "leader", "rollout_path": log.to_string_lossy()}
78
- });
79
- assert!(
80
- leader_node(&state_no_provider, &clf).unwrap().is_none(),
81
- "缺 provider → 省略 leader 节点(不猜 idle)"
82
- );
83
- }
84
-
85
- // _leader_node:path+provider 齐 → 经 classifier 产 role=leader 节点(C13)。
86
- #[test]
87
- fn leader_node_classified_via_injected_classifier() {
88
- let dir = std::env::temp_dir().join(format!("ta_rs_lnode2_{}", std::process::id()));
89
- std::fs::create_dir_all(&dir).unwrap();
90
- let log = dir.join("leader.jsonl");
91
- std::fs::write(&log, b"{\"type\":\"turn_complete\"}\n").unwrap();
92
- let state = serde_json::json!({
93
- "leader": {"id": "leader", "provider": "codex", "rollout_path": log.to_string_lossy()}
94
- });
95
- let clf = CountingClassifier::new(TurnState::Working);
96
- let node = leader_node(&state, &clf)
97
- .unwrap()
98
- .expect("path+provider 齐 → 有 leader 节点");
99
- assert_eq!(node.role, NodeRole::Leader);
100
- assert_eq!(node.node_id, "leader");
101
- assert_eq!(node.state, TurnState::Working);
102
- assert_eq!(
103
- clf.calls.get(),
104
- 1,
105
- "leader 分类经注入 classifier 一次,零 provider client"
106
- );
107
- }
108
-
109
- // =====================================================================
110
- // 9. classify_provider_turn_state 门面(unimplemented → RED)
111
- // unknown/abnormal 且有 event_sink → 写 idle_takeover.classify。
112
- // =====================================================================
113
-
114
- // 门面经注入 classifier 分类;空文本 → Unknown(本 mock),验证返回 TurnClassification。
115
- #[test]
116
- fn classify_provider_turn_state_returns_classification_via_injected_classifier() {
117
- let clf = CountingClassifier::new(TurnState::Idle);
118
- let c =
119
- classify_provider_turn_state(Provider::Codex, "{\"type\":\"turn_complete\"}", &clf, None)
120
- .unwrap();
121
- assert_eq!(c.state, TurnState::Idle);
122
- assert_eq!(clf.calls.get(), 1);
123
- // 空文本 → Unknown(unknown ≠ idle 命门下游)。
124
- let clf2 = CountingClassifier::new(TurnState::Idle);
125
- let c2 = classify_provider_turn_state(Provider::Codex, "", &clf2, None).unwrap();
126
- assert_eq!(c2.state, TurnState::Unknown);
127
- assert!(!c2.state.is_idle_for_takeover());
128
- }
129
-
130
- // event_sink + unknown/abnormal → 写 idle_takeover.classify(事件名字节锁)。
131
- #[test]
132
- fn classify_event_name_is_idle_takeover_classify() {
133
- assert_eq!(
134
- LeaderEvent::IdleTakeoverClassify.name(),
135
- "idle_takeover.classify"
136
- );
137
- }
138
-
139
- // =====================================================================
140
- // 10. push_idle_reminder(unimplemented → RED):!should_ping → no-op。
141
- // =====================================================================
142
-
143
- #[test]
144
- fn push_idle_reminder_noop_when_should_not_ping() {
145
- let ws = std::env::temp_dir().join(format!("ta_rs_push_{}", std::process::id()));
146
- std::fs::create_dir_all(&ws).unwrap();
147
- let event_log = crate::event_log::EventLog::new(&ws);
148
- let state = serde_json::json!({"leader": {"id": "leader"}});
149
- let result = TakeoverReminderResult {
150
- should_ping: false,
151
- message: None,
152
- interrupted_nodes: vec![],
153
- reason: Some("not_armed_no_worker_turn".into()),
154
- };
155
- // should_ping=false → no-op,返回 Ok(())。现 unimplemented → RED。
156
- push_idle_reminder(&ws, &state, &event_log, &result).unwrap();
157
- // 强化:no-op 必须真的什么都不做 —— 不写 idle_takeover.reminder 事件(EventLog 无该事件)。
158
- let events = event_log.tail(50).unwrap();
159
- assert!(
160
- !events
161
- .iter()
162
- .any(|e| e["event"] == serde_json::json!("idle_takeover.reminder")),
163
- "should_ping=false 时绝不写 reminder 事件"
164
- );
165
- }
166
-
167
- // #236 nag_removal (N35) — push_idle_reminder is now a no-op shim.
168
- // [OLD] assertion: should_ping=true → writes idle_takeover.reminder event (with
169
- // interrupted/reason byte-locked golden payload).
170
- // [NEW] assertion: even when should_ping=true, push_idle_reminder writes NO event
171
- // and emits NO leader-bound message; ownership/handover happens only via explicit
172
- // `claim-leader` / `takeover` commands. The function signature is preserved so
173
- // existing callers (coordinator/tick.rs, lifecycle wiring) still resolve.
174
- #[test]
175
- fn push_idle_reminder_is_silent_no_op_under_n35_even_when_should_ping_true() {
176
- let ws = std::env::temp_dir().join(format!("ta_rs_push2_{}", std::process::id()));
177
- std::fs::create_dir_all(&ws).unwrap();
178
- let event_log = crate::event_log::EventLog::new(&ws);
179
- let state = serde_json::json!({"leader": {"id": "leader"}});
180
- let result = TakeoverReminderResult {
181
- should_ping: true,
182
- message: Some("neutral reminder body".into()),
183
- interrupted_nodes: vec!["w1".into()],
184
- reason: Some("armed_all_idle".into()),
185
- };
186
- push_idle_reminder(&ws, &state, &event_log, &result).unwrap();
187
- let events = event_log.tail(50).unwrap();
188
- assert!(
189
- !events
190
- .iter()
191
- .any(|e| e["event"] == serde_json::json!("idle_takeover.reminder")),
192
- "#236 N35: push_idle_reminder must no longer emit the reminder nag event; got {events:?}"
193
- );
194
- }
195
-
196
3
  // idle_takeover.reminder / push_failed 事件名字节锁。
197
4
  #[test]
198
5
  fn idle_takeover_event_names_byte_locked() {
@@ -67,51 +67,6 @@ fn attach_leader_binds_pane_advances_epoch_and_persists() {
67
67
  assert_eq!(persisted["team_owner"]["owner_epoch"], serde_json::json!(1));
68
68
  }
69
69
 
70
- // autobind:$TMUX_PANE 缺 → Ok(None)(__init__.py:885-887,锁前直接返回,不开锁)。
71
- // 强化:断言这是 lock-not-acquired 早退 —— 不写 state.json(无 receiver/owner 落盘),
72
- // 不发任何 leader_receiver.* 事件(锁前 return,连 EventLog 都不构造)。
73
- #[test]
74
- fn autobind_returns_none_when_tmux_pane_missing() {
75
- if std::env::var_os("TMUX_PANE").is_some() {
76
- return; // 在 tmux 内:走绑定路径,本用例只验缺失分支。
77
- }
78
- let ws = std::env::temp_dir().join(format!("ta_rs_auto_{}", std::process::id()));
79
- std::fs::create_dir_all(&ws).unwrap();
80
- let r = autobind_leader_receiver_from_env(&ws, Provider::Codex, LeaseSource::Restart).unwrap();
81
- assert!(r.is_none(), "$TMUX_PANE 缺 → autobind 返回 Ok(None)");
82
- // lock-not-acquired 早退:state.json 未被写(load 兜底成空 state,无 team_owner/receiver)。
83
- let st = crate::state::persist::load_runtime_state(&ws).unwrap();
84
- assert!(
85
- st.get("leader_receiver").is_none(),
86
- "skip 路径不应落 receiver"
87
- );
88
- assert!(st.get("team_owner").is_none(), "skip 路径不应落 owner");
89
- // 早退发生在 EventLog 构造前 → 无任何事件文件。
90
- let events = crate::event_log::EventLog::new(&ws).tail(50).unwrap();
91
- assert!(events.is_empty(), "skip 路径绝不写审计事件");
92
- }
93
-
94
- // autobind 成功支:$TMUX_PANE 命中 → 锁内 attach_leader_to_state → Ok(Some(receiver))。
95
- // receiver.pane_id == 注入 pane,discovery==EnvPane($TMUX_PANE 直接命中)。
96
- // env 变更进程全局且与并行 test race,且依赖跨 lane 的 live pane resolver,故 real-machine-gated。
97
- #[test]
98
- #[ignore = "needs live $TMUX_PANE + cross-lane _resolve_leader_pane; env mutation races parallel tests"]
99
- fn autobind_binds_env_pane_on_success() {
100
- let ws = std::env::temp_dir().join(format!("ta_rs_auto_ok_{}", std::process::id()));
101
- std::fs::create_dir_all(&ws).unwrap();
102
- // SAFETY: #[ignore]d,单独 `--ignored --test-threads=1` 跑,不与并行 test race。
103
- unsafe { std::env::set_var("TMUX_PANE", "%42") };
104
- let r = autobind_leader_receiver_from_env(&ws, Provider::Codex, LeaseSource::Restart).unwrap();
105
- unsafe { std::env::remove_var("TMUX_PANE") };
106
- let receiver = r.expect("$TMUX_PANE 命中 → Ok(Some(receiver))");
107
- assert_eq!(receiver.pane_id, PaneId::new("%42"));
108
- assert_eq!(
109
- receiver.discovery,
110
- Some(Discovery::EnvPane),
111
- "$TMUX_PANE 命中 → discovery=env_pane"
112
- );
113
- }
114
-
115
70
  // claim_leader:无 ambiguous incident → 走 claim_lease_no_incident 直接 acquire/CAS。
116
71
  // 现 unimplemented → RED;锁住返回 LeaseResult。
117
72
  #[test]
@@ -1,6 +1,5 @@
1
1
  #![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
2
2
  use super::*;
3
- use std::cell::Cell;
4
3
 
5
4
  // ── helpers ────────────────────────────────────────────────────────────
6
5
  /// derive 一个真 LeaderSessionUuid(骨架无公开裸构造器,仅 `derive`)。
@@ -8,46 +7,6 @@ fn uuid(fp: &str, ws: &str, user: &str, team: &str) -> LeaderSessionUuid {
8
7
  LeaderSessionUuid::derive(fp, ws, user, team).unwrap()
9
8
  }
10
9
 
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,
22
- }
23
- }
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
- })
48
- }
49
- }
50
-
51
10
  static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
52
11
 
53
12
  /// RAII env override: restores prior values on drop (incl. panic unwind), so a
@@ -133,9 +92,13 @@ fn event_names(ws: &std::path::Path) -> Vec<String> {
133
92
 
134
93
  mod basics;
135
94
  mod byte_findings;
95
+ mod claim_leader_mailbox_flush_contract;
96
+ mod historical_inventory_upgrade_gate_red;
136
97
  mod identity;
137
98
  mod idle;
138
99
  mod lease_api;
139
100
  mod lease_claim;
140
101
  mod rediscover;
102
+ mod team_in_team_state_scope_red;
103
+ mod terminal_and_replay_vs_rerender_invariants_red;
141
104
  mod wake_start_owner;
@@ -0,0 +1,83 @@
1
+ use std::path::PathBuf;
2
+ use std::sync::atomic::{AtomicU64, Ordering};
3
+
4
+ const CALLER_IDENTITY_ENVS: &[&str] = &[
5
+ "TMUX",
6
+ "TMUX_PANE",
7
+ "TEAM_AGENT_LEADER_PANE_ID",
8
+ "TEAM_AGENT_LEADER_SESSION_UUID",
9
+ "TEAM_AGENT_LEADER_SESSION_UUID_OVERRIDE",
10
+ "TEAM_AGENT_LEADER_SESSION_NAME",
11
+ "TEAM_AGENT_LEADER_PROVIDER",
12
+ "TEAM_AGENT_MACHINE_FINGERPRINT",
13
+ "TEAM_AGENT_WORKSPACE",
14
+ "TEAM_AGENT_TEAM_ID",
15
+ "TEAM_AGENT_OWNER_TEAM_ID",
16
+ "TEAM_AGENT_ACTIVE_TEAM",
17
+ "TEAM_AGENT_ID",
18
+ "TEAM_AGENT_AGENT_ID",
19
+ "TEAM_AGENT_AUTH_MODE",
20
+ "TEAM_AGENT_LEADER_BYPASS",
21
+ "TEAM_AGENT_LEADER_BYPASS_SOURCE",
22
+ "TEAM_AGENT_LEADER_BYPASS_PROVIDER",
23
+ "TEAM_AGENT_LEADER_BYPASS_FLAG",
24
+ "TEAM_AGENT_MCP_AUTO_APPROVE",
25
+ "TEAM_AGENT_MCP_AUTO_APPROVE_SOURCE",
26
+ ];
27
+
28
+ static COUNTER: AtomicU64 = AtomicU64::new(0);
29
+
30
+ pub(super) struct RehomedTestEnv {
31
+ root: PathBuf,
32
+ previous: Vec<(&'static str, Option<String>)>,
33
+ }
34
+
35
+ impl RehomedTestEnv {
36
+ pub(super) fn enter(tag: &str) -> Self {
37
+ let root = std::env::temp_dir().join(format!(
38
+ "ta-leader-rehomed-{tag}-{}-{}",
39
+ std::process::id(),
40
+ COUNTER.fetch_add(1, Ordering::Relaxed)
41
+ ));
42
+ let _ = std::fs::remove_dir_all(&root);
43
+ let home = root.join("home");
44
+ std::fs::create_dir_all(&home).expect("create rehomed test HOME");
45
+
46
+ let mut previous = Vec::new();
47
+ for key in std::iter::once("HOME").chain(CALLER_IDENTITY_ENVS.iter().copied()) {
48
+ previous.push((key, std::env::var(key).ok()));
49
+ }
50
+ unsafe {
51
+ std::env::set_var("HOME", &home);
52
+ for key in CALLER_IDENTITY_ENVS {
53
+ std::env::remove_var(key);
54
+ }
55
+ }
56
+
57
+ Self { root, previous }
58
+ }
59
+
60
+ pub(super) fn workspace(&self, tag: &str) -> PathBuf {
61
+ let path = self.root.join(format!(
62
+ "workspace-{tag}-{}",
63
+ COUNTER.fetch_add(1, Ordering::Relaxed)
64
+ ));
65
+ std::fs::create_dir_all(&path).expect("create rehomed test workspace");
66
+ path
67
+ }
68
+ }
69
+
70
+ impl Drop for RehomedTestEnv {
71
+ fn drop(&mut self) {
72
+ for (key, value) in self.previous.drain(..).rev() {
73
+ unsafe {
74
+ if let Some(value) = value {
75
+ std::env::set_var(key, value);
76
+ } else {
77
+ std::env::remove_var(key);
78
+ }
79
+ }
80
+ }
81
+ let _ = std::fs::remove_dir_all(&self.root);
82
+ }
83
+ }