@team-agent/installer 0.5.26 → 0.5.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.lock CHANGED
@@ -575,7 +575,7 @@ dependencies = [
575
575
 
576
576
  [[package]]
577
577
  name = "team-agent"
578
- version = "0.5.26"
578
+ version = "0.5.27"
579
579
  dependencies = [
580
580
  "anyhow",
581
581
  "chrono",
package/Cargo.toml CHANGED
@@ -9,7 +9,7 @@ members = ["crates/team-agent", "crates/win-conpty-phase0", "crates/conpty-trans
9
9
 
10
10
  [workspace.package]
11
11
  edition = "2021"
12
- version = "0.5.26"
12
+ version = "0.5.27"
13
13
  license = "AGPL-3.0"
14
14
  rust-version = "1.95"
15
15
 
@@ -3463,9 +3463,16 @@ pub mod lifecycle_port {
3463
3463
  out
3464
3464
  }
3465
3465
 
3466
- /// 0.5.26 (`.team/artifacts/stale-team-saveconflict-locate.md` §7.2):
3467
- /// scoped shutdown 只有一队,`state.active_team_key` 指向被关的那队。
3466
+ /// 0.5.26 (`.team/artifacts/stale-team-saveconflict-locate.md` §7.2) /
3467
+ /// 0.5.27 (`.team/artifacts/0526-supermarket-shutdown-status-real-fail-locate.md` §5.1):
3468
+ /// scoped shutdown 只有一队,`state.active_team_key` 指向被关的那队;
3468
3469
  /// 只在 session_killed && !verification_degraded 时被调用。
3470
+ ///
3471
+ /// 同时 stamp 顶层 `state["status"]` 与 `teams[active_key]["status"]`:
3472
+ /// scoped 关队走 `save_team_scoped_state`,反写盘的源是 `compact(top-level)`
3473
+ /// (见 `state/persist.rs::save_team_scoped_state` +
3474
+ /// `projection::compact_team_state`),只标 nested 会被顶层视图 clobber 丢盘。
3475
+ /// 保留 nested 写入以维持同 tick 内的 in-memory 一致(其他 read-back 立即看到)。
3469
3476
  fn mark_active_team_shutdown(state: &mut Value, status: &str) {
3470
3477
  let Some(active_key) = state
3471
3478
  .get("active_team_key")
@@ -3474,6 +3481,9 @@ pub mod lifecycle_port {
3474
3481
  else {
3475
3482
  return;
3476
3483
  };
3484
+ if let Some(obj) = state.as_object_mut() {
3485
+ obj.insert("status".to_string(), json!(status));
3486
+ }
3477
3487
  let Some(teams) = state.get_mut("teams").and_then(Value::as_object_mut) else {
3478
3488
  return;
3479
3489
  };
@@ -331,6 +331,129 @@ fn scoped_shutdown_keeps_owned_endpoint_when_sibling_session_remains() {
331
331
  );
332
332
  }
333
333
 
334
+ #[test]
335
+ fn scoped_clean_shutdown_persists_team_shutdown_status_after_disk_roundtrip() {
336
+ let ws = tmp_shutdown_workspace("scoped-clean-status-roundtrip");
337
+ crate::state::persist::save_runtime_state(
338
+ &ws,
339
+ &json!({
340
+ "schema_version": 1,
341
+ "active_team_key": "current",
342
+ "team_key": "current",
343
+ "session_name": "team-current",
344
+ "team_dir": ws.display().to_string(),
345
+ "workspace": ws.display().to_string(),
346
+ "agents": {
347
+ "adminweb": {
348
+ "agent_id": "adminweb",
349
+ "status": "running",
350
+ "provider": "fake",
351
+ "window": "adminweb"
352
+ }
353
+ },
354
+ "teams": {
355
+ "current": {
356
+ "team_key": "current",
357
+ "session_name": "team-current",
358
+ "team_dir": ws.display().to_string(),
359
+ "workspace": ws.display().to_string(),
360
+ "agents": {
361
+ "adminweb": {
362
+ "agent_id": "adminweb",
363
+ "status": "running",
364
+ "provider": "fake",
365
+ "window": "adminweb"
366
+ }
367
+ }
368
+ }
369
+ }
370
+ }),
371
+ )
372
+ .unwrap();
373
+
374
+ let out = crate::cli::lifecycle_port::shutdown_with_transport(
375
+ &ws,
376
+ true,
377
+ Some("current"),
378
+ &CleanShutdownTransport::new(),
379
+ )
380
+ .expect("shutdown should complete");
381
+
382
+ assert_eq!(out["ok"], json!(true), "shutdown report: {out}");
383
+ assert_eq!(out["session_killed"], json!(true), "shutdown report: {out}");
384
+ assert_eq!(out["verification_degraded"], json!(false), "shutdown report: {out}");
385
+ let saved = crate::state::persist::load_runtime_state(&ws).unwrap();
386
+ assert_eq!(
387
+ saved.pointer("/teams/current/status").and_then(serde_json::Value::as_str),
388
+ Some("shutdown"),
389
+ "0.5.27 RED: clean scoped shutdown returned session_killed=true and not degraded, \
390
+ so disk state must persist teams.current.status=shutdown. The compact projected \
391
+ save path must not drop the tombstone; saved={saved}"
392
+ );
393
+ assert_eq!(
394
+ saved
395
+ .pointer("/teams/current/agents/adminweb/status")
396
+ .and_then(serde_json::Value::as_str),
397
+ Some("stopped"),
398
+ "0.5.27 RED: clean shutdown must also persist stopped agents under the retained team; saved={saved}"
399
+ );
400
+ }
401
+
402
+ #[test]
403
+ fn scoped_degraded_shutdown_does_not_persist_team_shutdown_status() {
404
+ let ws = tmp_shutdown_workspace("scoped-degraded-no-status");
405
+ crate::state::persist::save_runtime_state(
406
+ &ws,
407
+ &json!({
408
+ "schema_version": 1,
409
+ "active_team_key": "current",
410
+ "team_key": "current",
411
+ "session_name": "team-current",
412
+ "agents": {
413
+ "adminweb": {
414
+ "agent_id": "adminweb",
415
+ "status": "running",
416
+ "provider": "fake",
417
+ "window": "adminweb"
418
+ }
419
+ },
420
+ "teams": {
421
+ "current": {
422
+ "team_key": "current",
423
+ "session_name": "team-current",
424
+ "agents": {
425
+ "adminweb": {
426
+ "agent_id": "adminweb",
427
+ "status": "running",
428
+ "provider": "fake",
429
+ "window": "adminweb"
430
+ }
431
+ }
432
+ }
433
+ }
434
+ }),
435
+ )
436
+ .unwrap();
437
+
438
+ let out = crate::cli::lifecycle_port::shutdown_with_transport(
439
+ &ws,
440
+ true,
441
+ Some("current"),
442
+ &CleanShutdownTransport::new().with_probe_timeout("ps_table"),
443
+ )
444
+ .expect("shutdown should complete");
445
+
446
+ assert_eq!(out["session_killed"], json!(true), "shutdown report: {out}");
447
+ assert_eq!(out["verification_degraded"], json!(true), "shutdown report: {out}");
448
+ let saved = crate::state::persist::load_runtime_state(&ws).unwrap();
449
+ assert_ne!(
450
+ saved.pointer("/teams/current/status").and_then(serde_json::Value::as_str),
451
+ Some("shutdown"),
452
+ "degraded shutdown must remain dirty/diagnostic and must not stamp \
453
+ teams.current.status=shutdown; saved={saved}"
454
+ );
455
+ }
456
+
334
457
  #[test]
335
458
  fn repeated_owned_endpoint_shutdowns_leave_no_socket_file_growth() {
336
459
  let ws = tmp_shutdown_workspace("owned-loop-no-growth");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-agent/installer",
3
- "version": "0.5.26",
3
+ "version": "0.5.27",
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.26",
24
- "@team-agent/cli-darwin-x64": "0.5.26",
25
- "@team-agent/cli-linux-x64": "0.5.26"
23
+ "@team-agent/cli-darwin-arm64": "0.5.27",
24
+ "@team-agent/cli-darwin-x64": "0.5.27",
25
+ "@team-agent/cli-linux-x64": "0.5.27"
26
26
  },
27
27
  "scripts": {
28
28
  "postinstall": "node npm/bincheck.mjs",