@team-agent/installer 0.5.64 → 0.5.66

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 (105) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/Cargo.toml +5 -0
  4. package/crates/team-agent/src/cli/adapters.rs +6 -1
  5. package/crates/team-agent/src/cli/emit.rs +15 -0
  6. package/crates/team-agent/src/cli/mod.rs +9 -8
  7. package/crates/team-agent/src/cli/send/presentation.rs +1 -0
  8. package/crates/team-agent/src/cli/spec.rs +3 -0
  9. package/crates/team-agent/src/cli/tests/compile.rs +1 -1
  10. package/crates/team-agent/src/cli/tests/mod.rs +33 -2
  11. package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -1
  12. package/crates/team-agent/src/cli/tests/status_send.rs +13 -3
  13. package/crates/team-agent/src/cli/tests/verb_validate.rs +1 -1
  14. package/crates/team-agent/src/cli/types.rs +7 -0
  15. package/crates/team-agent/src/compiler/tests.rs +23 -20
  16. package/crates/team-agent/src/compiler.rs +25 -5
  17. package/crates/team-agent/src/conpty/backend.rs +16 -0
  18. package/crates/team-agent/src/coordinator/health.rs +17 -0
  19. package/crates/team-agent/src/coordinator/steps/abnormal.rs +55 -2
  20. package/crates/team-agent/src/db/migration.rs +2 -1
  21. package/crates/team-agent/src/db/schema.rs +16 -8
  22. package/crates/team-agent/src/leader/lease.rs +1 -2
  23. package/crates/team-agent/src/leader/start.rs +175 -10
  24. package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1 -1
  25. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +0 -2
  26. package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +7 -2
  27. package/crates/team-agent/src/lifecycle/launch/agent_state.rs +137 -2
  28. package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +1 -2
  29. package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +5 -0
  30. package/crates/team-agent/src/lifecycle/launch/spawn.rs +39 -2
  31. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +105 -48
  32. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +0 -2
  33. package/crates/team-agent/src/lifecycle/launch.rs +117 -21
  34. package/crates/team-agent/src/lifecycle/restart/agent.rs +89 -3
  35. package/crates/team-agent/src/lifecycle/restart/common.rs +9 -12
  36. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +59 -15
  37. package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -1
  38. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +4 -4
  39. package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +1 -1
  40. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -2
  41. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +4 -4
  42. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +24 -32
  43. package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +2 -2
  44. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +13 -11
  45. package/crates/team-agent/src/lifecycle/tests/core.rs +0 -18
  46. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +1 -1
  47. package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +1 -1
  48. package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +1 -1
  49. package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -0
  50. package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +3 -3
  51. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +2 -2
  52. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +17 -30
  53. package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +1 -1
  54. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +4 -4
  55. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +1 -1
  56. package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +2 -2
  57. package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +1 -1
  58. package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +1 -1
  59. package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +1 -1
  60. package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -2
  61. package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +1 -1
  62. package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +1 -1
  63. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +11 -7
  64. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +3 -3
  65. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1 -1
  66. package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +1 -1
  67. package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -1
  68. package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1 -1
  69. package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1 -1
  70. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +1 -1
  71. package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +1 -1
  72. package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +1 -1
  73. package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -2
  74. package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +1 -1
  75. package/crates/team-agent/src/lifecycle/tests.rs +11 -7
  76. package/crates/team-agent/src/lifecycle/types.rs +0 -2
  77. package/crates/team-agent/src/lifecycle/worker_command_context.rs +115 -22
  78. package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
  79. package/crates/team-agent/src/messaging/delivery.rs +29 -0
  80. package/crates/team-agent/src/messaging/helpers.rs +2 -0
  81. package/crates/team-agent/src/messaging/leader_receiver.rs +12 -0
  82. package/crates/team-agent/src/messaging/mod.rs +2 -0
  83. package/crates/team-agent/src/messaging/results.rs +5 -0
  84. package/crates/team-agent/src/messaging/send.rs +9 -0
  85. package/crates/team-agent/src/messaging/tests/main_preserved.rs +1 -1
  86. package/crates/team-agent/src/messaging/tests/runtime.rs +32 -0
  87. package/crates/team-agent/src/messaging/types.rs +2 -0
  88. package/crates/team-agent/src/messaging/wait.rs +366 -0
  89. package/crates/team-agent/src/messaging/watchers.rs +153 -6
  90. package/crates/team-agent/src/model/spec.rs +129 -6
  91. package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +3 -1
  92. package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +3 -1
  93. package/crates/team-agent/src/model/testdata/team.spec.yaml +3 -1
  94. package/crates/team-agent/src/provider/adapters/claude.rs +5 -2
  95. package/crates/team-agent/src/provider/adapters/codex.rs +5 -2
  96. package/crates/team-agent/src/provider/adapters/copilot.rs +5 -2
  97. package/crates/team-agent/src/provider/bypass_flags.rs +80 -0
  98. package/crates/team-agent/src/provider/mod.rs +2 -0
  99. package/crates/team-agent/src/tmux_backend.rs +35 -12
  100. package/crates/team-agent/src/transport/test_support.rs +20 -0
  101. package/crates/team-agent/src/transport.rs +28 -11
  102. package/package.json +4 -4
  103. package/crates/team-agent/src/lifecycle/launch/approval.rs +0 -134
  104. package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +0 -1187
  105. package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +0 -327
@@ -229,8 +229,9 @@ team_orchestrator or @<.team/runtime/mcp/worker_a.json>; got {value:?}"
229
229
  }
230
230
 
231
231
  /// Face 1 dangerous tier (C-5-1, verdict `dangerous_auto_approve_argv_contains_allow_all`):
232
- /// dangerous_auto_approve must map to `--allow-all` (claude --dangerously-skip-permissions
233
- /// parity, paths+urls included) — NOT the silently-narrower `--allow-all-tools`.
232
+ /// role `dangerously_skip_permissions: true` must map to `--allow-all` (claude
233
+ /// `--dangerously-skip-permissions` parity, paths+urls included) — NOT the
234
+ /// silently-narrower `--allow-all-tools`. 0.5.66 bypass 单源:bypass 由角色字段驱动。
234
235
  #[test]
235
236
  #[serial(env)]
236
237
  fn copilot_argv_dangerous_maps_to_allow_all() {
@@ -254,7 +255,7 @@ fn copilot_argv_dangerous_maps_to_allow_all() {
254
255
  let mut failures = Vec::new();
255
256
  if !argv.iter().any(|arg| arg == "--allow-all") {
256
257
  failures.push(format!(
257
- "C-5-1: dangerous_auto_approve must produce `--allow-all` (=--yolo, claude \
258
+ "C-5-1: dangerously_skip_permissions=true must produce `--allow-all` (=--yolo, claude \
258
259
  parity incl. paths+urls); argv={argv:?}"
259
260
  ));
260
261
  }
@@ -1101,8 +1102,8 @@ fn copilot_subscription_model_precedence_role_over_profile() {
1101
1102
  // point the agent at the profile
1102
1103
  let agent_md = team_dir.join("agents/worker_a.md");
1103
1104
  let body = std::fs::read_to_string(&agent_md).unwrap().replace(
1104
- "auth_mode: subscription\n",
1105
- "auth_mode: subscription\nprofile: subm\n",
1105
+ "auth_mode: subscription\ndangerously_skip_permissions: false\n",
1106
+ "auth_mode: subscription\ndangerously_skip_permissions: false\nprofile: subm\n",
1106
1107
  );
1107
1108
  std::fs::write(&agent_md, body).unwrap();
1108
1109
  seed_healthy_coordinator(&ws);
@@ -1387,7 +1388,7 @@ fn write_copilot_byok_team(ws: &Path, name: &str, model: Option<&str>) -> PathBu
1387
1388
  std::fs::write(
1388
1389
  team.join("agents/worker_a.md"),
1389
1390
  format!(
1390
- "---\nname: worker_a\nrole: Copilot Worker\nprovider: copilot\n{model_field}auth_mode: compatible_api\nprofile: byok\ntools:\n - mcp_team\n---\n\nCOPILOT ROLE BODY SENTINEL: BYOK worker.\n"
1391
+ "---\nname: worker_a\nrole: Copilot Worker\nprovider: copilot\n{model_field}auth_mode: compatible_api\ndangerously_skip_permissions: false\nprofile: byok\ntools:\n - mcp_team\n---\n\nCOPILOT ROLE BODY SENTINEL: BYOK worker.\n"
1391
1392
  ),
1392
1393
  )
1393
1394
  .unwrap();
@@ -1412,15 +1413,16 @@ fn write_copilot_team(ws: &Path, name: &str, tools: &[&str], dangerous: bool) ->
1412
1413
  let _ = TeamOpts;
1413
1414
  let team = ws.join(name);
1414
1415
  std::fs::create_dir_all(team.join("agents")).unwrap();
1415
- let dangerous_line = if dangerous {
1416
- "dangerous_auto_approve: true\n"
1416
+ // 0.5.66 bypass 单源:bypass 由角色字段驱动,不再是 TEAM.md dangerous_auto_approve。
1417
+ let role_bypass = if dangerous {
1418
+ "true"
1417
1419
  } else {
1418
- ""
1420
+ "false"
1419
1421
  };
1420
1422
  std::fs::write(
1421
1423
  team.join("TEAM.md"),
1422
1424
  format!(
1423
- "---\nname: {name}\nobjective: copilot provider contract fixture.\nprovider: copilot\n{dangerous_line}---\n\nTeam fixture.\n"
1425
+ "---\nname: {name}\nobjective: copilot provider contract fixture.\nprovider: copilot\n---\n\nTeam fixture.\n"
1424
1426
  ),
1425
1427
  )
1426
1428
  .unwrap();
@@ -1431,7 +1433,7 @@ fn write_copilot_team(ws: &Path, name: &str, tools: &[&str], dangerous: bool) ->
1431
1433
  std::fs::write(
1432
1434
  team.join("agents/worker_a.md"),
1433
1435
  format!(
1434
- "---\nname: worker_a\nrole: Copilot Worker\nprovider: copilot\nmodel: gpt-5\nauth_mode: subscription\ntools:\n{tool_lines}---\n\nCOPILOT ROLE BODY SENTINEL: serve the copilot adapter contract.\n"
1436
+ "---\nname: worker_a\nrole: Copilot Worker\nprovider: copilot\nmodel: gpt-5\nauth_mode: subscription\ndangerously_skip_permissions: {role_bypass}\ntools:\n{tool_lines}---\n\nCOPILOT ROLE BODY SENTINEL: serve the copilot adapter contract.\n"
1435
1437
  ),
1436
1438
  )
1437
1439
  .unwrap();
@@ -1010,24 +1010,6 @@ fn quick_start_empty_dir_returns_typed_report_not_error_path_only() {
1010
1010
  }
1011
1011
  }
1012
1012
 
1013
- // ───────────────────────────────────────────────────────────────────────
1014
- // detect_dangerous_approval — launch/config.py
1015
- // 默认进程(无 --dangerously-* 祖先)→ enabled=false / source=Disabled / inherited=false。
1016
- // launch 在 inherited=false 且无 --yes 时 raise DangerousApprovalRequired(core.py:120)。
1017
- // ───────────────────────────────────────────────────────────────────────
1018
-
1019
- #[test]
1020
- #[serial_test::serial(env)]
1021
- fn detect_dangerous_approval_clean_process_is_disabled() {
1022
- // Explicit mock ancestry keeps the test independent from the real Codex/CI
1023
- // process tree that runs cargo.
1024
- let _ancestry = EnvVarGuard::set("TEAM_AGENT_TEST_PROCESS_ANCESTRY_ARGV_JSON", "[]");
1025
- let got = detect_dangerous_approval().expect("探测祖先链应 Ok");
1026
- assert!(!got.enabled, "干净进程不应启用危险审批");
1027
- assert_eq!(got.source, DangerousApprovalSource::Disabled);
1028
- assert!(!got.inherited);
1029
- }
1030
-
1031
1013
  #[test]
1032
1014
  #[serial_test::serial(env)]
1033
1015
  fn leader_pane_env_absent_pane_fails_fast() {
@@ -746,7 +746,7 @@ fn write_team(
746
746
  std::fs::write(
747
747
  team.join("agents").join(format!("{}.md", agent.id)),
748
748
  format!(
749
- "---\nname: {}\nrole: {}\nprovider: {}\nmodel: fake\nauth_mode: subscription\ntools:\n{}---\n\n{}\n",
749
+ "---\nname: {}\nrole: {}\nprovider: {}\nmodel: fake\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n{}---\n\n{}\n",
750
750
  agent.id, agent.role, agent.provider, tools, agent.body
751
751
  ),
752
752
  )
@@ -358,7 +358,7 @@ impl DisplayFixture {
358
358
  std::fs::write(
359
359
  team.join("agents").join(format!("{worker}.md")),
360
360
  format!(
361
- "---\nname: {worker}\nrole: Adaptive fake worker {worker}\nprovider: fake\nmodel: fake\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker.\n"
361
+ "---\nname: {worker}\nrole: Adaptive fake worker {worker}\nprovider: fake\nmodel: fake\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWorker.\n"
362
362
  ),
363
363
  )
364
364
  .unwrap();
@@ -27,7 +27,7 @@ static SEQ: AtomicU32 = AtomicU32::new(0);
27
27
 
28
28
  const TEAM_MD: &str =
29
29
  "---\nname: f032team\nobjective: F032 lifecycle prompt fault probe.\nprovider: codex\n---\n\nF032.\n";
30
- const ROLE_MD: &str = "---\nname: implementer\nrole: Implementation Engineer\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nImplement.\n";
30
+ const ROLE_MD: &str = "---\nname: implementer\nrole: Implementation Engineer\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nImplement.\n";
31
31
 
32
32
  #[test]
33
33
  fn f032_launch_spawn_path_treats_startup_prompt_panic_as_best_effort() {
@@ -422,7 +422,10 @@ check behind it = false green); it must reflect an actual doctor result"
422
422
  /// T3-2 (claude_auth_hint parse failure): when `claude auth status` output is
423
423
  /// unparseable / unsuccessful, the hint must not default to Present (auth misreported
424
424
  /// as available). PATH-shims a `claude` that prints garbage and exits non-zero.
425
+ /// Mutates the process-global PATH/HOME — must not race host_cotenant's ps/tmux
426
+ /// shims (P0 pid-probe isolation), hence `#[serial(pid_probe)]`.
425
427
  #[test]
428
+ #[serial(pid_probe)]
426
429
  fn t3_claude_auth_hint_parse_failure_is_not_present() {
427
430
  let home = tmp_ws("t3-auth-home");
428
431
  let shim_dir = home.join("bin");
@@ -65,7 +65,7 @@ const POLLUTED_CALLER_AUTHORITY_ENVS: &[&str] = &[
65
65
  ];
66
66
 
67
67
  #[test]
68
- #[serial(env)]
68
+ #[serial(env, pid_probe)]
69
69
  fn polluted_caller_env_never_authorizes_teardown_to_kill_foreign_tmux() {
70
70
  let env = hermetic_guard::HermeticTestEnv::enter("p0-caller-env-sentinel");
71
71
  let shim_dir = env.root().join("bin");
@@ -150,7 +150,7 @@ exec "$TEAM_AGENT_TEST_REAL_TMUX" "$@"
150
150
  }
151
151
 
152
152
  #[test]
153
- #[serial(env)]
153
+ #[serial(env, pid_probe)]
154
154
  fn restart_allow_fresh_does_not_leave_same_role_host_cotenants() {
155
155
  let mut failures = Vec::new();
156
156
  let (env, workspace) = team_case("p0-restart", &["developer", "test-engineer", "arch-reviewer"]);
@@ -677,7 +677,7 @@ fn write_team_docs(workspace: &Path, workers: &[(&str, &str)]) {
677
677
  fs::create_dir_all(workspace.join("agents")).unwrap();
678
678
  fs::write(workspace.join("TEAM.md"), "---\nname: current\nobjective: P0 host co-tenant contract.\nprovider: codex\n---\n").unwrap();
679
679
  for (worker, model) in workers {
680
- let role = format!("---\nname: {worker}\nrole: {worker}\nprovider: codex\nmodel: {model}\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\n{worker}.\n");
680
+ let role = format!("---\nname: {worker}\nrole: {worker}\nprovider: codex\nmodel: {model}\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\n{worker}.\n");
681
681
  fs::write(workspace.join("agents").join(format!("{worker}.md")), role).unwrap();
682
682
  }
683
683
  }
@@ -3,7 +3,7 @@ use super::*;
3
3
 
4
4
  // ═════════════════════════════════════════════════════════════════════════
5
5
 
6
- const DELEG_ROLE_ALPHA_COMPAT: &str = "---\nname: alpha\nrole: Alpha Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: compatible_api\nprofile: alpha-compat\ntools:\n - mcp_team\n---\n\nAlpha.\n";
6
+ const DELEG_ROLE_ALPHA_COMPAT: &str = "---\nname: alpha\nrole: Alpha Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: compatible_api\ndangerously_skip_permissions: false\nprofile: alpha-compat\ntools:\n - mcp_team\n---\n\nAlpha.\n";
7
7
 
8
8
  type LaneKills = std::sync::Arc<std::sync::Mutex<Vec<String>>>;
9
9
  pub(super) type LaneSpawns = std::sync::Arc<std::sync::Mutex<Vec<(String, Vec<String>)>>>;
@@ -816,7 +816,7 @@ fn current_alias_authority_does_not_leak_into_real_sibling_same_id_retirement()
816
816
  );
817
817
  }
818
818
 
819
- const CHARLIE_ROLE: &str = "---\nname: charlie\nrole: Charlie Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nCharlie.\n";
819
+ const CHARLIE_ROLE: &str = "---\nname: charlie\nrole: Charlie Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nCharlie.\n";
820
820
 
821
821
  struct HermeticTestEnv {
822
822
  root: std::path::PathBuf,
@@ -31,8 +31,8 @@ struct HermeticTestEnv;
31
31
 
32
32
  const QS_TEAM_MD: &str =
33
33
  "---\nname: quickteam\nobjective: Quick start.\nprovider: codex\n---\n\nQuick-start team.\n";
34
- pub(super) const QS_VALID_ROLE: &str = "---\nname: implementer\nrole: Implementation Engineer\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nImplement bounded tasks.\n";
35
- const QS_ROLE_NO_PROVIDER: &str = "---\nname: implementer\nrole: Implementation Engineer\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nImplement bounded tasks.\n";
34
+ pub(super) const QS_VALID_ROLE: &str = "---\nname: implementer\nrole: Implementation Engineer\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nImplement bounded tasks.\n";
35
+ const QS_ROLE_NO_PROVIDER: &str = "---\nname: implementer\nrole: Implementation Engineer\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nImplement bounded tasks.\n";
36
36
 
37
37
  /// A REAL team dir `<base>/teamdir/{TEAM.md, agents/implementer.md}` — already in team-dir
38
38
  /// layout, so quick_start's prepare_quick_start_team returns it as-is and writes the compiled
@@ -57,7 +57,7 @@ fn quick_start_team_dir_with_roles(role_docs: &[(&str, &str)]) -> PathBuf {
57
57
 
58
58
  fn role_doc(id: &str) -> String {
59
59
  format!(
60
- "---\nname: {id}\nrole: {id} Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\n{id} worker.\n"
60
+ "---\nname: {id}\nrole: {id} Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\n{id} worker.\n"
61
61
  )
62
62
  }
63
63
 
@@ -448,7 +448,7 @@ fn add_agent_proceeds_past_owner_gate_and_reaches_recompile() {
448
448
  let role = ws.join("w2-role.md");
449
449
  std::fs::write(
450
450
  &role,
451
- "---\nname: w2\nrole: Second Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nSecond worker.\n",
451
+ "---\nname: w2\nrole: Second Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nSecond worker.\n",
452
452
  )
453
453
  .unwrap();
454
454
  let transport = OfflineTransport::new();
@@ -617,23 +617,8 @@ fn spine_quick_start_seeds_state_under_team_workspace_not_team_dir() {
617
617
  );
618
618
  }
619
619
 
620
- // #10 — launch dry-run safety is derived from spec.runtime.dangerous_auto_approve (source=runtime_config),
621
- // NOT a hardcoded Disabled. Golden config.py:effective_runtime_config.
622
- #[test]
623
- fn spine_launch_dry_run_safety_reflects_spec_dangerous() {
624
- let team = quick_start_team_dir_custom(QS_TEAM_MD_DANGEROUS, QS_VALID_ROLE);
625
- let spec_path = compiled_spec_path(&team);
626
- let report = launch(&spec_path, true, false, true).expect("dry_run launch");
627
- assert!(
628
- report.safety.enabled,
629
- "safety must reflect spec.runtime.dangerous_auto_approve=true"
630
- );
631
- assert_eq!(
632
- report.safety.source,
633
- DangerousApprovalSource::RuntimeConfig,
634
- "safety source must be runtime_config when spec-declared"
635
- );
636
- }
620
+ // #10 removed — launch dry-run safety was derived from spec.runtime.dangerous_auto_approve.
621
+ // 0.5.66 bypass 单源:该字段废弃,safety 改由 per-agent `dangerously_skip_permissions` 派生。
637
622
 
638
623
  // #12 — launch dry-run produces one RoutingDecision PER spec.task via route_task, with the real task id
639
624
  // and the route_task reason taxonomy — not one synthetic 'default_assignee' route. Golden core.py:77-88.
@@ -709,7 +694,7 @@ fn e5_add_agent_does_not_copy_role_into_platform_dir_and_injects_into_spec() {
709
694
  seed_canonical_team_identity(&team, "teamdir");
710
695
  // External role file OUTSIDE team_dir/agents, with recognizable body content.
711
696
  let role = team.parent().unwrap().join("w2-external-role.md");
712
- let role_body = "---\nname: w2\nrole: Second Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nUNIQUE-E5-BUG1-MARKER body for w2.\n";
697
+ let role_body = "---\nname: w2\nrole: Second Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nUNIQUE-E5-BUG1-MARKER body for w2.\n";
713
698
  std::fs::write(&role, role_body).unwrap();
714
699
  let transport = OfflineTransport::new();
715
700
  // The spawn may not complete under OfflineTransport; we only assert the no-copy + spec-inject
@@ -749,7 +734,7 @@ fn e5_restart_rebuilds_runtime_spec_from_role_docs() {
749
734
  // Edit a role doc AFTER initial compile: change alpha's role text.
750
735
  std::fs::write(
751
736
  ws.join("agents").join("alpha.md"),
752
- "---\nname: alpha\nrole: RENAMED Alpha Role\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nAlpha edited.\n",
737
+ "---\nname: alpha\nrole: RENAMED Alpha Role\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nAlpha edited.\n",
753
738
  )
754
739
  .unwrap();
755
740
  let transport = OfflineTransport::new();
@@ -974,7 +959,7 @@ fn e5_add_agent_resolves_team_dir_to_role_dir_when_runtime_spec_exists() {
974
959
  let role = team.join("w2-role.md");
975
960
  std::fs::write(
976
961
  &role,
977
- "---\nname: w2\nrole: Second Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nw2.\n",
962
+ "---\nname: w2\nrole: Second Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nw2.\n",
978
963
  )
979
964
  .unwrap();
980
965
  let transport = OfflineTransport::new();
@@ -1563,9 +1548,9 @@ fn quick_start_fresh_ws_spawns_resident_tmux_and_coordinator() {
1563
1548
  // Golden: restart/orchestration.py (Route-B resume spawn), lifecycle/start.py, lifecycle/operations.py.
1564
1549
  // ═════════════════════════════════════════════════════════════════════════
1565
1550
 
1566
- pub(super) const DELEG_ROLE_ALPHA: &str = "---\nname: alpha\nrole: Alpha Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nAlpha.\n";
1567
- pub(super) const DELEG_ROLE_BRAVO: &str = "---\nname: bravo\nrole: Bravo Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nBravo.\n";
1568
- const DELEG_ROLE_WORKER2: &str = "---\nname: worker2\nrole: Second Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nSecond worker.\n";
1551
+ pub(super) const DELEG_ROLE_ALPHA: &str = "---\nname: alpha\nrole: Alpha Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nAlpha.\n";
1552
+ pub(super) const DELEG_ROLE_BRAVO: &str = "---\nname: bravo\nrole: Bravo Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nBravo.\n";
1553
+ const DELEG_ROLE_WORKER2: &str = "---\nname: worker2\nrole: Second Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nSecond worker.\n";
1569
1554
 
1570
1555
  /// A workspace (= self-contained team dir) with a compiled 2-agent spec + state listing alpha/bravo as
1571
1556
  /// RESUMABLE (running, valid first_send_at, session_id) + a seeded HEALTHY coordinator.
@@ -2437,7 +2422,7 @@ fn restart_allow_fresh_copilot_keeps_session_id_null_until_capture_confirms() {
2437
2422
  .unwrap();
2438
2423
  std::fs::write(
2439
2424
  ws.join("agents").join("alpha.md"),
2440
- "---\nname: alpha\nrole: Alpha Worker\nprovider: copilot\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nAlpha.\n",
2425
+ "---\nname: alpha\nrole: Alpha Worker\nprovider: copilot\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nAlpha.\n",
2441
2426
  )
2442
2427
  .unwrap();
2443
2428
  let spec = crate::compiler::compile_team(&ws).expect("compile copilot team");
@@ -2541,7 +2526,7 @@ fn restart_allow_fresh_claude_clears_poisoned_session_id() {
2541
2526
  .unwrap();
2542
2527
  std::fs::write(
2543
2528
  ws.join("agents").join("alpha.md"),
2544
- "---\nname: alpha\nrole: Alpha\nprovider: claude\nmodel: sonnet\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nAlpha.\n",
2529
+ "---\nname: alpha\nrole: Alpha\nprovider: claude\nmodel: sonnet\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nAlpha.\n",
2545
2530
  )
2546
2531
  .unwrap();
2547
2532
  let spec = crate::compiler::compile_team(&ws).expect("compile");
@@ -2612,7 +2597,7 @@ fn fork_refuses_source_with_partial_tuple() {
2612
2597
  .unwrap();
2613
2598
  std::fs::write(
2614
2599
  ws.join("agents").join("src.md"),
2615
- "---\nname: src\nrole: Source\nprovider: codex\nmodel: gpt\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nSrc.\n",
2600
+ "---\nname: src\nrole: Source\nprovider: codex\nmodel: gpt\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nSrc.\n",
2616
2601
  )
2617
2602
  .unwrap();
2618
2603
  let spec = crate::compiler::compile_team(&ws).expect("compile");
@@ -3084,6 +3069,8 @@ fn quick_start_running_agent_state_shape_after_spawn_is_golden() {
3084
3069
  "mcp_config",
3085
3070
  "permissions",
3086
3071
  "effective_approval_policy",
3072
+ // 0.5.66 bypass 单源:fresh spawn 记录运行面 bypass 值(restart drift 挡板)。
3073
+ "as_launched_dangerously_skip_permissions",
3087
3074
  "session_id",
3088
3075
  "rollout_path",
3089
3076
  "captured_at",
@@ -410,7 +410,7 @@ impl RollbackFixture {
410
410
 
411
411
  fn agent_doc(name: &str, role: &str) -> String {
412
412
  format!(
413
- "---\nname: {name}\nrole: {role}\nprovider: codex\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\n{role}.\n"
413
+ "---\nname: {name}\nrole: {role}\nprovider: codex\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\n{role}.\n"
414
414
  )
415
415
  }
416
416
 
@@ -463,8 +463,8 @@ fn team_dir_with_roles(role_docs: &[(&str, &str)]) -> PathBuf {
463
463
  }
464
464
 
465
465
  fn breal_workspace() -> (PathBuf, PathBuf) {
466
- let w1 = "---\nname: w1\nrole: Phase B Real Worker One\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker one body.\n";
467
- let w2 = "---\nname: w2\nrole: Phase B Real Worker Two\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker two body.\n";
466
+ let w1 = "---\nname: w1\nrole: Phase B Real Worker One\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWorker one body.\n";
467
+ let w2 = "---\nname: w2\nrole: Phase B Real Worker Two\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWorker two body.\n";
468
468
  let team = team_dir_with_roles(&[("w1.md", w1), ("w2.md", w2)]);
469
469
  let workspace = team.parent().expect("team workspace").to_path_buf();
470
470
  seed_healthy_coordinator(&workspace);
@@ -483,7 +483,7 @@ fn breal_workspace() -> (PathBuf, PathBuf) {
483
483
  }
484
484
 
485
485
  fn breal_one_worker_workspace() -> (PathBuf, PathBuf) {
486
- let w1 = "---\nname: w1\nrole: Phase B Real Worker One\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker one body.\n";
486
+ let w1 = "---\nname: w1\nrole: Phase B Real Worker One\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWorker one body.\n";
487
487
  let team = team_dir_with_roles(&[("w1.md", w1)]);
488
488
  let workspace = team.parent().expect("team workspace").to_path_buf();
489
489
  seed_healthy_coordinator(&workspace);
@@ -526,7 +526,7 @@ fn strip_runtime_command_context(workspace: &std::path::Path) {
526
526
 
527
527
  fn role_doc(id: &str) -> String {
528
528
  format!(
529
- "---\nname: {id}\nrole: {id} Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\n{id} worker.\n"
529
+ "---\nname: {id}\nrole: {id} Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\n{id} worker.\n"
530
530
  )
531
531
  }
532
532
 
@@ -387,7 +387,7 @@ fn codex_ready_transport() -> OfflineTransport {
387
387
 
388
388
  fn role_doc(id: &str) -> String {
389
389
  format!(
390
- "---\nname: {id}\nrole: {id} Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\n{id} worker.\n"
390
+ "---\nname: {id}\nrole: {id} Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\n{id} worker.\n"
391
391
  )
392
392
  }
393
393
 
@@ -38,9 +38,9 @@ use hermetic::HermeticTestEnv;
38
38
  const TEAM_MD: &str =
39
39
  "---\nname: bug7\nobjective: BUG-7 worker readiness honesty contract.\nprovider: codex\n---\n\nTeam.\n";
40
40
 
41
- const CODEX_ROLE: &str = "---\nname: codexer\nrole: Codex Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nCodex worker.\n";
41
+ const CODEX_ROLE: &str = "---\nname: codexer\nrole: Codex Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nCodex worker.\n";
42
42
 
43
- const CLAUDE_ROLE: &str = "---\nname: clauder\nrole: Claude Worker\nprovider: claude\nmodel: claude-sonnet-4-6\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nClaude worker.\n";
43
+ const CLAUDE_ROLE: &str = "---\nname: clauder\nrole: Claude Worker\nprovider: claude\nmodel: claude-sonnet-4-6\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nClaude worker.\n";
44
44
 
45
45
  #[test]
46
46
  #[ignore = "real-machine: quick-start worker readiness gate"]
@@ -218,7 +218,7 @@ fn write_team_docs(team_dir: &Path, workers: &[String], provider: ProviderShape)
218
218
  fs::write(
219
219
  team_dir.join("agents").join(format!("{worker}.md")),
220
220
  format!(
221
- "---\nname: {worker}\nrole: Worker {worker}\nprovider: {}\nmodel: {}\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\n{worker}.\n",
221
+ "---\nname: {worker}\nrole: Worker {worker}\nprovider: {}\nmodel: {}\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\n{worker}.\n",
222
222
  provider.provider(),
223
223
  provider.model()
224
224
  ),
@@ -492,7 +492,7 @@ impl RestartFixture {
492
492
  std::fs::write(
493
493
  team.join("agents").join(format!("{agent}.md")),
494
494
  format!(
495
- "---\nname: {agent}\nrole: Worker {agent}\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker.\n"
495
+ "---\nname: {agent}\nrole: Worker {agent}\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWorker.\n"
496
496
  ),
497
497
  )
498
498
  .unwrap();
@@ -342,7 +342,7 @@ impl HotfixCase {
342
342
  std::fs::write(
343
343
  dir.join("agents").join(format!("{agent_id}.md")),
344
344
  format!(
345
- "---\nname: {agent_id}\nrole: Greeter\nprovider: {}\nmodel: test\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker.\n",
345
+ "---\nname: {agent_id}\nrole: Greeter\nprovider: {}\nmodel: test\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWorker.\n",
346
346
  provider_name(provider)
347
347
  ),
348
348
  )
@@ -1371,7 +1371,7 @@ impl RestartFixture {
1371
1371
  std::fs::write(
1372
1372
  team.join("agents/worker_a.md"),
1373
1373
  format!(
1374
- "---\nname: worker_a\nrole: Worker\nprovider: {provider}\nmodel: {}\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker.\n",
1374
+ "---\nname: worker_a\nrole: Worker\nprovider: {provider}\nmodel: {}\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWorker.\n",
1375
1375
  if provider == "claude" {
1376
1376
  "claude-sonnet-4-6"
1377
1377
  } else {
@@ -1409,7 +1409,7 @@ impl RestartFixture {
1409
1409
  std::fs::write(
1410
1410
  team.join("agents/worker_a.md"),
1411
1411
  format!(
1412
- "---\nname: worker_a\nrole: Worker\nprovider: {provider}\nmodel: {}\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker.\n",
1412
+ "---\nname: worker_a\nrole: Worker\nprovider: {provider}\nmodel: {}\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWorker.\n",
1413
1413
  if provider == "claude" {
1414
1414
  "claude-sonnet-4-6"
1415
1415
  } else {
@@ -151,7 +151,7 @@ impl ResumeFixture {
151
151
  .unwrap();
152
152
  std::fs::write(
153
153
  team.join("agents").join("worker_a.md"),
154
- "---\nname: worker_a\nrole: Worker A\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker.\n",
154
+ "---\nname: worker_a\nrole: Worker A\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWorker.\n",
155
155
  )
156
156
  .unwrap();
157
157
  let spec = team_agent::compiler::compile_team(&team).unwrap();
@@ -291,7 +291,7 @@ fn agent(id: &str, status: &str, topology: bool) -> Value {
291
291
  }
292
292
 
293
293
  fn role_doc(id: &str) -> String {
294
- format!("---\nname: {id}\nrole: {id}\nprovider: fake\nmodel: fake\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\n{id}.\n")
294
+ format!("---\nname: {id}\nrole: {id}\nprovider: fake\nmodel: fake\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\n{id}.\n")
295
295
  }
296
296
 
297
297
  fn assert_no_standards(case: &Case) {
@@ -167,13 +167,17 @@ fn restart_failure_aggregation_matches_serial_semantics_without_early_abort() {
167
167
  ["w1", "w2", "w4", "w5", "w6", "w7", "w8"],
168
168
  "R2: successful workers after w3 must still be spawned and reported"
169
169
  );
170
+ let mut observed_spawn_windows = transport
171
+ .spawn_calls()
172
+ .into_iter()
173
+ .map(|call| call.window)
174
+ .collect::<Vec<_>>();
175
+ let mut expected_spawn_windows = worker_ids(8);
176
+ observed_spawn_windows.sort();
177
+ expected_spawn_windows.sort();
170
178
  assert_eq!(
171
- transport
172
- .spawn_calls()
173
- .iter()
174
- .map(|call| call.window.as_str())
175
- .collect::<Vec<_>>(),
176
- worker_ids(8),
179
+ observed_spawn_windows,
180
+ expected_spawn_windows,
177
181
  "R2: failure aggregation must attempt the full plan; no early return after w3"
178
182
  );
179
183
 
@@ -484,7 +488,7 @@ fn write_team_docs(team_dir: &Path, workers: usize) {
484
488
 
485
489
  fn role_doc(worker: &str) -> String {
486
490
  format!(
487
- "---\nname: {worker}\nrole: Worker {worker}\nprovider: fake\nmodel: fake\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\n{worker}.\n"
491
+ "---\nname: {worker}\nrole: Worker {worker}\nprovider: fake\nmodel: fake\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\n{worker}.\n"
488
492
  )
489
493
  }
490
494
 
@@ -368,7 +368,7 @@ fn red6_cli_restart_reports_and_top_level_error_logs_are_redacted() {
368
368
  }
369
369
 
370
370
  #[test]
371
- #[serial(env)]
371
+ #[serial(env, pid_probe)]
372
372
  fn red7_pane_and_profile_scrubbers_converge_without_losing_existing_policy() {
373
373
  let root = Path::new(env!("CARGO_MANIFEST_DIR"));
374
374
  let tmux = fs::read_to_string(root.join("src/tmux_backend.rs")).unwrap();
@@ -685,12 +685,12 @@ fn write_profile_fixture(root: &Path, base_url: &str, token: &str) -> PathBuf {
685
685
  .unwrap();
686
686
  fs::write(
687
687
  team.join("TEAM.md"),
688
- "---\nname: redact-profile\nobjective: Synthetic redaction profile contract.\nprovider: claude\nauth_mode: compatible_api\n---\n\nTeam.\n",
688
+ "---\nname: redact-profile\nobjective: Synthetic redaction profile contract.\nprovider: claude\nauth_mode: compatible_api\ndangerously_skip_permissions: false\n---\n\nTeam.\n",
689
689
  )
690
690
  .unwrap();
691
691
  fs::write(
692
692
  team.join("agents/worker.md"),
693
- "---\nname: worker\nrole: Worker\nprovider: claude\nauth_mode: compatible_api\nprofile: local\nmodel: null\ntools:\n - mcp_team\n---\n\nWorker.\n",
693
+ "---\nname: worker\nrole: Worker\nprovider: claude\nauth_mode: compatible_api\ndangerously_skip_permissions: false\nprofile: local\nmodel: null\ntools:\n - mcp_team\n---\n\nWorker.\n",
694
694
  )
695
695
  .unwrap();
696
696
  team
@@ -861,7 +861,7 @@ fn contract_c_lifecycle_launch_command_carries_role_tools_and_real_mcp_context()
861
861
  .unwrap();
862
862
  std::fs::write(
863
863
  teamdir.join("agents").join("w1.md"),
864
- "---\nname: w1\nrole: Worker must call report_result exactly once.\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nAlways use report_result for completion.\n",
864
+ "---\nname: w1\nrole: Worker must call report_result exactly once.\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nAlways use report_result for completion.\n",
865
865
  )
866
866
  .unwrap();
867
867
  let spec = team_agent::compiler::compile_team(&teamdir).unwrap();
@@ -172,7 +172,7 @@ fn write_claude_compatible_team(ws: &Path) -> PathBuf {
172
172
  .unwrap();
173
173
  std::fs::write(
174
174
  team.join("agents/worker_a.md"),
175
- "---\nname: worker_a\nrole: Compat Worker\nprovider: claude\nmodel: claude-haiku\nauth_mode: compatible_api\nprofile: relay\ntools:\n - mcp_team\n---\n\nCompat worker.\n",
175
+ "---\nname: worker_a\nrole: Compat Worker\nprovider: claude\nmodel: claude-haiku\nauth_mode: compatible_api\ndangerously_skip_permissions: false\nprofile: relay\ntools:\n - mcp_team\n---\n\nCompat worker.\n",
176
176
  )
177
177
  .unwrap();
178
178
  std::fs::write(
@@ -244,7 +244,7 @@ fn write_team_dir(root: &Path, dir_name: &str, agent: &str) -> PathBuf {
244
244
 
245
245
  fn role_doc(agent: &str) -> String {
246
246
  format!(
247
- "---\nname: {agent}\nrole: Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker.\n"
247
+ "---\nname: {agent}\nrole: Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWorker.\n"
248
248
  )
249
249
  }
250
250
 
@@ -881,7 +881,7 @@ fn team_dir_with_name(root: &Path, dir_name: &str, spec_name: &str, agent_id: &s
881
881
 
882
882
  fn role_doc(name: &str) -> String {
883
883
  format!(
884
- "---\nname: {name}\nrole: Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker.\n"
884
+ "---\nname: {name}\nrole: Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWorker.\n"
885
885
  )
886
886
  }
887
887
 
@@ -1057,7 +1057,7 @@ fn team_dir(root: &Path, name: &str, agents: &[(&str, &str)]) -> PathBuf {
1057
1057
 
1058
1058
  fn role_doc(name: &str, role: &str) -> String {
1059
1059
  format!(
1060
- "---\nname: {name}\nrole: {role}\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\n{role}.\n"
1060
+ "---\nname: {name}\nrole: {role}\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\n{role}.\n"
1061
1061
  )
1062
1062
  }
1063
1063
 
@@ -390,7 +390,7 @@ impl RollbackFixture {
390
390
 
391
391
  fn agent_doc(name: &str, role: &str) -> String {
392
392
  format!(
393
- "---\nname: {name}\nrole: {role}\nprovider: codex\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\n{role}.\n"
393
+ "---\nname: {name}\nrole: {role}\nprovider: codex\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\n{role}.\n"
394
394
  )
395
395
  }
396
396
 
@@ -393,7 +393,7 @@ impl RestartAutoAttachCase {
393
393
  std::fs::write(
394
394
  workspace.join("agents").join(format!("{WORKER}.md")),
395
395
  format!(
396
- "---\nname: {WORKER}\nrole: Worker {WORKER}\nprovider: fake\nmodel: fake\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker.\n"
396
+ "---\nname: {WORKER}\nrole: Worker {WORKER}\nprovider: fake\nmodel: fake\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWorker.\n"
397
397
  ),
398
398
  )
399
399
  .expect("write worker role");
@@ -456,7 +456,7 @@ impl UpgradeFixture {
456
456
  std::fs::create_dir_all(self.team_dir.join("agents")).unwrap();
457
457
  std::fs::write(
458
458
  self.team_dir.join("agents").join("upgrade_worker.md"),
459
- "---\nname: upgrade_worker\nrole: Upgrade Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker.\n",
459
+ "---\nname: upgrade_worker\nrole: Upgrade Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWorker.\n",
460
460
  )
461
461
  .unwrap();
462
462
  let spec = team_agent::compiler::compile_team(&self.team_dir).unwrap();
@@ -39,9 +39,9 @@ use hermetic::HermeticTestEnv;
39
39
  const TEAM_MD: &str =
40
40
  "---\nname: verify031\nobjective: Verify mixed provider windows.\nprovider: codex\n---\n\nTeam.\n";
41
41
 
42
- const CODEX_ROLE: &str = "---\nname: codexer\nrole: Codex Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nCodex worker.\n";
42
+ const CODEX_ROLE: &str = "---\nname: codexer\nrole: Codex Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nCodex worker.\n";
43
43
 
44
- const CLAUDE_ROLE: &str = "---\nname: clauder\nrole: Claude Worker\nprovider: claude\nmodel: claude-sonnet-4-6\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nClaude worker.\n";
44
+ const CLAUDE_ROLE: &str = "---\nname: clauder\nrole: Claude Worker\nprovider: claude\nmodel: claude-sonnet-4-6\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nClaude worker.\n";
45
45
 
46
46
  #[test]
47
47
  #[serial(env)]
@@ -329,7 +329,7 @@ fn compiled_team_dir(label: &str, agents: &[(&str, &str)]) -> PathBuf {
329
329
 
330
330
  fn role_doc(name: &str, role: &str) -> String {
331
331
  format!(
332
- "---\nname: {name}\nrole: {role}\nprovider: codex\nmodel: codex-test-model\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\n{role}.\n"
332
+ "---\nname: {name}\nrole: {role}\nprovider: codex\nmodel: codex-test-model\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\n{role}.\n"
333
333
  )
334
334
  }
335
335