@team-agent/installer 0.5.119 → 0.5.121
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 +1 -1
- package/Cargo.toml +1 -1
- package/README.md +46 -1
- package/README.zh.md +46 -1
- package/crates/team-agent/src/cli/adapters.rs +11 -0
- package/crates/team-agent/src/cli/arguments.rs +401 -0
- package/crates/team-agent/src/cli/emit.rs +241 -142
- package/crates/team-agent/src/cli/intent/runtime.rs +1175 -0
- package/crates/team-agent/src/cli/intent/runtime_tests.rs +698 -0
- package/crates/team-agent/src/cli/intent/tests.rs +654 -0
- package/crates/team-agent/src/cli/intent.rs +642 -0
- package/crates/team-agent/src/cli/mod.rs +53 -6
- package/crates/team-agent/src/cli/named_address.rs +10 -1
- package/crates/team-agent/src/cli/spec.rs +22 -10
- package/crates/team-agent/src/cli/status_port/brief.rs +117 -16
- package/crates/team-agent/src/cli/tests/base.rs +6 -1
- package/crates/team-agent/src/cli/tests/parser_safety.rs +383 -0
- package/crates/team-agent/src/cli/tests/run_delegation.rs +63 -19
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +56 -0
- package/crates/team-agent/src/cli/tests/verb_install_skill.rs +3 -2
- package/crates/team-agent/src/cli/tests/verb_profile.rs +3 -1
- package/crates/team-agent/src/contract_runtime/backend.rs +1 -1
- package/crates/team-agent/src/db/message_store.rs +65 -0
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +8 -0
- package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +111 -4
- package/crates/team-agent/src/lifecycle/launch/cursor_mcp_iso.rs +244 -82
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +166 -77
- package/crates/team-agent/src/lifecycle/launch.rs +29 -0
- package/crates/team-agent/src/lifecycle/restart/common.rs +7 -1
- package/crates/team-agent/src/lifecycle/restart.rs +17 -0
- package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -3
- package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +5 -15
- package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -3
- package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +2 -3
- package/crates/team-agent/src/lifecycle/tests/cli_argv_routing_red.rs +10 -2
- package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +8 -1
- package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +3 -2
- package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +6 -9
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +7 -5
- package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +64 -19
- package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +5 -2
- package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +34 -10
- package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +2 -4
- package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +3 -4
- package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +5 -4
- package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +2 -4
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +78 -36
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +8 -4
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +6 -12
- package/crates/team-agent/src/lifecycle/tests/pi_fork_session_inheritance_red.rs +4 -0
- package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +1 -3
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +7 -0
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +47 -4
- package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -3
- package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -1
- package/crates/team-agent/src/lifecycle/types.rs +10 -0
- package/crates/team-agent/src/mcp_server/mod.rs +2 -0
- package/crates/team-agent/src/mcp_server/readiness.rs +432 -0
- package/crates/team-agent/src/mcp_server/readiness_tests.rs +242 -0
- package/crates/team-agent/src/mcp_server/wire.rs +96 -11
- package/crates/team-agent/src/messaging/delivery.rs +62 -0
- package/crates/team-agent/src/messaging/tests/dup_inject.rs +41 -1
- package/crates/team-agent/src/provider/adapter.rs +7 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +37 -0
- package/crates/team-agent/src/tmux_backend.rs +8 -1
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +71 -54
- package/skills/team-agent/command-coverage.json +283 -13
- package/skills/team-agent/docs/reference/team-agent-operator.md +220 -0
- package/skills/team-agent/references/bug-as-artifact-flow.md +1 -1
- package/skills/team-agent/references/recovery-runbook.md +61 -212
- package/skills/team-agent/references/team-agent-operator.md +181 -48
- package/skills/team-agent/references/team-in-team.md +47 -80
|
@@ -262,10 +262,7 @@ fn cursor_mcp_enable_child_env_applies_direct_profile_unsets() {
|
|
|
262
262
|
}
|
|
263
263
|
command.env("NO_PROXY", "fixture");
|
|
264
264
|
command.env("no_proxy", "fixture");
|
|
265
|
-
crate::lifecycle::launch::apply_cursor_mcp_enable_profile_env(
|
|
266
|
-
&mut command,
|
|
267
|
-
Some(&launch),
|
|
268
|
-
);
|
|
265
|
+
crate::lifecycle::launch::apply_cursor_mcp_enable_profile_env(&mut command, Some(&launch));
|
|
269
266
|
let output = command.output().expect("env child should run");
|
|
270
267
|
assert!(output.status.success());
|
|
271
268
|
let text = String::from_utf8(output.stdout).unwrap();
|
|
@@ -280,7 +277,9 @@ fn cursor_mcp_enable_child_env_applies_direct_profile_unsets() {
|
|
|
280
277
|
"GLOBAL_AGENT_HTTP_PROXY",
|
|
281
278
|
] {
|
|
282
279
|
assert!(
|
|
283
|
-
!text
|
|
280
|
+
!text
|
|
281
|
+
.lines()
|
|
282
|
+
.any(|line| line.starts_with(&format!("{key}="))),
|
|
284
283
|
"direct MCP child must omit {key}"
|
|
285
284
|
);
|
|
286
285
|
}
|
|
@@ -291,8 +290,9 @@ fn cursor_mcp_enable_child_env_applies_direct_profile_unsets() {
|
|
|
291
290
|
#[test]
|
|
292
291
|
#[serial(env)]
|
|
293
292
|
fn cursor_spawn_writes_identity_into_project_mcp_json() {
|
|
294
|
-
let _hermetic = hermetic_guard::HermeticTestEnv::
|
|
293
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("cursor-mcp-spawn");
|
|
295
294
|
let ws = tmp_dir("cursor-mcp-spawn");
|
|
295
|
+
super::launch_spawn::seed_healthy_coordinator(&ws);
|
|
296
296
|
let team = write_cursor_team(&ws, "cursortm", "cursor_writer");
|
|
297
297
|
let selected = cursor_mcp_json_path(&ws, "cursor_writer").unwrap();
|
|
298
298
|
assert!(
|
|
@@ -325,17 +325,49 @@ fn cursor_spawn_writes_identity_into_project_mcp_json() {
|
|
|
325
325
|
let enable_cwd = cursor_mcp_enable_working_dir(&ws, Some(&project));
|
|
326
326
|
assert_eq!(enable_cwd, physical_workspace_path(&project));
|
|
327
327
|
let argv_workspace = spawn_argv_workspace_flag(&ws).expect("spawn argv --workspace");
|
|
328
|
-
assert_eq!(
|
|
328
|
+
assert_eq!(
|
|
329
|
+
argv_workspace,
|
|
330
|
+
physical_workspace_path(&project).to_string_lossy()
|
|
331
|
+
);
|
|
332
|
+
let state = crate::state::persist::load_runtime_state(&ws).unwrap();
|
|
333
|
+
let row = state.pointer("/agents/cursor_writer").unwrap();
|
|
334
|
+
assert_eq!(row["spawn_cwd"], project.to_string_lossy().as_ref());
|
|
335
|
+
assert_eq!(row["session_id"], serde_json::Value::Null);
|
|
336
|
+
|
|
337
|
+
// Measured native layout: the store bucket uses --workspace primary, not
|
|
338
|
+
// --add-dir. A decoy in the real project's bucket must never be selected.
|
|
339
|
+
let sid = "d298b261-23bb-42bb-bbeb-b2ec08d81ca3";
|
|
340
|
+
for (cwd, id) in [(&project, sid), (&ws, "foreign-project-session")] {
|
|
341
|
+
let bucket = format!("{:x}", md5::compute(cwd.to_string_lossy().as_bytes()));
|
|
342
|
+
let archive = _hermetic.home().join(".cursor/chats").join(bucket).join(id);
|
|
343
|
+
std::fs::create_dir_all(&archive).unwrap();
|
|
344
|
+
std::fs::write(archive.join("meta.json"), b"{}").unwrap();
|
|
345
|
+
}
|
|
346
|
+
let mut capture = serde_json::json!({"agents": {"cursor_writer": row.clone()}});
|
|
347
|
+
let report = crate::provider::session::capture::capture_missing_provider_sessions_once(
|
|
348
|
+
&mut capture,
|
|
349
|
+
&mut crate::provider::get_adapter,
|
|
350
|
+
false,
|
|
351
|
+
0,
|
|
352
|
+
)
|
|
353
|
+
.unwrap();
|
|
354
|
+
assert_eq!(report.assigned, vec!["cursor_writer"]);
|
|
355
|
+
assert_eq!(capture["agents"]["cursor_writer"]["session_id"], sid);
|
|
356
|
+
assert_eq!(
|
|
357
|
+
capture["agents"]["cursor_writer"]["spawn_cwd"],
|
|
358
|
+
project.to_string_lossy().as_ref()
|
|
359
|
+
);
|
|
329
360
|
}
|
|
330
361
|
|
|
331
362
|
#[test]
|
|
332
363
|
#[serial(env)]
|
|
333
364
|
fn cursor_spawn_isolation_off_writes_workspace_mcp_json() {
|
|
334
|
-
let _hermetic = hermetic_guard::HermeticTestEnv::
|
|
365
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("cursor-mcp-spawn-legacy");
|
|
335
366
|
let key = "TEAM_AGENT_CURSOR_MCP_ISOLATION";
|
|
336
367
|
let prev = std::env::var(key).ok();
|
|
337
368
|
std::env::set_var(key, "0");
|
|
338
369
|
let ws = tmp_dir("cursor-mcp-spawn-legacy");
|
|
370
|
+
super::launch_spawn::seed_healthy_coordinator(&ws);
|
|
339
371
|
let team = write_cursor_team(&ws, "cursortm", "cursor_writer");
|
|
340
372
|
let result = quick_start_with_transport_in_workspace(
|
|
341
373
|
&ws,
|
|
@@ -359,13 +391,14 @@ fn cursor_spawn_isolation_off_writes_workspace_mcp_json() {
|
|
|
359
391
|
});
|
|
360
392
|
assert_cursor_identity_overlay(&text, "cursor_writer", &ws);
|
|
361
393
|
assert!(
|
|
362
|
-
!cursor_mcp_json_path(&ws, "cursor_writer")
|
|
363
|
-
.unwrap()
|
|
364
|
-
.exists(),
|
|
394
|
+
!cursor_mcp_json_path(&ws, "cursor_writer").unwrap().exists(),
|
|
365
395
|
"isolation-off must not materialize the per-seat project overlay"
|
|
366
396
|
);
|
|
367
397
|
let argv_workspace = spawn_argv_workspace_flag(&ws).expect("spawn argv --workspace");
|
|
368
|
-
assert_eq!(
|
|
398
|
+
assert_eq!(
|
|
399
|
+
argv_workspace,
|
|
400
|
+
physical_workspace_path(&ws).to_string_lossy()
|
|
401
|
+
);
|
|
369
402
|
}
|
|
370
403
|
|
|
371
404
|
fn assert_cursor_identity_overlay(text: &str, agent_id: &str, workspace: &Path) {
|
|
@@ -439,7 +472,7 @@ fn sample_mcp_config(agent_id: &str, workspace: &str) -> McpConfig {
|
|
|
439
472
|
McpConfig {
|
|
440
473
|
raw: serde_json::json!({
|
|
441
474
|
"team_orchestrator": {
|
|
442
|
-
"command":
|
|
475
|
+
"command": std::env::current_exe().unwrap(),
|
|
443
476
|
"args": ["mcp-server", "--workspace", workspace],
|
|
444
477
|
"env": {
|
|
445
478
|
"TEAM_AGENT_ID": agent_id,
|
|
@@ -456,12 +489,21 @@ fn sample_mcp_config(agent_id: &str, workspace: &str) -> McpConfig {
|
|
|
456
489
|
#[serial(env)]
|
|
457
490
|
fn isolated_cursor_prepare_materializes_per_seat_project_not_workspace_overlay() {
|
|
458
491
|
let ws = tmp_dir("cursor-iso-clean");
|
|
459
|
-
let project = prepare_cursor_seat_mcp(
|
|
460
|
-
|
|
461
|
-
|
|
492
|
+
let project = prepare_cursor_seat_mcp(
|
|
493
|
+
&ws,
|
|
494
|
+
"seat-iso",
|
|
495
|
+
&sample_mcp_config("seat-iso", &ws.to_string_lossy()),
|
|
496
|
+
)
|
|
497
|
+
.expect("prepare isolated cursor")
|
|
498
|
+
.expect("isolation defaults on");
|
|
462
499
|
assert!(
|
|
463
|
-
project.
|
|
464
|
-
|
|
500
|
+
!project.starts_with(&ws)
|
|
501
|
+
&& project
|
|
502
|
+
.file_name()
|
|
503
|
+
.unwrap()
|
|
504
|
+
.to_string_lossy()
|
|
505
|
+
.starts_with("ta-cursor-"),
|
|
506
|
+
"per-seat native project must be outside workspace Git ancestry: {}",
|
|
465
507
|
project.display()
|
|
466
508
|
);
|
|
467
509
|
assert!(project.join(".cursor").is_dir());
|
|
@@ -487,7 +529,10 @@ fn isolated_cursor_prepare_materializes_per_seat_project_not_workspace_overlay()
|
|
|
487
529
|
.find(|pair| pair[0] == "--workspace")
|
|
488
530
|
.map(|pair| pair[1].as_str())
|
|
489
531
|
.unwrap();
|
|
490
|
-
assert_eq!(
|
|
532
|
+
assert_eq!(
|
|
533
|
+
workspace_flag,
|
|
534
|
+
physical_workspace_path(&project).to_string_lossy()
|
|
535
|
+
);
|
|
491
536
|
assert_eq!(
|
|
492
537
|
include_str!("../launch/spawn.rs").contains("prepare_cursor_seat_mcp("),
|
|
493
538
|
true,
|
|
@@ -32,7 +32,9 @@ use team_agent::transport::test_support::OfflineTransport;
|
|
|
32
32
|
#[test]
|
|
33
33
|
#[serial(env)]
|
|
34
34
|
fn cursor_role_missing_model_uses_provider_default() {
|
|
35
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("cursor-no-model");
|
|
35
36
|
let ws = tmp_dir("cursor-no-model");
|
|
37
|
+
super::launch_spawn::seed_healthy_coordinator(&ws);
|
|
36
38
|
let team = write_role_team(&ws, "cursortm", "cursor_writer", None, None);
|
|
37
39
|
let transport = OfflineTransport::new();
|
|
38
40
|
quick_start_with_transport_in_workspace(
|
|
@@ -54,8 +56,9 @@ fn cursor_role_missing_model_uses_provider_default() {
|
|
|
54
56
|
#[test]
|
|
55
57
|
#[serial(env)]
|
|
56
58
|
fn cursor_role_explicit_model_reaches_argv() {
|
|
57
|
-
let _hermetic = hermetic_guard::HermeticTestEnv::
|
|
59
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("cursor-with-model");
|
|
58
60
|
let ws = tmp_dir("cursor-with-model");
|
|
61
|
+
super::launch_spawn::seed_healthy_coordinator(&ws);
|
|
59
62
|
let team = write_role_team(
|
|
60
63
|
&ws,
|
|
61
64
|
"cursortm",
|
|
@@ -116,7 +119,7 @@ fn cursor_role_with_effort_refuses_to_start() {
|
|
|
116
119
|
#[test]
|
|
117
120
|
#[serial(env)]
|
|
118
121
|
fn cursor_second_seat_in_same_workspace_is_refused() {
|
|
119
|
-
let _hermetic = hermetic_guard::HermeticTestEnv::
|
|
122
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("cursor-two");
|
|
120
123
|
let ws = tmp_dir("cursor-two");
|
|
121
124
|
let team = ws.join("cursortm");
|
|
122
125
|
std::fs::create_dir_all(team.join("agents")).unwrap();
|
|
@@ -26,21 +26,20 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
|
|
26
26
|
|
|
27
27
|
use crate::lifecycle::restart::classify_restart_plan_with_resume_validation;
|
|
28
28
|
use crate::lifecycle::types::ResumeDecision;
|
|
29
|
-
use crate::
|
|
30
|
-
|
|
31
|
-
};
|
|
29
|
+
use crate::model::ids::AgentId;
|
|
30
|
+
use crate::provider::session_scan::cursor::{cursor_session_archive_present, cursor_session_dir};
|
|
32
31
|
use crate::state::persist::{load_runtime_state, save_runtime_state};
|
|
33
32
|
use serial_test::serial;
|
|
34
|
-
use crate::model::ids::AgentId;
|
|
35
33
|
use team_agent::lifecycle::{
|
|
36
|
-
fork_agent_with_transport, quick_start_with_transport_in_workspace,
|
|
34
|
+
cursor_mcp_project_dir, fork_agent_with_transport, quick_start_with_transport_in_workspace,
|
|
35
|
+
restart_with_transport,
|
|
37
36
|
};
|
|
38
37
|
use team_agent::transport::test_support::OfflineTransport;
|
|
39
38
|
|
|
40
39
|
#[test]
|
|
41
40
|
#[serial(env)]
|
|
42
41
|
fn cursor_fresh_spawn_does_not_invent_session_id() {
|
|
43
|
-
let _hermetic = hermetic_guard::HermeticTestEnv::
|
|
42
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("cursor-fresh");
|
|
44
43
|
let (ws, _home, _guard, team) = seed_cursor("pending");
|
|
45
44
|
let transport = OfflineTransport::new();
|
|
46
45
|
quick_start_with_transport_in_workspace(&ws, &team, None, true, Some("cursortm"), &transport)
|
|
@@ -80,17 +79,22 @@ fn cursor_fresh_spawn_does_not_invent_session_id() {
|
|
|
80
79
|
#[test]
|
|
81
80
|
#[serial(env)]
|
|
82
81
|
fn cursor_restart_uses_resume_when_session_captured_and_archive_present() {
|
|
83
|
-
let _hermetic = hermetic_guard::HermeticTestEnv::
|
|
82
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("cursor-resume");
|
|
84
83
|
let (ws, home, _guard, team) = seed_cursor("resume");
|
|
85
84
|
let first = OfflineTransport::new();
|
|
86
85
|
quick_start_with_transport_in_workspace(&ws, &team, None, true, Some("cursortm"), &first)
|
|
87
86
|
.expect("initial cursor start");
|
|
88
87
|
let sid = "502896a1-72ba-4c53-9a86-b2da28780806";
|
|
88
|
+
let primary = cursor_mcp_project_dir(&ws, "cursor_writer").unwrap();
|
|
89
|
+
let primary_before = std::fs::canonicalize(&primary).unwrap();
|
|
89
90
|
let dir = {
|
|
90
91
|
let path = home
|
|
91
92
|
.join(".cursor")
|
|
92
93
|
.join("chats")
|
|
93
|
-
.join(
|
|
94
|
+
.join(format!(
|
|
95
|
+
"{:x}",
|
|
96
|
+
md5::compute(primary.to_string_lossy().as_bytes())
|
|
97
|
+
))
|
|
94
98
|
.join(sid);
|
|
95
99
|
std::fs::create_dir_all(&path).unwrap();
|
|
96
100
|
std::fs::write(path.join("store.db"), b"").unwrap();
|
|
@@ -100,7 +104,11 @@ fn cursor_restart_uses_resume_when_session_captured_and_archive_present() {
|
|
|
100
104
|
assert_eq!(cursor_session_dir(sid).as_deref(), Some(dir.as_path()));
|
|
101
105
|
|
|
102
106
|
let mut state = load_runtime_state(&ws).expect("load");
|
|
103
|
-
|
|
107
|
+
assert_eq!(
|
|
108
|
+
state["agents"]["cursor_writer"]["spawn_cwd"],
|
|
109
|
+
primary.to_string_lossy().as_ref()
|
|
110
|
+
);
|
|
111
|
+
patch_cursor_writer(&mut state, sid, &dir, &primary);
|
|
104
112
|
save_runtime_state(&ws, &state).expect("save captured");
|
|
105
113
|
|
|
106
114
|
seed_healthy_coordinator(&ws);
|
|
@@ -121,6 +129,21 @@ fn cursor_restart_uses_resume_when_session_captured_and_archive_present() {
|
|
|
121
129
|
!argv.iter().any(|a| a == "--continue"),
|
|
122
130
|
"resume argv must not use --continue; argv={argv:?}"
|
|
123
131
|
);
|
|
132
|
+
let after = load_runtime_state(&ws).unwrap();
|
|
133
|
+
assert_eq!(
|
|
134
|
+
after["agents"]["cursor_writer"]["spawn_cwd"],
|
|
135
|
+
primary.to_string_lossy().as_ref()
|
|
136
|
+
);
|
|
137
|
+
assert_eq!(after["agents"]["cursor_writer"]["session_id"], sid);
|
|
138
|
+
assert_eq!(
|
|
139
|
+
cursor_mcp_project_dir(&ws, "cursor_writer").unwrap(),
|
|
140
|
+
primary_before
|
|
141
|
+
);
|
|
142
|
+
assert_eq!(argv_flag(&argv, "--workspace").as_deref(), primary.to_str());
|
|
143
|
+
assert!(argv_contains_adjacent(
|
|
144
|
+
&argv,
|
|
145
|
+
&["--add-dir", ws.to_str().unwrap()]
|
|
146
|
+
));
|
|
124
147
|
}
|
|
125
148
|
|
|
126
149
|
#[test]
|
|
@@ -182,7 +205,7 @@ fn cursor_classify_resumes_when_archive_present_and_refuses_when_missing() {
|
|
|
182
205
|
#[test]
|
|
183
206
|
#[serial(env)]
|
|
184
207
|
fn cursor_fork_agent_refuses_as_unsupported_not_unverified() {
|
|
185
|
-
let _hermetic = hermetic_guard::HermeticTestEnv::
|
|
208
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("cursor-fork");
|
|
186
209
|
let (ws, _home, _guard, team) = seed_cursor("fork");
|
|
187
210
|
let transport = OfflineTransport::new();
|
|
188
211
|
quick_start_with_transport_in_workspace(&ws, &team, None, true, Some("cursortm"), &transport)
|
|
@@ -280,6 +303,7 @@ fn seed_healthy_coordinator(workspace: &Path) {
|
|
|
280
303
|
|
|
281
304
|
fn seed_cursor(tag: &str) -> (PathBuf, PathBuf, HomeGuard, PathBuf) {
|
|
282
305
|
let ws = tmp_dir(tag);
|
|
306
|
+
super::launch_spawn::seed_healthy_coordinator(&ws);
|
|
283
307
|
let home = tmp_dir(&format!("{tag}-home"));
|
|
284
308
|
let guard = HomeGuard::set(&home);
|
|
285
309
|
let team = write_cursor_role(&ws);
|
|
@@ -22,10 +22,7 @@ mod hermetic_guard;
|
|
|
22
22
|
fn _hermetic_boundary_marker(_: &hermetic_guard::HermeticTestEnv) {}
|
|
23
23
|
|
|
24
24
|
fn enter_hermetic(tag: &str) -> hermetic_guard::HermeticTestEnv {
|
|
25
|
-
|
|
26
|
-
env.scrub_tmux();
|
|
27
|
-
env.assert_no_real_tmux();
|
|
28
|
-
env
|
|
25
|
+
hermetic_guard::HermeticTestEnv::enter_with_leader(tag)
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
use std::path::{Path, PathBuf};
|
|
@@ -159,6 +156,7 @@ fn seed_healthy_coordinator(workspace: &Path) {
|
|
|
159
156
|
|
|
160
157
|
fn seed_grok(tag: &str, effort: Option<&str>) -> (PathBuf, PathBuf, HomeGuard, PathBuf) {
|
|
161
158
|
let ws = tmp_dir(tag);
|
|
159
|
+
super::launch_spawn::seed_healthy_coordinator(&ws);
|
|
162
160
|
let home = tmp_dir(&format!("{tag}-home"));
|
|
163
161
|
seed_grok_home(&home, Some(&ws));
|
|
164
162
|
let guard = HomeGuard::set(&home);
|
|
@@ -15,10 +15,7 @@ mod hermetic_guard;
|
|
|
15
15
|
fn _hermetic_boundary_marker(_: &hermetic_guard::HermeticTestEnv) {}
|
|
16
16
|
|
|
17
17
|
fn enter_hermetic(tag: &str) -> hermetic_guard::HermeticTestEnv {
|
|
18
|
-
|
|
19
|
-
env.scrub_tmux();
|
|
20
|
-
env.assert_no_real_tmux();
|
|
21
|
-
env
|
|
18
|
+
hermetic_guard::HermeticTestEnv::enter_with_leader(tag)
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
use std::path::{Path, PathBuf};
|
|
@@ -218,6 +215,7 @@ fn two_grok_seats_coexist_when_toml_has_no_per_seat_keys() {
|
|
|
218
215
|
fn leftover_per_seat_key_is_cleared_before_start() {
|
|
219
216
|
let _hermetic = enter_hermetic("grok-overlay-leftover-seat");
|
|
220
217
|
let ws = tmp_dir("grok-cwd-per-seat");
|
|
218
|
+
super::launch_spawn::seed_healthy_coordinator(&ws);
|
|
221
219
|
let home = tmp_dir("grok-cwd-per-seat-home");
|
|
222
220
|
seed_grok_home(&home, Some(&ws));
|
|
223
221
|
let _guard = HomeGuard::set(&home);
|
|
@@ -345,6 +343,7 @@ fn grok_missing_login_refuses_to_start() {
|
|
|
345
343
|
fn grok_spawn_writes_resolved_team_agent_into_project_grok_config() {
|
|
346
344
|
let _hermetic = enter_hermetic("grok-overlay-spawn");
|
|
347
345
|
let ws = tmp_dir("grok-mcp-overlay");
|
|
346
|
+
super::launch_spawn::seed_healthy_coordinator(&ws);
|
|
348
347
|
let home = tmp_dir("grok-mcp-overlay-home");
|
|
349
348
|
seed_grok_home(&home, Some(&ws));
|
|
350
349
|
let _guard = HomeGuard::set(&home);
|
|
@@ -14,10 +14,7 @@ mod hermetic_guard;
|
|
|
14
14
|
fn _hermetic_boundary_marker(_: &hermetic_guard::HermeticTestEnv) {}
|
|
15
15
|
|
|
16
16
|
fn enter_hermetic(tag: &str) -> hermetic_guard::HermeticTestEnv {
|
|
17
|
-
|
|
18
|
-
env.scrub_tmux();
|
|
19
|
-
env.assert_no_real_tmux();
|
|
20
|
-
env
|
|
17
|
+
hermetic_guard::HermeticTestEnv::enter_with_leader(tag)
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
use std::path::{Path, PathBuf};
|
|
@@ -31,7 +28,9 @@ use team_agent::transport::test_support::OfflineTransport;
|
|
|
31
28
|
#[test]
|
|
32
29
|
#[serial(env)]
|
|
33
30
|
fn grok_role_missing_model_uses_provider_default() {
|
|
31
|
+
let _hermetic = enter_hermetic("grok-no-model");
|
|
34
32
|
let ws = tmp_dir("grok-no-model");
|
|
33
|
+
super::launch_spawn::seed_healthy_coordinator(&ws);
|
|
35
34
|
let home = tmp_dir("grok-no-model-home");
|
|
36
35
|
seed_grok_home(&home, Some(&ws));
|
|
37
36
|
let _guard = HomeGuard::set(&home);
|
|
@@ -67,6 +66,7 @@ fn grok_role_missing_model_uses_provider_default() {
|
|
|
67
66
|
fn grok_role_explicit_model_reaches_argv() {
|
|
68
67
|
let _hermetic = enter_hermetic("grok-explicit-model");
|
|
69
68
|
let ws = tmp_dir("grok-with-model");
|
|
69
|
+
super::launch_spawn::seed_healthy_coordinator(&ws);
|
|
70
70
|
let home = tmp_dir("grok-with-model-home");
|
|
71
71
|
seed_grok_home(&home, Some(&ws));
|
|
72
72
|
let _guard = HomeGuard::set(&home);
|
|
@@ -103,6 +103,7 @@ fn grok_role_explicit_model_reaches_argv() {
|
|
|
103
103
|
fn claude_role_missing_model_still_starts() {
|
|
104
104
|
let _hermetic = enter_hermetic("grok-claude-missing-model");
|
|
105
105
|
let ws = tmp_dir("claude-no-model");
|
|
106
|
+
super::launch_spawn::seed_healthy_coordinator(&ws);
|
|
106
107
|
let team = write_role_team(
|
|
107
108
|
&ws,
|
|
108
109
|
"claudeteam",
|
|
@@ -22,10 +22,7 @@ mod hermetic_guard;
|
|
|
22
22
|
fn _hermetic_boundary_marker(_: &hermetic_guard::HermeticTestEnv) {}
|
|
23
23
|
|
|
24
24
|
fn enter_hermetic(tag: &str) -> hermetic_guard::HermeticTestEnv {
|
|
25
|
-
|
|
26
|
-
env.scrub_tmux();
|
|
27
|
-
env.assert_no_real_tmux();
|
|
28
|
-
env
|
|
25
|
+
hermetic_guard::HermeticTestEnv::enter_with_leader(tag)
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
use std::path::{Path, PathBuf};
|
|
@@ -212,6 +209,7 @@ fn seed_healthy_coordinator(workspace: &Path) {
|
|
|
212
209
|
|
|
213
210
|
fn seed_grok(tag: &str) -> (PathBuf, PathBuf, HomeGuard, PathBuf) {
|
|
214
211
|
let ws = tmp_dir(tag);
|
|
212
|
+
super::launch_spawn::seed_healthy_coordinator(&ws);
|
|
215
213
|
let home = tmp_dir(&format!("{tag}-home"));
|
|
216
214
|
seed_grok_home(&home, Some(&ws));
|
|
217
215
|
let guard = HomeGuard::set(&home);
|
|
@@ -174,8 +174,9 @@ fn unowned_running_ws() -> PathBuf {
|
|
|
174
174
|
#[test]
|
|
175
175
|
#[serial(env)]
|
|
176
176
|
fn quick_start_compiles_real_spec_to_team_spec_yaml() {
|
|
177
|
-
let _hermetic =
|
|
177
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("launch-spawn-compile-spec");
|
|
178
178
|
let team = quick_start_team_dir(QS_VALID_ROLE);
|
|
179
|
+
seed_healthy_coordinator(&crate::model::paths::team_workspace(&team).unwrap());
|
|
179
180
|
let transport = OfflineTransport::new();
|
|
180
181
|
let result = quick_start_with_transport(&team, None, true, None, &transport);
|
|
181
182
|
|
|
@@ -209,9 +210,10 @@ fn quick_start_compiles_real_spec_to_team_spec_yaml() {
|
|
|
209
210
|
#[test]
|
|
210
211
|
#[serial(env)]
|
|
211
212
|
fn quick_start_without_team_id_uses_compiled_team_name_as_canonical_key() {
|
|
212
|
-
let _hermetic =
|
|
213
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("launch-spawn-canonical-team");
|
|
213
214
|
let team = quick_start_team_dir(QS_VALID_ROLE);
|
|
214
215
|
let workspace = crate::model::paths::team_workspace(&team).unwrap();
|
|
216
|
+
seed_healthy_coordinator(&workspace);
|
|
215
217
|
let transport = OfflineTransport::new();
|
|
216
218
|
|
|
217
219
|
let result = quick_start_with_transport(&team, None, true, None, &transport);
|
|
@@ -231,8 +233,9 @@ fn quick_start_without_team_id_uses_compiled_team_name_as_canonical_key() {
|
|
|
231
233
|
#[test]
|
|
232
234
|
#[serial(env)]
|
|
233
235
|
fn existing_runtime_producer_scopes_canonical_team() {
|
|
234
|
-
let _hermetic =
|
|
236
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("launch-spawn-existing-producer");
|
|
235
237
|
let team = quick_start_team_dir(QS_VALID_ROLE);
|
|
238
|
+
seed_healthy_coordinator(&crate::model::paths::team_workspace(&team).unwrap());
|
|
236
239
|
let transport = OfflineTransport::new();
|
|
237
240
|
let first = quick_start_with_transport(&team, Some("from-name"), true, None, &transport)
|
|
238
241
|
.expect("first quick-start");
|
|
@@ -342,8 +345,9 @@ fn existing_runtime_empty_top_level_agents_still_emits_bob() {
|
|
|
342
345
|
#[test]
|
|
343
346
|
#[serial(env)]
|
|
344
347
|
fn quick_start_teamdir_under_dot_team_uses_project_workspace_for_status_and_collect() {
|
|
345
|
-
let _hermetic =
|
|
348
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("launch-spawn-dot-team-workspace");
|
|
346
349
|
let workspace = temp_ws();
|
|
350
|
+
seed_healthy_coordinator(&workspace);
|
|
347
351
|
let team = workspace.join(".team").join("current");
|
|
348
352
|
std::fs::create_dir_all(team.join("agents")).unwrap();
|
|
349
353
|
std::fs::write(team.join("TEAM.md"), QS_TEAM_MD).unwrap();
|
|
@@ -417,7 +421,7 @@ fn quick_start_teamdir_under_dot_team_uses_project_workspace_for_status_and_coll
|
|
|
417
421
|
#[test]
|
|
418
422
|
#[serial(env)]
|
|
419
423
|
fn quick_start_default_workspace_compiled_spec_uses_project_root() {
|
|
420
|
-
let _hermetic =
|
|
424
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("launch-spawn-default-workspace");
|
|
421
425
|
let workspace = temp_ws();
|
|
422
426
|
std::fs::create_dir_all(workspace.join("agents")).unwrap();
|
|
423
427
|
std::fs::write(workspace.join("TEAM.md"), QS_TEAM_MD).unwrap();
|
|
@@ -724,8 +728,9 @@ fn unowned_running_ws_all_paused() -> PathBuf {
|
|
|
724
728
|
#[test]
|
|
725
729
|
#[serial(env)]
|
|
726
730
|
fn spine_quick_start_seeds_state_under_team_workspace_not_team_dir() {
|
|
727
|
-
let _hermetic =
|
|
731
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("launch-spawn-spine-workspace");
|
|
728
732
|
let team = quick_start_team_dir(QS_VALID_ROLE); // <base>/teamdir
|
|
733
|
+
seed_healthy_coordinator(&crate::model::paths::team_workspace(&team).unwrap());
|
|
729
734
|
let transport = OfflineTransport::new();
|
|
730
735
|
let _ = quick_start_with_transport(&team, None, true, None, &transport);
|
|
731
736
|
let workspace = team.parent().unwrap(); // team_workspace(<base>/teamdir) = <base>
|
|
@@ -1302,21 +1307,32 @@ fn start_agent_session_generation_inherits_birth_or_stamps_recreated_session() {
|
|
|
1302
1307
|
/// Seed a HEALTHY coordinator at `workspace` (this process's pid + matching metadata + db schema) so
|
|
1303
1308
|
/// quick_start's internal start_coordinator returns AlreadyRunning — NO real daemon subprocess spawn.
|
|
1304
1309
|
pub(super) fn seed_healthy_coordinator(workspace: &std::path::Path) {
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
)
|
|
1314
|
-
.
|
|
1315
|
-
std::fs::
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
)
|
|
1319
|
-
|
|
1310
|
+
hermetic_guard::seed_in_process_coordinator(workspace);
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
#[test]
|
|
1314
|
+
#[cfg(unix)]
|
|
1315
|
+
#[serial(env)]
|
|
1316
|
+
fn issue337_owned_fixture_caller_admits_fresh_launch_without_guard_bypass() {
|
|
1317
|
+
let env = hermetic_guard::HermeticTestEnv::enter_with_leader("issue337-fixture-caller");
|
|
1318
|
+
let workspace = env.workspace("ws");
|
|
1319
|
+
let team = workspace.join("roles");
|
|
1320
|
+
std::fs::create_dir_all(team.join("agents")).unwrap();
|
|
1321
|
+
std::fs::write(team.join("TEAM.md"), QS_TEAM_MD).unwrap();
|
|
1322
|
+
std::fs::write(team.join("agents/implementer.md"), QS_VALID_ROLE).unwrap();
|
|
1323
|
+
seed_healthy_coordinator(&workspace);
|
|
1324
|
+
let transport = OfflineTransport::new();
|
|
1325
|
+
let report = quick_start_with_transport_in_workspace(
|
|
1326
|
+
&workspace, &team, None, true, Some("fixture-team"), &transport,
|
|
1327
|
+
).unwrap();
|
|
1328
|
+
let QuickStartReport::Ready { launch, .. } = report else { panic!("owned caller must admit launch: {report:?}") };
|
|
1329
|
+
assert!(launch.leader_receiver_attached);
|
|
1330
|
+
assert_eq!(launch.started.len(), 1);
|
|
1331
|
+
assert_eq!(transport.spawn_records().len(), 1);
|
|
1332
|
+
let socket = std::env::var("TMUX").unwrap().split(',').next().unwrap().to_string();
|
|
1333
|
+
assert!(std::path::Path::new(&socket).exists());
|
|
1334
|
+
drop(env);
|
|
1335
|
+
assert!(!std::path::Path::new(&socket).exists());
|
|
1320
1336
|
}
|
|
1321
1337
|
|
|
1322
1338
|
fn seed_canonical_team_identity(workspace: &std::path::Path, team_key: &str) {
|
|
@@ -1333,7 +1349,7 @@ fn seed_canonical_team_identity(workspace: &std::path::Path, team_key: &str) {
|
|
|
1333
1349
|
#[test]
|
|
1334
1350
|
#[serial(env)]
|
|
1335
1351
|
fn quick_start_with_transport_spawns_workers_not_dry_run() {
|
|
1336
|
-
let _hermetic =
|
|
1352
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("launch-spawn-spawn-path");
|
|
1337
1353
|
let team = quick_start_team_dir(QS_VALID_ROLE); // one agent: implementer / provider codex
|
|
1338
1354
|
let workspace = team.parent().expect("team_workspace(team_dir) = parent"); // where start_coordinator runs
|
|
1339
1355
|
seed_healthy_coordinator(workspace);
|
|
@@ -1422,7 +1438,7 @@ fn adaptive_layout_plan_8_workers_is_3_3_2() {
|
|
|
1422
1438
|
#[test]
|
|
1423
1439
|
#[serial(env)]
|
|
1424
1440
|
fn quick_start_default_adaptive_groups_workers_into_layout_panes() {
|
|
1425
|
-
let _hermetic =
|
|
1441
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("launch-spawn-adaptive-layout");
|
|
1426
1442
|
let roles = (1..=4)
|
|
1427
1443
|
.map(|i| {
|
|
1428
1444
|
let id = format!("w{i}");
|
|
@@ -1507,7 +1523,7 @@ fn quick_start_tmux_backend_uses_workspace_private_endpoint() {
|
|
|
1507
1523
|
#[test]
|
|
1508
1524
|
#[serial(env)]
|
|
1509
1525
|
fn quick_start_persists_selected_tmux_endpoint_and_attach_commands() {
|
|
1510
|
-
let _hermetic =
|
|
1526
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("launch-spawn-endpoint-attach");
|
|
1511
1527
|
let team = quick_start_team_dir(QS_VALID_ROLE);
|
|
1512
1528
|
let workspace = team.parent().expect("team_workspace(team_dir) = parent");
|
|
1513
1529
|
seed_healthy_coordinator(workspace);
|
|
@@ -1568,23 +1584,27 @@ fn annotate_runtime_tmux_endpoint_persists_workspace_socket_as_full_path() {
|
|
|
1568
1584
|
#[test]
|
|
1569
1585
|
#[serial(env)]
|
|
1570
1586
|
fn quick_start_preserves_managed_leader_topology_and_emits_leader_attach_command() {
|
|
1571
|
-
let
|
|
1572
|
-
let
|
|
1573
|
-
let
|
|
1587
|
+
let hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("launch-spawn-managed-leader");
|
|
1588
|
+
let workspace = hermetic.root();
|
|
1589
|
+
let team = workspace.join("teamdir");
|
|
1590
|
+
std::fs::create_dir_all(team.join("agents")).unwrap();
|
|
1591
|
+
std::fs::write(team.join("TEAM.md"), QS_TEAM_MD).unwrap();
|
|
1592
|
+
std::fs::write(team.join("agents/implementer.md"), QS_VALID_ROLE).unwrap();
|
|
1574
1593
|
seed_healthy_coordinator(workspace);
|
|
1594
|
+
let endpoint = std::env::var("TMUX").unwrap().split(',').next().unwrap().to_string();
|
|
1575
1595
|
crate::state::persist::save_runtime_state(
|
|
1576
1596
|
workspace,
|
|
1577
1597
|
&json!({
|
|
1578
1598
|
"active_team_key": "teamdir",
|
|
1579
|
-
"session_name":
|
|
1599
|
+
"session_name": null,
|
|
1580
1600
|
"is_external_leader": false,
|
|
1581
1601
|
"leader_client": {"diagnostic_only": true, "attach_mode": "attach-session"},
|
|
1582
|
-
"
|
|
1583
|
-
"
|
|
1602
|
+
"tmux_socket": endpoint,
|
|
1603
|
+
"tmux_endpoint": endpoint,
|
|
1584
1604
|
"agents": {},
|
|
1585
1605
|
"teams": {
|
|
1586
1606
|
"teamdir": {
|
|
1587
|
-
"session_name":
|
|
1607
|
+
"session_name": null,
|
|
1588
1608
|
"is_external_leader": false,
|
|
1589
1609
|
"leader_client": {"diagnostic_only": true, "attach_mode": "attach-session"},
|
|
1590
1610
|
"agents": {}
|
|
@@ -1593,9 +1613,25 @@ fn quick_start_preserves_managed_leader_topology_and_emits_leader_attach_command
|
|
|
1593
1613
|
}),
|
|
1594
1614
|
)
|
|
1595
1615
|
.expect("seed managed leader state");
|
|
1616
|
+
let attached = crate::cli::cmd_attach_leader(&crate::cli::AttachLeaderArgs {
|
|
1617
|
+
workspace: workspace.to_path_buf(), team: Some("teamdir".into()), pane: None,
|
|
1618
|
+
provider: crate::model::enums::Provider::Fake, confirm: false, json: true,
|
|
1619
|
+
}).expect("bind the real owned caller through the public lease port");
|
|
1620
|
+
assert_eq!(attached.exit, crate::cli::ExitCode::Ok, "{attached:?}");
|
|
1621
|
+
let mut state = crate::state::persist::load_runtime_state(workspace).unwrap();
|
|
1622
|
+
assert!(state["session_name"].is_null(), "leader-only bootstrap has no worker session");
|
|
1623
|
+
assert!(state["teams"]["teamdir"]["session_name"].is_null());
|
|
1624
|
+
assert!(state["teams"]["teamdir"]["agents"].as_object().unwrap().is_empty());
|
|
1625
|
+
// The owned fake caller models the already-managed launcher topology;
|
|
1626
|
+
// retain the real binding while setting the presentation fixture metadata.
|
|
1627
|
+
state["is_external_leader"] = json!(false);
|
|
1628
|
+
state["leader_client"] = json!({"diagnostic_only": true, "attach_mode": "attach-session"});
|
|
1629
|
+
state["teams"]["teamdir"]["is_external_leader"] = json!(false);
|
|
1630
|
+
state["teams"]["teamdir"]["leader_client"] = state["leader_client"].clone();
|
|
1631
|
+
crate::state::persist::save_runtime_state(workspace, &state).unwrap();
|
|
1596
1632
|
let transport = OfflineTransport::new();
|
|
1597
1633
|
|
|
1598
|
-
let report = quick_start_with_transport(&team, None, true,
|
|
1634
|
+
let report = quick_start_with_transport(&team, None, true, Some("teamdir"), &transport)
|
|
1599
1635
|
.expect("quick_start_with_transport must reach Ready");
|
|
1600
1636
|
let attach_commands = match report {
|
|
1601
1637
|
QuickStartReport::Ready {
|
|
@@ -2872,8 +2908,9 @@ fn stop_agent_adaptive_kills_target_pane_not_shared_layout_window() {
|
|
|
2872
2908
|
#[test]
|
|
2873
2909
|
#[serial(env)]
|
|
2874
2910
|
fn quick_start_seeds_tasks_key_from_compiled_spec() {
|
|
2875
|
-
let _hermetic =
|
|
2911
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("launch-spawn-tasks-key");
|
|
2876
2912
|
let team = quick_start_team_dir(QS_VALID_ROLE);
|
|
2913
|
+
seed_healthy_coordinator(&crate::model::paths::team_workspace(&team).unwrap());
|
|
2877
2914
|
let transport = OfflineTransport::new();
|
|
2878
2915
|
let _ = quick_start_with_transport(&team, None, true, None, &transport);
|
|
2879
2916
|
let workspace = team
|
|
@@ -2921,7 +2958,7 @@ fn quick_start_seeds_tasks_key_from_compiled_spec() {
|
|
|
2921
2958
|
// suffix (active_team_key, teams).
|
|
2922
2959
|
#[test]
|
|
2923
2960
|
#[serial(env)]
|
|
2924
|
-
fn
|
|
2961
|
+
fn quick_start_unbound_state_producer_seeds_spec_path_workspace_leader() {
|
|
2925
2962
|
let _hermetic = enter_hermetic("launch-spawn-state-seeds");
|
|
2926
2963
|
// Bug 2 owner team-scope: top-level owner triple is dropped when the seeded
|
|
2927
2964
|
// pane is empty; an ambient TMUX_PANE (tests run inside the dev's tmux session)
|
|
@@ -2932,10 +2969,14 @@ fn quick_start_state_seeds_spec_path_workspace_leader() {
|
|
|
2932
2969
|
let _tmux = EnvVarGuard::unset("TMUX");
|
|
2933
2970
|
let team = quick_start_team_dir(QS_VALID_ROLE);
|
|
2934
2971
|
let transport = OfflineTransport::new();
|
|
2935
|
-
let _ = quick_start_with_transport(&team, None, true, None, &transport);
|
|
2936
2972
|
let workspace = team
|
|
2937
2973
|
.parent()
|
|
2938
2974
|
.expect("team_workspace(<base>/teamdir) = <base>");
|
|
2975
|
+
// Test the real unbound state producer plus recording-only worker stage.
|
|
2976
|
+
// Public start correctly refuses this absent caller (separate regressions).
|
|
2977
|
+
crate::lifecycle::launch::materialize_unbound_state_for_test(
|
|
2978
|
+
workspace, &team, "quickteam", &transport,
|
|
2979
|
+
).expect("materialize the unbound producer fixture without public start");
|
|
2939
2980
|
// E5: spec_path in state now points to .team/runtime/<team_key>/, team_dir stays the user role dir.
|
|
2940
2981
|
let spec_path = quick_start_runtime_spec_path(&team);
|
|
2941
2982
|
let (raw, state) = raw_runtime_state(workspace);
|
|
@@ -3018,10 +3059,11 @@ fn quick_start_state_seeds_spec_path_workspace_leader() {
|
|
|
3018
3059
|
#[test]
|
|
3019
3060
|
#[serial(env)]
|
|
3020
3061
|
fn quick_start_running_agent_state_shape_after_spawn_is_golden() {
|
|
3021
|
-
let _hermetic =
|
|
3062
|
+
let _hermetic = hermetic_guard::HermeticTestEnv::enter_with_leader("launch-spawn-running-state");
|
|
3022
3063
|
const FIXED_SPAWNED_AT: &str = "2026-06-04T00:00:00+00:00";
|
|
3023
3064
|
let _clock_guard = EnvVarGuard::set("TEAM_AGENT_TEST_FIXED_SPAWNED_AT", FIXED_SPAWNED_AT);
|
|
3024
3065
|
let team = quick_start_team_dir(QS_VALID_ROLE);
|
|
3066
|
+
seed_healthy_coordinator(&crate::model::paths::team_workspace(&team).unwrap());
|
|
3025
3067
|
let transport = OfflineTransport::new();
|
|
3026
3068
|
let _ = quick_start_with_transport(&team, None, true, None, &transport);
|
|
3027
3069
|
let workspace = team
|