@atlashub/smartstack-cli 4.74.0 → 4.76.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 (121) hide show
  1. package/dist/index.js +152 -31
  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/SKILL.md +2 -2
  9. package/templates/skills/apex/_shared.md +1 -1
  10. package/templates/skills/apex/references/checks/backend-checks.sh +21 -7
  11. package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
  12. package/templates/skills/apex/references/checks/infrastructure-checks.sh +47 -10
  13. package/templates/skills/apex/references/checks/seed-checks.sh +47 -7
  14. package/templates/skills/apex/references/core-seed-data.md +20 -18
  15. package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +3 -0
  16. package/templates/skills/apex/references/post-checks.md +23 -3
  17. package/templates/skills/apex/references/smartstack-api.md +4 -4
  18. package/templates/skills/apex/references/smartstack-frontend.md +54 -8
  19. package/templates/skills/apex/references/smartstack-layers.md +6 -6
  20. package/templates/skills/apex/steps/step-00-init.md +75 -1
  21. package/templates/skills/apex/steps/step-03-execute.md +16 -4
  22. package/templates/skills/apex/steps/step-03b-layer1-seed.md +65 -6
  23. package/templates/skills/apex/steps/step-03c-layer2-backend.md +50 -5
  24. package/templates/skills/apex/steps/step-03d-layer3-frontend.md +226 -4
  25. package/templates/skills/apex/steps/step-04-examine.md +163 -0
  26. package/templates/skills/apex-verify/SKILL.md +110 -0
  27. package/templates/skills/apex-verify/references/audit-rules.md +50 -0
  28. package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
  29. package/templates/skills/apex-verify/steps/step-01-nav-audit.md +92 -0
  30. package/templates/skills/apex-verify/steps/step-02-crud-audit.md +127 -0
  31. package/templates/skills/apex-verify/steps/step-03-perm-audit.md +119 -0
  32. package/templates/skills/apex-verify/steps/step-04-route-audit.md +98 -0
  33. package/templates/skills/apex-verify/steps/step-05-report.md +110 -0
  34. package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +3 -0
  35. package/templates/skills/application/templates-frontend.md +2 -2
  36. package/templates/skills/business-analyse/SKILL.md +17 -3
  37. package/templates/skills/business-analyse/_shared.md +64 -0
  38. package/templates/skills/business-analyse/patterns/suggestion-catalog.md +34 -26
  39. package/templates/skills/business-analyse/questionnaire/01-context.md +13 -9
  40. package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +20 -27
  41. package/templates/skills/business-analyse/questionnaire.md +86 -9
  42. package/templates/skills/business-analyse/references/03-json-schemas.md +221 -0
  43. package/templates/skills/business-analyse/references/03-post-check-validation.md +208 -0
  44. package/templates/skills/business-analyse/references/03-smartstack-entity-guards.md +32 -0
  45. package/templates/skills/business-analyse/references/04-cross-module-validation.md +95 -0
  46. package/templates/skills/business-analyse/references/04-file-allocation.md +162 -0
  47. package/templates/skills/business-analyse/references/04-naming-audit-checks.md +174 -0
  48. package/templates/skills/business-analyse/references/04-semantic-validation-matrix.md +118 -0
  49. package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
  50. package/templates/skills/business-analyse/references/domain-research-playbook.md +234 -0
  51. package/templates/skills/business-analyse/references/entity-sourcing-presentation.md +166 -0
  52. package/templates/skills/business-analyse/references/init-resume-logic.md +70 -0
  53. package/templates/skills/business-analyse/references/module-completeness-challenge.md +174 -0
  54. package/templates/skills/business-analyse/references/multi-app-detection.md +149 -0
  55. package/templates/skills/business-analyse/references/portal-classification.md +52 -0
  56. package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
  57. package/templates/skills/business-analyse/references/validation-checklist.md +35 -6
  58. package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +50 -6
  59. package/templates/skills/business-analyse/steps/step-00-init.md +22 -190
  60. package/templates/skills/business-analyse/steps/step-01-cadrage.md +365 -269
  61. package/templates/skills/business-analyse/steps/step-02-structure.md +98 -20
  62. package/templates/skills/business-analyse/steps/step-03-specify.md +810 -229
  63. package/templates/skills/business-analyse/steps/step-04-consolidate.md +509 -278
  64. package/templates/skills/business-analyse-design/SKILL.md +10 -0
  65. package/templates/skills/business-analyse-design/references/screens-post-check.md +221 -0
  66. package/templates/skills/business-analyse-design/references/screens-type-mapping.md +138 -0
  67. package/templates/skills/business-analyse-design/references/smartcomponents-templates.md +225 -0
  68. package/templates/skills/{business-analyse → business-analyse-design}/references/spec-auto-inference.md +117 -117
  69. package/templates/skills/business-analyse-design/steps/step-01-screens.md +36 -162
  70. package/templates/skills/business-analyse-design/steps/step-02-wireframes.md +8 -7
  71. package/templates/skills/business-analyse-design/steps/step-03-navigation.md +89 -42
  72. package/templates/skills/business-analyse-develop/references/compact-loop.md +9 -0
  73. package/templates/skills/business-analyse-develop/references/handoff-quality-gate.md +132 -0
  74. package/templates/skills/business-analyse-develop/references/prd-v3-transformation.md +326 -0
  75. package/templates/skills/business-analyse-develop/references/report-reconciliation.md +140 -0
  76. package/templates/skills/business-analyse-develop/references/report-template.md +142 -0
  77. package/templates/skills/business-analyse-develop/steps/step-01-task.md +5 -177
  78. package/templates/skills/business-analyse-develop/steps/step-02-execute.md +17 -4
  79. package/templates/skills/business-analyse-develop/steps/step-03-commit.md +6 -2
  80. package/templates/skills/business-analyse-develop/steps/step-04-check.md +6 -0
  81. package/templates/skills/business-analyse-develop/steps/step-05-report.md +3 -269
  82. package/templates/skills/business-analyse-handoff/SKILL.md +10 -0
  83. package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +211 -0
  84. package/templates/skills/business-analyse-handoff/references/context-isolation-pattern.md +47 -0
  85. package/templates/skills/business-analyse-handoff/references/handoff-file-inventory.md +49 -0
  86. package/templates/skills/business-analyse-handoff/references/handoff-global-validation.md +142 -0
  87. package/templates/skills/business-analyse-handoff/references/prd-validation-checks.md +125 -0
  88. package/templates/skills/business-analyse-handoff/references/project-index-update.md +98 -0
  89. package/templates/skills/business-analyse-handoff/steps/step-01-transform.md +9 -160
  90. package/templates/skills/business-analyse-handoff/steps/step-02-export.md +10 -99
  91. package/templates/skills/business-analyse-html/SKILL.md +10 -0
  92. package/templates/skills/business-analyse-html/html/ba-interactive.html +504 -97
  93. package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +79 -2
  94. package/templates/skills/business-analyse-html/html/src/scripts/02-navigation.js +6 -46
  95. package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
  96. package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
  97. package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +94 -36
  98. package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +162 -0
  99. package/templates/skills/business-analyse-html/html/src/styles/10-diagrams.css +73 -0
  100. package/templates/skills/business-analyse-html/html/src/template.html +2 -0
  101. package/templates/skills/business-analyse-html/references/02-embedded-artifacts-building.md +144 -0
  102. package/templates/skills/business-analyse-html/references/02-feature-data-building.md +143 -0
  103. package/templates/skills/business-analyse-html/references/02-mapping-tables.md +442 -0
  104. package/templates/skills/business-analyse-html/references/02-normalization-helpers.md +139 -0
  105. package/templates/skills/business-analyse-html/references/02-screen-format-detection.md +283 -0
  106. package/templates/skills/business-analyse-html/references/02-self-check-validation.md +199 -0
  107. package/templates/skills/business-analyse-html/references/data-build.md +24 -1
  108. package/templates/skills/business-analyse-html/references/data-mapping.md +119 -17
  109. package/templates/skills/business-analyse-html/steps/step-02-build-data.md +18 -555
  110. package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
  111. package/templates/skills/business-analyse-quick/SKILL.md +807 -0
  112. package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
  113. package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
  114. package/templates/skills/business-analyse-review/SKILL.md +10 -0
  115. package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
  116. package/templates/skills/business-analyse-status/SKILL.md +8 -0
  117. package/templates/skills/dev-start/SKILL.md +143 -307
  118. package/templates/skills/efcore/SKILL.md +13 -0
  119. package/templates/skills/sketch/SKILL.md +15 -153
  120. package/templates/skills/ui-components/SKILL.md +1 -1
  121. package/templates/skills/ui-components/patterns/data-table.md +1 -1
@@ -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
 
@@ -41,6 +41,108 @@ if (delegate_mode && specification_loading_plan) {
41
41
  > - **Dashboard pages**: use `screen.kpis[]` for KPI card and chart definitions
42
42
  > - **Actions**: use `screen.actions[]` for action buttons (create, export, bulk operations)
43
43
 
44
+ ### Build Per-Entity PRD Context Block (delegate mode)
45
+
46
+ > **CRITICAL:** This block builds the authoritative specification that MUST be passed to `/ui-components`.
47
+ > Without it, `/ui-components` infers columns from entity structure and misses PRD-specified fields
48
+ > (e.g., Person Extension Pattern display fields like DisplayFirstName, DisplayLastName).
49
+
50
+ When companion specs are loaded, build a `{prd_context}` map keyed by entity name:
51
+
52
+ ```javascript
53
+ const prdContextMap = {}; // entityName → structured PRD block
54
+
55
+ if (screensData?.sections) {
56
+ for (const section of screensData.sections) {
57
+ for (const resource of (section.resources || [])) {
58
+ if (!resource.entity) continue;
59
+ const entityName = resource.entity;
60
+
61
+ if (!prdContextMap[entityName]) {
62
+ prdContextMap[entityName] = { screens: [], labels: {}, permissions: [] };
63
+ }
64
+
65
+ // Collect screen resource specs
66
+ prdContextMap[entityName].screens.push({
67
+ sectionCode: section.code,
68
+ sectionType: section.sectionType,
69
+ resourceType: resource.type,
70
+ columns: resource.columns || null,
71
+ fields: resource.fields || null,
72
+ actions: resource.actions || [],
73
+ defaultSort: resource.defaultSort || null,
74
+ defaultPageSize: resource.defaultPageSize || 20
75
+ });
76
+
77
+ // Collect section labels (4 languages)
78
+ if (section.labels) {
79
+ prdContextMap[entityName].labels[section.code] = section.labels;
80
+ }
81
+
82
+ // Collect permissions
83
+ if (section.permission) {
84
+ prdContextMap[entityName].permissions.push(section.permission);
85
+ }
86
+ }
87
+ }
88
+ }
89
+ ```
90
+
91
+ Then, for each entity, serialize the PRD context into a text block to inject into `/ui-components`:
92
+
93
+ ```javascript
94
+ function buildPrdBlock(entityName) {
95
+ const ctx = prdContextMap[entityName];
96
+ if (!ctx || ctx.screens.length === 0) return '';
97
+
98
+ let block = '\n--- PRD SPECIFICATION (AUTHORITATIVE — use these EXACT definitions) ---\n';
99
+
100
+ // SmartTable columns
101
+ const tableScreens = ctx.screens.filter(s => s.resourceType === 'SmartTable' && s.columns);
102
+ for (const screen of tableScreens) {
103
+ block += `\nCOLUMNS for DataTable (section: "${screen.sectionCode}") — use ALL, in this EXACT order:\n`;
104
+ for (const col of screen.columns) {
105
+ block += ` - key: "${col.field}", label_fr: "${col.label?.fr || col.field}", ` +
106
+ `label_en: "${col.label?.en || col.field}", format: "${col.format}", ` +
107
+ `sortable: ${col.sortable || false}\n`;
108
+ }
109
+ if (screen.actions.length > 0) {
110
+ block += `ACTIONS: ${screen.actions.join(', ')}\n`;
111
+ }
112
+ if (screen.defaultSort) {
113
+ block += `DEFAULT SORT: field="${screen.defaultSort.field}", direction="${screen.defaultSort.direction}"\n`;
114
+ }
115
+ block += `PAGE SIZE: ${screen.defaultPageSize}\n`;
116
+ }
117
+
118
+ // SmartForm fields
119
+ const formScreens = ctx.screens.filter(s => s.resourceType === 'SmartForm' && s.fields);
120
+ for (const screen of formScreens) {
121
+ block += `\nFORM FIELDS (section: "${screen.sectionCode}"):\n`;
122
+ for (const field of screen.fields) {
123
+ block += ` - name: "${field.name}", type: "${field.type}", required: ${field.required || false}\n`;
124
+ }
125
+ }
126
+
127
+ // Labels
128
+ block += '\nSECTION LABELS (use for page titles, breadcrumbs, i18n — 4 languages):\n';
129
+ for (const [code, labels] of Object.entries(ctx.labels)) {
130
+ block += ` ${code}: fr="${labels.fr}" en="${labels.en}" it="${labels.it}" de="${labels.de}"\n`;
131
+ }
132
+
133
+ // Permissions
134
+ if (ctx.permissions.length > 0) {
135
+ block += `\nPERMISSIONS: ${ctx.permissions.join(', ')}\n`;
136
+ }
137
+
138
+ block += '--- END PRD SPECIFICATION ---\n';
139
+ return block;
140
+ }
141
+ ```
142
+
143
+ **Usage:** When invoking `Skill("ui-components")`, append `buildPrdBlock(entityName)` to the arguments.
144
+ The `/ui-components` skill reads free-text instructions — the structured block is parsed naturally.
145
+
44
146
  ### ⛔ HARD RULE — /ui-components is NON-NEGOTIABLE (read BEFORE any Layer 3 action)
45
147
 
46
148
  > **VIOLATION CHECK:** If ANY .tsx page file was created by Write tool WITHOUT
@@ -115,7 +217,11 @@ TaskUpdate(taskId: progress_tracker_id,
115
217
  ### Frontend Tasks (sequential or parallel within layer)
116
218
 
117
219
  For each module:
118
- - API client: MCP scaffold_api_client
220
+ - API client: MCP scaffold_api_client — **MANDATORY, do not skip or generate manually.**
221
+ → The generated API client uses `apiClient` from `@/services/api` (auth, token refresh, tenant isolation).
222
+ → Hooks MUST import from the generated API service, NEVER use raw `fetch()` or direct axios.
223
+ → If `scaffold_api_client` is unavailable: generate the service manually using `apiClient` import,
224
+ but NEVER using `fetch()`. Pattern: `import apiClient from '@/services/api'; const res = await apiClient.get('/api/...');`
119
225
  - Routes — TWO mandatory steps:
120
226
  1. Generate registry: MCP scaffold_routes (source: 'controllers', outputFormat: 'componentRegistry', dryRun: false)
121
227
  → Creates/updates componentRegistry.generated.ts + navRoutes.generated.ts
@@ -126,6 +232,15 @@ For each module:
126
232
  → Create{Section}Page.tsx (/create route)
127
233
  → Edit{Section}Page.tsx (/:id/edit route)
128
234
  "detail" is NEVER a separate section — it's the /:id route of the list section.
235
+ - **CRUD Page Completeness (MANDATORY for every entity section with a ListPage):**
236
+ 1. ALL 4 page types MUST exist (List, Detail, Create/Form, Edit/Form)
237
+ 2. **Create button on ListPage (MANDATORY):** Every ListPage MUST include a "New" / "Create" button
238
+ in the header that calls `navigate('create')`. Without this button, users cannot access the create form.
239
+ 3. **componentRegistry registration:** ALL page types MUST be registered via `PageRegistry.register()`.
240
+ After `scaffold_routes`, verify the registry contains keys for ALL pages.
241
+ If form pages (create/edit) are missing from registry, add them manually.
242
+ 4. **POST-CHECK Gate PG-2b:** Grep every `*ListPage.tsx` for `navigate.*create`.
243
+ If absent → BLOCKING error — add the create button before proceeding.
129
244
  - Pages: **MANDATORY — INVOKE Skill("ui-components")** for ALL page generation.
130
245
  ⚠ BLOCKING REQUIREMENT: You MUST call Skill("ui-components") for EVERY page (.tsx).
131
246
  NEVER generate .tsx page code directly. NEVER write page content in Agent prompts.
@@ -163,7 +278,12 @@ For each module:
163
278
  → Follow JSON template from smartstack-frontend.md
164
279
  → Keys: actions, labels, errors, validation, columns, form, messages, empty
165
280
  → 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):
281
+ → **UTF-8 accents are MANDATORY** — NEVER use ASCII-only text for accented languages:
282
+ - FR: "Département" (not "Departement"), "employés" (not "employes"), "trouvée" (not "trouvee")
283
+ - DE: "Übersicht" (not "Ubersicht"), "Beschäftigung" (not "Beschaftigung")
284
+ - IT: "attività" (not "attivita"), "dipendentì" (not "dipendenti")
285
+ - EN: no accents needed (but proper apostrophes: "employee's" not "employees")
286
+ → **Register namespace in i18n config** (critical — POST-CHECK C39 + GATE PG-4 catch this, but fix it now):
167
287
  1. Find i18n config: `Glob("src/**/i18n/config.ts")` or `index.ts` or `i18n.ts`
168
288
  2. Read the config → find the resources/ns registration pattern
169
289
  3. Add the new namespace import + registration for all 4 languages
@@ -172,11 +292,20 @@ For each module:
172
292
  6. **I18n File/Import Alignment Check:** Verify that every locale JSON file on disk for this module
173
293
  has a corresponding import in the i18n config index.ts. Glob `src/**/i18n/locales/*/` for module files,
174
294
  then verify each is imported. Missing imports cause silent translation failures (keys show as raw strings).
295
+ 7. **If registration is missing after step 6:** This is a BLOCKING error. The page will show raw keys
296
+ (`list.title`, `form.department`) instead of translated text. Fix before proceeding.
175
297
  - Permissions: Call MCP generate_permissions for the module permission root (2 segments: {app}.{module}),
176
298
  then also call MCP generate_permissions for each section (3 segments: {app}.{module}.{section}).
177
299
  - Section routes: Ensure navigation seed data has ComponentKey matching PageRegistry keys.
178
300
  ComponentKey convention: `{app_code}.{module_code}.{section_code}` (dot-separated, lowercase).
179
301
  DynamicRouter generates the URL paths from seed data Route field.
302
+ **CRITICAL: PageRegistry keys use DOTS as hierarchy separator, NEVER hyphens.**
303
+ - Correct: `PageRegistry.register('rh.employes.list', lazy(...))`
304
+ - WRONG: `PageRegistry.register('rh-employes-list', lazy(...))`
305
+ NavigationService computes `ComponentKey = "{app.Code}.{module.Code}.{section.Code}"` at runtime.
306
+ DynamicRouter calls `PageRegistry.resolve(componentKey)` — keys MUST match exactly.
307
+ For client projects: `import { PageRegistry } from '@atlashub/smartstack';`
308
+ For SmartStack core: `import { PageRegistry } from '@/extensions/PageRegistry';`
180
309
  - MUST use src/pages/{App}/{Module}/ hierarchy (NOT flat)
181
310
 
182
311
  ### Documentation (after frontend pages exist)
@@ -227,13 +356,17 @@ IF NOT economy_mode AND entities.length > 1:
227
356
  # PHASE B — Sequential: pages via /ui-components (principal agent)
228
357
  # Snipper agents cannot call Skill() — only the principal agent can.
229
358
  For each entity (sequentially):
230
- **INVOKE Skill("ui-components")** — pass entity context:
359
+ **INVOKE Skill("ui-components")** — pass entity context + PRD spec:
231
360
  - Entity: {EntityName}, Module: {ModuleName}, App: {AppName}
232
361
  - Page types: List, Detail, Create, Edit (+ Dashboard if applicable)
233
362
  - "CSS: Use CSS variables ONLY — bg-[var(--bg-card)], text-[var(--text-primary)]"
234
363
  - "Forms: Create/Edit are FULL PAGES with own routes (/create, /:id/edit)"
235
364
  - "FK FIELDS: EntityLookup for ALL FK Guid fields"
236
365
  - "I18n: ALL text uses t('namespace:key', 'Fallback')"
366
+ - {buildPrdBlock(EntityName)} — append the PRD specification block from companion specs
367
+ - "COLUMNS ARE AUTHORITATIVE: Do NOT infer columns from entity attributes. Use ALL columns from the PRD SPECIFICATION block above, in the EXACT order listed. Do NOT add, remove, or reorder columns."
368
+ - "LABELS ARE AUTHORITATIVE: Use PRD SECTION LABELS for i18n t() fallback text and page titles. Do NOT invent labels."
369
+ - "ACTIONS ARE AUTHORITATIVE: Implement ALL action buttons listed in the PRD SPECIFICATION (create, edit, delete, approve, reject, export, etc.)."
237
370
  Generate form tests: co-located .test.tsx for Create and Edit pages
238
371
 
239
372
  ELSE:
@@ -245,12 +378,16 @@ ELSE:
245
378
  2. MCP scaffold_routes (outputFormat: 'componentRegistry')
246
379
  3. Verify PageRegistry registration (componentRegistry.generated.ts)
247
380
  No manual App.tsx wiring needed — DynamicRouter resolves at runtime
248
- 4. **INVOKE Skill("ui-components")** — pass entity context:
381
+ 4. **INVOKE Skill("ui-components")** — pass entity context + PRD spec:
249
382
  - Entity: {EntityName}, Module: {ModuleName}, App: {AppName}
250
383
  - Page types: List, Detail, Create, Edit (+ Dashboard if applicable)
251
384
  - "CSS: Use CSS variables ONLY — bg-[var(--bg-card)], text-[var(--text-primary)]"
252
385
  - "Forms: Create/Edit are FULL PAGES with own routes (/create, /:id/edit)"
253
386
  - "I18n: ALL text uses t('namespace:key', 'Fallback')"
387
+ - {buildPrdBlock(EntityName)} — append the PRD specification block from companion specs
388
+ - "COLUMNS ARE AUTHORITATIVE: Do NOT infer columns from entity attributes. Use ALL columns from the PRD SPECIFICATION block above, in the EXACT order listed."
389
+ - "LABELS ARE AUTHORITATIVE: Use PRD SECTION LABELS for i18n t() fallback text."
390
+ - "ACTIONS ARE AUTHORITATIVE: Implement ALL action buttons listed in the PRD SPECIFICATION."
254
391
  5. I18n: 4 JSON files (fr, en, it, de) + register namespace in i18n config
255
392
  6. Form tests: co-located .test.tsx for Create and Edit pages
256
393
  ```
@@ -281,6 +418,91 @@ When delegating to `/ui-components` skill, include explicit instructions:
281
418
  - "CSS: Use CSS variables ONLY — `bg-[var(--bg-card)]`, `text-[var(--text-primary)]`."
282
419
  - "Forms: Create/Edit forms are FULL PAGES with own routes (e.g., `/create`, `/:id/edit`)."
283
420
  - "I18n: ALL text must use `t('namespace:key', 'Fallback')`. Generate JSON files in `src/i18n/locales/`."
421
+ - "Hooks: MUST use apiClient from @/services/api — NEVER raw fetch() or direct axios import."
422
+ - "PRD COLUMNS: If a `--- PRD SPECIFICATION ---` block is present, use its columns[] as the EXACT DataTable columns. Do NOT add, remove, or reorder. Do NOT infer columns from entity attributes — the PRD is the source of truth."
423
+ - "PRD LABELS: If a `--- PRD SPECIFICATION ---` block is present, use its SECTION LABELS for page titles and i18n fallback text in all 4 languages."
424
+ - "PRD ACTIONS: If a `--- PRD SPECIFICATION ---` block lists actions (approve, reject, export, etc.), generate the corresponding buttons and handlers."
425
+
426
+ ### ⛔ POST-GENERATION VERIFICATION GATES (MANDATORY — run after ALL pages are created)
427
+
428
+ > **These checks are BLOCKING.** Do not commit until ALL pass. Each check targets a known failure mode
429
+ > observed in production runs where the agent bypassed /ui-components or delegated pages to Snipper agents.
430
+
431
+ ```
432
+ ### GATE PG-1: /ui-components Skill Invocation Check
433
+ # Verify that Skill("ui-components") was actually called during this layer execution.
434
+ # The HARD RULE says: "If ANY .tsx page was created by Write tool WITHOUT a prior
435
+ # Skill('ui-components') call, the frontend layer is INVALID."
436
+ #
437
+ # How to verify: Review your own tool call history in this conversation.
438
+ # IF you created ANY .tsx file in src/pages/ WITHOUT calling Skill("ui-components") first:
439
+ # → FAIL — delete ALL pages in src/pages/, invoke Skill("ui-components"), regenerate.
440
+ # → This is NOT optional. Pages generated without the skill WILL use fictional CSS classes,
441
+ # raw HTML <table>, and miss DataTable/EntityCard/CSS variables/responsive design.
442
+
443
+ ### GATE PG-2: Page Completeness Check (4 page types per section)
444
+ # For each section that has a ListPage, verify ALL 4 page types exist:
445
+ for section_dir in $(find src/pages -mindepth 2 -maxdepth 3 -type d); do
446
+ section_name=$(basename "$section_dir")
447
+ has_list=$(find "$section_dir" -name "*List*Page.tsx" | head -1)
448
+ if [ -n "$has_list" ]; then
449
+ has_detail=$(find "$section_dir" -name "*Detail*Page.tsx" | head -1)
450
+ has_create=$(find "$section_dir" -name "*Create*Page.tsx" -o -name "Create*Page.tsx" | head -1)
451
+ has_edit=$(find "$section_dir" -name "*Edit*Page.tsx" -o -name "Edit*Page.tsx" | head -1)
452
+ if [ -z "$has_detail" ] || [ -z "$has_create" ] || [ -z "$has_edit" ]; then
453
+ echo "FAIL PG-2: $section_dir missing pages: detail=${has_detail:-MISSING} create=${has_create:-MISSING} edit=${has_edit:-MISSING}"
454
+ # → Generate missing pages via Skill("ui-components")
455
+ fi
456
+ fi
457
+ done
458
+
459
+ ### GATE PG-3: No Stub Pages ("Under development" detection)
460
+ # Pages must be fully implemented — not placeholders.
461
+ grep -rl "Under development\|Under construction\|TODO.*implement\|Coming soon" src/pages/ && {
462
+ echo "FAIL PG-3: Stub pages detected — all pages must be fully implemented"
463
+ # → For each stub: invoke Skill("ui-components") to generate the real implementation
464
+ # → Stubs are NEVER acceptable as deliverables
465
+ }
466
+
467
+ ### GATE PG-4: I18n Namespace Registration Check
468
+ # JSON translation files MUST be imported in i18n config. Missing imports cause
469
+ # all translation keys to render as raw strings (e.g., "list.title" instead of "Liste des employés").
470
+ I18N_CONFIG=$(find src -path "*/i18n/index.ts" -o -path "*/i18n/config.ts" | head -1)
471
+ if [ -n "$I18N_CONFIG" ]; then
472
+ for json_file in $(find src -path "*/i18n/locales/fr/*.json" -type f); do
473
+ namespace=$(basename "$json_file" .json)
474
+ if ! grep -q "$namespace" "$I18N_CONFIG"; then
475
+ echo "FAIL PG-4: Namespace '$namespace' not registered in $I18N_CONFIG"
476
+ # → Add import + registration for all 4 languages (fr, en, it, de)
477
+ fi
478
+ done
479
+ fi
480
+
481
+ ### GATE PG-5: No Raw fetch() in Hooks
482
+ # Hooks MUST use apiClient from @/services/api (which handles auth, token refresh, tenant isolation).
483
+ # Raw fetch() bypasses authentication and session management.
484
+ grep -rl "fetch(" src/hooks/ && {
485
+ echo "FAIL PG-5: Raw fetch() found in hooks — must use apiClient from @/services/api"
486
+ # → Rewrite hooks to use: import apiClient from '@/services/api';
487
+ # → Replace fetch() calls with apiClient.get(), apiClient.post(), etc.
488
+ }
489
+
490
+ ### GATE PG-6: No Fictional CSS Classes
491
+ # Pages MUST use Tailwind + CSS variables. Fictional classes indicate /ui-components was bypassed.
492
+ FICTIONAL_CLASSES="page-container\|page-header\|page-actions\|filter-bar\|data-table\|btn btn-primary\|btn btn-outline\|btn-sm\|loading-spinner\|empty-state"
493
+ grep -rl "$FICTIONAL_CLASSES" src/pages/ && {
494
+ echo "FAIL PG-6: Fictional CSS classes found — pages were not generated by /ui-components"
495
+ # → Delete ALL pages, invoke Skill("ui-components"), regenerate from scratch
496
+ }
497
+
498
+ ### GATE PG-7: Agent Boundary Verification
499
+ # This is a self-check: review all Agent() calls made during Layer 3.
500
+ # IF any Agent(subagent_type='Snipper') prompt contained "Page.tsx" or "pages/" file creation:
501
+ # → VIOLATION of the agent boundary rule (Snipper cannot generate pages)
502
+ # → Delete pages created by Snipper, regenerate via Skill("ui-components")
503
+ ```
504
+
505
+ > **If ANY gate fails:** Fix the issue, then re-run ALL gates. Do not commit with failures.
284
506
 
285
507
  ### Frontend Tests Inline
286
508
 
@@ -168,6 +168,168 @@ for (const providerFile of providerFiles) {
168
168
 
169
169
  ---
170
170
 
171
+ ## 6d. PRD Compliance Verification (delegate mode only)
172
+
173
+ > **Run ONLY when `delegate_mode` is true AND companion spec files exist.**
174
+ > This checks that generated code matches PRD specifications — not just technical quality.
175
+ > The PRD companion files are the AUTHORITATIVE source for columns, actions, labels, and permissions.
176
+
177
+ ```javascript
178
+ if (delegate_mode) {
179
+ const specsDir = path.dirname(delegate_prd_path);
180
+ const prd = readJSON(delegate_prd_path);
181
+
182
+ const screensPath = path.join(specsDir, prd.specificationFiles?.screens);
183
+ const permsPath = path.join(specsDir, prd.specificationFiles?.permissions);
184
+ const screens = fileExists(screensPath) ? readJSON(screensPath) : null;
185
+ const perms = fileExists(permsPath) ? readJSON(permsPath) : null;
186
+
187
+ if (screens) {
188
+ // ──── PC-1 (BLOCKING): Column Completeness ────
189
+ // Every column defined in screens.json SmartTable resources MUST exist in the generated ListPage.
190
+ for (const section of screens.sections) {
191
+ for (const resource of (section.resources || [])) {
192
+ if (resource.type !== 'SmartTable' || !resource.columns) continue;
193
+ const entityName = resource.entity;
194
+ // Find the corresponding ListPage
195
+ const listPages = Glob(`src/pages/**/${entityName}*ListPage.tsx`);
196
+ if (listPages.length === 0) {
197
+ BLOCKING(`PC-1: No ListPage found for entity ${entityName} (section: ${section.code})`);
198
+ continue;
199
+ }
200
+ const pageContent = readFile(listPages[0]);
201
+ const missingCols = [];
202
+ for (const col of resource.columns) {
203
+ // Check for the column field name in the page content
204
+ if (!pageContent.includes(col.field)) {
205
+ missingCols.push(`${col.field} (${col.format})`);
206
+ }
207
+ }
208
+ if (missingCols.length > 0) {
209
+ BLOCKING(`PC-1: ${listPages[0]} missing ${missingCols.length}/${resource.columns.length} columns: ${missingCols.join(', ')}. ` +
210
+ `Fix: re-invoke Skill("ui-components") with the PRD SPECIFICATION block containing ALL columns.`);
211
+ }
212
+ }
213
+ }
214
+
215
+ // ──── PC-2 (BLOCKING): Action Button Completeness ────
216
+ // Actions defined in screens.json MUST have corresponding handlers in the generated pages.
217
+ for (const section of screens.sections) {
218
+ for (const resource of (section.resources || [])) {
219
+ if (!resource.actions || resource.actions.length === 0) continue;
220
+ const entityName = resource.entity;
221
+ const pages = Glob(`src/pages/**/${entityName}*Page.tsx`);
222
+ const allContent = pages.map(p => readFile(p)).join('\n');
223
+
224
+ for (const action of resource.actions) {
225
+ let found = false;
226
+ switch (action) {
227
+ case 'create': found = allContent.includes('navigate') && allContent.includes('create'); break;
228
+ case 'edit': found = allContent.includes('edit') || allContent.includes('Edit'); break;
229
+ case 'delete': found = allContent.includes('delete') || allContent.includes('Delete'); break;
230
+ case 'approve': found = allContent.includes('approve') || allContent.includes('Approve'); break;
231
+ case 'reject': found = allContent.includes('reject') || allContent.includes('Reject'); break;
232
+ case 'export': found = allContent.includes('export') || allContent.includes('Export'); break;
233
+ default: found = allContent.toLowerCase().includes(action.toLowerCase());
234
+ }
235
+ if (!found) {
236
+ BLOCKING(`PC-2: ${entityName} pages missing action handler for '${action}' (section: ${section.code}). ` +
237
+ `Fix: add ${action} button/handler to the relevant page.`);
238
+ }
239
+ }
240
+ }
241
+ }
242
+
243
+ // ──── PC-3 (WARNING): I18n Label Accent Accuracy ────
244
+ // French/German/Italian labels MUST have proper UTF-8 accents.
245
+ const ACCENT_PATTERNS_FR = {
246
+ 'Employes': 'Employés', 'Departement': 'Département', 'Departements': 'Départements',
247
+ 'Prenom': 'Prénom', 'Numero': 'Numéro', 'Societe': 'Société',
248
+ 'Categorie': 'Catégorie', 'Securite': 'Sécurité', 'Conge': 'Congé', 'Conges': 'Congés',
249
+ 'Generalites': 'Généralités', 'Resume': 'Résumé', 'Echeance': 'Échéance'
250
+ };
251
+ const i18nFiles = Glob(`src/**/i18n/locales/fr/*.json`);
252
+ for (const file of i18nFiles) {
253
+ const content = readFile(file);
254
+ for (const [ascii, accented] of Object.entries(ACCENT_PATTERNS_FR)) {
255
+ if (content.includes(`"${ascii}"`) || content.includes(`"${ascii} `)) {
256
+ WARNING(`PC-3: ${file} contains ASCII-only '${ascii}' — should be '${accented}'. Fix the accent.`);
257
+ }
258
+ }
259
+ }
260
+
261
+ // ──── PC-5 (WARNING): Section Page Completeness ────
262
+ // Every primary/functional section in screens.json should have a corresponding page.
263
+ for (const section of screens.sections) {
264
+ if (!['primary', 'functional'].includes(section.sectionType)) continue;
265
+ const sectionCode = section.code;
266
+ const resources = section.resources || [];
267
+ const entityNames = resources.map(r => r.entity).filter(Boolean);
268
+
269
+ // Check if any page exists for this section's entities
270
+ let pageFound = false;
271
+ for (const entity of entityNames) {
272
+ const pages = Glob(`src/pages/**/${entity}*Page.tsx`);
273
+ if (pages.length > 0) { pageFound = true; break; }
274
+ }
275
+ // Also check by section code (e.g., CalendarPage, ApprovePage)
276
+ if (!pageFound) {
277
+ const sectionPages = Glob(`src/pages/**/*${sectionCode}*Page.tsx`);
278
+ pageFound = sectionPages.length > 0;
279
+ }
280
+ if (!pageFound) {
281
+ WARNING(`PC-5: Section '${sectionCode}' (${section.sectionType}) defined in screens.json ` +
282
+ `but no corresponding page found. Consider generating it.`);
283
+ }
284
+ }
285
+ }
286
+
287
+ if (perms) {
288
+ // ──── PC-4 (BLOCKING): Permission Seed Data Completeness ────
289
+ // All permissionPaths from permissions.json MUST be seeded in PermissionsSeedData.
290
+ const permSeedFiles = Glob(`**/PermissionsSeedData.cs`);
291
+ const permClassFiles = Glob(`**/Permissions.cs`);
292
+ const allPermContent = [...permSeedFiles, ...permClassFiles].map(f => readFile(f)).join('\n');
293
+
294
+ for (const path of perms.permissionPaths) {
295
+ // Check for the permission path (may be stored as kebab-case or segments)
296
+ const segments = path.split('.');
297
+ const lastSegment = segments[segments.length - 1].toLowerCase();
298
+ if (!allPermContent.toLowerCase().includes(lastSegment)) {
299
+ BLOCKING(`PC-4: Permission path '${path}' not found in seed data. ` +
300
+ `Fix: add to PermissionsSeedData.cs and Permissions.cs constants.`);
301
+ }
302
+ }
303
+
304
+ // Verify roles from permissions.json exist in RolesSeedData
305
+ const roleSeedFiles = Glob(`**/RolesSeedData.cs`);
306
+ const allRoleContent = roleSeedFiles.map(f => readFile(f)).join('\n');
307
+ for (const role of perms.roles) {
308
+ if (!allRoleContent.includes(role.role)) {
309
+ WARNING(`PC-4b: Role '${role.role}' from permissions.json not found in RolesSeedData.cs.`);
310
+ }
311
+ }
312
+ }
313
+
314
+ // ──── PC-6 (WARNING): I18n Namespace Registration ────
315
+ // This overlaps with GATE PG-4 but cross-checks against screens.json sections.
316
+ const i18nConfig = Glob(`src/**/i18n/config.ts`)[0] || Glob(`src/**/i18n/index.ts`)[0];
317
+ if (i18nConfig && screens) {
318
+ const configContent = readFile(i18nConfig);
319
+ const moduleCode = prd.project?.module;
320
+ if (moduleCode && !configContent.includes(moduleCode)) {
321
+ WARNING(`PC-6: Module namespace '${moduleCode}' not registered in ${i18nConfig}. ` +
322
+ `Translation keys will render as raw strings.`);
323
+ }
324
+ }
325
+ }
326
+ ```
327
+
328
+ > **If ANY BLOCKING check fails:** Return to step-03d to fix the specific issue, then re-run section 6d.
329
+ > BLOCKING failures indicate the generated code does NOT match the PRD specification.
330
+
331
+ ---
332
+
171
333
  ## 7. Acceptance Criteria POST-CHECK
172
334
 
173
335
  For each AC from step-01:
@@ -210,6 +372,7 @@ AC1: {criterion} → PASS / FAIL (evidence)
210
372
  | Navigation translations (4 langs) | PASS / N/A |
211
373
  | Inline tests | PASS / N/A |
212
374
  | POST-CHECKs | PASS / N/A |
375
+ | PRD Compliance (delegate) | PASS / N/A |
213
376
  | Acceptance criteria | {X}/{Y} PASS |
214
377
 
215
378
  ---