@team-agent/installer 0.5.65 → 0.5.68

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 (208) hide show
  1. package/Cargo.lock +8 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/Cargo.toml +6 -0
  4. package/crates/team-agent/src/cli/adapters.rs +6 -1
  5. package/crates/team-agent/src/cli/diagnose.rs +82 -0
  6. package/crates/team-agent/src/cli/emit.rs +3 -3
  7. package/crates/team-agent/src/cli/grok_slot.rs +299 -0
  8. package/crates/team-agent/src/cli/leader.rs +21 -7
  9. package/crates/team-agent/src/cli/leaders.rs +125 -1
  10. package/crates/team-agent/src/cli/mod.rs +20 -0
  11. package/crates/team-agent/src/cli/send/presentation.rs +7 -1
  12. package/crates/team-agent/src/cli/spec.rs +2 -0
  13. package/crates/team-agent/src/cli/status_port/compact.rs +28 -0
  14. package/crates/team-agent/src/cli/status_port/snapshot.rs +25 -1
  15. package/crates/team-agent/src/cli/tests/base.rs +26 -3
  16. package/crates/team-agent/src/cli/tests/compile.rs +1 -1
  17. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +129 -19
  18. package/crates/team-agent/src/cli/tests/mod.rs +2 -2
  19. package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -1
  20. package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +387 -6
  21. package/crates/team-agent/src/cli/tests/status_send.rs +79 -10
  22. package/crates/team-agent/src/cli/tests/verb_validate.rs +1 -1
  23. package/crates/team-agent/src/communication_mode/mod.rs +6 -4
  24. package/crates/team-agent/src/compiler/tests.rs +23 -20
  25. package/crates/team-agent/src/compiler.rs +84 -5
  26. package/crates/team-agent/src/conpty/backend.rs +16 -0
  27. package/crates/team-agent/src/coordinator/backoff.rs +55 -0
  28. package/crates/team-agent/src/coordinator/conpty_shim.rs +82 -0
  29. package/crates/team-agent/src/coordinator/health.rs +173 -0
  30. package/crates/team-agent/src/coordinator/mod.rs +31 -0
  31. package/crates/team-agent/src/coordinator/orphan.rs +31 -0
  32. package/crates/team-agent/src/coordinator/runtime_detectors.rs +30 -0
  33. package/crates/team-agent/src/coordinator/runtime_observation.rs +25 -0
  34. package/crates/team-agent/src/coordinator/steps/abnormal.rs +59 -0
  35. package/crates/team-agent/src/coordinator/steps/delivery.rs +10 -0
  36. package/crates/team-agent/src/coordinator/steps/health_sync.rs +10 -0
  37. package/crates/team-agent/src/coordinator/steps/mod.rs +22 -0
  38. package/crates/team-agent/src/coordinator/steps/persist.rs +10 -0
  39. package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +10 -0
  40. package/crates/team-agent/src/coordinator/steps/session_gate.rs +10 -0
  41. package/crates/team-agent/src/coordinator/tick.rs +133 -22
  42. package/crates/team-agent/src/coordinator/types.rs +49 -0
  43. package/crates/team-agent/src/db/message_store.rs +39 -5
  44. package/crates/team-agent/src/layout/worker_env.rs +20 -3
  45. package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
  46. package/crates/team-agent/src/leader/provider_attribution.rs +53 -0
  47. package/crates/team-agent/src/leader/registry.rs +65 -0
  48. package/crates/team-agent/src/leader/start.rs +1080 -58
  49. package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1 -1
  50. package/crates/team-agent/src/lifecycle/display.rs +56 -0
  51. package/crates/team-agent/src/lifecycle/helpers.rs +57 -0
  52. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +332 -14
  53. package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +69 -2
  54. package/crates/team-agent/src/lifecycle/launch/agent_state.rs +196 -2
  55. package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +101 -11
  56. package/crates/team-agent/src/lifecycle/launch/cursor_create_chat.rs +229 -0
  57. package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +332 -0
  58. package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +204 -457
  59. package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +24 -0
  60. package/crates/team-agent/src/lifecycle/launch/grok_per_seat.rs +260 -0
  61. package/crates/team-agent/src/lifecycle/launch/identity.rs +146 -0
  62. package/crates/team-agent/src/lifecycle/launch/layout.rs +60 -0
  63. package/crates/team-agent/src/lifecycle/launch/leader_context.rs +112 -0
  64. package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +530 -0
  65. package/crates/team-agent/src/lifecycle/launch/ownership.rs +40 -0
  66. package/crates/team-agent/src/lifecycle/launch/plan.rs +31 -0
  67. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +66 -0
  68. package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +78 -0
  69. package/crates/team-agent/src/lifecycle/launch/readiness.rs +85 -38
  70. package/crates/team-agent/src/lifecycle/launch/role_source.rs +44 -44
  71. package/crates/team-agent/src/lifecycle/launch/spawn.rs +101 -4
  72. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +244 -48
  73. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +109 -2
  74. package/crates/team-agent/src/lifecycle/launch/worker_env.rs +214 -1
  75. package/crates/team-agent/src/lifecycle/launch.rs +209 -43
  76. package/crates/team-agent/src/lifecycle/lock.rs +42 -1
  77. package/crates/team-agent/src/lifecycle/mod.rs +11 -0
  78. package/crates/team-agent/src/lifecycle/pane_input_lock.rs +161 -0
  79. package/crates/team-agent/src/lifecycle/profile_launch.rs +98 -0
  80. package/crates/team-agent/src/lifecycle/profile_smoke.rs +51 -1
  81. package/crates/team-agent/src/lifecycle/restart/agent.rs +213 -3
  82. package/crates/team-agent/src/lifecycle/restart/common.rs +373 -18
  83. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +27 -0
  84. package/crates/team-agent/src/lifecycle/restart/preflight.rs +25 -0
  85. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +165 -15
  86. package/crates/team-agent/src/lifecycle/restart/remove.rs +196 -5
  87. package/crates/team-agent/src/lifecycle/restart/selection.rs +72 -0
  88. package/crates/team-agent/src/lifecycle/restart/team_state.rs +22 -0
  89. package/crates/team-agent/src/lifecycle/restart.rs +29 -0
  90. package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -1
  91. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +3 -4
  92. package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +1 -1
  93. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -2
  94. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +4 -4
  95. package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +309 -0
  96. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +159 -174
  97. package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +2 -2
  98. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +15 -13
  99. package/crates/team-agent/src/lifecycle/tests/core.rs +4 -18
  100. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +1 -1
  101. package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +287 -0
  102. package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +229 -0
  103. package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +353 -0
  104. package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +1 -1
  105. package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +1 -1
  106. package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +784 -0
  107. package/crates/team-agent/src/lifecycle/tests/gate_fixtures.rs +562 -0
  108. package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +239 -0
  109. package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +498 -0
  110. package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +250 -0
  111. package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +293 -0
  112. package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -0
  113. package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +3 -3
  114. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +65 -40
  115. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +20 -32
  116. package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +17 -9
  117. package/crates/team-agent/src/lifecycle/tests/mcp_tool_name_format_red.rs +69 -0
  118. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +24 -23
  119. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +98 -62
  120. package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +2 -2
  121. package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +1 -1
  122. package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +1 -1
  123. package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +3 -1
  124. package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -2
  125. package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +1 -1
  126. package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +1 -1
  127. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +41 -8
  128. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +3 -3
  129. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1 -1
  130. package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +1 -1
  131. package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -1
  132. package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1 -1
  133. package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1 -1
  134. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +1 -1
  135. package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +60 -2
  136. package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +1 -1
  137. package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -2
  138. package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +69 -26
  139. package/crates/team-agent/src/lifecycle/tests.rs +23 -15
  140. package/crates/team-agent/src/lifecycle/types.rs +61 -2
  141. package/crates/team-agent/src/lifecycle/worker_command_context.rs +200 -33
  142. package/crates/team-agent/src/mcp_server/tests/scoped.rs +100 -1
  143. package/crates/team-agent/src/mcp_server/tests.rs +196 -2
  144. package/crates/team-agent/src/messaging/delivery.rs +405 -40
  145. package/crates/team-agent/src/messaging/helpers.rs +2 -0
  146. package/crates/team-agent/src/messaging/leader_receiver.rs +53 -1
  147. package/crates/team-agent/src/messaging/results.rs +4 -0
  148. package/crates/team-agent/src/messaging/send.rs +34 -0
  149. package/crates/team-agent/src/messaging/tests/dup_inject.rs +406 -0
  150. package/crates/team-agent/src/messaging/tests/e23.rs +9 -0
  151. package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +66 -0
  152. package/crates/team-agent/src/messaging/tests/main_preserved.rs +1 -1
  153. package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
  154. package/crates/team-agent/src/messaging/types.rs +4 -1
  155. package/crates/team-agent/src/model/enums.rs +14 -5
  156. package/crates/team-agent/src/model/permissions.rs +3 -0
  157. package/crates/team-agent/src/model/spec.rs +129 -6
  158. package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +3 -1
  159. package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +3 -1
  160. package/crates/team-agent/src/model/testdata/team.spec.yaml +3 -1
  161. package/crates/team-agent/src/os_probe.rs +73 -2
  162. package/crates/team-agent/src/provider/adapter.rs +231 -4
  163. package/crates/team-agent/src/provider/adapters/claude.rs +5 -2
  164. package/crates/team-agent/src/provider/adapters/codex.rs +5 -2
  165. package/crates/team-agent/src/provider/adapters/copilot.rs +5 -2
  166. package/crates/team-agent/src/provider/adapters/cursor_agent.rs +86 -0
  167. package/crates/team-agent/src/provider/adapters/grok.rs +157 -0
  168. package/crates/team-agent/src/provider/adapters/mod.rs +2 -0
  169. package/crates/team-agent/src/provider/bypass_flags.rs +124 -0
  170. package/crates/team-agent/src/provider/classify.rs +4 -2
  171. package/crates/team-agent/src/provider/faults.rs +2 -1
  172. package/crates/team-agent/src/provider/mod.rs +13 -0
  173. package/crates/team-agent/src/provider/session/capture.rs +158 -20
  174. package/crates/team-agent/src/provider/session/context_fork/claude.rs +38 -1
  175. package/crates/team-agent/src/provider/session/context_fork/codex.rs +72 -1
  176. package/crates/team-agent/src/provider/session/context_fork/outcome.rs +57 -1
  177. package/crates/team-agent/src/provider/session/context_fork.rs +77 -3
  178. package/crates/team-agent/src/provider/session/mod.rs +18 -0
  179. package/crates/team-agent/src/provider/session/resume.rs +57 -0
  180. package/crates/team-agent/src/provider/session_scan/claude.rs +125 -1
  181. package/crates/team-agent/src/provider/session_scan/codex.rs +94 -1
  182. package/crates/team-agent/src/provider/session_scan/common.rs +204 -2
  183. package/crates/team-agent/src/provider/session_scan/copilot.rs +33 -1
  184. package/crates/team-agent/src/provider/session_scan/cursor.rs +538 -0
  185. package/crates/team-agent/src/provider/session_scan/grok.rs +288 -0
  186. package/crates/team-agent/src/provider/session_scan.rs +8 -0
  187. package/crates/team-agent/src/provider/submit_now.rs +94 -0
  188. package/crates/team-agent/src/provider/tests/adapter.rs +304 -0
  189. package/crates/team-agent/src/provider/types.rs +4 -2
  190. package/crates/team-agent/src/provider/wire.rs +23 -1
  191. package/crates/team-agent/src/state/persist.rs +301 -8
  192. package/crates/team-agent/src/state/repository.rs +5 -0
  193. package/crates/team-agent/src/tmux_backend/tests.rs +1737 -42
  194. package/crates/team-agent/src/tmux_backend.rs +1083 -75
  195. package/crates/team-agent/src/transport/test_support.rs +20 -0
  196. package/crates/team-agent/src/transport/tests/wire.rs +28 -2
  197. package/crates/team-agent/src/transport.rs +265 -1
  198. package/npm/install.mjs +129 -73
  199. package/package.json +4 -4
  200. package/skills/team-agent/SKILL.md +33 -238
  201. package/skills/team-agent/command-coverage.json +31 -0
  202. package/crates/team-agent/src/lifecycle/launch/approval.rs +0 -134
  203. package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +0 -59
  204. package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -483
  205. package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
  206. package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
  207. package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +0 -1187
  208. package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +0 -327
@@ -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>)>>>;
@@ -201,7 +201,13 @@ impl crate::transport::Transport for LaneTransport {
201
201
  r: crate::transport::CaptureRange,
202
202
  ) -> Result<crate::transport::CapturedText, crate::transport::TransportError> {
203
203
  Ok(crate::transport::CapturedText {
204
- text: String::new(),
204
+ // A fresh worker has reached the ready prompt by the time this
205
+ // deterministic lifecycle replay observes its pane. Returning
206
+ // the stable ready marker keeps the replay focused on the
207
+ // single-lock transaction instead of burning the provider's
208
+ // bounded startup-prompt polling interval on an unobservable
209
+ // empty screen.
210
+ text: "OpenAI Codex\n›".to_string(),
205
211
  range: r,
206
212
  })
207
213
  }
@@ -816,7 +822,7 @@ fn current_alias_authority_does_not_leak_into_real_sibling_same_id_retirement()
816
822
  );
817
823
  }
818
824
 
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";
825
+ 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
826
 
821
827
  struct HermeticTestEnv {
822
828
  root: std::path::PathBuf,
@@ -1390,9 +1396,11 @@ fn remove_preserves_external_role_source_for_both_flag_forms() {
1390
1396
  }
1391
1397
  }
1392
1398
 
1393
- // A-28 RED: the role markdown recorded for a seat is still a user asset. The
1394
- // default remove path must unregister the seat without deleting the managed
1395
- // registration-path document.
1399
+ // A-28 was: default remove must preserve registered managed markdown.
1400
+ // ledger.seat-supply-prereq overturned that for files under
1401
+ // `.team/dynamic-role-files/` (framework materialization, not user asset).
1402
+ // Keep the test name; flip the assertion. External --role-file is covered
1403
+ // by `remove_preserves_external_role_source_for_both_flag_forms`.
1396
1404
  #[test]
1397
1405
  fn remove_agent_preserves_registered_role_markdown_by_default() {
1398
1406
  let ws = lanea_ws_agents(json!({
@@ -1417,15 +1425,13 @@ fn remove_agent_preserves_registered_role_markdown_by_default() {
1417
1425
  None,
1418
1426
  &LaneTransport::new("team-laneateam", &[]),
1419
1427
  );
1420
- assert!(result.is_ok(), "remove-agent should unregister the seat: {result:?}");
1421
1428
  assert!(
1422
- role.exists(),
1423
- "A-28: remove-agent must preserve the registered role markdown by default"
1429
+ result.is_ok(),
1430
+ "remove-agent should unregister the seat: {result:?}"
1424
1431
  );
1425
- assert_eq!(
1426
- std::fs::read(&role).unwrap(),
1427
- original,
1428
- "A-28: remove-agent must preserve the registered role markdown by default"
1432
+ assert!(
1433
+ !role.exists(),
1434
+ "managed role markdown must be gone after remove-agent so the next same-id clone is not blocked"
1429
1435
  );
1430
1436
  }
1431
1437
 
@@ -1455,7 +1461,35 @@ fn remove_preserves_managed_path_symlink_escape() {
1455
1461
  );
1456
1462
  assert!(result.is_ok(), "remove failed: {result:?}");
1457
1463
  assert_eq!(std::fs::read(&external).unwrap(), b"external target\n");
1458
- assert!(managed.exists(), "symlink escape must be preserved");
1464
+ assert!(
1465
+ !managed.symlink_metadata().is_ok(),
1466
+ "managed symlink must be unlinked; must not follow and delete the external target"
1467
+ );
1468
+ }
1469
+
1470
+ #[test]
1471
+ fn remove_agent_deletes_unrecorded_default_managed_role() {
1472
+ let ws = lanea_ws_agents(json!({
1473
+ "alpha": { "status": "stopped", "provider": "codex", "window": "alpha" },
1474
+ "bravo": { "status": "stopped", "provider": "codex", "window": "bravo" }
1475
+ }));
1476
+ let role = ws.join(".team").join("dynamic-role-files").join("alpha.md");
1477
+ std::fs::create_dir_all(role.parent().unwrap()).unwrap();
1478
+ std::fs::write(&role, b"unrecorded default managed copy\n").unwrap();
1479
+
1480
+ let result = remove_agent_with_transport(
1481
+ &ws,
1482
+ &aid("alpha"),
1483
+ true,
1484
+ true,
1485
+ None,
1486
+ &LaneTransport::new("team-laneateam", &[]),
1487
+ );
1488
+ assert!(result.is_ok(), "remove failed: {result:?}");
1489
+ assert!(
1490
+ !role.exists(),
1491
+ "default managed path must be cleared even when state.dynamic_role_file is unset"
1492
+ );
1459
1493
  }
1460
1494
 
1461
1495
  #[test]
@@ -1659,9 +1693,8 @@ fn lanea_fork_dup_target_leader_id_is_already_exists() {
1659
1693
  fork_agent_with_transport(&ws, &aid("alpha"), &aid("leader"), None, false, None, &tx)
1660
1694
  );
1661
1695
  assert!(
1662
- text.contains("already exists"),
1663
- "golden operations.py:301-302 (_find_agent matches the leader): forking ONTO the leader id must raise \
1664
- 'agent id already exists: leader'; Rust skips the dup guard for the leader id and proceeds. got {text}"
1696
+ text.contains("unverified") || text.contains("未验证") || text.contains("refuses --as"),
1697
+ "codex in-window fork is unverified; leader-id duplicate is not reached. got {text}"
1665
1698
  );
1666
1699
  }
1667
1700
 
@@ -1679,9 +1712,11 @@ fn lanea_fork_window_already_exists_guard_before_spec_mutation() {
1679
1712
  fork_agent_with_transport(&ws, &aid("alpha"), &aid("newfork"), None, false, None, &tx)
1680
1713
  );
1681
1714
  assert!(
1682
- text.contains("tmux window already exists for fork target: team-laneateam:newfork"),
1683
- "golden operations.py:310-312: a pre-existing target window must raise 'tmux window already exists for \
1684
- fork target: team-laneateam:newfork' BEFORE spec mutation; Rust has no guard. got {text}"
1715
+ text.contains("unverified")
1716
+ || text.contains("未验证")
1717
+ || text.contains("refuses --as")
1718
+ || text.contains("tmux window already exists for fork target: team-laneateam:newfork"),
1719
+ "codex in-window fork is unverified; window-exists guard is not reached. got {text}"
1685
1720
  );
1686
1721
  let spec_text = std::fs::read_to_string(ws.join("team.spec.yaml")).unwrap();
1687
1722
  assert!(
@@ -1705,9 +1740,8 @@ fn lanea_fork_gate_error_text_and_spec_rollback_on_adapter_arm() {
1705
1740
  fork_agent_with_transport(&ws, &aid("alpha"), &aid("newfork"), None, false, None, &tx);
1706
1741
  let text = format!("{result:?}");
1707
1742
  assert!(
1708
- text.contains("codex does not support native session fork"),
1709
- "golden operations.py:329-330: the native-fork gate must raise 'codex does not support native session \
1710
- fork'; Rust surfaces the generic 'capability unsupported: Codex:fork'. got {text}"
1743
+ text.contains("unverified") || text.contains("未验证"),
1744
+ "codex is unverified (any auth), not 'does not support'. got {text}"
1711
1745
  );
1712
1746
  let spec_text = std::fs::read_to_string(ws.join("team.spec.yaml")).unwrap();
1713
1747
  assert!(
@@ -1736,25 +1770,16 @@ fn lanea_fork_report_session_id_is_not_pane_id() {
1736
1770
  json!(discoverable_source.to_string_lossy().to_string());
1737
1771
  crate::state::persist::save_runtime_state(&ws, &state).unwrap();
1738
1772
  let tx = LaneTransport::new("team-laneateam", &[]);
1739
- let report =
1740
- fork_agent_with_transport(&ws, &aid("alpha"), &aid("newfork"), None, false, None, &tx)
1741
- .expect("fork ok (codex subscription supports fork)");
1742
- assert_ne!(
1743
- report.session_id,
1744
- Some(crate::provider::SessionId::new("%newfork")),
1745
- "golden operations.py:399,408: report.session_id is the captured provider session id / None, NEVER the \
1746
- tmux pane id; Rust returns Some(pane_id='%newfork')"
1747
- );
1773
+ let result =
1774
+ fork_agent_with_transport(&ws, &aid("alpha"), &aid("newfork"), None, false, None, &tx);
1775
+ let err = match result {
1776
+ Err(error) => error.to_string(),
1777
+ Ok(_) => panic!("codex in-window fork is unverified and must refuse"),
1778
+ };
1748
1779
  assert!(
1749
- report.session_id.is_some(),
1750
- "a successful fork must carry the verified NEW provider session id"
1780
+ err.contains("unverified") || err.contains("未验证"),
1781
+ "codex must refuse as unverified; got {err}"
1751
1782
  );
1752
- let state = crate::state::persist::load_runtime_state(&ws).unwrap();
1753
- let row = &state["agents"]["newfork"];
1754
- assert!(row["spawned_at"]
1755
- .as_str()
1756
- .is_some_and(|value| chrono::DateTime::parse_from_rfc3339(value).is_ok()));
1757
- assert_eq!(row["spawn_epoch"], serde_json::json!(1));
1758
1783
  }
1759
1784
 
1760
1785
  #[test]
@@ -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");
@@ -2653,10 +2638,11 @@ fn fork_refuses_source_with_partial_tuple() {
2653
2638
  );
2654
2639
  let err = result.unwrap_err().to_string();
2655
2640
  assert!(
2656
- err.contains("source session backing is missing")
2641
+ err.contains("unverified")
2642
+ || err.contains("未验证")
2657
2643
  || err.contains("backing")
2658
2644
  || err.contains("incomplete"),
2659
- "0.4.6: fork refusal must name backing/incomplete cause; got error={err}"
2645
+ "0.4.6/in-window: unverified providers refuse before backing check; got error={err}"
2660
2646
  );
2661
2647
  }
2662
2648
 
@@ -3084,6 +3070,8 @@ fn quick_start_running_agent_state_shape_after_spawn_is_golden() {
3084
3070
  "mcp_config",
3085
3071
  "permissions",
3086
3072
  "effective_approval_policy",
3073
+ // 0.5.66 bypass 单源:fresh spawn 记录运行面 bypass 值(restart drift 挡板)。
3074
+ "as_launched_dangerously_skip_permissions",
3087
3075
  "session_id",
3088
3076
  "rollout_path",
3089
3077
  "captured_at",
@@ -92,23 +92,31 @@ fn fork_agent_rollback_cleans_spawned_window_spec_state_and_mcp_after_post_spawn
92
92
 
93
93
  assert!(
94
94
  result.is_err(),
95
- "failure-injection hook {FAIL_FORK_AFTER_SPAWN}=save_runtime_state must fail after spawn so post-spawn rollback is exercised; result={result:?}"
95
+ "in-window fork on this fixture must refuse before spawn; result={result:?}"
96
96
  );
97
+ let err = format!("{result:?}");
97
98
  assert!(
98
- transport
99
- .killed()
100
- .contains(&"team-rollbackteam:newfork".to_string()),
101
- "fork post-spawn rollback must kill the already-spawned window; killed={:?} result={result:?}",
99
+ err.contains("unverified") || err.contains("未验证") || err.contains("refuses --as"),
100
+ "codex in-window fork is unverified / --as is refused; no spawn to roll back. result={result:?}"
101
+ );
102
+ assert!(
103
+ transport.killed().is_empty(),
104
+ "unverified/in-place fork must not spawn a window to kill; killed={:?} result={result:?}",
102
105
  transport.killed()
103
106
  );
104
107
  assert_eq!(
105
108
  spec_after, spec_before,
106
- "fork post-spawn rollback must restore team.spec.yaml byte-for-byte; result={result:?}"
109
+ "refused in-window fork must not mutate team.spec.yaml; result={result:?}"
110
+ );
111
+ assert!(
112
+ state_after
113
+ .pointer("/agents/newfork")
114
+ .is_none(),
115
+ "refused in-window fork must not create a newfork row; state={state_after}"
107
116
  );
108
- assert_failed_fork_has_only_target_tombstone(&state_after, "newfork", "rollbackteam");
109
117
  assert!(
110
118
  !mcp_path.exists(),
111
- "fork post-spawn rollback must cleanup the MCP config written for the half-created agent; leaked={}",
119
+ "refused in-window fork must not write MCP config; leaked={}",
112
120
  mcp_path.display()
113
121
  );
114
122
 
@@ -410,7 +418,7 @@ impl RollbackFixture {
410
418
 
411
419
  fn agent_doc(name: &str, role: &str) -> String {
412
420
  format!(
413
- "---\nname: {name}\nrole: {role}\nprovider: codex\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\n{role}.\n"
421
+ "---\nname: {name}\nrole: {role}\nprovider: codex\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\n{role}.\n"
414
422
  )
415
423
  }
416
424
 
@@ -0,0 +1,69 @@
1
+ //! purpose: worker 契约里的 MCP 工具名必须按 provider 实测书写形式渲染
2
+ //! contract:
3
+ //! claude → mcp__team_orchestrator__send_message / mcp__team_orchestrator__report_result
4
+ //! grok → team_orchestrator__send_message / team_orchestrator__report_result
5
+ //! 已验证 provider 的渲染结果不得再含 `team_orchestrator.` 点号
6
+ //! boundary: 只钉 compile_worker_system_prompt 字面;未验证 provider 不在此猜形式
7
+
8
+ #![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
9
+
10
+ use crate::lifecycle::worker_command_context::{compile_worker_system_prompt, WorkerCommandAgent};
11
+ use crate::model::enums::Provider;
12
+
13
+ fn compiled_prompt(provider: Provider) -> String {
14
+ let agent = WorkerCommandAgent::from_json(
15
+ &serde_json::json!({
16
+ "id": "w",
17
+ "role": "developer",
18
+ "tools": ["mcp_team"],
19
+ "output_contract": {"format": "result_envelope_v1"},
20
+ }),
21
+ Some("w"),
22
+ provider,
23
+ )
24
+ .expect("test agent");
25
+ compile_worker_system_prompt(&agent).expect("compile prompt")
26
+ }
27
+
28
+ #[test]
29
+ fn claude_runtime_contract_pins_mcp_double_underscore_literals() {
30
+ for provider in [Provider::Claude, Provider::ClaudeCode] {
31
+ let prompt = compiled_prompt(provider);
32
+ assert!(
33
+ prompt.contains("mcp__team_orchestrator__report_result"),
34
+ "{provider:?} must render the claude transcript literal mcp__team_orchestrator__report_result; prompt={prompt}"
35
+ );
36
+ assert!(
37
+ prompt.contains("mcp__team_orchestrator__send_message"),
38
+ "{provider:?} must render the claude transcript literal mcp__team_orchestrator__send_message; prompt={prompt}"
39
+ );
40
+ }
41
+ }
42
+
43
+ #[test]
44
+ fn grok_runtime_contract_pins_server_double_underscore_literals() {
45
+ let prompt = compiled_prompt(Provider::Grok);
46
+ assert!(
47
+ prompt.contains("team_orchestrator__report_result"),
48
+ "grok must render team_orchestrator__report_result; prompt={prompt}"
49
+ );
50
+ assert!(
51
+ prompt.contains("team_orchestrator__send_message"),
52
+ "grok must render team_orchestrator__send_message; prompt={prompt}"
53
+ );
54
+ assert!(
55
+ !prompt.contains("mcp__team_orchestrator"),
56
+ "grok must not use the claude mcp__ prefix; prompt={prompt}"
57
+ );
58
+ }
59
+
60
+ #[test]
61
+ fn verified_provider_contracts_do_not_teach_dotted_tool_names() {
62
+ for provider in [Provider::Claude, Provider::ClaudeCode, Provider::Grok] {
63
+ let prompt = compiled_prompt(provider);
64
+ assert!(
65
+ !prompt.contains("team_orchestrator."),
66
+ "{provider:?} contract must not contain the dotted form team_orchestrator.; prompt={prompt}"
67
+ );
68
+ }
69
+ }
@@ -204,17 +204,21 @@ fn add_fork_remove_share_lifecycle_lock_behavior() {
204
204
  Duration::from_millis(120),
205
205
  Duration::from_secs(5),
206
206
  );
207
- assert_lock_timeout(
208
- crate::lifecycle::fork_agent_with_transport(
209
- &fork_workspace,
210
- &aid("alpha"),
211
- &fork_agent,
212
- None,
213
- false,
214
- None,
215
- &fork_transport,
216
- ),
217
- "fork-agent",
207
+ let fork_held = crate::lifecycle::fork_agent_with_transport(
208
+ &fork_workspace,
209
+ &aid("alpha"),
210
+ &fork_agent,
211
+ None,
212
+ false,
213
+ None,
214
+ &fork_transport,
215
+ );
216
+ let fork_held_text = format!("{fork_held:?}");
217
+ assert!(
218
+ fork_held_text.contains("unverified")
219
+ || fork_held_text.contains("未验证")
220
+ || fork_held_text.contains("LifecycleLockTimeout"),
221
+ "codex in-window fork is unverified (lock is not reached); got {fork_held:?}"
218
222
  );
219
223
  assert!(
220
224
  fork_transport.spawns().is_empty(),
@@ -232,7 +236,7 @@ fn add_fork_remove_share_lifecycle_lock_behavior() {
232
236
  );
233
237
  drop(override_guard);
234
238
  drop(held);
235
- crate::lifecycle::fork_agent_with_transport(
239
+ let fork_after = crate::lifecycle::fork_agent_with_transport(
236
240
  &fork_workspace,
237
241
  &aid("alpha"),
238
242
  &fork_agent,
@@ -240,14 +244,11 @@ fn add_fork_remove_share_lifecycle_lock_behavior() {
240
244
  false,
241
245
  None,
242
246
  &fork_transport,
243
- )
244
- .expect("fork-agent succeeds after lock release");
247
+ );
248
+ let fork_after_text = format!("{fork_after:?}");
245
249
  assert!(
246
- crate::state::persist::load_runtime_state(&fork_workspace)
247
- .unwrap()
248
- .pointer("/agents/newfork")
249
- .is_some(),
250
- "released fork-agent must add the fork row"
250
+ fork_after_text.contains("unverified") || fork_after_text.contains("未验证"),
251
+ "codex in-window fork stays unverified after lock release; got {fork_after:?}"
251
252
  );
252
253
 
253
254
  let remove_workspace = lanea_team_ws("stopped");
@@ -463,8 +464,8 @@ fn team_dir_with_roles(role_docs: &[(&str, &str)]) -> PathBuf {
463
464
  }
464
465
 
465
466
  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";
467
+ 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";
468
+ 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
469
  let team = team_dir_with_roles(&[("w1.md", w1), ("w2.md", w2)]);
469
470
  let workspace = team.parent().expect("team workspace").to_path_buf();
470
471
  seed_healthy_coordinator(&workspace);
@@ -483,7 +484,7 @@ fn breal_workspace() -> (PathBuf, PathBuf) {
483
484
  }
484
485
 
485
486
  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";
487
+ 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
488
  let team = team_dir_with_roles(&[("w1.md", w1)]);
488
489
  let workspace = team.parent().expect("team workspace").to_path_buf();
489
490
  seed_healthy_coordinator(&workspace);
@@ -526,7 +527,7 @@ fn strip_runtime_command_context(workspace: &std::path::Path) {
526
527
 
527
528
  fn role_doc(id: &str) -> String {
528
529
  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"
530
+ "---\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
531
  )
531
532
  }
532
533