fix(install): replace binaries atomically to avoid macOS SIGKILL on upgrade - #797
Merged
Merged
Conversation
…pgrade
cp onto an existing binary reuses the inode, which poisons the macOS
kernel's cached code-signature state for that vnode. Every exec after an
upgrade is then killed with SIGKILL ("zsh: killed af") even though
codesign --verify passes on disk. Stage to a temp file and mv into place
so upgrades always land on a fresh inode. Applies to both the agentfield
binary and the af-tray binary.
Co-Authored-By: Claude Fable 5 <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.
Problem
Re-running the install script over an existing installation left
afunrunnable on macOS — every invocation died withzsh: killed af(SIGKILL), including the script's own--versionverification step. Confusingly,codesign --verifyon the installed binary passed.Root cause:
install_binaryusedcpto overwrite the existing binary in place, reusing the same inode. When the old binary has a live process at overwrite time (e.g.af serverrunning during acurl | bashupgrade — the common case), the macOS kernel's cached per-vnode code-signing state is invalidated, and the path becomes permanently poisoned: every subsequent exec is either SIGKILLed or hangs in uninterruptible wait (Ustate, immune tokill -9), even though the on-disk signature verifies clean.Verified locally (Darwin 24.6.0, arm64)
cpover an idle binary (same or different content): fine — no poisoning.cpover a binary while a process was executing it: reproduced — subsequent execs hang in uninterruptible kernel wait; the poisoning persists after the old process dies.mvreplacement to the poisoned path: fixed — next exec runs normally (fresh inode clears the kernel's cached state).mvpath: run correctly.Fix
Stage the binary to a temp file in the install dir and
mvit into place. Rename gives upgrades a fresh inode and is atomic, so there is no window where the binary is missing and no in-place write to a mapped file. Applied to both copy sites:install_binary— theagentfieldCLI binaryinstall_tray— theaf-traybinary (same latent bug; the tray force-restarts on update, so an upgrade over a running tray would hit it)Workaround for already-broken installs
codesign -f -s - ~/.agentfield/bin/agentfield(re-sign in place), or delete the file and reinstall with the fixed script. Processes already hung inUstate clear on reboot.🤖 Generated with Claude Code