From 26f86a09d0f6ae9073d584c9eea2efb2701afc4f Mon Sep 17 00:00:00 2001 From: Sihao Liu Date: Thu, 15 Jan 2026 17:16:38 -0800 Subject: [PATCH 1/3] refactor(path): change loop directory from .humanize-loop.local to .humanize/rlcr - Update all scripts and hooks to use new .humanize/rlcr/ path - Add backward compatibility for legacy .humanize-loop.local paths - Change .gitignore pattern from .humanize-loop.local/ to .humanize* - Update all tests to use new path structure - Update documentation and prompt templates - Bump version to 1.1.4 All acceptance criteria met: - AC-1: All references updated with dual-path support - AC-2: .gitignore uses .humanize* pattern - AC-3: All 266 tests pass - AC-4: Tests verify new path structure - AC-5: Version updated to 1.1.4 - AC-6: Monitor script works with new path - AC-7: Legacy paths handled gracefully --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- .claude/CLAUDE.md | 3 +-- .gitignore | 5 ++-- README.md | 16 ++++++------- commands/cancel-rlcr-loop.md | 4 ++-- hooks/lib/loop-common.sh | 5 ++-- hooks/loop-bash-validator.sh | 5 ++-- hooks/loop-codex-stop-hook.sh | 14 +++++------ hooks/loop-edit-validator.sh | 16 +++++++++---- hooks/loop-plan-file-validator.sh | 2 +- hooks/loop-read-validator.sh | 4 ++-- hooks/loop-write-validator.sh | 24 ++++++++++++------- .../block/git-not-clean-humanize-local.md | 8 ++++--- .../codex/full-alignment-review.md | 8 +++---- scripts/humanize.sh | 10 ++++---- scripts/setup-rlcr-loop.sh | 8 +++---- tests/test-plan-file-hooks.sh | 24 +++++++++---------- tests/test-state-exit-naming.sh | 20 ++++++++-------- tests/test-templates-comprehensive.sh | 4 ++-- 20 files changed, 102 insertions(+), 82 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 1a2c75dc..36a2c6ae 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,7 +8,7 @@ "name": "humanize", "source": "./", "description": "Humanize - An iterative development plugin that uses Codex to review Claude's work. Creates a feedback loop where Claude implements plans and Codex independently reviews progress, ensuring quality through continuous refinement.", - "version": "1.1.3" + "version": "1.1.4" } ] } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index ae353f56..3838abeb 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "humanize", "description": "Humanize - An iterative development plugin that uses Codex to review Claude's work. Creates a feedback loop where Claude implements plans and Codex independently reviews progress, ensuring quality through continuous refinement.", - "version": "1.1.3", + "version": "1.1.4", "author": { "name": "humania-org" }, diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index f1b8703b..02782b8d 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -3,6 +3,5 @@ This is a Claude Code plugin that provides iterative development with Codex revi # Humanize Project Rules - Everything about this project, including but not limited to implementations, comments, tests and documentations should be in English. No Emoji or CJK char is allowed. -- **MANDATORY**: Every commit MUST include a version bump in `.claude-plugin/plugin.json` and `README.md` (the "Current Version" line). This applies to ALL commits without exception - bug fixes, features, documentation changes, etc. Increment the patch version (e.g., 1.0.0 -> 1.0.1) for each commit. -- Every `git push` or `git commit` MUST confirm with user first, MUST NOT commit or push to remote without check with user. +- If under `main` branch, every commit MUST include a version bump in `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json` and `README.md` (the "Current Version" line). If not under `main` branch, please make sure that the current branch's `version` in those three files has a incremental update compared to that of `main` branch. The `version` must be identical in those three files. - Version number must be in format of `X.Y.Z` where X/Y/Z is numeric number. Version MUST NOT include anything other than `X.Y.Z`. For example, a good version is `9.732.42`; Bad version examples (MUST NOT USE): `3.22.7-alpha` (extra "-alpha" string), `9.77.2 (2026-01-07)` (useless date/timestamp). diff --git a/.gitignore b/.gitignore index 284dfecf..5f8d4a79 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ # Scratchpad temp -# Humanize loop state directory (used by start-rlcr-loop) -.humanize-loop.local/ +# Humanize state directories (used by start-rlcr-loop) +# Matches .humanize/ (current) and .humanize-loop.local/ (legacy) +.humanize* # Python cache __pycache__/ diff --git a/README.md b/README.md index 8b2ed039..1d5b557b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Humanize -**Current Version: 1.1.3** +**Current Version: 1.1.4** > Derived from the [GAAC (GitHub-as-a-Context)](https://github.com/SihaoLiu/gaac) project. @@ -72,17 +72,17 @@ Your plan file should contain: ### Step 3: Monitor Progress -All iteration artifacts are saved in `.humanize-loop.local//`: +All iteration artifacts are saved in `.humanize/rlcr//`: ```bash # View current round -cat .humanize-loop.local/*/state.md +cat .humanize/rlcr/*/state.md # View Claude's latest summary -cat .humanize-loop.local/*/round-*-summary.md | tail -50 +cat .humanize/rlcr/*/round-*-summary.md | tail -50 # View Codex's review feedback -cat .humanize-loop.local/*/round-*-review-result.md | tail -50 +cat .humanize/rlcr/*/round-*-review-result.md | tail -50 ``` **Real-time Monitoring Dashboard** (Recommended): @@ -113,7 +113,7 @@ This provides a real-time dashboard showing: **The loop is fully interruptible** - you can exit Claude Code at any time and resume later: -- **Loop state**: Controlled solely by the presence of `state.md` in the current loop directory (newest timestamp in `.humanize-loop.local/`) +- **Loop state**: Controlled solely by the presence of `state.md` in the current loop directory (newest timestamp in `.humanize/rlcr/`) - **Resume**: Simply restart Claude Code in the same directory - the loop continues automatically - **Cancel**: Rename `state.md` to `cancel-state.md` to stop the loop permanently @@ -122,7 +122,7 @@ This provides a real-time dashboard showing: /humanize:cancel-rlcr-loop # Or manually rename state file (find newest loop directory first) -LOOP_DIR=$(ls -1d .humanize-loop.local/*/ | sort -r | head -1) +LOOP_DIR=$(ls -1d .humanize/rlcr/*/ | sort -r | head -1) mv "${LOOP_DIR}state.md" "${LOOP_DIR}cancel-state.md" ``` @@ -241,7 +241,7 @@ humanize/ ## State Directory Structure -When loop is active, creates: `.humanize-loop.local//` +When loop is active, creates: `.humanize/rlcr//` **Files Created**: - `state.md` - Current round, config (YAML frontmatter). Presence indicates active loop. diff --git a/commands/cancel-rlcr-loop.md b/commands/cancel-rlcr-loop.md index 20328652..5da800be 100644 --- a/commands/cancel-rlcr-loop.md +++ b/commands/cancel-rlcr-loop.md @@ -1,6 +1,6 @@ --- description: "Cancel active RLCR loop" -allowed-tools: ["Bash(ls -1d .humanize-loop.local/*/)", "Bash(mv .humanize-loop.local/*/state.md .humanize-loop.local/*/cancel-state.md)", "Bash(cat .humanize-loop.local/*/state.md)", "Read"] +allowed-tools: ["Bash(ls -1d .humanize/rlcr/*/)", "Bash(mv .humanize/rlcr/*/state.md .humanize/rlcr/*/cancel-state.md)", "Bash(cat .humanize/rlcr/*/state.md)", "Read"] hide-from-slash-command-tool: "true" --- @@ -11,7 +11,7 @@ To cancel the active loop: 1. Find the current loop directory (newest timestamp): ```bash -LOOP_DIR=$(ls -1d .humanize-loop.local/*/ 2>/dev/null | sort -r | head -1) +LOOP_DIR=$(ls -1d .humanize/rlcr/*/ 2>/dev/null | sort -r | head -1) echo "Loop dir: ${LOOP_DIR:-NONE}" ``` diff --git a/hooks/lib/loop-common.sh b/hooks/lib/loop-common.sh index e40ba462..be74a5ab 100755 --- a/hooks/lib/loop-common.sh +++ b/hooks/lib/loop-common.sh @@ -146,10 +146,11 @@ is_state_file_path() { echo "$path_lower" | grep -qE 'state\.md$' } -# Check if a path is inside .humanize-loop.local directory +# Check if a path is inside .humanize/rlcr directory (current) or .humanize-loop.local (legacy) is_in_humanize_loop_dir() { local path="$1" - echo "$path" | grep -q '\.humanize-loop\.local/' + # Match both new path (.humanize/rlcr/) and legacy path (.humanize-loop.local/) + echo "$path" | grep -qE '\.(humanize/rlcr|humanize-loop\.local)/' } # Check if a shell command attempts to modify a file matching the given pattern diff --git a/hooks/loop-bash-validator.sh b/hooks/loop-bash-validator.sh index ad8f4e4f..6fb6b2d3 100755 --- a/hooks/loop-bash-validator.sh +++ b/hooks/loop-bash-validator.sh @@ -34,7 +34,7 @@ COMMAND_LOWER=$(to_lower "$COMMAND") # ======================================== PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}" -LOOP_BASE_DIR="$PROJECT_ROOT/.humanize-loop.local" +LOOP_BASE_DIR="$PROJECT_ROOT/.humanize/rlcr" ACTIVE_LOOP_DIR=$(find_active_loop "$LOOP_BASE_DIR") # If no active loop, allow all commands @@ -79,8 +79,9 @@ fi # ======================================== # Plan backup is read-only - protects plan integrity during loop # Use command_modifies_file helper for consistent pattern matching +# Match both new path (.humanize/rlcr/) and legacy path (.humanize-loop.local/) -if command_modifies_file "$COMMAND_LOWER" "\.humanize-loop\.local(/[^/]+)?/plan\.md"; then +if command_modifies_file "$COMMAND_LOWER" "\.(humanize/rlcr|humanize-loop\.local)(/[^/]+)?/plan\.md"; then FALLBACK="Writing to plan.md backup is not allowed during RLCR loop." REASON=$(load_and_render_safe "$TEMPLATE_DIR" "block/plan-backup-protected.md" "$FALLBACK") echo "$REASON" >&2 diff --git a/hooks/loop-codex-stop-hook.sh b/hooks/loop-codex-stop-hook.sh index 35ae56a8..4a7fb23b 100755 --- a/hooks/loop-codex-stop-hook.sh +++ b/hooks/loop-codex-stop-hook.sh @@ -5,7 +5,7 @@ # Intercepts Claude's exit attempts and uses Codex to review work. # If Codex doesn't confirm completion, blocks exit and feeds review back. # -# State directory: .humanize-loop.local// +# State directory: .humanize/rlcr// # State file: state.md (current_round, max_iterations, codex config) # Summary file: round-N-summary.md (Claude's work summary) # Review prompt: round-N-review-prompt.md (prompt sent to Codex) @@ -41,7 +41,7 @@ HOOK_INPUT=$(cat) # ======================================== PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}" -LOOP_BASE_DIR="$PROJECT_ROOT/.humanize-loop.local" +LOOP_BASE_DIR="$PROJECT_ROOT/.humanize/rlcr" # Source shared loop functions and template loader SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)" @@ -354,17 +354,17 @@ if command -v git &>/dev/null && git rev-parse --git-dir &>/dev/null 2>&1; then # Check for special cases in untracked files UNTRACKED=$(echo "$GIT_STATUS" | grep '^??' || true) - # Check if .humanize-loop.local is untracked - if echo "$UNTRACKED" | grep -q '\.humanize-loop\.local'; then + # Check if .humanize or .humanize-loop.local is untracked + if echo "$UNTRACKED" | grep -qE '\.(humanize|humanize-loop\.local)'; then HUMANIZE_LOCAL_NOTE=$(load_template "$TEMPLATE_DIR" "block/git-not-clean-humanize-local.md" 2>/dev/null) if [[ -z "$HUMANIZE_LOCAL_NOTE" ]]; then - HUMANIZE_LOCAL_NOTE="Note: .humanize-loop.local/ is intentionally untracked." + HUMANIZE_LOCAL_NOTE="Note: .humanize/ directory is intentionally untracked." fi SPECIAL_NOTES="$SPECIAL_NOTES$HUMANIZE_LOCAL_NOTE" fi # Check for other untracked files (potential artifacts) - OTHER_UNTRACKED=$(echo "$UNTRACKED" | grep -v '\.humanize-loop\.local' || true) + OTHER_UNTRACKED=$(echo "$UNTRACKED" | grep -vE '\.(humanize|humanize-loop\.local)' || true) if [[ -n "$OTHER_UNTRACKED" ]]; then UNTRACKED_NOTE=$(load_template "$TEMPLATE_DIR" "block/git-not-clean-untracked.md" 2>/dev/null) if [[ -z "$UNTRACKED_NOTE" ]]; then @@ -855,7 +855,7 @@ if [[ "$LAST_LINE_TRIMMED" == "STOP" ]]; then echo "Codex detected development stagnation during Full Alignment Check (Round $CURRENT_ROUND)." >&2 echo "The loop has been stopped to prevent further unproductive iterations." >&2 echo "" >&2 - echo "Review the historical round files in .humanize-loop.local/$(basename "$LOOP_DIR")/ to understand what went wrong." >&2 + echo "Review the historical round files in .humanize/rlcr/$(basename "$LOOP_DIR")/ to understand what went wrong." >&2 echo "Consider:" >&2 echo " - Revisiting the original plan for clarity" >&2 echo " - Breaking down the task into smaller pieces" >&2 diff --git a/hooks/loop-edit-validator.sh b/hooks/loop-edit-validator.sh index dabf3d8f..efc80bf8 100755 --- a/hooks/loop-edit-validator.sh +++ b/hooks/loop-edit-validator.sh @@ -44,7 +44,7 @@ if is_round_file_type "$FILE_PATH_LOWER" "prompt"; then fi # ======================================== -# Check if File is in .humanize-loop.local +# Check if File is in .humanize/rlcr or .humanize-loop.local # ======================================== if ! is_in_humanize_loop_dir "$FILE_PATH"; then @@ -56,7 +56,7 @@ fi # ======================================== PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}" -LOOP_BASE_DIR="$PROJECT_ROOT/.humanize-loop.local" +LOOP_BASE_DIR="$PROJECT_ROOT/.humanize/rlcr" ACTIVE_LOOP_DIR=$(find_active_loop "$LOOP_BASE_DIR") if [[ -z "$ACTIVE_LOOP_DIR" ]]; then @@ -80,7 +80,8 @@ fi FILENAME=$(basename "$FILE_PATH") if [[ "$FILENAME" == "plan.md" ]]; then - if [[ "$FILE_PATH" == *"/.humanize-loop.local/"* ]]; then + # Match both new path (.humanize/rlcr/) and legacy path (.humanize-loop.local/) + if [[ "$FILE_PATH" == *"/.humanize/rlcr/"* ]] || [[ "$FILE_PATH" == *"/.humanize-loop.local/"* ]]; then FALLBACK="Editing plan.md backup is not allowed during RLCR loop." REASON=$(load_and_render_safe "$TEMPLATE_DIR" "block/plan-backup-protected.md" "$FALLBACK") echo "$REASON" >&2 @@ -104,7 +105,14 @@ fi if is_round_file_type "$FILE_PATH_LOWER" "summary"; then # Extract filename from path (portable - works in bash and zsh) - CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize-loop\.local/[^/]*/\(.*\)$|\1|p') + # Try new path first, then legacy path + CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize/rlcr/[^/]*/\(.*\)$|\1|p') + if [[ -z "$CLAUDE_FILENAME" ]]; then + CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize/rlcr/\(.*\)$|\1|p') + fi + if [[ -z "$CLAUDE_FILENAME" ]]; then + CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize-loop\.local/[^/]*/\(.*\)$|\1|p') + fi if [[ -z "$CLAUDE_FILENAME" ]]; then CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize-loop\.local/\(.*\)$|\1|p') fi diff --git a/hooks/loop-plan-file-validator.sh b/hooks/loop-plan-file-validator.sh index 962a552d..e628b3f8 100755 --- a/hooks/loop-plan-file-validator.sh +++ b/hooks/loop-plan-file-validator.sh @@ -20,7 +20,7 @@ source "$SCRIPT_DIR/lib/loop-common.sh" INPUT=$(cat) # Find active loop using shared function -LOOP_BASE_DIR="$PROJECT_ROOT/.humanize-loop.local" +LOOP_BASE_DIR="$PROJECT_ROOT/.humanize/rlcr" LOOP_DIR=$(find_active_loop "$LOOP_BASE_DIR") # If no active loop, allow exit diff --git a/hooks/loop-read-validator.sh b/hooks/loop-read-validator.sh index bb4f1b9b..4ddbb885 100755 --- a/hooks/loop-read-validator.sh +++ b/hooks/loop-read-validator.sh @@ -4,7 +4,7 @@ # # Blocks Claude from reading: # - Wrong round's prompt/summary files (outdated information) -# - Round files from wrong locations (not in .humanize-loop.local/) +# - Round files from wrong locations (not in .humanize/rlcr/) # - Round files from old session directories # - Todos files (should use native TodoWrite instead) # @@ -54,7 +54,7 @@ IN_HUMANIZE_LOOP_DIR=$(is_in_humanize_loop_dir "$FILE_PATH" && echo "true" || ec # ======================================== PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}" -LOOP_BASE_DIR="$PROJECT_ROOT/.humanize-loop.local" +LOOP_BASE_DIR="$PROJECT_ROOT/.humanize/rlcr" ACTIVE_LOOP_DIR=$(find_active_loop "$LOOP_BASE_DIR") if [[ -z "$ACTIVE_LOOP_DIR" ]]; then diff --git a/hooks/loop-write-validator.sh b/hooks/loop-write-validator.sh index 5a35244e..434f3760 100755 --- a/hooks/loop-write-validator.sh +++ b/hooks/loop-write-validator.sh @@ -6,7 +6,7 @@ # - Todos files (should use native TodoWrite instead) # - Prompt files (read-only, generated by Codex) # - Wrong round number summary files -# - Summary files outside .humanize-loop.local/ +# - Summary files outside .humanize/rlcr/ # - Goal tracker after Round 0 # @@ -51,13 +51,13 @@ fi IS_SUMMARY_FILE=$(is_round_file_type "$FILE_PATH_LOWER" "summary" && echo "true" || echo "false") IN_HUMANIZE_LOOP_DIR=$(is_in_humanize_loop_dir "$FILE_PATH" && echo "true" || echo "false") -# If not a summary file and not in .humanize-loop.local, allow normally +# If not a summary file and not in .humanize/rlcr, allow normally if [[ "$IS_SUMMARY_FILE" == "false" ]] && [[ "$IN_HUMANIZE_LOOP_DIR" == "false" ]]; then exit 0 fi -# For state.md, goal-tracker.md, and plan.md in .humanize-loop.local, we need further validation -# For other files in .humanize-loop.local that aren't summaries, allow them +# For state.md, goal-tracker.md, and plan.md in .humanize/rlcr, we need further validation +# For other files in .humanize/rlcr that aren't summaries, allow them FILENAME=$(basename "$FILE_PATH") IS_PLAN_BACKUP=$([[ "$FILENAME" == "plan.md" ]] && echo "true" || echo "false") if [[ "$IN_HUMANIZE_LOOP_DIR" == "true" ]] && [[ "$IS_SUMMARY_FILE" == "false" ]]; then @@ -71,7 +71,7 @@ fi # ======================================== PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}" -LOOP_BASE_DIR="$PROJECT_ROOT/.humanize-loop.local" +LOOP_BASE_DIR="$PROJECT_ROOT/.humanize/rlcr" ACTIVE_LOOP_DIR=$(find_active_loop "$LOOP_BASE_DIR") if [[ -z "$ACTIVE_LOOP_DIR" ]]; then @@ -94,7 +94,8 @@ fi # ======================================== if [[ "$IS_PLAN_BACKUP" == "true" ]]; then - if [[ "$FILE_PATH" == *"/.humanize-loop.local/"* ]]; then + # Match both new path (.humanize/rlcr/) and legacy path (.humanize-loop.local/) + if [[ "$FILE_PATH" == *"/.humanize/rlcr/"* ]] || [[ "$FILE_PATH" == *"/.humanize-loop.local/"* ]]; then FALLBACK="Writing to plan.md backup is not allowed during RLCR loop." REASON=$(load_and_render_safe "$TEMPLATE_DIR" "block/plan-backup-protected.md" "$FALLBACK") echo "$REASON" >&2 @@ -113,7 +114,7 @@ if is_goal_tracker_path "$FILE_PATH_LOWER" && [[ "$CURRENT_ROUND" -gt 0 ]]; then fi # ======================================== -# Block Summary Files Outside .humanize-loop.local +# Block Summary Files Outside .humanize/rlcr # ======================================== if [[ "$IS_SUMMARY_FILE" == "true" ]] && [[ "$IN_HUMANIZE_LOOP_DIR" == "false" ]]; then @@ -130,7 +131,14 @@ fi # Extract Path Components (portable - works in bash and zsh) # ======================================== -CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize-loop\.local/[^/]*/\(.*\)$|\1|p') +# Try new path first, then legacy path +CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize/rlcr/[^/]*/\(.*\)$|\1|p') +if [[ -z "$CLAUDE_FILENAME" ]]; then + CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize/rlcr/\(.*\)$|\1|p') +fi +if [[ -z "$CLAUDE_FILENAME" ]]; then + CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize-loop\.local/[^/]*/\(.*\)$|\1|p') +fi if [[ -z "$CLAUDE_FILENAME" ]]; then CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize-loop\.local/\(.*\)$|\1|p') fi diff --git a/prompt-template/block/git-not-clean-humanize-local.md b/prompt-template/block/git-not-clean-humanize-local.md index 3fabca9f..4a31517e 100644 --- a/prompt-template/block/git-not-clean-humanize-local.md +++ b/prompt-template/block/git-not-clean-humanize-local.md @@ -1,8 +1,10 @@ -**Special Case - .humanize-loop.local detected**: -The `.humanize-loop.local/` directory is created by humanize:start-rlcr-loop and should NOT be committed. +**Special Case - .humanize directory detected**: +The `.humanize/` directory is created by humanize:start-rlcr-loop and should NOT be committed. Please add it to .gitignore: ```bash -echo '.humanize*local*' >> .gitignore +echo '.humanize*' >> .gitignore git add .gitignore ``` + +Note: If you have a legacy `.humanize-loop.local/` directory, the `.humanize*` pattern will also exclude it. diff --git a/prompt-template/codex/full-alignment-review.md b/prompt-template/codex/full-alignment-review.md index 16221853..4394f98f 100644 --- a/prompt-template/codex/full-alignment-review.md +++ b/prompt-template/codex/full-alignment-review.md @@ -60,16 +60,16 @@ Critical blockers: [list if any] To implement the original plan at @{{PLAN_FILE}}, we have completed **{{COMPLETED_ITERATIONS}} iterations** (Round 0 to Round {{CURRENT_ROUND}}). -The project's `.humanize-loop.local/{{LOOP_TIMESTAMP}}/` directory contains the history of each round's iteration: +The project's `.humanize/rlcr/{{LOOP_TIMESTAMP}}/` directory contains the history of each round's iteration: - Round input prompts: `round-N-prompt.md` - Round output summaries: `round-N-summary.md` - Round review prompts: `round-N-review-prompt.md` - Round review results: `round-N-review-result.md` **How to Access Historical Files**: Read the historical review results and summaries using file paths like: -- `@.humanize-loop.local/{{LOOP_TIMESTAMP}}/round-{{PREV_ROUND}}-review-result.md` (previous round) -- `@.humanize-loop.local/{{LOOP_TIMESTAMP}}/round-{{PREV_PREV_ROUND}}-review-result.md` (2 rounds ago) -- `@.humanize-loop.local/{{LOOP_TIMESTAMP}}/round-{{PREV_ROUND}}-summary.md` (previous summary) +- `@.humanize/rlcr/{{LOOP_TIMESTAMP}}/round-{{PREV_ROUND}}-review-result.md` (previous round) +- `@.humanize/rlcr/{{LOOP_TIMESTAMP}}/round-{{PREV_PREV_ROUND}}-review-result.md` (2 rounds ago) +- `@.humanize/rlcr/{{LOOP_TIMESTAMP}}/round-{{PREV_ROUND}}-summary.md` (previous summary) **Your Task**: Review the historical review results, especially the **last 5 rounds** of development progress and review outcomes, to determine if the development has stalled. diff --git a/scripts/humanize.sh b/scripts/humanize.sh index 52bb3f67..b5ef7e5f 100755 --- a/scripts/humanize.sh +++ b/scripts/humanize.sh @@ -3,17 +3,17 @@ # Part of rc.d configuration # Compatible with both bash and zsh -# Monitor the latest Codex run log from .humanize-loop.local +# Monitor the latest Codex run log from .humanize/rlcr # Automatically switches to newer logs when they appear # Features a fixed status bar at the top showing session info _humanize_monitor_codex() { - local loop_dir=".humanize-loop.local" + local loop_dir=".humanize/rlcr" local current_file="" local current_session_dir="" local check_interval=2 # seconds between checking for new files local status_bar_height=10 # number of lines for status bar (goal tracker + git status) - # Check if .humanize-loop.local exists + # Check if .humanize/rlcr exists if [[ ! -d "$loop_dir" ]]; then echo "Error: $loop_dir directory not found in current directory" echo "Are you in a project with an active humanize loop?" @@ -54,7 +54,7 @@ _humanize_monitor_codex() { local sanitized_project=$(echo "$project_root" | sed 's/[^a-zA-Z0-9._-]/-/g' | sed 's/--*/-/g') local project_cache_dir="$cache_base/$sanitized_project" - # First, find valid session timestamps from local .humanize-loop.local + # First, find valid session timestamps from local .humanize/rlcr for session_dir in "$loop_dir"/*; do # Skip if glob didn't match anything [[ ! -e "$session_dir" ]] && continue @@ -532,7 +532,7 @@ humanize() { *) echo "Usage: humanize monitor rlcr-loop" echo "" - echo "Monitor the latest RLCR loop log from .humanize-loop.local" + echo "Monitor the latest RLCR loop log from .humanize/rlcr" echo "Features:" echo " - Fixed status bar showing session info, round progress, model config" echo " - Goal tracker summary: Ultimate Goal, AC progress, task status" diff --git a/scripts/setup-rlcr-loop.sh b/scripts/setup-rlcr-loop.sh index f4e60d06..c1755a60 100755 --- a/scripts/setup-rlcr-loop.sh +++ b/scripts/setup-rlcr-loop.sh @@ -83,13 +83,13 @@ STOPPING: MONITORING: # View current state: - cat .humanize-loop.local/*/state.md + cat .humanize/rlcr/*/state.md # View latest summary: - cat .humanize-loop.local/*/round-*-summary.md | tail -50 + cat .humanize/rlcr/*/round-*-summary.md | tail -50 # View Codex review: - cat .humanize-loop.local/*/round-*-review-result.md | tail -50 + cat .humanize/rlcr/*/round-*-review-result.md | tail -50 HELP_EOF exit 0 } @@ -385,7 +385,7 @@ fi # Setup State Directory # ======================================== -LOOP_BASE_DIR="$PROJECT_ROOT/.humanize-loop.local" +LOOP_BASE_DIR="$PROJECT_ROOT/.humanize/rlcr" # Create timestamp for this loop session TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S) diff --git a/tests/test-plan-file-hooks.sh b/tests/test-plan-file-hooks.sh index 04d36301..73575e71 100755 --- a/tests/test-plan-file-hooks.sh +++ b/tests/test-plan-file-hooks.sh @@ -53,7 +53,7 @@ setup_test_loop() { CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) # Create loop directory structure - LOOP_DIR="$TEST_DIR/.humanize-loop.local/2024-01-01_12-00-00" + LOOP_DIR="$TEST_DIR/.humanize/rlcr/2024-01-01_12-00-00" mkdir -p "$LOOP_DIR" # Create plan file (gitignored) @@ -251,16 +251,16 @@ else fail "Bash validator blocking rm" "exit 2 with plan error" "exit $EXIT_CODE, output: $RESULT" fi -# Test 8a: Bash validator blocks direct .humanize-loop.local/plan.md (no intermediate dir) +# Test 8a: Bash validator blocks direct .humanize/rlcr/plan.md (no intermediate dir) # This tests Fix #1 for the regex bypass vulnerability -echo "Test 8a: Block bash modifications to direct .humanize-loop.local/plan.md" -HOOK_INPUT='{"tool_name": "Bash", "tool_input": {"command": "echo evil > .humanize-loop.local/plan.md"}}' +echo "Test 8a: Block bash modifications to direct .humanize/rlcr/plan.md" +HOOK_INPUT='{"tool_name": "Bash", "tool_input": {"command": "echo evil > .humanize/rlcr/plan.md"}}' set +e RESULT=$(echo "$HOOK_INPUT" | "$PROJECT_ROOT/hooks/loop-bash-validator.sh" 2>&1) EXIT_CODE=$? set -e if [[ $EXIT_CODE -eq 2 ]] && echo "$RESULT" | grep -qi "plan"; then - pass "Bash validator blocks direct .humanize-loop.local/plan.md" + pass "Bash validator blocks direct .humanize/rlcr/plan.md" else fail "Bash validator direct plan.md" "exit 2 with plan error" "exit $EXIT_CODE, output: $RESULT" fi @@ -271,7 +271,7 @@ echo "" # Test 8.1: Block command substitution bypass attempt echo "Test 8.1: Block command substitution bypass" -HOOK_INPUT='{"tool_name": "Bash", "tool_input": {"command": "echo test > .humanize-loop.local/$(date +%Y)/plan.md"}}' +HOOK_INPUT='{"tool_name": "Bash", "tool_input": {"command": "echo test > .humanize/rlcr/$(date +%Y)/plan.md"}}' set +e RESULT=$(echo "$HOOK_INPUT" | "$PROJECT_ROOT/hooks/loop-bash-validator.sh" 2>&1) EXIT_CODE=$? @@ -284,7 +284,7 @@ fi # Test 8.2: Block glob expansion bypass attempt echo "Test 8.2: Block glob expansion bypass" -HOOK_INPUT='{"tool_name": "Bash", "tool_input": {"command": "echo test > .humanize-loop.local/*/plan.md"}}' +HOOK_INPUT='{"tool_name": "Bash", "tool_input": {"command": "echo test > .humanize/rlcr/*/plan.md"}}' set +e RESULT=$(echo "$HOOK_INPUT" | "$PROJECT_ROOT/hooks/loop-bash-validator.sh" 2>&1) EXIT_CODE=$? @@ -297,7 +297,7 @@ fi # Test 8.3: Block brace expansion bypass attempt echo "Test 8.3: Block brace expansion bypass" -HOOK_INPUT='{"tool_name": "Bash", "tool_input": {"command": "tee .humanize-loop.local/{a,b,c}/plan.md"}}' +HOOK_INPUT='{"tool_name": "Bash", "tool_input": {"command": "tee .humanize/rlcr/{a,b,c}/plan.md"}}' set +e RESULT=$(echo "$HOOK_INPUT" | "$PROJECT_ROOT/hooks/loop-bash-validator.sh" 2>&1) EXIT_CODE=$? @@ -310,7 +310,7 @@ fi # Test 8.4: Block piped command bypass attempt echo "Test 8.4: Block piped command bypass" -HOOK_INPUT='{"tool_name": "Bash", "tool_input": {"command": "cat input.txt | tee .humanize-loop.local/2024-01-01_12-00-00/plan.md"}}' +HOOK_INPUT='{"tool_name": "Bash", "tool_input": {"command": "cat input.txt | tee .humanize/rlcr/2024-01-01_12-00-00/plan.md"}}' set +e RESULT=$(echo "$HOOK_INPUT" | "$PROJECT_ROOT/hooks/loop-bash-validator.sh" 2>&1) EXIT_CODE=$? @@ -323,7 +323,7 @@ fi # Test 8.5: Block backtick command substitution bypass echo "Test 8.5: Block backtick command substitution bypass" -HOOK_INPUT='{"tool_name": "Bash", "tool_input": {"command": "echo test > .humanize-loop.local/`echo test`/plan.md"}}' +HOOK_INPUT='{"tool_name": "Bash", "tool_input": {"command": "echo test > .humanize/rlcr/`echo test`/plan.md"}}' set +e RESULT=$(echo "$HOOK_INPUT" | "$PROJECT_ROOT/hooks/loop-bash-validator.sh" 2>&1) EXIT_CODE=$? @@ -584,7 +584,7 @@ EOF git add tracked-plan.md git commit -q -m "Add plan" # Create loop directory -TRACKED_LOOP_DIR="$PWD/.humanize-loop.local/2024-01-01_12-00-00" +TRACKED_LOOP_DIR="$PWD/.humanize/rlcr/2024-01-01_12-00-00" mkdir -p "$TRACKED_LOOP_DIR" cp tracked-plan.md "$TRACKED_LOOP_DIR/plan.md" cat > "$TRACKED_LOOP_DIR/state.md" << EOF @@ -678,7 +678,7 @@ EOF git add tracked-plan.md git commit -q -m "Add plan" # Create loop directory and backup -TRACKED_LOOP_DIR="$PWD/.humanize-loop.local/2024-01-01_12-00-00" +TRACKED_LOOP_DIR="$PWD/.humanize/rlcr/2024-01-01_12-00-00" mkdir -p "$TRACKED_LOOP_DIR" cp tracked-plan.md "$TRACKED_LOOP_DIR/plan.md" cat > "$TRACKED_LOOP_DIR/state.md" << EOF diff --git a/tests/test-state-exit-naming.sh b/tests/test-state-exit-naming.sh index fe9c39cf..96bcab54 100755 --- a/tests/test-state-exit-naming.sh +++ b/tests/test-state-exit-naming.sh @@ -45,7 +45,7 @@ echo "init" > init.txt git add init.txt git commit -q -m "Initial" -LOOP_DIR="$TEST_DIR/.humanize-loop.local/2024-01-01_12-00-00" +LOOP_DIR="$TEST_DIR/.humanize/rlcr/2024-01-01_12-00-00" mkdir -p "$LOOP_DIR" # Create completed state (should not be detected as active) @@ -64,7 +64,7 @@ export CLAUDE_PROJECT_DIR="$TEST_DIR" # Source the loop-common.sh to get find_active_loop source "$PROJECT_ROOT/hooks/lib/loop-common.sh" -ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize-loop.local") +ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize/rlcr") if [[ -z "$ACTIVE_LOOP" ]]; then pass "complete-state.md not detected as active loop" else @@ -83,7 +83,7 @@ start_branch: main --- EOF -ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize-loop.local") +ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize/rlcr") if [[ -n "$ACTIVE_LOOP" ]]; then pass "state.md detected as active loop" else @@ -100,7 +100,7 @@ max_iterations: 42 --- EOF -ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize-loop.local") +ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize/rlcr") if [[ -z "$ACTIVE_LOOP" ]]; then pass "cancel-state.md not detected as active loop" else @@ -115,7 +115,7 @@ current_round: 2 --- EOF -ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize-loop.local") +ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize/rlcr") if [[ -z "$ACTIVE_LOOP" ]]; then pass "unexpected-state.md not detected as active loop" else @@ -131,7 +131,7 @@ max_iterations: 42 --- EOF -ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize-loop.local") +ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize/rlcr") if [[ -z "$ACTIVE_LOOP" ]]; then pass "maxiter-state.md not detected as active loop" else @@ -147,7 +147,7 @@ max_iterations: 42 --- EOF -ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize-loop.local") +ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize/rlcr") if [[ -z "$ACTIVE_LOOP" ]]; then pass "stop-state.md not detected as active loop" else @@ -156,7 +156,7 @@ fi # Test 7: Newer directory with state.md takes precedence echo "Test 7: Newer directory with state.md takes precedence" -NEWER_LOOP_DIR="$TEST_DIR/.humanize-loop.local/2024-01-02_12-00-00" +NEWER_LOOP_DIR="$TEST_DIR/.humanize/rlcr/2024-01-02_12-00-00" mkdir -p "$NEWER_LOOP_DIR" cat > "$NEWER_LOOP_DIR/state.md" << 'EOF' --- @@ -168,7 +168,7 @@ start_branch: main --- EOF -ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize-loop.local") +ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize/rlcr") if [[ "$ACTIVE_LOOP" == "$NEWER_LOOP_DIR" ]]; then pass "Newer directory with state.md takes precedence" else @@ -181,7 +181,7 @@ echo "" # Test 8: end_loop rejects invalid reason echo "Test 8: end_loop rejects invalid reason" -END_LOOP_TEST_DIR="$TEST_DIR/.humanize-loop.local/2024-01-03_12-00-00" +END_LOOP_TEST_DIR="$TEST_DIR/.humanize/rlcr/2024-01-03_12-00-00" mkdir -p "$END_LOOP_TEST_DIR" cat > "$END_LOOP_TEST_DIR/state.md" << 'EOF' --- diff --git a/tests/test-templates-comprehensive.sh b/tests/test-templates-comprehensive.sh index 06ed26df..0e7e8f3c 100755 --- a/tests/test-templates-comprehensive.sh +++ b/tests/test-templates-comprehensive.sh @@ -530,10 +530,10 @@ fi echo "" echo "Testing real template: codex/goal-tracker-update-section.md..." result=$(load_and_render "$TEMPLATE_DIR" "codex/goal-tracker-update-section.md" \ - "GOAL_TRACKER_FILE=.humanize-loop.local/20240101/goal-tracker.md") + "GOAL_TRACKER_FILE=.humanize/rlcr/20240101/goal-tracker.md") if echo "$result" | grep -q "Goal Tracker Update Requests" && \ - echo "$result" | grep -q ".humanize-loop.local/20240101/goal-tracker.md"; then + echo "$result" | grep -q ".humanize/rlcr/20240101/goal-tracker.md"; then pass "Real template: goal-tracker-update-section.md" else fail "Real template: goal-tracker-update-section.md" From c53e96d69b189cca3262a746e57f911f92003603 Mon Sep 17 00:00:00 2001 From: Sihao Liu Date: Thu, 15 Jan 2026 19:23:49 -0800 Subject: [PATCH 2/3] fix(legacy): remove backward compatibility for .humanize-loop.local paths Per the original plan requirement: "If there are legacy .humanize-loop.local folder in users' project, we don't use it anymore, we just suggest it to be added into .gitignore" Changes: - Update is_in_humanize_loop_dir() to only match .humanize/rlcr paths - Remove legacy path matching from write, edit, and bash validators - Simplify path extraction in validators to only use new path structure - Update git-not-clean template to not explicitly mention legacy paths - Simplify untracked file detection to match any .humanize* pattern Tests added: - Positive test: is_in_humanize_loop_dir detects .humanize/rlcr path - Negative test: is_in_humanize_loop_dir does NOT detect legacy path - Negative test: Legacy directory not searched when using new base path - Negative test: Bash validator allows writes to legacy path - Negative test: Write validator allows writes to legacy path - Negative test: Edit validator allows edits to legacy path All 277 tests pass. --- hooks/lib/loop-common.sh | 6 +-- hooks/loop-bash-validator.sh | 3 +- hooks/loop-codex-stop-hook.sh | 8 ++-- hooks/loop-edit-validator.sh | 12 +---- hooks/loop-write-validator.sh | 10 +--- .../block/git-not-clean-humanize-local.md | 2 - tests/test-plan-file-hooks.sh | 44 +++++++++++++++++ tests/test-state-exit-naming.sh | 48 +++++++++++++++++++ 8 files changed, 103 insertions(+), 30 deletions(-) diff --git a/hooks/lib/loop-common.sh b/hooks/lib/loop-common.sh index be74a5ab..95ae0e07 100755 --- a/hooks/lib/loop-common.sh +++ b/hooks/lib/loop-common.sh @@ -146,11 +146,11 @@ is_state_file_path() { echo "$path_lower" | grep -qE 'state\.md$' } -# Check if a path is inside .humanize/rlcr directory (current) or .humanize-loop.local (legacy) +# Check if a path is inside .humanize/rlcr directory +# Note: Legacy .humanize-loop.local paths are NOT treated as loop directories is_in_humanize_loop_dir() { local path="$1" - # Match both new path (.humanize/rlcr/) and legacy path (.humanize-loop.local/) - echo "$path" | grep -qE '\.(humanize/rlcr|humanize-loop\.local)/' + echo "$path" | grep -q '\.humanize/rlcr/' } # Check if a shell command attempts to modify a file matching the given pattern diff --git a/hooks/loop-bash-validator.sh b/hooks/loop-bash-validator.sh index 6fb6b2d3..8b198668 100755 --- a/hooks/loop-bash-validator.sh +++ b/hooks/loop-bash-validator.sh @@ -79,9 +79,8 @@ fi # ======================================== # Plan backup is read-only - protects plan integrity during loop # Use command_modifies_file helper for consistent pattern matching -# Match both new path (.humanize/rlcr/) and legacy path (.humanize-loop.local/) -if command_modifies_file "$COMMAND_LOWER" "\.(humanize/rlcr|humanize-loop\.local)(/[^/]+)?/plan\.md"; then +if command_modifies_file "$COMMAND_LOWER" "\.humanize/rlcr(/[^/]+)?/plan\.md"; then FALLBACK="Writing to plan.md backup is not allowed during RLCR loop." REASON=$(load_and_render_safe "$TEMPLATE_DIR" "block/plan-backup-protected.md" "$FALLBACK") echo "$REASON" >&2 diff --git a/hooks/loop-codex-stop-hook.sh b/hooks/loop-codex-stop-hook.sh index 4a7fb23b..18df65e6 100755 --- a/hooks/loop-codex-stop-hook.sh +++ b/hooks/loop-codex-stop-hook.sh @@ -354,17 +354,17 @@ if command -v git &>/dev/null && git rev-parse --git-dir &>/dev/null 2>&1; then # Check for special cases in untracked files UNTRACKED=$(echo "$GIT_STATUS" | grep '^??' || true) - # Check if .humanize or .humanize-loop.local is untracked - if echo "$UNTRACKED" | grep -qE '\.(humanize|humanize-loop\.local)'; then + # Check if .humanize* directories are untracked (includes .humanize/ and any legacy .humanize-* dirs) + if echo "$UNTRACKED" | grep -q '\.humanize'; then HUMANIZE_LOCAL_NOTE=$(load_template "$TEMPLATE_DIR" "block/git-not-clean-humanize-local.md" 2>/dev/null) if [[ -z "$HUMANIZE_LOCAL_NOTE" ]]; then - HUMANIZE_LOCAL_NOTE="Note: .humanize/ directory is intentionally untracked." + HUMANIZE_LOCAL_NOTE="Note: .humanize* directories are intentionally untracked." fi SPECIAL_NOTES="$SPECIAL_NOTES$HUMANIZE_LOCAL_NOTE" fi # Check for other untracked files (potential artifacts) - OTHER_UNTRACKED=$(echo "$UNTRACKED" | grep -vE '\.(humanize|humanize-loop\.local)' || true) + OTHER_UNTRACKED=$(echo "$UNTRACKED" | grep -v '\.humanize' || true) if [[ -n "$OTHER_UNTRACKED" ]]; then UNTRACKED_NOTE=$(load_template "$TEMPLATE_DIR" "block/git-not-clean-untracked.md" 2>/dev/null) if [[ -z "$UNTRACKED_NOTE" ]]; then diff --git a/hooks/loop-edit-validator.sh b/hooks/loop-edit-validator.sh index efc80bf8..f611d6fa 100755 --- a/hooks/loop-edit-validator.sh +++ b/hooks/loop-edit-validator.sh @@ -44,7 +44,7 @@ if is_round_file_type "$FILE_PATH_LOWER" "prompt"; then fi # ======================================== -# Check if File is in .humanize/rlcr or .humanize-loop.local +# Check if File is in .humanize/rlcr # ======================================== if ! is_in_humanize_loop_dir "$FILE_PATH"; then @@ -80,8 +80,7 @@ fi FILENAME=$(basename "$FILE_PATH") if [[ "$FILENAME" == "plan.md" ]]; then - # Match both new path (.humanize/rlcr/) and legacy path (.humanize-loop.local/) - if [[ "$FILE_PATH" == *"/.humanize/rlcr/"* ]] || [[ "$FILE_PATH" == *"/.humanize-loop.local/"* ]]; then + if [[ "$FILE_PATH" == *"/.humanize/rlcr/"* ]]; then FALLBACK="Editing plan.md backup is not allowed during RLCR loop." REASON=$(load_and_render_safe "$TEMPLATE_DIR" "block/plan-backup-protected.md" "$FALLBACK") echo "$REASON" >&2 @@ -105,17 +104,10 @@ fi if is_round_file_type "$FILE_PATH_LOWER" "summary"; then # Extract filename from path (portable - works in bash and zsh) - # Try new path first, then legacy path CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize/rlcr/[^/]*/\(.*\)$|\1|p') if [[ -z "$CLAUDE_FILENAME" ]]; then CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize/rlcr/\(.*\)$|\1|p') fi - if [[ -z "$CLAUDE_FILENAME" ]]; then - CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize-loop\.local/[^/]*/\(.*\)$|\1|p') - fi - if [[ -z "$CLAUDE_FILENAME" ]]; then - CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize-loop\.local/\(.*\)$|\1|p') - fi if [[ -n "$CLAUDE_FILENAME" ]]; then CLAUDE_ROUND=$(extract_round_number "$CLAUDE_FILENAME") diff --git a/hooks/loop-write-validator.sh b/hooks/loop-write-validator.sh index 434f3760..719f2464 100755 --- a/hooks/loop-write-validator.sh +++ b/hooks/loop-write-validator.sh @@ -94,8 +94,7 @@ fi # ======================================== if [[ "$IS_PLAN_BACKUP" == "true" ]]; then - # Match both new path (.humanize/rlcr/) and legacy path (.humanize-loop.local/) - if [[ "$FILE_PATH" == *"/.humanize/rlcr/"* ]] || [[ "$FILE_PATH" == *"/.humanize-loop.local/"* ]]; then + if [[ "$FILE_PATH" == *"/.humanize/rlcr/"* ]]; then FALLBACK="Writing to plan.md backup is not allowed during RLCR loop." REASON=$(load_and_render_safe "$TEMPLATE_DIR" "block/plan-backup-protected.md" "$FALLBACK") echo "$REASON" >&2 @@ -131,17 +130,10 @@ fi # Extract Path Components (portable - works in bash and zsh) # ======================================== -# Try new path first, then legacy path CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize/rlcr/[^/]*/\(.*\)$|\1|p') if [[ -z "$CLAUDE_FILENAME" ]]; then CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize/rlcr/\(.*\)$|\1|p') fi -if [[ -z "$CLAUDE_FILENAME" ]]; then - CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize-loop\.local/[^/]*/\(.*\)$|\1|p') -fi -if [[ -z "$CLAUDE_FILENAME" ]]; then - CLAUDE_FILENAME=$(echo "$FILE_PATH" | sed -n 's|.*\.humanize-loop\.local/\(.*\)$|\1|p') -fi if [[ -z "$CLAUDE_FILENAME" ]]; then exit 0 fi diff --git a/prompt-template/block/git-not-clean-humanize-local.md b/prompt-template/block/git-not-clean-humanize-local.md index 4a31517e..1b8d89e9 100644 --- a/prompt-template/block/git-not-clean-humanize-local.md +++ b/prompt-template/block/git-not-clean-humanize-local.md @@ -6,5 +6,3 @@ Please add it to .gitignore: echo '.humanize*' >> .gitignore git add .gitignore ``` - -Note: If you have a legacy `.humanize-loop.local/` directory, the `.humanize*` pattern will also exclude it. diff --git a/tests/test-plan-file-hooks.sh b/tests/test-plan-file-hooks.sh index 73575e71..95711d19 100755 --- a/tests/test-plan-file-hooks.sh +++ b/tests/test-plan-file-hooks.sh @@ -730,6 +730,50 @@ else fi fi +echo "" +echo "=== Test: Legacy Path Handling (NEGATIVE TESTS) ===" +echo "" + +# Test 15: Bash validator ALLOWS writes to legacy .humanize-loop.local (it's not a loop dir anymore) +echo "Test 15: Bash validator allows writes to legacy .humanize-loop.local" +HOOK_INPUT='{"tool_name": "Bash", "tool_input": {"command": "echo test > .humanize-loop.local/2024-01-01/plan.md"}}' +set +e +RESULT=$(echo "$HOOK_INPUT" | "$PROJECT_ROOT/hooks/loop-bash-validator.sh" 2>&1) +EXIT_CODE=$? +set -e +# Should exit 0 (allowed) because legacy path is no longer treated as a loop directory +if [[ $EXIT_CODE -eq 0 ]]; then + pass "Bash validator allows writes to legacy .humanize-loop.local" +else + fail "Bash validator legacy path" "exit 0 (allowed)" "exit $EXIT_CODE, output: $RESULT" +fi + +# Test 16: Write validator ALLOWS writes to legacy .humanize-loop.local plan.md +echo "Test 16: Write validator allows writes to legacy .humanize-loop.local plan.md" +HOOK_INPUT='{"tool_name": "Write", "tool_input": {"file_path": "'$TEST_DIR'/.humanize-loop.local/2024-01-01/plan.md"}}' +set +e +RESULT=$(echo "$HOOK_INPUT" | "$PROJECT_ROOT/hooks/loop-write-validator.sh" 2>&1) +EXIT_CODE=$? +set -e +if [[ $EXIT_CODE -eq 0 ]]; then + pass "Write validator allows writes to legacy .humanize-loop.local plan.md" +else + fail "Write validator legacy path" "exit 0 (allowed)" "exit $EXIT_CODE, output: $RESULT" +fi + +# Test 17: Edit validator ALLOWS edits to legacy .humanize-loop.local plan.md +echo "Test 17: Edit validator allows edits to legacy .humanize-loop.local plan.md" +HOOK_INPUT='{"tool_name": "Edit", "tool_input": {"file_path": "'$TEST_DIR'/.humanize-loop.local/2024-01-01/plan.md"}}' +set +e +RESULT=$(echo "$HOOK_INPUT" | "$PROJECT_ROOT/hooks/loop-edit-validator.sh" 2>&1) +EXIT_CODE=$? +set -e +if [[ $EXIT_CODE -eq 0 ]]; then + pass "Edit validator allows edits to legacy .humanize-loop.local plan.md" +else + fail "Edit validator legacy path" "exit 0 (allowed)" "exit $EXIT_CODE, output: $RESULT" +fi + echo "" echo "=========================================" echo "Test Results" diff --git a/tests/test-state-exit-naming.sh b/tests/test-state-exit-naming.sh index 96bcab54..0320efad 100755 --- a/tests/test-state-exit-naming.sh +++ b/tests/test-state-exit-naming.sh @@ -238,6 +238,54 @@ else fail "end_loop missing state file" "exit 1 with not found warning" "exit $EXIT_CODE: $RESULT" fi +echo "" +echo "=== Test: Path Detection (New vs Legacy) ===" +echo "" + +# Test 11: is_in_humanize_loop_dir correctly identifies NEW path +echo "Test 11: is_in_humanize_loop_dir detects .humanize/rlcr path" +NEW_PATH="/some/project/.humanize/rlcr/2024-01-01_12-00-00/state.md" +if is_in_humanize_loop_dir "$NEW_PATH"; then + pass "is_in_humanize_loop_dir detects .humanize/rlcr path" +else + fail "is_in_humanize_loop_dir new path" "returns true" "returns false" +fi + +# Test 12: is_in_humanize_loop_dir does NOT match legacy path (NEGATIVE TEST) +echo "Test 12: is_in_humanize_loop_dir does NOT detect legacy .humanize-loop.local path" +LEGACY_PATH="/some/project/.humanize-loop.local/2024-01-01_12-00-00/state.md" +if is_in_humanize_loop_dir "$LEGACY_PATH"; then + fail "is_in_humanize_loop_dir legacy path" "returns false (not a loop dir)" "returns true" +else + pass "is_in_humanize_loop_dir does NOT detect legacy .humanize-loop.local path" +fi + +# Test 13: The code only looks in .humanize/rlcr, not legacy paths +# This test verifies that even with a legacy directory present, the main search +# path is .humanize/rlcr, so legacy directories won't accidentally be used +echo "Test 13: Legacy directory not searched when using new base path" +# Create legacy directory with a state file +LEGACY_LOOP_DIR="$TEST_DIR/.humanize-loop.local/2024-01-01_12-00-00" +mkdir -p "$LEGACY_LOOP_DIR" +cat > "$LEGACY_LOOP_DIR/state.md" << 'EOF' +--- +current_round: 0 +max_iterations: 42 +plan_file: plan.md +plan_tracked: false +start_branch: main +--- +EOF +# Remove the new path state.md so only legacy exists +rm -f "$TEST_DIR/.humanize/rlcr/"*/state.md 2>/dev/null || true +# Search in the NEW base path - should find nothing because we removed state.md +ACTIVE_LOOP=$(find_active_loop "$TEST_DIR/.humanize/rlcr") +if [[ -z "$ACTIVE_LOOP" ]]; then + pass "Legacy directory not searched when using new base path" +else + fail "Legacy dir separation" "no active loop in new path" "$ACTIVE_LOOP" +fi + echo "" echo "=========================================" echo "Test Results" From 823cb32d01ae021ec8a0eb3dc1f18af901d2ba65 Mon Sep 17 00:00:00 2001 From: Sihao Liu Date: Thu, 15 Jan 2026 19:32:00 -0800 Subject: [PATCH 3/3] docs: remove legacy path references from comments Remove explicit .humanize-loop.local mentions from: - .gitignore comment (simplified to just describe .humanize*) - hooks/lib/loop-common.sh is_in_humanize_loop_dir() comment Legacy path references now only exist in test files for negative test coverage, as intended. --- .gitignore | 1 - hooks/lib/loop-common.sh | 1 - 2 files changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 5f8d4a79..31245b58 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ temp # Humanize state directories (used by start-rlcr-loop) -# Matches .humanize/ (current) and .humanize-loop.local/ (legacy) .humanize* # Python cache diff --git a/hooks/lib/loop-common.sh b/hooks/lib/loop-common.sh index 95ae0e07..eba9607f 100755 --- a/hooks/lib/loop-common.sh +++ b/hooks/lib/loop-common.sh @@ -147,7 +147,6 @@ is_state_file_path() { } # Check if a path is inside .humanize/rlcr directory -# Note: Legacy .humanize-loop.local paths are NOT treated as loop directories is_in_humanize_loop_dir() { local path="$1" echo "$path" | grep -q '\.humanize/rlcr/'