diff --git a/everyrow-mcp/scripts/everyrow-stop-guard.sh b/everyrow-mcp/scripts/everyrow-stop-guard.sh index 8375f7b1..80a3613f 100755 --- a/everyrow-mcp/scripts/everyrow-stop-guard.sh +++ b/everyrow-mcp/scripts/everyrow-stop-guard.sh @@ -13,12 +13,29 @@ if [ "$STOP_HOOK_ACTIVE" = "true" ]; then fi TASK_FILE="$HOME/.everyrow/task.json" +STALE_SECONDS=240 # 4 minutes if [ -f "$TASK_FILE" ]; then STATUS=$(jq -r '.status' "$TASK_FILE") TASK_ID=$(jq -r '.task_id' "$TASK_FILE") if [ "$STATUS" = "running" ]; then + # Check staleness using file mtime (updated on every everyrow_progress poll). + # If no poll has happened in STALE_SECONDS, the session that started this + # task is likely dead — clean up rather than blocking all future sessions. + if [[ "$(uname)" == "Darwin" ]]; then + FILE_MTIME=$(stat -f %m "$TASK_FILE") + else + FILE_MTIME=$(stat -c %Y "$TASK_FILE") + fi + NOW=$(date +%s) + ELAPSED=$(( NOW - FILE_MTIME )) + + if [ "$ELAPSED" -gt "$STALE_SECONDS" ]; then + rm -f "$TASK_FILE" + exit 0 + fi + jq -n \ --arg reason "[everyrow] Task $TASK_ID still running. Call everyrow_progress(task_id=\"$TASK_ID\") to check status." \ '{decision: "block", reason: $reason}' diff --git a/everyrow-mcp/tests/test_hook_stop_guard.sh b/everyrow-mcp/tests/test_hook_stop_guard.sh index bf11de3e..4a0161d8 100755 --- a/everyrow-mcp/tests/test_hook_stop_guard.sh +++ b/everyrow-mcp/tests/test_hook_stop_guard.sh @@ -7,8 +7,9 @@ TASK_FILE="$HOME/.everyrow/task.json" mkdir -p "$HOME/.everyrow" rm -f "$TASK_FILE" -# Test 1: Blocks when task is running -echo '{"task_id":"abc-123","status":"running","total":50,"completed":10}' > "$TASK_FILE" +# Test 1: Blocks when task is running (recently started) +NOW=$(date +%s) +echo "{\"task_id\":\"abc-123\",\"status\":\"running\",\"total\":50,\"completed\":10,\"started_at\":$NOW}" > "$TASK_FILE" RESULT=$(echo '{"stop_hook_active": false}' | bash "$SCRIPT_DIR/everyrow-stop-guard.sh") echo "$RESULT" | jq -e '.decision == "block"' || { echo "FAIL: should block"; exit 1; } echo "$RESULT" | jq -e '.reason | contains("abc-123")' || { echo "FAIL: reason should contain task_id"; exit 1; } @@ -31,6 +32,21 @@ RESULT=$(echo '{"stop_hook_active": false}' | bash "$SCRIPT_DIR/everyrow-stop-gu [ -z "$RESULT" ] || { echo "FAIL: should allow when completed"; exit 1; } echo "PASS: allows when completed" +# Test 5: Allows when task file is stale (mtime > 30 min ago) +echo "{\"task_id\":\"abc-123\",\"status\":\"running\",\"total\":50,\"completed\":10,\"started_at\":$NOW}" > "$TASK_FILE" +# Set file mtime to 2 hours ago +touch -t "$(date -v-2H '+%Y%m%d%H%M.%S' 2>/dev/null || date -d '2 hours ago' '+%Y%m%d%H%M.%S')" "$TASK_FILE" +RESULT=$(echo '{"stop_hook_active": false}' | bash "$SCRIPT_DIR/everyrow-stop-guard.sh") +[ -z "$RESULT" ] || { echo "FAIL: should allow when stale"; exit 1; } +[ ! -f "$TASK_FILE" ] || { echo "FAIL: should remove stale task file"; exit 1; } +echo "PASS: allows and cleans up stale task (mtime-based)" + +# Test 6: Blocks when task file is recent (mtime < 30 min ago, i.e. just written) +echo "{\"task_id\":\"abc-123\",\"status\":\"running\",\"total\":50,\"completed\":10,\"started_at\":$NOW}" > "$TASK_FILE" +RESULT=$(echo '{"stop_hook_active": false}' | bash "$SCRIPT_DIR/everyrow-stop-guard.sh") +echo "$RESULT" | jq -e '.decision == "block"' || { echo "FAIL: should block recent task"; exit 1; } +echo "PASS: blocks recent running task" + # Cleanup rm -f "$TASK_FILE" echo "ALL PASS: stop guard"