Skip to content

Commit 553c03c

Browse files
committed
Fix stop-gate dead option, test temp dir leak, and install-skill cp atomicity
- Remove --tasks-base-dir from rlcr-stop-gate.sh (hook never consumed it) - Fix test-stop-gate.sh to use single setup_test_dir call with subdirs, preventing EXIT trap overwrite that leaked first temp directory - Add exit-0 (allow) test cases for stop gate with no active loop - Make install-skill.sh cp fallback atomic: copy to temp sibling first, then swap into place to avoid data loss on partial copy failure
1 parent f72feb5 commit 553c03c

3 files changed

Lines changed: 59 additions & 29 deletions

File tree

scripts/install-skill.sh

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,17 @@ sync_dir() {
8686
if command -v rsync >/dev/null 2>&1; then
8787
rsync -a --delete "$src/" "$dst/"
8888
else
89-
rm -rf "$dst"
90-
mkdir -p "$dst"
91-
cp -a "$src/." "$dst/"
89+
# Copy to a temp sibling first so the destination is not destroyed
90+
# if cp fails partway through (disk full, permission error, etc.).
91+
local tmp_dst
92+
tmp_dst="$(mktemp -d "$(dirname "$dst")/.sync_tmp.XXXXXX")"
93+
if cp -a "$src/." "$tmp_dst/"; then
94+
rm -rf "$dst"
95+
mv "$tmp_dst" "$dst"
96+
else
97+
rm -rf "$tmp_dst"
98+
die "failed to copy $src to $dst"
99+
fi
92100
fi
93101
}
94102

scripts/rlcr-stop-gate.sh

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# 20 - Wrapper/runtime error
1212
#
1313
# Usage:
14-
# scripts/rlcr-stop-gate.sh [--session-id ID] [--transcript-path PATH] [--tasks-base-dir PATH] [--project-root PATH] [--json]
14+
# scripts/rlcr-stop-gate.sh [--session-id ID] [--transcript-path PATH] [--project-root PATH] [--json]
1515
#
1616

1717
set -euo pipefail
@@ -23,7 +23,6 @@ HOOK_SCRIPT="$HUMANIZE_ROOT/hooks/loop-codex-stop-hook.sh"
2323

2424
SESSION_ID="${CLAUDE_SESSION_ID:-}"
2525
TRANSCRIPT_PATH="${CLAUDE_TRANSCRIPT_PATH:-}"
26-
TASKS_BASE_DIR=""
2726
PRINT_JSON="false"
2827

2928
usage() {
@@ -33,7 +32,6 @@ Usage: rlcr-stop-gate.sh [options]
3332
Options:
3433
--session-id ID Session ID forwarded to hook input
3534
--transcript-path PATH Transcript path forwarded to hook input
36-
--tasks-base-dir PATH Task directory override (tests/debug only)
3735
--project-root PATH Project root (default: repo root)
3836
--json Print raw hook JSON on block
3937
-h, --help Show this help
@@ -52,11 +50,6 @@ while [[ $# -gt 0 ]]; do
5250
TRANSCRIPT_PATH="$2"
5351
shift 2
5452
;;
55-
--tasks-base-dir)
56-
[[ -n "${2:-}" ]] || { echo "Error: --tasks-base-dir requires a value" >&2; exit 20; }
57-
TASKS_BASE_DIR="$2"
58-
shift 2
59-
;;
6053
--project-root)
6154
[[ -n "${2:-}" ]] || { echo "Error: --project-root requires a value" >&2; exit 20; }
6255
PROJECT_ROOT="$2"
@@ -94,15 +87,13 @@ fi
9487
HOOK_INPUT=$(jq -n \
9588
--arg session_id "$SESSION_ID" \
9689
--arg transcript_path "$TRANSCRIPT_PATH" \
97-
--arg tasks_base_dir "$TASKS_BASE_DIR" \
9890
--arg cwd "$PROJECT_ROOT" \
9991
'{
10092
hook_event_name: "Stop",
10193
stop_hook_active: false,
10294
cwd: $cwd,
10395
session_id: ($session_id | select(length > 0)),
104-
transcript_path: ($transcript_path | select(length > 0)),
105-
tasks_base_dir: ($tasks_base_dir | select(length > 0))
96+
transcript_path: ($transcript_path | select(length > 0))
10697
}')
10798

10899
# Capture hook exit code explicitly to map non-zero to exit 20 (wrapper error)

tests/test-stop-gate.sh

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,57 +58,88 @@ agent_teams: false
5858
EOF_STATE
5959
}
6060

61-
# Test 1: Default project root should be caller cwd (not plugin install dir)
61+
# Single setup_test_dir call to avoid EXIT trap overwrite and temp dir leak.
6262
setup_test_dir
63-
setup_active_loop_fixture "$TEST_DIR/project"
63+
64+
# Test 1: Default project root should be caller cwd (not plugin install dir)
65+
T1_DIR="$TEST_DIR/t1"
66+
mkdir -p "$T1_DIR"
67+
setup_active_loop_fixture "$T1_DIR/project"
6468

6569
set +e
6670
(
67-
cd "$TEST_DIR/project"
71+
cd "$T1_DIR/project"
6872
"$GATE_SCRIPT"
69-
) > "$TEST_DIR/out1.txt" 2>&1
73+
) > "$T1_DIR/out.txt" 2>&1
7074
EXIT1=$?
7175
set -e
7276

7377
if [[ "$EXIT1" -eq 10 ]]; then
7478
pass "rlcr-stop-gate default project root uses cwd and blocks active loop"
7579
else
76-
OUTPUT1=$(cat "$TEST_DIR/out1.txt" 2>/dev/null || true)
80+
OUTPUT1=$(cat "$T1_DIR/out.txt" 2>/dev/null || true)
7781
fail "rlcr-stop-gate default project root uses cwd and blocks active loop" "exit 10" "exit $EXIT1; output: $OUTPUT1"
7882
fi
7983

80-
if grep -q "^BLOCK:" "$TEST_DIR/out1.txt" 2>/dev/null; then
84+
if grep -q "^BLOCK:" "$T1_DIR/out.txt" 2>/dev/null; then
8185
pass "rlcr-stop-gate reports a real loop blocking reason"
8286
else
83-
OUTPUT1=$(cat "$TEST_DIR/out1.txt" 2>/dev/null || true)
87+
OUTPUT1=$(cat "$T1_DIR/out.txt" 2>/dev/null || true)
8488
fail "rlcr-stop-gate reports a real loop blocking reason" "output containing BLOCK:" "$OUTPUT1"
8589
fi
8690

8791
# Test 2: --project-root override works from outside target repository
88-
setup_test_dir
89-
setup_active_loop_fixture "$TEST_DIR/project"
92+
T2_DIR="$TEST_DIR/t2"
93+
mkdir -p "$T2_DIR"
94+
setup_active_loop_fixture "$T2_DIR/project"
9095

9196
set +e
9297
(
93-
cd "$TEST_DIR"
94-
"$GATE_SCRIPT" --project-root "$TEST_DIR/project"
95-
) > "$TEST_DIR/out2.txt" 2>&1
98+
cd "$T2_DIR"
99+
"$GATE_SCRIPT" --project-root "$T2_DIR/project"
100+
) > "$T2_DIR/out.txt" 2>&1
96101
EXIT2=$?
97102
set -e
98103

99104
if [[ "$EXIT2" -eq 10 ]]; then
100105
pass "rlcr-stop-gate --project-root override blocks using target repo loop"
101106
else
102-
OUTPUT2=$(cat "$TEST_DIR/out2.txt" 2>/dev/null || true)
107+
OUTPUT2=$(cat "$T2_DIR/out.txt" 2>/dev/null || true)
103108
fail "rlcr-stop-gate --project-root override blocks using target repo loop" "exit 10" "exit $EXIT2; output: $OUTPUT2"
104109
fi
105110

106-
if grep -q "^BLOCK:" "$TEST_DIR/out2.txt" 2>/dev/null; then
111+
if grep -q "^BLOCK:" "$T2_DIR/out.txt" 2>/dev/null; then
107112
pass "rlcr-stop-gate --project-root output contains expected block reason"
108113
else
109-
OUTPUT2=$(cat "$TEST_DIR/out2.txt" 2>/dev/null || true)
114+
OUTPUT2=$(cat "$T2_DIR/out.txt" 2>/dev/null || true)
110115
fail "rlcr-stop-gate --project-root output contains expected block reason" "output containing BLOCK:" "$OUTPUT2"
111116
fi
112117

118+
# Test 3: No active loop -> gate allows exit (exit 0)
119+
T3_DIR="$TEST_DIR/t3"
120+
mkdir -p "$T3_DIR/empty-project"
121+
122+
set +e
123+
(
124+
cd "$T3_DIR/empty-project"
125+
"$GATE_SCRIPT"
126+
) > "$T3_DIR/out.txt" 2>&1
127+
EXIT3=$?
128+
set -e
129+
130+
if [[ "$EXIT3" -eq 0 ]]; then
131+
pass "rlcr-stop-gate exits 0 when no active loop exists"
132+
else
133+
OUTPUT3=$(cat "$T3_DIR/out.txt" 2>/dev/null || true)
134+
fail "rlcr-stop-gate exits 0 when no active loop exists" "exit 0" "exit $EXIT3; output: $OUTPUT3"
135+
fi
136+
137+
if grep -q "^ALLOW:" "$T3_DIR/out.txt" 2>/dev/null; then
138+
pass "rlcr-stop-gate reports ALLOW when no active loop"
139+
else
140+
OUTPUT3=$(cat "$T3_DIR/out.txt" 2>/dev/null || true)
141+
fail "rlcr-stop-gate reports ALLOW when no active loop" "output containing ALLOW:" "$OUTPUT3"
142+
fi
143+
113144
print_test_summary "RLCR Stop Gate Wrapper Test Summary"
114145
exit $?

0 commit comments

Comments
 (0)