Skip to content

Keep killed git processes from leaving locks that block self-updates - #37

Merged
elitecoder merged 5 commits into
mainfrom
fix/git-lock-immunity
Sep 28, 2026
Merged

elitecoder merged 5 commits into
mainfrom
fix/git-lock-immunity

Conversation

@elitecoder

Copy link
Copy Markdown
Owner

Summary

Since 11:53 today, every self-update failed with Unable to create '.git/index.lock': File exists, so #36 never went live.

Where the lock came from. The pulse log shows build-ws-context workspace:281 rc=124: timeout after 30s at 11:53:02.747, the same second as the lock's mtime. The pulse's run() SIGKILLed build-ws-context's process group while its git status --porcelain held .git/index.lock, which git takes to refresh the index. A SIGKILL can't be caught, so the lock stayed. Seven repos on this machine hold such locks. Four of them match a pulse build-ws-context timeout to the second (2026-09-01, 09-10, 09-11, 09-28).

What changes

  • No new locks from Assistant.
    • A timed-out child's process group now gets SIGTERM, 3 seconds, then SIGKILL. Git removes its lock files on SIGTERM.
    • Background git status and git log calls pass --no-optional-locks, so they never take the index lock. Self-update's _git does the same.
  • Self-update gets past a stale lock.
    • Before touching the tree, it removes <git-dir>/index.lock if the lock is older than 10 minutes, lsof shows no process has it open, and no git process is working in the repo.
    • The git-process check matters: git commit -a closes the lock file but keeps the lock while hooks and the editor run.
    • It re-checks the file's inode and mtime right before deleting.
    • Each removal is a ledger entry, so a new cause can't hide behind the cleanup.
  • A clean tree.
    • .gitignore now covers .claude/worktrees/ (Claude Code's agent worktrees) and .claude/.assistant/.
    • Those untracked folders made the tree look dirty and set off the daily auto-stash, which is what hit the lock today.

Deploying this

The live checkout can't pull this until its current lock is gone. The fix can't clear it, because it's blocked by the same lock. ~/dev/assistant/.git/index.lock has to be removed once by hand.

Validation

  • Full suite: 2,971 passed, 1 skipped. The changed-code coverage gate passes.
  • Mutation probes all fail a test:
    • drop --no-optional-locks (the index gets rewritten)
    • skip the clear (the update is pull-failed)
    • drop the git-process check
    • drop the re-check
    • SIGKILL without SIGTERM
    • no group SIGKILL after the grace
  • Two independent reviews: lock-removal safety, and regressions and blast radius.

Known limits

  • Only index.lock is cleared. A stale HEAD.lock or ref lock still fails loudly as pull-failed.
  • The other six stale locks, in other repos, aren't touched by this change.

🤖 Generated with Claude Code

elitecoder and others added 5 commits September 28, 2026 15:51
…dates, and clear stale ones

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… deleting, bound every kill wait, and ledger each clear

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@elitecoder
elitecoder merged commit bc8f91c into main Sep 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant