@atlashub/smartstack-cli 1.36.0 → 1.37.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlashub/smartstack-cli",
3
- "version": "1.36.0",
3
+ "version": "1.37.0",
4
4
  "description": "SmartStack Claude Code automation toolkit - GitFlow, APEX, EF Core migrations, prompts and more",
5
5
  "author": {
6
6
  "name": "SmartStack",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: step-01-navigation
3
- description: Generate navigation entity seeds using MCP scaffold_navigation
3
+ description: Generate navigation entity seeds using MCP scaffold_navigation (with fallback)
4
4
  prev_step: steps/step-00-init.md
5
5
  next_step: steps/step-02-permissions.md
6
6
  ---
@@ -9,16 +9,17 @@ next_step: steps/step-02-permissions.md
9
9
 
10
10
  ## MANDATORY EXECUTION RULES
11
11
 
12
- - ALWAYS use MCP `scaffold_navigation` tool - NEVER use templates
13
- - NEVER generate code manually - let MCP handle it
14
- - ALWAYS verify the output includes translations for 4 languages
15
- - YOU ARE AN ORCHESTRATOR calling MCP, not a generator
12
+ - PREFER MCP `scaffold_navigation` tool as the primary method
13
+ - If MCP is unavailable (`{mcp_available}` = false) or the call fails, use the FALLBACK PROCEDURE below
14
+ - ALWAYS verify the output includes translations for 4 languages (fr, en, it, de)
15
+ - ALWAYS WRITE generated code to the actual Configuration files (not just display)
16
+ - Store navigation GUID for use in subsequent steps
16
17
 
17
18
  ## YOUR TASK
18
19
 
19
- Call the SmartStack MCP `scaffold_navigation` tool to generate:
20
- 1. Navigation entity HasData() code
21
- 2. NavigationTranslation entries (4 languages)
20
+ Generate navigation entity seeds:
21
+ 1. Navigation entity HasData() code in the appropriate Configuration.cs
22
+ 2. NavigationTranslation entries (4 languages) in NavigationTranslationConfiguration.cs
22
23
 
23
24
  ---
24
25
 
@@ -36,10 +37,11 @@ From step-00-init:
36
37
  | `{descriptions}` | Object with fr, en, it, de |
37
38
  | `{icon}` | Lucide icon name |
38
39
  | `{display_order}` | Numeric display order |
40
+ | `{mcp_available}` | Boolean - MCP connectivity status |
39
41
 
40
42
  ---
41
43
 
42
- ## EXECUTION SEQUENCE
44
+ ## EXECUTION SEQUENCE (MCP Primary)
43
45
 
44
46
  ### 1. Call MCP scaffold_navigation
45
47
 
@@ -71,38 +73,19 @@ The tool returns:
71
73
  - HasData() code for NavigationTranslationConfiguration.cs
72
74
  - SeedData class template (optional)
73
75
 
74
- ### 3. Present Output to User
76
+ ### 3. Write Code to Files
75
77
 
76
- ```markdown
77
- ## Navigation Seeds Generated
78
-
79
- **Entity:** {level} - {code}
80
- **GUID:** {generated_guid}
81
- **Path:** {full_path}
82
-
83
- ### Files to Update
84
-
85
- 1. **{NavigationLevelConfiguration}.cs**
86
- [Show HasData code from MCP response]
87
-
88
- 2. **NavigationTranslationConfiguration.cs**
89
- [Show translation HasData code from MCP response]
90
-
91
- ### Recommended: Create SeedData Class
78
+ **CRITICAL:** WRITE the generated code to the actual Configuration files.
92
79
 
93
- [Show SeedData class from MCP response]
94
- ```
80
+ 1. Update `Navigation{Level}Configuration.cs` with the new HasData entry
81
+ 2. Update `NavigationTranslationConfiguration.cs` with the 4 translation entries
95
82
 
96
83
  ### 4. Store Generated GUID
97
84
 
98
- **CRITICAL:** Store the navigation entity GUID for use in step-02-permissions:
99
-
100
85
  ```
101
86
  {navigation_guid} = [GUID from MCP response]
102
87
  ```
103
88
 
104
- This GUID will be referenced when creating permissions.
105
-
106
89
  ---
107
90
 
108
91
  ## MCP RESPONSE HANDLING
@@ -110,35 +93,235 @@ This GUID will be referenced when creating permissions.
110
93
  ### Success Case
111
94
 
112
95
  If MCP returns successfully:
113
- - Extract and display all HasData code
96
+ - Write HasData code to Configuration files
114
97
  - Store `{navigation_guid}` for next step
115
98
  - Proceed to step-02-permissions.md
116
99
 
117
100
  ### Error Case
118
101
 
119
- If MCP call fails:
120
- - Display error message
121
- - Suggest manual alternatives
122
- - Do NOT proceed automatically
102
+ If MCP call fails or `{mcp_available}` = false:
103
+ - Log the error for reference
104
+ - Proceed to FALLBACK PROCEDURE below
105
+ - Do NOT stop the workflow
106
+
107
+ ---
108
+
109
+ ## FALLBACK PROCEDURE (When MCP Unavailable)
110
+
111
+ > This procedure generates navigation seeds following SmartStack.app patterns.
112
+ > Reference: `templates-seed.md` for code templates.
113
+
114
+ ### F1. Read Existing Configuration Files
115
+
116
+ **CRITICAL:** Before generating any code, read existing files to determine state:
117
+
118
+ 1. **Find the Navigation Configuration directory:**
119
+ ```
120
+ Glob: **/Persistence/Configurations/Navigation/Navigation*Configuration.cs
121
+ ```
122
+
123
+ 2. **Read NavigationTranslationConfiguration.cs** - Find the last GUID index:
124
+ ```
125
+ Search for: the last call to GenerateGuid(index++)
126
+ The index variable starts at 1 and increments per translation entry.
127
+ Your new translations MUST continue from the next index value.
128
+ ```
129
+
130
+ 3. **Read Navigation{Level}Configuration.cs** - Check existing entities:
131
+ ```
132
+ Read the Configuration for the target level (Context, Application, Module, Section).
133
+ Check if it already references a SeedData class: builder.HasData(Navigation{Level}SeedData.GetSeedData())
134
+ If yes: Read the corresponding SeedData class to find existing entries.
135
+ If no: You will need to add HasData() call.
136
+ ```
137
+
138
+ 4. **Read existing SeedData files** (if they exist):
139
+ ```
140
+ Glob: **/Seeding/Data/Navigation/Navigation{Level}SeedData.cs
141
+ Check for existing entity IDs to avoid collisions.
142
+ ```
143
+
144
+ ### F2. Determine Parent GUID
145
+
146
+ For non-context levels, find the parent entity GUID:
147
+
148
+ | Level | Parent | Where to Find Parent GUID |
149
+ |-------|--------|---------------------------|
150
+ | application | context | `NavigationContextSeedData.cs` → e.g. `PlatformContextId` |
151
+ | module | application | `NavigationApplicationSeedData.cs` → e.g. `AdministrationAppId` |
152
+ | section | module | `NavigationModuleSeedData.cs` → e.g. `UsersModuleId` |
153
+
154
+ Read the parent SeedData class and find the GUID matching `{parent_path}`.
155
+
156
+ ### F3. Generate Navigation Entity GUID
157
+
158
+ Generate a deterministic GUID for the new navigation entity:
159
+
160
+ ```csharp
161
+ // Use SHA256 hash of the full_path for deterministic generation
162
+ using var sha256 = System.Security.Cryptography.SHA256.Create();
163
+ var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes("navigation-{level}-{full_path}"));
164
+ var guid = new Guid(hash.Take(16).ToArray());
165
+ ```
166
+
167
+ **Rules:**
168
+ - NEVER use `Guid.NewGuid()`
169
+ - Read existing SeedData GUIDs to verify no collision
170
+ - Store result as `{navigation_guid}`
171
+
172
+ ### F4. Write Navigation Entity Seed
173
+
174
+ Based on `{level}`, write the seed entry.
175
+
176
+ **Option A: Project uses SeedData classes (SmartStack.app pattern)**
177
+
178
+ If `Navigation{Level}SeedData.cs` exists, add the new entity:
179
+
180
+ ```csharp
181
+ // In Infrastructure/Persistence/Seeding/Data/Navigation/Navigation{Level}SeedData.cs
182
+
183
+ // Add static GUID field
184
+ public static readonly Guid {PascalCode}Id = Guid.Parse("{navigation_guid}");
185
+
186
+ // Add to GetSeedData() return array
187
+ new {
188
+ Id = {PascalCode}Id,
189
+ ParentFk = Navigation{ParentLevel}SeedData.{ParentPascalCode}Id, // FK varies by level
190
+ Code = "{code}",
191
+ Label = "{labels.en}",
192
+ Description = "{descriptions.en}",
193
+ Icon = "{icon}",
194
+ IconType = IconType.Lucide,
195
+ Route = "/{full_path_with_slashes}",
196
+ DisplayOrder = {display_order},
197
+ IsActive = true,
198
+ CreatedAt = SeedConstants.SeedDate
199
+ }
200
+ ```
201
+
202
+ **FK property by level:**
203
+
204
+ | Level | FK Property | References |
205
+ |-------|-------------|------------|
206
+ | context | (none) | - |
207
+ | application | `ContextId` | NavigationContextSeedData.{Parent}Id |
208
+ | module | `ApplicationId` | NavigationApplicationSeedData.{Parent}Id |
209
+ | section | `ModuleId` | NavigationModuleSeedData.{Parent}Id |
210
+
211
+ **Option B: Project uses inline HasData**
212
+
213
+ If no SeedData class exists, add directly to `Navigation{Level}Configuration.cs`:
214
+
215
+ ```csharp
216
+ // In Configure method, add:
217
+ builder.HasData(new {
218
+ Id = Guid.Parse("{navigation_guid}"),
219
+ // ... same properties as Option A
220
+ });
221
+ ```
222
+
223
+ ### F5. Write Translation Entries
224
+
225
+ Add 4 translation entries to `NavigationTranslationConfiguration.cs`:
226
+
227
+ 1. Read the file to find the current highest `index` value
228
+ 2. Continue from `index + 1`
229
+ 3. Use the SAME `GenerateGuid` method already defined in the file:
230
+
231
+ ```csharp
232
+ // In GetSeedData() method, add at the end before return:
233
+
234
+ // {level}: {code} translations
235
+ translations.Add(new {
236
+ Id = GenerateGuid(index++),
237
+ EntityType = NavigationEntityType.{Level},
238
+ EntityId = Navigation{Level}SeedData.{PascalCode}Id, // or Guid.Parse("{navigation_guid}")
239
+ LanguageCode = "fr",
240
+ Label = "{labels.fr}",
241
+ Description = "{descriptions.fr}",
242
+ CreatedAt = seedDate
243
+ });
244
+ translations.Add(new {
245
+ Id = GenerateGuid(index++),
246
+ EntityType = NavigationEntityType.{Level},
247
+ EntityId = Navigation{Level}SeedData.{PascalCode}Id,
248
+ LanguageCode = "en",
249
+ Label = "{labels.en}",
250
+ Description = "{descriptions.en}",
251
+ CreatedAt = seedDate
252
+ });
253
+ translations.Add(new {
254
+ Id = GenerateGuid(index++),
255
+ EntityType = NavigationEntityType.{Level},
256
+ EntityId = Navigation{Level}SeedData.{PascalCode}Id,
257
+ LanguageCode = "it",
258
+ Label = "{labels.it}",
259
+ Description = "{descriptions.it}",
260
+ CreatedAt = seedDate
261
+ });
262
+ translations.Add(new {
263
+ Id = GenerateGuid(index++),
264
+ EntityType = NavigationEntityType.{Level},
265
+ EntityId = Navigation{Level}SeedData.{PascalCode}Id,
266
+ LanguageCode = "de",
267
+ Label = "{labels.de}",
268
+ Description = "{descriptions.de}",
269
+ CreatedAt = seedDate
270
+ });
271
+ ```
272
+
273
+ ### F6. Store Result
274
+
275
+ ```
276
+ {navigation_guid} = [generated GUID]
277
+ {seed_method} = "fallback" // Indicates MCP was not used
278
+ ```
279
+
280
+ ### F7. Validation Checklist
281
+
282
+ Before proceeding, verify:
283
+ - [ ] Deterministic GUID generated (not NewGuid())
284
+ - [ ] 4 languages present (fr, en, it, de)
285
+ - [ ] Translation index continues existing sequence (no gaps, no collisions)
286
+ - [ ] Parent GUID correctly references existing entity
287
+ - [ ] Route path matches `/{context}/{app}/{module}` pattern
288
+ - [ ] DisplayOrder is consistent with existing entities
289
+ - [ ] Code is WRITTEN to files, not just displayed
290
+
291
+ ### F8. Present Summary
292
+
293
+ ```markdown
294
+ ## Navigation Seeds Generated (Fallback)
295
+
296
+ **Entity:** {level} - {code}
297
+ **GUID:** {navigation_guid}
298
+ **Path:** {full_path}
299
+
300
+ ### Files Updated
301
+
302
+ 1. **Navigation{Level}SeedData.cs** (or Configuration.cs) - New entity entry
303
+ 2. **NavigationTranslationConfiguration.cs** - 4 translation entries added
304
+ ```
123
305
 
124
306
  ---
125
307
 
126
308
  ## SUCCESS METRICS
127
309
 
128
- - MCP scaffold_navigation called successfully
129
- - Navigation entity GUID obtained
130
- - HasData code displayed to user
131
- - Translation code displayed (4 languages)
310
+ - Navigation entity GUID obtained (via MCP or fallback)
311
+ - HasData code WRITTEN to Configuration files
312
+ - Translation code WRITTEN (4 languages)
313
+ - `{navigation_guid}` stored for step-02
132
314
  - Proceeded to step-02-permissions.md
133
315
 
134
316
  ## FAILURE MODES
135
317
 
136
- - MCP call failed (display error, stop)
137
318
  - Missing parent path for non-context level (return to step-00)
138
319
  - Invalid level (return to step-00)
320
+ - Parent entity not found in existing seeds (ask user for parent GUID)
139
321
 
140
322
  ---
141
323
 
142
324
  ## NEXT STEP
143
325
 
144
- After displaying navigation seeds, proceed to `./step-02-permissions.md`
326
+ After navigation seeds are generated (via MCP or fallback) and written to files,
327
+ proceed to `./step-02-permissions.md`
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: step-03-roles
3
- description: Generate role-permission mappings using MCP scaffold_role_permissions
3
+ description: Generate 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-04-backend.md
6
6
  ---
@@ -9,16 +9,18 @@ next_step: steps/step-04-backend.md
9
9
 
10
10
  ## MANDATORY EXECUTION RULES
11
11
 
12
- - ALWAYS use MCP `scaffold_role_permissions` tool - NEVER use templates
12
+ - PREFER MCP `scaffold_role_permissions` tool as the primary method
13
+ - If MCP is unavailable or the call fails, use the FALLBACK PROCEDURE below
13
14
  - ALWAYS assign permissions to default roles
14
15
  - NEVER leave permissions without role assignments
15
- - YOU ARE AN ORCHESTRATOR calling MCP, not a generator
16
+ - ALWAYS WRITE generated code to the actual RolePermissionConfiguration.cs file
16
17
 
17
18
  ## YOUR TASK
18
19
 
19
- Call the SmartStack MCP `scaffold_role_permissions` tool to generate:
20
+ Generate role-permission mappings:
20
21
  1. RolePermissionConfiguration.cs HasData() entries
21
- 2. Default role assignments (PlatformAdmin, TenantAdmin, StandardUser)
22
+ 2. Default role assignments (SuperAdmin, PlatformAdmin, TenantAdmin, StandardUser)
23
+ 3. Application-scoped role assignments (Admin, Manager, Contributor, Viewer)
22
24
 
23
25
  ---
24
26
 
@@ -31,10 +33,11 @@ From previous steps:
31
33
  | `{full_path}` | Complete navigation path (navRoute) |
32
34
  | `{level}` | context, application, module, or section |
33
35
  | `{permission_guids}` | GUIDs for generated permissions |
36
+ | `{mcp_available}` | Boolean - MCP connectivity status |
34
37
 
35
38
  ---
36
39
 
37
- ## EXECUTION SEQUENCE
40
+ ## EXECUTION SEQUENCE (MCP Primary)
38
41
 
39
42
  ### 1. Determine Default Role Assignments
40
43
 
@@ -66,30 +69,24 @@ The tool returns:
66
69
  - Permission ID variable references
67
70
  - Role ID variable references
68
71
 
69
- ### 4. Present Output to User
72
+ ### 4. Write Code to Files
73
+
74
+ **CRITICAL:** WRITE the generated code to the actual RolePermissionConfiguration.cs file.
75
+
76
+ ### 5. Present Summary
70
77
 
71
78
  ```markdown
72
79
  ## Role-Permission Mappings
73
80
 
74
- ### Assigned Permissions
75
-
76
81
  | Role | Permissions |
77
82
  |------|-------------|
78
83
  | SuperAdmin | `{full_path}.*` (via wildcard) |
79
84
  | PlatformAdmin | `{full_path}.read`, `.create`, `.update`, `.delete` |
80
85
  | TenantAdmin | `{full_path}.read`, `.create`, `.update` |
81
86
  | StandardUser | `{full_path}.read` |
82
-
83
- ### RolePermissionConfiguration.cs HasData
84
-
85
- Add to `Infrastructure/Persistence/Configurations/RolePermissionConfiguration.cs`:
86
-
87
- [Show HasData entries from MCP response]
88
87
  ```
89
88
 
90
- ### 5. Confirm with User (Optional)
91
-
92
- If the default role assignments don't match the user's needs:
89
+ ### 6. Confirm with User (Optional)
93
90
 
94
91
  ```yaml
95
92
  questions:
@@ -107,9 +104,43 @@ questions:
107
104
 
108
105
  ---
109
106
 
110
- ## DEFAULT ROLE GUIDS
107
+ ## MCP RESPONSE HANDLING
111
108
 
112
- SmartStack uses well-known GUIDs for default roles:
109
+ ### Success Case
110
+
111
+ If MCP returns successfully:
112
+ - Write RolePermission HasData code to file
113
+ - Show role-permission summary table
114
+ - Proceed to step-04-backend.md
115
+
116
+ ### Error Case
117
+
118
+ If MCP call fails or `{mcp_available}` = false:
119
+ - Log the error for reference
120
+ - Proceed to FALLBACK PROCEDURE below
121
+ - Do NOT stop the workflow
122
+
123
+ ---
124
+
125
+ ## FALLBACK PROCEDURE (When MCP Unavailable)
126
+
127
+ > This procedure generates role-permission HasData entries following SmartStack.app patterns.
128
+
129
+ ### F1. Read Existing RolePermissionConfiguration.cs
130
+
131
+ ```
132
+ Glob: **/Persistence/Configurations/Authorization/RolePermissionConfiguration.cs
133
+ ```
134
+
135
+ Read the file to determine:
136
+ - Existing role-permission mappings
137
+ - The GetSeedData() method structure
138
+ - Which roles already have which permissions
139
+ - The GUID generation method used (deterministic or hardcoded)
140
+
141
+ ### F2. Read Role GUIDs
142
+
143
+ **System-level roles** (well-known GUIDs):
113
144
 
114
145
  | Role | GUID |
115
146
  |------|------|
@@ -118,41 +149,132 @@ SmartStack uses well-known GUIDs for default roles:
118
149
  | TenantAdmin | `33333333-3333-3333-3333-333333333333` |
119
150
  | StandardUser | `44444444-4444-4444-4444-444444444444` |
120
151
 
121
- ---
152
+ **IMPORTANT:** Read the actual `RoleSeedData.cs` or `RoleConfiguration.cs` in the target project to confirm the actual role GUIDs. The above are defaults; the project may use different values.
153
+
154
+ **Application-scoped roles** (deterministic GUIDs based on application):
155
+
156
+ ```csharp
157
+ // Read the existing GenerateDeterministicGuid method in RolePermissionConfiguration.cs
158
+ // Typically uses MD5 hash:
159
+ private static Guid GenerateDeterministicGuid(Guid applicationId, string roleType)
160
+ {
161
+ using var md5 = System.Security.Cryptography.MD5.Create();
162
+ var input = $"{applicationId}-{roleType}";
163
+ var hash = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(input));
164
+ return new Guid(hash);
165
+ }
166
+ // roleType values: "admin", "manager", "contributor", "viewer"
167
+ ```
122
168
 
123
- ## MCP RESPONSE HANDLING
169
+ Find the `applicationId` from `NavigationApplicationSeedData.cs` matching `{full_path}`.
124
170
 
125
- ### Success Case
171
+ ### F3. Determine Context-Based Default Mappings
126
172
 
127
- If MCP returns successfully:
128
- - Display RolePermission HasData code
129
- - Show role-permission summary table
130
- - Proceed to step-04-backend.md
173
+ Based on `{full_path}` prefix:
131
174
 
132
- ### Error Case
175
+ | Context Prefix | SuperAdmin | PlatformAdmin | App Admin | App Manager | App Contributor | App Viewer |
176
+ |----------------|------------|---------------|-----------|-------------|-----------------|------------|
177
+ | `platform.*` | wildcard | Full CRUD | Full CRUD | CRU | CR | R |
178
+ | `business.*` | wildcard | Full CRUD | Full CRUD | CRU | CR | R |
179
+ | `personal.*` | wildcard | None | Full CRUD | CRU | CR | R |
180
+
181
+ ### F4. Generate RolePermission HasData Entries
182
+
183
+ Using `{permission_guids}` from step-02:
184
+
185
+ ```csharp
186
+ // In RolePermissionConfiguration.cs - GetSeedData() method
187
+ var seedDate = SeedConstants.SeedDate;
188
+
189
+ // ============================================================
190
+ // {MODULE_NAME} PERMISSIONS
191
+ // ============================================================
192
+
193
+ // SuperAdmin: already has *.* wildcard - no individual entries needed
194
+
195
+ // PlatformAdmin (for platform.* context)
196
+ rolePermissions.Add(new { RoleId = platformAdminRoleId, PermissionId = {permission_guids.read}, AssignedAt = seedDate });
197
+ rolePermissions.Add(new { RoleId = platformAdminRoleId, PermissionId = {permission_guids.create}, AssignedAt = seedDate });
198
+ rolePermissions.Add(new { RoleId = platformAdminRoleId, PermissionId = {permission_guids.update}, AssignedAt = seedDate });
199
+ rolePermissions.Add(new { RoleId = platformAdminRoleId, PermissionId = {permission_guids.delete}, AssignedAt = seedDate });
200
+
201
+ // Application-scoped: Admin → wildcard
202
+ rolePermissions.Add(new { RoleId = appAdminRoleId, PermissionId = {permission_guids.wildcard}, AssignedAt = seedDate });
203
+
204
+ // Application-scoped: Manager → CRUD
205
+ rolePermissions.Add(new { RoleId = appManagerRoleId, PermissionId = {permission_guids.read}, AssignedAt = seedDate });
206
+ rolePermissions.Add(new { RoleId = appManagerRoleId, PermissionId = {permission_guids.create}, AssignedAt = seedDate });
207
+ rolePermissions.Add(new { RoleId = appManagerRoleId, PermissionId = {permission_guids.update}, AssignedAt = seedDate });
208
+
209
+ // Application-scoped: Contributor → CR
210
+ rolePermissions.Add(new { RoleId = appContributorRoleId, PermissionId = {permission_guids.read}, AssignedAt = seedDate });
211
+ rolePermissions.Add(new { RoleId = appContributorRoleId, PermissionId = {permission_guids.create}, AssignedAt = seedDate });
212
+
213
+ // Application-scoped: Viewer → R
214
+ rolePermissions.Add(new { RoleId = appViewerRoleId, PermissionId = {permission_guids.read}, AssignedAt = seedDate });
215
+ ```
216
+
217
+ ### F5. Write Code to RolePermissionConfiguration.cs
218
+
219
+ **CRITICAL:** Do NOT just display code. WRITE it to the actual file.
220
+
221
+ 1. Open `RolePermissionConfiguration.cs`
222
+ 2. Find the `GetSeedData()` method
223
+ 3. Add the new role-permission entries to the list
224
+ 4. Add necessary permission GUID references (import from PermissionConfiguration or use inline)
225
+ 5. Add comments grouping the new entries: `// {MODULE_NAME} PERMISSIONS`
226
+
227
+ ### F6. Present Summary
228
+
229
+ ```markdown
230
+ ## Role-Permission Mappings Generated (Fallback)
231
+
232
+ | Role | Permissions |
233
+ |------|-------------|
234
+ | SuperAdmin | Already has wildcard access |
235
+ | PlatformAdmin | {full_path}.read, .create, .update, .delete |
236
+ | App Admin | {full_path}.* (wildcard) |
237
+ | App Manager | {full_path}.read, .create, .update |
238
+ | App Contributor | {full_path}.read, .create |
239
+ | App Viewer | {full_path}.read |
240
+
241
+ Written to: RolePermissionConfiguration.cs
242
+ ```
243
+
244
+ ### F7. Offer User Adjustment
245
+
246
+ ```yaml
247
+ questions:
248
+ - header: "Role Access"
249
+ question: "Default role-permission mappings have been applied. Adjust?"
250
+ options:
251
+ - label: "Keep defaults (Recommended)"
252
+ description: "Standard role hierarchy applied"
253
+ - label: "Custom adjustments"
254
+ description: "I want to change specific role permissions"
255
+ multiSelect: false
256
+ ```
133
257
 
134
- If MCP call fails:
135
- - Display error message
136
- - Suggest checking permission GUIDs
137
- - Provide manual template as fallback
258
+ If user selects "Custom adjustments", ask which roles/permissions to change and update the file accordingly.
138
259
 
139
260
  ---
140
261
 
141
262
  ## SUCCESS METRICS
142
263
 
143
- - MCP scaffold_role_permissions called successfully
144
- - Role-permission mappings displayed
264
+ - Role-permission mappings generated (via MCP or fallback)
265
+ - RolePermissionConfiguration.cs WRITTEN with new entries
145
266
  - All default roles have appropriate access
146
267
  - Proceeded to step-04-backend.md
147
268
 
148
269
  ## FAILURE MODES
149
270
 
150
- - MCP call failed (display error, suggest fallback)
151
- - Permission GUIDs not available (return to step-02)
152
- - Invalid navRoute (return to step-00)
271
+ - Permission GUIDs not available from step-02 (return to step-02)
272
+ - Role GUIDs not found in project (ask user, use well-known defaults)
273
+ - Invalid navRoute format (return to step-00)
153
274
 
154
275
  ---
155
276
 
156
277
  ## NEXT STEP
157
278
 
158
- After displaying role-permission mappings, proceed to `./step-04-backend.md`
279
+ After role-permission mappings are generated (via MCP or fallback) and written to files,
280
+ proceed to `./step-04-backend.md`
@@ -124,7 +124,110 @@ The tool generates (paths organized by navRoute hierarchy `{context}.{applicatio
124
124
  3. Run: `dotnet ef migrations add core_vX.X.X_XXX_Add{EntityName}`
125
125
  ```
126
126
 
127
- ### 6. Store Entity Info
127
+ ### 6. Entity Seeding (Optional)
128
+
129
+ > Ask the user if they want to seed initial data for the entity.
130
+ > This creates runtime seed data (applied at startup via DevDataSeeder), following the same
131
+ > architecture as core (SeedData provider pattern).
132
+
133
+ ```yaml
134
+ questions:
135
+ - header: "Seed Data"
136
+ question: "Would you like to generate initial seed data for {EntityName}?"
137
+ options:
138
+ - label: "Yes - Generate SeedData provider"
139
+ description: "Creates {EntityName}SeedData.cs + registers in DevDataSeeder (same pattern as core)"
140
+ - label: "No - Skip seeding"
141
+ description: "Entity starts empty (can add seed data later)"
142
+ multiSelect: false
143
+ ```
144
+
145
+ #### If User Selects YES:
146
+
147
+ **Reference:** See `templates-seed.md` section "TEMPLATE: ENTITY SEED DATA (SeedData Provider)"
148
+
149
+ **Generate the following files:**
150
+
151
+ 1. **`Infrastructure/Persistence/Seeding/Data/{Domain}/{EntityName}SeedData.cs`**
152
+
153
+ Follow the SmartStack.app pattern (same architecture as core):
154
+ - Static class with `internal static IEnumerable<{EntityName}SeedItem> GetAll{EntityName}s()`
155
+ - Use deterministic GUIDs (NEVER `Guid.NewGuid()`)
156
+ - Include 3-5 sample entities with varied, realistic data
157
+ - Internal record class `{EntityName}SeedItem` with all entity properties
158
+ - For multi-tenant entities: organize by tenant using `GetTenant1{EntityName}s()` helper methods
159
+ - Reference existing tenant/user IDs from `TenantSeedData`/`UserSeedData` for foreign keys
160
+
161
+ ```csharp
162
+ // Infrastructure/Persistence/Seeding/Data/{Domain}/{EntityName}SeedData.cs
163
+
164
+ public static class {EntityName}SeedData
165
+ {
166
+ public static readonly Guid Sample1Id = Guid.Parse("...");
167
+ public static readonly Guid Sample2Id = Guid.Parse("...");
168
+ public static readonly Guid Sample3Id = Guid.Parse("...");
169
+
170
+ internal static IEnumerable<{EntityName}SeedItem> GetAll{EntityName}s()
171
+ {
172
+ return new[]
173
+ {
174
+ new {EntityName}SeedItem { Id = Sample1Id, /* ... */ },
175
+ new {EntityName}SeedItem { Id = Sample2Id, /* ... */ },
176
+ new {EntityName}SeedItem { Id = Sample3Id, /* ... */ }
177
+ };
178
+ }
179
+ }
180
+
181
+ internal class {EntityName}SeedItem
182
+ {
183
+ public Guid Id { get; init; }
184
+ // ... all required entity properties
185
+ }
186
+ ```
187
+
188
+ 2. **Update `Infrastructure/Persistence/Seeding/DevDataSeeder.cs`**
189
+ - Add `using` for the new SeedData namespace
190
+ - Add `await Seed{EntityName}sAsync(cancellationToken);` in `SeedAsync()` method
191
+ - Add private seeding method:
192
+
193
+ ```csharp
194
+ private async Task Seed{EntityName}sAsync(CancellationToken cancellationToken)
195
+ {
196
+ _logger.LogInformation("Seeding demo {EntityName}s...");
197
+
198
+ var existingCount = await _context.{EntityName}s.CountAsync(cancellationToken);
199
+ if (existingCount > 0)
200
+ {
201
+ _logger.LogWarning("{EntityName}s already seeded ({Count} exist), skipping.", existingCount);
202
+ return;
203
+ }
204
+
205
+ var createdCount = 0;
206
+ foreach (var seedItem in {EntityName}SeedData.GetAll{EntityName}s())
207
+ {
208
+ var entity = {EntityName}.Create(/* map seedItem properties */);
209
+ typeof({EntityName}).GetProperty("Id")?.SetValue(entity, seedItem.Id);
210
+ _context.{EntityName}s.Add(entity);
211
+ createdCount++;
212
+ }
213
+
214
+ if (createdCount > 0)
215
+ await _context.SaveChangesAsync(cancellationToken);
216
+
217
+ _logger.LogInformation("Created {Count} demo {EntityName}s.", createdCount);
218
+ }
219
+ ```
220
+
221
+ #### If User Selects NO:
222
+
223
+ Skip this section and proceed to storing entity info.
224
+
225
+ ```markdown
226
+ > Skipping entity seed data. You can generate it later using the SeedData provider pattern
227
+ > documented in templates-seed.md.
228
+ ```
229
+
230
+ ### 7. Store Entity Info
128
231
 
129
232
  Store entity information for frontend generation:
130
233
 
@@ -132,6 +235,7 @@ Store entity information for frontend generation:
132
235
  {entity_name} = "{EntityName}"
133
236
  {entity_code} = "{code}"
134
237
  {api_route} = "/api/{code}"
238
+ {has_seed_data} = true/false // Whether SeedData provider was generated
135
239
  ```
136
240
 
137
241
  ---
@@ -168,6 +272,7 @@ This ensures:
168
272
  If MCP returns successfully:
169
273
  - Display all generated files
170
274
  - Show DbSet and DI registration instructions
275
+ - Ask about entity seeding (section 6)
171
276
  - Store entity info for frontend
172
277
  - Proceed to step-05-frontend.md
173
278
 
@@ -186,6 +291,7 @@ If MCP call fails:
186
291
  - Entity, Service, Controller generated
187
292
  - DTOs generated
188
293
  - NavRoute attribute included
294
+ - Entity seeding offered to user (SeedData.cs + DevDataSeeder if accepted)
189
295
  - Entity info stored for frontend
190
296
  - Proceeded to step-05-frontend.md
191
297
 
@@ -199,4 +305,5 @@ If MCP call fails:
199
305
 
200
306
  ## NEXT STEP
201
307
 
202
- After displaying backend code, proceed to `./step-05-frontend.md`
308
+ After backend code is generated and entity seeding is handled,
309
+ proceed to `./step-05-frontend.md`
@@ -605,12 +605,209 @@ $ORDER = 1
605
605
 
606
606
  ---
607
607
 
608
+ ## TEMPLATE: ENTITY SEED DATA (SeedData Provider)
609
+
610
+ > **Usage:** Runtime seed data for domain entities (Products, Orders, Tickets, etc.)
611
+ > **When:** User wants initial data for development/testing
612
+ > **Mechanism:** Loaded at startup by DevDataSeeder, NOT via EF Core HasData/migrations
613
+ > **Architecture:** Identical in core AND extensions
614
+
615
+ ### Two Seeding Mechanisms
616
+
617
+ | Mechanism | When to Use | Applied Via |
618
+ |-----------|-------------|-------------|
619
+ | **HasData()** in Configuration.cs | System entities (Navigation, Permissions, Roles) | EF Core migrations |
620
+ | **SeedData.cs + DevDataSeeder** | Domain entities (Tickets, Products, Orders) | Application startup |
621
+
622
+ **Rule:** Navigation, Permission, and RolePermission seeds use HasData().
623
+ Domain entity seeds use the SeedData provider pattern below.
624
+
625
+ ### SeedData Class Pattern
626
+
627
+ ```csharp
628
+ // Infrastructure/Persistence/Seeding/Data/{Domain}/{EntityName}SeedData.cs
629
+
630
+ using SmartStack.Domain.{Context}.{Application}.{Module};
631
+
632
+ namespace SmartStack.Infrastructure.Persistence.Seeding.Data.{Domain};
633
+
634
+ /// <summary>
635
+ /// Demo {EntityName} data for development and testing.
636
+ /// </summary>
637
+ public static class {EntityName}SeedData
638
+ {
639
+ // ============================================================
640
+ // DETERMINISTIC IDs - for referencing in other seed data
641
+ // ============================================================
642
+
643
+ public static readonly Guid Sample1Id = Guid.Parse("$SEED_GUID_1");
644
+ public static readonly Guid Sample2Id = Guid.Parse("$SEED_GUID_2");
645
+ public static readonly Guid Sample3Id = Guid.Parse("$SEED_GUID_3");
646
+
647
+ /// <summary>
648
+ /// Returns all demo {EntityName} entities.
649
+ /// </summary>
650
+ internal static IEnumerable<{EntityName}SeedItem> GetAll{EntityName}s()
651
+ {
652
+ return new[]
653
+ {
654
+ new {EntityName}SeedItem
655
+ {
656
+ Id = Sample1Id,
657
+ // ... entity properties with realistic sample data
658
+ CreatedByUserId = UserSeedData.SystemUserId
659
+ },
660
+ new {EntityName}SeedItem
661
+ {
662
+ Id = Sample2Id,
663
+ // ... varied data
664
+ CreatedByUserId = UserSeedData.SystemUserId
665
+ },
666
+ new {EntityName}SeedItem
667
+ {
668
+ Id = Sample3Id,
669
+ // ... varied data
670
+ CreatedByUserId = UserSeedData.SystemUserId
671
+ }
672
+ };
673
+ }
674
+ }
675
+
676
+ /// <summary>
677
+ /// Seed item DTO for {EntityName} (avoids domain factory methods during seeding).
678
+ /// </summary>
679
+ internal class {EntityName}SeedItem
680
+ {
681
+ public Guid Id { get; init; }
682
+ // ... all required properties matching the domain entity
683
+ public Guid CreatedByUserId { get; init; }
684
+ }
685
+ ```
686
+
687
+ ### Multi-Tenant Entity Seed Pattern
688
+
689
+ For entities belonging to a tenant, organize by tenant:
690
+
691
+ ```csharp
692
+ internal static IEnumerable<{EntityName}SeedItem> GetAll{EntityName}s()
693
+ {
694
+ var items = new List<{EntityName}SeedItem>();
695
+
696
+ items.AddRange(GetTenant1{EntityName}s());
697
+ items.AddRange(GetTenant2{EntityName}s());
698
+
699
+ return items;
700
+ }
701
+
702
+ private static IEnumerable<{EntityName}SeedItem> GetTenant1{EntityName}s()
703
+ {
704
+ var tenantId = TenantSeedData.AcmeCorporationId;
705
+ var userId = TenantMembershipSeedData.AcmeOwnerId;
706
+
707
+ return new[]
708
+ {
709
+ new {EntityName}SeedItem
710
+ {
711
+ Id = Guid.Parse("$GUID"),
712
+ TenantId = tenantId,
713
+ CreatedByUserId = userId,
714
+ // ... properties
715
+ }
716
+ };
717
+ }
718
+ ```
719
+
720
+ ### DevDataSeeder Registration Pattern
721
+
722
+ ```csharp
723
+ // In Infrastructure/Persistence/Seeding/DevDataSeeder.cs
724
+
725
+ // 1. Add using statement
726
+ using SmartStack.Infrastructure.Persistence.Seeding.Data.{Domain};
727
+
728
+ // 2. Add call in SeedAsync() method (after existing seed calls)
729
+ public async Task SeedAsync(CancellationToken cancellationToken = default)
730
+ {
731
+ // ... existing seed calls ...
732
+
733
+ await Seed{EntityName}sAsync(cancellationToken); // <-- ADD THIS
734
+
735
+ // ... summary logging ...
736
+ }
737
+
738
+ // 3. Add private seeding method
739
+ private async Task Seed{EntityName}sAsync(CancellationToken cancellationToken)
740
+ {
741
+ _logger.LogInformation("Seeding demo {EntityName}s...");
742
+
743
+ var existingCount = await _context.{EntityName}s.CountAsync(cancellationToken);
744
+ if (existingCount > 0)
745
+ {
746
+ _logger.LogWarning("{EntityName}s already seeded ({Count} exist), skipping.", existingCount);
747
+ return;
748
+ }
749
+
750
+ var createdCount = 0;
751
+
752
+ foreach (var seedItem in {EntityName}SeedData.GetAll{EntityName}s())
753
+ {
754
+ var entity = {EntityName}.Create(
755
+ // Map seedItem properties to factory method parameters
756
+ );
757
+
758
+ // Set deterministic ID (factory generates random GUID)
759
+ typeof({EntityName}).GetProperty("Id")?.SetValue(entity, seedItem.Id);
760
+
761
+ _context.{EntityName}s.Add(entity);
762
+ createdCount++;
763
+ }
764
+
765
+ if (createdCount > 0)
766
+ {
767
+ await _context.SaveChangesAsync(cancellationToken);
768
+ }
769
+
770
+ _logger.LogInformation("Created {Count} demo {EntityName}s.", createdCount);
771
+ }
772
+ ```
773
+
774
+ ### Seed Data GUID Generation
775
+
776
+ ```csharp
777
+ // Option 1: Hardcoded descriptive GUIDs (preferred for small sets)
778
+ public static readonly Guid ProductAlphaId = Guid.Parse("aa000001-0000-0000-0000-000000000001");
779
+ public static readonly Guid ProductBetaId = Guid.Parse("aa000001-0000-0000-0000-000000000002");
780
+
781
+ // Option 2: SHA256-based for larger sets
782
+ private static Guid GenerateEntityGuid(string uniqueKey)
783
+ {
784
+ using var sha256 = System.Security.Cryptography.SHA256.Create();
785
+ var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes($"{EntityName}-{uniqueKey}"));
786
+ return new Guid(hash.Take(16).ToArray());
787
+ }
788
+ ```
789
+
790
+ ### Best Practices
791
+
792
+ | Practice | Description |
793
+ |----------|-------------|
794
+ | Deterministic IDs | NEVER use `Guid.NewGuid()` - seeds must be idempotent |
795
+ | Idempotent | Always check `if exists` before creating |
796
+ | Use factory methods | Call `Entity.Create(...)` not `new Entity()` |
797
+ | Set ID via reflection | Factory methods generate random IDs; override after creation |
798
+ | Realistic data | Use plausible names, descriptions, amounts |
799
+ | Cover all states | Include entities in different statuses (active, closed, etc.) |
800
+ | Reference existing seeds | Use IDs from TenantSeedData, UserSeedData for foreign keys |
801
+ | SaveChanges per batch | Call SaveChanges after each entity group |
802
+
803
+ ---
804
+
608
805
  ## SEED CHECKLIST
609
806
 
610
807
  | Check | Status |
611
808
  |-------|--------|
612
809
  | ☐ Deterministic GUID (not NewGuid) | |
613
- | ☐ 4 languages for each entity | |
810
+ | ☐ 4 languages for each navigation entity | |
614
811
  | ☐ Index translations continue existing sequence | |
615
812
  | ☐ Route aligned with permission path | |
616
813
  | ☐ DisplayOrder consistent | |
@@ -619,6 +816,8 @@ $ORDER = 1
619
816
  | ☐ Resource permissions if sub-resources (Level 5) | |
620
817
  | ☐ Bulk Operations permissions created | |
621
818
  | ☐ RolePermissions assigned | |
819
+ | ☐ Entity SeedData.cs created (if user opted in) | |
820
+ | ☐ DevDataSeeder.cs updated (if user opted in) | |
622
821
 
623
822
  ---
624
823