Auto-compact should continue the same session instead of starting a new one

Status Open
Reported on v2.1.238
Maintainer reply None cached
Activity 0 comments · opened Aug 19, 2026

Environment

  • Claude Code 2.1.238 (originally reported on 2.1.235), Linux (Kubuntu 24.04), zsh, CLI
  • Long-running single-topic sessions, Remote Control in regular use

Summary

When auto-compact fires, Claude Code does not continue the conversation in the
current session — it creates a new session file that inherits the parent's
title verbatim. From the user's point of view one continuous conversation
silently becomes two entries in /resume with the same name, and there is no
way to tell which one to go back to.

The title half of this is already tracked (#78264, #82969, #80906, #85548).
This issue is about the layer underneath: the session identity should not
change just because the context filled up.
Compaction is a memory-management
detail; it should not be visible as a new item in the session list.

Manual /compact already does the right thing

This is the most useful thing I can add, and I only measured it recently.

On 2.1.238 I ran /compact by hand in a 6.6 MB session and checked the state
directory afterwards:

  • the session id was unchanged;
  • no new .jsonl appeared in the project directory — the file count stayed the same;
  • the isCompactSummary record was appended to the same transcript;
  • the conversation continued in the same session, and /resume still showed one row.

So the behaviour this issue asks for is already implemented and shipping.
The request is not "please build in-place compaction" — it is "please route
auto-compact and the resume dialog through the path /compact already takes."
Whatever divergence exists between those code paths looks like the whole bug.

How a session gets cloned

Ranked by how hard it is for a user to connect cause to outcome.

1. The resume cost-threshold dialog — the worst of them, and the default.

Resuming a long session shows:

This session is 2h 9m old and 312.5k tokens.

Resuming the full session will consume a substantial portion of your usage limits.
We recommend resuming from a summary.

> 1. Resume from summary (recommended)
  2. Resume full session as-is
  3. Don't ask me again

In the 2.1.235 bundle the three choices map to actions like this:

compact   → "Resume from summary (recommended)"     (runCompact)
continue  → "Resume full session as-is"
dismiss   → "Don't ask me again"

Option 1 — the default, the one marked recommended, the one the cursor starts
on — runs compaction, which creates a new session file inheriting the parent's
title verbatim. The user asked to return to a session and instead got a
second session with the same name
.

Three things make this path worse than the others:

  • It is framed as the safe choice. The dialog's stated purpose is to save

usage limits; nothing in it suggests it will fork anything.

  • It fires exactly on the sessions you care about most — the long ones with

the most history to lose track of.

  • The cost framing pushes you toward it. Option 2 is real money; option 1 is

free. Users will pick 1 repeatedly, and each time the list grows another
identical row.

2. Auto-compact, silently, whenever the context fills.

3. The Remote Control bridge, which re-stamps the title from the cloud copy
over the local one (https://github.com/anthropics/claude-code/issues/80906#issuecomment-5339657177).

4. Background agents, which are launched with --fork-session and so create
a new id by construction. Worth noting these were not my source: checking my
typed-command history, /background, --bg and claude agents appear zero
times across 261 commands.

What happens today

A single conversation of mine existed in three local session files at once.
All three began with the same first user message carrying the identical
timestamp 2026-08-18T13:53:51.977Z:

| file | size | origin |
|---|---|---|
| 7060dc65… | 1.6 MB | the session I was typing in |
| 0082f80a… | 649 KB | a branch I did not knowingly create |
| 269a9fd7… | 553 KB | contains isCompactSummary, created while the session was open |

The compaction-created file carries isCompactSummary but no forkedFrom
records
— so it is not going through the regular fork path, which does stamp
provenance and appends a " (fork)" suffix when no explicit name is set. That
asymmetry was reported in
https://github.com/anthropics/claude-code/issues/78264#issuecomment-5329784266.

Why this hurts

The failure is not cosmetic. Because the entries are indistinguishable, I
answered in the wrong branch of my own conversation more than once and lost
track of what I had written where. The workaround I fell into was never
closing the session
— which is exactly backwards, since the longer a session
stays open, the more certain auto-compact is to fire and clone it again.

No acceptable configuration exists

There is no setting to opt out of the cloning half. I searched the binary for
compactInPlace, sessionRollover, keepSessionId and similar — none exist.
What a user can actually choose today:

| option | outcome |
|---|---|
| leave defaults | conversation silently splits into a second session with the same name |
| autoCompactEnabled: false | no split, but the conversation hits a hard context wall |
| raise autoCompactWindow | compaction fires with the least possible headroom, and still eventually splits |
| de-duplicate the titles yourself | rows become distinguishable; the conversation is still split |

The third row deserves a note, because it looks like a mitigation and isn't.
Compaction has to summarise the conversation, and that work needs context
headroom of its own. The later the threshold, the less room is left at the
moment it fires — raising the window converges on "compact at the last possible
moment". Per the setting's own description the effective value is capped anyway:

The actual threshold is the minimum of this setting and your model's maximum context window.

So the first three rows are a choice of which failure to accept, and the fourth
only makes the symptom legible — it cannot rejoin a conversation that has
already been divided. This is the argument for treating the session-identity
layer as the actual bug rather than the naming layer: naming can be patched
from outside, session identity cannot.

Desired behaviour

In priority order:

  1. Compaction keeps the session. Same session id, same file, one entry in

/resume — exactly what manual /compact already does. The compacted
summary becomes part of that session's transcript rather than the seed of a
new one.

  1. If a new session id is genuinely required by the architecture, route it

through the same code path as a normal fork — write forkedFrom, and do not
copy the parent's title verbatim.

  1. Never show two identically named entries. /rename already refuses to

let two live sessions collide — there is a message for it in the binary:
*"another live session on this machine already holds that name, so this
session is …"*. Every path that writes a title should go through that same
check. Today compaction and the Remote Control bridge both bypass it.

Fallback: the minimum that makes a split survivable

If keeping the id turns out not to be feasible, this is the smallest behaviour
that would make the outcome navigable. I am stating it as a spec rather than a
wish because I had to implement the equivalent locally and the details mattered.

The rule: the name — and with it the "this is where you continue" role —
should follow the continuation, and the predecessor should become
distinguishable. Today it is the opposite: both rows carry the same name and the
older one is unmarked.

What it looks like now, right after an auto-compact:

Session hygiene          2 minutes ago  · 6.0MB     ← predecessor
Session hygiene          2 minutes ago  · 0.3MB     ← continuation

Nothing separates them. Same name, same timestamp — the predecessor's mtime is
updated by the compaction itself — and size is not a hint either, since a
continuation can easily be larger than what it continues (one of mine was:
21 MB continuation, 19 MB predecessor).

What would be enough:

Session hygiene                            2 minutes ago     ← continuation
Session hygiene (compacted 21.08 13:43)    2 hours ago       ← predecessor

The marker has to carry a timestamp, not just the word. If a topic lives for
a while and compaction fires more than once, a bare (compacted) suffix
produces several identical rows and the problem returns intact. This isn't
hypothetical — my own hook first appended only a date, and I had to change it to
date + time within two days, because a single day accumulates enough sessions
for a date to stop separating them.

Two details that are easy to get wrong:

  • "Most recent" has to mean last real message, not file mtime. Background

and bridged sessions get their files touched without anyone writing in them,
so mtime points at the wrong row.

  • A pinned/user-set name must migrate to the continuation, not stay on the

predecessor. I initially kept it on the predecessor and the result was
backwards: the clean name marked the dead branch while the live one carried
the suffix.

Why the local workaround is not a fix

I run a SessionStart hook that scans the project's session files, finds
colliding titles, and appends a disambiguator to all but the most recently
active one. It makes the picker readable, and I am keeping it. But it does not
close this issue:

  • It renames labels; it does not stop the splitting. One conversation is

still scattered across three files, and no hook can merge them back. The thing
I actually lost was continuity — which half of my own thread a given message
lives in — and a better label does not give that back.

  • It is reactive by construction. It can only run at session start, i.e.

after the clone already exists. There is always a window where the list is
ambiguous.

  • It is per-machine and per-project. It does nothing for the same sessions

seen from claude.ai or from mobile, where I cannot install a hook at all.

  • It fails silently, because session identity is not stable at hook time.

This one is new and worth spelling out. My hook located the project directory
by looking for <session_id>.jsonl under ~/.claude/projects/. That is wrong
in a way nothing tells you about: claude --resume creates a brand-new
session id first and shows the picker afterwards
, so at SessionStart the
file for that id does not exist yet. The hook found no project, returned
early, and did nothing at all — for two days, while looking installed and
healthy. It only ever worked when invoked as claude --resume <explicit-id>.
(The fix on my side was to fall back to transcript_path, which is derived
from the id rather than from the filesystem, and then to cwd. Both are in
the hook payload; neither is documented as the thing to rely on.)

  • It still costs me attention. The point of the request is to stop having to

think about this. Right now I hold a mental model of an implementation detail
— when compaction fires, what it clones, which entry is the live one — just to
answer in the right place.

A user should not need to write a hook against the tool's own state directory to
keep their session list usable — and when they do, it can quietly stop working
without any signal.

Related

  • #78264 — /compact copies the parent's title without forkedFrom
  • #80906 — paths that overwrite a renamed title, including the Remote Control bridge
  • #82969 — /clear
  • #85548 — the 64 KB head/tail parse window
  • #87958 — /cd changes the working directory but does not relocate the session

View original on GitHub ↗