Repository navigation
THI-416: Done banners say Claude's last reply, not the status bar - #50
Merged
tdody merged 6 commits intoOct 30, 2026
Conversation
…re recap and last_line (THI-416) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…eply, read backward from the end and cached per session (THI-416) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…session's composer, the turn's ✻/⎿ lines, right-aligned hints and OSC 8 links no longer pass for it (THI-416) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… the named composer rule (invariant 2) (THI-416) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t paragraph joined, not its last row; empty after a tool call (THI-416) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes THI-416. A "{pane} is done" banner's body was a line read off the pane's screen (
w.last_line), and on live panes that line was rarely the reply. In prod's/api/stateit was usually one of these:✻ Brewed for 42s · done 1:27 PM;⎿line:⎿ Allowed by auto mode classifier,⎿ Tip: …;new task? /clear to save 433.4k tokenshint above the composer;⧉row. Claude prints the session name in the composer's top rule (──── name ─).DIALOG_RULE_REdoesn't match a labeled rule, so the composer went undetected and THI-434's footer cut never applied.The reply, from the transcript (
turns.py,window_view.py)doneClaude pane,/api/statenow carrieslast_reply.turns.last_reply_for_sessionreads the session's JSONL backward from the end to the newest assistant text.turn_duration,artifact-autoreact-ledger,mode, and meta and sidechain events.Noneonce a prompt follows that text, so a new turn never shows the previous reply.**or backticks, no headings or code blocks) and the result capped at 400 characters.statper poll. Working panes aren't read.Banner (
notify/copy.js):last_reply, thenrecap, thenlast_line, then "Finished.".Screen fallback (
panes.py), for when no transcript is found:_composer_rowsaccepts a named top rule (_COMPOSER_TOP_RE). The lower rule and a dialog's rule must still be plain._last_meaningful_linereturns the start of the newest reply on screen: its⏺row plus the wrapped rows under it, up to the first blank row. Its last row had read as half a sentence; a banner said "that use it would fail.". It returns "" when the newest⏺is a tool call, including collapsed groups and MCP calls, so the banner says "Finished." instead of a tool header. With no⏺on screen, it falls back to the closest line that isn't chrome or the turn's own (✻,⎿, rows indented 20+ columns). All of this is local to it, so_detect_asked_questionreads the same lines as before.recap || last_line) until it's reloaded, and that's the code that produced the half-sentence banner on this machine._split_buffersstrips every escape (_strip_escapes), OSC 8 hyperlinks included, not just SGR.Docs:
docs/channels.md(howlast_replyis read), invariant 2 (the named rule), and the done row of the native-notifications spec.Worth a look in review
The named-rule fix also changes Reply on named sessions.
composer_readyused to find no composer there, so a banner's Reply always got a 409. Now it can type the reply. Every other check is unchanged:❯row, empty or a ghost suggestion;composer_holdsbefore Enter.A typed draft under a named rule is still refused (tested). If you'd rather keep Reply off for named sessions, the change is limited to
_composer_rows.Test plan
uv run pytest -q: 2027 passed. New:test_turns.py: the reply under bookkeeping, a notification turn,Noneafter a prompt, meta and sidechain skipped, the markdown flattening, the cap, a read past one chunk, giving up past the reach, a torn row, and the per-session cache.test_window_view.py:last_replypublished for a done pane only.test_panes.py:last_linecases for a named session's footer with an OSC 8 link, a⎿tip under the reply, the/clearhint, the start of a multi-paragraph reply rather than its last row, and turns that ended in a tool or MCP call;composer_ready;✻ Brewed for …now expect the reply line above it.npx vitest run: 1299 passed, including the done body's priority (last_reply>recap>last_line> "Finished.").bin/check: passes (3 pre-existing biome warnings in files this PR doesn't touch).static/distrebuilt.payloadForover the live/api/state;last_linewas the start of its newest reply, including the named session and the one under the/clearhint.🤖 Generated with Claude Code