agentic-loop 3.14.2 → 3.16.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.
@@ -50,6 +50,11 @@
50
50
  # - Has prerequisites array with DB reset command
51
51
  # - Prevents infinite retries on schema mismatch errors
52
52
  #
53
+ # CUSTOM CHECKS (.ralph/checks/prd/ or ~/.config/ralph/checks/prd/):
54
+ # - User-provided scripts that receive story JSON on stdin
55
+ # - Output issue descriptions to stdout (one per line)
56
+ # - Excluded from auto-fix (reported for manual review)
57
+ #
53
58
  # API configuration validation:
54
59
  # - If api.baseUrl configured, checks health endpoint is reachable
55
60
  # - Warns if default /health returns 404 (misconfigured healthEndpoint)
@@ -75,6 +80,9 @@
75
80
  # .tests.directory - Where tests live (for requireTests check)
76
81
  # .api.baseUrl - API base URL (enables API config validation)
77
82
  # .api.healthEndpoint - Health check path (default: /health, empty to disable)
83
+ # .ralph/checks/prd/check-* - Project-level custom checks (per-story)
84
+ # ~/.config/ralph/checks/prd/ - User-global custom checks (per-story)
85
+ # .checks.custom.<name> - Enable/disable individual custom checks
78
86
  #
79
87
  # ============================================================================
80
88
  # USAGE
@@ -184,6 +192,33 @@ validate_prd() {
184
192
  fi
185
193
  fi
186
194
 
195
+ # Deprecation warnings for old root-level fields
196
+ local deprecated_fields=""
197
+ if jq -e '.techStack' "$prd_file" >/dev/null 2>&1; then
198
+ deprecated_fields+="techStack "
199
+ fi
200
+ if jq -e '.globalConstraints' "$prd_file" >/dev/null 2>&1; then
201
+ deprecated_fields+="globalConstraints "
202
+ fi
203
+ if jq -e '.originalContext' "$prd_file" >/dev/null 2>&1; then
204
+ deprecated_fields+="originalContext "
205
+ fi
206
+ if jq -e '.testing' "$prd_file" >/dev/null 2>&1; then
207
+ deprecated_fields+="testing "
208
+ fi
209
+ if jq -e '.architecture' "$prd_file" >/dev/null 2>&1; then
210
+ deprecated_fields+="architecture "
211
+ fi
212
+ if jq -e '.testUsers' "$prd_file" >/dev/null 2>&1; then
213
+ deprecated_fields+="testUsers "
214
+ fi
215
+ if [[ -n "$deprecated_fields" ]]; then
216
+ echo ""
217
+ print_warning "Found deprecated root-level fields: $deprecated_fields"
218
+ echo " These should be in each story instead. Regenerate with /prd."
219
+ echo ""
220
+ fi
221
+
187
222
  # Validate API smoke test configuration
188
223
  _validate_api_config "$config"
189
224
 
@@ -191,7 +226,8 @@ validate_prd() {
191
226
  fix_hardcoded_paths "$prd_file" "$config"
192
227
 
193
228
  # Validate and fix individual stories
194
- _validate_and_fix_stories "$prd_file" || return 1
229
+ # $2 is optional dry_run flag — when "true", skip auto-fix
230
+ _validate_and_fix_stories "$prd_file" "${2:-}" || return 1
195
231
 
196
232
  return 0
197
233
  }
@@ -274,8 +310,10 @@ _validate_api_config() {
274
310
  }
275
311
 
276
312
  # Validate individual stories and auto-fix with Claude if needed
313
+ # $1: prd_file $2: optional "dry_run" — when "true", report issues but skip auto-fix
277
314
  _validate_and_fix_stories() {
278
315
  local prd_file="$1"
316
+ local dry_run="${2:-false}"
279
317
  local needs_fix=false
280
318
  local issues=""
281
319
  local story_count=0
@@ -285,9 +323,13 @@ _validate_and_fix_stories() {
285
323
  local cnt_frontend_tsc=0 cnt_frontend_url=0 cnt_frontend_context=0 cnt_frontend_mcp=0
286
324
  local cnt_auth_security=0 cnt_list_pagination=0 cnt_prose_steps=0
287
325
  local cnt_migration_prereq=0 cnt_naming_convention=0 cnt_bare_pytest=0
326
+ local cnt_custom=0
288
327
 
289
328
  echo " Checking test coverage..."
290
329
 
330
+ # Truncate custom check log per validation pass (name says "last", keep only current run)
331
+ : > "$RALPH_DIR/last_custom_check.log"
332
+
291
333
  # Only validate incomplete stories (skip stories that already passed)
292
334
  local story_ids
293
335
  story_ids=$(jq -r '.stories[] | select(.passes != true) | .id' "$prd_file" 2>/dev/null)
@@ -429,12 +471,31 @@ _validate_and_fix_stories() {
429
471
  fi
430
472
  fi
431
473
 
474
+ # Snapshot built-in issues before custom checks append
475
+ local builtin_story_issues="$story_issues"
476
+
477
+ # Check 8: User-defined custom checks (.ralph/checks/prd/ or ~/.config/ralph/checks/prd/)
478
+ if [[ -d ".ralph/checks/prd" ]] || [[ -d "$HOME/.config/ralph/checks/prd" ]]; then
479
+ local story_json
480
+ story_json=$(jq --arg id "$story_id" '.stories[] | select(.id==$id)' "$prd_file")
481
+ local custom_output
482
+ custom_output=$(_run_custom_prd_checks "$story_id" "$prd_file" "$story_json")
483
+ if [[ -n "$custom_output" ]]; then
484
+ story_issues+="$custom_output"
485
+ cnt_custom=$((cnt_custom + 1))
486
+ fi
487
+ fi
488
+
432
489
  # Track this story if it has issues
433
490
  if [[ -n "$story_issues" ]]; then
434
491
  needs_fix=true
435
492
  story_count=$((story_count + 1))
436
- issues+="$story_id: ${story_issues%%, }
493
+ # Only include built-in issues in auto-fix context
494
+ # Custom issues are user-defined rules that Claude auto-fix can't meaningfully address
495
+ if [[ -n "$builtin_story_issues" ]]; then
496
+ issues+="$story_id: ${builtin_story_issues%%, }
437
497
  "
498
+ fi
438
499
  fi
439
500
  done <<< "$story_ids"
440
501
 
@@ -456,6 +517,12 @@ _validate_and_fix_stories() {
456
517
  [[ $cnt_migration_prereq -gt 0 ]] && echo " ${cnt_migration_prereq}x migration: add prerequisites (DB reset)"
457
518
  [[ $cnt_naming_convention -gt 0 ]] && echo " ${cnt_naming_convention}x API consumer: add camelCase transformation note"
458
519
  [[ $cnt_bare_pytest -gt 0 ]] && echo " ${cnt_bare_pytest}x use 'uv run pytest' not bare 'pytest'"
520
+ [[ $cnt_custom -gt 0 ]] && echo " ${cnt_custom} stories with custom check issues"
521
+
522
+ # Skip auto-fix in dry-run mode
523
+ if [[ "$dry_run" == "true" ]]; then
524
+ return 0
525
+ fi
459
526
 
460
527
  # Check if Claude is available for auto-fix
461
528
  if command -v claude &>/dev/null; then
@@ -471,6 +538,55 @@ _validate_and_fix_stories() {
471
538
  return 0
472
539
  }
473
540
 
541
+ # Run user-defined custom PRD checks for a story
542
+ # Stdin to script: full story JSON | $1: story_id | $2: prd_file
543
+ # Output: issues on stdout (empty = no issues)
544
+ _run_custom_prd_checks() {
545
+ local story_id="$1"
546
+ local prd_file="$2"
547
+ local story_json="$3"
548
+ local custom_issues=""
549
+ local custom_log="$RALPH_DIR/last_custom_check.log"
550
+
551
+ local check_dirs=()
552
+ [[ -d ".ralph/checks/prd" ]] && check_dirs+=(".ralph/checks/prd")
553
+ [[ -d "$HOME/.config/ralph/checks/prd" ]] && check_dirs+=("$HOME/.config/ralph/checks/prd")
554
+ [[ ${#check_dirs[@]} -eq 0 ]] && return 0
555
+
556
+ for check_dir in "${check_dirs[@]}"; do
557
+ for check_script in "$check_dir"/check-*; do
558
+ [[ ! -f "$check_script" || ! -x "$check_script" ]] && continue
559
+
560
+ local check_key
561
+ check_key=$(basename "$check_script")
562
+ check_key="${check_key%.*}"
563
+ # Read directly instead of get_config — jq's // operator treats false as falsy
564
+ local enabled="true"
565
+ if [[ -f "$RALPH_DIR/config.json" ]]; then
566
+ local raw
567
+ raw=$(jq -r --arg key "$check_key" '.checks.custom[$key]' "$RALPH_DIR/config.json" 2>/dev/null)
568
+ [[ -n "$raw" && "$raw" != "null" ]] && enabled="$raw"
569
+ fi
570
+ [[ "$enabled" == "false" ]] && continue
571
+
572
+ # Run check — capture stdout for issues, stderr to log for debugging
573
+ local output=""
574
+ if ! output=$(echo "$story_json" | run_with_timeout 30 "$check_script" "$story_id" "$prd_file" 2>>"$custom_log"); then
575
+ # Script failed to execute — warn, don't silently swallow
576
+ print_warning "Custom check '$check_key' failed for story $story_id (see .ralph/last_custom_check.log)"
577
+ fi
578
+
579
+ if [[ -n "$output" ]]; then
580
+ while IFS= read -r line; do
581
+ [[ -n "$line" ]] && custom_issues+="${line}, "
582
+ done <<< "$output"
583
+ fi
584
+ done
585
+ done
586
+
587
+ echo "$custom_issues"
588
+ }
589
+
474
590
  # Optimize story test coverage using Claude
475
591
  _fix_stories_with_claude() {
476
592
  local prd_file="$1"
@@ -499,7 +615,7 @@ RULES:
499
615
  2. Backend stories MUST have apiContract with endpoint, request, response
500
616
  3. Frontend stories MUST have testUrl set to {config.urls.frontend}/[page-path]
501
617
  - Derive page path from story title (e.g., 'login form' → '/login', 'dashboard' → '/dashboard')
502
- 4. Frontend stories MUST have contextFiles array (include idea file path from originalContext)
618
+ 4. Frontend stories MUST have contextFiles array (include idea file path in each story's contextFiles)
503
619
  5. Frontend stories MUST have mcp array with browser tools: [\"playwright\", \"devtools\"]
504
620
  6. Auth stories MUST have security acceptanceCriteria:
505
621
  - Passwords hashed with bcrypt (cost 10+)
@@ -512,6 +628,8 @@ RULES:
512
628
  Example: \"prerequisites\": [{\"name\": \"Reset test DB\", \"command\": \"npm run db:reset:test\", \"when\": \"schema changes\"}]
513
629
  9. Frontend/general stories that consume APIs MUST have notes about naming conventions:
514
630
  Example: \"notes\": \"Transform API responses from snake_case to camelCase. Create typed interfaces with camelCase properties and map: const user = { userName: data.user_name }\"
631
+ 10. Each story should include its own techStack and constraints fields. Do NOT add these at the PRD root level.
632
+ Move any root-level techStack, globalConstraints, originalContext, testing, architecture, or testUsers into the relevant stories.
515
633
 
516
634
  CURRENT PRD:
517
635
  $(cat "$prd_file")
@@ -674,3 +792,53 @@ validate_stories_quick() {
674
792
 
675
793
  echo "$issues"
676
794
  }
795
+
796
+ # CLI entry point for on-demand PRD validation
797
+ # Usage: ralph prd-check [--dry-run] [prd-file]
798
+ ralph_prd_check() {
799
+ local dry_run=false
800
+ local prd_file=""
801
+
802
+ while [[ $# -gt 0 ]]; do
803
+ case "$1" in
804
+ --dry-run) dry_run=true; shift ;;
805
+ *) prd_file="$1"; shift ;;
806
+ esac
807
+ done
808
+
809
+ prd_file="${prd_file:-$RALPH_DIR/prd.json}"
810
+
811
+ if [[ ! -f "$prd_file" ]]; then
812
+ print_error "PRD not found: $prd_file"
813
+ echo "Generate one with: ralph prd or /prd"
814
+ return 1
815
+ fi
816
+
817
+ echo ""
818
+ print_info "=== PRD Validation ==="
819
+ echo ""
820
+
821
+ if [[ "$dry_run" == "true" ]]; then
822
+ # Dry run: validate structure + show issues without auto-fix
823
+ validate_prd "$prd_file" "true"
824
+ local rc=$?
825
+ local remaining
826
+ remaining=$(validate_stories_quick "$prd_file")
827
+ if [[ -n "$remaining" ]]; then
828
+ echo ""
829
+ echo " Remaining issues:"
830
+ echo "$remaining" | sed 's/^/ /'
831
+ fi
832
+ return $rc
833
+ else
834
+ if validate_prd "$prd_file"; then
835
+ echo ""
836
+ print_success "PRD validation passed!"
837
+ return 0
838
+ else
839
+ echo ""
840
+ print_error "PRD validation failed"
841
+ return 1
842
+ fi
843
+ fi
844
+ }
package/ralph/prd.sh CHANGED
@@ -96,43 +96,18 @@ ralph_prd() {
96
96
  printf '%s\n' ' "branch": "feature/feature-name",' >> "$prompt_file"
97
97
  printf '%s\n' ' "status": "pending"' >> "$prompt_file"
98
98
  printf '%s\n' ' },' >> "$prompt_file"
99
- printf '%s\n' ' "originalContext": "PASTE THE FULL ORIGINAL NOTES/DESCRIPTION HERE - this preserves intent for Claude during implementation",' >> "$prompt_file"
100
99
  printf '%s\n' ' "metadata": {' >> "$prompt_file"
101
- printf '%s\n' ' "security": ["list of security requirements"],' >> "$prompt_file"
102
- printf '%s\n' ' "scaling": ["list of scaling concerns"],' >> "$prompt_file"
103
- printf '%s\n' ' "relatedFeatures": ["list of related features"],' >> "$prompt_file"
100
+ printf '%s\n' ' "createdAt": "ISO timestamp",' >> "$prompt_file"
104
101
  printf '%s\n' ' "estimatedStories": 5,' >> "$prompt_file"
105
102
  printf '%s\n' ' "complexity": "low|medium|high"' >> "$prompt_file"
106
103
  printf '%s\n' ' },' >> "$prompt_file"
107
- printf '%s\n' ' "scalability": {' >> "$prompt_file"
108
- printf '%s\n' ' "expectedScale": "100s | 1000s | 10000s+ users",' >> "$prompt_file"
109
- printf '%s\n' ' "pagination": "cursor | offset | none",' >> "$prompt_file"
110
- printf '%s\n' ' "caching": "none | in-memory | redis",' >> "$prompt_file"
111
- printf '%s\n' ' "rateLimiting": "if needed, requests per minute"' >> "$prompt_file"
112
- printf '%s\n' ' },' >> "$prompt_file"
113
- printf '%s\n' ' "architecture": {' >> "$prompt_file"
114
- printf '%s\n' ' "directories": {' >> "$prompt_file"
115
- printf '%s\n' ' "components": "src/components/{feature}/",' >> "$prompt_file"
116
- printf '%s\n' ' "api": "src/api/",' >> "$prompt_file"
117
- printf '%s\n' ' "types": "src/types/",' >> "$prompt_file"
118
- printf '%s\n' ' "scripts": "scripts/",' >> "$prompt_file"
119
- printf '%s\n' ' "docs": "docs/"' >> "$prompt_file"
120
- printf '%s\n' ' },' >> "$prompt_file"
121
- printf '%s\n' ' "patterns": {' >> "$prompt_file"
122
- printf '%s\n' ' "reuse": ["existing components/utils to use"],' >> "$prompt_file"
123
- printf '%s\n' ' "follow": ["existing patterns to match"]' >> "$prompt_file"
124
- printf '%s\n' ' },' >> "$prompt_file"
125
- printf '%s\n' ' "principles": {' >> "$prompt_file"
126
- printf '%s\n' ' "maxFileLines": 300,' >> "$prompt_file"
127
- printf '%s\n' ' "singleResponsibility": true' >> "$prompt_file"
128
- printf '%s\n' ' },' >> "$prompt_file"
129
- printf '%s\n' ' "doNotCreate": ["things that already exist"]' >> "$prompt_file"
130
- printf '%s\n' ' },' >> "$prompt_file"
131
104
  printf '%s\n' ' "stories": [' >> "$prompt_file"
132
105
  printf '%s\n' ' {' >> "$prompt_file"
133
106
  printf '%s\n' ' "id": "TASK-001",' >> "$prompt_file"
134
107
  printf '%s\n' ' "title": "Story title",' >> "$prompt_file"
135
108
  printf '%s\n' ' "passes": false,' >> "$prompt_file"
109
+ printf '%s\n' ' "techStack": {"backend": "detected tech", "frontend": "detected tech"},' >> "$prompt_file"
110
+ printf '%s\n' ' "constraints": ["relevant rules for this story"],' >> "$prompt_file"
136
111
  printf '%s\n' " \"testUrl\": \"${test_url_base}/path/to/test\"," >> "$prompt_file"
137
112
  printf '%s\n' ' "files": {' >> "$prompt_file"
138
113
  printf '%s\n' ' "create": ["paths to new files"],' >> "$prompt_file"
@@ -142,6 +117,7 @@ ralph_prd() {
142
117
  printf '%s\n' ' "acceptanceCriteria": ["AC 1", "AC 2"],' >> "$prompt_file"
143
118
  printf '%s\n' ' "errorHandling": ["what happens on failure"],' >> "$prompt_file"
144
119
  printf '%s\n' ' "testSteps": ["executable shell commands only"],' >> "$prompt_file"
120
+ printf '%s\n' ' "contextFiles": ["docs/ideas/feature.md"],' >> "$prompt_file"
145
121
  printf '%s\n' ' "notes": ""' >> "$prompt_file"
146
122
  printf '%s\n' ' }' >> "$prompt_file"
147
123
  printf '%s\n' ' ]' >> "$prompt_file"
@@ -186,10 +162,11 @@ ralph_prd() {
186
162
  printf '%s\n' '- If complexity is "high" AND > 5 stories, MUST split' >> "$prompt_file"
187
163
  printf '%s\n' "- Each story should be completable in one Claude session (~10 min)" >> "$prompt_file"
188
164
  printf '\n%s\n' "If splitting is needed, generate ONLY phase 1 and note deferred items." >> "$prompt_file"
189
- printf '\n%s\n' "## Original Context - IMPORTANT" >> "$prompt_file"
190
- printf '\n%s\n' "Copy the FULL original feature notes into the 'originalContext' field verbatim." >> "$prompt_file"
191
- printf '%s\n' "This preserves the user's original intent for Claude during implementation." >> "$prompt_file"
192
- printf '%s\n' "Do NOT summarize - include the complete text." >> "$prompt_file"
165
+ printf '\n%s\n' "## Context in Stories - IMPORTANT" >> "$prompt_file"
166
+ printf '\n%s\n' "Each story must be self-contained. Include the idea file path in each story's 'contextFiles' array." >> "$prompt_file"
167
+ printf '%s\n' "Include 'techStack' with the relevant subset of detected technologies for that story." >> "$prompt_file"
168
+ printf '%s\n' "Include 'constraints' with any rules that apply to that specific story." >> "$prompt_file"
169
+ printf '%s\n' "Do NOT put techStack, constraints, or context at the PRD root level." >> "$prompt_file"
193
170
  printf '\n%s\n' "## Output" >> "$prompt_file"
194
171
  printf '\n%s\n' "After questions are answered, output the prd.json content between these exact markers:" >> "$prompt_file"
195
172
  printf '%s\n' "--- BEGIN PRD.JSON ---" >> "$prompt_file"
package/ralph/setup.sh CHANGED
@@ -94,6 +94,7 @@ ralph_setup() {
94
94
 
95
95
  # Run all setup steps
96
96
  setup_ralph_dir "$pkg_root"
97
+ setup_custom_checks
97
98
  setup_gitignore
98
99
  setup_claude_hooks "$pkg_root"
99
100
  setup_slash_commands "$pkg_root"
@@ -247,6 +248,24 @@ setup_ralph_dir() {
247
248
  fi
248
249
  }
249
250
 
251
+ # Set up custom PRD checks directory
252
+ setup_custom_checks() {
253
+ local checks_dir=".ralph/checks/prd"
254
+
255
+ # Skip if directory already exists
256
+ if [[ -d "$checks_dir" ]]; then
257
+ local count
258
+ count=$(ls -1 "$checks_dir"/check-* 2>/dev/null | wc -l | tr -d ' ')
259
+ if [[ "$count" -gt 0 ]]; then
260
+ echo " Custom PRD checks: $count script(s) in $checks_dir/"
261
+ fi
262
+ return 0
263
+ fi
264
+
265
+ mkdir -p "$checks_dir"
266
+ echo " Created $checks_dir/ (add custom check scripts here)"
267
+ }
268
+
250
269
  # Ensure .gitignore has necessary patterns
251
270
  setup_gitignore() {
252
271
  local patterns=(
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env bash
2
+ # shellcheck shell=bash
3
+ #
4
+ # Example custom PRD check for Ralph
5
+ #
6
+ # Place in: .ralph/checks/prd/ (project) or ~/.config/ralph/checks/prd/ (global)
7
+ # Must be executable: chmod +x check-example.sh
8
+ #
9
+ # Interface:
10
+ # stdin = story JSON object
11
+ # $1 = story ID
12
+ # $2 = PRD file path
13
+ # stdout = issue descriptions, one per line (empty = pass)
14
+ #
15
+ # Disable: {"checks": {"custom": {"check-example": false}}}
16
+
17
+ # story_id="$1" # available if you need the story ID
18
+ # prd_file="$2" # available if you need full PRD context
19
+ story_json=$(cat)
20
+
21
+ # Example: require all stories to have a description field
22
+ has_description=$(echo "$story_json" | jq -r '.description // empty')
23
+ if [[ -z "$has_description" ]]; then
24
+ echo "missing description field"
25
+ fi
@@ -6,34 +6,9 @@
6
6
  "status": "pending"
7
7
  },
8
8
 
9
- "originalContext": "docs/ideas/auth.md",
10
-
11
- "techStack": {
12
- "frontend": "React",
13
- "backend": "Node.js",
14
- "database": "PostgreSQL"
15
- },
16
-
17
- "testing": {
18
- "approach": "TDD",
19
- "unit": {
20
- "frontend": "vitest",
21
- "backend": "jest"
22
- },
23
- "integration": "playwright",
24
- "e2e": "playwright"
25
- },
26
-
27
- "globalConstraints": [
28
- "All API calls must have error handling",
29
- "Use existing UI components from src/components/ui",
30
- "Never store passwords in plain text",
31
- "Sanitize all user input before database operations"
32
- ],
33
-
34
9
  "metadata": {
35
10
  "createdAt": "2026-01-27T10:00:00Z",
36
- "estimatedStories": 2,
11
+ "estimatedStories": 3,
37
12
  "complexity": "medium"
38
13
  },
39
14
 
@@ -45,6 +20,17 @@
45
20
  "priority": 1,
46
21
  "passes": false,
47
22
 
23
+ "techStack": {
24
+ "backend": "Node.js",
25
+ "database": "PostgreSQL"
26
+ },
27
+
28
+ "constraints": [
29
+ "All API calls must have error handling",
30
+ "Never store passwords in plain text",
31
+ "Sanitize all user input before database operations"
32
+ ],
33
+
48
34
  "files": {
49
35
  "create": ["src/api/users.ts", "src/api/users.test.ts"],
50
36
  "modify": ["src/api/index.ts"],
@@ -69,6 +55,7 @@
69
55
  "testing": {
70
56
  "types": ["unit", "integration"],
71
57
  "approach": "TDD",
58
+ "runner": "jest",
72
59
  "files": {
73
60
  "unit": ["src/api/users.test.ts"]
74
61
  }
@@ -86,6 +73,15 @@
86
73
  "response": {"id": "string", "email": "string"}
87
74
  },
88
75
 
76
+ "testUsers": {
77
+ "admin": {"email": "admin@test.com", "password": "test123"},
78
+ "user": {"email": "user@test.com", "password": "test123"}
79
+ },
80
+
81
+ "contextFiles": [
82
+ "docs/ideas/auth.md"
83
+ ],
84
+
89
85
  "notes": "SECURITY: Use bcrypt with cost 10+. Never log passwords. Validate email format server-side even if validated client-side.",
90
86
  "dependsOn": []
91
87
  },
@@ -96,6 +92,14 @@
96
92
  "priority": 2,
97
93
  "passes": false,
98
94
 
95
+ "techStack": {
96
+ "frontend": "React"
97
+ },
98
+
99
+ "constraints": [
100
+ "Use existing UI components from src/components/ui"
101
+ ],
102
+
99
103
  "files": {
100
104
  "create": ["src/components/RegisterForm.tsx", "src/components/RegisterForm.test.tsx"],
101
105
  "modify": ["src/pages/index.tsx"],
@@ -122,6 +126,7 @@
122
126
  "testing": {
123
127
  "types": ["unit", "e2e"],
124
128
  "approach": "TDD",
129
+ "runner": "vitest",
125
130
  "files": {
126
131
  "unit": ["src/components/RegisterForm.test.tsx"],
127
132
  "e2e": ["tests/e2e/register.spec.ts"]
@@ -152,6 +157,15 @@
152
157
  "priority": 3,
153
158
  "passes": false,
154
159
 
160
+ "techStack": {
161
+ "backend": "Node.js",
162
+ "database": "PostgreSQL"
163
+ },
164
+
165
+ "constraints": [
166
+ "All API calls must have error handling"
167
+ ],
168
+
155
169
  "files": {
156
170
  "create": [],
157
171
  "modify": ["src/api/users.ts"],
@@ -176,6 +190,7 @@
176
190
  "testing": {
177
191
  "types": ["integration"],
178
192
  "approach": "TDD",
193
+ "runner": "jest",
179
194
  "files": {}
180
195
  },
181
196
 
@@ -190,6 +205,10 @@
190
205
  "response": {"data": "User[]", "total": "number", "page": "number", "limit": "number"}
191
206
  },
192
207
 
208
+ "contextFiles": [
209
+ "docs/ideas/auth.md"
210
+ ],
211
+
193
212
  "notes": "SCALE: Always paginate list endpoints. Enforce max limit to prevent memory issues. Add database index for sort column.",
194
213
  "dependsOn": ["TASK-001"]
195
214
  }