Skip to content

(restore): a backgrounded session is restored from its pre-fork transcript #518

Open

Description

@devsuitup

Restoring the open sessions after a restart resumed a conversation's pre-backgrounding transcript instead of its continuation, so the session came back without its last four days.

Measured on Windows (v0.0.91), 09/10:

  • 05/10 21:20 local: session 7615734f-… was backgrounded from its terminal ("Backgrounding after the current tool finishes…"). The conversation continued under a new id, e7abbcdc-…, whose transcript starts at 05/10 19:21:21Z and was last written 09/10 15:45:28Z.
  • After the app restart, restore launched claude --resume 7615734f-… --permission-mode auto --ide. That transcript ends 05/10 21:20. No process ran under e7abbcdc-… any more.
  • The resumed CLI printed "Remote Control not started here · another Claude Code on this machine (started 3d ago) already has Remote Control for this conversation", although that process no longer existed.

Impact: the restored tab looks like the live session but has lost days of context; acting on it duplicates a session that is supposed to be unique.

Likely cause (not verified in code): the working set keeps the id recorded at launch, and backgrounding moves the conversation to a new id that nothing re-keys (the same class #477 handles for /clear).

Expected: before resuming a saved entry, follow the conversation to its current id (continuation chain), and never resume an id that has a newer continuation; at worst, ask instead of resuming silently.

Activity

  1. devsuitup commented on Oct 9, 2026

    @devsuitup
    OwnerAuthor

    Diagnosis (read-only, code at origin/main):

    • The CLI's signal. Backgrounding appends {"type":"continued-in","sessionId":<old>,"continuedInSessionId":<new>} to the old transcript (7615734f, line 42050, 2026-10-05T19:21:12Z, right after "Backgrounding after the current tool finishes…"). A second one points to c1329340 (2026-10-08T20:23Z), so one id can have several continuations. The new transcript carries no back-link (sessionKind:"bg", no forkedFrom). The live link is the CLI descriptor ~/.claude/sessions/<pid>.json, whose sessionId changes (measured after an in-CLI /resume: pid 9368 now reads e7abbcdc).
    • Why no re-key. The only re-key path is session-transitions.js detectSessionTransitions (~330-430): a new jsonl is re-keyed only when forkedFrom matches (:395), otherwise "NO MATCH" (:409). A bg continuation has no forkedFrom, and /resume creates no file, so session-forked is never sent and the tab stays keyed on the launch id. The PTY itself stayed alive for four days (OSC 9 from 7615734f in main.log), so persistWorkingSet (public/app.js:175-200) kept writing 7615734f, and the trigger watcher logged "Session not found: e7abbcdc" on 06, 08 and 09/10. guardResume only checks whether the saved id is live elsewhere, which it was not.
    • Same class as /clear ((sessions): follow /clear to the new transcript instead of listing it apart #477), in-CLI /resume, and backgrounding: the PTY's conversation id changes while the PTY lives.

    Plan:

  2. devsuitup commented on Oct 9, 2026

    @devsuitup
    OwnerAuthor

    Part B is merged in #520: restore and manual open now follow continued-in.

    • One live continuation opens it and rewrites the saved entry.
    • A target whose transcript was never written is dropped when a sibling exists.
    • Ambiguous or unresolved cases never spawn. Automatic restore holds them in a notice, and a manual open asks, still offering the original.

    Part A (live re-key from the CLI descriptor on /resume / /clear) waits for #477.

    Follow-ups from the last review, not blocking:

    • liveElsewhereChecked ignores scheduledRuns.
    • When liveness is unknown, the notice says "choose" instead of "waiting for indexing".
    • The full read keeps the raw buffer next to the decoded string, so peak memory per read is about the file size; not measured.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions