@team-agent/installer 0.2.11 → 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 -119
  203. package/src/team_agent/coordinator/lifecycle.py +0 -411
  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 -218
  226. package/src/team_agent/idle_takeover.py +0 -59
  227. package/src/team_agent/idle_takeover_wiring.py +0 -114
  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 -262
  255. package/src/team_agent/messaging/delivery.py +0 -504
  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 -503
  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 -91
  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 -1243
  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 -144
  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 -693
  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,357 @@
1
+ use super::*;
2
+
3
+
4
+ // ════════════════════════════════════════════════════════════════════════
5
+ // GROUP A — serde byte-locks (audit/event wire values; changing one byte
6
+ // breaks downstream recognizers/event consumers). delivery.py / send.py /
7
+ // leader.py / scheduler.py / diagnose/comms.py.
8
+ // ════════════════════════════════════════════════════════════════════════
9
+
10
+ #[test]
11
+ fn delivery_status_serde_snake_case_byte_locked() {
12
+ let cases = [
13
+ (DeliveryStatus::Delivered, "\"delivered\""),
14
+ (DeliveryStatus::Failed, "\"failed\""),
15
+ (DeliveryStatus::Queued, "\"queued\""),
16
+ (DeliveryStatus::Blocked, "\"blocked\""),
17
+ (DeliveryStatus::Refused, "\"refused\""),
18
+ (DeliveryStatus::RetryScheduled, "\"retry_scheduled\""),
19
+ (
20
+ DeliveryStatus::TrustAutoAnswerExhausted,
21
+ "\"trust_auto_answer_exhausted\"",
22
+ ),
23
+ (DeliveryStatus::AlreadyDelivered, "\"already_delivered\""),
24
+ (DeliveryStatus::FallbackLog, "\"fallback_log\""),
25
+ (DeliveryStatus::BroadcastDelivered, "\"broadcast_delivered\""),
26
+ (DeliveryStatus::BroadcastPartial, "\"broadcast_partial\""),
27
+ (DeliveryStatus::FanoutDelivered, "\"fanout_delivered\""),
28
+ (DeliveryStatus::FanoutPartial, "\"fanout_partial\""),
29
+ ];
30
+ for (variant, wire) in cases {
31
+ assert_eq!(serde_json::to_string(&variant).unwrap(), wire);
32
+ }
33
+ }
34
+
35
+ #[test]
36
+ fn delivery_refusal_serde_snake_case_byte_locked() {
37
+ let cases = [
38
+ (DeliveryRefusal::TargetNotInTeam, "\"target_not_in_team\""),
39
+ (
40
+ DeliveryRefusal::HumanConfirmationRequired,
41
+ "\"human_confirmation_required\"",
42
+ ),
43
+ (DeliveryRefusal::MissingPermissions, "\"missing_permissions\""),
44
+ (DeliveryRefusal::RecipientBusy, "\"recipient_busy\""),
45
+ (DeliveryRefusal::UnknownRecipient, "\"unknown_recipient\""),
46
+ (DeliveryRefusal::TmuxTargetMissing, "\"tmux_target_missing\""),
47
+ (
48
+ DeliveryRefusal::MessageAlreadyClaimed,
49
+ "\"message_already_claimed\"",
50
+ ),
51
+ (DeliveryRefusal::LeaderNotAttached, "\"leader_not_attached\""),
52
+ (DeliveryRefusal::TeamOwnerMismatch, "\"team_owner_mismatch\""),
53
+ (DeliveryRefusal::Ambiguous, "\"ambiguous\""),
54
+ (
55
+ DeliveryRefusal::RecipientPaneInNonInputMode,
56
+ "\"recipient_pane_in_non_input_mode\"",
57
+ ),
58
+ (DeliveryRefusal::SessionDrift, "\"session_drift\""),
59
+ ];
60
+ for (variant, wire) in cases {
61
+ assert_eq!(serde_json::to_string(&variant).unwrap(), wire);
62
+ }
63
+ }
64
+
65
+ #[test]
66
+ fn delivery_stage_serde_snake_case_byte_locked() {
67
+ // delivery.py:309 injection.stage values.
68
+ assert_eq!(
69
+ serde_json::to_string(&DeliveryStage::TrustAutoAnswerDismissalWait).unwrap(),
70
+ "\"trust_auto_answer_dismissal_wait\""
71
+ );
72
+ assert_eq!(
73
+ serde_json::to_string(&DeliveryStage::Inject).unwrap(),
74
+ "\"inject\""
75
+ );
76
+ assert_eq!(
77
+ serde_json::to_string(&DeliveryStage::Submit).unwrap(),
78
+ "\"submit\""
79
+ );
80
+ assert_eq!(
81
+ serde_json::to_string(&DeliveryStage::VisibleCheck).unwrap(),
82
+ "\"visible_check\""
83
+ );
84
+ }
85
+
86
+ #[test]
87
+ fn scheduled_kind_serde_byte_locked() {
88
+ // scheduler.py:46,84,87 dispatch keys — exhaustive, no runtime fallback.
89
+ assert_eq!(
90
+ serde_json::to_string(&ScheduledKind::Send).unwrap(),
91
+ "\"send\""
92
+ );
93
+ assert_eq!(
94
+ serde_json::to_string(&ScheduledKind::HealthPing).unwrap(),
95
+ "\"health_ping\""
96
+ );
97
+ assert_eq!(
98
+ serde_json::to_string(&ScheduledKind::TrustRetry).unwrap(),
99
+ "\"trust_retry\""
100
+ );
101
+ }
102
+
103
+ #[test]
104
+ fn activity_status_serde_byte_locked() {
105
+ // activity_detector.py:107 status values consumed by ping/take-over.
106
+ assert_eq!(
107
+ serde_json::to_string(&ActivityStatus::Working).unwrap(),
108
+ "\"working\""
109
+ );
110
+ assert_eq!(
111
+ serde_json::to_string(&ActivityStatus::Idle).unwrap(),
112
+ "\"idle\""
113
+ );
114
+ assert_eq!(
115
+ serde_json::to_string(&ActivityStatus::Stuck).unwrap(),
116
+ "\"stuck\""
117
+ );
118
+ assert_eq!(
119
+ serde_json::to_string(&ActivityStatus::Uncertain).unwrap(),
120
+ "\"uncertain\""
121
+ );
122
+ }
123
+
124
+ #[test]
125
+ fn alert_type_serde_byte_locked() {
126
+ // scheduler.py:38 _ALERT_TYPES set members.
127
+ assert_eq!(
128
+ serde_json::to_string(&AlertType::Stuck).unwrap(),
129
+ "\"stuck\""
130
+ );
131
+ assert_eq!(
132
+ serde_json::to_string(&AlertType::IdleFallback).unwrap(),
133
+ "\"idle_fallback\""
134
+ );
135
+ assert_eq!(
136
+ serde_json::to_string(&AlertType::CrossWorkerDeadlock).unwrap(),
137
+ "\"cross_worker_deadlock\""
138
+ );
139
+ }
140
+
141
+ #[test]
142
+ fn check_status_serde_byte_locked() {
143
+ assert_eq!(
144
+ serde_json::to_string(&CheckStatus::Pass).unwrap(),
145
+ "\"pass\""
146
+ );
147
+ assert_eq!(
148
+ serde_json::to_string(&CheckStatus::Fail).unwrap(),
149
+ "\"fail\""
150
+ );
151
+ assert_eq!(
152
+ serde_json::to_string(&CheckStatus::Deferred).unwrap(),
153
+ "\"deferred\""
154
+ );
155
+ assert_eq!(
156
+ serde_json::to_string(&CheckStatus::NotImplemented).unwrap(),
157
+ "\"not_implemented\""
158
+ );
159
+ assert_eq!(
160
+ serde_json::to_string(&CheckStatus::NotChallenged).unwrap(),
161
+ "\"not_challenged\""
162
+ );
163
+ }
164
+
165
+ #[test]
166
+ fn check_kind_serde_byte_locked() {
167
+ // diagnose/comms.py:121,149 verifies values.
168
+ assert_eq!(
169
+ serde_json::to_string(&CheckKind::ReceiverBinding).unwrap(),
170
+ "\"receiver_binding\""
171
+ );
172
+ assert_eq!(
173
+ serde_json::to_string(&CheckKind::ContractSuite).unwrap(),
174
+ "\"contract_suite\""
175
+ );
176
+ assert_eq!(
177
+ serde_json::to_string(&CheckKind::NoProviderSdkCalls).unwrap(),
178
+ "\"no_provider_sdk_calls\""
179
+ );
180
+ }
181
+
182
+ #[test]
183
+ fn receiver_mode_serde_byte_locked() {
184
+ // leader.py:103,166 — only direct_tmux is a legal receiver mode.
185
+ assert_eq!(
186
+ serde_json::to_string(&ReceiverMode::DirectTmux).unwrap(),
187
+ "\"direct_tmux\""
188
+ );
189
+ }
190
+
191
+ // ════════════════════════════════════════════════════════════════════════
192
+ // GROUP B — bounded-retry constants + backoff (delivery.py:60-61,
193
+ // scheduler.py:134 / results.py:251, result_delivery.py:15). Locks the
194
+ // termination-guarantee so no infinite-loop regression.
195
+ // ════════════════════════════════════════════════════════════════════════
196
+
197
+ #[test]
198
+ fn trust_retry_max_attempts_is_four() {
199
+ assert_eq!(TRUST_RETRY_MAX_ATTEMPTS, 4);
200
+ }
201
+
202
+ #[test]
203
+ fn trust_retry_backoff_table_byte_locked() {
204
+ // delivery.py:60 _TRUST_RETRY_BACKOFF_SECONDS = {2:5, 3:15, 4:30}.
205
+ assert_eq!(TRUST_RETRY_BACKOFF_SECONDS, &[(2, 5), (3, 15), (4, 30)]);
206
+ }
207
+
208
+ #[test]
209
+ fn send_retry_max_attempts_is_three() {
210
+ // scheduler.py:134 / results.py:251 max_attempts = 3.
211
+ assert_eq!(SEND_RETRY_MAX_ATTEMPTS, 3);
212
+ }
213
+
214
+ #[test]
215
+ fn result_delivery_max_attempts_is_five() {
216
+ // result_delivery.py:15 _RESULT_DELIVERY_MAX_ATTEMPTS = 5.
217
+ assert_eq!(RESULT_DELIVERY_MAX_ATTEMPTS, 5);
218
+ }
219
+
220
+ // ════════════════════════════════════════════════════════════════════════
221
+ // GROUP C — pure-value helper impls already in the skeleton: the IRON LAW.
222
+ // ProviderSdkCalls::is_zero is implemented (GREEN check); the gate fns are
223
+ // unimplemented!() so they are RED until ported.
224
+ // ════════════════════════════════════════════════════════════════════════
225
+
226
+ #[test]
227
+ fn provider_sdk_calls_is_zero_only_when_all_three_zero() {
228
+ // diagnose/comms.py:148 `any(calls.values())` negated.
229
+ assert!(ProviderSdkCalls::default().is_zero());
230
+ assert!(ProviderSdkCalls {
231
+ anthropic: 0,
232
+ openai: 0,
233
+ httpx: 0
234
+ }
235
+ .is_zero());
236
+ assert!(!ProviderSdkCalls {
237
+ anthropic: 1,
238
+ openai: 0,
239
+ httpx: 0
240
+ }
241
+ .is_zero());
242
+ assert!(!ProviderSdkCalls {
243
+ anthropic: 0,
244
+ openai: 0,
245
+ httpx: 9
246
+ }
247
+ .is_zero());
248
+ }
249
+
250
+ #[test]
251
+ fn alert_type_all_is_sorted_full_set() {
252
+ // scheduler.py:269 sorted(_ALERT_TYPES) =
253
+ // ['cross_worker_deadlock','idle_fallback','stuck'].
254
+ assert_eq!(
255
+ AlertType::all(),
256
+ [
257
+ AlertType::CrossWorkerDeadlock,
258
+ AlertType::IdleFallback,
259
+ AlertType::Stuck
260
+ ]
261
+ );
262
+ }
263
+
264
+ #[test]
265
+ fn activity_status_idle_takeover_gate_uncertain_never_idle() {
266
+ // THE IRON LAW (bug-071/077/085): only Idle passes; Uncertain/Working/Stuck
267
+ // are explicitly blocked — Uncertain must NOT fall through to idle.
268
+ assert!(ActivityStatus::Idle.allows_idle_takeover());
269
+ assert!(!ActivityStatus::Uncertain.allows_idle_takeover());
270
+ assert!(!ActivityStatus::Working.allows_idle_takeover());
271
+ assert!(!ActivityStatus::Stuck.allows_idle_takeover());
272
+ }
273
+
274
+ // ════════════════════════════════════════════════════════════════════════
275
+ // GROUP D — result_delivery.py pure fns: format/parse dual + KEY INSERTION
276
+ // ORDER + None-vs-empty + watcher matching. Byte-level golden (probed).
277
+ // ════════════════════════════════════════════════════════════════════════
278
+
279
+ #[test]
280
+ fn format_result_watcher_notification_full_shape_byte_locked() {
281
+ // result_delivery.py:521 — with tests + result_id. INSERTION ORDER matters:
282
+ // result_id inserted at idx 1 first, THEN tests inserted at idx 1 → Tests line
283
+ // ends up ABOVE the Result id line. Tests capped at 3.
284
+ let result = json(serde_json::json!({
285
+ "task_id": "t1", "agent_id": "alice", "status": "success", "summary": "done",
286
+ "result_id": "res-99",
287
+ "tests": [
288
+ {"command": "pytest", "status": "passed"},
289
+ {"command": "lint", "status": "failed"},
290
+ {"command": "a", "status": "x"},
291
+ {"command": "extra", "status": "skip"}
292
+ ]
293
+ }));
294
+ assert_eq!(
295
+ format_result_watcher_notification(&result),
296
+ "Task t1 reported success from alice: done\n\
297
+ Tests: pytest=passed; lint=failed; a=x\n\
298
+ Result id: res-99\n\
299
+ Team Agent has collected this result and updated team_state.md. No manual polling is needed."
300
+ );
301
+ }
302
+
303
+ #[test]
304
+ fn format_result_watcher_notification_minimal_uses_defaults() {
305
+ // Empty result → unknown-task / unknown / unknown agent / completed defaults,
306
+ // NO Tests line, NO Result id line.
307
+ assert_eq!(
308
+ format_result_watcher_notification(&json(serde_json::json!({}))),
309
+ "Task unknown task reported unknown from unknown agent: completed\n\
310
+ Team Agent has collected this result and updated team_state.md. No manual polling is needed."
311
+ );
312
+ }
313
+
314
+ #[test]
315
+ fn format_result_watcher_notification_result_id_only_no_tests() {
316
+ let result = json(serde_json::json!({
317
+ "task_id": "t1", "agent_id": "bob", "status": "blocked", "summary": "s",
318
+ "result_id": "R1"
319
+ }));
320
+ assert_eq!(
321
+ format_result_watcher_notification(&result),
322
+ "Task t1 reported blocked from bob: s\n\
323
+ Result id: R1\n\
324
+ Team Agent has collected this result and updated team_state.md. No manual polling is needed."
325
+ );
326
+ }
327
+
328
+ #[test]
329
+ fn result_id_from_text_roundtrips_with_formatter() {
330
+ // result_delivery.py:415 — dual of the formatter (content-level dedupe key).
331
+ let result = json(serde_json::json!({
332
+ "task_id": "t1", "agent_id": "alice", "status": "success",
333
+ "summary": "done", "result_id": "res-99"
334
+ }));
335
+ let rendered = format_result_watcher_notification(&result);
336
+ assert_eq!(result_id_from_text(&rendered), Some("res-99".to_string()));
337
+ }
338
+
339
+ #[test]
340
+ fn result_id_from_text_none_when_absent() {
341
+ assert_eq!(result_id_from_text("no id here\nplain"), None);
342
+ }
343
+
344
+ #[test]
345
+ fn result_id_from_text_empty_value_is_none_not_empty_string() {
346
+ // "Result id: " with empty payload → None (the `or None` after strip).
347
+ assert_eq!(result_id_from_text("Result id: \nx"), None);
348
+ }
349
+
350
+ #[test]
351
+ fn result_id_from_text_strips_trailing_whitespace() {
352
+ assert_eq!(
353
+ result_id_from_text("Result id: abc \n"),
354
+ Some("abc".to_string())
355
+ );
356
+ }
357
+
@@ -0,0 +1,122 @@
1
+ use super::*;
2
+
3
+ // ── F1 [P1 byte-shape] — stuck_cancel suppression snapshot.delivered_message_ids must use golden's
4
+ // _DELIVERED_MESSAGE_STATUSES = {visible, submitted, delivered, acknowledged} (scheduler.py:27,376-383),
5
+ // sorted. The Rust delivered_message_ids (scheduler.rs:243) used {injected, visible, submitted,
6
+ // submitted_unverified, delivered} — the WRONG set (reviewer's explicit warning: don't copy the
7
+ // activity.rs deadlock query set): it EXCLUDES 'acknowledged' and INCLUDES 'injected'/'submitted_unverified'.
8
+ // (The snapshot also must carry BOTH assigned_task_ids AND delivered_message_ids — that structural half
9
+ // is already present; this pins the STATUS SET.)
10
+ #[test]
11
+ fn stuck_cancel_snapshot_delivered_message_ids_uses_golden_status_set() {
12
+ let ws = tmp_ws("f1_delivered");
13
+ crate::state::persist::save_runtime_state(
14
+ &ws,
15
+ &serde_json::json!({
16
+ "active_team_key": "teamX",
17
+ "agents": {"w1": {"status": "running", "provider": "codex"}}
18
+ }),
19
+ )
20
+ .unwrap();
21
+ let store = crate::message_store::MessageStore::open(&ws).unwrap();
22
+ // golden-delivered (must be IN delivered_message_ids): acknowledged + visible.
23
+ let m_ack = store.create_message(None, "leader", "w1", "ack-me", None, true, Some("teamX")).unwrap();
24
+ store.mark(&m_ack, "acknowledged", None).unwrap();
25
+ let m_vis = store.create_message(None, "leader", "w1", "vis", None, true, Some("teamX")).unwrap();
26
+ store.mark(&m_vis, "visible", None).unwrap();
27
+ // NOT golden-delivered (must be EXCLUDED): injected.
28
+ let m_inj = store.create_message(None, "leader", "w1", "inj", None, true, Some("teamX")).unwrap();
29
+ store.mark(&m_inj, "injected", None).unwrap();
30
+ drop(store);
31
+ let _ = stuck_cancel(&ws, "w1", None, "leader").unwrap();
32
+ let state = crate::state::persist::load_runtime_state(&ws).unwrap();
33
+ let snapshot = &state["coordinator"]["suppressed_idle_alerts"]["teamX"]["w1"]["stuck"]["snapshot"];
34
+ assert!(snapshot.get("assigned_task_ids").is_some(), "F1: snapshot must carry assigned_task_ids; got {snapshot}");
35
+ let delivered = snapshot
36
+ .get("delivered_message_ids")
37
+ .and_then(serde_json::Value::as_array)
38
+ .map(|a| a.iter().filter_map(|v| v.as_str().map(str::to_string)).collect::<Vec<_>>())
39
+ .unwrap_or_else(|| panic!("F1: snapshot must carry delivered_message_ids; got {snapshot}"));
40
+ assert!(
41
+ delivered.contains(&m_ack),
42
+ "F1: golden _DELIVERED_MESSAGE_STATUSES includes 'acknowledged' — the acknowledged message must be \
43
+ in delivered_message_ids; the Rust set omits it. got {delivered:?}"
44
+ );
45
+ assert!(delivered.contains(&m_vis), "F1: 'visible' is delivered (golden); got {delivered:?}");
46
+ assert!(
47
+ !delivered.contains(&m_inj),
48
+ "F1: 'injected' is NOT in golden _DELIVERED_MESSAGE_STATUSES — it must be EXCLUDED; the Rust set \
49
+ wrongly includes it. got {delivered:?}"
50
+ );
51
+ let mut sorted = delivered.clone();
52
+ sorted.sort();
53
+ assert_eq!(delivered, sorted, "F1: delivered_message_ids must be sorted (golden sorts); got {delivered:?}");
54
+ }
55
+ // ── collect --result-file INGEST SEMANTIC (golden results.py:58-73) ──────────────────────────────
56
+ // Golden `collect(result_file=…)` INDEPENDENTLY INGESTS a standalone result: a valid result_envelope_v1
57
+ // is `store.add_result(envelope)`'d (results.py:73) regardless of any in-flight delivery, then the
58
+ // collection loop collects it when its task_id is a known task (state.tasks) OR message-scoped (msg_+
59
+ // matching message). NO live in-flight task is required at ingest time. rt-host-b @ c262bf7 saw a
60
+ // VALID envelope (validate-result accepts the same) collect to exit-1 / empty output / NOT in the
61
+ // results table — i.e. the --result-file ingest path was a no-op (results.rs once did
62
+ // `let _ = (result_file, …)`). This pins the happy path: a valid envelope for a KNOWN task must be
63
+ // ingested into the results table AND collected, with ok=true. (Completes the previously-deferred
64
+ // collected_results golden — the fixture is built from the real compiler + state persistence.)
65
+ #[test]
66
+ fn collect_result_file_ingests_valid_known_task_envelope_into_results() {
67
+ let team = tmp_ws("collect_rf");
68
+ std::fs::create_dir_all(team.join("agents")).unwrap();
69
+ std::fs::write(
70
+ team.join("TEAM.md"),
71
+ "---\nname: ct\nobjective: collect --result-file probe.\nprovider: codex\n---\n\nteam.\n",
72
+ )
73
+ .unwrap();
74
+ std::fs::write(
75
+ team.join("agents").join("w1.md"),
76
+ "---\nname: w1\nrole: Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nW1.\n",
77
+ )
78
+ .unwrap();
79
+ let spec = crate::compiler::compile_team(&team).expect("compile collect team");
80
+ std::fs::write(team.join("team.spec.yaml"), crate::model::yaml::dumps(&spec)).unwrap();
81
+ // seed runtime state with a KNOWN task "task-1" so the collection loop accepts the ingested result.
82
+ crate::state::persist::save_runtime_state(
83
+ &team,
84
+ &serde_json::json!({
85
+ "spec_path": team.join("team.spec.yaml").to_string_lossy(),
86
+ "agents": {"w1": {"status": "running", "provider": "codex"}},
87
+ "tasks": [{"id": "task-1", "title": "t", "type": "impl", "assignee": "w1",
88
+ "deps": [], "acceptance": "x", "status": "pending"}]
89
+ }),
90
+ )
91
+ .unwrap();
92
+ // a schema-valid result_envelope_v1 (validate_result_envelope accepts it) for task-1.
93
+ let envelope = serde_json::json!({
94
+ "schema_version": "result_envelope_v1", "task_id": "task-1", "agent_id": "w1",
95
+ "status": "success", "summary": "done",
96
+ "changes": [], "tests": [], "risks": [], "artifacts": [], "next_actions": []
97
+ });
98
+ let rf = team.join("result.json");
99
+ std::fs::write(&rf, serde_json::to_string(&envelope).unwrap()).unwrap();
100
+ let out = collect(&team, Some(rf.as_path()), false)
101
+ .expect("collect with a valid --result-file must not error");
102
+ // golden: a valid standalone envelope is ingested + collected, ok=true (NOT a silent exit-1).
103
+ assert_eq!(out["ok"], serde_json::json!(true), "valid --result-file collect must be ok:true; got {out}");
104
+ let collected = out["collected_results"].as_array().expect("collected_results array");
105
+ assert_eq!(
106
+ collected.len(),
107
+ 1,
108
+ "golden: the --result-file envelope must be INGESTED (store.add_result) then collected; a no-op \
109
+ ingest leaves collected_results=[]. got {out}"
110
+ );
111
+ assert_eq!(collected[0]["task_id"], serde_json::json!("task-1"));
112
+ assert_eq!(collected[0]["agent_id"], serde_json::json!("w1"));
113
+ assert_eq!(collected[0]["status"], serde_json::json!("success"));
114
+ // the result is actually IN the results table (counts reflect it) — proves real ingestion.
115
+ assert_eq!(
116
+ out["results"]["total"], serde_json::json!(1),
117
+ "the ingested result must persist in the results table (counts.total=1); a no-op ingest yields 0. got {}",
118
+ out["results"]
119
+ );
120
+ assert_eq!(out["results"]["collected"], serde_json::json!(1), "the ingested result must be marked collected; got {}", out["results"]);
121
+ let _ = std::fs::remove_dir_all(&team);
122
+ }