@team-agent/installer 0.3.13 → 0.3.14

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.
@@ -121,12 +121,23 @@ fn quick_start_teamdir_under_dot_team_uses_project_workspace_for_status_and_coll
121
121
 
122
122
  let transport = OfflineTransport::new();
123
123
  let result = quick_start_with_transport(&team, None, true, true, None, &transport);
124
- assert!(matches!(result, Ok(QuickStartReport::Ready { .. })), "quick-start failed: {result:?}");
124
+ assert!(
125
+ matches!(result, Ok(QuickStartReport::Ready { .. })),
126
+ "quick-start failed: {result:?}"
127
+ );
125
128
 
126
129
  let state_path = crate::state::persist::runtime_state_path(&workspace);
127
- assert!(state_path.exists(), "quick-start .team/current must persist runtime state under project root");
128
130
  assert!(
129
- !workspace.join(".team").join(".team").join("runtime").join("state.json").exists(),
131
+ state_path.exists(),
132
+ "quick-start .team/current must persist runtime state under project root"
133
+ );
134
+ assert!(
135
+ !workspace
136
+ .join(".team")
137
+ .join(".team")
138
+ .join("runtime")
139
+ .join("state.json")
140
+ .exists(),
130
141
  "quick-start .team/current must not create nested .team/.team runtime state"
131
142
  );
132
143
 
@@ -137,11 +148,21 @@ fn quick_start_teamdir_under_dot_team_uses_project_workspace_for_status_and_coll
137
148
  crate::state::selector::SelectorMode::RuntimeOnly,
138
149
  )
139
150
  .expect("status/collect selector should resolve project root");
140
- assert_eq!(selected.run_workspace, workspace, "input={}", input.display());
151
+ assert_eq!(
152
+ selected.run_workspace,
153
+ workspace,
154
+ "input={}",
155
+ input.display()
156
+ );
141
157
  // E5: selector resolves spec_path to .team/runtime/<team_key>/ (team_key="current").
142
158
  let expected_spec = crate::model::paths::runtime_spec_path(&workspace, "current");
143
159
  assert_eq!(
144
- selected.spec_path.as_deref().map(std::fs::canonicalize).transpose().unwrap(),
160
+ selected
161
+ .spec_path
162
+ .as_deref()
163
+ .map(std::fs::canonicalize)
164
+ .transpose()
165
+ .unwrap(),
145
166
  Some(std::fs::canonicalize(&expected_spec).unwrap()),
146
167
  "input={}",
147
168
  input.display()
@@ -155,14 +176,20 @@ fn quick_start_teamdir_under_dot_team_uses_project_workspace_for_status_and_coll
155
176
  summary: false,
156
177
  json: true,
157
178
  });
158
- assert!(status.is_ok(), "status should normalize teamdir to project-root runtime state: {status:?}");
179
+ assert!(
180
+ status.is_ok(),
181
+ "status should normalize teamdir to project-root runtime state: {status:?}"
182
+ );
159
183
 
160
184
  let collect = crate::cli::cmd_collect(&crate::cli::CollectArgs {
161
185
  result_file: None,
162
186
  workspace: team.clone(),
163
187
  json: true,
164
188
  });
165
- assert!(collect.is_ok(), "collect should normalize teamdir to the same project-root state/spec: {collect:?}");
189
+ assert!(
190
+ collect.is_ok(),
191
+ "collect should normalize teamdir to the same project-root state/spec: {collect:?}"
192
+ );
166
193
  }
167
194
 
168
195
  // P0 — quick_start over an INVALID role doc (missing `provider`) must surface the REAL compile
@@ -222,7 +249,15 @@ fn launch_dry_run_resolves_real_plan_not_stub_error() {
222
249
  fn start_agent_proceeds_past_owner_gate_for_unowned_workspace() {
223
250
  let ws = unowned_running_ws();
224
251
  let transport = OfflineTransport::new();
225
- let result = start_agent_with_transport(&ws, &AgentId::new("w1"), false, false, true, None, &transport);
252
+ let result = start_agent_with_transport(
253
+ &ws,
254
+ &AgentId::new("w1"),
255
+ false,
256
+ false,
257
+ true,
258
+ None,
259
+ &transport,
260
+ );
226
261
  assert!(
227
262
  !matches!(result, Err(LifecycleError::OwnerRefused(_))),
228
263
  "start_agent must reach the real chain for a no-owner workspace, not hard-refuse owner; got {result:?}"
@@ -292,7 +327,10 @@ fn fork_agent_proceeds_past_owner_gate_for_unowned_workspace() {
292
327
  fn quick_start_full_ready_real_spawn() {
293
328
  let team = quick_start_team_dir(QS_VALID_ROLE);
294
329
  let report = quick_start(&team, None, true, true, None).expect("quick_start launches the team");
295
- assert!(matches!(report, QuickStartReport::Ready { .. }), "got {report:?}");
330
+ assert!(
331
+ matches!(report, QuickStartReport::Ready { .. }),
332
+ "got {report:?}"
333
+ );
296
334
  }
297
335
 
298
336
  // ═════════════════════════════════════════════════════════════════════════
@@ -375,7 +413,9 @@ fn compiled_spec_path_with_paused_agent(team: &std::path::Path) -> PathBuf {
375
413
  _ => None,
376
414
  })
377
415
  .expect("compiled spec must contain agents");
378
- let first = agents.first_mut().expect("compiled spec must contain an agent");
416
+ let first = agents
417
+ .first_mut()
418
+ .expect("compiled spec must contain an agent");
379
419
  let crate::model::yaml::Value::Map(agent) = first else {
380
420
  panic!("compiled agent must be a map");
381
421
  };
@@ -422,7 +462,10 @@ fn spine_launch_dry_run_safety_reflects_spec_dangerous() {
422
462
  let team = quick_start_team_dir_custom(QS_TEAM_MD_DANGEROUS, QS_VALID_ROLE);
423
463
  let spec_path = compiled_spec_path(&team);
424
464
  let report = launch(&spec_path, true, false, true).expect("dry_run launch");
425
- assert!(report.safety.enabled, "safety must reflect spec.runtime.dangerous_auto_approve=true");
465
+ assert!(
466
+ report.safety.enabled,
467
+ "safety must reflect spec.runtime.dangerous_auto_approve=true"
468
+ );
426
469
  assert_eq!(
427
470
  report.safety.source,
428
471
  DangerousApprovalSource::RuntimeConfig,
@@ -471,7 +514,14 @@ fn spine_add_agent_rejects_duplicate_agent_id() {
471
514
  let role = team.join("dup-role.md");
472
515
  std::fs::write(&role, QS_VALID_ROLE).unwrap(); // a role doc named 'implementer' = duplicate
473
516
  let transport = OfflineTransport::new();
474
- let result = add_agent_with_transport(&team, &AgentId::new("implementer"), &role, false, None, &transport);
517
+ let result = add_agent_with_transport(
518
+ &team,
519
+ &AgentId::new("implementer"),
520
+ &role,
521
+ false,
522
+ None,
523
+ &transport,
524
+ );
475
525
  let text = format!("{result:?}");
476
526
  assert!(
477
527
  text.contains("already exists"),
@@ -524,7 +574,7 @@ fn e5_add_agent_does_not_copy_role_into_platform_dir_and_injects_into_spec() {
524
574
  #[test]
525
575
  fn e5_restart_rebuilds_runtime_spec_from_role_docs() {
526
576
  let ws = restart_ws_two_resumable_workers(); // has TEAM.md + agents/{alpha,bravo}.md + state
527
- // Edit a role doc AFTER initial compile: change alpha's role text.
577
+ // Edit a role doc AFTER initial compile: change alpha's role text.
528
578
  std::fs::write(
529
579
  ws.join("agents").join("alpha.md"),
530
580
  "---\nname: alpha\nrole: RENAMED Alpha Role\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nAlpha edited.\n",
@@ -551,10 +601,13 @@ fn e5_restart_rebuilds_runtime_spec_from_role_docs() {
551
601
  #[test]
552
602
  fn red2_restart_finds_runtime_spec_not_missing_when_user_dir_has_no_spec() {
553
603
  let ws = restart_ws_two_resumable_workers(); // has TEAM.md + agents + state
554
- // Simulate spec-demote: remove any user-dir spec the fixture wrote; restart must still find
555
- // the runtime spec (rebuilt from role docs / read-order B), NOT report "missing spec".
604
+ // Simulate spec-demote: remove any user-dir spec the fixture wrote; restart must still find
605
+ // the runtime spec (rebuilt from role docs / read-order B), NOT report "missing spec".
556
606
  let _ = std::fs::remove_file(ws.join("team.spec.yaml"));
557
- assert!(!ws.join("team.spec.yaml").exists(), "user-dir spec removed (spec-demote condition)");
607
+ assert!(
608
+ !ws.join("team.spec.yaml").exists(),
609
+ "user-dir spec removed (spec-demote condition)"
610
+ );
558
611
  let transport = OfflineTransport::new();
559
612
  let result = restart_with_transport(&ws, false, None, &transport);
560
613
  // The gate must NOT short-circuit with "missing spec for restart" — restart must proceed
@@ -575,7 +628,10 @@ fn red2_restart_empty_workspace_still_reports_missing() {
575
628
  let result = restart_with_transport(&ws, false, None, &transport);
576
629
  let text = format!("{result:?}");
577
630
  assert!(
578
- text.contains("missing spec") || text.to_lowercase().contains("not found") || text.contains("no_local_team_context") || text.to_lowercase().contains("invalid workspace"),
631
+ text.contains("missing spec")
632
+ || text.to_lowercase().contains("not found")
633
+ || text.contains("no_local_team_context")
634
+ || text.to_lowercase().contains("invalid workspace"),
579
635
  "RED-2 negative: empty workspace restart must still error (missing/not-found); got {text}"
580
636
  );
581
637
  let _ = std::fs::remove_dir_all(&ws);
@@ -595,7 +651,11 @@ fn red2still_entry_gate_resolves_parent_dot_team_form_a() {
595
651
  let base = temp_ws();
596
652
  let team_dir = base.join("teamdir");
597
653
  std::fs::create_dir_all(team_dir.join("agents")).unwrap();
598
- std::fs::write(team_dir.join("TEAM.md"), "---\nname: teamdir\nobjective: o\nprovider: codex\n---\nt.\n").unwrap();
654
+ std::fs::write(
655
+ team_dir.join("TEAM.md"),
656
+ "---\nname: teamdir\nobjective: o\nprovider: codex\n---\nt.\n",
657
+ )
658
+ .unwrap();
599
659
  std::fs::write(team_dir.join("agents").join("w1.md"), QS_VALID_ROLE).unwrap();
600
660
  // .team lives in the PARENT (base), with a runtime spec — mirrors quick-start <dir> from base cwd.
601
661
  let spec = crate::compiler::compile_team(&team_dir).unwrap();
@@ -622,7 +682,10 @@ fn red2still_entry_gate_resolves_parent_dot_team_form_a() {
622
682
  );
623
683
  // Full restart on team_dir must NOT early-exit with missing spec.
624
684
  let transport = OfflineTransport::new();
625
- let text = format!("{:?}", restart_with_transport(&team_dir, false, None, &transport));
685
+ let text = format!(
686
+ "{:?}",
687
+ restart_with_transport(&team_dir, false, None, &transport)
688
+ );
626
689
  assert!(
627
690
  !text.contains("missing spec for restart") && !text.contains("active team spec not found"),
628
691
  "RED-2-STILL Form A: restart <team_dir> (.team in parent) must not report missing; got {text}"
@@ -635,7 +698,11 @@ fn red2still_entry_gate_same_dir_dot_team_form_b() {
635
698
  // Form B: .team in team_dir itself (cwd == team_dir at quick-start time).
636
699
  let team_dir = temp_ws();
637
700
  std::fs::create_dir_all(team_dir.join("agents")).unwrap();
638
- std::fs::write(team_dir.join("TEAM.md"), "---\nname: tb\nobjective: o\nprovider: codex\n---\nt.\n").unwrap();
701
+ std::fs::write(
702
+ team_dir.join("TEAM.md"),
703
+ "---\nname: tb\nobjective: o\nprovider: codex\n---\nt.\n",
704
+ )
705
+ .unwrap();
639
706
  std::fs::write(team_dir.join("agents").join("w1.md"), QS_VALID_ROLE).unwrap();
640
707
  let spec = crate::compiler::compile_team(&team_dir).unwrap();
641
708
  let runtime_spec = crate::model::paths::runtime_spec_path(&team_dir, "tb");
@@ -649,7 +716,10 @@ fn red2still_entry_gate_same_dir_dot_team_form_b() {
649
716
  )
650
717
  .unwrap();
651
718
  let transport = OfflineTransport::new();
652
- let text = format!("{:?}", restart_with_transport(&team_dir, false, None, &transport));
719
+ let text = format!(
720
+ "{:?}",
721
+ restart_with_transport(&team_dir, false, None, &transport)
722
+ );
653
723
  assert!(
654
724
  !text.contains("missing spec for restart") && !text.contains("active team spec not found"),
655
725
  "RED-2-STILL Form B: restart <team_dir> (.team in team_dir) must not report missing; got {text}"
@@ -703,7 +773,8 @@ fn e5_add_agent_resolves_team_dir_to_role_dir_when_runtime_spec_exists() {
703
773
  )
704
774
  .unwrap();
705
775
  let transport = OfflineTransport::new();
706
- let result = add_agent_with_transport(&team, &AgentId::new("w2"), &role, false, None, &transport);
776
+ let result =
777
+ add_agent_with_transport(&team, &AgentId::new("w2"), &role, false, None, &transport);
707
778
  // Core decoupling signature: must NOT fail because compile_team looked for TEAM.md/agents under
708
779
  // the runtime spec dir. Before the fix, team_dir=spec_workspace(runtime) → "missing TEAM.md".
709
780
  let text = format!("{result:?}");
@@ -825,15 +896,29 @@ fn launch_with_transport_records_one_spawn_per_agent_carrying_build_command() {
825
896
  "exactly one spawn per started agent; recorded={recorded:?} started={:?}",
826
897
  report.started
827
898
  );
828
- assert!(!report.started.is_empty(), "a real (non-dry-run) launch must spawn >=1 worker");
829
- assert!(!report.dry_run, "launch_with_transport(dry_run=false) must report dry_run=false");
830
- assert_eq!(recorded[0].0, "spawn_first", "the first worker uses new-session (spawn_first)");
831
899
  assert!(
832
- recorded.iter().any(|(_, argv)| argv.iter().any(|a| a == "codex")),
900
+ !report.started.is_empty(),
901
+ "a real (non-dry-run) launch must spawn >=1 worker"
902
+ );
903
+ assert!(
904
+ !report.dry_run,
905
+ "launch_with_transport(dry_run=false) must report dry_run=false"
906
+ );
907
+ assert_eq!(
908
+ recorded[0].0, "spawn_first",
909
+ "the first worker uses new-session (spawn_first)"
910
+ );
911
+ assert!(
912
+ recorded
913
+ .iter()
914
+ .any(|(_, argv)| argv.iter().any(|a| a == "codex")),
833
915
  "each spawn argv must carry the agent's provider build_command (codex); got {recorded:?}"
834
916
  );
835
917
  assert!(
836
- report.started.iter().any(|s| s.agent_id.as_str() == "implementer"),
918
+ report
919
+ .started
920
+ .iter()
921
+ .any(|s| s.agent_id.as_str() == "implementer"),
837
922
  "LaunchReport.started must list the compiled agent; got {:?}",
838
923
  report.started
839
924
  );
@@ -853,8 +938,17 @@ pub(super) fn seed_healthy_coordinator(workspace: &std::path::Path) {
853
938
  std::fs::create_dir_all(crate::model::paths::runtime_dir(workspace)).unwrap();
854
939
  let _ = crate::message_store::MessageStore::open(workspace).unwrap(); // create db schema (schema.ok)
855
940
  let me = crate::coordinator::Pid::new(std::process::id());
856
- crate::coordinator::write_coordinator_metadata(&wp, me, crate::coordinator::MetadataSource::Boot).unwrap();
857
- std::fs::write(crate::coordinator::coordinator_pid_path(&wp), me.to_string()).unwrap();
941
+ crate::coordinator::write_coordinator_metadata(
942
+ &wp,
943
+ me,
944
+ crate::coordinator::MetadataSource::Boot,
945
+ )
946
+ .unwrap();
947
+ std::fs::write(
948
+ crate::coordinator::coordinator_pid_path(&wp),
949
+ me.to_string(),
950
+ )
951
+ .unwrap();
858
952
  }
859
953
 
860
954
  // RED — quick_start_with_transport must drive the REAL spawn path: launch.dry_run==false, started
@@ -889,7 +983,10 @@ fn quick_start_with_transport_spawns_workers_not_dry_run() {
889
983
  launch.started
890
984
  );
891
985
  assert!(
892
- launch.started.iter().any(|s| s.agent_id.as_str() == "implementer"),
986
+ launch
987
+ .started
988
+ .iter()
989
+ .any(|s| s.agent_id.as_str() == "implementer"),
893
990
  "launch.started must list the compiled agent 'implementer'; got {:?}",
894
991
  launch.started
895
992
  );
@@ -899,9 +996,14 @@ fn quick_start_with_transport_spawns_workers_not_dry_run() {
899
996
  !recorded.is_empty(),
900
997
  "quick_start must drive the transport spawn path (>=1 spawn recorded); the dry-run bug records ZERO spawns"
901
998
  );
902
- assert_eq!(recorded[0].0, "spawn_first", "the first worker uses new-session (spawn_first); got {recorded:?}");
999
+ assert_eq!(
1000
+ recorded[0].0, "spawn_first",
1001
+ "the first worker uses new-session (spawn_first); got {recorded:?}"
1002
+ );
903
1003
  assert!(
904
- recorded.iter().any(|(_, argv)| argv.iter().any(|a| a == "codex")),
1004
+ recorded
1005
+ .iter()
1006
+ .any(|(_, argv)| argv.iter().any(|a| a == "codex")),
905
1007
  "the spawn argv must carry the agent's provider build_command (codex); got {recorded:?}"
906
1008
  );
907
1009
  }
@@ -917,7 +1019,10 @@ fn quick_start_fresh_ws_spawns_resident_tmux_and_coordinator() {
917
1019
  let report = quick_start(&team, None, true, true, None).expect("quick_start");
918
1020
  match report {
919
1021
  QuickStartReport::Ready { launch, .. } => {
920
- assert!(!launch.dry_run, "a real quick_start must spawn (not dry-run)");
1022
+ assert!(
1023
+ !launch.dry_run,
1024
+ "a real quick_start must spawn (not dry-run)"
1025
+ );
921
1026
  assert!(
922
1027
  !launch.started.is_empty(),
923
1028
  "a real quick_start must spawn >=1 worker into a live tmux session; got {:?}",
@@ -949,7 +1054,11 @@ pub(super) fn restart_ws_two_resumable_workers() -> PathBuf {
949
1054
  let bravo_rollout = ws.join("bravo-rollout.jsonl");
950
1055
  std::fs::write(&alpha_rollout, "{}\n").unwrap();
951
1056
  std::fs::write(&bravo_rollout, "{}\n").unwrap();
952
- std::fs::write(ws.join("TEAM.md"), "---\nname: restartteam\nobjective: Restart probe.\nprovider: codex\n---\n\nteam.\n").unwrap();
1057
+ std::fs::write(
1058
+ ws.join("TEAM.md"),
1059
+ "---\nname: restartteam\nobjective: Restart probe.\nprovider: codex\n---\n\nteam.\n",
1060
+ )
1061
+ .unwrap();
953
1062
  std::fs::write(ws.join("agents").join("alpha.md"), DELEG_ROLE_ALPHA).unwrap();
954
1063
  std::fs::write(ws.join("agents").join("bravo.md"), DELEG_ROLE_BRAVO).unwrap();
955
1064
  let spec = crate::compiler::compile_team(&ws).expect("compile 2-agent team");
@@ -1039,18 +1148,31 @@ fn restart_with_transport_spawns_resumable_workers_not_stub() {
1039
1148
 
1040
1149
  let ws = restart_ws_two_resumable_workers();
1041
1150
  let dead_after_first = OfflineTransport::new().with_session_absent_after_spawn_first();
1042
- let result =
1043
- restart_with_transport_with_readiness_deadline(&ws, false, None, &dead_after_first, Some(0));
1151
+ let result = restart_with_transport_with_readiness_deadline(
1152
+ &ws,
1153
+ false,
1154
+ None,
1155
+ &dead_after_first,
1156
+ Some(0),
1157
+ );
1044
1158
  let recorded = dead_after_first.spawn_records();
1045
1159
  assert_eq!(
1046
1160
  recorded.len(),
1047
- 2,
1048
- "if the session disappears after alpha, restart must isolate alpha and still try bravo; got result={result:?} recorded={recorded:?}"
1161
+ 1,
1162
+ "if a resumed session disappears after alpha, restart must fail fast before bravo; got result={result:?} recorded={recorded:?}"
1049
1163
  );
1050
1164
  assert!(
1051
1165
  recorded.iter().all(|(kind, _)| kind == "spawn_first"),
1052
1166
  "restart must not call spawn_into/new-window against a dead server; result={result:?} recorded={recorded:?}"
1053
1167
  );
1168
+ let result_debug = format!("{result:?}");
1169
+ let report = result.expect("resume integrity failure should return a typed failed report");
1170
+ let RestartReport::Failed { failed_agents, .. } = report else {
1171
+ panic!("resume integrity failure must not be Partial/Restarted; got {report:?}");
1172
+ };
1173
+ assert_eq!(failed_agents.len(), 1);
1174
+ assert_eq!(failed_agents[0].agent_id.as_str(), "alpha");
1175
+ assert_eq!(failed_agents[0].phase, "resume");
1054
1176
  assert!(
1055
1177
  dead_after_first.calls().iter().all(|call| *call != "kill_session"),
1056
1178
  "a single worker/session disappearance must not trigger another shared-session kill; calls={:?}",
@@ -1061,13 +1183,16 @@ fn restart_with_transport_spawns_resumable_workers_not_stub() {
1061
1183
  .iter()
1062
1184
  .find(|event| event.get("event").and_then(|v| v.as_str()) == Some("restart.agent_failed"))
1063
1185
  .expect("restart.agent_failed event for isolated alpha");
1064
- assert_eq!(failed.get("agent_id").and_then(|v| v.as_str()), Some("alpha"));
1186
+ assert_eq!(
1187
+ failed.get("agent_id").and_then(|v| v.as_str()),
1188
+ Some("alpha")
1189
+ );
1065
1190
  assert!(
1066
1191
  failed
1067
1192
  .get("error")
1068
1193
  .and_then(|v| v.as_str())
1069
1194
  .is_some_and(|error| error.contains("session_disappeared_after_spawn")),
1070
- "restart must report the first resumed agent/session disappearance explicitly; event={failed} result={result:?}"
1195
+ "restart must report the first resumed agent/session disappearance explicitly; event={failed} result={result_debug}"
1071
1196
  );
1072
1197
  }
1073
1198
 
@@ -1088,9 +1213,15 @@ fn restart_spawn_failure_isolated_to_partial_report() {
1088
1213
  else {
1089
1214
  panic!("single worker failure must return RestartReport::Partial; got {report:?}");
1090
1215
  };
1091
- assert!(coordinator_started, "partial restart still starts coordinator for successful agents");
1216
+ assert!(
1217
+ coordinator_started,
1218
+ "partial restart still starts coordinator for successful agents"
1219
+ );
1092
1220
  assert_eq!(
1093
- agents.iter().map(|agent| agent.agent_id.as_str()).collect::<Vec<_>>(),
1221
+ agents
1222
+ .iter()
1223
+ .map(|agent| agent.agent_id.as_str())
1224
+ .collect::<Vec<_>>(),
1094
1225
  vec!["alpha"]
1095
1226
  );
1096
1227
  assert_eq!(failed_agents.len(), 1);
@@ -1098,8 +1229,18 @@ fn restart_spawn_failure_isolated_to_partial_report() {
1098
1229
  assert_eq!(failed_agents[0].phase, "spawn");
1099
1230
 
1100
1231
  let state = crate::state::persist::load_runtime_state(&ws).unwrap();
1101
- assert_eq!(state.pointer("/agents/alpha/status").and_then(|v| v.as_str()), Some("running"));
1102
- assert_eq!(state.pointer("/agents/bravo/status").and_then(|v| v.as_str()), Some("failed"));
1232
+ assert_eq!(
1233
+ state
1234
+ .pointer("/agents/alpha/status")
1235
+ .and_then(|v| v.as_str()),
1236
+ Some("running")
1237
+ );
1238
+ assert_eq!(
1239
+ state
1240
+ .pointer("/agents/bravo/status")
1241
+ .and_then(|v| v.as_str()),
1242
+ Some("failed")
1243
+ );
1103
1244
  assert!(
1104
1245
  state
1105
1246
  .pointer("/agents/bravo/restart_error")
@@ -1113,7 +1254,10 @@ fn restart_spawn_failure_isolated_to_partial_report() {
1113
1254
  .iter()
1114
1255
  .find(|event| event.get("event").and_then(|v| v.as_str()) == Some("restart.completed"))
1115
1256
  .expect("restart.completed event for partial restart");
1116
- assert_eq!(completed.get("rc").and_then(|v| v.as_str()), Some("partial"));
1257
+ assert_eq!(
1258
+ completed.get("rc").and_then(|v| v.as_str()),
1259
+ Some("partial")
1260
+ );
1117
1261
  assert!(
1118
1262
  completed
1119
1263
  .get("successful_agents")
@@ -1201,10 +1345,14 @@ fn restart_times_out_when_spawned_worker_pane_is_not_addressable() {
1201
1345
  let events = crate::event_log::EventLog::new(&ws).tail(20).unwrap();
1202
1346
  let timeout = events
1203
1347
  .iter()
1204
- .find(|event| event.get("event").and_then(|v| v.as_str()) == Some("restart.readiness_timeout"))
1348
+ .find(|event| {
1349
+ event.get("event").and_then(|v| v.as_str()) == Some("restart.readiness_timeout")
1350
+ })
1205
1351
  .expect("restart.readiness_timeout event");
1206
1352
  assert_eq!(
1207
- timeout.get("worker_pane_addressable").and_then(|v| v.as_bool()),
1353
+ timeout
1354
+ .get("worker_pane_addressable")
1355
+ .and_then(|v| v.as_bool()),
1208
1356
  Some(false),
1209
1357
  "timeout event must carry the failed readiness condition: {timeout}"
1210
1358
  );
@@ -1230,7 +1378,15 @@ fn start_agent_with_transport_spawns_resume_not_stub() {
1230
1378
  seed_healthy_coordinator(&ws);
1231
1379
  let transport = OfflineTransport::new();
1232
1380
 
1233
- let _result = start_agent_with_transport(&ws, &AgentId::new("alpha"), false, false, false, None, &transport);
1381
+ let _result = start_agent_with_transport(
1382
+ &ws,
1383
+ &AgentId::new("alpha"),
1384
+ false,
1385
+ false,
1386
+ false,
1387
+ None,
1388
+ &transport,
1389
+ );
1234
1390
 
1235
1391
  let recorded = transport.spawn_records();
1236
1392
  assert_eq!(
@@ -1253,14 +1409,25 @@ fn start_agent_with_transport_spawns_resume_not_stub() {
1253
1409
  fn add_agent_with_transport_spawns_new_worker_not_stub() {
1254
1410
  let team = temp_ws().join("addteam");
1255
1411
  std::fs::create_dir_all(team.join("agents")).unwrap();
1256
- std::fs::write(team.join("TEAM.md"), "---\nname: addteam\nobjective: Add probe.\nprovider: codex\n---\n\nteam.\n").unwrap();
1412
+ std::fs::write(
1413
+ team.join("TEAM.md"),
1414
+ "---\nname: addteam\nobjective: Add probe.\nprovider: codex\n---\n\nteam.\n",
1415
+ )
1416
+ .unwrap();
1257
1417
  std::fs::write(team.join("agents").join("implementer.md"), QS_VALID_ROLE).unwrap(); // existing agent
1258
1418
  let role_file = team.join("worker2-role.md"); // OUTSIDE agents/ -> not a duplicate of an existing agent
1259
1419
  std::fs::write(&role_file, DELEG_ROLE_WORKER2).unwrap();
1260
1420
  seed_healthy_coordinator(&team);
1261
1421
  let transport = OfflineTransport::new();
1262
1422
 
1263
- let _result = add_agent_with_transport(&team, &AgentId::new("worker2"), &role_file, false, None, &transport);
1423
+ let _result = add_agent_with_transport(
1424
+ &team,
1425
+ &AgentId::new("worker2"),
1426
+ &role_file,
1427
+ false,
1428
+ None,
1429
+ &transport,
1430
+ );
1264
1431
 
1265
1432
  // (a) the recompiled spec was written (real subsystem step). E5: under .team/runtime/<team_key>/,
1266
1433
  // NOT the user team dir.
@@ -1306,7 +1473,9 @@ fn quick_start_seeds_tasks_key_from_compiled_spec() {
1306
1473
  let team = quick_start_team_dir(QS_VALID_ROLE);
1307
1474
  let transport = OfflineTransport::new();
1308
1475
  let _ = quick_start_with_transport(&team, None, true, true, None, &transport);
1309
- let workspace = team.parent().expect("team_workspace(<base>/teamdir) = <base>");
1476
+ let workspace = team
1477
+ .parent()
1478
+ .expect("team_workspace(<base>/teamdir) = <base>");
1310
1479
  let state = crate::state::persist::load_runtime_state(workspace)
1311
1480
  .expect("runtime state.json must exist after quick_start");
1312
1481
  // (b) the tasks KEY must always be present and a JSON array (golden launch/core.py:69 default []).
@@ -1318,10 +1487,14 @@ fn quick_start_seeds_tasks_key_from_compiled_spec() {
1318
1487
  state.as_object().map(|o| o.keys().cloned().collect::<Vec<_>>())
1319
1488
  ),
1320
1489
  };
1321
- let tasks = tasks.as_array().expect("`tasks` must be a JSON array (golden default [])");
1490
+ let tasks = tasks
1491
+ .as_array()
1492
+ .expect("`tasks` must be a JSON array (golden default [])");
1322
1493
  // (a) spec.tasks must be carried into runtime state — the doc compiler's default task id.
1323
1494
  assert!(
1324
- tasks.iter().any(|t| t.get("id").and_then(|v| v.as_str()) == Some("task_initial")),
1495
+ tasks
1496
+ .iter()
1497
+ .any(|t| t.get("id").and_then(|v| v.as_str()) == Some("task_initial")),
1325
1498
  "state.tasks must carry the compiled spec's task (id=task_initial); got {tasks:?}"
1326
1499
  );
1327
1500
  }
@@ -1355,11 +1528,18 @@ fn quick_start_state_seeds_spec_path_workspace_leader_display_backend() {
1355
1528
  let team = quick_start_team_dir(QS_VALID_ROLE);
1356
1529
  let transport = OfflineTransport::new();
1357
1530
  let _ = quick_start_with_transport(&team, None, true, true, None, &transport);
1358
- let workspace = team.parent().expect("team_workspace(<base>/teamdir) = <base>");
1531
+ let workspace = team
1532
+ .parent()
1533
+ .expect("team_workspace(<base>/teamdir) = <base>");
1359
1534
  // E5: spec_path in state now points to .team/runtime/<team_key>/, team_dir stays the user role dir.
1360
1535
  let spec_path = quick_start_runtime_spec_path(&team);
1361
1536
  let (raw, state) = raw_runtime_state(workspace);
1362
- let keys = state.as_object().expect("state root object").keys().cloned().collect::<Vec<_>>();
1537
+ let keys = state
1538
+ .as_object()
1539
+ .expect("state root object")
1540
+ .keys()
1541
+ .cloned()
1542
+ .collect::<Vec<_>>();
1363
1543
  assert_eq!(
1364
1544
  keys,
1365
1545
  vec![
@@ -1382,7 +1562,10 @@ fn quick_start_state_seeds_spec_path_workspace_leader_display_backend() {
1382
1562
  truth (no shadow copy on the root that callers could read across teams); \
1383
1563
  raw={raw}"
1384
1564
  );
1385
- assert_eq!(state["spec_path"], json!(spec_path.canonicalize().unwrap().to_string_lossy()));
1565
+ assert_eq!(
1566
+ state["spec_path"],
1567
+ json!(spec_path.canonicalize().unwrap().to_string_lossy())
1568
+ );
1386
1569
  assert_eq!(state["workspace"], json!(workspace.to_string_lossy()));
1387
1570
  assert_eq!(state["team_dir"], json!(team.to_string_lossy()));
1388
1571
  assert_eq!(state["session_name"], json!("team-quickteam"));
@@ -1392,13 +1575,17 @@ fn quick_start_state_seeds_spec_path_workspace_leader_display_backend() {
1392
1575
  "leader must be copied from compiled spec.leader"
1393
1576
  );
1394
1577
  assert!(
1395
- state["agents"].as_object().is_some_and(|agents| agents.contains_key("implementer")),
1578
+ state["agents"]
1579
+ .as_object()
1580
+ .is_some_and(|agents| agents.contains_key("implementer")),
1396
1581
  "existing agents value must remain seeded from compiled spec; got {:?}",
1397
1582
  state["agents"]
1398
1583
  );
1399
1584
  assert!(
1400
1585
  state["tasks"].as_array().is_some_and(|tasks| {
1401
- tasks.iter().any(|task| task.get("id").and_then(|id| id.as_str()) == Some("task_initial"))
1586
+ tasks
1587
+ .iter()
1588
+ .any(|task| task.get("id").and_then(|id| id.as_str()) == Some("task_initial"))
1402
1589
  }),
1403
1590
  "existing tasks value must remain seeded from compiled spec; got {:?}",
1404
1591
  state["tasks"]
@@ -1418,17 +1605,23 @@ fn quick_start_state_seeds_spec_path_workspace_leader_display_backend() {
1418
1605
  #[serial(env)]
1419
1606
  fn quick_start_running_agent_state_shape_after_spawn_is_golden() {
1420
1607
  const FIXED_SPAWNED_AT: &str = "2026-06-04T00:00:00+00:00";
1421
- let _clock_guard =
1422
- EnvVarGuard::set("TEAM_AGENT_TEST_FIXED_SPAWNED_AT", FIXED_SPAWNED_AT);
1608
+ let _clock_guard = EnvVarGuard::set("TEAM_AGENT_TEST_FIXED_SPAWNED_AT", FIXED_SPAWNED_AT);
1423
1609
  let team = quick_start_team_dir(QS_VALID_ROLE);
1424
1610
  let transport = OfflineTransport::new();
1425
1611
  let _ = quick_start_with_transport(&team, None, true, true, None, &transport);
1426
- let workspace = team.parent().expect("team_workspace(<base>/teamdir) = <base>");
1612
+ let workspace = team
1613
+ .parent()
1614
+ .expect("team_workspace(<base>/teamdir) = <base>");
1427
1615
  let (raw, state) = raw_runtime_state(workspace);
1428
1616
  let agent = state
1429
1617
  .pointer("/agents/implementer")
1430
1618
  .unwrap_or_else(|| panic!("implementer agent state missing; raw={raw}"));
1431
- let keys = agent.as_object().expect("agent state object").keys().cloned().collect::<Vec<_>>();
1619
+ let keys = agent
1620
+ .as_object()
1621
+ .expect("agent state object")
1622
+ .keys()
1623
+ .cloned()
1624
+ .collect::<Vec<_>>();
1432
1625
  assert_eq!(
1433
1626
  keys,
1434
1627
  vec![
@@ -1462,7 +1655,9 @@ fn quick_start_running_agent_state_shape_after_spawn_is_golden() {
1462
1655
  assert_eq!(agent["window"], json!("implementer"));
1463
1656
  assert_eq!(
1464
1657
  agent["mcp_config"],
1465
- json!(workspace.join(".team/runtime/mcp/implementer.json").to_string_lossy())
1658
+ json!(workspace
1659
+ .join(".team/runtime/mcp/implementer.json")
1660
+ .to_string_lossy())
1466
1661
  );
1467
1662
  assert_eq!(
1468
1663
  agent["permissions"],
@@ -1495,7 +1690,9 @@ fn quick_start_running_agent_state_shape_after_spawn_is_golden() {
1495
1690
  fn quick_start_paused_agent_state_is_paused_provider_only_and_not_spawned() {
1496
1691
  let team = quick_start_team_dir(QS_VALID_ROLE);
1497
1692
  let spec_path = compiled_spec_path_with_paused_agent(&team);
1498
- let workspace = team.parent().expect("team_workspace(<base>/teamdir) = <base>");
1693
+ let workspace = team
1694
+ .parent()
1695
+ .expect("team_workspace(<base>/teamdir) = <base>");
1499
1696
  crate::state::persist::save_runtime_state(
1500
1697
  workspace,
1501
1698
  &json!({
@@ -1519,7 +1716,12 @@ fn quick_start_paused_agent_state_is_paused_provider_only_and_not_spawned() {
1519
1716
  let agent = state
1520
1717
  .pointer("/agents/implementer")
1521
1718
  .unwrap_or_else(|| panic!("implementer agent state missing; raw={raw}"));
1522
- let keys = agent.as_object().expect("agent state object").keys().cloned().collect::<Vec<_>>();
1719
+ let keys = agent
1720
+ .as_object()
1721
+ .expect("agent state object")
1722
+ .keys()
1723
+ .cloned()
1724
+ .collect::<Vec<_>>();
1523
1725
  assert_eq!(
1524
1726
  keys,
1525
1727
  vec!["status", "provider"],
@@ -1041,6 +1041,11 @@ impl Transport for TmuxBackend {
1041
1041
  Ok(SetEnvOutcome::Applied)
1042
1042
  }
1043
1043
 
1044
+ fn kill_server(&self) -> Result<(), TransportError> {
1045
+ TmuxBackend::kill_server(self);
1046
+ Ok(())
1047
+ }
1048
+
1044
1049
  fn kill_session(&self, session: &SessionName) -> Result<(), TransportError> {
1045
1050
  let argv = self.tmux_argv(&[
1046
1051
  "tmux".to_string(),