@team-agent/installer 0.5.106 → 0.5.107

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 (29) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/tests/base.rs +112 -225
  4. package/crates/team-agent/src/compiler/tests.rs +46 -59
  5. package/crates/team-agent/src/leader/tests/byte_findings.rs +1 -14
  6. package/crates/team-agent/src/lifecycle/launch/pi_mcp.rs +57 -173
  7. package/crates/team-agent/src/lifecycle/launch/pi_mcp_extension.js +226 -0
  8. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +0 -37
  9. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +0 -9
  10. package/crates/team-agent/src/lifecycle/tests/fixtures/pi_native_mcp_host.mjs +71 -0
  11. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +0 -66
  12. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +0 -392
  13. package/crates/team-agent/src/lifecycle/tests/lifecycle_lock.rs +0 -5
  14. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +2 -361
  15. package/crates/team-agent/src/lifecycle/tests/pi_dual_entry_red.rs +1 -16
  16. package/crates/team-agent/src/lifecycle/tests/pi_executable_mcp_red.rs +20 -112
  17. package/crates/team-agent/src/lifecycle/tests/pi_fork_session_inheritance_red.rs +1 -12
  18. package/crates/team-agent/src/lifecycle/tests/pi_native_mcp_capability_red.rs +546 -0
  19. package/crates/team-agent/src/lifecycle/tests.rs +1 -0
  20. package/crates/team-agent/src/lifecycle/worker_command_context.rs +11 -54
  21. package/crates/team-agent/src/messaging/tests/wave2.rs +2 -302
  22. package/crates/team-agent/src/model/name_similarity.rs +52 -67
  23. package/crates/team-agent/src/model/task_graph.rs +37 -124
  24. package/crates/team-agent/src/model/yaml/tests.rs +46 -165
  25. package/crates/team-agent/src/provider/tests/adapter.rs +0 -20
  26. package/crates/team-agent/src/provider/tests/classify.rs +106 -198
  27. package/crates/team-agent/src/tmux_backend/tests.rs +178 -501
  28. package/crates/team-agent/src/transport/tests/wire.rs +0 -55
  29. package/package.json +4 -4
@@ -0,0 +1,71 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { pathToFileURL } from "node:url";
3
+
4
+ const [wrapperPath, receiptPath, cwd] = process.argv.slice(2);
5
+ if (!wrapperPath || !receiptPath || !cwd) throw new Error("expected wrapper, receipt, and cwd");
6
+
7
+ const registrations = [];
8
+ const unregistrations = [];
9
+ const handlers = new Map();
10
+ const extensionCommands = [{
11
+ name: "mcp",
12
+ source: "extension",
13
+ sourceInfo: { path: "builtin:mcp", source: "builtin", scope: "user", origin: "top-level" },
14
+ }];
15
+ const pi = {
16
+ on(event, handler) {
17
+ const list = handlers.get(event) ?? [];
18
+ list.push(handler);
19
+ handlers.set(event, list);
20
+ },
21
+ getCommands() {
22
+ return extensionCommands;
23
+ },
24
+ getMcpServers() {
25
+ return registrations.map(({ name, config }) => ({ name, config, extensionPath: "<native-n1-fixture>" }));
26
+ },
27
+ getAllTools() {
28
+ return registrations.flatMap(({ name, config }) => Object.entries(config.toolExposure ?? {})
29
+ .map(([tool, exposure]) => ({ name: `mcp__${name}__${tool}`, namespace: { name: `mcp__${name}` }, exposure })));
30
+ },
31
+ getActiveTools() {
32
+ return this.getAllTools().filter((tool) => tool.exposure !== "hidden").map((tool) => tool.name);
33
+ },
34
+ registerMcpServer(name, config) {
35
+ registrations.push({ name, config });
36
+ },
37
+ unregisterMcpServer(name) {
38
+ unregistrations.push(name);
39
+ },
40
+ };
41
+
42
+ const extension = await import(pathToFileURL(wrapperPath).href);
43
+ if (typeof extension.default !== "function") throw new Error("generated wrapper has no default extension factory");
44
+ await extension.default(pi);
45
+ const context = {
46
+ cwd,
47
+ sessionManager: {
48
+ getSessionFile: () => undefined,
49
+ getLeafId: () => undefined,
50
+ getEntries: () => [],
51
+ },
52
+ };
53
+ for (const handler of handlers.get("session_start") ?? []) {
54
+ await handler({ type: "session_start" }, context);
55
+ }
56
+ if (registrations.length !== 1) throw new Error(`expected one native MCP registration, saw ${registrations.length}`);
57
+ const expectedTools = Object.keys(registrations[0].config.toolExposure ?? {})
58
+ .map((tool) => `mcp__${registrations[0].name}__${tool}`);
59
+ const deadline = Date.now() + 3_000;
60
+ while (!expectedTools.every((name) => pi.getActiveTools().includes(name))) {
61
+ if (Date.now() >= deadline) throw new Error(`native MCP tools never became active: ${pi.getActiveTools()}`);
62
+ await new Promise((resolve) => setTimeout(resolve, 10));
63
+ }
64
+ const beforeAgentStart = handlers.get("before_agent_start")?.[0];
65
+ if (typeof beforeAgentStart !== "function") throw new Error("generated wrapper omitted before_agent_start wire binding");
66
+ const binding = await beforeAgentStart({ systemPrompt: "N1 fixture" }, context);
67
+ const wirePrompt = binding?.systemPrompt;
68
+ if (typeof wirePrompt !== "string" || !expectedTools.every((name) => wirePrompt.includes(name))) {
69
+ throw new Error(`native Team tool wire binding is incomplete: ${wirePrompt}`);
70
+ }
71
+ await writeFile(receiptPath, `${JSON.stringify({ registrations, unregistrations, activeTools: pi.getActiveTools(), expectedTools, wirePrompt }, null, 2)}\n`, "utf8");
@@ -1321,33 +1321,6 @@ fn lanea_stop_window_present_kills_and_stopped_true() {
1321
1321
  );
1322
1322
  }
1323
1323
 
1324
- // ── STOP #2 (stop-display-noop-2) [RED] — close ghostty_workspace slot: persist display.status/pane_title
1325
- // Golden operations.py:88-92 -> display/close.py:84-85 relabels the slot: display["status"]="stopped",
1326
- // display["pane_title"]=f"stopped: {agent_id}", written back into the persisted agent entry. Rust never
1327
- // touches the display (mark_agent_stopped leaves it as-is) and hardcodes display_closed:false. RED: the
1328
- // persisted display.status/pane_title are the in-process observable.
1329
- #[test]
1330
- #[ignore = "legacy external display contract removed; runtime is silent tmux only"]
1331
- fn lanea_stop_ghostty_workspace_relabels_slot_to_stopped() {
1332
- let ws = lanea_ws_agents(json!({
1333
- "alpha": { "status": "running", "provider": "codex", "window": "alpha",
1334
- "display": { "backend": "ghostty_workspace", "pane_id": "%5", "linked_session": "disp-alpha", "status": "running", "pane_title": "alpha" } }
1335
- }));
1336
- let tx = LaneTransport::new("team-laneateam", &["alpha"]);
1337
- let _ = stop_agent_with_transport(&ws, &aid("alpha"), None, &tx).expect("stop ok");
1338
- let state = crate::state::persist::load_runtime_state(&ws).expect("load state");
1339
- assert_eq!(
1340
- state.pointer("/agents/alpha/display/status").and_then(serde_json::Value::as_str),
1341
- Some("stopped"),
1342
- "stop must relabel a ghostty_workspace slot: display.status='stopped' (close.py:84); Rust leaves it 'running'"
1343
- );
1344
- assert_eq!(
1345
- state.pointer("/agents/alpha/display/pane_title").and_then(serde_json::Value::as_str),
1346
- Some("stopped: alpha"),
1347
- "stop must set display.pane_title='stopped: <id>' (close.py:85); Rust never touches the display"
1348
- );
1349
- }
1350
-
1351
1324
  // ── RESET #3 (reset-paused-restart-2) [RED] — reset of a PAUSED agent returns ok=true (NOT an Err) ───
1352
1325
  // Golden operations.py:126-140: after discard, reset re-spawns via start_agent(force,allow_fresh).
1353
1326
  // discard does NOT clear `paused`, so start_agent returns the refusal-shaped {ok:False,status:paused,
@@ -1851,42 +1824,3 @@ fn fresh_start_persists_boundary_from_before_transport_spawn() {
1851
1824
  spawned_at={spawned_at}, transport_observed_at={transport_observed_at}"
1852
1825
  );
1853
1826
  }
1854
-
1855
- // ── REMOVE #6/#12 (remove-rollback-no-agent-health-3 / remove-rollback-health-1) [SEAM #[ignore]] ────
1856
- // Golden _RemoveRollback captures `self.health = copy.deepcopy(store.agent_health().get(agent_id))`
1857
- // (agents.py:185) and restore() re-upserts it via _restore_agent_health (agents.py:215-218,268-278). The
1858
- // Rust RemoveRollback has NO health field and never restores it (restart.rs:972-1067). This is only
1859
- // observable when a step AFTER the agent_health delete fails — but Rust's only post-delete step is the
1860
- // snapshot, which golden runs OUTSIDE the rollback-protected region (agents.py:135). Exercising it
1861
- // golden-faithfully needs a production failure-injection seam at an in-try step after the delete (mirror
1862
- // the coordinator SaveHook). PORTER: add `health: Option<Value>` to RemoveRollback (capture the row
1863
- // before delete; restore re-upserts status||"IDLE"/last_output_at/context_usage_pct/current_task_id, or
1864
- // deletes if None) AND move save_team_runtime_snapshot OUTSIDE the rollback region (golden agents.py:135).
1865
- #[test]
1866
- #[ignore = "seam: agent_health rollback restore needs a failure-injection hook (post-delete, in-try) to \
1867
- exercise in-process; golden agents.py:185/215-218/268-278. Porter adds RemoveRollback.health + \
1868
- moves save_team_runtime_snapshot outside the rollback region."]
1869
- fn lanea_remove_rollback_restores_agent_health() {
1870
- // Golden contract (verified by reading agents.py): on a mid-remove failure after the agent_health
1871
- // row is deleted, rollback re-upserts the captured row so the health history is not lost.
1872
- }
1873
-
1874
- // ── FORK (fork-incomplete-rollback) [SEAM #[ignore]] — post-spawn rollback arms ─────────────────────
1875
- // Golden operations.py:384-394 wraps spec-mutation..start_coordinator in try/except; on ANY failure it
1876
- // (1) kills the spawned tmux window if present, (2) adapter.cleanup_mcp, (3) restores old spec text, and
1877
- // (4) restores prior state. Rust only restores the spec on the spawn_into arm (launch.rs:481); the
1878
- // save_runtime_state (486-487) and start_coordinator (488-493) failure arms leave the spec mutated, the
1879
- // already-spawned window un-killed, and the state un-rolled-back; install_mcp/cleanup_mcp are absent.
1880
- // The adapter.fork_plan arm IS covered HARD above (lanea_fork_gate_error_text_and_spec_rollback_on_adapter_arm).
1881
- // The post-SPAWN arms need a failure-injection seam after spawn_into (codex+subscription forks past
1882
- // adapter.fork_plan, so the spawn succeeds and there is no in-process way to fail save/coordinator cleanly).
1883
- // PORTER: a Drop guard armed after the spec write, disarmed on success — kills the window, restores spec
1884
- // + state, runs cleanup_mcp on every post-write error arm.
1885
- #[test]
1886
- #[ignore = "seam: fork post-spawn rollback arms (save_runtime_state / start_coordinator failure) need a \
1887
- failure-injection hook after spawn_into; golden operations.py:384-394. Porter wires a Drop \
1888
- guard (kill window + restore spec/state + cleanup_mcp) armed after the spec write."]
1889
- fn lanea_fork_rollback_complete_on_post_spawn_failure() {
1890
- // Golden contract (operations.py:384-394): a post-spawn failure kills the spawned window, restores
1891
- // the old spec text + prior state, and runs cleanup_mcp before re-raising.
1892
- }
@@ -2800,130 +2800,6 @@ fn fork_refuses_source_with_partial_tuple() {
2800
2800
  );
2801
2801
  }
2802
2802
 
2803
- #[test]
2804
- #[ignore = "legacy adaptive display layout contract removed; runtime is silent tmux only"]
2805
- fn add_agent_adaptive_splits_last_non_full_layout_window() {
2806
- let roles = [("w1.md", role_doc("w1")), ("w2.md", role_doc("w2"))];
2807
- let role_refs = roles
2808
- .iter()
2809
- .map(|(file, doc)| (*file, doc.as_str()))
2810
- .collect::<Vec<_>>();
2811
- let team = quick_start_team_dir_with_roles(&role_refs);
2812
- let session = "team-layout-add";
2813
- crate::state::persist::save_runtime_state(
2814
- &team,
2815
- &json!({
2816
- "session_name": session,
2817
- "legacy_display_key": "adaptive",
2818
- "active_team_key": "teamdir",
2819
- "agents": {
2820
- "w1": {"status": "running", "provider": "codex", "window": "team-w1", "layout_window": "team-w1", "layout_index": 0, "pane_index": 0, "pane_id": "%1"},
2821
- "w2": {"status": "running", "provider": "codex", "window": "team-w1", "layout_window": "team-w1", "layout_index": 0, "pane_index": 1, "pane_id": "%2"}
2822
- }
2823
- }),
2824
- )
2825
- .unwrap();
2826
- seed_healthy_coordinator(&team);
2827
- let role_file = team.join("w3-role.md");
2828
- std::fs::write(&role_file, role_doc("w3")).unwrap();
2829
- let transport = OfflineTransport::new()
2830
- .with_session_present(true)
2831
- .with_targets(vec![
2832
- layout_pane(session, "team-w1", "%1", 0),
2833
- layout_pane(session, "team-w1", "%2", 1),
2834
- ])
2835
- .with_pane_presence("%1", true)
2836
- .with_pane_presence("%2", true);
2837
-
2838
- add_agent_with_transport(
2839
- &team,
2840
- &AgentId::new("w3"),
2841
- &role_file,
2842
- true,
2843
- None,
2844
- &transport,
2845
- )
2846
- .expect("adaptive add-agent should start");
2847
-
2848
- assert_eq!(
2849
- transport.spawn_window_records().last(),
2850
- Some(&(String::from("spawn_split"), String::from("team-w1"))),
2851
- "last non-full layout window should be split"
2852
- );
2853
- let (_raw, state) = raw_runtime_state(&team);
2854
- assert_eq!(
2855
- state.pointer("/agents/w3/layout_window"),
2856
- Some(&json!("team-w1"))
2857
- );
2858
- assert_eq!(state.pointer("/agents/w3/pane_index"), Some(&json!(2)));
2859
- }
2860
-
2861
- #[test]
2862
- #[ignore = "legacy adaptive display layout contract removed; runtime is silent tmux only"]
2863
- fn add_agent_adaptive_creates_suffix_window_when_last_layout_full_and_name_collides() {
2864
- let roles = [
2865
- ("w1.md", role_doc("w1")),
2866
- ("w2.md", role_doc("w2")),
2867
- ("w3.md", role_doc("w3")),
2868
- ];
2869
- let role_refs = roles
2870
- .iter()
2871
- .map(|(file, doc)| (*file, doc.as_str()))
2872
- .collect::<Vec<_>>();
2873
- let team = quick_start_team_dir_with_roles(&role_refs);
2874
- let session = "team-layout-add-full";
2875
- crate::state::persist::save_runtime_state(
2876
- &team,
2877
- &json!({
2878
- "session_name": session,
2879
- "legacy_display_key": "adaptive",
2880
- "active_team_key": "teamdir",
2881
- "agents": {
2882
- "w1": {"status": "running", "provider": "codex", "window": "team-w1", "layout_window": "team-w1", "layout_index": 0, "pane_index": 0, "pane_id": "%1"},
2883
- "w2": {"status": "running", "provider": "codex", "window": "team-w1", "layout_window": "team-w1", "layout_index": 0, "pane_index": 1, "pane_id": "%2"},
2884
- "w3": {"status": "running", "provider": "codex", "window": "team-w1", "layout_window": "team-w1", "layout_index": 0, "pane_index": 2, "pane_id": "%3"}
2885
- }
2886
- }),
2887
- )
2888
- .unwrap();
2889
- seed_healthy_coordinator(&team);
2890
- let role_file = team.join("w4-role.md");
2891
- std::fs::write(&role_file, role_doc("w4")).unwrap();
2892
- let transport = OfflineTransport::new()
2893
- .with_session_present(true)
2894
- .with_targets(vec![
2895
- layout_pane(session, "team-w1", "%1", 0),
2896
- layout_pane(session, "team-w1", "%2", 1),
2897
- layout_pane(session, "team-w1", "%3", 2),
2898
- layout_pane(session, "team-w2", "%9", 0),
2899
- ])
2900
- .with_pane_presence("%1", true)
2901
- .with_pane_presence("%2", true)
2902
- .with_pane_presence("%3", true);
2903
-
2904
- add_agent_with_transport(
2905
- &team,
2906
- &AgentId::new("w4"),
2907
- &role_file,
2908
- true,
2909
- None,
2910
- &transport,
2911
- )
2912
- .expect("adaptive add-agent should start");
2913
-
2914
- assert_eq!(
2915
- transport.spawn_window_records().last(),
2916
- Some(&(String::from("spawn_into"), String::from("team-w2-2"))),
2917
- "full last layout window should create next window with collision suffix"
2918
- );
2919
- let (_raw, state) = raw_runtime_state(&team);
2920
- assert_eq!(
2921
- state.pointer("/agents/w4/layout_window"),
2922
- Some(&json!("team-w2-2"))
2923
- );
2924
- assert_eq!(state.pointer("/agents/w4/pane_index"), Some(&json!(0)));
2925
- }
2926
-
2927
2803
  #[test]
2928
2804
  fn stop_agent_adaptive_kills_target_pane_not_shared_layout_window() {
2929
2805
  let roles = [("w1.md", role_doc("w1")), ("w2.md", role_doc("w2"))];
@@ -2978,55 +2854,6 @@ fn stop_agent_adaptive_kills_target_pane_not_shared_layout_window() {
2978
2854
  );
2979
2855
  }
2980
2856
 
2981
- #[test]
2982
- #[ignore = "legacy adaptive display layout contract removed; runtime is silent tmux only"]
2983
- fn start_agent_adaptive_restarts_missing_pane_in_existing_layout_window() {
2984
- let ws = temp_ws();
2985
- crate::state::persist::save_runtime_state(
2986
- &ws,
2987
- &json!({
2988
- "session_name": "team-layout-restart",
2989
- "legacy_display_key": "adaptive",
2990
- "agents": {
2991
- "w1": {"status": "running", "provider": "codex", "role": "w1", "model": "gpt-5.5", "auth_mode": "subscription", "window": "team-w1", "layout_window": "team-w1", "layout_index": 0, "pane_index": 0, "pane_id": "%1"},
2992
- "w2": {"status": "running", "provider": "codex", "role": "w2", "model": "gpt-5.5", "auth_mode": "subscription", "window": "team-w1", "layout_window": "team-w1", "layout_index": 0, "pane_index": 1, "pane_id": "%2"}
2993
- }
2994
- }),
2995
- )
2996
- .unwrap();
2997
- seed_healthy_coordinator(&ws);
2998
- let transport = OfflineTransport::new()
2999
- .with_session_present(true)
3000
- .with_targets(vec![layout_pane("team-layout-restart", "team-w1", "%2", 1)])
3001
- .with_pane_presence("%1", false)
3002
- .with_pane_presence("%2", true);
3003
-
3004
- let outcome = start_agent_with_transport(
3005
- &ws,
3006
- &AgentId::new("w1"),
3007
- false,
3008
- true,
3009
- true,
3010
- None,
3011
- &transport,
3012
- )
3013
- .expect("adaptive restart should spawn");
3014
-
3015
- assert!(matches!(outcome, StartAgentOutcome::Running { .. }));
3016
- assert_eq!(
3017
- transport.spawn_window_records().last(),
3018
- Some(&(String::from("spawn_split"), String::from("team-w1"))),
3019
- "missing worker pane should be rebuilt in its existing layout window"
3020
- );
3021
- let (_raw, state) = raw_runtime_state(&ws);
3022
- assert_eq!(
3023
- state.pointer("/agents/w1/layout_window"),
3024
- Some(&json!("team-w1"))
3025
- );
3026
- assert_eq!(state.pointer("/agents/w1/window"), Some(&json!("team-w1")));
3027
- assert_eq!(state.pointer("/agents/w2/pane_id"), Some(&json!("%2")));
3028
- }
3029
-
3030
2857
  // ═════════════════════════════════════════════════════════════════════════
3031
2858
  // WAVE 2 · LANE A — agent-lifecycle byte-parity contracts. stop_agent / reset_agent / remove_agent /
3032
2859
  // fork_agent are stubs (OwnerRefused / RequirementUnmet / "session_id missing"). These RED contracts
@@ -3714,175 +3541,6 @@ fn e43_adaptive_placement_skips_existing_agent_whose_live_pane_window_name_diffe
3714
3541
  }
3715
3542
  }
3716
3543
 
3717
- /// **Regression guard — Fix A**: when an existing agent's live pane DOES
3718
- /// reside under the claimed layout_window, placement keeps the prior
3719
- /// behaviour (groups the new agent into that window). This is the canonical
3720
- /// adaptive 1-window-multiple-panes use case.
3721
- #[test]
3722
- #[ignore = "legacy adaptive display layout contract removed; runtime is silent tmux only"]
3723
- fn e43_adaptive_placement_groups_into_layout_window_when_live_pane_matches_claim() {
3724
- use crate::lifecycle::launch::adaptive_placement_for_agent;
3725
- use crate::model::ids::AgentId;
3726
- use crate::transport::{SessionName, WindowName};
3727
-
3728
- let state = json!({
3729
- "legacy_display_key": "adaptive",
3730
- "session_name": "team-iso",
3731
- "agents": {
3732
- "alpha": {
3733
- "status": "running",
3734
- "provider": "codex",
3735
- "layout_window": "team-w1",
3736
- "pane_id": "%1",
3737
- }
3738
- }
3739
- });
3740
-
3741
- let transport = OfflineTransport::new()
3742
- .with_targets(vec![layout_pane("team-iso", "team-w1", "%1", 0)])
3743
- .with_windows(vec![WindowName::new("team-w1")]);
3744
-
3745
- let placement = adaptive_placement_for_agent(
3746
- &state,
3747
- &transport,
3748
- &SessionName::new("team-iso"),
3749
- &AgentId::new("beta"),
3750
- )
3751
- .expect("placement should succeed");
3752
-
3753
- assert_eq!(
3754
- placement.layout_window.as_str(),
3755
- "team-w1",
3756
- "E43 Fix A regression guard: when alpha's live pane DOES live under \
3757
- layout_window 'team-w1', the new agent beta should be grouped into \
3758
- the same window (canonical adaptive behaviour). Got {placement:?}"
3759
- );
3760
- assert!(
3761
- !placement.starts_window,
3762
- "E43 Fix A regression guard: grouping into an existing window means \
3763
- starts_window=false (split into the window). Got {placement:?}"
3764
- );
3765
- }
3766
-
3767
- /// **RED — Fix B**: `adaptive_existing_placement_for_agent` must NOT return a
3768
- /// `starts_window=true` placement that names a window NOT present in
3769
- /// `live_windows`. When the agent's claimed `layout_window` is stale and the
3770
- /// session is effectively per-agent (live windows are named after agent_ids),
3771
- /// the placement must fall back to a per-agent window named after the
3772
- /// agent_id itself — so the subsequent spawn opens a new window, not a split
3773
- /// of a phantom.
3774
- #[test]
3775
- #[ignore = "legacy adaptive display layout contract removed; runtime is silent tmux only"]
3776
- fn e43_adaptive_existing_placement_falls_back_to_agent_id_window_when_claim_missing_from_live() {
3777
- use crate::lifecycle::launch::adaptive_existing_placement_for_agent;
3778
- use crate::model::ids::AgentId;
3779
- use crate::transport::{SessionName, WindowName};
3780
-
3781
- let state = json!({
3782
- "legacy_display_key": "adaptive",
3783
- "session_name": "team-iso",
3784
- "agents": {
3785
- "demo-director": {
3786
- "status": "running",
3787
- "provider": "codex",
3788
- // Stale claim — live session has no team-w2 window.
3789
- "layout_window": "team-w2",
3790
- "pane_id": "%99",
3791
- }
3792
- }
3793
- });
3794
-
3795
- let transport = OfflineTransport::new()
3796
- .with_targets(vec![
3797
- layout_pane("team-iso", "architect", "%1", 0),
3798
- layout_pane("team-iso", "test-engineer", "%2", 0),
3799
- ])
3800
- .with_windows(vec![
3801
- WindowName::new("architect"),
3802
- WindowName::new("test-engineer"),
3803
- ]);
3804
-
3805
- let placement = adaptive_existing_placement_for_agent(
3806
- &state,
3807
- &transport,
3808
- &SessionName::new("team-iso"),
3809
- &AgentId::new("demo-director"),
3810
- )
3811
- .expect("placement should resolve");
3812
-
3813
- assert_ne!(
3814
- placement.layout_window.as_str(),
3815
- "team-w2",
3816
- "E43 Fix B: stale claim 'team-w2' is not in live_windows; placement \
3817
- must NOT reuse that name (otherwise spawn issues split -t :team-w2 \
3818
- and tmux says 'can't find window: team-w2'). Got {placement:?}"
3819
- );
3820
- assert_eq!(
3821
- placement.layout_window.as_str(),
3822
- "demo-director",
3823
- "E43 Fix B: when the claim is stale and the live layout is \
3824
- per-agent-named, fall back to a window named after the agent_id \
3825
- (canonical per-agent pattern). Got {placement:?}"
3826
- );
3827
- assert!(
3828
- placement.starts_window,
3829
- "E43 Fix B: the fallback opens a NEW window (starts_window=true); \
3830
- do not try to split an existing window with the wrong name. Got \
3831
- {placement:?}"
3832
- );
3833
- }
3834
-
3835
- /// **Regression guard — Fix B**: when the agent's claimed layout_window IS
3836
- /// in live_windows, the existing-placement path keeps its current behaviour
3837
- /// (return a starts_window=false placement that splits into the existing
3838
- /// window).
3839
- #[test]
3840
- #[ignore = "legacy adaptive display layout contract removed; runtime is silent tmux only"]
3841
- fn e43_adaptive_existing_placement_keeps_starts_window_false_when_claim_in_live_windows() {
3842
- use crate::lifecycle::launch::adaptive_existing_placement_for_agent;
3843
- use crate::model::ids::AgentId;
3844
- use crate::transport::{SessionName, WindowName};
3845
-
3846
- let state = json!({
3847
- "legacy_display_key": "adaptive",
3848
- "session_name": "team-iso",
3849
- "agents": {
3850
- "alpha": {
3851
- "status": "running",
3852
- "provider": "codex",
3853
- "layout_window": "team-w1",
3854
- "pane_id": "%1",
3855
- "pane_index": 1u64,
3856
- }
3857
- }
3858
- });
3859
-
3860
- let transport = OfflineTransport::new()
3861
- .with_targets(vec![layout_pane("team-iso", "team-w1", "%1", 0)])
3862
- .with_windows(vec![WindowName::new("team-w1")]);
3863
-
3864
- let placement = adaptive_existing_placement_for_agent(
3865
- &state,
3866
- &transport,
3867
- &SessionName::new("team-iso"),
3868
- &AgentId::new("alpha"),
3869
- )
3870
- .expect("placement should resolve");
3871
-
3872
- assert_eq!(
3873
- placement.layout_window.as_str(),
3874
- "team-w1",
3875
- "E43 Fix B regression guard: when claim 'team-w1' IS in live_windows, \
3876
- existing-placement must keep it. Got {placement:?}"
3877
- );
3878
- // pane_index=1 → starts_window=false in the existing behaviour.
3879
- assert!(
3880
- !placement.starts_window,
3881
- "E43 Fix B regression guard: existing window + pane_index>0 → \
3882
- starts_window=false. Got {placement:?}"
3883
- );
3884
- }
3885
-
3886
3544
  /// **RED — Fix C end-to-end**: `add_agent` end-to-end must NOT emit a
3887
3545
  /// spawn_split with the stale window name. With state carrying
3888
3546
  /// `legacy_display_key=adaptive` + stale `layout_window=team-w2` residue and
@@ -4178,56 +3836,6 @@ fn e45_add_agent_opens_new_window_does_not_split_into_per_agent_window() {
4178
3836
  );
4179
3837
  }
4180
3838
 
4181
- /// **Regression guard**: when the live session DOES have a real `team-w1`
4182
- /// adaptive window with a live pane that matches state's claim, the
4183
- /// existing E43+adaptive grouping behaviour is preserved (split into team-w1
4184
- /// as before). E45 only changes behaviour for per-agent windows.
4185
- #[test]
4186
- #[ignore = "legacy adaptive display layout contract removed; runtime is silent tmux only"]
4187
- fn e45_real_team_w_adaptive_layout_still_groups_into_layout_window() {
4188
- use crate::lifecycle::launch::adaptive_placement_for_agent;
4189
- use crate::model::ids::AgentId;
4190
- use crate::transport::{SessionName, WindowName};
4191
-
4192
- let state = json!({
4193
- "legacy_display_key": "adaptive",
4194
- "session_name": "team-iso",
4195
- "agents": {
4196
- "alpha": {
4197
- "status": "running",
4198
- "provider": "codex",
4199
- "layout_window": "team-w1",
4200
- "layout_index": 0u64,
4201
- "pane_id": "%1",
4202
- }
4203
- }
4204
- });
4205
-
4206
- let transport = OfflineTransport::new()
4207
- .with_targets(vec![layout_pane("team-iso", "team-w1", "%1", 0)])
4208
- .with_windows(vec![WindowName::new("team-w1")]);
4209
-
4210
- let placement = adaptive_placement_for_agent(
4211
- &state,
4212
- &transport,
4213
- &SessionName::new("team-iso"),
4214
- &AgentId::new("beta"),
4215
- )
4216
- .expect("E45 regression guard: real team-w1 adaptive layout must still group new agents in");
4217
-
4218
- assert_eq!(
4219
- placement.layout_window.as_str(),
4220
- "team-w1",
4221
- "E45 regression guard: real `team-w1` adaptive window must still \
4222
- host new agents (canonical adaptive behaviour). Got {placement:?}"
4223
- );
4224
- assert!(
4225
- !placement.starts_window,
4226
- "E45 regression guard: grouping into existing team-w1 → \
4227
- starts_window=false. Got {placement:?}"
4228
- );
4229
- }
4230
-
4231
3839
  /// **Regression guard**: helper `is_adaptive_layout_window` golden cases.
4232
3840
  /// `team-w1`, `team-w42`, `team-w7-foo` are adaptive; agent_id names and
4233
3841
  /// other shapes are not.
@@ -130,11 +130,6 @@ fn lifecycle_lock_long_held_event_records_holder_and_waiter() {
130
130
  );
131
131
  }
132
132
 
133
- #[test]
134
- fn lifecycle_lock_phase_b_golden_source_guard() {
135
- r2_lifecycle_lock_exists_precondition();
136
- }
137
-
138
133
  #[test]
139
134
  fn r2_lifecycle_lock_exists_precondition() {
140
135
  let src = manifest_src();