From 37f3ad07968feeed038bde86c0cfe1c617cd81b5 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 12 Jun 2026 02:17:37 +0200 Subject: [PATCH] remove the tmux-era TranscriptPublisher class (T-385) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No production code constructed it since the stream-json pivot (D-77) — only its own test did. The ClaudeConversation bus-addressing constants stay; the meta sidebar and team panel host still consume them for member-status messages. The companion finding — the team roster surfaces listening to TeamMemberJoined events nothing emits — is real rewiring work, split out as T-396 (drive the roster from TeamBroker membership, then delete the ghost event types). Co-Authored-By: Claude Fable 5 --- .../claude/src/transcript_publisher.dart | 49 ++---------- .../claude/transcript_publisher_test.dart | 75 ++----------------- 2 files changed, 15 insertions(+), 109 deletions(-) diff --git a/lib/builtin/claude/src/transcript_publisher.dart b/lib/builtin/claude/src/transcript_publisher.dart index e74ce8c3..9b367c9f 100644 --- a/lib/builtin/claude/src/transcript_publisher.dart +++ b/lib/builtin/claude/src/transcript_publisher.dart @@ -1,18 +1,14 @@ -/// Bridges a [TranscriptReader] onto the kernel [MessageBus] (epic T-132, -/// D-75). +/// Bus addressing for Claude conversation content (epic T-132, D-75). /// -/// One reader tails a workspace transcript; this publisher republishes -/// every [ConversationItem] as a bus [Message]. Any number of Claude -/// panels can then subscribe to the same conversation via the bus instead -/// of each owning its own reader — the decoupling the team panels -/// (T-139/T-140) need, where a single observer feeds the lead tile plus a -/// tile per teammate. +/// The tmux-era `TranscriptPublisher` that used to live here (one reader +/// tailing a transcript, republished onto the bus) had no production +/// constructor calls after the stream-json pivot (D-77) and was removed +/// in the T-385 dead-code sweep. The [ClaudeConversation] channel/key +/// constants remain — the meta sidebar and team panel host still consume +/// them for member-status messages. library; -import 'dart:async'; - import 'package:clide/builtin/claude/src/transcript_reader.dart'; -import 'package:clide/kernel/src/events/message_bus.dart'; /// Bus addressing for Claude conversation content. abstract final class ClaudeConversation { @@ -29,7 +25,7 @@ abstract final class ClaudeConversation { /// Channel for a teammate's conversation (team work, T-139/T-140). static String teammateChannel(String agentId) => 'conversation/$agentId'; - /// Key under which the [ConversationItem] travels in a [Message]'s data. + /// Key under which the [ConversationItem] travels in a bus message's data. static const itemKey = 'item'; /// Shared channel carrying each team member's live status (T-157). Every @@ -44,32 +40,3 @@ abstract final class ClaudeConversation { if (status.contextTokens != null) 'contextTokens': status.contextTokens, }; } - -class TranscriptPublisher { - /// Starts republishing [reader]'s items onto [messages] under - /// [ClaudeConversation.publisher] / [channel]. The subscription is - /// attached synchronously, so a controller that subscribes before the - /// reader's first poll never misses the initial tail. - TranscriptPublisher({required MessageBus messages, required TranscriptReader reader, this.channel = ClaudeConversation.leadChannel}) - : _messages = messages, - _reader = reader { - _sub = _reader.stream.listen((item) { - _messages.publish(ClaudeConversation.publisher, channel, {ClaudeConversation.itemKey: item}); - }); - } - - final MessageBus _messages; - final TranscriptReader _reader; - final String channel; - late final StreamSubscription _sub; - - /// Live session status (model / permission-mode / context) from the - /// underlying reader — passed through for the status strip (T-145). - Stream get statusStream => _reader.statusStream; - - /// Stops publishing and tears down the underlying reader. - Future dispose() async { - await _sub.cancel(); - await _reader.dispose(); - } -} diff --git a/test/builtin/claude/transcript_publisher_test.dart b/test/builtin/claude/transcript_publisher_test.dart index 8c2d9f1b..09aa001f 100644 --- a/test/builtin/claude/transcript_publisher_test.dart +++ b/test/builtin/claude/transcript_publisher_test.dart @@ -1,78 +1,17 @@ -/// Tests for TranscriptPublisher — bridges a TranscriptReader onto the -/// kernel MessageBus (T-137/D-75). Pure Dart: MessageBus + reader have no -/// Flutter dependency, so this runs under `package:test`. +/// Tests for the ClaudeConversation bus-addressing constants. The +/// TranscriptPublisher class this file used to cover was removed in the +/// T-385 dead-code sweep (no production constructor calls since the +/// stream-json pivot, D-77). library; -import 'dart:convert'; -import 'dart:io'; - import 'package:clide/builtin/claude/src/transcript_publisher.dart'; import 'package:clide/builtin/claude/src/transcript_reader.dart'; -import 'package:clide/kernel/src/events/message_bus.dart'; import 'package:test/test.dart'; -Map _userLine(String uuid, String text) => { - 'type': 'user', - 'uuid': uuid, - 'parentUuid': '', - 'isSidechain': false, - 'version': '2.1.143', - 'timestamp': '2026-05-16T08:53:06.708Z', - 'message': {'role': 'user', 'content': text}, -}; - -Map _asstLine(String uuid, String text) => { - 'type': 'assistant', - 'uuid': uuid, - 'parentUuid': '', - 'isSidechain': false, - 'version': '2.1.143', - 'timestamp': '2026-05-16T08:53:07.708Z', - 'message': { - 'role': 'assistant', - 'content': [ - {'type': 'text', 'text': text}, - ], - }, -}; - void main() { - group('TranscriptPublisher', () { - late Directory base; - const workspace = '/pub/ws'; - - setUp(() async => base = await Directory.systemTemp.createTemp('transcript_publisher_test_')); - tearDown(() async => base.delete(recursive: true)); - - // Serialized: this MessageBus republish assertion is timing-sensitive and - // flaked in the parallel flutter pool; runs in the --concurrency=1 pass (T-193). - test('republishes reader items onto the bus (lead channel + item key)', tags: ['serial'], () async { - final dir = Directory('${base.path}/${workspace.replaceAll('/', '-')}'); - await dir.create(recursive: true); - File('${dir.path}/session-abc.jsonl').writeAsStringSync('${[_userLine('u1', 'hello'), _asstLine('a1', 'hi there')].map(jsonEncode).join('\n')}\n'); - - final bus = MessageBus(); - addTearDown(bus.dispose); - final received = []; - // Subscribe before the publisher starts the reader's first poll. - final sub = bus.subscribe(publisher: ClaudeConversation.publisher, channel: ClaudeConversation.leadChannel).listen(received.add); - - final reader = TranscriptReader(workspace, projectsBase: base.path, pollInterval: const Duration(milliseconds: 20)); - final pub = TranscriptPublisher(messages: bus, reader: reader); - - await Future.delayed(const Duration(milliseconds: 200)); - await sub.cancel(); - await pub.dispose(); - - expect(received, hasLength(2)); - expect(received.every((m) => m.data[ClaudeConversation.itemKey] is ConversationItem), isTrue); - final items = received.map((m) => m.data[ClaudeConversation.itemKey]).toList(); - expect(items.first, isA()); - expect((items.first as UserMessage).text, 'hello'); - expect(items[1], isA()); - }); - - test('teammateChannel namespaces by agentId', () { + group('ClaudeConversation addressing', () { + test('sessionChannel + teammateChannel namespace by id', () { + expect(ClaudeConversation.sessionChannel('abc-123'), 'conversation/abc-123'); expect(ClaudeConversation.teammateChannel('coder@team-x'), 'conversation/coder@team-x'); });