test / unit + widget + golden + a11y (push) Failing after 31s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 31s
Findings note for the tmux agent-team work (epic T-132), validated from real on-disk artifacts (42 past team configs, real team + subagent transcripts, current-version .meta.json) plus a synthetic control-mode test and the tmux manual — no live team run / quota needed. Key results: teammates get tmux panes (config tmuxPaneId) and write transcripts at <munged>/<sid>/subagents/agent-<hex>.jsonl + .meta.json; tmux 3.6a has no %pane-died (use %window-close/%layout-change), and polling list-panes -a is the reliable lifecycle baseline; the real risk is identity linkage — pane/teammate (config: name@team, agentType, %pane) joins to the transcript only via agentType (config <-> .meta.json), ambiguous for same-type teammates, so T-139 needs a joinedAt/mtime or lead-transcript tiebreaker. Co-Authored-By: Claude <noreply@anthropic.com>
5.3 KiB
5.3 KiB
Spike: Claude Code team / transcript internals (T-134)
Pinned to: claude 2.1.148, tmux 3.6a. These are undocumented, version-drifting internal contracts (per D-75) — re-validate on a CC bump.
Method: validated from real on-disk artifacts (42 past team config.jsons, real
team + Task-tool subagent transcripts, .meta.json written by the current version) plus
a synthetic tmux -L clide control-mode test and the tmux manual. No live team run was
needed to answer the questions — the existing artifacts are conclusive and cost no quota.
Findings
1. Teammates get tmux panes; transcripts live under subagents/
- Confirmed: across real teams, 42 teammate members carry a populated
tmuxPaneId(e.g.%5,%120); the lead'stmuxPaneIdis"". So teammates spawn as panes and the config records the pane id. - Teammate transcript location:
~/.claude/projects/<munged-cwd>/<session-id>/subagents/agent-<hex>.jsonl, with a siblingagent-<hex>.meta.json.<munged-cwd> = absolutePath.replaceAll('/','-')(leading-kept). Note: a team's<session-id>dir held 0 top-level*.jsonland 44subagents/agent-*.jsonl— teammate content is the subagent files, not top-level sessions. - Each subagent record carries
agentId(the hex, e.g.a2a3530— matches the filename),sessionId(the dir),isSidechain: true,slug(a random codename),type(user/assistant/…). This is the same JSONL schemaTranscriptReader(T-136) already parses.
2. Lifecycle signal — control mode vs. polling
- tmux 3.6a control-mode notifications (from
man tmux):%window-add,%window-close,%window-pane-changed,%layout-change,%unlinked-window-add,%unlinked-window-close,%session-changed,%sessions-changed,%pane-mode-changed,%exit,%output/%extended-output, … There is NO%pane-died(an-idea.md assumed one). Pane/teammate exit surfaces via%window-close/%layout-change/%window-pane-changed. - Driving control mode from code is finicky: a
tmux -L clide -C attachcaptured%session-changed/%exitbut the attach exited early under non-interactive Bash; reliably consuming the stream needs a long-lived managed client. - Polling
tmux -L clide list-panes -a -F '...'works and is reliable (validated: it enumerated panes with pane-id/pid/title). Recommendation for T-139: use polling as the baseline lifecycle source; treat control mode as a later optimization.
3. Team config schema (~/.claude/teams/<team>/config.json)
- Top keys:
name,description,createdAt,leadAgentId,leadSessionId,members[]. - Member keys:
name,agentId(=<name>@<team>),agentType,model,cwd,joinedAt,subscriptions,tmuxPaneId. (Some runs also carrybackendType/isActive/mode— optional, version-varying.) The lead member has emptytmuxPaneId.
4. ⚠️ Identity linkage — the real risk for T-139
The pane/teammate identity in config does NOT share a key with the transcript file:
- Config:
{name: gestalt, agentId: gestalt@control-interaction, agentType: gestalt, tmuxPaneId: %120}. - Transcript:
agent-<hex>.jsonl(recordsagentId = <hex>,slug = <random>) +agent-<hex>.meta.json = {agentType, description}. - The only join key is
agentType(config.member.agentType ↔.meta.json.agentType). This is unambiguous only when teammates have distinct agentTypes (e.g.control-interaction: gestalt/ozzie/tyre/…). For same-type teammates (e.g.art-requirements: 3×general-purpose) agentType is ambiguous → need a disambiguator: spawn order /joinedAttimestamp vs. file mtime, or parse the lead transcript's teammate-spawn records (likely carry both ids). T-139 must handle this; recommend: join on agentType, fall back to ordering byjoinedAt/mtime, and investigate the lead transcript's spawn events for an explicit hex↔name link.
5. Resolved elsewhere
- Session-id discovery (check 5): Claude doesn't expose its session id; pick newest
*.jsonlby mtime — already implemented inTranscriptReader(T-136). - Paste (check 4): scope to
@pathfile references oversend-keys(text channel). Clipboard image paste needs an interactive display ($DISPLAY) and is out of scope for headless validation; decide the composer's image handling in T-138/T-006.
Recommendations for the team tickets
- T-139 (observer): poll
list-panes -afor lifecycle; read teamconfig.jsonfor the roster- pane ids; tail teammate transcripts at
<munged>/<sid>/subagents/agent-<hex>.jsonl; resolve pane→transcript viaagentTypejoin with ajoinedAt/mtime tiebreaker (and confirm whether the lead transcript gives an explicit link). Isolate all of this behind the one module (D-75).
- pane ids; tail teammate transcripts at
- Re-validate on any CC version bump — key off the transcript
versionfield.
Not done
- A live, real-time team run (watching a pane + transcript appear live) was not executed — the static artifacts answer every question and a live run costs quota without adding certainty. The one item a fresh run would pin precisely: whether the lead transcript records an explicit teammate hex↔name mapping (would remove the same-type ambiguity). Worth a short observed run when T-139 is implemented.