fix(session-cache): stop /clear from listing a phantom session - #157
Open
abate wants to merge 1 commit into
Open
fix(session-cache): stop /clear from listing a phantom session#157abate wants to merge 1 commit into
abate wants to merge 1 commit into
Conversation
/clear does not stay in the current transcript: the CLI opens a NEW jsonl and writes only bookkeeping into it — a local-command caveat, the <command-name>/clear</command-name> record, empty stdout. The indexer took that record as the session summary, so every /clear added a "2 msgs" sidebar entry titled "/clear clear </com", and every session actually started by /clear kept that title instead of its first prompt (53 of the 99 transcripts in one project folder here). The first-prompt scan now classifies user records. Local-command bookkeeping is skipped, <local-command-stdout> included — the CLI writes a command's own output back as a user record, which titled every session where /model preceded the first prompt. A bare slash-command record is only a fallback title, used when the transcript also holds an assistant turn (a headless "/code-review high" run); with no real turn both readers return null and nothing is indexed, the same way a brand-new session stays out of the sidebar until its first prompt. Migration v9 purges the rows the old parser wrote, plus the cache_meta gate of their folders so the next reconcile re-reads exactly those files. Neither kind of row can heal on its own: a phantom sits on a file that never changes again, and a mistitled one keeps its summary because the header-only refresh path only overwrites a summary it can re-derive.
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.
Symptom
Clearing a session adds a session to the sidebar. Reported with this view — two entries titled
/clear clear </com, the top one appearing the instant/clearran:Cause
/cleardoes not stay in the current transcript — the CLI opens a new.jsonland writes only bookkeeping into it:{"type":"user","isMeta":true,"message":{"content":"<local-command-caveat>Caveat: …</local-command-caveat>"}} {"type":"user","message":{"content":"<command-name>/clear</command-name>\n<command-message>clear</command-message>\n<command-args></command-args>"}} {"type":"system","subtype":"local_command","content":"<local-command-stdout></local-command-stdout>"}readSessionFile()skipped the caveat but accepted the<command-name>record as the summary. Two consequences:messageCount2, so it is indexed and listed — a session the user never started./clearis titled from that same record;cleanDisplayName()strips the tags and leaves the truncated fragment/clear clear </com. 53 of the 99 transcripts in one project folder on the reporter's machine are titled this way.Same class, found while fixing it:
<local-command-stdout>also arrives as atype: "user"record, so a/modelbefore the first prompt titled the session<local-command-stdout>Set model to Sonnet 5….Fix
read-session-file.jsnow classifies each user record (classifyUserText()), shared by both readers:skip<bash-input>,<bash-stdout>,<local-command-caveat>,<local-command-stdout>command<command-name>/x</command-name>+<command-args>promptA
commandrecord becomes the title only when the transcript also holds an assistant turn — so a headless/code-review highrun still reads as/code-review highinstead of raw XML. With no real turn, bothreadSessionFile()andreadSessionDisplayHeader()returnnulland nothing is indexed, exactly how a brand-new session stays out of the sidebar until its first prompt.Migration v9
Rows written by the old parser cannot heal on their own:
h.summary || cachedEntry.summary, and the header read now declines to re-derive a bookkeeping summary.v9 therefore purges rows whose summary starts with
<command-name>or<local-command-stdout>(plus theirsearch_map/search_content/search_ftsentries) and drops thecache_metagate for their folders, so the nextreconcileCacheFromFilesystem()re-reads exactly those files — every other file in the folder still hits thefileMtimefast path. No full re-index, and a no-op on installs that never hit the bug.Verification
Against the reporter's real
~/.claude/projectstree: the phantom (21e03eba, 2 KB) is no longer indexed, and every other session in that folder now shows its own first prompt instead of/clear …or<local-command-stdout>….test/read-session-file-slash-command.test.js— the bookkeeping-only transcript,/clear+ real prompt, command-as-fallback title,/modeloutput,<scheduled-task>precedencetest/db-purge-command-summaries.test.js— v9 purges only the affected rows, clears only the affected folder gate, leaves the search tables clean, and is a no-op otherwise.ai/contexts/session-cache.md— records the rule and why the migration existsTakes effect on next launch; the affected folders re-index once at that startup.