add tmux team observer + member lifecycle events (T-139, D-75)
test / unit + widget + golden + a11y (push) Failing after 27s
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 24s

team_observer.dart is the single drift-containment point for Claude
Code's experimental tmux team mode. It discovers the active team for a
workspace (~/.claude/teams/<team>/config.json, matched by member cwd),
polls `tmux -L clide list-panes -a`, and correlates live panes with the
config's tmuxPaneId to emit TeamMemberBorn / TeamMemberDied — identity
(name, agentType, model, colour, pane) comes from the config, so it's
reliable regardless of transcript drift.

Each teammate's subagent transcript is resolved best-effort and streamed
on a per-agent MessageBus channel via TranscriptPublisher (TranscriptReader
gains an explicit `file:` for this). The config<->transcript join is the
fragile part: no shared key, so it uses a sibling .meta.json agentType
when present, else zips members-by-joinedAt against files-by-mtime. This
join needs validation against a live team run.

App wiring + visible surfacing land with the teammate tiles (T-140).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-23 01:13:46 +02:00
co-authored by Claude Opus 4.7
parent cda289b8b1
commit fc55f58e6b
7 changed files with 735 additions and 5 deletions
+11 -5
View File
@@ -187,10 +187,12 @@ class TranscriptReader {
void Function(String)? onWarn,
String? projectsBase,
int? initialTailBytes,
String? file,
}) : _pollInterval = pollInterval,
_onWarn = onWarn ?? _defaultWarn,
_projectsBase = projectsBase ?? _defaultProjectsBase(),
_initialTailBytes = initialTailBytes ?? _defaultInitialTailBytes;
_initialTailBytes = initialTailBytes ?? _defaultInitialTailBytes,
_explicitFile = file;
final String workspacePath;
final Duration _pollInterval;
@@ -205,6 +207,11 @@ class TranscriptReader {
/// temp directory instead of the user's home.
final String _projectsBase;
/// When set, tail this exact file instead of discovering the newest
/// `.jsonl` in the munged dir. Used for teammate subagent transcripts
/// (T-139), whose path the team observer resolves explicitly.
final String? _explicitFile;
static String _defaultProjectsBase() {
final home = Platform.environment['HOME'] ?? '';
return home.isNotEmpty ? '$home/.claude/projects' : '.claude/projects';
@@ -287,10 +294,9 @@ class TranscriptReader {
}
Future<void> _tick(StreamController<ConversationItem> controller) async {
final dir = _mungedDir();
// Discover or refresh the active session file.
final newest = await _newestJsonl(dir);
// A teammate reader tails one fixed file; otherwise discover the
// newest session `.jsonl` in the munged dir.
final newest = _explicitFile ?? await _newestJsonl(_mungedDir());
if (newest == null) return;
if (newest != _currentPath) {