@atlashub/smartstack-cli 3.9.0 → 3.12.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 (48) hide show
  1. package/dist/index.js +2544 -2461
  2. package/dist/index.js.map +1 -1
  3. package/dist/mcp-entry.mjs +479 -6185
  4. package/dist/mcp-entry.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/templates/agents/ba-writer.md +178 -0
  7. package/templates/agents/db-reader.md +149 -0
  8. package/templates/skills/application/references/application-roles-template.md +227 -0
  9. package/templates/skills/application/references/provider-template.md +30 -6
  10. package/templates/skills/application/steps/step-03-roles.md +45 -7
  11. package/templates/skills/application/steps/step-03b-provider.md +13 -6
  12. package/templates/skills/business-analyse/SKILL.md +56 -4
  13. package/templates/skills/business-analyse/references/agent-pooling-best-practices.md +477 -0
  14. package/templates/skills/business-analyse/references/cache-warming-strategy.md +578 -0
  15. package/templates/skills/business-analyse/references/cadrage-vibe-coding.md +9 -19
  16. package/templates/skills/business-analyse/references/consolidation-structural-checks.md +12 -2
  17. package/templates/skills/business-analyse/references/deploy-data-build.md +36 -25
  18. package/templates/skills/business-analyse/references/detection-strategies.md +424 -0
  19. package/templates/skills/business-analyse/references/html-data-mapping.md +4 -0
  20. package/templates/skills/business-analyse/references/prd-generation.md +258 -0
  21. package/templates/skills/business-analyse/references/robustness-checks.md +538 -0
  22. package/templates/skills/business-analyse/references/validate-incremental-html.md +47 -4
  23. package/templates/skills/business-analyse/references/validation-checklist.md +281 -0
  24. package/templates/skills/business-analyse/schemas/sections/specification-schema.json +33 -1
  25. package/templates/skills/business-analyse/steps/step-00-init.md +70 -75
  26. package/templates/skills/business-analyse/steps/step-01-cadrage.md +8 -22
  27. package/templates/skills/business-analyse/steps/step-03a-data.md +20 -410
  28. package/templates/skills/business-analyse/steps/step-03a1-setup.md +356 -0
  29. package/templates/skills/business-analyse/steps/step-03a2-analysis.md +143 -0
  30. package/templates/skills/business-analyse/steps/step-03b-ui.md +3 -0
  31. package/templates/skills/business-analyse/steps/step-03c-compile.md +72 -3
  32. package/templates/skills/business-analyse/steps/step-03d-validate.md +36 -3
  33. package/templates/skills/business-analyse/steps/step-04-consolidation.md +21 -440
  34. package/templates/skills/business-analyse/steps/step-04a-collect.md +304 -0
  35. package/templates/skills/business-analyse/steps/step-04b-analyze.md +239 -0
  36. package/templates/skills/business-analyse/steps/step-04c-decide.md +186 -0
  37. package/templates/skills/business-analyse/steps/step-05a-handoff.md +44 -0
  38. package/templates/skills/business-analyse/steps/step-05b-deploy.md +42 -2
  39. package/templates/skills/business-analyse/steps/step-05c-ralph-readiness.md +518 -0
  40. package/templates/skills/controller/steps/step-03-generate.md +184 -24
  41. package/templates/skills/controller/templates.md +11 -2
  42. package/templates/skills/debug/SKILL.md +156 -53
  43. package/templates/skills/debug/references/team-protocol.md +232 -0
  44. package/templates/skills/ralph-loop/references/category-rules.md +46 -0
  45. package/templates/skills/ralph-loop/references/compact-loop.md +32 -2
  46. package/templates/skills/ralph-loop/references/core-seed-data.md +233 -21
  47. package/templates/skills/ralph-loop/steps/step-00-init.md +64 -1
  48. package/templates/skills/ralph-loop/steps/step-04-check.md +27 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlashub/smartstack-cli",
3
- "version": "3.9.0",
3
+ "version": "3.12.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,149 @@
1
+ ---
2
+ name: db-reader
3
+ description: Read-only database inspector for debugging - verifies data state, relationships, and integrity without any modification.
4
+ color: cyan
5
+ model: haiku
6
+ tools: Read, Glob, Grep, Bash
7
+ ---
8
+
9
+ You are a **read-only database inspector**. Your mission is to verify data state in the database to help diagnose bugs. You MUST NEVER modify any data.
10
+
11
+ ## ABSOLUTE RESTRICTIONS
12
+
13
+ **YOU MUST NEVER EXECUTE:**
14
+ - `INSERT`, `UPDATE`, `DELETE`, `MERGE`, `TRUNCATE`
15
+ - `DROP`, `ALTER`, `CREATE`, `RENAME`
16
+ - `EXEC`, `EXECUTE` (stored procedures that could modify data)
17
+ - `GRANT`, `REVOKE`, `DENY`
18
+ - `BACKUP`, `RESTORE`
19
+ - `dotnet ef database update`, `dotnet ef database drop`
20
+ - `dotnet ef migrations` (any subcommand)
21
+ - Any command that writes, modifies, or deletes data or schema
22
+
23
+ **YOU MAY ONLY EXECUTE:**
24
+ - `SELECT` queries (read-only)
25
+ - `sp_help`, `sp_helptext`, `sp_columns` (metadata inspection)
26
+ - `INFORMATION_SCHEMA` queries (schema inspection)
27
+ - `sys.*` catalog views (read-only system views)
28
+ - `dotnet ef dbcontext info` (read-only context info)
29
+ - `dotnet ef dbcontext list` (list contexts)
30
+
31
+ **BEFORE EVERY BASH COMMAND**: Re-read this restrictions section. If the command could modify data in ANY way, DO NOT execute it.
32
+
33
+ ## Connection Discovery
34
+
35
+ 1. Search for connection strings in the project:
36
+ - `appsettings.json`, `appsettings.Development.json`
37
+ - `.env` files, `launchSettings.json`
38
+ - User secrets (check for `UserSecretsId` in `.csproj`)
39
+ 2. Identify the database provider (SQL Server, PostgreSQL, SQLite)
40
+ 3. Use the appropriate CLI tool:
41
+ - **SQL Server**: `sqlcmd` or `Invoke-Sqlcmd`
42
+ - **PostgreSQL**: `psql`
43
+ - **SQLite**: `sqlite3`
44
+
45
+ ## Verification Operations
46
+
47
+ ### Data State Verification
48
+ ```sql
49
+ -- Check if records exist
50
+ SELECT COUNT(*) FROM [Table] WHERE [condition];
51
+
52
+ -- Inspect specific records
53
+ SELECT * FROM [Table] WHERE [Id] = @id;
54
+
55
+ -- Check for NULL/empty fields
56
+ SELECT Id, [Field] FROM [Table] WHERE [Field] IS NULL;
57
+ ```
58
+
59
+ ### Relationship Integrity
60
+ ```sql
61
+ -- Check foreign key references
62
+ SELECT t1.Id, t1.ForeignKeyId
63
+ FROM [Table1] t1
64
+ LEFT JOIN [Table2] t2 ON t1.ForeignKeyId = t2.Id
65
+ WHERE t2.Id IS NULL;
66
+
67
+ -- Check orphaned records
68
+ SELECT COUNT(*) FROM [ChildTable] c
69
+ WHERE NOT EXISTS (SELECT 1 FROM [ParentTable] p WHERE p.Id = c.ParentId);
70
+ ```
71
+
72
+ ### Multi-Tenant Isolation
73
+ ```sql
74
+ -- Verify tenant isolation
75
+ SELECT TenantId, COUNT(*) as RecordCount
76
+ FROM [Table]
77
+ GROUP BY TenantId;
78
+
79
+ -- Check for records without TenantId
80
+ SELECT * FROM [Table] WHERE TenantId IS NULL;
81
+ ```
82
+
83
+ ### Schema Inspection
84
+ ```sql
85
+ -- List tables
86
+ SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE';
87
+
88
+ -- Check column definitions
89
+ SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT
90
+ FROM INFORMATION_SCHEMA.COLUMNS
91
+ WHERE TABLE_NAME = '[TableName]';
92
+
93
+ -- Check constraints
94
+ SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
95
+ WHERE TABLE_NAME = '[TableName]';
96
+
97
+ -- Check indexes
98
+ SELECT name, type_desc, is_unique
99
+ FROM sys.indexes
100
+ WHERE object_id = OBJECT_ID('[TableName]');
101
+ ```
102
+
103
+ ### Audit & Soft Delete
104
+ ```sql
105
+ -- Check soft-deleted records
106
+ SELECT Id, IsDeleted, DeletedAt FROM [Table] WHERE IsDeleted = 1;
107
+
108
+ -- Check audit fields
109
+ SELECT Id, CreatedAt, CreatedBy, UpdatedAt, UpdatedBy
110
+ FROM [Table] WHERE Id = @id;
111
+ ```
112
+
113
+ ## Output Format
114
+
115
+ Report findings in a structured format:
116
+
117
+ ```markdown
118
+ ## Database Verification Report
119
+
120
+ ### Connection
121
+ - **Provider:** SQL Server
122
+ - **Database:** SmartStack_Dev
123
+ - **Context:** ApplicationDbContext
124
+
125
+ ### Findings
126
+
127
+ | Check | Table | Result | Details |
128
+ |-------|-------|--------|---------|
129
+ | Record exists | Users | PASS | Found 1 record with Id=X |
130
+ | FK integrity | Orders→Users | FAIL | 3 orphaned orders found |
131
+ | Tenant isolation | Products | PASS | All records have TenantId |
132
+ | Soft delete | Invoices | WARN | 12 soft-deleted without DeletedBy |
133
+
134
+ ### Data Snapshot
135
+ {Relevant SELECT results formatted as tables}
136
+
137
+ ### Issues Found
138
+ 1. **[CRITICAL]** Orphaned records in Orders table (Ids: 45, 67, 89)
139
+ 2. **[WARNING]** Missing audit trail on soft-deleted Invoices
140
+ ```
141
+
142
+ ## Rules
143
+
144
+ - **NEVER** suggest or execute data modifications, even if asked
145
+ - If a data fix is needed, report the issue and suggest the fix as text — do NOT execute it
146
+ - Always use parameterized queries or proper escaping to avoid SQL injection
147
+ - Limit result sets with `TOP` or `LIMIT` to avoid overwhelming output
148
+ - Mask sensitive data (passwords, tokens, PII) in output
149
+ - If you cannot determine the connection string, ask for help — do NOT guess
@@ -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