@atlashub/smartstack-cli 3.23.0 → 3.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/index.js +5 -0
  2. package/dist/index.js.map +1 -1
  3. package/dist/mcp-entry.mjs +96 -24
  4. package/dist/mcp-entry.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/templates/mcp-scaffolding/component.tsx.hbs +21 -1
  7. package/templates/skills/apex/references/smartstack-api.md +174 -5
  8. package/templates/skills/apex/references/smartstack-frontend.md +1101 -0
  9. package/templates/skills/apex/references/smartstack-layers.md +81 -5
  10. package/templates/skills/apex/steps/step-01-analyze.md +27 -3
  11. package/templates/skills/apex/steps/step-02-plan.md +5 -1
  12. package/templates/skills/apex/steps/step-03-execute.md +47 -5
  13. package/templates/skills/apex/steps/step-04-validate.md +300 -0
  14. package/templates/skills/apex/steps/step-05-examine.md +7 -0
  15. package/templates/skills/apex/steps/step-07-tests.md +19 -0
  16. package/templates/skills/business-analyse/_shared.md +6 -6
  17. package/templates/skills/business-analyse/patterns/suggestion-catalog.md +1 -1
  18. package/templates/skills/business-analyse/questionnaire/07-ui.md +3 -3
  19. package/templates/skills/business-analyse/references/cadrage-pre-analysis.md +1 -1
  20. package/templates/skills/business-analyse/references/entity-architecture-decision.md +3 -3
  21. package/templates/skills/business-analyse/references/handoff-file-templates.md +13 -5
  22. package/templates/skills/business-analyse/references/spec-auto-inference.md +14 -14
  23. package/templates/skills/business-analyse/steps/step-01-cadrage.md +2 -2
  24. package/templates/skills/business-analyse/steps/step-02-decomposition.md +1 -1
  25. package/templates/skills/business-analyse/steps/step-03a1-setup.md +2 -2
  26. package/templates/skills/business-analyse/steps/step-03b-ui.md +2 -1
  27. package/templates/skills/business-analyse/steps/step-05a-handoff.md +15 -4
  28. package/templates/skills/business-analyse/templates/tpl-frd.md +2 -2
  29. package/templates/skills/business-analyse/templates-frd.md +2 -2
  30. package/templates/skills/efcore/steps/migration/step-02-create.md +14 -1
  31. package/templates/skills/ralph-loop/references/category-rules.md +71 -9
  32. package/templates/skills/ralph-loop/references/compact-loop.md +3 -3
  33. package/templates/skills/ralph-loop/references/core-seed-data.md +10 -0
  34. package/templates/skills/ralph-loop/steps/step-02-execute.md +190 -1
  35. package/templates/skills/validate-feature/steps/step-01-compile.md +4 -1
  36. package/templates/skills/validate-feature/steps/step-05-db-validation.md +86 -1
@@ -489,7 +489,7 @@ Auto-added:
489
489
  | Produits/Products | Commerce, Inventaire | Catalogue avec variantes, tarification, stock |
490
490
 
491
491
  **Section pattern for module dedie:**
492
- - Section `list` (TOUJOURS) : grille + create en modale + clic vers detail
492
+ - Section `list` (TOUJOURS) : grille + bouton create (navigue vers page `/create`) + clic vers detail
493
493
  - Page detail avec onglets : Infos + onglets relationnels
494
494
  - Sections additionnelles UNIQUEMENT si : dashboard, approve, import, rapport
495
495
 
@@ -39,8 +39,8 @@
39
39
  | Need | Component | Détail |
40
40
  |------|-----------|--------|
41
41
  | List with pagination | DataTable | Tri, pagination, sélection, actions inline |
42
- | Creation form | Form + Modal | Validation FluentValidation, i18n des erreurs |
43
- | Edit form | Form + Modal | Pré-remplissage, dirty check, optimistic locking |
42
+ | Creation form | Form page (`/create` route) | Validation FluentValidation, i18n des erreurs |
43
+ | Edit form | Form page (`/:id/edit` route) | Pré-remplissage, dirty check, optimistic locking |
44
44
  | Filters | FilterBar | Filtres combinables, persistance URL params |
45
45
  | Export | ExportButton | CSV/Excel, respecte les filtres actifs |
46
46
  | Cards/Grid | CardGrid | Affichage visuel alternatif au tableau |
@@ -58,7 +58,7 @@
58
58
  | Question | If answer is vague/insufficient | Probe |
59
59
  |----------|-------------------------------|-------|
60
60
  | Q7.1 (devices) | "Desktop uniquement" | "Aucun accès mobile ? Même pas en consultation ? Tablette en atelier/terrain ?" |
61
- | Q7.5 (screens) | "Un écran de liste" | "Avec création/édition inline ou en modal ? Détail en side panel ou page séparée ?" |
61
+ | Q7.5 (screens) | "Un écran de liste" | "Avec pages dédiées pour création et édition ? Détail en page avec onglets ?" |
62
62
  | Q7.5 (screens) | Pas de mention dashboard | "L'utilisateur a-t-il besoin d'un tableau de bord avec des KPIs avant de plonger dans les données ?" |
63
63
  | Q7.7 (key info) | "Toutes les colonnes" | "Sur mobile/petit écran, quelles 3-4 colonnes sont indispensables ? Le reste = détail secondaire" |
64
64
  | Q7.8 (actions) | "CRUD classique" | "Actions métier spécifiques ? (valider, dupliquer, archiver, changer statut, assigner)" |
@@ -84,7 +84,7 @@ For each detected module, anticipate sections (Level 4) and resources (Level 5)
84
84
  | integration | list | sync-status-grid, config-form, log-viewer | Infos, Config, Logs |
85
85
 
86
86
  > **RULE:** Sections = functional zones only (`list`, `dashboard`, `approve`, `import`, `rapport`, `planning`).
87
- > `create`/`edit` = actions (modal/drawer) within the `list` page. `detail` = tabbed page reached by clicking a row in `list`.
87
+ > `create`/`edit` = separate pages with own URL routes (`/create` and `/:id/edit`). `detail` = tabbed page reached by clicking a row in `list`.
88
88
 
89
89
  **Always check:** Does the user's description imply additional functional sections? (e.g., "reports" → `dashboard`, "calendar view" → `planning`, "approval workflow" → `approve`)
90
90
 
@@ -121,7 +121,7 @@ L'entite {E} est-elle CORE pour cette application ?
121
121
  Module
122
122
  ├── Section: list
123
123
  │ └── Page principale avec grille/table
124
- │ └── Action "Creer" (ouvre formulaire en modale/drawer DANS cette page)
124
+ │ └── Action "Creer" (navigue vers la page /create avec formulaire)
125
125
  │ └── Clic sur une ligne → navigation vers la page detail
126
126
  │ └── Resources : {entity}-grid, {entity}-filters, {entity}-form
127
127
 
@@ -152,8 +152,8 @@ Module
152
152
 
153
153
  | PAS une section | C'est quoi en realite |
154
154
  |-----------------|----------------------|
155
- | `create` | Action (bouton) dans la page `list`, ouvre un formulaire en modale/drawer |
156
- | `edit` | Action dans la page detail, bascule les champs en mode edition |
155
+ | `create` | Action (bouton) dans la page `list`, navigue vers la page `/create` avec formulaire |
156
+ | `edit` | Action dans la page list/detail, navigue vers la page `/:id/edit` avec formulaire pre-rempli |
157
157
  | `detail` | Page de detail accessible par navigation depuis `list` (pas une section menu) |
158
158
  | `delete` | Action avec confirmation dans la page `list` ou `detail` |
159
159
  | `search` | Composant de filtrage integre dans la section `list` |
@@ -70,14 +70,22 @@ From `specification.uiWireframes[]`, `specification.dashboards[]` and `analysis.
70
70
 
71
71
  ```json
72
72
  "frontend": [
73
- { "path": "src/pages/{ModuleName}/{PageName}Page.tsx", "type": "Page", "linkedUCs": [], "linkedWireframes": ["{module}-list"], "module": "{moduleCode}", "wireframeAcceptanceCriteria": "Layout MUST match wireframe..." },
74
- { "path": "src/pages/{ModuleName}/{PageName}DetailPage.tsx", "type": "Page", "linkedUCs": [], "linkedWireframes": ["{module}-detail"], "module": "{moduleCode}" },
75
- { "path": "src/pages/{ModuleName}/{DashboardName}DashboardPage.tsx", "type": "DashboardPage", "linkedUCs": [], "linkedWireframes": ["{module}-dashboard"], "module": "{moduleCode}", "dashboardRef": "{module}-dashboard", "instructions": "Use Recharts library..." },
76
- { "path": "src/components/{ModuleName}/{ComponentName}.tsx", "type": "Component", "linkedUCs": [], "linkedWireframes": [], "module": "{moduleCode}" },
77
- { "path": "src/hooks/use{ModuleName}{Hook}.ts", "type": "Hook", "linkedUCs": [], "module": "{moduleCode}" }
73
+ { "path": "src/pages/{ContextPascal}/{ApplicationName}/{ModuleName}/{PageName}Page.tsx", "type": "Page", "linkedUCs": [], "linkedWireframes": ["{module}-list"], "module": "{moduleCode}", "wireframeAcceptanceCriteria": "Layout MUST match wireframe...", "skill": "/ui-components" },
74
+ { "path": "src/pages/{ContextPascal}/{ApplicationName}/{ModuleName}/{PageName}DetailPage.tsx", "type": "Page", "linkedUCs": [], "linkedWireframes": ["{module}-detail"], "module": "{moduleCode}", "skill": "/ui-components" },
75
+ { "path": "src/pages/{ContextPascal}/{ApplicationName}/{ModuleName}/{DashboardName}DashboardPage.tsx", "type": "DashboardPage", "linkedUCs": [], "linkedWireframes": ["{module}-dashboard"], "module": "{moduleCode}", "dashboardRef": "{module}-dashboard", "instructions": "Use Recharts library...", "skill": "/ui-components" },
76
+ { "path": "src/components/{ModuleName}/{ComponentName}.tsx", "type": "Component", "linkedUCs": [], "linkedWireframes": [], "module": "{moduleCode}", "skill": "/ui-components" },
77
+ { "path": "src/hooks/use{ModuleName}{Hook}.ts", "type": "Hook", "linkedUCs": [], "module": "{moduleCode}" },
78
+ { "path": "src/i18n/locales/fr/{moduleLower}.json", "type": "I18n", "language": "fr", "module": "{moduleCode}" },
79
+ { "path": "src/i18n/locales/en/{moduleLower}.json", "type": "I18n", "language": "en", "module": "{moduleCode}" },
80
+ { "path": "src/i18n/locales/it/{moduleLower}.json", "type": "I18n", "language": "it", "module": "{moduleCode}" },
81
+ { "path": "src/i18n/locales/de/{moduleLower}.json", "type": "I18n", "language": "de", "module": "{moduleCode}" }
78
82
  ]
79
83
  ```
80
84
 
85
+ **Page generation:** ALL pages and components MUST be generated via `/ui-components` skill (entity lists, grids, tables, dashboards, charts). The `"skill"` field indicates which skill to invoke.
86
+
87
+ **I18n generation:** 4 JSON files per module (fr, en, it, de) with identical key structures. `{moduleLower}` = lowercase module code. See `smartstack-frontend.md` for JSON template (keys: actions, labels, columns, form, errors, validation, messages, empty).
88
+
81
89
  **Dashboard acceptance criteria:** Chart library (Recharts), chart types matching spec, KPI cards, filters, CSS variables, responsive layout, wireframe-matching positions.
82
90
 
83
91
  ## 4.6 SeedData Files
@@ -9,7 +9,7 @@
9
9
  | string + unique | text | yes | yes | clickAction: navigate:detail |
10
10
  | string | text | yes | yes | — |
11
11
  | enum / status | badge | yes | yes (multi-select) | colorMap from lifeCycles if exists |
12
- | FK:Entity | text (join .name) | yes | yes (entity-select) | Display related entity name |
12
+ | FK:Entity | text (join .name) | yes | yes (EntityLookup) | Display related entity name, filter via searchable EntityLookup |
13
13
  | decimal | currency | yes | no | — |
14
14
  | int | number | yes | no | — |
15
15
  | datetime | date-relative | yes | yes (date-range) | — |
@@ -23,7 +23,7 @@
23
23
  | string | Input | entity.required | — |
24
24
  | string (multiline) | TextArea | entity.required | rows: 4 |
25
25
  | enum | Select | entity.required | source: enum name |
26
- | FK:Entity | EntitySelect | entity.required | source: target entity, searchable |
26
+ | FK:Entity | EntityLookup | entity.required | source: target entity, searchable. Component: `@/components/ui/EntityLookup`. NEVER plain text input for FK Guid fields. Backend API MUST support `?search=` param. See `smartstack-frontend.md` section 6. |
27
27
  | decimal | NumberInput | entity.required | — |
28
28
  | int | NumberInput | entity.required | — |
29
29
  | datetime | DatePicker | entity.required | — |
@@ -31,22 +31,22 @@
31
31
 
32
32
  ## Auto-Generated UI Components by featureType
33
33
 
34
- > **RULE:** Sections = functional zones only. `create`/`edit` are actions within `list`. `detail` is a tabbed page reached from `list`.
34
+ > **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`.
35
35
 
36
- | featureType | Sections (functional zones) | List page includes | Detail page tabs |
37
- |---|---|---|---|
38
- | data-centric | list | grid, filters, create form (modal) | Infos, {relations} |
39
- | workflow | list, approve | grid, filters, create form (modal) | Infos, {relations}, Historique |
40
- | integration | list | grid, filters, config form | Infos, Config, Logs |
41
- | reporting | dashboard | — | — |
42
- | full-module | list, dashboard | grid, filters, create form (modal) | Infos, {relations}, Historique |
36
+ | featureType | Sections (functional zones) | List page includes | Form pages | Detail page tabs |
37
+ |---|---|---|---|---|
38
+ | data-centric | list | grid, filters, create button | `/create` page, `/:id/edit` page | Infos, {relations} |
39
+ | workflow | list, approve | grid, filters, create button | `/create` page, `/:id/edit` page | Infos, {relations}, Historique |
40
+ | integration | list | grid, filters, config button | `/create` page, `/:id/edit` page | Infos, Config, Logs |
41
+ | reporting | dashboard | — | — | — |
42
+ | full-module | list, dashboard | grid, filters, create button | `/create` page, `/:id/edit` page | Infos, {relations}, Historique |
43
43
 
44
44
  ## Component Generation Rules
45
45
 
46
- 1. **list section:** SmartTable with all non-long-text attributes as columns, actions = [view, edit, delete], defaultSort = { createdAt, desc }. Create action opens SmartForm in modal/drawer.
47
- 2. **create form (in list):** SmartForm with all writable attributes as fields, component inferred from type. Opens as modal/drawer from the list page.
48
- 3. **detail page (from list click):** TabPanel with: Info tab (all attributes), relation tabs (child SmartTable for each 1:N relationship), History tab (if auditable). See "Detail Page Tab Auto-Inference" below for exact rules.
49
- 4. **edit (in detail):** Same form as create but pre-filled (mode: edit). Inline in the detail page Info tab.
46
+ 1. **list section:** SmartTable with all non-long-text attributes as columns, actions = [view, edit, delete], defaultSort = { createdAt, desc }. Create button navigates to `/create` page. Edit action navigates to `/:id/edit` page.
47
+ 2. **create page (`/create` route):** SmartForm (full page) with all writable attributes as fields, component inferred from type. Back button + form + submit. NEVER a modal.
48
+ 3. **edit page (`/:id/edit` route):** Same form as create but pre-filled with entity data. Full page with back button. NEVER a modal or inline toggle.
49
+ 4. **detail page (from list click):** TabPanel with: Info tab (all attributes read-only + edit button navigating to `/:id/edit`), relation tabs (child SmartTable for each 1:N relationship), History tab (if auditable). See "Detail Page Tab Auto-Inference" below for exact rules.
50
50
  5. **dashboard section:** Trigger 3d (dashboard specification)
51
51
 
52
52
  ## Detail Page Tab Auto-Inference
@@ -136,7 +136,7 @@ pre_analysis:
136
136
 
137
137
  **Modules identifiés :**
138
138
  - **{Module 1}** : {functional description}
139
- - Section **list** : {entity}-grid, {entity}-filters, {entity}-form (création en modale)
139
+ - Section **list** : {entity}-grid, {entity}-filters + pages `/create` et `/:id/edit`
140
140
  - Page détail (onglets) : {Infos, {relation_tabs}...}
141
141
  - {IF dashboard needed:} Section **dashboard** : {kpi-cards, charts...}
142
142
  - **{Module 2}** : {functional description}
@@ -601,7 +601,7 @@ BEFORE transitioning to step-02:
601
601
  f. **List detail page tabs** — for entities accessible via click from `list`
602
602
 
603
603
  > **SECTION RULE:** Sections are functional zones (list, dashboard, approve, import, rapport...).
604
- > `create` and `edit` are ACTIONS within the `list` page (modal/drawer).
604
+ > `create` and `edit` are SEPARATE PAGES with their own URL routes (`/create` and `/:id/edit`).
605
605
  > `detail` is a page with tabs accessible by clicking a row in `list`, NOT a standalone section.
606
606
 
607
607
  4. **OUTPUT the matrix as text** (do NOT put it inside AskUserQuestion — it won't render):
@@ -73,7 +73,7 @@ For each mustHave/shouldHave scope item:
73
73
  > "entities": ["Anticipated entity names"],
74
74
  > "estimatedComplexity": "simple|medium|complex",
75
75
  > "anticipatedSections": [
76
- > { "code": "list", "description": "Main page: grid, create action (modal), click to detail", "resources": ["entity-grid", "entity-filters", "entity-form"] },
76
+ > { "code": "list", "description": "Main page: grid, create button (navigates to /create page), click to detail", "resources": ["entity-grid", "entity-filters", "entity-create-page", "entity-edit-page"] },
77
77
  > { "code": "dashboard", "description": "Module KPIs and overview (if needed)", "resources": ["entity-kpi-cards", "entity-charts"] }
78
78
  > ],
79
79
  > "detailTabs": ["Informations", "RelatedEntity1", "Historique"]
@@ -200,7 +200,7 @@ Total: {N} requirements mapped to this module
200
200
  For each module, propose standard sections based on module type:
201
201
 
202
202
  > **RULE:** Sections = functional zones only. `list` is ALWAYS present (default).
203
- > `create`/`edit` = actions within the `list` page (modal/drawer). `detail` = tabbed page from list click.
203
+ > `create`/`edit` = separate pages with own URL routes (`/create` and `/:id/edit`). `detail` = tabbed page from list click.
204
204
 
205
205
  | Module Type | Sections (functional zones) | Detail page tabs (from list click) |
206
206
  |-------------|---------------------------|-----------------------------------|
@@ -291,7 +291,7 @@ See [references/spec-auto-inference.md](../references/spec-auto-inference.md) fo
291
291
  - Entity attribute → SmartTable column mapping (9 type rules)
292
292
  - Entity attribute → SmartForm field mapping (8 type rules)
293
293
  - Auto-generated sections per featureType (5 types)
294
- - Component generation rules (list section, create form in modal, detail page with tabs, dashboard section)
294
+ - Component generation rules (list section, create/edit form pages with own routes, detail page with tabs, dashboard section)
295
295
  - Status/lifecycle enhancement rules
296
296
 
297
297
  Write auto-generated sections to `specification.sections[]` via ba-writer.enrichSection()
@@ -319,7 +319,8 @@ For each section, identify what resources/components are needed:
319
319
 
320
320
  - List section → DataGrid, FilterBar, SearchInput, ExportButton, CreateButton
321
321
  - Detail page (from list click) → BackButton, DetailHeader, StatusBadge, TabPanel, DetailCard, SmartForm (edit mode), SmartTable (relation tabs), Timeline (history tab)
322
- - Create (in list) → SmartForm (modal/drawer), ValidationMessages, SubmitButton
322
+ - Create page (`/create` route) → SmartForm (full page), ValidationMessages, SubmitButton, BackButton
323
+ - Edit page (`/:id/edit` route) → SmartForm (full page, pre-filled), ValidationMessages, SubmitButton, BackButton
323
324
  - Approve section → StatusTransitionPanel, CommentBox, ApproveRejectButtons
324
325
  - Dashboard section → StatCard, RechartsChart (Bar/Line/Pie/Area), DashboardGrid, FilterBar
325
326
 
@@ -287,13 +287,24 @@ For each module, generate these SEPARATE frontend file entries (not one monolith
287
287
 
288
288
  ```json
289
289
  "frontend": [
290
- { "path": "src/pages/{Mod}/{ListPage}Page.tsx", "type": "Page", "linkedWireframes": ["{module}-list"], "module": "{moduleCode}" },
291
- { "path": "src/pages/{Mod}/{DetailPage}Page.tsx", "type": "Page", "linkedWireframes": ["{module}-detail"], "module": "{moduleCode}" },
290
+ { "path": "src/pages/{Context}/{App}/{Mod}/{ListPage}Page.tsx", "type": "Page", "linkedWireframes": ["{module}-list"], "module": "{moduleCode}", "skill": "/ui-components" },
291
+ { "path": "src/pages/{Context}/{App}/{Mod}/{Entity}CreatePage.tsx", "type": "Page", "linkedWireframes": ["{module}-create"], "module": "{moduleCode}", "skill": "/ui-components", "fkFields": ["EmployeeId→Employee", "DepartmentId→Department"] },
292
+ { "path": "src/pages/{Context}/{App}/{Mod}/{Entity}EditPage.tsx", "type": "Page", "linkedWireframes": ["{module}-edit"], "module": "{moduleCode}", "skill": "/ui-components", "fkFields": ["EmployeeId→Employee", "DepartmentId→Department"] },
293
+ { "path": "src/pages/{Context}/{App}/{Mod}/{DetailPage}Page.tsx", "type": "Page", "linkedWireframes": ["{module}-detail"], "module": "{moduleCode}", "skill": "/ui-components" },
292
294
  { "path": "src/hooks/use{Module}.ts", "type": "Hook", "module": "{moduleCode}" },
293
- { "path": "src/i18n/{module}.json", "type": "I18n", "module": "{moduleCode}" }
295
+ { "path": "src/i18n/locales/fr/{moduleLower}.json", "type": "I18n", "language": "fr", "module": "{moduleCode}" },
296
+ { "path": "src/i18n/locales/en/{moduleLower}.json", "type": "I18n", "language": "en", "module": "{moduleCode}" },
297
+ { "path": "src/i18n/locales/it/{moduleLower}.json", "type": "I18n", "language": "it", "module": "{moduleCode}" },
298
+ { "path": "src/i18n/locales/de/{moduleLower}.json", "type": "I18n", "language": "de", "module": "{moduleCode}" }
294
299
  ]
295
300
  ```
296
301
 
302
+ **FK fields in forms:** Create/Edit page entries MUST include `"fkFields"` array listing FK properties and their target entities (format: `"PropertyId→TargetEntity"`). This tells ralph-loop to use `EntityLookup` component instead of plain text inputs. Derive FK fields from `analysis.entities[].relationships[]` where `type = "N:1"` or from properties ending in `Id` with a navigation property. See `smartstack-frontend.md` section 6 for the EntityLookup pattern.
303
+
304
+ **Page generation:** ALL pages MUST use `/ui-components` skill (indicated by `"skill"` field). NEVER write raw TSX without the skill patterns.
305
+
306
+ **I18n generation:** 4 JSON files per module with identical key structures. `{moduleLower}` = lowercase module code. Keys: actions, labels, columns, form, errors, validation, messages, empty. See `smartstack-frontend.md` template.
307
+
297
308
  Additional per-section pages (dashboard, import, etc.) are separate entries.
298
309
 
299
310
  **Route wiring task (MANDATORY — separate entry):**
@@ -344,7 +355,7 @@ Add to progress.txt after all module tasks.
344
355
  > "application": [{"path": "src/Application/Services/{Ctx}/{App}/{Mod}/{Svc}Service.cs", "type": "Service|Dto|Validator", "linkedFRs": [], "linkedUCs": [], "module": "..."}],
345
356
  > "infrastructure": [{"path": "src/Infrastructure/Persistence/Configurations/{Ctx}/{App}/{Mod}/{Entity}Configuration.cs", "type": "EFConfiguration", "linkedFRs": [], "module": "..."}],
346
357
  > "api": [{"path": "src/API/Controllers/{CtxShort}/{App}/{Entity}Controller.cs", "type": "ApiController", "linkedUCs": [], "linkedFRs": [], "module": "..."}],
347
- > "frontend": [{"path": "src/pages/{Mod}/{Page}Page.tsx", "type": "Page|Component|Hook|DashboardPage", "linkedUCs": [], "linkedWireframes": [], "module": "..."}],
358
+ > "frontend": [{"path": "src/pages/{Ctx}/{App}/{Mod}/{Page}Page.tsx", "type": "Page|Component|Hook|DashboardPage|I18n", "linkedUCs": [], "linkedWireframes": [], "module": "...", "skill": "/ui-components"}],
348
359
  > "seedData": [{"path": "src/Infrastructure/Persistence/Seeding/Data/{Mod}/...SeedData.cs", "type": "SeedData", "category": "core|business", "source": "...", "module": "..."}],
349
360
  > "tests": [{"path": "src/Tests/Unit/Domain/{Ctx}/{App}/{Mod}/{Entity}Tests.cs", "type": "UnitTests|IntegrationTests|SecurityTests", "linkedFRs": [], "linkedUCs": [], "module": "..."}]
350
361
  > },
@@ -100,8 +100,8 @@
100
100
 
101
101
  | Element | Type | Behavior | Permission |
102
102
  |---------|------|----------|------------|
103
- | [+ New] | Button | Opens create modal | `{module}.create` |
104
- | [E] | Icon | Opens edit modal | `{module}.update` |
103
+ | [+ New] | Button | Navigates to `/create` page | `{module}.create` |
104
+ | [E] | Icon | Navigates to `/:id/edit` page | `{module}.update` |
105
105
  | [D] | Icon | Confirmation + delete | `{module}.delete` |
106
106
 
107
107
  ## 5. Validation and Messages
@@ -272,8 +272,8 @@
272
272
 
273
273
  | Element | Type | Behavior | Permission |
274
274
  |---------|------|----------|------------|
275
- | [+ New] | Button | Opens create modal | `{module}.create` |
276
- | [E] | Icon | Opens edit modal | `{module}.update` |
275
+ | [+ New] | Button | Navigates to `/create` page | `{module}.create` |
276
+ | [E] | Icon | Navigates to `/:id/edit` page | `{module}.update` |
277
277
  | [D] | Icon | Confirmation + delete | `{module}.delete` |
278
278
 
279
279
  ## 5. Validation and Messages
@@ -107,7 +107,20 @@ dotnet ef migrations add "$MIGRATION_NAME" \
107
107
  --verbose
108
108
  ```
109
109
 
110
- ### 6. Verify Creation
110
+ ### 6. Cleanup EF Core Design-Time Artifacts
111
+
112
+ ```bash
113
+ # EF Core's Roslyn BuildHost creates corrupted bin folders on Windows (U+F05C instead of backslash)
114
+ # Clean up any bin*Debug folders that are NOT the normal bin/Debug path
115
+ for d in src/*/bin?Debug; do
116
+ if [ -d "$d" ]; then
117
+ echo "Removing corrupted EF Core artifact: $d"
118
+ rm -rf "$d"
119
+ fi
120
+ done
121
+ ```
122
+
123
+ ### 7. Verify Creation
111
124
 
112
125
  ```bash
113
126
  # Check files were created
@@ -56,8 +56,9 @@ Execution sequence:
56
56
  > NEVER use `$HOME/.dotnet/tools` alone — on WSL, `$HOME` resolves to `/home/{user}` where .NET SDK is not installed.
57
57
  1. Call `mcp__smartstack__suggest_migration` → get standardized name
58
58
  2. `dotnet ef migrations add {Name} --project src/{Infra}.csproj --startup-project src/{Api}.csproj -o Persistence/Migrations`
59
- 3. `dotnet ef database update --project src/{Infra}.csproj --startup-project src/{Api}.csproj`
60
- 4. `dotnet build --no-restore` verify build passes
59
+ 3. Cleanup corrupted EF Core artifacts: `for d in src/*/bin?Debug; do [ -d "$d" ] && rm -rf "$d"; done`
60
+ 4. `dotnet ef database update --project src/{Infra}.csproj --startup-project src/{Api}.csproj`
61
+ 5. `dotnet build --no-restore` → verify build passes
61
62
 
62
63
  **BLOCKING:** If migration fails, DO NOT proceed. Fix the EF configs first.
63
64
 
@@ -281,6 +282,8 @@ fi
281
282
  - Swagger XML documentation
282
283
  - Consistent route patterns: `api/{context}/{app}/{module}`
283
284
  - Return DTOs, never domain entities
285
+ - **ALL GetAll endpoints MUST accept `?search=` query parameter** (enables EntityLookup on frontend)
286
+ - **GetAll returns paginated results:** `PaginatedResult<T>` with items, totalCount, page, pageSize
284
287
 
285
288
  **Controller coverage (BLOCKING):**
286
289
  - EVERY entity in the module MUST have a controller with CRUD endpoints (GET list, GET by id, POST, PUT, DELETE)
@@ -307,18 +310,28 @@ fi
307
310
  3. **Wire routes to App.tsx (detect pattern: `contextRoutes` array OR JSX `<Route>`):**
308
311
  - **Pattern A** (`contextRoutes: ContextRouteExtensions` in App.tsx): add to `contextRoutes.{context}[]` with RELATIVE paths → auto-injected into both standard + tenant trees
309
312
  - **Pattern B** (JSX `<Route>` in App.tsx): insert `<Route>` entries inside correct Layout wrapper (BOTH standard AND tenant-prefixed `/t/:slug/...` blocks)
310
- 4. Create pages using SmartStack components
313
+ 4. Create pages using **`/ui-components` skill** (MANDATORY for entity lists, grids, tables, dashboards, charts). The skill ensures CSS variables, EntityCard, SmartTable, loading/error/empty states, and responsive grid patterns.
311
314
  5. Create preferences hook: `use{Module}Preferences.ts`
312
- 6. Generate i18n (4 languages: fr, en, it, de)
315
+ 6. Generate i18n (4 languages: fr, en, it, de) — see I18n section below for detailed rules
313
316
  7. `npm run typecheck` MUST pass (BLOCKING)
314
317
 
315
318
  **Components:**
316
319
  - Lists: `SmartTable` + `SmartFilter` (NOT HTML `<table>`)
317
320
  - Grids: `EntityCard` (NOT custom `<div>` cards)
318
321
  - Detail: `EntityDetailCard`, `StatusBadge`, tab layout
319
- - Forms: `SmartForm` with FluentValidation-backed fields
322
+ - Forms: `SmartForm` with FluentValidation-backed fields — **MUST be full pages, NEVER modals**
323
+ - FK fields: `EntityLookup` for searchable entity selection (NEVER plain text for Guid FK)
320
324
  - Dashboard: `StatCard`, Recharts components
321
325
 
326
+ **Form pages (CRITICAL — ZERO modals/popups/drawers):**
327
+ - Create form: `EntityCreatePage.tsx` with route `/{module}/create`
328
+ - Edit form: `EntityEditPage.tsx` with route `/{module}/:id/edit`
329
+ - ALL forms are full pages with their own URL — NEVER Modal/Dialog/Drawer/Popup
330
+ - Back button with `navigate(-1)` on every form page
331
+ - Use React.lazy() + `<Suspense fallback={<PageLoader />}>` for form page imports
332
+ - **Form tests (MANDATORY):** Co-located `EntityCreatePage.test.tsx` and `EntityEditPage.test.tsx`
333
+ → Cover: rendering, validation, submit, pre-fill (edit), navigation, error handling
334
+
322
335
  **Layout wrapper mapping:**
323
336
 
324
337
  | Context | Layout | Route path |
@@ -355,17 +368,43 @@ Adding routes to clientRoutes[] instead of contextRoutes.{context}[] → MUST us
355
368
  '00000000-0000-0000-0000-000000000000' → use dynamic ID from auth context or route params
356
369
  const api = axios.create(...) → use @/services/api/apiClient (single instance)
357
370
  useXxx with raw axios inside hooks → hooks MUST use the shared apiClient from @/services/api
371
+ <input type="text" value={...employeeId} → FK Guid fields MUST use EntityLookup (searchable select)
372
+ placeholder="Enter ID" / "Enter GUID" → EntityLookup provides search-based entity selection
373
+ <Modal>/<Dialog>/<Drawer>/<Popup> for forms → forms are FULL PAGES with own URL routes (/create, /:id/edit)
374
+ useState(showCreateModal/editDialog) → navigate to form page, NEVER toggle modal visibility
358
375
  ```
359
376
 
360
377
  ---
361
378
 
362
- ## I18n
379
+ ## I18n (MANDATORY for frontend)
380
+
381
+ > **CRITICAL:** Every frontend module MUST have 4 translation files. Missing i18n = broken UI (untranslated labels, empty buttons).
382
+
383
+ **File path:** `src/i18n/locales/{lang}/{moduleLower}.json` (4 files: fr, en, it, de)
384
+
385
+ **JSON key structure (identical across all 4 languages):**
386
+ ```json
387
+ {
388
+ "actions": { "create": "...", "edit": "...", "delete": "...", "save": "...", "cancel": "...", "search": "...", "filter": "...", "export": "...", "refresh": "..." },
389
+ "labels": { "title": "...", "description": "...", "status": "...", "createdAt": "...", "updatedAt": "..." },
390
+ "columns": { "name": "...", "status": "...", "date": "...", "actions": "..." },
391
+ "form": { "name": "...", "description": "...", "submit": "...", "required": "..." },
392
+ "errors": { "loadFailed": "...", "saveFailed": "...", "deleteFailed": "...", "notFound": "..." },
393
+ "validation": { "required": "...", "minLength": "...", "maxLength": "...", "invalid": "..." },
394
+ "messages": { "created": "...", "updated": "...", "deleted": "...", "confirmDelete": "..." },
395
+ "empty": { "title": "...", "description": "...", "action": "..." }
396
+ }
397
+ ```
398
+
399
+ **Usage pattern in TSX:** `t('{moduleLower}:actions.create', 'Create')` — ALWAYS namespace prefix + fallback value.
363
400
 
364
401
  **Rules:**
365
- - 4 JSON files: fr, en, it, de
366
- - Identical key structures across all languages
402
+ - 4 JSON files: fr, en, it, de — ALL mandatory (not just fr/en)
403
+ - Identical key structures across all languages — same keys, translated values
367
404
  - All UI labels, validation messages, button text, empty states
368
- - Depends on frontend task completion
405
+ - Depends on frontend page completion (pages define which keys are needed)
406
+ - Add entity-specific keys under `labels`, `columns`, `form` for each entity field
407
+ - Reference `smartstack-frontend.md` for the complete template
369
408
 
370
409
  ---
371
410
 
@@ -439,6 +478,29 @@ useXxx with raw axios inside hooks → hooks MUST use the shared apiCl
439
478
  - Coverage >= 80%
440
479
  - No `[Fact(Skip = "...")]`
441
480
 
481
+ **Frontend test completeness (BLOCKING):**
482
+
483
+ After all frontend pages are generated, verify test coverage:
484
+
485
+ ```bash
486
+ # Count page files vs test files
487
+ PAGE_COUNT=$(find src/pages/ -name "*.tsx" ! -name "*.test.tsx" 2>/dev/null | wc -l)
488
+ TEST_COUNT=$(find src/pages/ -name "*.test.tsx" 2>/dev/null | wc -l)
489
+
490
+ if [ "$PAGE_COUNT" -gt 0 ] && [ "$TEST_COUNT" -eq 0 ]; then
491
+ echo "BLOCKING: Category D — No .test.tsx files found in src/pages/"
492
+ echo "Every module with pages MUST have at least one test file"
493
+ echo "Pages found: $PAGE_COUNT, Tests found: 0"
494
+ exit 1
495
+ fi
496
+
497
+ # Minimum ratio: at least 1 test per 3 pages
498
+ MIN_TESTS=$(( (PAGE_COUNT + 2) / 3 ))
499
+ if [ "$TEST_COUNT" -lt "$MIN_TESTS" ]; then
500
+ echo "WARNING: Low test coverage — $TEST_COUNT tests for $PAGE_COUNT pages (minimum: $MIN_TESTS)"
501
+ fi
502
+ ```
503
+
442
504
  ---
443
505
 
444
506
  ## Validation (FINAL — BLOCKING)
@@ -99,8 +99,8 @@ Batch: {batch.length} [{firstCategory}] → {batch.map(t => `[${t.id}] ${t.descr
99
99
  | `infrastructure` with migration | `Migrations/` folder has .cs files | No migration files |
100
100
  | `infrastructure` with seed data | Seed data .cs files exist | 0 files created |
101
101
  | `application` | Service + DTO + Validator files exist | 0 files created |
102
- | `api` | Controller .cs files exist | 0 files created |
103
- | `frontend` | .tsx page files exist | 0 files created |
102
+ | `api` | Controller .cs files exist + GetAll supports `?search=` | 0 files created OR GetAll without search param |
103
+ | `frontend` | .tsx page files exist + FK fields use EntityLookup (not plain text) | 0 files created OR FK Guid field as `<input type="text">` |
104
104
  | `test` | Test project dir exists AND contains test .cs files | No test project or 0 test files |
105
105
  | `validation` | `dotnet test` exit 0 AND `dotnet build` exit 0 | Either command fails |
106
106
 
@@ -108,7 +108,7 @@ Batch: {batch.length} [{firstCategory}] → {batch.map(t => `[${t.id}] ${t.descr
108
108
 
109
109
  | Category | Action |
110
110
  |----------|--------|
111
- | `infrastructure` with `_migrationMeta` | Migration sequence: `suggest_migration` MCP → `dotnet ef migrations add` → `dotnet ef database update` → `dotnet build` |
111
+ | `infrastructure` with `_migrationMeta` | Migration sequence: `suggest_migration` MCP → `dotnet ef migrations add` → cleanup corrupted artifacts (`for d in src/*/bin?Debug; do [ -d "$d" ] && rm -rf "$d"; done`) → `dotnet ef database update` → `dotnet build` |
112
112
  | `infrastructure` with seed data keywords | **MANDATORY:** Read `references/core-seed-data.md` → implement templates → `dotnet build` |
113
113
  | `frontend` | MCP-first: `scaffold_api_client` → `scaffold_routes` (outputFormat: clientRoutes) → **wire to App.tsx (detect pattern: `contextRoutes` array OR JSX `<Route>`)** → create pages → `npm run typecheck && npm run lint` |
114
114
  | `test` | **Create tests:** `scaffold_tests` MCP → **Run:** `dotnet test --verbosity normal` → **Fix loop:** if fail → fix SOURCE CODE → rebuild → retest → repeat until 100% pass |
@@ -1253,3 +1253,13 @@ public class {Module}DevDataSeeder : IDevDataSeeder
1253
1253
  - Using `Guid.NewGuid()` for TenantId
1254
1254
  - Omitting idempotency check (`AnyAsync`)
1255
1255
  - Hardcoding TenantId inline (use `SeedConstants.DefaultTenantId`)
1256
+
1257
+ ### CROSS-SCHEMA FK WARNING
1258
+
1259
+ > `SeedConstants.DefaultTenantId` MUST reference a tenant that EXISTS in `core.tenant_Tenants`.
1260
+ > The SmartStack platform seeds a default tenant during `InitializeSmartStackAsync()`.
1261
+ > If you use a custom GUID, ensure it is created BEFORE `DevDataSeeder` runs (Order >= 200).
1262
+ >
1263
+ > **Pipeline validation:**
1264
+ > - ralph-loop POST-CHECK warns if GUID not found in project config
1265
+ > - validate-feature step-05 verifies FK exists in real database via SQL query