@team-agent/installer 0.4.0 → 0.4.2
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 +82 -22
- package/crates/team-agent/src/cli/diagnose.rs +6 -2
- package/crates/team-agent/src/cli/emit.rs +301 -38
- package/crates/team-agent/src/cli/mod.rs +7 -14
- package/crates/team-agent/src/cli/status_port.rs +12 -1
- package/crates/team-agent/src/cli/tests/base.rs +6 -2
- package/crates/team-agent/src/cli/tests/lane_c.rs +1 -1
- package/crates/team-agent/src/cli/tests/leader_watch.rs +5 -3
- package/crates/team-agent/src/cli/tests/main_preserved.rs +28 -2
- package/crates/team-agent/src/cli/tests/peer_allow.rs +19 -0
- package/crates/team-agent/src/cli/tests/repair_state_byte_lock.rs +72 -0
- package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -3
- package/crates/team-agent/src/cli/tests/status_send.rs +1 -0
- package/crates/team-agent/src/cli/types.rs +35 -2
- package/crates/team-agent/src/coordinator/runtime_detectors.rs +7 -2
- package/crates/team-agent/src/coordinator/tick.rs +86 -16
- package/crates/team-agent/src/diagnose/comms.rs +10 -2
- package/crates/team-agent/src/leader/lease.rs +98 -19
- package/crates/team-agent/src/leader/rediscover/tests.rs +21 -7
- package/crates/team-agent/src/leader/rediscover.rs +16 -7
- package/crates/team-agent/src/leader/start.rs +25 -12
- package/crates/team-agent/src/leader/tests/byte_findings.rs +11 -2
- package/crates/team-agent/src/leader/tests/lease_claim.rs +248 -7
- package/crates/team-agent/src/lifecycle/launch.rs +116 -100
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +5 -12
- package/crates/team-agent/src/lifecycle/tests/core.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +16 -15
- package/crates/team-agent/src/lifecycle/types.rs +1 -1
- package/crates/team-agent/src/messaging/leader_receiver.rs +22 -8
- package/crates/team-agent/src/messaging/send.rs +21 -4
- package/crates/team-agent/src/provider/adapter.rs +22 -0
- package/crates/team-agent/src/provider/session/capture.rs +228 -0
- package/crates/team-agent/src/state/identity.rs +42 -12
- package/crates/team-agent/src/state/identity_keys.rs +134 -0
- package/crates/team-agent/src/state/mod.rs +8 -0
- package/crates/team-agent/src/state/owner_gate.rs +11 -1
- package/crates/team-agent/src/state/ownership.rs +556 -0
- package/crates/team-agent/src/state/paths.rs +358 -0
- package/crates/team-agent/src/state/persist.rs +43 -5
- package/crates/team-agent/src/state/projection.rs +13 -5
- package/crates/team-agent/src/tmux_backend.rs +12 -0
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +3 -3
|
@@ -93,9 +93,9 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
93
93
|
}
|
|
94
94
|
"status" => cmd_status_for_team(&status_args(args, cwd), parse_args(args).team.as_deref())
|
|
95
95
|
.map(emit_result),
|
|
96
|
-
"stop" => cmd_shutdown(&shutdown_args(args, cwd)).map(emit_result),
|
|
97
|
-
"shutdown" => cmd_shutdown(&shutdown_args(args, cwd)).map(emit_result),
|
|
98
|
-
"restart" => cmd_restart(&restart_args(args, cwd)).map(emit_result),
|
|
96
|
+
"stop" => cmd_shutdown(&shutdown_args(args, cwd)?).map(emit_result),
|
|
97
|
+
"shutdown" => cmd_shutdown(&shutdown_args(args, cwd)?).map(emit_result),
|
|
98
|
+
"restart" => cmd_restart(&restart_args(args, cwd)?).map(emit_result),
|
|
99
99
|
"restart-agent" => cmd_reset_agent(&reset_agent_args(args, cwd)?).map(emit_result),
|
|
100
100
|
"start-agent" => cmd_start_agent(&start_agent_args(args, cwd)?).map(emit_result),
|
|
101
101
|
"stop-agent" => cmd_stop_agent(&stop_agent_args(args, cwd)?).map(emit_result),
|
|
@@ -127,7 +127,7 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
127
127
|
}
|
|
128
128
|
"validate-result" => cmd_validate_result(&validate_result_args(args)?).map(emit_result),
|
|
129
129
|
"collect" => {
|
|
130
|
-
cmd_collect_for_team(&collect_args(args, cwd)
|
|
130
|
+
cmd_collect_for_team(&collect_args(args, cwd)?, parse_args(args).team.as_deref())
|
|
131
131
|
.map(emit_result)
|
|
132
132
|
}
|
|
133
133
|
"settle" => cmd_settle(&settle_args(args, cwd)).map(emit_result),
|
|
@@ -215,7 +215,7 @@ fn command_help(command: Option<&str>) -> String {
|
|
|
215
215
|
)
|
|
216
216
|
}
|
|
217
217
|
Some("init") => "usage: team-agent init [--workspace WORKSPACE] [--force] [--json]".to_string(),
|
|
218
|
-
Some("quick-start") => "usage: team-agent quick-start [TEAMDIR] [--workspace WORKSPACE] [--name NAME] [--team-id TEAM|--team TEAM] [--yes] [--
|
|
218
|
+
Some("quick-start") => "usage: team-agent quick-start [TEAMDIR] [--workspace WORKSPACE] [--name NAME] [--team-id TEAM|--team TEAM] [--yes] [--no-display] [--json]\n\ndefaults: display_backend=adaptive; set display_backend: none in TEAM.md or pass --no-display to use one worker window per agent.".to_string(),
|
|
219
219
|
Some("start") => "usage: team-agent start [TEAMDIR] [--yes] [--fresh] [--json]".to_string(),
|
|
220
220
|
Some("compile") => "usage: team-agent compile --team TEAM [--out FILE] [--json]".to_string(),
|
|
221
221
|
Some("send") => "usage: team-agent send TARGET MESSAGE... [--workspace WORKSPACE] [--team TEAM] [--targets AGENTS] [--task TASK] [--sender SENDER] [--watch-result] [--requires-ack|--no-ack] [--no-wait] [--timeout SECONDS] [--confirm-human] [--message-id ID] [--json]".to_string(),
|
|
@@ -234,28 +234,28 @@ fn command_help(command: Option<&str>) -> String {
|
|
|
234
234
|
Some("fork-agent") => "usage: team-agent fork-agent SOURCE_AGENT --as AGENT [--label LABEL] [--workspace WORKSPACE] [--team TEAM] [--no-display] [--json]".to_string(),
|
|
235
235
|
Some("remove-agent") => "usage: team-agent remove-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--from-spec] [--confirm] [--force] [--json]".to_string(),
|
|
236
236
|
Some("purge-agent") => "usage: team-agent purge-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--force] [--json]".to_string(),
|
|
237
|
-
Some("stuck-list") => "usage: team-agent stuck-list [--workspace WORKSPACE] [--json]".to_string(),
|
|
237
|
+
Some("stuck-list") => "usage: team-agent stuck-list [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
238
238
|
Some("stuck-cancel") => "usage: team-agent stuck-cancel AGENT [--workspace WORKSPACE] [--alert-type stuck|idle_fallback|cross_worker_deadlock|all] [--json]".to_string(),
|
|
239
239
|
Some("acknowledge-idle") => "usage: team-agent acknowledge-idle [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
240
240
|
Some("takeover") => "usage: team-agent takeover [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json]".to_string(),
|
|
241
241
|
Some("claim-leader") => "usage: team-agent claim-leader [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json]".to_string(),
|
|
242
242
|
Some("attach-leader") => "usage: team-agent attach-leader [--workspace WORKSPACE] [--team TEAM] [--pane PANE] [--provider PROVIDER] [--confirm] [--json]".to_string(),
|
|
243
243
|
Some("identity") => "usage: team-agent identity [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
244
|
-
Some("approvals") => "usage: team-agent approvals [AGENT] [--workspace WORKSPACE] [--json]".to_string(),
|
|
245
|
-
Some("inbox") => "usage: team-agent inbox AGENT [--workspace WORKSPACE] [--limit N] [--since CURSOR] [--json]".to_string(),
|
|
244
|
+
Some("approvals") => "usage: team-agent approvals [AGENT] [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
245
|
+
Some("inbox") => "usage: team-agent inbox AGENT [--workspace WORKSPACE] [--team TEAM] [--limit N] [--since CURSOR] [--json]".to_string(),
|
|
246
246
|
Some("doctor") => "usage: team-agent doctor [SPEC] [--workspace WORKSPACE] [--team TEAM] [--gate orphans|comms] [--comms] [--fix] [--fix-schema] [--cleanup-orphans] [--confirm] [--json]".to_string(),
|
|
247
247
|
Some("watch") => "usage: team-agent watch [--workspace WORKSPACE] [--team TEAM]".to_string(),
|
|
248
|
-
Some("sessions") => "usage: team-agent sessions [--workspace WORKSPACE] [--json]".to_string(),
|
|
248
|
+
Some("sessions") => "usage: team-agent sessions [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
249
249
|
Some("validate") => "usage: team-agent validate [SPEC] [--json]".to_string(),
|
|
250
250
|
Some("install-skill") => "usage: team-agent install-skill (--source DIR | --uninstall) [--target codex|claude|copilot|all] [--dest DIR] [--dry-run] [--json]".to_string(),
|
|
251
251
|
Some("profile") => "usage: team-agent profile COMMAND NAME [--workspace WORKSPACE] [--team TEAM] [--auth-mode MODE] [--json]".to_string(),
|
|
252
252
|
Some("validate-result") => "usage: team-agent validate-result [ENVELOPE] [--file FILE|--result JSON] [--json]".to_string(),
|
|
253
253
|
Some("collect") => "usage: team-agent collect [--workspace WORKSPACE] [--team TEAM] [--result-file FILE] [--json]".to_string(),
|
|
254
254
|
Some("settle") => "usage: team-agent settle [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
255
|
-
Some("repair-state") => "usage: team-agent repair-state --task TASK --status STATUS [SUMMARY] [--assignee AGENT] [--workspace WORKSPACE] [--json]".to_string(),
|
|
256
|
-
Some("diagnose") => "usage: team-agent diagnose [--workspace WORKSPACE] [--json]".to_string(),
|
|
255
|
+
Some("repair-state") => "usage: team-agent repair-state --task TASK --status STATUS [SUMMARY] [--assignee AGENT] [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
256
|
+
Some("diagnose") => "usage: team-agent diagnose [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
257
257
|
Some("preflight") => "usage: team-agent preflight [TEAMDIR] [--json]".to_string(),
|
|
258
|
-
Some("wait-ready") => "usage: team-agent wait-ready [--workspace WORKSPACE] [--timeout SECONDS] [--json]".to_string(),
|
|
258
|
+
Some("wait-ready") => "usage: team-agent wait-ready [--workspace WORKSPACE] [--team TEAM] [--timeout SECONDS] [--json]".to_string(),
|
|
259
259
|
Some("e2e") => "usage: team-agent e2e [--workspace WORKSPACE] [--providers LIST] [--real] [--json]".to_string(),
|
|
260
260
|
Some("peek") => "usage: team-agent peek AGENT [--workspace WORKSPACE] [--tail N|--head N] [--search TEXT] [--allow-raw-screen] [--json]".to_string(),
|
|
261
261
|
Some("coordinator") => "usage: team-agent coordinator [--workspace WORKSPACE] [--once] [--tick-interval SECONDS]".to_string(),
|
|
@@ -613,7 +613,6 @@ struct ParsedArgs {
|
|
|
613
613
|
team: Option<String>,
|
|
614
614
|
json: bool,
|
|
615
615
|
yes: bool,
|
|
616
|
-
fresh: bool,
|
|
617
616
|
name: Option<String>,
|
|
618
617
|
team_id: Option<String>,
|
|
619
618
|
targets: Option<String>,
|
|
@@ -685,7 +684,6 @@ fn parse_args(args: &[String]) -> ParsedArgs {
|
|
|
685
684
|
"--team" => parsed.team = next_arg(args, &mut i),
|
|
686
685
|
"--json" => parsed.json = true,
|
|
687
686
|
"--yes" => parsed.yes = true,
|
|
688
|
-
"--fresh" => parsed.fresh = true,
|
|
689
687
|
"--name" => parsed.name = next_arg(args, &mut i),
|
|
690
688
|
"--team-id" => parsed.team_id = next_arg(args, &mut i),
|
|
691
689
|
"--targets" | "--target" | "--to" => parsed.targets = next_arg(args, &mut i),
|
|
@@ -804,6 +802,13 @@ fn required_pos(parsed: &ParsedArgs, index: usize, name: &str) -> Result<String,
|
|
|
804
802
|
}
|
|
805
803
|
|
|
806
804
|
fn quick_start_args(args: &[String], cwd: &Path) -> Result<QuickStartArgs, CliError> {
|
|
805
|
+
if has_arg(args, "--fresh") {
|
|
806
|
+
return Err(CliError::Usage(
|
|
807
|
+
"quick-start no longer accepts --fresh. Reset semantics moved to \
|
|
808
|
+
`team-agent restart --allow-fresh`, which requires explicit user \
|
|
809
|
+
confirmation.".to_string(),
|
|
810
|
+
));
|
|
811
|
+
}
|
|
807
812
|
let parsed = parse_args(args);
|
|
808
813
|
let workspace = workspace(&parsed, cwd);
|
|
809
814
|
let agents_dir = parsed
|
|
@@ -822,7 +827,6 @@ fn quick_start_args(args: &[String], cwd: &Path) -> Result<QuickStartArgs, CliEr
|
|
|
822
827
|
name: parsed.name,
|
|
823
828
|
team_id: parsed.team_id.or(parsed.team),
|
|
824
829
|
yes: parsed.yes,
|
|
825
|
-
fresh: parsed.fresh,
|
|
826
830
|
no_display: parsed.no_display,
|
|
827
831
|
json: parsed.json,
|
|
828
832
|
})
|
|
@@ -949,6 +953,31 @@ fn fallback_report_result_args(
|
|
|
949
953
|
})
|
|
950
954
|
}
|
|
951
955
|
|
|
956
|
+
/// Stage 4 of identity-boundary unified plan (architect direction
|
|
957
|
+
/// 2026-06-24, .team/artifacts/identity-boundary-unified-plan.md §2 Stage
|
|
958
|
+
/// 4): destructive command ambiguity gate. When the workspace has 2+
|
|
959
|
+
/// alive teams and the caller did not pass `--team`, refuse with a
|
|
960
|
+
/// usage error listing the candidates so the operator picks explicitly.
|
|
961
|
+
/// Single-team workspaces (the 0.4.x baseline) are unaffected — the
|
|
962
|
+
/// `CommandScope::resolve` helper returns `Resolved` and this function
|
|
963
|
+
/// is a no-op.
|
|
964
|
+
fn refuse_if_multi_alive_team_missing_scope(
|
|
965
|
+
command: &str,
|
|
966
|
+
workspace: &Path,
|
|
967
|
+
requested_team: Option<&str>,
|
|
968
|
+
) -> Result<(), CliError> {
|
|
969
|
+
let scope = crate::state::paths::CommandScope::resolve(workspace, requested_team);
|
|
970
|
+
if scope.is_ambiguous() {
|
|
971
|
+
let candidates = scope.candidates().join(", ");
|
|
972
|
+
return Err(CliError::Usage(format!(
|
|
973
|
+
"{command}: workspace has multiple alive teams ({candidates}); \
|
|
974
|
+
pass `--team <key>` to choose one (refusing to default to any \
|
|
975
|
+
single team — Stage 4 identity-boundary contract)"
|
|
976
|
+
)));
|
|
977
|
+
}
|
|
978
|
+
Ok(())
|
|
979
|
+
}
|
|
980
|
+
|
|
952
981
|
fn allow_peer_talk_args(args: &[String], cwd: &Path) -> Result<AllowPeerTalkArgs, CliError> {
|
|
953
982
|
let parsed = parse_args(args);
|
|
954
983
|
Ok(AllowPeerTalkArgs {
|
|
@@ -956,6 +985,7 @@ fn allow_peer_talk_args(args: &[String], cwd: &Path) -> Result<AllowPeerTalkArgs
|
|
|
956
985
|
b: required_pos(&parsed, 1, "b")?,
|
|
957
986
|
workspace: workspace(&parsed, cwd),
|
|
958
987
|
json: parsed.json,
|
|
988
|
+
team: parsed.team,
|
|
959
989
|
})
|
|
960
990
|
}
|
|
961
991
|
|
|
@@ -967,6 +997,7 @@ fn status_args(args: &[String], cwd: &Path) -> StatusArgs {
|
|
|
967
997
|
detail: parsed.detail,
|
|
968
998
|
summary: parsed.summary,
|
|
969
999
|
json: parsed.json,
|
|
1000
|
+
team: parsed.team,
|
|
970
1001
|
}
|
|
971
1002
|
}
|
|
972
1003
|
|
|
@@ -984,6 +1015,7 @@ fn approvals_args(args: &[String], cwd: &Path) -> ApprovalsArgs {
|
|
|
984
1015
|
agent: parsed.positionals.first().cloned(),
|
|
985
1016
|
workspace: workspace(&parsed, cwd),
|
|
986
1017
|
json: parsed.json,
|
|
1018
|
+
team: parsed.team,
|
|
987
1019
|
}
|
|
988
1020
|
}
|
|
989
1021
|
|
|
@@ -995,6 +1027,7 @@ fn inbox_args(args: &[String], cwd: &Path) -> Result<InboxArgs, CliError> {
|
|
|
995
1027
|
limit: parsed.limit.unwrap_or(20),
|
|
996
1028
|
since: parsed.since,
|
|
997
1029
|
json: parsed.json,
|
|
1030
|
+
team: parsed.team,
|
|
998
1031
|
})
|
|
999
1032
|
}
|
|
1000
1033
|
|
|
@@ -1042,29 +1075,33 @@ fn identity_args(args: &[String], cwd: &Path) -> IdentityArgs {
|
|
|
1042
1075
|
}
|
|
1043
1076
|
}
|
|
1044
1077
|
|
|
1045
|
-
fn shutdown_args(args: &[String], cwd: &Path) -> ShutdownArgs {
|
|
1078
|
+
fn shutdown_args(args: &[String], cwd: &Path) -> Result<ShutdownArgs, CliError> {
|
|
1046
1079
|
let parsed = parse_args(args);
|
|
1047
|
-
|
|
1048
|
-
|
|
1080
|
+
let workspace = workspace(&parsed, cwd);
|
|
1081
|
+
refuse_if_multi_alive_team_missing_scope("shutdown", &workspace, parsed.team.as_deref())?;
|
|
1082
|
+
Ok(ShutdownArgs {
|
|
1083
|
+
workspace,
|
|
1049
1084
|
team: parsed.team,
|
|
1050
1085
|
keep_logs: parsed.keep_logs,
|
|
1051
1086
|
json: parsed.json,
|
|
1052
|
-
}
|
|
1087
|
+
})
|
|
1053
1088
|
}
|
|
1054
1089
|
|
|
1055
|
-
fn restart_args(args: &[String], cwd: &Path) -> RestartArgs {
|
|
1090
|
+
fn restart_args(args: &[String], cwd: &Path) -> Result<RestartArgs, CliError> {
|
|
1056
1091
|
let parsed = parse_args(args);
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1092
|
+
let workspace = parsed
|
|
1093
|
+
.positionals
|
|
1094
|
+
.first()
|
|
1095
|
+
.map(PathBuf::from)
|
|
1096
|
+
.unwrap_or_else(|| workspace(&parsed, cwd));
|
|
1097
|
+
refuse_if_multi_alive_team_missing_scope("restart", &workspace, parsed.team.as_deref())?;
|
|
1098
|
+
Ok(RestartArgs {
|
|
1099
|
+
workspace,
|
|
1063
1100
|
team: parsed.team,
|
|
1064
1101
|
allow_fresh: parsed.allow_fresh,
|
|
1065
1102
|
session_converge_deadline_ms: parsed.session_converge_deadline_ms,
|
|
1066
1103
|
json: parsed.json,
|
|
1067
|
-
}
|
|
1104
|
+
})
|
|
1068
1105
|
}
|
|
1069
1106
|
|
|
1070
1107
|
fn start_agent_args(args: &[String], cwd: &Path) -> Result<StartAgentArgs, CliError> {
|
|
@@ -1092,9 +1129,11 @@ fn stop_agent_args(args: &[String], cwd: &Path) -> Result<StopAgentArgs, CliErro
|
|
|
1092
1129
|
|
|
1093
1130
|
fn reset_agent_args(args: &[String], cwd: &Path) -> Result<ResetAgentArgs, CliError> {
|
|
1094
1131
|
let parsed = parse_args(args);
|
|
1132
|
+
let workspace = workspace(&parsed, cwd);
|
|
1133
|
+
refuse_if_multi_alive_team_missing_scope("reset-agent", &workspace, parsed.team.as_deref())?;
|
|
1095
1134
|
Ok(ResetAgentArgs {
|
|
1096
1135
|
agent: required_pos(&parsed, 0, "agent")?,
|
|
1097
|
-
workspace
|
|
1136
|
+
workspace,
|
|
1098
1137
|
team: parsed.team,
|
|
1099
1138
|
discard_session: parsed.discard_session,
|
|
1100
1139
|
no_display: parsed.no_display,
|
|
@@ -1133,9 +1172,11 @@ fn fork_agent_args(args: &[String], cwd: &Path) -> Result<ForkAgentArgs, CliErro
|
|
|
1133
1172
|
|
|
1134
1173
|
fn remove_agent_args(args: &[String], cwd: &Path) -> Result<RemoveAgentArgs, CliError> {
|
|
1135
1174
|
let parsed = parse_args(args);
|
|
1175
|
+
let workspace = workspace(&parsed, cwd);
|
|
1176
|
+
refuse_if_multi_alive_team_missing_scope("remove-agent", &workspace, parsed.team.as_deref())?;
|
|
1136
1177
|
Ok(RemoveAgentArgs {
|
|
1137
1178
|
agent: required_pos(&parsed, 0, "agent")?,
|
|
1138
|
-
workspace
|
|
1179
|
+
workspace,
|
|
1139
1180
|
team: parsed.team,
|
|
1140
1181
|
from_spec: parsed.from_spec,
|
|
1141
1182
|
confirm: parsed.confirm,
|
|
@@ -1149,16 +1190,24 @@ fn stuck_list_args(args: &[String], cwd: &Path) -> StuckListArgs {
|
|
|
1149
1190
|
StuckListArgs {
|
|
1150
1191
|
workspace: workspace(&parsed, cwd),
|
|
1151
1192
|
json: parsed.json,
|
|
1193
|
+
team: parsed.team,
|
|
1152
1194
|
}
|
|
1153
1195
|
}
|
|
1154
1196
|
|
|
1155
1197
|
fn stuck_cancel_args(args: &[String], cwd: &Path) -> Result<StuckCancelArgs, CliError> {
|
|
1156
1198
|
let parsed = parse_args(args);
|
|
1199
|
+
let workspace = workspace(&parsed, cwd);
|
|
1200
|
+
refuse_if_multi_alive_team_missing_scope(
|
|
1201
|
+
"stuck-cancel",
|
|
1202
|
+
&workspace,
|
|
1203
|
+
parsed.team.as_deref(),
|
|
1204
|
+
)?;
|
|
1157
1205
|
Ok(StuckCancelArgs {
|
|
1158
1206
|
agent: required_pos(&parsed, 0, "agent")?,
|
|
1159
|
-
workspace
|
|
1207
|
+
workspace,
|
|
1160
1208
|
alert_type: alert_type(parsed.alert_type.as_deref())?,
|
|
1161
1209
|
json: parsed.json,
|
|
1210
|
+
team: parsed.team,
|
|
1162
1211
|
})
|
|
1163
1212
|
}
|
|
1164
1213
|
|
|
@@ -1212,6 +1261,7 @@ fn sessions_args(args: &[String], cwd: &Path) -> SessionsArgs {
|
|
|
1212
1261
|
SessionsArgs {
|
|
1213
1262
|
workspace: workspace(&parsed, cwd),
|
|
1214
1263
|
json: parsed.json,
|
|
1264
|
+
team: parsed.team,
|
|
1215
1265
|
}
|
|
1216
1266
|
}
|
|
1217
1267
|
|
|
@@ -1250,13 +1300,16 @@ fn validate_result_args(args: &[String]) -> Result<ValidateResultArgs, CliError>
|
|
|
1250
1300
|
})
|
|
1251
1301
|
}
|
|
1252
1302
|
|
|
1253
|
-
fn collect_args(args: &[String], cwd: &Path) -> CollectArgs {
|
|
1303
|
+
fn collect_args(args: &[String], cwd: &Path) -> Result<CollectArgs, CliError> {
|
|
1254
1304
|
let parsed = parse_args(args);
|
|
1255
|
-
|
|
1256
|
-
|
|
1305
|
+
let workspace = workspace(&parsed, cwd);
|
|
1306
|
+
refuse_if_multi_alive_team_missing_scope("collect", &workspace, parsed.team.as_deref())?;
|
|
1307
|
+
Ok(CollectArgs {
|
|
1308
|
+
workspace,
|
|
1257
1309
|
result_file: parsed.result_file,
|
|
1258
1310
|
json: parsed.json,
|
|
1259
|
-
|
|
1311
|
+
team: parsed.team,
|
|
1312
|
+
})
|
|
1260
1313
|
}
|
|
1261
1314
|
|
|
1262
1315
|
fn settle_args(args: &[String], cwd: &Path) -> SettleArgs {
|
|
@@ -1270,8 +1323,10 @@ fn settle_args(args: &[String], cwd: &Path) -> SettleArgs {
|
|
|
1270
1323
|
|
|
1271
1324
|
fn repair_state_args(args: &[String], cwd: &Path) -> Result<RepairStateArgs, CliError> {
|
|
1272
1325
|
let parsed = parse_args(args);
|
|
1326
|
+
let workspace = workspace(&parsed, cwd);
|
|
1327
|
+
refuse_if_multi_alive_team_missing_scope("repair-state", &workspace, parsed.team.as_deref())?;
|
|
1273
1328
|
Ok(RepairStateArgs {
|
|
1274
|
-
workspace
|
|
1329
|
+
workspace,
|
|
1275
1330
|
task_id: parsed
|
|
1276
1331
|
.task
|
|
1277
1332
|
.ok_or_else(|| CliError::Usage("missing --task".to_string()))?,
|
|
@@ -1281,6 +1336,7 @@ fn repair_state_args(args: &[String], cwd: &Path) -> Result<RepairStateArgs, Cli
|
|
|
1281
1336
|
.ok_or_else(|| CliError::Usage("missing --status".to_string()))?,
|
|
1282
1337
|
summary: option_value(args, "--summary").or_else(|| parsed.positionals.first().cloned()),
|
|
1283
1338
|
json: parsed.json,
|
|
1339
|
+
team: parsed.team,
|
|
1284
1340
|
})
|
|
1285
1341
|
}
|
|
1286
1342
|
|
|
@@ -1308,6 +1364,7 @@ fn diagnose_args(args: &[String], cwd: &Path) -> DiagnoseArgs {
|
|
|
1308
1364
|
DiagnoseArgs {
|
|
1309
1365
|
workspace: workspace(&parsed, cwd),
|
|
1310
1366
|
json: parsed.json,
|
|
1367
|
+
team: parsed.team,
|
|
1311
1368
|
}
|
|
1312
1369
|
}
|
|
1313
1370
|
|
|
@@ -1331,6 +1388,7 @@ fn wait_ready_args(args: &[String], cwd: &Path) -> WaitReadyArgs {
|
|
|
1331
1388
|
workspace: workspace(&parsed, cwd),
|
|
1332
1389
|
timeout: parsed.timeout.unwrap_or(60.0),
|
|
1333
1390
|
json: parsed.json,
|
|
1391
|
+
team: parsed.team,
|
|
1334
1392
|
}
|
|
1335
1393
|
}
|
|
1336
1394
|
|
|
@@ -1529,7 +1587,7 @@ mod tests {
|
|
|
1529
1587
|
for (command, flags) in [
|
|
1530
1588
|
(
|
|
1531
1589
|
"quick-start",
|
|
1532
|
-
&["--workspace", "--team-id", "--yes", "--
|
|
1590
|
+
&["--workspace", "--team-id", "--yes", "--json"][..],
|
|
1533
1591
|
),
|
|
1534
1592
|
(
|
|
1535
1593
|
"send",
|
|
@@ -1638,11 +1696,19 @@ mod tests {
|
|
|
1638
1696
|
"collect",
|
|
1639
1697
|
&["--workspace", "--team", "--result-file", "--json"][..],
|
|
1640
1698
|
),
|
|
1699
|
+
("stuck-list", &["--workspace", "--team", "--json"][..]),
|
|
1700
|
+
("approvals", &["--workspace", "--team", "--json"][..]),
|
|
1701
|
+
(
|
|
1702
|
+
"inbox",
|
|
1703
|
+
&["--workspace", "--team", "--limit", "--since", "--json"][..],
|
|
1704
|
+
),
|
|
1705
|
+
("sessions", &["--workspace", "--team", "--json"][..]),
|
|
1641
1706
|
(
|
|
1642
1707
|
"repair-state",
|
|
1643
|
-
&["--task", "--status", "--assignee", "--workspace", "--json"][..],
|
|
1708
|
+
&["--task", "--status", "--assignee", "--workspace", "--team", "--json"][..],
|
|
1644
1709
|
),
|
|
1645
|
-
("
|
|
1710
|
+
("diagnose", &["--workspace", "--team", "--json"][..]),
|
|
1711
|
+
("wait-ready", &["--workspace", "--team", "--timeout", "--json"][..]),
|
|
1646
1712
|
(
|
|
1647
1713
|
"peek",
|
|
1648
1714
|
&[
|
|
@@ -1667,6 +1733,10 @@ mod tests {
|
|
|
1667
1733
|
);
|
|
1668
1734
|
}
|
|
1669
1735
|
}
|
|
1736
|
+
assert!(
|
|
1737
|
+
!command_help(Some("quick-start")).contains("--fresh"),
|
|
1738
|
+
"quick-start help must not advertise removed reset semantics"
|
|
1739
|
+
);
|
|
1670
1740
|
}
|
|
1671
1741
|
|
|
1672
1742
|
#[test]
|
|
@@ -1724,4 +1794,197 @@ mod tests {
|
|
|
1724
1794
|
assert_eq!(levenshtein("status", "status"), 0);
|
|
1725
1795
|
assert_eq!(levenshtein("statu", "status"), 1);
|
|
1726
1796
|
}
|
|
1797
|
+
|
|
1798
|
+
// ─── Stage 4: multi-team ambiguity refusal for destructive commands ───
|
|
1799
|
+
|
|
1800
|
+
fn seed_two_alive_teams_in(ws: &std::path::Path) {
|
|
1801
|
+
crate::state::persist::save_runtime_state(
|
|
1802
|
+
ws,
|
|
1803
|
+
&serde_json::json!({
|
|
1804
|
+
"teams": {
|
|
1805
|
+
"alpha": {"status": "alive"},
|
|
1806
|
+
"beta": {"status": "alive"},
|
|
1807
|
+
},
|
|
1808
|
+
}),
|
|
1809
|
+
)
|
|
1810
|
+
.unwrap();
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
#[test]
|
|
1814
|
+
fn refuse_helper_passes_on_single_alive_team() {
|
|
1815
|
+
let ws = tmp_workspace();
|
|
1816
|
+
crate::state::persist::save_runtime_state(
|
|
1817
|
+
&ws,
|
|
1818
|
+
&serde_json::json!({"teams": {"alpha": {"status": "alive"}}}),
|
|
1819
|
+
)
|
|
1820
|
+
.unwrap();
|
|
1821
|
+
assert!(
|
|
1822
|
+
refuse_if_multi_alive_team_missing_scope("stuck-cancel", &ws, None).is_ok(),
|
|
1823
|
+
"single-alive-team workspace must not trigger the ambiguity refusal"
|
|
1824
|
+
);
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
#[test]
|
|
1828
|
+
fn refuse_helper_passes_when_explicit_team_provided_even_in_multi_alive() {
|
|
1829
|
+
let ws = tmp_workspace();
|
|
1830
|
+
seed_two_alive_teams_in(&ws);
|
|
1831
|
+
assert!(
|
|
1832
|
+
refuse_if_multi_alive_team_missing_scope("collect", &ws, Some("alpha")).is_ok(),
|
|
1833
|
+
"explicit --team alpha must bypass the ambiguity gate"
|
|
1834
|
+
);
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
#[test]
|
|
1838
|
+
fn refuse_helper_refuses_when_multi_alive_team_and_no_explicit_team() {
|
|
1839
|
+
let ws = tmp_workspace();
|
|
1840
|
+
seed_two_alive_teams_in(&ws);
|
|
1841
|
+
let err = refuse_if_multi_alive_team_missing_scope("repair-state", &ws, None)
|
|
1842
|
+
.expect_err("multi-alive-team must refuse without --team");
|
|
1843
|
+
let message = err.to_string();
|
|
1844
|
+
assert!(
|
|
1845
|
+
message.contains("multiple alive teams"),
|
|
1846
|
+
"refusal must name the ambiguity; got: {message}"
|
|
1847
|
+
);
|
|
1848
|
+
assert!(
|
|
1849
|
+
message.contains("alpha") && message.contains("beta"),
|
|
1850
|
+
"refusal must list candidate teams; got: {message}"
|
|
1851
|
+
);
|
|
1852
|
+
assert!(
|
|
1853
|
+
message.contains("repair-state"),
|
|
1854
|
+
"refusal must name the command for diagnostic clarity; got: {message}"
|
|
1855
|
+
);
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
#[test]
|
|
1859
|
+
fn stuck_cancel_args_builder_refuses_on_multi_alive_team() {
|
|
1860
|
+
let ws = tmp_workspace();
|
|
1861
|
+
seed_two_alive_teams_in(&ws);
|
|
1862
|
+
let argv = cli_argv(&[
|
|
1863
|
+
"worker_a",
|
|
1864
|
+
"--workspace",
|
|
1865
|
+
&ws.to_string_lossy(),
|
|
1866
|
+
]);
|
|
1867
|
+
let err = stuck_cancel_args(&argv, &ws).expect_err("must refuse");
|
|
1868
|
+
assert!(
|
|
1869
|
+
err.to_string().contains("multiple alive teams"),
|
|
1870
|
+
"stuck-cancel args builder must surface the refusal; got: {err}"
|
|
1871
|
+
);
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
#[test]
|
|
1875
|
+
fn collect_args_builder_refuses_on_multi_alive_team() {
|
|
1876
|
+
let ws = tmp_workspace();
|
|
1877
|
+
seed_two_alive_teams_in(&ws);
|
|
1878
|
+
let argv = cli_argv(&["--workspace", &ws.to_string_lossy()]);
|
|
1879
|
+
let err = collect_args(&argv, &ws).expect_err("must refuse");
|
|
1880
|
+
assert!(
|
|
1881
|
+
err.to_string().contains("multiple alive teams"),
|
|
1882
|
+
"collect args builder must surface the refusal; got: {err}"
|
|
1883
|
+
);
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
#[test]
|
|
1887
|
+
fn repair_state_args_builder_refuses_on_multi_alive_team_before_task_validation() {
|
|
1888
|
+
let ws = tmp_workspace();
|
|
1889
|
+
seed_two_alive_teams_in(&ws);
|
|
1890
|
+
// The ambiguity gate must fire BEFORE `--task` / `--status` validation
|
|
1891
|
+
// so the operator sees the multi-team confusion before any other
|
|
1892
|
+
// usage error.
|
|
1893
|
+
let argv = cli_argv(&["--workspace", &ws.to_string_lossy()]);
|
|
1894
|
+
let err = repair_state_args(&argv, &ws).expect_err("must refuse");
|
|
1895
|
+
let message = err.to_string();
|
|
1896
|
+
assert!(
|
|
1897
|
+
message.contains("multiple alive teams"),
|
|
1898
|
+
"ambiguity gate must precede --task/--status validation; got: {message}"
|
|
1899
|
+
);
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
#[test]
|
|
1903
|
+
fn shutdown_args_builder_refuses_on_multi_alive_team() {
|
|
1904
|
+
let ws = tmp_workspace();
|
|
1905
|
+
seed_two_alive_teams_in(&ws);
|
|
1906
|
+
let argv = cli_argv(&["--workspace", &ws.to_string_lossy()]);
|
|
1907
|
+
let err = shutdown_args(&argv, &ws).expect_err("must refuse");
|
|
1908
|
+
assert!(
|
|
1909
|
+
err.to_string().contains("multiple alive teams"),
|
|
1910
|
+
"shutdown args builder must surface the refusal; got: {err}"
|
|
1911
|
+
);
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
#[test]
|
|
1915
|
+
fn restart_args_builder_refuses_on_multi_alive_team() {
|
|
1916
|
+
let ws = tmp_workspace();
|
|
1917
|
+
seed_two_alive_teams_in(&ws);
|
|
1918
|
+
let argv = cli_argv(&[&ws.to_string_lossy()]);
|
|
1919
|
+
let err = restart_args(&argv, &ws).expect_err("must refuse");
|
|
1920
|
+
assert!(
|
|
1921
|
+
err.to_string().contains("multiple alive teams"),
|
|
1922
|
+
"restart args builder must surface the refusal; got: {err}"
|
|
1923
|
+
);
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
#[test]
|
|
1927
|
+
fn reset_agent_args_builder_refuses_on_multi_alive_team_before_agent_validation() {
|
|
1928
|
+
let ws = tmp_workspace();
|
|
1929
|
+
seed_two_alive_teams_in(&ws);
|
|
1930
|
+
let argv = cli_argv(&["--workspace", &ws.to_string_lossy()]);
|
|
1931
|
+
let err = reset_agent_args(&argv, &ws).expect_err("must refuse");
|
|
1932
|
+
assert!(
|
|
1933
|
+
err.to_string().contains("multiple alive teams"),
|
|
1934
|
+
"reset-agent args builder must surface the refusal; got: {err}"
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
#[test]
|
|
1939
|
+
fn remove_agent_args_builder_refuses_on_multi_alive_team_before_agent_validation() {
|
|
1940
|
+
let ws = tmp_workspace();
|
|
1941
|
+
seed_two_alive_teams_in(&ws);
|
|
1942
|
+
let argv = cli_argv(&["--workspace", &ws.to_string_lossy()]);
|
|
1943
|
+
let err = remove_agent_args(&argv, &ws).expect_err("must refuse");
|
|
1944
|
+
assert!(
|
|
1945
|
+
err.to_string().contains("multiple alive teams"),
|
|
1946
|
+
"remove-agent args builder must surface the refusal; got: {err}"
|
|
1947
|
+
);
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
// ──────────── Stage QR: quick-start/restart separation ────────────
|
|
1951
|
+
// Design doc: .team/artifacts/quickstart-restart-separation-design.md
|
|
1952
|
+
|
|
1953
|
+
#[test]
|
|
1954
|
+
fn quick_start_refuses_fresh_flag_with_restart_guidance() {
|
|
1955
|
+
// QR contract: `--fresh` is gone from quick-start. The flag is
|
|
1956
|
+
// not advertised or carried in QuickStartArgs, but scripts that
|
|
1957
|
+
// still pass it get a clear redirect to restart --allow-fresh.
|
|
1958
|
+
let ws = tmp_workspace();
|
|
1959
|
+
let argv = cli_argv(&[
|
|
1960
|
+
"--workspace",
|
|
1961
|
+
&ws.to_string_lossy(),
|
|
1962
|
+
"--fresh",
|
|
1963
|
+
]);
|
|
1964
|
+
let err = quick_start_args(&argv, &ws).expect_err("must refuse --fresh");
|
|
1965
|
+
let message = err.to_string();
|
|
1966
|
+
assert!(
|
|
1967
|
+
message.contains("no longer accepts --fresh"),
|
|
1968
|
+
"QR: refusal must say --fresh is gone; got: {message}"
|
|
1969
|
+
);
|
|
1970
|
+
assert!(
|
|
1971
|
+
message.contains("restart --allow-fresh"),
|
|
1972
|
+
"QR: refusal must redirect to `restart --allow-fresh`; got: {message}"
|
|
1973
|
+
);
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
#[test]
|
|
1977
|
+
fn quick_start_without_fresh_flag_still_builds_args() {
|
|
1978
|
+
// Without --fresh, args build normally (the initial-creation path).
|
|
1979
|
+
let ws = tmp_workspace();
|
|
1980
|
+
let argv = cli_argv(&[
|
|
1981
|
+
"--workspace",
|
|
1982
|
+
&ws.to_string_lossy(),
|
|
1983
|
+
]);
|
|
1984
|
+
let args = quick_start_args(&argv, &ws).expect("must build");
|
|
1985
|
+
assert_eq!(args.workspace, ws);
|
|
1986
|
+
// No `fresh` field anymore — the struct must compile and round-trip
|
|
1987
|
+
// without it.
|
|
1988
|
+
let _ = args.no_display;
|
|
1989
|
+
}
|
|
1727
1990
|
}
|
|
@@ -117,7 +117,6 @@ pub mod lifecycle_port {
|
|
|
117
117
|
name: Option<&str>,
|
|
118
118
|
team_id: Option<&str>,
|
|
119
119
|
yes: bool,
|
|
120
|
-
fresh: bool,
|
|
121
120
|
open_display: bool,
|
|
122
121
|
) -> Result<Value, CliError> {
|
|
123
122
|
match crate::lifecycle::quick_start_in_workspace_with_display(
|
|
@@ -125,7 +124,6 @@ pub mod lifecycle_port {
|
|
|
125
124
|
agents_dir,
|
|
126
125
|
name,
|
|
127
126
|
yes,
|
|
128
|
-
fresh,
|
|
129
127
|
team_id,
|
|
130
128
|
open_display,
|
|
131
129
|
) {
|
|
@@ -3745,18 +3743,13 @@ pub mod leader_port {
|
|
|
3745
3743
|
}
|
|
3746
3744
|
|
|
3747
3745
|
fn team_owner_value(state: &Value, team_id: &TeamKey) -> Option<Value> {
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
state.get("team_owner").cloned()
|
|
3756
|
-
} else {
|
|
3757
|
-
None
|
|
3758
|
-
}
|
|
3759
|
-
})
|
|
3746
|
+
// Stage 2 (identity-boundary unified plan, architect direction
|
|
3747
|
+
// 2026-06-23): route through the ownership repository so all owner
|
|
3748
|
+
// reads see the same precedence (teams.<key> > top-level when
|
|
3749
|
+
// team_state_key matches). The repository preserves the pre-Stage-2
|
|
3750
|
+
// semantics of this helper and adds an `OwnershipSource` tag that
|
|
3751
|
+
// diagnose/status surfaces can consume in later stages.
|
|
3752
|
+
crate::state::ownership::read_owner_value(state, team_id.as_str()).cloned()
|
|
3760
3753
|
}
|
|
3761
3754
|
|
|
3762
3755
|
fn family_a_owner_value(
|
|
@@ -265,6 +265,16 @@ use rusqlite::params;
|
|
|
265
265
|
pub fn approvals(workspace: &Path, agent: Option<&str>, as_json: bool) -> Result<Value, CliError> {
|
|
266
266
|
let _ = as_json;
|
|
267
267
|
let state = read_runtime_state(workspace);
|
|
268
|
+
approvals_scoped(workspace, &state, agent, as_json)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
pub fn approvals_scoped(
|
|
272
|
+
workspace: &Path,
|
|
273
|
+
state: &Value,
|
|
274
|
+
agent: Option<&str>,
|
|
275
|
+
as_json: bool,
|
|
276
|
+
) -> Result<Value, CliError> {
|
|
277
|
+
let _ = as_json;
|
|
268
278
|
let session = state.get("session_name").and_then(Value::as_str).filter(|s| !s.is_empty());
|
|
269
279
|
let mut approvals = Vec::new();
|
|
270
280
|
if let (Some(session), Some(agents)) = (session, state.get("agents").and_then(Value::as_object)) {
|
|
@@ -333,12 +343,13 @@ use rusqlite::params;
|
|
|
333
343
|
limit: usize,
|
|
334
344
|
since: Option<&str>,
|
|
335
345
|
as_json: bool,
|
|
346
|
+
owner_team_id: Option<&str>,
|
|
336
347
|
) -> Result<Value, CliError> {
|
|
337
348
|
let _ = as_json;
|
|
338
349
|
let store = crate::message_store::MessageStore::open(workspace)
|
|
339
350
|
.map_err(|e| CliError::Runtime(e.to_string()))?;
|
|
340
351
|
let mut messages = store
|
|
341
|
-
.inbox(agent, limit,
|
|
352
|
+
.inbox(agent, limit, owner_team_id)
|
|
342
353
|
.map_err(|e| CliError::Runtime(e.to_string()))?;
|
|
343
354
|
if let Some(cutoff) = since.and_then(parse_rfc3339) {
|
|
344
355
|
messages.retain(|message| {
|
|
@@ -455,11 +455,13 @@ latest result: none";
|
|
|
455
455
|
let payload = err.to_payload(Path::new("/tmp/cli-error-123.log"), "quick-start");
|
|
456
456
|
assert_eq!(payload.reason.as_deref(), Some("tmux_session_name_conflict"));
|
|
457
457
|
assert_eq!(payload.session_name.as_deref(), Some("my-team"));
|
|
458
|
-
// E8 (N38): quick-start 撞已有 runtime 引导到 restart(resume)
|
|
458
|
+
// E8 (N38): quick-start 撞已有 runtime 引导到 restart(resume);
|
|
459
|
+
// context reset is only through restart --allow-fresh with explicit consent.
|
|
459
460
|
assert_eq!(
|
|
460
461
|
payload.action,
|
|
461
462
|
"tmux session `my-team` already exists. It may be your own existing team. \
|
|
462
|
-
To resume it use `team-agent restart
|
|
463
|
+
To resume it use `team-agent restart`. \
|
|
464
|
+
If recovery is impossible, use `team-agent restart --allow-fresh` only after explicit context-loss consent. \
|
|
463
465
|
Only if you want a separate team, change `name:` in TEAM.md and run quick-start again. \
|
|
464
466
|
Never terminate existing tmux sessions from quick-start."
|
|
465
467
|
);
|
|
@@ -635,6 +637,7 @@ Truncated: more fallback entries available; run team-agent inbox leader";
|
|
|
635
637
|
detail: false,
|
|
636
638
|
summary: true,
|
|
637
639
|
json: true,
|
|
640
|
+
team: None,
|
|
638
641
|
};
|
|
639
642
|
let err = cmd_status(&args).unwrap_err();
|
|
640
643
|
assert!(
|
|
@@ -652,6 +655,7 @@ Truncated: more fallback entries available; run team-agent inbox leader";
|
|
|
652
655
|
detail: false,
|
|
653
656
|
summary: true,
|
|
654
657
|
json: false,
|
|
658
|
+
team: None,
|
|
655
659
|
};
|
|
656
660
|
let err = cmd_status(&args).unwrap_err();
|
|
657
661
|
assert!(
|