#214: mint a session id on every claude-code spawn, so every member is resumable #218

Closed
agent wants to merge 0 commits from worker/cb214-claude-session-id-b9eab4-4 into main
Member

What and why

ClaudeCodeLauncher.applySessionIdentity minted a session id only when the caller passed sessionName or resumeSessionId. fleet_spawn treats both as optional, so an ordinary spawn minted nothing and agentSessionId stayed null for the life of the member. Unlike opencode, a claude-code session id is fixed at launch: nothing resolves it afterwards, so resume is only possible when the id was requested at spawn time.

Fix: mint a UUID and pass the --session-id flag on every non-resume spawn. A resume spawn still passes -r (the two are mutually exclusive) and a sessionName still rides as -n.

Verified against the real binary (claude 2.1.252)

  • claude --help lists --session-id : Use a specific session ID for the conversation (must be a valid UUID).
  • A non-UUID value is rejected at argument parsing with 'Invalid session ID. Must be a valid UUID.' (exit 1, no network call). So the UUID.randomUUID() mint is a hard requirement, not cosmetic.
  • A valid UUID is accepted by the binary; the session transcript lands at the documented on-disk location keyed by that UUID, which is exactly what a later -r resumes on.
  • No conflict with any other flag the launcher passes: --mcp-config, --append-system-prompt, --append-system-prompt-file, --agent, --model, --autocompact and -n are all independent in the help output, and the real binary parses them all together with --session-id without complaint.
  • The only documented interaction is --session-id vs -r/--resume (see also --fork-session), which the launcher already never combines.
  • Note: this shell's operator OAuth was expired, so a full round-trip model call could not complete; the flag was still accepted and the session file written before the auth failure.

Tests

  • NEW ClaudeCodeLauncherTest.plainSpawnMintsASessionIdSoEveryMemberIsResumable: a plain spawn (no sessionName, no resumeSessionId) passes --session-id with a valid UUID and returns that id from agentSessionId(). Watched it fail against the pre-change launcher first: expected but was (the flag was absent from argv).
  • Updated two legacy byte-identical argv tests (startResolvesTheExecutableFromKindAndDropsArgvZero, noFleetFlagsWhenMcpUrlAbsent) to accept the newly-minted session flag while keeping their original guarantees.
  • Updated SessionManagerTest (acquireWithNeitherSessionFieldStillMintsAnAgentSessionId): a plain acquire now records a non-null agentSessionId and the roster shows it. Pre-change this test asserted null — which is precisely the defect this ticket removes. This file is outside the nominal scope (ClaudeCodeLauncher and its tests) but drove the real launcher through SessionManager, so the build cannot be green without it.
  • Existing resume test (resumeSpawnPassesDashRAndNeverASessionId) and named-session test (freshSpawnMintsASessionIdAndPassesTheName) pass unchanged.

Build

mvn clean install: Tests run: 1071, Failures: 0, Errors: 0, Skipped: 0 — BUILD SUCCESS.

## What and why ClaudeCodeLauncher.applySessionIdentity minted a session id only when the caller passed sessionName or resumeSessionId. fleet_spawn treats both as optional, so an ordinary spawn minted nothing and agentSessionId stayed null for the life of the member. Unlike opencode, a claude-code session id is fixed at launch: nothing resolves it afterwards, so resume is only possible when the id was requested at spawn time. Fix: mint a UUID and pass the --session-id flag on every non-resume spawn. A resume spawn still passes -r <id> (the two are mutually exclusive) and a sessionName still rides as -n. ## Verified against the real binary (claude 2.1.252) - claude --help lists --session-id <uuid>: Use a specific session ID for the conversation (must be a valid UUID). - A non-UUID value is rejected at argument parsing with 'Invalid session ID. Must be a valid UUID.' (exit 1, no network call). So the UUID.randomUUID() mint is a hard requirement, not cosmetic. - A valid UUID is accepted by the binary; the session transcript lands at the documented on-disk location keyed by that UUID, which is exactly what a later -r resumes on. - No conflict with any other flag the launcher passes: --mcp-config, --append-system-prompt, --append-system-prompt-file, --agent, --model, --autocompact and -n are all independent in the help output, and the real binary parses them all together with --session-id without complaint. - The only documented interaction is --session-id vs -r/--resume (see also --fork-session), which the launcher already never combines. - Note: this shell's operator OAuth was expired, so a full round-trip model call could not complete; the flag was still accepted and the session file written before the auth failure. ## Tests - NEW ClaudeCodeLauncherTest.plainSpawnMintsASessionIdSoEveryMemberIsResumable: a plain spawn (no sessionName, no resumeSessionId) passes --session-id with a valid UUID and returns that id from agentSessionId(). Watched it fail against the pre-change launcher first: expected <true> but was <false> (the flag was absent from argv). - Updated two legacy byte-identical argv tests (startResolvesTheExecutableFromKindAndDropsArgvZero, noFleetFlagsWhenMcpUrlAbsent) to accept the newly-minted session flag while keeping their original guarantees. - Updated SessionManagerTest (acquireWithNeitherSessionFieldStillMintsAnAgentSessionId): a plain acquire now records a non-null agentSessionId and the roster shows it. Pre-change this test asserted null — which is precisely the defect this ticket removes. This file is outside the nominal scope (ClaudeCodeLauncher and its tests) but drove the real launcher through SessionManager, so the build cannot be green without it. - Existing resume test (resumeSpawnPassesDashRAndNeverASessionId) and named-session test (freshSpawnMintsASessionIdAndPassesTheName) pass unchanged. ## Build mvn clean install: Tests run: 1071, Failures: 0, Errors: 0, Skipped: 0 — BUILD SUCCESS.
agent added 1 commit 2026-09-01 06:31:57 +02:00
#214: mint a session id on every claude-code spawn, so every member is resumable
CI / contract (pull_request) Successful in 1m15s
CI / build (pull_request) Successful in 1m52s
ff3b49cd1e
Owner

Merged to main as bf0e09b. Full suite on the merged result: Tests run: 1079, Failures: 0, Errors: 0, Skipped: 0 — mvn clean install, unpiped, run by me.

You did the one thing the brief actually turned on: you checked the binary instead of reasoning about the flag. claude 2.1.252, --session-id requires a valid UUID and refuses anything else at argument parsing, and the only documented interaction is with -r. That is what made this safe to take the minting branch rather than the fallback.

I verified the -r interaction myself in the merged code, because that is the one way this could break a working path: the resume branch adds -r and returns before the mint, so -r and --session-id are never on the same argv. The javadoc's claim matches the code.

I will confirm a live claude-code member still starts with the new flag after I redeploy — you correctly said you could not do that and did not claim it.

Merged to `main` as `bf0e09b`. Full suite on the merged result: **Tests run: 1079, Failures: 0, Errors: 0, Skipped: 0** — `mvn clean install`, unpiped, run by me. You did the one thing the brief actually turned on: you checked the binary instead of reasoning about the flag. `claude 2.1.252`, `--session-id` requires a valid UUID and refuses anything else at argument parsing, and the only documented interaction is with `-r`. That is what made this safe to take the minting branch rather than the fallback. I verified the `-r` interaction myself in the merged code, because that is the one way this could break a working path: the resume branch adds `-r` and **returns before** the mint, so `-r` and `--session-id` are never on the same argv. The javadoc's claim matches the code. I will confirm a live claude-code member still starts with the new flag after I redeploy — you correctly said you could not do that and did not claim it.
ltms closed this pull request 2026-09-01 09:13:39 +02:00
Some checks are pending
CI / contract (pull_request) Successful in 1m15s
CI / build (pull_request) Successful in 1m52s

Pull request closed

Sign in to join this conversation.