@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.
Files changed (54) hide show
  1. package/dist/index.js +87 -41
  2. package/dist/index.js.map +1 -1
  3. package/package.json +1 -1
  4. package/templates/skills/apex/SKILL.md +2 -2
  5. package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
  6. package/templates/skills/apex/references/checks/seed-checks.sh +47 -7
  7. package/templates/skills/apex/references/core-seed-data.md +20 -18
  8. package/templates/skills/apex/references/post-checks.md +18 -1
  9. package/templates/skills/apex/references/smartstack-api.md +4 -4
  10. package/templates/skills/apex/references/smartstack-frontend.md +1 -1
  11. package/templates/skills/apex/references/smartstack-layers.md +6 -6
  12. package/templates/skills/apex/steps/step-00-init.md +1 -1
  13. package/templates/skills/apex/steps/step-03b-layer1-seed.md +26 -0
  14. package/templates/skills/apex/steps/step-03d-layer3-frontend.md +124 -2
  15. package/templates/skills/apex/steps/step-04-examine.md +163 -0
  16. package/templates/skills/apex-verify/SKILL.md +110 -0
  17. package/templates/skills/apex-verify/references/audit-rules.md +50 -0
  18. package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
  19. package/templates/skills/apex-verify/steps/step-01-nav-audit.md +92 -0
  20. package/templates/skills/apex-verify/steps/step-02-crud-audit.md +127 -0
  21. package/templates/skills/apex-verify/steps/step-03-perm-audit.md +119 -0
  22. package/templates/skills/apex-verify/steps/step-04-route-audit.md +98 -0
  23. package/templates/skills/apex-verify/steps/step-05-report.md +110 -0
  24. package/templates/skills/application/templates-frontend.md +2 -2
  25. package/templates/skills/business-analyse/SKILL.md +3 -3
  26. package/templates/skills/business-analyse/_shared.md +37 -0
  27. package/templates/skills/business-analyse/references/03-json-schemas.md +11 -3
  28. package/templates/skills/business-analyse/references/03-post-check-validation.md +64 -0
  29. package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
  30. package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
  31. package/templates/skills/business-analyse/references/validation-checklist.md +5 -5
  32. package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +15 -4
  33. package/templates/skills/business-analyse/steps/step-03-specify.md +162 -4
  34. package/templates/skills/business-analyse/steps/step-04-consolidate.md +211 -1
  35. package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +3 -0
  36. package/templates/skills/business-analyse-html/html/ba-interactive.html +198 -16
  37. package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +64 -0
  38. package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
  39. package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
  40. package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +6 -3
  41. package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +46 -0
  42. package/templates/skills/business-analyse-html/references/02-feature-data-building.md +4 -2
  43. package/templates/skills/business-analyse-html/references/data-build.md +2 -0
  44. package/templates/skills/business-analyse-html/references/data-mapping.md +88 -21
  45. package/templates/skills/business-analyse-html/steps/step-02-build-data.md +6 -0
  46. package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
  47. package/templates/skills/business-analyse-quick/SKILL.md +807 -0
  48. package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
  49. package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
  50. package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
  51. package/templates/skills/dev-start/SKILL.md +7 -7
  52. package/templates/skills/sketch/SKILL.md +15 -153
  53. package/templates/skills/ui-components/SKILL.md +1 -1
  54. package/templates/skills/ui-components/patterns/data-table.md +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlashub/smartstack-cli",
3
- "version": "4.75.0",
3
+ "version": "4.76.0",
4
4
  "description": "SmartStack Claude Code automation toolkit - GitFlow, EF Core migrations, prompts and more",
5
5
  "author": {
6
6
  "name": "SmartStack",
@@ -26,8 +26,8 @@ Execute incremental SmartStack development using the APEX methodology. This skil
26
26
  /apex -e add status field to Project entity # Economy (no agents)
27
27
  /apex -r # Resume previous
28
28
 
29
- # From /sketch (vague idea → precise command):
30
- /sketch une app RH avec employés et absences # → generates /apex command
29
+ # From /business-analyse-quick (vague idea → Mini-PRD):
30
+ /business-analyse-quick une app RH avec employés # → generates PRD for /apex -d
31
31
 
32
32
  # From /business-analyse-develop (automated multi-module):
33
33
  # /business-analyse-develop delegates to /apex -d automatically
@@ -443,6 +443,32 @@ if [ -n "$APP_TSX" ]; then
443
443
  fi
444
444
  fi
445
445
 
446
+ # POST-CHECK C64: ListPages must have Create/New button (BLOCKING)
447
+ # Every ListPage must contain a navigate('create') or Link to="create" for the New button.
448
+ # Without it, users cannot access the create form (broken CRUD workflow).
449
+ # Note: C5 catches dead links (navigate exists but page missing). C64 catches missing buttons entirely.
450
+ LIST_PAGES=$(find web/ src/pages/ -name "*ListPage.tsx" 2>/dev/null | grep -v node_modules | grep -v "\.test\." | grep -v "\.spec\." || true)
451
+ if [ -n "$LIST_PAGES" ]; then
452
+ FAIL_C64=false
453
+ for LP in $LIST_PAGES; do
454
+ # Skip dashboard-like list pages (no CRUD expected)
455
+ if echo "$LP" | grep -qiP "dashboard|calendar|approve|balance"; then
456
+ continue
457
+ fi
458
+ HAS_CREATE_NAV=$(grep -P "navigate\(.*['\"\`]create['\"\`]|navigate\(.*['\"\`].*\/create['\"\`]|to=['\"\`]create['\"\`]|to=\{.*create.*\}" "$LP" 2>/dev/null || true)
459
+ if [ -z "$HAS_CREATE_NAV" ]; then
460
+ echo "BLOCKING: ListPage missing Create/New button: $LP"
461
+ echo " Every ListPage MUST have a 'New' button with navigate('create') in the page header."
462
+ echo " Without it, users cannot access the create form — CRUD workflow is broken."
463
+ echo " Fix: Add to page header: <button onClick={() => navigate('create')}>New</button>"
464
+ FAIL_C64=true
465
+ fi
466
+ done
467
+ if [ "$FAIL_C64" = true ]; then
468
+ FAIL=true
469
+ fi
470
+ fi
471
+
446
472
  if [ "$FAIL" = true ]; then
447
473
  exit 1
448
474
  fi
@@ -41,9 +41,6 @@ if [ -n "$APP_TSX" ] && [ -n "$SEED_ROUTES" ]; then
41
41
  SEED_NORM=$(echo "$SEED_SUFFIX" | tr '[:upper:]' '[:lower:]' | tr -d '-')
42
42
  MATCH_FOUND=false
43
43
  for FE_PATH in $FRONTEND_PATHS; do
44
- if echo "$FE_PATH" | grep -qP '/list$'; then
45
- echo "WARNING: Frontend route ends with /list — should use index route instead: $FE_PATH"
46
- fi
47
44
  FE_BASE=$(echo "$FE_PATH" | sed 's|/list$||;s|/new$||;s|/:id.*||;s|/create$||')
48
45
  FE_NORM=$(echo "$FE_BASE" | tr '[:upper:]' '[:lower:]' | tr -d '-')
49
46
  if [ "$SEED_NORM" = "$FE_NORM" ]; then
@@ -174,14 +171,13 @@ if [ -n "$SECTION_SEED_FILES" ] && [ -n "$PERM_FILE" ]; then
174
171
  done
175
172
  fi
176
173
 
177
- # POST-CHECK C21: FORBIDDEN route patterns — /list and /detail/:id (WARNING)
174
+ # POST-CHECK C21: FORBIDDEN route patterns — /detail/:id (WARNING)
178
175
  SEED_NAV_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*NavigationSeedData.cs" -o -name "*NavigationSectionSeedData.cs" 2>/dev/null)
179
176
  if [ -n "$SEED_NAV_FILES" ]; then
180
- BAD_ROUTES=$(grep -Pn 'Route\s*=\s*.*"[^"]*/(list|detail)["/]' $SEED_NAV_FILES 2>/dev/null | grep -v '//.*Route' || true)
177
+ BAD_ROUTES=$(grep -Pn 'Route\s*=\s*.*"[^"]*/(detail)["/]' $SEED_NAV_FILES 2>/dev/null | grep -v '//.*Route' || true)
181
178
  if [ -n "$BAD_ROUTES" ]; then
182
179
  echo "WARNING: FORBIDDEN route pattern in seed data"
183
- echo " - 'list' section route = module route (NO /list suffix)"
184
- echo " - 'detail' section route = module route + /:id (NOT /detail/:id)"
180
+ echo " - 'detail' is an implicit route do NOT use /detail/:id as section route"
185
181
  echo "$BAD_ROUTES"
186
182
  fi
187
183
  fi
@@ -531,6 +527,50 @@ if [ "$HAS_GLOBAL_CONVERTER" = false ]; then
531
527
  fi
532
528
  fi
533
529
 
530
+ # POST-CHECK C63: Reserved section codes must NOT be seeded as visible menu items (BLOCKING)
531
+ # Reserved codes: detail, create, edit, *-detail
532
+ # These are internal route targets resolved by DynamicRouter convention, NOT sidebar menu items.
533
+ SEED_NAV_FILES=$(find src/ -path "*/Seeding/Data/*" \( -name "*NavigationSeedData.cs" -o -name "*NavigationModuleSeedData.cs" \) 2>/dev/null)
534
+ if [ -n "$SEED_NAV_FILES" ]; then
535
+ for f in $SEED_NAV_FILES; do
536
+ for RESERVED in detail create edit; do
537
+ # Pattern A: SectionData record — new("detail", ...)
538
+ MATCHES=$(grep -Pn "new\s*\(\s*\"${RESERVED}\"" "$f" 2>/dev/null || true)
539
+ if [ -n "$MATCHES" ]; then
540
+ echo "BLOCKING: Reserved section code '${RESERVED}' seeded as menu item in $f"
541
+ echo "$MATCHES"
542
+ echo " Reserved codes (detail, create, edit) are internal route targets, NOT sidebar menu items."
543
+ echo " Fix: Remove this section entry. DynamicRouter resolves /:id, /create, /:id/edit by convention."
544
+ FAIL=true
545
+ fi
546
+ # Pattern B: NavigationSectionSeedEntry — Code = "detail"
547
+ MATCHES=$(grep -Pn "Code\s*=\s*\"${RESERVED}\"" "$f" 2>/dev/null || true)
548
+ if [ -n "$MATCHES" ]; then
549
+ # Check if IsActive = false is nearby (acceptable)
550
+ HAS_INACTIVE=$(grep -A 10 "Code\s*=\s*\"${RESERVED}\"" "$f" | grep -P "IsActive\s*=\s*false" 2>/dev/null || true)
551
+ if [ -z "$HAS_INACTIVE" ]; then
552
+ echo "BLOCKING: Reserved section code '${RESERVED}' seeded with IsActive=true in $f"
553
+ echo "$MATCHES"
554
+ echo " Fix: Remove this section, or set IsActive = false."
555
+ FAIL=true
556
+ fi
557
+ fi
558
+ done
559
+ # Check for *-detail codes (e.g., department-detail)
560
+ DETAIL_SUFFIX=$(grep -Pn "new\s*\(\s*\"[a-z]+-detail\"" "$f" 2>/dev/null || true)
561
+ if [ -z "$DETAIL_SUFFIX" ]; then
562
+ DETAIL_SUFFIX=$(grep -Pn "Code\s*=\s*\"[a-z]+-detail\"" "$f" 2>/dev/null || true)
563
+ fi
564
+ if [ -n "$DETAIL_SUFFIX" ]; then
565
+ echo "BLOCKING: Section code with '-detail' suffix seeded as menu item in $f"
566
+ echo "$DETAIL_SUFFIX"
567
+ echo " Detail routes are the /:id route of their parent section."
568
+ echo " Fix: Remove this section entry. Use parent section's /:id route instead."
569
+ FAIL=true
570
+ fi
571
+ done
572
+ fi
573
+
534
574
  if [ "$FAIL" = true ]; then
535
575
  exit 1
536
576
  fi
@@ -404,12 +404,11 @@ public static readonly Guid {ResourcePascal}ResourceId = Guid.NewGuid();
404
404
 
405
405
  ### Section Methods (add to {ModulePascal}NavigationSeedData.cs)
406
406
 
407
- > **ROUTE SPECIAL CASES (list and detail):**
408
- > The `list` and `detail` sections are view modes of the module, NOT functional sub-areas.
409
- > - `list` section route = module route (e.g., `/human-resources/employees`) — NO `/list` suffix
410
- > - `detail` section route = module route + `/:id` (e.g., `/human-resources/employees/:id`) — NOT `/detail/:id`
411
- > - Do not use: `/{module}/list`, `/{module}/detail/:id`
412
- > - Other sections (dashboard, approve, import) = module route + `/{section-kebab}` (normal)
407
+ > **ROUTE CONVENTION:**
408
+ > ALL sections use a UNIFORM route pattern: module route + `/{section-code}`.
409
+ > - `list` section route = module route + `/list` (e.g., `/human-resources/employees/list`)
410
+ > - `detail` section route = module route + `/:id` (e.g., `/human-resources/employees/:id`) — NOT `/detail/:id` (detail is an implicit route, not a section)
411
+ > - Other sections (dashboard, approve, import) = module route + `/{section-kebab}` (same rule)
413
412
 
414
413
  ```csharp
415
414
  // --- Add AFTER GetTranslationEntries() in {ModulePascal}NavigationSeedData.cs ---
@@ -434,14 +433,18 @@ public static IEnumerable<NavigationSectionSeedEntry> GetSectionEntries(Guid mod
434
433
  Description = "{section1_desc_en}",
435
434
  Icon = "{section1_icon}",
436
435
  IconType = IconType.Lucide,
437
- // ROUTE CONVENTION:
438
- // - "list" section → same as module route (no extra segment)
439
- // - "detail" section → module route + "/:id"
440
- // - Other sections module route + "/{section-kebab}"
441
- // Do not use: "/employees/list", "/employees/detail/:id"
436
+ // ROUTE CONVENTION: ALL sections use module route + "/{section-code}" (uniform rule)
437
+ // - "list" section → module route + "/list" (e.g., /human-resources/employees/list)
438
+ // - Other sections → module route + "/{section-kebab}" (same rule)
439
+ // Implicit routes (detail, create, edit) are NOT sections registered by DynamicRouter convention
442
440
  Route = "{section_route}", // From seedDataCore.navigationSections[].route
443
441
  DisplayOrder = {section1_sort},
444
- IsActive = true
442
+ // VISIBILITY RULE (MANDATORY):
443
+ // Reserved codes ("detail", "create", "edit") and codes containing "-detail"
444
+ // are internal route targets, NOT sidebar menu items.
445
+ // → IsActive = false for these codes (DynamicRouter resolves them by convention)
446
+ // → IsActive = true for all other sections (visible in sidebar menu)
447
+ IsActive = true // Set to FALSE if section code is "detail", "create", "edit", or "*-detail"
445
448
  }
446
449
  // Repeat for each section...
447
450
  };
@@ -522,15 +525,14 @@ public static IEnumerable<NavigationResourceSeedEntry> GetResourceEntries(Guid s
522
525
  {
523
526
  // RESOURCE ROUTE CONVENTION:
524
527
  // Resources inherit their parent section's resolved route as base:
525
- // - Under "list" section → base = module route (no /list)
526
- // - Under "detail" section → base = module route (no /detail, resource routes don't include /:id)
528
+ // - Under "list" section → base = module route + /list
527
529
  // - Under other sections → base = module route + /{section-kebab}
528
530
  // Then append: /{resource-kebab}
529
531
  //
530
532
  // Example: resource "export" under section "dashboard":
531
533
  // Route = /human-resources/employees/dashboard/export
532
534
  // Example: resource "employees-grid" under section "list":
533
- // Route = /human-resources/employees/employees-grid (NOT /employees/list/employees-grid)
535
+ // Route = /human-resources/employees/list/employees-grid
534
536
  new NavigationResourceSeedEntry
535
537
  {
536
538
  Id = {Resource1Pascal}ResourceId,
@@ -539,7 +541,7 @@ public static IEnumerable<NavigationResourceSeedEntry> GetResourceEntries(Guid s
539
541
  Label = "{resource1_label_en}",
540
542
  EntityType = "{resource1_entity}",
541
543
  // Use parent section's resolved route + /{resource-kebab}
542
- // For "list"/"detail" sections, the section route = module route (no /list or /detail segment)
544
+ // For "list" sections, route = module route + /list. "detail" is implicit (not a section).
543
545
  Route = "{resource_route}", // From seedDataCore: parent section route + /{resource-kebab}
544
546
  DisplayOrder = 1
545
547
  }
@@ -591,10 +593,10 @@ public class NavigationResourceSeedEntry
591
593
  | `{section_label_xx}` | `specification.navigation.entries[]` where `level == "section"` → `labels.xx` |
592
594
  | `{section_icon}` | `seedDataCore.navigationSections[].icon` |
593
595
  | `{section_sort}` | `seedDataCore.navigationSections[].sort` |
594
- | `{section_route}` | `seedDataCore.navigationSections[].route` — **SPECIAL CASES:** `list` module route (no `/list`), `detail` → module route + `/:id` (no `/detail/:id`), others module route + `/{section-kebab}` |
596
+ | `{section_route}` | `seedDataCore.navigationSections[].route` — ALL sections use uniform rule: module route + `/{section-code}` (`list` → module route + `/list`). `detail` is implicit (not a section): module route + `/:id`. |
595
597
  | `{resourceCode}` | `seedDataCore.navigationResources[].code` |
596
598
  | `{resource_entity}` | `seedDataCore.navigationResources[].entity` |
597
- | `{resource_route}` | Computed from parent section route + `/{resource-kebab}`. **SPECIAL CASES:** if parent section is `list` → module route + `/{resource-kebab}` (no `/list/`), if parent is `detail` → module route + `/{resource-kebab}` (no `/detail/`). |
599
+ | `{resource_route}` | Computed from parent section route + `/{resource-kebab}`. For `list` parent → module route + `/list/{resource-kebab}`. For other parents → module route + `/{section-kebab}/{resource-kebab}`. |
598
600
  | `{parentSectionCode}` | `seedDataCore.navigationResources[].parentCode` |
599
601
 
600
602
  ---
@@ -75,6 +75,7 @@ bash references/checks/infrastructure-checks.sh
75
75
  | C37 | CRITICAL | Detail page tabs must NOT navigate() — content switches locally | frontend-checks.sh |
76
76
  | C49 | BLOCKING | Route Ordering in App.tsx — static before dynamic | frontend-checks.sh |
77
77
  | C52 | BLOCKING | Frontend route paths must include module segment | frontend-checks.sh |
78
+ | C64 | BLOCKING | ListPages must have Create/New button (navigate to create route) | frontend-checks.sh |
78
79
 
79
80
  ### Seed Data — Navigation, Roles, Permissions (C1-C2, C10, C15-C23, C32-C35, C44-C48, C53)
80
81
 
@@ -89,7 +90,7 @@ bash references/checks/infrastructure-checks.sh
89
90
  | C18 | BLOCKING | Permissions.cs static constants must exist | seed-checks.sh |
90
91
  | C19 | BLOCKING | ApplicationRolesSeedData.cs must exist | seed-checks.sh |
91
92
  | C20 | WARNING | Section route completeness (NavigationSection → frontend route + permissions) | seed-checks.sh |
92
- | C21 | WARNING | FORBIDDEN route patterns — /list and /detail/:id | seed-checks.sh |
93
+ | C21 | WARNING | FORBIDDEN route patterns — /detail/:id | seed-checks.sh |
93
94
  | C22 | WARNING | Permission path segment count (2-4 dots expected) | seed-checks.sh |
94
95
  | C23 | BLOCKING | IClientSeedDataProvider must have 4 methods with real implementation (not stubs) + DI registration | seed-checks.sh |
95
96
  | C32 | CRITICAL | Translation seed data must have idempotency guard | seed-checks.sh |
@@ -104,6 +105,7 @@ bash references/checks/infrastructure-checks.sh
104
105
  | C53 | BLOCKING | Enum serialization — JsonStringEnumConverter required | seed-checks.sh |
105
106
  | C57 | BLOCKING | SeedDataProvider Seed*Async methods must NOT be `return Task.CompletedTask` or empty body — stubs cause empty menu | seed-checks.sh |
106
107
  | C58 | BLOCKING | File paths in filesToCreate must be valid C# identifiers — no spaces, apostrophes, or accents | seed-checks.sh |
108
+ | C63 | BLOCKING | Reserved section codes (detail/create/edit/*-detail) must NOT be seeded as menu items | seed-checks.sh |
107
109
 
108
110
  ### Architecture — Clean Architecture Layer Isolation (A1-A8)
109
111
 
@@ -135,6 +137,21 @@ bash references/checks/infrastructure-checks.sh
135
137
  | C60 | BLOCKING | Controllers must inject ISender _mediator, NOT direct business services | infrastructure-checks.sh |
136
138
  | C61 | BLOCKING | Controllers with [NavRoute] must have [Authorize] attribute | infrastructure-checks.sh |
137
139
 
140
+ ### PRD Compliance — Delegate Mode Only (PC-1 to PC-6)
141
+
142
+ > **These checks run ONLY in delegate mode** (when `/apex -d` is used with companion spec files).
143
+ > They validate that generated code matches the PRD specifications from `.ralph/` companion files.
144
+ > Implemented in `step-04-examine.md` section 6d — no separate .sh script needed.
145
+
146
+ | ID | Severity | Description | Source |
147
+ |----|----------|-------------|--------|
148
+ | PC-1 | BLOCKING | All columns from screens.json SmartTable resources must exist in generated ListPage | step-04 §6d |
149
+ | PC-2 | BLOCKING | All actions from screens.json (create, edit, delete, approve, reject, export) must have handlers in pages | step-04 §6d |
150
+ | PC-3 | WARNING | I18n labels must have correct UTF-8 accents (FR: Employés not Employes, DE: Übersicht not Ubersicht) | step-04 §6d |
151
+ | PC-4 | BLOCKING | All permissionPaths from permissions.json must exist in PermissionsSeedData.cs and Permissions.cs | step-04 §6d |
152
+ | PC-5 | WARNING | Every primary/functional section in screens.json must have a corresponding page file | step-04 §6d |
153
+ | PC-6 | WARNING | Module i18n namespace must be registered in i18n config (prevents raw key display) | step-04 §6d |
154
+
138
155
  ---
139
156
 
140
157
  ## Summary
@@ -343,10 +343,10 @@ public class {Name}Controller : ControllerBase
343
343
  | Module | `/{app-kebab}/{module-kebab}` | `/human-resources/employees` |
344
344
  | Section | `/{app-kebab}/{module-kebab}/{section-kebab}` | `/human-resources/employees/departments` |
345
345
 
346
- **Route special cases:** `list` and `detail` sections are view modes, NOT sub-areas:
347
- - `list` route = module route (e.g., `/human-resources/employees`)
348
- - `detail` route = module route + `/:id`
349
- - Do NOT use: `/employees/list`, `/employees/detail/:id`
346
+ **Route convention:** ALL sections use uniform route = module route + `/{section-code}`:
347
+ - `list` route = module route + `/list` (e.g., `/human-resources/employees/list`)
348
+ - `detail` is an implicit route (not a section): module route + `/:id`
349
+ - Do NOT use: `/employees/detail/:id` (detail is implicit)
350
350
 
351
351
  **Rules:**
352
352
  - Routes ALWAYS start with `/`, include full hierarchy, use kebab-case
@@ -256,7 +256,7 @@ const applicationRoutes: ApplicationRouteExtensions = {
256
256
  };
257
257
  ```
258
258
 
259
- **Section-level routes:** `list` and `detail` do NOT add path segments (handled by `index: true` and `:id`). Only other sections (dashboard, approve, import) add `{section-kebab}` child routes.
259
+ **Section-level routes:** ALL sections add their code as path segment (`list` `/list`, `dashboard` → `/dashboard`, `approve` `/approve`). `detail`/`create`/`edit` are implicit routes (`/:id`, `/create`, `/:id/edit`), NOT sections.
260
260
 
261
261
  **PermissionGuard for sections:**
262
262
  ```tsx
@@ -175,12 +175,12 @@ var sectionRoute = $"{moduleRoute}/{ToKebabCase(sectionCode)}";
175
175
  // → "/human-resources/employees/departments"
176
176
  ```
177
177
 
178
- **Route special cases (list and detail sections):**
179
- > `list` and `detail` are view modes of the module, NOT functional sub-areas.
180
- > - `list` section route = module route (e.g., `/human-resources/employees`) — NO `/list` suffix
181
- > - `detail` section route = module route + `/:id` (e.g., `/human-resources/employees/:id`) — NOT `/detail/:id`
182
- > - Do not use: `/employees/list`, `/employees/detail/:id`
183
- > - Other sections (dashboard, approve, import) = module route + `/{section-kebab}` (normal)
178
+ **Route convention:**
179
+ > ALL sections use a UNIFORM route pattern: module route + `/{section-code}`.
180
+ > - `list` section route = module route + `/list` (e.g., `/human-resources/employees/list`)
181
+ > - `detail` is an implicit route (not a section): module route + `/:id` (e.g., `/human-resources/employees/:id`)
182
+ > - Other sections (dashboard, approve, import) = module route + `/{section-kebab}` (same rule)
183
+ > - Do NOT use: `/employees/detail/:id` (detail is implicit, not a section code)
184
184
 
185
185
  **Do not:**
186
186
  - Use deterministic/sequential/fixed GUIDs — always use `Guid.NewGuid()`
@@ -164,7 +164,7 @@ IF precision_score < 2:
164
164
  ║ Your request looks more like a discovery/design task. ║
165
165
  ║ ║
166
166
  ║ Suggestions: ║
167
- ║ 1. Use /sketch to quickly design your module (~2 min)
167
+ ║ 1. Use /business-analyse-quick to design your module
168
168
  ║ 2. Use /business-analyse for full analysis (40+ questions) ║
169
169
  ║ 3. Rewrite your prompt with entities and fields, e.g.: ║
170
170
  ║ /apex add employees section with Employee entity ║
@@ -100,6 +100,32 @@ IF existing_seed_files is NOT EMPTY:
100
100
 
101
101
  ---
102
102
 
103
+ ### Reserved Section Code Guard (MANDATORY — runs before ANY section seeding)
104
+
105
+ > **CRITICAL RULE:** Reserved section codes must NOT be seeded as menu-visible sections.
106
+
107
+ ```
108
+ RESERVED_SECTION_CODES = ["detail", "create", "edit"]
109
+
110
+ For each section in {sections} (from PRD or entity_section_map):
111
+ IF section.code IN RESERVED_SECTION_CODES OR section.code contains "-detail":
112
+ → Do NOT seed this section in NavigationSeedData at all
113
+ → "detail" is the /:id route of its parent list section (DynamicRouter convention)
114
+ → "create" is the /create route (DynamicRouter convention)
115
+ → "edit" is the /:id/edit route (DynamicRouter convention)
116
+ → "*-detail" (e.g., "department-detail") is the /:id route of its parent section
117
+ → Set permissionMode = "inherit" (no dedicated permissions)
118
+ → LOG: "Skipped reserved section '{section.code}' — resolved by DynamicRouter convention"
119
+
120
+ ELSE:
121
+ → Seed normally with IsActive = true
122
+
123
+ In delegate mode (-d): PRD data may contain reserved codes that were not challenged.
124
+ This guard catches them regardless of mode.
125
+ ```
126
+
127
+ ---
128
+
103
129
  ### Per-Module Seed Data (CREATE or UPDATE)
104
130
 
105
131
  #### If SEED_MODE = "CREATE"
@@ -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
@@ -130,6 +232,15 @@ For each module:
130
232
  → Create{Section}Page.tsx (/create route)
131
233
  → Edit{Section}Page.tsx (/:id/edit route)
132
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.
133
244
  - Pages: **MANDATORY — INVOKE Skill("ui-components")** for ALL page generation.
134
245
  ⚠ BLOCKING REQUIREMENT: You MUST call Skill("ui-components") for EVERY page (.tsx).
135
246
  NEVER generate .tsx page code directly. NEVER write page content in Agent prompts.
@@ -245,13 +356,17 @@ IF NOT economy_mode AND entities.length > 1:
245
356
  # PHASE B — Sequential: pages via /ui-components (principal agent)
246
357
  # Snipper agents cannot call Skill() — only the principal agent can.
247
358
  For each entity (sequentially):
248
- **INVOKE Skill("ui-components")** — pass entity context:
359
+ **INVOKE Skill("ui-components")** — pass entity context + PRD spec:
249
360
  - Entity: {EntityName}, Module: {ModuleName}, App: {AppName}
250
361
  - Page types: List, Detail, Create, Edit (+ Dashboard if applicable)
251
362
  - "CSS: Use CSS variables ONLY — bg-[var(--bg-card)], text-[var(--text-primary)]"
252
363
  - "Forms: Create/Edit are FULL PAGES with own routes (/create, /:id/edit)"
253
364
  - "FK FIELDS: EntityLookup for ALL FK Guid fields"
254
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.)."
255
370
  Generate form tests: co-located .test.tsx for Create and Edit pages
256
371
 
257
372
  ELSE:
@@ -263,12 +378,16 @@ ELSE:
263
378
  2. MCP scaffold_routes (outputFormat: 'componentRegistry')
264
379
  3. Verify PageRegistry registration (componentRegistry.generated.ts)
265
380
  No manual App.tsx wiring needed — DynamicRouter resolves at runtime
266
- 4. **INVOKE Skill("ui-components")** — pass entity context:
381
+ 4. **INVOKE Skill("ui-components")** — pass entity context + PRD spec:
267
382
  - Entity: {EntityName}, Module: {ModuleName}, App: {AppName}
268
383
  - Page types: List, Detail, Create, Edit (+ Dashboard if applicable)
269
384
  - "CSS: Use CSS variables ONLY — bg-[var(--bg-card)], text-[var(--text-primary)]"
270
385
  - "Forms: Create/Edit are FULL PAGES with own routes (/create, /:id/edit)"
271
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."
272
391
  5. I18n: 4 JSON files (fr, en, it, de) + register namespace in i18n config
273
392
  6. Form tests: co-located .test.tsx for Create and Edit pages
274
393
  ```
@@ -300,6 +419,9 @@ When delegating to `/ui-components` skill, include explicit instructions:
300
419
  - "Forms: Create/Edit forms are FULL PAGES with own routes (e.g., `/create`, `/:id/edit`)."
301
420
  - "I18n: ALL text must use `t('namespace:key', 'Fallback')`. Generate JSON files in `src/i18n/locales/`."
302
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."
303
425
 
304
426
  ### ⛔ POST-GENERATION VERIFICATION GATES (MANDATORY — run after ALL pages are created)
305
427