@team-agent/installer 0.5.99 → 0.5.100

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.
@@ -106,92 +106,6 @@ fn seed_team_spec(ws: &std::path::Path) {
106
106
  std::fs::write(ws.join("team.spec.yaml"), spec).unwrap();
107
107
  }
108
108
 
109
- // ── ACK-CRACK [P1 byte-shape] — acknowledge_idle must write golden's TTL suppression shape ───────
110
- // golden runtime.py:680-688: manual-acknowledge persists
111
- // coordinator.idle_acknowledged[team] = {acknowledged_at, expires_at, ttl_seconds}
112
- // coordinator.suppressed_idle_alerts[team][worker].idle_fallback =
113
- // {suppressed_at, suppressed_by:"manual_acknowledge", manual_acknowledge:true, expires_at, ttl_seconds}
114
- // The clear logic does datetime.fromisoformat(entry["expires_at"]); a MISSING expires_at -> ValueError
115
- // -> "invalid_suppression_timestamp" -> immediate self-clear (latent crack once detect_idle_fallbacks
116
- // is ported). So BOTH idle_acknowledged and the entry MUST carry a non-empty expires_at.
117
- #[test]
118
- fn acknowledge_idle_writes_golden_ttl_suppression_shape() {
119
- let ws = tmp_workspace();
120
- crate::state::persist::save_runtime_state(
121
- &ws,
122
- &serde_json::json!({
123
- "active_team_key": "teamX",
124
- "agents": {"w1": {"status": "running", "provider": "codex"}}
125
- }),
126
- )
127
- .unwrap();
128
- let _ = lifecycle_port::acknowledge_idle(&ws, None).expect("acknowledge_idle ok");
129
- let state = crate::state::persist::load_runtime_state(&ws).unwrap();
130
- let ack = &state["coordinator"]["idle_acknowledged"]["teamX"];
131
- assert!(
132
- ack.get("expires_at")
133
- .and_then(serde_json::Value::as_str)
134
- .is_some_and(|s| !s.is_empty()),
135
- "ACK-CRACK: idle_acknowledged[team] must carry a non-empty expires_at (golden); got {ack}"
136
- );
137
- assert!(
138
- ack.get("ttl_seconds").is_some(),
139
- "idle_acknowledged[team] must carry ttl_seconds; got {ack}"
140
- );
141
- let entry = &state["coordinator"]["suppressed_idle_alerts"]["teamX"]["w1"]["idle_fallback"];
142
- assert!(
143
- entry.get("expires_at").and_then(serde_json::Value::as_str).is_some_and(|s| !s.is_empty()),
144
- "ACK-CRACK: the manual-ack suppression entry must carry expires_at (else clear logic ValueErrors \
145
- -> instant self-clear); got {entry}"
146
- );
147
- assert_eq!(
148
- entry["suppressed_by"],
149
- serde_json::json!("manual_acknowledge"),
150
- "golden suppressed_by; got {entry}"
151
- );
152
- assert_eq!(
153
- entry["manual_acknowledge"],
154
- serde_json::json!(true),
155
- "golden manual_acknowledge:true; got {entry}"
156
- );
157
- }
158
- // ── ACK return-shape [P1 byte-parity] — acknowledge_idle must RETURN golden's keys ───────────────
159
- // golden runtime.py:691: return {ok, team, agent_id, acknowledged_at, expires_at, ttl_seconds}.
160
- // Rust (cli/mod.rs) returns only {ok, team, ttl_seconds} -> missing agent_id, acknowledged_at,
161
- // expires_at. RED. (acknowledged_at/expires_at are the same values written into idle_acknowledged.)
162
- #[test]
163
- fn acknowledge_idle_return_carries_golden_keys() {
164
- let ws = tmp_workspace();
165
- crate::state::persist::save_runtime_state(
166
- &ws,
167
- &serde_json::json!({ "active_team_key": "teamX", "agents": {"w1": {"status": "running", "provider": "codex"}} }),
168
- )
169
- .unwrap();
170
- let r = lifecycle_port::acknowledge_idle(&ws, None).expect("acknowledge_idle ok");
171
- let obj = r.as_object().expect("ack returns a dict");
172
- for key in [
173
- "ok",
174
- "team",
175
- "agent_id",
176
- "acknowledged_at",
177
- "expires_at",
178
- "ttl_seconds",
179
- ] {
180
- assert!(
181
- obj.contains_key(key),
182
- "ACK return-shape: golden return carries `{key}` (runtime.py:691: ok/team/agent_id/\
183
- acknowledged_at/expires_at/ttl_seconds); Rust omits it. got keys {:?}",
184
- obj.keys().collect::<Vec<_>>()
185
- );
186
- }
187
- assert!(
188
- obj.get("expires_at")
189
- .and_then(serde_json::Value::as_str)
190
- .is_some_and(|s| !s.is_empty()),
191
- "ACK return-shape: expires_at must be a non-empty timestamp; got {r}"
192
- );
193
- let _ = std::fs::remove_dir_all(&ws);
194
- }
195
109
  // ── BUG-2 [real bug] — inbox must RETURN the stored messages, not a hardcoded []. ────────────────
196
110
  // Golden status/inbox.py:35-38 -> MessageStore.inbox(agent_id) (core.py:242, owner_team_id=None):
197
111
  // select <MESSAGE_SELECT> from messages where sender = ? or recipient = ? order by created_at desc
@@ -680,16 +594,7 @@ fn remove_agent_from_spec_refusal_is_not_success_envelope() {
680
594
  fn stuck_cancel_persists_suppression_and_stuck_list_reads_state() {
681
595
  let ws = tmp_workspace();
682
596
  seed_collect_state(&ws);
683
- let out = json_output(
684
- cmd_stuck_cancel(&StuckCancelArgs {
685
- agent: "fake_impl".to_string(),
686
- workspace: ws.clone(),
687
- alert_type: None,
688
- json: true,
689
- team: None,
690
- })
691
- .unwrap(),
692
- );
597
+ let out = messaging::stuck_cancel(&ws, "fake_impl", None, "leader").unwrap();
693
598
  let team_key = seeded_team_key(&ws);
694
599
  assert_eq!(out["ok"], json!(true));
695
600
  assert_eq!(
@@ -715,98 +620,10 @@ fn stuck_cancel_persists_suppression_and_stuck_list_reads_state() {
715
620
  && e["agent_id"] == json!("fake_impl")),
716
621
  "stuck_cancel must write coordinator.idle_alert_suppressed"
717
622
  );
718
- let listed = json_output(
719
- cmd_stuck_list(&StuckListArgs {
720
- workspace: ws.clone(),
721
- json: true,
722
- team: None,
723
- })
724
- .unwrap(),
725
- );
623
+ let listed = messaging::stuck_list(&ws).unwrap();
726
624
  assert_eq!(
727
625
  listed["suppressed_idle_alerts"]["fake_impl"]["stuck"]["suppressed_by"],
728
626
  json!("leader"),
729
627
  "stuck-list must read the persisted state mirror, not return a hard-coded empty list"
730
628
  );
731
629
  }
732
-
733
- #[test]
734
- fn stuck_cancel_explicit_team_is_rejected_until_backend_is_scoped() {
735
- let ws = tmp_workspace();
736
- seed_collect_state(&ws);
737
- let err = cmd_stuck_cancel(&StuckCancelArgs {
738
- agent: "fake_impl".to_string(),
739
- workspace: ws.clone(),
740
- alert_type: None,
741
- json: true,
742
- team: Some("current".to_string()),
743
- })
744
- .expect_err("explicit --team must not silently write global stuck suppression");
745
- assert!(
746
- err.to_string().contains("not supported yet"),
747
- "stuck-cancel --team must be an explicit refusal until backend supports scoped writes; got {err}"
748
- );
749
- let _ = std::fs::remove_dir_all(&ws);
750
- }
751
-
752
- #[test]
753
- fn stuck_cancel_invalid_alert_type_is_rejected() {
754
- let ws = tmp_workspace();
755
- seed_collect_state(&ws);
756
- let code = run(
757
- &cli_argv(&[
758
- "stuck-cancel",
759
- "fake_impl",
760
- "--workspace",
761
- &ws.to_string_lossy(),
762
- "--alert-type",
763
- "bogus",
764
- "--json",
765
- ]),
766
- &ws,
767
- );
768
- assert_eq!(
769
- code,
770
- ExitCode::Error,
771
- "Python rejects alert_type outside stuck/idle_fallback/cross_worker_deadlock/all; Rust must not silently coerce bogus to stuck"
772
- );
773
- }
774
- #[test]
775
- fn acknowledge_idle_records_manual_idle_fallback_suppression_and_event() {
776
- let ws = tmp_workspace();
777
- seed_collect_state(&ws);
778
- let out = json_output(
779
- cmd_acknowledge_idle(&AcknowledgeIdleArgs {
780
- team: None,
781
- workspace: ws.clone(),
782
- json: true,
783
- })
784
- .unwrap(),
785
- );
786
- let team_key = seeded_team_key(&ws);
787
- assert_eq!(out["ok"], json!(true));
788
- assert_eq!(out["team"], json!(team_key));
789
- assert_eq!(out["ttl_seconds"], json!(1800));
790
- let state = read_state(&ws);
791
- let ack = &state["coordinator"]["idle_acknowledged"][&team_key];
792
- assert_eq!(ack["ttl_seconds"], json!(1800));
793
- assert!(ack["acknowledged_at"].as_str().is_some());
794
- assert_eq!(
795
- state["coordinator"]["suppressed_idle_alerts"][&team_key]["fake_impl"]["idle_fallback"]
796
- ["suppressed_by"],
797
- json!("manual_acknowledge")
798
- );
799
- assert_eq!(
800
- state["coordinator"]["suppressed_idle_alerts"][&team_key]["fake_impl"]["idle_fallback"]
801
- ["manual_acknowledge"],
802
- json!(true)
803
- );
804
- assert!(
805
- read_events(&ws)
806
- .iter()
807
- .any(|e| e["event"] == json!("coordinator.idle_acknowledged")
808
- && e["team"] == json!(team_key)
809
- && e["ttl_seconds"] == json!(1800)),
810
- "acknowledge-idle must emit coordinator.idle_acknowledged"
811
- );
812
- }
@@ -332,15 +332,15 @@ fn dispatch_routes_diagnose_checks_runtime_beyond_healthy_leader() {
332
332
  leader_env: Default::default(),
333
333
  }]);
334
334
  crate::transport_factory::with_leader_endpoint_transport(endpoint, transport, || {
335
- for command in ["doctor", "diagnose"] {
336
- let code = run(
337
- &cli_argv(&[command, "--workspace", &ws.to_string_lossy(), "--json"]),
338
- &ws,
339
- );
340
- assert_eq!(code, ExitCode::Error, "healthy binding alone cannot hide missing coordinator");
341
- }
342
- let result = cmd_diagnose(&DiagnoseArgs {
335
+ let code = run(
336
+ &cli_argv(&["doctor", "--workspace", &ws.to_string_lossy(), "--json"]),
337
+ &ws,
338
+ );
339
+ assert_eq!(code, ExitCode::Error, "healthy binding alone cannot hide missing coordinator");
340
+ let result = cmd_doctor(&DoctorArgs {
343
341
  workspace: ws.clone(), json: true, team: None,
342
+ spec: None, gate: None, comms: false, fix: false,
343
+ fix_schema: false, cleanup_orphans: false, confirm: false,
344
344
  }).expect("diagnostic report");
345
345
  let CmdOutput::Json(report) = result.output else {
346
346
  panic!("expected JSON diagnostic report");
@@ -377,7 +377,7 @@ fn dispatch_routes_diagnose_self_signed_attached_without_registry_is_not_ok() {
377
377
  let ws = tmp_workspace();
378
378
  seed_self_signed_attached_leader(&ws);
379
379
  let code = run(
380
- &cli_argv(&["diagnose", "--workspace", &ws.to_string_lossy(), "--json"]),
380
+ &cli_argv(&["doctor", "--workspace", &ws.to_string_lossy(), "--json"]),
381
381
  &ws,
382
382
  );
383
383
  assert_ne!(
@@ -138,7 +138,7 @@ mod tests {
138
138
  "providers": {"claude": {"version": "secret"}},
139
139
  "runtime": {"workspace": "/tmp/secret"}
140
140
  });
141
- let output = render("diagnose", &report);
141
+ let output = render("doctor", &report);
142
142
  assert!(output.contains("issue: short_issue"));
143
143
  assert!(output.contains("issue: code=object_issue"));
144
144
  assert!(output.contains("repair: action=run repair"));
@@ -312,14 +312,6 @@ pub struct QuickStartArgs {
312
312
  // user-confirmed. quick-start is now an initial-creation-only verb.
313
313
  }
314
314
 
315
- /// `init`(`parser.py` bootstrap verb)。
316
- #[derive(Debug, Clone, PartialEq, Eq)]
317
- pub struct InitArgs {
318
- pub workspace: PathBuf,
319
- pub force: bool,
320
- pub json: bool,
321
- }
322
-
323
315
  /// `send`(`parser.py:262`)。`target` xor `--to`(fanout);`message` 多 token join 空格。
324
316
  #[derive(Debug, Clone, PartialEq)]
325
317
  pub struct SendArgs {
@@ -554,36 +546,6 @@ pub struct RemoveAgentArgs {
554
546
  pub json: bool,
555
547
  }
556
548
 
557
- /// `stuck-list`(`parser.py:424`)。
558
- #[derive(Debug, Clone, PartialEq, Eq)]
559
- pub struct StuckListArgs {
560
- pub workspace: PathBuf,
561
- pub json: bool,
562
- /// Stage 4: explicit `--team` scope (read-only enumeration).
563
- pub team: Option<String>,
564
- }
565
-
566
- /// `stuck-cancel`(`parser.py:429`)。`--alert-type` ∈ {stuck, idle_fallback, all},默认 stuck。
567
- #[derive(Debug, Clone, PartialEq, Eq)]
568
- pub struct StuckCancelArgs {
569
- pub agent: String,
570
- pub workspace: PathBuf,
571
- /// `None` 表 `all`(展开全集);`Some(AlertType)` 表单类型。
572
- pub alert_type: Option<AlertType>,
573
- pub json: bool,
574
- /// Parsed but rejected until stuck suppression storage grows a team-scoped
575
- /// backend selector.
576
- pub team: Option<String>,
577
- }
578
-
579
- /// `acknowledge-idle`(`parser.py:436`)。
580
- #[derive(Debug, Clone, PartialEq, Eq)]
581
- pub struct AcknowledgeIdleArgs {
582
- pub team: Option<String>,
583
- pub workspace: PathBuf,
584
- pub json: bool,
585
- }
586
-
587
549
  /// `doctor`(`parser.py:318`)。`--gate` ∈ {orphans, comms};`--fix` 须配 `--gate`(`cmd_doctor` 校验)。
588
550
  #[derive(Debug, Clone, PartialEq, Eq)]
589
551
  pub struct DoctorArgs {
@@ -683,17 +645,6 @@ pub struct WaitArgs {
683
645
  pub json: bool,
684
646
  }
685
647
 
686
- /// `diagnose` compatibility spelling for the canonical `doctor` health report.
687
- /// The CLI normalizes it to `DoctorArgs`; this narrow type remains for direct
688
- /// Rust callers that used the pre-unification API.
689
- #[derive(Debug, Clone, PartialEq, Eq)]
690
- pub struct DiagnoseArgs {
691
- pub workspace: PathBuf,
692
- pub json: bool,
693
- /// Stage 4: explicit `--team` scope (read-only health report).
694
- pub team: Option<String>,
695
- }
696
-
697
648
  /// `preflight`(`parser.py:160`)。
698
649
  #[derive(Debug, Clone, PartialEq, Eq)]
699
650
  pub struct PreflightArgs {
@@ -1313,7 +1313,7 @@ fn apply_endpoint_convergence(
1313
1313
  "persisted": false,
1314
1314
  "checked_paths": convergence_checked_paths(team_id.as_str()),
1315
1315
  "action": format!(
1316
- "old tmux endpoint {old_endpoint} still has this team's session or pane tuple; run team-agent diagnose --json before retrying restart"
1316
+ "old tmux endpoint {old_endpoint} still has this team's session or pane tuple; run team-agent doctor --json before retrying restart"
1317
1317
  ),
1318
1318
  })),
1319
1319
  false,
@@ -336,7 +336,7 @@ fn restart_with_selected_team_and_transport(
336
336
  .first()
337
337
  .cloned()
338
338
  .unwrap_or_else(|| "dirty_topology".to_string()),
339
- error: "restart refused: tmux endpoint/socket topology is inconsistent; run diagnose from the intended leader socket before restarting".to_string(),
339
+ error: "restart refused: tmux endpoint/socket topology is inconsistent; run doctor from the intended leader socket before restarting".to_string(),
340
340
  issue_ids: topology_issue_ids,
341
341
  });
342
342
  }
@@ -1804,7 +1804,7 @@ fn restart_readiness_timeout_message(
1804
1804
  - tmux session created: {session}\n\
1805
1805
  - worker pane addressable: {pane}\n\
1806
1806
  - coordinator alive: {coordinator}\n\
1807
- Action: check coordinator log {log}, then inspect this workspace with `team-agent diagnose --workspace '{workspace}'`\n\
1807
+ Action: check coordinator log {log}, then inspect this workspace with `team-agent doctor --workspace '{workspace}'`\n\
1808
1808
  Log: coordinator_log={log} state={state} pid_file={pid}",
1809
1809
  missing = restart_readiness_missing_summary(readiness),
1810
1810
  session = yes_no(readiness.session_created),
@@ -2703,7 +2703,7 @@ fn restart_failure_phase(
2703
2703
  // 0.5.39 Slice 1 (tmux-server-death-locate §11.1 B): promote
2704
2704
  // spawn/readiness errors whose stderr contains "server exited
2705
2705
  // unexpectedly" to `tmux_server_crashed`, so downstream event
2706
- // classification and diagnose next_actions ("team-agent diagnose")
2706
+ // classification and doctor next_actions ("team-agent doctor")
2707
2707
  // don't misattribute a whole-server death to a per-agent provider
2708
2708
  // failure. Restart itself does not recover from server crashes in
2709
2709
  // 0.5.39 (§11.1 C is Slice 3, later car); it just classifies clearly.
@@ -2776,7 +2776,7 @@ fn write_restart_agent_failed_event(
2776
2776
  "phase": phase,
2777
2777
  "error": error,
2778
2778
  "action": format!(
2779
- "inspect worker {} output, then restart that worker with `team-agent restart-agent {}` or rerun `team-agent restart --allow-fresh`",
2779
+ "inspect worker {} output, then reset that worker with `team-agent reset-agent {} --discard-session` or rerun `team-agent restart --allow-fresh`",
2780
2780
  decision.agent_id,
2781
2781
  decision.agent_id
2782
2782
  ),
@@ -3262,7 +3262,7 @@ fn restart_failure_next_actions(failed_agents: &[RestartFailedAgent]) -> Vec<Str
3262
3262
  .iter()
3263
3263
  .map(|failure| {
3264
3264
  format!(
3265
- "inspect worker {} output, then restart that worker with `team-agent restart-agent {}` or rerun `team-agent restart --allow-fresh`",
3265
+ "inspect worker {} output, then reset that worker with `team-agent reset-agent {} --discard-session` or rerun `team-agent restart --allow-fresh`",
3266
3266
  failure.agent_id, failure.agent_id
3267
3267
  )
3268
3268
  })
@@ -4911,7 +4911,7 @@ tasks:
4911
4911
  },
4912
4912
  std::time::Duration::from_secs(30),
4913
4913
  );
4914
- assert!(message.contains("team-agent diagnose --workspace"));
4914
+ assert!(message.contains("team-agent doctor --workspace"));
4915
4915
  assert!(message.contains(workspace.to_string_lossy().as_ref()));
4916
4916
  assert!(!message.contains("--allow-fresh"));
4917
4917
  assert!(!message.contains("restart <agent>"));
@@ -122,7 +122,7 @@ fn stale_snapshot_cannot_flip_status_or_diagnose_ok_readiness() {
122
122
  "--json",
123
123
  ]);
124
124
  let diagnose_before = case.run_json(&[
125
- "diagnose",
125
+ "doctor",
126
126
  "--workspace",
127
127
  case.workspace_str(),
128
128
  "--team",
@@ -142,7 +142,7 @@ fn stale_snapshot_cannot_flip_status_or_diagnose_ok_readiness() {
142
142
  "--json",
143
143
  ]);
144
144
  let diagnose_after = case.run_json(&[
145
- "diagnose",
145
+ "doctor",
146
146
  "--workspace",
147
147
  case.workspace_str(),
148
148
  "--team",
@@ -182,7 +182,7 @@ fn diagnose_missing_profile_is_rc1_ok_false_and_zero_token() {
182
182
  .unwrap();
183
183
 
184
184
  let output = Command::new(crate::lifecycle::tests::test_binary_path())
185
- .args(["diagnose", team.to_str().unwrap(), "--workspace", ws.to_str().unwrap(), "--json"])
185
+ .args(["doctor", team.to_str().unwrap(), "--workspace", ws.to_str().unwrap(), "--json"])
186
186
  .current_dir(&team)
187
187
  .output()
188
188
  .expect("diagnose subprocess should run");
@@ -1,7 +1,7 @@
1
1
  use super::launch_spawn::seed_healthy_coordinator;
2
2
  use super::*;
3
3
  use crate::cli::{
4
- cmd_diagnose, cmd_send, cmd_status, lifecycle_port, CmdOutput, CmdResult, DiagnoseArgs,
4
+ cmd_doctor, cmd_send, cmd_status, lifecycle_port, CmdOutput, CmdResult, DoctorArgs,
5
5
  SendArgs, StatusArgs,
6
6
  };
7
7
  use crate::transport::test_support::OfflineTransport;
@@ -582,10 +582,17 @@ fn run_diagnose_after_quick_start() -> Value {
582
582
  .is_some(),
583
583
  "index-missing diagnose requires a persisted seeded owner"
584
584
  );
585
- let diagnose = cmd_diagnose(&DiagnoseArgs {
585
+ let diagnose = cmd_doctor(&DoctorArgs {
586
586
  workspace,
587
587
  json: true,
588
588
  team: Some("teamdir".to_string()),
589
+ spec: None,
590
+ gate: None,
591
+ comms: false,
592
+ fix: false,
593
+ fix_schema: false,
594
+ cleanup_orphans: false,
595
+ confirm: false,
589
596
  });
590
597
  match cmd_value(diagnose) {
591
598
  Value::Object(map) => map
@@ -272,66 +272,6 @@ fn c1_runtime_commands_resolve_active_team_for_status_send_and_collect() {
272
272
  );
273
273
  }
274
274
 
275
- #[test]
276
- #[ignore = "real-machine: command-file gate uses real team-agent binary/lifecycle"]
277
- fn c2_stop_team_level_command_exists_and_keeps_state_while_stopping_runtime() {
278
- let ws = tmp_dir("c2-stop");
279
- seed_runtime_state(&ws);
280
-
281
- let out = run(
282
- &["stop", "--workspace", ws.to_str().unwrap(), "--json"],
283
- &ws,
284
- );
285
- let err = String::from_utf8_lossy(&out.stderr);
286
- assert!(
287
- !err.contains("invalid choice: 'stop'"),
288
- "CR-005: canonical `team-agent stop --workspace <ws> --json` must be a registered team-level stop verb, not argparse invalid-choice; stderr={err:?}"
289
- );
290
- assert_eq!(
291
- out.status.code(),
292
- Some(0),
293
- "CR-005: stop should stop the runtime while keeping state on a valid workspace; stdout={} stderr={}",
294
- String::from_utf8_lossy(&out.stdout),
295
- err
296
- );
297
- let value = stdout_json(&out);
298
- assert_eq!(
299
- value["ok"],
300
- json!(true),
301
- "stop must return a successful JSON envelope"
302
- );
303
- assert!(
304
- team_agent::state::persist::runtime_state_path(&ws).exists(),
305
- "stop keeps runtime state for later restart"
306
- );
307
- }
308
-
309
- #[test]
310
- #[ignore = "real-machine: command-file gate uses real team-agent binary/lifecycle"]
311
- fn c2_canonical_verbs_expose_help_instead_of_invalid_choice() {
312
- let cwd = tmp_dir("c2-help-verbs");
313
- let mut failures = Vec::new();
314
- for verb in ["start", "restart-agent", "purge-agent"] {
315
- let out = run(&[verb, "--help"], &cwd);
316
- let stdout = String::from_utf8_lossy(&out.stdout);
317
- let stderr = String::from_utf8_lossy(&out.stderr);
318
- if out.status.code() != Some(0)
319
- || stderr.contains("invalid choice:")
320
- || !(stdout.contains("usage") || stderr.contains("usage"))
321
- {
322
- failures.push(format!(
323
- "{verb}: code={:?} stdout={stdout:?} stderr={stderr:?}",
324
- out.status.code()
325
- ));
326
- }
327
- }
328
- assert!(
329
- failures.is_empty(),
330
- "CR-030/032/035/063: canonical lifecycle verbs must be registered and expose help, not invalid-choice:\n{}",
331
- failures.join("\n")
332
- );
333
- }
334
-
335
275
  #[test]
336
276
  #[ignore = "real-machine: command-file gate uses real team-agent binary/lifecycle"]
337
277
  fn c2_quick_start_help_is_zero_token_help_only_and_does_not_compile_workspace() {
@@ -80,7 +80,7 @@ fn quick_start_workspace_override_is_not_dropped_before_runtime_state_write() {
80
80
  let quick_start_args = source_section(
81
81
  &cli_types,
82
82
  "pub struct QuickStartArgs",
83
- "pub struct InitArgs",
83
+ "pub struct SendArgs",
84
84
  );
85
85
  assert!(
86
86
  quick_start_args.contains("pub workspace: PathBuf"),
@@ -97,7 +97,7 @@ fn quick_start_workspace_override_is_not_dropped_before_runtime_state_write() {
97
97
 
98
98
  let emit =
99
99
  std::fs::read_to_string(concat!(env!("CARGO_MANIFEST_DIR"), "/src/cli/emit.rs")).unwrap();
100
- let quick_start_args_fn = source_section(&emit, "fn quick_start_args", "fn init_args");
100
+ let quick_start_args_fn = source_section(&emit, "fn quick_start_args", "fn resolve_cli_path");
101
101
  assert!(
102
102
  quick_start_args_fn.contains("workspace: workspace.clone()")
103
103
  || quick_start_args_fn.contains("workspace,"),
@@ -551,11 +551,11 @@ fn coordinator_unavailable_outcome(
551
551
  );
552
552
  let warning = if persist {
553
553
  format!(
554
- "coordinator is not running; message was queued for {recipient} and will retry. Run `team-agent diagnose` or restart the team."
554
+ "coordinator is not running; message was queued for {recipient} and will retry. Run `team-agent doctor` or restart the team."
555
555
  )
556
556
  } else {
557
557
  format!(
558
- "coordinator protocol or schema is incompatible; message was not queued for {recipient}. Run `team-agent diagnose` or upgrade the runtime."
558
+ "coordinator protocol or schema is incompatible; message was not queued for {recipient}. Run `team-agent doctor` or upgrade the runtime."
559
559
  )
560
560
  };
561
561
  event_log.write(
@@ -172,7 +172,7 @@ pub enum AuthHintStatus {
172
172
  pub enum NoPingReason {
173
173
  /// worker 从未 open 过 turn(`idle_predicate.py:61`)。
174
174
  NotArmedNoWorkerTurn,
175
- /// suppress flag 置位(acknowledge-idle 后,`idle_predicate.py:63`)。
175
+ /// Suppress flag from a legacy idle acknowledgement (`idle_predicate.py:63`).
176
176
  Acknowledged,
177
177
  /// armed 但 elapsed < debounce(`idle_predicate.py:65`)。
178
178
  DebounceActive,
@@ -419,9 +419,6 @@ pub enum StateWriteIntent<'a> {
419
419
  SchedulerSuppression {
420
420
  owner_team_id: Option<&'a str>,
421
421
  },
422
- IdleAck {
423
- team_key: Option<&'a str>,
424
- },
425
422
  TaskRepair {
426
423
  team_key: Option<&'a str>,
427
424
  },
@@ -650,8 +647,6 @@ fn route_direct(
650
647
  },
651
648
  // SchedulerSuppression -> root save (messaging/scheduler.rs:259).
652
649
  StateWriteIntent::SchedulerSuppression { .. } => helper_write_root(workspace, state),
653
- // IdleAck -> root save (cli/mod.rs:2544).
654
- StateWriteIntent::IdleAck { .. } => helper_write_root(workspace, state),
655
650
  // TaskRepair -> `save_team_scoped_state` (cli/adapters.rs:675).
656
651
  StateWriteIntent::TaskRepair { .. } => helper_write_team_scoped(workspace, state),
657
652
  // FakeE2eSeed / SelfMigration are diagnostic seams. The current
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-agent/installer",
3
- "version": "0.5.99",
3
+ "version": "0.5.100",
4
4
  "description": "npx installer for Team Agent",
5
5
  "keywords": [
6
6
  "codex",
@@ -20,9 +20,9 @@
20
20
  "team-agent-installer": "npm/install.mjs"
21
21
  },
22
22
  "optionalDependencies": {
23
- "@team-agent/cli-darwin-arm64": "0.5.99",
24
- "@team-agent/cli-darwin-x64": "0.5.99",
25
- "@team-agent/cli-linux-x64": "0.5.99"
23
+ "@team-agent/cli-darwin-arm64": "0.5.100",
24
+ "@team-agent/cli-darwin-x64": "0.5.100",
25
+ "@team-agent/cli-linux-x64": "0.5.100"
26
26
  },
27
27
  "scripts": {
28
28
  "postinstall": "node npm/bincheck.mjs",
@@ -157,10 +157,10 @@ Resume/repair:
157
157
  team-agent restart "$WORKSPACE" --team "$TEAM" --json
158
158
  ```
159
159
 
160
- For a single worker where the leader has explicitly selected that worker:
160
+ A single-worker clean-context reset is not a resume operation. Only after the user explicitly approves discarding that worker's session, use:
161
161
 
162
162
  ```bash
163
- team-agent restart-agent "$AGENT_ID" --workspace "$WORKSPACE" --team "$TEAM" --json
163
+ team-agent reset-agent "$AGENT_ID" --workspace "$WORKSPACE" --team "$TEAM" --discard-session --json
164
164
  ```
165
165
 
166
166
  Validate:
@@ -112,12 +112,6 @@ For an existing worker that should restart with a blank provider context, the le
112
112
  team-agent reset-agent "$AGENT_ID" --workspace "$WORKSPACE" --team "$TEAM" --discard-session --json
113
113
  ```
114
114
 
115
- or the equivalent restart form:
116
-
117
- ```bash
118
- team-agent restart-agent "$AGENT_ID" --workspace "$WORKSPACE" --team "$TEAM" --discard-session --json
119
- ```
120
-
121
115
  Use this only when the user intentionally wants a clean context. For incident recovery, follow `recovery-runbook.md`, where context-preserving repair comes first.
122
116
 
123
117
  ## Framework Gaps To Track