Symptom
After gstack setup (fresh install or upgrade), Bun-compiled binaries in ~/.claude/skills/gstack/{browse,design,make-pdf,bin}/ are killed by macOS Gatekeeper with SIGKILL (exit status 137) on first invocation. The setup itself reports success, so the failure surfaces later when a skill tries to call $B, $D, etc.
Repro on my machine
macOS 14.x, Apple Silicon (arm64). gstack 1.15.0.0 (HEAD 6209163 in ~/.claude/skills/gstack/browse/dist/.version).
$ for b in browse/dist/browse browse/dist/find-browse design/dist/design make-pdf/dist/pdf bin/gstack-global-discover; do
p="$HOME/.claude/skills/gstack/$b"
codesign -dv "$p" 2>&1 | head -1
done
/Users/.../browse/dist/browse: code object is not signed at all
/Users/.../browse/dist/find-browse: code object is not signed at all
/Users/.../design/dist/design: code object is not signed at all
/Users/.../make-pdf/dist/pdf: code object is not signed at all
/Users/.../bin/gstack-global-discover: code object is not signed at all
5/5 binaries unsigned despite the codesign loop in setup (added in #1056 / v0.18.4.0).
Manually running codesign --remove-signature <bin> && codesign -s - -f <bin> produces a working ad-hoc signature and the binary runs fine, so the codesign machinery itself works on this machine — it's the setup-time invocation that didn't take.
Root cause (3 compounding issues in setup lines 247-264)
codesign --remove-signature "$_bin_path" 2>/dev/null || true
if ! codesign -s - -f "$_bin_path" 2>/dev/null; then
log "warning: codesign failed for $_bin (binary may not run on Apple Silicon)"
fi
2>/dev/null discards the actual codesign error. Whatever caused the failure on a given machine (transient lock, fs ACL, Xcode CLT state, etc.) is invisible — both to the user and to anyone trying to debug from a bug report.
log "warning: ..." routes to the log channel, not the user's terminal. The setup completes with no visible indication that a critical step failed.
- The block lives inside the
NEEDS_BUILD=1 branch. If a subsequent gstack setup invocation skips the build (binary exists from a previous half-broken install), the codesign block is also skipped — re-running setup doesn't self-heal a broken install.
The combined effect is that any transient codesign failure during the one setup run that built the binary leaves the install permanently broken from the user's perspective, with no user-visible signal until a skill actually calls into the binary and gets exit 137.
Suggested fix
Three small, defensive changes in setup:
- Move the codesign loop out of the
NEEDS_BUILD=1 branch so it runs on every gstack setup. Idempotent and cheap (<1s for all 5 binaries).
- Capture codesign's stderr instead of discarding it; surface it on failure.
- After signing, verify with
codesign -dv and fail the setup loudly if any binary remains unsigned. Don't claim "ready" for a half-broken install.
Sketch:
if [ "$(uname -s)" = "Darwin" ] && [ "$(uname -m)" = "arm64" ]; then
_sign_failed=()
for _bin in browse/dist/browse browse/dist/find-browse design/dist/design make-pdf/dist/pdf bin/gstack-global-discover; do
_bin_path="$SOURCE_GSTACK_DIR/$_bin"
[ -f "$_bin_path" ] && [ -x "$_bin_path" ] || continue
codesign --remove-signature "$_bin_path" >/dev/null 2>&1 || true
if ! _err=$(codesign -s - -f "$_bin_path" 2>&1); then
echo "codesign failed for $_bin: $_err" >&2
_sign_failed+=("$_bin")
continue
fi
if ! codesign -dv "$_bin_path" >/dev/null 2>&1; then
echo "codesign reported success but binary remains unsigned: $_bin" >&2
_sign_failed+=("$_bin")
fi
done
if [ ${#_sign_failed[@]} -gt 0 ]; then
echo "ERROR: gstack binaries failed to codesign — these will SIGKILL (exit 137) on first run:" >&2
printf ' - %s\n' "${_sign_failed[@]}" >&2
exit 1
fi
fi
A test in test/setup-codesign.test.ts shape — assert that after gstack setup, every binary in the loop returns 0 from codesign -dv.
Workaround for affected users
cd ~/.claude/skills/gstack
for b in browse/dist/browse browse/dist/find-browse design/dist/design make-pdf/dist/pdf bin/gstack-global-discover; do
[ -f "$b" ] && [ -x "$b" ] || continue
codesign --remove-signature "$b" 2>/dev/null || true
codesign -s - -f "$b"
codesign -dv "$b" 2>&1 | head -1
done
Symptom
After
gstack setup(fresh install or upgrade), Bun-compiled binaries in~/.claude/skills/gstack/{browse,design,make-pdf,bin}/are killed by macOS Gatekeeper with SIGKILL (exit status 137) on first invocation. The setup itself reports success, so the failure surfaces later when a skill tries to call$B,$D, etc.Repro on my machine
macOS 14.x, Apple Silicon (arm64). gstack
1.15.0.0(HEAD6209163in~/.claude/skills/gstack/browse/dist/.version).5/5 binaries unsigned despite the codesign loop in
setup(added in #1056 / v0.18.4.0).Manually running
codesign --remove-signature <bin> && codesign -s - -f <bin>produces a working ad-hoc signature and the binary runs fine, so the codesign machinery itself works on this machine — it's the setup-time invocation that didn't take.Root cause (3 compounding issues in
setuplines 247-264)2>/dev/nulldiscards the actual codesign error. Whatever caused the failure on a given machine (transient lock, fs ACL, Xcode CLT state, etc.) is invisible — both to the user and to anyone trying to debug from a bug report.log "warning: ..."routes to the log channel, not the user's terminal. The setup completes with no visible indication that a critical step failed.NEEDS_BUILD=1branch. If a subsequentgstack setupinvocation skips the build (binary exists from a previous half-broken install), the codesign block is also skipped — re-running setup doesn't self-heal a broken install.The combined effect is that any transient codesign failure during the one setup run that built the binary leaves the install permanently broken from the user's perspective, with no user-visible signal until a skill actually calls into the binary and gets exit 137.
Suggested fix
Three small, defensive changes in
setup:NEEDS_BUILD=1branch so it runs on everygstack setup. Idempotent and cheap (<1s for all 5 binaries).codesign -dvand fail the setup loudly if any binary remains unsigned. Don't claim "ready" for a half-broken install.Sketch:
A test in
test/setup-codesign.test.tsshape — assert that aftergstack setup, every binary in the loop returns 0 fromcodesign -dv.Workaround for affected users