@atlashub/smartstack-cli 3.9.0 → 3.10.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 (22) hide show
  1. package/package.json +1 -1
  2. package/templates/agents/ba-writer.md +178 -0
  3. package/templates/skills/application/references/application-roles-template.md +227 -0
  4. package/templates/skills/application/references/provider-template.md +30 -6
  5. package/templates/skills/application/steps/step-03-roles.md +45 -7
  6. package/templates/skills/application/steps/step-03b-provider.md +13 -6
  7. package/templates/skills/business-analyse/SKILL.md +56 -4
  8. package/templates/skills/business-analyse/references/agent-pooling-best-practices.md +477 -0
  9. package/templates/skills/business-analyse/references/cache-warming-strategy.md +578 -0
  10. package/templates/skills/business-analyse/references/robustness-checks.md +538 -0
  11. package/templates/skills/business-analyse/schemas/sections/specification-schema.json +33 -1
  12. package/templates/skills/business-analyse/steps/step-00-init.md +166 -0
  13. package/templates/skills/business-analyse/steps/step-03a-data.md +36 -0
  14. package/templates/skills/business-analyse/steps/step-03c-compile.md +71 -2
  15. package/templates/skills/business-analyse/steps/step-03d-validate.md +274 -0
  16. package/templates/skills/business-analyse/steps/step-04-consolidation.md +166 -0
  17. package/templates/skills/business-analyse/steps/step-05a-handoff.md +44 -0
  18. package/templates/skills/business-analyse/steps/step-05b-deploy.md +21 -2
  19. package/templates/skills/business-analyse/steps/step-05c-ralph-readiness.md +526 -0
  20. package/templates/skills/controller/steps/step-03-generate.md +184 -24
  21. package/templates/skills/controller/templates.md +11 -2
  22. package/templates/skills/ralph-loop/references/core-seed-data.md +173 -21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlashub/smartstack-cli",
3
- "version": "3.9.0",
3
+ "version": "3.10.0",
4
4
  "description": "SmartStack Claude Code automation toolkit - GitFlow, EF Core migrations, prompts and more",
5
5
  "author": {
6
6
  "name": "SmartStack",
@@ -88,6 +88,102 @@ Merge a section into an existing feature.json.
88
88
  9. **Cross-Reference Validation (for specification and handoff sections):** See CROSS-REFERENCE VALIDATION section below
89
89
  10. Return confirmation with section size and status
90
90
 
91
+ ### enrichSectionIncremental
92
+ Incrementally update a section in feature.json using PATCH-style operations instead of full section replacement. Optimized for large files and preventing file size issues.
93
+
94
+ **Input:**
95
+ - featureId: FEAT-NNN or full path to feature.json
96
+ - section: one of [discovery, analysis, specification, validation, handoff, suggestions, cadrage, consolidation, modules, dependencyGraph, metadata.workflow]
97
+ - operation: "merge" | "append" | "update" | "delete"
98
+ - path: JSON path within the section (e.g., "entities[2]", "useCases", "modules[0].status")
99
+ - data: the data to merge/append/update
100
+
101
+ **Operations:**
102
+
103
+ 1. **merge** - Deep merge data into existing section
104
+ ```javascript
105
+ // Example: Add new entities to analysis.entities without rewriting entire analysis
106
+ enrichSectionIncremental({
107
+ featureId: "FEAT-001",
108
+ section: "analysis",
109
+ operation: "merge",
110
+ path: "entities",
111
+ data: [
112
+ { name: "NewEntity", description: "...", attributes: [...] }
113
+ ]
114
+ })
115
+ // Result: analysis.entities now has existing entities + NewEntity
116
+ ```
117
+
118
+ 2. **append** - Append item to an array
119
+ ```javascript
120
+ // Example: Add a single business rule without rewriting all rules
121
+ enrichSectionIncremental({
122
+ featureId: "FEAT-001",
123
+ section: "analysis",
124
+ operation: "append",
125
+ path: "businessRules",
126
+ data: { id: "BR-VAL-ABC-042", name: "...", statement: "..." }
127
+ })
128
+ ```
129
+
130
+ 3. **update** - Update specific field in section
131
+ ```javascript
132
+ // Example: Update module status without rewriting entire modules array
133
+ enrichSectionIncremental({
134
+ featureId: "FEAT-001",
135
+ section: "modules",
136
+ operation: "update",
137
+ path: "[0].status", // Path to first module's status field
138
+ data: "handed-off"
139
+ })
140
+ ```
141
+
142
+ 4. **delete** - Remove item from section
143
+ ```javascript
144
+ // Example: Remove a specific entity
145
+ enrichSectionIncremental({
146
+ featureId: "FEAT-001",
147
+ section: "analysis",
148
+ operation: "delete",
149
+ path: "entities[2]" // Delete third entity
150
+ })
151
+ ```
152
+
153
+ **Process:**
154
+ 1. Find and read feature.json (use findFeature if given ID)
155
+ 2. Navigate to the specified section
156
+ 3. Apply the incremental operation:
157
+ - **merge**: Deep merge arrays (append unique items), shallow merge objects
158
+ - **append**: Push item to array at path
159
+ - **update**: Set value at path
160
+ - **delete**: Remove item at path
161
+ 4. Update metadata.updatedAt with current timestamp
162
+ 5. Update metadata.updatedBy with agent name
163
+ 6. Write back with pretty-print (2-space indent)
164
+ 7. **File Size Check:** If resulting file > 100KB, display WARNING
165
+ 8. Validate schema before writing
166
+ 9. **Cross-Reference Validation:** Same rules as enrichSection
167
+ 10. Return confirmation with operation summary and file size
168
+
169
+ **File Size Management:**
170
+ - Before write: Check if file would exceed 100KB
171
+ - If > 100KB: Display WARNING with recommendation to split into smaller operations
172
+ - If > 500KB: BLOCKING ERROR - file too large, must use smaller chunks
173
+ - Track cumulative file size growth across operations
174
+
175
+ **Advantages over enrichSection:**
176
+ - 50-70% reduction in tokens for large sections
177
+ - Avoids "file too large" errors by updating incrementally
178
+ - Allows progressive enrichment without reading/writing entire sections
179
+ - Better performance for repeated updates (e.g., module loop)
180
+
181
+ **Use Cases:**
182
+ - Adding entities one-by-one during module specification
183
+ - Updating module status in master without rewriting all modules
184
+ - Appending business rules progressively
185
+ - Updating handoff sections module-by-module
186
+
91
187
  ### enrichModuleHandoff
92
188
  Write the handoff section into a module feature.json. Specialized operation for step-05 module loop.
93
189
 
@@ -474,6 +570,88 @@ Before EVERY enrichSection() call for specification or handoff sections, validat
474
570
  6. **Pretty-print JSON** - use 2-space indentation
475
571
  7. **Timestamp management** - always set metadata.updatedAt to current ISO timestamp on write
476
572
  8. **Idempotency** - calling the same operation twice with same data should produce same result
573
+ 9. **File size management** - check file size before write, use incremental operations for large files
574
+
575
+ ## File Size Management (CRITICAL)
576
+
577
+ **Problem:** Large feature.json files (>100KB) can cause write failures and token exhaustion.
578
+
579
+ **Solution:** Progressive monitoring and incremental operations.
580
+
581
+ ### Size Thresholds
582
+
583
+ | File Size | Status | Action |
584
+ |-----------|--------|--------|
585
+ | < 50KB | ✓ Safe | Use enrichSection normally |
586
+ | 50-100KB | ⚠ Warning | Display warning, recommend enrichSectionIncremental for next operations |
587
+ | 100-500KB | ⚠ High | STRONGLY recommend enrichSectionIncremental, limit enrichSection use |
588
+ | > 500KB | ✗ Critical | BLOCK enrichSection, REQUIRE enrichSectionIncremental or split file |
589
+
590
+ ### Pre-Write File Size Check
591
+
592
+ Before EVERY write operation:
593
+
594
+ ```javascript
595
+ const currentFileSize = getFileSize(featurePath);
596
+ const estimatedNewSize = currentFileSize + newDataSize;
597
+
598
+ if (estimatedNewSize > 100 * 1024) { // 100KB
599
+ WARNING(`Feature.json will be ${formatBytes(estimatedNewSize)} after write`);
600
+ WARNING(`Recommend using enrichSectionIncremental for future operations`);
601
+ }
602
+
603
+ if (estimatedNewSize > 500 * 1024) { // 500KB
604
+ BLOCKING_ERROR(`Feature.json would exceed 500KB (${formatBytes(estimatedNewSize)})`);
605
+ BLOCKING_ERROR(`Use enrichSectionIncremental instead of enrichSection`);
606
+ STOP;
607
+ }
608
+ ```
609
+
610
+ ### Operation Selection Guide
611
+
612
+ | Scenario | Recommended Operation | Reason |
613
+ |----------|----------------------|--------|
614
+ | First-time section write | `enrichSection` | No existing data, full write needed |
615
+ | Module loop (3+ iterations) | `enrichSectionIncremental` | Avoids rewriting same data multiple times |
616
+ | Large sections (>20KB) | `enrichSectionIncremental` | Reduces token usage by 50-70% |
617
+ | File size > 100KB | `enrichSectionIncremental` (REQUIRED) | Prevents file size bloat |
618
+ | Single field update | `enrichSectionIncremental` with `update` | Most efficient for targeted changes |
619
+
620
+ ### Monitoring & Reporting
621
+
622
+ After EVERY write, report file size status:
623
+
624
+ ```
625
+ ✓ feature.json written successfully
626
+ Path: docs/business/HumanResources/Projects/business-analyse/v1.0/feature.json
627
+ Size: 87.3 KB (↑ 12.1 KB from previous)
628
+ Status: ⚠ Approaching 100KB threshold
629
+ Recommendation: Use enrichSectionIncremental for remaining modules
630
+ ```
631
+
632
+ ### Splitting Large Files (Advanced)
633
+
634
+ If a module feature.json exceeds 500KB despite incremental operations:
635
+
636
+ 1. **Split specification section** into separate files:
637
+ - `specification-entities.json` (entities, relationships)
638
+ - `specification-rules.json` (business rules, validations)
639
+ - `specification-ui.json` (wireframes, sections, navigation)
640
+
641
+ 2. **Update feature.json** with file references:
642
+ ```json
643
+ {
644
+ "specification": {
645
+ "$ref": "./specification-entities.json",
646
+ "$ref2": "./specification-rules.json",
647
+ "$ref3": "./specification-ui.json"
648
+ }
649
+ }
650
+ ```
651
+
652
+ 3. **ba-reader** auto-resolves references when reading
653
+
654
+ **Note:** File splitting is a LAST RESORT. Prefer enrichSectionIncremental first.
477
655
 
478
656
  ## Error Handling
479
657
 
@@ -0,0 +1,227 @@
1
+ # Application Roles Seed Data Template
2
+
3
+ > Referenced from `core-seed-data.md` and `step-03-roles.md` — C# template for application-scoped roles in client projects.
4
+
5
+ ---
6
+
7
+ ## Problem Statement
8
+
9
+ When using `IClientSeedDataProvider` (client projects with `seeding_strategy = "provider"`), role-permission mappings reference roles by their `Code`:
10
+
11
+ ```csharp
12
+ var role = roles.FirstOrDefault(r => r.Code == mapping.RoleCode); // "admin", "manager", "contributor", "viewer"
13
+ ```
14
+
15
+ **However**, the current templates do NOT create these application-scoped roles. They assume:
16
+ - System roles (SuperAdmin, PlatformAdmin, TenantAdmin, StandardUser) exist in Core
17
+ - Application-scoped roles (Admin, Manager, Contributor, Viewer) already exist with valid `Code` values
18
+
19
+ **Result:** Role-permission mappings fail silently when `role == null`.
20
+
21
+ ---
22
+
23
+ ## Solution: Application Roles Seed Data
24
+
25
+ Create application-scoped roles with deterministic GUIDs and valid `Code` values.
26
+
27
+ ---
28
+
29
+ ## File Location
30
+
31
+ **Path:** `Infrastructure/Persistence/Seeding/Data/ApplicationRolesSeedData.cs`
32
+
33
+ This file should be created **ONCE per application** (not per module).
34
+
35
+ ---
36
+
37
+ ## Template
38
+
39
+ ```csharp
40
+ using SmartStack.Domain.Platform.Administration.Roles;
41
+
42
+ namespace {BaseNamespace}.Infrastructure.Persistence.Seeding.Data;
43
+
44
+ /// <summary>
45
+ /// Application-scoped role seed data for {AppLabel}.
46
+ /// Defines the 4 standard application roles: Admin, Manager, Contributor, Viewer.
47
+ /// Consumed by IClientSeedDataProvider at application startup.
48
+ /// </summary>
49
+ public static class ApplicationRolesSeedData
50
+ {
51
+ // Deterministic GUIDs for application roles
52
+ // Generated from: "role-{applicationId}-{roleType}"
53
+ private static readonly Guid ApplicationId = {ApplicationGuid}; // From NavigationApplicationSeedData
54
+
55
+ public static readonly Guid AdminRoleId = GenerateRoleGuid("admin");
56
+ public static readonly Guid ManagerRoleId = GenerateRoleGuid("manager");
57
+ public static readonly Guid ContributorRoleId = GenerateRoleGuid("contributor");
58
+ public static readonly Guid ViewerRoleId = GenerateRoleGuid("viewer");
59
+
60
+ /// <summary>
61
+ /// Returns application-scoped role entries for seeding into core.auth_Roles.
62
+ /// </summary>
63
+ public static IEnumerable<ApplicationRoleSeedEntry> GetRoleEntries()
64
+ {
65
+ yield return new ApplicationRoleSeedEntry
66
+ {
67
+ Id = AdminRoleId,
68
+ Code = "admin",
69
+ Name = "{AppLabel} Admin",
70
+ Description = "Full administrative access to {AppLabel}",
71
+ ApplicationId = ApplicationId,
72
+ IsSystem = false,
73
+ IsActive = true,
74
+ DisplayOrder = 1
75
+ };
76
+
77
+ yield return new ApplicationRoleSeedEntry
78
+ {
79
+ Id = ManagerRoleId,
80
+ Code = "manager",
81
+ Name = "{AppLabel} Manager",
82
+ Description = "Management access to {AppLabel} (Create, Read, Update)",
83
+ ApplicationId = ApplicationId,
84
+ IsSystem = false,
85
+ IsActive = true,
86
+ DisplayOrder = 2
87
+ };
88
+
89
+ yield return new ApplicationRoleSeedEntry
90
+ {
91
+ Id = ContributorRoleId,
92
+ Code = "contributor",
93
+ Name = "{AppLabel} Contributor",
94
+ Description = "Contributor access to {AppLabel} (Create, Read)",
95
+ ApplicationId = ApplicationId,
96
+ IsSystem = false,
97
+ IsActive = true,
98
+ DisplayOrder = 3
99
+ };
100
+
101
+ yield return new ApplicationRoleSeedEntry
102
+ {
103
+ Id = ViewerRoleId,
104
+ Code = "viewer",
105
+ Name = "{AppLabel} Viewer",
106
+ Description = "Read-only access to {AppLabel}",
107
+ ApplicationId = ApplicationId,
108
+ IsSystem = false,
109
+ IsActive = true,
110
+ DisplayOrder = 4
111
+ };
112
+ }
113
+
114
+ private static Guid GenerateRoleGuid(string roleType)
115
+ {
116
+ using var sha256 = System.Security.Cryptography.SHA256.Create();
117
+ var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes($"role-{ApplicationId}-{roleType}"));
118
+ return new Guid(hash.Take(16).ToArray());
119
+ }
120
+ }
121
+
122
+ /// <summary>Seed entry DTO for application role.</summary>
123
+ public class ApplicationRoleSeedEntry
124
+ {
125
+ public Guid Id { get; init; }
126
+ public string Code { get; init; } = null!;
127
+ public string Name { get; init; } = null!;
128
+ public string Description { get; init; } = null!;
129
+ public Guid ApplicationId { get; init; }
130
+ public bool IsSystem { get; init; }
131
+ public bool IsActive { get; init; }
132
+ public int DisplayOrder { get; init; }
133
+ }
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Placeholder Replacement
139
+
140
+ | Placeholder | Description | Example |
141
+ |-------------|-------------|---------|
142
+ | `{BaseNamespace}` | Root namespace of the client project | `SmartStack.Modules.RessourcesHumaines` |
143
+ | `{AppLabel}` | Human-readable application label (EN) | `Human Resources` |
144
+ | `{ApplicationGuid}` | GUID of the application (from NavigationApplicationSeedData) | `30f1fbba-e8c3-4879-9a49-d18deaa70a83` |
145
+
146
+ ---
147
+
148
+ ## Integration into IClientSeedDataProvider
149
+
150
+ Add a new method `SeedRolesAsync()` to the provider:
151
+
152
+ ```csharp
153
+ public async Task SeedRolesAsync(ICoreDbContext context, CancellationToken ct)
154
+ {
155
+ // Check idempotence
156
+ var exists = await context.Roles
157
+ .AnyAsync(r => r.ApplicationId == ApplicationRolesSeedData.ApplicationId, ct);
158
+ if (exists) return;
159
+
160
+ // Create application-scoped roles using factory method
161
+ foreach (var entry in ApplicationRolesSeedData.GetRoleEntries())
162
+ {
163
+ var role = Role.Create(
164
+ entry.Code,
165
+ entry.Name,
166
+ entry.Description,
167
+ entry.ApplicationId,
168
+ entry.IsSystem);
169
+
170
+ context.Roles.Add(role);
171
+ }
172
+
173
+ await ((DbContext)context).SaveChangesAsync(ct);
174
+ }
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Execution Order in Provider
180
+
181
+ **CRITICAL:** Roles must be created BEFORE role-permission mappings.
182
+
183
+ ```
184
+ 1. SeedNavigationAsync() → Creates application + modules + translations
185
+ 2. SeedRolesAsync() → Creates application-scoped roles (NEW)
186
+ 3. SeedPermissionsAsync() → Creates permissions
187
+ 4. SeedRolePermissionsAsync() → Maps roles to permissions (now succeeds because roles exist)
188
+ ```
189
+
190
+ ---
191
+
192
+ ## Verification Checklist
193
+
194
+ Before marking the task as completed, verify:
195
+
196
+ - [ ] `ApplicationRolesSeedData.cs` created in `Infrastructure/Persistence/Seeding/Data/`
197
+ - [ ] Deterministic GUIDs used (NEVER `Guid.NewGuid()`)
198
+ - [ ] 4 roles defined: Admin, Manager, Contributor, Viewer
199
+ - [ ] Each role has a valid `Code` value ("admin", "manager", "contributor", "viewer")
200
+ - [ ] Each role has `ApplicationId` set to the application GUID
201
+ - [ ] `SeedRolesAsync()` method added to `IClientSeedDataProvider`
202
+ - [ ] `SeedRolesAsync()` is idempotent (checks existence before inserting)
203
+ - [ ] `Role.Create()` factory method used (NEVER `new Role()`)
204
+ - [ ] `SaveChangesAsync()` called after role creation
205
+ - [ ] Execution order: Navigation → Roles → Permissions → RolePermissions
206
+ - [ ] `dotnet build` passes after generation
207
+
208
+ ---
209
+
210
+ ## Notes
211
+
212
+ - **Application ID source:** Read from the navigation application created in `SeedNavigationAsync()` or from `{AppPascal}NavigationSeedData.cs`
213
+ - **Role factory method:** Use `Role.Create(code, name, description, applicationId, isSystem)` from SmartStack.Domain
214
+ - **Code uniqueness:** Role codes must be unique within the application scope
215
+ - **System roles:** These are NOT system roles (IsSystem = false) - they are application-scoped roles
216
+ - **Tenant isolation:** Application-scoped roles are automatically tenant-isolated via the Core authorization system
217
+
218
+ ---
219
+
220
+ ## Migration Impact
221
+
222
+ **For existing projects without application roles:**
223
+ 1. Generate `ApplicationRolesSeedData.cs` using this template
224
+ 2. Add `SeedRolesAsync()` method to the existing `IClientSeedDataProvider`
225
+ 3. Update the provider's execution to call `SeedRolesAsync()` BEFORE `SeedRolePermissionsAsync()`
226
+ 4. Run the application - roles will be created on next startup
227
+ 5. Role-permission mappings will now succeed
@@ -19,7 +19,7 @@ using SmartStack.Domain.Platform.Administration.Roles;
19
19
  namespace {BaseNamespace}.Infrastructure.Persistence.Seeding;
20
20
 
21
21
  /// <summary>
22
- /// Seeds {AppLabel} navigation, permissions, and role-permission data
22
+ /// Seeds {AppLabel} navigation, roles, permissions, and role-permission data
23
23
  /// into the SmartStack Core schema at application startup.
24
24
  /// Implements <see cref="IClientSeedDataProvider"/> for runtime seeding
25
25
  /// (no Core migrations required).
@@ -75,6 +75,29 @@ public class {AppPascalName}SeedDataProvider : IClientSeedDataProvider
75
75
  await ((DbContext)context).SaveChangesAsync(ct);
76
76
  }
77
77
 
78
+ public async Task SeedRolesAsync(ICoreDbContext context, CancellationToken ct)
79
+ {
80
+ // Check idempotence
81
+ var applicationId = ApplicationRolesSeedData.ApplicationId;
82
+ var exists = await context.Roles
83
+ .AnyAsync(r => r.ApplicationId == applicationId, ct);
84
+ if (exists) return;
85
+
86
+ // Create application-scoped roles (Admin, Manager, Contributor, Viewer)
87
+ // Use data from ApplicationRolesSeedData.cs
88
+ foreach (var entry in ApplicationRolesSeedData.GetRoleEntries())
89
+ {
90
+ var role = Role.Create(
91
+ entry.Code,
92
+ entry.Name,
93
+ entry.Description,
94
+ entry.ApplicationId,
95
+ entry.IsSystem);
96
+ context.Roles.Add(role);
97
+ }
98
+ await ((DbContext)context).SaveChangesAsync(ct);
99
+ }
100
+
78
101
  public async Task SeedPermissionsAsync(ICoreDbContext context, CancellationToken ct)
79
102
  {
80
103
  // Check idempotence
@@ -126,9 +149,10 @@ services.AddScoped<IClientSeedDataProvider, {AppPascalName}SeedDataProvider>();
126
149
 
127
150
  ## Critical Rules
128
151
 
129
- 1. **Factory methods mandatory**: `NavigationModule.Create(...)`, `Permission.CreateForModule(...)`, `RolePermission.Create(...)` — NEVER `new Entity()`
152
+ 1. **Factory methods mandatory**: `NavigationModule.Create(...)`, `Role.Create(...)`, `Permission.CreateForModule(...)`, `RolePermission.Create(...)` — NEVER `new Entity()`
130
153
  2. **Idempotence**: Each Seed method checks existence before inserting
131
- 3. **SaveChangesAsync per group**: Navigation → save → Permissions → save → RolePermissions → save
132
- 4. **Deterministic GUIDs**: Use IDs from SeedData classes (not `Guid.NewGuid()`)
133
- 5. **Resolve FK by Code**: Parent modules are found by `Code`, not hardcoded GUID
134
- 6. **Order property**: Use `100` as default. If multiple providers exist, they run in Order sequence
154
+ 3. **SaveChangesAsync per group**: Navigation → save → Roles → save → Permissions → save → RolePermissions → save
155
+ 4. **Execution order**: `SeedRolesAsync()` MUST be called BEFORE `SeedRolePermissionsAsync()` (roles must exist before mapping)
156
+ 5. **Deterministic GUIDs**: Use IDs from SeedData classes (not `Guid.NewGuid()`)
157
+ 6. **Resolve FK by Code**: Parent modules and roles are found by `Code`, not hardcoded GUID
158
+ 7. **Order property**: Use `100` as default. If multiple providers exist, they run in Order sequence
@@ -1,23 +1,29 @@
1
1
  ---
2
2
  name: step-03-roles
3
- description: Generate role-permission mappings using MCP scaffold_role_permissions (with fallback)
3
+ description: Generate application roles and role-permission mappings using MCP scaffold_role_permissions (with fallback)
4
4
  prev_step: steps/step-02-permissions.md
5
5
  next_step: steps/step-03b-provider.md
6
6
  ---
7
7
 
8
- # Step 3: Role-Permission Mapping
8
+ # Step 3: Application Roles & Role-Permission Mapping
9
9
 
10
10
  ## MANDATORY EXECUTION RULES
11
11
 
12
+ - For **client projects** (`seeding_strategy = "provider"`): Generate ApplicationRolesSeedData.cs and module role mappings
13
+ - For **core projects** (`seeding_strategy = "hasdata"`): Use RolePermissionConfiguration.cs
12
14
  - PREFER MCP `scaffold_role_permissions` tool as the primary method
13
15
  - If MCP is unavailable or the call fails, use the FALLBACK PROCEDURE below
14
16
  - ALWAYS assign permissions to default roles
15
17
  - NEVER leave permissions without role assignments
16
- - ALWAYS WRITE generated code to the actual RolePermissionConfiguration.cs file
18
+ - ALWAYS WRITE generated code to the actual files
17
19
 
18
20
  ## YOUR TASK
19
21
 
20
- Generate role-permission mappings:
22
+ For **client projects**:
23
+ 1. **ApplicationRolesSeedData.cs** (once per application) — defines the 4 application-scoped roles
24
+ 2. **{Module}RolePermissionSeedData.cs** (per module) — maps permissions to roles by Code
25
+
26
+ For **core projects**:
21
27
  1. RolePermissionConfiguration.cs HasData() entries
22
28
  2. Default role assignments (SuperAdmin, PlatformAdmin, TenantAdmin, StandardUser)
23
29
  3. Application-scoped role assignments (Admin, Manager, Contributor, Viewer)
@@ -131,13 +137,38 @@ If MCP call fails or `{mcp_available}` = false:
131
137
  **For core (`{seeding_strategy}` = "hasdata"):** Write in RolePermissionConfiguration.cs (existing pattern)
132
138
 
133
139
  **For client (`{seeding_strategy}` = "provider"):** DO NOT write in RolePermissionConfiguration.cs (does not exist in client projects).
134
- Instead, create:
135
- - `Infrastructure/Persistence/Seeding/Data/{Domain}/{Module}RolePermissionSeedData.cs`
140
+
141
+ Instead, create TWO files:
142
+
143
+ ### 1. ApplicationRolesSeedData.cs (ONCE per application)
144
+
145
+ **File:** `Infrastructure/Persistence/Seeding/Data/ApplicationRolesSeedData.cs`
146
+
147
+ **Purpose:** Defines the 4 standard application-scoped roles (Admin, Manager, Contributor, Viewer) with valid `Code` values.
148
+
149
+ **CRITICAL:** Without this file, role-permission mappings in `SeedRolePermissionsAsync()` will fail silently because `roles.FirstOrDefault(r => r.Code == mapping.RoleCode)` will return null.
150
+
151
+ See [references/application-roles-template.md](../references/application-roles-template.md) for the complete template.
152
+
153
+ **Key requirements:**
154
+ - Deterministic GUIDs based on `role-{applicationId}-{roleType}`
155
+ - 4 roles: Admin, Manager, Contributor, Viewer
156
+ - Each role has a valid `Code` property ("admin", "manager", "contributor", "viewer")
157
+ - `ApplicationId` references the navigation application GUID
158
+ - `IsSystem = false` (application-scoped, not system roles)
159
+
160
+ **Detection:** Check if ApplicationRolesSeedData.cs exists. If yes, skip creation (already exists from Module 1). If no, create it.
161
+
162
+ ### 2. {Module}RolePermissionSeedData.cs (PER module)
163
+
164
+ **File:** `Infrastructure/Persistence/Seeding/Data/{Domain}/{Module}RolePermissionSeedData.cs`
165
+
166
+ **Purpose:** Maps permissions to roles by Code (e.g., "admin" → "{navRoute}.*").
136
167
 
137
168
  Content: static class with method `GetRolePermissionEntries()` that returns the role-permission mapping data.
138
169
  These entries will be consumed by the `IClientSeedDataProvider` at step 03b.
139
170
 
140
- **After creating RolePermission SeedData:** Proceed to step-03b-provider.md (which will skip for core projects).
171
+ **After creating both files:** Proceed to step-03b-provider.md (which will skip for core projects).
141
172
 
142
173
  ---
143
174
 
@@ -280,6 +311,13 @@ If user selects "Custom adjustments", ask which roles/permissions to change and
280
311
 
281
312
  ## SUCCESS METRICS
282
313
 
314
+ **For client projects:**
315
+ - ApplicationRolesSeedData.cs created (once per application)
316
+ - {Module}RolePermissionSeedData.cs created with role-permission mappings
317
+ - All 4 application roles defined with valid Code values
318
+ - Proceeded to step-03b-provider.md
319
+
320
+ **For core projects:**
283
321
  - Role-permission mappings generated (via MCP or fallback)
284
322
  - RolePermissionConfiguration.cs WRITTEN with new entries
285
323
  - All default roles have appropriate access
@@ -50,9 +50,10 @@ From previous steps:
50
50
  ## FILES TO GENERATE
51
51
 
52
52
  See [references/provider-template.md](../references/provider-template.md) for the complete implementation:
53
- - **Provider class** (`{AppPascalName}SeedDataProvider.cs`) with 3 seed methods (Navigation, Permissions, RolePermissions)
53
+ - **ApplicationRolesSeedData.cs** (once per application) defines application-scoped roles (Admin, Manager, Contributor, Viewer)
54
+ - **Provider class** (`{AppPascalName}SeedDataProvider.cs`) with 4 seed methods (Navigation, Roles, Permissions, RolePermissions)
54
55
  - **DI registration** pattern for `Infrastructure/DependencyInjection.cs`
55
- - **6 critical rules** (factory methods, idempotence, SaveChangesAsync order, deterministic GUIDs, FK by Code, Order property)
56
+ - **7 critical rules** (factory methods, idempotence, execution order, SaveChangesAsync order, deterministic GUIDs, FK by Code, Order property)
56
57
 
57
58
  ---
58
59
 
@@ -64,9 +65,11 @@ Identify all SeedData files created in previous steps:
64
65
 
65
66
  ```
66
67
  Glob: **/Persistence/Seeding/Data/{Domain}/*SeedData.cs
68
+ Glob: **/Persistence/Seeding/Data/ApplicationRolesSeedData.cs
67
69
  ```
68
70
 
69
71
  Expected files:
72
+ - `ApplicationRolesSeedData.cs` (application-level, once per app)
70
73
  - `{Module}NavigationSeedData.cs` (from step 01)
71
74
  - `{Module}NavigationTranslationSeedData.cs` (from step 01)
72
75
  - `{Module}PermissionSeedData.cs` (from step 02)
@@ -96,9 +99,11 @@ Add the `IClientSeedDataProvider` registration.
96
99
  ### 4. Verify
97
100
 
98
101
  Before proceeding to step-04, verify:
99
- - [ ] Provider generated with 3 methods (SeedNavigationAsync, SeedPermissionsAsync, SeedRolePermissionsAsync)
102
+ - [ ] ApplicationRolesSeedData.cs created (once per application)
103
+ - [ ] Provider generated with 4 methods (SeedNavigationAsync, SeedRolesAsync, SeedPermissionsAsync, SeedRolePermissionsAsync)
104
+ - [ ] Execution order: Navigation → Roles → Permissions → RolePermissions
100
105
  - [ ] Registered in DI (`services.AddScoped<IClientSeedDataProvider, ...>()`)
101
- - [ ] Consumes SeedData classes from steps 01-03
106
+ - [ ] Consumes SeedData classes from steps 01-03 + ApplicationRolesSeedData
102
107
  - [ ] Idempotent (check existence before insert)
103
108
  - [ ] Uses factory methods (no `new Entity()`)
104
109
 
@@ -106,9 +111,11 @@ Before proceeding to step-04, verify:
106
111
 
107
112
  ## SUCCESS METRICS
108
113
 
109
- - Provider class generated with all 3 seed methods
114
+ - ApplicationRolesSeedData.cs created (application-level)
115
+ - Provider class generated with all 4 seed methods (Navigation, Roles, Permissions, RolePermissions)
116
+ - Correct execution order enforced
110
117
  - DI registration added
111
- - SeedData classes from steps 01-03 properly consumed
118
+ - SeedData classes from steps 01-03 + ApplicationRolesSeedData properly consumed
112
119
  - All methods are idempotent
113
120
  - Factory methods used throughout
114
121
  - Proceeded to step-04-backend.md