@atlashub/smartstack-cli 4.74.0 → 4.76.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +152 -31
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +14 -3
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/agents/ba-reader.md +17 -15
- package/templates/agents/ba-writer.md +49 -51
- package/templates/skills/apex/SKILL.md +2 -2
- package/templates/skills/apex/_shared.md +1 -1
- package/templates/skills/apex/references/checks/backend-checks.sh +21 -7
- package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
- package/templates/skills/apex/references/checks/infrastructure-checks.sh +47 -10
- package/templates/skills/apex/references/checks/seed-checks.sh +47 -7
- package/templates/skills/apex/references/core-seed-data.md +20 -18
- package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +3 -0
- package/templates/skills/apex/references/post-checks.md +23 -3
- package/templates/skills/apex/references/smartstack-api.md +4 -4
- package/templates/skills/apex/references/smartstack-frontend.md +54 -8
- package/templates/skills/apex/references/smartstack-layers.md +6 -6
- package/templates/skills/apex/steps/step-00-init.md +75 -1
- package/templates/skills/apex/steps/step-03-execute.md +16 -4
- package/templates/skills/apex/steps/step-03b-layer1-seed.md +65 -6
- package/templates/skills/apex/steps/step-03c-layer2-backend.md +50 -5
- package/templates/skills/apex/steps/step-03d-layer3-frontend.md +226 -4
- package/templates/skills/apex/steps/step-04-examine.md +163 -0
- package/templates/skills/apex-verify/SKILL.md +110 -0
- package/templates/skills/apex-verify/references/audit-rules.md +50 -0
- package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
- package/templates/skills/apex-verify/steps/step-01-nav-audit.md +92 -0
- package/templates/skills/apex-verify/steps/step-02-crud-audit.md +127 -0
- package/templates/skills/apex-verify/steps/step-03-perm-audit.md +119 -0
- package/templates/skills/apex-verify/steps/step-04-route-audit.md +98 -0
- package/templates/skills/apex-verify/steps/step-05-report.md +110 -0
- package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +3 -0
- package/templates/skills/application/templates-frontend.md +2 -2
- package/templates/skills/business-analyse/SKILL.md +17 -3
- package/templates/skills/business-analyse/_shared.md +64 -0
- package/templates/skills/business-analyse/patterns/suggestion-catalog.md +34 -26
- package/templates/skills/business-analyse/questionnaire/01-context.md +13 -9
- package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +20 -27
- package/templates/skills/business-analyse/questionnaire.md +86 -9
- package/templates/skills/business-analyse/references/03-json-schemas.md +221 -0
- package/templates/skills/business-analyse/references/03-post-check-validation.md +208 -0
- package/templates/skills/business-analyse/references/03-smartstack-entity-guards.md +32 -0
- package/templates/skills/business-analyse/references/04-cross-module-validation.md +95 -0
- package/templates/skills/business-analyse/references/04-file-allocation.md +162 -0
- package/templates/skills/business-analyse/references/04-naming-audit-checks.md +174 -0
- package/templates/skills/business-analyse/references/04-semantic-validation-matrix.md +118 -0
- package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
- package/templates/skills/business-analyse/references/domain-research-playbook.md +234 -0
- package/templates/skills/business-analyse/references/entity-sourcing-presentation.md +166 -0
- package/templates/skills/business-analyse/references/init-resume-logic.md +70 -0
- package/templates/skills/business-analyse/references/module-completeness-challenge.md +174 -0
- package/templates/skills/business-analyse/references/multi-app-detection.md +149 -0
- package/templates/skills/business-analyse/references/portal-classification.md +52 -0
- package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
- package/templates/skills/business-analyse/references/validation-checklist.md +35 -6
- package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +50 -6
- package/templates/skills/business-analyse/steps/step-00-init.md +22 -190
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +365 -269
- package/templates/skills/business-analyse/steps/step-02-structure.md +98 -20
- package/templates/skills/business-analyse/steps/step-03-specify.md +810 -229
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +509 -278
- package/templates/skills/business-analyse-design/SKILL.md +10 -0
- package/templates/skills/business-analyse-design/references/screens-post-check.md +221 -0
- package/templates/skills/business-analyse-design/references/screens-type-mapping.md +138 -0
- package/templates/skills/business-analyse-design/references/smartcomponents-templates.md +225 -0
- package/templates/skills/{business-analyse → business-analyse-design}/references/spec-auto-inference.md +117 -117
- package/templates/skills/business-analyse-design/steps/step-01-screens.md +36 -162
- package/templates/skills/business-analyse-design/steps/step-02-wireframes.md +8 -7
- package/templates/skills/business-analyse-design/steps/step-03-navigation.md +89 -42
- package/templates/skills/business-analyse-develop/references/compact-loop.md +9 -0
- package/templates/skills/business-analyse-develop/references/handoff-quality-gate.md +132 -0
- package/templates/skills/business-analyse-develop/references/prd-v3-transformation.md +326 -0
- package/templates/skills/business-analyse-develop/references/report-reconciliation.md +140 -0
- package/templates/skills/business-analyse-develop/references/report-template.md +142 -0
- package/templates/skills/business-analyse-develop/steps/step-01-task.md +5 -177
- package/templates/skills/business-analyse-develop/steps/step-02-execute.md +17 -4
- package/templates/skills/business-analyse-develop/steps/step-03-commit.md +6 -2
- package/templates/skills/business-analyse-develop/steps/step-04-check.md +6 -0
- package/templates/skills/business-analyse-develop/steps/step-05-report.md +3 -269
- package/templates/skills/business-analyse-handoff/SKILL.md +10 -0
- package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +211 -0
- package/templates/skills/business-analyse-handoff/references/context-isolation-pattern.md +47 -0
- package/templates/skills/business-analyse-handoff/references/handoff-file-inventory.md +49 -0
- package/templates/skills/business-analyse-handoff/references/handoff-global-validation.md +142 -0
- package/templates/skills/business-analyse-handoff/references/prd-validation-checks.md +125 -0
- package/templates/skills/business-analyse-handoff/references/project-index-update.md +98 -0
- package/templates/skills/business-analyse-handoff/steps/step-01-transform.md +9 -160
- package/templates/skills/business-analyse-handoff/steps/step-02-export.md +10 -99
- package/templates/skills/business-analyse-html/SKILL.md +10 -0
- package/templates/skills/business-analyse-html/html/ba-interactive.html +504 -97
- package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +79 -2
- package/templates/skills/business-analyse-html/html/src/scripts/02-navigation.js +6 -46
- package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +94 -36
- package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +162 -0
- package/templates/skills/business-analyse-html/html/src/styles/10-diagrams.css +73 -0
- package/templates/skills/business-analyse-html/html/src/template.html +2 -0
- package/templates/skills/business-analyse-html/references/02-embedded-artifacts-building.md +144 -0
- package/templates/skills/business-analyse-html/references/02-feature-data-building.md +143 -0
- package/templates/skills/business-analyse-html/references/02-mapping-tables.md +442 -0
- package/templates/skills/business-analyse-html/references/02-normalization-helpers.md +139 -0
- package/templates/skills/business-analyse-html/references/02-screen-format-detection.md +283 -0
- package/templates/skills/business-analyse-html/references/02-self-check-validation.md +199 -0
- package/templates/skills/business-analyse-html/references/data-build.md +24 -1
- package/templates/skills/business-analyse-html/references/data-mapping.md +119 -17
- package/templates/skills/business-analyse-html/steps/step-02-build-data.md +18 -555
- package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
- package/templates/skills/business-analyse-quick/SKILL.md +807 -0
- package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
- package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
- package/templates/skills/business-analyse-review/SKILL.md +10 -0
- package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
- package/templates/skills/business-analyse-status/SKILL.md +8 -0
- package/templates/skills/dev-start/SKILL.md +143 -307
- package/templates/skills/efcore/SKILL.md +13 -0
- package/templates/skills/sketch/SKILL.md +15 -153
- package/templates/skills/ui-components/SKILL.md +1 -1
- package/templates/skills/ui-components/patterns/data-table.md +1 -1
|
@@ -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
|
---
|
|
@@ -48,6 +48,9 @@ import './extensions/componentRegistry.generated';
|
|
|
48
48
|
|
|
49
49
|
## For Client SDK Pages
|
|
50
50
|
|
|
51
|
+
> **CRITICAL:** Keys use DOT notation (`rh.employes.list`), NEVER hyphens (`rh-employes-list`).
|
|
52
|
+
> Dots are hierarchy separators matching `NavigationService.ComponentKey` computation at runtime.
|
|
53
|
+
|
|
51
54
|
Clients register their own pages directly:
|
|
52
55
|
|
|
53
56
|
```tsx
|
|
@@ -39,7 +39,7 @@ bash references/checks/infrastructure-checks.sh
|
|
|
39
39
|
| S8 | BLOCKING | Write endpoints must NOT use Read permissions | security-checks.sh |
|
|
40
40
|
| S9 | BLOCKING | FK relationships must enforce tenant isolation | security-checks.sh |
|
|
41
41
|
|
|
42
|
-
### Backend — Entity, Service & Controller Checks (V1-V2, C8, C12, C14, C28-C31, C54)
|
|
42
|
+
### Backend — Entity, Service & Controller Checks (V1-V2, C8, C12, C14, C28-C31, C54, C62)
|
|
43
43
|
|
|
44
44
|
| ID | Severity | Description | Script |
|
|
45
45
|
|----|----------|-------------|--------|
|
|
@@ -53,6 +53,7 @@ bash references/checks/infrastructure-checks.sh
|
|
|
53
53
|
| C30 | BLOCKING | Code regex must support hyphens | backend-checks.sh |
|
|
54
54
|
| C31 | WARNING | CreateDto must NOT have Code field when service uses ICodeGenerator | backend-checks.sh |
|
|
55
55
|
| C54 | BLOCKING | No helper method calls inside .Select() on IQueryable | backend-checks.sh |
|
|
56
|
+
| C62 | WARNING | Services must use TenantContextRequiredException for tenant check, NOT InvalidOperationException | backend-checks.sh |
|
|
56
57
|
|
|
57
58
|
### Frontend — CSS, Forms, Components, I18n (C3a, C3-C7, C9, C11, C24-C27, C36-C37, C49, C52)
|
|
58
59
|
|
|
@@ -74,6 +75,7 @@ bash references/checks/infrastructure-checks.sh
|
|
|
74
75
|
| C37 | CRITICAL | Detail page tabs must NOT navigate() — content switches locally | frontend-checks.sh |
|
|
75
76
|
| C49 | BLOCKING | Route Ordering in App.tsx — static before dynamic | frontend-checks.sh |
|
|
76
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 |
|
|
77
79
|
|
|
78
80
|
### Seed Data — Navigation, Roles, Permissions (C1-C2, C10, C15-C23, C32-C35, C44-C48, C53)
|
|
79
81
|
|
|
@@ -88,7 +90,7 @@ bash references/checks/infrastructure-checks.sh
|
|
|
88
90
|
| C18 | BLOCKING | Permissions.cs static constants must exist | seed-checks.sh |
|
|
89
91
|
| C19 | BLOCKING | ApplicationRolesSeedData.cs must exist | seed-checks.sh |
|
|
90
92
|
| C20 | WARNING | Section route completeness (NavigationSection → frontend route + permissions) | seed-checks.sh |
|
|
91
|
-
| C21 | WARNING | FORBIDDEN route patterns — /
|
|
93
|
+
| C21 | WARNING | FORBIDDEN route patterns — /detail/:id | seed-checks.sh |
|
|
92
94
|
| C22 | WARNING | Permission path segment count (2-4 dots expected) | seed-checks.sh |
|
|
93
95
|
| C23 | BLOCKING | IClientSeedDataProvider must have 4 methods with real implementation (not stubs) + DI registration | seed-checks.sh |
|
|
94
96
|
| C32 | CRITICAL | Translation seed data must have idempotency guard | seed-checks.sh |
|
|
@@ -103,6 +105,7 @@ bash references/checks/infrastructure-checks.sh
|
|
|
103
105
|
| C53 | BLOCKING | Enum serialization — JsonStringEnumConverter required | seed-checks.sh |
|
|
104
106
|
| C57 | BLOCKING | SeedDataProvider Seed*Async methods must NOT be `return Task.CompletedTask` or empty body — stubs cause empty menu | seed-checks.sh |
|
|
105
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 |
|
|
106
109
|
|
|
107
110
|
### Architecture — Clean Architecture Layer Isolation (A1-A8)
|
|
108
111
|
|
|
@@ -117,7 +120,7 @@ bash references/checks/infrastructure-checks.sh
|
|
|
117
120
|
| A7 | WARNING | No direct repository usage in controllers | architecture-checks.sh |
|
|
118
121
|
| A8 | BLOCKING | API endpoints must match handoff apiEndpointSummary | architecture-checks.sh |
|
|
119
122
|
|
|
120
|
-
### Infrastructure — Migration & Build (C13, C38-C43, C50-C51, C56)
|
|
123
|
+
### Infrastructure — Migration & Build (C13, C38-C43, C50-C51, C56, C60-C61)
|
|
121
124
|
|
|
122
125
|
| ID | Severity | Description | Script |
|
|
123
126
|
|----|----------|-------------|--------|
|
|
@@ -131,6 +134,23 @@ bash references/checks/infrastructure-checks.sh
|
|
|
131
134
|
| C50 | BLOCKING | NavRoute Uniqueness — no duplicate NavRoute values | infrastructure-checks.sh |
|
|
132
135
|
| C51 | WARNING | NavRoute Segments vs Controller Hierarchy (depth matching) | infrastructure-checks.sh |
|
|
133
136
|
| C56 | BLOCKING | Hierarchy Artifact Completeness — current run only (entities + sections) | infrastructure-checks.sh |
|
|
137
|
+
| C60 | BLOCKING | Controllers must inject ISender _mediator, NOT direct business services | infrastructure-checks.sh |
|
|
138
|
+
| C61 | BLOCKING | Controllers with [NavRoute] must have [Authorize] attribute | infrastructure-checks.sh |
|
|
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 |
|
|
134
154
|
|
|
135
155
|
---
|
|
136
156
|
|
|
@@ -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
|
|
@@ -121,24 +121,67 @@ t('employees:messages.created', '{{entity}} created', { entity: 'Employee' }) //
|
|
|
121
121
|
t('common:actions.save', 'Save') // Cross-namespace
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
-
### Namespace Registration (CRITICAL — POST-CHECK C39)
|
|
124
|
+
### Namespace Registration (CRITICAL — POST-CHECK C39 + GATE PG-4)
|
|
125
125
|
|
|
126
|
-
After creating i18n JSON files, register each namespace in `src/i18n/config.ts`:
|
|
126
|
+
After creating i18n JSON files, register each namespace in `src/i18n/config.ts` or `index.ts`:
|
|
127
127
|
|
|
128
128
|
```typescript
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
// CORRECT — Full i18n config with module namespaces registered
|
|
130
|
+
import i18n from 'i18next';
|
|
131
|
+
import { initReactI18next } from 'react-i18next';
|
|
132
|
+
import LanguageDetector from 'i18next-browser-languagedetector';
|
|
133
|
+
|
|
134
|
+
// Import ALL module translation files for each language
|
|
135
|
+
import frEmployes from './locales/fr/employes.json';
|
|
136
|
+
import enEmployes from './locales/en/employes.json';
|
|
137
|
+
import itEmployes from './locales/it/employes.json';
|
|
138
|
+
import deEmployes from './locales/de/employes.json';
|
|
139
|
+
import frAbsences from './locales/fr/absences.json';
|
|
140
|
+
import enAbsences from './locales/en/absences.json';
|
|
141
|
+
import itAbsences from './locales/it/absences.json';
|
|
142
|
+
import deAbsences from './locales/de/absences.json';
|
|
143
|
+
// ... repeat for each module
|
|
144
|
+
|
|
145
|
+
i18n
|
|
146
|
+
.use(LanguageDetector)
|
|
147
|
+
.use(initReactI18next)
|
|
148
|
+
.init({
|
|
149
|
+
fallbackLng: 'fr',
|
|
150
|
+
ns: ['employes', 'absences'], // List ALL namespaces
|
|
151
|
+
defaultNS: 'employes',
|
|
152
|
+
interpolation: { escapeValue: false },
|
|
153
|
+
resources: {
|
|
154
|
+
fr: { employes: frEmployes, absences: frAbsences },
|
|
155
|
+
en: { employes: enEmployes, absences: enAbsences },
|
|
156
|
+
it: { employes: itEmployes, absences: itAbsences },
|
|
157
|
+
de: { employes: deEmployes, absences: deAbsences },
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
export default i18n;
|
|
132
162
|
```
|
|
133
163
|
|
|
134
|
-
|
|
164
|
+
```typescript
|
|
165
|
+
// WRONG — Missing namespace imports (keys show as raw strings like "list.title")
|
|
166
|
+
i18n.init({
|
|
167
|
+
resources: {
|
|
168
|
+
en: { translation: { welcome: 'Welcome' } },
|
|
169
|
+
fr: { translation: { welcome: 'Bienvenue' } },
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
// ❌ Module JSON files exist on disk but are NEVER loaded → useTranslation('employes') returns empty
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Root cause (test-apex-007, test-ba-010): JSON files existed but namespaces were not registered → `useTranslation(['module'])` returned empty strings. All t() calls displayed raw keys like `list.title`, `form.department` instead of translated text.
|
|
135
176
|
|
|
136
177
|
**Rules:**
|
|
137
|
-
- Provide fallback value as 2nd argument to `t()`
|
|
178
|
+
- Provide fallback value as 2nd argument to `t()`: `t('employes:list.title', 'Liste des employés')`
|
|
138
179
|
- Use namespace prefix: `t('namespace:key')` — never `t('key')` without namespace
|
|
139
180
|
- Generate 4 language files (fr, en, it, de) with identical key structures
|
|
140
181
|
- Register new namespaces in i18n config file after creating JSON files
|
|
141
182
|
- Do not hardcode user-facing strings in JSX
|
|
183
|
+
- **After writing i18n JSON files:** IMMEDIATELY update i18n config with imports + resources registration
|
|
184
|
+
- **Verify:** `grep -q "{namespace}" src/**/i18n/index.ts` MUST match for EVERY module namespace
|
|
142
185
|
|
|
143
186
|
---
|
|
144
187
|
|
|
@@ -213,7 +256,7 @@ const applicationRoutes: ApplicationRouteExtensions = {
|
|
|
213
256
|
};
|
|
214
257
|
```
|
|
215
258
|
|
|
216
|
-
**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.
|
|
217
260
|
|
|
218
261
|
**PermissionGuard for sections:**
|
|
219
262
|
```tsx
|
|
@@ -285,9 +328,12 @@ Do NOT use: `bg-white`, `border-gray-200`, `text-gray-900`, hardcoded hex/rgb.
|
|
|
285
328
|
- Do not use raw `<table>` — use `DataTable`
|
|
286
329
|
- Do not create custom spinners — use `Loader2` from lucide-react
|
|
287
330
|
- Do not import axios directly — use `@/services/api/apiClient`
|
|
331
|
+
- **Do not use raw `fetch()` — use `apiClient` from `@/services/api`** (handles auth, token refresh, tenant isolation)
|
|
288
332
|
- Use `PageLoader` as Suspense fallback
|
|
289
333
|
- Use `EntityLookup` for FK Guid fields (not `<select>` or `<input>` for GUIDs)
|
|
290
334
|
|
|
335
|
+
> **API access rule:** ALL API calls in hooks and pages MUST use `apiClient` (or the typed API service generated by MCP `scaffold_api_client`). Using raw `fetch()` bypasses authentication, token refresh, and tenant isolation — the page will fail silently when the user is logged in. This applies to custom hooks in `src/hooks/` as well as inline API calls in pages.
|
|
336
|
+
|
|
291
337
|
---
|
|
292
338
|
|
|
293
339
|
## 6. Foreign Key Fields & Entity Lookup
|
|
@@ -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 ║
|
|
@@ -224,6 +224,79 @@ All naming variants are derived from {app_name}, {module_code}, {sections}. Do N
|
|
|
224
224
|
|
|
225
225
|
Call `mcp__smartstack__validate_conventions` after computing derivations. Fix any errors BEFORE step-01.
|
|
226
226
|
|
|
227
|
+
### 4g. Entity-Section Mapping (DETERMINISTIC)
|
|
228
|
+
|
|
229
|
+
> Each entity MUST be mapped to a hierarchy level. This determines the entity's NavRoute segment count
|
|
230
|
+
> (2 vs 3 segments) and whether section-level permissions are needed.
|
|
231
|
+
> **This mapping is consumed by:** step-03b (permissions), step-03c (controllers), step-04 (validation).
|
|
232
|
+
|
|
233
|
+
**Rules:**
|
|
234
|
+
|
|
235
|
+
1. The entity whose name derives from `{module_code}` is the **module-level entity**.
|
|
236
|
+
It gets `NavRoute = {app}.{module}` (2 segments). Its permissions are module-level.
|
|
237
|
+
|
|
238
|
+
2. Every other entity that matches a section from `{sections}` is a **section-level entity**.
|
|
239
|
+
It gets `NavRoute = {app}.{module}.{section_code}` (3 segments). Its permissions are section-level.
|
|
240
|
+
|
|
241
|
+
3. Entities that match NO section stay **module-level** (share the module's NavRoute).
|
|
242
|
+
This is the case for true sub-resources without their own navigation entry (e.g., InvoiceLine).
|
|
243
|
+
|
|
244
|
+
**Matching algorithm:**
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
FOR EACH entity in {entities}:
|
|
248
|
+
entity_kebab = kebab(pluralize(entity.name)) // "Contact" → "contacts", "Department" → "departments"
|
|
249
|
+
|
|
250
|
+
IF entity_kebab == {module_code} OR singularize(entity_kebab) == singularize({module_code}):
|
|
251
|
+
entity.level = "module"
|
|
252
|
+
entity.navRoute = "{app_name_kebab}.{module_code}"
|
|
253
|
+
entity.section_code = null
|
|
254
|
+
|
|
255
|
+
ELSE:
|
|
256
|
+
matched = {sections}.find(s =>
|
|
257
|
+
s.code == entity_kebab // exact: "contacts" == "contacts"
|
|
258
|
+
OR s.code == singularize(entity_kebab) // singular: "types" for "AbsenceType"
|
|
259
|
+
OR s.code.endsWith(entity_kebab) // suffix: "absence-types" ends with "types"
|
|
260
|
+
OR entity_kebab.endsWith(s.code) // reverse: "project-members" ends with "members"
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
IF matched:
|
|
264
|
+
entity.level = "section"
|
|
265
|
+
entity.navRoute = "{app_name_kebab}.{module_code}.{matched.code}"
|
|
266
|
+
entity.section_code = matched.code
|
|
267
|
+
ELSE:
|
|
268
|
+
entity.level = "module"
|
|
269
|
+
entity.navRoute = "{app_name_kebab}.{module_code}"
|
|
270
|
+
entity.section_code = null
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Delegate mode override:** When `permissionPaths` companion includes section-level paths
|
|
274
|
+
(3+ dot segments like `app.module.section.action`), use those paths as authoritative source
|
|
275
|
+
to derive entity-section mapping instead of name heuristics.
|
|
276
|
+
|
|
277
|
+
**Storage:**
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
{entity_section_map}: [
|
|
281
|
+
{ entity: "Employee", level: "module", navRoute: "rh.employes", section_code: null },
|
|
282
|
+
{ entity: "Department", level: "section", navRoute: "rh.employes.departments", section_code: "departments" }
|
|
283
|
+
]
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Propagated to: step-03b (permissions), step-03c (controllers), step-03-execute (state), step-04 (validation).
|
|
287
|
+
|
|
288
|
+
**Post-mapping validation:**
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
# No duplicate NavRoutes among section-level entities
|
|
292
|
+
section_navroutes = {entity_section_map}.filter(e => e.level == "section").map(e => e.navRoute)
|
|
293
|
+
IF duplicates(section_navroutes) → BLOCKING: two entities mapped to same section
|
|
294
|
+
|
|
295
|
+
# All section_codes exist in {sections}
|
|
296
|
+
FOR EACH entry in {entity_section_map} WHERE section_code != null:
|
|
297
|
+
IF {sections}.find(s => s.code == entry.section_code) == null → BLOCKING: orphan section_code
|
|
298
|
+
```
|
|
299
|
+
|
|
227
300
|
---
|
|
228
301
|
|
|
229
302
|
## 5. Collect Technical Parameters
|
|
@@ -323,6 +396,7 @@ APEX INIT COMPLETE — v{{SMARTSTACK_VERSION}}
|
|
|
323
396
|
Task: {task_description}
|
|
324
397
|
Hierarchy: {app_name} → {module_code} → {sections[].code}
|
|
325
398
|
Entities: {entities} | Complexity: {module_complexity} | Deps: {has_dependencies}
|
|
399
|
+
Entity Map: {entity_section_map.map(e => `${e.entity}→${e.navRoute}`).join(', ')}
|
|
326
400
|
Code: {code_patterns summary} | PRD: {prd_path||none} | Feature: {feature_path||none}
|
|
327
401
|
Flags: {active_flags} | MCP: {available|degraded} | Needs: {migration/seed/workflow/notification}
|
|
328
402
|
Specs: {specification_loading_plan ? 'companion files available (layer-specific loading)' : 'none'}
|
|
@@ -45,7 +45,7 @@ Execute all layers (Layer 0 → Layer 1 → Layer 2 → Layer 3 → Layer 4).
|
|
|
45
45
|
```
|
|
46
46
|
BEFORE starting Layer N:
|
|
47
47
|
Verify these variables are still accessible:
|
|
48
|
-
{app_name}, {module_code}, {sections}, {entities}, {code_patterns}
|
|
48
|
+
{app_name}, {module_code}, {sections}, {entities}, {code_patterns}, {entity_section_map}
|
|
49
49
|
IF delegate_mode: also verify {delegate_prd_path}, {specification_loading_plan}
|
|
50
50
|
|
|
51
51
|
IF any variable is missing or empty:
|
|
@@ -71,7 +71,12 @@ BEFORE starting Layer N:
|
|
|
71
71
|
- {entities}: Glob("src/**/Domain/Entities/{module_code}/*.cs") → entity names
|
|
72
72
|
- {sections}: Glob("src/**/Seeding/Data/{module_code}/*NavigationSeedData.cs") → parse
|
|
73
73
|
- {code_patterns}: Read state.json or re-derive from DependencyInjection.cs
|
|
74
|
-
|
|
74
|
+
- {entity_section_map}: IF state.json has it → use directly. ELSE re-derive:
|
|
75
|
+
For each entity, check controllers for [NavRoute] value:
|
|
76
|
+
Grep("\[NavRoute\(\"", "src/**/Controllers/**/{Entity}*Controller.cs")
|
|
77
|
+
→ extract navRoute segments → determine level (2 seg = module, 3 seg = section)
|
|
78
|
+
Fallback: re-apply step-00 §4g matching algorithm with {entities} and {sections}
|
|
79
|
+
4. IF recovered: verify consistency with naming derivation rules (step-00 §4f, §4g)
|
|
75
80
|
5. IF Layer N-1 already completed: skip to Layer N directly
|
|
76
81
|
|
|
77
82
|
Cost: ~5-8 tool calls. Only triggered if context was compressed.
|
|
@@ -160,14 +165,21 @@ After each layer's build gate passes, write state to `.claude/output/apex/{task_
|
|
|
160
165
|
"app_name": "HumanResources",
|
|
161
166
|
"module_code": "employee-management",
|
|
162
167
|
"sections": ["employees", "departments"],
|
|
163
|
-
"entities": ["Employee", "Department"]
|
|
168
|
+
"entities": ["Employee", "Department"],
|
|
169
|
+
"entity_section_map": [
|
|
170
|
+
{ "entity": "Employee", "level": "module", "navRoute": "human-resources.employee-management", "section_code": null },
|
|
171
|
+
{ "entity": "Department", "level": "section", "navRoute": "human-resources.employee-management.departments", "section_code": "departments" }
|
|
172
|
+
]
|
|
164
173
|
}
|
|
165
174
|
```
|
|
166
175
|
|
|
167
|
-
> **Fields `delegate_mode`, `delegate_prd_path`, `app_name`, `module_code`, `sections`, `entities`**
|
|
176
|
+
> **Fields `delegate_mode`, `delegate_prd_path`, `app_name`, `module_code`, `sections`, `entities`, `entity_section_map`**
|
|
168
177
|
> are persisted so that Context Recovery Protocol (above) can restore the full execution context
|
|
169
178
|
> after context compression or resume (`-r`). The `specification_loading_plan` is rebuilt from the
|
|
170
179
|
> PRD file at `delegate_prd_path` — no need to serialize it directly.
|
|
180
|
+
> The `entity_section_map` is critical for Layers 2-3: it determines per-entity NavRoute and
|
|
181
|
+
> section-level permission generation. If lost, it can be re-derived from controller [NavRoute] attributes
|
|
182
|
+
> or by re-applying step-00 §4g matching algorithm.
|
|
171
183
|
|
|
172
184
|
---
|
|
173
185
|
|
|
@@ -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"
|
|
@@ -113,19 +139,45 @@ Generate all files from scratch using `references/core-seed-data.md` templates:
|
|
|
113
139
|
→ If resources defined: GetResourceEntries() + resource translations
|
|
114
140
|
→ Query actual parent from DB for FK (NOT deterministic GUID)
|
|
115
141
|
|
|
116
|
-
4. Permissions.cs
|
|
142
|
+
4. Permissions.cs — MODULE-LEVEL
|
|
117
143
|
→ MCP generate_permissions (PRIMARY tool)
|
|
118
144
|
→ Static constants: public static class {Module} { public const string Read = "..."; }
|
|
119
145
|
→ Permission paths MUST use kebab-case matching NavRoute codes
|
|
120
146
|
|
|
121
|
-
|
|
147
|
+
4b. Permissions.cs — SECTION-LEVEL (for each entity in {entity_section_map} where level == "section")
|
|
148
|
+
→ Add nested class per section inside the module class:
|
|
149
|
+
public static class {SectionPascal} {
|
|
150
|
+
public const string View = "{navRoute}.{section_code}.read";
|
|
151
|
+
public const string Create = "{navRoute}.{section_code}.create";
|
|
152
|
+
...
|
|
153
|
+
}
|
|
154
|
+
→ Apply permissionMode inference (core-seed-data.md Step C2):
|
|
155
|
+
- section code == "detail" → inherit (SKIP — no section permissions)
|
|
156
|
+
- section code == "dashboard" → read-only (read permission only)
|
|
157
|
+
- section code == "list" or "kanban" or "calendar" or "weekly" → inherit (SKIP — view of module entity)
|
|
158
|
+
- otherwise → crud (wildcard + read + create + update + delete)
|
|
159
|
+
→ Reference: core-seed-data.md lines 736-799 for full template
|
|
160
|
+
|
|
161
|
+
5. PermissionsSeedData.cs — MODULE-LEVEL
|
|
122
162
|
→ MCP generate_permissions first, fallback template
|
|
123
163
|
→ Paths match Permissions.cs, wildcard + CRUD
|
|
124
164
|
|
|
125
|
-
|
|
165
|
+
5b. PermissionsSeedData.cs — SECTION-LEVEL (for each entity where level == "section" AND permissionMode != "inherit")
|
|
166
|
+
→ Add CreateSectionPermissions(Guid {sectionCode}SectionId) method
|
|
167
|
+
→ Section-level entries: wildcard + CRUD matching Permissions.cs nested class
|
|
168
|
+
→ Reference: core-seed-data.md Step C2 lines 758-779 for template
|
|
169
|
+
|
|
170
|
+
6. RolesSeedData.cs — MODULE-LEVEL
|
|
126
171
|
→ Admin=wildcard(*), Manager=CRU, Contributor=CR, Viewer=R
|
|
127
172
|
→ Code-based role mapping (not deterministic GUIDs for roles)
|
|
128
173
|
→ Look up roles by Code at runtime
|
|
174
|
+
|
|
175
|
+
6b. RolesSeedData.cs — SECTION-LEVEL (for each entity where level == "section" AND permissionMode != "inherit")
|
|
176
|
+
→ Add section-level mappings in GetRolePermissionMappings():
|
|
177
|
+
Admin: section wildcard ({navRoute}.{section_code}.*)
|
|
178
|
+
Manager: read + create + update for section
|
|
179
|
+
Contributor: read + create for section
|
|
180
|
+
Viewer: read for section
|
|
129
181
|
```
|
|
130
182
|
|
|
131
183
|
#### If SEED_MODE = "UPDATE"
|
|
@@ -241,9 +293,16 @@ ELSE IF Program.cs ONLY calls `MigrateAsync()` without seed data execution:
|
|
|
241
293
|
For each section in {sections} from step-00:
|
|
242
294
|
✓ NavigationModuleSeedData has section entry + translations (4 langs)
|
|
243
295
|
✓ NavigationModuleSeedData has route: /{app}/{module}/{section}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
✓
|
|
296
|
+
|
|
297
|
+
For each entity in {entity_section_map} where level == "section" AND permissionMode != "inherit":
|
|
298
|
+
✓ Permissions.cs has SECTION-LEVEL nested class {SectionPascal} with constants
|
|
299
|
+
✓ PermissionsSeedData has CreateSectionPermissions(Guid {section}SectionId) method
|
|
300
|
+
✓ RolesSeedData.GetRolePermissionMappings() includes section-level role mappings
|
|
301
|
+
|
|
302
|
+
Module-level (always):
|
|
303
|
+
✓ Permissions.cs has MODULE-LEVEL constants (Read/Create/Update/Delete/Wildcard)
|
|
304
|
+
✓ PermissionsSeedData has module-level HasData() for each permission
|
|
305
|
+
✓ RolesSeedData maps all 4 roles to module permissions
|
|
247
306
|
✓ {App}SeedDataProvider references all seed data classes
|
|
248
307
|
✓ Program.cs executes seed data providers after MigrateAsync()
|
|
249
308
|
✓ DI registers: services.AddScoped<IClientSeedDataProvider, {App}SeedDataProvider>()
|