Repository navigation
Conversation
…res dead owners acquireBuildLock already took over a .build.lock whose pid was gone, but: - the owner probe counted every kill(pid, 0) error as "dead", so a lock held by a live process of another user (EPERM) was taken over; - takeover was unlink-then-create, so several processes that saw the same stale lock could each remove the fresh lock another had just created and all proceed (8 concurrent contenders: 2-5 winners in every trial); - nothing said a lock had been taken over; - status() reported build_in_progress: true for any existing lock file, including one left by a process that no longer exists. Now ESRCH means dead and EPERM (or any other error) means held. Takeover goes through an exclusive claim file per stale lock instance; only the claim holder re-checks the lock content and replaces it, and a claim left by a dead claimant is skipped. A one-line notice goes to stderr (onNotice overrides it). Unreadable locks use the same takeover, but only once they are older than 5 s, since a fresh one is still being written. The 15-minute age rule is unchanged. status() reports build_in_progress: false when the recorded owner pid no longer exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 25, 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.
Problem
acquireBuildLockalready takes over a.build.lockwhose owner pid is gone, or which is older than 15 minutes. A plainsyncafter a SIGKILLed run therefore goes through onmain. Around that path, four things were wrong:isPidAlivetreated everykill(pid, 0)error as dead. A lock held by a live process of another user (EPERM) was taken over.mainlet 2 to 5 of them through in every one of 20 trials.status()/graph_statusreportedbuild_in_progress: truefor any existing lock file, including one left by a process that no longer exists. That is what made the leftover lock look like a blocker.Fix
lockOwnerState(pid)treats ESRCH (or an invalid pid) as dead. EPERM, or any other error, counts as held..build.lock.takeover-<hash>-<gen>,openSync(…, 'wx')).[gsd-graph] taking over stale build lock <path> (pid 1234 is not running; started … by cli). A newonNoticeoption overrides it.status()reportsbuild_in_progress: falsewhen the recorded owner pid no longer exists. A live owner still reportstrue, even past 15 minutes, because the build may simply be long-running.build_lockedtroubleshooting row indocs/FULL-GUIDE.mdis updated.Tests
tests/lock.test.ts:main's lock code, the same setup gave 2 to 5 winners in 20 of 20 trials; with this change, exactly 1 in 20 of 20.tests/status.test.ts:build_in_progressisfalsefor an exited owner pid andtruefor a live one.npm run buildandnpm test(438/438) pass on Node 22 and 24;npm run test:coveragepasses on Node 24.Fixes #5
🤖 Generated with Claude Code