@atlashub/smartstack-cli 4.51.0 → 4.53.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 (28) hide show
  1. package/dist/index.js +53 -1
  2. package/dist/index.js.map +1 -1
  3. package/package.json +1 -1
  4. package/templates/skills/apex/references/core-seed-data.md +15 -0
  5. package/templates/skills/apex/references/error-classification.md +27 -3
  6. package/templates/skills/apex/references/post-checks.md +3 -1
  7. package/templates/skills/apex/steps/step-00-init.md +57 -0
  8. package/templates/skills/apex/steps/step-03-execute.md +33 -5
  9. package/templates/skills/apex/steps/step-03b-layer1-seed.md +18 -0
  10. package/templates/skills/apex/steps/step-03d-layer3-frontend.md +3 -0
  11. package/templates/skills/apex/steps/step-04-examine.md +35 -0
  12. package/templates/skills/business-analyse/references/canonical-json-formats.md +200 -0
  13. package/templates/skills/business-analyse/steps/step-03-specify.md +94 -20
  14. package/templates/skills/business-analyse-design/steps/step-01-screens.md +41 -4
  15. package/templates/skills/business-analyse-develop/references/init-resume-recovery.md +54 -0
  16. package/templates/skills/business-analyse-develop/steps/step-00-init.md +10 -3
  17. package/templates/skills/business-analyse-develop/steps/step-01-task.md +14 -2
  18. package/templates/skills/business-analyse-develop/steps/step-04-check.md +12 -2
  19. package/templates/skills/business-analyse-handoff/references/entity-canonicalization.md +158 -0
  20. package/templates/skills/business-analyse-handoff/steps/step-01-transform.md +26 -3
  21. package/templates/skills/business-analyse-handoff/steps/step-02-export.md +14 -0
  22. package/templates/skills/business-analyse-html/SKILL.md +4 -0
  23. package/templates/skills/business-analyse-html/references/data-build.md +24 -17
  24. package/templates/skills/business-analyse-html/references/data-mapping.md +79 -35
  25. package/templates/skills/business-analyse-html/references/output-modes.md +2 -1
  26. package/templates/skills/business-analyse-html/steps/step-01-collect.md +7 -2
  27. package/templates/skills/business-analyse-html/steps/step-02-build-data.md +155 -40
  28. package/templates/skills/business-analyse-html/steps/step-04-verify.md +22 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlashub/smartstack-cli",
3
- "version": "4.51.0",
3
+ "version": "4.53.0",
4
4
  "description": "SmartStack Claude Code automation toolkit - GitFlow, EF Core migrations, prompts and more",
5
5
  "author": {
6
6
  "name": "SmartStack",
@@ -1003,6 +1003,21 @@ public class RolePermissionSeedEntry
1003
1003
 
1004
1004
  ## 6. IClientSeedDataProvider Implementation
1005
1005
 
1006
+ > **IMPORTANT — HasData vs Seed*Async: TWO DIFFERENT MECHANISMS**
1007
+ >
1008
+ > | Mechanism | Where | When | Purpose |
1009
+ > |-----------|-------|------|---------|
1010
+ > | **EF Core `HasData()`** | `*Configuration.cs` (Infrastructure) | At migration time | Seeds static reference data into tables via `INSERT` in migration SQL. Data is part of the migration — no runtime code needed. |
1011
+ > | **`IClientSeedDataProvider.Seed*Async()`** | `*SeedDataProvider.cs` (Infrastructure) | At runtime startup | Seeds navigation, roles, permissions, role-permissions into the **Core** schema. Runs AFTER `MigrateAsync()` in `Program.cs`. |
1012
+ >
1013
+ > **Common mistake:** Generating `HasData()` calls for navigation/permissions instead of `Seed*Async()` methods.
1014
+ > Navigation and permission data goes into the **Core** schema (`core.nav_*`, `core.auth_*`) which is managed
1015
+ > by SmartStack, not by the client's migration. Only `IClientSeedDataProvider` can write to Core tables at runtime.
1016
+ >
1017
+ > **ANTI-STUB WARNING:** Each of the 4 Seed methods MUST contain real `context.{DbSet}.Add()` calls.
1018
+ > If ANY method is just `return Task.CompletedTask;`, the module will be **invisible** in the UI (empty menu).
1019
+ > This happens when this reference file is evicted from context by compression — see step-03b safeguard.
1020
+
1006
1021
  **File:** `Infrastructure/Persistence/Seeding/{AppPascalName}SeedDataProvider.cs`
1007
1022
 
1008
1023
  ### Critical Rules
@@ -100,6 +100,28 @@
100
100
 
101
101
  ---
102
102
 
103
+ ### Category G: PRD Quality Error (FIX = fix PRD or re-run handoff)
104
+
105
+ > **These errors are NOT caused by code generation — they originate from invalid PRD data.**
106
+ > The handoff generated file paths with illegal characters (spaces, apostrophes, accents in entity names).
107
+ > Code generation faithfully reproduced the invalid paths → build failure.
108
+
109
+ | Error Pattern | Example | Fix |
110
+ |--------------|---------|-----|
111
+ | `error CS1001: Identifier expected` with file name containing spaces | `Type d'absence.cs` → CS1001 | Re-run `/business-analyse-handoff` with entity canonicalization |
112
+ | `error CS1513/CS1514` with file name containing apostrophes | `Congé.cs` → accent in identifier | Fix entity name in BA or re-run handoff |
113
+ | Path contains non-ASCII characters in `.cs` file names | `Département.cs` | Canonicalize: strip diacritics, PascalCase |
114
+
115
+ **Detection heuristic:** If the error file path contains spaces, apostrophes, or accented characters, it is a PRD quality error, not a code error. Do NOT attempt to fix the generated code — fix the PRD source.
116
+
117
+ **Fix procedure:**
118
+ 1. Identify the invalid entity name from the error file path
119
+ 2. Report to user: "PRD quality error — entity name '{name}' is not a valid C# identifier"
120
+ 3. Either: manually canonicalize in the PRD file, or re-run `/business-analyse-handoff` (which now includes canonicalization)
121
+ 4. Re-run `/apex -d` with the corrected PRD
122
+
123
+ ---
124
+
103
125
  ## Decision Tree
104
126
 
105
127
  When a build or runtime error occurs, follow this tree:
@@ -123,9 +145,11 @@ BUILD/RUNTIME ERROR
123
145
  | YES -> Category E (edit config files)
124
146
  |
125
147
  +-- error CS#### (C# compiler error) ?
126
- | YES -> Is the missing type in ANY project file?
127
- | NO -> Category A (missing package)
128
- | YES -> Category F (fix source code)
148
+ | YES -> Does the error file path contain spaces, apostrophes, or accents?
149
+ | YES -> Category G (PRD quality error — fix handoff, not code)
150
+ | NO -> Is the missing type in ANY project file?
151
+ | NO -> Category A (missing package)
152
+ | YES -> Category F (fix source code)
129
153
  |
130
154
  +-- Test failure (Assert/Expected) ?
131
155
  YES -> Category F (fix source code, NOT tests)
@@ -90,7 +90,7 @@ bash references/checks/infrastructure-checks.sh
90
90
  | C20 | WARNING | Section route completeness (NavigationSection → frontend route + permissions) | seed-checks.sh |
91
91
  | C21 | WARNING | FORBIDDEN route patterns — /list and /detail/:id | seed-checks.sh |
92
92
  | C22 | WARNING | Permission path segment count (2-4 dots expected) | seed-checks.sh |
93
- | C23 | BLOCKING | IClientSeedDataProvider must have 4 methods + DI registration | seed-checks.sh |
93
+ | C23 | BLOCKING | IClientSeedDataProvider must have 4 methods with real implementation (not stubs) + DI registration | seed-checks.sh |
94
94
  | C32 | CRITICAL | Translation seed data must have idempotency guard | seed-checks.sh |
95
95
  | C33 | CRITICAL | Resource seed data must use actual section IDs from DB | seed-checks.sh |
96
96
  | C34 | BLOCKING | NavRoute segments must use kebab-case for multi-word codes — MCP overlap | seed-checks.sh |
@@ -101,6 +101,8 @@ bash references/checks/infrastructure-checks.sh
101
101
  | C47 | WARNING | Person Extension entities must not duplicate User fields | seed-checks.sh |
102
102
  | C48 | CRITICAL | Person Extension service must Include(User) | seed-checks.sh |
103
103
  | C53 | BLOCKING | Enum serialization — JsonStringEnumConverter required | seed-checks.sh |
104
+ | C57 | BLOCKING | SeedDataProvider Seed*Async methods must NOT be `return Task.CompletedTask` or empty body — stubs cause empty menu | seed-checks.sh |
105
+ | C58 | BLOCKING | File paths in filesToCreate must be valid C# identifiers — no spaces, apostrophes, or accents | seed-checks.sh |
104
106
 
105
107
  ### Architecture — Clean Architecture Layer Isolation (A1-A8)
106
108
 
@@ -94,6 +94,46 @@ if (prd.specificationFiles) {
94
94
 
95
95
  Each layer step (step-03a through step-03d) will load its companion files at the start. This provides full BA specifications (entity attributes, BR formulas, UC steps, screen columns) without loading the entire spec corpus.
96
96
 
97
+ **PRD Quality Gate (delegate mode — BLOCKING):**
98
+
99
+ > Validate that PRD file paths are valid C# identifiers BEFORE starting execution.
100
+ > Invalid entity names (French with spaces/apostrophes/accents) cause CS1001 build errors
101
+ > that waste 3+ retry iterations before being correctly classified.
102
+
103
+ ```javascript
104
+ // Extract all file paths from PRD
105
+ const filesToCreate = prd.$version === '4.0.0' ? prd.expectedFiles : prd.implementation?.filesToCreate;
106
+ if (filesToCreate) {
107
+ const invalidPaths = [];
108
+ for (const [category, files] of Object.entries(filesToCreate)) {
109
+ for (const file of (files || [])) {
110
+ const filePath = file.path || file;
111
+ // Extract filename (without extension) from path
112
+ const fileName = filePath.split('/').pop().replace(/\.(cs|tsx|ts|json)$/, '');
113
+ // Check for illegal characters in C# identifiers
114
+ if (/[\s'àâäéèêëïîôùûüçÀÂÄÉÈÊËÏÎÔÙÛÜÇ]/.test(fileName)) {
115
+ invalidPaths.push({ category, path: filePath, fileName });
116
+ }
117
+ }
118
+ }
119
+
120
+ if (invalidPaths.length > 0) {
121
+ console.error('BLOCKING — PRD QUALITY ERROR (not a code error):');
122
+ console.error('The following file paths contain illegal C# identifier characters:');
123
+ for (const p of invalidPaths) {
124
+ console.error(` ${p.category}: ${p.path} (invalid name: "${p.fileName}")`);
125
+ }
126
+ console.error('');
127
+ console.error('FIX: Re-run /business-analyse-handoff which now includes entity name canonicalization.');
128
+ console.error(' Or manually fix entity names in the PRD (strip accents, remove spaces/apostrophes, PascalCase).');
129
+ console.error('');
130
+ console.error('This is a PRD quality error (Category G), NOT a code generation error.');
131
+ console.error('Do NOT attempt to generate code from this PRD — it will fail with CS1001.');
132
+ STOP; // Do not proceed with invalid PRD
133
+ }
134
+ }
135
+ ```
136
+
97
137
  **Jump to:** section 3 (MCP verify) → section 6 (determine needs) → section 9 (summary)
98
138
 
99
139
  ---
@@ -248,6 +288,23 @@ needs_notification = {module_complexity} in ["crud-workflow","complex"] OR menti
248
288
 
249
289
  Read latest task directory in `.claude/output/apex/`:
250
290
  - **If state.json exists:** resume step-03 at next uncompleted layer (skip completed)
291
+ - **Delegate context recovery:** If `state.delegate_mode === true`:
292
+ 1. Restore `{delegate_prd_path}` from `state.delegate_prd_path`
293
+ 2. Restore `{app_name}`, `{module_code}`, `{sections}`, `{entities}` from state
294
+ 3. Re-read PRD at `{delegate_prd_path}` to rebuild `{specification_loading_plan}`:
295
+ ```javascript
296
+ const prd = readJSON(state.delegate_prd_path);
297
+ if (prd.specificationFiles) {
298
+ const sf = prd.specificationFiles;
299
+ specification_loading_plan = {
300
+ layer0_domain: [sf.entities],
301
+ layer1_seed: [sf.entities, sf.permissions],
302
+ layer2_backend: [sf.rules, sf.usecases],
303
+ layer3_frontend: [sf.screens, sf.usecases]
304
+ };
305
+ }
306
+ ```
307
+ 4. Set `{delegate_mode} = true` — companion spec loading will work in remaining layers
251
308
  - **Else if 00-context.md exists:** restore step-00 state, re-derive post-step-00 from git + files
252
309
  - **Else:** full re-derive from git history + files
253
310
 
@@ -46,19 +46,35 @@ Execute all layers (Layer 0 → Layer 1 → Layer 2 → Layer 3 → Layer 4).
46
46
  BEFORE starting Layer N:
47
47
  Verify these variables are still accessible:
48
48
  {app_name}, {module_code}, {sections}, {entities}, {code_patterns}
49
+ IF delegate_mode: also verify {delegate_prd_path}, {specification_loading_plan}
49
50
 
50
51
  IF any variable is missing or empty:
51
52
  1. Read .claude/output/apex/{task_id}/state.json (if exists)
52
- 2. IF state.json missing re-derive from filesystem:
53
+ 2. IF state.json has delegate context:
54
+ - {delegate_prd_path} = state.delegate_prd_path
55
+ - {delegate_mode} = state.delegate_mode
56
+ - Re-read PRD at {delegate_prd_path} to rebuild specification_loading_plan:
57
+ const prd = readJSON(delegate_prd_path);
58
+ if (prd.specificationFiles) {
59
+ specification_loading_plan = {
60
+ layer0_domain: [prd.specificationFiles.entities],
61
+ layer1_seed: [prd.specificationFiles.entities, prd.specificationFiles.permissions],
62
+ layer2_backend: [prd.specificationFiles.rules, prd.specificationFiles.usecases],
63
+ layer3_frontend: [prd.specificationFiles.screens, prd.specificationFiles.usecases]
64
+ };
65
+ }
66
+ - {app_name} = state.app_name || prd.project?.application || prd.metadata?.applicationCode
67
+ - {module_code} = state.module_code || prd.project?.module || prd.metadata?.moduleCode
68
+ 3. IF state.json missing OR no delegate context → re-derive from filesystem:
53
69
  - {app_name}: Glob("docs/business/*/") → first directory name
54
70
  - {module_code}: Glob("src/**/Domain/Entities/*/") → target module directory
55
71
  - {entities}: Glob("src/**/Domain/Entities/{module_code}/*.cs") → entity names
56
72
  - {sections}: Glob("src/**/Seeding/Data/{module_code}/*NavigationSeedData.cs") → parse
57
73
  - {code_patterns}: Read state.json or re-derive from DependencyInjection.cs
58
- 3. IF recovered: verify consistency with naming derivation rules (step-00 §4f)
59
- 4. IF Layer N-1 already completed: skip to Layer N directly
74
+ 4. IF recovered: verify consistency with naming derivation rules (step-00 §4f)
75
+ 5. IF Layer N-1 already completed: skip to Layer N directly
60
76
 
61
- Cost: ~5 tool calls. Only triggered if context was compressed.
77
+ Cost: ~5-8 tool calls. Only triggered if context was compressed.
62
78
  ```
63
79
 
64
80
  ---
@@ -126,6 +142,7 @@ Layer 4: feat({module}): [devdata] test data for development # if applicable
126
142
  ## State Auto-Save (after each layer)
127
143
 
128
144
  > **Automatic** — not dependent on `-s` flag. Enables reliable resume after context loss.
145
+ > **CRITICAL (audit ba-003):** Delegate context MUST be persisted for resume to work in delegate mode.
129
146
 
130
147
  After each layer's build gate passes, write state to `.claude/output/apex/{task_id}/state.json`:
131
148
 
@@ -137,10 +154,21 @@ After each layer's build gate passes, write state to `.claude/output/apex/{task_
137
154
  "files_created": ["Employee.cs", "EmployeeConfiguration.cs", "..."],
138
155
  "build_gates": { "layer0": "pass", "layer1": "pass", "layer2": "pass" },
139
156
  "commits": ["abc1234", "def5678", "ghi9012"],
140
- "timestamp": "2026-03-06T14:30:00Z"
157
+ "timestamp": "2026-03-06T14:30:00Z",
158
+ "delegate_mode": true,
159
+ "delegate_prd_path": ".ralph/prd-employees.json",
160
+ "app_name": "HumanResources",
161
+ "module_code": "employee-management",
162
+ "sections": ["employees", "departments"],
163
+ "entities": ["Employee", "Department"]
141
164
  }
142
165
  ```
143
166
 
167
+ > **Fields `delegate_mode`, `delegate_prd_path`, `app_name`, `module_code`, `sections`, `entities`**
168
+ > are persisted so that Context Recovery Protocol (above) can restore the full execution context
169
+ > after context compression or resume (`-r`). The `specification_loading_plan` is rebuilt from the
170
+ > PRD file at `delegate_prd_path` — no need to serialize it directly.
171
+
144
172
  ---
145
173
 
146
174
  ## Save Output (if save_mode)
@@ -42,6 +42,24 @@ TaskUpdate(taskId: progress_tracker_id,
42
42
 
43
43
  > This layer is required. Seed data makes modules visible in the UI. Without it, the module exists in code but is invisible to users. Reference: `references/core-seed-data.md` (loaded above) for complete C# templates.
44
44
 
45
+ > **CONTEXT COMPRESSION SAFEGUARD (delegate mode):**
46
+ > In delegate mode (`-d`), `references/core-seed-data.md` may have been evicted from context by compression
47
+ > before Layer 1 begins (it was loaded at step-03 entry but is ~1464 lines).
48
+ >
49
+ > **CHECK:** Can you see Section 6 "IClientSeedDataProvider Implementation" from `core-seed-data.md` in your context?
50
+ > - **YES** → Proceed normally using the templates from that section.
51
+ > - **NO** → **Re-read** `references/core-seed-data.md` lines 1004-1296 NOW (the IClientSeedDataProvider template).
52
+ > This section contains the complete C# template for the provider class with all 4 Seed methods.
53
+ >
54
+ > **WARNING — ANTI-STUB RULE:**
55
+ > The SeedDataProvider MUST contain real implementation code. Each of the 4 methods
56
+ > (`SeedNavigationAsync`, `SeedRolesAsync`, `SeedPermissionsAsync`, `SeedRolePermissionsAsync`)
57
+ > MUST have actual entity creation logic with `context.{DbSet}.Add()` calls.
58
+ >
59
+ > **BLOCKING:** If ANY Seed method body is just `return Task.CompletedTask;` or `{ }` (empty),
60
+ > the seed data layer is INVALID. The module will be invisible in the UI (menu vide / empty menu).
61
+ > Re-read the template from `references/core-seed-data.md` Section 6 and generate proper implementation.
62
+
45
63
  ---
46
64
 
47
65
  ### Mode Detection: CREATE vs UPDATE
@@ -169,6 +169,9 @@ For each module:
169
169
  3. Add the new namespace import + registration for all 4 languages
170
170
  4. If config uses dynamic imports: add namespace to the `ns` array
171
171
  5. Verify: `grep -q "{module_namespace}" src/**/i18n/config.ts` → must match
172
+ 6. **I18n File/Import Alignment Check:** Verify that every locale JSON file on disk for this module
173
+ has a corresponding import in the i18n config index.ts. Glob `src/**/i18n/locales/*/` for module files,
174
+ then verify each is imported. Missing imports cause silent translation failures (keys show as raw strings).
172
175
  - Permissions: Call MCP generate_permissions for the module permission root (2 segments: {app}.{module}),
173
176
  then also call MCP generate_permissions for each section (3 segments: {app}.{module}.{section}).
174
177
  - Section routes: Ensure navigation seed data has ComponentKey matching PageRegistry keys.
@@ -131,6 +131,41 @@ IF ANY cell = NO → BLOCKING — return to step-03b to fix
131
131
 
132
132
  Execute all checks from `references/post-checks.md`. If any fails, fix in step-03 and re-validate.
133
133
 
134
+ ### 6c. SeedDataProvider Content Verification (BLOCKING)
135
+
136
+ > **LESSON LEARNED (audit ba-003):** SeedDataProvider existed and had 4 methods (passed C23),
137
+ > but all methods were `return Task.CompletedTask` — stubs generated when `core-seed-data.md`
138
+ > was evicted from context by compression. Result: empty menu, 0 applications visible.
139
+
140
+ ```javascript
141
+ // Find the SeedDataProvider file
142
+ const providerFiles = Glob("**/Seeding/*SeedDataProvider.cs");
143
+ for (const providerFile of providerFiles) {
144
+ const content = readFile(providerFile);
145
+
146
+ // Check 1: No stub methods — each Seed*Async must have real implementation
147
+ const methods = ['SeedNavigationAsync', 'SeedRolesAsync', 'SeedPermissionsAsync', 'SeedRolePermissionsAsync'];
148
+ for (const method of methods) {
149
+ const methodRegex = new RegExp(`${method}[^{]*\\{([^}]*)\\}`, 's');
150
+ const match = content.match(methodRegex);
151
+ if (match) {
152
+ const body = match[1].trim();
153
+ if (body === 'return Task.CompletedTask;' || body === '' || body === 'return;') {
154
+ BLOCKING_ERROR(`${providerFile}: ${method} is a STUB (${body || 'empty body'}). ` +
155
+ `Re-read references/core-seed-data.md Section 6 and generate real implementation. ` +
156
+ `Stub seed methods cause EMPTY MENU — 0 applications visible in UI.`);
157
+ }
158
+ }
159
+ }
160
+
161
+ // Check 2: Must contain actual entity creation calls
162
+ if (!content.includes('.Add(') && !content.includes('.AddRange(')) {
163
+ BLOCKING_ERROR(`${providerFile}: No .Add() or .AddRange() calls found. ` +
164
+ `SeedDataProvider must actually insert seed data, not just return.`);
165
+ }
166
+ }
167
+ ```
168
+
134
169
  ---
135
170
 
136
171
  ## 7. Acceptance Criteria POST-CHECK
@@ -0,0 +1,200 @@
1
+ # Canonical JSON Formats Reference
2
+
3
+ > **Purpose:** Single source of truth for all JSON file formats produced by the BA pipeline.
4
+ > **Used by:** `/business-analyse` (step-03), `/business-analyse-design` (step-01), `/business-analyse-html`, `/business-analyse-handoff`, `/business-analyse-develop`
5
+ > **Rule:** All producers MUST write canonical format. All consumers MUST accept canonical + deprecated fallbacks.
6
+
7
+ ---
8
+
9
+ ## entities.json
10
+
11
+ ```json
12
+ {
13
+ "entities": [
14
+ {
15
+ "name": "Employee",
16
+ "description": "...",
17
+ "personRoleConfig": { "variant": "mandatory", "userFields": ["firstName", "lastName", "email"] },
18
+ "attributes": [
19
+ { "name": "code", "type": "string", "required": true, "description": "..." },
20
+ { "name": "userId", "type": "string", "required": true, "description": "FK auth_Users (ASP.NET Identity)" }
21
+ ],
22
+ "versionedAttributes": [
23
+ { "entity": "Salary", "attributes": ["grossAmount", "effectiveDate"], "reason": "..." }
24
+ ],
25
+ "relationships": [
26
+ { "target": "Department", "type": "ManyToOne", "description": "..." }
27
+ ]
28
+ }
29
+ ]
30
+ }
31
+ ```
32
+
33
+ **Convention guards:**
34
+ - Person entities → `personRoleConfig` + `userId` (string), NO firstName/lastName/email
35
+ - Versioned data → `versionedAttributes[]`, NOT inline fields
36
+ - FK naming → always `{entity}Id` suffix
37
+ - FK to auth_Users → type `string` (ASP.NET Identity), NOT `guid`
38
+
39
+ ---
40
+
41
+ ## usecases.json
42
+
43
+ ```json
44
+ {
45
+ "useCases": [
46
+ {
47
+ "id": "UC-MODULE-001",
48
+ "name": "Créer un employé",
49
+ "sectionCode": "list",
50
+ "primaryActor": "Responsable RH",
51
+ "preconditions": ["..."],
52
+ "mainScenario": [
53
+ "L'utilisateur ouvre la page de création",
54
+ "Il remplit les champs obligatoires"
55
+ ],
56
+ "alternativeScenarios": [
57
+ { "name": "Données invalides", "steps": ["Le système affiche les erreurs"] }
58
+ ],
59
+ "businessRules": ["BR-VAL-MODULE-001"],
60
+ "result": "L'entité est créée"
61
+ }
62
+ ]
63
+ }
64
+ ```
65
+
66
+ | Canonical key | Deprecated alternatives | Notes |
67
+ |---------------|------------------------|-------|
68
+ | `useCases` (root) | `usecases` | camelCase is canonical |
69
+ | `primaryActor` | `actor` | |
70
+ | `mainScenario` (string[]) | `steps` (object[] or string[]) | MUST be strings, never `{step, action}` objects |
71
+ | `alternativeScenarios` (object[]) | `alternativeFlows`, `alternative` (string) | Must be `[{name, steps}]` |
72
+
73
+ ---
74
+
75
+ ## rules.json
76
+
77
+ ```json
78
+ {
79
+ "rules": [
80
+ {
81
+ "id": "BR-VAL-MODULE-001",
82
+ "name": "Validation date",
83
+ "category": "validation",
84
+ "sectionCode": "list",
85
+ "statement": "La date ne peut pas être dans le futur",
86
+ "example": "Date = 2027-01-01 → erreur",
87
+ "entities": ["Employee"],
88
+ "severity": "blocking"
89
+ }
90
+ ]
91
+ }
92
+ ```
93
+
94
+ | Canonical key | Deprecated alternatives |
95
+ |---------------|------------------------|
96
+ | `rules` (root) | `businessRules` |
97
+ | `statement` | `description` |
98
+ | `id` | `name` (as identifier) |
99
+
100
+ Categories: `validation`, `calculation`, `workflow`, `security`, `data`, `notification`
101
+
102
+ ---
103
+
104
+ ## permissions.json
105
+
106
+ ```json
107
+ {
108
+ "roles": [
109
+ { "role": "RH Admin", "description": "..." }
110
+ ],
111
+ "permissionPaths": [
112
+ "HumanResources.Employees.Read",
113
+ "HumanResources.Employees.Create"
114
+ ],
115
+ "matrix": {
116
+ "roleAssignments": [
117
+ { "role": "RH Admin", "permissions": ["HumanResources.Employees.*"] }
118
+ ]
119
+ }
120
+ }
121
+ ```
122
+
123
+ ---
124
+
125
+ ## screens.json
126
+
127
+ ```json
128
+ {
129
+ "sections": [
130
+ {
131
+ "sectionCode": "list",
132
+ "sectionLabel": "Liste des employés",
133
+ "resources": [
134
+ {
135
+ "code": "employees-grid",
136
+ "type": "SmartTable",
137
+ "label": "Grille des employés",
138
+ "columns": [
139
+ { "field": "code", "label": "Code", "type": "text", "sortable": true }
140
+ ],
141
+ "filters": [
142
+ { "field": "status", "type": "select", "label": "Statut" }
143
+ ],
144
+ "actions": ["create", "export"],
145
+ "permission": "HumanResources.Employees.Read"
146
+ }
147
+ ]
148
+ }
149
+ ]
150
+ }
151
+ ```
152
+
153
+ | Canonical key | Deprecated alternatives | Notes |
154
+ |---------------|------------------------|-------|
155
+ | `sections` (root) | `screens` (flat array) | `sections[]` with `resources[]` is canonical |
156
+ | `sectionCode` | `id`, `code` | |
157
+ | `sectionLabel` | `displayName`, `label` | |
158
+ | resource `type` | `layout`, `componentType` | Must be: SmartTable, SmartForm, SmartDashboard, SmartKanban, SmartCard, SmartFilter |
159
+ | column `field` | `code`, `name`, `id`, `fieldCode` | |
160
+ | column `label` | `displayName` | |
161
+
162
+ **DEPRECATED format (do NOT produce):**
163
+ ```json
164
+ {
165
+ "screens": [
166
+ { "screen": "employees-list", "section": "list", "componentType": "SmartTable", "columns": [...] }
167
+ ]
168
+ }
169
+ ```
170
+
171
+ ---
172
+
173
+ ## Consumer normalization pattern
174
+
175
+ All downstream consumers should normalize input using this priority chain:
176
+
177
+ ```javascript
178
+ // usecases
179
+ const rawUCs = data.useCases || data.usecases || [];
180
+ const uc = {
181
+ name: raw.name || raw.title || raw.id,
182
+ actor: raw.primaryActor || raw.actor,
183
+ steps: (raw.mainScenario || raw.steps || []).map(s =>
184
+ typeof s === 'string' ? s : (s.action || s.description || "")
185
+ ),
186
+ alternatives: raw.alternativeScenarios || raw.alternativeFlows || []
187
+ };
188
+
189
+ // screens
190
+ const sections = data.sections || [];
191
+ const flatScreens = data.screens || [];
192
+ // If flat format: wrap each screen as a section with 1 resource
193
+
194
+ // columns
195
+ const col = {
196
+ field: raw.field || raw.code || raw.name || raw.id,
197
+ label: raw.label || raw.displayName || raw.code,
198
+ type: raw.renderAs === "badge" ? "badge" : (raw.type || raw.dataType || "text")
199
+ };
200
+ ```