Repository navigation
Fixed session name not saved to the stopped session in the Save & Switch dialog - #206
Conversation
…tch dialog The name entered in the Save & Switch dialog could be written into the wrong session file, or lost entirely when choosing Restart Advanced. The controller now captures the session identity at launch instead of guessing it at stop time: - the effective CLAUDE_CONFIG_DIR (including profile extra env vars, otherwise ~/.claude), so named profiles write into their own store; - the session ID the running process uses: RESUME_SPECIFIC -> resume ID, CONTINUE_LAST -> newest .jsonl by mtime at launch, NEW -> the file created after launch (excluding a pre-launch snapshot of existing IDs). stopAndRename() renames exactly that ID in exactly that directory, and skips the write when the title is unchanged. The dialog prefills the name as customTitle -> slug -> empty, never the raw session ID (previously a UUID was offered for brand-new sessions and then stored as the title). The session list in the selector is refreshed right after Restart Advanced so the saved name is visible immediately. New helpers in ClaudeSessionStore: listSessionIds, findLatestSessionId, findSessionIdCreatedAfter, findSession, sessionDirForLog. ClaudeProcess exposes the resolved config dir (configDirFrom/resolveConfigDir/getConfigDir). Co-Authored-By: Claude Code <noreply@anthropic.com>
Codex Review Summary: ❌ FailedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cab2ccdae1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…session The session identity was fixed at launch, so a conversation switch made with Claude's own /resume picker left stopAndRename() renaming the session that was open before the switch. The controller now re-resolves the active session whenever its ID is needed: the file the running process wrote last (mtime at or after the launch) is the active conversation. Tabs sharing a project and config store publish and claim their own sessions first, so another tab's file is never mistaken for the one being closed. Session closures release all claims; only live controllers count as owners. Also made the mtime-based test fixtures deterministic by setting explicit file modification times instead of relying on wall-clock writes, which could tie within one millisecond and make the newest-file lookup nondeterministic. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Both review findings are addressed in 06a850c:
Full suite: 1182 tests, 0 failures. 🤖 Generated with Claude Code |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 06a850c4a4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Resolving one tab's session could make an unresolved sibling claim the newer file on its behalf, leaving the resolving tab with no ID and its entered name dropped on stop. New sessions now bind to the process that created them: when a prompt is submitted, a short background poll claims the file written since that prompt (or since launch for input typed directly into the terminal), and the 500 ms screen poll does the same. A tab never resolves another tab's identity: the two-pass publish that forced sibling lookups is removed, so only a tab's own process-specific evidence claims a session. The strict lookup excludes sessions already claimed by other live tabs and ignores files older than the launch. Regression test newSessionsClaimTheirOwnFileBeforeSiblingGuesses reproduces the reported ordering (A unresolved, B resolves first) and fails against the previous implementation. Co-Authored-By: Claude Code <noreply@anthropic.com>
An unresolved NEW tab scanned its session directory on every 500 ms screen poll even before the first prompt. Without a plugin-submitted prompt there is no attribution anchor, so that fallback now runs every ten ticks (~5 s); with a prompt it still runs on every tick. Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e30f26d5ce
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The session-identity poll skipped tabs whose ID was already known, so a conversation switched inside the TUI left the previous session claimed by that tab forever: a sibling switching to it excluded its own current file and resolved to the abandoned one. The PTY waiter also never released the claims when Claude quit or crashed with the tab still open, keeping an exited tab an owner indefinitely. Unified the resolution into one `refreshSessionIdentity()` that re-samples the newest file written since the launch (or the last plugin prompt), switches the identity, claims the new session and releases the old claim; it now runs for known and unknown identities alike (throttled in the screen poll, every 300 ms for 3 s after a prompt, and synchronously in `stopAndRename`). The pre-launch snapshot still excludes pre-existing files while a NEW session is unresolved. The waiter thread reports the process exit so `LIVE_SESSIONS` and the claims are released, and `claimedByOthers()` now also ignores owners whose process is not alive. Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 86d40bd9a8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…races A NEW tab can resume an existing conversation before it creates a JSONL of its own. The pre-launch snapshot marked that file ineligible forever, so the resumed conversation was rejected on every scan and `stopAndRename()` silently skipped the entered name. The snapshot is now only applied while the plugin has not submitted a prompt: after a prompt, anything written since it belongs to this tab, which also covers the resumed file. The exit callback of the PTY waiter raced with process replacement during Save & Switch: it passed the owner check, was descheduled, and after the restart removed the new run from LIVE_SESSIONS and wiped its claims. Exit cleanup is now generation-based — each start gets a new generation, and the check and cleanup happen under the same lock that replaces the process, so a late callback from a stopped run is a no-op. Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f9f8cfcaf2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…he EDT A background identity scan captured the working dir, config dir and launch time of one process run but published its result blindly: when Save & Switch replaced the process while the scan was in flight, the old run could set `activeSessionId` to an ID from the session store it was scanning, and later resolutions preserved it because the resumed file predated the new launch. Capturing the ownership generation with the scan inputs and re-checking it under `processOwnershipLock` at publish time discards results of a replaced run; the fields are also captured under the same lock so a scan sees a consistent snapshot. The periodic scan enumerated the whole session store from the Swing EDT (every 5 s per tab, every 500 ms while a NEW identity was unresolved after a prompt), freezing the UI on large histories, and the synchronized method could make the EDT wait behind a background resolver. The scan now runs on a shared single-thread worker queued from the screen poll, with at most one scan per controller in flight; the per-prompt resolver thread is replaced by an immediate queued scan plus the faster cadence while unresolved. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
All Codex findings on this PR are fixed. Summary (details in the in-thread replies):
Build: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d0a6e505de
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Scans of one process run can overlap: the synchronous dialog/stop path and the background worker both scan. A slow worker that selected session A could publish after a newer scan had already published B and undo B's identity and claim, because the single-flight flag only serialized the scheduled scans. Every scan now reserves a monotonic sequence number under the ownership lock, and a result is published only if no scan that started later has published before it. Claims were keyed by the bare session ID, so a session file copied between the default store and an isolated profile made a live tab in one store hide the other store's file: a tab switching to the copy kept its previous identity and renamed the wrong session. Claims are now keyed by (config dir, project dir, session ID), and the scan exclusion only considers claims from the store being scanned. Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 421bffc1af
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…d the history reload off the EDT SessionModePanel.reload()/refreshTable() replaced the table model, which fires fireTableDataChanged() and drops the JTable selection while RESUME_SPECIFIC stays selected. After "Restart Advanced..." the selector was refreshed this way, so Open passed a null resume ID and silently launched a new conversation. The selected row is now remembered by session ID and restored after the model update, and ClaudeSessionSelectorPanel.onOpen() rejects RESUME_SPECIFIC when no row is selected. refreshSessionList() also parsed every candidate JSONL on the Swing EDT during chat teardown, pausing the IDE for projects with large transcripts. The list is now loaded on a daemon worker with a request sequence that drops superseded results, and only the table update runs on the EDT. Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d3a443f5a4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…h release A background session-list load started for one project/profile could finish after the user switched to another one and replace both the table and its stored store with the previous selection. SessionModePanel now applies such a list only while the directory and config dir still match what the panel shows (applySessionsIfCurrent), so Open can no longer pair a stale session ID with the current directory and Rename cannot target the old store. Session claims used computeIfAbsent() followed by Set.add(), and the release path removed empty entries with a separate sweep. A claim racing a PTY exit could land in an owner set that had just been detached from the map, silently losing it and letting a sibling scan adopt the conversation. Claim membership and empty-entry cleanup now happen inside compute() for the key, so a claim cannot be lost. Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 78ea35d625
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ession mtimes stopProcess() bumped processGeneration under processOwnershipLock but cleared processStartedAt / activeConfigDir / promptSubmittedAt afterwards, outside the lock. A queued identity scan could therefore start between the two and capture the new generation together with the old run's inputs; its result was accepted after the stop released the claims, restoring the stopped identity and leaving the controller claiming both IDs. The generation bump, identity inputs, LIVE_SESSIONS membership and claim release now happen in one critical section. findLatestSessionIdWrittenSince() compares file mtimes with the launch/prompt time recorded in milliseconds, but filesystems with 1-2 s timestamp granularity round a file created right after the prompt below that cutoff. A NEW session whose first turn finished in the same timestamp bucket stayed unresolved and lost the title entered in Save & Switch. The scan now retries with a 2 s slack when the strict pass finds nothing, restricting the retry to files that did not exist in the pre-launch snapshot so a conversation touched by an unrelated process just before the prompt still cannot be adopted. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 20ee8efb22
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Two tabs can resume the same saved conversation, in which case claimSession() records both as owners. claimedByOthers() then reported that session to each of them, so a tab excluded its own active file from the scan; a newer, unrelated file written by a third tab was resolved instead and stopAndRename() overwrote the other conversation's title. A key for which this controller is an owner is now not treated as claimed by others. Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a41019fc41
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…e mtime slack to saved conversations The identity scan treated a file claimed by another live tab as an unconditional exclusion. When this tab joined another tab's conversation from its own resume picker, its previous file stopped changing, so no unclaimed file was written since its prompt and it kept resolving to the conversation it had left; stopAndRename() then wrote the entered title to the wrong conversation. When a known identity and a prompt of our own exist and no unclaimed file was written since that prompt, the scan now follows the newest file written since the prompt even if another live tab owns it, sharing the ownership instead of ignoring the switch. While this tab keeps writing its own conversation the strict pass already finds that file, so another tab's newer file is still not adopted. The coarse-timestamp retry previously excluded every pre-launch session ID, which also rejected a saved conversation resumed in-process (such files exist before the launch). The retry now only applies that snapshot exclusion when the tab has not submitted a prompt yet, so the rounded mtime is tolerated for resumed saved conversations while pre-launch files still cannot be adopted before a prompt. Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e805f4ac36
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The recorder's menu context and the tracked conversation could still outlive the event they described, so Save & Switch could either swallow a real prompt or name the conversation a /resume had left behind. - The ARROW-digit worker navigates out of a focused text editor so the digit answer selects an ordinary option, but it never recorded the forwarded focus: a digit typed in the terminal kept counting as editor text and the prompt after it was swallowed with the menu. Each arrow is now written and tracked inside one terminalInputLine section. - Every answer worker's submitting key (Enter, or Right for checkbox menus) was written outside the recorder lock and the menu retired only afterwards, so a prompt typed right after the key was classified against the dismissed menu. writePtyAnsweringMenu() writes the key and retires the menu in one lock section, and all workers use it. - The guard for an unresolved /resume switch only covered identities established at launch. A NEW tab that had resolved its conversation by prompt evidence was renamed after the picker left it behind: the guard now protects whatever identity is still tracked while the switch has not been followed by a submission. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d38267dd96
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Two ways the session name could still land in, or be composed from, the wrong conversation. - writePtyAnsweringMenu() retired the menu under the recorder lock but kept the line typed into it while the answer worker paused. A text option's answer or a picker's search filter then prefixed the next prompt sent from the panel, and the composed text was published as exact evidence of a prompt the conversation never received. The submit now discards the menu-owned line and cursor together with the retirement. - The rename-ambiguity check excluded every file another live tab had launched on. A launch or resume does not prove which process recorded this run's submission — it may be the file this run wrote — so the exclusion hid a competing file and let the stop rename the tab's guess. Only the tracked candidate and the launch conversation stay out of the competition now; every other file carrying the submission keeps the identity ambiguous. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
@codex review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
@codex review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
@codex review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b15b4c598f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ty gaps Require an evidence-based scan result from after a conversation switch before the stop-time guard lets a rename through: a prompt submitted after the switch proves nothing about where the picker went, since the destination may be a live sibling's file no evidence pass can adopt, and the same prompt also lands in the launch conversation when the selection never took effect. The guard now tracks the anchor of the scan result that identified a conversation written since the switch instead of comparing submission times. Publish the line the answer worker's Enter submits when terminal input already answered the choice menu during the worker's pause: the TUI's input line is a fresh prompt draft by then, so discarding it dropped the only evidence that follows the conversation the prompt was written to. Roll back a pending /resume switch when its picker is cancelled: opening the picker switches nothing, so the evidence the command replaced (conversation switch, prompt text and time, external-input time) is snapshotted at the submission and restored by every cancellation path — terminal Esc (consumed by the next write or at stop time), panel cancel, and the GUI send that consumes the pending Esc. A confirmed selection disarms the snapshot. Regression tests: aPromptBeforeTheDestinationRecordDoesNotUnblockTheRename, aSwitchResolvedByTheDestinationRecordStaysRenamable, theWorkersEnterAfterAnEarlyMenuAnswerIsRecorded, aCancelledPickerKeepsTheLaunchConversationRenamable, aCancelledPickerRestoresThePromptEvidence. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
@codex review |
The /resume Enter moves externalInputAt before the command branch arms the picker switch, so the snapshot the cancellation restored held the command's own time instead of the pre-command one. After a cancel-restore the earlier prompt then looked stale to the stop-time lookup, which skipped its text evidence and the cutoff it judges competition by. Captured the cutoff inside the recorder lock before any input is applied and passed it to armPickerSwitch. Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 57d5579d3b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ChoiceMenuModel escapedMenu = openChoiceMenu(); | ||
| terminalEscapeOnMenu = escapedMenu != null; | ||
| terminalEscapeOnPicker = isConversationPicker(escapedMenu); |
There was a problem hiding this comment.
Recognize picker cancellation before the menu is published
If the user opens /resume and presses Esc before the 400/500 ms screen-detection timers publish the picker, openChoiceMenu() is still null here. Both escape flags remain false, so neither subsequent input nor stopAndRename() calls restorePickerSwitch(). Consequently, conversationSwitchAt remains set even though the original conversation is still active. If the user then enters a name in Save & Switch without submitting another persisted prompt, identityRemainsAmbiguous() skips the rename and the entered name is lost. Cancellation handling needs to account for the already-armed picker state rather than requiring the menu to have reached the model.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in d4b156e.
You are right: the pending /resume admission is armed long before the screen poll publishes the picker, so an Esc pressed in that window reached appendTerminalInput() with openChoiceMenu() == null. Both escape flags stayed false, the cancellation was never consumed, and identityRemainsAmbiguous() kept treating the conversation as switched — the entered name was dropped.
appendTerminalInput() now classifies an Esc as "cancelled picker" when no menu is published but a picker admission is pending (escapedMenu == null && pickerSwitchPending), so the existing consumption paths (next write, stopAndRename() while the Esc is still pending) roll the switch evidence back.
Regression test: aPickerCancelledBeforeItIsPublishedKeepsTheLaunchConversationRenamable — submits /resume, sends Esc without ever publishing a menu to the model, then stops with a name. It fails before the change (expected: <A-title> but was: <null>) and passes after it. ClaudeSessionControllerTest 246/246, full mvn test 1403 tests with 0 failures.
The screen detector publishes the /resume picker a few hundred milliseconds after the command, so an Esc pressed right away arrives while openChoiceMenu() is still null. The Esc was then not classified as a menu cancellation, the pending switch was never rolled back, and the stop treated the conversation as switched and dropped the entered name. An Esc with no published menu is now classified as the cancelled picker while a /resume admission is pending. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
@codex review |
The panel's Cancel button sends its own Esc to the PTY, bypassing the terminal recorder, so the pending /resume switch was only rolled back when the screen poll had already published the picker. The same unpublished-menu window therefore lost the entered name when the user cancelled from the panel instead of the keyboard. The cancel now treats a pending /resume admission as the picker too. Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ce5b01cfb0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (enterOnAnsweredMenu) { | ||
| draft = terminalInputLine.toString().trim(); | ||
| draftExact = !terminalHistoryRecall && !terminalCompletionEdited; | ||
| resumeDraft = draftExact && isResumeCommand(draft); |
There was a problem hiding this comment.
Recognize
/clear when a menu worker submits a fresh line
If terminal Enter answers an ordinary menu during an answer worker's pause and the worker's final Enter submits a newly typed /clear, this recovery path records /clear as a human prompt without advancing conversationSwitchAt; only /resume receives command handling. A subsequent recalled or Tab-completed prompt in a launch-known tab therefore skips the unheld-destination lookup in refreshSessionIdentity(). An isolated controller/store replay with another live writer on launch conversation a consequently renames a, even though the new clear destination b is strictly newer and contains the actual prompt. Apply the existing isConversationCommand() handling here too, arming a cancellable picker only for /resume.
Useful? React with 👍 / 👎.
| ChoiceMenuModel menu = openChoiceMenu(); | ||
| if (isConversationPicker(menu) || (menu == null && pickerSwitchPending)) { | ||
| restorePickerSwitch(); | ||
| } |
There was a problem hiding this comment.
Retire a published picker when the prompt panel cancels it
With Hide menu and an already-published /resume picker, cancelling from the prompt panel restores the previous evidence but leaves the model menu and its recorded search draft active; the plugin-written Esc bypasses the input listener. If the next panel prompt completes before that stale menu is cleared, completeGuiSend() mistakes its Enter for a picker selection, drops the real prompt evidence, and sets conversationSwitchAt again. An isolated recorder/store replay then makes even a single-file launch ambiguous and loses the Save & Switch name. The new unpublished-picker cancellation test does not exercise this published-menu state; retire the menu and discard its draft atomically with the cancelling write.
Useful? React with 👍 / 👎.
| if (resumeDraft) { | ||
| scheduleIdentityRefresh(); | ||
| } else { | ||
| rememberSentPrompt(draft, submittedAt, draftExact); |
There was a problem hiding this comment.
Publish recovered prompt evidence under the recorder lock
When terminal input has already answered the picker, the worker's final Enter can submit a fresh prompt, but this publishes its evidence after releasing terminalInputLine. A terminal writer waiting on that lock can submit a newer prompt first, after which this call overwrites lastPromptText and promptSubmittedAt with the older draft while externalInputAt retains the newer time. A two-writer replay of the new early-answer recovery path reproduces that reversal: both prompt-evidence branches fail their promptAt >= externalAt guard, leaving a switch into a sibling's live conversation unresolved and suppressing the requested rename. Publish the captured evidence inside the same critical section as its Enter and recorder reset.
Useful? React with 👍 / 👎.
…wer workers - writePtyAnsweringMenu: a /clear submitted by the worker's Enter is a conversation switch like /resume, so it is no longer recorded as prompt text; only /resume arms the cancellable picker. - The recovered prompt evidence is now published inside the recorder critical section together with its Enter and recorder reset, and only when it is not older than the evidence already recorded, so a terminal writer that submits concurrently keeps its newer text and time. - cancelPrompt: the plugin-written Esc bypasses the recorder, so a published menu is now retired and its search text discarded atomically with the cancelling write; otherwise the next panel send's Enter was read as a picker selection and its prompt evidence was dropped. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 10406ffa0c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (isConversationPicker(menu) || (menu == null && pickerSwitchPending)) { | ||
| restorePickerSwitch(); | ||
| } | ||
| if (menu != null) { |
There was a problem hiding this comment.
Clear search text when cancelling an unpublished picker
If /resume opens and the user types a search filter before the screen poll publishes the picker, panel Cancel restores the previous switch state but this guard skips clearing the filter from terminalInputLine. Fresh evidence beyond the unpublished-picker cancellation fix: an extracted replay of /resume<Enter>, filter, panel Cancel, then panel hello publishes exact filterhello, although the TUI submits only hello. This breaks the actual conversation's prompt match and can redirect Save & Switch to an unrelated transcript containing filterhello, even when the real conversation is newer. Discard the picker-owned draft when cancelling a pending unpublished picker too.
Useful? React with 👍 / 👎.
| // The menu is gone, so this Enter submits the draft on the | ||
| // fresh line — whether it was typed after a terminal Enter | ||
| // that answered the menu or after a digit the panel sent. | ||
| rememberSentPrompt(draftLine, submittedAt, draftExact); |
There was a problem hiding this comment.
Handle conversation commands in recovered panel submissions
With Hide menu, terminal Enter can answer an ordinary menu during a panel send's pause, after which the user can type /clear and have the panel's delayed Enter submit it here. Fresh evidence beyond the writePtyAnsweringMenu() command fix: this separate recovery path still records /clear as prompt text and leaves conversationSwitchAt == 0 in an extracted current-method replay. A subsequent recalled or Tab-completed prompt in a launch-known tab therefore skips the unheld-destination lookup; if another live tab appends a user record to the launch conversation, Save & Switch can rename that old file even though the cleared conversation is newer. Apply the existing conversation-command handling to recovered panel drafts as well.
Useful? React with 👍 / 👎.
The panel cancel discards the picker's search text only when the menu reached the model. When the cancel lands before the screen poll publishes the picker, the digits typed into it stay in the recorder and would prefix the next prompt, so a pending /resume admission now drops the draft too. Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8d51b3f652
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (findPromptBearingSession(dir, configDir, since, exclude, null, | ||
| promptAt, promptText) != null) { |
There was a problem hiding this comment.
Preserve ambiguity when the competitor scan hits its limit
When two NEW tabs submit the same prompt, a tab can temporarily track its sibling's file before its own JSONL appears. If eight unrelated files subsequently have newer mtimes, findPromptBearingSession() examines only those eight and this check misses the real competing file; the relaxed pass has the same cap. An isolated resolver/store replay makes ambiguity change from true to false and stopAndRename("A-title") rename the sibling's b.jsonl while the matching a.jsonl is still present. Search competitors without the discovery cap, or fail closed when the search is truncated.
Useful? React with 👍 / 👎.
| confirmPickerSwitch(); | ||
| conversationSwitchAt = System.currentTimeMillis(); |
There was a problem hiding this comment.
Confirm picker switches only after the submitting key is written
If the user selects a different GUI row in /resume and presses terminal Esc during the arrow worker's navigation delay, this has already cleared pickerSwitchPending, so cancellation cannot restore the pre-picker evidence. Fresh evidence beyond the earlier picker-cancellation fixes: an isolated replay using the current ARROW:1 worker leaves conversationSwitchAt set after Esc and makes stopAndRename() discard the entered name, whereas cancelling before starting that worker correctly renames the original conversation. The worker's later Enter submits an empty ordinary line, not a picker selection, so it supplies no destination evidence. Keep cancellation reversible until the submitting Enter actually answers the still-open picker.
Useful? React with 👍 / 👎.
|
@codex review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Problem
In the Save & Switch dialog, a name entered for the session being closed could be written into the wrong session file, or lost entirely — most visibly when choosing Restart Advanced... with a named connection profile (e.g. Work, which has an isolated config dir) and with several sessions/tabs in the same project.
Three combined causes:
stopAndRename()passedconfigDir = null, so renames always went to~/.claude, ignoring the profile's isolated store (and corrupting an unrelated session there).findMostRecent()— the newest.jsonlby mtime, which can be another tab's session; it also returnsnullfor an unparseable newest file and silently skipped the rename.SavedSession.displayName(), which falls back to the session ID — a brand-new session showed a UUID, and accepting it stored a UUID as the title.Fix
The controller is now the single source of truth for the running session's identity, captured at launch:
CLAUDE_CONFIG_DIR(profile extra env vars included, else~/.claude);RESUME_SPECIFIC→ resume ID,CONTINUE_LAST→ newest.jsonlby mtime at launch,NEW→ the file created after launch, excluding a pre-launch snapshot of existing IDs.stopAndRename()renames exactly that ID in exactly that directory (skipping unchanged titles). The dialog prefills customTitle → slug → empty, never a UUID. The selector's session list is refreshed right after Restart Advanced.Tests
ClaudeSessionTabPrefillNameTest(6 tests).listSessionIds,findLatestSessionId,findSessionIdCreatedAfter,findSession.🤖 Generated with Claude Code