@team-agent/installer 0.2.10 → 0.3.0

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 (326) hide show
  1. package/Cargo.lock +744 -0
  2. package/Cargo.toml +34 -0
  3. package/crates/team-agent/Cargo.toml +33 -0
  4. package/crates/team-agent/src/cli/adapters.rs +1343 -0
  5. package/crates/team-agent/src/cli/diagnose.rs +554 -0
  6. package/crates/team-agent/src/cli/emit.rs +1077 -0
  7. package/crates/team-agent/src/cli/helpers.rs +88 -0
  8. package/crates/team-agent/src/cli/leader.rs +216 -0
  9. package/crates/team-agent/src/cli/mod.rs +1141 -0
  10. package/crates/team-agent/src/cli/profile.rs +306 -0
  11. package/crates/team-agent/src/cli/send.rs +215 -0
  12. package/crates/team-agent/src/cli/status.rs +179 -0
  13. package/crates/team-agent/src/cli/status_port.rs +502 -0
  14. package/crates/team-agent/src/cli/tests/base.rs +616 -0
  15. package/crates/team-agent/src/cli/tests/compile.rs +96 -0
  16. package/crates/team-agent/src/cli/tests/divergence.rs +509 -0
  17. package/crates/team-agent/src/cli/tests/lane_c.rs +333 -0
  18. package/crates/team-agent/src/cli/tests/leader_watch.rs +395 -0
  19. package/crates/team-agent/src/cli/tests/main_preserved.rs +675 -0
  20. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +390 -0
  21. package/crates/team-agent/src/cli/tests/mod.rs +97 -0
  22. package/crates/team-agent/src/cli/tests/peer_allow.rs +137 -0
  23. package/crates/team-agent/src/cli/tests/repair_state_byte_lock.rs +302 -0
  24. package/crates/team-agent/src/cli/tests/run_delegation.rs +305 -0
  25. package/crates/team-agent/src/cli/tests/status_send.rs +385 -0
  26. package/crates/team-agent/src/cli/tests/verb_profile.rs +182 -0
  27. package/crates/team-agent/src/cli/tests/verb_settle.rs +236 -0
  28. package/crates/team-agent/src/cli/tests/verb_validate.rs +184 -0
  29. package/crates/team-agent/src/cli/types.rs +605 -0
  30. package/crates/team-agent/src/compiler/tests.rs +701 -0
  31. package/crates/team-agent/src/compiler.rs +489 -0
  32. package/crates/team-agent/src/coordinator/backoff.rs +153 -0
  33. package/crates/team-agent/src/coordinator/health.rs +436 -0
  34. package/crates/team-agent/src/coordinator/mod.rs +80 -0
  35. package/crates/team-agent/src/coordinator/orphan.rs +179 -0
  36. package/crates/team-agent/src/coordinator/tests/abnormal.rs +255 -0
  37. package/crates/team-agent/src/coordinator/tests/basics.rs +262 -0
  38. package/crates/team-agent/src/coordinator/tests/daemon.rs +323 -0
  39. package/crates/team-agent/src/coordinator/tests/health_sync.rs +263 -0
  40. package/crates/team-agent/src/coordinator/tests/main_preserved.rs +136 -0
  41. package/crates/team-agent/src/coordinator/tests/mod.rs +310 -0
  42. package/crates/team-agent/src/coordinator/tests/spine.rs +261 -0
  43. package/crates/team-agent/src/coordinator/tests/takeover.rs +227 -0
  44. package/crates/team-agent/src/coordinator/tests/tick_core.rs +256 -0
  45. package/crates/team-agent/src/coordinator/tests/watch.rs +167 -0
  46. package/crates/team-agent/src/coordinator/tick.rs +2032 -0
  47. package/crates/team-agent/src/coordinator/types.rs +584 -0
  48. package/crates/team-agent/src/db/migration.rs +716 -0
  49. package/crates/team-agent/src/db/mod.rs +23 -0
  50. package/crates/team-agent/src/db/schema.rs +378 -0
  51. package/crates/team-agent/src/event_log.rs +375 -0
  52. package/crates/team-agent/src/fake_worker.rs +253 -0
  53. package/crates/team-agent/src/leader/helpers.rs +190 -0
  54. package/crates/team-agent/src/leader/inject.rs +33 -0
  55. package/crates/team-agent/src/leader/lease.rs +1063 -0
  56. package/crates/team-agent/src/leader/mod.rs +99 -0
  57. package/crates/team-agent/src/leader/owner_bind.rs +292 -0
  58. package/crates/team-agent/src/leader/rediscover/tests.rs +525 -0
  59. package/crates/team-agent/src/leader/rediscover.rs +1099 -0
  60. package/crates/team-agent/src/leader/start.rs +273 -0
  61. package/crates/team-agent/src/leader/takeover.rs +235 -0
  62. package/crates/team-agent/src/leader/tests/basics.rs +183 -0
  63. package/crates/team-agent/src/leader/tests/byte_findings.rs +234 -0
  64. package/crates/team-agent/src/leader/tests/identity.rs +206 -0
  65. package/crates/team-agent/src/leader/tests/idle.rs +271 -0
  66. package/crates/team-agent/src/leader/tests/lease_api.rs +225 -0
  67. package/crates/team-agent/src/leader/tests/lease_claim.rs +253 -0
  68. package/crates/team-agent/src/leader/tests/mod.rs +125 -0
  69. package/crates/team-agent/src/leader/tests/rediscover.rs +351 -0
  70. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +204 -0
  71. package/crates/team-agent/src/leader/types.rs +487 -0
  72. package/crates/team-agent/src/lib.rs +85 -0
  73. package/crates/team-agent/src/lifecycle/display.rs +228 -0
  74. package/crates/team-agent/src/lifecycle/helpers.rs +112 -0
  75. package/crates/team-agent/src/lifecycle/launch/plan.rs +227 -0
  76. package/crates/team-agent/src/lifecycle/launch.rs +1833 -0
  77. package/crates/team-agent/src/lifecycle/mod.rs +62 -0
  78. package/crates/team-agent/src/lifecycle/restart/agent.rs +533 -0
  79. package/crates/team-agent/src/lifecycle/restart/common.rs +517 -0
  80. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +41 -0
  81. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +268 -0
  82. package/crates/team-agent/src/lifecycle/restart/remove.rs +780 -0
  83. package/crates/team-agent/src/lifecycle/restart/selection.rs +208 -0
  84. package/crates/team-agent/src/lifecycle/restart/team_state.rs +242 -0
  85. package/crates/team-agent/src/lifecycle/restart.rs +76 -0
  86. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +455 -0
  87. package/crates/team-agent/src/lifecycle/tests/core.rs +989 -0
  88. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +583 -0
  89. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +933 -0
  90. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +265 -0
  91. package/crates/team-agent/src/lifecycle/tests.rs +27 -0
  92. package/crates/team-agent/src/lifecycle/types.rs +685 -0
  93. package/crates/team-agent/src/main.rs +41 -0
  94. package/crates/team-agent/src/mcp_server/helpers.rs +228 -0
  95. package/crates/team-agent/src/mcp_server/mod.rs +183 -0
  96. package/crates/team-agent/src/mcp_server/normalize.rs +312 -0
  97. package/crates/team-agent/src/mcp_server/tests/golden.rs +283 -0
  98. package/crates/team-agent/src/mcp_server/tests/normalize.rs +244 -0
  99. package/crates/team-agent/src/mcp_server/tests/scoped.rs +189 -0
  100. package/crates/team-agent/src/mcp_server/tests/send.rs +222 -0
  101. package/crates/team-agent/src/mcp_server/tests/tools.rs +158 -0
  102. package/crates/team-agent/src/mcp_server/tests/wire.rs +159 -0
  103. package/crates/team-agent/src/mcp_server/tests.rs +38 -0
  104. package/crates/team-agent/src/mcp_server/tools.rs +603 -0
  105. package/crates/team-agent/src/mcp_server/types.rs +421 -0
  106. package/crates/team-agent/src/mcp_server/wire.rs +388 -0
  107. package/crates/team-agent/src/message_store.rs +767 -0
  108. package/crates/team-agent/src/messaging/activity.rs +433 -0
  109. package/crates/team-agent/src/messaging/delivery.rs +542 -0
  110. package/crates/team-agent/src/messaging/helpers.rs +209 -0
  111. package/crates/team-agent/src/messaging/leader_receiver.rs +340 -0
  112. package/crates/team-agent/src/messaging/mod.rs +147 -0
  113. package/crates/team-agent/src/messaging/peers.rs +32 -0
  114. package/crates/team-agent/src/messaging/results.rs +537 -0
  115. package/crates/team-agent/src/messaging/scheduler.rs +344 -0
  116. package/crates/team-agent/src/messaging/selftest.rs +100 -0
  117. package/crates/team-agent/src/messaging/send.rs +582 -0
  118. package/crates/team-agent/src/messaging/tests/basic.rs +357 -0
  119. package/crates/team-agent/src/messaging/tests/main_preserved.rs +122 -0
  120. package/crates/team-agent/src/messaging/tests/mod.rs +293 -0
  121. package/crates/team-agent/src/messaging/tests/runtime.rs +1422 -0
  122. package/crates/team-agent/src/messaging/tests/spine.rs +437 -0
  123. package/crates/team-agent/src/messaging/trust.rs +192 -0
  124. package/crates/team-agent/src/messaging/types.rs +355 -0
  125. package/crates/team-agent/src/messaging/watchers.rs +591 -0
  126. package/crates/team-agent/src/model/enums.rs +311 -0
  127. package/crates/team-agent/src/model/errors.rs +17 -0
  128. package/crates/team-agent/src/model/ids.rs +155 -0
  129. package/crates/team-agent/src/model/mod.rs +22 -0
  130. package/crates/team-agent/src/model/paths.rs +228 -0
  131. package/crates/team-agent/src/model/permissions.rs +567 -0
  132. package/crates/team-agent/src/model/routing.rs +340 -0
  133. package/crates/team-agent/src/model/spec.rs +680 -0
  134. package/crates/team-agent/src/model/task_graph.rs +380 -0
  135. package/crates/team-agent/src/model/testdata/fuzz.golden.yaml +43 -0
  136. package/crates/team-agent/src/model/testdata/fuzz.yaml +43 -0
  137. package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +207 -0
  138. package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +206 -0
  139. package/crates/team-agent/src/model/testdata/team.spec.yaml +206 -0
  140. package/crates/team-agent/src/model/yaml/tests.rs +288 -0
  141. package/crates/team-agent/src/model/yaml.rs +800 -0
  142. package/crates/team-agent/src/packaging/install.rs +305 -0
  143. package/crates/team-agent/src/packaging/migrate.rs +30 -0
  144. package/crates/team-agent/src/packaging/mod.rs +82 -0
  145. package/crates/team-agent/src/packaging/repair.rs +24 -0
  146. package/crates/team-agent/src/packaging/tests.rs +829 -0
  147. package/crates/team-agent/src/packaging/types.rs +369 -0
  148. package/crates/team-agent/src/provider/adapter.rs +801 -0
  149. package/crates/team-agent/src/provider/approvals/mod.rs +2 -0
  150. package/crates/team-agent/src/provider/approvals/parsing.rs +452 -0
  151. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +163 -0
  152. package/crates/team-agent/src/provider/classify.rs +456 -0
  153. package/crates/team-agent/src/provider/faults.rs +136 -0
  154. package/crates/team-agent/src/provider/helpers.rs +41 -0
  155. package/crates/team-agent/src/provider/mod.rs +53 -0
  156. package/crates/team-agent/src/provider/startup_prompt.rs +423 -0
  157. package/crates/team-agent/src/provider/tests/adapter.rs +239 -0
  158. package/crates/team-agent/src/provider/tests/classify.rs +240 -0
  159. package/crates/team-agent/src/provider/tests/faults.rs +120 -0
  160. package/crates/team-agent/src/provider/tests/idle.rs +208 -0
  161. package/crates/team-agent/src/provider/tests/wire.rs +213 -0
  162. package/crates/team-agent/src/provider/tests.rs +31 -0
  163. package/crates/team-agent/src/provider/types.rs +424 -0
  164. package/crates/team-agent/src/state/identity.rs +656 -0
  165. package/crates/team-agent/src/state/mod.rs +58 -0
  166. package/crates/team-agent/src/state/owner_gate.rs +423 -0
  167. package/crates/team-agent/src/state/persist.rs +712 -0
  168. package/crates/team-agent/src/state/projection.rs +657 -0
  169. package/crates/team-agent/src/state/selector.rs +105 -0
  170. package/crates/team-agent/src/state/testdata/state-rich.canonical.json +133 -0
  171. package/crates/team-agent/src/tmux_backend/tests.rs +586 -0
  172. package/crates/team-agent/src/tmux_backend.rs +758 -0
  173. package/crates/team-agent/src/transport/test_support.rs +252 -0
  174. package/crates/team-agent/src/transport/tests/behavior.rs +327 -0
  175. package/crates/team-agent/src/transport/tests/mod.rs +199 -0
  176. package/crates/team-agent/src/transport/tests/wire.rs +527 -0
  177. package/crates/team-agent/src/transport.rs +774 -0
  178. package/npm/install.mjs +90 -106
  179. package/package.json +15 -13
  180. package/crates/team-agent-core/Cargo.toml +0 -12
  181. package/crates/team-agent-core/src/lib.rs +0 -332
  182. package/crates/team-agent-core/src/main.rs +0 -152
  183. package/pyproject.toml +0 -18
  184. package/scripts/install.py +0 -88
  185. package/scripts/run_regression_tests.py +0 -83
  186. package/src/team_agent/__init__.py +0 -3
  187. package/src/team_agent/__main__.py +0 -5
  188. package/src/team_agent/_legacy_pane_discovery.py +0 -186
  189. package/src/team_agent/abnormal_track.py +0 -253
  190. package/src/team_agent/approvals/__init__.py +0 -65
  191. package/src/team_agent/approvals/constants.py +0 -6
  192. package/src/team_agent/approvals/parsing.py +0 -176
  193. package/src/team_agent/approvals/runtime_prompts.py +0 -171
  194. package/src/team_agent/approvals/status.py +0 -176
  195. package/src/team_agent/cli/__init__.py +0 -137
  196. package/src/team_agent/cli/commands.py +0 -481
  197. package/src/team_agent/cli/e2e.py +0 -202
  198. package/src/team_agent/cli/helpers.py +0 -226
  199. package/src/team_agent/cli/parser.py +0 -540
  200. package/src/team_agent/compiler.py +0 -334
  201. package/src/team_agent/coordinator/__init__.py +0 -53
  202. package/src/team_agent/coordinator/__main__.py +0 -83
  203. package/src/team_agent/coordinator/lifecycle.py +0 -363
  204. package/src/team_agent/coordinator/metadata.py +0 -61
  205. package/src/team_agent/coordinator/paths.py +0 -17
  206. package/src/team_agent/diagnose/__init__.py +0 -48
  207. package/src/team_agent/diagnose/checks.py +0 -101
  208. package/src/team_agent/diagnose/comms.py +0 -213
  209. package/src/team_agent/diagnose/health.py +0 -241
  210. package/src/team_agent/diagnose/orphan_cleanup.py +0 -364
  211. package/src/team_agent/diagnose/preflight.py +0 -194
  212. package/src/team_agent/diagnose/quick_start.py +0 -324
  213. package/src/team_agent/display/__init__.py +0 -92
  214. package/src/team_agent/display/adaptive.py +0 -511
  215. package/src/team_agent/display/backend.py +0 -46
  216. package/src/team_agent/display/close.py +0 -154
  217. package/src/team_agent/display/ghostty.py +0 -77
  218. package/src/team_agent/display/rebuild.py +0 -102
  219. package/src/team_agent/display/tiling.py +0 -156
  220. package/src/team_agent/display/worker_window.py +0 -114
  221. package/src/team_agent/display/workspace.py +0 -382
  222. package/src/team_agent/errors.py +0 -10
  223. package/src/team_agent/events.py +0 -84
  224. package/src/team_agent/fake_worker.py +0 -80
  225. package/src/team_agent/idle_predicate.py +0 -200
  226. package/src/team_agent/idle_takeover.py +0 -59
  227. package/src/team_agent/idle_takeover_wiring.py +0 -111
  228. package/src/team_agent/launch/__init__.py +0 -41
  229. package/src/team_agent/launch/bootstrap.py +0 -85
  230. package/src/team_agent/launch/config.py +0 -106
  231. package/src/team_agent/launch/core.py +0 -301
  232. package/src/team_agent/launch/requirements.py +0 -57
  233. package/src/team_agent/leader/__init__.py +0 -926
  234. package/src/team_agent/leader_binding.py +0 -183
  235. package/src/team_agent/lifecycle/__init__.py +0 -5
  236. package/src/team_agent/lifecycle/agents.py +0 -278
  237. package/src/team_agent/lifecycle/operations.py +0 -411
  238. package/src/team_agent/lifecycle/paste_buffer_hygiene.py +0 -39
  239. package/src/team_agent/lifecycle/start.py +0 -363
  240. package/src/team_agent/mcp_server/__init__.py +0 -42
  241. package/src/team_agent/mcp_server/__main__.py +0 -7
  242. package/src/team_agent/mcp_server/contracts.py +0 -148
  243. package/src/team_agent/mcp_server/normalize.py +0 -257
  244. package/src/team_agent/mcp_server/server.py +0 -150
  245. package/src/team_agent/mcp_server/tools.py +0 -352
  246. package/src/team_agent/message_store/__init__.py +0 -23
  247. package/src/team_agent/message_store/agent_health.py +0 -113
  248. package/src/team_agent/message_store/core.py +0 -497
  249. package/src/team_agent/message_store/leader_notification_log.py +0 -198
  250. package/src/team_agent/message_store/result_watchers.py +0 -251
  251. package/src/team_agent/message_store/schema.py +0 -308
  252. package/src/team_agent/message_store/schema_migration.py +0 -448
  253. package/src/team_agent/messaging/__init__.py +0 -1
  254. package/src/team_agent/messaging/activity_detector.py +0 -254
  255. package/src/team_agent/messaging/delivery.py +0 -473
  256. package/src/team_agent/messaging/deps.py +0 -247
  257. package/src/team_agent/messaging/idle_alerts.py +0 -423
  258. package/src/team_agent/messaging/internal_delivery.py +0 -46
  259. package/src/team_agent/messaging/leader.py +0 -497
  260. package/src/team_agent/messaging/leader_api_errors.py +0 -216
  261. package/src/team_agent/messaging/leader_panes.py +0 -673
  262. package/src/team_agent/messaging/owner_bypass.py +0 -29
  263. package/src/team_agent/messaging/result_delivery.py +0 -539
  264. package/src/team_agent/messaging/results.py +0 -447
  265. package/src/team_agent/messaging/scheduler.py +0 -450
  266. package/src/team_agent/messaging/send.py +0 -532
  267. package/src/team_agent/messaging/session_drift.py +0 -94
  268. package/src/team_agent/messaging/tmux_io.py +0 -506
  269. package/src/team_agent/messaging/tmux_prompt.py +0 -338
  270. package/src/team_agent/messaging/trust_auto_answer.py +0 -52
  271. package/src/team_agent/orchestrator/__init__.py +0 -376
  272. package/src/team_agent/orchestrator/plan.py +0 -122
  273. package/src/team_agent/orchestrator/state.py +0 -128
  274. package/src/team_agent/paths.py +0 -45
  275. package/src/team_agent/permissions.py +0 -123
  276. package/src/team_agent/profiles/__init__.py +0 -82
  277. package/src/team_agent/profiles/constants.py +0 -19
  278. package/src/team_agent/profiles/core.py +0 -407
  279. package/src/team_agent/profiles/helpers.py +0 -69
  280. package/src/team_agent/profiles/provider_env.py +0 -188
  281. package/src/team_agent/profiles/smoke.py +0 -201
  282. package/src/team_agent/provider_cli/__init__.py +0 -43
  283. package/src/team_agent/provider_cli/adapter.py +0 -172
  284. package/src/team_agent/provider_cli/base.py +0 -48
  285. package/src/team_agent/provider_cli/claude.py +0 -457
  286. package/src/team_agent/provider_cli/codex.py +0 -336
  287. package/src/team_agent/provider_cli/copilot.py +0 -8
  288. package/src/team_agent/provider_cli/fake.py +0 -39
  289. package/src/team_agent/provider_cli/gemini.py +0 -95
  290. package/src/team_agent/provider_cli/opencode.py +0 -8
  291. package/src/team_agent/provider_cli/prompt.py +0 -62
  292. package/src/team_agent/provider_cli/registry.py +0 -18
  293. package/src/team_agent/provider_cli/unsupported.py +0 -32
  294. package/src/team_agent/provider_state/README.md +0 -78
  295. package/src/team_agent/provider_state/__init__.py +0 -86
  296. package/src/team_agent/provider_state/claude.py +0 -86
  297. package/src/team_agent/provider_state/codex.py +0 -84
  298. package/src/team_agent/provider_state/common.py +0 -207
  299. package/src/team_agent/provider_state/registry.py +0 -118
  300. package/src/team_agent/providers.py +0 -163
  301. package/src/team_agent/quality_gates.py +0 -104
  302. package/src/team_agent/restart/__init__.py +0 -34
  303. package/src/team_agent/restart/orchestration.py +0 -554
  304. package/src/team_agent/restart/selection.py +0 -89
  305. package/src/team_agent/restart/snapshot.py +0 -70
  306. package/src/team_agent/routing.py +0 -84
  307. package/src/team_agent/runtime.py +0 -1239
  308. package/src/team_agent/rust_core.py +0 -327
  309. package/src/team_agent/sessions/__init__.py +0 -25
  310. package/src/team_agent/sessions/capture.py +0 -143
  311. package/src/team_agent/sessions/inventory.py +0 -44
  312. package/src/team_agent/sessions/resume.py +0 -135
  313. package/src/team_agent/simple_yaml.py +0 -236
  314. package/src/team_agent/spec.py +0 -370
  315. package/src/team_agent/state.py +0 -602
  316. package/src/team_agent/status/__init__.py +0 -63
  317. package/src/team_agent/status/approvals.py +0 -52
  318. package/src/team_agent/status/compact.py +0 -158
  319. package/src/team_agent/status/constants.py +0 -18
  320. package/src/team_agent/status/inbox.py +0 -58
  321. package/src/team_agent/status/peek.py +0 -117
  322. package/src/team_agent/status/queries.py +0 -199
  323. package/src/team_agent/task_graph.py +0 -80
  324. package/src/team_agent/terminal.py +0 -57
  325. package/src/team_agent/wake.py +0 -58
  326. package/src/team_agent/watch/__init__.py +0 -145
@@ -0,0 +1,395 @@
1
+ use super::*;
2
+
3
+ // =========================================================================
4
+ // (DELEGATION sweep, rt-host-a loop #2) — cmd_send persistence + shutdown teardown.
5
+ // =========================================================================
6
+
7
+ // 1 [P0, CONFIRMED BUG] — cmd_send (cli/send.rs) returns delivery_json synthetic (true,"delivered")
8
+ // and NEVER calls messaging::send_message, so NO `messages` row is persisted. RED: after cmd_send to
9
+ // a worker on a seeded ws (real MessageStore), assert a messages row was PERSISTED (query by
10
+ // recipient+content) — proving the real messaging::send_message -> MessageStore.create_message path.
11
+ // DB-persist is observable without a transport. (messaging::send_message is ALSO a stub today, so the
12
+ // porter wires BOTH cmd_send->send_message AND send_message->persist.)
13
+ //
14
+ // OLD seed: flat `{"agents": {"w1": ...}}` worked because send_message read agents
15
+ // directly off the raw runtime state.
16
+ // NEW seed (Bug 1/2 — team-in-team state scope, see tests/team_in_team_state_scope_red.rs):
17
+ // cmd_send → resolve_active_team yields a team_key, send_message projects the
18
+ // raw state through `project_top_level_view(team_key)` which reads agents off
19
+ // `teams[team_key].agents`. The seed therefore lives under `teams.current.agents`
20
+ // with `active_team_key=current`; the delegation + persistence behavior under test
21
+ // is unchanged — only the shape of "an in-team recipient" is now nested.
22
+ #[test]
23
+ fn cli_send_persists_real_message_row() {
24
+ let ws = deleg_uniq_dir("send");
25
+ let _ = crate::message_store::MessageStore::open(&ws).unwrap(); // real store at the workspace
26
+ // w1 must be a known team agent — golden send.py refuses non-team targets
27
+ // (target_not_in_team); an in-team recipient is the one that persists. Bug 1/2
28
+ // scopes agents under teams[<key>].agents (NEW shape).
29
+ crate::state::persist::save_runtime_state(
30
+ &ws,
31
+ &serde_json::json!({
32
+ "active_team_key": "current",
33
+ "teams": {"current": {"agents": {"w1": {"provider": "codex"}}}}
34
+ }),
35
+ )
36
+ .unwrap();
37
+ let args = SendArgs {
38
+ target: Some("w1".to_string()),
39
+ message: vec!["hello-real-delegation".to_string()],
40
+ targets: None,
41
+ workspace: ws.clone(),
42
+ team: None,
43
+ task: None,
44
+ sender: "leader".to_string(),
45
+ no_ack: false,
46
+ no_wait: true,
47
+ watch_result: false,
48
+ timeout: 0.0,
49
+ confirm_human: false,
50
+ json: true,
51
+ message_id: None,
52
+ };
53
+ let _ = cmd_send(&args);
54
+
55
+ let store = crate::message_store::MessageStore::open(&ws).unwrap();
56
+ let conn = crate::db::schema::open_db(store.db_path()).unwrap();
57
+ let count: i64 = conn
58
+ .query_row(
59
+ "select count(*) from messages where recipient = 'w1' and content = 'hello-real-delegation'",
60
+ [],
61
+ |r| r.get(0),
62
+ )
63
+ .unwrap();
64
+ assert!(
65
+ count >= 1,
66
+ "cmd_send must delegate to messaging::send_message and PERSIST a `messages` row (recipient=w1); \
67
+ the synthetic delivery_json writes NO DB row -> count={count}"
68
+ );
69
+ }
70
+
71
+ // 5 [P1, CONFIRMED PARTIAL] — shutdown stops the coordinator but NEVER kills the team tmux session
72
+ // (-> orphan worker panes). #[ignore] real-machine: the wired shutdown kills the real tmux session.
73
+ // SEAM NEEDED (note to porter): add shutdown_with_transport(workspace, keep_logs, team, &dyn Transport)
74
+ // (mirror restart_with_transport) so this can assert IN-PROCESS that transport.kill_session(team
75
+ // session) was called via a RecordingTransport — the clean "team session killed / workers reaped"
76
+ // observable. Until that seam lands, this asserts the real teardown surfaces (session-kill / stop).
77
+ #[test]
78
+ #[ignore = "real-machine: shutdown kills the team tmux session. PORTER SEAM: add \
79
+ shutdown_with_transport(workspace, keep_logs, team, &dyn Transport) so kill_session is \
80
+ assertable in-process via a RecordingTransport (workers reaped, no orphan panes)."]
81
+ fn cli_shutdown_kills_team_session_real_teardown() {
82
+ let ws = seed_status_workspace(); // state.json with a running agent + session_name
83
+ let args = ShutdownArgs { workspace: ws, team: None, keep_logs: true, json: true };
84
+ let text = format!("{:?}", cmd_shutdown(&args)).to_lowercase();
85
+ assert!(
86
+ text.contains("session") || text.contains("killed") || text.contains("kill_session"),
87
+ "shutdown must KILL the team tmux session + reap workers (real teardown); the rt-host-a partial \
88
+ bug stops the coordinator but leaves the session + workers running (orphans); got {text}"
89
+ );
90
+ }
91
+
92
+ // =========================================================================
93
+ // WAVE-2 Lane B — CLI leader handler delegation byte-parity (leader_port::*).
94
+ // The three CLI verbs are thin pass-throughs (cli/commands.py:152-161):
95
+ // cmd_takeover -> runtime.takeover(ws, team, confirm)
96
+ // cmd_claim_leader -> runtime.claim_leader(ws, team, confirm) (Family A)
97
+ // cmd_identity -> runtime.leader_identity(ws, team)
98
+ // leader_port::{takeover,claim_leader,leader_identity} are STUBS returning
99
+ // the WRONG shape today -> these LOCK the golden dict so the porter wires
100
+ // them into leader::* / runtime.* and matches byte-for-byte.
101
+ // Golden re-probed @ team-agent-public (probe_claim.py / probe_rtclaim.py /
102
+ // probe_lid.py). Label: RED = stub returns wrong shape today.
103
+ // =========================================================================
104
+
105
+ fn leader_port_ws(tag: &str) -> std::path::PathBuf {
106
+ let dir = std::env::temp_dir().join(format!(
107
+ "ta-cli-leaderport-{}-{}",
108
+ tag,
109
+ std::process::id()
110
+ ));
111
+ std::fs::create_dir_all(&dir).unwrap();
112
+ dir
113
+ }
114
+
115
+ // #235 / I-RN-3 — explicit takeover is unconditional once the caller has a live pane:
116
+ // it is not a permission gate and must replace a live owner, advancing owner_epoch.
117
+ // `--confirm` may remain a UX affordance, but it is not the authority check.
118
+ #[test]
119
+ fn leader_port_takeover_refuses_without_confirm_byte_parity_obsolete_now_unconditional() {
120
+ let cli = std::fs::read_to_string(
121
+ std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/cli/mod.rs"),
122
+ )
123
+ .unwrap();
124
+ assert_eq!(
125
+ cli.matches("claim_leader(workspace, team, true)").count(),
126
+ 1,
127
+ "takeover must call the lease path as an explicit takeover, not pass the CLI confirm flag as a permission gate"
128
+ );
129
+
130
+ let ws = leader_port_ws("tk_unconditional_live");
131
+ let team_id = crate::model::ids::TeamKey::new("current");
132
+ let caller = crate::transport::PaneId::new("%5");
133
+ let mut state = json!({
134
+ "session_name": "team-agent-x",
135
+ "team_owner": {
136
+ "pane_id": "%1",
137
+ "provider": "codex",
138
+ "machine_fingerprint": "fp",
139
+ "leader_session_uuid": "OWNERUUID",
140
+ "owner_epoch": 2,
141
+ "claimed_at": "t",
142
+ "claimed_via": "claim-leader"
143
+ },
144
+ "leader_receiver": {
145
+ "pane_id": "%1",
146
+ "provider": "codex",
147
+ "owner_epoch": 2,
148
+ "leader_session_uuid": "OWNERUUID"
149
+ }
150
+ });
151
+ let event_log = crate::event_log::EventLog::new(&ws);
152
+ let live = LeaderPortSeededLiveness::new(&["%1", "%5"]);
153
+ let r = crate::leader::claim_lease_no_incident(
154
+ &ws,
155
+ &mut state,
156
+ None,
157
+ &team_id,
158
+ &caller,
159
+ true,
160
+ &event_log,
161
+ &live,
162
+ )
163
+ .unwrap();
164
+
165
+ assert!(r.ok, "explicit takeover must succeed even when old owner pane is live: {r:?}");
166
+ assert_eq!(r.status, crate::leader::LeaseStatus::Claimed);
167
+ assert_eq!(r.owner_epoch, Some(crate::model::ids::OwnerEpoch(3)));
168
+ assert_eq!(r.bound_pane_id, Some(caller.clone()));
169
+ assert_eq!(state["leader_receiver"]["pane_id"], json!("%5"));
170
+ assert_eq!(state["team_owner"]["pane_id"], json!("%5"));
171
+ assert_eq!(state["team_owner"]["owner_epoch"], json!(3));
172
+ }
173
+
174
+ struct LeaderPortSeededLiveness {
175
+ live_panes: std::collections::BTreeSet<String>,
176
+ }
177
+
178
+ impl LeaderPortSeededLiveness {
179
+ fn new(panes: &[&str]) -> Self {
180
+ Self {
181
+ live_panes: panes.iter().map(|pane| (*pane).to_string()).collect(),
182
+ }
183
+ }
184
+ }
185
+
186
+ impl crate::state::owner_gate::PaneLivenessProbe for LeaderPortSeededLiveness {
187
+ fn liveness(&self, pane_id: &str) -> crate::model::enums::PaneLiveness {
188
+ if self.live_panes.contains(pane_id) {
189
+ crate::model::enums::PaneLiveness::Live
190
+ } else {
191
+ crate::model::enums::PaneLiveness::Dead
192
+ }
193
+ }
194
+ }
195
+
196
+ // RED — takeover(confirm=true) with no $TMUX_PANE: the Family A positive-source
197
+ // bind gate fires -> refused caller_pane_missing with the bind diagnostic dict.
198
+ // golden probe_claim.py takeover(confirm=True): {ok:false, status:"refused",
199
+ // reason:"caller_pane_missing", caller_pane_id:"", caller_current_command:"",
200
+ // hint:"run team-agent from inside your leader pane (the tmux pane you want to
201
+ // own this team)."}. Current stub returns {ok:true,...} (wrong) -> RED.
202
+ #[test]
203
+ #[ignore = "RED needs $TMUX_PANE ABSENT (Family A bind gate); run `--ignored` in a non-tmux shell. \
204
+ Inside tmux the live-pane resolver would engage. Seam: porter wires leader_port::takeover \
205
+ -> runtime.takeover whose Family A bind refuses caller_pane_missing when $TMUX_PANE missing."]
206
+ fn leader_port_takeover_confirm_without_pane_refuses_caller_pane_missing() {
207
+ if std::env::var_os("TMUX_PANE").is_some() {
208
+ return; // inside tmux the bind gate would pass; this case verifies the missing arm.
209
+ }
210
+ let ws = leader_port_ws("tk_confirm_nopane");
211
+ // seed a resolvable team so the gate reaches the bind step (not team_target_unresolved).
212
+ let st = json!({"session_name": "team-agent-x", "teams": {"team-agent-x": {}}});
213
+ let path = crate::state::persist::runtime_state_path(&ws);
214
+ std::fs::create_dir_all(path.parent().unwrap()).unwrap();
215
+ std::fs::write(&path, serde_json::to_string(&st).unwrap()).unwrap();
216
+ let v = super::leader_port::takeover(&ws, Some("team-agent-x"), true).unwrap();
217
+ assert_eq!(v["ok"], json!(false));
218
+ assert_eq!(v["status"], json!("refused"));
219
+ assert_eq!(v["reason"], json!("caller_pane_missing"));
220
+ assert_eq!(v["caller_pane_id"], json!(""));
221
+ assert_eq!(v["caller_current_command"], json!(""));
222
+ assert_eq!(
223
+ v["hint"],
224
+ json!("run team-agent from inside your leader pane (the tmux pane you want to own this team).")
225
+ );
226
+ }
227
+
228
+ // RED — claim_leader(confirm=false) with no $TMUX_PANE: runtime.claim_leader is
229
+ // ALSO Family A (runtime.py:791) -> the bind gate fires FIRST, so the no-pane
230
+ // refusal is caller_pane_missing (NOT the leader-lane "not_in_tmux_pane").
231
+ // golden probe_rtclaim.py. This pins the runtime-vs-leader distinction:
232
+ // the CLI projection must reflect runtime.claim_leader's Family A bind gate.
233
+ // Current stub returns {ok:true, inbox_hint:...} (wrong) -> RED.
234
+ #[test]
235
+ #[ignore = "RED needs $TMUX_PANE ABSENT (Family A bind gate fires first); run `--ignored` in a \
236
+ non-tmux shell. Inside tmux the resolver would engage. Seam: porter wires \
237
+ leader_port::claim_leader -> runtime.claim_leader (Family A) whose bind refuses \
238
+ caller_pane_missing (NOT leader-lane not_in_tmux_pane) when $TMUX_PANE missing."]
239
+ fn leader_port_claim_leader_no_pane_refuses_caller_pane_missing_family_a() {
240
+ if std::env::var_os("TMUX_PANE").is_some() {
241
+ return;
242
+ }
243
+ let ws = leader_port_ws("claim_nopane");
244
+ let v = super::leader_port::claim_leader(&ws, None, false).unwrap();
245
+ assert_eq!(v["ok"], json!(false));
246
+ assert_eq!(v["status"], json!("refused"));
247
+ assert_eq!(
248
+ v["reason"],
249
+ json!("caller_pane_missing"),
250
+ "runtime.claim_leader (Family A) bind gate -> caller_pane_missing, NOT not_in_tmux_pane"
251
+ );
252
+ assert_eq!(v["caller_pane_id"], json!(""));
253
+ assert_eq!(
254
+ v["hint"],
255
+ json!("run team-agent from inside your leader pane (the tmux pane you want to own this team).")
256
+ );
257
+ }
258
+
259
+ // RED — leader_identity(): CLI directly emits leader.leader_identity's 9-key
260
+ // dict (runtime.leader_identity is imported from leader). golden probe_lid.py
261
+ // keys: ok, uuid_prefix, machine_fingerprint, workspace_abspath, os_user,
262
+ // team_id, current_pane_id, last_seen_at, source. Current stub returns
263
+ // {ok:true, team:...} (wrong shape) -> RED.
264
+ #[test]
265
+ fn leader_port_leader_identity_emits_nine_key_dict() {
266
+ let ws = leader_port_ws("identity");
267
+ std::fs::create_dir_all(crate::model::paths::runtime_dir(&ws)).unwrap();
268
+ let v = super::leader_port::leader_identity(&ws, None).unwrap();
269
+ assert_eq!(v["ok"], json!(true));
270
+ let obj = v.as_object().expect("identity → JSON object");
271
+ for key in [
272
+ "ok", "uuid_prefix", "machine_fingerprint", "workspace_abspath",
273
+ "os_user", "team_id", "current_pane_id", "last_seen_at", "source",
274
+ ] {
275
+ assert!(obj.contains_key(key), "golden identity dict must carry '{key}', got {obj:?}");
276
+ }
277
+ // no override/state uuid → source is the leader-plan "derived" string.
278
+ assert_eq!(v["source"], json!("derived"));
279
+ // uuid_prefix is exactly 12 hex chars (derive[:12]).
280
+ let prefix = v["uuid_prefix"].as_str().expect("uuid_prefix str");
281
+ assert_eq!(prefix.len(), 12, "uuid_prefix == derived[:12]");
282
+ assert!(prefix.chars().all(|c| c.is_ascii_hexdigit()));
283
+ // no team registered + no TMUX_PANE/receiver → these are JSON null.
284
+ if std::env::var_os("TMUX_PANE").is_none() {
285
+ assert_eq!(v["current_pane_id"], serde_json::Value::Null);
286
+ }
287
+ assert_eq!(v["last_seen_at"], serde_json::Value::Null);
288
+ }
289
+
290
+ // ── cmd_watch (cli/adapters.rs:58) [RED] — today a CmdResult::none() no-op ───────────────────────
291
+ // Golden cmd_watch (cli/commands.py:103-109) DELEGATES to run_watch(workspace.resolve(), team) and
292
+ // exits 0. Golden run_watch (watch/__init__.py:25-37) is a `while True` LIVE TAIL that streams
293
+ // render_event_line output (collect_watch_lines) for the watched team. The Rust cmd_watch returns
294
+ // CmdResult::none() — a no-op that never touches the watch subsystem. The watch LINE SHAPE is itself
295
+ // byte-locked by coordinator::render_event_line (coordinator/tests.rs GROUP H); the cli contract here
296
+ // is that cmd_watch must DELEGATE and surface those rendered lines. Seeded a result_received event ->
297
+ // golden render = "result_received: <agent> -> <summary>" (render_event_line: agent_id + summary[:80]).
298
+ //
299
+ // RED confirmed TODAY: cmd_watch=none() returns immediately (no watch line) -> the assertion fails
300
+ // (and does NOT hang). PORTER: wire cmd_watch -> coordinator::run_watch(resolved workspace, team,
301
+ // interval, sink). Since golden's tail is `while True`, the cli port needs a TERMINATING / bounded
302
+ // entry to be both byte-parity AND unit-testable (collect the current watch lines into the CmdResult,
303
+ // or stream to stdout with a bounded test seam) — a blocking unit test is unacceptable.
304
+ #[test]
305
+ fn cmd_watch_delegates_and_surfaces_rendered_watch_lines_not_noop() {
306
+ let ws = tmp_workspace();
307
+ let logs = crate::model::paths::logs_dir(&ws);
308
+ std::fs::create_dir_all(&logs).unwrap();
309
+ std::fs::write(
310
+ logs.join("events.jsonl"),
311
+ "{\"event\":\"result_received\",\"agent_id\":\"alpha\",\"summary\":\"did the thing\"}\n",
312
+ )
313
+ .unwrap();
314
+ let r = cmd_watch(&WatchArgs { workspace: ws.clone(), team: None })
315
+ .expect("cmd_watch returns a CmdResult");
316
+ let text = match &r.output {
317
+ CmdOutput::Human(s) => s.clone(),
318
+ CmdOutput::Json(v) => v.to_string(),
319
+ CmdOutput::None => String::new(),
320
+ };
321
+ assert!(
322
+ text.contains("result_received: alpha -> did the thing"),
323
+ "cmd_watch must DELEGATE to the watch subsystem (run_watch -> render_event_line) and surface the \
324
+ rendered watch line; today it is a CmdResult::none() no-op. got output={:?}",
325
+ r.output
326
+ );
327
+ }
328
+
329
+ // D7 [WARN] — WAVE-2 Lane B: empty caller binds refuse as caller_pane_missing.
330
+ #[test]
331
+ #[serial_test::serial(env)]
332
+ fn d7_lease_refusal_dict_is_golden_minimal_four_keys() {
333
+ use std::sync::Mutex;
334
+ static D7_ENV: Mutex<()> = Mutex::new(());
335
+ let _g = D7_ENV.lock().unwrap_or_else(|p| p.into_inner());
336
+ let _env = EnvGuard::set(&[
337
+ ("TMUX_PANE", Some("")), // empty caller -> caller_pane_missing via the lease path
338
+ ("TEAM_AGENT_LEADER_PANE_ID", None),
339
+ ]);
340
+ let ws = tmp_workspace();
341
+ crate::state::persist::save_runtime_state(&ws, &json!({})).unwrap(); // claim_leader loads state
342
+ let result = super::leader_port::claim_leader(&ws, None, false);
343
+ let v = result.expect("claim_leader projection");
344
+ let obj = v.as_object().expect("lease dict");
345
+ assert_eq!(
346
+ obj.get("reason").and_then(|r| r.as_str()),
347
+ Some("caller_pane_missing"),
348
+ "precondition: empty caller -> caller_pane_missing; got {v:?}"
349
+ );
350
+ let keys: std::collections::BTreeSet<&str> = obj.keys().map(String::as_str).collect();
351
+ assert_eq!(
352
+ keys,
353
+ ["ok", "status", "reason", "caller_pane_id", "caller_current_command", "hint"]
354
+ .into_iter()
355
+ .collect::<std::collections::BTreeSet<_>>(),
356
+ "golden caller_pane_missing refusal key set"
357
+ );
358
+ }
359
+
360
+ struct EnvGuard {
361
+ previous: Vec<(&'static str, Option<String>)>,
362
+ }
363
+
364
+ impl EnvGuard {
365
+ fn set(values: &[(&'static str, Option<&'static str>)]) -> Self {
366
+ let previous = values
367
+ .iter()
368
+ .map(|(key, _)| (*key, std::env::var(key).ok()))
369
+ .collect::<Vec<_>>();
370
+ for (key, value) in values {
371
+ unsafe {
372
+ if let Some(value) = value {
373
+ std::env::set_var(key, value);
374
+ } else {
375
+ std::env::remove_var(key);
376
+ }
377
+ }
378
+ }
379
+ Self { previous }
380
+ }
381
+ }
382
+
383
+ impl Drop for EnvGuard {
384
+ fn drop(&mut self) {
385
+ for (key, value) in self.previous.drain(..).rev() {
386
+ unsafe {
387
+ if let Some(value) = value {
388
+ std::env::set_var(key, value);
389
+ } else {
390
+ std::env::remove_var(key);
391
+ }
392
+ }
393
+ }
394
+ }
395
+ }