@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.
- 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 +414 -1
- package/.documentation/installation.html +5 -1
- package/.documentation/ralph-loop.html +365 -216
- package/.documentation/test-web.html +5 -1
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +7 -24
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -2
- package/templates/agents/ba-writer.md +142 -15
- package/templates/mcp-scaffolding/controller.cs.hbs +5 -1
- package/templates/skills/apex/SKILL.md +9 -3
- 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 +463 -3
- package/templates/skills/apex/references/smartstack-api.md +76 -8
- package/templates/skills/apex/references/smartstack-frontend.md +74 -1
- package/templates/skills/apex/references/smartstack-layers.md +21 -3
- package/templates/skills/apex/steps/step-00-init.md +121 -1
- package/templates/skills/apex/steps/step-01-analyze.md +58 -0
- package/templates/skills/apex/steps/step-02-plan.md +36 -0
- package/templates/skills/apex/steps/step-03-execute.md +114 -7
- package/templates/skills/apex/steps/step-04-examine.md +116 -2
- package/templates/skills/business-analyse/SKILL.md +31 -20
- package/templates/skills/business-analyse/_module-loop.md +68 -9
- package/templates/skills/business-analyse/_shared.md +80 -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 +15 -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 +220 -38
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +184 -5
- package/templates/skills/business-analyse/steps/step-01b-applications.md +423 -0
- package/templates/skills/business-analyse/steps/step-02-decomposition.md +23 -6
- package/templates/skills/business-analyse/steps/step-03c-compile.md +14 -2
- package/templates/skills/business-analyse/steps/step-03d-validate.md +32 -7
- 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 +14 -17
- package/templates/skills/ralph-loop/references/category-rules.md +63 -683
- package/templates/skills/ralph-loop/references/compact-loop.md +188 -428
- package/templates/skills/ralph-loop/references/section-splitting.md +439 -0
- package/templates/skills/ralph-loop/references/team-orchestration.md +13 -14
- package/templates/skills/ralph-loop/steps/step-01-task.md +27 -0
- package/templates/skills/ralph-loop/steps/step-02-execute.md +80 -691
- 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 +31 -123
- package/scripts/health-check.sh +0 -168
- package/scripts/postinstall.js +0 -18
|
@@ -187,11 +187,38 @@ t('common:actions.save', 'Save')
|
|
|
187
187
|
t('common:errors.network', 'Network error')
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
+
### Namespace Registration (CRITICAL)
|
|
191
|
+
|
|
192
|
+
> **After creating i18n JSON files, you MUST register each namespace in the i18n config.**
|
|
193
|
+
> Root cause (test-apex-007): JSON files existed but namespaces were not registered → `useTranslation(['module'])` returned empty strings.
|
|
194
|
+
|
|
195
|
+
In the i18n config file (`src/i18n/config.ts` or `src/i18n/index.ts`), add each new namespace:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// Example: registering new module namespaces
|
|
199
|
+
import employees from './locales/fr/employees.json';
|
|
200
|
+
import projects from './locales/fr/projects.json';
|
|
201
|
+
import clients from './locales/fr/clients.json';
|
|
202
|
+
|
|
203
|
+
// In resources configuration:
|
|
204
|
+
resources: {
|
|
205
|
+
fr: { employees, projects, clients, common, navigation },
|
|
206
|
+
en: { employees: employeesEn, projects: projectsEn, clients: clientsEn, ... },
|
|
207
|
+
// ... it, de
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// OR with ns array:
|
|
211
|
+
ns: ['common', 'navigation', 'employees', 'projects', 'clients'],
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
POST-CHECK 45 validates this. Unregistered namespaces → BLOCKING.
|
|
215
|
+
|
|
190
216
|
### Rules
|
|
191
217
|
|
|
192
218
|
- **ALWAYS** provide a fallback value as 2nd argument to `t()`
|
|
193
219
|
- **ALWAYS** use namespace prefix: `t('namespace:key')`
|
|
194
220
|
- **ALWAYS** generate 4 language files (fr, en, it, de) with identical key structures
|
|
221
|
+
- **ALWAYS** register new namespaces in i18n config file after creating JSON files
|
|
195
222
|
- **NEVER** hardcode user-facing strings in JSX
|
|
196
223
|
- **NEVER** use `t('key')` without namespace prefix
|
|
197
224
|
|
|
@@ -325,7 +352,7 @@ export function EntityDetailPage() {
|
|
|
325
352
|
useEffect(() => {
|
|
326
353
|
if (!visitedTabsRef.current.has(activeTab)) {
|
|
327
354
|
visitedTabsRef.current.add(activeTab);
|
|
328
|
-
// Load tab-specific data here
|
|
355
|
+
// Load tab-specific data here (e.g., fetch leaves for this employee)
|
|
329
356
|
}
|
|
330
357
|
}, [activeTab]);
|
|
331
358
|
|
|
@@ -336,6 +363,52 @@ export function EntityDetailPage() {
|
|
|
336
363
|
}
|
|
337
364
|
```
|
|
338
365
|
|
|
366
|
+
### Tab Behavior Rules (CRITICAL)
|
|
367
|
+
|
|
368
|
+
> **CRITICAL: Tabs on detail pages switch content LOCALLY — they NEVER navigate to other pages.**
|
|
369
|
+
> Each tab renders its content INLINE within the same page component.
|
|
370
|
+
> Sub-resource data (e.g., an employee's leaves) loads via API call filtered by the parent entity ID.
|
|
371
|
+
|
|
372
|
+
**Tab state management:**
|
|
373
|
+
- Tabs use `useState<TabKey>('info')` for the active tab — LOCAL React state only
|
|
374
|
+
- Tab click handler: `onClick={() => setActiveTab(tabKey)}` — NEVER `navigate()`
|
|
375
|
+
- Tab content: conditional rendering `{activeTab === 'tabKey' && <TabContent />}`
|
|
376
|
+
- Lazy loading: `visitedTabsRef` tracks which tabs have been visited to avoid redundant API calls
|
|
377
|
+
|
|
378
|
+
**Tab content for sub-resources:**
|
|
379
|
+
```tsx
|
|
380
|
+
// CORRECT — sub-resource data loaded INLINE within the tab
|
|
381
|
+
{activeTab === 'leaves' && (
|
|
382
|
+
<div>
|
|
383
|
+
<LeaveRequestsTable employeeId={entity.id} />
|
|
384
|
+
{/* Optional "View all" link INSIDE the tab content area */}
|
|
385
|
+
<Link to={`../leaves?employee=${entity.id}`}>
|
|
386
|
+
{t('employees:tabs.viewAllLeaves', 'View all leave requests')}
|
|
387
|
+
</Link>
|
|
388
|
+
</div>
|
|
389
|
+
)}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**FORBIDDEN tab patterns:**
|
|
393
|
+
```tsx
|
|
394
|
+
// FORBIDDEN — tab click handler navigates to another page
|
|
395
|
+
const handleTabClick = (tab: TabKey) => {
|
|
396
|
+
setActiveTab(tab);
|
|
397
|
+
if (tab === 'leaves') navigate(`../leaves?employee=${id}`); // ← BREAKS tab UX
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
// FORBIDDEN — tab content is empty because navigation already left the page
|
|
401
|
+
{activeTab === 'info' && <div>...</div>}
|
|
402
|
+
// Leaves tab: nothing renders here, user is already on another page
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Why this matters:**
|
|
406
|
+
- Navigating away loses the detail page context (entity data, scroll position, other tab state)
|
|
407
|
+
- Users expect tabs to switch content in-place, not redirect to a different page
|
|
408
|
+
- The browser back button should go to the list page, not toggle between tabs
|
|
409
|
+
|
|
410
|
+
**POST-CHECK 43 enforces this rule.**
|
|
411
|
+
|
|
339
412
|
---
|
|
340
413
|
|
|
341
414
|
## 3b. Form Pages Pattern (Create / Edit)
|
|
@@ -51,6 +51,11 @@
|
|
|
51
51
|
|
|
52
52
|
**BLOCKING:** `dotnet build --no-restore` MUST pass after migration.
|
|
53
53
|
|
|
54
|
+
> **CRITICAL — Migration must cover ALL entities (POST-CHECK 44, 49):**
|
|
55
|
+
> Create/update migration AFTER ALL entities and EF configs are registered in DbContext.
|
|
56
|
+
> Verify with `dotnet ef migrations has-pending-model-changes` — must report NO pending changes.
|
|
57
|
+
> If entities are added incrementally across modules, create a NEW migration for each batch.
|
|
58
|
+
|
|
54
59
|
---
|
|
55
60
|
|
|
56
61
|
## Layer 1 — Application (parallel with API)
|
|
@@ -75,10 +80,13 @@
|
|
|
75
80
|
- **ALL services MUST inject `ICurrentUserService`** for audit trails
|
|
76
81
|
- **ALL services MUST inject `ILogger<T>`** for production diagnostics
|
|
77
82
|
- CQRS with MediatR
|
|
78
|
-
- FluentValidation for all commands
|
|
83
|
+
- FluentValidation for all commands — **MUST register validators via DI:**
|
|
84
|
+
`services.AddValidatorsFromAssemblyContaining<Create{Entity}DtoValidator>();`
|
|
85
|
+
Without DI registration, `[FromBody]` DTOs are never validated (POST-CHECK 46)
|
|
86
|
+
- **Date fields in DTOs MUST use `DateOnly`**, not `string` (POST-CHECK 47). See `smartstack-api.md` DTO Type Mapping.
|
|
79
87
|
- DTOs separate from domain entities
|
|
80
88
|
- Service interfaces in Application, implementations in Infrastructure
|
|
81
|
-
- **FORBIDDEN:** `tenantId: Guid.Empty`, `TenantId!.Value`, queries without TenantId filter, `ICurrentUser` (does not exist — use `ICurrentUserService` + `ICurrentTenantService`)
|
|
89
|
+
- **FORBIDDEN:** `tenantId: Guid.Empty`, `TenantId!.Value`, queries without TenantId filter, `ICurrentUser` (does not exist — use `ICurrentUserService` + `ICurrentTenantService`), `string` type for date fields
|
|
82
90
|
|
|
83
91
|
---
|
|
84
92
|
|
|
@@ -101,6 +109,16 @@
|
|
|
101
109
|
|
|
102
110
|
**Rules:**
|
|
103
111
|
- `[RequirePermission(Permissions.{Module}.{Action})]` on EVERY endpoint
|
|
112
|
+
- **NavRoute values MUST use kebab-case for ALL multi-word segments:**
|
|
113
|
+
- `[NavRoute("business.human-resources.employees")]` (CORRECT)
|
|
114
|
+
- `[NavRoute("business.humanresources.employees")]` (WRONG — missing hyphens)
|
|
115
|
+
- `[NavRoute("business.project-management.projects")]` (CORRECT)
|
|
116
|
+
- `[NavRoute("business.projectmanagement.projects")]` (WRONG)
|
|
117
|
+
- Permission paths MUST use kebab-case matching NavRoute codes (e.g., `business.human-resources.employees.read`)
|
|
118
|
+
- FORBIDDEN: concatenated segments like `humanresources` — must be `human-resources`
|
|
119
|
+
- POST-CHECK 48 detects non-kebab-case NavRoute values. POST-CHECK 41 detects non-kebab-case permissions.
|
|
120
|
+
- **FORBIDDEN:** `[Route("api/...")]` alongside `[NavRoute]` — causes 404s (POST-CHECK 50)
|
|
121
|
+
- `[NavRoute]` is the ONLY route attribute needed — resolves routes from DB at startup
|
|
104
122
|
- NEVER use `[Authorize]` without specific permission
|
|
105
123
|
- Swagger XML documentation
|
|
106
124
|
- Return DTOs, never domain entities
|
|
@@ -178,7 +196,7 @@ private static Guid GenerateDeterministicGuid(string input)
|
|
|
178
196
|
|
|
179
197
|
| Level | Code (C#) | Route (DB) |
|
|
180
198
|
|-------|-----------|-----------|
|
|
181
|
-
| Application | `
|
|
199
|
+
| Application | `human-resources` | `/business/human-resources` |
|
|
182
200
|
| Module | `employees` | `/business/human-resources/employees` |
|
|
183
201
|
| Section | `departments` | `/business/human-resources/employees/departments` |
|
|
184
202
|
| Resource | `export` | `/business/human-resources/employees/departments/export` |
|
|
@@ -28,7 +28,7 @@ Extract flags from the raw input:
|
|
|
28
28
|
```
|
|
29
29
|
Input: "{raw_input}"
|
|
30
30
|
|
|
31
|
-
Flags to detect: -a, -x, -s, -e, -r, -pr
|
|
31
|
+
Flags to detect: -a, -x, -s, -e, -r, -pr, -d {path}
|
|
32
32
|
Remaining text after flag removal = {task_description}
|
|
33
33
|
```
|
|
34
34
|
|
|
@@ -41,6 +41,53 @@ save_mode: false
|
|
|
41
41
|
economy_mode: false
|
|
42
42
|
pr_mode: false
|
|
43
43
|
resume_mode: false
|
|
44
|
+
delegate_mode: false
|
|
45
|
+
delegate_prd_path: null
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**If `-d {path}` detected:** set `delegate_mode = true`, `delegate_prd_path = {path}`, force `auto_mode = true`, `economy_mode = true`.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 1b. Delegate Mode Fast Path (if -d)
|
|
53
|
+
|
|
54
|
+
> **When `/ralph-loop` invokes `/apex -d {prd_path}`, all context is extracted from the PRD file.**
|
|
55
|
+
> Skip interactive sections (hierarchy definition, challenge questions).
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
IF delegate_mode:
|
|
59
|
+
Read prd.json from {delegate_prd_path}
|
|
60
|
+
|
|
61
|
+
Extract context:
|
|
62
|
+
{context_code} = prd.project.context (e.g., "business")
|
|
63
|
+
{app_name} = prd.project.application (e.g., "HumanResources")
|
|
64
|
+
{module_code} = prd.project.module (e.g., "EmployeeManagement")
|
|
65
|
+
{prd_path} = {delegate_prd_path}
|
|
66
|
+
{feature_path} = prd.source.feature_json_path (if exists)
|
|
67
|
+
|
|
68
|
+
Extract sections from infrastructure tasks with _seedDataMeta:
|
|
69
|
+
Find task where _seedDataMeta.coreSeedData.navigationSections exists
|
|
70
|
+
{sections} = _seedDataMeta.coreSeedData.navigationSections[]
|
|
71
|
+
→ map to: { code, label, description, route, displayOrder, navigation }
|
|
72
|
+
|
|
73
|
+
Extract entities from domain tasks:
|
|
74
|
+
{entities} = prd.tasks.filter(t => t.category == 'domain').map(t => entity name from t.description)
|
|
75
|
+
|
|
76
|
+
Extract complexity:
|
|
77
|
+
{module_complexity} = heuristic from task count:
|
|
78
|
+
<= 10 tasks → "simple-crud"
|
|
79
|
+
<= 20 tasks → "crud-rules"
|
|
80
|
+
<= 30 tasks → "crud-workflow"
|
|
81
|
+
> 30 tasks → "complex"
|
|
82
|
+
|
|
83
|
+
Set needs:
|
|
84
|
+
{needs_seed_data} = prd.tasks.some(t => t.category == 'seedData' || t.category == 'infrastructure')
|
|
85
|
+
{needs_migration} = prd.tasks.some(t => t._migrationMeta != null)
|
|
86
|
+
{has_dependencies} = prd.tasks.some(t => t.dependencies.length > 0) ? "references" : "none"
|
|
87
|
+
{key_properties} = [] (extracted during step-01 from feature.json if available)
|
|
88
|
+
|
|
89
|
+
SKIP sections 2 (detect context), 4 (hierarchy), 5 (challenge questions)
|
|
90
|
+
Jump to section 3 (MCP verify) → then section 6 (determine needs, already set) → section 9 (summary)
|
|
44
91
|
```
|
|
45
92
|
|
|
46
93
|
---
|
|
@@ -147,6 +194,79 @@ These values are propagated to:
|
|
|
147
194
|
|
|
148
195
|
---
|
|
149
196
|
|
|
197
|
+
## 5d. Scope Complexity Guard (BLOCKING)
|
|
198
|
+
|
|
199
|
+
> **Root cause (test-apex-007):** `/apex` was invoked with 3 applications and 7 entities.
|
|
200
|
+
> The model's context window saturated, causing: incomplete migrations, lost conventions,
|
|
201
|
+
> missing pages, unregistered i18n, forgotten DI registrations.
|
|
202
|
+
>
|
|
203
|
+
> `/apex` is designed for **incremental** development (1 module at a time).
|
|
204
|
+
> Multi-module projects should use `/ralph-loop` which calls `/apex -d` per module.
|
|
205
|
+
|
|
206
|
+
### Scope Detection
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
entity_count = {entities}.length
|
|
210
|
+
section_count = {sections}.length
|
|
211
|
+
app_count = number of DISTINCT applications detected in {task_description}
|
|
212
|
+
(heuristics: "application X et application Y", "module X, module Y, module Z"
|
|
213
|
+
with different app names, "3 apps", etc.)
|
|
214
|
+
|
|
215
|
+
scope_score = entity_count + (section_count * 0.5) + (app_count * 3)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Guard Rules
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
IF delegate_mode (-d flag):
|
|
222
|
+
→ SKIP guard (ralph-loop already handles scope per module)
|
|
223
|
+
|
|
224
|
+
ELSE IF app_count > 1:
|
|
225
|
+
→ BLOCKING: "Multiple applications detected ({app_count} apps, {entity_count} entities).
|
|
226
|
+
/apex handles 1 module at a time. For multi-module projects, use:
|
|
227
|
+
- /ralph-loop (automated: reads PRD, generates all modules sequentially)
|
|
228
|
+
- Multiple /apex calls (manual: one /apex per module)
|
|
229
|
+
|
|
230
|
+
To override this guard, split your request:
|
|
231
|
+
/apex -e add HR employee management
|
|
232
|
+
/apex -e add CRM client management
|
|
233
|
+
/apex -e add Project management"
|
|
234
|
+
|
|
235
|
+
ELSE IF scope_score > 8:
|
|
236
|
+
→ WARNING: "Large scope detected ({entity_count} entities, {section_count} sections).
|
|
237
|
+
/apex works best with ≤ 4 entities per invocation.
|
|
238
|
+
Consider splitting into smaller iterations.
|
|
239
|
+
Proceeding, but expect higher risk of omissions."
|
|
240
|
+
|
|
241
|
+
AskUserQuestion:
|
|
242
|
+
header: "Scope"
|
|
243
|
+
question: "Le périmètre est large ({entity_count} entités). Réduire le scope ou continuer ?"
|
|
244
|
+
options:
|
|
245
|
+
- label: "Continue anyway"
|
|
246
|
+
description: "Proceed with full scope (higher risk of omissions)"
|
|
247
|
+
- label: "Split into iterations"
|
|
248
|
+
description: "Focus on the first module/app, then run /apex again for the rest"
|
|
249
|
+
- label: "Use /ralph-loop"
|
|
250
|
+
description: "Switch to /ralph-loop for automated multi-module orchestration"
|
|
251
|
+
|
|
252
|
+
IF "Split" → ask user which module to focus on first, update {entities}/{sections}
|
|
253
|
+
IF "/ralph-loop" → display command to run, STOP execution
|
|
254
|
+
|
|
255
|
+
ELSE:
|
|
256
|
+
→ PASS (scope is manageable)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Recommended Scope per /apex Invocation
|
|
260
|
+
|
|
261
|
+
| Metric | Safe | Warning | Blocking |
|
|
262
|
+
|--------|------|---------|----------|
|
|
263
|
+
| Applications | 1 | 1 (large) | >1 |
|
|
264
|
+
| Entities | 1-4 | 5-6 | >6 without -d |
|
|
265
|
+
| Sections | 1-3 | 4-5 | >5 without -d |
|
|
266
|
+
| Sub-resources | 0-2 | 3-4 | >4 without -d |
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
150
270
|
## 6. Determine Needs
|
|
151
271
|
|
|
152
272
|
```
|
|
@@ -34,6 +34,43 @@ This saves ~2 minutes of Glob searches on an empty project.
|
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
|
37
|
+
## 0b. Delegate Mode Fast Path (if delegate_mode)
|
|
38
|
+
|
|
39
|
+
> **When called via `/ralph-loop -d`, PRD tasks provide pre-computed scope. Reduce exploration to verification only.**
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
IF delegate_mode:
|
|
43
|
+
Read PRD from {delegate_prd_path} (already loaded in step-00)
|
|
44
|
+
|
|
45
|
+
1. Extract entity list from domain tasks:
|
|
46
|
+
entities[] = prd.tasks.filter(category == 'domain').map(entity name from description)
|
|
47
|
+
|
|
48
|
+
2. Extract FK relationships from frontend tasks with _frontendMeta:
|
|
49
|
+
fkFields[] = _frontendMeta.fkFields[] (e.g., ["DepartmentId→Department", "CompanyId→Company"])
|
|
50
|
+
|
|
51
|
+
3. Determine tenant modes per entity:
|
|
52
|
+
IF feature.json exists at {feature_path}:
|
|
53
|
+
Read entity definitions → extract tenantMode per entity
|
|
54
|
+
ELSE:
|
|
55
|
+
Default all entities to tenantMode: "strict"
|
|
56
|
+
|
|
57
|
+
4. Read feature.json for wireframes/componentMapping if {feature_path} exists:
|
|
58
|
+
→ enriches frontend generation in step-03
|
|
59
|
+
|
|
60
|
+
5. Perform LIGHTWEIGHT code exploration (verification only):
|
|
61
|
+
- Glob("**/Domain/Entities/**/{module_code}/**/*.cs") → what already exists?
|
|
62
|
+
- Glob("**/Infrastructure/Persistence/Seeding/Data/**/*SeedData*.cs") → existing seed data?
|
|
63
|
+
- Glob("src/pages/**/{module_code}/**/*.tsx") → existing pages?
|
|
64
|
+
→ Only to determine create vs modify vs skip (NOT deep analysis)
|
|
65
|
+
|
|
66
|
+
6. Gap Analysis: Mark all PRD tasks as "create" unless existing code found
|
|
67
|
+
|
|
68
|
+
SKIP: Full Agent Teams exploration (section 2), challenge question follow-up
|
|
69
|
+
Jump to section 5 (Gap Analysis summary) → section 7 (Analysis Summary)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
37
74
|
## 1. Context Sources
|
|
38
75
|
|
|
39
76
|
Read available context (in order of priority):
|
|
@@ -174,6 +211,27 @@ Cross-reference with step-00 challenge responses:
|
|
|
174
211
|
|
|
175
212
|
---
|
|
176
213
|
|
|
214
|
+
## 5b. Scope Re-Check (after exploration)
|
|
215
|
+
|
|
216
|
+
> **Re-validate scope after code exploration reveals the true entity count.**
|
|
217
|
+
> Step-00 guard uses the user's description (may undercount). Now we know the actual entities.
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
IF NOT delegate_mode:
|
|
221
|
+
actual_entities = count of entities marked "create" in gap analysis
|
|
222
|
+
actual_sections = count of sections marked "create"
|
|
223
|
+
|
|
224
|
+
IF actual_entities > 6:
|
|
225
|
+
WARNING: "Code exploration reveals {actual_entities} entities to create.
|
|
226
|
+
This exceeds the recommended maximum (4) for a single /apex invocation.
|
|
227
|
+
Risk: incomplete migrations, lost conventions, missing pages.
|
|
228
|
+
Consider: split into {ceil(actual_entities/4)} iterations of ~4 entities each."
|
|
229
|
+
|
|
230
|
+
AskUserQuestion: (same options as step-00 section 5d)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
177
235
|
## 6. Analysis Validation (User Checkpoint)
|
|
178
236
|
|
|
179
237
|
> **Objective:** Present findings and validate scope with the user BEFORE planning.
|
|
@@ -16,6 +16,42 @@ Read `references/smartstack-layers.md` for layer execution rules and skill/MCP m
|
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
+
## 0. Delegate Mode Fast Path (if delegate_mode)
|
|
20
|
+
|
|
21
|
+
> **When called via `/ralph-loop -d`, PRD tasks already define the scope. Map directly to layers.**
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
IF delegate_mode:
|
|
25
|
+
Read PRD from {delegate_prd_path}
|
|
26
|
+
|
|
27
|
+
Map each PRD task to a layer based on task.category:
|
|
28
|
+
domain → Layer 0
|
|
29
|
+
infrastructure → Layer 0
|
|
30
|
+
application → Layer 1
|
|
31
|
+
api → Layer 1
|
|
32
|
+
seedData → Layer 1
|
|
33
|
+
frontend → Layer 2
|
|
34
|
+
test → Layer 3
|
|
35
|
+
|
|
36
|
+
For each task:
|
|
37
|
+
file_path = task.files_changed.created[0] (primary file)
|
|
38
|
+
action = "create" (default for delegate mode)
|
|
39
|
+
tool = infer from category:
|
|
40
|
+
domain/infrastructure → MCP scaffold_extension
|
|
41
|
+
api → /controller skill or MCP scaffold_extension
|
|
42
|
+
seedData → MCP generate_permissions + ref core-seed-data.md
|
|
43
|
+
frontend → /ui-components skill + MCP scaffold_api_client + MCP scaffold_routes
|
|
44
|
+
test → MCP scaffold_tests
|
|
45
|
+
acceptance_criteria = task.acceptance_criteria
|
|
46
|
+
|
|
47
|
+
Use task._seedDataMeta, _frontendMeta, _migrationMeta for enriched context.
|
|
48
|
+
|
|
49
|
+
SKIP: User checkpoint (section 6) — auto_mode implied
|
|
50
|
+
Jump to section 5 (Estimated Commits) → NEXT STEP
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
19
55
|
## 1. Map Changes to Layers
|
|
20
56
|
|
|
21
57
|
For each element identified in step-01 (create or modify), assign to a SmartStack layer:
|
|
@@ -17,6 +17,8 @@ All code goes through skills (/controller, /application, /ui-components, /efcore
|
|
|
17
17
|
- **ALWAYS** read `references/smartstack-frontend.md` — lazy loading, i18n, page structure, CSS variables
|
|
18
18
|
- Read `references/smartstack-layers.md` for execution rules per layer
|
|
19
19
|
- If NOT `{economy_mode}` AND Layer 1 has parallel work: read `references/agent-teams-protocol.md`
|
|
20
|
+
- If `{delegate_mode}` AND seedData tasks exist: read `references/core-seed-data.md` — comprehensive seed data templates
|
|
21
|
+
- If build failure during execution: read `references/error-classification.md` — error diagnosis categories A-F
|
|
20
22
|
|
|
21
23
|
---
|
|
22
24
|
|
|
@@ -42,16 +44,26 @@ For each entity:
|
|
|
42
44
|
|
|
43
45
|
### Migration (BLOCKING)
|
|
44
46
|
|
|
47
|
+
> **CRITICAL — Migration must cover ALL entities, not just the first batch.**
|
|
48
|
+
> Root cause (test-apex-007): Migration was created once for 3 entities, then 4 more entities
|
|
49
|
+
> were added later without re-running migration → 4 entities had no database tables.
|
|
50
|
+
> **RULE:** Create/update migration AFTER ALL entities and EF configs are registered in DbContext.
|
|
51
|
+
> If entities are added incrementally, create a NEW migration for each batch.
|
|
52
|
+
|
|
45
53
|
```
|
|
46
|
-
1.
|
|
47
|
-
2.
|
|
48
|
-
3.
|
|
54
|
+
1. Verify ALL entities have been added as DbSet in ExtensionsDbContext
|
|
55
|
+
2. Verify ALL EF configurations are registered (ApplyConfigurationsFromAssembly or individual)
|
|
56
|
+
3. MCP suggest_migration → get standardized name
|
|
57
|
+
4. dotnet ef migrations add {Name} --project src/{Infra}.csproj --startup-project src/{Api}.csproj -o Persistence/Migrations
|
|
58
|
+
5. Cleanup corrupted EF Core artifacts:
|
|
49
59
|
for d in src/*/bin?Debug; do [ -d "$d" ] && rm -rf "$d"; done
|
|
50
|
-
|
|
51
|
-
|
|
60
|
+
6. dotnet ef database update (if local DB)
|
|
61
|
+
7. dotnet build → MUST PASS
|
|
62
|
+
8. Verify: dotnet ef migrations has-pending-model-changes → MUST report "No pending model changes"
|
|
52
63
|
```
|
|
53
64
|
|
|
54
65
|
**BLOCKING:** If build fails after migration, fix EF configs before proceeding.
|
|
66
|
+
**BLOCKING:** If `has-pending-model-changes` reports pending changes, entities are missing from the migration — create a new migration.
|
|
55
67
|
|
|
56
68
|
### Post-Layer 0 Build Gate
|
|
57
69
|
|
|
@@ -65,10 +77,66 @@ dotnet build
|
|
|
65
77
|
|
|
66
78
|
## Layer 1 — Application + API + Seed Data
|
|
67
79
|
|
|
80
|
+
### NavRoute and Permission Kebab-Case (CRITICAL)
|
|
81
|
+
|
|
82
|
+
> **ALL NavRoute segments and permission codes MUST use kebab-case for multi-word identifiers.**
|
|
83
|
+
> Root cause (test-apex-007): Controllers had `[NavRoute("business.humanresources.employees")]`
|
|
84
|
+
> instead of `[NavRoute("business.human-resources.employees")]`. This mismatched seed data routes
|
|
85
|
+
> and permission codes, causing 404s and permission denials at runtime.
|
|
86
|
+
|
|
87
|
+
**Rules:**
|
|
88
|
+
- NavRoute: `business.human-resources.employees` (NEVER `business.humanresources.employees`)
|
|
89
|
+
- Permissions: `business.human-resources.employees.read` (segments MATCH NavRoute exactly)
|
|
90
|
+
- Seed data codes: `human-resources` (NEVER `humanresources`)
|
|
91
|
+
- C# class names stay PascalCase (`HumanResourcesController`) — only route/permission strings use kebab-case
|
|
92
|
+
- POST-CHECKs 41 + 48 validate this. Fix BEFORE committing.
|
|
93
|
+
|
|
94
|
+
### Controller Route Attribute (BLOCKING)
|
|
95
|
+
|
|
96
|
+
> **Controllers with `[NavRoute]` must NOT have `[Route]` attribute.**
|
|
97
|
+
> Root cause (test-apex-007): ALL 7 controllers had BOTH `[Route("api/...")]` AND `[NavRoute("...")]`.
|
|
98
|
+
> In SmartStack, `[NavRoute]` resolves routes dynamically from Navigation entities in the database at startup.
|
|
99
|
+
> Having `[Route]` alongside causes route conflicts → all endpoints return 404.
|
|
100
|
+
|
|
101
|
+
**Rules:**
|
|
102
|
+
- `[NavRoute("context.app.module")]` is the ONLY route attribute needed on controllers
|
|
103
|
+
- **FORBIDDEN:** `[Route("api/business/human-resources/employees")]` alongside `[NavRoute]`
|
|
104
|
+
- **FORBIDDEN:** `[Route("api/[controller]")]` alongside `[NavRoute]`
|
|
105
|
+
- If generating via MCP `scaffold_extension` with `navRoute` option → output is correct (NavRoute only)
|
|
106
|
+
- If generating via `/controller` skill → verify NO `[Route]` is added
|
|
107
|
+
- POST-CHECK 50 validates this. Fix BEFORE committing.
|
|
108
|
+
|
|
109
|
+
### Validators DI Registration (CRITICAL)
|
|
110
|
+
|
|
111
|
+
> After creating validators, they MUST be registered in DI. Without registration, `[FromBody]` DTOs are never validated.
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
In DependencyInjection.cs (or ServiceCollectionExtensions.cs):
|
|
115
|
+
services.AddValidatorsFromAssemblyContaining<Create{Entity}DtoValidator>();
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
POST-CHECK 46 validates this. If validators exist but no DI registration → BLOCKING.
|
|
119
|
+
|
|
68
120
|
### If economy_mode: Sequential execution
|
|
69
121
|
|
|
70
122
|
Execute each item from the plan sequentially using skills and MCP.
|
|
71
123
|
|
|
124
|
+
### Date Fields — Use DateOnly (CRITICAL)
|
|
125
|
+
|
|
126
|
+
> **ALL date-only fields in DTOs MUST use `DateOnly`, NEVER `string`.**
|
|
127
|
+
> Root cause (test-apex-007): WorkLog DTO had `string Date` instead of `DateOnly Date`.
|
|
128
|
+
> This causes: no date validation, inconsistent date formats, parsing errors.
|
|
129
|
+
|
|
130
|
+
**Type mapping for DTOs:**
|
|
131
|
+
| Domain type | DTO type | Example |
|
|
132
|
+
|-------------|----------|---------|
|
|
133
|
+
| `DateTime` | `DateTime` | `CreatedAt`, `UpdatedAt` |
|
|
134
|
+
| Date-only field | `DateOnly` | `Date`, `StartDate`, `EndDate`, `BirthDate` |
|
|
135
|
+
| `string` for date | **FORBIDDEN** | Never use `string` for dates |
|
|
136
|
+
| `DateTime` for date-only | **Avoid** | Use `DateOnly` when no time component needed |
|
|
137
|
+
|
|
138
|
+
POST-CHECK 47 validates this. If a DTO has `string` type for a property named `*Date*` → BLOCKING.
|
|
139
|
+
|
|
72
140
|
### Code Generation (if entities have codePattern != "manual")
|
|
73
141
|
|
|
74
142
|
For each entity with auto-generated code pattern (from feature.json or step-02 decisions):
|
|
@@ -92,15 +160,27 @@ Code stays in CreateDto, user provides it, validator has regex rule.
|
|
|
92
160
|
**For frontend tasks (economy_mode):** Follow the same rules as exec-frontend above:
|
|
93
161
|
- `MCP scaffold_api_client` → API client + types + React Query hook
|
|
94
162
|
- `MCP scaffold_routes` with `outputFormat: 'clientRoutes'` for lazy imports
|
|
95
|
-
-
|
|
96
|
-
- **
|
|
163
|
+
- **INVOKE `/ui-components` skill** (read SKILL.md + ALL patterns) — MANDATORY for ALL page types
|
|
164
|
+
- **Create ALL 4 page types per module:**
|
|
165
|
+
- `ListPage.tsx` — entity list with SmartTable/EntityCard
|
|
166
|
+
- `DetailPage.tsx` — entity detail view
|
|
167
|
+
- `EntityCreatePage.tsx` (route: `/create`) — FULL PAGE form, NEVER modal
|
|
168
|
+
- `EntityEditPage.tsx` (route: `/:id/edit`) — FULL PAGE form, NEVER modal
|
|
169
|
+
- **Wire ALL routes in App.tsx:** `index` (ListPage), `:id` (DetailPage), `create` (CreatePage), `:id/edit` (EditPage)
|
|
97
170
|
- **FK FIELDS (CRITICAL):** Any Guid FK property (e.g., EmployeeId, DepartmentId) MUST use `EntityLookup` component — NEVER a `<select>` dropdown, NEVER a `<input type="text">`. A `<select>` loaded from API state is NOT a substitute for EntityLookup. See `smartstack-frontend.md` section 6.
|
|
98
171
|
- **ZERO modals/popups/drawers for forms — ALL forms are full pages with their own URL**
|
|
172
|
+
- **TABS ON DETAIL PAGES (CRITICAL):** Tabs MUST switch content LOCALLY via `setActiveTab()` — NEVER `navigate()` to another page. Sub-resource data (e.g., employee's leaves) loads inline via API call filtered by parent entity ID. See `smartstack-frontend.md` section 3 "Tab Behavior Rules".
|
|
99
173
|
- **Form tests: Generate `EntityCreatePage.test.tsx` and `EntityEditPage.test.tsx` (co-located)**
|
|
100
174
|
- **SECTION PERMISSIONS:** After calling `MCP generate_permissions` for the module navRoute (3 segments: `{context}.{app}.{module}`), also call it for EACH section navRoute (4 segments: `{context}.{app}.{module}.{section}`)
|
|
101
175
|
- **SECTION ROUTES:** After generating module routes, add section child routes to the module's `children` array. Wire `PermissionGuard` for section routes with section-level permissions.
|
|
176
|
+
- **SUB-RESOURCE COMPLETENESS:** If a section controller has sub-resource endpoints (e.g., `[HttpGet("types")]` for LeaveTypes inside LeavesController), you MUST EITHER:
|
|
177
|
+
- Create dedicated frontend pages for the sub-resource (ListPage, CreatePage, EditPage) with routes wired in App.tsx, OR
|
|
178
|
+
- NOT include any `navigate()` button that links to those sub-resource pages
|
|
179
|
+
- **Prefer separate controllers** with `[NavRoute(..., Suffix = "types")]` — see `smartstack-api.md` Sub-Resource Pattern
|
|
180
|
+
- A dead link (navigate to a route with no page) is a BLOCKING issue (POST-CHECK 42)
|
|
102
181
|
- Read `references/smartstack-frontend.md` for mandatory patterns (sections 3b + 8)
|
|
103
182
|
- Generate i18n JSON files for all 4 languages (fr, en, it, de) — `src/i18n/locales/{lang}/{module}.json`
|
|
183
|
+
- **I18n REGISTRATION (CRITICAL):** After creating i18n JSON files, register EACH new namespace in the i18n config file (config.ts/index.ts/i18n.ts). Unregistered namespaces → `useTranslation(['module'])` returns empty strings at runtime. POST-CHECK 45 validates this.
|
|
104
184
|
- All pages must follow loading → error → content pattern with CSS variables
|
|
105
185
|
|
|
106
186
|
### If NOT economy_mode: Agent Teams (parallel)
|
|
@@ -146,6 +226,12 @@ Spawn 2 teammates (Opus, full tools):
|
|
|
146
226
|
→ ZERO modals/popups/drawers/dialogs for forms
|
|
147
227
|
→ Back button with navigate(-1) on every form page
|
|
148
228
|
→ See smartstack-frontend.md section 3b for templates
|
|
229
|
+
- **TABS ON DETAIL PAGES (CRITICAL):** Tabs MUST switch content LOCALLY:
|
|
230
|
+
→ Tab click handler: setActiveTab(tabKey) ONLY — NEVER navigate() to another page
|
|
231
|
+
→ Tab content: render inline with {activeTab === 'tabKey' && <TabContent />}
|
|
232
|
+
→ Sub-resource data: fetch via API filtered by parent ID, display in tab panel
|
|
233
|
+
→ FORBIDDEN: navigate('../leaves?employee=${id}') in tab handler
|
|
234
|
+
→ See smartstack-frontend.md section 3 'Tab Behavior Rules'
|
|
149
235
|
- **FK FIELDS (CRITICAL):** Foreign key Guid fields (e.g., EmployeeId) MUST use EntityLookup:
|
|
150
236
|
→ NEVER render FK fields as `<input>` — users cannot type GUIDs
|
|
151
237
|
→ NEVER render FK fields as `<select>` — even with API-loaded options, `<select>` is NOT acceptable
|
|
@@ -382,6 +468,27 @@ Each file MUST contain these keys: `title`, `description`, `actions`, `labels`,
|
|
|
382
468
|
|
|
383
469
|
---
|
|
384
470
|
|
|
471
|
+
## PRD State Update (delegate mode only)
|
|
472
|
+
|
|
473
|
+
> **After completing EACH layer**, update the PRD file so `/ralph-loop` can track progress.
|
|
474
|
+
|
|
475
|
+
```
|
|
476
|
+
IF delegate_mode:
|
|
477
|
+
After each layer completes successfully:
|
|
478
|
+
Read {delegate_prd_path}
|
|
479
|
+
For each task executed in this layer:
|
|
480
|
+
task.status = 'completed' (or 'failed' if errors)
|
|
481
|
+
task.completed_at = new Date().toISOString()
|
|
482
|
+
task.iteration = 1 (delegate mode = single iteration)
|
|
483
|
+
task.commit_hash = $(git rev-parse --short HEAD)
|
|
484
|
+
task.files_changed = { created: [...files created...], modified: [...files modified...] }
|
|
485
|
+
task.validation = "APEX_PASS" (or error details)
|
|
486
|
+
prd.updated_at = new Date().toISOString()
|
|
487
|
+
Write back to {delegate_prd_path}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
385
492
|
## Commits Per Layer
|
|
386
493
|
|
|
387
494
|
After each layer completes, gates pass, and builds pass:
|