agentic-loop 3.4.7 → 3.5.2
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/idea.md +42 -252
- package/.claude/commands/prd.md +474 -71
- package/README.md +4 -1
- package/package.json +1 -1
- package/ralph/ci.sh +193 -34
- package/ralph/hooks/protect-prd.sh +12 -20
- package/ralph/loop.sh +51 -194
- package/ralph/prd.sh +5 -0
- package/ralph/setup.sh +298 -16
- package/ralph/verify/tests.sh +58 -3
- package/templates/PROMPT.md +143 -191
- package/templates/config/fullstack.json +1 -1
- package/templates/examples/CLAUDE-fullstack.md +3 -2
- package/templates/examples/CLAUDE-node.md +2 -1
- package/templates/examples/CLAUDE-react.md +2 -1
- package/templates/github/workflows/nightly.yml +9 -74
- package/templates/github/workflows/pr.yml +7 -31
- package/templates/signs.json +7 -0
package/ralph/setup.sh
CHANGED
|
@@ -2,6 +2,80 @@
|
|
|
2
2
|
# shellcheck shell=bash
|
|
3
3
|
# setup.sh - Set up agentic-loop in a project
|
|
4
4
|
|
|
5
|
+
# Append missing sections from template to existing PROMPT.md
|
|
6
|
+
# Usage: append_prompt_sections <template_file> <target_file>
|
|
7
|
+
append_prompt_sections() {
|
|
8
|
+
local template="$1"
|
|
9
|
+
local target="$2"
|
|
10
|
+
local added=false
|
|
11
|
+
|
|
12
|
+
# Key sections to check and append if missing
|
|
13
|
+
local sections=(
|
|
14
|
+
"### 3. Verify It Actually Works"
|
|
15
|
+
"**Playwright MCP**"
|
|
16
|
+
"**Chrome DevTools MCP**"
|
|
17
|
+
"## Verification Checklist"
|
|
18
|
+
"## Code Quality Standards"
|
|
19
|
+
"## Architecture Rules"
|
|
20
|
+
"## Scalability Rules"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
for section in "${sections[@]}"; do
|
|
24
|
+
if ! grep -qF "$section" "$target" 2>/dev/null; then
|
|
25
|
+
# Section missing - extract and append it
|
|
26
|
+
case "$section" in
|
|
27
|
+
"### 3. Verify It Actually Works"|"**Playwright MCP**"|"**Chrome DevTools MCP**")
|
|
28
|
+
# Extract the browser verification block
|
|
29
|
+
if ! grep -qF "Playwright MCP" "$target" 2>/dev/null; then
|
|
30
|
+
echo "" >> "$target"
|
|
31
|
+
echo "### 3. Verify It Actually Works" >> "$target"
|
|
32
|
+
echo "" >> "$target"
|
|
33
|
+
echo "**You have browser tools - USE THEM to verify your work:**" >> "$target"
|
|
34
|
+
echo "" >> "$target"
|
|
35
|
+
echo "**Playwright MCP** (testing & automation):" >> "$target"
|
|
36
|
+
echo "- Navigate to URLs and verify page content" >> "$target"
|
|
37
|
+
echo "- Take screenshots to verify UI renders correctly" >> "$target"
|
|
38
|
+
echo "- Click elements and fill forms to test interactions" >> "$target"
|
|
39
|
+
echo "- Get accessibility snapshots for a11y testing" >> "$target"
|
|
40
|
+
echo "" >> "$target"
|
|
41
|
+
echo "**Chrome DevTools MCP** (debugging & inspection):" >> "$target"
|
|
42
|
+
echo "- Inspect DOM elements and check console for errors" >> "$target"
|
|
43
|
+
echo "- Debug network requests and responses" >> "$target"
|
|
44
|
+
echo "- Check element styles and computed properties" >> "$target"
|
|
45
|
+
echo "" >> "$target"
|
|
46
|
+
echo "**Do NOT say you're done until:**" >> "$target"
|
|
47
|
+
echo "- All unit tests pass" >> "$target"
|
|
48
|
+
echo "- You've opened the browser and visually verified the feature works" >> "$target"
|
|
49
|
+
echo "- Console has no errors" >> "$target"
|
|
50
|
+
echo "- Error states are handled gracefully" >> "$target"
|
|
51
|
+
added=true
|
|
52
|
+
break # Don't re-add for the other MCP checks
|
|
53
|
+
fi
|
|
54
|
+
;;
|
|
55
|
+
"## Verification Checklist")
|
|
56
|
+
echo "" >> "$target"
|
|
57
|
+
echo "## Verification Checklist" >> "$target"
|
|
58
|
+
echo "" >> "$target"
|
|
59
|
+
echo "Before considering any story complete:" >> "$target"
|
|
60
|
+
echo "" >> "$target"
|
|
61
|
+
echo "- [ ] All acceptance criteria are met" >> "$target"
|
|
62
|
+
echo "- [ ] All error handling from story is implemented" >> "$target"
|
|
63
|
+
echo "- [ ] TypeScript/code compiles without errors" >> "$target"
|
|
64
|
+
echo "- [ ] Unit tests written and passing" >> "$target"
|
|
65
|
+
echo "- [ ] **Browser verified** - used MCP tools to visually confirm it works" >> "$target"
|
|
66
|
+
echo "- [ ] No console errors" >> "$target"
|
|
67
|
+
echo "- [ ] Linting passes" >> "$target"
|
|
68
|
+
added=true
|
|
69
|
+
;;
|
|
70
|
+
esac
|
|
71
|
+
fi
|
|
72
|
+
done
|
|
73
|
+
|
|
74
|
+
if [[ "$added" == "true" ]]; then
|
|
75
|
+
echo " Updated PROMPT.md with new sections"
|
|
76
|
+
fi
|
|
77
|
+
}
|
|
78
|
+
|
|
5
79
|
ralph_setup() {
|
|
6
80
|
echo ""
|
|
7
81
|
echo " _ _ _ _ "
|
|
@@ -83,7 +157,7 @@ setup_ralph_dir() {
|
|
|
83
157
|
echo " Created .ralph/config.json"
|
|
84
158
|
fi
|
|
85
159
|
|
|
86
|
-
# Copy signs template
|
|
160
|
+
# Copy or merge signs template
|
|
87
161
|
if [[ ! -f ".ralph/signs.json" ]]; then
|
|
88
162
|
if [[ -f "$pkg_root/templates/signs.json" ]]; then
|
|
89
163
|
cp "$pkg_root/templates/signs.json" ".ralph/signs.json"
|
|
@@ -91,12 +165,41 @@ setup_ralph_dir() {
|
|
|
91
165
|
echo '{"signs": []}' > ".ralph/signs.json"
|
|
92
166
|
fi
|
|
93
167
|
echo " Created .ralph/signs.json"
|
|
168
|
+
else
|
|
169
|
+
# Merge new default signs into existing file
|
|
170
|
+
if [[ -f "$pkg_root/templates/signs.json" ]] && command -v jq &>/dev/null; then
|
|
171
|
+
local existing_ids new_signs_added=0
|
|
172
|
+
existing_ids=$(jq -r '.signs[].id // empty' ".ralph/signs.json" 2>/dev/null | tr '\n' '|')
|
|
173
|
+
|
|
174
|
+
# Add signs from template that don't exist locally
|
|
175
|
+
while IFS= read -r sign; do
|
|
176
|
+
local sign_id
|
|
177
|
+
sign_id=$(echo "$sign" | jq -r '.id // empty')
|
|
178
|
+
if [[ -n "$sign_id" && ! "$existing_ids" =~ "$sign_id" ]]; then
|
|
179
|
+
# Add this sign to local file
|
|
180
|
+
local tmp_file
|
|
181
|
+
tmp_file=$(mktemp)
|
|
182
|
+
jq --argjson new_sign "$sign" '.signs += [$new_sign]' ".ralph/signs.json" > "$tmp_file"
|
|
183
|
+
mv "$tmp_file" ".ralph/signs.json"
|
|
184
|
+
((new_signs_added++))
|
|
185
|
+
fi
|
|
186
|
+
done < <(jq -c '.signs[]' "$pkg_root/templates/signs.json" 2>/dev/null)
|
|
187
|
+
|
|
188
|
+
if [[ $new_signs_added -gt 0 ]]; then
|
|
189
|
+
echo " Merged $new_signs_added new sign(s) into .ralph/signs.json"
|
|
190
|
+
fi
|
|
191
|
+
fi
|
|
94
192
|
fi
|
|
95
193
|
|
|
96
|
-
# Create PROMPT.md
|
|
97
|
-
if [[
|
|
98
|
-
|
|
99
|
-
|
|
194
|
+
# Create or update PROMPT.md
|
|
195
|
+
if [[ -f "$pkg_root/templates/PROMPT.md" ]]; then
|
|
196
|
+
if [[ ! -f "PROMPT.md" ]]; then
|
|
197
|
+
cp "$pkg_root/templates/PROMPT.md" "PROMPT.md"
|
|
198
|
+
echo " Created PROMPT.md"
|
|
199
|
+
else
|
|
200
|
+
# Append missing sections to existing PROMPT.md
|
|
201
|
+
append_prompt_sections "$pkg_root/templates/PROMPT.md" "PROMPT.md"
|
|
202
|
+
fi
|
|
100
203
|
fi
|
|
101
204
|
|
|
102
205
|
# Auto-detect project settings
|
|
@@ -468,18 +571,12 @@ EOF
|
|
|
468
571
|
# Set up GitHub Actions CI/CD
|
|
469
572
|
setup_github_ci() {
|
|
470
573
|
local pkg_root="$1"
|
|
471
|
-
local template_dir="$pkg_root/templates/github/workflows"
|
|
472
574
|
|
|
473
575
|
# Skip if not a git repo
|
|
474
576
|
if [[ ! -d ".git" ]]; then
|
|
475
577
|
return 0
|
|
476
578
|
fi
|
|
477
579
|
|
|
478
|
-
# Skip if templates don't exist
|
|
479
|
-
if [[ ! -d "$template_dir" ]]; then
|
|
480
|
-
return 0
|
|
481
|
-
fi
|
|
482
|
-
|
|
483
580
|
# Skip if already set up
|
|
484
581
|
if [[ -f ".github/workflows/pr.yml" ]] && [[ -f ".github/workflows/nightly.yml" ]]; then
|
|
485
582
|
return 0
|
|
@@ -493,7 +590,7 @@ setup_github_ci() {
|
|
|
493
590
|
read -r -p " Set up GitHub Actions? [Y/n] " response
|
|
494
591
|
|
|
495
592
|
if [[ "$response" =~ ^[Nn]$ ]]; then
|
|
496
|
-
echo " Skipped GitHub Actions (run '
|
|
593
|
+
echo " Skipped GitHub Actions (run 'npx agentic-loop ci install' later)"
|
|
497
594
|
return 0
|
|
498
595
|
fi
|
|
499
596
|
|
|
@@ -502,15 +599,200 @@ setup_github_ci() {
|
|
|
502
599
|
# Create workflows directory
|
|
503
600
|
mkdir -p .github/workflows
|
|
504
601
|
|
|
505
|
-
#
|
|
602
|
+
# Read config values for dynamic generation
|
|
603
|
+
local backend_dir frontend_dir test_cmd
|
|
604
|
+
backend_dir=$(get_config '.directories.backend' "")
|
|
605
|
+
frontend_dir=$(get_config '.directories.frontend' "")
|
|
606
|
+
test_cmd=$(get_config '.checks.testCommand' "")
|
|
607
|
+
|
|
608
|
+
# Generate PR workflow
|
|
506
609
|
if [[ ! -f ".github/workflows/pr.yml" ]]; then
|
|
507
|
-
|
|
610
|
+
_generate_pr_workflow "$backend_dir" "$frontend_dir"
|
|
508
611
|
echo " Created .github/workflows/pr.yml (fast PR checks)"
|
|
509
612
|
fi
|
|
510
613
|
|
|
511
|
-
#
|
|
614
|
+
# Generate nightly workflow
|
|
512
615
|
if [[ ! -f ".github/workflows/nightly.yml" ]]; then
|
|
513
|
-
|
|
616
|
+
_generate_nightly_workflow "$backend_dir" "$frontend_dir" "$test_cmd"
|
|
514
617
|
echo " Created .github/workflows/nightly.yml (nightly full tests)"
|
|
515
618
|
fi
|
|
516
619
|
}
|
|
620
|
+
|
|
621
|
+
# Generate PR workflow based on project structure
|
|
622
|
+
_generate_pr_workflow() {
|
|
623
|
+
local backend_dir="$1"
|
|
624
|
+
local frontend_dir="$2"
|
|
625
|
+
|
|
626
|
+
cat > .github/workflows/pr.yml << 'HEADER'
|
|
627
|
+
# Fast PR checks - lint only, no tests
|
|
628
|
+
name: PR Check
|
|
629
|
+
|
|
630
|
+
on:
|
|
631
|
+
pull_request:
|
|
632
|
+
branches: [main, master]
|
|
633
|
+
|
|
634
|
+
jobs:
|
|
635
|
+
lint:
|
|
636
|
+
runs-on: ubuntu-latest
|
|
637
|
+
steps:
|
|
638
|
+
- uses: actions/checkout@v4
|
|
639
|
+
HEADER
|
|
640
|
+
|
|
641
|
+
# Detect and add Python steps
|
|
642
|
+
if [[ -f "pyproject.toml" ]] || [[ -f "requirements.txt" ]] || [[ -n "$backend_dir" && -f "$backend_dir/pyproject.toml" ]]; then
|
|
643
|
+
local py_dir="${backend_dir:-.}"
|
|
644
|
+
cat >> .github/workflows/pr.yml << EOF
|
|
645
|
+
|
|
646
|
+
- name: Set up Python
|
|
647
|
+
uses: actions/setup-python@v5
|
|
648
|
+
with:
|
|
649
|
+
python-version: '3.11'
|
|
650
|
+
|
|
651
|
+
- name: Install Python dependencies
|
|
652
|
+
run: |
|
|
653
|
+
pip install ruff uv
|
|
654
|
+
cd $py_dir && uv pip install -e . --system 2>/dev/null || pip install -e . 2>/dev/null || true
|
|
655
|
+
|
|
656
|
+
- name: Ruff lint
|
|
657
|
+
run: cd $py_dir && ruff check .
|
|
658
|
+
EOF
|
|
659
|
+
fi
|
|
660
|
+
|
|
661
|
+
# Detect and add Node.js steps
|
|
662
|
+
if [[ -f "package.json" ]] || [[ -n "$frontend_dir" && -f "$frontend_dir/package.json" ]]; then
|
|
663
|
+
local node_dir="${frontend_dir:-.}"
|
|
664
|
+
cat >> .github/workflows/pr.yml << EOF
|
|
665
|
+
|
|
666
|
+
- name: Set up Node.js
|
|
667
|
+
uses: actions/setup-node@v4
|
|
668
|
+
with:
|
|
669
|
+
node-version: '20'
|
|
670
|
+
|
|
671
|
+
- name: Install Node dependencies
|
|
672
|
+
run: cd $node_dir && npm ci
|
|
673
|
+
|
|
674
|
+
- name: Lint
|
|
675
|
+
run: cd $node_dir && npm run lint 2>/dev/null || true
|
|
676
|
+
|
|
677
|
+
- name: TypeScript check
|
|
678
|
+
run: cd $node_dir && npx tsc --noEmit 2>/dev/null || true
|
|
679
|
+
|
|
680
|
+
- name: Build
|
|
681
|
+
run: cd $node_dir && npm run build 2>/dev/null || true
|
|
682
|
+
EOF
|
|
683
|
+
fi
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
# Generate nightly workflow based on project structure
|
|
687
|
+
_generate_nightly_workflow() {
|
|
688
|
+
local backend_dir="$1"
|
|
689
|
+
local frontend_dir="$2"
|
|
690
|
+
local test_cmd="$3"
|
|
691
|
+
|
|
692
|
+
cat > .github/workflows/nightly.yml << 'HEADER'
|
|
693
|
+
# Nightly comprehensive test suite
|
|
694
|
+
name: Nightly Tests
|
|
695
|
+
|
|
696
|
+
on:
|
|
697
|
+
schedule:
|
|
698
|
+
- cron: '0 3 * * *' # 3am UTC daily
|
|
699
|
+
workflow_dispatch:
|
|
700
|
+
|
|
701
|
+
jobs:
|
|
702
|
+
test:
|
|
703
|
+
runs-on: ubuntu-latest
|
|
704
|
+
HEADER
|
|
705
|
+
|
|
706
|
+
# Add services if backend exists (likely needs DB)
|
|
707
|
+
if [[ -n "$backend_dir" ]] || [[ -f "pyproject.toml" ]]; then
|
|
708
|
+
cat >> .github/workflows/nightly.yml << 'EOF'
|
|
709
|
+
|
|
710
|
+
services:
|
|
711
|
+
postgres:
|
|
712
|
+
image: postgres:15
|
|
713
|
+
env:
|
|
714
|
+
POSTGRES_USER: test
|
|
715
|
+
POSTGRES_PASSWORD: test
|
|
716
|
+
POSTGRES_DB: test
|
|
717
|
+
ports:
|
|
718
|
+
- 5432:5432
|
|
719
|
+
options: >-
|
|
720
|
+
--health-cmd pg_isready
|
|
721
|
+
--health-interval 10s
|
|
722
|
+
--health-timeout 5s
|
|
723
|
+
--health-retries 5
|
|
724
|
+
|
|
725
|
+
env:
|
|
726
|
+
DATABASE_URL: postgresql://test:test@localhost:5432/test
|
|
727
|
+
EOF
|
|
728
|
+
fi
|
|
729
|
+
|
|
730
|
+
cat >> .github/workflows/nightly.yml << 'EOF'
|
|
731
|
+
|
|
732
|
+
steps:
|
|
733
|
+
- uses: actions/checkout@v4
|
|
734
|
+
EOF
|
|
735
|
+
|
|
736
|
+
# Add Python setup and tests
|
|
737
|
+
if [[ -f "pyproject.toml" ]] || [[ -n "$backend_dir" && -f "$backend_dir/pyproject.toml" ]]; then
|
|
738
|
+
local py_dir="${backend_dir:-.}"
|
|
739
|
+
local py_test_cmd="${test_cmd:-pytest -v --tb=short}"
|
|
740
|
+
|
|
741
|
+
cat >> .github/workflows/nightly.yml << EOF
|
|
742
|
+
|
|
743
|
+
- name: Set up Python
|
|
744
|
+
uses: actions/setup-python@v5
|
|
745
|
+
with:
|
|
746
|
+
python-version: '3.11'
|
|
747
|
+
|
|
748
|
+
- name: Install Python dependencies
|
|
749
|
+
run: |
|
|
750
|
+
pip install uv
|
|
751
|
+
cd $py_dir && uv pip install -e ".[dev]" --system 2>/dev/null || pip install -e . 2>/dev/null || true
|
|
752
|
+
|
|
753
|
+
- name: Run migrations
|
|
754
|
+
run: cd $py_dir && alembic upgrade head 2>/dev/null || true
|
|
755
|
+
continue-on-error: true
|
|
756
|
+
|
|
757
|
+
- name: Python tests
|
|
758
|
+
run: cd $py_dir && $py_test_cmd
|
|
759
|
+
continue-on-error: true
|
|
760
|
+
EOF
|
|
761
|
+
fi
|
|
762
|
+
|
|
763
|
+
# Add Node.js setup and tests
|
|
764
|
+
if [[ -f "package.json" ]] || [[ -n "$frontend_dir" && -f "$frontend_dir/package.json" ]]; then
|
|
765
|
+
local node_dir="${frontend_dir:-.}"
|
|
766
|
+
cat >> .github/workflows/nightly.yml << EOF
|
|
767
|
+
|
|
768
|
+
- name: Set up Node.js
|
|
769
|
+
uses: actions/setup-node@v4
|
|
770
|
+
with:
|
|
771
|
+
node-version: '20'
|
|
772
|
+
|
|
773
|
+
- name: Install Node dependencies
|
|
774
|
+
run: cd $node_dir && npm ci
|
|
775
|
+
|
|
776
|
+
- name: Node tests
|
|
777
|
+
run: cd $node_dir && npm test 2>/dev/null || true
|
|
778
|
+
continue-on-error: true
|
|
779
|
+
EOF
|
|
780
|
+
fi
|
|
781
|
+
|
|
782
|
+
# Add PRD tests
|
|
783
|
+
cat >> .github/workflows/nightly.yml << 'EOF'
|
|
784
|
+
|
|
785
|
+
- name: Run PRD tests
|
|
786
|
+
if: hashFiles('.ralph/prd.json') != ''
|
|
787
|
+
run: npx agentic-loop test prd 2>/dev/null || true
|
|
788
|
+
continue-on-error: true
|
|
789
|
+
|
|
790
|
+
notify:
|
|
791
|
+
needs: test
|
|
792
|
+
runs-on: ubuntu-latest
|
|
793
|
+
if: failure()
|
|
794
|
+
steps:
|
|
795
|
+
- name: Notify on failure
|
|
796
|
+
run: echo "Nightly tests failed!"
|
|
797
|
+
EOF
|
|
798
|
+
}
|
package/ralph/verify/tests.sh
CHANGED
|
@@ -223,6 +223,57 @@ run_unit_tests() {
|
|
|
223
223
|
fi
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
# Expand config placeholders in a string
|
|
227
|
+
# Usage: expand_config_vars "curl {config.urls.backend}/api"
|
|
228
|
+
# Expands: {config.urls.backend}, {config.urls.frontend}, {config.directories.*}
|
|
229
|
+
_expand_config_vars() {
|
|
230
|
+
local input="$1"
|
|
231
|
+
local config="$RALPH_DIR/config.json"
|
|
232
|
+
|
|
233
|
+
# No config file, return as-is
|
|
234
|
+
[[ ! -f "$config" ]] && echo "$input" && return
|
|
235
|
+
|
|
236
|
+
local result="$input"
|
|
237
|
+
|
|
238
|
+
# Expand {config.urls.backend}
|
|
239
|
+
if [[ "$result" == *"{config.urls.backend}"* ]]; then
|
|
240
|
+
local backend_url
|
|
241
|
+
backend_url=$(jq -r '.urls.backend // .api.baseUrl // empty' "$config" 2>/dev/null)
|
|
242
|
+
if [[ -n "$backend_url" ]]; then
|
|
243
|
+
result="${result//\{config.urls.backend\}/$backend_url}"
|
|
244
|
+
fi
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
# Expand {config.urls.frontend}
|
|
248
|
+
if [[ "$result" == *"{config.urls.frontend}"* ]]; then
|
|
249
|
+
local frontend_url
|
|
250
|
+
frontend_url=$(jq -r '.urls.frontend // .testUrlBase // empty' "$config" 2>/dev/null)
|
|
251
|
+
if [[ -n "$frontend_url" ]]; then
|
|
252
|
+
result="${result//\{config.urls.frontend\}/$frontend_url}"
|
|
253
|
+
fi
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
# Expand {config.directories.backend}
|
|
257
|
+
if [[ "$result" == *"{config.directories.backend}"* ]]; then
|
|
258
|
+
local backend_dir
|
|
259
|
+
backend_dir=$(jq -r '.directories.backend // empty' "$config" 2>/dev/null)
|
|
260
|
+
if [[ -n "$backend_dir" ]]; then
|
|
261
|
+
result="${result//\{config.directories.backend\}/$backend_dir}"
|
|
262
|
+
fi
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
# Expand {config.directories.frontend}
|
|
266
|
+
if [[ "$result" == *"{config.directories.frontend}"* ]]; then
|
|
267
|
+
local frontend_dir
|
|
268
|
+
frontend_dir=$(jq -r '.directories.frontend // empty' "$config" 2>/dev/null)
|
|
269
|
+
if [[ -n "$frontend_dir" ]]; then
|
|
270
|
+
result="${result//\{config.directories.frontend\}/$frontend_dir}"
|
|
271
|
+
fi
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
echo "$result"
|
|
275
|
+
}
|
|
276
|
+
|
|
226
277
|
# Verify PRD acceptance criteria / test steps
|
|
227
278
|
verify_prd_criteria() {
|
|
228
279
|
local story="$1"
|
|
@@ -247,9 +298,13 @@ verify_prd_criteria() {
|
|
|
247
298
|
while IFS= read -r step; do
|
|
248
299
|
[[ -z "$step" ]] && continue
|
|
249
300
|
|
|
250
|
-
|
|
301
|
+
# Expand config placeholders (e.g., {config.urls.backend})
|
|
302
|
+
local expanded_step
|
|
303
|
+
expanded_step=$(_expand_config_vars "$step")
|
|
304
|
+
|
|
305
|
+
echo -n " $expanded_step... "
|
|
251
306
|
|
|
252
|
-
if safe_exec "$
|
|
307
|
+
if safe_exec "$expanded_step" "$log_file"; then
|
|
253
308
|
print_success "passed"
|
|
254
309
|
else
|
|
255
310
|
print_error "failed"
|
|
@@ -260,7 +315,7 @@ verify_prd_criteria() {
|
|
|
260
315
|
# Save failure details for retry context
|
|
261
316
|
{
|
|
262
317
|
echo "PRD test step $step_index failed for $story:"
|
|
263
|
-
echo " Command: $
|
|
318
|
+
echo " Command: $expanded_step"
|
|
264
319
|
echo " Error output:"
|
|
265
320
|
tail -30 "$log_file" | sed 's/^/ /'
|
|
266
321
|
echo ""
|