agentic-loop 3.10.2 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/api.md +496 -0
- package/.claude/commands/aws.md +408 -0
- package/bin/ralph.sh +2 -1
- package/package.json +2 -1
- package/ralph/code-check.sh +307 -0
- package/ralph/loop.sh +80 -27
- package/ralph/prd-check.sh +498 -0
- package/ralph/utils.sh +66 -351
- package/templates/config/elixir.json +1 -1
- package/templates/config/fastmcp.json +1 -1
- package/templates/config/fullstack.json +1 -1
- package/templates/config/go.json +1 -1
- package/templates/config/minimal.json +1 -1
- package/templates/config/node.json +1 -1
- package/templates/config/python.json +1 -1
- package/templates/config/rust.json +1 -1
- package/ralph/verify.sh +0 -106
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# shellcheck shell=bash
|
|
3
|
+
#
|
|
4
|
+
# code-check.sh - Code verification pipeline for Ralph autonomous development loop
|
|
5
|
+
#
|
|
6
|
+
# ============================================================================
|
|
7
|
+
# OVERVIEW
|
|
8
|
+
# ============================================================================
|
|
9
|
+
# After Claude writes code for a story, this pipeline verifies the work before
|
|
10
|
+
# marking it complete. If verification fails, context is saved and Claude
|
|
11
|
+
# retries with knowledge of what went wrong.
|
|
12
|
+
#
|
|
13
|
+
# Philosophy: Claude handles complex verification (visual, UX, logic) using
|
|
14
|
+
# MCP browser tools. Ralph handles deterministic checks (lint, tests, commands).
|
|
15
|
+
#
|
|
16
|
+
# ============================================================================
|
|
17
|
+
# VERIFICATION PIPELINE (5 steps, fail-fast)
|
|
18
|
+
# ============================================================================
|
|
19
|
+
#
|
|
20
|
+
# [1/5] Lint checks - ESLint, Ruff, golangci-lint, etc.
|
|
21
|
+
# [2/5] Tests - Verify test files exist + run unit tests
|
|
22
|
+
# [3/5] PRD test steps - Execute testSteps commands from prd.json
|
|
23
|
+
# [4/5] API smoke test - Hit health endpoint (if configured)
|
|
24
|
+
# [5/5] Frontend smoke - Load page, check for errors (if configured)
|
|
25
|
+
#
|
|
26
|
+
# Pipeline stops at first failure to save time.
|
|
27
|
+
#
|
|
28
|
+
# ============================================================================
|
|
29
|
+
# FAILURE CONTEXT & LEARNING
|
|
30
|
+
# ============================================================================
|
|
31
|
+
# When verification fails, save_failure_context() ACCUMULATES errors across
|
|
32
|
+
# retries (not just the last failure). This lets Claude see patterns:
|
|
33
|
+
#
|
|
34
|
+
# === Attempt 1 failed for TASK-001 ===
|
|
35
|
+
# ERROR: relation "users" does not exist
|
|
36
|
+
# ---
|
|
37
|
+
# === Attempt 2 failed for TASK-001 ===
|
|
38
|
+
# ERROR: relation "users" does not exist
|
|
39
|
+
# ---
|
|
40
|
+
#
|
|
41
|
+
# Seeing "same error 3 times" signals a structural issue (missing migration,
|
|
42
|
+
# wrong prerequisites) rather than a simple bug to fix.
|
|
43
|
+
#
|
|
44
|
+
# Context is:
|
|
45
|
+
# - Appended per attempt (not overwritten)
|
|
46
|
+
# - Capped at 200 lines to avoid huge prompts
|
|
47
|
+
# - Cleared when switching to a new story
|
|
48
|
+
# - Cleared on success
|
|
49
|
+
#
|
|
50
|
+
# STRUCTURAL ERROR DETECTION:
|
|
51
|
+
# Some errors indicate structural issues (not code bugs) that can't be fixed
|
|
52
|
+
# by retrying. These are detected and flagged with actionable suggestions:
|
|
53
|
+
#
|
|
54
|
+
# - "column does not exist" → Suggest DB reset (schema mismatch)
|
|
55
|
+
# - "pending migration" → Suggest running migrations
|
|
56
|
+
# - "connection refused" → Suggest starting services
|
|
57
|
+
#
|
|
58
|
+
# This prevents infinite retry loops on issues that need manual intervention.
|
|
59
|
+
#
|
|
60
|
+
# ============================================================================
|
|
61
|
+
# CONFIGURATION (via .ralph/config.json)
|
|
62
|
+
# ============================================================================
|
|
63
|
+
#
|
|
64
|
+
# .checks.lint - Run linting (default: true)
|
|
65
|
+
# .checks.test - Run tests: true, false, or "final" (last story only)
|
|
66
|
+
# .checks.requireTests - Require test files for new code (default: false)
|
|
67
|
+
# .api.baseUrl - API URL for smoke tests
|
|
68
|
+
# .api.healthEndpoint - Health check path (default: /api/v1/health)
|
|
69
|
+
# .urls.frontend - Frontend URL for smoke tests
|
|
70
|
+
#
|
|
71
|
+
# ============================================================================
|
|
72
|
+
# MODULES
|
|
73
|
+
# ============================================================================
|
|
74
|
+
# verify/lint.sh - Linting and auto-fix (run_configured_checks)
|
|
75
|
+
# verify/tests.sh - Test existence + execution (verify_test_files_exist,
|
|
76
|
+
# run_unit_tests, verify_prd_criteria)
|
|
77
|
+
# verify/api.sh - API/frontend smoke tests (run_api_smoke_test,
|
|
78
|
+
# run_frontend_smoke_test)
|
|
79
|
+
#
|
|
80
|
+
# DEPENDENCIES: Requires utils.sh to be sourced first (for get_config, print_*)
|
|
81
|
+
#
|
|
82
|
+
# ============================================================================
|
|
83
|
+
|
|
84
|
+
# Source verification modules
|
|
85
|
+
VERIFY_DIR="${RALPH_LIB:-$(dirname "${BASH_SOURCE[0]}")}"
|
|
86
|
+
source "$VERIFY_DIR/verify/lint.sh"
|
|
87
|
+
source "$VERIFY_DIR/verify/tests.sh"
|
|
88
|
+
source "$VERIFY_DIR/verify/api.sh"
|
|
89
|
+
|
|
90
|
+
run_verification() {
|
|
91
|
+
local story="$1"
|
|
92
|
+
|
|
93
|
+
echo ""
|
|
94
|
+
print_info "=== Verification: $story ==="
|
|
95
|
+
echo ""
|
|
96
|
+
|
|
97
|
+
# Get story type for targeted checks
|
|
98
|
+
local story_type
|
|
99
|
+
story_type=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .type // "general"' "$RALPH_DIR/prd.json" 2>/dev/null)
|
|
100
|
+
export RALPH_STORY_TYPE="$story_type"
|
|
101
|
+
|
|
102
|
+
local failed=0
|
|
103
|
+
|
|
104
|
+
# ========================================
|
|
105
|
+
# STEP 1: Run lint checks
|
|
106
|
+
# ========================================
|
|
107
|
+
echo " [1/5] Running lint checks..."
|
|
108
|
+
if ! run_configured_checks "$story_type"; then
|
|
109
|
+
failed=1
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# ========================================
|
|
113
|
+
# STEP 2: Verify tests exist + run them
|
|
114
|
+
# ========================================
|
|
115
|
+
if [[ $failed -eq 0 ]]; then
|
|
116
|
+
echo ""
|
|
117
|
+
echo " [2/5] Running tests..."
|
|
118
|
+
# First check that test files exist for new code
|
|
119
|
+
if ! verify_test_files_exist; then
|
|
120
|
+
failed=1
|
|
121
|
+
elif ! run_unit_tests; then
|
|
122
|
+
failed=1
|
|
123
|
+
fi
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# ========================================
|
|
127
|
+
# STEP 3: Run PRD test steps
|
|
128
|
+
# ========================================
|
|
129
|
+
if [[ $failed -eq 0 ]]; then
|
|
130
|
+
echo ""
|
|
131
|
+
echo " [3/5] Running PRD test steps..."
|
|
132
|
+
if ! verify_prd_criteria "$story"; then
|
|
133
|
+
failed=1
|
|
134
|
+
fi
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# ========================================
|
|
138
|
+
# STEP 4: API smoke test (if configured)
|
|
139
|
+
# ========================================
|
|
140
|
+
if [[ $failed -eq 0 ]]; then
|
|
141
|
+
if ! run_api_smoke_test "$story"; then
|
|
142
|
+
failed=1
|
|
143
|
+
fi
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# ========================================
|
|
147
|
+
# STEP 5: Frontend smoke test (if configured)
|
|
148
|
+
# ========================================
|
|
149
|
+
if [[ $failed -eq 0 ]]; then
|
|
150
|
+
if ! run_frontend_smoke_test "$story"; then
|
|
151
|
+
failed=1
|
|
152
|
+
fi
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# ========================================
|
|
156
|
+
# Final result
|
|
157
|
+
# ========================================
|
|
158
|
+
echo ""
|
|
159
|
+
if [[ $failed -eq 0 ]]; then
|
|
160
|
+
print_success "=== All verification passed ==="
|
|
161
|
+
return 0
|
|
162
|
+
else
|
|
163
|
+
print_error "=== Verification failed ==="
|
|
164
|
+
save_failure_context "$story"
|
|
165
|
+
return 1
|
|
166
|
+
fi
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# ============================================================================
|
|
170
|
+
# FAILURE CONTEXT
|
|
171
|
+
# ============================================================================
|
|
172
|
+
# Accumulates failure history across retries so Claude can identify patterns.
|
|
173
|
+
# If the same error appears multiple times, it's likely a structural issue
|
|
174
|
+
# (missing prerequisites, wrong approach) not a simple bug.
|
|
175
|
+
#
|
|
176
|
+
# Output format in .ralph/last_failure.txt:
|
|
177
|
+
# === Attempt 1 failed for STORY-ID ===
|
|
178
|
+
# <verification output>
|
|
179
|
+
# ---
|
|
180
|
+
# === Attempt 2 failed for STORY-ID ===
|
|
181
|
+
# <verification output>
|
|
182
|
+
# ---
|
|
183
|
+
#
|
|
184
|
+
save_failure_context() {
|
|
185
|
+
local story="$1"
|
|
186
|
+
local context_file="$RALPH_DIR/last_failure.txt"
|
|
187
|
+
|
|
188
|
+
# Get current attempt number from prd.json
|
|
189
|
+
local attempt
|
|
190
|
+
attempt=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .retryCount // 1' "$RALPH_DIR/prd.json" 2>/dev/null || echo "1")
|
|
191
|
+
|
|
192
|
+
# Append to failure history (not overwrite)
|
|
193
|
+
{
|
|
194
|
+
echo ""
|
|
195
|
+
echo "=== Attempt $attempt failed for $story ==="
|
|
196
|
+
echo ""
|
|
197
|
+
if [[ -f "$RALPH_DIR/last_verification.log" ]]; then
|
|
198
|
+
# Shorter excerpt per attempt since we're accumulating
|
|
199
|
+
tail -50 "$RALPH_DIR/last_verification.log"
|
|
200
|
+
fi
|
|
201
|
+
echo ""
|
|
202
|
+
echo "---"
|
|
203
|
+
} >> "$context_file"
|
|
204
|
+
|
|
205
|
+
# Cap file size - keep last ~200 lines to avoid huge prompts
|
|
206
|
+
if [[ -f "$context_file" ]]; then
|
|
207
|
+
local line_count
|
|
208
|
+
line_count=$(wc -l < "$context_file" | tr -d ' ')
|
|
209
|
+
if [[ $line_count -gt 200 ]]; then
|
|
210
|
+
tail -200 "$context_file" > "$context_file.tmp" && mv "$context_file.tmp" "$context_file"
|
|
211
|
+
fi
|
|
212
|
+
fi
|
|
213
|
+
|
|
214
|
+
# Detect structural errors and add actionable suggestions
|
|
215
|
+
_detect_structural_errors "$context_file"
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
# ============================================================================
|
|
219
|
+
# STRUCTURAL ERROR DETECTION
|
|
220
|
+
# ============================================================================
|
|
221
|
+
# Detects error patterns that indicate structural issues (not code bugs).
|
|
222
|
+
# These can't be fixed by retrying - they need specific actions like DB reset.
|
|
223
|
+
#
|
|
224
|
+
_detect_structural_errors() {
|
|
225
|
+
local context_file="$1"
|
|
226
|
+
[[ ! -f "$context_file" ]] && return
|
|
227
|
+
|
|
228
|
+
local error_content
|
|
229
|
+
error_content=$(cat "$context_file")
|
|
230
|
+
|
|
231
|
+
# Schema/column errors - suggest DB reset
|
|
232
|
+
# Only show if not already detected (avoid duplicate markers on retry)
|
|
233
|
+
if echo "$error_content" | grep -qiE "(column.*does not exist|relation.*does not exist|no such column|unknown column|undefined column)" && \
|
|
234
|
+
! grep -q ">>> STRUCTURAL ISSUE: Database schema mismatch" "$context_file" 2>/dev/null; then
|
|
235
|
+
echo ""
|
|
236
|
+
print_warning "STRUCTURAL ISSUE DETECTED: Database schema mismatch"
|
|
237
|
+
echo ""
|
|
238
|
+
echo " The test database is missing columns/tables that the code expects."
|
|
239
|
+
echo " This usually happens when:"
|
|
240
|
+
echo " - Migrations were added but test DB wasn't reset"
|
|
241
|
+
echo " - Models were modified without running migrations"
|
|
242
|
+
echo ""
|
|
243
|
+
echo " SUGGESTED FIX (don't retry code - fix the schema):"
|
|
244
|
+
local reset_cmd
|
|
245
|
+
reset_cmd=$(get_config '.commands.resetDb' "")
|
|
246
|
+
if [[ -n "$reset_cmd" ]]; then
|
|
247
|
+
echo " $reset_cmd"
|
|
248
|
+
else
|
|
249
|
+
echo " # Add to .ralph/config.json:"
|
|
250
|
+
echo " {\"commands\": {\"resetDb\": \"npm run db:reset:test\"}}"
|
|
251
|
+
echo ""
|
|
252
|
+
echo " # Or run manually:"
|
|
253
|
+
echo " dropdb test_db && createdb test_db && alembic upgrade head"
|
|
254
|
+
fi
|
|
255
|
+
echo ""
|
|
256
|
+
|
|
257
|
+
# Append suggestion to failure context for Claude
|
|
258
|
+
{
|
|
259
|
+
echo ""
|
|
260
|
+
echo ">>> STRUCTURAL ISSUE: Database schema mismatch"
|
|
261
|
+
echo ">>> ACTION NEEDED: Reset test database, don't just retry code"
|
|
262
|
+
echo ">>> This is NOT a code bug - the test DB is missing schema changes"
|
|
263
|
+
} >> "$context_file"
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
# Migration pending errors
|
|
267
|
+
if echo "$error_content" | grep -qiE "(pending migration|migrations are pending|migrate your database|alembic.*head)" && \
|
|
268
|
+
! grep -q ">>> STRUCTURAL ISSUE: Pending migrations" "$context_file" 2>/dev/null; then
|
|
269
|
+
echo ""
|
|
270
|
+
print_warning "STRUCTURAL ISSUE DETECTED: Pending migrations"
|
|
271
|
+
echo ""
|
|
272
|
+
echo " Migrations need to be applied before tests can run."
|
|
273
|
+
echo ""
|
|
274
|
+
echo " SUGGESTED FIX:"
|
|
275
|
+
local migrate_cmd
|
|
276
|
+
migrate_cmd=$(get_config '.migrations.command' "alembic upgrade head")
|
|
277
|
+
echo " $migrate_cmd"
|
|
278
|
+
echo ""
|
|
279
|
+
|
|
280
|
+
{
|
|
281
|
+
echo ""
|
|
282
|
+
echo ">>> STRUCTURAL ISSUE: Pending migrations"
|
|
283
|
+
echo ">>> ACTION NEEDED: Run migrations before retrying"
|
|
284
|
+
} >> "$context_file"
|
|
285
|
+
fi
|
|
286
|
+
|
|
287
|
+
# Connection refused - service not running
|
|
288
|
+
if echo "$error_content" | grep -qiE "(connection refused|ECONNREFUSED|could not connect|connection error)" && \
|
|
289
|
+
! grep -q ">>> STRUCTURAL ISSUE: Service not running" "$context_file" 2>/dev/null; then
|
|
290
|
+
echo ""
|
|
291
|
+
print_warning "STRUCTURAL ISSUE DETECTED: Service not running"
|
|
292
|
+
echo ""
|
|
293
|
+
echo " A required service (database, API, etc.) is not running."
|
|
294
|
+
echo ""
|
|
295
|
+
echo " SUGGESTED FIX:"
|
|
296
|
+
local dev_cmd
|
|
297
|
+
dev_cmd=$(get_config '.commands.dev' "docker compose up -d")
|
|
298
|
+
echo " $dev_cmd"
|
|
299
|
+
echo ""
|
|
300
|
+
|
|
301
|
+
{
|
|
302
|
+
echo ""
|
|
303
|
+
echo ">>> STRUCTURAL ISSUE: Service not running"
|
|
304
|
+
echo ">>> ACTION NEEDED: Start required services before retrying"
|
|
305
|
+
} >> "$context_file"
|
|
306
|
+
fi
|
|
307
|
+
}
|
package/ralph/loop.sh
CHANGED
|
@@ -178,7 +178,12 @@ run_loop() {
|
|
|
178
178
|
local iteration=0
|
|
179
179
|
local last_story=""
|
|
180
180
|
local consecutive_failures=0
|
|
181
|
-
local
|
|
181
|
+
local consecutive_timeouts=0
|
|
182
|
+
local max_story_retries
|
|
183
|
+
local max_timeouts=5 # Skip after 5 consecutive timeouts (likely too large/complex)
|
|
184
|
+
# Default to 15 retries - generous enough for transient issues, catches infinite loops
|
|
185
|
+
# Override with config.json: "maxStoryRetries": 25
|
|
186
|
+
max_story_retries=$(get_config '.maxStoryRetries' "15")
|
|
182
187
|
local total_attempts=0
|
|
183
188
|
local skipped_stories=()
|
|
184
189
|
local start_time
|
|
@@ -226,34 +231,58 @@ run_loop() {
|
|
|
226
231
|
|
|
227
232
|
((total_attempts++))
|
|
228
233
|
|
|
229
|
-
# Track repeated failures on same story
|
|
234
|
+
# Track repeated failures on same story (also load from prd.json for restart persistence)
|
|
230
235
|
if [[ "$story" == "$last_story" ]]; then
|
|
231
236
|
((consecutive_failures++))
|
|
232
|
-
|
|
233
|
-
# Circuit breaker: skip to next story after max retries
|
|
234
|
-
if [[ $consecutive_failures -gt $max_story_retries ]]; then
|
|
235
|
-
print_error "Circuit breaker: $story failed $max_story_retries times, skipping to next story"
|
|
236
|
-
echo ""
|
|
237
|
-
echo " Saved failure context to: $RALPH_DIR/failures/$story.txt"
|
|
238
|
-
mkdir -p "$RALPH_DIR/failures"
|
|
239
|
-
cp "$RALPH_DIR/last_failure.txt" "$RALPH_DIR/failures/$story.txt" 2>/dev/null || true
|
|
240
|
-
# Clear failure context so it doesn't leak into next story
|
|
241
|
-
rm -f "$RALPH_DIR/last_failure.txt"
|
|
242
|
-
skipped_stories+=("$story")
|
|
243
|
-
# Mark as skipped (not passed, but move on)
|
|
244
|
-
jq --arg id "$story" '(.stories[] | select(.id==$id)) |= . + {skipped: true}' "$RALPH_DIR/prd.json" > "$RALPH_DIR/prd.json.tmp" && mv "$RALPH_DIR/prd.json.tmp" "$RALPH_DIR/prd.json"
|
|
245
|
-
last_story=""
|
|
246
|
-
consecutive_failures=0
|
|
247
|
-
continue
|
|
248
|
-
fi
|
|
249
|
-
|
|
250
|
-
# Quick retry - no delay needed (Claude API isn't rate-limited)
|
|
251
|
-
print_warning "Retry $consecutive_failures/$max_story_retries for $story"
|
|
252
237
|
else
|
|
253
|
-
|
|
238
|
+
# New story - clear failure history from previous story
|
|
239
|
+
rm -f "$RALPH_DIR/last_failure.txt"
|
|
240
|
+
# Load retry count from prd.json (persists across restarts)
|
|
241
|
+
consecutive_failures=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .retryCount // 0' "$RALPH_DIR/prd.json")
|
|
242
|
+
consecutive_failures=$((consecutive_failures + 1))
|
|
243
|
+
consecutive_timeouts=0
|
|
254
244
|
last_story="$story"
|
|
255
245
|
fi
|
|
256
246
|
|
|
247
|
+
# Persist retry count to prd.json (survives restarts)
|
|
248
|
+
jq --arg id "$story" --argjson count "$consecutive_failures" \
|
|
249
|
+
'(.stories[] | select(.id==$id)) |= . + {retryCount: $count}' \
|
|
250
|
+
"$RALPH_DIR/prd.json" > "$RALPH_DIR/prd.json.tmp" && mv "$RALPH_DIR/prd.json.tmp" "$RALPH_DIR/prd.json"
|
|
251
|
+
|
|
252
|
+
# Circuit breaker: skip to next story after max retries (prevents infinite loops)
|
|
253
|
+
# Note: This is NOT meant to stop legitimate retrying - 15 attempts is generous.
|
|
254
|
+
# If a story consistently fails after this many tries, it likely needs manual review
|
|
255
|
+
# (vague test steps, missing prerequisites, or fundamentally broken requirements).
|
|
256
|
+
if [[ $consecutive_failures -gt $max_story_retries ]]; then
|
|
257
|
+
print_error "Story $story has failed $consecutive_failures times - likely needs manual review"
|
|
258
|
+
echo ""
|
|
259
|
+
echo " This usually means:"
|
|
260
|
+
echo " - Test steps are too vague or ambiguous"
|
|
261
|
+
echo " - Missing prerequisites (DB setup, env vars, etc.)"
|
|
262
|
+
echo " - Story scope is too large - consider breaking it up"
|
|
263
|
+
echo ""
|
|
264
|
+
echo " Failure context saved to: $RALPH_DIR/failures/$story.txt"
|
|
265
|
+
mkdir -p "$RALPH_DIR/failures"
|
|
266
|
+
cp "$RALPH_DIR/last_failure.txt" "$RALPH_DIR/failures/$story.txt" 2>/dev/null || true
|
|
267
|
+
rm -f "$RALPH_DIR/last_failure.txt"
|
|
268
|
+
skipped_stories+=("$story")
|
|
269
|
+
jq --arg id "$story" '(.stories[] | select(.id==$id)) |= . + {skipped: true, skipReason: "exceeded max retries"}' "$RALPH_DIR/prd.json" > "$RALPH_DIR/prd.json.tmp" && mv "$RALPH_DIR/prd.json.tmp" "$RALPH_DIR/prd.json"
|
|
270
|
+
last_story=""
|
|
271
|
+
consecutive_failures=0
|
|
272
|
+
continue
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
# Show retry status (but don't make it scary - retrying is normal!)
|
|
276
|
+
if [[ $consecutive_failures -gt 1 ]]; then
|
|
277
|
+
if [[ $consecutive_failures -le 3 ]]; then
|
|
278
|
+
print_info "Attempt $consecutive_failures for $story (normal - refining solution)"
|
|
279
|
+
elif [[ $consecutive_failures -le 8 ]]; then
|
|
280
|
+
print_warning "Attempt $consecutive_failures/$max_story_retries for $story"
|
|
281
|
+
else
|
|
282
|
+
print_warning "Attempt $consecutive_failures/$max_story_retries for $story (getting close to limit)"
|
|
283
|
+
fi
|
|
284
|
+
fi
|
|
285
|
+
|
|
257
286
|
# 2. Session startup checklist (skip on retries)
|
|
258
287
|
[[ $consecutive_failures -gt 1 ]] && startup_checklist "true" || startup_checklist "false"
|
|
259
288
|
|
|
@@ -373,18 +402,42 @@ run_loop() {
|
|
|
373
402
|
fi
|
|
374
403
|
|
|
375
404
|
if [[ $claude_exit_code -ne 0 ]]; then
|
|
376
|
-
|
|
377
|
-
|
|
405
|
+
((consecutive_timeouts++))
|
|
406
|
+
print_warning "Claude session ended (timeout or error) - timeout $consecutive_timeouts/$max_timeouts"
|
|
407
|
+
log_progress "$story" "TIMEOUT" "Claude session ended after ${timeout_seconds}s (timeout $consecutive_timeouts)"
|
|
378
408
|
rm -f "$prompt_file"
|
|
379
409
|
|
|
380
410
|
# Session may be broken - reset for next attempt
|
|
381
411
|
session_started=false
|
|
382
412
|
|
|
413
|
+
# Skip on repeated timeouts (story is too large/complex for single session)
|
|
414
|
+
if [[ $consecutive_timeouts -ge $max_timeouts ]]; then
|
|
415
|
+
print_error "Story $story timed out $max_timeouts times - needs to be broken up"
|
|
416
|
+
echo ""
|
|
417
|
+
echo " Consecutive timeouts indicate the story is too large for a single"
|
|
418
|
+
echo " Claude session (${timeout_seconds}s). Consider:"
|
|
419
|
+
echo " - Breaking it into smaller, focused stories"
|
|
420
|
+
echo " - Increasing maxSessionSeconds in config.json"
|
|
421
|
+
echo ""
|
|
422
|
+
mkdir -p "$RALPH_DIR/failures"
|
|
423
|
+
echo "Story $story timed out $max_timeouts consecutive times (${timeout_seconds}s each)" > "$RALPH_DIR/failures/$story.txt"
|
|
424
|
+
echo "Consider breaking this story into smaller pieces." >> "$RALPH_DIR/failures/$story.txt"
|
|
425
|
+
skipped_stories+=("$story")
|
|
426
|
+
jq --arg id "$story" '(.stories[] | select(.id==$id)) |= . + {skipped: true, skipReason: "repeated timeouts"}' "$RALPH_DIR/prd.json" > "$RALPH_DIR/prd.json.tmp" && mv "$RALPH_DIR/prd.json.tmp" "$RALPH_DIR/prd.json"
|
|
427
|
+
last_story=""
|
|
428
|
+
consecutive_failures=0
|
|
429
|
+
consecutive_timeouts=0
|
|
430
|
+
continue
|
|
431
|
+
fi
|
|
432
|
+
|
|
383
433
|
# If running specific story, exit on failure
|
|
384
434
|
[[ -n "$specific_story" ]] && return 1
|
|
385
435
|
continue
|
|
386
436
|
fi
|
|
387
437
|
|
|
438
|
+
# Reset timeout counter on successful Claude run
|
|
439
|
+
consecutive_timeouts=0
|
|
440
|
+
|
|
388
441
|
rm -f "$prompt_file"
|
|
389
442
|
session_started=true # Mark session as active for subsequent stories
|
|
390
443
|
|
|
@@ -402,9 +455,9 @@ run_loop() {
|
|
|
402
455
|
local verify_log="$RALPH_DIR/last_verification.log"
|
|
403
456
|
set -o pipefail
|
|
404
457
|
if run_verification "$story" 2>&1 | tee "$verify_log"; then
|
|
405
|
-
# Mark story as complete
|
|
458
|
+
# Mark story as complete and reset retry count
|
|
406
459
|
update_json "$RALPH_DIR/prd.json" \
|
|
407
|
-
--arg id "$story" '(.stories[] | select(.id==$id)
|
|
460
|
+
--arg id "$story" '(.stories[] | select(.id==$id)) |= . + {passes: true, retryCount: 0}'
|
|
408
461
|
|
|
409
462
|
# Clear failure context on success
|
|
410
463
|
rm -f "$RALPH_DIR/last_failure.txt"
|