Repository navigation
fix(statusline): concurrent session creates no longer fall out of tmux - #531
Merged
Merged
Conversation
… creates stay in tmux ensureStatusLineExporterScript() rewrites ~/.codeman/statusline-exporter.sh via a temp file + rename whenever the script content changes (a fresh data dir, or a release that changes it). The temp name was pid + Date.now(), so claude sessions created in the same millisecond (spawn_workers, a multi-tab Run) shared one temp path: the first rename consumed it and every other writer failed with ENOENT on chmod or rename. createSession() treats that as a mux failure and falls back to a direct PTY, so those sessions silently ran outside tmux (no reattach after a server restart) while quick-start still reported success. Measured on a fresh isolated instance, 4 concurrent claude quick-starts: master put 2 of 4 in tmux in both rounds; with this change 4 of 4, both rounds. The temp suffix now comes from randomBytes, like the skill writer in the same file and user-store.ts already do. The new test freezes Date.now() and runs eight refreshes at once; it fails on master with the same ENOENT. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
When Claude sessions are created at the same moment (the codeman skill's
spawn_workers, or a multi-tab Run), some of them can silently end up outside tmux: they run on Codeman's direct-PTY fallback, so they don't survive a server restart, while the create call still reports success. This PR fixes the cause, a temp-file name that wasn't unique, with a one-line change plus a regression test. No API, UI or behavior changes otherwise.Cause
Every claude launch goes through
ensureStatusLineExporterScript()(src/hooks-config.ts), which keeps~/.codeman/statusline-exporter.shup to date for the plan-usage chip. When the script's content needs changing (a fresh data dir, or a release that changes the script), it writes a temp file and renames it into place, so live sessions never execute a half-written file.The temp name was
statusline-exporter.sh.<pid>.<Date.now()>.tmp. Within one server process, two creates in the same millisecond produce the same temp path. The first writer renames it away, and the others then fail withENOENTonchmodorrename.TmuxManager.createSession()treats that as a mux failure, andSessionfalls back to a direct PTY:Fix
The temp suffix now comes from
randomBytes(6), the same pattern already used by the skill-file writer in the same file and byuser-store.ts. Concurrent writers each rename their own complete, executable copy into place; the content is identical, so whichever lands last is correct.Testing
New test in
test/hooks-config.test.ts: freezesDate.now()(which is what two creates in one millisecond amount to), runs eight refreshes at once, and checks they all succeed, the script is fresh and executable, and no temp files are left. On master it fails with the sameENOENT ... renameerror as the log line above; with the fix it passes.hooks-configandstatusline-cli-flagsuites pass (104 tests), and tsc, eslint and prettier are clean.End to end on a fresh isolated instance (
CODEMAN_INSTANCE, its own tmux socket, data dir and cases dir), firing four concurrent claudePOST /api/quick-startcalls with real Claude Code 2.1.289:On master, quick-start returned success for all four in both rounds.
Notes
src/custom-model-hosts.tshas a similar fixed temp name (<file>.<pid>.tmp) on an async write path, but its writers are user-driven route handlers and it also has a separate read-modify-write question, so it's left out of this PR.