@atlashub/smartstack-cli 4.75.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 +87 -41
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/skills/apex/SKILL.md +2 -2
- package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
- 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/post-checks.md +18 -1
- package/templates/skills/apex/references/smartstack-api.md +4 -4
- package/templates/skills/apex/references/smartstack-frontend.md +1 -1
- package/templates/skills/apex/references/smartstack-layers.md +6 -6
- package/templates/skills/apex/steps/step-00-init.md +1 -1
- package/templates/skills/apex/steps/step-03b-layer1-seed.md +26 -0
- package/templates/skills/apex/steps/step-03d-layer3-frontend.md +124 -2
- 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/templates-frontend.md +2 -2
- package/templates/skills/business-analyse/SKILL.md +3 -3
- package/templates/skills/business-analyse/_shared.md +37 -0
- package/templates/skills/business-analyse/references/03-json-schemas.md +11 -3
- package/templates/skills/business-analyse/references/03-post-check-validation.md +64 -0
- package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
- package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
- package/templates/skills/business-analyse/references/validation-checklist.md +5 -5
- package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +15 -4
- package/templates/skills/business-analyse/steps/step-03-specify.md +162 -4
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +211 -1
- package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +3 -0
- package/templates/skills/business-analyse-html/html/ba-interactive.html +198 -16
- package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +64 -0
- 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 +6 -3
- package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +46 -0
- package/templates/skills/business-analyse-html/references/02-feature-data-building.md +4 -2
- package/templates/skills/business-analyse-html/references/data-build.md +2 -0
- package/templates/skills/business-analyse-html/references/data-mapping.md +88 -21
- package/templates/skills/business-analyse-html/steps/step-02-build-data.md +6 -0
- 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/references/review-data-mapping.md +6 -0
- package/templates/skills/dev-start/SKILL.md +7 -7
- 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
|
@@ -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
|
---
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: apex-verify
|
|
3
|
+
description: |
|
|
4
|
+
Audit a generated SmartStack APEX application for common generation issues.
|
|
5
|
+
Use this skill when:
|
|
6
|
+
- Verifying a generated app has no missing CRUD pages or buttons
|
|
7
|
+
- Checking navigation seed data for reserved section codes in menu
|
|
8
|
+
- Auditing permission coverage (controllers vs Permissions.cs vs seed data)
|
|
9
|
+
- Validating route alignment (frontend vs backend vs seed)
|
|
10
|
+
- After /apex or /business-analyse-develop completes
|
|
11
|
+
argument-hint: "[--scope=all|nav|crud|perm|route] [--fix]"
|
|
12
|
+
model: sonnet
|
|
13
|
+
allowed-tools: "Read, Grep, Glob, Bash, ToolSearch"
|
|
14
|
+
entry_point: steps/step-00-init.md
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<objective>
|
|
18
|
+
Audit a SmartStack application generated by /apex or /business-analyse-develop for common post-generation issues:
|
|
19
|
+
|
|
20
|
+
1. **Navigation** — Reserved section codes (detail, create, edit) seeded as visible menu items
|
|
21
|
+
2. **CRUD** — Missing page types, missing "New" buttons on list pages, incomplete componentRegistry
|
|
22
|
+
3. **Permissions** — Cross-reference controllers, Permissions.cs, PermissionsSeedData, RolesSeedData
|
|
23
|
+
4. **Routes** — Alignment between seed data routes, PageRegistry keys, and controller NavRoutes
|
|
24
|
+
|
|
25
|
+
The audit is **read-only** by default. Use `--fix` to display actionable fix commands.
|
|
26
|
+
</objective>
|
|
27
|
+
|
|
28
|
+
<quick_start>
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
/apex-verify # Full audit (all 4 categories)
|
|
32
|
+
/apex-verify --scope=nav # Navigation menu audit only
|
|
33
|
+
/apex-verify --scope=crud # CRUD completeness audit only
|
|
34
|
+
/apex-verify --scope=perm # Permission cross-reference only
|
|
35
|
+
/apex-verify --scope=route # Route alignment only
|
|
36
|
+
/apex-verify --fix # Full audit + fix suggestions
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
</quick_start>
|
|
40
|
+
|
|
41
|
+
<parameters>
|
|
42
|
+
|
|
43
|
+
<flags>
|
|
44
|
+
| Flag | Description |
|
|
45
|
+
|------|-------------|
|
|
46
|
+
| `--scope=all` | Audit all 4 categories (default) |
|
|
47
|
+
| `--scope=nav` | Navigation audit only (reserved section codes) |
|
|
48
|
+
| `--scope=crud` | CRUD completeness only (pages, buttons, registry) |
|
|
49
|
+
| `--scope=perm` | Permission cross-reference only |
|
|
50
|
+
| `--scope=route` | Route alignment only |
|
|
51
|
+
| `--fix` | Display actionable fix commands for each finding |
|
|
52
|
+
</flags>
|
|
53
|
+
</parameters>
|
|
54
|
+
|
|
55
|
+
<workflow>
|
|
56
|
+
1. **Initialize** — Detect project structure, locate seed data, controllers, pages
|
|
57
|
+
2. **Navigation Audit** — Check for reserved section codes seeded as menu items
|
|
58
|
+
3. **CRUD Audit** — Verify 4 page types per section, create buttons, componentRegistry
|
|
59
|
+
4. **Permission Audit** — Cross-reference controllers, Permissions.cs, seed data, roles
|
|
60
|
+
5. **Route Audit** — Verify frontend/backend/seed alignment
|
|
61
|
+
6. **Report** — Consolidated findings with severity and fix suggestions
|
|
62
|
+
</workflow>
|
|
63
|
+
|
|
64
|
+
<state_variables>
|
|
65
|
+
| Variable | Type | Description |
|
|
66
|
+
|----------|------|-------------|
|
|
67
|
+
| `{scope}` | string | all, nav, crud, perm, route |
|
|
68
|
+
| `{fix_mode}` | boolean | Display fix commands |
|
|
69
|
+
| `{api_root}` | string | Path to backend src/ |
|
|
70
|
+
| `{web_root}` | string | Path to frontend web/ |
|
|
71
|
+
| `{seed_files}` | string[] | All *NavigationSeedData.cs files |
|
|
72
|
+
| `{controller_files}` | string[] | All *Controller.cs files |
|
|
73
|
+
| `{page_files}` | string[] | All *.tsx page files |
|
|
74
|
+
| `{registry_file}` | string | componentRegistry.generated.ts path |
|
|
75
|
+
| `{permissions_file}` | string | Permissions.cs path |
|
|
76
|
+
| `{findings}` | object[] | All findings with ID, severity, message, fix |
|
|
77
|
+
</state_variables>
|
|
78
|
+
|
|
79
|
+
<entry_point>
|
|
80
|
+
|
|
81
|
+
**FIRST ACTION:** Load `steps/step-00-init.md`
|
|
82
|
+
|
|
83
|
+
</entry_point>
|
|
84
|
+
|
|
85
|
+
<step_files>
|
|
86
|
+
| Step | File | Purpose |
|
|
87
|
+
|------|------|---------|
|
|
88
|
+
| 00 | `steps/step-00-init.md` | Parse flags, detect project structure, resolve paths |
|
|
89
|
+
| 01 | `steps/step-01-nav-audit.md` | Audit navigation seed data for reserved section codes |
|
|
90
|
+
| 02 | `steps/step-02-crud-audit.md` | Audit CRUD completeness (pages, buttons, registry) |
|
|
91
|
+
| 03 | `steps/step-03-perm-audit.md` | Audit permissions cross-reference |
|
|
92
|
+
| 04 | `steps/step-04-route-audit.md` | Audit route alignment |
|
|
93
|
+
| 05 | `steps/step-05-report.md` | Generate consolidated report |
|
|
94
|
+
</step_files>
|
|
95
|
+
|
|
96
|
+
<execution_rules>
|
|
97
|
+
- **Read-only** — This skill NEVER modifies code (unless --fix is used, and even then only displays commands)
|
|
98
|
+
- **Evidence-based** — Every finding must reference a specific file:line
|
|
99
|
+
- **Comprehensive** — Check ALL files, not a sample
|
|
100
|
+
- **Actionable** — Every finding must include a fix suggestion
|
|
101
|
+
- **Scoped** — Only run audits matching {scope}
|
|
102
|
+
- **SmartStack-aware** — Respect SmartStack conventions (PageRegistry, DynamicRouter, NavRoute, SeedData patterns)
|
|
103
|
+
</execution_rules>
|
|
104
|
+
|
|
105
|
+
<success_criteria>
|
|
106
|
+
- All applicable audits completed with zero false positives
|
|
107
|
+
- Each finding has: ID, severity (BLOCKING/CRITICAL/WARNING), file:line, description, fix
|
|
108
|
+
- Consolidated report with category totals
|
|
109
|
+
- Executive summary with pass/fail per category
|
|
110
|
+
</success_criteria>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Audit Rules Reference
|
|
2
|
+
|
|
3
|
+
> Complete codification of all audit rules used by /apex-verify.
|
|
4
|
+
> Each rule has a unique ID, severity, description, detection method, and fix pattern.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Navigation Rules (NAV-xxx)
|
|
9
|
+
|
|
10
|
+
| ID | Severity | Description | Detection | Fix |
|
|
11
|
+
|----|----------|-------------|-----------|-----|
|
|
12
|
+
| NAV-001 | BLOCKING | Reserved section code (detail/create/edit) seeded as visible menu item | Grep NavigationSeedData for `Code = "detail"` / `"create"` / `"edit"` without `IsActive = false` | Remove section from seed data, or set `IsActive = false` |
|
|
13
|
+
| NAV-002 | BLOCKING | Section code contains "-detail" seeded as menu item | Grep for codes matching `*-detail` pattern | Remove section entry — detail is resolved as /:id under parent section |
|
|
14
|
+
| NAV-003 | BLOCKING | Route contains forbidden segment (/detail/, /create/, /edit/) | Grep seed data routes for `/detail/`, `/create/`, `/edit/` segments | Use convention routes: /:id, /create, /:id/edit (no explicit segments) |
|
|
15
|
+
| NAV-004 | WARNING | Reserved section code with IsActive = false | Code is reserved but hidden from menu | Acceptable — remove if unnecessary |
|
|
16
|
+
| NAV-005 | WARNING | Section code not in kebab-case | Regex check: `^[a-z][a-z0-9]*(-[a-z0-9]+)*$` | Rename to kebab-case |
|
|
17
|
+
|
|
18
|
+
## CRUD Rules (CRUD-xxx)
|
|
19
|
+
|
|
20
|
+
| ID | Severity | Description | Detection | Fix |
|
|
21
|
+
|----|----------|-------------|-----------|-----|
|
|
22
|
+
| CRUD-001 | BLOCKING | ListPage missing for entity | Glob for `*ListPage.tsx` matching entity | Generate via `/ui-components` |
|
|
23
|
+
| CRUD-002 | BLOCKING | DetailPage missing for entity | Glob for `*DetailPage.tsx` matching entity | Generate via `/ui-components` |
|
|
24
|
+
| CRUD-003 | BLOCKING | Create/Edit page missing for entity | Glob for `*CreatePage.tsx`, `*EditPage.tsx`, `*FormPage.tsx` | Generate via `/ui-components` |
|
|
25
|
+
| CRUD-004 | BLOCKING | ListPage has no Create/New button | Grep for `navigate.*create` or `Link.*create` | Add navigate('create') button in page header |
|
|
26
|
+
| CRUD-005 | BLOCKING | Page exists but not registered in componentRegistry | Read registry, check for matching key | Add `PageRegistry.register()` call or run `scaffold_routes` |
|
|
27
|
+
| CRUD-006 | WARNING | Registry entry has no matching page file | Read registry, check file existence | Remove orphan registry entry or create the page |
|
|
28
|
+
| CRUD-007 | WARNING | Form page has no submit handler | Grep for `onSubmit`, `handleSubmit`, `api.create`, `api.post` | Implement form submission logic |
|
|
29
|
+
|
|
30
|
+
## Permission Rules (PERM-xxx)
|
|
31
|
+
|
|
32
|
+
| ID | Severity | Description | Detection | Fix |
|
|
33
|
+
|----|----------|-------------|-----------|-----|
|
|
34
|
+
| PERM-001 | BLOCKING | Controller uses permission not defined in Permissions.cs | Cross-ref `[RequirePermission]` vs `Permissions.cs` | Add missing constant to Permissions.cs |
|
|
35
|
+
| PERM-002 | BLOCKING | Permissions.cs constant not seeded in PermissionsSeedData | Cross-ref Permissions.cs paths vs seed data | Add `HasData()` entry in PermissionsSeedData |
|
|
36
|
+
| PERM-003 | CRITICAL | Seeded permission has no role mapping | Cross-ref seed data vs RolesSeedData | Add role-permission mapping in RolesSeedData |
|
|
37
|
+
| PERM-004 | CRITICAL | Admin role missing wildcard for module | Check admin role has `*.module.*` wildcard | Add wildcard permission for admin role |
|
|
38
|
+
| PERM-005 | WARNING | Permission path segments not in kebab-case | Regex check on permission paths | Rename to kebab-case |
|
|
39
|
+
| PERM-006 | WARNING | Viewer role has write permission | Check viewer role permissions | Remove write permissions from viewer role |
|
|
40
|
+
| PERM-007 | WARNING | Permission defined but unused by any controller | Cross-ref Permissions.cs vs controllers | Remove dead permission or add controller usage |
|
|
41
|
+
|
|
42
|
+
## Route Rules (ROUTE-xxx)
|
|
43
|
+
|
|
44
|
+
| ID | Severity | Description | Detection | Fix |
|
|
45
|
+
|----|----------|-------------|-----------|-----|
|
|
46
|
+
| ROUTE-001 | BLOCKING | Seed data route has no matching PageRegistry entry | Cross-ref seed routes vs registry keys | Add PageRegistry.register() or run scaffold_routes |
|
|
47
|
+
| ROUTE-002 | WARNING | Controller NavRoute has no matching seed data entry | Cross-ref NavRoute attrs vs seed routes | Add navigation seed data entry |
|
|
48
|
+
| ROUTE-003 | BLOCKING | PageRegistry references non-existent page file | Read registry imports, check file existence | Create page or fix import path |
|
|
49
|
+
| ROUTE-004 | WARNING | Orphan page — exists on disk but not registered | Cross-ref page files vs registry | Register page or delete orphan |
|
|
50
|
+
| ROUTE-005 | WARNING | Implicit route (detail/create/edit) not resolvable | Check registry + DynamicRouter conventions | Register in componentRegistry |
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: step-00-init
|
|
3
|
+
description: Parse flags, detect project structure, resolve paths for APEX verification
|
|
4
|
+
next_step: steps/step-01-nav-audit.md
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Step 0: Initialization
|
|
8
|
+
|
|
9
|
+
## MANDATORY EXECUTION RULES:
|
|
10
|
+
- NEVER skip project detection
|
|
11
|
+
- ALWAYS resolve actual filesystem paths before proceeding
|
|
12
|
+
- YOU ARE AN INITIALIZER, not a scanner
|
|
13
|
+
|
|
14
|
+
## YOUR TASK:
|
|
15
|
+
Parse the flags, detect the SmartStack project structure, and locate all files needed for auditing.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## EXECUTION SEQUENCE:
|
|
20
|
+
|
|
21
|
+
### 1. Parse Input
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
Arguments from $ARGUMENTS:
|
|
25
|
+
--scope=nav -> {scope} = "nav"
|
|
26
|
+
--scope=crud -> {scope} = "crud"
|
|
27
|
+
--scope=perm -> {scope} = "perm"
|
|
28
|
+
--scope=route -> {scope} = "route"
|
|
29
|
+
--scope=all -> {scope} = "all" (default)
|
|
30
|
+
--fix -> {fix_mode} = true (default: false)
|
|
31
|
+
|
|
32
|
+
No arguments -> {scope} = "all", {fix_mode} = false
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Detect Project Structure
|
|
36
|
+
|
|
37
|
+
Use Glob to locate all required files:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
Backend paths:
|
|
41
|
+
{api_root} = src/*/Api/ or src/*.Api/
|
|
42
|
+
Verify: *.sln exists at project root
|
|
43
|
+
|
|
44
|
+
Frontend paths:
|
|
45
|
+
{web_root} = web/*/src/ or web/*-web/src/
|
|
46
|
+
Verify: package.json exists in web/*/
|
|
47
|
+
|
|
48
|
+
Key files to locate:
|
|
49
|
+
{seed_files}:
|
|
50
|
+
Glob "**/Seeding/Data/**/*NavigationSeedData.cs"
|
|
51
|
+
Glob "**/Seeding/Data/**/*NavigationModuleSeedData.cs"
|
|
52
|
+
|
|
53
|
+
{controller_files}:
|
|
54
|
+
Glob "**/Controllers/**/*Controller.cs"
|
|
55
|
+
|
|
56
|
+
{page_files}:
|
|
57
|
+
Glob "web/**/src/pages/**/*Page.tsx"
|
|
58
|
+
|
|
59
|
+
{registry_file}:
|
|
60
|
+
Glob "**/componentRegistry.generated.ts" or "**/componentRegistry*.ts"
|
|
61
|
+
|
|
62
|
+
{permissions_file}:
|
|
63
|
+
Glob "**/Authorization/Permissions.cs" or "**/Common/Authorization/Permissions.cs"
|
|
64
|
+
|
|
65
|
+
{perm_seed_files}:
|
|
66
|
+
Glob "**/Seeding/Data/**/*PermissionsSeedData.cs"
|
|
67
|
+
Glob "**/Seeding/Data/**/*SeedDataProvider.cs"
|
|
68
|
+
|
|
69
|
+
{role_seed_files}:
|
|
70
|
+
Glob "**/Seeding/Data/**/*RolesSeedData.cs"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 3. Validate Paths
|
|
74
|
+
|
|
75
|
+
For each required file set:
|
|
76
|
+
- If empty: record as CRITICAL warning (cannot audit that category)
|
|
77
|
+
- If found: record paths for next steps
|
|
78
|
+
|
|
79
|
+
### 4. Show Summary and Proceed
|
|
80
|
+
|
|
81
|
+
Display:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
APEX Verification — Initialization
|
|
85
|
+
====================================
|
|
86
|
+
|
|
87
|
+
| Setting | Value |
|
|
88
|
+
|---------|-------|
|
|
89
|
+
| Scope | {scope} |
|
|
90
|
+
| Fix mode | {fix_mode} |
|
|
91
|
+
|
|
92
|
+
Project Structure:
|
|
93
|
+
| Category | Files Found |
|
|
94
|
+
|----------|-------------|
|
|
95
|
+
| Navigation seed data | {count} files |
|
|
96
|
+
| Controllers | {count} files |
|
|
97
|
+
| Frontend pages | {count} files |
|
|
98
|
+
| Component registry | {found/MISSING} |
|
|
99
|
+
| Permissions.cs | {found/MISSING} |
|
|
100
|
+
| Permission seed data | {count} files |
|
|
101
|
+
| Role seed data | {count} files |
|
|
102
|
+
|
|
103
|
+
-> Proceeding to audits...
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## SUCCESS METRICS:
|
|
109
|
+
- Scope and flags correctly parsed
|
|
110
|
+
- All filesystem paths resolved and validated
|
|
111
|
+
- Summary displayed with file counts
|
|
112
|
+
- Ready for audit steps
|
|
113
|
+
|
|
114
|
+
## NEXT STEP:
|
|
115
|
+
Based on {scope}:
|
|
116
|
+
- `all` or `nav` → proceed to `./step-01-nav-audit.md`
|
|
117
|
+
- `crud` → skip to `./step-02-crud-audit.md`
|
|
118
|
+
- `perm` → skip to `./step-03-perm-audit.md`
|
|
119
|
+
- `route` → skip to `./step-04-route-audit.md`
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: step-01-nav-audit
|
|
3
|
+
description: Audit navigation seed data for reserved section codes appearing as menu items
|
|
4
|
+
next_step: steps/step-02-crud-audit.md
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Step 1: Navigation Audit
|
|
8
|
+
|
|
9
|
+
## YOUR TASK:
|
|
10
|
+
Scan all navigation seed data files for sections with reserved codes that should NOT appear as sidebar menu items.
|
|
11
|
+
|
|
12
|
+
## RESERVED SECTION CODES:
|
|
13
|
+
```
|
|
14
|
+
RESERVED = ["detail", "create", "edit"]
|
|
15
|
+
```
|
|
16
|
+
These are internal route targets (/:id, /create, /:id/edit), NOT sidebar menu items.
|
|
17
|
+
Any section with one of these codes — or containing "detail" (e.g., "department-detail") — must NOT be a visible menu entry.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## EXECUTION SEQUENCE:
|
|
22
|
+
|
|
23
|
+
### 1. Scan Navigation Seed Data
|
|
24
|
+
|
|
25
|
+
For each file in {seed_files}:
|
|
26
|
+
|
|
27
|
+
**1a. Extract all section entries**
|
|
28
|
+
|
|
29
|
+
Use Grep to find section definitions. Two patterns to check:
|
|
30
|
+
|
|
31
|
+
Pattern A — SectionData record array:
|
|
32
|
+
```csharp
|
|
33
|
+
new("detail", "/path/:id", "icon", 4, (...))
|
|
34
|
+
```
|
|
35
|
+
→ Grep for `new\s*\("([^"]+)"` in SectionData arrays
|
|
36
|
+
|
|
37
|
+
Pattern B — NavigationSectionSeedEntry objects:
|
|
38
|
+
```csharp
|
|
39
|
+
Code = "detail",
|
|
40
|
+
```
|
|
41
|
+
→ Grep for `Code\s*=\s*"([^"]+)"`
|
|
42
|
+
|
|
43
|
+
**1b. Check each section code against RESERVED list**
|
|
44
|
+
|
|
45
|
+
For each extracted section code:
|
|
46
|
+
- If code == "detail" OR code == "create" OR code == "edit" → **BLOCKING finding**
|
|
47
|
+
- If code contains "-detail" (e.g., "department-detail") → **BLOCKING finding**
|
|
48
|
+
- Check if `IsActive = false` is set nearby (within 10 lines) — if so, downgrade to WARNING (it's hidden but still seeded, which is acceptable)
|
|
49
|
+
|
|
50
|
+
### 2. Check Route Patterns
|
|
51
|
+
|
|
52
|
+
For each section route in seed data:
|
|
53
|
+
- Route ending with `/:id` that is NOT the "list" section's detail route → WARNING (may be a separate detail section that should be merged)
|
|
54
|
+
- Route containing `/detail/` or `/create/` or `/edit/` as segments → **BLOCKING** (forbidden patterns per SmartStack convention)
|
|
55
|
+
|
|
56
|
+
### 3. Record Findings
|
|
57
|
+
|
|
58
|
+
For each issue found, create a finding:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
{
|
|
62
|
+
id: "NAV-001", // Sequential per category
|
|
63
|
+
severity: "BLOCKING",
|
|
64
|
+
category: "Navigation",
|
|
65
|
+
file: "EmployeesNavigationSeedData.cs",
|
|
66
|
+
line: 25,
|
|
67
|
+
message: "Reserved section code 'detail' is seeded as a visible menu item",
|
|
68
|
+
fix: "Remove this section entry from the Sections[] array. Detail routes are resolved by DynamicRouter convention (/:id under list section)."
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Finding IDs:
|
|
73
|
+
|
|
74
|
+
| ID | Severity | Rule |
|
|
75
|
+
|----|----------|------|
|
|
76
|
+
| NAV-001 | BLOCKING | Reserved section code (detail/create/edit) seeded as menu item |
|
|
77
|
+
| NAV-002 | BLOCKING | Section code contains "-detail" (e.g., department-detail) seeded as menu item |
|
|
78
|
+
| NAV-003 | BLOCKING | Route contains forbidden segment (/detail/, /create/, /edit/) |
|
|
79
|
+
| NAV-004 | WARNING | Reserved section code with IsActive = false (acceptable but unnecessary) |
|
|
80
|
+
| NAV-005 | WARNING | Section code not in kebab-case |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## SUCCESS METRICS:
|
|
85
|
+
- All NavigationSeedData files scanned
|
|
86
|
+
- Every section code checked against reserved list
|
|
87
|
+
- Every route checked for forbidden patterns
|
|
88
|
+
- Findings recorded with file:line evidence
|
|
89
|
+
|
|
90
|
+
## NEXT STEP:
|
|
91
|
+
If {scope} is `all` or `crud` → proceed to `./step-02-crud-audit.md`
|
|
92
|
+
If {scope} is `nav` → skip to `./step-05-report.md`
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: step-02-crud-audit
|
|
3
|
+
description: Audit CRUD completeness - 4 page types per section, create buttons, componentRegistry
|
|
4
|
+
next_step: steps/step-03-perm-audit.md
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Step 2: CRUD Audit
|
|
8
|
+
|
|
9
|
+
## YOUR TASK:
|
|
10
|
+
Verify that every section in the application has complete CRUD support: 4 page types, create button on list pages, and proper componentRegistry registration.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## EXECUTION SEQUENCE:
|
|
15
|
+
|
|
16
|
+
### 1. Identify Sections to Audit
|
|
17
|
+
|
|
18
|
+
From {seed_files}, extract all section codes that represent CRUD entities.
|
|
19
|
+
|
|
20
|
+
**Exclude from audit:**
|
|
21
|
+
- Sections with code: "dashboard", "calendar", "import-export", "approve", "balances" (these are specialized views, not CRUD entities)
|
|
22
|
+
- Sections with reserved codes: "detail", "create", "edit" (these shouldn't exist as sections)
|
|
23
|
+
|
|
24
|
+
**Include in audit:**
|
|
25
|
+
- "list" sections → audit as the main entity section
|
|
26
|
+
- "departments", "absence-types", or any other entity section → audit for CRUD completeness
|
|
27
|
+
|
|
28
|
+
For each auditable section, determine the entity name:
|
|
29
|
+
- Section code → PascalCase entity name (e.g., "departments" → "Department", "absence-types" → "AbsenceType")
|
|
30
|
+
|
|
31
|
+
### 2. Check 4 Page Types per Section
|
|
32
|
+
|
|
33
|
+
For each entity, search in {page_files} for:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
Required page types (at least 3 naming conventions per type):
|
|
37
|
+
ListPage: {Entity}ListPage.tsx OR {Entity}sPage.tsx OR {Entities}Page.tsx
|
|
38
|
+
DetailPage: {Entity}DetailPage.tsx OR {Entity}Page.tsx
|
|
39
|
+
CreatePage: Create{Entity}Page.tsx OR {Entity}CreatePage.tsx OR {Entity}FormPage.tsx
|
|
40
|
+
EditPage: Edit{Entity}Page.tsx OR {Entity}EditPage.tsx OR {Entity}FormPage.tsx (shared)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Note: Create and Edit may share a single `{Entity}FormPage.tsx` — this is acceptable.
|
|
44
|
+
|
|
45
|
+
**Findings:**
|
|
46
|
+
|
|
47
|
+
| ID | Severity | Rule |
|
|
48
|
+
|----|----------|------|
|
|
49
|
+
| CRUD-001 | BLOCKING | ListPage missing for entity |
|
|
50
|
+
| CRUD-002 | BLOCKING | DetailPage missing for entity |
|
|
51
|
+
| CRUD-003 | BLOCKING | Create/Edit page missing for entity (no CreatePage, EditPage, or FormPage) |
|
|
52
|
+
|
|
53
|
+
### 3. Check Create Button on List Pages
|
|
54
|
+
|
|
55
|
+
For each ListPage found:
|
|
56
|
+
|
|
57
|
+
Grep for navigate patterns:
|
|
58
|
+
```
|
|
59
|
+
navigate('create')
|
|
60
|
+
navigate(`create`)
|
|
61
|
+
navigate('new')
|
|
62
|
+
navigate(`${basePath}/create`)
|
|
63
|
+
navigate(`create`)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Also check for link patterns:
|
|
67
|
+
```
|
|
68
|
+
<Link to="create"
|
|
69
|
+
to={`create`}
|
|
70
|
+
to="new"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
If NONE found → **BLOCKING finding** (users cannot access create form)
|
|
74
|
+
|
|
75
|
+
| ID | Severity | Rule |
|
|
76
|
+
|----|----------|------|
|
|
77
|
+
| CRUD-004 | BLOCKING | ListPage has no Create/New button (no navigate to create route) |
|
|
78
|
+
|
|
79
|
+
### 4. Check componentRegistry Registration
|
|
80
|
+
|
|
81
|
+
Read {registry_file} and extract all `PageRegistry.register()` calls.
|
|
82
|
+
|
|
83
|
+
For each entity section:
|
|
84
|
+
- Check for list page registration: key matching `*.{section}` or `*.{module}.{section}`
|
|
85
|
+
- Check for detail page registration: key matching `*.{section}.detail` or the `:id` route
|
|
86
|
+
- Check for create page registration: key matching `*.{section}.create`
|
|
87
|
+
- Check for edit page registration: key matching `*.{section}.edit`
|
|
88
|
+
|
|
89
|
+
| ID | Severity | Rule |
|
|
90
|
+
|----|----------|------|
|
|
91
|
+
| CRUD-005 | BLOCKING | Page exists on disk but NOT registered in componentRegistry |
|
|
92
|
+
| CRUD-006 | WARNING | componentRegistry entry has no matching page file on disk |
|
|
93
|
+
|
|
94
|
+
### 5. Check Form Submit Handlers
|
|
95
|
+
|
|
96
|
+
For each Create/Edit/Form page:
|
|
97
|
+
|
|
98
|
+
Grep for submit patterns:
|
|
99
|
+
```
|
|
100
|
+
onSubmit
|
|
101
|
+
handleSubmit
|
|
102
|
+
api.create(
|
|
103
|
+
api.post(
|
|
104
|
+
api.update(
|
|
105
|
+
api.put(
|
|
106
|
+
apiClient.post(
|
|
107
|
+
apiClient.put(
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
If NONE found → WARNING (form exists but may not submit data)
|
|
111
|
+
|
|
112
|
+
| ID | Severity | Rule |
|
|
113
|
+
|----|----------|------|
|
|
114
|
+
| CRUD-007 | WARNING | Form page has no visible submit handler |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## SUCCESS METRICS:
|
|
119
|
+
- All entity sections identified from seed data
|
|
120
|
+
- 4 page types checked per entity
|
|
121
|
+
- Create button presence verified on all list pages
|
|
122
|
+
- componentRegistry cross-referenced with page files
|
|
123
|
+
- Form submit handlers checked
|
|
124
|
+
|
|
125
|
+
## NEXT STEP:
|
|
126
|
+
If {scope} is `all` or `perm` → proceed to `./step-03-perm-audit.md`
|
|
127
|
+
If {scope} is `crud` → skip to `./step-05-report.md`
|