@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.
- package/Cargo.lock +8 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/Cargo.toml +6 -0
- package/crates/team-agent/src/cli/adapters.rs +6 -1
- package/crates/team-agent/src/cli/diagnose.rs +82 -0
- package/crates/team-agent/src/cli/emit.rs +3 -3
- package/crates/team-agent/src/cli/grok_slot.rs +299 -0
- package/crates/team-agent/src/cli/leader.rs +21 -7
- package/crates/team-agent/src/cli/leaders.rs +125 -1
- package/crates/team-agent/src/cli/mod.rs +20 -0
- package/crates/team-agent/src/cli/send/presentation.rs +7 -1
- package/crates/team-agent/src/cli/spec.rs +2 -0
- package/crates/team-agent/src/cli/status_port/compact.rs +28 -0
- package/crates/team-agent/src/cli/status_port/snapshot.rs +25 -1
- package/crates/team-agent/src/cli/tests/base.rs +26 -3
- package/crates/team-agent/src/cli/tests/compile.rs +1 -1
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +129 -19
- package/crates/team-agent/src/cli/tests/mod.rs +2 -2
- package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -1
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +387 -6
- package/crates/team-agent/src/cli/tests/status_send.rs +79 -10
- package/crates/team-agent/src/cli/tests/verb_validate.rs +1 -1
- package/crates/team-agent/src/communication_mode/mod.rs +6 -4
- package/crates/team-agent/src/compiler/tests.rs +23 -20
- package/crates/team-agent/src/compiler.rs +84 -5
- package/crates/team-agent/src/conpty/backend.rs +16 -0
- package/crates/team-agent/src/coordinator/backoff.rs +55 -0
- package/crates/team-agent/src/coordinator/conpty_shim.rs +82 -0
- package/crates/team-agent/src/coordinator/health.rs +173 -0
- package/crates/team-agent/src/coordinator/mod.rs +31 -0
- package/crates/team-agent/src/coordinator/orphan.rs +31 -0
- package/crates/team-agent/src/coordinator/runtime_detectors.rs +30 -0
- package/crates/team-agent/src/coordinator/runtime_observation.rs +25 -0
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +59 -0
- package/crates/team-agent/src/coordinator/steps/delivery.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/health_sync.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/mod.rs +22 -0
- package/crates/team-agent/src/coordinator/steps/persist.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/session_gate.rs +10 -0
- package/crates/team-agent/src/coordinator/tick.rs +133 -22
- package/crates/team-agent/src/coordinator/types.rs +49 -0
- package/crates/team-agent/src/db/message_store.rs +39 -5
- package/crates/team-agent/src/layout/worker_env.rs +20 -3
- package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
- package/crates/team-agent/src/leader/provider_attribution.rs +53 -0
- package/crates/team-agent/src/leader/registry.rs +65 -0
- package/crates/team-agent/src/leader/start.rs +1080 -58
- package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/display.rs +56 -0
- package/crates/team-agent/src/lifecycle/helpers.rs +57 -0
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +332 -14
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +69 -2
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +196 -2
- package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +101 -11
- package/crates/team-agent/src/lifecycle/launch/cursor_create_chat.rs +229 -0
- package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +332 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +204 -457
- package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +24 -0
- package/crates/team-agent/src/lifecycle/launch/grok_per_seat.rs +260 -0
- package/crates/team-agent/src/lifecycle/launch/identity.rs +146 -0
- package/crates/team-agent/src/lifecycle/launch/layout.rs +60 -0
- package/crates/team-agent/src/lifecycle/launch/leader_context.rs +112 -0
- package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +530 -0
- package/crates/team-agent/src/lifecycle/launch/ownership.rs +40 -0
- package/crates/team-agent/src/lifecycle/launch/plan.rs +31 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +66 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +78 -0
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +85 -38
- package/crates/team-agent/src/lifecycle/launch/role_source.rs +44 -44
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +101 -4
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +244 -48
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +109 -2
- package/crates/team-agent/src/lifecycle/launch/worker_env.rs +214 -1
- package/crates/team-agent/src/lifecycle/launch.rs +209 -43
- package/crates/team-agent/src/lifecycle/lock.rs +42 -1
- package/crates/team-agent/src/lifecycle/mod.rs +11 -0
- package/crates/team-agent/src/lifecycle/pane_input_lock.rs +161 -0
- package/crates/team-agent/src/lifecycle/profile_launch.rs +98 -0
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +51 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +213 -3
- package/crates/team-agent/src/lifecycle/restart/common.rs +373 -18
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +27 -0
- package/crates/team-agent/src/lifecycle/restart/preflight.rs +25 -0
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +165 -15
- package/crates/team-agent/src/lifecycle/restart/remove.rs +196 -5
- package/crates/team-agent/src/lifecycle/restart/selection.rs +72 -0
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +22 -0
- package/crates/team-agent/src/lifecycle/restart.rs +29 -0
- package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +3 -4
- package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +4 -4
- package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +309 -0
- package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +159 -174
- package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +15 -13
- package/crates/team-agent/src/lifecycle/tests/core.rs +4 -18
- package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +287 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +229 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +353 -0
- package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +784 -0
- package/crates/team-agent/src/lifecycle/tests/gate_fixtures.rs +562 -0
- package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +239 -0
- package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +498 -0
- package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +250 -0
- package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +293 -0
- package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -0
- package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +65 -40
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +20 -32
- package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +17 -9
- package/crates/team-agent/src/lifecycle/tests/mcp_tool_name_format_red.rs +69 -0
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +24 -23
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +98 -62
- package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +3 -1
- package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +41 -8
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +60 -2
- package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +69 -26
- package/crates/team-agent/src/lifecycle/tests.rs +23 -15
- package/crates/team-agent/src/lifecycle/types.rs +61 -2
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +200 -33
- package/crates/team-agent/src/mcp_server/tests/scoped.rs +100 -1
- package/crates/team-agent/src/mcp_server/tests.rs +196 -2
- package/crates/team-agent/src/messaging/delivery.rs +405 -40
- package/crates/team-agent/src/messaging/helpers.rs +2 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +53 -1
- package/crates/team-agent/src/messaging/results.rs +4 -0
- package/crates/team-agent/src/messaging/send.rs +34 -0
- package/crates/team-agent/src/messaging/tests/dup_inject.rs +406 -0
- package/crates/team-agent/src/messaging/tests/e23.rs +9 -0
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +66 -0
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +1 -1
- package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
- package/crates/team-agent/src/messaging/types.rs +4 -1
- package/crates/team-agent/src/model/enums.rs +14 -5
- package/crates/team-agent/src/model/permissions.rs +3 -0
- package/crates/team-agent/src/model/spec.rs +129 -6
- package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.yaml +3 -1
- package/crates/team-agent/src/os_probe.rs +73 -2
- package/crates/team-agent/src/provider/adapter.rs +231 -4
- package/crates/team-agent/src/provider/adapters/claude.rs +5 -2
- package/crates/team-agent/src/provider/adapters/codex.rs +5 -2
- package/crates/team-agent/src/provider/adapters/copilot.rs +5 -2
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +86 -0
- package/crates/team-agent/src/provider/adapters/grok.rs +157 -0
- package/crates/team-agent/src/provider/adapters/mod.rs +2 -0
- package/crates/team-agent/src/provider/bypass_flags.rs +124 -0
- package/crates/team-agent/src/provider/classify.rs +4 -2
- package/crates/team-agent/src/provider/faults.rs +2 -1
- package/crates/team-agent/src/provider/mod.rs +13 -0
- package/crates/team-agent/src/provider/session/capture.rs +158 -20
- package/crates/team-agent/src/provider/session/context_fork/claude.rs +38 -1
- package/crates/team-agent/src/provider/session/context_fork/codex.rs +72 -1
- package/crates/team-agent/src/provider/session/context_fork/outcome.rs +57 -1
- package/crates/team-agent/src/provider/session/context_fork.rs +77 -3
- package/crates/team-agent/src/provider/session/mod.rs +18 -0
- package/crates/team-agent/src/provider/session/resume.rs +57 -0
- package/crates/team-agent/src/provider/session_scan/claude.rs +125 -1
- package/crates/team-agent/src/provider/session_scan/codex.rs +94 -1
- package/crates/team-agent/src/provider/session_scan/common.rs +204 -2
- package/crates/team-agent/src/provider/session_scan/copilot.rs +33 -1
- package/crates/team-agent/src/provider/session_scan/cursor.rs +538 -0
- package/crates/team-agent/src/provider/session_scan/grok.rs +288 -0
- package/crates/team-agent/src/provider/session_scan.rs +8 -0
- package/crates/team-agent/src/provider/submit_now.rs +94 -0
- package/crates/team-agent/src/provider/tests/adapter.rs +304 -0
- package/crates/team-agent/src/provider/types.rs +4 -2
- package/crates/team-agent/src/provider/wire.rs +23 -1
- package/crates/team-agent/src/state/persist.rs +301 -8
- package/crates/team-agent/src/state/repository.rs +5 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +1737 -42
- package/crates/team-agent/src/tmux_backend.rs +1083 -75
- package/crates/team-agent/src/transport/test_support.rs +20 -0
- package/crates/team-agent/src/transport/tests/wire.rs +28 -2
- package/crates/team-agent/src/transport.rs +265 -1
- package/npm/install.mjs +129 -73
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +33 -238
- package/skills/team-agent/command-coverage.json +31 -0
- package/crates/team-agent/src/lifecycle/launch/approval.rs +0 -134
- package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +0 -59
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -483
- package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
- package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
- package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +0 -1187
- package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +0 -327
|
@@ -102,18 +102,10 @@ fn phase_b_golden_events_state_status_zero_drift() {
|
|
|
102
102
|
team_key: "teamdir",
|
|
103
103
|
lifecycle_op: phase_b_reset_discard_session,
|
|
104
104
|
});
|
|
105
|
-
|
|
106
|
-
std::fs::create_dir_all(baseline.parent().expect("baseline parent")).unwrap();
|
|
107
|
-
std::fs::write(&baseline, pretty(&actual)).unwrap();
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
let expected = std::fs::read_to_string(&baseline)
|
|
111
|
-
.unwrap_or_else(|e| panic!("missing golden baseline {}: {e}", baseline.display()));
|
|
112
|
-
let expected: Value = serde_json::from_str(&expected).expect("parse golden baseline");
|
|
105
|
+
let expected = load_phase_golden(&baseline);
|
|
113
106
|
assert_eq!(
|
|
114
|
-
actual,
|
|
115
|
-
|
|
116
|
-
"phase B golden drift; update intentionally with TEAM_AGENT_UPDATE_GOLDEN=1 only after review"
|
|
107
|
+
actual, expected,
|
|
108
|
+
"phase B golden drift; surgical key edits only — do not regenerate the file"
|
|
117
109
|
);
|
|
118
110
|
}
|
|
119
111
|
|
|
@@ -126,18 +118,10 @@ fn phase_c_golden_events_state_status_zero_drift() {
|
|
|
126
118
|
team_key: "teamdir",
|
|
127
119
|
lifecycle_op: phase_b_reset_discard_session,
|
|
128
120
|
});
|
|
129
|
-
|
|
130
|
-
std::fs::create_dir_all(baseline.parent().expect("baseline parent")).unwrap();
|
|
131
|
-
std::fs::write(&baseline, pretty(&actual)).unwrap();
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
let expected = std::fs::read_to_string(&baseline)
|
|
135
|
-
.unwrap_or_else(|e| panic!("missing golden baseline {}: {e}", baseline.display()));
|
|
136
|
-
let expected: Value = serde_json::from_str(&expected).expect("parse golden baseline");
|
|
121
|
+
let expected = load_phase_golden(&baseline);
|
|
137
122
|
assert_eq!(
|
|
138
|
-
actual,
|
|
139
|
-
|
|
140
|
-
"phase C golden drift; update intentionally with TEAM_AGENT_UPDATE_GOLDEN=1 only after review"
|
|
123
|
+
actual, expected,
|
|
124
|
+
"phase C golden drift; surgical key edits only — do not regenerate the file"
|
|
141
125
|
);
|
|
142
126
|
}
|
|
143
127
|
|
|
@@ -150,18 +134,10 @@ fn phase_d_golden_events_state_status_zero_drift() {
|
|
|
150
134
|
team_key: "teamdir",
|
|
151
135
|
lifecycle_op: phase_b_reset_discard_session,
|
|
152
136
|
});
|
|
153
|
-
|
|
154
|
-
std::fs::create_dir_all(baseline.parent().expect("baseline parent")).unwrap();
|
|
155
|
-
std::fs::write(&baseline, pretty(&actual)).unwrap();
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
let expected = std::fs::read_to_string(&baseline)
|
|
159
|
-
.unwrap_or_else(|e| panic!("missing golden baseline {}: {e}", baseline.display()));
|
|
160
|
-
let expected: Value = serde_json::from_str(&expected).expect("parse golden baseline");
|
|
137
|
+
let expected = load_phase_golden(&baseline);
|
|
161
138
|
assert_eq!(
|
|
162
|
-
actual,
|
|
163
|
-
|
|
164
|
-
"phase D golden drift; update intentionally with TEAM_AGENT_UPDATE_GOLDEN=1 only after review"
|
|
139
|
+
actual, expected,
|
|
140
|
+
"phase D golden drift; surgical key edits only — do not regenerate the file"
|
|
165
141
|
);
|
|
166
142
|
}
|
|
167
143
|
|
|
@@ -174,18 +150,10 @@ fn phase_e_golden_events_state_status_zero_drift() {
|
|
|
174
150
|
team_key: "teamdir",
|
|
175
151
|
lifecycle_op: phase_b_reset_discard_session,
|
|
176
152
|
});
|
|
177
|
-
|
|
178
|
-
std::fs::create_dir_all(baseline.parent().expect("baseline parent")).unwrap();
|
|
179
|
-
std::fs::write(&baseline, pretty(&actual)).unwrap();
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
let expected = std::fs::read_to_string(&baseline)
|
|
183
|
-
.unwrap_or_else(|e| panic!("missing golden baseline {}: {e}", baseline.display()));
|
|
184
|
-
let expected: Value = serde_json::from_str(&expected).expect("parse golden baseline");
|
|
153
|
+
let expected = load_phase_golden(&baseline);
|
|
185
154
|
assert_eq!(
|
|
186
|
-
actual,
|
|
187
|
-
|
|
188
|
-
"phase E golden drift; update intentionally with TEAM_AGENT_UPDATE_GOLDEN=1 only after review"
|
|
155
|
+
actual, expected,
|
|
156
|
+
"phase E golden drift; surgical key edits only — do not regenerate the file"
|
|
189
157
|
);
|
|
190
158
|
}
|
|
191
159
|
|
|
@@ -198,18 +166,39 @@ fn phase_f_golden_events_state_status_zero_drift() {
|
|
|
198
166
|
team_key: "teamdir",
|
|
199
167
|
lifecycle_op: phase_b_reset_discard_session,
|
|
200
168
|
});
|
|
201
|
-
|
|
202
|
-
std::fs::create_dir_all(baseline.parent().expect("baseline parent")).unwrap();
|
|
203
|
-
std::fs::write(&baseline, pretty(&actual)).unwrap();
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
let expected = std::fs::read_to_string(&baseline)
|
|
207
|
-
.unwrap_or_else(|e| panic!("missing golden baseline {}: {e}", baseline.display()));
|
|
208
|
-
let expected: Value = serde_json::from_str(&expected).expect("parse golden baseline");
|
|
169
|
+
let expected = load_phase_golden(&baseline);
|
|
209
170
|
assert_eq!(
|
|
210
|
-
actual,
|
|
211
|
-
|
|
212
|
-
|
|
171
|
+
actual, expected,
|
|
172
|
+
"phase F golden drift; surgical key edits only — do not regenerate the file"
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
#[test]
|
|
177
|
+
#[serial_test::serial(env)]
|
|
178
|
+
fn phase_golden_unbound_not_ready_requires_claim_leader() {
|
|
179
|
+
// 已废除的行为:旧实现只说不 ready、不说怎么恢复,把人晾在原地。此断言证明它确实没了。
|
|
180
|
+
// Independent of golden.json so a later regenerate cannot erase it.
|
|
181
|
+
// Companion status keys grok_slot ← 77bddb95 / 0c319cca; next_action ← 080903a2.
|
|
182
|
+
let compact = run_compact_status_after_quick_start();
|
|
183
|
+
let not_ready = compact
|
|
184
|
+
.get("not_ready")
|
|
185
|
+
.and_then(Value::as_object)
|
|
186
|
+
.unwrap_or_else(|| panic!("compact must carry not_ready; got {compact}"));
|
|
187
|
+
let reasons = not_ready
|
|
188
|
+
.get("reasons")
|
|
189
|
+
.and_then(Value::as_array)
|
|
190
|
+
.cloned()
|
|
191
|
+
.unwrap_or_default();
|
|
192
|
+
assert!(
|
|
193
|
+
reasons
|
|
194
|
+
.iter()
|
|
195
|
+
.any(|reason| reason.as_str() == Some("leader_receiver_unbound")),
|
|
196
|
+
"tombstone fixture must still produce leader_receiver_unbound; not_ready={not_ready:?}"
|
|
197
|
+
);
|
|
198
|
+
assert_eq!(
|
|
199
|
+
not_ready.get("next_action").and_then(Value::as_str),
|
|
200
|
+
Some("claim-leader"),
|
|
201
|
+
"when not_ready.reasons contains leader_receiver_unbound, next_action must be claim-leader; not_ready={not_ready:?}"
|
|
213
202
|
);
|
|
214
203
|
}
|
|
215
204
|
|
|
@@ -387,7 +376,7 @@ fn codex_ready_transport() -> OfflineTransport {
|
|
|
387
376
|
|
|
388
377
|
fn role_doc(id: &str) -> String {
|
|
389
378
|
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"
|
|
379
|
+
"---\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
380
|
)
|
|
392
381
|
}
|
|
393
382
|
|
|
@@ -466,6 +455,59 @@ fn phase_fixture_path(phase: &str) -> PathBuf {
|
|
|
466
455
|
.join(phase)
|
|
467
456
|
}
|
|
468
457
|
|
|
458
|
+
fn load_phase_golden(path: &Path) -> Value {
|
|
459
|
+
let text = std::fs::read_to_string(path)
|
|
460
|
+
.unwrap_or_else(|e| panic!("missing golden baseline {}: {e}", path.display()));
|
|
461
|
+
let stripped = strip_full_line_json_comments(&text);
|
|
462
|
+
serde_json::from_str(&stripped).unwrap_or_else(|e| {
|
|
463
|
+
panic!(
|
|
464
|
+
"parse golden baseline {} after stripping // review comments: {e}",
|
|
465
|
+
path.display()
|
|
466
|
+
)
|
|
467
|
+
})
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
fn strip_full_line_json_comments(text: &str) -> String {
|
|
471
|
+
text.lines()
|
|
472
|
+
.filter(|line| !line.trim_start().starts_with("//"))
|
|
473
|
+
.collect::<Vec<_>>()
|
|
474
|
+
.join("\n")
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
fn run_compact_status_after_quick_start() -> Value {
|
|
478
|
+
let hermetic = HermeticTestEnv::enter("tombstone-claim-leader");
|
|
479
|
+
let _permission_mode = EnvVarGuard::set(ANCESTRY_ENV, "[]");
|
|
480
|
+
let team = two_worker_team_dir(&hermetic);
|
|
481
|
+
let workspace = team.parent().expect("workspace").to_path_buf();
|
|
482
|
+
seed_healthy_coordinator(&workspace);
|
|
483
|
+
let launch_transport = codex_ready_transport();
|
|
484
|
+
quick_start_with_transport_in_workspace_with_display(
|
|
485
|
+
&workspace,
|
|
486
|
+
&team,
|
|
487
|
+
None,
|
|
488
|
+
true,
|
|
489
|
+
None,
|
|
490
|
+
&launch_transport,
|
|
491
|
+
false,
|
|
492
|
+
)
|
|
493
|
+
.expect("tombstone fixture must quick-start");
|
|
494
|
+
let status = cmd_status(&StatusArgs {
|
|
495
|
+
agent: None,
|
|
496
|
+
workspace,
|
|
497
|
+
detail: false,
|
|
498
|
+
summary: false,
|
|
499
|
+
json: true,
|
|
500
|
+
team: Some("teamdir".to_string()),
|
|
501
|
+
});
|
|
502
|
+
match cmd_value(status) {
|
|
503
|
+
Value::Object(map) => map
|
|
504
|
+
.get("output")
|
|
505
|
+
.cloned()
|
|
506
|
+
.unwrap_or_else(|| panic!("compact status missing output")),
|
|
507
|
+
other => panic!("compact status must be a JSON object, got {other}"),
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
469
511
|
struct EnvVarGuard {
|
|
470
512
|
key: &'static str,
|
|
471
513
|
previous: Option<String>,
|
|
@@ -843,12 +885,6 @@ fn key_is_id(key: &str, text: &str) -> bool {
|
|
|
843
885
|
|| text.starts_with("msg_")
|
|
844
886
|
}
|
|
845
887
|
|
|
846
|
-
fn pretty(value: &Value) -> String {
|
|
847
|
-
let mut text = serde_json::to_string_pretty(value).unwrap();
|
|
848
|
-
text.push('\n');
|
|
849
|
-
text
|
|
850
|
-
}
|
|
851
|
-
|
|
852
888
|
#[test]
|
|
853
889
|
fn phase_golden_normalizer_maps_custom_cargo_target_deps_binary_before_tmp_aliasing() {
|
|
854
890
|
let ctx = NormalizeCtx {
|
|
@@ -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
|
)
|
|
@@ -1066,6 +1066,8 @@ fn provider_name(provider: Provider) -> &'static str {
|
|
|
1066
1066
|
Provider::Codex => "codex",
|
|
1067
1067
|
Provider::Copilot => "copilot",
|
|
1068
1068
|
Provider::GeminiCli => "gemini",
|
|
1069
|
+
Provider::Grok => "grok",
|
|
1070
|
+
Provider::CursorAgent => "cursor_agent",
|
|
1069
1071
|
Provider::Fake => "fake",
|
|
1070
1072
|
}
|
|
1071
1073
|
}
|
|
@@ -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) {
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: restart spawn overlap and identity contracts
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: restart_spawn_overlap_identity
|
|
6
|
+
//! what: controlled transport facts prove overlapping worker spawns and stable persisted identities
|
|
7
|
+
//! depends:
|
|
8
|
+
//! - crate::lifecycle::restart
|
|
9
|
+
//! - hermetic fake transport with spawn trace
|
|
10
|
+
//! boundary:
|
|
11
|
+
//! - overlap/max-in-flight facts replace scheduler-sensitive total-time thresholds
|
|
12
|
+
//! - no production scheduler, pool, or state-machine changes
|
|
13
|
+
//! maturity: wired
|
|
14
|
+
//! ---
|
|
15
|
+
//!
|
|
1
16
|
//! 0.5.38 RED contract: startup latency instrumentation and bounded worker spawn.
|
|
2
17
|
//!
|
|
3
18
|
//! References:
|
|
@@ -83,6 +98,10 @@ fn restart_records_parallel_spawn_overlap_and_deterministic_state() {
|
|
|
83
98
|
"expected at least two new-window spawns to overlap after the initial session creator; calls={calls:?}"
|
|
84
99
|
));
|
|
85
100
|
}
|
|
101
|
+
assert!(
|
|
102
|
+
transport.max_in_flight() >= 2,
|
|
103
|
+
"expected at least two concurrent spawn calls; calls={calls:?}"
|
|
104
|
+
);
|
|
86
105
|
let agent_keys = state
|
|
87
106
|
.pointer("/agents")
|
|
88
107
|
.and_then(Value::as_object)
|
|
@@ -272,7 +291,6 @@ fn eight_worker_restart_with_100ms_spawn_delay_beats_serial_baseline() {
|
|
|
272
291
|
let delay = Duration::from_millis(100);
|
|
273
292
|
let transport = StartupLatencyTransport::new().with_spawn_delay(delay);
|
|
274
293
|
|
|
275
|
-
let started = Instant::now();
|
|
276
294
|
let report = restart_with_transport_with_readiness_deadline(
|
|
277
295
|
&case.workspace,
|
|
278
296
|
true,
|
|
@@ -281,7 +299,6 @@ fn eight_worker_restart_with_100ms_spawn_delay_beats_serial_baseline() {
|
|
|
281
299
|
Some(2_000),
|
|
282
300
|
)
|
|
283
301
|
.expect("R4 setup: restart should complete against delayed transport");
|
|
284
|
-
let elapsed = started.elapsed();
|
|
285
302
|
assert!(
|
|
286
303
|
matches!(report, RestartReport::Restarted { .. }),
|
|
287
304
|
"R4 setup: restart must reach Restarted; report={report:?}"
|
|
@@ -293,14 +310,18 @@ fn eight_worker_restart_with_100ms_spawn_delay_beats_serial_baseline() {
|
|
|
293
310
|
8,
|
|
294
311
|
"R4 setup: timing smoke must exercise all 8 workers; calls={calls:?}"
|
|
295
312
|
);
|
|
296
|
-
let serial_baseline = delay * calls.len() as u32;
|
|
297
313
|
assert!(
|
|
298
|
-
|
|
299
|
-
|
|
314
|
+
has_overlap(
|
|
315
|
+
&calls
|
|
316
|
+
.iter()
|
|
317
|
+
.filter(|call| call.kind == "spawn_into")
|
|
318
|
+
.collect::<Vec<_>>(),
|
|
319
|
+
),
|
|
320
|
+
"R4: bounded-concurrency restart must overlap spawn calls; calls={calls:?}"
|
|
300
321
|
);
|
|
301
322
|
assert!(
|
|
302
|
-
|
|
303
|
-
"R4:
|
|
323
|
+
transport.max_in_flight() >= 2,
|
|
324
|
+
"R4: max in-flight spawn count must prove overlap; calls={calls:?}"
|
|
304
325
|
);
|
|
305
326
|
assert_phase_events(&case.events(), "restart.phase", &["spawn_all", "completed"]);
|
|
306
327
|
}
|
|
@@ -488,7 +509,7 @@ fn write_team_docs(team_dir: &Path, workers: usize) {
|
|
|
488
509
|
|
|
489
510
|
fn role_doc(worker: &str) -> String {
|
|
490
511
|
format!(
|
|
491
|
-
"---\nname: {worker}\nrole: Worker {worker}\nprovider: fake\nmodel: fake\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\n{worker}.\n"
|
|
512
|
+
"---\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"
|
|
492
513
|
)
|
|
493
514
|
}
|
|
494
515
|
|
|
@@ -583,6 +604,8 @@ struct TransportState {
|
|
|
583
604
|
calls: Vec<SpawnCall>,
|
|
584
605
|
panes: BTreeMap<String, PaneInfo>,
|
|
585
606
|
next_pane: usize,
|
|
607
|
+
in_flight: usize,
|
|
608
|
+
max_in_flight: usize,
|
|
586
609
|
}
|
|
587
610
|
|
|
588
611
|
#[derive(Clone)]
|
|
@@ -625,6 +648,10 @@ impl StartupLatencyTransport {
|
|
|
625
648
|
self.state.lock().unwrap().calls.clone()
|
|
626
649
|
}
|
|
627
650
|
|
|
651
|
+
fn max_in_flight(&self) -> usize {
|
|
652
|
+
self.state.lock().unwrap().max_in_flight
|
|
653
|
+
}
|
|
654
|
+
|
|
628
655
|
fn pane_snapshot(&self) -> Vec<Value> {
|
|
629
656
|
self.state
|
|
630
657
|
.lock()
|
|
@@ -648,11 +675,17 @@ impl StartupLatencyTransport {
|
|
|
648
675
|
window: &WindowName,
|
|
649
676
|
) -> Result<SpawnResult, TransportError> {
|
|
650
677
|
let start = Instant::now();
|
|
678
|
+
{
|
|
679
|
+
let mut state = self.state.lock().unwrap();
|
|
680
|
+
state.in_flight += 1;
|
|
681
|
+
state.max_in_flight = state.max_in_flight.max(state.in_flight);
|
|
682
|
+
}
|
|
651
683
|
if !self.spawn_delay.is_zero() {
|
|
652
684
|
std::thread::sleep(self.spawn_delay);
|
|
653
685
|
}
|
|
654
686
|
let end = Instant::now();
|
|
655
687
|
let mut state = self.state.lock().unwrap();
|
|
688
|
+
state.in_flight -= 1;
|
|
656
689
|
let pane_id = PaneId::new(format!("%{}", state.next_pane));
|
|
657
690
|
state.next_pane += 1;
|
|
658
691
|
state.calls.push(SpawnCall {
|
|
@@ -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
|
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
//! purpose: contracts that tests touching product side effects stay inside one hermetic HOME/registry/socket/env boundary
|
|
2
|
+
//! contract: R1-R6 source guards; R6 needles match as identifiers, not as substrings of longer names
|
|
3
|
+
//! boundary: does not own HermeticTestEnv; does not drop or whitelist R6 needles
|
|
4
|
+
//!
|
|
1
5
|
//! task#8 RED contract: tests that exercise Team Agent product side effects
|
|
2
6
|
//! must run inside one HOME/registry/socket/env hermetic boundary.
|
|
3
7
|
//!
|
|
@@ -233,6 +237,40 @@ fn r6_static_guard_rejects_dangerous_tests_without_hermetic_boundary() {
|
|
|
233
237
|
);
|
|
234
238
|
}
|
|
235
239
|
|
|
240
|
+
#[test]
|
|
241
|
+
fn r6_mcp_tool_name_literal_is_not_message_plane_send() {
|
|
242
|
+
let source = r#"
|
|
243
|
+
assert!(prompt.contains("mcp__team_orchestrator__send_message"));
|
|
244
|
+
assert!(prompt.contains("team_orchestrator__send_message"));
|
|
245
|
+
"#;
|
|
246
|
+
assert!(
|
|
247
|
+
static_guard_offenders([(
|
|
248
|
+
"synthetic_mcp_tool_name_literal.rs".to_string(),
|
|
249
|
+
source.to_string()
|
|
250
|
+
)])
|
|
251
|
+
.is_empty(),
|
|
252
|
+
"MCP tool-name literals are not the send_message surface"
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
#[test]
|
|
257
|
+
fn r6_bare_send_message_identifier_without_hermetic_is_still_offender() {
|
|
258
|
+
let source = r#"
|
|
259
|
+
fn tap() {
|
|
260
|
+
let _ = send_message("to", "body");
|
|
261
|
+
}
|
|
262
|
+
"#;
|
|
263
|
+
let offenders = static_guard_offenders([(
|
|
264
|
+
"synthetic_bare_send_message.rs".to_string(),
|
|
265
|
+
source.to_string(),
|
|
266
|
+
)]);
|
|
267
|
+
assert!(
|
|
268
|
+
offenders.iter().any(|offender| offender.path == "synthetic_bare_send_message.rs"
|
|
269
|
+
&& offender.signals.contains(&"send_message")),
|
|
270
|
+
"bare send_message identifier without HermeticTestEnv must remain an offender; offenders={offenders:?}"
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
236
274
|
fn assert_contains_all(label: &str, source: &str, needles: &[&str], message: &str) {
|
|
237
275
|
let missing = needles
|
|
238
276
|
.iter()
|
|
@@ -315,10 +353,30 @@ fn dangerous_signals(source: &str) -> Vec<&'static str> {
|
|
|
315
353
|
"attach-leader",
|
|
316
354
|
]
|
|
317
355
|
.into_iter()
|
|
318
|
-
.filter(|needle| source
|
|
356
|
+
.filter(|needle| contains_identifier(source, needle))
|
|
319
357
|
.collect()
|
|
320
358
|
}
|
|
321
359
|
|
|
360
|
+
fn contains_identifier(source: &str, ident: &str) -> bool {
|
|
361
|
+
let bytes = source.as_bytes();
|
|
362
|
+
let mut start = 0;
|
|
363
|
+
while let Some(rel) = source[start..].find(ident) {
|
|
364
|
+
let idx = start + rel;
|
|
365
|
+
let before_ok = idx == 0 || !is_ident_char(bytes[idx - 1]);
|
|
366
|
+
let after = idx + ident.len();
|
|
367
|
+
let after_ok = after == bytes.len() || !is_ident_char(bytes[after]);
|
|
368
|
+
if before_ok && after_ok {
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
start = idx + 1;
|
|
372
|
+
}
|
|
373
|
+
false
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
fn is_ident_char(b: u8) -> bool {
|
|
377
|
+
matches!(b, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'_')
|
|
378
|
+
}
|
|
379
|
+
|
|
322
380
|
fn dangerous_test_files() -> Vec<(String, String)> {
|
|
323
381
|
let root = repo_root();
|
|
324
382
|
let mut files = Vec::new();
|
|
@@ -393,7 +451,7 @@ impl RestartAutoAttachCase {
|
|
|
393
451
|
std::fs::write(
|
|
394
452
|
workspace.join("agents").join(format!("{WORKER}.md")),
|
|
395
453
|
format!(
|
|
396
|
-
"---\nname: {WORKER}\nrole: Worker {WORKER}\nprovider: fake\nmodel: fake\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nWorker.\n"
|
|
454
|
+
"---\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
455
|
),
|
|
398
456
|
)
|
|
399
457
|
.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)]
|