@atlashub/smartstack-cli 4.26.0 → 4.28.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/dist/mcp-entry.mjs +33 -11
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/agents/ba-writer.md +46 -46
- package/templates/project/appsettings.json.template +4 -6
- package/templates/skills/apex/SKILL.md +1 -0
- package/templates/skills/apex/references/challenge-questions.md +17 -0
- package/templates/skills/apex/references/core-seed-data.md +27 -4
- package/templates/skills/apex/references/post-checks.md +330 -0
- package/templates/skills/apex/references/smartstack-layers.md +31 -0
- package/templates/skills/apex/steps/step-02-plan.md +9 -0
- package/templates/skills/apex/steps/step-03-execute.md +102 -4
- package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +33 -0
- package/templates/skills/business-analyse/references/consolidation-structural-checks.md +17 -0
- package/templates/skills/business-analyse/references/spec-auto-inference.md +12 -7
- package/templates/skills/business-analyse/steps/step-00-init.md +19 -9
- package/templates/skills/business-analyse/steps/step-02-structure.md +20 -6
- package/templates/skills/business-analyse/steps/step-03-specify.md +7 -0
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +2 -14
- package/templates/skills/controller/references/mcp-scaffold-workflow.md +20 -0
- package/templates/skills/derive-prd/references/handoff-file-templates.md +25 -1
- package/templates/skills/derive-prd/references/handoff-seeddata-generation.md +3 -1
- package/templates/skills/ralph-loop/references/category-completeness.md +125 -0
- package/templates/skills/ralph-loop/references/compact-loop.md +90 -3
- package/templates/skills/ralph-loop/references/module-transition.md +60 -0
- package/templates/skills/ralph-loop/steps/step-04-check.md +207 -12
- package/templates/skills/ralph-loop/steps/step-05-report.md +205 -14
|
@@ -257,6 +257,8 @@ var sectionRoute = $"{moduleRoute}/{ToKebabCase(sectionCode)}";
|
|
|
257
257
|
- POST-CHECK C34 detects non-kebab-case NavRoute values. POST-CHECK C35 detects non-kebab-case permissions
|
|
258
258
|
- Do not combine `[Route("api/...")]` with `[NavRoute]` — causes 404s (POST-CHECK C43)
|
|
259
259
|
- `[NavRoute]` is the only route attribute needed — resolves routes from DB at startup
|
|
260
|
+
- **NavRoute uniqueness:** Each controller MUST have a unique NavRoute value. Two controllers sharing the same NavRoute causes routing conflicts and 404s (POST-CHECK C50)
|
|
261
|
+
- **NavRoute segment count vs hierarchy:** If a controller lives in a section subfolder (e.g., `Controllers/{App}/Employees/ContractsController.cs`), its NavRoute MUST have 3 segments (`app.module.section`), not 2. A 2-segment NavRoute on a section controller causes API 404s because the resolved route doesn't match the expected path (POST-CHECK C51)
|
|
260
262
|
- Do not use `[Authorize]` without specific permission
|
|
261
263
|
- Swagger XML documentation
|
|
262
264
|
- Return DTOs, never domain entities
|
|
@@ -307,12 +309,41 @@ const [loading, setLoading] = useState(true);
|
|
|
307
309
|
|
|
308
310
|
**Loader:** `Loader2` from `lucide-react` for spinners, `PageLoader` for Suspense fallback
|
|
309
311
|
|
|
312
|
+
### RULE — Frontend Route Ordering
|
|
313
|
+
|
|
314
|
+
> **CRITICAL:** React Router matches routes top-to-bottom. Dynamic routes (`:id`, `:id/edit`) placed BEFORE static routes (`dashboard`, `create`) will swallow the static paths — e.g., `/employees/dashboard` matches `:id` with `id="dashboard"` → 404.
|
|
315
|
+
|
|
316
|
+
**Order (MANDATORY):**
|
|
317
|
+
|
|
318
|
+
1. Index/list route (path: `''` or `'{section}'`)
|
|
319
|
+
2. Create route (`'{section}/create'`)
|
|
320
|
+
3. Static sections (`'{section}/dashboard'`, `'{section}/departments'`, etc.)
|
|
321
|
+
4. Dynamic routes (`'{section}/:id'`, `'{section}/:id/edit'`)
|
|
322
|
+
5. Redirect routes (`Navigate`) — ALWAYS LAST
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
// ✅ CORRECT — static before dynamic
|
|
326
|
+
{ path: 'employees', element: <EmployeesPage /> },
|
|
327
|
+
{ path: 'employees/create', element: <CreateEmployeePage /> },
|
|
328
|
+
{ path: 'employees/dashboard', element: <EmployeeDashboardPage /> },
|
|
329
|
+
{ path: 'employees/:id', element: <EmployeeDetailPage /> },
|
|
330
|
+
{ path: 'employees/:id/edit', element: <EditEmployeePage /> },
|
|
331
|
+
{ path: '', element: <Navigate to="employees" replace /> },
|
|
332
|
+
|
|
333
|
+
// ❌ WRONG — :id before dashboard → dashboard is unreachable (matched as id="dashboard")
|
|
334
|
+
{ path: 'employees/:id', element: <EmployeeDetailPage /> },
|
|
335
|
+
{ path: 'employees/dashboard', element: <EmployeeDashboardPage /> }, // NEVER REACHED
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
POST-CHECK C49 detects this anti-pattern and BLOCKS.
|
|
339
|
+
|
|
310
340
|
### Section Routes (when module has sections)
|
|
311
341
|
|
|
312
342
|
If the module defines `{sections}`, generate frontend routes for EACH section as children of the module route:
|
|
313
343
|
|
|
314
344
|
```tsx
|
|
315
345
|
// Section routes are nested inside the module's children array
|
|
346
|
+
// IMPORTANT: static routes BEFORE dynamic routes (see Route Ordering rule above)
|
|
316
347
|
{ path: '{section-kebab}', element: <Suspense fallback={<PageLoader />}><{Section}Page /></Suspense> },
|
|
317
348
|
{ path: '{section-kebab}/create', element: <Suspense fallback={<PageLoader />}><Create{Section}Page /></Suspense> },
|
|
318
349
|
{ path: '{section-kebab}/:id', element: <Suspense fallback={<PageLoader />}><{Section}DetailPage /></Suspense> },
|
|
@@ -108,6 +108,15 @@ Verify the plan respects dependencies:
|
|
|
108
108
|
- Build gate between EVERY layer (must pass): Layer 0 → 1 → 2 → 3 → 4
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
### 4b. Module Code Collision Guard (BLOCKING)
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
IF appCode == moduleCode:
|
|
115
|
+
BLOCK — module_code identique à app_code cause des segments doublés (e.g. /hr/hr).
|
|
116
|
+
Suggestion : "{appCode}-core", "{appCode}-management"
|
|
117
|
+
ASK user pour un module_code différent.
|
|
118
|
+
```
|
|
119
|
+
|
|
111
120
|
---
|
|
112
121
|
|
|
113
122
|
## 5. Estimated Commits
|
|
@@ -290,6 +290,57 @@ When launching agents for multi-entity layers:
|
|
|
290
290
|
- Agent principal updates activeForm after each entity completion:
|
|
291
291
|
`TaskUpdate(taskId: layer2_task_id, activeForm: "Building {EntityName} backend (2/5 entities)")`
|
|
292
292
|
|
|
293
|
+
### Controller NavRoute Attribute (MANDATORY)
|
|
294
|
+
|
|
295
|
+
After controller generation, verify `[NavRoute]` attribute is present on every controller:
|
|
296
|
+
- Expected: `[NavRoute("{app_name}.{module_code}.{section_code}")]` on the controller class
|
|
297
|
+
- If missing: Add it manually above `[Authorize]`
|
|
298
|
+
- When calling `scaffold_extension(type: "controller")`, always pass `navRoute` in options
|
|
299
|
+
- This is REQUIRED for `scaffold_routes` to auto-detect routes in Layer 3
|
|
300
|
+
|
|
301
|
+
### Guard: NavRoute Uniqueness and Segment Count (MANDATORY)
|
|
302
|
+
|
|
303
|
+
**BEFORE proceeding past Layer 2**, verify for EACH controller:
|
|
304
|
+
|
|
305
|
+
1. **Unique NavRoute:** No two controllers may share the same `[NavRoute("...")]` value. Duplicate NavRoutes cause routing conflicts → 404s on one of the controllers.
|
|
306
|
+
|
|
307
|
+
2. **Segment count matches hierarchy:** Count the dots in the NavRoute value:
|
|
308
|
+
- 1 dot = 2 segments (module-level, e.g., `human-resources.employees`) — controller is at `Controllers/{App}/`
|
|
309
|
+
- 2 dots = 3 segments (section-level, e.g., `human-resources.employees.contracts`) — controller is at `Controllers/{App}/{Module}/` or in a section subfolder
|
|
310
|
+
- **If a controller is in a section subfolder** (e.g., `Controllers/{App}/Employees/ContractsController.cs`) **but has only 2 segments** → the API route will be wrong → 404. It MUST have 3 segments.
|
|
311
|
+
- 0 dots = INVALID → BLOCK
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
# Quick validation
|
|
315
|
+
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
|
|
316
|
+
for f in $CTRL_FILES; do
|
|
317
|
+
NAVROUTE=$(grep -oP '\[NavRoute\("\K[^"]+' "$f")
|
|
318
|
+
if [ -n "$NAVROUTE" ]; then
|
|
319
|
+
DOTS=$(echo "$NAVROUTE" | tr -cd '.' | wc -c)
|
|
320
|
+
if [ "$DOTS" -eq 0 ]; then
|
|
321
|
+
echo "BLOCKING: NavRoute '$NAVROUTE' has only 1 segment (need minimum 2): $f"
|
|
322
|
+
exit 1
|
|
323
|
+
fi
|
|
324
|
+
# Check if controller is in a section subfolder but NavRoute has only 2 segments
|
|
325
|
+
DEPTH=$(echo "$f" | grep -oP 'Controllers/[^/]+/[^/]+/' | wc -l)
|
|
326
|
+
if [ "$DEPTH" -gt 0 ] && [ "$DOTS" -eq 1 ]; then
|
|
327
|
+
echo "WARNING: Controller in section subfolder but NavRoute has only 2 segments: $f"
|
|
328
|
+
echo " NavRoute: $NAVROUTE — expected 3 segments (app.module.section)"
|
|
329
|
+
fi
|
|
330
|
+
fi
|
|
331
|
+
done
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
# Quick check: all controllers must have [NavRoute] (not just [Route])
|
|
336
|
+
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
|
|
337
|
+
for f in $CTRL_FILES; do
|
|
338
|
+
if ! grep -q "\[NavRoute" "$f" && grep -q "\[Route" "$f"; then
|
|
339
|
+
echo "WARNING: $f has [Route] but no [NavRoute] — add [NavRoute] for route auto-detection"
|
|
340
|
+
fi
|
|
341
|
+
done
|
|
342
|
+
```
|
|
343
|
+
|
|
293
344
|
### Post-Layer 2 Build Gate
|
|
294
345
|
|
|
295
346
|
```bash
|
|
@@ -368,16 +419,31 @@ TaskUpdate(taskId: progress_tracker_id,
|
|
|
368
419
|
|
|
369
420
|
For each module:
|
|
370
421
|
- API client: MCP scaffold_api_client
|
|
371
|
-
- Routes
|
|
422
|
+
- Routes — TWO mandatory steps:
|
|
423
|
+
1. Generate registry: MCP scaffold_routes (source: 'controllers', outputFormat: 'applicationRoutes', dryRun: false)
|
|
424
|
+
→ Creates navRoutes.generated.ts (required by POST-CHECK C2)
|
|
425
|
+
2. Wire to App.tsx (see below)
|
|
372
426
|
- Wire Routes to App.tsx: After scaffold_routes, routes must be wired into App.tsx:
|
|
373
427
|
→ Read App.tsx and detect the routing pattern
|
|
374
428
|
→ Pattern A (`applicationRoutes: ApplicationRouteExtensions`): add routes to `applicationRoutes['{application_kebab}'][]` with RELATIVE paths
|
|
375
429
|
→ Pattern B (JSX `<Route>`): nest routes inside `<Route path="/{application}" element={<AppLayout />}>` + duplicate in tenant block
|
|
430
|
+
→ **BEFORE wiring:** Verify route ordering — static routes (`create`, `dashboard`, `departments`) MUST come BEFORE dynamic routes (`:id`, `:id/edit`). Redirect routes (`Navigate`) MUST be LAST. See `references/smartstack-layers.md` "RULE — Frontend Route Ordering".
|
|
376
431
|
→ Do not add business routes to `clientRoutes[]` — it is only for non-app routes (`/about`, `/pricing`)
|
|
377
432
|
→ All business applications use `<AppLayout />` as layout wrapper
|
|
378
433
|
→ See `references/frontend-route-wiring-app-tsx.md` for full Pattern A/B detection and examples
|
|
379
434
|
→ Verify: `mcp__smartstack__validate_frontend_routes (scope: 'routes')`
|
|
380
|
-
- Pages:
|
|
435
|
+
- Pages per section: When a section exists (e.g., "list"), generate ALL 4 page types:
|
|
436
|
+
→ {Section}ListPage.tsx (index route)
|
|
437
|
+
→ {Section}DetailPage.tsx (/:id route) — click on list item navigates here
|
|
438
|
+
→ Create{Section}Page.tsx (/create route)
|
|
439
|
+
→ Edit{Section}Page.tsx (/:id/edit route)
|
|
440
|
+
"detail" is NEVER a separate section — it's the /:id route of the list section.
|
|
441
|
+
- Pages: **MANDATORY — INVOKE Skill("ui-components")** for ALL page generation.
|
|
442
|
+
⚠ BLOCKING REQUIREMENT: You MUST call Skill("ui-components") for EVERY page (.tsx).
|
|
443
|
+
NEVER generate .tsx page code directly. NEVER write page content in Agent prompts.
|
|
444
|
+
NEVER use Write tool to create pages without first calling Skill("ui-components").
|
|
445
|
+
The skill loads the full style guide + patterns (entity-card, data-table, dashboard-chart, grid-layout).
|
|
446
|
+
Follow smartstack-frontend.md patterns:
|
|
381
447
|
→ React.lazy() for all page imports (named export wrapping)
|
|
382
448
|
→ `<Suspense fallback={<PageLoader />}>` around all lazy components
|
|
383
449
|
→ Page structure: hooks → useEffect(load) → loading → error → content
|
|
@@ -451,7 +517,7 @@ IF NOT economy_mode AND entities.length > 1:
|
|
|
451
517
|
prompt='Execute Layer 3 frontend for {EntityName}:
|
|
452
518
|
**MANDATORY: Read references/smartstack-frontend.md FIRST**
|
|
453
519
|
- API client: MCP scaffold_api_client
|
|
454
|
-
- Routes: MCP scaffold_routes (outputFormat: "applicationRoutes")
|
|
520
|
+
- Routes: MCP scaffold_routes (outputFormat: "applicationRoutes", dryRun: false) → MUST generate navRoutes.generated.ts
|
|
455
521
|
- Wire Routes to App.tsx (BLOCKING): detect Pattern A/B, wire accordingly
|
|
456
522
|
→ See references/frontend-route-wiring-app-tsx.md for full patterns
|
|
457
523
|
→ Verify: mcp__smartstack__validate_frontend_routes (scope: "routes")
|
|
@@ -466,7 +532,21 @@ IF NOT economy_mode AND entities.length > 1:
|
|
|
466
532
|
# All agents launched in parallel
|
|
467
533
|
|
|
468
534
|
ELSE:
|
|
469
|
-
# Agent principal handles all entities
|
|
535
|
+
# Economy mode: Agent principal handles all entities SEQUENTIALLY.
|
|
536
|
+
# MANDATORY: You MUST still call Skill("ui-components") for page generation.
|
|
537
|
+
# economy_mode only disables parallel agents — it does NOT skip /ui-components.
|
|
538
|
+
For each entity (sequentially):
|
|
539
|
+
1. MCP scaffold_api_client
|
|
540
|
+
2. MCP scaffold_routes (outputFormat: 'applicationRoutes')
|
|
541
|
+
3. Wire routes to App.tsx (Pattern A/B — see references/frontend-route-wiring-app-tsx.md)
|
|
542
|
+
4. **INVOKE Skill("ui-components")** — pass entity context:
|
|
543
|
+
- Entity: {EntityName}, Module: {ModuleName}, App: {AppName}
|
|
544
|
+
- Page types: List, Detail, Create, Edit (+ Dashboard if applicable)
|
|
545
|
+
- "CSS: Use CSS variables ONLY — bg-[var(--bg-card)], text-[var(--text-primary)]"
|
|
546
|
+
- "Forms: Create/Edit are FULL PAGES with own routes (/create, /:id/edit)"
|
|
547
|
+
- "I18n: ALL text uses t('namespace:key', 'Fallback')"
|
|
548
|
+
5. I18n: 4 JSON files (fr, en, it, de) + register namespace in i18n config
|
|
549
|
+
6. Form tests: co-located .test.tsx for Create and Edit pages
|
|
470
550
|
```
|
|
471
551
|
|
|
472
552
|
### Parallel Agents + TaskCreate Integration
|
|
@@ -495,6 +575,24 @@ When delegating to `/ui-components` skill, include explicit instructions:
|
|
|
495
575
|
- "Forms: Create/Edit forms are FULL PAGES with own routes (e.g., `/create`, `/:id/edit`)."
|
|
496
576
|
- "I18n: ALL text must use `t('namespace:key', 'Fallback')`. Generate JSON files in `src/i18n/locales/`."
|
|
497
577
|
|
|
578
|
+
### HARD RULE — /ui-components is NON-NEGOTIABLE
|
|
579
|
+
|
|
580
|
+
> **VIOLATION CHECK:** If ANY .tsx page file was created by Write tool WITHOUT
|
|
581
|
+
> a prior Skill("ui-components") call in this execution, the frontend layer is INVALID.
|
|
582
|
+
>
|
|
583
|
+
> **You MUST NOT:**
|
|
584
|
+
> - Generate .tsx page code in Agent prompts and dispatch to Snipper agents
|
|
585
|
+
> - Write page content directly via the Write tool
|
|
586
|
+
> - Copy-paste page templates from your knowledge
|
|
587
|
+
>
|
|
588
|
+
> **You MUST:**
|
|
589
|
+
> - Call Skill("ui-components") which loads the style guide, responsive guidelines,
|
|
590
|
+
> accessibility rules, and pattern files (entity-card, data-table, dashboard-chart, grid-layout, kanban)
|
|
591
|
+
> - Let /ui-components generate all pages with correct conventions
|
|
592
|
+
>
|
|
593
|
+
> **Why this matters:** Without the skill, pages miss CSS variables, DataTable/EntityCard,
|
|
594
|
+
> memo()/useCallback, responsive mobile-first design, and accessibility patterns.
|
|
595
|
+
|
|
498
596
|
### Frontend Tests Inline
|
|
499
597
|
|
|
500
598
|
> **Tests are scaffolded and run WITHIN Layer 3, not deferred to step-07.**
|
|
@@ -69,6 +69,39 @@ const applicationRoutes: ApplicationRouteExtensions = {
|
|
|
69
69
|
|
|
70
70
|
Routes are automatically injected into BOTH standard (`/{application}/...`) and tenant-prefixed (`/t/:slug/{application}/...`) route trees by `mergeRoutes()`. No manual duplication needed.
|
|
71
71
|
|
|
72
|
+
#### RULE — Route Ordering in applicationRoutes
|
|
73
|
+
|
|
74
|
+
> **CRITICAL:** Routes within each application key MUST follow static-before-dynamic order.
|
|
75
|
+
> React Router matches top-to-bottom — a `:id` route placed before a `dashboard` route
|
|
76
|
+
> will match `dashboard` as an `id` parameter → 404.
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
const applicationRoutes: ApplicationRouteExtensions = {
|
|
80
|
+
'human-resources': [
|
|
81
|
+
// ✅ CORRECT ORDER — static before dynamic
|
|
82
|
+
{ path: 'employees', element: <EmployeesPage /> },
|
|
83
|
+
{ path: 'employees/create', element: <CreateEmployeePage /> },
|
|
84
|
+
{ path: 'employees/dashboard', element: <EmployeeDashboardPage /> },
|
|
85
|
+
{ path: 'employees/:id', element: <EmployeeDetailPage /> },
|
|
86
|
+
{ path: 'employees/:id/edit', element: <EditEmployeePage /> },
|
|
87
|
+
|
|
88
|
+
// Redirect routes ALWAYS LAST
|
|
89
|
+
{ path: '', element: <Navigate to="employees" replace /> },
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
// ❌ FORBIDDEN — :id before static routes
|
|
96
|
+
'human-resources': [
|
|
97
|
+
{ path: 'employees/:id', element: <EmployeeDetailPage /> }, // ← WRONG: catches 'dashboard'
|
|
98
|
+
{ path: 'employees/dashboard', element: <DashboardPage /> }, // ← NEVER REACHED
|
|
99
|
+
]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
See `smartstack-layers.md` "RULE — Frontend Route Ordering" for the full ordering specification.
|
|
103
|
+
POST-CHECK C49 detects and BLOCKS this anti-pattern.
|
|
104
|
+
|
|
72
105
|
#### Custom Applications (Pattern A)
|
|
73
106
|
|
|
74
107
|
Custom application keys (any key **not** in the built-in list: `administration`, `support`, `user`, `api`) are fully supported in `applicationRoutes`. `mergeRoutes()` automatically:
|
|
@@ -89,6 +89,21 @@ FOR each entity in analysis.entities[]:
|
|
|
89
89
|
IF endpoints.length === 0 -> ERROR: "Entity {entity.name} has NO endpoints — missing controller"
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
## H. Permission Granularity Check
|
|
93
|
+
|
|
94
|
+
> Validates that section permission modes are respected in the generated permission data.
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
FOR each module:
|
|
98
|
+
FOR each section in anticipatedSections[]:
|
|
99
|
+
IF section.sectionType == "view" OR section.permissionMode == "inherit":
|
|
100
|
+
IF section has own permissions in seedDataCore.permissions[] -> WARNING: "Section '{code}' is type 'view' but has own permissions — should inherit from module"
|
|
101
|
+
IF section.permissionMode == "read-only":
|
|
102
|
+
IF section has create/update/delete permissions -> ERROR: "Section '{code}' is read-only but has write permissions"
|
|
103
|
+
IF section.sectionType == "primary" AND NOT section.permissionMode:
|
|
104
|
+
-> WARNING: "Primary section '{code}' missing explicit permissionMode — defaulting to 'crud'"
|
|
105
|
+
```
|
|
106
|
+
|
|
92
107
|
## Result Aggregation
|
|
93
108
|
|
|
94
109
|
```json
|
|
@@ -100,6 +115,8 @@ FOR each entity in analysis.entities[]:
|
|
|
100
115
|
{ "check": "id-uniqueness", "module": "...", "status": "PASS|ERROR", "details": "..." },
|
|
101
116
|
{ "check": "wireframe-layout", "module": "...", "status": "PASS|ERROR", "details": "..." },
|
|
102
117
|
{ "check": "seeddata-translations", "module": "...", "status": "PASS|ERROR", "details": "..." }
|
|
118
|
+
,
|
|
119
|
+
{ "check": "permission-granularity", "module": "...", "status": "PASS|WARNING|ERROR", "details": "..." }
|
|
103
120
|
]
|
|
104
121
|
}
|
|
105
122
|
```
|
|
@@ -34,13 +34,18 @@
|
|
|
34
34
|
|
|
35
35
|
> **RULE:** Sections = functional zones only. `create`/`edit` are separate pages with own URL routes (`/create` and `/:id/edit`). `detail` is a tabbed page reached from `list`.
|
|
36
36
|
|
|
37
|
-
| featureType | Sections (functional zones) | List page includes | Form pages | Detail page tabs |
|
|
38
|
-
|
|
39
|
-
| data-centric | list | grid, filters, create button | `/create` page, `/:id/edit` page | Infos, {relations} |
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
37
|
+
| featureType | Sections (functional zones) | permissionMode | List page includes | Form pages | Detail page tabs |
|
|
38
|
+
|---|---|---|---|---|---|
|
|
39
|
+
| data-centric | list | `crud` | grid, filters, create button | `/create` page, `/:id/edit` page | Infos, {relations} |
|
|
40
|
+
| data-centric | (detail — implicit) | `inherit` | — | — | — |
|
|
41
|
+
| workflow | list | `crud` | grid, filters, create button | `/create` page, `/:id/edit` page | Infos, {relations}, Historique |
|
|
42
|
+
| workflow | approve | `custom:approve,reject` | — | — | — |
|
|
43
|
+
| integration | list | `crud` | grid, filters, config button | `/create` page, `/:id/edit` page | Infos, Config, Logs |
|
|
44
|
+
| reporting | dashboard | `read-only` | — | — | — |
|
|
45
|
+
| full-module | list | `crud` | grid, filters, create button | `/create` page, `/:id/edit` page | Infos, {relations}, Historique |
|
|
46
|
+
| full-module | dashboard | `read-only` | — | — | — |
|
|
47
|
+
|
|
48
|
+
> **RULE:** `detail` is NEVER a section with its own permission set. It is always `sectionType: view`, `permissionMode: inherit`. Detail pages inherit permissions from their parent module.
|
|
44
49
|
|
|
45
50
|
## Component Generation Rules
|
|
46
51
|
|
|
@@ -357,13 +357,12 @@ IF workflow_mode = "project":
|
|
|
357
357
|
})
|
|
358
358
|
|
|
359
359
|
Output path: docs/business-analyse/v{version}/index.json
|
|
360
|
-
Thematic files
|
|
360
|
+
Thematic files — EXACTLY these 3, NO OTHERS:
|
|
361
361
|
- cadrage.json
|
|
362
362
|
- validation.json
|
|
363
363
|
- consolidation.json
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
// Do NOT create them at project/app level — empty files mislead downstream tools.
|
|
364
|
+
FORBIDDEN: entities.json, rules.json, usecases.json, permissions.json, screens.json, handoff.json
|
|
365
|
+
Creating any FORBIDDEN file at project level is a BLOCKING ERROR.
|
|
367
366
|
|
|
368
367
|
Store:
|
|
369
368
|
project_id: string // PROJ-NNN
|
|
@@ -394,18 +393,29 @@ ELSE:
|
|
|
394
393
|
})
|
|
395
394
|
|
|
396
395
|
Output path: docs/{app}/business-analyse/v{version}/index.json
|
|
397
|
-
Thematic files
|
|
396
|
+
Thematic files — EXACTLY these 3, NO OTHERS:
|
|
398
397
|
- cadrage.json
|
|
399
398
|
- validation.json
|
|
400
|
-
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
// Do NOT create them at app level — empty files mislead derive-prd and ralph-loop.
|
|
399
|
+
- consolidation.json
|
|
400
|
+
FORBIDDEN: entities.json, rules.json, usecases.json, permissions.json, screens.json, handoff.json
|
|
401
|
+
Creating any FORBIDDEN file at application level is a BLOCKING ERROR.
|
|
404
402
|
```
|
|
405
403
|
|
|
406
404
|
> **Note:** In project mode, per-application index.json files are created later in step-01-cadrage.
|
|
407
405
|
> In single-app mode, step-02 (structure) determines if it's single or multi-module.
|
|
408
406
|
|
|
407
|
+
### POST-CREATE VERIFICATION (MANDATORY)
|
|
408
|
+
|
|
409
|
+
After creating thematic files, verify no forbidden files were created:
|
|
410
|
+
|
|
411
|
+
```
|
|
412
|
+
List all *.json files in {docs_dir}/
|
|
413
|
+
Expected (project/application): [index.json, cadrage.json, validation.json, consolidation.json]
|
|
414
|
+
If any file NOT in expected list is found → DELETE it immediately + log WARNING
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
This guard catches any accidental creation of module-only files at the wrong scope.
|
|
418
|
+
|
|
409
419
|
## Step 10: Update Config
|
|
410
420
|
|
|
411
421
|
Update `.business-analyse/config.json` with new feature information.
|
|
@@ -72,16 +72,27 @@ For each module, anticipate the navigation structure:
|
|
|
72
72
|
For each section:
|
|
73
73
|
- code (kebab-case, e.g., "list", "detail", "dashboard")
|
|
74
74
|
- label (display name)
|
|
75
|
+
- sectionType: primary | functional | view | embedded
|
|
76
|
+
- permissionMode: crud | custom | read-only | inherit
|
|
75
77
|
- resources: [
|
|
76
78
|
{ code, type (SmartTable|SmartForm|SmartCard|SmartKanban|SmartDashboard|SmartFilter), label }
|
|
77
79
|
]
|
|
78
80
|
```
|
|
79
81
|
|
|
82
|
+
**Section classification rules:**
|
|
83
|
+
|
|
84
|
+
| sectionType | permissionMode | When to use | Examples |
|
|
85
|
+
|---|---|---|---|
|
|
86
|
+
| `primary` | `crud` | Main entry point of the module, visible in menu | list |
|
|
87
|
+
| `functional` | `crud` or `custom` | Independent functional zone with own access control | approve, import, planning |
|
|
88
|
+
| `view` | `inherit` | Subordinate view reached from a primary section | detail, edit |
|
|
89
|
+
| `embedded` | `read-only` | Widget or tab embedded in another section | dashboard (when embedded in module) |
|
|
90
|
+
|
|
80
91
|
Common patterns:
|
|
81
|
-
- **Data-centric module**: list (
|
|
82
|
-
- **Workflow module**: list + detail +
|
|
83
|
-
- **Reporting module**: dashboard (
|
|
84
|
-
- **Full module**: list + detail + dashboard
|
|
92
|
+
- **Data-centric module**: list (`primary`/`crud`) + detail (`view`/`inherit`)
|
|
93
|
+
- **Workflow module**: list (`primary`/`crud`) + detail (`view`/`inherit`) + approve (`functional`/`custom`)
|
|
94
|
+
- **Reporting module**: dashboard (`primary`/`read-only`) + detail (`view`/`inherit`)
|
|
95
|
+
- **Full module**: list (`primary`/`crud`) + detail (`view`/`inherit`) + dashboard (`embedded`/`read-only`)
|
|
85
96
|
|
|
86
97
|
### 4. Dependency Graph
|
|
87
98
|
|
|
@@ -111,6 +122,9 @@ For EACH identified element, ask yourself:
|
|
|
111
122
|
- Does this module need a dashboard?
|
|
112
123
|
- Is the list/detail pattern sufficient or are there other views?
|
|
113
124
|
- Are there workflow steps that need dedicated sections?
|
|
125
|
+
- Does this section need its own access control? If not → `view` or `embedded`
|
|
126
|
+
- Is this a read-only view (dashboard, balances, statistics)? If yes → `permissionMode: read-only`
|
|
127
|
+
- Is this section reached by clicking a row in another section? If yes → always `view`
|
|
114
128
|
|
|
115
129
|
**Resources:**
|
|
116
130
|
- Is SmartTable the right component for this list?
|
|
@@ -159,8 +173,8 @@ Write via ba-writer:
|
|
|
159
173
|
"priority": "must",
|
|
160
174
|
"entities": ["Employee", "Contract"],
|
|
161
175
|
"anticipatedSections": [
|
|
162
|
-
{ "code": "list", "label": "Liste", "resources": [{ "code": "employees-grid", "type": "SmartTable" }] },
|
|
163
|
-
{ "code": "detail", "label": "Fiche", "resources": [{ "code": "employee-form", "type": "SmartForm" }] }
|
|
176
|
+
{ "code": "list", "label": "Liste", "sectionType": "primary", "permissionMode": "crud", "resources": [{ "code": "employees-grid", "type": "SmartTable" }] },
|
|
177
|
+
{ "code": "detail", "label": "Fiche", "sectionType": "view", "permissionMode": "inherit", "resources": [{ "code": "employee-form", "type": "SmartForm" }] }
|
|
164
178
|
]
|
|
165
179
|
}
|
|
166
180
|
],
|
|
@@ -123,6 +123,10 @@ Define the permission matrix:
|
|
|
123
123
|
}
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
+
**Auto-detection rules:**
|
|
127
|
+
- If the cadrage mentions "export", "Excel", "CSV", or "télécharger" → automatically add `.export` permission and an export use case (UC-{PREFIX}-EXPORT)
|
|
128
|
+
- If the cadrage mentions "import", "importer", "upload" → automatically add `.import` permission and an import use case (UC-{PREFIX}-IMPORT)
|
|
129
|
+
|
|
126
130
|
### F. Interface Specs — Delegated to /ba-design-ui
|
|
127
131
|
|
|
128
132
|
> **Screen specifications are NOT produced in this step.**
|
|
@@ -163,6 +167,9 @@ Before advancing to step 04, verify:
|
|
|
163
167
|
- [ ] All entity relationships reference existing entities
|
|
164
168
|
- [ ] All UC business rule references exist
|
|
165
169
|
- [ ] All permission paths follow the convention
|
|
170
|
+
- [ ] Sections with `sectionType: view` do NOT appear in `permissionPaths` (they inherit from module)
|
|
171
|
+
- [ ] Sections with `permissionMode: read-only` only have `.read` in `permissionPaths` (no create/update/delete)
|
|
172
|
+
- [ ] Sections with `permissionMode: inherit` have ZERO entries in `permissionPaths`
|
|
166
173
|
|
|
167
174
|
## Transition
|
|
168
175
|
|
|
@@ -423,20 +423,8 @@ ba-writer.enrichSection({
|
|
|
423
423
|
// Update status
|
|
424
424
|
ba-writer.updateStatus({feature_id}, "consolidated");
|
|
425
425
|
|
|
426
|
-
//
|
|
427
|
-
|
|
428
|
-
// Module-level data lives in module directories — do NOT declare empty app-level files.
|
|
429
|
-
ba-writer.enrichSection({
|
|
430
|
-
featureId: {feature_id},
|
|
431
|
-
section: "files",
|
|
432
|
-
data: {
|
|
433
|
-
// Keep only files that exist and have content at app level
|
|
434
|
-
cadrage: { path: "cadrage.json", hash: "framed", lastModified: now() },
|
|
435
|
-
validation: { path: "validation.json", hash: "consolidated", lastModified: now() }
|
|
436
|
-
// REMOVED: entities, rules, usecases, permissions, screens, handoff
|
|
437
|
-
// These exist only at module level (flat-file architecture)
|
|
438
|
-
}
|
|
439
|
-
});
|
|
426
|
+
// Clean up any forbidden files that may have been created at app level
|
|
427
|
+
ba-writer.cleanupAppLevelFiles({ featureId: {feature_id} });
|
|
440
428
|
|
|
441
429
|
// Save workflow state for resume support
|
|
442
430
|
ba-writer.enrichSection({
|
|
@@ -123,6 +123,26 @@ if (actualNavRoute !== permission_path) {
|
|
|
123
123
|
console.warn(` Got: "${actualNavRoute}"`);
|
|
124
124
|
// Warning only — proceed
|
|
125
125
|
}
|
|
126
|
+
|
|
127
|
+
// Validate segment count
|
|
128
|
+
const segments = actualNavRoute ? actualNavRoute.split('.').length : 0;
|
|
129
|
+
if (segments < 2) {
|
|
130
|
+
console.error(`BLOCKING: NavRoute "${actualNavRoute}" has only ${segments} segment(s) — minimum 2 required`);
|
|
131
|
+
console.error(` Format: "app.module" (2 segments) or "app.module.section" (3 segments)`);
|
|
132
|
+
STOP;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// If entity is in a section subfolder, NavRoute must have 3+ segments
|
|
136
|
+
// Check: controller path has 3+ levels after Controllers/ → section-level
|
|
137
|
+
const controllerDir = controllerCall.controllerFile.replace(/[^/]*$/, '');
|
|
138
|
+
const depthAfterControllers = controllerDir.split('Controllers/')[1]?.split('/').filter(Boolean).length || 0;
|
|
139
|
+
if (depthAfterControllers >= 2 && segments < 3) {
|
|
140
|
+
console.warn(`WARNING: Controller is in a section subfolder (depth=${depthAfterControllers})`);
|
|
141
|
+
console.warn(` but NavRoute "${actualNavRoute}" has only ${segments} segments`);
|
|
142
|
+
console.warn(` Expected 3 segments: "app.module.section"`);
|
|
143
|
+
console.warn(` A 2-segment NavRoute on a section controller causes API 404s`);
|
|
144
|
+
// Warning — developer should verify and fix
|
|
145
|
+
}
|
|
126
146
|
```
|
|
127
147
|
|
|
128
148
|
---
|
|
@@ -30,6 +30,12 @@ From `usecases.json > useCases[]`:
|
|
|
30
30
|
|
|
31
31
|
Include: Service per UC cluster, DTOs for API contracts, Validators (FluentValidation), Query handlers
|
|
32
32
|
|
|
33
|
+
**Validator generation rules:**
|
|
34
|
+
- Every entity with Create and/or Update use cases MUST have a corresponding Validator
|
|
35
|
+
- Validators MUST be registered in DI (`services.AddScoped<IValidator<CreateXxxDto>, CreateXxxValidator>()`)
|
|
36
|
+
- Validators MUST be injected into controllers/services that handle POST/PUT operations
|
|
37
|
+
- NO TODO/placeholder comments allowed in Validators — all validation rules from business rules (BR-VAL-*) must be implemented
|
|
38
|
+
|
|
33
39
|
## 4.3 Infrastructure Files
|
|
34
40
|
|
|
35
41
|
From `entities.json > entities[]`:
|
|
@@ -48,7 +54,8 @@ Generated from `usecases.json` + `entities.json`:
|
|
|
48
54
|
|
|
49
55
|
```json
|
|
50
56
|
"api": [
|
|
51
|
-
{ "path": "src/API/Controllers/{ApplicationName}/{EntityName}Controller.cs", "type": "ApiController", "linkedUCs": [], "linkedFRs": [], "module": "{moduleCode}" }
|
|
57
|
+
{ "path": "src/API/Controllers/{ApplicationName}/{EntityName}Controller.cs", "type": "ApiController", "navRoute": "{app-kebab}.{module-kebab}", "isSection": false, "linkedUCs": [], "linkedFRs": [], "module": "{moduleCode}" },
|
|
58
|
+
{ "path": "src/API/Controllers/{ApplicationName}/{ModuleName}/{SectionEntityName}Controller.cs", "type": "ApiController", "navRoute": "{app-kebab}.{module-kebab}.{section-kebab}", "isSection": true, "linkedUCs": [], "linkedFRs": [], "module": "{moduleCode}" }
|
|
52
59
|
]
|
|
53
60
|
```
|
|
54
61
|
|
|
@@ -80,6 +87,23 @@ From `screens.json > screens[]` and `usecases.json > useCases[]`:
|
|
|
80
87
|
|
|
81
88
|
**Dashboard acceptance criteria:** Chart library (Recharts), chart types matching spec, KPI cards, filters, CSS variables, responsive layout, wireframe-matching positions.
|
|
82
89
|
|
|
90
|
+
## 4.5b Notification Files (CONDITIONAL)
|
|
91
|
+
|
|
92
|
+
> Generated only when `lifeCycles[].transitions[].effects[]` contains entries with `type: "notification"`.
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
"notifications": [
|
|
96
|
+
{ "path": "src/Application/Notifications/{ApplicationName}/{ModuleName}/{NotificationName}Notification.cs", "type": "Notification", "linkedUCs": [], "module": "{moduleCode}", "description": "Notification triggered by lifecycle transition" },
|
|
97
|
+
{ "path": "src/Application/Notifications/{ApplicationName}/{ModuleName}/{NotificationName}NotificationHandler.cs", "type": "NotificationHandler", "linkedUCs": [], "module": "{moduleCode}", "description": "Handler that sends in-app/email notification" }
|
|
98
|
+
]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Generation rules:**
|
|
102
|
+
- One Notification + Handler pair per unique `notification` effect in lifecycle transitions
|
|
103
|
+
- NotificationName derived from transition: `{Entity}{TransitionName}` (e.g., `OrderApproved`)
|
|
104
|
+
- Handler must use `INotificationService` for in-app and `IEmailService` for email type
|
|
105
|
+
- Notification must include: recipient resolution, template reference, and payload mapping
|
|
106
|
+
|
|
83
107
|
## 4.6 SeedData Files
|
|
84
108
|
|
|
85
109
|
**OBLIGATORY: 2 app-level CORE + per module CORE (NavigationModule + NavigationSections + Permissions + Roles) + business per module:**
|
|
@@ -127,7 +127,9 @@ const seedDataCore = {
|
|
|
127
127
|
? `/${toKebabCase(appCode)}/${toKebabCase(m.code)}/:id`
|
|
128
128
|
: `/${toKebabCase(appCode)}/${toKebabCase(m.code)}/${toKebabCase(s.code)}`,
|
|
129
129
|
displayOrder: (j + 1) * 10,
|
|
130
|
-
navigation: s.code === "detail" ? "hidden" : "visible"
|
|
130
|
+
navigation: s.code === "detail" ? "hidden" : "visible",
|
|
131
|
+
// Propagate permissionMode for section-level permission generation
|
|
132
|
+
permissionMode: s.permissionMode || (s.code === "detail" ? "inherit" : s.code === "dashboard" ? "read-only" : "crud")
|
|
131
133
|
}))
|
|
132
134
|
);
|
|
133
135
|
|