file T-274 (Claude status bar stays empty)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 09:26:19 +02:00
co-authored by Claude Opus 4.8
parent 9a8f8641f7
commit a5dddd815a
2 changed files with 39 additions and 0 deletions
@@ -785,3 +785,4 @@ INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by,
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-267', 'status', 'ready', 'in_progress', NULL, '2026-06-07 17:22:06', '2026-06-07 17:22:06', '2026-06-07 17:22:06', NULL, 'bd4730367ab0af0d33d145a56748b2ab', 1) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-262', 'status', 'backlog', 'in_progress', NULL, '2026-06-07 17:23:39', '2026-06-07 17:23:39', '2026-06-07 17:23:39', NULL, 'dd562ebdd3cb176d4d58bdf07190924a', 1) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-262', 'status', 'in_progress', 'done', NULL, '2026-06-07 17:41:11', '2026-06-07 17:41:11', '2026-06-07 17:41:11', NULL, '2e581770ff1d438497379beaa530b9c6', 1) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-263', 'status', 'backlog', 'in_progress', NULL, '2026-06-08 06:53:38', '2026-06-08 06:53:38', '2026-06-08 06:53:38', NULL, 'f182186b0196ff0c7970cef5330efef1', 1) ON CONFLICT(hash) DO NOTHING;
+38
View File
@@ -1737,3 +1737,41 @@ IMPLEMENTATION NOTES (from streamlining analysis):
C Error-card symmetry decision: this ticket merges only SUCCESS into the tool card; the error path intentionally stays a separate prominent red "· error" card (conversation_view.dart ~L337) for failure visibility. Decide explicitly: keep that asymmetry (success folds, failure stays two-card) OR also stamp a red status mark on the tool-use header for visual symmetry while still keeping the separate red card. Default leaning: keep the separate red card; optionally add the red header mark.
D Activity-cluster coupling: activity_cluster.groupConversation currently folds a tool CALL and its RESULT as TWO separate foldable items into a cluster (activity_cluster.dart ~L100-109, classifying each by toolUseId). Once this ticket makes call+result a single self-contained card, the grouping pass must treat the tool call as ONE unit (its result is part of the card, no longer a separate foldable item) or the result will double-render (once folded into the card, once as a cluster item). Update _isFoldable / the pairing logic accordingly and add a test that a merged tool card is not double-counted.', 'done', 'medium', NULL, NULL, 'D-78', '2026-06-07 08:10:42', '2026-06-07 17:41:11', NULL, '536c793d50d899e4d403b5a2a246f6a5', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_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 (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-263', 'story', 'T-267', 'Fold sub-agent prompt into the Agent card; relabel from you to agent prompt', 'When Claude launches a sub-agent (the Agent/Task tool), the sub-agent conversation runs as a SIDECHAIN. The prompt Claude wrote for that sub-agent comes through the transcript as a UserMessage with isSidechain=true but injected=false. The conversation view does not look at isSidechain, so it renders that prompt with the blue "you" label exactly like real user input — FALSELY implying the user sent it. It sits as a standalone block below the Agent tool-use card. This is misleading: the user did not write that prompt.
Confirmed facts from the code: every transcript envelope carries BOTH isSidechain AND parentUuid (transcript_reader.dart ~L547 parses isSidechain but never USES it; parentUuid is dropped entirely). The Agent/Task tool-use is rendered by _toolUse in lib/builtin/claude/src/conversation_view.dart (only AskUserQuestion is special-cased today). The "you" vs "context" label is chosen in conversation_view.dart (~L200): UserMessage with injected=true "context" (muted); else "you" (blue, tokens.globalFocus). isSidechain is currently never read by any rendering/grouping logic.
Design (confirmed with user):
1. Never label a sidechain prompt "you". A UserMessage with isSidechain=true is an AGENT PROMPT, not user input label it "agent prompt" with muted / de-emphasized styling (same precedent as injected "context" messages, D-78). Never the blue "you" treatment.
2. Fold the prompt INTO the Agent tool-use card above it, COLLAPSED BY DEFAULT. The Agent card becomes the container that owns its prompt: expand the card to reveal the prompt; collapsed (default) it is hidden. The standalone prompt block is SUPPRESSED from the list same fold-a-standalone-item-into-its-card pattern as T-262.
3. Prompt folds into the CALL, not the results. Only the prompt folds into the collapsed Agent call card. The sub-agent work (its thinking / tool calls / results) stays VISIBLE as the results chain after the call not buried. Shape: [collapsed Agent call, owning the prompt] then [the run results], with no misleading "you" block anywhere. Agent prompts are valuable enough to keep with the call rather than fold into the subsequent results chain.
4. Link the prompt to the RIGHT Agent card via parentUuid. Parse parentUuid (currently dropped) so each sidechain prompt attaches to its spawning Agent tool-use. This matters when several agents run in parallel in one turn a positional "nearest preceding Agent" heuristic would misattach; the parent link is the robust route. Heuristic only as a fallback when the link cannot be resolved.
Touch points:
- transcript_reader.dart: capture parentUuid from the envelope and expose it on ConversationItem (it is present in the JSONL, just not parsed).
- conversation_view.dart: relabel sidechain UserMessage to "agent prompt" (stop the "you" path); fold it into the Agent card body; special-case the Agent/Task tool name (today only AskUserQuestion is special-cased); suppress the standalone block.
- activity_cluster.dart: ensure the suppressed prompt is not double-counted in grouping; leave the sidechain results in the chain.
Edge cases:
- Parallel agents in one turn each prompt attaches to its own card (the reason for parentUuid).
- Orphan prompt (no resolvable parent) still render as "agent prompt", muted/collapsed, never "you".
- Nested / background sub-agents.
Tests: unit/widget coverage for sidechain-prompt relabel (never "you"), folding into the Agent card (collapsed hides, expand reveals), suppression of the standalone block, parentUuid attachment with parallel agents, and the orphan fallback. Add a golden for the Agent card with a folded prompt.
Refs: D-78 (interaction zone / display-only conversation widgets; injected/context muting precedent). Related: T-262 (fold-standalone-item-into-its-card pattern).
IMPLEMENTATION NOTE (E Agent-card layered ordering, from streamlining analysis):
With this ticket (fold prompt) and T-262 (merge success result) both landing, the Agent/Task card ends up owning multiple segments: the tool INPUT, the folded PROMPT, and via T-262 the sub-agents final returned RESULT (the Task ToolResultMessage). Define a deliberate layered order when expanded (e.g. call/input prompt returned result) with clear sub-labels/dividers so the Agent card stays readable and does not become a kitchen sink. Coordinate with T-262 (result merge) and T-264 (nesting the whole run): the nested run region vs the returned-result segment must not duplicate the sub-agent output.', 'in_progress', 'medium', NULL, NULL, 'D-78', '2026-06-07 08:34:02', '2026-06-08 06:53:38', NULL, '0e4c99f123f408855af890ceee68b486', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_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 (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-274', 'bug', NULL, 'Claude bottom status bar often stays empty / doesn''t update', 'The bottom status-bar context slot for the Claude pane (model · permission-mode · context, T-145/T-150) frequently renders empty and doesn''t update. The slot is fed by the active pane''s status widget via the focus service (PaneContextStatusItem -> ClidePane.statusWidget in claude_pane.dart), sourced from StreamJsonSession.statusStream (model/permissionMode from the ''system/init'' event; cost/contextWindow from ''result'' events in stream_json_session.dart).
Repro: open clide; the status line is often blank and stays blank until/unless a turn completes (or never populates).
Likely suspects to investigate:
- status only published while the pane is the focused contribution (active==true) if focus isn''t on the Claude pane, the slot clears.
- statusStream may not emit until the first ''result''/''init'' event; a resumed session (--resume) may not re-emit init, so model/mode never arrive.
- _statusWidget returns null when _status.isEmpty AND no skills, so an unstarted/!init session shows nothing.
- focus-slot wiring (FocusTracker) may not re-publish on pane (re)build / session rebind.
Acceptance: the Claude status line shows model · mode · context promptly after a session starts/resumes and stays current across turns and focus changes; add a test covering the resumed-session (no fresh init) case.', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-08 07:26:13', '2026-06-08 07:26:13', NULL, 'cc0f2ec60e2b8e4e97374b4c708d6125', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_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);