[BUG] Closed session transcripts are bulk-rewritten with a shared mtime, destroying file-date signal (114 files at one identical second)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version available to me (2.1.247, bundled with Claude Desktop; latest published is 2.1.251)
What's Wrong?
In ~/.claude/projects/<project>/, a large batch of session transcripts share one identical filesystem mtime — the same single second — while their contents show no activity anywhere near that time. The result is that mtime no longer dates a session.
Measured on this machine on 2026-08-31:
- 1,206 top-level session
.jsonlfiles across all project dirs; 1,090 distinct mtime epochs - largest cluster: 114 files all at epoch
1787422837(2026-08-22T21:20:37 local), all within a single project dir - for those 114,
mtimeminus the last timestamped record inside the file: min 3 days, median 17 days, max 47 days — 93 of 114 are stamped more than 7 days after their last real event - tail record type of those files:
last-prompt×76,mode×21,attachment×10,queue-operation×4,user×2,assistant×1
The last-prompt and mode records carry no timestamp field. So the write leaves no trace in content — only mtime moves. Two examples: a session whose last real event is 2026-07-22T14:00:18Z, and one whose last real event is 2026-08-04T10:19:24Z, both end with an untimed last-prompt record and both carry the 2026-08-22 mtime.
I have not identified what triggers the write, and I'm deliberately not asserting a cause. The observable facts are: (1) many long-closed transcripts were appended to in one bulk operation, (2) the appended records are timestamp-free metadata types, (3) mtime moved and content did not.
What Should Happen?
Either of these would fix it:
- writes to an already-closed transcript preserve its mtime, or
- any appended record carries a
timestamp, so the write is auditable from content
Currently neither holds: the file's date says one thing, its contents say another, and nothing records what caused the write.
Why It Matters
mtime is the cheapest and most common way to date a session. Concretely:
ls -lt ~/.claude/projects/<project>/reports a burst of 114 sessions on a day when no work happened, and hides the days work actually did happen- any tooling that selects "recent sessions" by mtime — backup selection, log rotation, cleanup scripts, session-resume pickers, usage analysis — silently operates on the wrong set
- a retention policy keyed on mtime will preserve stale sessions and can age out recent ones
This is a silent-wrong-output failure: nothing errors, and the wrong answer looks exactly like the right one.
Steps to Reproduce
Run against a ~/.claude/projects tree with a few months of history:
import os, json, datetime, collections
root = os.path.expanduser("~/.claude/projects")
files = []
for dp, dn, fn in os.walk(root):
if "/subagents" in dp:
continue
files += [os.path.join(dp, f) for f in fn if f.endswith(".jsonl")]
c = collections.Counter(int(os.stat(f).st_mtime) for f in files)
epoch, n = c.most_common(1)[0]
print(f"{n} files share mtime {epoch} "
f"({datetime.datetime.fromtimestamp(epoch)}) out of {len(files)}")
for p in files:
if int(os.stat(p).st_mtime) != epoch:
continue
last = None
tail = None
with open(p, errors="replace") as fh:
for ln in fh:
try:
o = json.loads(ln)
except Exception:
continue
if o.get("timestamp"):
last = o["timestamp"]
tail = o.get("type")
print(os.path.basename(p), "last_event:", last, "tail_type:", tail)
Expected if healthy: mtimes spread across the days work occurred, and each file's mtime close to its last timestamped record.
Is this a regression?
Unknown — I have no known-good version to compare against.
Environment
- macOS (Darwin 25.5.0), Apple Silicon
CLAUDE_CODE_ENTRYPOINT=claude-desktop, session running 2.1.247