@team-agent/installer 0.5.111 → 0.5.112

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 (38) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +1 -743
  4. package/crates/team-agent/src/cli/diagnose.rs +2 -424
  5. package/crates/team-agent/src/cli/emit.rs +31 -275
  6. package/crates/team-agent/src/cli/mod.rs +2 -9
  7. package/crates/team-agent/src/cli/spec.rs +0 -3
  8. package/crates/team-agent/src/cli/tests/leader_watch.rs +2 -86
  9. package/crates/team-agent/src/cli/tests/main_preserved.rs +15 -140
  10. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +1 -258
  11. package/crates/team-agent/src/cli/tests/mod.rs +0 -2
  12. package/crates/team-agent/src/cli/types.rs +4 -108
  13. package/crates/team-agent/src/coordinator/health.rs +3 -283
  14. package/crates/team-agent/src/coordinator/mod.rs +0 -5
  15. package/crates/team-agent/src/coordinator/tests/basics.rs +0 -9
  16. package/crates/team-agent/src/coordinator/tests/mod.rs +0 -1
  17. package/crates/team-agent/src/coordinator/tests/tick_core.rs +0 -17
  18. package/crates/team-agent/src/coordinator/types.rs +1 -21
  19. package/crates/team-agent/src/db/message_store.rs +0 -42
  20. package/crates/team-agent/src/db/migration.rs +49 -9
  21. package/crates/team-agent/src/db/schema.rs +27 -20
  22. package/crates/team-agent/src/leader/owner_bind.rs +0 -24
  23. package/crates/team-agent/src/leader/tests/byte_findings.rs +0 -20
  24. package/crates/team-agent/src/leader/tests/identity.rs +0 -32
  25. package/crates/team-agent/src/leader/tests/lease_claim.rs +0 -55
  26. package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -51
  27. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +23 -23
  28. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +29 -55
  29. package/crates/team-agent/src/messaging/mod.rs +0 -2
  30. package/crates/team-agent/src/messaging/results.rs +0 -58
  31. package/crates/team-agent/src/messaging/tests/runtime.rs +0 -46
  32. package/crates/team-agent/src/state/repository.rs +1 -5
  33. package/package.json +4 -4
  34. package/templates/team_state.md +1 -1
  35. package/crates/team-agent/src/cli/tests/peer_allow.rs +0 -164
  36. package/crates/team-agent/src/cli/tests/verb_validate.rs +0 -195
  37. package/crates/team-agent/src/coordinator/tests/watch.rs +0 -223
  38. package/crates/team-agent/src/messaging/peers.rs +0 -34
@@ -251,7 +251,6 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
251
251
  | "clone-agent"
252
252
  | "fork-agent"
253
253
  | "remove-agent"
254
- | "allow-peer-talk"
255
254
  | "profile"
256
255
  )
257
256
  {
@@ -262,9 +261,6 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
262
261
  match command {
263
262
  "quick-start" => cmd_quick_start(&quick_start_args(args, cwd)?).map(emit_result),
264
263
  "send" => cmd_send(&send_args(args, cwd)?).map(emit_result),
265
- "allow-peer-talk" => {
266
- cmd_allow_peer_talk(&allow_peer_talk_args(args, cwd)?).map(emit_result)
267
- }
268
264
  "status" => cmd_status_for_team(&status_args(args, cwd), parse_args(args).team.as_deref())
269
265
  .map(emit_result),
270
266
  "shutdown" => cmd_shutdown(&shutdown_args(args, cwd)?).map(emit_result),
@@ -306,33 +302,13 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
306
302
  fn is_machine_command(command: &str) -> bool {
307
303
  matches!(
308
304
  command,
309
- "results"
310
- | "wait"
311
- | "wait-ready"
312
- | "preflight"
313
- | "validate"
314
- | "identity"
315
- | "sessions"
316
- | "watch"
317
- | "e2e"
318
- | "peek"
319
- | "coordinator"
320
- | "attach-app-server-leader"
305
+ "wait" | "coordinator" | "attach-app-server-leader"
321
306
  )
322
307
  }
323
308
 
324
309
  fn dispatch_machine(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliError> {
325
310
  match command {
326
- "results" => cmd_results(&results_args(args, cwd)?).map(emit_result),
327
311
  "wait" => cmd_wait(&wait_args(args, cwd)?).map(emit_result),
328
- "wait-ready" => cmd_wait_ready(&wait_ready_args(args, cwd)).map(emit_result),
329
- "preflight" => cmd_preflight(&preflight_args(args, cwd)).map(emit_result),
330
- "validate" => cmd_validate(&validate_args(args, cwd)).map(emit_result),
331
- "identity" => cmd_identity(&identity_args(args, cwd)).map(emit_result),
332
- "sessions" => cmd_sessions(&sessions_args(args, cwd)).map(emit_result),
333
- "watch" => cmd_watch(&watch_args(args, cwd)).map(emit_result),
334
- "e2e" => cmd_e2e(&e2e_args(args, cwd)).map(emit_result),
335
- "peek" => cmd_peek(&peek_args(args, cwd)?).map(emit_result),
336
312
  "coordinator" => run_coordinator(args, cwd),
337
313
  "attach-app-server-leader" => {
338
314
  cmd_attach_app_server_leader(&attach_app_server_leader_args(args, cwd)?)
@@ -387,7 +363,7 @@ pub(crate) fn default_help() -> String {
387
363
  append_help_section(
388
364
  &mut out,
389
365
  "观察与协作",
390
- &["leaders", "doctor", "approvals", "allow-peer-talk"],
366
+ &["leaders", "doctor", "approvals"],
391
367
  );
392
368
  append_help_section(&mut out, "设置", &["route", "profile", "install-skill"]);
393
369
  append_help_section(
@@ -487,8 +463,6 @@ pub(super) fn command_help(command: Option<&str>) -> String {
487
463
  "team-agent doctor --workspace .\nteam-agent doctor --workspace . --team help-demo --json", "按实际问题与所选队伍范围处理;不要把体检建议当成已执行的修复。"),
488
464
  "approvals" => ("可选 <agent> 查看一位队友;仅观察权限询问,不自动批准。",
489
465
  "team-agent approvals\nteam-agent approvals worker --json", "确认权限用途后,在工具的实际权限询问处处理,再看 status。"),
490
- "allow-peer-talk" => ("指定两位现有队友;--workspace 选择项目。此命令不支持 --team。",
491
- "team-agent allow-peer-talk worker reviewer\nteam-agent allow-peer-talk worker reviewer --workspace . --json", "再向队友发送交流任务;允许交流不等于已经互发消息。"),
492
466
  "profile" => ("init 创建配置,doctor 检查配置,show 显示设置;NAME 是配置名。\n--auth-mode 登录方式;--proxy-mode direct 不走代理、inherit 沿用环境代理。\n配置存放在所选工作区,不因 --team 自动变成队伍独立配置;不要输出凭证。",
493
467
  "team-agent profile init local --auth-mode subscription\nteam-agent profile doctor local\nteam-agent profile show local", "检查通过后用 add-agent/start-agent --profile local;需要登录时使用工具原生登录入口。"),
494
468
  "install-skill" => ("--source 必须是已核实的指南目录;--target 选择 codex/claude/copilot/all。\n--dest 覆盖安装位置;--dry-run 只预览;--uninstall 移除目标指南。此项不是起队前提。",
@@ -572,7 +546,6 @@ fn emit_usage_error(message: &str) {
572
546
  eprintln!("错误:{message}");
573
547
  }
574
548
 
575
- /// `cmd_validate` delegates to runtime validate_file.
576
549
  /// `install-skill` 参数(RED-1 根治:把 skill 安装单源收敛到二进制,install.mjs 调它)。
577
550
  struct InstallSkillArgs {
578
551
  target: crate::packaging::SkillTarget,
@@ -688,93 +661,10 @@ fn cmd_install_skill(args: &InstallSkillArgs) -> Result<CmdResult, CliError> {
688
661
  ))
689
662
  }
690
663
 
691
- pub fn cmd_validate(args: &ValidateArgs) -> Result<CmdResult, CliError> {
692
- let spec = resolve_path(&args.spec);
693
- let value = if spec.is_dir() {
694
- validate_team_dir(&spec)?
695
- } else {
696
- validate_spec_file(&spec)?
697
- };
698
- Ok(CmdResult::from_json(value, args.json))
699
- }
700
-
701
- fn validate_spec_file(spec_path: &Path) -> Result<Value, CliError> {
702
- let text = std::fs::read_to_string(spec_path)?;
703
- let base_dir = spec_path.parent().unwrap_or_else(|| Path::new("."));
704
- let spec =
705
- crate::model::spec::load_and_validate_spec(&text, base_dir).map_err(model_error_to_cli)?;
706
- let team = spec
707
- .get("team")
708
- .and_then(|team| team.get("name"))
709
- .and_then(crate::model::yaml::Value::as_str)
710
- .unwrap_or("");
711
- let workspace = spec
712
- .get("team")
713
- .and_then(|team| team.get("workspace"))
714
- .and_then(crate::model::yaml::Value::as_str)
715
- .map(PathBuf::from)
716
- .unwrap_or_else(|| base_dir.to_path_buf());
717
- let mut obj = Map::new();
718
- obj.insert("ok".to_string(), Value::Bool(true));
719
- obj.insert("team".to_string(), Value::String(team.to_string()));
720
- obj.insert(
721
- "workspace".to_string(),
722
- Value::String(workspace.to_string_lossy().to_string()),
723
- );
724
- Ok(Value::Object(obj))
725
- }
726
-
727
- fn validate_team_dir(team_dir: &Path) -> Result<Value, CliError> {
728
- let spec = crate::compiler::compile_team(team_dir).map_err(model_error_to_cli)?;
729
- let team = spec
730
- .get("team")
731
- .and_then(|team| team.get("name"))
732
- .and_then(crate::model::yaml::Value::as_str)
733
- .unwrap_or("");
734
- let workspace = spec
735
- .get("team")
736
- .and_then(|team| team.get("workspace"))
737
- .and_then(crate::model::yaml::Value::as_str)
738
- .map(PathBuf::from)
739
- .unwrap_or_else(|| team_dir.to_path_buf());
740
- let agents = spec
741
- .get("agents")
742
- .and_then(crate::model::yaml::Value::as_list)
743
- .map(|agents| {
744
- agents
745
- .iter()
746
- .filter_map(|agent| {
747
- agent
748
- .get("id")
749
- .and_then(crate::model::yaml::Value::as_str)
750
- .map(|id| Value::String(id.to_string()))
751
- })
752
- .collect::<Vec<_>>()
753
- })
754
- .unwrap_or_default();
755
- let mut obj = Map::new();
756
- obj.insert("ok".to_string(), Value::Bool(true));
757
- obj.insert("type".to_string(), Value::String("team_dir".to_string()));
758
- obj.insert(
759
- "workspace".to_string(),
760
- Value::String(workspace.to_string_lossy().to_string()),
761
- );
762
- obj.insert("team".to_string(), Value::String(team.to_string()));
763
- obj.insert("agents".to_string(), Value::Array(agents));
764
- Ok(Value::Object(obj))
765
- }
766
-
767
664
  fn resolve_path(path: &Path) -> PathBuf {
768
665
  std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf())
769
666
  }
770
667
 
771
- fn model_error_to_cli(error: crate::model::errors::ModelError) -> CliError {
772
- match error {
773
- crate::model::errors::ModelError::Validation(message) => CliError::Runtime(message),
774
- other => CliError::Runtime(other.to_string()),
775
- }
776
- }
777
-
778
668
  fn emit_cli_error(command: &str, args: &[String], cwd: &Path, error: &CliError) -> ExitCode {
779
669
  let parsed = parse_args(args);
780
670
  let workspace = workspace(&parsed, cwd);
@@ -951,14 +841,9 @@ struct ParsedArgs {
951
841
  once: bool,
952
842
  tick_interval: Option<f64>,
953
843
  status_value: Option<String>,
954
- providers: Option<String>,
955
- allow_raw_screen: bool,
956
- tail: Option<usize>,
957
- head: Option<usize>,
958
844
  search: Option<String>,
959
845
  file: Option<PathBuf>,
960
846
  result: Option<String>,
961
- real: bool,
962
847
  assignee: Option<String>,
963
848
  auth_mode: Option<String>,
964
849
  proxy_mode: Option<String>,
@@ -1045,14 +930,9 @@ fn parse_args(args: &[String]) -> ParsedArgs {
1045
930
  parsed.tick_interval = next_arg(args, &mut i).and_then(|v| v.parse::<f64>().ok())
1046
931
  }
1047
932
  "--status" => parsed.status_value = next_arg(args, &mut i),
1048
- "--providers" => parsed.providers = next_arg(args, &mut i),
1049
- "--allow-raw-screen" => parsed.allow_raw_screen = true,
1050
- "--tail" => parsed.tail = next_arg(args, &mut i).and_then(|v| v.parse::<usize>().ok()),
1051
- "--head" => parsed.head = next_arg(args, &mut i).and_then(|v| v.parse::<usize>().ok()),
1052
933
  "--search" => parsed.search = next_arg(args, &mut i),
1053
934
  "--file" => parsed.file = next_arg(args, &mut i).map(PathBuf::from),
1054
935
  "--result" => parsed.result = next_arg(args, &mut i),
1055
- "--real" => parsed.real = true,
1056
936
  "--assignee" => parsed.assignee = next_arg(args, &mut i),
1057
937
  "--auth-mode" => parsed.auth_mode = next_arg(args, &mut i),
1058
938
  "--proxy-mode" => parsed.proxy_mode = next_arg(args, &mut i),
@@ -1458,17 +1338,6 @@ fn refuse_if_multi_alive_team_missing_scope(
1458
1338
  Ok(())
1459
1339
  }
1460
1340
 
1461
- fn allow_peer_talk_args(args: &[String], cwd: &Path) -> Result<AllowPeerTalkArgs, CliError> {
1462
- let parsed = parse_args(args);
1463
- Ok(AllowPeerTalkArgs {
1464
- a: required_pos(&parsed, 0, "a")?,
1465
- b: required_pos(&parsed, 1, "b")?,
1466
- workspace: workspace(&parsed, cwd),
1467
- json: parsed.json,
1468
- team: parsed.team,
1469
- })
1470
- }
1471
-
1472
1341
  fn status_args(args: &[String], cwd: &Path) -> StatusArgs {
1473
1342
  let parsed = parse_args(args);
1474
1343
  StatusArgs {
@@ -1481,14 +1350,6 @@ fn status_args(args: &[String], cwd: &Path) -> StatusArgs {
1481
1350
  }
1482
1351
  }
1483
1352
 
1484
- fn watch_args(args: &[String], cwd: &Path) -> WatchArgs {
1485
- let parsed = parse_args(args);
1486
- WatchArgs {
1487
- workspace: workspace(&parsed, cwd),
1488
- team: parsed.team,
1489
- }
1490
- }
1491
-
1492
1353
  fn wait_args(args: &[String], cwd: &Path) -> Result<WaitArgs, CliError> {
1493
1354
  let parsed = parse_args(args);
1494
1355
  let workspace = workspace(&parsed, cwd);
@@ -1586,15 +1447,6 @@ fn attach_app_server_leader_args(
1586
1447
  })
1587
1448
  }
1588
1449
 
1589
- fn identity_args(args: &[String], cwd: &Path) -> IdentityArgs {
1590
- let parsed = parse_args(args);
1591
- IdentityArgs {
1592
- workspace: workspace(&parsed, cwd),
1593
- team: parsed.team,
1594
- json: parsed.json,
1595
- }
1596
- }
1597
-
1598
1450
  fn shutdown_args(args: &[String], cwd: &Path) -> Result<ShutdownArgs, CliError> {
1599
1451
  let parsed = parse_args(args);
1600
1452
  let workspace = workspace(&parsed, cwd);
@@ -1917,15 +1769,6 @@ fn doctor_gate(raw: Option<&str>) -> Option<DoctorGate> {
1917
1769
  }
1918
1770
  }
1919
1771
 
1920
- fn sessions_args(args: &[String], cwd: &Path) -> SessionsArgs {
1921
- let parsed = parse_args(args);
1922
- SessionsArgs {
1923
- workspace: workspace(&parsed, cwd),
1924
- json: parsed.json,
1925
- team: parsed.team,
1926
- }
1927
- }
1928
-
1929
1772
  fn leaders_args(args: &[String], _cwd: &Path) -> Result<LeadersArgs, CliError> {
1930
1773
  let mut view = None;
1931
1774
  let mut query = None;
@@ -2031,18 +1874,6 @@ fn leaders_args(args: &[String], _cwd: &Path) -> Result<LeadersArgs, CliError> {
2031
1874
  })
2032
1875
  }
2033
1876
 
2034
- fn validate_args(args: &[String], cwd: &Path) -> ValidateArgs {
2035
- let parsed = parse_args(args);
2036
- ValidateArgs {
2037
- spec: parsed
2038
- .positionals
2039
- .first()
2040
- .map(PathBuf::from)
2041
- .unwrap_or_else(|| cwd.join("team.spec.yaml")),
2042
- json: parsed.json,
2043
- }
2044
- }
2045
-
2046
1877
  fn profile_args(args: &[String], cwd: &Path) -> Result<ProfileArgs, CliError> {
2047
1878
  let parsed = parse_args(args);
2048
1879
  let workspace = resolve_cli_path(cwd, &workspace(&parsed, cwd));
@@ -2057,106 +1888,6 @@ fn profile_args(args: &[String], cwd: &Path) -> Result<ProfileArgs, CliError> {
2057
1888
  })
2058
1889
  }
2059
1890
 
2060
- fn results_args(args: &[String], cwd: &Path) -> Result<ResultsArgs, CliError> {
2061
- if args
2062
- .iter()
2063
- .any(|arg| arg == "--to" || arg.starts_with("--to="))
2064
- {
2065
- return Err(CliError::Usage(
2066
- "results does not accept --to; it is a read-only command".to_string(),
2067
- ));
2068
- }
2069
- let parsed = parse_args(args);
2070
- let case_id = option_value(args, "--case")
2071
- .filter(|value| !value.is_empty())
2072
- .ok_or_else(|| CliError::Usage("missing --case".to_string()))?;
2073
- Ok(ResultsArgs {
2074
- case_id,
2075
- workspace: workspace(&parsed, cwd),
2076
- team: parsed.team,
2077
- json: parsed.json,
2078
- })
2079
- }
2080
-
2081
- fn option_value(args: &[String], flag: &str) -> Option<String> {
2082
- let prefix = format!("{flag}=");
2083
- let mut i = 0usize;
2084
- while i < args.len() {
2085
- let arg = args.get(i)?;
2086
- if let Some(value) = arg.strip_prefix(&prefix) {
2087
- return Some(value.to_string());
2088
- }
2089
- if arg == flag {
2090
- return args
2091
- .get(i.saturating_add(1))
2092
- .filter(|value| !value.starts_with('-'))
2093
- .cloned();
2094
- }
2095
- i = i.saturating_add(1);
2096
- }
2097
- None
2098
- }
2099
-
2100
- fn preflight_args(args: &[String], cwd: &Path) -> PreflightArgs {
2101
- let parsed = parse_args(args);
2102
- let team = parsed
2103
- .team
2104
- .as_deref()
2105
- .map(PathBuf::from)
2106
- .or_else(|| parsed.positionals.first().map(PathBuf::from))
2107
- .unwrap_or_else(|| cwd.to_path_buf());
2108
- PreflightArgs {
2109
- team,
2110
- json: parsed.json,
2111
- }
2112
- }
2113
-
2114
- fn wait_ready_args(args: &[String], cwd: &Path) -> WaitReadyArgs {
2115
- let parsed = parse_args(args);
2116
- WaitReadyArgs {
2117
- workspace: workspace(&parsed, cwd),
2118
- timeout: parsed.timeout.unwrap_or(60.0),
2119
- json: parsed.json,
2120
- team: parsed.team,
2121
- }
2122
- }
2123
-
2124
- fn e2e_args(args: &[String], cwd: &Path) -> E2eArgs {
2125
- let parsed = parse_args(args);
2126
- let providers = parsed
2127
- .providers
2128
- .as_deref()
2129
- .unwrap_or("fake")
2130
- .split(',')
2131
- .filter_map(|p| {
2132
- let trimmed = p.trim();
2133
- if trimmed.is_empty() {
2134
- None
2135
- } else {
2136
- Some(trimmed.to_string())
2137
- }
2138
- })
2139
- .collect();
2140
- E2eArgs {
2141
- workspace: workspace(&parsed, cwd),
2142
- providers,
2143
- real: parsed.real,
2144
- json: parsed.json,
2145
- }
2146
- }
2147
-
2148
- fn peek_args(args: &[String], cwd: &Path) -> Result<PeekArgs, CliError> {
2149
- let parsed = parse_args(args);
2150
- Ok(PeekArgs {
2151
- agent: required_pos(&parsed, 0, "agent")?,
2152
- workspace: workspace(&parsed, cwd),
2153
- tail: parsed.tail.unwrap_or(80),
2154
- head: parsed.head,
2155
- search: parsed.search,
2156
- allow_raw_screen: parsed.allow_raw_screen,
2157
- json: parsed.json,
2158
- })
2159
- }
2160
1891
  fn run_coordinator(args: &[String], cwd: &Path) -> Result<ExitCode, CliError> {
2161
1892
  let parsed = parse_args(args);
2162
1893
  let workspace = crate::coordinator::WorkspacePath::new(workspace(&parsed, cwd));
@@ -2343,7 +2074,7 @@ mod tests {
2343
2074
  .filter(|spec| spec.default_help)
2344
2075
  .map(|spec| spec.name)
2345
2076
  .collect();
2346
- assert_eq!(expected.len(), 30, "the public catalog is Human30");
2077
+ assert_eq!(expected.len(), 29, "the public catalog is Human29");
2347
2078
  for spec_name in &expected {
2348
2079
  assert!(
2349
2080
  visible.iter().any(|command| command == spec_name),
@@ -2357,7 +2088,7 @@ mod tests {
2357
2088
  expected_sorted.sort();
2358
2089
  assert_eq!(
2359
2090
  actual, expected_sorted,
2360
- "default help must match the exact Human30 public spec set, not a slack threshold; got {visible:?}"
2091
+ "default help must match the exact Human29 public spec set, not a slack threshold; got {visible:?}"
2361
2092
  );
2362
2093
  assert!(
2363
2094
  top_help.contains("copilot"),
@@ -2392,6 +2123,32 @@ mod tests {
2392
2123
  }
2393
2124
  }
2394
2125
 
2126
+ #[test]
2127
+ fn retired_commands_have_no_catalog_machine_or_help_gate() {
2128
+ for command in [
2129
+ "e2e",
2130
+ "allow-peer-talk",
2131
+ "results",
2132
+ "validate",
2133
+ "identity",
2134
+ "sessions",
2135
+ "watch",
2136
+ "peek",
2137
+ "wait-ready",
2138
+ "preflight",
2139
+ ] {
2140
+ assert!(
2141
+ command_spec(command).is_none(),
2142
+ "retired catalog entry: {command}"
2143
+ );
2144
+ assert!(
2145
+ !is_machine_command(command),
2146
+ "retired Machine route: {command}"
2147
+ );
2148
+ assert!(!is_known_subcommand(command), "retired help gate: {command}");
2149
+ }
2150
+ }
2151
+
2395
2152
  #[test]
2396
2153
  fn leaders_help_publishes_selectors_and_prune() {
2397
2154
  let help = command_help(Some("leaders"));
@@ -2405,7 +2162,6 @@ mod tests {
2405
2162
  #[test]
2406
2163
  fn observation_a_commands_have_terminal_tiers() {
2407
2164
  for (command, tier) in [
2408
- ("allow-peer-talk", CommandTier::Core),
2409
2165
  ("approvals", CommandTier::Core),
2410
2166
  ("profile", CommandTier::Core),
2411
2167
  ("install-skill", CommandTier::Core),
@@ -2547,7 +2303,7 @@ mod tests {
2547
2303
  }
2548
2304
  }
2549
2305
  let cwd = tmp_workspace();
2550
- for command in ["sessions", "wait-ready", "peek", "coordinator"] {
2306
+ for command in ["wait", "attach-app-server-leader", "coordinator"] {
2551
2307
  assert_eq!(
2552
2308
  run(&cli_argv(&[command, "--help"]), &cwd),
2553
2309
  ExitCode::Usage,
@@ -5629,8 +5629,8 @@ pub mod diagnose_port {
5629
5629
  }
5630
5630
  }
5631
5631
 
5632
- /// PLACEHOLDER → leader lane(`runtime.{takeover,claim_leader,leader_identity}` 的 CLI 视图)。
5633
- /// leader.rs 已有 `claim_leader`/`leader_identity`(返 `LeaseResult`/`Value`);CLI 需 `takeover` +
5632
+ /// PLACEHOLDER → leader lane(`runtime.{takeover,claim_leader}` 的 CLI 视图)。
5633
+ /// leader.rs 已有 `claim_leader`(返 `LeaseResult`);CLI 需 `takeover` +
5634
5634
  /// 把 `LeaseResult` 投影成稳定 `--json` 形状。这两步由 leader 集成收口,本层仅声明 CLI 委派面。
5635
5635
  pub mod leader_port {
5636
5636
  use super::*;
@@ -5769,13 +5769,6 @@ pub mod leader_port {
5769
5769
  Ok(value)
5770
5770
  }
5771
5771
 
5772
- ///
5773
- /// `runtime.leader_identity(workspace, team)`(`cmd_identity`)。
5774
- pub fn leader_identity(workspace: &Path, team: Option<&str>) -> Result<Value, CliError> {
5775
- crate::leader::leader_identity(workspace, team)
5776
- .map_err(|e| CliError::Runtime(e.to_string()))
5777
- }
5778
-
5779
5772
  /// E7 (0.5.9 host-leader-registry-design §14 step 3): after a
5780
5773
  /// canonical binding command succeeds, write a derived discovery
5781
5774
  /// entry to `~/.team-agent/leaders`. Registry write failure is
@@ -49,7 +49,6 @@ pub(crate) enum DispatchKind {
49
49
  Leaders,
50
50
  Doctor,
51
51
  Approvals,
52
- AllowPeerTalk,
53
52
  Route,
54
53
  Profile,
55
54
  InstallSkill,
@@ -75,7 +74,6 @@ pub(crate) const ALL_DISPATCH_KINDS: &[DispatchKind] = &[
75
74
  DispatchKind::Leaders,
76
75
  DispatchKind::Doctor,
77
76
  DispatchKind::Approvals,
78
- DispatchKind::AllowPeerTalk,
79
77
  DispatchKind::Route,
80
78
  DispatchKind::Profile,
81
79
  DispatchKind::InstallSkill,
@@ -125,7 +123,6 @@ pub(crate) const COMMAND_SPECS: &[CommandSpec] = &[
125
123
  CommandSpec { name: "leaders", tier: CommandTier::Core, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Leaders), summary: "查看本机可用的主控及其所属队伍", usage: "team-agent leaders [QUERY|--search TEXT] [--all|--stale] [--json] | --prune [--dry-run] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
126
124
  CommandSpec { name: "doctor", tier: CommandTier::Core, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Doctor), summary: "检查队伍并给出恢复建议,默认不修复", usage: "team-agent doctor [SPEC] [--workspace WORKSPACE] [--team TEAM] [--comms] [--gate orphans|comms] [--fix] [--fix-schema] [--cleanup-orphans] [--confirm] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
127
125
  CommandSpec { name: "approvals", tier: CommandTier::Core, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Approvals), summary: "查看队友正在等待的权限确认", usage: "team-agent approvals [<agent>] [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
128
- CommandSpec { name: "allow-peer-talk", tier: CommandTier::Core, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::AllowPeerTalk), summary: "允许两位队友直接交流", usage: "team-agent allow-peer-talk <agent> OTHER_AGENT [--workspace WORKSPACE] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
129
126
  CommandSpec { name: "route", tier: CommandTier::Core, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::Route), summary: "设置工具启动时额外使用的命令行参数", usage: "team-agent route [status|enable|disable|show [TOOL]|set TOOL -- ARG...|add TOOL -- ARG...|clear TOOL] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
130
127
  CommandSpec { name: "profile", tier: CommandTier::Core, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::Profile), summary: "管理登录方式和代理设置", usage: "team-agent profile init|doctor|show NAME [--workspace WORKSPACE] [--team TEAM] [--auth-mode MODE] [--proxy-mode direct|inherit] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
131
128
  CommandSpec { name: "install-skill", tier: CommandTier::Core, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::InstallSkill), summary: "可选:安装已有的公开操作指南", usage: "team-agent install-skill (--source DIR|--uninstall) [--target codex|claude|copilot|all] [--dest DIR] [--dry-run] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
@@ -100,11 +100,10 @@ fn cli_shutdown_kills_team_session_real_teardown() {
100
100
 
101
101
  // =========================================================================
102
102
  // WAVE-2 Lane B — CLI leader handler delegation byte-parity (leader_port::*).
103
- // The three CLI verbs are thin pass-throughs (cli/commands.py:152-161):
103
+ // The retained CLI verbs are thin pass-throughs (cli/commands.py:152-157):
104
104
  // cmd_takeover -> runtime.takeover(ws, team, confirm)
105
105
  // cmd_claim_leader -> runtime.claim_leader(ws, team, confirm) (Family A)
106
- // cmd_identity -> runtime.leader_identity(ws, team)
107
- // leader_port::{takeover,claim_leader,leader_identity} are STUBS returning
106
+ // leader_port::{takeover,claim_leader} are STUBS returning
108
107
  // the WRONG shape today -> these LOCK the golden dict so the porter wires
109
108
  // them into leader::* / runtime.* and matches byte-for-byte.
110
109
  // Golden re-probed @ team-agent-public (probe_claim.py / probe_rtclaim.py /
@@ -297,89 +296,6 @@ fn leader_port_claim_leader_no_pane_refuses_caller_pane_missing_family_a() {
297
296
  );
298
297
  }
299
298
 
300
- // RED — leader_identity(): CLI directly emits leader.leader_identity's 9-key
301
- // dict (runtime.leader_identity is imported from leader). golden probe_lid.py
302
- // keys: ok, uuid_prefix, machine_fingerprint, workspace_abspath, os_user,
303
- // team_id, current_pane_id, last_seen_at, source. Current stub returns
304
- // {ok:true, team:...} (wrong shape) -> RED.
305
- #[test]
306
- fn leader_port_leader_identity_emits_nine_key_dict() {
307
- let ws = leader_port_ws("identity");
308
- std::fs::create_dir_all(crate::model::paths::runtime_dir(&ws)).unwrap();
309
- let v = super::leader_port::leader_identity(&ws, None).unwrap();
310
- assert_eq!(v["ok"], json!(true));
311
- let obj = v.as_object().expect("identity → JSON object");
312
- for key in [
313
- "ok",
314
- "uuid_prefix",
315
- "machine_fingerprint",
316
- "workspace_abspath",
317
- "os_user",
318
- "team_id",
319
- "current_pane_id",
320
- "last_seen_at",
321
- "source",
322
- ] {
323
- assert!(
324
- obj.contains_key(key),
325
- "golden identity dict must carry '{key}', got {obj:?}"
326
- );
327
- }
328
- // no override/state uuid → source is the leader-plan "derived" string.
329
- assert_eq!(v["source"], json!("derived"));
330
- // uuid_prefix is exactly 12 hex chars (derive[:12]).
331
- let prefix = v["uuid_prefix"].as_str().expect("uuid_prefix str");
332
- assert_eq!(prefix.len(), 12, "uuid_prefix == derived[:12]");
333
- assert!(prefix.chars().all(|c| c.is_ascii_hexdigit()));
334
- // no team registered + no TMUX_PANE/receiver → these are JSON null.
335
- if std::env::var_os("TMUX_PANE").is_none() {
336
- assert_eq!(v["current_pane_id"], serde_json::Value::Null);
337
- }
338
- assert_eq!(v["last_seen_at"], serde_json::Value::Null);
339
- }
340
-
341
- // ── cmd_watch (cli/adapters.rs:58) [RED] — today a CmdResult::none() no-op ───────────────────────
342
- // Golden cmd_watch (cli/commands.py:103-109) DELEGATES to run_watch(workspace.resolve(), team) and
343
- // exits 0. Golden run_watch (watch/__init__.py:25-37) is a `while True` LIVE TAIL that streams
344
- // render_event_line output (collect_watch_lines) for the watched team. The Rust cmd_watch returns
345
- // CmdResult::none() — a no-op that never touches the watch subsystem. The watch LINE SHAPE is itself
346
- // byte-locked by coordinator::render_event_line (coordinator/tests.rs GROUP H); the cli contract here
347
- // is that cmd_watch must DELEGATE and surface those rendered lines. Seeded a result_received event ->
348
- // golden render = "result_received: <agent> -> <summary>" (render_event_line: agent_id + summary[:80]).
349
- //
350
- // RED confirmed TODAY: cmd_watch=none() returns immediately (no watch line) -> the assertion fails
351
- // (and does NOT hang). PORTER: wire cmd_watch -> coordinator::run_watch(resolved workspace, team,
352
- // interval, sink). Since golden's tail is `while True`, the cli port needs a TERMINATING / bounded
353
- // entry to be both byte-parity AND unit-testable (collect the current watch lines into the CmdResult,
354
- // or stream to stdout with a bounded test seam) — a blocking unit test is unacceptable.
355
- #[test]
356
- fn cmd_watch_delegates_and_surfaces_rendered_watch_lines_not_noop() {
357
- let ws = tmp_workspace();
358
- let logs = crate::model::paths::logs_dir(&ws);
359
- std::fs::create_dir_all(&logs).unwrap();
360
- std::fs::write(
361
- logs.join("events.jsonl"),
362
- "{\"event\":\"result_received\",\"agent_id\":\"alpha\",\"summary\":\"did the thing\"}\n",
363
- )
364
- .unwrap();
365
- let r = cmd_watch(&WatchArgs {
366
- workspace: ws.clone(),
367
- team: None,
368
- })
369
- .expect("cmd_watch returns a CmdResult");
370
- let text = match &r.output {
371
- CmdOutput::Human(s) => s.clone(),
372
- CmdOutput::Json(v) => v.to_string(),
373
- CmdOutput::None => String::new(),
374
- };
375
- assert!(
376
- text.contains("result_received: alpha -> did the thing"),
377
- "cmd_watch must DELEGATE to the watch subsystem (run_watch -> render_event_line) and surface the \
378
- rendered watch line; today it is a CmdResult::none() no-op. got output={:?}",
379
- r.output
380
- );
381
- }
382
-
383
299
  // D7 [WARN] — WAVE-2 Lane B: empty caller binds refuse as caller_pane_missing.
384
300
  #[test]
385
301
  #[serial_test::serial(env)]