@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.
- package/dist/index.js +152 -31
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +14 -3
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/agents/ba-reader.md +17 -15
- package/templates/agents/ba-writer.md +49 -51
- package/templates/skills/apex/SKILL.md +2 -2
- package/templates/skills/apex/_shared.md +1 -1
- package/templates/skills/apex/references/checks/backend-checks.sh +21 -7
- package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
- package/templates/skills/apex/references/checks/infrastructure-checks.sh +47 -10
- package/templates/skills/apex/references/checks/seed-checks.sh +47 -7
- package/templates/skills/apex/references/core-seed-data.md +20 -18
- package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +3 -0
- package/templates/skills/apex/references/post-checks.md +23 -3
- package/templates/skills/apex/references/smartstack-api.md +4 -4
- package/templates/skills/apex/references/smartstack-frontend.md +54 -8
- package/templates/skills/apex/references/smartstack-layers.md +6 -6
- package/templates/skills/apex/steps/step-00-init.md +75 -1
- package/templates/skills/apex/steps/step-03-execute.md +16 -4
- package/templates/skills/apex/steps/step-03b-layer1-seed.md +65 -6
- package/templates/skills/apex/steps/step-03c-layer2-backend.md +50 -5
- package/templates/skills/apex/steps/step-03d-layer3-frontend.md +226 -4
- package/templates/skills/apex/steps/step-04-examine.md +163 -0
- package/templates/skills/apex-verify/SKILL.md +110 -0
- package/templates/skills/apex-verify/references/audit-rules.md +50 -0
- package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
- package/templates/skills/apex-verify/steps/step-01-nav-audit.md +92 -0
- package/templates/skills/apex-verify/steps/step-02-crud-audit.md +127 -0
- package/templates/skills/apex-verify/steps/step-03-perm-audit.md +119 -0
- package/templates/skills/apex-verify/steps/step-04-route-audit.md +98 -0
- package/templates/skills/apex-verify/steps/step-05-report.md +110 -0
- package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +3 -0
- package/templates/skills/application/templates-frontend.md +2 -2
- package/templates/skills/business-analyse/SKILL.md +17 -3
- package/templates/skills/business-analyse/_shared.md +64 -0
- package/templates/skills/business-analyse/patterns/suggestion-catalog.md +34 -26
- package/templates/skills/business-analyse/questionnaire/01-context.md +13 -9
- package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +20 -27
- package/templates/skills/business-analyse/questionnaire.md +86 -9
- package/templates/skills/business-analyse/references/03-json-schemas.md +221 -0
- package/templates/skills/business-analyse/references/03-post-check-validation.md +208 -0
- package/templates/skills/business-analyse/references/03-smartstack-entity-guards.md +32 -0
- package/templates/skills/business-analyse/references/04-cross-module-validation.md +95 -0
- package/templates/skills/business-analyse/references/04-file-allocation.md +162 -0
- package/templates/skills/business-analyse/references/04-naming-audit-checks.md +174 -0
- package/templates/skills/business-analyse/references/04-semantic-validation-matrix.md +118 -0
- package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
- package/templates/skills/business-analyse/references/domain-research-playbook.md +234 -0
- package/templates/skills/business-analyse/references/entity-sourcing-presentation.md +166 -0
- package/templates/skills/business-analyse/references/init-resume-logic.md +70 -0
- package/templates/skills/business-analyse/references/module-completeness-challenge.md +174 -0
- package/templates/skills/business-analyse/references/multi-app-detection.md +149 -0
- package/templates/skills/business-analyse/references/portal-classification.md +52 -0
- package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
- package/templates/skills/business-analyse/references/validation-checklist.md +35 -6
- package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +50 -6
- package/templates/skills/business-analyse/steps/step-00-init.md +22 -190
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +365 -269
- package/templates/skills/business-analyse/steps/step-02-structure.md +98 -20
- package/templates/skills/business-analyse/steps/step-03-specify.md +810 -229
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +509 -278
- package/templates/skills/business-analyse-design/SKILL.md +10 -0
- package/templates/skills/business-analyse-design/references/screens-post-check.md +221 -0
- package/templates/skills/business-analyse-design/references/screens-type-mapping.md +138 -0
- package/templates/skills/business-analyse-design/references/smartcomponents-templates.md +225 -0
- package/templates/skills/{business-analyse → business-analyse-design}/references/spec-auto-inference.md +117 -117
- package/templates/skills/business-analyse-design/steps/step-01-screens.md +36 -162
- package/templates/skills/business-analyse-design/steps/step-02-wireframes.md +8 -7
- package/templates/skills/business-analyse-design/steps/step-03-navigation.md +89 -42
- package/templates/skills/business-analyse-develop/references/compact-loop.md +9 -0
- package/templates/skills/business-analyse-develop/references/handoff-quality-gate.md +132 -0
- package/templates/skills/business-analyse-develop/references/prd-v3-transformation.md +326 -0
- package/templates/skills/business-analyse-develop/references/report-reconciliation.md +140 -0
- package/templates/skills/business-analyse-develop/references/report-template.md +142 -0
- package/templates/skills/business-analyse-develop/steps/step-01-task.md +5 -177
- package/templates/skills/business-analyse-develop/steps/step-02-execute.md +17 -4
- package/templates/skills/business-analyse-develop/steps/step-03-commit.md +6 -2
- package/templates/skills/business-analyse-develop/steps/step-04-check.md +6 -0
- package/templates/skills/business-analyse-develop/steps/step-05-report.md +3 -269
- package/templates/skills/business-analyse-handoff/SKILL.md +10 -0
- package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +211 -0
- package/templates/skills/business-analyse-handoff/references/context-isolation-pattern.md +47 -0
- package/templates/skills/business-analyse-handoff/references/handoff-file-inventory.md +49 -0
- package/templates/skills/business-analyse-handoff/references/handoff-global-validation.md +142 -0
- package/templates/skills/business-analyse-handoff/references/prd-validation-checks.md +125 -0
- package/templates/skills/business-analyse-handoff/references/project-index-update.md +98 -0
- package/templates/skills/business-analyse-handoff/steps/step-01-transform.md +9 -160
- package/templates/skills/business-analyse-handoff/steps/step-02-export.md +10 -99
- package/templates/skills/business-analyse-html/SKILL.md +10 -0
- package/templates/skills/business-analyse-html/html/ba-interactive.html +504 -97
- package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +79 -2
- package/templates/skills/business-analyse-html/html/src/scripts/02-navigation.js +6 -46
- package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +94 -36
- package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +162 -0
- package/templates/skills/business-analyse-html/html/src/styles/10-diagrams.css +73 -0
- package/templates/skills/business-analyse-html/html/src/template.html +2 -0
- package/templates/skills/business-analyse-html/references/02-embedded-artifacts-building.md +144 -0
- package/templates/skills/business-analyse-html/references/02-feature-data-building.md +143 -0
- package/templates/skills/business-analyse-html/references/02-mapping-tables.md +442 -0
- package/templates/skills/business-analyse-html/references/02-normalization-helpers.md +139 -0
- package/templates/skills/business-analyse-html/references/02-screen-format-detection.md +283 -0
- package/templates/skills/business-analyse-html/references/02-self-check-validation.md +199 -0
- package/templates/skills/business-analyse-html/references/data-build.md +24 -1
- package/templates/skills/business-analyse-html/references/data-mapping.md +119 -17
- package/templates/skills/business-analyse-html/steps/step-02-build-data.md +18 -555
- package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
- package/templates/skills/business-analyse-quick/SKILL.md +807 -0
- package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
- package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
- package/templates/skills/business-analyse-review/SKILL.md +10 -0
- package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
- package/templates/skills/business-analyse-status/SKILL.md +8 -0
- package/templates/skills/dev-start/SKILL.md +143 -307
- package/templates/skills/efcore/SKILL.md +13 -0
- package/templates/skills/sketch/SKILL.md +15 -153
- package/templates/skills/ui-components/SKILL.md +1 -1
- 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
|
-
|
|
134
|
-
|
|
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:
|
|
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 →
|
|
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
|
-
→ **
|
|
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
|
---
|