@team-agent/installer 0.5.41 β 0.5.43
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/crates/team-agent/src/cli/adapters.rs +129 -54
- package/crates/team-agent/src/cli/diagnose.rs +1 -2
- package/crates/team-agent/src/cli/emit.rs +1 -7
- package/crates/team-agent/src/cli/helpers.rs +3 -1
- package/crates/team-agent/src/cli/leader.rs +2 -1
- package/crates/team-agent/src/cli/mod.rs +27 -7
- package/crates/team-agent/src/cli/named_address.rs +14 -5
- package/crates/team-agent/src/cli/profile.rs +19 -7
- package/crates/team-agent/src/cli/send.rs +9 -3
- package/crates/team-agent/src/cli/status.rs +8 -30
- package/crates/team-agent/src/cli/status_port.rs +1341 -1272
- package/crates/team-agent/src/cli/tests/base.rs +738 -660
- package/crates/team-agent/src/cli/tests/compile.rs +45 -18
- package/crates/team-agent/src/cli/tests/divergence.rs +462 -444
- package/crates/team-agent/src/cli/tests/lane_c.rs +365 -282
- package/crates/team-agent/src/cli/tests/leader_watch.rs +356 -329
- package/crates/team-agent/src/cli/tests/main_preserved.rs +672 -564
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +284 -224
- package/crates/team-agent/src/cli/tests/mod.rs +17 -7
- package/crates/team-agent/src/cli/tests/named_address.rs +8 -2
- package/crates/team-agent/src/cli/tests/peer_allow.rs +10 -2
- package/crates/team-agent/src/cli/tests/run_delegation.rs +314 -273
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +26 -15
- package/crates/team-agent/src/cli/tests/status_send.rs +707 -629
- package/crates/team-agent/src/cli/tests/verb_install_skill.rs +20 -4
- package/crates/team-agent/src/cli/tests/verb_profile.rs +46 -17
- package/crates/team-agent/src/cli/tests/verb_validate.rs +15 -3
- package/crates/team-agent/src/codex_app_server.rs +2 -5
- package/crates/team-agent/src/compiler/tests.rs +139 -33
- package/crates/team-agent/src/compiler.rs +55 -22
- package/crates/team-agent/src/conpty/backend.rs +23 -33
- package/crates/team-agent/src/coordinator/backoff.rs +2 -7
- package/crates/team-agent/src/coordinator/conpty_shim.rs +55 -67
- package/crates/team-agent/src/coordinator/health.rs +46 -31
- package/crates/team-agent/src/coordinator/mod.rs +3 -3
- package/crates/team-agent/src/coordinator/orphan.rs +22 -10
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +47 -52
- package/crates/team-agent/src/coordinator/tests/abnormal.rs +55 -19
- package/crates/team-agent/src/coordinator/tests/basics.rs +179 -41
- package/crates/team-agent/src/coordinator/tests/daemon.rs +53 -13
- package/crates/team-agent/src/coordinator/tests/health_sync.rs +78 -19
- package/crates/team-agent/src/coordinator/tests/main_preserved.rs +61 -11
- package/crates/team-agent/src/coordinator/tests/mod.rs +33 -39
- package/crates/team-agent/src/coordinator/tests/spine.rs +52 -12
- package/crates/team-agent/src/coordinator/tests/takeover.rs +73 -15
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +50 -15
- package/crates/team-agent/src/coordinator/tests/watch.rs +74 -20
- package/crates/team-agent/src/coordinator/tick.rs +19 -1
- package/crates/team-agent/src/db/message_store.rs +138 -30
- package/crates/team-agent/src/db/migration.rs +249 -61
- package/crates/team-agent/src/db/schema.rs +303 -82
- package/crates/team-agent/src/diagnose/comms.rs +9 -2
- package/crates/team-agent/src/diagnose/mod.rs +1 -3
- package/crates/team-agent/src/diagnose/orphans.rs +79 -61
- package/crates/team-agent/src/event_log.rs +70 -16
- package/crates/team-agent/src/layout/manager.rs +15 -4
- package/crates/team-agent/src/layout/mod.rs +4 -4
- package/crates/team-agent/src/layout/overlay.rs +10 -3
- package/crates/team-agent/src/layout/placement.rs +5 -1
- package/crates/team-agent/src/layout/recovery.rs +4 -2
- package/crates/team-agent/src/layout/runtime_sessions.rs +7 -7
- package/crates/team-agent/src/layout/sessions.rs +17 -9
- package/crates/team-agent/src/layout/tmux_endpoint.rs +1 -1
- package/crates/team-agent/src/layout/worker_env.rs +52 -19
- package/crates/team-agent/src/leader/helpers.rs +7 -1
- package/crates/team-agent/src/leader/lease.rs +195 -82
- package/crates/team-agent/src/leader/owner_bind.rs +55 -22
- package/crates/team-agent/src/leader/rediscover/tests.rs +88 -24
- package/crates/team-agent/src/leader/rediscover.rs +74 -25
- package/crates/team-agent/src/leader/start.rs +75 -54
- package/crates/team-agent/src/leader/takeover.rs +46 -11
- package/crates/team-agent/src/leader/tests/basics.rs +320 -167
- package/crates/team-agent/src/leader/tests/byte_findings.rs +361 -219
- package/crates/team-agent/src/leader/tests/identity.rs +428 -356
- package/crates/team-agent/src/leader/tests/idle.rs +285 -254
- package/crates/team-agent/src/leader/tests/lease_api.rs +338 -274
- package/crates/team-agent/src/leader/tests/lease_claim.rs +643 -593
- package/crates/team-agent/src/leader/tests/mod.rs +115 -99
- package/crates/team-agent/src/leader/tests/rediscover.rs +74 -22
- package/crates/team-agent/src/leader/tests/wake_start_owner.rs +237 -211
- package/crates/team-agent/src/lib.rs +4 -4
- package/crates/team-agent/src/lifecycle/display.rs +7 -3
- package/crates/team-agent/src/lifecycle/launch.rs +6 -1
- package/crates/team-agent/src/lifecycle/mod.rs +9 -1
- package/crates/team-agent/src/lifecycle/profile_launch.rs +77 -34
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +3 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +1 -6
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +1 -4
- package/crates/team-agent/src/lifecycle/restart/preflight.rs +6 -5
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +26 -22
- package/crates/team-agent/src/lifecycle/restart/remove.rs +45 -35
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +63 -17
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +251 -84
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +198 -48
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +2 -1
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +152 -32
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +1 -5
- package/crates/team-agent/src/lifecycle/tests.rs +2 -2
- package/crates/team-agent/src/main.rs +4 -4
- package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +47 -20
- package/crates/team-agent/src/mcp_server/mod.rs +11 -2
- package/crates/team-agent/src/mcp_server/types.rs +14 -3
- package/crates/team-agent/src/mcp_server/wire.rs +230 -68
- package/crates/team-agent/src/messaging/delivery.rs +20 -18
- package/crates/team-agent/src/messaging/helpers.rs +25 -4
- package/crates/team-agent/src/messaging/leader_receiver.rs +4 -5
- package/crates/team-agent/src/messaging/mod.rs +2 -3
- package/crates/team-agent/src/messaging/selftest.rs +46 -26
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +47 -12
- package/crates/team-agent/src/messaging/tests/runtime.rs +57 -22
- package/crates/team-agent/src/messaging/tests/spine.rs +154 -40
- package/crates/team-agent/src/messaging/tests/wave2.rs +31 -32
- package/crates/team-agent/src/messaging/trust.rs +25 -2
- package/crates/team-agent/src/messaging/watchers.rs +37 -9
- package/crates/team-agent/src/model/enums.rs +65 -16
- package/crates/team-agent/src/model/ids.rs +12 -3
- package/crates/team-agent/src/model/paths.rs +28 -7
- package/crates/team-agent/src/model/permissions.rs +176 -33
- package/crates/team-agent/src/model/routing.rs +66 -20
- package/crates/team-agent/src/model/spec.rs +365 -69
- package/crates/team-agent/src/model/task_graph.rs +36 -9
- package/crates/team-agent/src/model/yaml/tests.rs +24 -6
- package/crates/team-agent/src/model/yaml.rs +7 -6
- package/crates/team-agent/src/packaging/install.rs +23 -9
- package/crates/team-agent/src/packaging/migrate.rs +5 -7
- package/crates/team-agent/src/packaging/mod.rs +9 -1
- package/crates/team-agent/src/packaging/repair.rs +13 -6
- package/crates/team-agent/src/packaging/tests.rs +63 -16
- package/crates/team-agent/src/packaging/types.rs +22 -7
- package/crates/team-agent/src/platform/argv.rs +4 -1
- package/crates/team-agent/src/platform/file_lock.rs +22 -8
- package/crates/team-agent/src/platform/process.rs +21 -22
- package/crates/team-agent/src/provider/adapters/claude.rs +1 -3
- package/crates/team-agent/src/provider/approvals/parsing.rs +134 -26
- package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +13 -3
- package/crates/team-agent/src/provider/classify.rs +127 -42
- package/crates/team-agent/src/provider/faults.rs +17 -5
- package/crates/team-agent/src/provider/helpers.rs +6 -5
- package/crates/team-agent/src/provider/startup_prompt.rs +75 -27
- package/crates/team-agent/src/state/repository.rs +27 -14
- package/crates/team-agent/src/tmux_backend/tests.rs +1637 -1398
- package/crates/team-agent/src/tmux_backend.rs +80 -44
- package/crates/team-agent/src/topology.rs +27 -20
- package/crates/team-agent/src/transport/test_support.rs +20 -23
- package/crates/team-agent/src/transport/tests/behavior.rs +292 -293
- package/crates/team-agent/src/transport/tests/mod.rs +178 -187
- package/crates/team-agent/src/transport/tests/wire.rs +561 -525
- package/crates/team-agent/src/transport.rs +12 -17
- package/crates/team-agent/src/transport_factory.rs +29 -14
- package/package.json +4 -4
|
@@ -37,35 +37,35 @@ impl Drop for WorkspaceCleanup {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
40
|
+
// =========================================================================
|
|
41
|
+
// WAVE-2 NON-SUB CHECKPOINT β missing CLI subcommands (ABSENT from cli/emit.rs dispatch).
|
|
42
|
+
// emit.rs `dispatch` has NO arm for sessions, peek, collect, e2e, diagnose,
|
|
43
|
+
// preflight, wait-ready -> they fall to `_ => Ok(ExitCode::Error)`. These REDs
|
|
44
|
+
// assert the dispatch ROUTES each subcommand: `run([sub,...]) == ExitCode::Ok` for a golden
|
|
45
|
+
// EXIT-0 scenario (today unrouted -> ExitCode::Error -> RED; green once the porter adds the
|
|
46
|
+
// dispatch arm + handler). Golden exit codes + JSON shapes probed via `python3 -m team_agent <sub>`.
|
|
47
|
+
//
|
|
48
|
+
// OBSERVABILITY NOTE: `run()` exposes only ExitCode (Ok=0/Error=1); it prints via println!, which
|
|
49
|
+
// libtest intercepts (thread-local capture), so an fd-level stdout byte-capture is unreliable
|
|
50
|
+
// under `cargo test`. The exact golden --json byte-shape is therefore LOCKED in each doc-comment
|
|
51
|
+
// as the porter's parity obligation; the in-process assertion is the routing (exit code). A
|
|
52
|
+
// follow-up can byte-lock output once each handler exists as a callable `cmd_*`/`*_port` symbol.
|
|
53
|
+
//
|
|
54
|
+
// Only golden-EXIT-0, tmux-SAFE scenarios make a clean RED (an exit-1 scenario's Error is
|
|
55
|
+
// indistinguishable from the unknown-subcommand Error -> false-green, forbidden). preflight/
|
|
56
|
+
// wait-ready/e2e/peek cannot reach golden-exit-0 on CI without real tmux/providers/a live team,
|
|
57
|
+
// so they are #[ignore] real-effect seams (documented shape), NOT false-green exit-1 asserts.
|
|
58
|
+
// =========================================================================
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
fn cli_argv(items: &[&str]) -> Vec<String> {
|
|
61
|
+
items.iter().map(|s| (*s).to_string()).collect()
|
|
62
|
+
}
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
/// The exact golden fake spec (cli/e2e.py `_fake_spec`, dumped via simple_yaml) with the literal
|
|
65
|
+
/// `/WS` placeholder substituted to the real workspace β the minimal VALID team.spec.yaml that
|
|
66
|
+
/// `collect` accepts. (load_spec rejects partial specs: requires
|
|
67
|
+
/// communication/context/leader/routing/runtime/tasks.)
|
|
68
|
+
const FAKE_SPEC_YAML: &str = r#"version: 1
|
|
69
69
|
team:
|
|
70
70
|
name: "fake-e2e"
|
|
71
71
|
mode: "supervisor_worker"
|
|
@@ -163,100 +163,106 @@ tasks:
|
|
|
163
163
|
risk: "low"
|
|
164
164
|
"#;
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
166
|
+
fn seed_team_spec(ws: &std::path::Path) {
|
|
167
|
+
let spec = FAKE_SPEC_YAML.replace("/WS", &ws.to_string_lossy());
|
|
168
|
+
std::fs::write(ws.join("team.spec.yaml"), spec).unwrap();
|
|
169
|
+
}
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
171
|
+
fn seed_collect_runtime_state(ws: &std::path::Path) {
|
|
172
|
+
crate::state::persist::save_runtime_state(
|
|
173
|
+
ws,
|
|
174
|
+
&json!({
|
|
175
|
+
"active_team_key": "fake-e2e",
|
|
176
|
+
"team_dir": ws.to_string_lossy().to_string(),
|
|
177
|
+
"spec_path": ws.join("team.spec.yaml").to_string_lossy().to_string(),
|
|
178
|
+
"session_name": "team-agent-fake-e2e",
|
|
179
|
+
"leader": {"id": "leader"},
|
|
180
|
+
"agents": {
|
|
181
|
+
"fake_impl": {
|
|
182
|
+
"status": "running",
|
|
183
|
+
"provider": "fake",
|
|
184
|
+
"role": "implementation_engineer",
|
|
185
|
+
"window": "fake_impl",
|
|
186
|
+
"owner_team_id": "fake-e2e"
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"tasks": [{
|
|
190
|
+
"id": "task_impl",
|
|
191
|
+
"title": "Fake implementation",
|
|
192
|
+
"type": "implementation",
|
|
193
|
+
"status": "pending",
|
|
194
|
+
"assignee": "fake_impl"
|
|
195
|
+
}]
|
|
196
|
+
}),
|
|
197
|
+
)
|
|
198
|
+
.unwrap();
|
|
199
|
+
}
|
|
200
200
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
201
|
+
// ββ sessions ββ golden cli/parser.py:230 `cmd_sessions` -> runtime.sessions(ws). EXIT 0.
|
|
202
|
+
// `team-agent sessions --workspace <ws> --json` on an empty ws ->
|
|
203
|
+
// {"ok":true,"sessions":[],"workspace":"<ws>"} (--json sort_keys). RED: unrouted -> Error.
|
|
204
|
+
#[test]
|
|
205
|
+
fn dispatch_routes_sessions_subcommand() {
|
|
206
|
+
let ws = tmp_workspace();
|
|
207
|
+
let code = run(
|
|
208
|
+
&cli_argv(&["sessions", "--workspace", &ws.to_string_lossy(), "--json"]),
|
|
209
|
+
&ws,
|
|
210
|
+
);
|
|
211
|
+
assert_eq!(
|
|
209
212
|
code,
|
|
210
213
|
ExitCode::Ok,
|
|
211
214
|
"`sessions` must ROUTE to cmd_sessions (golden parser.py:230, exit 0 {{ok,sessions,workspace}}); \
|
|
212
215
|
today it falls to the unknown-subcommand arm (emit.rs:77) -> Error"
|
|
213
216
|
);
|
|
214
|
-
|
|
215
|
-
|
|
217
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
218
|
+
}
|
|
216
219
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
220
|
+
// ββ collect ββ golden parser.py:292 `cmd_collect` -> runtime.collect(ws). With a valid
|
|
221
|
+
// team.spec.yaml present and nothing to collect -> EXIT 0, golden:
|
|
222
|
+
// {"collected":[],"collected_results":[],"coordinator":{"ok":false,"status":"not_required"},
|
|
223
|
+
// "delivered_messages":[],"invalid_results":[],"ok":true,"results":{...},"state_file":"<ws>/team_state.md"}
|
|
224
|
+
// RED: unrouted -> Error.
|
|
225
|
+
#[test]
|
|
226
|
+
#[serial(env)]
|
|
227
|
+
fn dispatch_routes_collect_with_spec() {
|
|
228
|
+
let _env = EnvUnsetGuard::unset(&[
|
|
229
|
+
"TEAM_AGENT_WORKSPACE",
|
|
230
|
+
"TEAM_AGENT_TEAM_ID",
|
|
231
|
+
"TEAM_AGENT_OWNER_TEAM_ID",
|
|
232
|
+
"TEAM_AGENT_ACTIVE_TEAM",
|
|
233
|
+
"TEAM_AGENT_ID",
|
|
234
|
+
"TEAM_AGENT_LEADER_PANE_ID",
|
|
235
|
+
"TEAM_AGENT_LEADER_SESSION_UUID",
|
|
236
|
+
"TEAM_AGENT_LEADER_SESSION_UUID_OVERRIDE",
|
|
237
|
+
"TEAM_AGENT_LEADER_PROVIDER",
|
|
238
|
+
"TMUX",
|
|
239
|
+
"TMUX_PANE",
|
|
240
|
+
]);
|
|
241
|
+
let ws = tmp_workspace();
|
|
242
|
+
let _cleanup = WorkspaceCleanup(ws.clone());
|
|
243
|
+
seed_team_spec(&ws);
|
|
244
|
+
seed_collect_runtime_state(&ws);
|
|
245
|
+
let code = run(
|
|
246
|
+
&cli_argv(&["collect", "--workspace", &ws.to_string_lossy(), "--json"]),
|
|
247
|
+
&ws,
|
|
248
|
+
);
|
|
249
|
+
assert_eq!(
|
|
244
250
|
code,
|
|
245
251
|
ExitCode::Ok,
|
|
246
252
|
"`collect` must ROUTE to cmd_collect (parser.py:292); with a valid spec golden exits 0 \
|
|
247
253
|
{{collected,collected_results,coordinator,delivered_messages,invalid_results,ok,results,state_file}}; \
|
|
248
254
|
today -> unknown-subcommand Error"
|
|
249
255
|
);
|
|
250
|
-
|
|
256
|
+
}
|
|
251
257
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
258
|
+
// ββ diagnose ββ golden parser.py:298 `cmd_diagnose` -> runtime.diagnose(ws) (diagnose/health.py:19).
|
|
259
|
+
// ok:true only when there are zero issues. Seed leader_receiver=attached + NO session_name + NO
|
|
260
|
+
// agents -> issues=[] -> EXIT 0, golden top keys (sort): event_log,issues,ok,runtime,suggested_repairs.
|
|
261
|
+
// RED: unrouted -> Error.
|
|
262
|
+
#[test]
|
|
263
|
+
fn dispatch_routes_diagnose_healthy_leader() {
|
|
264
|
+
let ws = tmp_workspace();
|
|
265
|
+
std::fs::write(
|
|
260
266
|
ws.join(".team").join("runtime").join("state.json"),
|
|
261
267
|
serde_json::to_vec(&json!({
|
|
262
268
|
"leader": {"id": "leader"},
|
|
@@ -265,165 +271,219 @@ tasks:
|
|
|
265
271
|
.unwrap(),
|
|
266
272
|
)
|
|
267
273
|
.unwrap();
|
|
268
|
-
|
|
269
|
-
|
|
274
|
+
let code = run(
|
|
275
|
+
&cli_argv(&["diagnose", "--workspace", &ws.to_string_lossy(), "--json"]),
|
|
276
|
+
&ws,
|
|
277
|
+
);
|
|
278
|
+
assert_eq!(
|
|
270
279
|
code,
|
|
271
280
|
ExitCode::Ok,
|
|
272
281
|
"`diagnose` must ROUTE to cmd_diagnose (parser.py:298); a healthy (attached, no-session, \
|
|
273
282
|
no-agent) state yields zero issues -> exit 0 {{event_log,issues,ok,runtime,suggested_repairs}}; \
|
|
274
283
|
today -> unknown-subcommand Error"
|
|
275
284
|
);
|
|
276
|
-
|
|
277
|
-
|
|
285
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
286
|
+
}
|
|
278
287
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
288
|
+
// ββ preflight (#[ignore] real-machine) ββ golden parser.py:160 `cmd_preflight` -> runtime.preflight
|
|
289
|
+
// (Path(args.team)). Validates a team role-doc dir: checks compile(TEAM.md)/tmux/ghostty/rust_core/
|
|
290
|
+
// profile_dir; golden -> {"blockers":[...],"checks":[...],"details_log":...,"next_actions":[...],
|
|
291
|
+
// "ok":bool,"summary":...}. Reaches ok:true (exit 0) ONLY with a valid TEAM.md + ghostty installed,
|
|
292
|
+
// which CI cannot supply (ghostty absent -> blocker) -> not a clean in-process exit-0 RED.
|
|
293
|
+
#[test]
|
|
294
|
+
#[ignore = "real-machine: `preflight --team <dir>` needs a valid TEAM.md role-doc dir + ghostty to \
|
|
286
295
|
reach ok:true; on CI it golden-exits 1 (compile/ghostty blockers) which is \
|
|
287
296
|
indistinguishable from the unknown-subcommand Error (false-green). Routes to cmd_preflight \
|
|
288
297
|
(parser.py:160); shape {blockers,checks,details_log,next_actions,ok,summary}"]
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
298
|
+
fn dispatch_routes_preflight_real_machine() {
|
|
299
|
+
let ws = tmp_workspace();
|
|
300
|
+
let code = run(
|
|
301
|
+
&cli_argv(&["preflight", "--team", &ws.to_string_lossy(), "--json"]),
|
|
302
|
+
&ws,
|
|
303
|
+
);
|
|
304
|
+
assert_eq!(
|
|
305
|
+
code,
|
|
306
|
+
ExitCode::Ok,
|
|
307
|
+
"preflight must ROUTE + exit 0 on a valid team dir"
|
|
308
|
+
);
|
|
309
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
310
|
+
}
|
|
295
311
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
312
|
+
// ββ wait-ready (#[ignore] real-machine) ββ golden parser.py:171 `cmd_wait_ready` -> runtime.wait_ready
|
|
313
|
+
// (ws, timeout). Polls worker readiness; golden -> {"details_log":...,"next_actions":[...],"ok":bool,
|
|
314
|
+
// "readiness":{cli_prompt_ready,mcp_ready,process_started,task_prompt_delivered},"summary":...}.
|
|
315
|
+
// ok:true (exit 0) needs a LIVE ready team; CI has none -> golden-exits 1 (false-green vs unknown).
|
|
316
|
+
#[test]
|
|
317
|
+
#[ignore = "real-machine: `wait-ready` polls a LIVE team's readiness; with no workers golden-exits 1 \
|
|
302
318
|
(false-green vs unknown-subcommand Error). Routes to cmd_wait_ready (parser.py:171); shape \
|
|
303
319
|
{details_log,next_actions,ok,readiness{cli_prompt_ready,mcp_ready,process_started,task_prompt_delivered},summary}"]
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
320
|
+
fn dispatch_routes_wait_ready_real_machine() {
|
|
321
|
+
let ws = tmp_workspace();
|
|
322
|
+
let code = run(
|
|
323
|
+
&cli_argv(&[
|
|
324
|
+
"wait-ready",
|
|
325
|
+
"--workspace",
|
|
326
|
+
&ws.to_string_lossy(),
|
|
327
|
+
"--timeout",
|
|
328
|
+
"1",
|
|
329
|
+
"--json",
|
|
330
|
+
]),
|
|
331
|
+
&ws,
|
|
332
|
+
);
|
|
333
|
+
assert_eq!(
|
|
334
|
+
code,
|
|
335
|
+
ExitCode::Ok,
|
|
336
|
+
"wait-ready must ROUTE + exit 0 once the team is ready"
|
|
337
|
+
);
|
|
338
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
339
|
+
}
|
|
310
340
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
341
|
+
// ββ e2e --providers fake (#[ignore] real-machine) ββ golden parser.py:449 `cmd_e2e` (cli/e2e.py:12).
|
|
342
|
+
// `--providers fake` runs a REAL end-to-end: runtime.launch(spec, auto_approve=True) (spawns a tmux
|
|
343
|
+
// team) -> send_message -> sleep -> collect -> shutdown. Result envelope (e2e.py:16,48-56):
|
|
344
|
+
// {"workspace":str,"providers":{"fake":{"ok":bool,"launch":{...},"send":{...},"collect":{...},
|
|
345
|
+
// "shutdown":{...}}},"ok":bool}. Real tmux spawn -> #[ignore] (NOT runnable in-process).
|
|
346
|
+
#[test]
|
|
347
|
+
#[ignore = "real-machine: `e2e --providers fake` calls runtime.launch -> spawns a REAL tmux team \
|
|
318
348
|
(send/collect/shutdown). Routes to cmd_e2e (parser.py:449 / e2e.py:12); envelope \
|
|
319
349
|
{workspace,providers:{fake:{ok,launch,send,collect,shutdown}},ok}"]
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
350
|
+
fn dispatch_routes_e2e_fake_real_machine() {
|
|
351
|
+
let ws = tmp_workspace();
|
|
352
|
+
let code = run(
|
|
353
|
+
&cli_argv(&[
|
|
354
|
+
"e2e",
|
|
355
|
+
"--providers",
|
|
356
|
+
"fake",
|
|
357
|
+
"--workspace",
|
|
358
|
+
&ws.to_string_lossy(),
|
|
359
|
+
"--json",
|
|
360
|
+
]),
|
|
361
|
+
&ws,
|
|
362
|
+
);
|
|
363
|
+
assert_eq!(
|
|
364
|
+
code,
|
|
365
|
+
ExitCode::Ok,
|
|
366
|
+
"e2e --providers fake must ROUTE + run the fake end-to-end (ok:true)"
|
|
367
|
+
);
|
|
368
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
369
|
+
}
|
|
326
370
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
371
|
+
// ββ peek (#[ignore] real-machine) ββ golden parser.py:201 `cmd_peek` (commands.py:118). Requires
|
|
372
|
+
// `--allow-raw-screen` (else TeamAgentError) + a mutually-exclusive --head/--tail/--search; then
|
|
373
|
+
// runtime.peek captures a LIVE tmux pane (tmux capture-pane). Real tmux -> #[ignore]. Routes to
|
|
374
|
+
// cmd_peek; --json returns the peek dict (text + capture metadata).
|
|
375
|
+
#[test]
|
|
376
|
+
#[ignore = "real-machine: `peek <agent> --tail N --allow-raw-screen` captures a LIVE tmux pane \
|
|
333
377
|
(tmux capture-pane); needs a running worker pane. Routes to cmd_peek (parser.py:201); \
|
|
334
378
|
--json returns the peek dict (raw screen text + metadata)"]
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
379
|
+
fn dispatch_routes_peek_real_machine() {
|
|
380
|
+
let ws = tmp_workspace();
|
|
381
|
+
let code = run(
|
|
382
|
+
&cli_argv(&[
|
|
383
|
+
"peek",
|
|
384
|
+
"fake_impl",
|
|
385
|
+
"--workspace",
|
|
386
|
+
&ws.to_string_lossy(),
|
|
387
|
+
"--tail",
|
|
388
|
+
"20",
|
|
389
|
+
"--allow-raw-screen",
|
|
390
|
+
"--json",
|
|
391
|
+
]),
|
|
392
|
+
&ws,
|
|
393
|
+
);
|
|
394
|
+
assert_eq!(
|
|
395
|
+
code,
|
|
396
|
+
ExitCode::Ok,
|
|
397
|
+
"peek must ROUTE + capture the live pane (real machine)"
|
|
398
|
+
);
|
|
399
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
400
|
+
}
|
|
344
401
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
402
|
+
// CONTRACT (shared-root, real-machine-driven; golden = correct-behavior baseline): wait_readiness
|
|
403
|
+
// derives cli_prompt_ready from the LIFECYCLE status β an alive worker (status="running") IS
|
|
404
|
+
// cli_prompt_ready (golden quick_start.py:173 `status β {running, busy}`). Rust (diagnose.rs:280)
|
|
405
|
+
// requires cli_prompt_ready flag / startup_prompts=="complete" / status=="ready" and does NOT accept
|
|
406
|
+
// "running" β an alive fake worker never becomes ready β wait_ready times out. Same shared root as the
|
|
407
|
+
// deferred_busy regression: lifecycle "running" is the authoritative alive/ready signal, not a
|
|
408
|
+
// turn-level flag. (process_started/mcp_ready/task_prompt_delivered are satisfied here so `ready`
|
|
409
|
+
// hinges solely on the cli_prompt_ready derivation.)
|
|
410
|
+
#[test]
|
|
411
|
+
fn contract_alive_worker_running_is_cli_prompt_ready_and_ready() {
|
|
412
|
+
// A-5: missing leader_receiver no longer counts as attached; this contract's
|
|
413
|
+
// subject is the cli_prompt_ready derivation, so the fixture carries an
|
|
414
|
+
// attached receiver to keep `ready` hinging on it alone.
|
|
415
|
+
let state = serde_json::json!({
|
|
416
|
+
"leader_receiver": {"status": "attached", "pane_id": "%9"},
|
|
417
|
+
"agents": {"w1": {
|
|
418
|
+
"status": "running",
|
|
419
|
+
"pane_id": "%1",
|
|
420
|
+
"mcp_ready": true,
|
|
421
|
+
"first_send_at": "2026-01-01T00:00:00Z"
|
|
422
|
+
}}});
|
|
423
|
+
let r = crate::cli::diagnose::wait_readiness(&state);
|
|
424
|
+
assert_eq!(
|
|
368
425
|
r.get("cli_prompt_ready").and_then(serde_json::Value::as_bool),
|
|
369
426
|
Some(true),
|
|
370
427
|
"CONTRACT: an alive worker (lifecycle status=running) is cli_prompt_ready (golden statusβ{{running,busy}}); got {r:?}"
|
|
371
428
|
);
|
|
372
|
-
|
|
429
|
+
assert_eq!(
|
|
373
430
|
r.get("ready").and_then(serde_json::Value::as_bool),
|
|
374
431
|
Some(true),
|
|
375
432
|
"status=running + pane_id + mcp_ready + first_send_at β all four readiness signals derive true β ready (no timeout); got {r:?}"
|
|
376
433
|
);
|
|
377
|
-
|
|
434
|
+
}
|
|
378
435
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
436
|
+
// CONTRACT (real-machine wait_ready product FAIL @ 8ea5df5): a live fake quick-start worker times out
|
|
437
|
+
// with process_started=false + mcp_ready=false. golden (quick_start.py:172/174):
|
|
438
|
+
// process_started = bool(last["tmux_session_present"]) (the live tmux session exists)
|
|
439
|
+
// mcp_ready = all(Path(agent["mcp_config"]).exists()) (each agent's mcp_config FILE exists)
|
|
440
|
+
// The Rust launch (launch.rs:220-228) persists status="running" + mcp_config (path) but NO pane_id/pid
|
|
441
|
+
// and NO mcp_ready flag. wait_readiness reads per-agent pane_id/pid for process_started (-> false) and an
|
|
442
|
+
// mcp_ready FLAG for mcp_ready (-> false), so a live worker never becomes ready. The shared-root fix only
|
|
443
|
+
// corrected cli_prompt_ready (status=running); these two signals still read the wrong source. This RED
|
|
444
|
+
// uses the REALISTIC post-launch state (no synthetic pane_id / mcp_ready flag).
|
|
445
|
+
#[test]
|
|
446
|
+
fn contract_wait_ready_derives_process_started_from_session_and_mcp_ready_from_file() {
|
|
447
|
+
let dir = std::env::temp_dir().join(format!(
|
|
448
|
+
"ta-wr-{}-{}",
|
|
449
|
+
std::process::id(),
|
|
450
|
+
std::time::SystemTime::now()
|
|
451
|
+
.duration_since(std::time::UNIX_EPOCH)
|
|
452
|
+
.unwrap()
|
|
453
|
+
.as_nanos()
|
|
454
|
+
));
|
|
455
|
+
std::fs::create_dir_all(&dir).unwrap();
|
|
456
|
+
let mcp = dir.join("mcp.json");
|
|
457
|
+
std::fs::write(&mcp, "{}").unwrap();
|
|
458
|
+
let state = serde_json::json!({
|
|
459
|
+
"session_name": "ta-fake",
|
|
460
|
+
"tmux_session_present": true, // golden status() top-level signal: the live tmux session exists
|
|
461
|
+
// A-5: missing leader_receiver no longer counts as attached (see above).
|
|
462
|
+
"leader_receiver": {"status": "attached", "pane_id": "%9"},
|
|
463
|
+
"agents": { "w1": {
|
|
464
|
+
"status": "running",
|
|
465
|
+
"mcp_config": mcp.to_string_lossy(),
|
|
466
|
+
"first_send_at": "2026-01-01T00:00:00Z"
|
|
467
|
+
}}
|
|
468
|
+
});
|
|
469
|
+
let r = crate::cli::diagnose::wait_readiness(&state);
|
|
470
|
+
assert_eq!(
|
|
411
471
|
r.get("process_started").and_then(serde_json::Value::as_bool),
|
|
412
472
|
Some(true),
|
|
413
473
|
"CONTRACT: process_started derives from tmux_session_present (golden quick_start.py:172), NOT \
|
|
414
474
|
per-agent pane_id/pid (the launch writes neither); got {r:?}"
|
|
415
475
|
);
|
|
416
|
-
|
|
476
|
+
assert_eq!(
|
|
417
477
|
r.get("mcp_ready").and_then(serde_json::Value::as_bool),
|
|
418
478
|
Some(true),
|
|
419
479
|
"CONTRACT: mcp_ready derives from each agent's mcp_config FILE existence (golden quick_start.py:174), \
|
|
420
480
|
NOT an mcp_ready flag (never set); got {r:?}"
|
|
421
481
|
);
|
|
422
|
-
|
|
482
|
+
assert_eq!(
|
|
423
483
|
r.get("ready").and_then(serde_json::Value::as_bool),
|
|
424
484
|
Some(true),
|
|
425
485
|
"a live fake quick-start worker (session present + status running + mcp_config file + task sent) \
|
|
426
486
|
must be ready, not timeout; got {r:?}"
|
|
427
487
|
);
|
|
428
|
-
|
|
429
|
-
|
|
488
|
+
let _ = std::fs::remove_dir_all(&dir);
|
|
489
|
+
}
|