agentic-loop 3.4.2 → 3.4.4

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/README.md CHANGED
@@ -24,6 +24,8 @@ Ralph reads the PRD and implements each story autonomously. It spawns Claude, ru
24
24
  - `/vibe-check`, `/review` - On-demand quality and security checks
25
25
  - Pre-commit hooks - Block secrets, hardcoded URLs, debug statements
26
26
  - Claude Code hooks - Real-time warnings while coding
27
+ - GitHub Actions CI/CD - Fast PR checks + comprehensive nightly tests
28
+ - Test file enforcement - Fails if new code lacks corresponding tests
27
29
 
28
30
  ---
29
31
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-loop",
3
- "version": "3.4.2",
3
+ "version": "3.4.4",
4
4
  "description": "Autonomous AI coding loop - PRD-driven development with Claude Code",
5
5
  "author": "Allie Jones <allie@allthrive.ai>",
6
6
  "license": "MIT",
package/ralph/ci.sh CHANGED
@@ -19,6 +19,7 @@ ralph_ci() {
19
19
  echo "Commands:"
20
20
  echo " install - Install GitHub Actions workflows"
21
21
  echo " status - Check CI status"
22
+ return 1
22
23
  ;;
23
24
  esac
24
25
  }
package/ralph/loop.sh CHANGED
@@ -325,12 +325,12 @@ run_loop() {
325
325
  timeout_seconds=$(get_config '.maxSessionSeconds' "$DEFAULT_TIMEOUT_SECONDS")
326
326
 
327
327
  # Run Claude - first story gets fresh session, subsequent continue the session
328
- local claude_cmd="claude -p --dangerously-skip-permissions --verbose"
328
+ local -a claude_args=(-p --dangerously-skip-permissions --verbose)
329
329
  if [[ "$session_started" == "true" ]]; then
330
- claude_cmd="claude --continue -p --dangerously-skip-permissions --verbose"
330
+ claude_args=(--continue "${claude_args[@]}")
331
331
  fi
332
332
 
333
- if ! cat "$prompt_file" | run_with_timeout "$timeout_seconds" $claude_cmd; then
333
+ if ! cat "$prompt_file" | run_with_timeout "$timeout_seconds" claude "${claude_args[@]}"; then
334
334
  print_warning "Claude session ended (timeout or error)"
335
335
  log_progress "$story" "TIMEOUT" "Claude session ended after ${timeout_seconds}s"
336
336
  rm -f "$prompt_file"
package/ralph/test.sh CHANGED
@@ -14,8 +14,6 @@ ralph_test() {
14
14
  echo ""
15
15
 
16
16
  local failed=0
17
- local total=0
18
- local passed=0
19
17
 
20
18
  case "$mode" in
21
19
  all)
@@ -76,11 +74,18 @@ run_full_test_suite() {
76
74
  echo "Running: $test_cmd"
77
75
  echo ""
78
76
 
79
- if eval "$test_cmd"; then
77
+ local log_file
78
+ log_file=$(mktemp)
79
+
80
+ if safe_exec "$test_cmd" "$log_file"; then
80
81
  print_success "Unit tests passed"
82
+ rm -f "$log_file"
81
83
  return 0
82
84
  else
83
85
  print_error "Unit tests failed"
86
+ echo ""
87
+ tail -50 "$log_file"
88
+ rm -f "$log_file"
84
89
  return 1
85
90
  fi
86
91
  }
@@ -131,13 +136,16 @@ run_all_prd_tests() {
131
136
 
132
137
  echo -n " $step... "
133
138
 
134
- if eval "$step" >/dev/null 2>&1; then
139
+ local step_log
140
+ step_log=$(mktemp)
141
+ if safe_exec "$step" "$step_log"; then
135
142
  print_success "passed"
136
143
  ((passed++))
137
144
  else
138
145
  print_error "failed"
139
146
  ((failed++))
140
147
  fi
148
+ rm -f "$step_log"
141
149
  done <<< "$test_steps"
142
150
 
143
151
  echo ""
@@ -4,26 +4,81 @@
4
4
 
5
5
  # Check that new/modified source files have corresponding test files
6
6
  # This catches the case where Claude writes code but forgets tests
7
+ # Config: .checks.requireTests = true|false (default: false)
7
8
  verify_test_files_exist() {
8
9
  local story_type="${RALPH_STORY_TYPE:-general}"
9
10
 
11
+ # Check if this check is enabled in config
12
+ local require_tests
13
+ require_tests=$(get_config '.checks.requireTests' "false")
14
+ if [[ "$require_tests" != "true" ]]; then
15
+ return 0
16
+ fi
17
+
10
18
  # Skip for frontend stories (handled differently with .test.tsx pattern)
11
19
  [[ "$story_type" == "frontend" ]] && return 0
12
20
 
13
21
  echo -n " Test files exist for new code... "
14
22
 
23
+ local missing_tests=()
24
+ local checked=0
25
+
26
+ # Check Python files if this is a Python project
27
+ if [[ -f "pyproject.toml" ]] || [[ -f "requirements.txt" ]] || [[ -f "setup.py" ]]; then
28
+ _check_python_test_files missing_tests checked
29
+ fi
30
+
31
+ # Check Go files if this is a Go project
32
+ if [[ -f "go.mod" ]]; then
33
+ _check_go_test_files missing_tests checked
34
+ fi
35
+
36
+ # If nothing to check, skip
37
+ if [[ $checked -eq 0 ]]; then
38
+ print_success "skipped (no new source files)"
39
+ return 0
40
+ fi
41
+
42
+ if [[ ${#missing_tests[@]} -eq 0 ]]; then
43
+ print_success "passed ($checked files checked)"
44
+ return 0
45
+ else
46
+ print_error "missing tests"
47
+ echo ""
48
+ echo " The following files need test files:"
49
+ for file in "${missing_tests[@]}"; do
50
+ echo " $file"
51
+ done
52
+ echo ""
53
+ echo " Create test files for new code before completing the story."
54
+ echo " To disable this check: set .checks.requireTests = false in config.json"
55
+
56
+ # Save for failure context
57
+ {
58
+ echo "Missing test files for new code:"
59
+ for file in "${missing_tests[@]}"; do
60
+ echo " $file"
61
+ done
62
+ } > "$RALPH_DIR/last_test_existence_failure.log"
63
+
64
+ return 1
65
+ fi
66
+ }
67
+
68
+ # Helper: Check Python files have corresponding test files
69
+ # Usage: _check_python_test_files <missing_array_name> <checked_var_name>
70
+ _check_python_test_files() {
71
+ local -n _missing=$1
72
+ local -n _checked=$2
73
+
15
74
  # Get list of modified Python files (excluding tests themselves)
16
75
  local modified_files
17
76
  modified_files=$(git diff --name-only HEAD~1 2>/dev/null | grep '\.py$' | grep -v 'test_' | grep -v '_test\.py' | grep -v '/tests/' || true)
18
77
 
19
- # If no Python files modified, skip
20
- if [[ -z "$modified_files" ]]; then
21
- print_success "skipped (no new Python files)"
22
- return 0
23
- fi
78
+ [[ -z "$modified_files" ]] && return 0
24
79
 
25
- local missing_tests=()
26
- local checked=0
80
+ local backend_dir
81
+ backend_dir=$(get_config '.directories.backend' "")
27
82
 
28
83
  while IFS= read -r src_file; do
29
84
  [[ -z "$src_file" ]] && continue
@@ -35,11 +90,11 @@ verify_test_files_exist() {
35
90
  [[ "$src_file" == *"/alembic/"* ]] && continue
36
91
  [[ "$src_file" == *"config"* ]] && continue
37
92
  [[ "$src_file" == *"settings"* ]] && continue
93
+ [[ "$src_file" == *"conftest"* ]] && continue
38
94
 
39
- ((checked++))
95
+ ((_checked++))
40
96
 
41
- # Determine expected test file location
42
- local base_name dir_name test_file
97
+ local base_name dir_name
43
98
  base_name=$(basename "$src_file" .py)
44
99
  dir_name=$(dirname "$src_file")
45
100
 
@@ -52,9 +107,6 @@ verify_test_files_exist() {
52
107
  "tests/${base_name}_test.py"
53
108
  )
54
109
 
55
- # Check for backend dir patterns
56
- local backend_dir
57
- backend_dir=$(get_config '.directories.backend' "")
58
110
  if [[ -n "$backend_dir" ]]; then
59
111
  possible_tests+=(
60
112
  "$backend_dir/tests/test_${base_name}.py"
@@ -71,35 +123,46 @@ verify_test_files_exist() {
71
123
  done
72
124
 
73
125
  if [[ "$found" == "false" ]]; then
74
- missing_tests+=("$src_file")
126
+ _missing+=("$src_file → test_${base_name}.py")
75
127
  fi
76
128
  done <<< "$modified_files"
129
+ }
77
130
 
78
- if [[ ${#missing_tests[@]} -eq 0 ]]; then
79
- print_success "passed ($checked files checked)"
80
- return 0
81
- else
82
- print_error "missing tests"
83
- echo ""
84
- echo " The following files need test files:"
85
- for file in "${missing_tests[@]}"; do
86
- local base_name
87
- base_name=$(basename "$file" .py)
88
- echo " $file → test_${base_name}.py"
89
- done
90
- echo ""
91
- echo " Create test files for new code before completing the story."
131
+ # Helper: Check Go files have corresponding test files
132
+ # Usage: _check_go_test_files <missing_array_name> <checked_var_name>
133
+ _check_go_test_files() {
134
+ local -n _missing=$1
135
+ local -n _checked=$2
92
136
 
93
- # Save for failure context
94
- {
95
- echo "Missing test files for new code:"
96
- for file in "${missing_tests[@]}"; do
97
- echo " $file"
98
- done
99
- } > "$RALPH_DIR/last_test_existence_failure.log"
137
+ # Get list of modified Go files (excluding tests themselves)
138
+ local modified_files
139
+ modified_files=$(git diff --name-only HEAD~1 2>/dev/null | grep '\.go$' | grep -v '_test\.go$' || true)
100
140
 
101
- return 1
102
- fi
141
+ [[ -z "$modified_files" ]] && return 0
142
+
143
+ while IFS= read -r src_file; do
144
+ [[ -z "$src_file" ]] && continue
145
+ [[ ! -f "$src_file" ]] && continue
146
+
147
+ # Skip generated files, main.go, etc.
148
+ [[ "$src_file" == *"_generated.go" ]] && continue
149
+ [[ "$src_file" == *"/vendor/"* ]] && continue
150
+ [[ "$(basename "$src_file")" == "main.go" ]] && continue
151
+ [[ "$(basename "$src_file")" == "doc.go" ]] && continue
152
+
153
+ ((_checked++))
154
+
155
+ local base_name dir_name
156
+ base_name=$(basename "$src_file" .go)
157
+ dir_name=$(dirname "$src_file")
158
+
159
+ # Go convention: foo.go -> foo_test.go in same directory
160
+ local test_file="$dir_name/${base_name}_test.go"
161
+
162
+ if [[ ! -f "$test_file" ]]; then
163
+ _missing+=("$src_file → ${base_name}_test.go")
164
+ fi
165
+ done <<< "$modified_files"
103
166
  }
104
167
 
105
168
  # Run unit tests
@@ -24,19 +24,24 @@ For each story, you must:
24
24
 
25
25
  **Every new code file MUST have a corresponding test file.**
26
26
 
27
- For **backend** stories (Python/API):
27
+ For **Python** backend stories:
28
28
  - New file `foo.py` → create `tests/test_foo.py`
29
29
  - Test each public function/method
30
30
  - Test error cases (invalid input, missing data, API failures)
31
31
  - Test edge cases (empty lists, None values, boundary conditions)
32
32
  - Use pytest fixtures for database/API mocking
33
33
 
34
+ For **Go** projects:
35
+ - New file `foo.go` → create `foo_test.go` in same directory
36
+ - Use table-driven tests for multiple cases
37
+ - Test error paths and edge cases
38
+
34
39
  For **frontend** stories (TypeScript/React):
35
40
  - New component `Foo.tsx` → create `Foo.test.tsx`
36
41
  - Test rendering, user interactions, error states
37
42
  - Test loading states and empty states
38
43
 
39
- **Do NOT skip tests.** If you create code without tests, verification will fail.
44
+ **Do NOT skip tests.** If test enforcement is enabled, verification will fail without tests.
40
45
 
41
46
  ### 3. Verify It Actually Works
42
47
 
@@ -79,7 +79,7 @@ jobs:
79
79
 
80
80
  # PRD testSteps (if ralph is set up)
81
81
  - name: Install ralph
82
- run: npm install -g thrivekit 2>/dev/null || true
82
+ run: npm install -g agentic-loop 2>/dev/null || true
83
83
 
84
84
  - name: Run PRD tests
85
85
  if: hashFiles('.ralph/prd.json') != ''