@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
@@ -98,18 +98,6 @@ fn status_enums_serialize_to_exact_python_strings() {
98
98
  serde_json::to_string(&MetadataSource::Start).unwrap(),
99
99
  "\"start\"",
100
100
  ),
101
- (
102
- serde_json::to_string(&AbnormalDecision::Skip).unwrap(),
103
- "\"skip\"",
104
- ),
105
- (
106
- serde_json::to_string(&AbnormalDecision::NotifyBlacklist).unwrap(),
107
- "\"notify_blacklist\"",
108
- ),
109
- (
110
- serde_json::to_string(&AbnormalDecision::NotifyDefault).unwrap(),
111
- "\"notify_default\"",
112
- ),
113
101
  (
114
102
  serde_json::to_string(&WholeTeamGoneClass::Alive).unwrap(),
115
103
  "\"alive\"",
@@ -132,178 +120,6 @@ fn status_enums_serialize_to_exact_python_strings() {
132
120
  }
133
121
  }
134
122
 
135
- #[test]
136
- fn coordinator_event_tags_are_byte_stable() {
137
- // 逐一钉 events.jsonl tag(events.py 稳定契约)。
138
- let pid = Pid(4242);
139
- let cases: Vec<(CoordinatorEvent, &str)> = vec![
140
- (
141
- CoordinatorEvent::Boot {
142
- workspace: "/w".into(),
143
- once: true,
144
- },
145
- "coordinator.boot",
146
- ),
147
- (
148
- CoordinatorEvent::Started {
149
- pid,
150
- log: "/l".into(),
151
- },
152
- "coordinator.started",
153
- ),
154
- (CoordinatorEvent::Stopped { pid }, "coordinator.stopped"),
155
- (CoordinatorEvent::Exit { stop: true }, "coordinator.exit"),
156
- (
157
- CoordinatorEvent::SessionMissing {
158
- session: "s".into(),
159
- },
160
- "coordinator.session_missing",
161
- ),
162
- (
163
- CoordinatorEvent::OrphanSelfTerminate {
164
- initial_ppid: 9,
165
- current_ppid: 1,
166
- workspace: "/w".into(),
167
- },
168
- "coordinator.orphan_self_terminate",
169
- ),
170
- (
171
- CoordinatorEvent::TickError {
172
- error: "boom".into(),
173
- exc_type: "OSError".into(),
174
- consecutive_failures: 1,
175
- next_sleep_sec: 5.0,
176
- },
177
- "coordinator.tick_error",
178
- ),
179
- (
180
- CoordinatorEvent::TickErrorSuppressed {
181
- consecutive_failures: 2,
182
- next_sleep_sec: 10.0,
183
- },
184
- "coordinator.tick_error.suppressed",
185
- ),
186
- (
187
- CoordinatorEvent::TickRecovered {
188
- consecutive_failures: 3,
189
- },
190
- "coordinator.tick_recovered",
191
- ),
192
- (
193
- CoordinatorEvent::RestartIncompatible {
194
- pid: Some(pid),
195
- expected_protocol: 2,
196
- expected_schema: 3,
197
- },
198
- "coordinator.restart_incompatible",
199
- ),
200
- (
201
- CoordinatorEvent::RestartIncompatibleStopFailed { pid: Some(pid) },
202
- "coordinator.restart_incompatible_stop_failed",
203
- ),
204
- (
205
- CoordinatorEvent::SchemaIncompatible {
206
- table: Some("messages".into()),
207
- missing_columns: vec!["owner_team_id".into()],
208
- },
209
- "coordinator.schema_incompatible",
210
- ),
211
- (
212
- CoordinatorEvent::IdleTakeoverUnknownPersistent {
213
- node_id: "w1".into(),
214
- provider: Some(Provider::Codex),
215
- auth_mode: None,
216
- consecutive_ticks: 60,
217
- rollout_path: None,
218
- },
219
- "idle_takeover.unknown_persistent",
220
- ),
221
- (
222
- CoordinatorEvent::AbnormalNotify {
223
- signature: "sig".into(),
224
- turn_id: None,
225
- decision: AbnormalDecision::NotifyDefault,
226
- },
227
- "abnormal.notify",
228
- ),
229
- (
230
- CoordinatorEvent::AbnormalWholeTeamGone {
231
- classification: WholeTeamGoneClass::UnexpectedExit,
232
- },
233
- "abnormal.whole_team_gone",
234
- ),
235
- (
236
- CoordinatorEvent::LeaderNotificationLogPruned { removed: 7 },
237
- "leader_notification.log_pruned",
238
- ),
239
- (
240
- CoordinatorEvent::LeaderNotificationPruneFailed { error: "io".into() },
241
- "leader_notification.prune_failed",
242
- ),
243
- (
244
- CoordinatorEvent::RuntimeStateSaveFailed {
245
- phase: "tick_end".into(),
246
- error: "replace".into(),
247
- exc_type: "OSError".into(),
248
- },
249
- "runtime.state.save_failed",
250
- ),
251
- ];
252
- for (evt, want_tag) in &cases {
253
- let json = serde_json::to_value(evt).unwrap();
254
- assert_eq!(json["event"], *want_tag, "tag mismatch for {want_tag}");
255
- }
256
- }
257
-
258
- #[test]
259
- fn tick_error_event_carries_exact_python_fields() {
260
- // __main__.py:69-74 — error / exc_type / consecutive_failures / next_sleep_sec.
261
- let evt = CoordinatorEvent::TickError {
262
- error: "os.replace failed".into(),
263
- exc_type: "OSError".into(),
264
- consecutive_failures: 4,
265
- next_sleep_sec: 40.0,
266
- };
267
- let json = serde_json::to_value(&evt).unwrap();
268
- assert_eq!(json["event"], "coordinator.tick_error");
269
- assert_eq!(json["error"], "os.replace failed");
270
- assert_eq!(json["exc_type"], "OSError");
271
- assert_eq!(json["consecutive_failures"], 4);
272
- assert_eq!(json["next_sleep_sec"], 40.0);
273
- }
274
-
275
- #[test]
276
- fn unknown_persistent_event_provider_none_is_null_not_missing() {
277
- // lifecycle.py:407 rollout_path=node.get(...) — None 漏穿堵死(bug-085):
278
- // provider/rollout_path 缺失序列化为 JSON null,绝不省略键。
279
- let evt = CoordinatorEvent::IdleTakeoverUnknownPersistent {
280
- node_id: "w7".into(),
281
- provider: None,
282
- auth_mode: None,
283
- consecutive_ticks: 72,
284
- rollout_path: None,
285
- };
286
- let json = serde_json::to_value(&evt).unwrap();
287
- assert_eq!(json["event"], "idle_takeover.unknown_persistent");
288
- assert_eq!(json["node_id"], "w7");
289
- assert!(
290
- json.get("provider").is_some(),
291
- "provider key MUST be present (None→null)"
292
- );
293
- assert!(json["provider"].is_null(), "provider None → JSON null");
294
- assert!(
295
- json.get("auth_mode").is_some(),
296
- "auth_mode key MUST be present"
297
- );
298
- assert!(json["auth_mode"].is_null(), "auth_mode None → JSON null");
299
- assert!(
300
- json.get("rollout_path").is_some(),
301
- "rollout_path key MUST be present"
302
- );
303
- assert!(json["rollout_path"].is_null());
304
- assert_eq!(json["consecutive_ticks"], 72);
305
- }
306
-
307
123
  #[test]
308
124
  fn coordinator_metadata_json_field_names_are_stable() {
309
125
  // metadata.py:50-57 — 稳定 coordinator.json 契约。
@@ -25,23 +25,15 @@ fn meta(pid: u32, proto: u32, schema: i64) -> CoordinatorMetadata {
25
25
  }
26
26
  }
27
27
 
28
- /// `ProviderRegistry` mock — 断言**零** provider-client 调用 (MUST-NOT-13 / §84).
29
- /// `adapter_for`/`error_lists` 各记一次调用计数;abnormal-track 只允许触碰 `error_lists`,
30
- /// 绝不触碰 `adapter_for`(那会走真实 provider client crate)。
28
+ /// `ProviderRegistry` mock — 记录 provider-client adapter 调用 (MUST-NOT-13 / §84).
31
29
  struct MockRegistry {
32
- whitelist: Vec<String>,
33
- blacklist: Vec<String>,
34
30
  adapter_calls: std::cell::Cell<u32>,
35
- error_list_calls: std::cell::Cell<u32>,
36
31
  }
37
32
 
38
33
  impl MockRegistry {
39
- fn new(whitelist: &[&str], blacklist: &[&str]) -> Self {
34
+ fn new(_whitelist: &[&str], _blacklist: &[&str]) -> Self {
40
35
  Self {
41
- whitelist: whitelist.iter().map(|s| s.to_string()).collect(),
42
- blacklist: blacklist.iter().map(|s| s.to_string()).collect(),
43
36
  adapter_calls: std::cell::Cell::new(0),
44
- error_list_calls: std::cell::Cell::new(0),
45
37
  }
46
38
  }
47
39
  }
@@ -52,13 +44,6 @@ impl ProviderRegistry for MockRegistry {
52
44
  self.adapter_calls.set(self.adapter_calls.get() + 1);
53
45
  crate::provider::get_adapter(_provider)
54
46
  }
55
- fn error_lists(&self, _provider: Provider) -> ErrorLists {
56
- self.error_list_calls.set(self.error_list_calls.get() + 1);
57
- ErrorLists {
58
- whitelist: self.whitelist.clone(),
59
- blacklist: self.blacklist.clone(),
60
- }
61
- }
62
47
  }
63
48
 
64
49
  /// in-memory `MarkerStore` —— durable marker 落盘探针。
@@ -106,13 +106,6 @@ fn tick_side_effect_order_is_the_fixed_sequence() {
106
106
  "detect_drift",
107
107
  "detect_api_errors",
108
108
  "atomic_save",
109
- // 0.5.36 (`.team/artifacts/supermarket-api-error-recovery-locate.md` §7.3):
110
- // post-save recovery step reloads fresh state, consumes due
111
- // recovery intents written pre-save, and drives the lifecycle
112
- // helper. Sits between atomic_save and collect_results by design
113
- // so nested lifecycle saves cannot be clobbered by tick's final
114
- // in-memory save.
115
- "attempt_api_error_recoveries",
116
109
  "collect_results",
117
110
  "prune_dedupe_log",
118
111
  ];
@@ -260,45 +253,3 @@ fn p2_render_result_received_truncates_summary_to_80_chars() {
260
253
  "summary must be truncated to 80 chars"
261
254
  );
262
255
  }
263
-
264
- // P1 — idle_takeover.unknown_persistent must carry the auth_mode field (null when absent)
265
- // (lifecycle.py:401-408). NOTE: the variant currently lacks the field; the porter adds
266
- // `auth_mode` between provider and consecutive_ticks and updates this construction.
267
- #[test]
268
- fn p2_unknown_persistent_event_serializes_auth_mode_key() {
269
- let evt = CoordinatorEvent::IdleTakeoverUnknownPersistent {
270
- node_id: "w7".into(),
271
- provider: None,
272
- auth_mode: None,
273
- consecutive_ticks: 72,
274
- rollout_path: None,
275
- };
276
- let json = serde_json::to_value(&evt).unwrap();
277
- assert!(
278
- json.get("auth_mode").is_some(),
279
- "idle_takeover.unknown_persistent must serialize an auth_mode key (null when absent)"
280
- );
281
- }
282
-
283
- // P1 — process_abnormal_records matches a LOWERCASED signature too (abnormal_track.py:49,198):
284
- // raw has no needle but signature 'TimeoutError' matches blacklist 'timeout'. Current is
285
- // case-sensitive on `raw` only and ignores the signature → NotifyDefault.
286
- #[test]
287
- fn p2_abnormal_matches_lowercased_signature_too() {
288
- let reg = MockRegistry::new(&[], &["timeout"]);
289
- let records =
290
- vec![serde_json::json!({"raw":"nothing here","signature":"TimeoutError","kind":"error"})];
291
- let out = process_abnormal_records(
292
- &records,
293
- &reg,
294
- Provider::Codex,
295
- &AbnormalNotificationState::default(),
296
- )
297
- .unwrap();
298
- assert_eq!(out.notifications.len(), 1);
299
- assert_eq!(
300
- out.notifications[0].decision,
301
- AbnormalDecision::NotifyBlacklist,
302
- "blacklist 'timeout' must match the lowercased signature 'TimeoutError'"
303
- );
304
- }
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! Coordinator core:daemon lifecycle 宿主 + 单次 tick 编排(19 步固定顺序)+ health/start/stop。
2
3
 
3
4
  use std::collections::BTreeMap;
@@ -19,6 +20,7 @@ use super::health::{
19
20
  pid_is_running, read_coordinator_metadata, write_coordinator_metadata,
20
21
  };
21
22
  use super::runtime_observation::{self, CapturedRuntimeFact};
23
+ use super::steps::TickStepGroup;
22
24
  use super::types::{
23
25
  AgentId, CoordinatorHealthStatus, HealthReport, MetadataSource, Pid, ProviderRegistry,
24
26
  SchemaHealth, StartError, StartOutcome, StartReport, StopError, StopOutcome, StopReport,
@@ -225,7 +227,7 @@ impl Coordinator {
225
227
  /// `if let Some(rec) = &self.order_recorder { rec.lock()...push(STEP_NAME) }`(tick
226
228
  /// 副作用 ORDER 测试断言固定序列)。生产两者均 `None`,零开销。
227
229
  pub fn tick(&self) -> Result<TickReport, TickError> {
228
- self.record_step("load_state");
230
+ self.record_step(TickStepGroup::SessionGate, "load_state");
229
231
  let raw_state = crate::state::persist::load_runtime_state(self.workspace.as_path())?;
230
232
  // Issue 2 (Round 3b gate review §6): when the runtime carries
231
233
  // `active_team_key` AND `teams.<key>` exists, project the team-scoped
@@ -246,7 +248,7 @@ impl Coordinator {
246
248
  let event_log = EventLog::new(self.workspace.as_path());
247
249
  increment_coordinator_tick_iteration_count(&self.workspace);
248
250
 
249
- self.record_step("tmux_session_gate");
251
+ self.record_step(TickStepGroup::SessionGate, "tmux_session_gate");
250
252
  if let Some(session_name) = state
251
253
  .get("session_name")
252
254
  .and_then(Value::as_str)
@@ -268,7 +270,7 @@ impl Coordinator {
268
270
  }
269
271
  }
270
272
 
271
- self.record_step("capture_missing");
273
+ self.record_step(TickStepGroup::SessionGate, "capture_missing");
272
274
  let pending_context_fork_audits =
273
275
  match self.capture_missing_sessions(&mut state, &event_log) {
274
276
  Ok(audits) => audits,
@@ -293,10 +295,10 @@ impl Coordinator {
293
295
  .unwrap_or_default();
294
296
  let has_work_obligation = tick_has_work_obligation(&store);
295
297
 
296
- self.record_step("refresh_statuses");
298
+ self.record_step(TickStepGroup::HealthSync, "refresh_statuses");
297
299
  // TODO(spine slice 2b): split lightweight runtime status refresh from health sync.
298
300
 
299
- self.record_step("startup_prompts");
301
+ self.record_step(TickStepGroup::RuntimePrompts, "startup_prompts");
300
302
  self.handle_startup_prompts(&mut state, &event_log, &pane_snapshot, &window_snapshot);
301
303
 
302
304
  // #229 step2-retry: once an agent's `startup_prompts` flipped to `handled`
@@ -318,7 +320,7 @@ impl Coordinator {
318
320
  // 否则 deliver_pending(下行投递主干)够不到,消息卡 accepted。
319
321
  // bug-084 哲学 + A-6 同族:每步独立 try,失败写 `coordinator.tick.<step>_failed`
320
322
  // 事件后继续走下一步;tick 本身仍返 Ok。
321
- self.record_step("runtime_prompts");
323
+ self.record_step(TickStepGroup::RuntimePrompts, "runtime_prompts");
322
324
  if let Err(error) = self.handle_runtime_approval_prompts(
323
325
  &mut state,
324
326
  &event_log,
@@ -331,7 +333,7 @@ impl Coordinator {
331
333
  );
332
334
  }
333
335
 
334
- self.record_step("sync_health");
336
+ self.record_step(TickStepGroup::HealthSync, "sync_health");
335
337
  // P5 (C-P5-1, N3): ONE pane snapshot per tick, shared by sync_health and the
336
338
  // abnormal-exit pass (same-tick reuse only — the snapshot does not outlive
337
339
  // this tick; every tick re-reads).
@@ -365,7 +367,7 @@ impl Coordinator {
365
367
  );
366
368
  }
367
369
 
368
- self.record_step("deliver_pending");
370
+ self.record_step(TickStepGroup::Delivery, "deliver_pending");
369
371
  let delivered = crate::messaging::deliver_pending_messages(
370
372
  self.workspace.as_path(),
371
373
  &state,
@@ -376,7 +378,7 @@ impl Coordinator {
376
378
  .map(|message_id| DeliveredMessage { message_id })
377
379
  .collect::<Vec<_>>();
378
380
 
379
- self.record_step("fire_scheduled");
381
+ self.record_step(TickStepGroup::Delivery, "fire_scheduled");
380
382
  let scheduled = crate::messaging::fire_due_scheduled_events(
381
383
  self.workspace.as_path(),
382
384
  &store,
@@ -394,18 +396,18 @@ impl Coordinator {
394
396
  // (deliver_pending / fire_scheduled / collect_results) above and below this
395
397
  // block continue to flow unchanged. `_state` / `_store` here are intentionally
396
398
  // unused (the lookups they powered were nag inputs only).
397
- self.record_step("detect_stuck");
399
+ self.record_step(TickStepGroup::Abnormal, "detect_stuck");
398
400
  let stuck: Vec<AgentId> = Vec::new();
399
- self.record_step("record_unknown_idle");
400
- self.record_step("evaluate_takeover");
401
+ self.record_step(TickStepGroup::Abnormal, "record_unknown_idle");
402
+ self.record_step(TickStepGroup::Abnormal, "evaluate_takeover");
401
403
  let idle_alerts: Vec<IdleAlert> = Vec::new();
402
- self.record_step("detect_deadlocks");
404
+ self.record_step(TickStepGroup::Abnormal, "detect_deadlocks");
403
405
  let deadlock_alerts: Vec<DeadlockAlert> = Vec::new();
404
406
  let _ = &store;
405
407
 
406
- self.record_step("detect_compaction");
407
- self.record_step("detect_drift");
408
- self.record_step("detect_api_errors");
408
+ self.record_step(TickStepGroup::Abnormal, "detect_compaction");
409
+ self.record_step(TickStepGroup::Abnormal, "detect_drift");
410
+ self.record_step(TickStepGroup::Abnormal, "detect_api_errors");
409
411
  let leader_capture = self.capture_leader_receiver(&state);
410
412
  let observations = runtime_observation::observe(
411
413
  self.workspace.as_path(),
@@ -425,7 +427,7 @@ impl Coordinator {
425
427
  results: Vec::new(),
426
428
  };
427
429
 
428
- self.record_step("atomic_save");
430
+ self.record_step(TickStepGroup::Persist, "atomic_save");
429
431
  let saved = match &self.save_hook {
430
432
  Some(hook) => hook(&self.workspace, &state),
431
433
  None => {
@@ -468,26 +470,13 @@ impl Coordinator {
468
470
  })?;
469
471
  }
470
472
 
471
- // 0.5.36 (`.team/artifacts/supermarket-api-error-recovery-locate.md` §7.3):
472
- // post-save recovery step. Reloads fresh state, consumes due
473
- // recovery intents that were WRITTEN during the pre-save detector
474
- // step, and drives the lifecycle helper. Runs OUTSIDE the pre-save
475
- // window so nested lifecycle saves cannot be clobbered by this
476
- // tick's final in-memory save (R6 boundary guard).
477
- self.record_step("attempt_api_error_recoveries");
478
- crate::coordinator::steps::abnormal::attempt_due_recoveries(
479
- self.workspace.as_path(),
480
- &event_log,
481
- self.transport.as_ref(),
482
- );
483
-
484
- self.record_step("collect_results");
473
+ self.record_step(TickStepGroup::Delivery, "collect_results");
485
474
  collections.results =
486
475
  collect_results(crate::messaging::collect_results_and_notify_watchers(
487
476
  self.workspace.as_path(),
488
477
  &event_log,
489
478
  )?);
490
- self.record_step("prune_dedupe_log");
479
+ self.record_step(TickStepGroup::Persist, "prune_dedupe_log");
491
480
  Ok(base_tick_report(true, false, None, Some(true), collections))
492
481
  }
493
482
 
@@ -1430,7 +1419,8 @@ impl Coordinator {
1430
1419
  super::health::message_store_schema_health(&self.workspace)
1431
1420
  }
1432
1421
 
1433
- fn record_step(&self, step: &'static str) {
1422
+ fn record_step(&self, group: TickStepGroup, step: &'static str) {
1423
+ debug_assert!(TickStepGroup::ordered().contains(&group));
1434
1424
  if let Some(recorder) = &self.order_recorder {
1435
1425
  if let Ok(mut guard) = recorder.lock() {
1436
1426
  guard.push(step);
@@ -2757,13 +2747,6 @@ mod u1_tests {
2757
2747
  ),
2758
2748
  )
2759
2749
  }
2760
-
2761
- fn error_lists(
2762
- &self,
2763
- _provider: crate::provider::Provider,
2764
- ) -> super::super::types::ErrorLists {
2765
- super::super::types::ErrorLists::default()
2766
- }
2767
2750
  }
2768
2751
 
2769
2752
  #[test]