From 1e24f0022d6444bd1c8ce6eb4ca5e0b63d0aaf82 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Thu, 11 Jun 2026 19:17:09 +0200 Subject: [PATCH] close out T-325 in the ticket board Persist the live-tail story's done transition (ticket-DB sweep only). Co-Authored-By: Claude Opus 4.8 (1M context) --- .pql/changelog/ticket_history/2026-06.sql | 1 + .pql/changelog/tickets/2026-06.sql | 29 +++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/.pql/changelog/ticket_history/2026-06.sql b/.pql/changelog/ticket_history/2026-06.sql index 64498c1a..9575ee79 100644 --- a/.pql/changelog/ticket_history/2026-06.sql +++ b/.pql/changelog/ticket_history/2026-06.sql @@ -3632,3 +3632,4 @@ INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, chang INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBDSQ2GBSP0ZH4RHZG2PMR0R', 'description', NULL, 'Introduce a ''modes'' capability in the extension manifest (lib/extension/src/manifest.dart) as an open vocabulary: edit and read now, with remote/ssh/webui reserved (D-94). The extension host (lib/extension/src/host.dart) activates an extension only when the active workspace mode is in its declared set; an undeclared extension defaults to edit-only. Then classify the builtins: read-mode-safe = editor (view), files (tree + D-79 grep), git (status/log/diff viewing), terminal, claude; goes dark = pql (search/query/backlinks), decisions, tickets, graph; partial = problems (keep non-pql diagnostics, drop the pql.doctor row). This is the substrate read-mode degrade (T-357) gates on, and the seam the SSH-remote question (Q-23) is expected to resolve into.', NULL, '2026-06-11 13:38:31', '2026-06-11 13:38:31', '2026-06-11 13:38:31', NULL, 'ba0a16d581d6c81fd0cc39ffab15887d', 2) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2T11GCV1EV07DYD5BZENTM', 'status', 'ready', 'in_progress', NULL, '2026-06-11 14:18:34', '2026-06-11 14:18:34', '2026-06-11 14:18:34', NULL, 'a2a75bcb471d6eba8a215d56922c6337', 2) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2T11GCV1EV07DYD5BZENTM', 'status', 'in_progress', 'in_progress', NULL, '2026-06-11 14:59:53', '2026-06-11 14:59:53', '2026-06-11 14:59:53', NULL, '96bef4f722871cb468f67b55d6447acc', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2T11GCV1EV07DYD5BZENTM', 'status', 'in_progress', 'done', NULL, '2026-06-11 17:09:12', '2026-06-11 17:09:12', '2026-06-11 17:09:12', NULL, '591cc2576194fd9400b42aa6c932f0a8', 2) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-06.sql b/.pql/changelog/tickets/2026-06.sql index daf68abb..cc78f007 100644 --- a/.pql/changelog/tickets/2026-06.sql +++ b/.pql/changelog/tickets/2026-06.sql @@ -3641,3 +3641,32 @@ Claude Code CLI executes every Bash tool itself; clide only ingests stream-json - Expanding a Bash card whose command tails a file shows a live scrolling terminal sub-card that updates as the file grows; collapsing it tears the follower down. - A Bash card with no file-backed source shows a clear "nothing to follow" affordance, never a broken/empty terminal. - No follower process is spawned until the card is expanded.', 'in_progress', 'medium', NULL, NULL, NULL, '2026-06-10 12:00:52', '2026-06-11 14:59:53', NULL, '2b485906c225f811862fb5ec97ae37cf', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2T11GCV1EV07DYD5BZENTM', 'story', '06FB0TNQM5TWC00GW0P3X02HZW', 'Live tail sub-card inside expanded Bash activity cards', 'Expanded Bash activity cards should be able to show a live, scrolling tail of a file-backed source the command is following, so the user can watch the same output Claude is tailing — connected lazily, only while the card is expanded. + +**Motivation** +Long-running / tailing Bash steps render only a final result block once the command finishes (see screenshot in T-304 thread). The user can''t follow progress. We want an embedded scrolling terminal sub-card inside the expanded activity card for visibility. + +**Hard constraint (feasibility, established by investigation)** +Claude Code CLI executes every Bash tool itself; clide only ingests stream-json events (stream_json_session.dart -> parseTranscriptChunk). A tool_result arrives as ONE complete block — clide never sees in-flight tool output, and the controller''s in-place upsert path is hardcoded for `partial-` prose deltas, not tool results (controller.dart). We cannot tap Claude''s subprocess fd. So a fully-general "mirror the running command''s stdout" is NOT possible. + +**Viable scope: independent file-tail on expand** +- On card expand, parse the Bash command string (AssistantToolUse.input["command"], transcript_reader.dart) for a file-backed source: `tail -f X`, `tail -n N X`, `cat X`, `less X`, known log paths, or a background task writing to a file. +- If found, clide opens its OWN read-only follower — a NativePty running `tail -f X` (native_pty.dart already exposes Stream) or a Dart file-watcher — and renders it in an embedded scrolling terminal sub-card, reusing TerminalView. +- Lazy lifecycle: connect on expand, disconnect/dispose on collapse. No connection until expanded. +- Read-only & safe: clide observes the same file; it never re-runs or intercepts Claude''s command. +- Graceful when there is no file-backed source (e.g. `git push ... | tail -25`, a pipe inside Claude''s process): show a muted "no independent source to follow" note rather than faking output. + +**Where** +- Card body: conversation_card.dart / conversation_view.dart (_toolUseCollapser, the Bash tool card). Add an optional live-tail segment below the RESULT segment. +- Key the live connection on AssistantToolUse.toolUseId. +- Embedded terminal: reuse lib/src/terminal TerminalView + lib/src/pty NativePty. + +**Open questions (settle in review / may need a Q-record)** +- Command-parsing surface: which commands/forms count as "tailable"? Keep it a small, explicit allowlist to avoid mis-following. +- Security: only follow paths inside the workspace? How to handle absolute paths outside the repo. +- Lifecycle when the underlying command has already finished (file static) vs still running. + +**Acceptance** +- Expanding a Bash card whose command tails a file shows a live scrolling terminal sub-card that updates as the file grows; collapsing it tears the follower down. +- A Bash card with no file-backed source shows a clear "nothing to follow" affordance, never a broken/empty terminal. +- No follower process is spawned until the card is expanded.', 'done', 'medium', NULL, NULL, NULL, '2026-06-10 12:00:52', '2026-06-11 17:09:12', NULL, '050a31bfe1ef9cf220f306ab3922f84c', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);