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/ralph/utils.sh CHANGED
@@ -429,367 +429,82 @@ send_notification() {
429
429
  return 0
430
430
  }
431
431
 
432
- # Validate PRD structure
433
- # Returns 0 if valid, 1 if invalid with helpful error messages
434
- validate_prd() {
432
+ # Replace hardcoded paths/URLs with config placeholders
433
+ # Makes PRDs portable across machines and environments
434
+ fix_hardcoded_paths() {
435
435
  local prd_file="$1"
436
-
437
- # Check file exists
438
- if [[ ! -f "$prd_file" ]]; then
439
- print_error "PRD file not found: $prd_file"
440
- return 1
441
- fi
442
-
443
- # Check valid JSON
444
- if ! jq -e . "$prd_file" >/dev/null 2>&1; then
445
- print_error "prd.json is not valid JSON."
446
- echo ""
447
- echo "Fix it manually or regenerate with:"
448
- echo " /idea 'your feature'"
449
- echo ""
450
- return 1
451
- fi
452
-
453
- # Check feature.name is set
454
- local feature_name
455
- feature_name=$(jq -r '.feature.name // empty' "$prd_file" 2>/dev/null)
456
- if [[ -z "$feature_name" || "$feature_name" == "null" ]]; then
457
- print_error "prd.json is missing .feature.name"
458
- echo ""
459
- echo "Add a feature name to your PRD or regenerate with:"
460
- echo " /idea 'your feature'"
461
- echo ""
462
- return 1
463
- fi
464
-
465
- # Check for stories array
466
- if ! jq -e '.stories' "$prd_file" >/dev/null 2>&1; then
467
- print_error "prd.json is missing 'stories' array."
468
- echo ""
469
- echo "Regenerate with: /idea 'your feature'"
470
- echo ""
471
- return 1
472
- fi
473
-
474
- # Check stories is not empty
475
- local story_count
476
- story_count=$(jq '.stories | length' "$prd_file" 2>/dev/null || echo "0")
477
- if [[ "$story_count" == "0" ]]; then
478
- print_error "prd.json has no stories."
479
- echo ""
480
- echo "Regenerate with: /idea 'your feature'"
481
- echo ""
482
- return 1
483
- fi
484
-
485
- # Check each story has required fields
486
- local invalid_stories
487
- invalid_stories=$(jq -r '.stories[] | select(.id == null or .id == "" or .title == null or .title == "") | .id // "unnamed"' "$prd_file" 2>/dev/null)
488
- if [[ -n "$invalid_stories" ]]; then
489
- print_error "Some stories are missing required fields (id, title):"
490
- echo "$invalid_stories" | head -5
491
- echo ""
492
- echo "Fix the PRD or regenerate with: /idea 'your feature'"
493
- echo ""
494
- return 1
495
- fi
496
-
497
- # Check stories have passes field (initialize if missing)
498
- local missing_passes
499
- missing_passes=$(jq '[.stories[] | select(.passes == null)] | length' "$prd_file" 2>/dev/null || echo "0")
500
- if [[ "$missing_passes" != "0" ]]; then
501
- print_info "Initializing $missing_passes stories with passes=false..."
502
- update_json "$prd_file" '(.stories[] | select(.passes == null) | .passes) = false'
503
- fi
504
-
505
- # Check feature name exists
506
- local feature_name
507
- feature_name=$(jq -r '.feature.name // empty' "$prd_file" 2>/dev/null)
508
- if [[ -z "$feature_name" ]]; then
509
- print_warning "PRD is missing feature name (will show as 'unnamed')"
510
- fi
511
-
512
- # Check if project has tests (from config)
513
- local config="$RALPH_DIR/config.json"
514
- if [[ -f "$config" ]]; then
515
- local require_tests
516
- require_tests=$(jq -r '.checks.requireTests // true' "$config" 2>/dev/null)
517
- local test_dir
518
- test_dir=$(jq -r '.tests.directory // empty' "$config" 2>/dev/null)
519
-
520
- if [[ "$require_tests" == "true" && -z "$test_dir" ]]; then
521
- echo ""
522
- print_warning "No test directory configured in .ralph/config.json"
523
- echo " Without tests, Ralph can only verify syntax and API responses."
524
- echo " Import errors and integration issues won't be caught."
525
- echo ""
526
- echo " To fix: Add tests, or set in .ralph/config.json:"
527
- echo " {\"tests\": {\"directory\": \"src\", \"patterns\": \"*.test.ts\"}}"
528
- echo " To silence: {\"checks\": {\"requireTests\": false}}"
529
- echo ""
436
+ local config_file="$2"
437
+ local modified=false
438
+
439
+ # Read current PRD
440
+ local prd_content
441
+ prd_content=$(cat "$prd_file")
442
+
443
+ # Get URLs from config (if available)
444
+ local backend_url="" frontend_url=""
445
+ if [[ -f "$config_file" ]]; then
446
+ backend_url=$(jq -r '.urls.backend // .api.baseUrl // empty' "$config_file" 2>/dev/null)
447
+ frontend_url=$(jq -r '.urls.frontend // .playwright.baseUrl // empty' "$config_file" 2>/dev/null)
448
+ fi
449
+
450
+ # Check for hardcoded absolute paths (non-portable)
451
+ if echo "$prd_content" | grep -qE '"/Users/|"/home/|"C:\\|"/var/|"/opt/' ; then
452
+ echo " Removing hardcoded absolute paths..."
453
+ # Remove common absolute path prefixes, keep relative path
454
+ prd_content=$(echo "$prd_content" | sed -E 's|"/Users/[^"]*/([^"]+)"|"\1"|g')
455
+ prd_content=$(echo "$prd_content" | sed -E 's|"/home/[^"]*/([^"]+)"|"\1"|g')
456
+ modified=true
457
+ fi
458
+
459
+ # Replace hardcoded backend URLs with {config.urls.backend}
460
+ if [[ -n "$backend_url" ]] && echo "$prd_content" | grep -qF "\"$backend_url" ; then
461
+ echo " Replacing hardcoded backend URL with {config.urls.backend}..."
462
+ prd_content=$(echo "$prd_content" | sed "s|$backend_url|{config.urls.backend}|g")
463
+ modified=true
464
+ fi
465
+
466
+ # Replace hardcoded frontend URLs with {config.urls.frontend}
467
+ if [[ -n "$frontend_url" ]] && echo "$prd_content" | grep -qF "\"$frontend_url" ; then
468
+ echo " Replacing hardcoded frontend URL with {config.urls.frontend}..."
469
+ prd_content=$(echo "$prd_content" | sed "s|$frontend_url|{config.urls.frontend}|g")
470
+ modified=true
471
+ fi
472
+
473
+ # Replace hardcoded health endpoints with config placeholder
474
+ if echo "$prd_content" | grep -qE '/api(/v[0-9]+)?/health|/health' ; then
475
+ echo " Replacing hardcoded health endpoints with {config.api.healthEndpoint}..."
476
+ prd_content=$(echo "$prd_content" | sed -E 's|/api/v[0-9]+/health|{config.api.healthEndpoint}|g')
477
+ prd_content=$(echo "$prd_content" | sed -E 's|/api/health|{config.api.healthEndpoint}|g')
478
+ prd_content=$(echo "$prd_content" | sed -E 's|"/health"|"{config.api.healthEndpoint}"|g')
479
+ modified=true
480
+ fi
481
+
482
+ # Replace common localhost patterns if no config URLs set
483
+ if [[ -z "$backend_url" ]]; then
484
+ # Common backend ports: 8000, 8001, 8080, 3001, 4000, 5000
485
+ if echo "$prd_content" | grep -qE 'http://localhost:(8000|8001|8080|3001|4000|5000)' ; then
486
+ echo " Replacing hardcoded localhost backend URLs with {config.urls.backend}..."
487
+ prd_content=$(echo "$prd_content" | sed -E 's|http://localhost:(8000|8001|8080|3001|4000|5000)|{config.urls.backend}|g')
488
+ modified=true
530
489
  fi
531
490
  fi
532
491
 
533
- # Validate and fix individual stories
534
- validate_and_fix_stories "$prd_file" || return 1
535
-
536
- return 0
537
- }
538
-
539
- # Validate individual stories and auto-fix with Claude if needed
540
- # Checks: testSteps quality, apiContract, testUrl, contextFiles, security, scale
541
- validate_and_fix_stories() {
542
- local prd_file="$1"
543
- local needs_fix=false
544
- local issues=""
545
-
546
- echo " Validating story quality..."
547
-
548
- # Get all story IDs
549
- local story_ids
550
- story_ids=$(jq -r '.stories[].id' "$prd_file" 2>/dev/null)
551
-
552
- while IFS= read -r story_id; do
553
- [[ -z "$story_id" ]] && continue
554
-
555
- local story_issues=""
556
- local story_type
557
- story_type=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .type // "unknown"' "$prd_file")
558
- local story_title
559
- story_title=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .title // ""' "$prd_file")
560
-
561
- # Check 1: testSteps quality
562
- local test_steps
563
- test_steps=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .testSteps // [] | join(" ")' "$prd_file")
564
-
565
- if [[ -z "$test_steps" ]]; then
566
- story_issues+="no testSteps, "
567
- elif [[ "$story_type" == "backend" ]]; then
568
- # Backend must have curl, not just npm test/pytest
569
- if ! echo "$test_steps" | grep -q "curl "; then
570
- story_issues+="backend needs curl tests (npm test alone uses mocks), "
571
- fi
572
- elif [[ "$story_type" == "frontend" ]]; then
573
- # Frontend must have tsc or playwright
574
- if ! echo "$test_steps" | grep -qE "(tsc --noEmit|playwright)"; then
575
- story_issues+="frontend needs tsc --noEmit or playwright tests, "
576
- fi
577
- fi
578
-
579
- # Check 2: Backend needs apiContract
580
- if [[ "$story_type" == "backend" ]]; then
581
- local has_contract
582
- has_contract=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .apiContract // empty' "$prd_file")
583
- if [[ -z "$has_contract" || "$has_contract" == "null" ]]; then
584
- story_issues+="backend missing apiContract, "
585
- fi
492
+ if [[ -z "$frontend_url" ]]; then
493
+ # Common frontend ports: 3000, 5173, 4200, 8080
494
+ if echo "$prd_content" | grep -qE 'http://localhost:(3000|5173|4200)' ; then
495
+ echo " Replacing hardcoded localhost frontend URLs with {config.urls.frontend}..."
496
+ prd_content=$(echo "$prd_content" | sed -E 's|http://localhost:(3000|5173|4200)|{config.urls.frontend}|g')
497
+ modified=true
586
498
  fi
587
-
588
- # Check 3: Frontend needs testUrl and contextFiles
589
- if [[ "$story_type" == "frontend" ]]; then
590
- local has_url
591
- has_url=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .testUrl // empty' "$prd_file")
592
- if [[ -z "$has_url" || "$has_url" == "null" ]]; then
593
- story_issues+="frontend missing testUrl, "
594
- fi
595
-
596
- local context_files
597
- context_files=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .contextFiles // [] | length' "$prd_file")
598
- if [[ "$context_files" == "0" ]]; then
599
- story_issues+="frontend missing contextFiles (idea file + styleguide), "
600
- fi
601
- fi
602
-
603
- # Check 4: Auth stories need security criteria
604
- if echo "$story_title" | grep -qiE "(login|auth|password|register|signup|sign.?up)"; then
605
- local criteria
606
- criteria=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .acceptanceCriteria // [] | join(" ")' "$prd_file")
607
- if ! echo "$criteria" | grep -qiE "(hash|bcrypt|sanitiz|inject|rate.?limit)"; then
608
- story_issues+="auth story missing security criteria (password hashing/rate limiting), "
609
- fi
610
- fi
611
-
612
- # Check 5: List endpoints need scale criteria
613
- # Note: "search" excluded - search endpoints often return single/filtered results
614
- if echo "$story_title" | grep -qiE "(list|get all|fetch all|index)"; then
615
- local criteria
616
- criteria=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .acceptanceCriteria // [] | join(" ")' "$prd_file")
617
- if ! echo "$criteria" | grep -qiE "(pagina|limit|page=|per.?page)"; then
618
- story_issues+="list endpoint missing pagination criteria, "
619
- fi
620
- fi
621
-
622
- # Report issues for this story
623
- if [[ -n "$story_issues" ]]; then
624
- needs_fix=true
625
- issues+="$story_id: ${story_issues%%, }
626
- "
627
- fi
628
- done <<< "$story_ids"
629
-
630
- # If issues found, attempt to fix with Claude
631
- if [[ "$needs_fix" == "true" ]]; then
632
- print_warning "Story quality issues found:"
633
- echo "$issues" | while IFS= read -r line; do
634
- [[ -n "$line" ]] && echo " $line"
635
- done
636
- echo ""
637
-
638
- # Check if Claude is available for auto-fix
639
- if command -v claude &>/dev/null; then
640
- echo " Attempting auto-fix with Claude..."
641
- fix_stories_with_claude "$prd_file" "$issues"
642
- else
643
- echo " Claude CLI not found - fix these issues manually or regenerate PRD."
644
- echo ""
645
- return 1
646
- fi
647
- else
648
- print_success "All stories validated"
649
499
  fi
650
500
 
651
- return 0
652
- }
653
-
654
- # Fix story issues using Claude
655
- fix_stories_with_claude() {
656
- local prd_file="$1"
657
- local issues="$2"
658
-
659
- local fix_prompt="Fix the following issues in this PRD. Output the COMPLETE fixed prd.json.
660
-
661
- ISSUES FOUND:
662
- $issues
663
-
664
- RULES FOR FIXING:
665
- 1. Backend stories MUST have testSteps with curl commands that hit real endpoints
666
- Example: curl -s -X POST {config.urls.backend}/api/users -d '...' | jq -e '.id'
667
- 2. Backend stories MUST have apiContract with endpoint, request, response
668
- 3. Frontend stories MUST have testUrl set to {config.urls.frontend}/page
669
- 4. Frontend stories MUST have contextFiles array (include idea file path from originalContext)
670
- 5. Auth stories MUST have security acceptanceCriteria:
671
- - Passwords hashed with bcrypt (cost 10+)
672
- - Passwords NEVER in API responses
673
- - Rate limiting on login attempts
674
- 6. List endpoints MUST have pagination acceptanceCriteria:
675
- - Returns paginated results (max 100 per page)
676
- - Accepts ?page=N&limit=N query params
677
-
678
- CURRENT PRD:
679
- $(cat "$prd_file")
680
-
681
- Output ONLY the fixed JSON, no explanation. Start with { and end with }."
682
-
683
- local raw_response
684
- raw_response=$(echo "$fix_prompt" | run_with_timeout "$CODE_REVIEW_TIMEOUT_SECONDS" claude -p 2>/dev/null)
685
-
686
- # Extract JSON from response (Claude sometimes adds text before/after)
687
- local fixed_prd
688
- fixed_prd=$(echo "$raw_response" | sed -n '/^[[:space:]]*{/,/^[[:space:]]*}[[:space:]]*$/p' | head -1000)
689
-
690
- # If sed extraction failed, try the raw response
691
- if [[ -z "$fixed_prd" ]]; then
692
- fixed_prd="$raw_response"
693
- fi
694
-
695
- # Validate the response is valid JSON with required structure
696
- if echo "$fixed_prd" | jq -e '.stories' >/dev/null 2>&1; then
697
- # Timestamped backup (preserves history across multiple fixes)
698
- local backup_file="${prd_file}.$(date +%Y%m%d-%H%M%S).bak"
699
- cp "$prd_file" "$backup_file"
700
-
701
- # Write fixed PRD
702
- echo "$fixed_prd" > "$prd_file"
703
- print_success "PRD auto-fixed (backup at $backup_file)"
704
-
705
- # Re-validate to confirm fixes
706
- echo " Re-validating..."
707
- local remaining_issues
708
- remaining_issues=$(validate_stories_quick "$prd_file")
709
- if [[ -n "$remaining_issues" ]]; then
710
- print_warning "Some issues remain - may need manual fixes:"
711
- echo "$remaining_issues" | tr ',' '\n' | while IFS= read -r line; do
712
- [[ -n "$line" ]] && echo " $line"
713
- done
714
- else
715
- print_success "All issues resolved"
716
- fi
717
- else
718
- print_error "Claude returned invalid JSON - fix manually"
719
- echo " Response preview: $(echo "$raw_response" | head -3)"
720
- return 1
501
+ # Write back if modified
502
+ if [[ "$modified" == "true" ]]; then
503
+ echo "$prd_content" > "$prd_file"
504
+ print_success "Paths updated to use config placeholders"
721
505
  fi
722
506
  }
723
507
 
724
- # Quick validation without auto-fix (for re-checking after fix)
725
- # Checks all the same things as validate_and_fix_stories() but returns issues string
726
- validate_stories_quick() {
727
- local prd_file="$1"
728
- local issues=""
729
-
730
- local story_ids
731
- story_ids=$(jq -r '.stories[].id' "$prd_file" 2>/dev/null)
732
-
733
- while IFS= read -r story_id; do
734
- [[ -z "$story_id" ]] && continue
735
-
736
- local story_type
737
- story_type=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .type // "unknown"' "$prd_file")
738
- local story_title
739
- story_title=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .title // ""' "$prd_file")
740
- local test_steps
741
- test_steps=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .testSteps // [] | join(" ")' "$prd_file")
742
-
743
- # Check 1: testSteps quality
744
- if [[ "$story_type" == "backend" ]] && ! echo "$test_steps" | grep -q "curl "; then
745
- issues+="$story_id: missing curl tests, "
746
- fi
747
- if [[ "$story_type" == "frontend" ]] && ! echo "$test_steps" | grep -qE "(tsc --noEmit|playwright)"; then
748
- issues+="$story_id: missing tsc/playwright tests, "
749
- fi
750
-
751
- # Check 2: Backend needs apiContract
752
- if [[ "$story_type" == "backend" ]]; then
753
- local has_contract
754
- has_contract=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .apiContract // empty' "$prd_file")
755
- if [[ -z "$has_contract" || "$has_contract" == "null" ]]; then
756
- issues+="$story_id: missing apiContract, "
757
- fi
758
- fi
759
-
760
- # Check 3: Frontend needs testUrl and contextFiles
761
- if [[ "$story_type" == "frontend" ]]; then
762
- local has_url
763
- has_url=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .testUrl // empty' "$prd_file")
764
- [[ -z "$has_url" || "$has_url" == "null" ]] && issues+="$story_id: missing testUrl, "
765
-
766
- local context_files
767
- context_files=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .contextFiles // [] | length' "$prd_file")
768
- [[ "$context_files" == "0" ]] && issues+="$story_id: missing contextFiles, "
769
- fi
770
-
771
- # Check 4: Auth stories need security criteria
772
- if echo "$story_title" | grep -qiE "(login|auth|password|register|signup|sign.?up)"; then
773
- local criteria
774
- criteria=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .acceptanceCriteria // [] | join(" ")' "$prd_file")
775
- if ! echo "$criteria" | grep -qiE "(hash|bcrypt|sanitiz|inject|rate.?limit)"; then
776
- issues+="$story_id: missing security criteria, "
777
- fi
778
- fi
779
-
780
- # Check 5: List endpoints need scale criteria
781
- if echo "$story_title" | grep -qiE "(list|get all|fetch all|index)"; then
782
- local criteria
783
- criteria=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .acceptanceCriteria // [] | join(" ")' "$prd_file")
784
- if ! echo "$criteria" | grep -qiE "(pagina|limit|page=|per.?page)"; then
785
- issues+="$story_id: missing pagination criteria, "
786
- fi
787
- fi
788
- done <<< "$story_ids"
789
-
790
- echo "$issues"
791
- }
792
-
793
508
  # Detect Python runner (uv, poetry, pipenv, or plain python)
794
509
  detect_python_runner() {
795
510
  local search_dir="${1:-.}"
@@ -50,7 +50,7 @@
50
50
 
51
51
  "api": {
52
52
  "baseUrl": "http://localhost:4000",
53
- "healthEndpoint": "/api/health",
53
+ "healthEndpoint": "/api/v1/health",
54
54
  "smokeEndpoints": [],
55
55
  "timeout": 30
56
56
  },
@@ -51,7 +51,7 @@
51
51
 
52
52
  "api": {
53
53
  "baseUrl": "http://localhost:8000",
54
- "healthEndpoint": "/health",
54
+ "healthEndpoint": "/api/v1/health",
55
55
  "timeout": 30,
56
56
  "transport": "sse"
57
57
  },
@@ -52,7 +52,7 @@
52
52
 
53
53
  "api": {
54
54
  "baseUrl": "http://localhost:8000",
55
- "healthEndpoint": "/api/health",
55
+ "healthEndpoint": "/api/v1/health",
56
56
  "smokeEndpoints": [],
57
57
  "timeout": 30
58
58
  },
@@ -50,7 +50,7 @@
50
50
 
51
51
  "api": {
52
52
  "baseUrl": "http://localhost:8080",
53
- "healthEndpoint": "/health",
53
+ "healthEndpoint": "/api/v1/health",
54
54
  "timeout": 30
55
55
  },
56
56
 
@@ -50,7 +50,7 @@
50
50
 
51
51
  "api": {
52
52
  "baseUrl": "http://localhost:3000",
53
- "healthEndpoint": "/health",
53
+ "healthEndpoint": "/api/v1/health",
54
54
  "timeout": 30
55
55
  },
56
56
 
@@ -50,7 +50,7 @@
50
50
 
51
51
  "api": {
52
52
  "baseUrl": "http://localhost:3000",
53
- "healthEndpoint": "/api/health",
53
+ "healthEndpoint": "/api/v1/health",
54
54
  "smokeEndpoints": [],
55
55
  "timeout": 30
56
56
  },
@@ -50,7 +50,7 @@
50
50
 
51
51
  "api": {
52
52
  "baseUrl": "http://localhost:8000",
53
- "healthEndpoint": "/api/health",
53
+ "healthEndpoint": "/api/v1/health",
54
54
  "smokeEndpoints": [],
55
55
  "timeout": 30
56
56
  },
@@ -50,7 +50,7 @@
50
50
 
51
51
  "api": {
52
52
  "baseUrl": "http://localhost:8080",
53
- "healthEndpoint": "/health",
53
+ "healthEndpoint": "/api/v1/health",
54
54
  "timeout": 30
55
55
  },
56
56
 
package/ralph/verify.sh DELETED
@@ -1,106 +0,0 @@
1
- #!/usr/bin/env bash
2
- # shellcheck shell=bash
3
- # verify.sh - Lightweight verification for ralph
4
- #
5
- # Philosophy: Claude verifies its own work using MCP browser tools.
6
- # Ralph just runs lint, tests, and testSteps from the PRD.
7
-
8
- # Source verification modules
9
- VERIFY_DIR="${RALPH_LIB:-$(dirname "${BASH_SOURCE[0]}")}"
10
- source "$VERIFY_DIR/verify/lint.sh"
11
- source "$VERIFY_DIR/verify/tests.sh"
12
- source "$VERIFY_DIR/verify/api.sh"
13
-
14
- run_verification() {
15
- local story="$1"
16
-
17
- echo ""
18
- print_info "=== Verification: $story ==="
19
- echo ""
20
-
21
- # Get story type for targeted checks
22
- local story_type
23
- story_type=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .type // "general"' "$RALPH_DIR/prd.json" 2>/dev/null)
24
- export RALPH_STORY_TYPE="$story_type"
25
-
26
- local failed=0
27
-
28
- # ========================================
29
- # STEP 1: Run lint checks
30
- # ========================================
31
- echo " [1/5] Running lint checks..."
32
- if ! run_configured_checks "$story_type"; then
33
- failed=1
34
- fi
35
-
36
- # ========================================
37
- # STEP 2: Verify tests exist + run them
38
- # ========================================
39
- if [[ $failed -eq 0 ]]; then
40
- echo ""
41
- echo " [2/5] Running tests..."
42
- # First check that test files exist for new code
43
- if ! verify_test_files_exist; then
44
- failed=1
45
- elif ! run_unit_tests; then
46
- failed=1
47
- fi
48
- fi
49
-
50
- # ========================================
51
- # STEP 3: Run PRD test steps
52
- # ========================================
53
- if [[ $failed -eq 0 ]]; then
54
- echo ""
55
- echo " [3/5] Running PRD test steps..."
56
- if ! verify_prd_criteria "$story"; then
57
- failed=1
58
- fi
59
- fi
60
-
61
- # ========================================
62
- # STEP 4: API smoke test (if configured)
63
- # ========================================
64
- if [[ $failed -eq 0 ]]; then
65
- if ! run_api_smoke_test "$story"; then
66
- failed=1
67
- fi
68
- fi
69
-
70
- # ========================================
71
- # STEP 5: Frontend smoke test (if configured)
72
- # ========================================
73
- if [[ $failed -eq 0 ]]; then
74
- if ! run_frontend_smoke_test "$story"; then
75
- failed=1
76
- fi
77
- fi
78
-
79
- # ========================================
80
- # Final result
81
- # ========================================
82
- echo ""
83
- if [[ $failed -eq 0 ]]; then
84
- print_success "=== All verification passed ==="
85
- return 0
86
- else
87
- print_error "=== Verification failed ==="
88
- save_failure_context "$story"
89
- return 1
90
- fi
91
- }
92
-
93
- # Save failure context for next iteration
94
- save_failure_context() {
95
- local story="$1"
96
- local context_file="$RALPH_DIR/last_failure.txt"
97
-
98
- {
99
- echo "=== Verification failed for $story ==="
100
- echo ""
101
- # Just include the verification output - Claude can figure out what went wrong
102
- if [[ -f "$RALPH_DIR/last_verification.log" ]]; then
103
- tail -100 "$RALPH_DIR/last_verification.log"
104
- fi
105
- } > "$context_file"
106
- }