Captain session identity: session_id wiring (#709) + self-describing -n names (#708) - #710
Merged
Conversation
Resolve the captain's own sessionId from the same registry entry statusFor already reads (keyed by the launch-time socket path) and wire it into ClaudePeerChannel's sessionIdFor, instead of hardcoding undefined. Restores the pid-reuse guard and drops the "Another Claude session sent a message" wrapper on captain-bound sends. Falls back to omitting session_id (never throws) when the captain hasn't registered yet.
squadrant never passed claude's `-n, --name` flag, so every session's display name auto-derived from the cwd basename (e.g. "squadrant-fix-706-67", "helpa-06") — indistinguishable via ListAgents. A crew that could not tell its captain apart from an unrelated session sent its PR report to the wrong one. Adds `@squadrant/shared`'s captainSessionName/crewSessionName (sanitized to the same safe-filename charset captainSocketPath() already enforces) and wires them into both claude launch paths: buildAgentCmd (captain/command, launch.ts) and the claude driver's buildCommand (crew, crew-spawn.ts). Format: "squadrant-captain-<project>" and "squadrant-crew-<project>-<name>". `-n` is claude-only — added as an optional SpawnOptions field only the claude driver reads; codex/gemini/opencode buildCommand implementations were checked and ignore unknown fields safely, so they're unaffected.
This was referenced Aug 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two related identity gaps closed together (same files, one branch):
#709 — captain-bound channel now carries
session_id.captain-channel-factory.tshardcodedsessionIdFor: () => undefined. Resolves it from the same registry entrystatusForalready reads (keyed by the launch-time socket path viareadClaudeStatusBySocketPath), extracted into a small testablecaptainSessionIdFor. This:registry.ts:137(was silently skipped for every captain send)CREW DONE/ping/Telegram sendssession_id(never throws) when the captain hasn't registered in~/.claude/sessions/yet#708 — captains and crews now get a self-describing name. squadrant never passed claude's
-n, --nameflag, so display names auto-derived from the cwd basename (squadrant-fix-706-67,helpa-06) — a crew callingListAgentscouldn't tell its own captain apart from an unrelated session and misrouted its report. AddedcaptainSessionName(project)/crewSessionName(project, name)in@squadrant/shared(sanitized to the same safe-filename charsetcaptainSocketPath()already enforces) and wired into both claude launch paths:buildAgentCmd(captain/command,launch.ts) →squadrant-captain-<project>buildCommand(crew,crew-spawn.ts) →squadrant-crew-<project>-<name>-nis claude-only. Added as an optionalSpawnOptions.sessionNamefield that only the claude driver reads — codex/gemini/opencodebuildCommandimplementations were checked and ignore unknown fields safely (each reads only the specificopts.*keys it cares about), so this is a no-op for them.Proof (#709 success criterion)
packages/cli/src/lib/__tests__/captain-channel-factory.test.tsbuilds a realClaudePeerChannelwired the same waybuildCaptainChannel()is, with a stubwirecapturing the envelope:envelope.session_id === "sess-captain-1"envelope.session_id === undefined,send()still resolves (no throw)Test plan
pnpm buildcleanpnpm test: 2734 → 2747 tests (207 → 208 files), all passing — 13 new tests, none deletednode dist/index.js --helpexit 0node dist/squadrantd.js --helpexit 0registry.test.ts(readClaudeStatusBySocketPath sessionId),captain-channel-factory.test.ts(new, envelope proof),session-name.test.ts(new),launch.test.ts(resolveCaptainSessionName),launch-captain-socket.test.ts(per-project name regression, same class of bug as [agent-report] launch.ts builds the captain messaging socket from the positional arg — every --all / parallel launch collides on squadrant-captain-undefined.sock #706),crew-spawn.test.ts(sessionName wiring)Not touched: daemon, captainChannel/controlChannel semantics, no drive-by refactors.