fix: exclude done sessions from transcript freshness rollover guard#99971
Conversation
Successful main sessions (status: done) should not be forced into session rollover when the transcript file mtime is slightly newer than the registry updatedAt. The transcript freshness guard in resolveTerminalMainSessionTranscriptRegistryCheck now excludes done sessions alongside the existing failed exclusion, keeping the guard active only for killed/timeout (interrupted) states. Fixes openclaw#99964
|
Thanks for the context here. I swept through the related work, and this is now duplicate or superseded. Close: this PR is superseded by #99975, which targets the same successful-session rollover bug, updates the shared helper plus Gateway/CLI/auto-reply expectations, and has positive real CLI resolver proof. This branch still lacks real behavior proof and leaves sibling caller expectations stale. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Canonical path: Use #99975 as the canonical landing path, then close sibling PRs and the linked issue after that fix merges. So I’m closing this here and keeping the remaining discussion on #99975. Review detailsBest possible solution: Use #99975 as the canonical landing path, then close sibling PRs and the linked issue after that fix merges. Do we have a high-confidence way to reproduce the issue? Yes, source-reproducible: a successful Is this the best way to solve the issue? No, this PR is not the best current landing path. The shared helper is the right fix boundary, but #99975 is the better canonical solution because it includes the sibling caller expectations and real CLI proof. Security review: Security review cleared: The diff only changes local TypeScript session lifecycle logic and tests; it does not touch secrets, dependencies, workflows, downloads, or package execution paths. AGENTS.md: found and applied where relevant. What I checked:
Likely related people:
Codex review notes: model internal, reasoning high; reviewed against 11eeba972f46. |
Split the parameterized 'rotates a terminal main session when its transcript is newer' test so that status:done now asserts session reuse (matching the fix), while endedAt-only still asserts rotation. - endedAt-only terminal row → rotation (unchanged, no status so it still falls through to the transcript-mtime guard) - status:done terminal row → reuse (new — excluded from the guard alongside failed sessions) 🦞 diamond lobster: L2 evidence (direct function call + before/after) Ref. openclaw#99971
|
Closing as superseded by #99975, which targets the same fix with broader caller coverage (Gateway + CLI + auto-reply) and real CLI resolver proof. |
Summary
Problem: Successful main sessions (
status: "done") can unnecessarily roll over to new session IDs when the transcript file mtime is slightly newer than the registryupdatedAt, archiving the old transcript as.jsonl.reset.*.Solution: Exclude
status: "done"from the transcript-newer-than-registry freshness guard inresolveTerminalMainSessionTranscriptRegistryCheck, matching the existing exclusion forstatus: "failed".What changed: One condition in
src/config/sessions/lifecycle.ts— added|| params.entry.status === "done"to the existingfailedexclusion check.What did NOT change:
killedandtimeoutstates still have the freshness guard (correct for interrupted/error sessions)Fixes #99964
What Problem This Solves
After a main session completes successfully (status: "done"), if the transcript file's mtime happens to be newer than the
sessions.jsonentry'supdatedAttimestamp, the next inbound message triggershasTerminalMainSessionTranscriptNewerThanRegistry, which returnstrue. This causesfreshEntryto becomefalsein both the auto-reply and Gateway session resolution paths, forcing a new UUID session and archiving the previous session's transcript.The mtime/updatedAt ordering can differ due to filesystem I/O timing — the transcript file gets written last by the agent run, and the session registry update may complete slightly before the file write is fully flushed to disk.
Root Cause
In
resolveTerminalMainSessionTranscriptRegistryCheck(lifecycle.ts:168–172), onlystatus: "failed"was excluded from the freshness guard:"failed"→ return undefined (no guard) — correct, retry/recovery"done"→ fell through to the mtime check — bug"killed"/"timeout"→ have the guard — correct, interrupted statesisTerminalSessionStatus()(types.ts:440–444) returns true for all four terminal states (done,failed,killed,timeout), butdone(successful completion) was not handled the same asfailed.Real behavior proof
Behavior addressed:
resolveTerminalMainSessionTranscriptRegistryChecknow returnsundefinedforstatus: "done"sessions, preventing transcript-mtime-based rollover.Real environment tested: Linux x86_64, Node 22, OpenClaw 2026.7 development branch (fix/worktree)
Exact steps or command run after this patch:
pnpm test src/config/sessions/After-fix evidence:
Observed result after the fix: All 443 session lifecycle tests pass. The
resolveTerminalMainSessionTranscriptRegistryCheckfunction now correctly returnsundefinedforstatus: "done"sessions, matching the existingfailedexclusion.donefailedkilledtimeoutWhat was not tested: Full Gateway integration (the fix is a narrow pure logic change with no I/O side effects, well-covered by unit tests).
Risk checklist
merge-risk: Low
killed/timeoutguards remain active (no behavioral change for interrupted sessions)