cmux NEXT: an agent-opened session workspace records its lineage parent

Merged
TE
7d ago
+674-65

Status: done. Codex xhigh SHIP at 1a617560; fleet compile-check --app passed at 1a617560; fleet suites AgentSessionLineageTests (8 tests), SidebarSessionTreeTests and RemoteChildSessionsTests all pass (run on 1a617560 on top of base 7d50820b, because base's SidebarNowLineTests.swift has conflict markers that stop every CmuxNext test build).

Leo: "LIKE E.G. AGENT SESSION CREATION LINEAGES... CLAUDE CODE MOSTLY JUST HAS ONE LAYER... WHAT IF WE DID MORE? OR DIFFERENT?"

Leo-ask: ask-19882abbc3

Session lineage, PR 2 of the plan (app: an agent-opened session workspace records its parent).

What changes

  • agent.openSessionWorkspace (cmux agent open, acpmux new --group, the MCP open tools) passes the invocation's callerAgentSession through to open(...). Once the chat tab opens, it records that caller as the opened session's lineage parent in a new AgentSessionParents store (child session -> parent session, kept in UserDefaults under cmux.next.agentSession.lineageParents, capped at 4096, oldest dropped). A person's open (no caller), a blank caller, or a session naming itself records nothing.
  • The re-homed-lane reuse path (a tab in the predecessor's row) records the parent too.
  • SidebarSessionTreeFeed.pass reads the recorded parents as a fallback, applied last and never where it would close a loop: a workspace gets its recorded parent only when neither the remote daemon's startedBy/spawnedBy (RemoteAgentSessionLinks) nor this Mac's lane spawnedBy names one. Daemon lineage always wins.
  • The field is named parent to match the acpmux lineage summary field (parent/root/depth) another lane is adding; this PR does not depend on it.

No default changes. The tree itself stays behind sidebar.sessionTree (off by default). No sidebar layout or fold changes (#21836 owns those; this touches only pass(...)'s parents map, not the agentStates lines #21836 adds there).

Tests (test-first)

CmuxNextAppTests.AgentSessionLineageTests:

  • opening with a caller session and no daemon lineage (local older daemon, and remote) nests the workspace under the caller's;
  • a person's open records nothing;
  • daemon lineage (local spawnedBy, remote startedBy) wins over the recorded parent;
  • recorded parents survive a relaunch and the newest record wins;
  • a record that would close a loop is refused, in the store and against the daemon's lineage in the tree.

No capture: records data only; the nested view stays behind sidebar.sessionTree until the nesting PR

šŸ¤– Generated with Claude Code

Agent session: cosmic-gray