From f800c3ca6b9b6d7fc7058b6ff38925a9ee108d82 Mon Sep 17 00:00:00 2001 From: Mike Rapadas Date: Wed, 17 Jun 2026 14:15:18 -0400 Subject: [PATCH] =?UTF-8?q?fix(install):=20bump=20BOI=20pin=20v3.3.1?= =?UTF-8?q?=E2=86=92v3.3.2=20+=20atomic-rename=20deploy=20(FIX-017)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stale BOI_VERSION=v3.3.1 pin made install.sh (run by codex-parity tests) revert ~/.boi/src/boi to v3.3.1 + rebuild + relink on every run — a version flap that regressed the deployed daemon off the v3.3.2 stability cluster. Two changes: - VERSIONS: BOI_VERSION v3.3.1 → v3.3.2 (the released latest) so install.sh short-circuits instead of reverting. - install.sh: deploy the built binary as a real file via atomic rename (cp tmp + mv) instead of `ln -sf` to the live cargo build output. Rebuilding in place overwrote the Mach-O the running daemon was mapped from → macOS AMFI SIGKILLed it ("Code Signature Invalid"); atomic rename gives a fresh inode so the live process keeps its own until its next restart. Fast-path now requires a non-symlink real file (`[ ! -L ]`) to force one-time migration off the old symlink layout. Co-Authored-By: Claude Opus 4.8 (1M context) --- VERSIONS | 2 +- install.sh | 25 ++++++++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/VERSIONS b/VERSIONS index 3fb1491..dd2823f 100644 --- a/VERSIONS +++ b/VERSIONS @@ -9,5 +9,5 @@ # 2.0.0") to the v2 TOML engine (control-socket daemon, ~/.boi/v2). The v3.0.0 # release supersedes the old 2.0.0 line (V1 lives only in git history now). -BOI_VERSION=v3.3.1 +BOI_VERSION=v3.3.2 HARNESS_VERSION=v0.8.0 diff --git a/install.sh b/install.sh index 8345cbf..eaa7d26 100755 --- a/install.sh +++ b/install.sh @@ -370,11 +370,16 @@ install_or_upgrade_boi() { # Fast path: the machine-owned build already provides the pinned version. # (Also makes repeated install.sh runs — e.g. from test suites — no-ops.) + # `boi_bin` must be a REAL FILE, not a symlink (FIX-017, 2026-06-17): the old + # layout symlinked `boi_bin` → the cargo build output and rebuilt it in place, + # so a rebuild overwrote the very Mach-O the running daemon was mapped from → + # macOS AMFI invalidated the live process's code signature and SIGKILLed it + # ("Code Signature Invalid"). We now deploy a real-file copy via atomic + # rename. `[ ! -L ]` forces a one-time migration off any pre-existing symlink. # `|| true` inside the substitution: a present-but-unrunnable binary (e.g. # interrupted build) must fall through to the rebuild below, not errexit # the whole installer. - if [ -x "$boi_bin" ] && \ - [ "$(readlink "$boi_bin" 2>/dev/null)" = "$boi_build/target/release/boi" ]; then + if [ -x "$boi_bin" ] && [ ! -L "$boi_bin" ]; then local current current="v$("$boi_bin" --version 2>/dev/null | awk '/^boi /{print $2}' | tail -1 || true)" if [ "$current" = "$BOI_VERSION" ]; then @@ -418,9 +423,19 @@ install_or_upgrade_boi() { tail -20 "$build_log" >&2 || true return } - # Symlink binary - ln -sf "$boi_build/target/release/boi" "$boi_bin" - echo " BOI $BOI_VERSION built and linked ✓" + # Deploy as a STABLE real file via atomic rename — never symlink to (or + # overwrite in place) the build output the running daemon is mapped from + # (FIX-017). The copy gets boi_bin a fresh inode; rename(2) is atomic, so + # a live daemon keeps its old inode alive until its next restart instead + # of being AMFI-SIGKILLed ("Code Signature Invalid"). Temp on the SAME + # filesystem (sibling path) so the rename cannot fall back to a copy. + local boi_tmp="$boi_bin.new.$$" + cp -f "$boi_build/target/release/boi" "$boi_tmp" && chmod +x "$boi_tmp" && mv -f "$boi_tmp" "$boi_bin" || { + echo " BOI: failed to install built binary to $boi_bin" >&2 + rm -f "$boi_tmp" 2>/dev/null || true + return + } + echo " BOI $BOI_VERSION built and installed (atomic) ✓" else echo " ⚠️ Rust/cargo not found — cannot build BOI binary" echo " Install Rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"