@atlashub/smartstack-cli 4.74.0 → 4.75.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 (93) hide show
  1. package/dist/index.js +111 -36
  2. package/dist/index.js.map +1 -1
  3. package/dist/mcp-entry.mjs +14 -3
  4. package/dist/mcp-entry.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/templates/agents/ba-reader.md +17 -15
  7. package/templates/agents/ba-writer.md +49 -51
  8. package/templates/skills/apex/_shared.md +1 -1
  9. package/templates/skills/apex/references/checks/backend-checks.sh +21 -7
  10. package/templates/skills/apex/references/checks/infrastructure-checks.sh +47 -10
  11. package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +3 -0
  12. package/templates/skills/apex/references/post-checks.md +5 -2
  13. package/templates/skills/apex/references/smartstack-frontend.md +53 -7
  14. package/templates/skills/apex/steps/step-00-init.md +74 -0
  15. package/templates/skills/apex/steps/step-03-execute.md +16 -4
  16. package/templates/skills/apex/steps/step-03b-layer1-seed.md +39 -6
  17. package/templates/skills/apex/steps/step-03c-layer2-backend.md +50 -5
  18. package/templates/skills/apex/steps/step-03d-layer3-frontend.md +102 -2
  19. package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +3 -0
  20. package/templates/skills/business-analyse/SKILL.md +14 -0
  21. package/templates/skills/business-analyse/_shared.md +27 -0
  22. package/templates/skills/business-analyse/patterns/suggestion-catalog.md +34 -26
  23. package/templates/skills/business-analyse/questionnaire/01-context.md +13 -9
  24. package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +20 -27
  25. package/templates/skills/business-analyse/questionnaire.md +86 -9
  26. package/templates/skills/business-analyse/references/03-json-schemas.md +213 -0
  27. package/templates/skills/business-analyse/references/03-post-check-validation.md +144 -0
  28. package/templates/skills/business-analyse/references/03-smartstack-entity-guards.md +32 -0
  29. package/templates/skills/business-analyse/references/04-cross-module-validation.md +95 -0
  30. package/templates/skills/business-analyse/references/04-file-allocation.md +162 -0
  31. package/templates/skills/business-analyse/references/04-naming-audit-checks.md +174 -0
  32. package/templates/skills/business-analyse/references/04-semantic-validation-matrix.md +118 -0
  33. package/templates/skills/business-analyse/references/domain-research-playbook.md +234 -0
  34. package/templates/skills/business-analyse/references/entity-sourcing-presentation.md +166 -0
  35. package/templates/skills/business-analyse/references/init-resume-logic.md +70 -0
  36. package/templates/skills/business-analyse/references/module-completeness-challenge.md +174 -0
  37. package/templates/skills/business-analyse/references/multi-app-detection.md +149 -0
  38. package/templates/skills/business-analyse/references/portal-classification.md +52 -0
  39. package/templates/skills/business-analyse/references/validation-checklist.md +30 -1
  40. package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +37 -4
  41. package/templates/skills/business-analyse/steps/step-00-init.md +22 -190
  42. package/templates/skills/business-analyse/steps/step-01-cadrage.md +365 -269
  43. package/templates/skills/business-analyse/steps/step-02-structure.md +98 -20
  44. package/templates/skills/business-analyse/steps/step-03-specify.md +652 -229
  45. package/templates/skills/business-analyse/steps/step-04-consolidate.md +308 -287
  46. package/templates/skills/business-analyse-design/SKILL.md +10 -0
  47. package/templates/skills/business-analyse-design/references/screens-post-check.md +221 -0
  48. package/templates/skills/business-analyse-design/references/screens-type-mapping.md +138 -0
  49. package/templates/skills/business-analyse-design/references/smartcomponents-templates.md +225 -0
  50. package/templates/skills/{business-analyse → business-analyse-design}/references/spec-auto-inference.md +117 -117
  51. package/templates/skills/business-analyse-design/steps/step-01-screens.md +36 -162
  52. package/templates/skills/business-analyse-design/steps/step-02-wireframes.md +8 -7
  53. package/templates/skills/business-analyse-design/steps/step-03-navigation.md +89 -42
  54. package/templates/skills/business-analyse-develop/references/compact-loop.md +9 -0
  55. package/templates/skills/business-analyse-develop/references/handoff-quality-gate.md +132 -0
  56. package/templates/skills/business-analyse-develop/references/prd-v3-transformation.md +326 -0
  57. package/templates/skills/business-analyse-develop/references/report-reconciliation.md +140 -0
  58. package/templates/skills/business-analyse-develop/references/report-template.md +142 -0
  59. package/templates/skills/business-analyse-develop/steps/step-01-task.md +5 -177
  60. package/templates/skills/business-analyse-develop/steps/step-02-execute.md +17 -4
  61. package/templates/skills/business-analyse-develop/steps/step-03-commit.md +6 -2
  62. package/templates/skills/business-analyse-develop/steps/step-04-check.md +6 -0
  63. package/templates/skills/business-analyse-develop/steps/step-05-report.md +3 -269
  64. package/templates/skills/business-analyse-handoff/SKILL.md +10 -0
  65. package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +208 -0
  66. package/templates/skills/business-analyse-handoff/references/context-isolation-pattern.md +47 -0
  67. package/templates/skills/business-analyse-handoff/references/handoff-file-inventory.md +49 -0
  68. package/templates/skills/business-analyse-handoff/references/handoff-global-validation.md +142 -0
  69. package/templates/skills/business-analyse-handoff/references/prd-validation-checks.md +125 -0
  70. package/templates/skills/business-analyse-handoff/references/project-index-update.md +98 -0
  71. package/templates/skills/business-analyse-handoff/steps/step-01-transform.md +9 -160
  72. package/templates/skills/business-analyse-handoff/steps/step-02-export.md +10 -99
  73. package/templates/skills/business-analyse-html/SKILL.md +10 -0
  74. package/templates/skills/business-analyse-html/html/ba-interactive.html +306 -81
  75. package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +15 -2
  76. package/templates/skills/business-analyse-html/html/src/scripts/02-navigation.js +6 -46
  77. package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +88 -33
  78. package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +116 -0
  79. package/templates/skills/business-analyse-html/html/src/styles/10-diagrams.css +73 -0
  80. package/templates/skills/business-analyse-html/html/src/template.html +2 -0
  81. package/templates/skills/business-analyse-html/references/02-embedded-artifacts-building.md +144 -0
  82. package/templates/skills/business-analyse-html/references/02-feature-data-building.md +141 -0
  83. package/templates/skills/business-analyse-html/references/02-mapping-tables.md +442 -0
  84. package/templates/skills/business-analyse-html/references/02-normalization-helpers.md +139 -0
  85. package/templates/skills/business-analyse-html/references/02-screen-format-detection.md +283 -0
  86. package/templates/skills/business-analyse-html/references/02-self-check-validation.md +199 -0
  87. package/templates/skills/business-analyse-html/references/data-build.md +22 -1
  88. package/templates/skills/business-analyse-html/references/data-mapping.md +40 -5
  89. package/templates/skills/business-analyse-html/steps/step-02-build-data.md +12 -555
  90. package/templates/skills/business-analyse-review/SKILL.md +10 -0
  91. package/templates/skills/business-analyse-status/SKILL.md +8 -0
  92. package/templates/skills/dev-start/SKILL.md +143 -307
  93. package/templates/skills/efcore/SKILL.md +13 -0
@@ -121,24 +121,67 @@ t('employees:messages.created', '{{entity}} created', { entity: 'Employee' }) //
121
121
  t('common:actions.save', 'Save') // Cross-namespace
122
122
  ```
123
123
 
124
- ### Namespace Registration (CRITICAL — POST-CHECK C39)
124
+ ### Namespace Registration (CRITICAL — POST-CHECK C39 + GATE PG-4)
125
125
 
126
- After creating i18n JSON files, register each namespace in `src/i18n/config.ts`:
126
+ After creating i18n JSON files, register each namespace in `src/i18n/config.ts` or `index.ts`:
127
127
 
128
128
  ```typescript
129
- import employees from './locales/fr/employees.json';
130
- // In resources: fr: { employees, common, navigation, ... }
131
- // OR in ns array: ns: ['common', 'navigation', 'employees'],
129
+ // CORRECT Full i18n config with module namespaces registered
130
+ import i18n from 'i18next';
131
+ import { initReactI18next } from 'react-i18next';
132
+ import LanguageDetector from 'i18next-browser-languagedetector';
133
+
134
+ // Import ALL module translation files for each language
135
+ import frEmployes from './locales/fr/employes.json';
136
+ import enEmployes from './locales/en/employes.json';
137
+ import itEmployes from './locales/it/employes.json';
138
+ import deEmployes from './locales/de/employes.json';
139
+ import frAbsences from './locales/fr/absences.json';
140
+ import enAbsences from './locales/en/absences.json';
141
+ import itAbsences from './locales/it/absences.json';
142
+ import deAbsences from './locales/de/absences.json';
143
+ // ... repeat for each module
144
+
145
+ i18n
146
+ .use(LanguageDetector)
147
+ .use(initReactI18next)
148
+ .init({
149
+ fallbackLng: 'fr',
150
+ ns: ['employes', 'absences'], // List ALL namespaces
151
+ defaultNS: 'employes',
152
+ interpolation: { escapeValue: false },
153
+ resources: {
154
+ fr: { employes: frEmployes, absences: frAbsences },
155
+ en: { employes: enEmployes, absences: enAbsences },
156
+ it: { employes: itEmployes, absences: itAbsences },
157
+ de: { employes: deEmployes, absences: deAbsences },
158
+ },
159
+ });
160
+
161
+ export default i18n;
132
162
  ```
133
163
 
134
- Root cause (test-apex-007): JSON files existed but namespaces were not registered → `useTranslation(['module'])` returned empty strings.
164
+ ```typescript
165
+ // WRONG — Missing namespace imports (keys show as raw strings like "list.title")
166
+ i18n.init({
167
+ resources: {
168
+ en: { translation: { welcome: 'Welcome' } },
169
+ fr: { translation: { welcome: 'Bienvenue' } },
170
+ },
171
+ });
172
+ // ❌ Module JSON files exist on disk but are NEVER loaded → useTranslation('employes') returns empty
173
+ ```
174
+
175
+ Root cause (test-apex-007, test-ba-010): JSON files existed but namespaces were not registered → `useTranslation(['module'])` returned empty strings. All t() calls displayed raw keys like `list.title`, `form.department` instead of translated text.
135
176
 
136
177
  **Rules:**
137
- - Provide fallback value as 2nd argument to `t()`
178
+ - Provide fallback value as 2nd argument to `t()`: `t('employes:list.title', 'Liste des employés')`
138
179
  - Use namespace prefix: `t('namespace:key')` — never `t('key')` without namespace
139
180
  - Generate 4 language files (fr, en, it, de) with identical key structures
140
181
  - Register new namespaces in i18n config file after creating JSON files
141
182
  - Do not hardcode user-facing strings in JSX
183
+ - **After writing i18n JSON files:** IMMEDIATELY update i18n config with imports + resources registration
184
+ - **Verify:** `grep -q "{namespace}" src/**/i18n/index.ts` MUST match for EVERY module namespace
142
185
 
143
186
  ---
144
187
 
@@ -285,9 +328,12 @@ Do NOT use: `bg-white`, `border-gray-200`, `text-gray-900`, hardcoded hex/rgb.
285
328
  - Do not use raw `<table>` — use `DataTable`
286
329
  - Do not create custom spinners — use `Loader2` from lucide-react
287
330
  - Do not import axios directly — use `@/services/api/apiClient`
331
+ - **Do not use raw `fetch()` — use `apiClient` from `@/services/api`** (handles auth, token refresh, tenant isolation)
288
332
  - Use `PageLoader` as Suspense fallback
289
333
  - Use `EntityLookup` for FK Guid fields (not `<select>` or `<input>` for GUIDs)
290
334
 
335
+ > **API access rule:** ALL API calls in hooks and pages MUST use `apiClient` (or the typed API service generated by MCP `scaffold_api_client`). Using raw `fetch()` bypasses authentication, token refresh, and tenant isolation — the page will fail silently when the user is logged in. This applies to custom hooks in `src/hooks/` as well as inline API calls in pages.
336
+
291
337
  ---
292
338
 
293
339
  ## 6. Foreign Key Fields & Entity Lookup
@@ -224,6 +224,79 @@ All naming variants are derived from {app_name}, {module_code}, {sections}. Do N
224
224
 
225
225
  Call `mcp__smartstack__validate_conventions` after computing derivations. Fix any errors BEFORE step-01.
226
226
 
227
+ ### 4g. Entity-Section Mapping (DETERMINISTIC)
228
+
229
+ > Each entity MUST be mapped to a hierarchy level. This determines the entity's NavRoute segment count
230
+ > (2 vs 3 segments) and whether section-level permissions are needed.
231
+ > **This mapping is consumed by:** step-03b (permissions), step-03c (controllers), step-04 (validation).
232
+
233
+ **Rules:**
234
+
235
+ 1. The entity whose name derives from `{module_code}` is the **module-level entity**.
236
+ It gets `NavRoute = {app}.{module}` (2 segments). Its permissions are module-level.
237
+
238
+ 2. Every other entity that matches a section from `{sections}` is a **section-level entity**.
239
+ It gets `NavRoute = {app}.{module}.{section_code}` (3 segments). Its permissions are section-level.
240
+
241
+ 3. Entities that match NO section stay **module-level** (share the module's NavRoute).
242
+ This is the case for true sub-resources without their own navigation entry (e.g., InvoiceLine).
243
+
244
+ **Matching algorithm:**
245
+
246
+ ```
247
+ FOR EACH entity in {entities}:
248
+ entity_kebab = kebab(pluralize(entity.name)) // "Contact" → "contacts", "Department" → "departments"
249
+
250
+ IF entity_kebab == {module_code} OR singularize(entity_kebab) == singularize({module_code}):
251
+ entity.level = "module"
252
+ entity.navRoute = "{app_name_kebab}.{module_code}"
253
+ entity.section_code = null
254
+
255
+ ELSE:
256
+ matched = {sections}.find(s =>
257
+ s.code == entity_kebab // exact: "contacts" == "contacts"
258
+ OR s.code == singularize(entity_kebab) // singular: "types" for "AbsenceType"
259
+ OR s.code.endsWith(entity_kebab) // suffix: "absence-types" ends with "types"
260
+ OR entity_kebab.endsWith(s.code) // reverse: "project-members" ends with "members"
261
+ )
262
+
263
+ IF matched:
264
+ entity.level = "section"
265
+ entity.navRoute = "{app_name_kebab}.{module_code}.{matched.code}"
266
+ entity.section_code = matched.code
267
+ ELSE:
268
+ entity.level = "module"
269
+ entity.navRoute = "{app_name_kebab}.{module_code}"
270
+ entity.section_code = null
271
+ ```
272
+
273
+ **Delegate mode override:** When `permissionPaths` companion includes section-level paths
274
+ (3+ dot segments like `app.module.section.action`), use those paths as authoritative source
275
+ to derive entity-section mapping instead of name heuristics.
276
+
277
+ **Storage:**
278
+
279
+ ```
280
+ {entity_section_map}: [
281
+ { entity: "Employee", level: "module", navRoute: "rh.employes", section_code: null },
282
+ { entity: "Department", level: "section", navRoute: "rh.employes.departments", section_code: "departments" }
283
+ ]
284
+ ```
285
+
286
+ Propagated to: step-03b (permissions), step-03c (controllers), step-03-execute (state), step-04 (validation).
287
+
288
+ **Post-mapping validation:**
289
+
290
+ ```
291
+ # No duplicate NavRoutes among section-level entities
292
+ section_navroutes = {entity_section_map}.filter(e => e.level == "section").map(e => e.navRoute)
293
+ IF duplicates(section_navroutes) → BLOCKING: two entities mapped to same section
294
+
295
+ # All section_codes exist in {sections}
296
+ FOR EACH entry in {entity_section_map} WHERE section_code != null:
297
+ IF {sections}.find(s => s.code == entry.section_code) == null → BLOCKING: orphan section_code
298
+ ```
299
+
227
300
  ---
228
301
 
229
302
  ## 5. Collect Technical Parameters
@@ -323,6 +396,7 @@ APEX INIT COMPLETE — v{{SMARTSTACK_VERSION}}
323
396
  Task: {task_description}
324
397
  Hierarchy: {app_name} → {module_code} → {sections[].code}
325
398
  Entities: {entities} | Complexity: {module_complexity} | Deps: {has_dependencies}
399
+ Entity Map: {entity_section_map.map(e => `${e.entity}→${e.navRoute}`).join(', ')}
326
400
  Code: {code_patterns summary} | PRD: {prd_path||none} | Feature: {feature_path||none}
327
401
  Flags: {active_flags} | MCP: {available|degraded} | Needs: {migration/seed/workflow/notification}
328
402
  Specs: {specification_loading_plan ? 'companion files available (layer-specific loading)' : 'none'}
@@ -45,7 +45,7 @@ Execute all layers (Layer 0 → Layer 1 → Layer 2 → Layer 3 → Layer 4).
45
45
  ```
46
46
  BEFORE starting Layer N:
47
47
  Verify these variables are still accessible:
48
- {app_name}, {module_code}, {sections}, {entities}, {code_patterns}
48
+ {app_name}, {module_code}, {sections}, {entities}, {code_patterns}, {entity_section_map}
49
49
  IF delegate_mode: also verify {delegate_prd_path}, {specification_loading_plan}
50
50
 
51
51
  IF any variable is missing or empty:
@@ -71,7 +71,12 @@ BEFORE starting Layer N:
71
71
  - {entities}: Glob("src/**/Domain/Entities/{module_code}/*.cs") → entity names
72
72
  - {sections}: Glob("src/**/Seeding/Data/{module_code}/*NavigationSeedData.cs") → parse
73
73
  - {code_patterns}: Read state.json or re-derive from DependencyInjection.cs
74
- 4. IF recovered: verify consistency with naming derivation rules (step-00 §4f)
74
+ - {entity_section_map}: IF state.json has it use directly. ELSE re-derive:
75
+ For each entity, check controllers for [NavRoute] value:
76
+ Grep("\[NavRoute\(\"", "src/**/Controllers/**/{Entity}*Controller.cs")
77
+ → extract navRoute segments → determine level (2 seg = module, 3 seg = section)
78
+ Fallback: re-apply step-00 §4g matching algorithm with {entities} and {sections}
79
+ 4. IF recovered: verify consistency with naming derivation rules (step-00 §4f, §4g)
75
80
  5. IF Layer N-1 already completed: skip to Layer N directly
76
81
 
77
82
  Cost: ~5-8 tool calls. Only triggered if context was compressed.
@@ -160,14 +165,21 @@ After each layer's build gate passes, write state to `.claude/output/apex/{task_
160
165
  "app_name": "HumanResources",
161
166
  "module_code": "employee-management",
162
167
  "sections": ["employees", "departments"],
163
- "entities": ["Employee", "Department"]
168
+ "entities": ["Employee", "Department"],
169
+ "entity_section_map": [
170
+ { "entity": "Employee", "level": "module", "navRoute": "human-resources.employee-management", "section_code": null },
171
+ { "entity": "Department", "level": "section", "navRoute": "human-resources.employee-management.departments", "section_code": "departments" }
172
+ ]
164
173
  }
165
174
  ```
166
175
 
167
- > **Fields `delegate_mode`, `delegate_prd_path`, `app_name`, `module_code`, `sections`, `entities`**
176
+ > **Fields `delegate_mode`, `delegate_prd_path`, `app_name`, `module_code`, `sections`, `entities`, `entity_section_map`**
168
177
  > are persisted so that Context Recovery Protocol (above) can restore the full execution context
169
178
  > after context compression or resume (`-r`). The `specification_loading_plan` is rebuilt from the
170
179
  > PRD file at `delegate_prd_path` — no need to serialize it directly.
180
+ > The `entity_section_map` is critical for Layers 2-3: it determines per-entity NavRoute and
181
+ > section-level permission generation. If lost, it can be re-derived from controller [NavRoute] attributes
182
+ > or by re-applying step-00 §4g matching algorithm.
171
183
 
172
184
  ---
173
185
 
@@ -113,19 +113,45 @@ Generate all files from scratch using `references/core-seed-data.md` templates:
113
113
  → If resources defined: GetResourceEntries() + resource translations
114
114
  → Query actual parent from DB for FK (NOT deterministic GUID)
115
115
 
116
- 4. Permissions.cs
116
+ 4. Permissions.cs — MODULE-LEVEL
117
117
  → MCP generate_permissions (PRIMARY tool)
118
118
  → Static constants: public static class {Module} { public const string Read = "..."; }
119
119
  → Permission paths MUST use kebab-case matching NavRoute codes
120
120
 
121
- 5. PermissionsSeedData.cs
121
+ 4b. Permissions.cs — SECTION-LEVEL (for each entity in {entity_section_map} where level == "section")
122
+ → Add nested class per section inside the module class:
123
+ public static class {SectionPascal} {
124
+ public const string View = "{navRoute}.{section_code}.read";
125
+ public const string Create = "{navRoute}.{section_code}.create";
126
+ ...
127
+ }
128
+ → Apply permissionMode inference (core-seed-data.md Step C2):
129
+ - section code == "detail" → inherit (SKIP — no section permissions)
130
+ - section code == "dashboard" → read-only (read permission only)
131
+ - section code == "list" or "kanban" or "calendar" or "weekly" → inherit (SKIP — view of module entity)
132
+ - otherwise → crud (wildcard + read + create + update + delete)
133
+ → Reference: core-seed-data.md lines 736-799 for full template
134
+
135
+ 5. PermissionsSeedData.cs — MODULE-LEVEL
122
136
  → MCP generate_permissions first, fallback template
123
137
  → Paths match Permissions.cs, wildcard + CRUD
124
138
 
125
- 6. RolesSeedData.cs
139
+ 5b. PermissionsSeedData.cs — SECTION-LEVEL (for each entity where level == "section" AND permissionMode != "inherit")
140
+ → Add CreateSectionPermissions(Guid {sectionCode}SectionId) method
141
+ → Section-level entries: wildcard + CRUD matching Permissions.cs nested class
142
+ → Reference: core-seed-data.md Step C2 lines 758-779 for template
143
+
144
+ 6. RolesSeedData.cs — MODULE-LEVEL
126
145
  → Admin=wildcard(*), Manager=CRU, Contributor=CR, Viewer=R
127
146
  → Code-based role mapping (not deterministic GUIDs for roles)
128
147
  → Look up roles by Code at runtime
148
+
149
+ 6b. RolesSeedData.cs — SECTION-LEVEL (for each entity where level == "section" AND permissionMode != "inherit")
150
+ → Add section-level mappings in GetRolePermissionMappings():
151
+ Admin: section wildcard ({navRoute}.{section_code}.*)
152
+ Manager: read + create + update for section
153
+ Contributor: read + create for section
154
+ Viewer: read for section
129
155
  ```
130
156
 
131
157
  #### If SEED_MODE = "UPDATE"
@@ -241,9 +267,16 @@ ELSE IF Program.cs ONLY calls `MigrateAsync()` without seed data execution:
241
267
  For each section in {sections} from step-00:
242
268
  ✓ NavigationModuleSeedData has section entry + translations (4 langs)
243
269
  ✓ NavigationModuleSeedData has route: /{app}/{module}/{section}
244
- ✓ Permissions.cs has constants for this section (Read/Create/Update/Delete/Wildcard)
245
- PermissionsSeedData has HasData() for each permission
246
- RolesSeedData maps all 4 roles to section permissions
270
+
271
+ For each entity in {entity_section_map} where level == "section" AND permissionMode != "inherit":
272
+ Permissions.cs has SECTION-LEVEL nested class {SectionPascal} with constants
273
+ ✓ PermissionsSeedData has CreateSectionPermissions(Guid {section}SectionId) method
274
+ ✓ RolesSeedData.GetRolePermissionMappings() includes section-level role mappings
275
+
276
+ Module-level (always):
277
+ ✓ Permissions.cs has MODULE-LEVEL constants (Read/Create/Update/Delete/Wildcard)
278
+ ✓ PermissionsSeedData has module-level HasData() for each permission
279
+ ✓ RolesSeedData maps all 4 roles to module permissions
247
280
  ✓ {App}SeedDataProvider references all seed data classes
248
281
  ✓ Program.cs executes seed data providers after MigrateAsync()
249
282
  ✓ DI registers: services.AddScoped<IClientSeedDataProvider, {App}SeedDataProvider>()
@@ -122,21 +122,48 @@ For each entity where {code_patterns} defines strategy != "manual":
122
122
  > **AGENT BOUNDARY:** Snipper agents do NOT have access to MCP tools or Bash.
123
123
  > MCP scaffolding and bash validation MUST be executed by the principal agent.
124
124
 
125
+ #### Pre-Phase A — NavRoute Uniqueness Validation
126
+
127
+ > **BLOCKING gate.** Run BEFORE any scaffold_extension call.
128
+
129
+ ```
130
+ # Verify {entity_section_map} produces unique NavRoutes for section-level entities
131
+ allNavRoutes = {entity_section_map}.map(e => e.navRoute)
132
+ duplicates = allNavRoutes.filter((nr, i, arr) => arr.indexOf(nr) !== i)
133
+
134
+ # Module-level entities MAY share NavRoute (sub-resources without own section)
135
+ # Section-level entities MUST have unique NavRoutes
136
+ sectionNavRoutes = {entity_section_map}.filter(e => e.level == "section").map(e => e.navRoute)
137
+ sectionDuplicates = sectionNavRoutes.filter((nr, i, arr) => arr.indexOf(nr) !== i)
138
+
139
+ IF sectionDuplicates.length > 0:
140
+ BLOCKING: "Two section-level entities share the same NavRoute: {sectionDuplicates}"
141
+ → Fix entity_section_map in step-00 §4g before proceeding
142
+ ```
143
+
125
144
  #### Phase A — Sequential: MCP scaffolding (principal agent)
126
145
 
127
- For each entity, the principal agent calls MCP directly:
146
+ For each entity, the principal agent calls MCP directly with **per-entity NavRoute from `{entity_section_map}`**:
128
147
 
129
148
  ```
130
149
  IF NOT economy_mode AND entities.length > 1:
131
150
 
132
151
  FOR EACH entity in entities:
133
- 1. mcp__smartstack__scaffold_extension(type: "dto", name: "{Entity}", options: { navRoute: "{navRoute}" })
134
- 2. mcp__smartstack__scaffold_extension(type: "controller", name: "{Entity}", options: { navRoute: "{navRoute}" })
152
+ # Resolve per-entity NavRoute from entity_section_map (step-00 §4g)
153
+ entityMap = {entity_section_map}.find(e => e.entity == entity)
154
+ entityNavRoute = entityMap.navRoute // e.g., "crm.clients.contacts" for Contact
155
+ entityLevel = entityMap.level // "module" or "section"
156
+
157
+ 1. mcp__smartstack__scaffold_extension(type: "dto", name: "{Entity}", options: { navRoute: "{entityNavRoute}" })
158
+ 2. mcp__smartstack__scaffold_extension(type: "controller", name: "{Entity}", options: { navRoute: "{entityNavRoute}" })
135
159
 
136
160
  # Immediate NavRoute verification (principal has Bash access)
137
- 3. Verify: [NavRoute] present, [Route] absent, segment count >= 2
161
+ 3. Verify:
162
+ - [NavRoute] present, [Route] absent
163
+ - NavRoute value == entityNavRoute (exact match)
164
+ - Segment count matches level: "module" → 2 segments (1 dot), "section" → 3 segments (2 dots)
138
165
  If [Route] found alongside [NavRoute] → remove [Route] immediately
139
- If [NavRoute] missing → add it with correct value
166
+ If [NavRoute] missing or wrong value fix with entityNavRoute
140
167
  ```
141
168
 
142
169
  #### Phase B — Parallel: post-scaffold adjustments (Snipper agents)
@@ -160,6 +187,7 @@ After ALL Phase A scaffolding completes, launch Snipper agents in parallel:
160
187
 
161
188
  DO NOT regenerate controller or DTO files — already created by MCP in Phase A.
162
189
  DO NOT add [Route] attribute — NavRoute is already set correctly.
190
+ NavRoute for this entity: {entityNavRoute} (from entity_section_map — DO NOT change it)
163
191
  Files already created: {list of paths from Phase A}')
164
192
  # All agents launched in parallel
165
193
  ```
@@ -293,11 +321,28 @@ for ENTITY in $ENTITIES; do
293
321
  ALL_NAVROUTES="$ALL_NAVROUTES
294
322
  $NR"
295
323
  done
324
+
325
+ # Check MediatR pattern — MCP scaffold_extension generates ISender, manual code uses I*Service
326
+ FAIL_MEDIATOR=false
327
+ for ENTITY in $ENTITIES; do
328
+ CTRL=$(find src/ -path "*/Controllers/*" -name "${ENTITY}*Controller.cs" 2>/dev/null | head -1)
329
+ [ -z "$CTRL" ] && continue
330
+ if grep -qE 'private readonly I[A-Za-z0-9_]+Service ' "$CTRL" && ! grep -q 'ISender' "$CTRL"; then
331
+ echo "BLOCKING: $CTRL injects service directly instead of ISender _mediator"
332
+ echo " MCP scaffold_extension was NOT called — regenerate via MCP"
333
+ FAIL_MEDIATOR=true
334
+ fi
335
+ done
336
+ if [ "$FAIL_MEDIATOR" = true ]; then
337
+ echo "BLOCKING: One or more controllers bypass MCP scaffold_extension (HARD RULE violation)"
338
+ exit 1
339
+ fi
296
340
  ```
297
341
 
298
342
  > IF any BLOCKING found: fix the controller(s) BEFORE proceeding to Layer 3.
299
343
  > The fix is: remove [Route], add [NavRoute] with the correct dot-separated value,
300
344
  > add `using SmartStack.Api.Routing;`.
345
+ > If ISender missing: delete the controller and call `mcp__smartstack__scaffold_extension(type: 'controller')`.
301
346
 
302
347
  ### Post-Layer 2 Build Gate
303
348
 
@@ -115,7 +115,11 @@ TaskUpdate(taskId: progress_tracker_id,
115
115
  ### Frontend Tasks (sequential or parallel within layer)
116
116
 
117
117
  For each module:
118
- - API client: MCP scaffold_api_client
118
+ - API client: MCP scaffold_api_client — **MANDATORY, do not skip or generate manually.**
119
+ → The generated API client uses `apiClient` from `@/services/api` (auth, token refresh, tenant isolation).
120
+ → Hooks MUST import from the generated API service, NEVER use raw `fetch()` or direct axios.
121
+ → If `scaffold_api_client` is unavailable: generate the service manually using `apiClient` import,
122
+ but NEVER using `fetch()`. Pattern: `import apiClient from '@/services/api'; const res = await apiClient.get('/api/...');`
119
123
  - Routes — TWO mandatory steps:
120
124
  1. Generate registry: MCP scaffold_routes (source: 'controllers', outputFormat: 'componentRegistry', dryRun: false)
121
125
  → Creates/updates componentRegistry.generated.ts + navRoutes.generated.ts
@@ -163,7 +167,12 @@ For each module:
163
167
  → Follow JSON template from smartstack-frontend.md
164
168
  → Keys: actions, labels, errors, validation, columns, form, messages, empty
165
169
  → All t() calls must use namespace prefix + fallback: t('ns:key', 'Default text')
166
- → **Register namespace in i18n config** (critical POST-CHECK C39 catches this, but fix it now):
170
+ → **UTF-8 accents are MANDATORY** — NEVER use ASCII-only text for accented languages:
171
+ - FR: "Département" (not "Departement"), "employés" (not "employes"), "trouvée" (not "trouvee")
172
+ - DE: "Übersicht" (not "Ubersicht"), "Beschäftigung" (not "Beschaftigung")
173
+ - IT: "attività" (not "attivita"), "dipendentì" (not "dipendenti")
174
+ - EN: no accents needed (but proper apostrophes: "employee's" not "employees")
175
+ → **Register namespace in i18n config** (critical — POST-CHECK C39 + GATE PG-4 catch this, but fix it now):
167
176
  1. Find i18n config: `Glob("src/**/i18n/config.ts")` or `index.ts` or `i18n.ts`
168
177
  2. Read the config → find the resources/ns registration pattern
169
178
  3. Add the new namespace import + registration for all 4 languages
@@ -172,11 +181,20 @@ For each module:
172
181
  6. **I18n File/Import Alignment Check:** Verify that every locale JSON file on disk for this module
173
182
  has a corresponding import in the i18n config index.ts. Glob `src/**/i18n/locales/*/` for module files,
174
183
  then verify each is imported. Missing imports cause silent translation failures (keys show as raw strings).
184
+ 7. **If registration is missing after step 6:** This is a BLOCKING error. The page will show raw keys
185
+ (`list.title`, `form.department`) instead of translated text. Fix before proceeding.
175
186
  - Permissions: Call MCP generate_permissions for the module permission root (2 segments: {app}.{module}),
176
187
  then also call MCP generate_permissions for each section (3 segments: {app}.{module}.{section}).
177
188
  - Section routes: Ensure navigation seed data has ComponentKey matching PageRegistry keys.
178
189
  ComponentKey convention: `{app_code}.{module_code}.{section_code}` (dot-separated, lowercase).
179
190
  DynamicRouter generates the URL paths from seed data Route field.
191
+ **CRITICAL: PageRegistry keys use DOTS as hierarchy separator, NEVER hyphens.**
192
+ - Correct: `PageRegistry.register('rh.employes.list', lazy(...))`
193
+ - WRONG: `PageRegistry.register('rh-employes-list', lazy(...))`
194
+ NavigationService computes `ComponentKey = "{app.Code}.{module.Code}.{section.Code}"` at runtime.
195
+ DynamicRouter calls `PageRegistry.resolve(componentKey)` — keys MUST match exactly.
196
+ For client projects: `import { PageRegistry } from '@atlashub/smartstack';`
197
+ For SmartStack core: `import { PageRegistry } from '@/extensions/PageRegistry';`
180
198
  - MUST use src/pages/{App}/{Module}/ hierarchy (NOT flat)
181
199
 
182
200
  ### Documentation (after frontend pages exist)
@@ -281,6 +299,88 @@ When delegating to `/ui-components` skill, include explicit instructions:
281
299
  - "CSS: Use CSS variables ONLY — `bg-[var(--bg-card)]`, `text-[var(--text-primary)]`."
282
300
  - "Forms: Create/Edit forms are FULL PAGES with own routes (e.g., `/create`, `/:id/edit`)."
283
301
  - "I18n: ALL text must use `t('namespace:key', 'Fallback')`. Generate JSON files in `src/i18n/locales/`."
302
+ - "Hooks: MUST use apiClient from @/services/api — NEVER raw fetch() or direct axios import."
303
+
304
+ ### ⛔ POST-GENERATION VERIFICATION GATES (MANDATORY — run after ALL pages are created)
305
+
306
+ > **These checks are BLOCKING.** Do not commit until ALL pass. Each check targets a known failure mode
307
+ > observed in production runs where the agent bypassed /ui-components or delegated pages to Snipper agents.
308
+
309
+ ```
310
+ ### GATE PG-1: /ui-components Skill Invocation Check
311
+ # Verify that Skill("ui-components") was actually called during this layer execution.
312
+ # The HARD RULE says: "If ANY .tsx page was created by Write tool WITHOUT a prior
313
+ # Skill('ui-components') call, the frontend layer is INVALID."
314
+ #
315
+ # How to verify: Review your own tool call history in this conversation.
316
+ # IF you created ANY .tsx file in src/pages/ WITHOUT calling Skill("ui-components") first:
317
+ # → FAIL — delete ALL pages in src/pages/, invoke Skill("ui-components"), regenerate.
318
+ # → This is NOT optional. Pages generated without the skill WILL use fictional CSS classes,
319
+ # raw HTML <table>, and miss DataTable/EntityCard/CSS variables/responsive design.
320
+
321
+ ### GATE PG-2: Page Completeness Check (4 page types per section)
322
+ # For each section that has a ListPage, verify ALL 4 page types exist:
323
+ for section_dir in $(find src/pages -mindepth 2 -maxdepth 3 -type d); do
324
+ section_name=$(basename "$section_dir")
325
+ has_list=$(find "$section_dir" -name "*List*Page.tsx" | head -1)
326
+ if [ -n "$has_list" ]; then
327
+ has_detail=$(find "$section_dir" -name "*Detail*Page.tsx" | head -1)
328
+ has_create=$(find "$section_dir" -name "*Create*Page.tsx" -o -name "Create*Page.tsx" | head -1)
329
+ has_edit=$(find "$section_dir" -name "*Edit*Page.tsx" -o -name "Edit*Page.tsx" | head -1)
330
+ if [ -z "$has_detail" ] || [ -z "$has_create" ] || [ -z "$has_edit" ]; then
331
+ echo "FAIL PG-2: $section_dir missing pages: detail=${has_detail:-MISSING} create=${has_create:-MISSING} edit=${has_edit:-MISSING}"
332
+ # → Generate missing pages via Skill("ui-components")
333
+ fi
334
+ fi
335
+ done
336
+
337
+ ### GATE PG-3: No Stub Pages ("Under development" detection)
338
+ # Pages must be fully implemented — not placeholders.
339
+ grep -rl "Under development\|Under construction\|TODO.*implement\|Coming soon" src/pages/ && {
340
+ echo "FAIL PG-3: Stub pages detected — all pages must be fully implemented"
341
+ # → For each stub: invoke Skill("ui-components") to generate the real implementation
342
+ # → Stubs are NEVER acceptable as deliverables
343
+ }
344
+
345
+ ### GATE PG-4: I18n Namespace Registration Check
346
+ # JSON translation files MUST be imported in i18n config. Missing imports cause
347
+ # all translation keys to render as raw strings (e.g., "list.title" instead of "Liste des employés").
348
+ I18N_CONFIG=$(find src -path "*/i18n/index.ts" -o -path "*/i18n/config.ts" | head -1)
349
+ if [ -n "$I18N_CONFIG" ]; then
350
+ for json_file in $(find src -path "*/i18n/locales/fr/*.json" -type f); do
351
+ namespace=$(basename "$json_file" .json)
352
+ if ! grep -q "$namespace" "$I18N_CONFIG"; then
353
+ echo "FAIL PG-4: Namespace '$namespace' not registered in $I18N_CONFIG"
354
+ # → Add import + registration for all 4 languages (fr, en, it, de)
355
+ fi
356
+ done
357
+ fi
358
+
359
+ ### GATE PG-5: No Raw fetch() in Hooks
360
+ # Hooks MUST use apiClient from @/services/api (which handles auth, token refresh, tenant isolation).
361
+ # Raw fetch() bypasses authentication and session management.
362
+ grep -rl "fetch(" src/hooks/ && {
363
+ echo "FAIL PG-5: Raw fetch() found in hooks — must use apiClient from @/services/api"
364
+ # → Rewrite hooks to use: import apiClient from '@/services/api';
365
+ # → Replace fetch() calls with apiClient.get(), apiClient.post(), etc.
366
+ }
367
+
368
+ ### GATE PG-6: No Fictional CSS Classes
369
+ # Pages MUST use Tailwind + CSS variables. Fictional classes indicate /ui-components was bypassed.
370
+ FICTIONAL_CLASSES="page-container\|page-header\|page-actions\|filter-bar\|data-table\|btn btn-primary\|btn btn-outline\|btn-sm\|loading-spinner\|empty-state"
371
+ grep -rl "$FICTIONAL_CLASSES" src/pages/ && {
372
+ echo "FAIL PG-6: Fictional CSS classes found — pages were not generated by /ui-components"
373
+ # → Delete ALL pages, invoke Skill("ui-components"), regenerate from scratch
374
+ }
375
+
376
+ ### GATE PG-7: Agent Boundary Verification
377
+ # This is a self-check: review all Agent() calls made during Layer 3.
378
+ # IF any Agent(subagent_type='Snipper') prompt contained "Page.tsx" or "pages/" file creation:
379
+ # → VIOLATION of the agent boundary rule (Snipper cannot generate pages)
380
+ # → Delete pages created by Snipper, regenerate via Skill("ui-components")
381
+ ```
382
+
383
+ > **If ANY gate fails:** Fix the issue, then re-run ALL gates. Do not commit with failures.
284
384
 
285
385
  ### Frontend Tests Inline
286
386
 
@@ -48,6 +48,9 @@ import './extensions/componentRegistry.generated';
48
48
 
49
49
  ## For Client SDK Pages
50
50
 
51
+ > **CRITICAL:** Keys use DOT notation (`rh.employes.list`), NEVER hyphens (`rh-employes-list`).
52
+ > Dots are hierarchy separators matching `NavigationService.ComponentKey` computation at runtime.
53
+
51
54
  Clients register their own pages directly:
52
55
 
53
56
  ```tsx
@@ -236,3 +236,17 @@ STEP 04 (consolidate) — FINAL BA STEP
236
236
  ## Entry Point
237
237
 
238
238
  **FIRST ACTION:** Load `steps/step-00-init.md`
239
+
240
+ <success_criteria>
241
+ - Web research performed: >= 2 WebSearch per application + 1 per complex module (Level 1 + Level 2)
242
+ - cadrage.json with domain research findings, business process mapping, competitive analysis
243
+ - Module completeness challenged per application against industry archetypes (module-completeness-challenge.md)
244
+ - ALL CORE modules present or explicitly excluded with documented justification in globalScope.outOfScope
245
+ - Section completeness: workflow modules have >= 5 sections including contextual views (open-requests, rejected, history, etc.)
246
+ - Business rules: >= 2 domain-specific rules per module (not just generic CRUD validation)
247
+ - entities.json, rules.json, usecases.json, permissions.json par module
248
+ - Validation cross-module completee (modele de donnees, coherence permissions, flux E2E)
249
+ - validation.json with qualityAssessment (self-scoring) and processCoverage
250
+ - Quality self-assessment score >= 7/10 (research + modules + sections + rules)
251
+ - Statut feature mis a jour a "consolidated" dans index.json
252
+ </success_criteria>
@@ -1,5 +1,32 @@
1
1
  # Business Analysis — Shared Conventions
2
2
 
3
+ ## Fundamental Analysis Rules
4
+
5
+ 1. **NEVER skip web research.** Domain research is MANDATORY and BLOCKING.
6
+ An analysis without research is a 2/10 regardless of structural quality.
7
+
8
+ 2. **NEVER accept a thin application.** Every application MUST be challenged
9
+ against industry standard module counts (see `references/module-completeness-challenge.md`).
10
+ A 1-module billing application is ALWAYS wrong — challenge it.
11
+
12
+ 3. **ALWAYS propose domain-specific business rules.** Generic CRUD rules
13
+ (date validation, required fields) are the MINIMUM baseline. Domain rules
14
+ (half-day absences, team capacity, carry-over limits, credit note linking,
15
+ sequential invoice numbering) are what make an analysis valuable.
16
+
17
+ 4. **ALWAYS propose contextual views.** A workflow module without filtered
18
+ views (open-requests, rejected, history, my-{entity}) is incomplete.
19
+
20
+ 5. **ALWAYS process application by application.** Research each app's domain
21
+ before decomposing into modules. Challenge module completeness per app.
22
+
23
+ 6. **ACT AS AN EXPERT ANALYST, NOT A NOTE-TAKER.** Challenge, propose,
24
+ question. If the user says "billing with invoices", your job is to say
25
+ "standard billing also includes credit notes, payment tracking, and
26
+ dunning. Would you like to add these?" The user came to you for expertise.
27
+
28
+ ---
29
+
3
30
  ## JSON-First Architecture
4
31
 
5
32
  Feature data is stored as granular JSON files, NOT markdown. Each module has an `index.json` referencing thematic files.