feat(annotate-last): --exclude-active-turn ignores the launching turn - #1375
Open
BenNewman100 wants to merge 2 commits into
Open
feat(annotate-last): --exclude-active-turn ignores the launching turn#1375BenNewman100 wants to merge 2 commits into
BenNewman100 wants to merge 2 commits into
Conversation
When an agent launches annotate-last from its own tool call (rather than a !-inline slash command), it can still write to the transcript after the process starts, and its acknowledgement becomes "the last message". The Codex path already excludes the active turn unconditionally; Claude Code did not. - --exclude-active-turn (opt-in, Claude Code path): select and list only assistant messages before the newest human prompt on the active branch - Fail open to file order when the cutoff empties the branch (fresh /compact), mirroring the existing empty-branch fallback - A cutoff that empties the right log is reported and stops the candidate walk instead of drifting to an older session - Flag accepted and inert on the stdin, Codex, Droid, and Copilot paths
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
Adds an opt-in
--exclude-active-turnflag toplannotator annotate-last/last. On the Claude Code path it restricts message selection (and the picker) to assistant messages written before the active turn — everything from the newest human prompt onward is ignored. Same semantics the Codex path already applies unconditionally viabeforeActiveTurn.Problem
annotate-lastreads the transcript once at startup and takes the newest assistant text on the active branch, with no length or content filter. That is correct for the!-inline/plannotator-lastslash command, where the binary runs before the agent gets a turn.It breaks when the agent launches the command itself — e.g. a skill that runs
plannotator annotate-last --jsonfrom its shell tool in the background (the only launch style that survives the Claude Code Bash tool's command timeout on long reviews). The agent is then free to write to the transcript after the process starts, and its acknowledgement ("Opened it for you.") races the read and usually wins. The user is shown the ack instead of the message they asked for; the intended message is still in the picker, but the default is wrong every time. Prompting the agent to "emit no text in the launching turn" is not reliable enough to fix this.Why a flag, not default-on
The
!-inline path can't use a turn cutoff: Claude Code records the slash command's user entry only after the bang command finishes, so at read time the newest human prompt on disk belongs to the previous turn and the cutoff would skip the message the user wants. Default-on would break existing/plannotator-lastusers; opt-in leaves that path byte-identical and lets model-invoked launchers pass the flag.Also considered: ignoring entries newer than the process start time. It needs no flag and works for every launch style, but it only removes text written after launch — a preamble the agent writes before the tool call still wins. The turn cutoff covers both, so this PR goes with the flag.
Changes
session-log.ts:findActiveTurnStartIndex(newest human prompt, restricted to the active branch so a/rewindorphan can't anchor), anexcludeActiveTurnoption, andgetRecentRenderedMessagesDetailedwhich reportsemptiedByActiveTurn.getRecentRenderedMessageskeeps its signature and delegates./compactthe branch holds only the launching turn), the cutoff is redone in file order — mirroring the existing empty-branch fallback — so the pre-compaction messages are still offered.index.ts: parse the flag with the other globals; pass it through intryLogCandidates. A log that is emptied by the cutoff stops the candidate walk and exits 1 withNo assistant message precedes the current turn (--exclude-active-turn).— previously an empty result meant "wrong file" and the walk could drift to an older session's transcript.cli.ts: usage + option text. Docs:commands/annotate-last.mdgets a section on when to pass it.Tests
session-log.test.ts— 9 new cases: ack after launch, preamble before launch, unchanged default without the option, multi-chunk message before the anchor,/rewind+ cutoff, no human prompt, fresh/compactfail-open, nothing before the turn (emptiedByActiveTurn), anchor helper respects the branch set.cli.test.ts— help text.Verified live from a Claude Code session launching the CLI from a Bash tool call: without the flag it selects the agent's in-turn text; with the flag it selects the message preceding the turn's prompt.