Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions plugins/autonomy/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ merged work-package PRs (#333, #343, #356, #372, #377, #600, #676).
now carries all three (verdict aggregation under the unanimity invariant including the
timeout and no-verdict cases, refusing to count two checkers that resolve to one instance, and
lens drawing), each stated as a hole until the build trigger fires.
- **`lane-stop-gate.test.sh` test-hygiene fixes from the #2065 verification pass (#2086).** The FIFO
hang case now fails closed when `mkfifo` is unavailable, uses `timeout` instead of `kill -9` on a
subshell that can orphan a grandchild, and `rc0` rejects an empty exit-code argument. The hook
comment records that `umask 077` is advisory on MSYS and documents the mid-lane downgrade over-gate
window.

## [0.16.2]

Expand Down
7 changes: 6 additions & 1 deletion plugins/autonomy/hooks/lane-stop-gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ gate_arm_owned() {
# a symlink aimed elsewhere decides nothing. Leave it untouched and honor.
[[ ! -e "$claim" || (-f "$claim" && ! -L "$claim") ]] || return 0
if [[ -n "$me" ]] && (
# umask 077 is best-effort on MSYS (the claim may still land 644); the
# exclusive-create is what binds the record, not the file mode.
umask 077
set -o noclobber
printf '%s\n' "$me" >"$claim"
Expand Down Expand Up @@ -231,7 +233,9 @@ gate_load_arm_record() {
# Compatibility read: a record claimed before the sidecar existed carries its
# owner in the record itself and has no claim file. That field stays
# authoritative so an upgrade cannot let a second session claim a record
# already bound to a live lane; nothing writes it any more.
# already bound to a live lane; nothing writes it any more. Mid-lane downgrade
# (newer hook → older without sidecar support) can leave a sidecar the older
# hook ignores — over-gating only; re-arm after downgrade.
claimed=$(printf '%s' "$json" | jq -r '.session_id // empty' 2>/dev/null)
if [[ -n "$claimed" ]]; then
[[ -n "$SESSION_ID" && "$claimed" == "$SESSION_ID" ]] || return 1
Expand Down Expand Up @@ -369,6 +373,7 @@ marker_identity() {
# the next alternative, and a host with neither returns the empty identity this
# function documents (#1784)
stat -c '%Y %s' -- "$1" 2>/dev/null ||
# portability-ok: BSD ladder rung paired with GNU `stat -c` above (#1784)
stat -f '%m %z' -- "$1" 2>/dev/null ||
printf ''
}
Expand Down
47 changes: 29 additions & 18 deletions plugins/autonomy/hooks/lane-stop-gate.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,10 @@ if [[ "$OUT" == *'LANE-STOP-OK'* ]]; then ok "the nudge names the default token,
# The claim-ownership cases below assert the exit code alongside the decision.
# A Stop hook that starts exiting non-zero — 127 for a helper that stopped
# resolving — is a regression a stdout-only assertion passes over in silence.
rc0() { [[ "$1" -eq 0 ]] || fail "$2 exited $1"; }
rc0() {
[[ -n "${1:-}" ]] || fail "$2 had no exit code to check"
[[ "$1" -eq 0 ]] || fail "$2 exited $1"
}

# --- Case 41: the PERSISTED owner decides, not the presenting session --------
# Case 32 proves a sequential replay is refused; that passes whether the claim
Expand Down Expand Up @@ -801,30 +804,38 @@ if is_block "$OUT"; then ok "an ownerless claim still honors the arm (fail direc
ARM_ID_10="fedcba9876543210"
bash "$ARM" --id "$ARM_ID_10" --cwd "$WORK" 2>/dev/null
rm -f "$SETTINGS"
mkfifo "$DATA_DIR/lane-arms/$ARM_ID_10.claim"
if ! mkfifo "$DATA_DIR/lane-arms/$ARM_ID_10.claim" 2>/dev/null; then
ok "mkfifo unavailable; skip non-regular claim path hang case"
elif ! command -v timeout >/dev/null 2>&1 ||
! timeout --help 2>&1 | grep -Fq -- '--kill-after'; then
ok "GNU timeout unavailable; skip non-regular claim path hang case"
else
FIFO_INPUT=$(build_input Stop "no token" false "" "sess-fifo")
FIFO_OUT="$WORK/fifo-out"
(cd "$UNRELATED" && printf '%s' "$(build_input Stop "no token" false "" "sess-fifo")" |
env -u CLAUDE_PLUGIN_OPTION_LANE_STOP_GATE_ENABLED \
-u CLAUDE_PLUGIN_OPTION_LANE_STOP_GATE_SENTINEL \
-u CLAUDE_PLUGIN_OPTION_LANE_STOP_GATE_MARKER \
-u CLAUDE_PLUGIN_DATA \
CLAUDE_PLUGIN_OPTION_LANE_NOTIFY_ENABLED=false \
CLAUDE_PLUGIN_OPTION_LANE_STOP_GATE_ARM_ID="$ARM_ID_10" \
bash "$HOOK" 2>/dev/null >"$FIFO_OUT") &
FIFO_PID=$!
for _ in $(seq 1 60); do
kill -0 "$FIFO_PID" 2>/dev/null || break
sleep 0.25
done
if kill -0 "$FIFO_PID" 2>/dev/null; then
kill -9 "$FIFO_PID" 2>/dev/null
FIFO_RC=0
# shellcheck disable=SC2016 # bash -c program is single-quoted; \$1..\$4 expand in the child
if timeout 5 bash -c '
Comment thread
kyle-sexton marked this conversation as resolved.
cd "$1" && printf "%s" "$2" |
env -u CLAUDE_PLUGIN_OPTION_LANE_STOP_GATE_ENABLED \
-u CLAUDE_PLUGIN_OPTION_LANE_STOP_GATE_SENTINEL \
-u CLAUDE_PLUGIN_OPTION_LANE_STOP_GATE_MARKER \
-u CLAUDE_PLUGIN_DATA \
CLAUDE_PLUGIN_OPTION_LANE_NOTIFY_ENABLED=false \
CLAUDE_PLUGIN_OPTION_LANE_STOP_GATE_ARM_ID="$3" \
bash "$4" 2>/dev/null
' _ "$UNRELATED" "$FIFO_INPUT" "$ARM_ID_10" "$HOOK" >"$FIFO_OUT" 2>/dev/null; then
FIFO_RC=0
else
FIFO_RC=$?
fi
if [[ "$FIFO_RC" -eq 124 ]]; then
fail "a planted FIFO at the claim path hung the Stop hook"
elif is_block "$(cat "$FIFO_OUT" 2>/dev/null)"; then
ok "a non-regular file at the claim path neither hangs the hook nor loses the gate"
else
fail "a planted FIFO at the claim path left the lane ungated"
fi
wait "$FIFO_PID" 2>/dev/null
fi

echo
echo "PASS=$PASS FAIL=$FAIL"
Expand Down
Loading