From fbee4b1facd6a50f40f1234547917255f590e394 Mon Sep 17 00:00:00 2001 From: Mark Olson <8377635+m4rkolson@users.noreply.github.com> Date: Sat, 5 Sep 2026 14:49:42 -0400 Subject: [PATCH] Fix Pulse menu bar installer reporting success on a failed load install.sh used `launchctl load`, the legacy launchctl API, which writes "Load failed: N: ..." to stderr but exits 0. `set -euo pipefail` therefore never fired, and the script went on to print "Installation complete" and "LifeOS Pulse menu bar is now running" whether or not anything started. Two failures had to be closed, not one: - `launchctl load` returning 0 on failure. Replaced with the domain-target API (`launchctl bootstrap gui/$UID`), which returns a real exit status, preceded by a `bootout` so a stale registration of the same label cannot fail the bootstrap with EX 5 (Input/output error). - A bootstrap succeeding while nothing runs. launchd accepts a job whose plist names a nonexistent program and returns 0, which is exactly the case where the app bundle is missing from ~/Applications. Added a poll on the deployed binary so success is claimed only once a process exists, and a failure path that points at the menubar log and prints the job's last exit code. Tested on macOS 15 (Darwin 25.6.0), Apple silicon: red against a label pointing at a nonexistent binary (exit 1), green against a real one (exit 0), and end to end against a live install, which now completes with no "Load failed" line and leaves the job at state = running with last exit code = (never exited), where it previously showed 78. Fixes #2068 Co-Authored-By: Claude Opus 5 (1M context) --- .../install/LIFEOS/PULSE/MenuBar/install.sh | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/LifeOS/install/LIFEOS/PULSE/MenuBar/install.sh b/LifeOS/install/LIFEOS/PULSE/MenuBar/install.sh index 84bd645243..ecf66084aa 100755 --- a/LifeOS/install/LIFEOS/PULSE/MenuBar/install.sh +++ b/LifeOS/install/LIFEOS/PULSE/MenuBar/install.sh @@ -7,6 +7,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" HOME_DIR="$HOME" APP_NAME="LifeOS Pulse" +BINARY_NAME="LifeOS Pulse" APP_DIR="$HOME_DIR/Applications" APP_DEST="$APP_DIR/$APP_NAME.app" OLD_APP="$APP_DIR/PAI Monitor.app" @@ -70,8 +71,37 @@ echo " Installed $PLIST_DST" # Ensure logs directory exists mkdir -p "$HOME_DIR/.claude/LIFEOS/PULSE/logs" -launchctl load "$PLIST_DST" -echo " Loaded $PLIST_LABEL" +# `launchctl load` is the legacy API: it prints "Load failed: N: ..." to stderr +# but still exits 0, so `set -e` cannot see it. Use the domain-target API, which +# returns a real exit status, and bootout first so a stale registration of the +# same label cannot fail the bootstrap with EX 5 (Input/output error). +DOMAIN="gui/$(id -u)" +launchctl bootout "$DOMAIN/$PLIST_LABEL" 2>/dev/null || true + +if ! launchctl bootstrap "$DOMAIN" "$PLIST_DST"; then + echo " ERROR: launchctl bootstrap failed for $PLIST_LABEL" >&2 + echo " The menu bar is NOT installed. Plist: $PLIST_DST" >&2 + exit 1 +fi +echo " Bootstrapped $PLIST_LABEL" + +# A successful bootstrap only means launchd accepted the job definition. +# Confirm the process actually came up before claiming the menu bar is running. +BINARY_PATH="$APP_DEST/Contents/MacOS/$BINARY_NAME" +for _ in $(seq 1 10); do + if pgrep -f "$BINARY_PATH" >/dev/null 2>&1; then + RUNNING=1 + break + fi + sleep 0.5 +done + +if [ "${RUNNING:-0}" -ne 1 ]; then + echo " ERROR: $PLIST_LABEL was bootstrapped but no process is running." >&2 + echo " Check $HOME_DIR/.claude/LIFEOS/PULSE/logs/menubar-stderr.log" >&2 + launchctl print "$DOMAIN/$PLIST_LABEL" 2>&1 | grep -E "last exit code|state =" >&2 || true + exit 1 +fi echo "" echo "=== Installation complete ==="