@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
@@ -0,0 +1,394 @@
1
+ //! 0.5.61 communication-mode runtime-contract assembly RED.
2
+ //!
3
+ //! The observation boundary is the real quick-start command plan captured by a
4
+ //! transport. The test consumes `CommunicationMode::ALL`; it does not copy the
5
+ //! product templates. Only stable section ids and requirement-level semantic
6
+ //! markers are asserted.
7
+
8
+ #![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
9
+
10
+ #[path = "../../../tests/support/hermetic.rs"]
11
+ mod hermetic_guard;
12
+ #[allow(dead_code)]
13
+ fn _hermetic_boundary_marker(_: &hermetic_guard::HermeticTestEnv) {}
14
+
15
+ use std::collections::BTreeMap;
16
+ use std::path::{Path, PathBuf};
17
+ use std::sync::atomic::{AtomicU64, Ordering};
18
+ use std::sync::Mutex;
19
+
20
+ use team_agent::communication_mode::CommunicationMode;
21
+ use team_agent::lifecycle::quick_start_with_transport_in_workspace;
22
+ use team_agent::transport::{
23
+ AttachOutcome, BackendKind, CaptureRange, CapturedText, InjectPayload, InjectReport,
24
+ InjectStage, InjectVerification, Key, PaneField, PaneId, PaneInfo, SessionName, SetEnvOutcome,
25
+ SpawnResult, SubmitVerification, Target, Transport, TransportError, TurnVerification,
26
+ WindowName,
27
+ };
28
+
29
+ const CONTRACT_HEADING_PREFIX: &str = "# Team Agent communication contract: ";
30
+
31
+ #[test]
32
+ fn t06_each_official_mode_selects_one_runtime_contract_in_real_spawn_prompt() {
33
+ let root = fixture("official", None);
34
+ let transport = RecordingTransport::default();
35
+ quick_start_with_transport_in_workspace(
36
+ &root,
37
+ &root,
38
+ None,
39
+ true,
40
+ Some("communication-contract-red"),
41
+ &transport,
42
+ )
43
+ .expect("official modes must reach real worker command assembly");
44
+
45
+ let spawns = transport.spawns.lock().unwrap();
46
+ assert_eq!(
47
+ spawns.len(),
48
+ CommunicationMode::ALL.len(),
49
+ "T06: fixture must exercise every product-catalog mode exactly once"
50
+ );
51
+ for mode in CommunicationMode::ALL.iter().copied() {
52
+ let id = mode.as_str();
53
+ let spawn = spawns
54
+ .iter()
55
+ .find(|spawn| spawn.window.as_str().contains(id))
56
+ .unwrap_or_else(|| panic!("T06/{id}: no spawn recorded for product-catalog mode"));
57
+ let prompt = flag_value(&spawn.argv, "--append-system-prompt");
58
+ let own_heading = format!("{CONTRACT_HEADING_PREFIX}{id}");
59
+ assert!(
60
+ prompt.contains(&own_heading),
61
+ "T06/{id}: assembled launch prompt lacks the selected official contract section; expected structural heading {own_heading:?}; prompt={prompt:?}"
62
+ );
63
+ for other in CommunicationMode::ALL
64
+ .iter()
65
+ .copied()
66
+ .filter(|other| *other != mode)
67
+ {
68
+ let other_heading = format!("{CONTRACT_HEADING_PREFIX}{}", other.as_str());
69
+ assert!(
70
+ !prompt.contains(&other_heading),
71
+ "T06/{id}: assembled prompt contains mutually exclusive contract {other_heading:?}"
72
+ );
73
+ }
74
+ assert!(
75
+ prompt.contains(&format!("ROLE BODY SENTINEL {id}"))
76
+ && prompt.contains("Team Agent Teammate Runtime Contract")
77
+ && prompt.contains("report_result exactly once"),
78
+ "T06/{id}: mode selection must preserve persona body and common exact-once contract"
79
+ );
80
+ }
81
+ }
82
+
83
+ #[test]
84
+ fn t06_selected_templates_project_the_two_signed_communication_boundaries() {
85
+ let root = fixture("semantics", None);
86
+ let transport = RecordingTransport::default();
87
+ quick_start_with_transport_in_workspace(
88
+ &root,
89
+ &root,
90
+ None,
91
+ true,
92
+ Some("communication-contract-semantics"),
93
+ &transport,
94
+ )
95
+ .expect("official modes must assemble");
96
+ let spawns = transport.spawns.lock().unwrap();
97
+ let prompt = |mode: CommunicationMode| {
98
+ let id = mode.as_str();
99
+ let spawn = spawns
100
+ .iter()
101
+ .find(|spawn| spawn.window.as_str().contains(id))
102
+ .unwrap_or_else(|| panic!("T06/{id}: missing recorded spawn"));
103
+ flag_value(&spawn.argv, "--append-system-prompt")
104
+ };
105
+
106
+ let leader = prompt(CommunicationMode::LeaderCentric);
107
+ for marker in ["Progress", "blocker", "question"] {
108
+ assert!(
109
+ leader.contains(marker),
110
+ "T06/leader_centric: official template must retain {marker:?} guidance; prompt={leader:?}"
111
+ );
112
+ }
113
+
114
+ let orchestrated = prompt(CommunicationMode::Orchestrated);
115
+ for marker in ["declared channel", "task-related", "ACK"] {
116
+ assert!(
117
+ orchestrated.contains(marker),
118
+ "T06/orchestrated: official template must state the on-demand channel/response boundary via {marker:?}; prompt={orchestrated:?}"
119
+ );
120
+ }
121
+ }
122
+
123
+ #[test]
124
+ fn t07_final_spawn_prompt_does_not_leak_leader_centric_obligations_into_orchestrated() {
125
+ let root = fixture("negative-boundary", None);
126
+ let transport = RecordingTransport::default();
127
+ quick_start_with_transport_in_workspace(
128
+ &root,
129
+ &root,
130
+ None,
131
+ true,
132
+ Some("communication-contract-negative-boundary"),
133
+ &transport,
134
+ )
135
+ .expect("official modes must assemble");
136
+ let spawns = transport.spawns.lock().unwrap();
137
+ let orchestrated = spawns
138
+ .iter()
139
+ .find(|spawn| {
140
+ spawn
141
+ .window
142
+ .as_str()
143
+ .contains(CommunicationMode::Orchestrated.as_str())
144
+ })
145
+ .map(|spawn| flag_value(&spawn.argv, "--append-system-prompt"))
146
+ .expect("T07/orchestrated: missing recorded spawn");
147
+
148
+ for required in [
149
+ "All communication must go through Team Agent MCP tools.",
150
+ "If blocked or waiting, send_message to the leader. Do not wait silently.",
151
+ "report_result exactly once",
152
+ ] {
153
+ assert!(
154
+ orchestrated.contains(required),
155
+ "T07/orchestrated positive control: removing unconditional duties must preserve common MCP, blocked-reporting, and exact-once obligations; missing {required:?}; prompt={orchestrated:?}"
156
+ );
157
+ }
158
+
159
+ let default_root = default_fixture("leader-centric-positive-control");
160
+ let default_transport = RecordingTransport::default();
161
+ quick_start_with_transport_in_workspace(
162
+ &default_root,
163
+ &default_root,
164
+ None,
165
+ true,
166
+ Some("communication-contract-default-positive-control"),
167
+ &default_transport,
168
+ )
169
+ .expect("omitted communication_mode must assemble as leader_centric");
170
+ let default_spawns = default_transport.spawns.lock().unwrap();
171
+ let default_prompt = default_spawns
172
+ .first()
173
+ .map(|spawn| flag_value(&spawn.argv, "--append-system-prompt"))
174
+ .expect("T07/leader_centric positive control: missing recorded default spawn");
175
+ for required in [
176
+ "Progress, blockers, questions:",
177
+ "When you receive a message from the leader or a teammate, you MUST respond",
178
+ ] {
179
+ assert!(
180
+ default_prompt.contains(required),
181
+ "T07/leader_centric positive control: omitted mode must retain {required:?}; prompt={default_prompt:?}"
182
+ );
183
+ }
184
+
185
+ let leaked = [
186
+ "Progress, blockers, questions:",
187
+ "When you receive a message from the leader or a teammate, you MUST respond",
188
+ ]
189
+ .into_iter()
190
+ .filter(|forbidden| orchestrated.contains(forbidden))
191
+ .collect::<Vec<_>>();
192
+ assert!(
193
+ leaked.is_empty(),
194
+ "T07/orchestrated: assembled final prompt leaks unconditional leader_centric obligations {leaked:?}; prompt={orchestrated:?}"
195
+ );
196
+ }
197
+
198
+ #[test]
199
+ fn t06_third_mode_is_rejected_before_any_worker_command_is_assembled() {
200
+ let root = fixture("unknown", Some("synthetic_third_mode"));
201
+ let transport = RecordingTransport::default();
202
+ let error = quick_start_with_transport_in_workspace(
203
+ &root,
204
+ &root,
205
+ None,
206
+ true,
207
+ Some("communication-contract-unknown"),
208
+ &transport,
209
+ )
210
+ .expect_err("T06: a third runtime contract shape must fail closed before launch");
211
+ assert!(
212
+ error.to_string().contains("communication_mode")
213
+ && error.to_string().contains("synthetic_third_mode"),
214
+ "T06: rejection must identify the invalid communication mode; error={error}"
215
+ );
216
+ assert!(
217
+ transport.spawns.lock().unwrap().is_empty(),
218
+ "T06: invalid mode must not leave a partially launched roster"
219
+ );
220
+ }
221
+
222
+ fn fixture(tag: &str, team_mode: Option<&str>) -> PathBuf {
223
+ static NEXT: AtomicU64 = AtomicU64::new(0);
224
+ let base = std::env::var_os("TEAM_AGENT_TEST_TMP")
225
+ .map(PathBuf::from)
226
+ .unwrap_or_else(std::env::temp_dir);
227
+ let root = base.join(format!(
228
+ "communication-mode-runtime-red-{tag}-{}-{}",
229
+ std::process::id(),
230
+ NEXT.fetch_add(1, Ordering::Relaxed)
231
+ ));
232
+ let _ = std::fs::remove_dir_all(&root);
233
+ std::fs::create_dir_all(root.join("agents")).unwrap();
234
+ let team_mode = team_mode
235
+ .map(|mode| format!("communication_mode: {mode}\n"))
236
+ .unwrap_or_default();
237
+ std::fs::write(
238
+ root.join("TEAM.md"),
239
+ format!(
240
+ "---\nname: communication-mode-runtime-red\nprovider: claude\n{team_mode}---\n\nTeam fixture.\n"
241
+ ),
242
+ )
243
+ .unwrap();
244
+ for mode in CommunicationMode::ALL.iter().copied() {
245
+ let id = mode.as_str();
246
+ std::fs::write(
247
+ root.join("agents").join(format!("worker_{id}.md")),
248
+ format!(
249
+ "---\nname: worker_{id}\nrole: Communication Worker\nprovider: claude\ncommunication_mode: {id}\ntools:\n - mcp_team\n---\n\nROLE BODY SENTINEL {id}\n"
250
+ ),
251
+ )
252
+ .unwrap();
253
+ }
254
+ root
255
+ }
256
+
257
+ fn default_fixture(tag: &str) -> PathBuf {
258
+ let root = fixture(tag, None);
259
+ for mode in CommunicationMode::ALL {
260
+ std::fs::remove_file(
261
+ root.join("agents")
262
+ .join(format!("worker_{}.md", mode.as_str())),
263
+ )
264
+ .unwrap();
265
+ }
266
+ std::fs::write(
267
+ root.join("agents").join("worker_default.md"),
268
+ "---\nname: worker_default\nrole: Communication Worker\nprovider: claude\ntools:\n - mcp_team\n---\n\nROLE BODY SENTINEL default\n",
269
+ )
270
+ .unwrap();
271
+ root
272
+ }
273
+
274
+ fn flag_value(argv: &[String], flag: &str) -> String {
275
+ argv.windows(2)
276
+ .find(|pair| pair[0] == flag)
277
+ .map(|pair| pair[1].clone())
278
+ .unwrap_or_default()
279
+ }
280
+
281
+ #[derive(Debug, Clone)]
282
+ struct RecordedSpawn {
283
+ window: WindowName,
284
+ argv: Vec<String>,
285
+ }
286
+
287
+ #[derive(Debug, Default)]
288
+ struct RecordingTransport {
289
+ spawns: Mutex<Vec<RecordedSpawn>>,
290
+ }
291
+
292
+ impl Transport for RecordingTransport {
293
+ fn kind(&self) -> BackendKind {
294
+ BackendKind::Tmux
295
+ }
296
+
297
+ fn spawn_first(
298
+ &self,
299
+ session: &SessionName,
300
+ window: &WindowName,
301
+ argv: &[String],
302
+ cwd: &Path,
303
+ env: &BTreeMap<String, String>,
304
+ ) -> Result<SpawnResult, TransportError> {
305
+ self.spawn_into(session, window, argv, cwd, env)
306
+ }
307
+
308
+ fn spawn_into(
309
+ &self,
310
+ session: &SessionName,
311
+ window: &WindowName,
312
+ argv: &[String],
313
+ _cwd: &Path,
314
+ _env: &BTreeMap<String, String>,
315
+ ) -> Result<SpawnResult, TransportError> {
316
+ let mut spawns = self.spawns.lock().unwrap();
317
+ spawns.push(RecordedSpawn {
318
+ window: window.clone(),
319
+ argv: argv.to_vec(),
320
+ });
321
+ Ok(SpawnResult {
322
+ pane_id: PaneId::new(format!("%{}", spawns.len())),
323
+ session: session.clone(),
324
+ window: window.clone(),
325
+ child_pid: Some(30_000 + spawns.len() as u32),
326
+ })
327
+ }
328
+
329
+ fn inject(
330
+ &self,
331
+ _target: &Target,
332
+ _payload: &InjectPayload,
333
+ _submit: Key,
334
+ _bracketed: bool,
335
+ ) -> Result<InjectReport, TransportError> {
336
+ Ok(InjectReport {
337
+ stage_reached: InjectStage::Submit,
338
+ inject_verification: InjectVerification::CaptureContainsToken,
339
+ submit_verification: SubmitVerification::EnterSentWithoutPlaceholderCheck,
340
+ turn_verification: TurnVerification::NotYetObserved,
341
+ attempts: 1,
342
+ submit_diagnostics: None,
343
+ })
344
+ }
345
+
346
+ fn send_keys(&self, _target: &Target, _keys: &[Key]) -> Result<(), TransportError> {
347
+ Ok(())
348
+ }
349
+ fn capture(
350
+ &self,
351
+ _target: &Target,
352
+ range: CaptureRange,
353
+ ) -> Result<CapturedText, TransportError> {
354
+ Ok(CapturedText {
355
+ text: String::new(),
356
+ range,
357
+ })
358
+ }
359
+ fn query(&self, _target: &Target, field: PaneField) -> Result<Option<String>, TransportError> {
360
+ Ok((field == PaneField::PaneWidth).then(|| "120".to_string()))
361
+ }
362
+ fn liveness(
363
+ &self,
364
+ _pane: &PaneId,
365
+ ) -> Result<team_agent::transport::PaneLiveness, TransportError> {
366
+ Ok(team_agent::transport::PaneLiveness::Live)
367
+ }
368
+ fn list_targets(&self) -> Result<Vec<PaneInfo>, TransportError> {
369
+ Ok(Vec::new())
370
+ }
371
+ fn has_session(&self, _session: &SessionName) -> Result<bool, TransportError> {
372
+ Ok(false)
373
+ }
374
+ fn list_windows(&self, _session: &SessionName) -> Result<Vec<WindowName>, TransportError> {
375
+ Ok(Vec::new())
376
+ }
377
+ fn set_session_env(
378
+ &self,
379
+ _session: &SessionName,
380
+ _key: &str,
381
+ _value: &str,
382
+ ) -> Result<SetEnvOutcome, TransportError> {
383
+ Ok(SetEnvOutcome::Applied)
384
+ }
385
+ fn kill_session(&self, _session: &SessionName) -> Result<(), TransportError> {
386
+ Ok(())
387
+ }
388
+ fn kill_window(&self, _target: &Target) -> Result<(), TransportError> {
389
+ Ok(())
390
+ }
391
+ fn attach_session(&self, _session: &SessionName) -> Result<AttachOutcome, TransportError> {
392
+ Ok(AttachOutcome::Attached)
393
+ }
394
+ }