@atlashub/smartstack-cli 3.32.0 → 3.34.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/.documentation/agents.html +5 -1
- package/.documentation/apex.html +644 -0
- package/.documentation/business-analyse.html +81 -1
- package/.documentation/cli-commands.html +5 -1
- package/.documentation/commands.html +5 -1
- package/.documentation/efcore.html +5 -1
- package/.documentation/gitflow.html +5 -1
- package/.documentation/hooks.html +5 -1
- package/.documentation/index.html +60 -2
- package/.documentation/init.html +5 -1
- package/.documentation/installation.html +5 -1
- package/.documentation/ralph-loop.html +365 -216
- package/.documentation/test-web.html +5 -1
- package/package.json +1 -1
- package/templates/agents/ba-writer.md +142 -15
- package/templates/skills/apex/SKILL.md +7 -1
- package/templates/skills/apex/_shared.md +49 -4
- package/templates/skills/{ralph-loop → apex}/references/core-seed-data.md +20 -11
- package/templates/skills/{ralph-loop → apex}/references/error-classification.md +2 -1
- package/templates/skills/apex/references/post-checks.md +238 -3
- package/templates/skills/apex/references/smartstack-api.md +47 -7
- package/templates/skills/apex/references/smartstack-frontend.md +47 -1
- package/templates/skills/apex/references/smartstack-layers.md +3 -1
- package/templates/skills/apex/steps/step-00-init.md +48 -1
- package/templates/skills/apex/steps/step-01-analyze.md +37 -0
- package/templates/skills/apex/steps/step-02-plan.md +36 -0
- package/templates/skills/apex/steps/step-03-execute.md +42 -2
- package/templates/skills/apex/steps/step-04-examine.md +110 -2
- package/templates/skills/business-analyse/SKILL.md +29 -19
- package/templates/skills/business-analyse/_module-loop.md +68 -9
- package/templates/skills/business-analyse/_shared.md +71 -21
- package/templates/skills/business-analyse/questionnaire/00-application.md +4 -2
- package/templates/skills/business-analyse/questionnaire/00b-project.md +85 -0
- package/templates/skills/business-analyse/references/deploy-modes.md +69 -0
- package/templates/skills/business-analyse/references/team-orchestration.md +158 -7
- package/templates/skills/business-analyse/schemas/application-schema.json +2 -1
- package/templates/skills/business-analyse/schemas/project-schema.json +490 -0
- package/templates/skills/business-analyse/schemas/sections/metadata-schema.json +2 -1
- package/templates/skills/business-analyse/steps/step-00-init.md +30 -4
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +62 -2
- package/templates/skills/business-analyse/steps/step-01b-applications.md +252 -0
- package/templates/skills/business-analyse/steps/step-02-decomposition.md +23 -6
- package/templates/skills/business-analyse/steps/step-03d-validate.md +27 -6
- package/templates/skills/business-analyse/steps/step-04a-collect.md +111 -0
- package/templates/skills/business-analyse/steps/step-05a-handoff.md +296 -103
- package/templates/skills/business-analyse/steps/step-05b-deploy.md +46 -14
- package/templates/skills/documentation/SKILL.md +92 -2
- package/templates/skills/ralph-loop/SKILL.md +9 -17
- package/templates/skills/ralph-loop/references/category-rules.md +43 -692
- package/templates/skills/ralph-loop/references/compact-loop.md +104 -427
- package/templates/skills/ralph-loop/references/team-orchestration.md +13 -14
- package/templates/skills/ralph-loop/steps/step-02-execute.md +49 -704
- package/templates/skills/ralph-loop/steps/step-03-commit.md +38 -79
- package/templates/skills/ralph-loop/steps/step-04-check.md +39 -58
- package/templates/skills/ralph-loop/steps/step-05-report.md +12 -123
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Compact Loop —
|
|
1
|
+
# Compact Loop — Delegate to /apex
|
|
2
2
|
|
|
3
3
|
> **Loaded by:** step-04 section 5 (after first full iteration)
|
|
4
|
-
> **Purpose:** Execute ALL remaining tasks autonomously
|
|
4
|
+
> **Purpose:** Execute ALL remaining tasks autonomously by delegating to /apex.
|
|
5
5
|
> **EXECUTION GUARANTEE:** This loop runs until ALL tasks are done, max iterations, or dead-end.
|
|
6
6
|
> **ABSOLUTE RULES:**
|
|
7
7
|
> - NEVER stop the loop
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
> - NEVER ask for confirmation
|
|
10
10
|
> - NEVER re-read step files
|
|
11
11
|
> - NEVER pause between iterations
|
|
12
|
+
> - NEVER generate code directly — delegate to /apex
|
|
12
13
|
> - Execute A → B → C → D → back to step-04 section 1 → repeat
|
|
13
14
|
|
|
14
15
|
---
|
|
@@ -26,8 +27,35 @@ Display compact progress:
|
|
|
26
27
|
|
|
27
28
|
```javascript
|
|
28
29
|
const prd = readJSON('.ralph/prd.json');
|
|
30
|
+
const MAX_RETRIES = 3;
|
|
29
31
|
|
|
30
|
-
//
|
|
32
|
+
// RETRY: Reset failed tasks to pending if retries remain (max 3 attempts per task)
|
|
33
|
+
for (const task of prd.tasks) {
|
|
34
|
+
if (task.status !== 'failed') continue;
|
|
35
|
+
const retryCount = task._retryCount || 0;
|
|
36
|
+
if (retryCount < MAX_RETRIES) {
|
|
37
|
+
task.status = 'pending';
|
|
38
|
+
task._retryCount = retryCount + 1;
|
|
39
|
+
task.error = null;
|
|
40
|
+
console.log(`RETRY [${task.id}] attempt ${retryCount + 1}/${MAX_RETRIES}: ${task.description.substring(0, 60)}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Unblock tasks whose dependencies are no longer failed (due to retries above)
|
|
45
|
+
for (const task of prd.tasks) {
|
|
46
|
+
if (task.status !== 'blocked') continue;
|
|
47
|
+
const stillBlocked = task.dependencies.some(depId => {
|
|
48
|
+
const dep = prd.tasks.find(t => t.id === depId);
|
|
49
|
+
return dep && (dep.status === 'failed' || dep.status === 'blocked');
|
|
50
|
+
});
|
|
51
|
+
if (!stillBlocked) {
|
|
52
|
+
task.status = 'pending';
|
|
53
|
+
task.error = null;
|
|
54
|
+
console.log(`UNBLOCKED [${task.id}]: dependencies resolved`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Block pending tasks whose dependencies are still failed/blocked
|
|
31
59
|
for (const task of prd.tasks) {
|
|
32
60
|
if (task.status !== 'pending') continue;
|
|
33
61
|
const depsBlocked = task.dependencies.some(depId => {
|
|
@@ -37,6 +65,8 @@ for (const task of prd.tasks) {
|
|
|
37
65
|
if (depsBlocked) { task.status = 'blocked'; task.error = 'Blocked by failed dependency'; }
|
|
38
66
|
}
|
|
39
67
|
|
|
68
|
+
writeJSON('.ralph/prd.json', prd);
|
|
69
|
+
|
|
40
70
|
// Find ALL eligible tasks (dependencies met)
|
|
41
71
|
const eligible = prd.tasks.filter(task => {
|
|
42
72
|
if (task.status !== 'pending') return false;
|
|
@@ -63,463 +93,110 @@ Batch: {batch.length} [{firstCategory}] → {batch.map(t => `[${t.id}] ${t.descr
|
|
|
63
93
|
|
|
64
94
|
---
|
|
65
95
|
|
|
66
|
-
## B.
|
|
67
|
-
|
|
68
|
-
**For EACH task in batch:**
|
|
69
|
-
|
|
70
|
-
1. Mark `task.status = 'in_progress'`, `task.started_at = now`
|
|
71
|
-
2. **Read `references/category-rules.md`** for category-specific rules
|
|
72
|
-
2bis. **For frontend tasks:** Also read back the original **feature.json** (via `prd.source.featurePath`) to access wireframe `componentMapping`, `columnDefs`, `layout`, `emptyState`, `rowActions`, and `i18n` keys that the PRD does NOT carry forward. The PRD only has the file list — the behavioral spec is in feature.json.
|
|
73
|
-
3. Implement the task following SmartStack conventions
|
|
74
|
-
4. Track `files_created` and `files_modified`
|
|
75
|
-
|
|
76
|
-
**Execution cycle per task:**
|
|
77
|
-
1. Generate code following category-rules.md
|
|
78
|
-
2. **BUILD verification (BLOCKING per task):**
|
|
79
|
-
- Backend (domain/infra/app/api): `dotnet build --no-restore` → MUST exit 0
|
|
80
|
-
- Frontend: `npm run typecheck` → MUST exit 0
|
|
81
|
-
3. If build fails → **CLASSIFY ERROR FIRST** (read `references/error-classification.md`):
|
|
82
|
-
- **Category A/B (assembly/package):** Run `dotnet add package {Name}` → `dotnet restore` → rebuild. Do NOT edit source code.
|
|
83
|
-
- **Category C (DI):** Edit DI registration file only → rebuild.
|
|
84
|
-
- **Category D (migration):** Run migration commands → rebuild.
|
|
85
|
-
- **Category E (config):** Edit config file → rebuild.
|
|
86
|
-
- **Category F (code):** Fix source code → rebuild → loop until pass.
|
|
87
|
-
- **If 2+ rebuild attempts fail on same error and Category F was chosen:** Re-classify — likely wrong category.
|
|
88
|
-
4. **ARTIFACT verification (BLOCKING per task):**
|
|
89
|
-
- `files_created` MUST NOT be empty for code-generating tasks
|
|
90
|
-
- Category-specific checks (see below)
|
|
91
|
-
- IF no artifacts produced → task.status = 'failed', task.error = 'No artifacts created'
|
|
92
|
-
5. Verify acceptance criteria from task description
|
|
93
|
-
6. If failed after 3 attempts: `task.status = 'failed'`, `task.error = reason`, continue to next in batch
|
|
94
|
-
|
|
95
|
-
**Category-specific artifact checks (step 4 above):**
|
|
96
|
-
|
|
97
|
-
| Category | Artifact Check | FAIL if |
|
|
98
|
-
|----------|---------------|---------|
|
|
99
|
-
| `domain` | Entity .cs files exist in Domain/ | 0 files created |
|
|
100
|
-
| `infrastructure` with migration | `Migrations/` folder has .cs files | No migration files |
|
|
101
|
-
| `infrastructure` with seed data | Seed data .cs files exist | 0 files created |
|
|
102
|
-
| `application` | Service + DTO + Validator files exist | 0 files created |
|
|
103
|
-
| `api` | Controller .cs files exist + GetAll supports `?search=` | 0 files created OR GetAll without search param |
|
|
104
|
-
| `frontend` | .tsx page files exist + FK fields use EntityLookup (not plain text) | 0 files created OR FK Guid field as `<input type="text">` |
|
|
105
|
-
| `test` | Test project dir exists AND contains test .cs files | No test project or 0 test files |
|
|
106
|
-
| `validation` | `dotnet test` exit 0 AND `dotnet build` exit 0 | Either command fails |
|
|
107
|
-
|
|
108
|
-
**Category-specific triggers:**
|
|
109
|
-
|
|
110
|
-
| Category | Action |
|
|
111
|
-
|----------|--------|
|
|
112
|
-
| `infrastructure` with `_migrationMeta` | Migration sequence: `suggest_migration` MCP → `dotnet ef migrations add` → cleanup corrupted artifacts (`for d in src/*/bin?Debug; do [ -d "$d" ] && rm -rf "$d"; done`) → `dotnet ef database update` → `dotnet build` |
|
|
113
|
-
| `infrastructure` with seed data keywords | **MANDATORY:** Read `references/core-seed-data.md` → implement templates → `dotnet build` |
|
|
114
|
-
| `frontend` | MCP-first: `scaffold_api_client` → `scaffold_routes` (outputFormat: clientRoutes) → **wire to App.tsx** → **LOAD `/ui-components` skill** before creating ANY page → create pages using skill patterns (SmartTable, EntityCard, CSS variables, StatusBadge) → ALL visible text via `t()` from first line → `npm run typecheck && npm run lint` → **run frontend POST-CHECKs (section 3bis below)** |
|
|
115
|
-
| `test` | **Create tests:** `scaffold_tests` MCP → **Run:** `dotnet test --verbosity normal` → **Fix loop:** if fail → fix SOURCE CODE → rebuild → retest → repeat until 100% pass |
|
|
116
|
-
| `validation` | `dotnet clean && dotnet restore && dotnet build` → `dotnet test` (full) → `validate_conventions` MCP |
|
|
117
|
-
|
|
118
|
-
**Post-batch verification (BLOCKING — ALL must pass before commit):**
|
|
119
|
-
|
|
120
|
-
1. **Backend build:**
|
|
121
|
-
```bash
|
|
122
|
-
dotnet build --no-restore
|
|
123
|
-
```
|
|
124
|
-
If FAIL → fix → rebuild → DO NOT commit until exit 0
|
|
125
|
-
|
|
126
|
-
2. **Tests (if test tasks in batch OR after infrastructure batch):**
|
|
127
|
-
```bash
|
|
128
|
-
TEST_PROJECT="tests/$(basename $(pwd)).Tests.Unit"
|
|
129
|
-
dotnet test "$TEST_PROJECT" --no-build --verbosity normal
|
|
130
|
-
```
|
|
131
|
-
If FAIL → fix source code (NOT tests) → rebuild → retest → loop until 100% pass
|
|
132
|
-
|
|
133
|
-
2bis. **Runtime assembly check (if api/validation tasks in batch):**
|
|
134
|
-
```bash
|
|
135
|
-
API_PROJECT=$(ls src/*Api*/*.csproj 2>/dev/null | head -1)
|
|
136
|
-
if [ -n "$API_PROJECT" ]; then
|
|
137
|
-
dotnet run --project "$API_PROJECT" --urls "http://localhost:5098" > /tmp/ralph-startup-check.log 2>&1 &
|
|
138
|
-
CHECK_PID=$!
|
|
139
|
-
sleep 5
|
|
140
|
-
if ! kill -0 $CHECK_PID 2>/dev/null; then
|
|
141
|
-
CRASH_OUTPUT=$(cat /tmp/ralph-startup-check.log 2>/dev/null)
|
|
142
|
-
echo "RUNTIME ASSEMBLY ERROR DETECTED"
|
|
143
|
-
# Classify per references/error-classification.md
|
|
144
|
-
# If Category A: dotnet add package → rebuild → DO NOT count as iteration failure
|
|
145
|
-
fi
|
|
146
|
-
kill $CHECK_PID 2>/dev/null
|
|
147
|
-
wait $CHECK_PID 2>/dev/null
|
|
148
|
-
rm -f /tmp/ralph-startup-check.log
|
|
149
|
-
fi
|
|
150
|
-
```
|
|
151
|
-
If FAIL → classify error per `references/error-classification.md` → apply fix → rebuild → verify again
|
|
152
|
-
|
|
153
|
-
2ter. **DI completeness (if application tasks in batch):**
|
|
154
|
-
Verify `Application/DependencyInjection.cs` registers all validators and services:
|
|
155
|
-
- Count validators in `Validators/` folder
|
|
156
|
-
- Verify `AddValidatorsFromAssemblyContaining` is present
|
|
157
|
-
- If empty/TODO → FAIL → fix DI registration → rebuild
|
|
158
|
-
|
|
159
|
-
3. **Frontend (if frontend/i18n tasks in batch):**
|
|
160
|
-
```bash
|
|
161
|
-
npm run typecheck && npm run lint
|
|
162
|
-
```
|
|
163
|
-
If FAIL → fix → re-check → loop until pass
|
|
164
|
-
|
|
165
|
-
3bis. **Frontend POST-CHECKs (BLOCKING — if frontend tasks in batch):**
|
|
166
|
-
|
|
167
|
-
> **CRITICAL:** These checks enforce /ui-components compliance, i18n, and route conventions.
|
|
168
|
-
> Without them, generated code silently regresses to raw HTML tables, hardcoded colors, and English-only text.
|
|
169
|
-
|
|
170
|
-
```bash
|
|
171
|
-
# Resolve paths dynamically (handles web/ prefix)
|
|
172
|
-
PAGE_DIR=$(find . -path "*/src/pages" -not -path "*/node_modules/*" -type d 2>/dev/null | head -1)
|
|
173
|
-
HOOK_DIR=$(find . -path "*/src/hooks" -not -path "*/node_modules/*" -type d 2>/dev/null | head -1)
|
|
174
|
-
COMP_DIR=$(find . -path "*/src/components" -not -path "*/node_modules/*" -type d 2>/dev/null | head -1)
|
|
175
|
-
WEB_SRC=$(find . -name "App.tsx" -not -path "*/node_modules/*" -exec dirname {} \; 2>/dev/null | head -1)
|
|
176
|
-
|
|
177
|
-
if [ -n "$PAGE_DIR" ]; then
|
|
178
|
-
# BLOCKING: No raw HTML <table> — MUST use SmartTable/DataTable
|
|
179
|
-
RAW_TABLES=$(grep -rPn '<table[\s>]|<thead[\s>]|<tbody[\s>]' "$PAGE_DIR" --include="*.tsx" 2>/dev/null)
|
|
180
|
-
if [ -n "$RAW_TABLES" ]; then
|
|
181
|
-
echo "BLOCKING: Raw HTML <table> detected — MUST use SmartTable or DataTable"
|
|
182
|
-
echo "$RAW_TABLES" | head -5
|
|
183
|
-
# FIX REQUIRED before commit
|
|
184
|
-
fi
|
|
185
|
-
|
|
186
|
-
# BLOCKING: No hardcoded Tailwind colors — MUST use CSS variables
|
|
187
|
-
HARDCODED_COLORS=$(grep -rPn '(?:bg|text|border|ring)-(?: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}' "$PAGE_DIR" "$COMP_DIR" --include="*.tsx" 2>/dev/null | head -10)
|
|
188
|
-
if [ -n "$HARDCODED_COLORS" ]; then
|
|
189
|
-
echo "BLOCKING: Hardcoded Tailwind colors — MUST use CSS variables (bg-[var(--bg-secondary)])"
|
|
190
|
-
echo "$HARDCODED_COLORS" | head -5
|
|
191
|
-
# FIX REQUIRED before commit
|
|
192
|
-
fi
|
|
193
|
-
|
|
194
|
-
# BLOCKING: No window.confirm() — use ConfirmDialog component
|
|
195
|
-
BAD_CONFIRM=$(grep -rPn 'window\.confirm\s*\(' "$PAGE_DIR" --include="*.tsx" 2>/dev/null | grep -v '//')
|
|
196
|
-
if [ -n "$BAD_CONFIRM" ]; then
|
|
197
|
-
echo "BLOCKING: window.confirm() detected — use ConfirmDialog component with i18n"
|
|
198
|
-
echo "$BAD_CONFIRM"
|
|
199
|
-
# FIX REQUIRED before commit
|
|
200
|
-
fi
|
|
201
|
-
|
|
202
|
-
# BLOCKING: No hardcoded English UI text in JSX
|
|
203
|
-
HARDCODED_TEXT=$(grep -rPn '>\s*(Create|Edit|Delete|Save|Cancel|Search|Loading|Error|No .* found|Are you sure|Submit|Back|Actions|Status)\s*<' "$PAGE_DIR" --include="*.tsx" 2>/dev/null | grep -v test | grep -v '//' | head -10)
|
|
204
|
-
if [ -n "$HARDCODED_TEXT" ]; then
|
|
205
|
-
echo "BLOCKING: Hardcoded English text — ALL visible text MUST use t() from useTranslation()"
|
|
206
|
-
echo "$HARDCODED_TEXT" | head -5
|
|
207
|
-
# FIX REQUIRED before commit
|
|
208
|
-
fi
|
|
209
|
-
|
|
210
|
-
# BLOCKING: No modals/drawers for forms — forms are full pages
|
|
211
|
-
MODAL_IMPORTS=$(grep -rPn "import.*\b(Modal|Dialog|Drawer|Popup|Sheet)\b" "$PAGE_DIR" --include="*.tsx" 2>/dev/null)
|
|
212
|
-
if [ -n "$MODAL_IMPORTS" ]; then
|
|
213
|
-
echo "BLOCKING: Form modals/dialogs detected — forms MUST be full pages with own URL"
|
|
214
|
-
echo "$MODAL_IMPORTS"
|
|
215
|
-
# FIX REQUIRED before commit
|
|
216
|
-
fi
|
|
217
|
-
fi
|
|
218
|
-
|
|
219
|
-
if [ -n "$HOOK_DIR" ]; then
|
|
220
|
-
# BLOCKING: Hooks must use i18n for error messages
|
|
221
|
-
HARDCODED_ERRORS=$(grep -rPn "(setError|throw new Error)\(['\"][A-Z]" "$HOOK_DIR" --include="*.ts" --include="*.tsx" 2>/dev/null | grep -v "t(" | head -10)
|
|
222
|
-
if [ -n "$HARDCODED_ERRORS" ]; then
|
|
223
|
-
echo "BLOCKING: Hardcoded error messages in hooks — MUST use t('{mod}:errors.xxx')"
|
|
224
|
-
echo "$HARDCODED_ERRORS" | head -5
|
|
225
|
-
# FIX REQUIRED before commit
|
|
226
|
-
fi
|
|
227
|
-
fi
|
|
228
|
-
|
|
229
|
-
# BLOCKING: i18n file structure — 4 languages with module-specific JSON files
|
|
230
|
-
if [ -n "$WEB_SRC" ]; then
|
|
231
|
-
I18N_DIR="$WEB_SRC/i18n/locales"
|
|
232
|
-
if [ -d "$I18N_DIR" ]; then
|
|
233
|
-
for LANG in fr en it de; do
|
|
234
|
-
if [ ! -d "$I18N_DIR/$LANG" ]; then
|
|
235
|
-
echo "BLOCKING: Missing i18n locale directory: $I18N_DIR/$LANG"
|
|
236
|
-
# FIX REQUIRED before commit
|
|
237
|
-
fi
|
|
238
|
-
MODULE_JSONS=$(find "$I18N_DIR/$LANG" -name "*.json" ! -name "common.json" ! -name "navigation.json" 2>/dev/null)
|
|
239
|
-
if [ -z "$MODULE_JSONS" ]; then
|
|
240
|
-
echo "BLOCKING: No module translation files in $I18N_DIR/$LANG/"
|
|
241
|
-
# FIX REQUIRED before commit
|
|
242
|
-
fi
|
|
243
|
-
done
|
|
244
|
-
else
|
|
245
|
-
echo "BLOCKING: Missing i18n/locales directory"
|
|
246
|
-
# FIX REQUIRED before commit
|
|
247
|
-
fi
|
|
248
|
-
fi
|
|
249
|
-
|
|
250
|
-
# Cross-validate route conventions (frontend vs backend)
|
|
251
|
-
APP_TSX=$(find . -name "App.tsx" -not -path "*/node_modules/*" 2>/dev/null | head -1)
|
|
252
|
-
API_CONTROLLERS=$(find . -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
|
|
253
|
-
if [ -n "$APP_TSX" ] && [ -n "$API_CONTROLLERS" ]; then
|
|
254
|
-
API_SEGMENTS=$(grep -ohP '\[Route\("api/([^"]+)"\)\]' $API_CONTROLLERS | sed 's/\[Route("api\///;s/")\]//' | tr '/' '\n' | sort -u)
|
|
255
|
-
CLIENT_PATHS=$(grep -ohP "path:\s*['\"]([^'\"]+)['\"]" "$APP_TSX" | sed "s/path:\s*['\"]//;s/['\"]//" | sort -u)
|
|
256
|
-
for SEG in $API_SEGMENTS; do
|
|
257
|
-
if echo "$SEG" | grep -qP '[a-z]+-[a-z]+'; then
|
|
258
|
-
NO_HYPHEN=$(echo "$SEG" | tr -d '-')
|
|
259
|
-
if echo "$CLIENT_PATHS" | grep -q "$NO_HYPHEN"; then
|
|
260
|
-
echo "BLOCKING: Frontend uses '$NO_HYPHEN' but backend uses '$SEG' (kebab-case mismatch)"
|
|
261
|
-
# FIX REQUIRED before commit
|
|
262
|
-
fi
|
|
263
|
-
fi
|
|
264
|
-
done
|
|
265
|
-
fi
|
|
266
|
-
```
|
|
267
|
-
# BLOCKING: No custom entity hooks wrapping services (test-v4-014 root cause)
|
|
268
|
-
if [ -n "$HOOK_DIR" ]; then
|
|
269
|
-
CUSTOM_HOOKS=$(find "$HOOK_DIR" -name "use*.ts" -o -name "use*.tsx" 2>/dev/null | grep -iv "Preferences" | grep -v "node_modules")
|
|
270
|
-
if [ -n "$CUSTOM_HOOKS" ]; then
|
|
271
|
-
echo "BLOCKING: Custom entity hooks detected — pages MUST call services directly"
|
|
272
|
-
echo "$CUSTOM_HOOKS"
|
|
273
|
-
echo "Pattern: page useCallback → service.getAll() → setData(result.items)"
|
|
274
|
-
echo "FORBIDDEN: useEmployees(), useProjects() etc. wrapping services with useState/useEffect"
|
|
275
|
-
# FIX REQUIRED: delete custom hooks, move API calls into page components
|
|
276
|
-
fi
|
|
277
|
-
fi
|
|
278
|
-
|
|
279
|
-
# BLOCKING: No direct array types in services — MUST use PaginatedResult<T>
|
|
280
|
-
if [ -n "$WEB_SRC" ]; then
|
|
281
|
-
SVC_DIR=$(find "$WEB_SRC" -path "*/services" -not -path "*/node_modules/*" -type d 2>/dev/null | head -1)
|
|
282
|
-
if [ -n "$SVC_DIR" ]; then
|
|
283
|
-
DIRECT_ARRAYS=$(grep -rPn 'api\.get<[A-Z]\w+\[\]>' "$SVC_DIR" --include="*.ts" 2>/dev/null)
|
|
284
|
-
if [ -n "$DIRECT_ARRAYS" ]; then
|
|
285
|
-
echo "BLOCKING: Direct array response types — MUST use PaginatedResult<T>"
|
|
286
|
-
echo "$DIRECT_ARRAYS"
|
|
287
|
-
echo "Pattern: api.get<PaginatedResult<Employee>>(url) then .items in page"
|
|
288
|
-
# FIX REQUIRED before commit
|
|
289
|
-
fi
|
|
290
|
-
fi
|
|
291
|
-
fi
|
|
292
|
-
|
|
293
|
-
# BLOCKING: lazy() imports MUST have Suspense boundary in App.tsx
|
|
294
|
-
if [ -n "$APP_TSX" ]; then
|
|
295
|
-
HAS_LAZY=$(grep -c 'lazy(' "$APP_TSX" 2>/dev/null)
|
|
296
|
-
HAS_SUSPENSE=$(grep -c 'Suspense' "$APP_TSX" 2>/dev/null)
|
|
297
|
-
if [ "$HAS_LAZY" -gt 0 ] && [ "$HAS_SUSPENSE" -eq 0 ]; then
|
|
298
|
-
echo "BLOCKING: lazy() imports without <Suspense> boundary in App.tsx"
|
|
299
|
-
echo "Every lazy() page MUST be wrapped: <Suspense fallback={<PageLoader />}>"
|
|
300
|
-
# FIX REQUIRED before commit
|
|
301
|
-
fi
|
|
302
|
-
fi
|
|
303
|
-
|
|
304
|
-
If ANY POST-CHECK fails → fix the code → re-run ALL checks → loop until ALL pass.
|
|
305
|
-
**NEVER commit with failing POST-CHECKs.**
|
|
306
|
-
|
|
307
|
-
3ter. **Dependency audit (if frontend tasks in batch):**
|
|
308
|
-
```bash
|
|
309
|
-
cd {frontend_dir}
|
|
310
|
-
npm ls --depth=0 2>&1 | grep "MISSING" && echo "FAIL: missing deps" && exit 1
|
|
311
|
-
```
|
|
312
|
-
If FAIL → `npm install {missing_packages}` → re-check → loop until pass
|
|
313
|
-
|
|
314
|
-
4. **MCP validation:**
|
|
315
|
-
`mcp__smartstack__validate_conventions` ONCE for the whole batch
|
|
316
|
-
|
|
317
|
-
5. **DB validation (if infrastructure/migration tasks in batch):**
|
|
318
|
-
```bash
|
|
319
|
-
# Quick check: pending model changes after batch?
|
|
320
|
-
INFRA_PROJECT=$(ls src/*Infrastructure*/*.csproj 2>/dev/null | head -1)
|
|
321
|
-
API_PROJECT=$(ls src/*Api*/*.csproj 2>/dev/null | head -1)
|
|
322
|
-
if [ -n "$INFRA_PROJECT" ] && [ -n "$API_PROJECT" ]; then
|
|
323
|
-
dotnet ef migrations has-pending-model-changes \
|
|
324
|
-
--project "$INFRA_PROJECT" \
|
|
325
|
-
--startup-project "$API_PROJECT"
|
|
326
|
-
if [ $? -ne 0 ]; then
|
|
327
|
-
echo "MIGRATION MISSING: Model has uncommitted changes"
|
|
328
|
-
# Create fix task for missing migration
|
|
329
|
-
fi
|
|
330
|
-
|
|
331
|
-
# If batch included migration tasks: verify migration applies on real SQL Server
|
|
332
|
-
DB_NAME="SmartStack_Ralph_Check_${iteration}"
|
|
333
|
-
CONN_STRING="Server=(localdb)\\MSSQLLocalDB;Database=$DB_NAME;Integrated Security=true;TrustServerCertificate=true;Connect Timeout=120;"
|
|
334
|
-
dotnet ef database update \
|
|
335
|
-
--connection "$CONN_STRING" \
|
|
336
|
-
--project "$INFRA_PROJECT" \
|
|
337
|
-
--startup-project "$API_PROJECT"
|
|
338
|
-
if [ $? -ne 0 ]; then
|
|
339
|
-
echo "MIGRATION BROKEN: Cannot apply migrations on SQL Server"
|
|
340
|
-
# Fix migration → rebuild → DO NOT commit until this passes
|
|
341
|
-
fi
|
|
342
|
-
|
|
343
|
-
# Cleanup temp database
|
|
344
|
-
sqlcmd -S "(localdb)\MSSQLLocalDB" -Q "IF DB_ID('$DB_NAME') IS NOT NULL BEGIN ALTER DATABASE [$DB_NAME] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE [$DB_NAME]; END" 2>/dev/null
|
|
345
|
-
fi
|
|
346
|
-
```
|
|
347
|
-
If FAIL → migration is broken → fix → rebuild → DO NOT commit
|
|
348
|
-
|
|
349
|
-
5bis. **Navigation section route CRUD suffix check (BLOCKING — if seed data tasks in batch):**
|
|
350
|
-
```bash
|
|
351
|
-
# Detect /list or /detail/:id in navigation section routes
|
|
352
|
-
SEED_NAV_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*NavigationSeedData.cs" 2>/dev/null)
|
|
353
|
-
if [ -n "$SEED_NAV_FILES" ]; then
|
|
354
|
-
CRUD_ROUTES=$(grep -rn 'Route.*=.*\/list\b\|Route.*=.*\/detail' $SEED_NAV_FILES 2>/dev/null | grep -v '//.*Route')
|
|
355
|
-
if [ -n "$CRUD_ROUTES" ]; then
|
|
356
|
-
echo "BLOCKING: Navigation section routes contain /list or /detail/:id suffixes"
|
|
357
|
-
echo "Convention: list → module route (no suffix), detail → module route + /:id"
|
|
358
|
-
echo "$CRUD_ROUTES"
|
|
359
|
-
# FIX REQUIRED before commit
|
|
360
|
-
fi
|
|
361
|
-
fi
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
5ter. **Core Seed Data Integrity (BLOCKING — if seed data tasks in batch):**
|
|
365
|
-
```bash
|
|
366
|
-
# Verify NavigationApplicationSeedData.cs exists
|
|
367
|
-
APP_SEED=$(find . -path "*/Seeding/Data/NavigationApplicationSeedData.cs" 2>/dev/null | head -1)
|
|
368
|
-
if [ -z "$APP_SEED" ]; then
|
|
369
|
-
echo "❌ BLOCKING: NavigationApplicationSeedData.cs NOT FOUND"
|
|
370
|
-
echo "Without this: nav_Applications empty → menu invisible → modules inaccessible"
|
|
371
|
-
echo "Fix: Generate from core-seed-data.md section 1b"
|
|
372
|
-
# Create fix task
|
|
373
|
-
fi
|
|
374
|
-
|
|
375
|
-
# Verify no hardcoded placeholders in provider
|
|
376
|
-
PROVIDER=$(find . -path "*/Seeding/*SeedDataProvider.cs" 2>/dev/null | head -1)
|
|
377
|
-
if [ -n "$PROVIDER" ]; then
|
|
378
|
-
if grep -qE '\{appLabel_|\{appDesc_|\{appIcon\}|\{ApplicationGuid\}' "$PROVIDER" 2>/dev/null; then
|
|
379
|
-
echo "❌ BLOCKING: SeedDataProvider has unresolved placeholders"
|
|
380
|
-
echo "Fix: Use NavigationApplicationSeedData.GetApplicationEntry()"
|
|
381
|
-
fi
|
|
382
|
-
fi
|
|
383
|
-
|
|
384
|
-
# Verify ApplicationRolesSeedData references real GUID
|
|
385
|
-
ROLES_SEED=$(find . -path "*/Seeding/Data/ApplicationRolesSeedData.cs" 2>/dev/null | head -1)
|
|
386
|
-
if [ -n "$ROLES_SEED" ]; then
|
|
387
|
-
if grep -q '{ApplicationGuid}' "$ROLES_SEED" 2>/dev/null; then
|
|
388
|
-
echo "❌ BLOCKING: ApplicationRolesSeedData still has {ApplicationGuid} placeholder"
|
|
389
|
-
echo "Fix: Replace with NavigationApplicationSeedData.ApplicationId"
|
|
390
|
-
fi
|
|
391
|
-
fi
|
|
392
|
-
|
|
393
|
-
# Quick startup test to verify seed data runs without exceptions
|
|
394
|
-
API_PROJECT=$(ls src/*Api*/*.csproj 2>/dev/null | head -1)
|
|
395
|
-
if [ -n "$API_PROJECT" ] && [ -n "$APP_SEED" ]; then
|
|
396
|
-
echo "Running seed data startup test..."
|
|
397
|
-
dotnet run --project "$API_PROJECT" --urls "http://localhost:0" -- --environment Development > /tmp/ralph-seed-check.log 2>&1 &
|
|
398
|
-
SEED_PID=$!
|
|
399
|
-
sleep 10
|
|
400
|
-
if ! kill -0 $SEED_PID 2>/dev/null; then
|
|
401
|
-
SEED_LOG=$(cat /tmp/ralph-seed-check.log 2>/dev/null | tail -20)
|
|
402
|
-
echo "❌ BLOCKING: Application crashed during startup — seed data likely failed"
|
|
403
|
-
echo "Last 20 lines: $SEED_LOG"
|
|
404
|
-
# Do NOT continue to frontend/test — fix seed data first
|
|
405
|
-
else
|
|
406
|
-
echo "✓ Seed data startup test passed"
|
|
407
|
-
kill $SEED_PID 2>/dev/null
|
|
408
|
-
wait $SEED_PID 2>/dev/null
|
|
409
|
-
fi
|
|
410
|
-
rm -f /tmp/ralph-seed-check.log
|
|
411
|
-
fi
|
|
412
|
-
```
|
|
413
|
-
If FAIL → fix seed data → rebuild → DO NOT continue to frontend/test tasks
|
|
414
|
-
|
|
415
|
-
**Error resolution cycle:**
|
|
416
|
-
1. Read the FULL error output (not just first line)
|
|
417
|
-
2. Identify root cause: file path + line number
|
|
418
|
-
3. Fix the issue in source code
|
|
419
|
-
4. Rebuild/retest
|
|
420
|
-
5. If still failing → repeat from step 1
|
|
421
|
-
6. **NEVER** commit with failing build or tests
|
|
96
|
+
## B. Delegate Batch to /apex
|
|
422
97
|
|
|
423
|
-
|
|
98
|
+
> **Ralph NEVER generates code.** All code generation, MCP calls, POST-CHECKs,
|
|
99
|
+
> and build verification are handled by apex.
|
|
100
|
+
|
|
101
|
+
### B1. Mark Batch In-Progress
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
for (const task of batch) {
|
|
105
|
+
task.status = 'in_progress';
|
|
106
|
+
task.started_at = new Date().toISOString();
|
|
107
|
+
}
|
|
108
|
+
writeJSON('.ralph/prd.json', prd);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### B2. Invoke /apex
|
|
424
112
|
|
|
425
|
-
|
|
113
|
+
**INVOKE `/apex -d .ralph/prd.json`**
|
|
426
114
|
|
|
427
|
-
|
|
115
|
+
Apex handles everything for the current module:
|
|
116
|
+
- Reads PRD, extracts context (context_code, app_name, module_code, entities, sections)
|
|
117
|
+
- Executes ALL layers: domain → infrastructure → migration → application → api → seed data → frontend → tests
|
|
118
|
+
- Runs full POST-CHECKs (43 checks from `references/post-checks.md`)
|
|
119
|
+
- Commits per layer (atomic commits)
|
|
120
|
+
- Validates with MCP (`validate_conventions`)
|
|
121
|
+
- Updates task statuses in the PRD file directly
|
|
428
122
|
|
|
429
|
-
> **
|
|
430
|
-
|
|
431
|
-
|
|
123
|
+
> **FLAGS:** `-d` implies `-a` (auto, no user confirmation) and `-e` (economy, no nested teams).
|
|
124
|
+
|
|
125
|
+
### B3. Verify Post-Apex Results
|
|
432
126
|
|
|
433
127
|
```javascript
|
|
434
|
-
|
|
435
|
-
const
|
|
128
|
+
// Re-read PRD after apex execution
|
|
129
|
+
const updatedPrd = readJSON('.ralph/prd.json');
|
|
130
|
+
const batchIds = batch.map(t => t.id);
|
|
436
131
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
132
|
+
const completed = updatedPrd.tasks.filter(t => batchIds.includes(t.id) && t.status === 'completed').length;
|
|
133
|
+
const failed = updatedPrd.tasks.filter(t => batchIds.includes(t.id) && t.status === 'failed').length;
|
|
134
|
+
const pending = updatedPrd.tasks.filter(t => batchIds.includes(t.id) && t.status === 'pending').length;
|
|
135
|
+
|
|
136
|
+
if (failed > 0) {
|
|
137
|
+
console.log(`WARNING: ${failed} tasks failed during apex execution`);
|
|
138
|
+
// Failed tasks will be retried in next loop iteration if retries remain
|
|
442
139
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
140
|
+
|
|
141
|
+
if (pending > 0) {
|
|
142
|
+
console.log(`INFO: ${pending} tasks still pending — apex may not have reached them`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log(`Apex completed: ${completed}/${batchIds.length} tasks`);
|
|
446
146
|
```
|
|
447
147
|
|
|
448
|
-
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## C. Commit PRD State
|
|
151
|
+
|
|
152
|
+
### C1. Update Progress File (MANDATORY)
|
|
449
153
|
|
|
450
154
|
```javascript
|
|
451
|
-
const
|
|
452
|
-
const
|
|
453
|
-
const
|
|
155
|
+
const prdCheck = readJSON('.ralph/prd.json');
|
|
156
|
+
const totalCompleted = prdCheck.tasks.filter(t => t.status === 'completed').length;
|
|
157
|
+
const total = prdCheck.tasks.length;
|
|
454
158
|
|
|
455
|
-
// Append to progress.txt (NOT overwrite)
|
|
456
159
|
appendFile('.ralph/progress.txt',
|
|
457
|
-
`Iteration ${
|
|
458
|
-
`${
|
|
459
|
-
` Files: ${batch.reduce((acc, t) => acc + (t.files_changed?.created?.length || 0), 0)} created\n`
|
|
160
|
+
`Iteration ${prdCheck.config.current_iteration}: Delegated to /apex — ` +
|
|
161
|
+
`${completed}/${batchIds.length} batch tasks completed. ${totalCompleted}/${total} overall.\n`
|
|
460
162
|
);
|
|
461
163
|
```
|
|
462
164
|
|
|
463
|
-
###
|
|
165
|
+
### C2. Increment Iteration
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
prdCheck.config.current_iteration++;
|
|
169
|
+
prdCheck.updated_at = new Date().toISOString();
|
|
170
|
+
writeJSON('.ralph/prd.json', prdCheck);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### C3. Git Commit (PRD state only — apex already committed code)
|
|
464
174
|
|
|
465
175
|
```bash
|
|
466
|
-
# Stage all changed files from batch
|
|
467
|
-
git add {all_files_from_batch}
|
|
468
176
|
git add .ralph/prd.json .ralph/progress.txt
|
|
469
177
|
[ -f .ralph/modules-queue.json ] && git add .ralph/modules-queue.json
|
|
470
178
|
|
|
471
179
|
git commit -m "$(cat <<'EOF'
|
|
472
|
-
|
|
180
|
+
chore(ralph): update PRD state — iteration {iteration}
|
|
473
181
|
|
|
474
|
-
Tasks: {
|
|
475
|
-
|
|
476
|
-
{module ? "Module: " + module : ""}
|
|
182
|
+
Tasks: {completed}/{batch_size} completed via /apex
|
|
183
|
+
Overall: {totalCompleted}/{total}
|
|
477
184
|
|
|
478
185
|
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
479
186
|
EOF
|
|
480
187
|
)"
|
|
481
|
-
|
|
482
|
-
COMMIT_HASH=$(git rev-parse --short HEAD)
|
|
483
188
|
```
|
|
484
189
|
|
|
485
|
-
### C4.
|
|
190
|
+
### C4. PRD Sync Verification (HARD CHECK)
|
|
486
191
|
|
|
487
192
|
```javascript
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
if (task.commit_hash === 'pending-commit') task.commit_hash = COMMIT_HASH;
|
|
491
|
-
}
|
|
492
|
-
prd.history.push({
|
|
493
|
-
iteration: prd.config.current_iteration - 1,
|
|
494
|
-
task_ids: batch.map(t => t.id),
|
|
495
|
-
action: 'batch-completed',
|
|
496
|
-
timestamp: now,
|
|
497
|
-
commit_hash: COMMIT_HASH,
|
|
498
|
-
notes: "{summary}"
|
|
499
|
-
});
|
|
500
|
-
writeJSON('.ralph/prd.json', prd);
|
|
501
|
-
```
|
|
193
|
+
const prdVerify = readJSON('.ralph/prd.json');
|
|
194
|
+
const actuallyCompleted = prdVerify.tasks.filter(t => batchIds.includes(t.id) && t.status === 'completed');
|
|
502
195
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
```javascript
|
|
508
|
-
const prdCheck = readJSON('.ralph/prd.json');
|
|
509
|
-
const batchIds = batch.map(t => t.id);
|
|
510
|
-
const actuallyCompleted = prdCheck.tasks.filter(t => batchIds.includes(t.id) && t.status === 'completed');
|
|
511
|
-
|
|
512
|
-
if (actuallyCompleted.length !== batch.filter(t => t.status !== 'failed').length) {
|
|
513
|
-
console.error('PRD SYNC ERROR: Tasks executed but not marked completed in prd.json');
|
|
514
|
-
console.error(`Expected: ${batch.filter(t => t.status !== 'failed').length} completed, Got: ${actuallyCompleted.length}`);
|
|
515
|
-
// Force re-write
|
|
516
|
-
for (const task of batch) {
|
|
517
|
-
const prdTask = prdCheck.tasks.find(t => t.id === task.id);
|
|
518
|
-
if (prdTask && task.status === 'completed') prdTask.status = 'completed';
|
|
519
|
-
if (prdTask) prdTask.commit_hash = COMMIT_HASH;
|
|
520
|
-
}
|
|
521
|
-
writeJSON('.ralph/prd.json', prdCheck);
|
|
522
|
-
console.log('PRD SYNC REPAIRED');
|
|
196
|
+
if (actuallyCompleted.length !== completed) {
|
|
197
|
+
console.error('PRD SYNC ERROR: Mismatch between apex results and prd.json');
|
|
198
|
+
// Force re-read — apex may have written after our read
|
|
199
|
+
// DO NOT overwrite — apex is the source of truth for task statuses
|
|
523
200
|
}
|
|
524
201
|
```
|
|
525
202
|
|
|
@@ -82,29 +82,28 @@ for (const layer of layers) {
|
|
|
82
82
|
|
|
83
83
|
## 4. Teammate Prompt Template
|
|
84
84
|
|
|
85
|
-
Each teammate receives a self-contained prompt
|
|
85
|
+
Each teammate receives a self-contained prompt that delegates to `/apex`:
|
|
86
86
|
|
|
87
87
|
```
|
|
88
88
|
You are a Ralph Loop module worker for module "${moduleCode}".
|
|
89
89
|
|
|
90
90
|
## Your Mission
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
Execute ALL tasks in the PRD by delegating to /apex.
|
|
92
|
+
You are an ORCHESTRATOR — you NEVER generate code directly.
|
|
93
93
|
|
|
94
|
-
## PRD
|
|
95
|
-
${
|
|
94
|
+
## PRD File
|
|
95
|
+
${prdFile}
|
|
96
96
|
|
|
97
|
-
## Execution
|
|
98
|
-
1.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
7. Commit after each batch: git add + git commit
|
|
97
|
+
## Execution
|
|
98
|
+
1. INVOKE `/apex -d ${prdFile}`
|
|
99
|
+
→ Apex handles all layers: domain → infrastructure → migration → seed data → application → api → frontend → tests
|
|
100
|
+
→ Apex runs POST-CHECKs, MCP validation, build verification, and commits per layer
|
|
101
|
+
→ Apex updates task statuses in the PRD file directly
|
|
102
|
+
2. After apex returns, re-read ${prdFile} to check task statuses
|
|
103
|
+
3. If tasks remain pending/failed, re-invoke `/apex -d ${prdFile}`
|
|
105
104
|
|
|
106
105
|
## Communication Protocol
|
|
107
|
-
- After
|
|
106
|
+
- After apex completes and build passes:
|
|
108
107
|
SendMessage({ type: "message", recipient: "team-lead", content: "LAYER_READY:${moduleCode}", summary: "${moduleCode} foundation ready" })
|
|
109
108
|
- When ALL tasks complete:
|
|
110
109
|
SendMessage({ type: "message", recipient: "team-lead", content: "MODULE_COMPLETE:${moduleCode}", summary: "${moduleCode} complete" })
|