link cleared sessions back to the conversation they continue
/clear starts a new claude session and the list showed it as an unrelated conversation. claude records no parent link anywhere — not in compactMetadata, logicalParentUuid, summary.leafUuid, the per-session slug, or the live process registry — so infer it: a cleared transcript opens with /clear, and /clear happens inside one process, so the parent is the conversation in the same group that was writing to disk at the instant this one began (4ms apart, measured). matching is on per-minute activity rather than updatedAt, because resuming a parent moves its end time past its child's birth and lost the link entirely for two of the three cleared sessions here. the window is symmetric because clearing makes claude summarise the conversation it is ending, so the parent's final record can land after the child's first. ambiguity fails closed — the wrong parent also renames the conversation. read-only: nothing is written back to claude's store, and an explicit title always wins. also: cleared sessions were titled "<command-name>/clear</command-name>" because claude does not set isMeta on slash commands; and the chat header was hardcoded to undefined, so it read "New chat" above every conversation you opened. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -275,6 +275,76 @@ step 4 above is the one I most want you to actually try, because it is the bug t
|
||||
|
||||
---
|
||||
|
||||
## 14. `/clear` no longer loses the thread
|
||||
|
||||
**What to click.** Open `/chat` on this project's group. Look at the top of the list. This conversation
|
||||
should be titled **“Platform Arch 2”**, with a second meta line reading _continues Platform Arch_. Open
|
||||
it: the header — which used to say “New chat” for every conversation you opened — now says “Platform
|
||||
Arch 2”, and under it is a link back to the original. Click it, and you are in the conversation this one
|
||||
grew out of.
|
||||
|
||||
Nothing was migrated to make that happen. It is derived from transcripts that were already on disk, so
|
||||
it applied to your history the moment the server restarted.
|
||||
|
||||
**What was actually wrong.** You guessed right: `/clear` starts a genuinely new Claude session, and the
|
||||
list was showing it as an unrelated conversation. Two separate defects were stacked on top of that.
|
||||
|
||||
The first was cosmetic but ugly: cleared sessions were titled `<command-name>/clear</command-name>`.
|
||||
Claude records slash commands as ordinary user entries and — this is the part that fooled the original
|
||||
code — **does not set `isMeta` on them**, so the “first human message” that becomes the title was the
|
||||
`/clear` itself. Skipping `<command-name>` entries fixes it, and is why these sessions now fall back to
|
||||
their first real message when no parent can be found.
|
||||
|
||||
The second is the interesting one. **Claude records no parent link anywhere.** I checked all five places
|
||||
it could plausibly live: `compactMetadata` and `logicalParentUuid` (those are in-file compaction — see
|
||||
below), `summary.leafUuid` (every one resolves inside its own transcript, never across files), the
|
||||
per-session `slug` (a random name like `precious-brewing-kazoo`, not a lineage), and
|
||||
`~/.claude/sessions/<pid>.json` (a live process registry, gone when the process is). So the link has to
|
||||
be inferred, and this is the only inference in that file.
|
||||
|
||||
**What it infers from.** A cleared transcript opens with `/clear`, and `/clear` happens inside one
|
||||
process — the old transcript's last write and the new one's first write are the same moment. Measured
|
||||
here: 4ms apart. So the parent is the conversation in the same group that was writing to disk at the
|
||||
instant this one began.
|
||||
|
||||
Two things about that rule are not obvious, and both were found by running it against your real history
|
||||
rather than by reasoning:
|
||||
|
||||
- **The window is symmetric.** Clearing makes Claude summarise the conversation it is ending, and that
|
||||
costs a model call — so the parent's _final_ record can land a few seconds **after** the child's first
|
||||
one. One session here sits exactly there, and a before-only window silently lost it.
|
||||
- **It matches on activity, never on “when did it end”.** My first version compared the child's birth to
|
||||
the parent's last-modified time. That threw away any parent you later went back and **resumed**, because
|
||||
resuming moves its end time days past its child's birth. Two of your three cleared sessions were in
|
||||
that state and found no parent at all. Each transcript now carries the set of minutes it wrote in, which
|
||||
survives resumption.
|
||||
|
||||
**Where it says nothing.** If two transcripts in the group were active in that minute, it names neither —
|
||||
no parent is a far smaller mistake than the wrong parent, because the wrong parent also **renames** the
|
||||
conversation. One session of yours (in `~/dockers`) still shows no parent: its nearest candidate was
|
||||
active three hours away, which means the conversation it came from is genuinely not on disk any more.
|
||||
That is the rule working, not failing.
|
||||
|
||||
**It is read-only, on purpose.** Nothing is written back into Claude's store, so the numbering is a
|
||||
display-time guess that costs nothing if it is wrong and disappears on the next read. An explicit title —
|
||||
Claude's own summary, or a rename you typed — always wins and is never overwritten. My suggestion is to
|
||||
live with it for a week; if it never guesses wrong, we can promote it to a real `summary` record so the
|
||||
name sticks in the terminal too. That is deliberately not built yet.
|
||||
|
||||
**`/compact` needed nothing.** I had assumed it forked a session like `/clear` does. It does not — it
|
||||
appends to the same transcript and keeps the same session id (63 in-file compactions in the parent of
|
||||
this conversation alone). So compaction was already invisible in the list, correctly.
|
||||
|
||||
**Also fixed in passing:** the chat header was hardcoded to `sessionTitle={undefined}`, so it read “New
|
||||
chat” above every conversation you opened, resumed or not. It now takes the title from the same place
|
||||
the list row does, which is also what keeps the two from disagreeing about the numbering.
|
||||
|
||||
**Not verified:** the inference itself I ran against your real transcripts and checked case by case (the
|
||||
numbers above are measurements, not estimates). The UI — the meta line, the header, the link back — has
|
||||
not been through a browser.
|
||||
|
||||
---
|
||||
|
||||
## Things noticed and deliberately left alone
|
||||
|
||||
- **`useChatWebSocket` silently ignores unparseable frames.** That one is intentional and the comment
|
||||
|
||||
Reference in New Issue
Block a user