Skip to content

fix(claude-code): derive task session ID from workspace; add transcript_retention_days; emit Stop-hook idle sentinel#866

Open
morganl-ant wants to merge 1 commit intocoder:mainfrom
morganl-ant:anthropic/session-lifecycle
Open

fix(claude-code): derive task session ID from workspace; add transcript_retention_days; emit Stop-hook idle sentinel#866
morganl-ant wants to merge 1 commit intocoder:mainfrom
morganl-ant:anthropic/session-lifecycle

Conversation

@morganl-ant
Copy link
Copy Markdown

@morganl-ant morganl-ant commented Apr 22, 2026

Problem

The module pins task-mode Claude sessions to a single hardcoded UUID (cd32e253-ca16-4fd3-9825-d837e74ae3c2). When a workspace home directory is templated, snapshot-restored, or otherwise shared across workspaces, every workspace tries to claim the same Claude session ID and the CLI rejects the second one with "Session ID X is already in use". See #726.

Two related lifecycle gaps while in here:

  • Session JSONL transcripts under ~/.claude/projects/ are never pruned by the module, so long-lived workspaces accumulate unbounded transcript files unless the user knows to set cleanupPeriodDays.
  • There is no machine-readable signal that Claude has gone idle, which makes it hard for templates to drive Coder's workspace autostop or activity bumping off agent state.

Changes

  • start.sh: derive TASK_SESSION_ID as uuid5(NAMESPACE_URL, "coder-workspace://" + data.coder_workspace.me.id). The result is stable across restarts of a given workspace and unique across workspaces. Falls back to using the workspace ID directly if python3 is unavailable, and to the legacy hardcoded value only when no workspace ID is provided.
  • start.sh: when is_valid_session rejects a transcript file, rename it to *.jsonl.bak instead of deleting it so the conversation is recoverable.
  • main.tf: pass ARG_WORKSPACE_ID to both scripts; add transcript_retention_days (number, default null, validated >= 1).
  • install.sh: write /etc/claude-code/managed-settings.d/30-coder-lifecycle.json containing a Stop hook that touches ~/.claude-module/last-stop on every turn end, plus cleanupPeriodDays when transcript_retention_days is set. The local managed-settings file is read by the Claude CLI on every backend (Anthropic API, Bedrock, Vertex, gateway), so this works regardless of how inference is routed. Skips with a warning if /etc/claude-code is not writable.
  • Tests: replace the hardcoded session ID literal with a deriveTaskSessionId helper that recomputes the UUIDv5 from the rendered script (the coder provider returns environment-dependent workspace IDs). Adds tests for the derived ID, the .bak quarantine, and the lifecycle settings file.
  • README: document the per-workspace session ID and transcript_retention_days.

Relationship to #861

#861 deletes start.sh and removes session/continue handling from this module entirely. If that direction lands, the session ID derivation and quarantine logic here should move to whatever module owns Tasks start orchestration. Filing this against main so #726 is fixed for current users in the meantime; the transcript_retention_days input and Stop-hook drop-in are install-time and remain useful regardless.

Validation

  • terraform fmt clean
  • terraform validate clean
  • terraform test 19/19 pass
  • shellcheck --severity=warning clean on both scripts
  • bash -n clean on both scripts
  • bun integration tests not run locally; relying on CI for those.

Fixes #726


Disclosure: I work on Claude Code at Anthropic. This is one of a small set of contributions we are proposing to the claude-code module; happy to discuss the overall direction in whichever venue the maintainers prefer.

…pt_retention_days; emit Stop-hook idle sentinel

The hardcoded TASK_SESSION_ID caused 'Session ID already in use'
collisions whenever a home directory was templated or shared across
workspaces. Derive a per-workspace UUIDv5 from data.coder_workspace.me.id
instead so the session is stable across restarts of the same workspace
but unique across workspaces.

While here:
- Quarantine invalid session files to .bak instead of silently deleting
  them so transcripts are recoverable.
- Add transcript_retention_days input that maps to Claude Code's
  cleanupPeriodDays setting via /etc/claude-code/managed-settings.d/.
- Register a Stop hook that touches ~/.claude-module/last-stop so
  template authors can wire workspace autostop or activity tracking off
  the file mtime.

Fixes coder#726
@morganl-ant morganl-ant marked this pull request as ready for review April 22, 2026 20:57
@matifali matifali added the version:patch Add to PRs requiring a patch version upgrade label Apr 23, 2026
Copy link
Copy Markdown
Member

@matifali matifali left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@morganl-ant thanks for your contribution. I think we can merge this to main and release a new patch version to unblock your users. Bit for other PRs, let's rebase them once #861 (likely today) lands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

version:patch Add to PRs requiring a patch version upgrade

Projects

None yet

Development

Successfully merging this pull request may close these issues.

claude-code: stale session state causes "Session ID already in use" after workspace restart

2 participants