@team-agent/installer 0.5.97 → 0.5.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +0 -64
  4. package/crates/team-agent/src/cli/diagnose.rs +0 -16
  5. package/crates/team-agent/src/cli/emit.rs +15 -42
  6. package/crates/team-agent/src/cli/mod.rs +130 -31
  7. package/crates/team-agent/src/cli/spec.rs +7 -10
  8. package/crates/team-agent/src/cli/tests/main_preserved.rs +0 -1
  9. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +0 -1
  10. package/crates/team-agent/src/cli/tests/mod.rs +0 -1
  11. package/crates/team-agent/src/cli/tests/run_delegation.rs +0 -3
  12. package/crates/team-agent/src/cli/tests/verb_validate.rs +0 -1
  13. package/crates/team-agent/src/cli/types.rs +0 -14
  14. package/crates/team-agent/src/compiler/tests.rs +5 -8
  15. package/crates/team-agent/src/compiler.rs +6 -14
  16. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +1 -1
  17. package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +1 -1
  18. package/crates/team-agent/src/lifecycle/launch/agent_state.rs +5 -25
  19. package/crates/team-agent/src/lifecycle/launch/identity.rs +2 -18
  20. package/crates/team-agent/src/lifecycle/launch/layout.rs +7 -29
  21. package/crates/team-agent/src/lifecycle/launch/leader_context.rs +1 -1
  22. package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +1 -1
  23. package/crates/team-agent/src/lifecycle/launch/ownership.rs +1 -1
  24. package/crates/team-agent/src/lifecycle/launch/pi_mcp.rs +7 -7
  25. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +12 -49
  26. package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +3 -9
  27. package/crates/team-agent/src/lifecycle/launch/readiness.rs +1 -1
  28. package/crates/team-agent/src/lifecycle/launch/spawn.rs +2 -61
  29. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +2 -15
  30. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +3 -7
  31. package/crates/team-agent/src/lifecycle/launch/worker_env.rs +1 -1
  32. package/crates/team-agent/src/lifecycle/launch.rs +3 -4
  33. package/crates/team-agent/src/lifecycle/mod.rs +0 -8
  34. package/crates/team-agent/src/lifecycle/restart/agent.rs +62 -8
  35. package/crates/team-agent/src/lifecycle/restart/common.rs +0 -32
  36. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +0 -9
  37. package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +1 -2
  38. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +0 -1
  39. package/crates/team-agent/src/lifecycle/tests/core.rs +0 -213
  40. package/crates/team-agent/src/lifecycle/tests/issue238_codex_effort_contract.rs +96 -0
  41. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +1 -0
  42. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +31 -77
  43. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +2 -2
  44. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +4 -8
  45. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +7 -7
  46. package/crates/team-agent/src/lifecycle/tests/pi_executable_mcp_red.rs +1 -1
  47. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +3 -4
  48. package/crates/team-agent/src/lifecycle/tests.rs +1 -2
  49. package/crates/team-agent/src/lifecycle/types.rs +3 -133
  50. package/crates/team-agent/src/model/enums.rs +21 -71
  51. package/crates/team-agent/src/model/spec.rs +5 -20
  52. package/crates/team-agent/src/provider/adapters/codex.rs +2 -2
  53. package/crates/team-agent/src/provider/types.rs +6 -5
  54. package/examples/team.spec.yaml +0 -1
  55. package/package.json +4 -4
  56. package/schemas/team.schema.json +2 -2
  57. package/skills/team-agent/SKILL.md +1 -1
  58. package/skills/team-agent/references/team-agent-operator.md +23 -38
  59. package/crates/team-agent/src/cli/tests/compile.rs +0 -192
  60. package/crates/team-agent/src/lifecycle/display.rs +0 -550
  61. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +0 -884
  62. package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +0 -481
@@ -43,7 +43,7 @@ team-agent codex
43
43
  team-agent claude
44
44
  ```
45
45
 
46
- Pass provider flags after the provider name, for example `team-agent codex --dangerously-bypass-approvals-and-sandbox`. Existing tmux layouts are valid too, including Finder/Ghostty launchers, as long as `team-agent quick-start` is invoked from the leader's current tmux pane. Do not start a real team from a naked terminal that Team Agent cannot address through tmux.
46
+ Pass provider flags after the provider name, for example `team-agent codex --dangerously-bypass-approvals-and-sandbox`. Run `team-agent quick-start` from the leader's current tmux pane. Do not start a real team from a naked terminal that Team Agent cannot address through tmux.
47
47
 
48
48
  **0.5.66:** leader `--dangerously-*` flags no longer inherit bypass to workers. Per-worker bypass is the role field `dangerously_skip_permissions` (see Permissions).
49
49
 
@@ -55,7 +55,7 @@ When the user has been communicating in Chinese throughout the conversation, all
55
55
 
56
56
  ## Minimal Copy-Paste Team
57
57
 
58
- `dangerously_skip_permissions` is **required** on every role doc (boolean). `TEAM.md` `dangerous_auto_approve` is **not** the 0.5.66 bypass switch (see Permissions). Values under `provider_models:` below are **fill-ins for omitted role `model`**, not a validity catalog (lookup: `team-agent compile --team <dir> --out /tmp/spec.yaml --json` and read the compiled agent `model`).
58
+ `dangerously_skip_permissions` is **required** on every role doc (boolean). `TEAM.md` `dangerous_auto_approve` is **not** the 0.5.66 bypass switch (see Permissions). Values under `provider_models:` below are **fill-ins for omitted role `model`**, not a validity catalog; the values are configuration fallbacks.
59
59
 
60
60
  ```bash
61
61
  mkdir -p .team/current/agents
@@ -98,14 +98,7 @@ team-agent quick-start .team/current
98
98
 
99
99
  YAML lists must be block style. Use `tools:\n - fs_read`; do not use `tools: [fs_read, mcp_team]`.
100
100
 
101
- Display choices (set `display_backend:` in `TEAM.md` to opt in):
102
-
103
- - `none` (default): headless / no GUI window manager. The team runs entirely in the per-workspace tmux server; this is what the demo above uses.
104
- - `adaptive`: framework picks an available GUI layout for the local platform.
105
- - `ghostty_workspace`: one Ghostty window. Workers are shown in tmux tabs/windows, up to 3 side-by-side panes per tab. Four workers become `3 + 1`; eight become `3 + 3 + 2`.
106
- - `ghostty_window`: one Ghostty window per worker.
107
-
108
- **Omitting `display_backend` defaults to `none`** (changed in 0.3.4). Set `display_backend: adaptive` (or one of the explicit ghostty variants) in `TEAM.md` only when the user wants GUI windows.
101
+ Workers always run headlessly in independent windows on the per-workspace tmux server. No GUI display backend is configured in `TEAM.md`.
109
102
 
110
103
  ## Private Tmux Socket
111
104
 
@@ -179,7 +172,7 @@ codex debug models
179
172
 
180
173
  That command is the Codex catalog. This skill does **not** keep a model id list. If `codex` is not on PATH, the lookup is unavailable; do not invent slugs.
181
174
 
182
- Role docs may omit `model` for non-grok subscription workers; compile then fills from `TEAM.md` `provider_models` or leaves `null` when a profile is present (see compatible_api). **Do not treat those fill-ins as a validity catalog.** **Grok roles cannot omit `model`** — compile fails closed (same `compile --team` command as below).
175
+ Role docs may omit `model` for non-grok subscription workers; compile then fills from `TEAM.md` `provider_models` or leaves `null` when a profile is present (see compatible_api). **Do not treat those fill-ins as a validity catalog.** **Grok roles cannot omit `model`** — the internal compiler fails closed.
183
176
 
184
177
  Claude: run `claude auth status`; if missing, run `claude auth login`. Team Agent stores Claude worker sessions by passing `--session-id` and resumes with `--resume`.
185
178
  Use `provider: claude` or `provider: claude_code` for Claude workers.
@@ -233,15 +226,17 @@ POST {BASE_URL}/v1/chat/completions
233
226
 
234
227
  Fail = report the HTTP/body. Success = leave the id alone. **Do not substitute a different id.**
235
228
 
236
- How compile currently wires role `model` vs profile `MODEL` (not a validity list):
229
+ How role `model` and profile `MODEL` are resolved (not a validity list):
230
+
231
+ The internal compiler preserves a role `model:` when present. If it is omitted, provider/team defaults may fill it; a profile-deferred role may remain `null`. A profile's `MODEL` does not override an explicit role model, and the compiler does not compare the two. Actual profile/provider readiness is checked during launch and preflight, not by a public export command.
232
+
233
+ For an existing spec file or team directory at `$d`, run the retained validation:
237
234
 
238
235
  ```bash
239
- team-agent compile --help
236
+ team-agent validate "$d" --json
240
237
  ```
241
238
 
242
- Observed (exit 0): `usage: team-agent compile --team TEAM [--out FILE] [--json]`.
243
-
244
- Recreate: `auth_mode: compatible_api` without `profile` → compile exit 1 `profile is required when auth_mode is 'compatible_api'`. Role `model:` set + `profile:` set → compiled agent `model` is the role string; profile `MODEL` is not read at compile. Role omits `model` + `profile:` → compiled `model: null`. Compile does **not** compare the two. Whether launch refuses a mismatch was **not** spawned-verified.
239
+ A file input runs schema structural validation. A team-directory input internally compiles `TEAM.md` and `agents/*.md` and validates role-document completeness. Neither input writes or exports a spec file.
245
240
 
246
241
  How to check `--auth-mode` literals (the CLI does not print an allow-list on this gauge):
247
242
 
@@ -265,7 +260,7 @@ Observed (all exit 0):
265
260
 
266
261
  ```text
267
262
  usage: team-agent restart [WORKSPACE] [--team TEAM] [--allow-fresh] [--session-converge-deadline SECONDS] [--json]
268
- usage: team-agent add-agent AGENT --role-file FILE [--force] [--workspace WORKSPACE] [--team TEAM] [--no-display] [--json]
263
+ usage: team-agent add-agent AGENT --role-file FILE [--force] [--workspace WORKSPACE] [--team TEAM] [--json]
269
264
  usage: team-agent shutdown [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]
270
265
  ```
271
266
 
@@ -289,26 +284,19 @@ team-agent permission-modes
289
284
 
290
285
  Observed (exit 1): `invalid choice: 'permission-modes'`.
291
286
 
292
- `team-agent --help` does **not** list `permission_mode`, `TEAM_AGENT_LEADER_BYPASS`, or `dangerous_auto_approve`. Lookup is `team-agent compile` / `team-agent validate` on a throwaway team dir, not a flag enum.
287
+ `team-agent --help` does **not** list `permission_mode`, `TEAM_AGENT_LEADER_BYPASS`, or `dangerous_auto_approve`. Use `team-agent validate "$d" --json` with an existing spec file or team directory; it does not enumerate permission modes.
293
288
 
294
289
  ### Live control: role `dangerously_skip_permissions`
295
290
 
296
- Required boolean on every role doc. Missing or non-bool fails compile.
291
+ Required boolean on every role doc. Missing or non-bool fails the internal compiler; this is checked again before lifecycle launch.
297
292
 
298
- Recreate:
293
+ For an existing spec file, the retained schema check is:
299
294
 
300
295
  ```bash
301
- d=$(mktemp -d) && mkdir -p "$d/agents"
302
- printf '%s\n' '---' 'name: t' 'objective: t' 'provider: fake' '---' 'x' > "$d/TEAM.md"
303
- printf '%s\n' '---' 'name: coder' 'role: Worker' 'provider: fake' 'model: fake' 'auth_mode: subscription' 'tools:' ' - mcp_team' '---' 'b' > "$d/agents/coder.md"
304
- team-agent compile --team "$d" --out /tmp/ta-compile-out.yaml --json
296
+ team-agent validate "$d" --json
305
297
  ```
306
298
 
307
- Observed (exit 1): `missing front matter field dangerously_skip_permissions. This field must be declared explicitly; it controls whether the agent launches with permission prompts bypassed.` JSON also includes `"action": "run \`team-agent doctor\` or inspect the log path shown here"`.
308
-
309
- Non-bool (`dangerously_skip_permissions: bypass`) observed (exit 1): `front matter field dangerously_skip_permissions must be a boolean.`
310
-
311
- Legal values reachable from that error: **boolean `true` or `false`**. No other literals are accepted.
299
+ With a team-directory input, `validate` internally compiles `TEAM.md` and `agents/*.md` and validates role-document completeness; with a file input, it runs schema structural validation. Neither input writes or exports a spec file. The internal compiler rejects non-bool (`dangerously_skip_permissions: bypass`) and accepts only boolean **`true`** or **`false`**.
312
300
 
313
301
  `true` is the per-worker bypass opt-in. `false` is the default you should copy unless the user explicitly wants prompts skipped.
314
302
 
@@ -345,7 +333,7 @@ What the sources actually do (reader can re-open these files):
345
333
  | `required_dangerously_skip_permissions` | `crates/team-agent/src/compiler.rs` | Compile **requires** role `dangerously_skip_permissions` bool. This is the 0.5.66 worker bypass source. |
346
334
  | `resolved_tool_strings_for_command` | `crates/team-agent/src/lifecycle/worker_command_context.rs` | `true` appends tool sentinel `dangerous_auto_approve`; comment: no longer consumes team/runtime/leader `DangerousApproval`. |
347
335
  | `provider_bypass_flag` | `crates/team-agent/src/provider/bypass_flags.rs` | **唯一权威**: role `dangerously_skip_permissions: true` decides whether to add a provider bypass argv flag. Maps provider → flag string only. |
348
- | spec allowed key `permission_mode` | `crates/team-agent/src/model/spec.rs` | Historical; **not consumed** by compiler (comment: 0.5.66 起不再被 compiler 消费). CLI compile of `permission_mode: bypass` and `permission_mode: not-a-mode` both `ok: true` with the key absent from spec. |
336
+ | spec allowed key `permission_mode` | `crates/team-agent/src/model/spec.rs` | Historical; **not consumed** by compiler (comment: 0.5.66 起不再被 compiler 消费); the key is absent from the compiled agent spec. |
349
337
  | `apply_mcp_auto_approval_env` | `crates/team-agent/src/lifecycle/launch/worker_env.rs` | Writes `TEAM_AGENT_LEADER_BYPASS` `1`/`0` from `DangerousApproval` when source is `LeaderProcess`. Separate from compile. |
350
338
  | `worker_spawn_env` test | `crates/team-agent/src/layout/worker_env.rs` | **Strips** inherited `TEAM_AGENT_LEADER_BYPASS` from parent env. |
351
339
  | `claude_dangerous_auto_approve` (and siblings) | `crates/team-agent/src/provider/adapters/*.rs` | Adapters look for tool name `dangerous_auto_approve`, not `TEAM.md`. |
@@ -462,7 +450,7 @@ On success, `send --json` includes `message_id`. The deprecation warning itself
462
450
  - `team-agent start-agent coder --workspace .` repairs one missing worker window without interrupting other workers.
463
451
  - `team-agent doctor` checks local dependencies and provider auth hints.
464
452
  - `team-agent results --case CASE_ID` reads reported results for a case.
465
- - `team-agent compile` / `team-agent validate` are hidden from top-level `--help` but `--help` on those verbs works; use them to check role docs without launching.
453
+ - `team-agent validate` is hidden from top-level `--help` but `--help` on the verb works; use it to check an existing spec without launching.
466
454
 
467
455
  <!-- command-coverage:normative-start -->
468
456
  ### Normative command inventory
@@ -620,16 +608,13 @@ For any non-zero `team-agent` exit, report the command, exit code, last about 20
620
608
 
621
609
  **If the error JSON/text includes a structured `action` field, run that `action` first.** This rule does not expire with skill versions.
622
610
 
623
- Reproduce a structured `action` (missing `dangerously_skip_permissions` → compile JSON):
611
+ For an existing spec file or team directory, use the retained validation:
624
612
 
625
613
  ```bash
626
- d=$(mktemp -d) && mkdir -p "$d/agents"
627
- printf '%s\n' '---' 'name: t' 'objective: t' 'provider: fake' '---' 'x' > "$d/TEAM.md"
628
- printf '%s\n' '---' 'name: coder' 'role: Worker' 'provider: fake' 'model: fake' 'auth_mode: subscription' 'tools:' ' - mcp_team' '---' 'b' > "$d/agents/coder.md"
629
- team-agent compile --team "$d" --out /tmp/ta-failure-rules-compile.yaml --json
614
+ team-agent validate "$d" --json
630
615
  ```
631
616
 
632
- Observed (exit 1): JSON includes `"ok": false` and `"action": "run \`team-agent doctor\` or inspect the log path shown here"`, plus `missing front matter field dangerously_skip_permissions`. Prefer the `action` over guessing flags.
617
+ A validation failure may include a structured `action`; prefer that action over guessing flags.
633
618
 
634
619
  `coordinator.session_missing` is a self-healing transient: run `team-agent status --json`, if `ready` is true (or workers show `running`) continue; do not shutdown and wait.
635
620
 
@@ -637,7 +622,7 @@ Observed (exit 1): JSON includes `"ok": false` and `"action": "run \`team-agent
637
622
 
638
623
  Examples of `action` observed on this gauge:
639
624
 
640
- - compile/add-agent usage failure: `"action": "run \`team-agent doctor\` or inspect the log path shown here"`
625
+ - add-agent usage failure: `"action": "run \`team-agent doctor\` or inspect the log path shown here"`
641
626
  - send with no runtime: `"action": "Run team-agent quick-start/restart in the target workspace, or choose a workspace that has .team/runtime/state.json"`
642
627
  - `claim-leader` from a worker pane: `"action": "pane %21 is registered as worker developer-d108; run claim-leader from the leader's own pane, not a worker pane"` (exit 1, `ok: false`, `reason: caller_not_leader_shaped`)
643
628
 
@@ -1,192 +0,0 @@
1
- use super::*;
2
-
3
- fn compile_team_dir(tag: &str) -> std::path::PathBuf {
4
- let team = tmp_workspace().join(tag);
5
- std::fs::create_dir_all(team.join("agents")).unwrap();
6
- std::fs::write(
7
- team.join("TEAM.md"),
8
- "---\nname: compileteam\nobjective: Compile probe.\nprovider: fake\n---\n\nCompile team.\n",
9
- )
10
- .unwrap();
11
- std::fs::write(
12
- team.join("agents").join("worker.md"),
13
- "---\nname: worker\nrole: Worker\nprovider: fake\nmodel: fake\ntools:\n - mcp_team\ndangerously_skip_permissions: false\n---\n\nWorker role.\n",
14
- )
15
- .unwrap();
16
- team
17
- }
18
-
19
- fn compile_team_dir_with_owner_team_id(tag: &str) -> std::path::PathBuf {
20
- let team = compile_team_dir(tag);
21
- std::fs::write(
22
- team.join("TEAM.md"),
23
- "---\nname: compileteam\nobjective: Compile probe.\nprovider: fake\nowner_team_id: user-set-owner\n---\n\nCompile team.\n",
24
- )
25
- .unwrap();
26
- team
27
- }
28
-
29
- #[test]
30
- fn cmd_compile_json_and_human_match_golden_shape_and_writes_out() {
31
- let team = compile_team_dir("compile-ok");
32
- let out = team.parent().unwrap().join("out.yaml");
33
- let args = CompileArgs {
34
- team: team.clone(),
35
- out: out.clone(),
36
- json: true,
37
- };
38
-
39
- let result = cmd_compile(&args).expect("compile");
40
- assert_eq!(result.exit, ExitCode::Ok);
41
- assert!(
42
- out.exists(),
43
- "compile must write the compiled spec to --out"
44
- );
45
- assert!(
46
- std::fs::read_to_string(&out)
47
- .unwrap()
48
- .contains("version: 1"),
49
- "compiled out file must contain the spec YAML"
50
- );
51
-
52
- let json_text = emit(&result.output, true).unwrap();
53
- let expected_json = format!(
54
- "{{\n \"agents\": [\n \"worker\"\n ],\n \"ok\": true,\n \"out\": \"{}\",\n \"team_dir\": \"{}\"\n}}",
55
- out.to_string_lossy(),
56
- team.to_string_lossy()
57
- );
58
- assert_eq!(
59
- json_text, expected_json,
60
- "golden --json is sorted pretty JSON"
61
- );
62
-
63
- let human_text = emit(&result.output, false).unwrap();
64
- let expected_human = format!(
65
- "ok: True\nteam_dir: {}\nout: {}\nagents: [\"worker\"]",
66
- team.to_string_lossy(),
67
- out.to_string_lossy()
68
- );
69
- assert_eq!(
70
- human_text, expected_human,
71
- "golden human output preserves cmd_compile insertion order"
72
- );
73
- }
74
-
75
- #[test]
76
- fn cmd_compile_ignores_user_owner_team_id_and_emits_warning_event() {
77
- let team = compile_team_dir_with_owner_team_id("compile-ignored-owner");
78
- let workspace = team.parent().unwrap().to_path_buf();
79
- let out = workspace.join("ignored-owner-out.yaml");
80
- let args = CompileArgs {
81
- team: team.clone(),
82
- out: out.clone(),
83
- json: true,
84
- };
85
-
86
- let result = cmd_compile(&args).expect("compile");
87
- assert_eq!(result.exit, ExitCode::Ok);
88
- let compiled = std::fs::read_to_string(&out).unwrap();
89
- assert!(
90
- !compiled.contains("owner_team_id"),
91
- "user-set owner_team_id must not be compiled into team.spec.yaml"
92
- );
93
- let events = crate::event_log::EventLog::new(&workspace).tail(0).unwrap();
94
- let event = events
95
- .iter()
96
- .find(|event| {
97
- event.get("event").and_then(serde_json::Value::as_str) == Some("spec.field_ignored")
98
- })
99
- .expect("owner_team_id warning event");
100
- assert_eq!(
101
- event.get("field").and_then(serde_json::Value::as_str),
102
- Some("owner_team_id")
103
- );
104
- assert_eq!(
105
- event.get("value").and_then(serde_json::Value::as_str),
106
- Some("user-set-owner")
107
- );
108
- assert_eq!(
109
- event.get("action").and_then(serde_json::Value::as_str),
110
- Some("remove owner_team_id from TEAM.md")
111
- );
112
- }
113
-
114
- #[test]
115
- fn cmd_compile_without_owner_team_id_emits_no_ignored_field_event() {
116
- let team = compile_team_dir("compile-no-ignored-owner");
117
- let workspace = team.parent().unwrap().to_path_buf();
118
- let out = workspace.join("no-ignored-owner-out.yaml");
119
- let args = CompileArgs {
120
- team: team.clone(),
121
- out,
122
- json: true,
123
- };
124
-
125
- let result = cmd_compile(&args).expect("compile");
126
- assert_eq!(result.exit, ExitCode::Ok);
127
- let events = crate::event_log::EventLog::new(&workspace).tail(0).unwrap();
128
- assert!(
129
- events.iter().all(|event| {
130
- event.get("event").and_then(serde_json::Value::as_str) != Some("spec.field_ignored")
131
- }),
132
- "TEAM.md without owner_team_id must not emit ignored-field warning"
133
- );
134
- }
135
-
136
- #[test]
137
- fn run_dispatches_compile_and_error_path_exits_error() {
138
- let team = compile_team_dir("compile-dispatch");
139
- let out = team.parent().unwrap().join("dispatch.yaml");
140
- let argv = vec![
141
- "compile".to_string(),
142
- "--team".to_string(),
143
- team.to_string_lossy().to_string(),
144
- "--out".to_string(),
145
- out.to_string_lossy().to_string(),
146
- "--json".to_string(),
147
- ];
148
- assert_eq!(run(&argv, team.parent().unwrap()), ExitCode::Ok);
149
- assert!(
150
- out.exists(),
151
- "dispatch compile must route to cmd_compile and write --out"
152
- );
153
-
154
- let bad = tmp_workspace().join("compile-bad");
155
- std::fs::create_dir_all(bad.join("agents")).unwrap();
156
- std::fs::write(
157
- bad.join("TEAM.md"),
158
- "---\nname: badteam\nobjective: Bad.\nprovider: fake\n---\n",
159
- )
160
- .unwrap();
161
- std::fs::write(
162
- bad.join("agents").join("broken.md"),
163
- "---\nname: broken\nrole: Broken\nmodel: fake\n---\n",
164
- )
165
- .unwrap();
166
- let bad_out = bad.parent().unwrap().join("bad.yaml");
167
- let bad_args = CompileArgs {
168
- team: bad.clone(),
169
- out: bad_out.clone(),
170
- json: true,
171
- };
172
- let err = cmd_compile(&bad_args).unwrap_err().to_string();
173
- assert!(
174
- err.contains("missing front matter field provider"),
175
- "got {err}"
176
- );
177
- assert_eq!(
178
- run(
179
- &[
180
- "compile".to_string(),
181
- "--team".to_string(),
182
- bad.to_string_lossy().to_string(),
183
- "--out".to_string(),
184
- bad_out.to_string_lossy().to_string(),
185
- "--json".to_string(),
186
- ],
187
- bad.parent().unwrap(),
188
- ),
189
- ExitCode::Error,
190
- "invalid compile input must exit 1, not fall through as an unknown subcommand"
191
- );
192
- }