@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
package/package.json
CHANGED
|
@@ -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 /
|
|
30
|
-
/
|
|
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 — /
|
|
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*.*"[^"]*/(
|
|
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 " - '
|
|
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
|
|
408
|
-
>
|
|
409
|
-
> - `list` section route = module route (e.g., `/human-resources/employees`)
|
|
410
|
-
> - `detail` section route = module route + `/:id` (e.g., `/human-resources/employees/:id`) — NOT `/detail/:id`
|
|
411
|
-
> -
|
|
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 →
|
|
439
|
-
// -
|
|
440
|
-
//
|
|
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
|
-
|
|
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
|
|
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/
|
|
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"
|
|
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` —
|
|
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}`.
|
|
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 — /
|
|
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
|
|
347
|
-
- `list` route = module route (e.g., `/human-resources/employees`)
|
|
348
|
-
- `detail` route
|
|
349
|
-
- Do NOT use: `/employees/
|
|
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:**
|
|
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
|
|
179
|
-
>
|
|
180
|
-
> - `list` section route = module route (e.g., `/human-resources/employees`)
|
|
181
|
-
> - `detail`
|
|
182
|
-
> -
|
|
183
|
-
> -
|
|
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 /
|
|
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
|
|