Skip to content

Commit 0f025cb

Browse files
authored
Merge pull request #86 from xyyy1420/dev
Block tracked Humanize loop state from entering git history
2 parents 67fa1ae + 421ae97 commit 0f025cb

6 files changed

Lines changed: 164 additions & 11 deletions

File tree

hooks/lib/loop-common.sh

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1221,7 +1221,7 @@ git_adds_humanize() {
12211221
# Check for direct .humanize reference (blocked regardless of other flags)
12221222
# Handles: .humanize, ./.humanize, path/to/.humanize, ".humanize", '.humanize'
12231223
# Pattern matches .humanize at start, after space, after / or ./ AND followed by end, /, or space
1224-
# This avoids over-blocking .humanizeconfig or .humanize-backup
1224+
# This avoids over-blocking .humanizeconfig or .humanize-backup.
12251225
if echo "$add_args_normalized" | grep -qE '(^|[[:space:]]|/)\.humanize($|/|[[:space:]])'; then
12261226
return 0
12271227
fi
@@ -1319,6 +1319,56 @@ IMPORTANT: The commit message must NOT contain the literal string \".humanize\"
13191319
load_and_render_safe "$TEMPLATE_DIR" "block/git-add-humanize.md" "$fallback"
13201320
}
13211321

1322+
# Return success if local Humanize runtime state has entered git tracking or the index.
1323+
# Untracked .humanize state is allowed; tracked or staged state must be blocked.
1324+
# Usage: git_has_tracked_humanize_state [project_root]
1325+
#
1326+
# Intentionally scoped to .humanize/ to stay consistent with git_adds_humanize,
1327+
# which explicitly allows unrelated paths like .humanize-backup or
1328+
# .humanizeconfig (see tests/test-humanize-escape.sh). ls-files covers both
1329+
# committed entries and paths staged via git add; paths the user has staged for
1330+
# removal via git rm --cached are correctly omitted so the user can unstick
1331+
# themselves without being re-blocked.
1332+
git_has_tracked_humanize_state() {
1333+
local project_root="${1:-.}"
1334+
1335+
if [[ ! -d "$project_root/.git" ]] && ! git -C "$project_root" rev-parse --git-dir >/dev/null 2>&1; then
1336+
return 1
1337+
fi
1338+
1339+
if git -C "$project_root" ls-files -- .humanize 2>/dev/null | grep -q '.'; then
1340+
return 0
1341+
fi
1342+
1343+
return 1
1344+
}
1345+
1346+
# Standard message for blocking tracked/staged .humanize state.
1347+
# Usage: git_tracked_humanize_blocked_message
1348+
git_tracked_humanize_blocked_message() {
1349+
local fallback="# Tracked Humanize State Blocked
1350+
1351+
Detected tracked or staged files under \`.humanize/\`.
1352+
1353+
These files are local Humanize loop state and must remain outside version control.
1354+
1355+
## Required Fix
1356+
1357+
1. Remove Humanize state from the index:
1358+
1359+
git rm --cached -r .humanize
1360+
1361+
2. Keep only real project files staged.
1362+
3. Retry the stop action after the local state is no longer tracked.
1363+
1364+
## Important
1365+
1366+
- Do NOT use \`git add -f\` on Humanize state files.
1367+
- Do NOT commit RLCR trackers, round summaries, contracts, or cancel/finalize markers."
1368+
1369+
load_and_render_safe "$TEMPLATE_DIR" "block/git-tracked-humanize.md" "$fallback"
1370+
}
1371+
13221372
# Standard message for blocking direct execution of hook scripts
13231373
# Usage: stop_hook_direct_execution_blocked_message
13241374
stop_hook_direct_execution_blocked_message() {

hooks/loop-codex-stop-hook.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,21 @@ if [[ "$GIT_IS_REPO" == "true" ]]; then
622622
GIT_ISSUES=""
623623
SPECIAL_NOTES=""
624624

625+
if git_has_tracked_humanize_state "$PROJECT_ROOT"; then
626+
cleanup_stale_index_lock
627+
REASON=$(git_tracked_humanize_blocked_message)
628+
629+
jq -n \
630+
--arg reason "$REASON" \
631+
--arg msg "Loop: Blocked - tracked Humanize state detected, remove it from git first" \
632+
'{
633+
"decision": "block",
634+
"reason": $reason,
635+
"systemMessage": $msg
636+
}'
637+
exit 0
638+
fi
639+
625640
# Check for uncommitted changes (staged or unstaged) using cached status.
626641
# Exclude untracked .humanize/ paths and .humanize-* dash-separated legacy
627642
# variants from the dirty determination because local plugin state under

prompt-template/block/git-not-clean.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ You are trying to stop, but you have **{{GIT_ISSUES}}**.
55
**Required Actions**:
66
0. If the `code-simplifier` plugin is installed, use it to review and simplify your code before committing. Invoke via: `/code-simplifier`, `@agent-code-simplifier`, or `@code-simplifier:code-simplifier (agent)`
77
1. Review untracked files - add build artifacts to `.gitignore`
8-
2. Stage real changes: `git add <files>` (or `git add -A` if all files should be tracked)
8+
2. Stage only real changes with specific paths: `git add <files>`
99
3. Commit with a descriptive message following project conventions
1010

1111
**Important Rules**:
12+
- Do NOT use `git add -A`, `git add --all`, or `git add .` during an active RLCR loop
13+
- Never stage `.humanize/` or legacy `.humanize-*` loop artifacts
1214
- Commit message must follow project conventions
1315
- AI tools (Claude, Codex, etc.) must NOT have authorship in commits
1416
- Do NOT include `Co-Authored-By: Claude` or similar AI attribution
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Tracked Humanize State Blocked
2+
3+
Detected tracked or staged files under `.humanize/`.
4+
5+
These files are local Humanize loop state and must remain outside version control.
6+
7+
## Required Fix
8+
9+
1. Remove Humanize state from the index:
10+
11+
git rm --cached -r .humanize
12+
13+
2. Keep only real project files staged.
14+
3. Retry the stop action after the local state is no longer tracked.
15+
16+
## Important
17+
18+
- Do NOT use `git add -f` on Humanize state files.
19+
- Do NOT commit RLCR trackers, round summaries, contracts, or cancel/finalize markers.

tests/test-humanize-escape.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ assert_blocks "git add ./.humanize/" "Block: ./.humanize/ with trailing slash"
8585
assert_blocks "git add ./.humanize/file.md" "Block: ./.humanize/file.md"
8686
assert_blocks "git add path/to/.humanize" "Block: path/to/.humanize"
8787
assert_blocks "git add ../project/.humanize" "Block: ../project/.humanize"
88+
assert_blocks "git add .humanize/rlcr/2026-03-01_00-00-00/goal-tracker.md" "Block: RLCR goal tracker path"
89+
assert_blocks "git add .humanize/rlcr/2026-03-01_00-00-00/round-3-summary.md" "Block: RLCR round summary path"
90+
assert_blocks "git add .humanize/rlcr/2026-03-01_00-00-00/round-3-contract.md" "Block: RLCR round contract path"
8891

8992
# ========================================
9093
# Test Group 2: Quoted Path Variants
@@ -98,6 +101,7 @@ assert_blocks "git add '.humanize'" "Block: single-quoted .humanize"
98101
assert_blocks 'git add "./.humanize"' "Block: double-quoted ./.humanize"
99102
assert_blocks "git add './.humanize'" "Block: single-quoted ./.humanize"
100103
assert_blocks 'git add "path/to/.humanize"' "Block: double-quoted path/to/.humanize"
104+
assert_blocks 'git add ".humanize/rlcr/2026-03-01_00-00-00/goal-tracker.md"' "Block: double-quoted RLCR file path"
101105

102106
# ========================================
103107
# Test Group 3: Combined Force and Path Variants
@@ -109,6 +113,7 @@ echo ""
109113
assert_blocks "git add -f ./.humanize" "Block: -f with ./.humanize"
110114
assert_blocks "git add --force ./.humanize" "Block: --force with ./.humanize"
111115
assert_blocks 'git add -f ".humanize"' "Block: -f with quoted .humanize"
116+
assert_blocks "git add -f .humanize/rlcr/2026-03-01_00-00-00/goal-tracker.md" "Block: -f with RLCR goal tracker"
112117

113118
# Force flag with broad scope (blocks gitignore bypass)
114119
assert_blocks "git add -f ." "Block: -f . (force with current dir)"

tests/test-stop-gate.sh

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,30 +115,92 @@ else
115115
fail "rlcr-stop-gate --project-root output contains expected block reason" "output containing BLOCK:" "$OUTPUT2"
116116
fi
117117

118-
# Test 3: No active loop -> gate allows exit (exit 0)
118+
# Test 3: Tracked Humanize state blocks before normal loop validation
119119
T3_DIR="$TEST_DIR/t3"
120-
mkdir -p "$T3_DIR/empty-project"
120+
mkdir -p "$T3_DIR"
121+
setup_active_loop_fixture "$T3_DIR/project"
122+
echo "tracked" > "$T3_DIR/project/.humanize/rlcr/2026-03-01_00-00-00/goal-tracker.md"
123+
git -C "$T3_DIR/project" add -f .humanize/rlcr/2026-03-01_00-00-00/goal-tracker.md
121124

122125
set +e
123126
(
124-
cd "$T3_DIR/empty-project"
127+
cd "$T3_DIR/project"
125128
"$GATE_SCRIPT"
126129
) > "$T3_DIR/out.txt" 2>&1
127130
EXIT3=$?
128131
set -e
129132

130-
if [[ "$EXIT3" -eq 0 ]]; then
131-
pass "rlcr-stop-gate exits 0 when no active loop exists"
133+
if [[ "$EXIT3" -eq 10 ]]; then
134+
pass "rlcr-stop-gate blocks tracked Humanize state"
132135
else
133136
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"
137+
fail "rlcr-stop-gate blocks tracked Humanize state" "exit 10" "exit $EXIT3; output: $OUTPUT3"
135138
fi
136139

137-
if grep -q "^ALLOW:" "$T3_DIR/out.txt" 2>/dev/null; then
138-
pass "rlcr-stop-gate reports ALLOW when no active loop"
140+
if grep -q "Tracked Humanize State Blocked" "$T3_DIR/out.txt" 2>/dev/null; then
141+
pass "rlcr-stop-gate reports tracked Humanize state with dedicated reason"
139142
else
140143
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"
144+
fail "rlcr-stop-gate reports tracked Humanize state with dedicated reason" "output containing Tracked Humanize State Blocked" "$OUTPUT3"
145+
fi
146+
147+
# Test 4: Unrelated dot-prefixed files that happen to start with .humanize-
148+
# must not be treated as loop state. .humanize-backup and .humanizeconfig are
149+
# explicitly allowed by the git add validator (tests/test-humanize-escape.sh);
150+
# the tracked-state guard must stay consistent and ignore them.
151+
T4_DIR="$TEST_DIR/t4"
152+
mkdir -p "$T4_DIR"
153+
setup_active_loop_fixture "$T4_DIR/project"
154+
echo "not loop state" > "$T4_DIR/project/.humanize-backup"
155+
echo "not loop state" > "$T4_DIR/project/.humanizeconfig"
156+
git -C "$T4_DIR/project" add -f .humanize-backup .humanizeconfig
157+
158+
set +e
159+
(
160+
cd "$T4_DIR/project"
161+
"$GATE_SCRIPT"
162+
) > "$T4_DIR/out.txt" 2>&1
163+
EXIT4=$?
164+
set -e
165+
166+
if [[ "$EXIT4" -eq 10 ]]; then
167+
pass "rlcr-stop-gate does not confuse .humanize-backup with loop state"
168+
else
169+
OUTPUT4=$(cat "$T4_DIR/out.txt" 2>/dev/null || true)
170+
fail "rlcr-stop-gate does not confuse .humanize-backup with loop state" "exit 10" "exit $EXIT4; output: $OUTPUT4"
171+
fi
172+
173+
if ! grep -q "Tracked Humanize State Blocked" "$T4_DIR/out.txt" 2>/dev/null; then
174+
pass "rlcr-stop-gate does not emit tracked-state reason for .humanize-backup"
175+
else
176+
OUTPUT4=$(cat "$T4_DIR/out.txt" 2>/dev/null || true)
177+
fail "rlcr-stop-gate does not emit tracked-state reason for .humanize-backup" "no Tracked Humanize State Blocked line" "$OUTPUT4"
178+
fi
179+
180+
# Test 5: No active loop -> gate allows exit (exit 0)
181+
T5_DIR="$TEST_DIR/t5"
182+
mkdir -p "$T5_DIR/empty-project"
183+
184+
set +e
185+
(
186+
cd "$T5_DIR/empty-project"
187+
"$GATE_SCRIPT"
188+
) > "$T5_DIR/out.txt" 2>&1
189+
EXIT5=$?
190+
set -e
191+
192+
if [[ "$EXIT5" -eq 0 ]]; then
193+
pass "rlcr-stop-gate exits 0 when no active loop exists"
194+
else
195+
OUTPUT5=$(cat "$T5_DIR/out.txt" 2>/dev/null || true)
196+
fail "rlcr-stop-gate exits 0 when no active loop exists" "exit 0" "exit $EXIT5; output: $OUTPUT5"
197+
fi
198+
199+
if grep -q "^ALLOW:" "$T5_DIR/out.txt" 2>/dev/null; then
200+
pass "rlcr-stop-gate reports ALLOW when no active loop"
201+
else
202+
OUTPUT5=$(cat "$T5_DIR/out.txt" 2>/dev/null || true)
203+
fail "rlcr-stop-gate reports ALLOW when no active loop" "output containing ALLOW:" "$OUTPUT5"
142204
fi
143205

144206
print_test_summary "RLCR Stop Gate Wrapper Test Summary"

0 commit comments

Comments
 (0)