@team-agent/installer 0.5.29 → 0.5.32

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 CHANGED
@@ -575,7 +575,7 @@ dependencies = [
575
575
 
576
576
  [[package]]
577
577
  name = "team-agent"
578
- version = "0.5.29"
578
+ version = "0.5.32"
579
579
  dependencies = [
580
580
  "anyhow",
581
581
  "chrono",
package/Cargo.toml CHANGED
@@ -9,7 +9,7 @@ members = ["crates/team-agent", "crates/win-conpty-phase0", "crates/conpty-trans
9
9
 
10
10
  [workspace.package]
11
11
  edition = "2021"
12
- version = "0.5.29"
12
+ version = "0.5.32"
13
13
  license = "AGPL-3.0"
14
14
  rust-version = "1.95"
15
15
 
@@ -295,13 +295,33 @@ pub fn cmd_status_for_team(args: &StatusArgs, team: Option<&str>) -> Result<CmdR
295
295
  )?;
296
296
  return Ok(CmdResult::from_json(value, true));
297
297
  }
298
- Ok(CmdResult::human(append_reminder(
299
- status_port::format_status_scoped(
298
+ let mut text = status_port::format_status_scoped(
299
+ &selected.run_workspace,
300
+ &selected.state,
301
+ Some(&selected.team_key),
302
+ args.agent.as_deref(),
303
+ )?;
304
+ if args.detail {
305
+ let value = status_port::status_scoped(
300
306
  &selected.run_workspace,
301
307
  &selected.state,
302
308
  Some(&selected.team_key),
303
- args.agent.as_deref(),
304
- )?,
309
+ false,
310
+ true,
311
+ )?;
312
+ if let Some(hint) = value
313
+ .pointer("/runtime/hint")
314
+ .and_then(serde_json::Value::as_str)
315
+ .filter(|hint| !hint.is_empty())
316
+ {
317
+ if !text.is_empty() {
318
+ text.push('\n');
319
+ }
320
+ text.push_str(hint);
321
+ }
322
+ }
323
+ Ok(CmdResult::human(append_reminder(
324
+ text,
305
325
  crate::cli::STATUS_REMINDER,
306
326
  )))
307
327
  }
@@ -378,33 +398,6 @@ pub fn cmd_sessions(args: &SessionsArgs) -> Result<CmdResult, CliError> {
378
398
  ))
379
399
  }
380
400
 
381
- /// `cmd_validate_result`(`commands.py:206`)。
382
- pub fn cmd_validate_result(args: &ValidateResultArgs) -> Result<CmdResult, CliError> {
383
- let raw = if let Some(path) = &args.file {
384
- std::fs::read_to_string(path)?
385
- } else if let Some(envelope) = &args.envelope {
386
- envelope.clone()
387
- } else if let Some(result) = &args.result {
388
- result.clone()
389
- } else {
390
- let mut input = String::new();
391
- std::io::Read::read_to_string(&mut std::io::stdin(), &mut input)?;
392
- input
393
- };
394
- let envelope: Value = serde_json::from_str(&raw)?;
395
- crate::model::spec::validate_result_envelope(&envelope)
396
- .map_err(|e| CliError::Runtime(e.to_string()))?;
397
- Ok(CmdResult::from_json(
398
- json!({
399
- "ok": true,
400
- "task_id": envelope.get("task_id").cloned().unwrap_or(Value::Null),
401
- "agent_id": envelope.get("agent_id").cloned().unwrap_or(Value::Null),
402
- "status": envelope.get("status").cloned().unwrap_or(Value::Null),
403
- }),
404
- args.json,
405
- ))
406
- }
407
-
408
401
  /// `cmd_collect`(`parser.py:292`)。
409
402
  pub fn cmd_collect(args: &CollectArgs) -> Result<CmdResult, CliError> {
410
403
  cmd_collect_for_team(args, args.team.as_deref())
@@ -481,162 +474,6 @@ pub fn cmd_collect_for_team(args: &CollectArgs, team: Option<&str>) -> Result<Cm
481
474
  ))
482
475
  }
483
476
 
484
- /// `cmd_settle`(`commands.py:86`)。
485
- pub fn cmd_settle(args: &SettleArgs) -> Result<CmdResult, CliError> {
486
- match settle_value(&args.workspace, args.team.as_deref()) {
487
- Ok(value) => Ok(CmdResult::from_json(value, args.json)),
488
- Err(error) => Ok(CmdResult::from_json(
489
- json!({
490
- "ok": false,
491
- "error": error.to_string(),
492
- "workspace": args.workspace.to_string_lossy().to_string(),
493
- }),
494
- args.json,
495
- )),
496
- }
497
- }
498
-
499
- fn settle_value(workspace: &Path, team: Option<&str>) -> Result<Value, CliError> {
500
- // Bug #6 (prerelease 0.4.0 gate review §6): pass explicit team through to
501
- // resolve_active_team so settle scopes collect/status/team-state to the
502
- // requested team. Without this, the selector falls back to top-level
503
- // active_team_key and settle operates on the wrong team. resolve_active_team
504
- // logic unchanged per 不可改项.
505
- let selected = crate::state::selector::resolve_active_team(
506
- workspace,
507
- team,
508
- crate::state::selector::SelectorMode::RuntimeOnly,
509
- )
510
- .map_err(|e| CliError::Runtime(e.to_string()))?;
511
- let mut collect = messaging::collect_for_team(
512
- &selected.run_workspace,
513
- None,
514
- false,
515
- Some(&selected.team_key),
516
- )?;
517
- if collect.get("ok").and_then(Value::as_bool) == Some(false) {
518
- let message = collect
519
- .get("error")
520
- .and_then(Value::as_str)
521
- .unwrap_or("collect failed");
522
- return Err(CliError::Runtime(message.to_string()));
523
- }
524
- let coordinator_log = crate::coordinator::coordinator_log_path(
525
- &crate::coordinator::WorkspacePath::new(selected.run_workspace.clone()),
526
- );
527
- let collect_object = collect
528
- .as_object_mut()
529
- .ok_or_else(|| CliError::Runtime("collect returned non-object output".to_string()))?;
530
- collect_object.insert(
531
- "coordinator".to_string(),
532
- json!({
533
- "ok": true,
534
- "status": "started",
535
- "log": coordinator_log.to_string_lossy().to_string(),
536
- }),
537
- );
538
- collect_object.insert("team_key".to_string(), json!(selected.team_key.clone()));
539
- collect_object.insert("active_team_key".to_string(), json!(selected.team_key.clone()));
540
- collect_object.insert("team".to_string(), json!(selected.team_key.clone()));
541
- if let Some(collected_results) = collect_object.get("collected_results").cloned() {
542
- collect_object.insert("collected".to_string(), collected_results);
543
- }
544
- let status_state =
545
- crate::state::projection::select_runtime_state(&selected.run_workspace, Some(&selected.team_key))?;
546
- let state_file = match (selected.spec_path.as_ref(), selected.spec_workspace.as_ref()) {
547
- (Some(spec_path), Some(spec_workspace)) => match load_team_spec_at(spec_path)? {
548
- Some(spec) => crate::lifecycle::restart::write_team_state(spec_workspace, &spec, &status_state)
549
- .map_err(|e| CliError::Runtime(e.to_string()))?
550
- .to_string_lossy()
551
- .to_string(),
552
- None => collect
553
- .get("state_file")
554
- .and_then(Value::as_str)
555
- .unwrap_or("")
556
- .to_string(),
557
- },
558
- _ => collect
559
- .get("state_file")
560
- .and_then(Value::as_str)
561
- .unwrap_or("")
562
- .to_string(),
563
- };
564
- if let Some(obj) = collect.as_object_mut() {
565
- obj.insert("state_file".to_string(), json!(state_file.clone()));
566
- }
567
- let mut status = status_port::status_scoped(
568
- &selected.run_workspace,
569
- &status_state,
570
- Some(&selected.team_key),
571
- true,
572
- false,
573
- )?;
574
- if let Some(obj) = status.as_object_mut() {
575
- obj.insert("team_key".to_string(), json!(selected.team_key.clone()));
576
- obj.insert("active_team_key".to_string(), json!(selected.team_key.clone()));
577
- obj.insert("team".to_string(), json!(selected.team_key.clone()));
578
- }
579
- let details_log = write_settle_details_log(&selected.run_workspace, &collect, &status)?;
580
- let collected_count = collect
581
- .get("collected")
582
- .and_then(Value::as_array)
583
- .map_or(0, Vec::len);
584
- let settled_results = settle_collected_results_for_team(
585
- collect.get("collected_results"),
586
- &selected.team_key,
587
- );
588
- Ok(json!({
589
- "ok": true,
590
- "summary": format!("collected {collected_count} result(s)"),
591
- "next_actions": ["Review team_state.md and decide whether to continue or shutdown."],
592
- "details_log": details_log.to_string_lossy().to_string(),
593
- "collected_results": settled_results,
594
- "collected": collect.get("collected").cloned().unwrap_or_else(|| json!([])),
595
- "results": collect.get("results").cloned().unwrap_or_else(|| json!({})),
596
- "state_file": state_file,
597
- "status": status,
598
- "collect": collect,
599
- "team_key": selected.team_key,
600
- "active_team_key": selected.team_key,
601
- "team": selected.team_key,
602
- "workspace": selected.run_workspace.to_string_lossy().to_string(),
603
- }))
604
- }
605
-
606
- fn settle_collected_results_for_team(value: Option<&Value>, team_key: &str) -> Value {
607
- let Some(Value::Array(results)) = value else {
608
- return json!([]);
609
- };
610
- Value::Array(
611
- results
612
- .iter()
613
- .map(|result| {
614
- let mut result = result.clone();
615
- if let Some(obj) = result.as_object_mut() {
616
- obj.insert("owner_team_id".to_string(), json!(team_key));
617
- }
618
- result
619
- })
620
- .collect(),
621
- )
622
- }
623
-
624
- fn write_settle_details_log(workspace: &Path, collect: &Value, status: &Value) -> Result<PathBuf, CliError> {
625
- let logs = workspace.join(".team").join("logs");
626
- std::fs::create_dir_all(&logs)?;
627
- let timestamp = std::time::SystemTime::now()
628
- .duration_since(std::time::UNIX_EPOCH)
629
- .map_or(0, |duration| duration.as_secs());
630
- let path = logs.join(format!("settle-{timestamp}.json"));
631
- let details = json!({
632
- "collect": collect,
633
- "status": status,
634
- });
635
- let text = serde_json::to_string_pretty(&crate::cli::sort_json(&details))?;
636
- std::fs::write(&path, text)?;
637
- Ok(path)
638
- }
639
-
640
477
  /// `cmd_allow_peer_talk`(`parser.py allow-peer-talk`).
641
478
  pub fn cmd_allow_peer_talk(args: &AllowPeerTalkArgs) -> Result<CmdResult, CliError> {
642
479
  if args.team.is_some() {
@@ -648,65 +485,6 @@ pub fn cmd_allow_peer_talk(args: &AllowPeerTalkArgs) -> Result<CmdResult, CliErr
648
485
  Ok(CmdResult::from_json(value, args.json))
649
486
  }
650
487
 
651
- /// `cmd_repair_state`(`parser.py:303`)。
652
- pub fn cmd_repair_state(args: &RepairStateArgs) -> Result<CmdResult, CliError> {
653
- if !is_repair_task_status(&args.status) {
654
- return Err(CliError::Runtime(format!(
655
- "unknown task status for repair: {}",
656
- args.status
657
- )));
658
- }
659
- let selected = crate::state::selector::resolve_active_team(
660
- &args.workspace,
661
- args.team.as_deref(),
662
- crate::state::selector::SelectorMode::RequireSpec,
663
- )
664
- .map_err(|e| CliError::Runtime(e.to_string()))?;
665
- let mut state = selected.state;
666
- let before = find_task_projection(&state, &args.task_id).unwrap_or_else(repair_task_projection_null);
667
- update_task(
668
- &mut state,
669
- &args.task_id,
670
- args.assignee.as_deref(),
671
- &args.status,
672
- args.summary.as_deref(),
673
- );
674
- let after = find_task_projection(&state, &args.task_id).unwrap_or_else(repair_task_projection_null);
675
- crate::state::projection::save_team_scoped_state(&selected.run_workspace, &state)?;
676
- let spec_path = selected
677
- .spec_path
678
- .as_ref()
679
- .ok_or_else(|| CliError::Runtime("team.spec.yaml not found".to_string()))?;
680
- let spec = load_team_spec_at(spec_path)?
681
- .ok_or_else(|| CliError::Runtime("team.spec.yaml not found".to_string()))?;
682
- let spec_workspace = selected
683
- .spec_workspace
684
- .as_ref()
685
- .ok_or_else(|| CliError::Runtime("active team spec workspace not found".to_string()))?;
686
- let state_file = crate::lifecycle::restart::write_team_state(spec_workspace, &spec, &state)
687
- .map_err(|e| CliError::Runtime(e.to_string()))?;
688
- crate::event_log::EventLog::new(&selected.run_workspace)
689
- .write(
690
- "repair_state.task",
691
- json!({
692
- "task_id": args.task_id,
693
- "before": before,
694
- "after": after,
695
- }),
696
- )
697
- .map_err(|e| CliError::Runtime(e.to_string()))?;
698
- Ok(CmdResult::from_json(
699
- json!({
700
- "ok": true,
701
- "task_id": args.task_id,
702
- "before": before,
703
- "after": after,
704
- "state_file": state_file.to_string_lossy().to_string(),
705
- }),
706
- args.json,
707
- ))
708
- }
709
-
710
488
  /// `cmd_diagnose`(`parser.py:298`)。
711
489
  pub fn cmd_diagnose(args: &DiagnoseArgs) -> Result<CmdResult, CliError> {
712
490
  let selected = crate::state::selector::resolve_active_team(
@@ -1290,77 +1068,6 @@ fn window_target(agent_state: &Value, agent_id: &str) -> Value {
1290
1068
  }
1291
1069
 
1292
1070
 
1293
- fn find_task_projection(state: &Value, task_id: &str) -> Option<Value> {
1294
- state
1295
- .get("tasks")
1296
- .and_then(Value::as_array)
1297
- .and_then(|tasks| tasks.iter().find(|task| task.get("id").and_then(Value::as_str) == Some(task_id)))
1298
- .map(repair_task_projection)
1299
- }
1300
-
1301
- fn update_task(
1302
- state: &mut Value,
1303
- task_id: &str,
1304
- assignee: Option<&str>,
1305
- status: &str,
1306
- summary: Option<&str>,
1307
- ) -> Value {
1308
- if let Some(tasks) = state.get_mut("tasks").and_then(Value::as_array_mut) {
1309
- for task in tasks {
1310
- if task.get("id").and_then(Value::as_str) == Some(task_id) {
1311
- if let Some(obj) = task.as_object_mut() {
1312
- if let Some(assignee) = assignee {
1313
- obj.insert("assignee".to_string(), Value::String(assignee.to_string()));
1314
- }
1315
- obj.insert("status".to_string(), Value::String(status.to_string()));
1316
- if let Some(summary) = summary {
1317
- obj.insert("last_result_summary".to_string(), Value::String(summary.to_string()));
1318
- }
1319
- return Value::Object(obj.clone());
1320
- }
1321
- }
1322
- }
1323
- }
1324
- json!({
1325
- "id": task_id,
1326
- "assignee": assignee.unwrap_or(""),
1327
- "status": status,
1328
- "summary": summary.unwrap_or(""),
1329
- })
1330
- }
1331
-
1332
- fn is_repair_task_status(status: &str) -> bool {
1333
- matches!(
1334
- status,
1335
- "blocked" | "cancelled" | "done" | "failed" | "needs_retry" | "pending" | "ready" | "running"
1336
- )
1337
- }
1338
-
1339
- fn repair_task_projection(task: &Value) -> Value {
1340
- let mut map = serde_json::Map::new();
1341
- map.insert(
1342
- "assignee".to_string(),
1343
- task.get("assignee").cloned().unwrap_or(Value::Null),
1344
- );
1345
- map.insert(
1346
- "status".to_string(),
1347
- task.get("status").cloned().unwrap_or(Value::Null),
1348
- );
1349
- map.insert(
1350
- "last_result_summary".to_string(),
1351
- task.get("last_result_summary").cloned().unwrap_or(Value::Null),
1352
- );
1353
- Value::Object(map)
1354
- }
1355
-
1356
- fn repair_task_projection_null() -> Value {
1357
- let mut map = serde_json::Map::new();
1358
- map.insert("assignee".to_string(), Value::Null);
1359
- map.insert("status".to_string(), Value::Null);
1360
- map.insert("last_result_summary".to_string(), Value::Null);
1361
- Value::Object(map)
1362
- }
1363
-
1364
1071
  fn load_team_spec_optional(workspace: &Path, state: &Value) -> Result<Option<crate::model::yaml::Value>, CliError> {
1365
1072
  let spec_path = state
1366
1073
  .get("spec_path")
@@ -276,7 +276,7 @@ fn coordinator_issue_value(
276
276
 
277
277
  fn coordinator_repair_hint(id: &str, _health: &crate::coordinator::HealthReport) -> Value {
278
278
  let hint_action = match id {
279
- "coordinator_schema_incompatible" => "team-agent repair-state --schema",
279
+ "coordinator_schema_incompatible" => "team-agent doctor --fix-schema --json",
280
280
  _ => "team-agent restart",
281
281
  };
282
282
  json!({
@@ -96,12 +96,6 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
96
96
  "quick-start" => cmd_quick_start(&quick_start_args(args, cwd)?).map(emit_result),
97
97
  "compile" => cmd_compile(&compile_args(args, cwd)?).map(emit_result),
98
98
  "send" => cmd_send(&send_args(args, cwd)?).map(emit_result),
99
- "fallback-send-leader" => {
100
- cmd_fallback_send_leader(&fallback_send_leader_args(args, cwd)?).map(emit_result)
101
- }
102
- "fallback-report-result" => {
103
- cmd_fallback_report_result(&fallback_report_result_args(args, cwd)?).map(emit_result)
104
- }
105
99
  "allow-peer-talk" => {
106
100
  cmd_allow_peer_talk(&allow_peer_talk_args(args, cwd)?).map(emit_result)
107
101
  }
@@ -147,17 +141,10 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
147
141
  "validate" => cmd_validate(&validate_args(args, cwd)).map(emit_result),
148
142
  "install-skill" => cmd_install_skill(&install_skill_args(args)?).map(emit_result),
149
143
  "profile" => cmd_profile(&profile_args(args, cwd)?).map(emit_result),
150
- "validate-result" if has_arg(args, "--result") => {
151
- eprintln!("team-agent: error: unrecognized arguments: --result");
152
- Ok(ExitCode::Usage)
153
- }
154
- "validate-result" => cmd_validate_result(&validate_result_args(args)?).map(emit_result),
155
144
  "collect" => {
156
145
  cmd_collect_for_team(&collect_args(args, cwd)?, parse_args(args).team.as_deref())
157
146
  .map(emit_result)
158
147
  }
159
- "settle" => cmd_settle(&settle_args(args, cwd)).map(emit_result),
160
- "repair-state" => cmd_repair_state(&repair_state_args(args, cwd)?).map(emit_result),
161
148
  "diagnose" => cmd_diagnose(&diagnose_args(args, cwd)).map(emit_result),
162
149
  "preflight" => cmd_preflight(&preflight_args(args, cwd)).map(emit_result),
163
150
  "wait-ready" => cmd_wait_ready(&wait_ready_args(args, cwd)).map(emit_result),
@@ -173,8 +160,6 @@ const DISPATCH_COMMANDS: &[&str] = &[
173
160
  "quick-start",
174
161
  "compile",
175
162
  "send",
176
- "fallback-send-leader",
177
- "fallback-report-result",
178
163
  "allow-peer-talk",
179
164
  "status",
180
165
  "stop",
@@ -205,10 +190,7 @@ const DISPATCH_COMMANDS: &[&str] = &[
205
190
  "validate",
206
191
  "install-skill",
207
192
  "profile",
208
- "validate-result",
209
193
  "collect",
210
- "settle",
211
- "repair-state",
212
194
  "diagnose",
213
195
  "preflight",
214
196
  "wait-ready",
@@ -327,8 +309,6 @@ fn command_help(command: Option<&str>) -> String {
327
309
  Some("start") => compat_hidden_help("start", "usage: team-agent start [TEAMDIR] [--yes] [--fresh] [--json]"),
328
310
  Some("compile") => "usage: team-agent compile --team TEAM [--out FILE] [--json]".to_string(),
329
311
  Some("send") => "usage: team-agent send TARGET MESSAGE... [--workspace WORKSPACE] [--team TEAM] [--targets AGENTS] [--to-name NAME] [--pane PANE] [--task TASK] [--sender SENDER] [--watch-result] [--requires-ack|--no-ack] [--no-wait] [--timeout SECONDS] [--confirm-human] [--message-id ID] [--json]\n\nMVP: name-based cross-workspace addressing assumes trusted local caller; no auth gate.".to_string(),
330
- Some("fallback-send-leader") => compat_hidden_help("fallback-send-leader", "usage: team-agent fallback-send-leader --workspace WORKSPACE --team TEAM --sender AGENT --message-id ID --content TEXT --primary-error ERROR [--task TASK] [--json]\n\nEmergency one-shot fallback only after a leader-bound MCP send transport failure; restart-agent after use."),
331
- Some("fallback-report-result") => compat_hidden_help("fallback-report-result", "usage: team-agent fallback-report-result --workspace WORKSPACE --team TEAM --agent-id AGENT --task-id TASK --result-json JSON --primary-error ERROR [--json]\n\nEmergency one-shot fallback only after a report_result MCP transport failure; persists the result DB row, then restart-agent after use."),
332
312
  Some("allow-peer-talk") => "usage: team-agent allow-peer-talk A B [--workspace WORKSPACE] [--json]".to_string(),
333
313
  Some("status") => "usage: team-agent status [AGENT] [--workspace WORKSPACE] [--team TEAM] [--summary|--json] [--detail]\n\n默认输出: worker,空闲|工作|错误;错误细分走 status --summary".to_string(),
334
314
  Some("stop") => compat_hidden_help("stop", "usage: team-agent stop [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]"),
@@ -358,10 +338,7 @@ fn command_help(command: Option<&str>) -> String {
358
338
  Some("validate") => "usage: team-agent validate [SPEC] [--json]".to_string(),
359
339
  Some("install-skill") => "usage: team-agent install-skill (--source DIR | --uninstall) [--target codex|claude|copilot|all] [--dest DIR] [--dry-run] [--json]".to_string(),
360
340
  Some("profile") => "usage: team-agent profile COMMAND NAME [--workspace WORKSPACE] [--team TEAM] [--auth-mode MODE] [--json]".to_string(),
361
- Some("validate-result") => "usage: team-agent validate-result [ENVELOPE] [--file FILE|--result JSON] [--json]".to_string(),
362
341
  Some("collect") => "usage: team-agent collect [--workspace WORKSPACE] [--team TEAM] [--result-file FILE] [--json]".to_string(),
363
- Some("settle") => "usage: team-agent settle [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
364
- Some("repair-state") => "usage: team-agent repair-state --task TASK --status STATUS [SUMMARY] [--assignee AGENT] [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
365
342
  Some("diagnose") => "usage: team-agent diagnose [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
366
343
  Some("preflight") => "usage: team-agent preflight [TEAMDIR] [--json]".to_string(),
367
344
  Some("wait-ready") => "usage: team-agent wait-ready [--workspace WORKSPACE] [--team TEAM] [--timeout SECONDS] [--json]".to_string(),
@@ -1059,60 +1036,6 @@ fn send_args(args: &[String], cwd: &Path) -> Result<SendArgs, CliError> {
1059
1036
  })
1060
1037
  }
1061
1038
 
1062
- fn fallback_send_leader_args(
1063
- args: &[String],
1064
- cwd: &Path,
1065
- ) -> Result<FallbackSendLeaderArgs, CliError> {
1066
- let parsed = parse_args(args);
1067
- Ok(FallbackSendLeaderArgs {
1068
- workspace: workspace(&parsed, cwd),
1069
- team: parsed.team,
1070
- sender: parsed
1071
- .sender
1072
- .or(parsed.agent_id)
1073
- .ok_or_else(|| CliError::Usage("missing --sender <agent>".to_string()))?,
1074
- task: parsed.task.or(parsed.task_id),
1075
- message_id: parsed
1076
- .message_id
1077
- .ok_or_else(|| CliError::Usage("missing --message-id <id>".to_string()))?,
1078
- content: parsed
1079
- .content
1080
- .or_else(|| (!parsed.positionals.is_empty()).then(|| parsed.positionals.join(" ")))
1081
- .ok_or_else(|| CliError::Usage("missing --content <text>".to_string()))?,
1082
- primary_error: parsed
1083
- .primary_error
1084
- .ok_or_else(|| CliError::Usage("missing --primary-error <error>".to_string()))?,
1085
- json: parsed.json,
1086
- })
1087
- }
1088
-
1089
- fn fallback_report_result_args(
1090
- args: &[String],
1091
- cwd: &Path,
1092
- ) -> Result<FallbackReportResultArgs, CliError> {
1093
- let parsed = parse_args(args);
1094
- Ok(FallbackReportResultArgs {
1095
- workspace: workspace(&parsed, cwd),
1096
- team: parsed.team,
1097
- agent_id: parsed
1098
- .agent_id
1099
- .or(parsed.sender)
1100
- .ok_or_else(|| CliError::Usage("missing --agent-id <agent>".to_string()))?,
1101
- task_id: parsed
1102
- .task_id
1103
- .or(parsed.task)
1104
- .ok_or_else(|| CliError::Usage("missing --task-id <task>".to_string()))?,
1105
- result_json: parsed
1106
- .result_json
1107
- .or_else(|| parsed.result)
1108
- .ok_or_else(|| CliError::Usage("missing --result-json <json>".to_string()))?,
1109
- primary_error: parsed
1110
- .primary_error
1111
- .ok_or_else(|| CliError::Usage("missing --primary-error <error>".to_string()))?,
1112
- json: parsed.json,
1113
- })
1114
- }
1115
-
1116
1039
  /// Stage 4 of identity-boundary unified plan (architect direction
1117
1040
  /// 2026-06-24, .team/artifacts/identity-boundary-unified-plan.md §2 Stage
1118
1041
  /// 4): destructive command ambiguity gate. When the workspace has 2+
@@ -1471,16 +1394,6 @@ fn profile_args(args: &[String], cwd: &Path) -> Result<ProfileArgs, CliError> {
1471
1394
  })
1472
1395
  }
1473
1396
 
1474
- fn validate_result_args(args: &[String]) -> Result<ValidateResultArgs, CliError> {
1475
- let parsed = parse_args(args);
1476
- Ok(ValidateResultArgs {
1477
- envelope: parsed.positionals.first().cloned(),
1478
- file: parsed.file,
1479
- result: parsed.result,
1480
- json: parsed.json,
1481
- })
1482
- }
1483
-
1484
1397
  fn collect_args(args: &[String], cwd: &Path) -> Result<CollectArgs, CliError> {
1485
1398
  let parsed = parse_args(args);
1486
1399
  let workspace = workspace(&parsed, cwd);
@@ -1493,34 +1406,6 @@ fn collect_args(args: &[String], cwd: &Path) -> Result<CollectArgs, CliError> {
1493
1406
  })
1494
1407
  }
1495
1408
 
1496
- fn settle_args(args: &[String], cwd: &Path) -> SettleArgs {
1497
- let parsed = parse_args(args);
1498
- SettleArgs {
1499
- workspace: workspace(&parsed, cwd),
1500
- team: parsed.team.clone(),
1501
- json: parsed.json,
1502
- }
1503
- }
1504
-
1505
- fn repair_state_args(args: &[String], cwd: &Path) -> Result<RepairStateArgs, CliError> {
1506
- let parsed = parse_args(args);
1507
- let workspace = workspace(&parsed, cwd);
1508
- refuse_if_multi_alive_team_missing_scope("repair-state", &workspace, parsed.team.as_deref())?;
1509
- Ok(RepairStateArgs {
1510
- workspace,
1511
- task_id: parsed
1512
- .task
1513
- .ok_or_else(|| CliError::Usage("missing --task".to_string()))?,
1514
- assignee: parsed.assignee,
1515
- status: parsed
1516
- .status_value
1517
- .ok_or_else(|| CliError::Usage("missing --status".to_string()))?,
1518
- summary: option_value(args, "--summary").or_else(|| parsed.positionals.first().cloned()),
1519
- json: parsed.json,
1520
- team: parsed.team,
1521
- })
1522
- }
1523
-
1524
1409
  fn option_value(args: &[String], flag: &str) -> Option<String> {
1525
1410
  let prefix = format!("{flag}=");
1526
1411
  let mut i = 0usize;
@@ -1789,11 +1674,6 @@ mod tests {
1789
1674
  let top_help = command_help(None);
1790
1675
  let visible = visible_help_commands(&top_help);
1791
1676
  for command in [
1792
- "fallback-send-leader",
1793
- "fallback-report-result",
1794
- "repair-state",
1795
- "settle",
1796
- "validate-result",
1797
1677
  "leaders",
1798
1678
  "doctor",
1799
1679
  "e2e",
@@ -1809,14 +1689,7 @@ mod tests {
1809
1689
 
1810
1690
  #[test]
1811
1691
  fn compat_hidden_help_has_sunset_action() {
1812
- for command in [
1813
- "fallback-send-leader",
1814
- "fallback-report-result",
1815
- "stop",
1816
- "restart-agent",
1817
- "start",
1818
- "init",
1819
- ] {
1692
+ for command in ["stop", "restart-agent", "start", "init"] {
1820
1693
  let help = command_help(Some(command)).to_lowercase();
1821
1694
  assert!(help.contains("status: hidden compatibility command"));
1822
1695
  assert!(help.contains("sunset: c2"));
@@ -1980,17 +1853,6 @@ mod tests {
1980
1853
  &["--workspace", "--team", "--limit", "--since", "--json"][..],
1981
1854
  ),
1982
1855
  ("sessions", &["--workspace", "--team", "--json"][..]),
1983
- (
1984
- "repair-state",
1985
- &[
1986
- "--task",
1987
- "--status",
1988
- "--assignee",
1989
- "--workspace",
1990
- "--team",
1991
- "--json",
1992
- ][..],
1993
- ),
1994
1856
  ("diagnose", &["--workspace", "--team", "--json"][..]),
1995
1857
  (
1996
1858
  "wait-ready",
@@ -2203,7 +2065,7 @@ mod tests {
2203
2065
  fn refuse_helper_refuses_when_multi_alive_team_and_no_explicit_team() {
2204
2066
  let ws = tmp_workspace();
2205
2067
  seed_two_alive_teams_in(&ws);
2206
- let err = refuse_if_multi_alive_team_missing_scope("repair-state", &ws, None)
2068
+ let err = refuse_if_multi_alive_team_missing_scope("collect", &ws, None)
2207
2069
  .expect_err("multi-alive-team must refuse without --team");
2208
2070
  let message = err.to_string();
2209
2071
  assert!(
@@ -2215,7 +2077,7 @@ mod tests {
2215
2077
  "refusal must list candidate teams; got: {message}"
2216
2078
  );
2217
2079
  assert!(
2218
- message.contains("repair-state"),
2080
+ message.contains("collect"),
2219
2081
  "refusal must name the command for diagnostic clarity; got: {message}"
2220
2082
  );
2221
2083
  }
@@ -2244,22 +2106,6 @@ mod tests {
2244
2106
  );
2245
2107
  }
2246
2108
 
2247
- #[test]
2248
- fn repair_state_args_builder_refuses_on_multi_alive_team_before_task_validation() {
2249
- let ws = tmp_workspace();
2250
- seed_two_alive_teams_in(&ws);
2251
- // The ambiguity gate must fire BEFORE `--task` / `--status` validation
2252
- // so the operator sees the multi-team confusion before any other
2253
- // usage error.
2254
- let argv = cli_argv(&["--workspace", &ws.to_string_lossy()]);
2255
- let err = repair_state_args(&argv, &ws).expect_err("must refuse");
2256
- let message = err.to_string();
2257
- assert!(
2258
- message.contains("multiple alive teams"),
2259
- "ambiguity gate must precede --task/--status validation; got: {message}"
2260
- );
2261
- }
2262
-
2263
2109
  #[test]
2264
2110
  fn shutdown_args_builder_refuses_on_multi_alive_team() {
2265
2111
  let ws = tmp_workspace();