@atlashub/smartstack-cli 4.25.0 → 4.27.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/index.js +765 -517
- package/dist/index.js.map +1 -1
- 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 -42
- 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/post-checks.md +48 -0
- package/templates/skills/apex/steps/step-03-execute.md +63 -2
- package/templates/skills/ba-generate-html/references/data-build.md +22 -13
- package/templates/skills/ba-generate-html/references/data-mapping.md +33 -24
- package/templates/skills/ba-generate-html/steps/step-01-collect.md +15 -6
- package/templates/skills/ba-generate-html/steps/step-02-build-data.md +37 -22
- package/templates/skills/business-analyse/steps/step-00-init.md +22 -14
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +3 -0
- package/templates/skills/derive-prd/steps/step-01-transform.md +6 -2
- package/templates/skills/derive-prd/steps/step-02-export.md +12 -0
- package/templates/skills/ralph-loop/references/category-completeness.md +3 -3
- package/templates/skills/ralph-loop/references/compact-loop.md +81 -14
- package/templates/skills/ralph-loop/references/init-resume-recovery.md +1 -1
- package/templates/skills/ralph-loop/references/module-transition.md +30 -5
- package/templates/skills/ralph-loop/references/multi-module-queue.md +4 -4
- package/templates/skills/ralph-loop/references/section-splitting.md +6 -6
- package/templates/skills/ralph-loop/steps/step-01-task.md +14 -4
- package/templates/skills/ralph-loop/steps/step-02-execute.md +14 -6
- package/templates/skills/ralph-loop/steps/step-03-commit.md +15 -4
- package/templates/skills/ralph-loop/steps/step-04-check.md +19 -5
- package/templates/skills/ralph-loop/steps/step-05-report.md +35 -3
package/package.json
CHANGED
|
@@ -18,17 +18,18 @@ Write and update granular JSON files for project-level (multi-app), application-
|
|
|
18
18
|
- Module-level: `docs/{app}/{module}/business-analyse/v{X.Y}/index.json` + thematic files
|
|
19
19
|
|
|
20
20
|
**Thematic files (v2 granular architecture):**
|
|
21
|
-
- `index.json` — metadata, version, hash manifest, module registry
|
|
22
|
-
- `cadrage.json` — stakeholders, problem/vision, risks, acceptance criteria
|
|
23
|
-
- `
|
|
24
|
-
- `
|
|
25
|
-
- `
|
|
26
|
-
- `
|
|
27
|
-
- `
|
|
28
|
-
- `
|
|
29
|
-
- `
|
|
30
|
-
- `
|
|
31
|
-
- `
|
|
21
|
+
- `index.json` — metadata, version, hash manifest, module registry (ALL scopes)
|
|
22
|
+
- `cadrage.json` — stakeholders, problem/vision, risks, acceptance criteria (ALL scopes)
|
|
23
|
+
- `validation.json` — validation rules and consistency checks (ALL scopes)
|
|
24
|
+
- `consolidation.json` — cross-module interactions and E2E flows (application/project ONLY)
|
|
25
|
+
- `navigation.json` — navigation tree (application/project ONLY, created by ba-design-ui)
|
|
26
|
+
- `entities.json` — entity definitions with attributes and relationships (**MODULE ONLY**)
|
|
27
|
+
- `rules.json` — business rules with categories and conditions (**MODULE ONLY**)
|
|
28
|
+
- `usecases.json` — use cases and functional requirements (**MODULE ONLY**)
|
|
29
|
+
- `permissions.json` — permission matrix and role assignments (**MODULE ONLY**)
|
|
30
|
+
- `screens.json` — UI wireframes and navigation (**MODULE ONLY**)
|
|
31
|
+
- `handoff.json` — complexity, file catalog, BR-to-code mapping (**MODULE ONLY**)
|
|
32
|
+
- `review.json` — preserved review comments and change summary (ALL scopes)
|
|
32
33
|
|
|
33
34
|
> **Backward compatibility:** If only 1 application, the project level is NOT created. The application-level index.json remains the master.
|
|
34
35
|
|
|
@@ -62,9 +63,12 @@ Create initial index.json and empty thematic files with metadata and draft statu
|
|
|
62
63
|
- For project scope: modules: []
|
|
63
64
|
- For application scope: modules: []
|
|
64
65
|
- For module scope: (no modules array)
|
|
65
|
-
5. Create empty thematic files
|
|
66
|
-
- Project
|
|
67
|
-
-
|
|
66
|
+
5. Create empty thematic files — ONLY the files listed for the scope. Creating ANY unlisted file is a **BLOCKING ERROR**.
|
|
67
|
+
- Project: cadrage.json, validation.json, consolidation.json — **EXACTLY 3 files, NO OTHERS**
|
|
68
|
+
- Application: cadrage.json, validation.json, consolidation.json — **EXACTLY 3 files, NO OTHERS**
|
|
69
|
+
- Module: entities.json, rules.json, usecases.json, permissions.json, screens.json, validation.json, handoff.json — **EXACTLY 7 files**
|
|
70
|
+
|
|
71
|
+
**FORBIDDEN at project/application level:** entities.json, rules.json, usecases.json, permissions.json, screens.json, handoff.json — these exist ONLY at module level.
|
|
68
72
|
6. Update `.business-analyse/config.json` with new lastFeatureId
|
|
69
73
|
7. IF scope = "module" AND applicationRef provided AND moduleCode provided:
|
|
70
74
|
a. Read master index.json (via applicationRef FEAT-NNN)
|
|
@@ -107,7 +111,8 @@ Create a project-level index.json for multi-application analysis. Only used when
|
|
|
107
111
|
- fileHashes: {}
|
|
108
112
|
- applications: []
|
|
109
113
|
- applicationDependencyGraph: {}
|
|
110
|
-
5. Create thematic files
|
|
114
|
+
5. Create thematic files — **EXACTLY 3 files, NO OTHERS:** cadrage.json, validation.json, consolidation.json
|
|
115
|
+
**FORBIDDEN at project level:** entities.json, rules.json, usecases.json, permissions.json, screens.json, handoff.json — these exist ONLY at module level.
|
|
111
116
|
6. Update `.business-analyse/config.json` with new lastProjectId
|
|
112
117
|
7. Deploy schemas to `docs/business-analyse/schemas/` (including project-schema.json)
|
|
113
118
|
8. Return project ID (PROJ-NNN) and path
|
|
@@ -174,6 +179,10 @@ Write a complete thematic file and update its hash in index.json.
|
|
|
174
179
|
|
|
175
180
|
**Process:**
|
|
176
181
|
1. Find and read index.json (use findFeature if given ID)
|
|
182
|
+
1b. **SCOPE GUARD (BLOCKING):** If index.json scope is "project" or "application", verify themeName is ALLOWED:
|
|
183
|
+
- Allowed: [cadrage, validation, consolidation, navigation, review]
|
|
184
|
+
- FORBIDDEN: [entities, rules, usecases, permissions, screens, handoff]
|
|
185
|
+
If themeName is FORBIDDEN → **REJECT with BLOCKING ERROR.** Do NOT create the file.
|
|
177
186
|
2. Determine thematic filename: `{themeName}.json`
|
|
178
187
|
3. Create full path: `{version_dir}/{themeName}.json`
|
|
179
188
|
4. Write thematic file with pretty-print (2-space indent)
|
|
@@ -331,6 +340,20 @@ Increment the module loop counter in the master index.json.
|
|
|
331
340
|
9. Write back index.json
|
|
332
341
|
10. Return new index and whether loop is complete
|
|
333
342
|
|
|
343
|
+
### cleanupAppLevelFiles
|
|
344
|
+
Remove forbidden thematic files at project/application level and clean their entries from fileHashes.
|
|
345
|
+
|
|
346
|
+
**Input:** featureId: FEAT-NNN
|
|
347
|
+
|
|
348
|
+
**Process:**
|
|
349
|
+
1. Read index.json (verify scope is "project" or "application")
|
|
350
|
+
2. FORBIDDEN = [entities.json, rules.json, usecases.json, permissions.json, screens.json, handoff.json]
|
|
351
|
+
3. For each FORBIDDEN file: if file exists in version directory → DELETE it
|
|
352
|
+
4. For each FORBIDDEN file: if referenced in fileHashes → REMOVE entry
|
|
353
|
+
5. If `files` property exists in index.json: remove entries for forbidden files
|
|
354
|
+
6. Update metadata.updatedAt, write index.json
|
|
355
|
+
7. Return list of cleaned files
|
|
356
|
+
|
|
334
357
|
### createVersion
|
|
335
358
|
Create a new version for refactoring or major changes.
|
|
336
359
|
|
|
@@ -466,43 +489,29 @@ docs/business-analyse/
|
|
|
466
489
|
v1.0/
|
|
467
490
|
index.json ← PROJECT metadata
|
|
468
491
|
cadrage.json
|
|
469
|
-
entities.json
|
|
470
|
-
rules.json
|
|
471
|
-
usecases.json
|
|
472
|
-
permissions.json
|
|
473
|
-
screens.json
|
|
474
492
|
validation.json
|
|
475
493
|
consolidation.json
|
|
494
|
+
# NO entities/rules/usecases/permissions/screens/handoff — MODULE ONLY
|
|
476
495
|
|
|
477
496
|
docs/{app}/business-analyse/
|
|
478
497
|
v1.0/
|
|
479
498
|
index.json ← APPLICATION metadata
|
|
480
499
|
cadrage.json
|
|
481
|
-
entities.json
|
|
482
|
-
rules.json
|
|
483
|
-
usecases.json
|
|
484
|
-
permissions.json
|
|
485
|
-
screens.json
|
|
486
500
|
validation.json
|
|
487
501
|
consolidation.json
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
cadrage.json
|
|
491
|
-
entities.json
|
|
492
|
-
...
|
|
502
|
+
navigation.json ← (created by ba-design-ui)
|
|
503
|
+
# NO entities/rules/usecases/permissions/screens/handoff — MODULE ONLY
|
|
493
504
|
|
|
494
505
|
docs/{app}/{module}/business-analyse/
|
|
495
506
|
v1.0/
|
|
496
507
|
index.json ← MODULE metadata
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
508
|
+
entities.json
|
|
509
|
+
rules.json
|
|
510
|
+
usecases.json
|
|
511
|
+
permissions.json
|
|
512
|
+
screens.json
|
|
500
513
|
validation.json
|
|
501
514
|
handoff.json
|
|
502
|
-
v1.1/
|
|
503
|
-
index.json
|
|
504
|
-
discovery.json
|
|
505
|
-
...
|
|
506
515
|
```
|
|
507
516
|
|
|
508
517
|
Versions are stored as separate directories. Each directory contains index.json + thematic files.
|
|
@@ -722,11 +731,6 @@ if (estimatedNewSize > 500 * 1024) { // 500KB
|
|
|
722
731
|
"fileHashes": {
|
|
723
732
|
"index.json": "pqr678...",
|
|
724
733
|
"cadrage.json": "stu901...",
|
|
725
|
-
"entities.json": "vwx234...",
|
|
726
|
-
"rules.json": "yza567...",
|
|
727
|
-
"usecases.json": "bcd890...",
|
|
728
|
-
"permissions.json": "efg123...",
|
|
729
|
-
"screens.json": "hij456...",
|
|
730
734
|
"validation.json": "klm789...",
|
|
731
735
|
"consolidation.json": "nop012..."
|
|
732
736
|
},
|
|
@@ -7,10 +7,8 @@
|
|
|
7
7
|
"FailOnMigrationError": true,
|
|
8
8
|
"EnableDevSeeding": false,
|
|
9
9
|
"CorsOrigins": [
|
|
10
|
-
"http://localhost:
|
|
11
|
-
"http://localhost:
|
|
12
|
-
"http://localhost:5175",
|
|
13
|
-
"http://localhost:6173"
|
|
10
|
+
"http://localhost:3000",
|
|
11
|
+
"http://localhost:5173"
|
|
14
12
|
]
|
|
15
13
|
},
|
|
16
14
|
"Jwt": {
|
|
@@ -34,7 +32,7 @@
|
|
|
34
32
|
"SecretExpiresAt": "",
|
|
35
33
|
"CallbackPath": "/api/auth/google/callback"
|
|
36
34
|
},
|
|
37
|
-
"FrontendUrl": "http://localhost:
|
|
35
|
+
"FrontendUrl": "http://localhost:3000"
|
|
38
36
|
},
|
|
39
37
|
"Session": {
|
|
40
38
|
"IdleTimeoutMinutes": 20,
|
|
@@ -125,7 +123,7 @@
|
|
|
125
123
|
"Provider": "Development",
|
|
126
124
|
"FromEmail": "noreply@{{ProjectDomain}}",
|
|
127
125
|
"FromName": "{{ProjectName}}",
|
|
128
|
-
"BaseUrl": "http://localhost:
|
|
126
|
+
"BaseUrl": "http://localhost:3000",
|
|
129
127
|
"TokenExpiration": {
|
|
130
128
|
"EmailConfirmation": "24:00:00",
|
|
131
129
|
"PasswordReset": "01:00:00"
|
|
@@ -149,6 +149,7 @@ Execute incremental SmartStack development using the APEX methodology. This skil
|
|
|
149
149
|
- **Parallel Agent tool** - Parallel execution for scan (step-01) and within Layer 2/3 (step-03) for multi-entity, unless economy_mode
|
|
150
150
|
- **Tests inline** - Backend tests run after Layer 2, frontend tests run after Layer 3 (max 3 fix iterations each). Step-07 = final sweep (security + coverage).
|
|
151
151
|
- **Exception: seed data** — The templates in core-seed-data.md and person-extension-pattern.md are generated directly because no MCP tool covers seed data creation. This is a documented exception to the "orchestrate, never generate" rule.
|
|
152
|
+
- **Frontend pages: ALWAYS via Skill("ui-components")** — economy_mode affects parallelization only, NOT whether /ui-components is called. NEVER generate .tsx pages directly, even in delegate or economy mode.
|
|
152
153
|
- **Save outputs** if `{save_mode}` = true
|
|
153
154
|
- **Commits per layer** - Atomic commits after each execution layer
|
|
154
155
|
- **Delegate mode** (`-d`): Read PRD context, skip challenge questions, auto+economy mode implied. Used when `/ralph-loop` delegates code generation to `/apex`.
|
|
@@ -82,8 +82,25 @@ questions:
|
|
|
82
82
|
multiSelect: true
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
**Reserved codes (NOT valid sections):**
|
|
86
|
+
- `detail` — auto-generated as `/:id` route when "list" section exists
|
|
87
|
+
- `create` — auto-generated as `/create` route when "list" section exists
|
|
88
|
+
- `edit` — auto-generated as `/:id/edit` route when "list" section exists
|
|
89
|
+
|
|
90
|
+
These are **view modes**, not sections. A "list" section automatically generates 4 pages: ListPage (index), DetailPage (/:id), CreatePage (/create), EditPage (/:id/edit).
|
|
91
|
+
|
|
85
92
|
**Validation:**
|
|
86
93
|
```
|
|
94
|
+
RESERVED_SECTION_CODES = ["detail", "create", "edit"]
|
|
95
|
+
|
|
96
|
+
IF any section.code IN RESERVED_SECTION_CODES:
|
|
97
|
+
DISPLAY: "'{section.code}' is a view mode, not a section.
|
|
98
|
+
The 'list' section automatically includes detail (/:id), create (/create),
|
|
99
|
+
and edit (/:id/edit) pages. Remove '{section.code}' from your sections."
|
|
100
|
+
→ Remove the offending section(s) from the list
|
|
101
|
+
→ Re-ask the sections question if {sections}.length == 0
|
|
102
|
+
→ DO NOT proceed
|
|
103
|
+
|
|
87
104
|
IF {sections}.length == 0:
|
|
88
105
|
DISPLAY: "Every module must have at least one section. Please select or define at least one."
|
|
89
106
|
→ Re-ask the sections question
|
|
@@ -117,6 +117,21 @@ if [ -n "$APP_TSX" ]; then
|
|
|
117
117
|
fi
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
+
### POST-CHECK S7: Controllers must NOT use Guid.Empty for tenantId/userId (OWASP A01)
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
|
|
124
|
+
if [ -n "$CTRL_FILES" ]; then
|
|
125
|
+
BAD_GUID=$(grep -Pn 'Guid\.Empty' $CTRL_FILES 2>/dev/null)
|
|
126
|
+
if [ -n "$BAD_GUID" ]; then
|
|
127
|
+
echo "BLOCKING (OWASP A01): Controller uses Guid.Empty — tenant isolation bypassed"
|
|
128
|
+
echo "$BAD_GUID"
|
|
129
|
+
echo "Fix: Use _currentTenant.TenantId from ICurrentTenantService"
|
|
130
|
+
exit 1
|
|
131
|
+
fi
|
|
132
|
+
fi
|
|
133
|
+
```
|
|
134
|
+
|
|
120
135
|
---
|
|
121
136
|
|
|
122
137
|
## Backend — Entity, Service & Controller Checks
|
|
@@ -154,6 +169,39 @@ fi
|
|
|
154
169
|
|
|
155
170
|
## Frontend — CSS, Forms, Components, I18n
|
|
156
171
|
|
|
172
|
+
### POST-CHECK C3a: Frontend must not be empty if Layer 3 was planned (BLOCKING)
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# If foundation_mode is false AND App.tsx exists, verify frontend was generated
|
|
176
|
+
APP_TSX=$(find web/ src/ -name "App.tsx" -not -path "*/node_modules/*" 2>/dev/null | head -1)
|
|
177
|
+
if [ -n "$APP_TSX" ]; then
|
|
178
|
+
# Check if applicationRoutes is an empty object
|
|
179
|
+
EMPTY_ROUTES=$(grep -P "applicationRoutes.*=\s*\{[\s/]*\}" "$APP_TSX" 2>/dev/null)
|
|
180
|
+
if [ -n "$EMPTY_ROUTES" ]; then
|
|
181
|
+
echo "BLOCKING: applicationRoutes in App.tsx is empty — Layer 3 frontend was NOT executed"
|
|
182
|
+
echo "Expected: at least one application key with route definitions"
|
|
183
|
+
echo "Fix: Run Layer 3 (scaffold_routes + scaffold_extension + route wiring)"
|
|
184
|
+
exit 1
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
# Check pages/ directory is not empty
|
|
188
|
+
PAGE_COUNT=$(find web/ src/ -path "*/pages/*" -name "*.tsx" -not -path "*/node_modules/*" 2>/dev/null | wc -l)
|
|
189
|
+
if [ "$PAGE_COUNT" -eq 0 ]; then
|
|
190
|
+
echo "BLOCKING: No page components found in pages/ directory"
|
|
191
|
+
echo "Fix: Generate pages via scaffold_extension or /ui-components"
|
|
192
|
+
exit 1
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
# Check navRoutes.generated.ts exists
|
|
196
|
+
NAV_ROUTES=$(find web/ src/ -name "navRoutes.generated.ts" -not -path "*/node_modules/*" 2>/dev/null | head -1)
|
|
197
|
+
if [ -z "$NAV_ROUTES" ]; then
|
|
198
|
+
echo "BLOCKING: navRoutes.generated.ts not found — scaffold_routes was never called"
|
|
199
|
+
echo "Fix: Run scaffold_routes(source: 'controllers', outputFormat: 'applicationRoutes')"
|
|
200
|
+
exit 1
|
|
201
|
+
fi
|
|
202
|
+
fi
|
|
203
|
+
```
|
|
204
|
+
|
|
157
205
|
### POST-CHECK C3: Translation files must exist for all 4 languages (if frontend)
|
|
158
206
|
|
|
159
207
|
```bash
|
|
@@ -290,6 +290,24 @@ 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
|
+
```bash
|
|
302
|
+
# Quick check: all controllers must have [NavRoute] (not just [Route])
|
|
303
|
+
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
|
|
304
|
+
for f in $CTRL_FILES; do
|
|
305
|
+
if ! grep -q "\[NavRoute" "$f" && grep -q "\[Route" "$f"; then
|
|
306
|
+
echo "WARNING: $f has [Route] but no [NavRoute] — add [NavRoute] for route auto-detection"
|
|
307
|
+
fi
|
|
308
|
+
done
|
|
309
|
+
```
|
|
310
|
+
|
|
293
311
|
### Post-Layer 2 Build Gate
|
|
294
312
|
|
|
295
313
|
```bash
|
|
@@ -377,7 +395,18 @@ For each module:
|
|
|
377
395
|
→ All business applications use `<AppLayout />` as layout wrapper
|
|
378
396
|
→ See `references/frontend-route-wiring-app-tsx.md` for full Pattern A/B detection and examples
|
|
379
397
|
→ Verify: `mcp__smartstack__validate_frontend_routes (scope: 'routes')`
|
|
380
|
-
- Pages:
|
|
398
|
+
- Pages per section: When a section exists (e.g., "list"), generate ALL 4 page types:
|
|
399
|
+
→ {Section}ListPage.tsx (index route)
|
|
400
|
+
→ {Section}DetailPage.tsx (/:id route) — click on list item navigates here
|
|
401
|
+
→ Create{Section}Page.tsx (/create route)
|
|
402
|
+
→ Edit{Section}Page.tsx (/:id/edit route)
|
|
403
|
+
"detail" is NEVER a separate section — it's the /:id route of the list section.
|
|
404
|
+
- Pages: **MANDATORY — INVOKE Skill("ui-components")** for ALL page generation.
|
|
405
|
+
⚠ BLOCKING REQUIREMENT: You MUST call Skill("ui-components") for EVERY page (.tsx).
|
|
406
|
+
NEVER generate .tsx page code directly. NEVER write page content in Agent prompts.
|
|
407
|
+
NEVER use Write tool to create pages without first calling Skill("ui-components").
|
|
408
|
+
The skill loads the full style guide + patterns (entity-card, data-table, dashboard-chart, grid-layout).
|
|
409
|
+
Follow smartstack-frontend.md patterns:
|
|
381
410
|
→ React.lazy() for all page imports (named export wrapping)
|
|
382
411
|
→ `<Suspense fallback={<PageLoader />}>` around all lazy components
|
|
383
412
|
→ Page structure: hooks → useEffect(load) → loading → error → content
|
|
@@ -466,7 +495,21 @@ IF NOT economy_mode AND entities.length > 1:
|
|
|
466
495
|
# All agents launched in parallel
|
|
467
496
|
|
|
468
497
|
ELSE:
|
|
469
|
-
# Agent principal handles all entities
|
|
498
|
+
# Economy mode: Agent principal handles all entities SEQUENTIALLY.
|
|
499
|
+
# MANDATORY: You MUST still call Skill("ui-components") for page generation.
|
|
500
|
+
# economy_mode only disables parallel agents — it does NOT skip /ui-components.
|
|
501
|
+
For each entity (sequentially):
|
|
502
|
+
1. MCP scaffold_api_client
|
|
503
|
+
2. MCP scaffold_routes (outputFormat: 'applicationRoutes')
|
|
504
|
+
3. Wire routes to App.tsx (Pattern A/B — see references/frontend-route-wiring-app-tsx.md)
|
|
505
|
+
4. **INVOKE Skill("ui-components")** — pass entity context:
|
|
506
|
+
- Entity: {EntityName}, Module: {ModuleName}, App: {AppName}
|
|
507
|
+
- Page types: List, Detail, Create, Edit (+ Dashboard if applicable)
|
|
508
|
+
- "CSS: Use CSS variables ONLY — bg-[var(--bg-card)], text-[var(--text-primary)]"
|
|
509
|
+
- "Forms: Create/Edit are FULL PAGES with own routes (/create, /:id/edit)"
|
|
510
|
+
- "I18n: ALL text uses t('namespace:key', 'Fallback')"
|
|
511
|
+
5. I18n: 4 JSON files (fr, en, it, de) + register namespace in i18n config
|
|
512
|
+
6. Form tests: co-located .test.tsx for Create and Edit pages
|
|
470
513
|
```
|
|
471
514
|
|
|
472
515
|
### Parallel Agents + TaskCreate Integration
|
|
@@ -495,6 +538,24 @@ When delegating to `/ui-components` skill, include explicit instructions:
|
|
|
495
538
|
- "Forms: Create/Edit forms are FULL PAGES with own routes (e.g., `/create`, `/:id/edit`)."
|
|
496
539
|
- "I18n: ALL text must use `t('namespace:key', 'Fallback')`. Generate JSON files in `src/i18n/locales/`."
|
|
497
540
|
|
|
541
|
+
### HARD RULE — /ui-components is NON-NEGOTIABLE
|
|
542
|
+
|
|
543
|
+
> **VIOLATION CHECK:** If ANY .tsx page file was created by Write tool WITHOUT
|
|
544
|
+
> a prior Skill("ui-components") call in this execution, the frontend layer is INVALID.
|
|
545
|
+
>
|
|
546
|
+
> **You MUST NOT:**
|
|
547
|
+
> - Generate .tsx page code in Agent prompts and dispatch to Snipper agents
|
|
548
|
+
> - Write page content directly via the Write tool
|
|
549
|
+
> - Copy-paste page templates from your knowledge
|
|
550
|
+
>
|
|
551
|
+
> **You MUST:**
|
|
552
|
+
> - Call Skill("ui-components") which loads the style guide, responsive guidelines,
|
|
553
|
+
> accessibility rules, and pattern files (entity-card, data-table, dashboard-chart, grid-layout, kanban)
|
|
554
|
+
> - Let /ui-components generate all pages with correct conventions
|
|
555
|
+
>
|
|
556
|
+
> **Why this matters:** Without the skill, pages miss CSS variables, DataTable/EntityCard,
|
|
557
|
+
> memo()/useCallback, responsive mobile-first design, and accessibility patterns.
|
|
558
|
+
|
|
498
559
|
### Frontend Tests Inline
|
|
499
560
|
|
|
500
561
|
> **Tests are scaffolded and run WITHIN Layer 3, not deferred to step-07.**
|
|
@@ -63,11 +63,13 @@ const FEATURE_DATA = {
|
|
|
63
63
|
],
|
|
64
64
|
moduleSpecs: {
|
|
65
65
|
// CRITICAL: Must have ONE entry per module with ALL module data
|
|
66
|
+
// FLAT-FILE: Data comes from collected_data.modules[moduleCode] (flat files), NOT module index.json
|
|
66
67
|
"{moduleCode}": {
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
// const mod = collected_data.modules[moduleCode];
|
|
69
|
+
useCases: mod.usecases?.useCases || [],
|
|
70
|
+
businessRules: mod.rules?.rules || [],
|
|
69
71
|
// ENTITY SAFETY NET: map fields[] → attributes[] if agent deviated from canonical format
|
|
70
|
-
entities: (
|
|
72
|
+
entities: (mod.entities?.entities || []).map(ent => ({
|
|
71
73
|
name: ent.name,
|
|
72
74
|
description: ent.description || "",
|
|
73
75
|
attributes: (ent.attributes || []).length > 0
|
|
@@ -79,8 +81,8 @@ const FEATURE_DATA = {
|
|
|
79
81
|
: (ent.fields || []).filter(f => f.foreignKey)
|
|
80
82
|
.map(f => `${f.foreignKey.table} (N:1) - ${f.description || f.name}`)
|
|
81
83
|
})),
|
|
82
|
-
permissions:
|
|
83
|
-
apiEndpoints:
|
|
84
|
+
permissions: mod.permissions?.matrix || mod.permissions?.permissions || [],
|
|
85
|
+
apiEndpoints: mod.usecases?.apiEndpoints || []
|
|
84
86
|
}
|
|
85
87
|
},
|
|
86
88
|
consolidation: {
|
|
@@ -102,10 +104,15 @@ const FEATURE_DATA = {
|
|
|
102
104
|
### Build Process
|
|
103
105
|
|
|
104
106
|
1. Extract metadata from master index.json
|
|
105
|
-
2. Extract cadrage from master
|
|
106
|
-
3. Extract stakeholders from master.stakeholders
|
|
107
|
+
2. Extract cadrage from master cadrage.json (CONVERT scope keys)
|
|
108
|
+
3. Extract stakeholders from master cadrage.stakeholders
|
|
107
109
|
4. Iterate ALL modules and populate moduleSpecs (THIS IS CRITICAL — empty moduleSpecs = BUG)
|
|
108
|
-
5. For EACH module,
|
|
110
|
+
5. For EACH module, read FLAT FILES from `collected_data.modules[moduleCode]`:
|
|
111
|
+
- `entities.json` → entities
|
|
112
|
+
- `rules.json` → businessRules
|
|
113
|
+
- `usecases.json` → useCases
|
|
114
|
+
- `permissions.json` → permissions
|
|
115
|
+
- `screens.json` → wireframes
|
|
109
116
|
6. Extract consolidation data (integrations, shared entities, E2E flows)
|
|
110
117
|
7. Extract handoff section (complexity, strategy, module order, file counts)
|
|
111
118
|
|
|
@@ -119,10 +126,12 @@ const FEATURE_DATA = {
|
|
|
119
126
|
const EMBEDDED_ARTIFACTS = {
|
|
120
127
|
wireframes: {
|
|
121
128
|
// PER-MODULE keyed object (NOT a flat array)
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
[moduleCode]:
|
|
129
|
+
// FLAT-FILE: Read from screens.json in each module directory (collected in step-01)
|
|
130
|
+
// const mod = collected_data.modules[moduleCode];
|
|
131
|
+
// const screens = mod.screens?.screens || [];
|
|
132
|
+
[moduleCode]: screens
|
|
133
|
+
.filter(s => s.wireframe || s.mockup || s.mockupFormat)
|
|
134
|
+
.map(wf => ({
|
|
126
135
|
screen: wf.screen || wf.name || wf.title || wf.id || "", // SAFETY NET: fallback name/title/id → screen
|
|
127
136
|
section: wf.section || "", // e.g. "list"
|
|
128
137
|
format: wf.mockupFormat || "ascii", // RENAME: mockupFormat → format
|
|
@@ -160,7 +169,7 @@ const EMBEDDED_ARTIFACTS = {
|
|
|
160
169
|
|
|
161
170
|
### Artifact Gathering
|
|
162
171
|
|
|
163
|
-
1. For EACH module: read `
|
|
172
|
+
1. For EACH module: read `screens.json` flat file from `collected_data.modules[moduleCode].screens`, filter screens with wireframe data, **rename fields** (`mockupFormat`→`format`, `mockup`/`ascii`/`content`→`content`, `screen`/`name`/`title`→`screen`), store under `wireframes[moduleCode]`
|
|
164
173
|
2. Read master's `consolidation.e2eFlows[]` and build e2eFlows array with diagram generation
|
|
165
174
|
3. Read master's `dependencyGraph` and build nodes/edges
|
|
166
175
|
4. Serialize as JSON with 2-space indentation
|
|
@@ -93,29 +93,32 @@ Build a JSON object following this **exact mapping** from index.json to the HTML
|
|
|
93
93
|
}
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
### Module Specs Mapping
|
|
96
|
+
### Module Specs Mapping (FLAT-FILE ARCHITECTURE)
|
|
97
97
|
|
|
98
98
|
For EACH module in `master.modules[]`:
|
|
99
99
|
|
|
100
|
-
1.
|
|
101
|
-
2.
|
|
100
|
+
1. Use flat file data from `collected_data.modules[moduleCode]` (loaded in step-01)
|
|
101
|
+
2. Data sources: `entities.json`, `rules.json`, `usecases.json`, `permissions.json`, `screens.json`
|
|
102
|
+
3. Do NOT read from `moduleIndex.specification` or `moduleIndex.analysis` — these don't exist in flat-file format
|
|
102
103
|
|
|
103
104
|
```javascript
|
|
105
|
+
const mod = collected_data.modules[moduleCode];
|
|
106
|
+
|
|
104
107
|
moduleSpecs[moduleCode] = {
|
|
105
|
-
useCases: (
|
|
108
|
+
useCases: (mod.usecases?.useCases || []).map(uc => ({
|
|
106
109
|
name: uc.name,
|
|
107
110
|
actor: uc.primaryActor,
|
|
108
111
|
steps: (uc.mainScenario || []).join("\n"), // array → newline-separated string
|
|
109
112
|
alternative: (uc.alternativeScenarios || [])
|
|
110
113
|
.map(a => a.name + ": " + (a.steps || []).join(", ")).join("\n")
|
|
111
114
|
})),
|
|
112
|
-
businessRules: (
|
|
115
|
+
businessRules: (mod.rules?.rules || []).map(br => ({
|
|
113
116
|
name: br.name,
|
|
114
117
|
category: br.category, // "validation"|"calculation"|"workflow"|"security"|"data"
|
|
115
118
|
statement: br.statement,
|
|
116
119
|
example: (br.examples || []).map(e => e.input + " → " + e.expected).join("; ")
|
|
117
120
|
})),
|
|
118
|
-
entities: (
|
|
121
|
+
entities: (mod.entities?.entities || []).map(ent => ({
|
|
119
122
|
name: ent.name,
|
|
120
123
|
description: ent.description || "",
|
|
121
124
|
attributes: (ent.attributes || []).map(a => ({
|
|
@@ -128,7 +131,7 @@ moduleSpecs[moduleCode] = {
|
|
|
128
131
|
r.target + " (" + r.type + ") - " + (r.description || "")
|
|
129
132
|
)
|
|
130
133
|
})),
|
|
131
|
-
permissions: buildPermissionKeys(
|
|
134
|
+
permissions: buildPermissionKeys(mod.permissions), // see below
|
|
132
135
|
notes: "",
|
|
133
136
|
mockupNotes: "" // Deprecated: wireframes now embedded separately in EMBEDDED_ARTIFACTS
|
|
134
137
|
}
|
|
@@ -139,9 +142,10 @@ moduleSpecs[moduleCode] = {
|
|
|
139
142
|
The HTML uses `"Role|Action"` format (e.g. `"RH Admin|Consulter"`):
|
|
140
143
|
|
|
141
144
|
```javascript
|
|
142
|
-
|
|
145
|
+
// Input: permissions object from flat file (mod.permissions), NOT moduleFeature
|
|
146
|
+
function buildPermissionKeys(permissionsData) {
|
|
143
147
|
const keys = [];
|
|
144
|
-
const matrix =
|
|
148
|
+
const matrix = permissionsData?.matrix || permissionsData?.permissionMatrix;
|
|
145
149
|
if (!matrix) return keys;
|
|
146
150
|
const actionMap = { read: "Consulter", create: "Creer", update: "Modifier",
|
|
147
151
|
delete: "Supprimer", validate: "Valider", export: "Exporter",
|
|
@@ -207,25 +211,30 @@ Build a JSON object containing ALL visual artifacts from module JSON files (inde
|
|
|
207
211
|
}
|
|
208
212
|
```
|
|
209
213
|
|
|
210
|
-
### Wireframes Mapping
|
|
214
|
+
### Wireframes Mapping (FLAT-FILE: from screens.json)
|
|
211
215
|
|
|
212
216
|
For EACH module in `master.modules[]`:
|
|
213
217
|
|
|
214
218
|
```javascript
|
|
215
|
-
//
|
|
216
|
-
const
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
219
|
+
// Use flat file data from collected_data (loaded in step-01)
|
|
220
|
+
const mod = collected_data.modules[moduleCode];
|
|
221
|
+
const screens = mod.screens?.screens || [];
|
|
222
|
+
|
|
223
|
+
// Extract wireframes from screens that have mockup/wireframe data
|
|
224
|
+
const wireframes = screens
|
|
225
|
+
.filter(s => s.wireframe || s.mockup || s.mockupFormat)
|
|
226
|
+
.map(wf => ({
|
|
227
|
+
screen: wf.screen || wf.name || wf.id || "", // e.g. "UM-list", "UM-form"
|
|
228
|
+
section: wf.section || "", // e.g. "list", "form"
|
|
229
|
+
format: wf.mockupFormat || "ascii", // "ascii" | "svg"
|
|
230
|
+
content: wf.mockup || wf.ascii || wf.content || "", // ASCII art or SVG markup
|
|
231
|
+
description: wf.description || "",
|
|
232
|
+
elements: wf.elements || [], // ["DataGrid", "FilterBar", ...]
|
|
233
|
+
actions: wf.actions || [], // ["filter", "sort", "create", ...]
|
|
234
|
+
componentMapping: wf.componentMapping || [], // [{ wireframeElement, reactComponent }]
|
|
235
|
+
layout: wf.layout || null, // { type, regions: [...] }
|
|
236
|
+
permissionsRequired: wf.permissionsRequired || []
|
|
237
|
+
}));
|
|
229
238
|
|
|
230
239
|
// Store in artifacts object
|
|
231
240
|
EMBEDDED_ARTIFACTS.wireframes[moduleCode] = wireframes;
|
|
@@ -51,7 +51,11 @@ ELSE:
|
|
|
51
51
|
BLOCKING ERROR: "Feature status: {feature.status}, expected consolidated"
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
### 4. Read All JSON Files
|
|
54
|
+
### 4. Read All JSON Files (FLAT-FILE ARCHITECTURE)
|
|
55
|
+
|
|
56
|
+
> **CRITICAL:** Module data lives in separate flat files (entities.json, usecases.json, etc.),
|
|
57
|
+
> NOT inside index.json. The module index.json only contains metadata and file pointers.
|
|
58
|
+
> You MUST read each flat file individually.
|
|
55
59
|
|
|
56
60
|
```
|
|
57
61
|
collected_data = {
|
|
@@ -63,13 +67,18 @@ collected_data = {
|
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
FOR each module in master.index.modules[]:
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
// Resolve module directory path
|
|
71
|
+
moduleDir = "{app}/{module.code}/business-analyse/v{version}/"
|
|
72
|
+
moduleIndex = READ(moduleDir + "index.json")
|
|
68
73
|
|
|
74
|
+
// Read ALL flat files from module directory
|
|
69
75
|
collected_data.modules[module.code] = {
|
|
70
76
|
index: moduleIndex,
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
entities: READ(moduleDir + "entities.json") || { entities: [] },
|
|
78
|
+
rules: READ(moduleDir + "rules.json") || { rules: [] },
|
|
79
|
+
usecases: READ(moduleDir + "usecases.json") || { useCases: [] },
|
|
80
|
+
permissions: READ(moduleDir + "permissions.json") || { roles: [], permissions: [], matrix: [] },
|
|
81
|
+
screens: READ(moduleDir + "screens.json") || { screens: [] }
|
|
73
82
|
}
|
|
74
83
|
```
|
|
75
84
|
|
|
@@ -78,7 +87,7 @@ FOR each module in master.index.modules[]:
|
|
|
78
87
|
```
|
|
79
88
|
JSON collected:
|
|
80
89
|
Master: index.json + cadrage.json
|
|
81
|
-
Modules: {module_count}
|
|
90
|
+
Modules: {module_count} modules (flat files: entities, rules, usecases, permissions, screens)
|
|
82
91
|
→ Proceeding to data build...
|
|
83
92
|
```
|
|
84
93
|
|