@atlashub/smartstack-cli 3.33.0 → 3.35.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.
Files changed (65) hide show
  1. package/.documentation/agents.html +5 -1
  2. package/.documentation/apex.html +644 -0
  3. package/.documentation/business-analyse.html +81 -1
  4. package/.documentation/cli-commands.html +5 -1
  5. package/.documentation/commands.html +5 -1
  6. package/.documentation/efcore.html +5 -1
  7. package/.documentation/gitflow.html +5 -1
  8. package/.documentation/hooks.html +5 -1
  9. package/.documentation/index.html +60 -2
  10. package/.documentation/init.html +414 -1
  11. package/.documentation/installation.html +5 -1
  12. package/.documentation/ralph-loop.html +365 -216
  13. package/.documentation/test-web.html +5 -1
  14. package/dist/index.js +32 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/mcp-entry.mjs +7 -24
  17. package/dist/mcp-entry.mjs.map +1 -1
  18. package/package.json +1 -2
  19. package/templates/agents/ba-writer.md +142 -15
  20. package/templates/mcp-scaffolding/controller.cs.hbs +5 -1
  21. package/templates/skills/apex/SKILL.md +9 -3
  22. package/templates/skills/apex/_shared.md +49 -4
  23. package/templates/skills/{ralph-loop → apex}/references/core-seed-data.md +20 -11
  24. package/templates/skills/{ralph-loop → apex}/references/error-classification.md +2 -1
  25. package/templates/skills/apex/references/post-checks.md +463 -3
  26. package/templates/skills/apex/references/smartstack-api.md +76 -8
  27. package/templates/skills/apex/references/smartstack-frontend.md +74 -1
  28. package/templates/skills/apex/references/smartstack-layers.md +21 -3
  29. package/templates/skills/apex/steps/step-00-init.md +121 -1
  30. package/templates/skills/apex/steps/step-01-analyze.md +58 -0
  31. package/templates/skills/apex/steps/step-02-plan.md +36 -0
  32. package/templates/skills/apex/steps/step-03-execute.md +114 -7
  33. package/templates/skills/apex/steps/step-04-examine.md +116 -2
  34. package/templates/skills/business-analyse/SKILL.md +31 -20
  35. package/templates/skills/business-analyse/_module-loop.md +68 -9
  36. package/templates/skills/business-analyse/_shared.md +80 -21
  37. package/templates/skills/business-analyse/questionnaire/00-application.md +4 -2
  38. package/templates/skills/business-analyse/questionnaire/00b-project.md +85 -0
  39. package/templates/skills/business-analyse/references/deploy-modes.md +69 -0
  40. package/templates/skills/business-analyse/references/team-orchestration.md +158 -7
  41. package/templates/skills/business-analyse/schemas/application-schema.json +15 -1
  42. package/templates/skills/business-analyse/schemas/project-schema.json +490 -0
  43. package/templates/skills/business-analyse/schemas/sections/metadata-schema.json +2 -1
  44. package/templates/skills/business-analyse/steps/step-00-init.md +220 -38
  45. package/templates/skills/business-analyse/steps/step-01-cadrage.md +184 -5
  46. package/templates/skills/business-analyse/steps/step-01b-applications.md +423 -0
  47. package/templates/skills/business-analyse/steps/step-02-decomposition.md +23 -6
  48. package/templates/skills/business-analyse/steps/step-03c-compile.md +14 -2
  49. package/templates/skills/business-analyse/steps/step-03d-validate.md +32 -7
  50. package/templates/skills/business-analyse/steps/step-04a-collect.md +111 -0
  51. package/templates/skills/business-analyse/steps/step-05a-handoff.md +296 -103
  52. package/templates/skills/business-analyse/steps/step-05b-deploy.md +46 -14
  53. package/templates/skills/documentation/SKILL.md +92 -2
  54. package/templates/skills/ralph-loop/SKILL.md +14 -17
  55. package/templates/skills/ralph-loop/references/category-rules.md +63 -683
  56. package/templates/skills/ralph-loop/references/compact-loop.md +188 -428
  57. package/templates/skills/ralph-loop/references/section-splitting.md +439 -0
  58. package/templates/skills/ralph-loop/references/team-orchestration.md +13 -14
  59. package/templates/skills/ralph-loop/steps/step-01-task.md +27 -0
  60. package/templates/skills/ralph-loop/steps/step-02-execute.md +80 -691
  61. package/templates/skills/ralph-loop/steps/step-03-commit.md +38 -79
  62. package/templates/skills/ralph-loop/steps/step-04-check.md +39 -58
  63. package/templates/skills/ralph-loop/steps/step-05-report.md +31 -123
  64. package/scripts/health-check.sh +0 -168
  65. package/scripts/postinstall.js +0 -18
@@ -1,28 +1,27 @@
1
1
  ---
2
2
  name: step-02-execute
3
- description: Execute task(s) with dependency checks and rich tracking
3
+ description: Delegate module execution to /apex
4
4
  next_step: steps/step-03-commit.md
5
5
  ---
6
6
 
7
- # Step 2: Execute Task
7
+ # Step 2: Delegate to /apex
8
8
 
9
9
  > **CONTEXT OPTIMIZATION:** Read ONCE (first iteration). Subsequent iterations use compact-loop.md.
10
10
 
11
11
  ## YOUR TASK:
12
12
 
13
- Execute the current task from prd.json. First iteration = ONE task. After that, compact loop handles batches.
13
+ Delegate the current module's tasks to `/apex -d` for code generation. Ralph handles orchestration only.
14
14
 
15
- **ULTRA THINK about the implementation.**
15
+ **ULTRA THINK about the delegation context.**
16
16
 
17
17
  ---
18
18
 
19
19
  ## EXECUTION RULES:
20
20
 
21
- 1. **ATOMIC CHANGES** — Complete and working
22
- 2. **USE MCP** — Validate with SmartStack MCP (once per batch)
23
- 3. **TRACK FILES** — Record every file created or modified
24
- 4. **CHECK DEPENDENCIES** — Verify before starting
25
- 5. **BATCH ALLOWED** — Same category, max 5 tasks
21
+ 1. **VERIFY DEPENDENCIES** — All task dependencies must be completed
22
+ 2. **DELEGATE TO /apex** — All code generation via `/apex -d`
23
+ 3. **TRACK RESULTS** — Verify PRD state after apex returns
24
+ 4. **NEVER GENERATE CODE** — Ralph orchestrates, apex generates
26
25
 
27
26
  ---
28
27
 
@@ -51,723 +50,113 @@ task.started_at = new Date().toISOString();
51
50
  writeJSON('.ralph/prd.json', prd);
52
51
  ```
53
52
 
54
- ### 3. Understand and Execute
53
+ ### 3. Delegate to /apex
55
54
 
56
- **ULTRA THINK:** What needs to be done? What files? What acceptance criteria? What category?
55
+ #### 3a. Section-Split Mode
57
56
 
58
- **Read `references/category-rules.md` for category-specific execution rules.**
59
-
60
- Key category triggers:
61
-
62
- | Category | Special Action |
63
- |----------|---------------|
64
- | `infrastructure` with `_migrationMeta` | Migration sequence: MCP suggest → add → update → build |
65
- | `infrastructure` with seed data keywords | **MANDATORY**: Read `references/core-seed-data.md` |
66
- | `frontend` | MCP-first: scaffold_api_client → scaffold_routes → `/ui-components` skill (pages) → form pages (create/edit = full pages, ZERO modals) → form tests → i18n (4 langues) |
67
- | `test` | Generate via MCP → run → fix loop → 100% pass required |
68
- | `validation` | Clean build → full test suite → MCP validate |
69
-
70
- ### 4. Implement
71
-
72
- - Create/modify files following SmartStack conventions
73
- - Track: `{files_created} = []`, `{files_modified} = []`
74
- - Use MCP tools per category (see category-rules.md)
75
-
76
- ### 5. Post-Infrastructure Build Gate (BLOCKING)
77
-
78
- **After completing ALL infrastructure tasks (configs + migration + seed data):**
79
-
80
- ```bash
81
- dotnet build --no-restore
82
- ```
83
-
84
- MUST pass before proceeding to application/api/test/frontend. Fix if fails.
85
-
86
- **POST-CHECK: Route kebab-case validation (BLOCKING)**
87
-
88
- After generating NavigationSeedData files, verify routes follow kebab-case convention:
89
-
90
- ```bash
91
- # Search for routes with uppercase in NavigationSeedData files
92
- UPPERCASE_ROUTES=$(grep -E 'Route = "?/[^"]*[A-Z]' Infrastructure/Persistence/Seeding/Data/*/NavigationSeedData.cs 2>/dev/null)
93
-
94
- if [ -n "$UPPERCASE_ROUTES" ]; then
95
- echo "❌ ERROR: Routes must be kebab-case lowercase. Found PascalCase in NavigationSeedData routes:"
96
- echo "$UPPERCASE_ROUTES"
97
- echo ""
98
- echo "Expected format: /business/human-resources/projects"
99
- echo "Fix: Use ToKebabCase() helper in route generation (see core-seed-data.md)"
100
- exit 1
101
- fi
102
-
103
- # Search for multi-word route segments missing hyphens (e.g., "humanresources" instead of "human-resources")
104
- MULTI_WORD_ROUTES=$(grep -oP 'Route\s*=\s*"([^"]+)"' Infrastructure/Persistence/Seeding/Data/*/NavigationSeedData.cs 2>/dev/null | grep -P '"[^"]*(?:human(?:resources|resource)|time(?:management|tracking)|project(?:management|member)|leave(?:balance|request))[^"]*"')
105
-
106
- if [ -n "$MULTI_WORD_ROUTES" ]; then
107
- echo "❌ BLOCKING: Route segments contain concatenated multi-word names without hyphens"
108
- echo "Found: $MULTI_WORD_ROUTES"
109
- echo ""
110
- echo "Convention: multi-word segments MUST use kebab-case"
111
- echo " humanresources → human-resources"
112
- echo " timemanagement → time-management"
113
- echo "Fix: Apply ToKebabCase() to all route segments in NavigationSeedData"
114
- exit 1
115
- fi
116
- ```
57
+ ```javascript
58
+ if (prd._sectionSplit?.enabled) {
59
+ // Find next pending phase with dependencies met
60
+ const nextPhase = prd._sectionSplit.phases.find(p => p.status === 'pending');
117
61
 
118
- **Why this matters:**
119
- - Backend seed data defines menu navigation routes
120
- - Frontend React Router expects kebab-case URLs
121
- - Mismatch causes 404 on menu click
122
- - Convention: `HumanResources` (C# code) → `human-resources` (web URL)
123
-
124
- **Fix:** If check fails, routes were generated without `ToKebabCase()` transformation. Regenerate NavigationSeedData with the helper.
125
-
126
- **POST-CHECK: Core Seed Data Integrity (BLOCKING)**
127
-
128
- After generating ALL seed data files (NavigationApplicationSeedData, NavigationModuleSeedData, PermissionsSeedData, RolesSeedData, ApplicationRolesSeedData, IClientSeedDataProvider), verify the complete chain:
129
-
130
- ```bash
131
- # 1. NavigationApplicationSeedData.cs exists
132
- APP_SEED=$(find . -path "*/Seeding/Data/NavigationApplicationSeedData.cs" 2>/dev/null | head -1)
133
- if [ -z "$APP_SEED" ]; then
134
- echo "❌ BLOCKING: NavigationApplicationSeedData.cs NOT FOUND"
135
- echo "Without this, nav_Applications is empty → navigation menu invisible"
136
- echo "Fix: Generate from core-seed-data.md section 1b using seedDataCore.navigationApplications"
137
- exit 1
138
- fi
139
-
140
- # 2. ApplicationRolesSeedData.cs references NavigationApplicationSeedData.ApplicationId (no placeholder)
141
- if grep -q '{ApplicationGuid}' Infrastructure/Persistence/Seeding/Data/ApplicationRolesSeedData.cs 2>/dev/null; then
142
- echo "❌ BLOCKING: ApplicationRolesSeedData still has {ApplicationGuid} placeholder"
143
- echo "Fix: Replace with NavigationApplicationSeedData.ApplicationId"
144
- exit 1
145
- fi
146
-
147
- # 3. IClientSeedDataProvider has no hardcoded app placeholders
148
- PROVIDER=$(find . -path "*/Seeding/*SeedDataProvider.cs" 2>/dev/null | head -1)
149
- if [ -n "$PROVIDER" ]; then
150
- if grep -qE '\{appLabel_|appDesc_|appIcon\}' "$PROVIDER" 2>/dev/null; then
151
- echo "❌ BLOCKING: SeedDataProvider has hardcoded {appLabel_xx}/{appIcon} placeholders"
152
- echo "Fix: Use NavigationApplicationSeedData.GetApplicationEntry() and GetTranslationEntries()"
153
- exit 1
154
- fi
155
- fi
156
-
157
- # 4. Quick startup test — verify seed data doesn't crash at runtime
158
- echo "Running startup test to verify seed data..."
159
- dotnet run --project "$API_PROJECT" --urls "http://localhost:0" -- --environment Development &
160
- RUN_PID=$!
161
- sleep 8
162
-
163
- if ! kill -0 $RUN_PID 2>/dev/null; then
164
- echo "❌ BLOCKING: Application crashed during startup (seed data likely failed)"
165
- echo "Check logs for: navigation, role, or permission seed data errors"
166
- wait $RUN_PID 2>/dev/null
167
- exit 1
168
- else
169
- echo "✓ Startup test passed — seed data executed without crash"
170
- kill $RUN_PID 2>/dev/null
171
- wait $RUN_PID 2>/dev/null
172
- fi
173
- ```
62
+ if (!nextPhase) {
63
+ // All phases done skip to step-04 completion check
64
+ goto STEP_04;
65
+ }
174
66
 
175
- **Why this matters:**
176
- - If `nav_Applications` is empty → navigation menu shows nothing → user can't access modules
177
- - If `auth_Roles` is empty → role-permission mappings fail silently → RBAC broken
178
- - If `auth_Permissions` is empty → all authorization checks reject → 403 on every endpoint
179
- - These are **foundational** — without them, ALL subsequent features (frontend, API, tests) are useless
180
-
181
- **POST-CHECK: Section route/permission completeness (BLOCKING — when sections defined)**
182
-
183
- After generating section seed data, verify completeness:
184
-
185
- ```bash
186
- # 1. Verify every NavigationSection seed route has a frontend route match
187
- SECTION_SEED_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*NavigationSeedData.cs" -exec grep -l 'GetSectionEntries' {} \; 2>/dev/null)
188
- if [ -n "$SECTION_SEED_FILES" ]; then
189
- echo "Sections defined — verifying frontend route matches..."
190
- SECTION_ROUTES=$(grep -ohP 'Route\s*=\s*ToKebabCase\(\$?"([^"]+)"\)' $SECTION_SEED_FILES 2>/dev/null | grep -i section)
191
- APP_TSX="$WEB_SRC/App.tsx"
192
- if [ -n "$APP_TSX" ] && [ -n "$SECTION_ROUTES" ]; then
193
- while IFS= read -r ROUTE; do
194
- ROUTE_PATH=$(echo "$ROUTE" | grep -oP '"([^"]+)"' | tr -d '"')
195
- if [ -n "$ROUTE_PATH" ] && ! grep -q "$ROUTE_PATH" "$APP_TSX" 2>/dev/null; then
196
- echo "WARNING: Section route '$ROUTE_PATH' not found in App.tsx — add frontend route"
197
- fi
198
- done <<< "$SECTION_ROUTES"
199
- fi
200
-
201
- # 2. Verify section-level permissions exist when sections are defined
202
- PERM_SEED=$(find . -path "*/Seeding/Data/*" -name "PermissionsSeedData.cs" 2>/dev/null)
203
- if [ -n "$PERM_SEED" ]; then
204
- for PS in $PERM_SEED; do
205
- HAS_SECTION_PERMS=$(grep -c 'sectionCode\|SectionPascal\|Level = PermissionLevel.Section' "$PS" 2>/dev/null)
206
- if [ "$HAS_SECTION_PERMS" -eq 0 ]; then
207
- echo "BLOCKING: Sections defined but PermissionsSeedData missing section-level permissions: $PS"
208
- echo "Fix: Add section-level permission GUIDs and entries per core-seed-data.md Step C2"
209
- exit 1
210
- fi
211
- done
212
- fi
213
-
214
- # 3. Verify section-level role mappings exist
215
- ROLE_SEED=$(find . -path "*/Seeding/Data/*" -name "RolesSeedData.cs" 2>/dev/null)
216
- if [ -n "$ROLE_SEED" ]; then
217
- for RS in $ROLE_SEED; do
218
- HAS_SECTION_ROLES=$(grep -c 'sectionCode' "$RS" 2>/dev/null)
219
- if [ "$HAS_SECTION_ROLES" -eq 0 ]; then
220
- echo "BLOCKING: Sections defined but RolesSeedData missing section-level role mappings: $RS"
221
- echo "Fix: Add section-level role-permission entries per core-seed-data.md section 5"
222
- exit 1
223
- fi
224
- done
225
- fi
226
- fi
227
- ```
67
+ const depsOk = nextPhase.dependsOn.every(depIdx =>
68
+ prd._sectionSplit.phases[depIdx].status === 'completed'
69
+ );
228
70
 
229
- **POST-CHECK: Navigation section route CRUD suffix detection (BLOCKING)**
230
-
231
- After generating NavigationSeedData files with sections, verify no route contains `/list` or `/detail` suffixes:
232
-
233
- ```bash
234
- # Detect CRUD suffixes in navigation section routes
235
- CRUD_ROUTES=$(grep -rn 'Route.*=.*\/list\b\|Route.*=.*\/detail' Infrastructure/Persistence/Seeding/Data/ 2>/dev/null | grep -v '//.*Route')
236
-
237
- if [ -n "$CRUD_ROUTES" ]; then
238
- echo "BLOCKING: Navigation section routes contain CRUD suffixes"
239
- echo "Convention:"
240
- echo " - 'list' section route = module route (NO /list suffix)"
241
- echo " - 'detail' section route = module route + /:id (NOT /detail/:id)"
242
- echo " - Other sections = module route + /{section-kebab}"
243
- echo ""
244
- echo "Found:"
245
- echo "$CRUD_ROUTES"
246
- echo ""
247
- echo "Fix: Remove /list suffix (use module route), replace /detail/:id with /:id"
248
- exit 1
249
- fi
250
- ```
71
+ if (!depsOk) {
72
+ console.warn(`Phase ${nextPhase.phase} blocked by incomplete dependencies`);
73
+ goto STEP_04;
74
+ }
251
75
 
252
- **Why this matters:**
253
- - React Router defines NO `/list` child route the module route IS the list view
254
- - React Router uses `/:id` for detail views, NOT `/detail/:id`
255
- - Mismatch causes 404 on navigation menu click
256
-
257
- **POST-CHECK: Permission path segment count (WARNING — when permissions defined)**
258
-
259
- ```bash
260
- PERM_FILES=$(find Infrastructure/Persistence/Seeding/Data/ -name "PermissionsSeedData.cs" 2>/dev/null)
261
- if [ -n "$PERM_FILES" ]; then
262
- MALFORMED=$(grep -oP 'Path\s*=\s*"([^"]+)"' $PERM_FILES | grep -oP '"[^"]+"' | tr -d '"' | while read -r path; do
263
- DOTS=$(echo "$path" | tr -cd '.' | wc -c)
264
- if echo "$path" | grep -qP '\.\*$'; then continue; fi
265
- if [ "$DOTS" -lt 3 ] || [ "$DOTS" -gt 5 ]; then
266
- echo " Unexpected segment count ($((DOTS+1))): $path"
267
- fi
268
- done)
269
- if [ -n "$MALFORMED" ]; then
270
- echo "WARNING: Permission paths with unexpected segment count:"
271
- echo "$MALFORMED"
272
- echo " Expected: 4 segments (module-level) or 5 segments (section-level)"
273
- fi
274
- fi
275
- ```
76
+ const phaseLabel = nextPhase.type === 'foundation'
77
+ ? 'Phase 0: Foundation (all entities + migration + module seed data)'
78
+ : `Phase ${nextPhase.phase}: Section "${nextPhase.sectionCode}" (services + controllers + pages + tests)`;
79
+ console.log(`SECTION SPLIT: ${phaseLabel}`);
276
80
 
277
- **POST-CHECK: DefaultTenantId cross-schema FK validation (WARNING)**
278
-
279
- After generating SeedConstants.cs and DevDataSeeder, verify that `DefaultTenantId` is not a phantom GUID that doesn't exist in `core.tenant_Tenants`:
280
-
281
- ```bash
282
- # Find SeedConstants.cs
283
- SEED_CONSTANTS=$(find . -path "*/SeedConstants.cs" 2>/dev/null | head -1)
284
- if [ -n "$SEED_CONSTANTS" ]; then
285
- # Extract the DefaultTenantId GUID value
286
- TENANT_GUID=$(grep -oP 'DefaultTenantId\s*=\s*Guid\.Parse\("([^"]+)"\)' "$SEED_CONSTANTS" | grep -oP '"[^"]+"' | tr -d '"')
287
-
288
- if [ -z "$TENANT_GUID" ]; then
289
- echo "⚠️ WARNING: SeedConstants.cs found but DefaultTenantId not defined"
290
- echo "DevDataSeeder requires SeedConstants.DefaultTenantId for business seed data"
291
- echo "Fix: Add 'public static readonly Guid DefaultTenantId = Guid.Parse(\"...\");' to SeedConstants.cs"
292
- else
293
- # Verify the GUID is referenced somewhere in platform config (not an invented value)
294
- GUID_FOUND=$(grep -r "$TENANT_GUID" appsettings*.json src/ 2>/dev/null | grep -v SeedConstants)
295
-
296
- if [ -z "$GUID_FOUND" ]; then
297
- echo "⚠️ WARNING: SeedConstants.DefaultTenantId ($TENANT_GUID) not found in platform config"
298
- echo "This GUID must exist in core.tenant_Tenants at runtime"
299
- echo "Verify: The SmartStack platform seeds this tenant during InitializeSmartStackAsync()"
300
- echo "If using a custom TenantId, ensure it's created before DevDataSeeder runs (Order >= 200)"
301
- echo "validate-feature step-05 will verify this FK exists in real database"
302
- else
303
- echo "✓ SeedConstants.DefaultTenantId ($TENANT_GUID) found in platform config"
304
- fi
305
- fi
306
- fi
307
- ```
81
+ // INVOKE /apex -d {nextPhase.prdFile}
82
+ // Apex sees a normal (smaller) PRD and executes normally
308
83
 
309
- **Why this matters:**
310
- - If `DefaultTenantId` references a GUID not in `core.tenant_Tenants`, DevDataSeeder inserts orphan rows
311
- - Business entities with invalid `TenantId` FK cause runtime 500 errors
312
- - The tenant must be seeded by `InitializeSmartStackAsync()` BEFORE DevDataSeeder (Order >= 200) runs
84
+ // After apex returns: merge results into master PRD
85
+ // Read references/section-splitting.md sections 7-9 (execution, merging, failure handling)
313
86
 
314
- ### 6. Validate with MCP
87
+ nextPhase.status = 'completed';
88
+ prd._sectionSplit.currentPhase = nextPhase.phase;
89
+ writeJSON('.ralph/prd.json', prd);
315
90
 
91
+ // Continue to step-03 (commit), then step-04 loops back for next phase
92
+ }
316
93
  ```
317
- mcp__smartstack__validate_conventions: checks: ["all"]
318
94
 
319
- # Category-specific:
320
- infrastructure → mcp__smartstack__check_migrations
321
- api → mcp__smartstack__validate_security
322
- frontend → mcp__smartstack__validate_frontend_routes
323
- test → mcp__smartstack__analyze_test_coverage
324
- ```
95
+ > See `references/section-splitting.md` for full detection, mapping, PRD construction, and merge logic.
325
96
 
326
- If validation fails: fix re-validate → do NOT proceed until clean.
97
+ #### 3b. Standard Mode (no split)
327
98
 
328
- ### 7. Check Acceptance Criteria
99
+ **INVOKE `/apex -d .ralph/prd.json`**
329
100
 
330
- Verify against `{current_task_criteria}`. If not met: continue working. If impossible: document in `task.error`.
101
+ This delegates ALL remaining tasks for the current module to apex:
331
102
 
332
- ### 8. Build & Test Verification (BLOCKING)
103
+ - Apex reads the PRD, extracts context (`context_code`, `app_name`, `module_code`, `entities`, `sections`)
104
+ - Apex executes ALL layers: domain → infrastructure → migration → application → api → seed data → frontend → tests
105
+ - Apex runs full POST-CHECKs (50 checks from `references/post-checks.md`)
106
+ - Apex commits per layer (atomic commits)
107
+ - Apex validates with MCP (`validate_conventions`)
108
+ - Apex updates task statuses in the PRD file directly
333
109
 
334
- **Backend EVERY task (domain, infra, application, api):**
335
- ```bash
336
- dotnet build --no-restore
337
- ```
338
- If FAIL → read error output → identify file:line → fix code → rebuild → loop until exit code 0.
110
+ > **FLAGS:** `-d` implies `-a` (auto, no user confirmation) and `-e` (economy, no nested teams).
111
+ > Ralph manages parallelism via team orchestration, not apex.
339
112
 
340
- **Test category tasks MANDATORY execution:**
341
- ```bash
342
- TEST_PROJECT="tests/$(basename $(pwd)).Tests.Unit"
343
- dotnet build "$TEST_PROJECT" --no-restore
344
- dotnet test "$TEST_PROJECT" --no-build --verbosity normal
345
- ```
346
- If FAIL → analyze failing test names → fix SOURCE CODE (not tests) → rebuild → retest.
347
- **Loop until 100% pass — NEVER proceed with failing tests.**
113
+ ### 4. Verify Post-Apex Results
348
114
 
349
- **Frontend tasks — MANDATORY verification:**
350
- ```bash
351
- npm run typecheck # TypeScript compilation — MUST exit 0
352
- npm run lint # ESLint MUST exit 0
353
- ```
354
- If FAIL → read errors → fix TSX/TS files → re-run → loop until pass.
115
+ ```javascript
116
+ // Re-read PRD after apex execution
117
+ const updatedPrd = readJSON('.ralph/prd.json');
118
+ const moduleTasks = updatedPrd.tasks.filter(t => t.module === {current_module});
355
119
 
356
- **Frontend POST-CHECKs (BLOCKING for frontend category):**
120
+ const completed = moduleTasks.filter(t => t.status === 'completed').length;
121
+ const failed = moduleTasks.filter(t => t.status === 'failed').length;
122
+ const pending = moduleTasks.filter(t => t.status === 'pending').length;
357
123
 
358
- > **Path resolution:** Web projects may live in subdirectories (e.g., `web/app-web/src/pages/`).
359
- > All POST-CHECKs below use dynamic path discovery instead of hardcoded `src/pages/`.
124
+ if (failed > 0) {
125
+ console.log(`WARNING: ${failed} tasks failed during apex execution`);
126
+ // Failed tasks will be retried in compact loop
127
+ }
360
128
 
361
- ```bash
362
- # Resolve frontend directories dynamically (handles web/ prefix and varied project structures)
363
- PAGE_DIR=$(find . -path "*/src/pages" -not -path "*/node_modules/*" -type d 2>/dev/null | head -1)
364
- HOOK_DIR=$(find . -path "*/src/hooks" -not -path "*/node_modules/*" -type d 2>/dev/null | head -1)
365
- COMP_DIR=$(find . -path "*/src/components" -not -path "*/node_modules/*" -type d 2>/dev/null | head -1)
366
- WEB_SRC=$(find . -name "App.tsx" -not -path "*/node_modules/*" -exec dirname {} \; 2>/dev/null | head -1)
367
- # All POST-CHECKs below use $PAGE_DIR, $HOOK_DIR, $COMP_DIR, $WEB_SRC instead of hardcoded paths
368
- ```
129
+ if (pending > 0) {
130
+ console.log(`INFO: ${pending} tasks still pending will be handled in compact loop`);
131
+ }
369
132
 
370
- ```bash
371
- # POST-CHECK: Forms must be full pages — ZERO modals/popups/drawers
372
- PAGE_FILES=$(find "$PAGE_DIR" -name "*.tsx" 2>/dev/null)
373
- if [ -n "$PAGE_FILES" ]; then
374
- MODAL_IMPORTS=$(grep -Pn "import.*(?:Modal|Dialog|Drawer|Popup|Sheet)" $PAGE_FILES 2>/dev/null)
375
- if [ -n "$MODAL_IMPORTS" ]; then
376
- echo "BLOCKING: Form pages must NOT use Modal/Dialog/Drawer/Popup components"
377
- echo "Create/Edit forms MUST be full pages with own URL routes (/create, /:id/edit)"
378
- echo "$MODAL_IMPORTS"
379
- exit 1
380
- fi
381
- fi
382
-
383
- # POST-CHECK: No raw HTML <table> in page files — MUST use SmartTable/DataTable (BLOCKING)
384
- PAGE_FILES=$(find "$PAGE_DIR" -name "*.tsx" 2>/dev/null)
385
- if [ -n "$PAGE_FILES" ]; then
386
- RAW_TABLES=$(grep -Pn '<table[\s>]|<thead[\s>]|<tbody[\s>]' $PAGE_FILES 2>/dev/null)
387
- if [ -n "$RAW_TABLES" ]; then
388
- echo "BLOCKING: Raw HTML <table> detected in page files — MUST use SmartTable or DataTable"
389
- echo "SmartStack convention: Lists MUST use SmartTable + SmartFilter (see /ui-components skill)"
390
- echo "Import: import { DataTable } from '@/components/ui/DataTable'"
391
- echo ""
392
- echo "Found:"
393
- echo "$RAW_TABLES"
394
- exit 1
395
- fi
396
- fi
397
-
398
- # POST-CHECK: No hardcoded Tailwind colors — MUST use CSS variables (BLOCKING)
399
- PAGE_FILES=$(find "$PAGE_DIR" -name "*.tsx" -o -name "*.tsx" 2>/dev/null | sort -u)
400
- COMPONENT_FILES=$(find "$COMP_DIR" -name "*.tsx" 2>/dev/null)
401
- ALL_TSX="$PAGE_FILES $COMPONENT_FILES"
402
- if [ -n "$ALL_TSX" ]; then
403
- # Match bg-{color}-{shade} or text-{color}-{shade} patterns (excluding bg-white, bg-black, bg-transparent, bg-current)
404
- HARDCODED_COLORS=$(grep -Pn '(?:bg|text|border|ring|from|to|via)-(?:red|blue|green|yellow|orange|purple|pink|indigo|teal|cyan|emerald|violet|fuchsia|rose|amber|lime|sky|slate|gray|zinc|neutral|stone)-\d{2,3}' $ALL_TSX 2>/dev/null | head -20)
405
- if [ -n "$HARDCODED_COLORS" ]; then
406
- echo "BLOCKING: Hardcoded Tailwind colors detected — MUST use CSS variables"
407
- echo "SmartStack convention: ALL colors via CSS variables for theming support"
408
- echo " bg-blue-600 → bg-[var(--color-accent-600)]"
409
- echo " text-red-500 → text-[var(--error-text)]"
410
- echo " bg-green-100 → bg-[var(--success-bg)]"
411
- echo " bg-gray-50 → bg-[var(--bg-secondary)]"
412
- echo ""
413
- echo "Found:"
414
- echo "$HARDCODED_COLORS"
415
- exit 1
416
- fi
417
- fi
418
-
419
- # POST-CHECK: List pages must import SmartTable/DataTable OR EntityCard (WARNING)
420
- LIST_PAGES=$(find "$PAGE_DIR" -name "*ListPage.tsx" -o -name "*sPage.tsx" 2>/dev/null | grep -v test)
421
- if [ -n "$LIST_PAGES" ]; then
422
- for LP in $LIST_PAGES; do
423
- HAS_TABLE=$(grep -c 'SmartTable\|DataTable' "$LP" 2>/dev/null)
424
- HAS_CARD=$(grep -c 'EntityCard' "$LP" 2>/dev/null)
425
- if [ "$HAS_TABLE" -eq 0 ] && [ "$HAS_CARD" -eq 0 ]; then
426
- echo "WARNING: List page missing SmartTable/DataTable or EntityCard: $LP"
427
- echo "List pages MUST use SmartStack UI components from /ui-components skill"
428
- echo " Tables: import { DataTable } from '@/components/ui/DataTable'"
429
- echo " Cards: import { EntityCard } from '@/components/ui/EntityCard'"
430
- fi
431
- done
432
- fi
433
-
434
- # POST-CHECK: No window.confirm() — MUST use confirmation component (BLOCKING)
435
- PAGE_FILES=$(find "$PAGE_DIR" -name "*.tsx" 2>/dev/null)
436
- if [ -n "$PAGE_FILES" ]; then
437
- BAD_CONFIRM=$(grep -Pn 'window\.confirm\s*\(|(?<!\w)confirm\s*\(' $PAGE_FILES 2>/dev/null | grep -v '//' | grep -v test)
438
- if [ -n "$BAD_CONFIRM" ]; then
439
- echo "BLOCKING: window.confirm() detected — MUST use a SmartStack confirmation component"
440
- echo "Native browser dialogs break theming and i18n"
441
- echo "Use a ConfirmDialog component with translated messages"
442
- echo ""
443
- echo "Found:"
444
- echo "$BAD_CONFIRM"
445
- exit 1
446
- fi
447
- fi
448
-
449
- # POST-CHECK: Pages must handle loading, error, and empty states (WARNING)
450
- PAGE_FILES=$(find "$PAGE_DIR" -name "*ListPage.tsx" -o -name "*sPage.tsx" 2>/dev/null | grep -v test)
451
- if [ -n "$PAGE_FILES" ]; then
452
- for LP in $PAGE_FILES; do
453
- HAS_LOADING=$(grep -c 'loading\|isLoading\|skeleton\|Skeleton\|PageLoader\|spinner' "$LP" 2>/dev/null)
454
- HAS_ERROR=$(grep -c 'error\|Error' "$LP" 2>/dev/null)
455
- HAS_EMPTY=$(grep -c 'empty\|Empty\|no.*found\|length === 0' "$LP" 2>/dev/null)
456
- MISSING=""
457
- [ "$HAS_LOADING" -eq 0 ] && MISSING="${MISSING}loading "
458
- [ "$HAS_ERROR" -eq 0 ] && MISSING="${MISSING}error "
459
- [ "$HAS_EMPTY" -eq 0 ] && MISSING="${MISSING}empty "
460
- if [ -n "$MISSING" ]; then
461
- echo "WARNING: List page missing state handling ($MISSING): $LP"
462
- echo "All list pages MUST handle: loading skeleton, error with retry, empty state"
463
- fi
464
- done
465
- fi
466
-
467
- # POST-CHECK: Create/Edit pages must exist for each module with a list page
468
- LIST_PAGES=$(find "$PAGE_DIR" -name "*ListPage.tsx" -o -name "*sPage.tsx" 2>/dev/null | grep -v test)
469
- if [ -n "$LIST_PAGES" ]; then
470
- for LP in $LIST_PAGES; do
471
- DIR=$(dirname "$LP"); MOD=$(basename "$DIR")
472
- if [ -z "$(find "$DIR" -name "*CreatePage.tsx" 2>/dev/null)" ]; then
473
- echo "WARNING: Module $MOD has list page but no CreatePage"
474
- fi
475
- if [ -z "$(find "$DIR" -name "*EditPage.tsx" 2>/dev/null)" ]; then
476
- echo "WARNING: Module $MOD has list page but no EditPage"
477
- fi
478
- done
479
- fi
480
-
481
- # First check: at least one test file must exist in pages/
482
- PAGE_FILES=$(find "$PAGE_DIR" -name "*.tsx" ! -name "*.test.tsx" 2>/dev/null)
483
- if [ -n "$PAGE_FILES" ]; then
484
- TEST_FILES=$(find "$PAGE_DIR" -name "*.test.tsx" 2>/dev/null)
485
- if [ -z "$TEST_FILES" ]; then
486
- echo "BLOCKING: No frontend test files found in src/pages/"
487
- echo "Every module with pages MUST have at least one .test.tsx file"
488
- exit 1
489
- fi
490
- fi
491
-
492
- # POST-CHECK: Form pages must have companion test files
493
- FORM_PAGES=$(find "$PAGE_DIR" -name "*CreatePage.tsx" -o -name "*EditPage.tsx" 2>/dev/null | grep -v test)
494
- if [ -n "$FORM_PAGES" ]; then
495
- for FP in $FORM_PAGES; do
496
- TEST="${FP%.tsx}.test.tsx"
497
- if [ ! -f "$TEST" ]; then
498
- echo "BLOCKING: Form page missing test file: $FP → expected $TEST"
499
- exit 1
500
- fi
501
- done
502
- fi
503
-
504
- # POST-CHECK: FK fields must NOT be plain text inputs — use EntityLookup
505
- FORM_PAGES=$(find "$PAGE_DIR" -name "*CreatePage.tsx" -o -name "*EditPage.tsx" 2>/dev/null | grep -v test | grep -v node_modules)
506
- if [ -n "$FORM_PAGES" ]; then
507
- # Match any input with name ending in "Id" (except hidden inputs)
508
- FK_TEXT_INPUTS=$(grep -Pn '<input[^>]*name=["\x27][a-zA-Z]*Id["\x27]' $FORM_PAGES 2>/dev/null | grep -v 'type=["\x27]hidden["\x27]')
509
- if [ -n "$FK_TEXT_INPUTS" ]; then
510
- echo "BLOCKING: FK Guid fields rendered as plain text inputs — MUST use EntityLookup"
511
- echo "See smartstack-frontend.md section 6 for the EntityLookup pattern"
512
- echo "$FK_TEXT_INPUTS"
513
- exit 1
514
- fi
515
- FK_PLACEHOLDER=$(grep -Pn 'placeholder=["\x27].*[Ee]nter.*[Ii][Dd]|placeholder=["\x27].*[Gg][Uu][Ii][Dd]' $FORM_PAGES 2>/dev/null)
516
- if [ -n "$FK_PLACEHOLDER" ]; then
517
- echo "BLOCKING: Form placeholder asks user to enter ID/GUID — use EntityLookup instead"
518
- echo "$FK_PLACEHOLDER"
519
- exit 1
520
- fi
521
- fi
522
-
523
- # POST-CHECK: Backend GetAll endpoints must support ?search= for EntityLookup
524
- CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
525
- if [ -n "$CTRL_FILES" ]; then
526
- for f in $CTRL_FILES; do
527
- if grep -q "\[HttpGet\]" "$f" && grep -q "GetAll" "$f"; then
528
- if ! grep -q "search" "$f"; then
529
- echo "WARNING: Controller missing search parameter on GetAll: $f"
530
- echo "GetAll MUST accept ?search= to enable EntityLookup on frontend"
531
- fi
532
- fi
533
- done
534
- fi
535
-
536
- # POST-CHECK: Route seed data vs frontend cross-validation (CONVENTION + EXISTENCE)
537
- SEED_NAV_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*NavigationSeedData.cs" 2>/dev/null)
538
- APP_TSX="$WEB_SRC/App.tsx"
539
- if [ -n "$SEED_NAV_FILES" ] && [ -n "$APP_TSX" ]; then
540
- SEED_ROUTES=$(grep -ohP 'Route\s*=\s*"([^"]+)"' $SEED_NAV_FILES | sed 's/Route\s*=\s*"//' | sed 's/"//' | sort -u)
541
- CLIENT_PATHS=$(grep -ohP "path:\s*['\"]([^'\"]+)['\"]" "$APP_TSX" | sed "s/path:\s*['\"]//;s/['\"]//" | sort -u)
542
- if [ -n "$SEED_ROUTES" ] && [ -z "$CLIENT_PATHS" ]; then
543
- echo "WARNING: Seed data has navigation routes but App.tsx has no client routes defined"
544
- fi
545
- # Cross-check: frontend paths must use same kebab-case segments as backend API routes
546
- API_CONTROLLERS=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
547
- if [ -n "$API_CONTROLLERS" ] && [ -n "$CLIENT_PATHS" ]; then
548
- API_SEGMENTS=$(grep -ohP '\[Route\("api/([^"]+)"\)\]' $API_CONTROLLERS | sed 's/\[Route("api\///;s/")\]//' | tr '/' '\n' | sort -u)
549
- for SEG in $API_SEGMENTS; do
550
- # Check if frontend path contains same segment (kebab-case match)
551
- if echo "$SEG" | grep -qP '[a-z]+-[a-z]+'; then
552
- # Multi-word segment like "human-resources" — verify frontend uses same convention
553
- NO_HYPHEN=$(echo "$SEG" | tr -d '-')
554
- if echo "$CLIENT_PATHS" | grep -q "$NO_HYPHEN"; then
555
- echo "BLOCKING: Frontend route uses '$NO_HYPHEN' but backend API uses '$SEG' (kebab-case)"
556
- echo "Fix: Frontend paths MUST match backend route convention"
557
- echo " Backend: api/business/$SEG/..."
558
- echo " Frontend: /business/$SEG/... (NOT /business/$NO_HYPHEN/...)"
559
- exit 1
560
- fi
561
- fi
562
- done
563
- fi
564
- fi
565
-
566
- # POST-CHECK: HasQueryFilter anti-pattern
567
- CONFIG_FILES=$(find src/ -path "*/Configurations/*" -name "*Configuration.cs" 2>/dev/null)
568
- if [ -n "$CONFIG_FILES" ]; then
569
- BAD_FILTER=$(grep -Pn 'HasQueryFilter.*Guid\.Empty' $CONFIG_FILES 2>/dev/null)
570
- if [ -n "$BAD_FILTER" ]; then
571
- echo "BLOCKING: HasQueryFilter uses Guid.Empty — must use runtime tenant filter"
572
- echo "$BAD_FILTER"
573
- exit 1
574
- fi
575
- fi
576
-
577
- # POST-CHECK: PaginatedResult<T> for GetAll
578
- SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
579
- if [ -n "$SERVICE_FILES" ]; then
580
- BAD_GETALL=$(grep -Pn 'Task<(List|IEnumerable|IList|ICollection)<' $SERVICE_FILES 2>/dev/null | grep -i "getall")
581
- if [ -n "$BAD_GETALL" ]; then
582
- echo "BLOCKING: GetAll methods must return PaginatedResult<T>, not List/IEnumerable"
583
- echo "$BAD_GETALL"
584
- exit 1
585
- fi
586
- fi
587
-
588
- # POST-CHECK: i18n file structure — per-module JSON files MUST exist (BLOCKING)
589
- WEB_DIR=$(find . -name "App.tsx" -not -path "*/node_modules/*" -exec dirname {} \; 2>/dev/null | head -1)
590
- if [ -n "$WEB_DIR" ]; then
591
- I18N_DIR="$WEB_DIR/i18n/locales"
592
- if [ -d "$I18N_DIR" ]; then
593
- for LANG in fr en it de; do
594
- LANG_DIR="$I18N_DIR/$LANG"
595
- if [ ! -d "$LANG_DIR" ]; then
596
- echo "BLOCKING: Missing i18n locale directory: $LANG_DIR"
597
- echo "All 4 languages (fr, en, it, de) MUST have a dedicated directory"
598
- exit 1
599
- fi
600
- # Check for at least one module-specific JSON (not just common.json)
601
- MODULE_JSONS=$(find "$LANG_DIR" -name "*.json" ! -name "common.json" ! -name "navigation.json" 2>/dev/null)
602
- if [ -z "$MODULE_JSONS" ]; then
603
- echo "BLOCKING: No module translation files in $LANG_DIR/"
604
- echo "Each module MUST have src/i18n/locales/$LANG/{moduleLower}.json"
605
- exit 1
606
- fi
607
- done
608
- else
609
- echo "BLOCKING: Missing i18n/locales directory — i18n file structure not created"
610
- echo "Expected: src/i18n/locales/{fr,en,it,de}/{module}.json"
611
- exit 1
612
- fi
613
- fi
614
-
615
- # POST-CHECK: i18n required key structure (BLOCKING — upgraded from WARNING)
616
- if [ -d "$I18N_DIR/fr" ]; then
617
- for f in $(find "$I18N_DIR/fr/" -name "*.json" 2>/dev/null); do
618
- BASENAME=$(basename "$f")
619
- case "$BASENAME" in common.json|navigation.json) continue;; esac
620
- for KEY in "actions" "labels" "errors" "messages" "empty"; do
621
- if ! grep -q "\"$KEY\"" "$f"; then
622
- echo "BLOCKING: i18n file missing required key group '$KEY': $f"
623
- echo "All module i18n files MUST contain: actions, labels, errors, messages, empty"
624
- exit 1
625
- fi
626
- done
627
- done
628
- fi
629
-
630
- # POST-CHECK: Pages must NOT contain hardcoded English UI text (BLOCKING)
631
- PAGE_FILES=$(find "$PAGE_DIR" -name "*.tsx" ! -name "*.test.tsx" 2>/dev/null)
632
- if [ -n "$PAGE_FILES" ]; then
633
- # Detect common hardcoded English patterns in JSX (excluding imports/comments/code)
634
- HARDCODED_TEXT=$(grep -Pn '>\s*(Create|Edit|Delete|Save|Cancel|Search|Loading|Error|No .* found|Are you sure|Submit|Back|Actions|Status|Name|Description|Total)\s*<' $PAGE_FILES 2>/dev/null | grep -v '//' | grep -v 'test' | head -20)
635
- if [ -n "$HARDCODED_TEXT" ]; then
636
- echo "BLOCKING: Hardcoded English UI text found in page files"
637
- echo "ALL visible text MUST use t() from useTranslation() — NEVER hardcode strings"
638
- echo "Pattern: t('{module}:actions.create', 'Create') — namespace + fallback"
639
- echo ""
640
- echo "Found:"
641
- echo "$HARDCODED_TEXT"
642
- exit 1
643
- fi
644
- fi
645
-
646
- # POST-CHECK: Hooks must NOT contain hardcoded error messages (BLOCKING)
647
- HOOK_FILES=$(find "$HOOK_DIR" -name "*.ts" -o -name "*.tsx" 2>/dev/null)
648
- if [ -n "$HOOK_FILES" ]; then
649
- HARDCODED_ERRORS=$(grep -Pn "(setError|throw new Error)\(['\"](?!t\()[A-Z][a-z]+" $HOOK_FILES 2>/dev/null | head -15)
650
- if [ -n "$HARDCODED_ERRORS" ]; then
651
- echo "BLOCKING: Hardcoded error messages in hooks — MUST use i18n t() function"
652
- echo "Hooks must import useTranslation and use t('{module}:errors.loadFailed') for all messages"
653
- echo ""
654
- echo "Found:"
655
- echo "$HARDCODED_ERRORS"
656
- exit 1
657
- fi
658
- fi
659
-
660
- # POST-CHECK: SeedConstants must NOT contain ContextId (pre-seeded by SmartStack core)
661
- SEED_CONST_FILES=$(find src/ -path "*/Seeding/*" -name "SeedConstants.cs" 2>/dev/null)
662
- if [ -n "$SEED_CONST_FILES" ]; then
663
- BAD_CTX=$(grep -Pn 'ContextId\s*=' $SEED_CONST_FILES 2>/dev/null)
664
- if [ -n "$BAD_CTX" ]; then
665
- echo "BLOCKING: SeedConstants must NOT contain a ContextId constant"
666
- echo "Context IDs are pre-seeded by SmartStack core — look up by code at runtime"
667
- echo "$BAD_CTX"
668
- exit 1
669
- fi
670
- fi
671
- SEED_ALL_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*.cs" 2>/dev/null)
672
- if [ -n "$SEED_ALL_FILES" ]; then
673
- BAD_CTX_GUID=$(grep -Pn 'DeterministicGuid\("nav:(business|platform|personal)"\)' $SEED_ALL_FILES 2>/dev/null)
674
- if [ -n "$BAD_CTX_GUID" ]; then
675
- echo "BLOCKING: Deterministic GUID for NavigationContext detected"
676
- echo "Fix: Look up context by code at runtime in SeedDataProvider.SeedNavigationAsync()"
677
- echo "$BAD_CTX_GUID"
678
- exit 1
679
- fi
680
- fi
681
-
682
- # POST-CHECK: RolePermission seed data must NOT use deterministic role GUIDs
683
- SEED_ALL_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*.cs" 2>/dev/null)
684
- SEED_CONST_FILES=$(find src/ -path "*/Seeding/*" -name "SeedConstants.cs" 2>/dev/null)
685
- if [ -n "$SEED_ALL_FILES" ]; then
686
- BAD_ROLE_GUID=$(grep -Pn 'DeterministicGuid\("role:' $SEED_ALL_FILES $SEED_CONST_FILES 2>/dev/null)
687
- if [ -n "$BAD_ROLE_GUID" ]; then
688
- echo "BLOCKING: Deterministic GUID for role detected"
689
- echo "System roles are pre-seeded by SmartStack core — look up by Code at runtime"
690
- echo "$BAD_ROLE_GUID"
691
- exit 1
692
- fi
693
- fi
694
-
695
- # POST-CHECK: Services must NOT use TenantId!.Value (crashes with 500 instead of 401)
696
- SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
697
- if [ -n "$SERVICE_FILES" ]; then
698
- BAD_PATTERN=$(grep -Pn 'TenantId!\s*\.Value|TenantId!\s*\.ToString|\.TenantId!' $SERVICE_FILES 2>/dev/null)
699
- if [ -n "$BAD_PATTERN" ]; then
700
- echo "BLOCKING: TenantId!.Value causes 500 when tenant context is missing"
701
- echo "Fix: var tenantId = _currentTenant.TenantId ?? throw new TenantContextRequiredException();"
702
- echo "NEVER use UnauthorizedAccessException for tenant context — it returns 401 which clears the frontend token."
703
- echo "$BAD_PATTERN"
704
- exit 1
705
- fi
706
- fi
707
-
708
- # POST-CHECK: Services must NOT use UnauthorizedAccessException for tenant context (causes token clearing)
709
- if [ -n "$SERVICE_FILES" ]; then
710
- BAD_UNAUTH=$(grep -Pn 'UnauthorizedAccessException.*[Tt]enant' $SERVICE_FILES 2>/dev/null)
711
- if [ -n "$BAD_UNAUTH" ]; then
712
- echo "BLOCKING: Services use UnauthorizedAccessException for tenant context — causes 401 which clears the frontend token"
713
- echo "$BAD_UNAUTH"
714
- echo "Fix: var tenantId = _currentTenant.TenantId ?? throw new TenantContextRequiredException();"
715
- exit 1
716
- fi
717
- fi
718
-
719
- # POST-CHECK: IAuditableEntity + Validator pairing
720
- ENTITY_FILES=$(find src/ -path "*/Domain/Entities/Business/*" -name "*.cs" 2>/dev/null)
721
- if [ -n "$ENTITY_FILES" ]; then
722
- for f in $ENTITY_FILES; do
723
- if grep -q "ITenantEntity" "$f" && ! grep -q "IAuditableEntity" "$f"; then
724
- echo "WARNING: Entity has ITenantEntity but missing IAuditableEntity: $f"
725
- fi
726
- done
727
- fi
728
- CREATE_VALIDATORS=$(find src/ -path "*/Validators/*" -name "Create*Validator.cs" 2>/dev/null)
729
- if [ -n "$CREATE_VALIDATORS" ]; then
730
- for CV in $CREATE_VALIDATORS; do
731
- UV=$(echo "$CV" | sed 's/Create/Update/')
732
- if [ ! -f "$UV" ]; then
733
- echo "BLOCKING: CreateValidator without matching UpdateValidator: $CV"
734
- echo "Expected: $UV"
735
- exit 1
736
- fi
737
- done
738
- fi
133
+ console.log(`Apex completed: ${completed}/${moduleTasks.length} tasks`);
739
134
  ```
740
135
 
741
- **Error resolution cycle (ALL categories):**
742
- 1. Read the FULL error output (not just first line)
743
- 2. Identify root cause: file path + line number
744
- 3. Fix the issue in source code
745
- 4. Rebuild: `dotnet build --no-restore` or `npm run typecheck`
746
- 5. If still failing → repeat from step 1
747
- 6. **NEVER** mark task completed with failing build or tests
748
- 7. **NEVER** skip the verification — it is BLOCKING
749
-
750
- ### 9. Update Task State
136
+ ### 5. Update Progress
751
137
 
752
138
  ```javascript
753
- task.files_changed = { created: {files_created}, modified: {files_modified} };
754
- task.validation = {validation_result};
755
- if ({task_failed}) { task.status = 'failed'; task.error = '{reason}'; task.completed_at = now; }
756
- prd.updated_at = new Date().toISOString();
757
- writeJSON('.ralph/prd.json', prd);
139
+ // Append to progress file
140
+ const progressEntry = `
141
+ [Iteration ${prd.config.current_iteration}] Delegated to /apex
142
+ Module: ${prd.project.module}
143
+ Completed: ${completed}/${moduleTasks.length}
144
+ Failed: ${failed}
145
+ Pending: ${pending}
146
+ `;
147
+ appendFile('.ralph/progress.txt', progressEntry);
758
148
  ```
759
149
 
760
- Note: `task.status` stays `"in_progress"` until step-03 confirms commit. Only `"failed"` is set here.
761
-
762
150
  ---
763
151
 
764
152
  ## CONSTRAINTS:
765
153
 
766
- **DO:** Implement exactly what task describes, follow patterns, validate with MCP, track files, check criteria.
767
- **DON'T:** Implement other tasks, refactor unrelated code, add extras, skip validation.
154
+ - **DO:** Delegate to apex, verify results, maintain PRD state
155
+ - **DON'T:** Generate code directly, run inline POST-CHECKs, invoke MCP tools for generation
156
+ - **DON'T:** Stop to ask the user anything — fully autonomous
768
157
 
769
158
  ---
770
159
 
771
160
  ## NEXT STEP:
772
161
 
773
- After task executed and validated, proceed to `./step-03-commit.md`
162
+ Proceed to `./step-03-commit.md`