@atlashub/smartstack-cli 3.43.0 → 3.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp-entry.mjs +201 -22
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/agents/efcore/conflicts.md +22 -2
- package/templates/agents/efcore/migration.md +11 -0
- package/templates/agents/efcore/rebase-snapshot.md +7 -0
- package/templates/agents/efcore/scan.md +24 -2
- package/templates/agents/efcore/squash.md +7 -0
- package/templates/agents/gitflow/init.md +195 -12
- package/templates/skills/apex/SKILL.md +14 -9
- package/templates/skills/apex/_shared.md +3 -0
- package/templates/skills/apex/references/analysis-methods.md +1 -1
- package/templates/skills/apex/references/challenge-questions.md +21 -0
- package/templates/skills/apex/references/core-seed-data.md +59 -104
- package/templates/skills/apex/references/post-checks.md +289 -225
- package/templates/skills/apex/references/smartstack-api.md +33 -35
- package/templates/skills/apex/references/smartstack-frontend.md +99 -3
- package/templates/skills/apex/references/smartstack-layers.md +145 -23
- package/templates/skills/apex/steps/step-00-init.md +2 -2
- package/templates/skills/apex/steps/step-01-analyze.md +1 -0
- package/templates/skills/apex/steps/step-02-plan.md +4 -3
- package/templates/skills/apex/steps/step-03-execute.md +24 -24
- package/templates/skills/apex/steps/step-04-examine.md +64 -24
- package/templates/skills/apex/steps/step-05-deep-review.md +1 -1
- package/templates/skills/apex/steps/step-08-run-tests.md +21 -13
- package/templates/skills/application/references/application-roles-template.md +10 -15
- package/templates/skills/application/references/backend-entity-seeding.md +6 -5
- package/templates/skills/application/references/backend-seeding-and-dto-output.md +1 -1
- package/templates/skills/application/references/nav-fallback-procedure.md +14 -17
- package/templates/skills/application/references/provider-template.md +5 -5
- package/templates/skills/application/references/roles-client-project-handling.md +1 -1
- package/templates/skills/application/references/roles-fallback-procedure.md +10 -15
- package/templates/skills/application/steps/step-01-navigation.md +1 -1
- package/templates/skills/application/steps/step-02-permissions.md +3 -3
- package/templates/skills/application/steps/step-03b-provider.md +1 -0
- package/templates/skills/application/templates-seed.md +41 -47
- package/templates/skills/business-analyse/references/team-orchestration.md +2 -2
- package/templates/skills/controller/steps/step-04-perms.md +1 -1
- package/templates/skills/efcore/references/troubleshooting.md +2 -2
- package/templates/skills/efcore/steps/rebase-snapshot/step-00-init.md +2 -2
- package/templates/skills/efcore/steps/squash/step-00-init.md +2 -2
- package/templates/skills/apex/references/examine-build-validation.md +0 -82
- package/templates/skills/apex/references/execution-frontend-gates.md +0 -177
- package/templates/skills/apex/references/execution-frontend-patterns.md +0 -105
- package/templates/skills/apex/references/execution-layer1-rules.md +0 -96
- package/templates/skills/apex/references/initialization-challenge-flow.md +0 -110
- package/templates/skills/apex/references/planning-layer-mapping.md +0 -151
|
@@ -130,14 +130,51 @@ fi
|
|
|
130
130
|
|
|
131
131
|
---
|
|
132
132
|
|
|
133
|
-
## 4
|
|
133
|
+
## 4. Build Verification
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
135
|
+
```bash
|
|
136
|
+
# Backend
|
|
137
|
+
dotnet clean && dotnet restore && dotnet build
|
|
138
|
+
# Note: WSL bin\Debug cleanup handled by PostToolUse hook (wsl-dotnet-cleanup.sh)
|
|
139
|
+
|
|
140
|
+
# Frontend (if applicable)
|
|
141
|
+
npm run typecheck
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**BLOCKING:** Both must pass. If failure, classify error per `references/error-classification.md` (categories A-F).
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 5. Migration Validation (if needs_migration)
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
INFRA_PROJECT=$(ls src/*Infrastructure*/*.csproj 2>/dev/null | head -1)
|
|
152
|
+
API_PROJECT=$(ls src/*Api*/*.csproj 2>/dev/null | head -1)
|
|
153
|
+
|
|
154
|
+
# Pending model changes check (BLOCKING if pending)
|
|
155
|
+
dotnet ef migrations has-pending-model-changes \
|
|
156
|
+
--project "$INFRA_PROJECT" \
|
|
157
|
+
--startup-project "$API_PROJECT"
|
|
158
|
+
|
|
159
|
+
# Migration application test (SQL Server LocalDB)
|
|
160
|
+
DB_NAME="SmartStack_Apex_Examine_$(date +%s)"
|
|
161
|
+
CONN_STRING="Server=(localdb)\\MSSQLLocalDB;Database=$DB_NAME;Integrated Security=true;TrustServerCertificate=true;Connect Timeout=120;"
|
|
162
|
+
dotnet ef database update \
|
|
163
|
+
--connection "$CONN_STRING" \
|
|
164
|
+
--project "$INFRA_PROJECT" \
|
|
165
|
+
--startup-project "$API_PROJECT"
|
|
166
|
+
|
|
167
|
+
# Integration tests on real SQL Server (if project exists)
|
|
168
|
+
INT_TEST_PROJECT=$(ls tests/*Tests.Integration*/*.csproj 2>/dev/null | head -1)
|
|
169
|
+
if [ -n "$INT_TEST_PROJECT" ]; then
|
|
170
|
+
dotnet test "$INT_TEST_PROJECT" --no-build --verbosity normal
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
# Cleanup
|
|
174
|
+
sqlcmd -S "(localdb)\MSSQLLocalDB" -Q "IF DB_ID('$DB_NAME') IS NOT NULL BEGIN ALTER DATABASE [$DB_NAME] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE [$DB_NAME]; END" 2>/dev/null
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**BLOCKING** if migration fails on SQL Server (common: SQLite-only syntax, column type mismatches, missing FK targets).
|
|
141
178
|
|
|
142
179
|
---
|
|
143
180
|
|
|
@@ -145,11 +182,11 @@ fi
|
|
|
145
182
|
|
|
146
183
|
| File | Checks |
|
|
147
184
|
|------|--------|
|
|
148
|
-
| NavigationApplicationSeedData | MUST be first,
|
|
149
|
-
| ApplicationRolesSeedData | 4 roles (admin, manager, contributor, viewer),
|
|
150
|
-
| NavigationModuleSeedData |
|
|
151
|
-
| ↳ Section methods (same file) | (conditional: if sections exist) GetSectionEntries + GetSectionTranslationEntries, 4 languages,
|
|
152
|
-
| ↳ Resource methods (same file) | (conditional: if resources exist) GetResourceEntries + resource translations, 4 languages,
|
|
185
|
+
| NavigationApplicationSeedData | MUST be first, random GUID via Guid.NewGuid() (resolved by Code at runtime), 4 lang translations |
|
|
186
|
+
| ApplicationRolesSeedData | 4 roles (admin, manager, contributor, viewer), random GUIDs, references NavigationApplicationSeedData.ApplicationId |
|
|
187
|
+
| NavigationModuleSeedData | Random GUIDs (`Guid.NewGuid()`), 4 languages (fr, en, it, de), GetModuleEntry + GetTranslationEntries |
|
|
188
|
+
| ↳ Section methods (same file) | (conditional: if sections exist) GetSectionEntries + GetSectionTranslationEntries, 4 languages, random GUIDs, query actual module from DB for FK |
|
|
189
|
+
| ↳ Resource methods (same file) | (conditional: if resources exist) GetResourceEntries + resource translations, 4 languages, random GUIDs, query actual section from DB for FK |
|
|
153
190
|
| Permissions.cs | Static constants class with `public static class {Module} { Read, Create, Update, Delete }`, paths match PermissionsSeedData |
|
|
154
191
|
| PermissionsSeedData | MCP generate_permissions used, paths match Permissions.cs, wildcard + CRUD |
|
|
155
192
|
| RolesSeedData | Admin=wildcard(*), Manager=CRU, Contributor=CR, Viewer=R. Role-permission entries reference permission paths from PermissionsSeedData |
|
|
@@ -160,7 +197,10 @@ fi
|
|
|
160
197
|
|
|
161
198
|
## 6b. BLOCKING POST-CHECKs
|
|
162
199
|
|
|
163
|
-
|
|
200
|
+
> **MCP FIRST:** Run `validate_conventions`, `validate_security`, and `validate_frontend_routes` before bash checks.
|
|
201
|
+
> These 3 MCP tools cover ~10 additional checks automatically.
|
|
202
|
+
|
|
203
|
+
Load and execute all checks from `references/post-checks.md` (6 security defense-in-depth + 44 convention checks).
|
|
164
204
|
|
|
165
205
|
**If ANY POST-CHECK fails → fix in step-03, re-validate.**
|
|
166
206
|
|
|
@@ -193,18 +233,18 @@ AC2: {criterion} → PASS / FAIL (evidence: {file:line or test})
|
|
|
193
233
|
| DB tests | Integration tests on SQL Server | PASS / N/A |
|
|
194
234
|
| Frontend | routes, lazy loading, forms as pages, i18n (4 langs + keys) | PASS / N/A |
|
|
195
235
|
| Security | TenantId filter, RequirePermission, no Guid.Empty, no !.Value | PASS / N/A |
|
|
196
|
-
| Seed data | completeness,
|
|
236
|
+
| Seed data | completeness, random GUIDs (Guid.NewGuid()), no ContextId/RoleId constants | PASS / N/A |
|
|
197
237
|
| Code quality | PaginatedResult, EntityLookup (no FK select/input), CSS vars, search param | PASS / N/A |
|
|
198
|
-
| Migration completeness | ModelSnapshot covers ALL DbSet entities (POST-CHECK
|
|
199
|
-
| NavRoute kebab-case | NavRoute + permissions use kebab-case (POST-CHECK
|
|
200
|
-
| DateOnly DTOs | Date fields use DateOnly not string (POST-CHECK
|
|
201
|
-
| I18n registration | Namespaces registered in i18n config (POST-CHECK
|
|
202
|
-
| Validators DI | FluentValidation registered in DI (POST-CHECK
|
|
203
|
-
| Route/NavRoute conflict | No [Route] alongside [NavRoute] on controllers (POST-CHECK
|
|
204
|
-
| Role-permission matrix | Admin=wildcard, Manager=CRU, Contributor=CR, Viewer=R (POST-CHECK
|
|
205
|
-
| PermissionAction enum | No Enum.Parse, only typed enum values 0-10 (POST-CHECK
|
|
206
|
-
| Navigation translations | 4 langs per level, section/resource translations present (POST-CHECK
|
|
207
|
-
| POST-CHECKs |
|
|
238
|
+
| Migration completeness | ModelSnapshot covers ALL DbSet entities (POST-CHECK 36, 40) | PASS / N/A |
|
|
239
|
+
| NavRoute kebab-case | NavRoute + permissions use kebab-case (POST-CHECK 32) | PASS / N/A |
|
|
240
|
+
| DateOnly DTOs | Date fields use DateOnly not string (POST-CHECK 39) | PASS / N/A |
|
|
241
|
+
| I18n registration | Namespaces registered in i18n config (POST-CHECK 37) | PASS / N/A |
|
|
242
|
+
| Validators DI | FluentValidation registered in DI (POST-CHECK 38) | PASS / N/A |
|
|
243
|
+
| Route/NavRoute conflict | No [Route] alongside [NavRoute] on controllers (POST-CHECK 41) | PASS / N/A |
|
|
244
|
+
| Role-permission matrix | Admin=wildcard, Manager=CRU, Contributor=CR, Viewer=R (POST-CHECK 42) | PASS / N/A |
|
|
245
|
+
| PermissionAction enum | No Enum.Parse, only typed enum values 0-10 (POST-CHECK 43) | PASS / N/A |
|
|
246
|
+
| Navigation translations | 4 langs per level, section/resource translations present (POST-CHECK 44) | PASS / N/A |
|
|
247
|
+
| POST-CHECKs | 3 MCP tools + 50 bash checks (6 security + 44 convention) | PASS / N/A |
|
|
208
248
|
| Acceptance criteria | AC1..ACn | {X}/{Y} PASS |
|
|
209
249
|
```
|
|
210
250
|
|
|
@@ -54,7 +54,7 @@ For each changed file, check:
|
|
|
54
54
|
- [ ] Frontend in correct hierarchy (App/Module)
|
|
55
55
|
|
|
56
56
|
**SmartStack conventions:**
|
|
57
|
-
- [ ]
|
|
57
|
+
- [ ] Random GUIDs in seed data via Guid.NewGuid() (no deterministic/sequential/fixed)
|
|
58
58
|
- [ ] 4 languages in translations
|
|
59
59
|
- [ ] CSS variables (not hardcoded colors)
|
|
60
60
|
- [ ] SmartTable/SmartForm (not raw HTML tables/forms)
|
|
@@ -24,10 +24,25 @@ dotnet build --no-restore
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
-
## 2. Run Full Test Suite
|
|
27
|
+
## 2. Run Full Test Suite (backend + frontend in parallel)
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
|
|
30
|
+
# Run backend and frontend tests in parallel when both exist
|
|
31
|
+
WEB_DIR=$(find . -name "vitest.config.ts" -not -path "*/node_modules/*" -exec dirname {} \; 2>/dev/null | head -1)
|
|
32
|
+
|
|
33
|
+
if [ -n "$WEB_DIR" ]; then
|
|
34
|
+
# Both exist → run in parallel
|
|
35
|
+
dotnet test --no-build --verbosity normal &
|
|
36
|
+
BACKEND_PID=$!
|
|
37
|
+
(cd "$WEB_DIR" && npm run test) &
|
|
38
|
+
FRONTEND_PID=$!
|
|
39
|
+
wait $BACKEND_PID; BACKEND_RC=$?
|
|
40
|
+
wait $FRONTEND_PID; FRONTEND_RC=$?
|
|
41
|
+
[ $BACKEND_RC -ne 0 ] || [ $FRONTEND_RC -ne 0 ] && echo "FAILURES DETECTED"
|
|
42
|
+
else
|
|
43
|
+
# Backend only
|
|
44
|
+
dotnet test --no-build --verbosity normal
|
|
45
|
+
fi
|
|
31
46
|
```
|
|
32
47
|
|
|
33
48
|
---
|
|
@@ -86,22 +101,15 @@ questions:
|
|
|
86
101
|
|
|
87
102
|
---
|
|
88
103
|
|
|
89
|
-
## 5.
|
|
104
|
+
## 5. Typecheck (if applicable)
|
|
90
105
|
|
|
91
|
-
|
|
92
|
-
# Run frontend tests first
|
|
93
|
-
WEB_DIR=$(find . -name "vitest.config.ts" -not -path "*/node_modules/*" -exec dirname {} \; 2>/dev/null | head -1)
|
|
94
|
-
if [ -n "$WEB_DIR" ]; then
|
|
95
|
-
cd "$WEB_DIR"
|
|
96
|
-
npm run test
|
|
97
|
-
cd -
|
|
98
|
-
fi
|
|
106
|
+
> **Note:** Frontend tests already ran in parallel with backend tests in section 2.
|
|
99
107
|
|
|
100
|
-
|
|
108
|
+
```bash
|
|
101
109
|
npm run typecheck
|
|
102
110
|
```
|
|
103
111
|
|
|
104
|
-
**
|
|
112
|
+
**MUST PASS.** If frontend tests failed in section 2, apply the same fix loop as backend (fix CODE, not tests, max 5 iterations).
|
|
105
113
|
|
|
106
114
|
---
|
|
107
115
|
|
|
@@ -22,7 +22,7 @@ var role = roles.FirstOrDefault(r => r.Code == mapping.RoleCode); // "admin", "
|
|
|
22
22
|
|
|
23
23
|
## Solution: Application Roles Seed Data
|
|
24
24
|
|
|
25
|
-
Create application-scoped roles with
|
|
25
|
+
Create application-scoped roles with random GUIDs (`Guid.NewGuid()`) and valid `Code` values.
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
@@ -48,14 +48,13 @@ namespace {BaseNamespace}.Infrastructure.Persistence.Seeding.Data;
|
|
|
48
48
|
/// </summary>
|
|
49
49
|
public static class ApplicationRolesSeedData
|
|
50
50
|
{
|
|
51
|
-
//
|
|
52
|
-
// Generated from: "role-{applicationId}-{roleType}"
|
|
51
|
+
// Random GUIDs — idempotence is handled by Code-based lookups, not fixed IDs
|
|
53
52
|
public static readonly Guid ApplicationId = NavigationApplicationSeedData.ApplicationId;
|
|
54
53
|
|
|
55
|
-
public static readonly Guid AdminRoleId =
|
|
56
|
-
public static readonly Guid ManagerRoleId =
|
|
57
|
-
public static readonly Guid ContributorRoleId =
|
|
58
|
-
public static readonly Guid ViewerRoleId =
|
|
54
|
+
public static readonly Guid AdminRoleId = Guid.NewGuid();
|
|
55
|
+
public static readonly Guid ManagerRoleId = Guid.NewGuid();
|
|
56
|
+
public static readonly Guid ContributorRoleId = Guid.NewGuid();
|
|
57
|
+
public static readonly Guid ViewerRoleId = Guid.NewGuid();
|
|
59
58
|
|
|
60
59
|
/// <summary>
|
|
61
60
|
/// Returns application-scoped role entries for seeding into core.auth_Roles.
|
|
@@ -111,12 +110,8 @@ public static class ApplicationRolesSeedData
|
|
|
111
110
|
};
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
|
|
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
|
-
}
|
|
113
|
+
// No deterministic GUID generation — all IDs are random via Guid.NewGuid()
|
|
114
|
+
// Roles are resolved by Code at runtime, not by fixed GUIDs
|
|
120
115
|
}
|
|
121
116
|
|
|
122
117
|
/// <summary>Seed entry DTO for application role.</summary>
|
|
@@ -141,7 +136,7 @@ public class ApplicationRoleSeedEntry
|
|
|
141
136
|
|-------------|-------------|---------|
|
|
142
137
|
| `{BaseNamespace}` | Root namespace of the client project | `SmartStack.Modules.RessourcesHumaines` |
|
|
143
138
|
| `{AppLabel}` | Human-readable application label (EN) | `Human Resources` |
|
|
144
|
-
| `ApplicationId` |
|
|
139
|
+
| `ApplicationId` | From `NavigationApplicationSeedData.ApplicationId` — resolved at runtime via Code lookup | Auto-resolved |
|
|
145
140
|
|
|
146
141
|
---
|
|
147
142
|
|
|
@@ -194,7 +189,7 @@ public async Task SeedRolesAsync(ICoreDbContext context, CancellationToken ct)
|
|
|
194
189
|
Before marking the task as completed, verify:
|
|
195
190
|
|
|
196
191
|
- [ ] `ApplicationRolesSeedData.cs` created in `Infrastructure/Persistence/Seeding/Data/`
|
|
197
|
-
- [ ]
|
|
192
|
+
- [ ] Random GUIDs used via `Guid.NewGuid()` (no deterministic/sequential/fixed values)
|
|
198
193
|
- [ ] 4 roles defined: Admin, Manager, Contributor, Viewer
|
|
199
194
|
- [ ] Each role has a valid `Code` value ("admin", "manager", "contributor", "viewer")
|
|
200
195
|
- [ ] Each role has `ApplicationId` set to the application GUID
|
|
@@ -8,16 +8,17 @@
|
|
|
8
8
|
|
|
9
9
|
Follow SmartStack.app pattern:
|
|
10
10
|
- Static class with `internal static IEnumerable<{EntityName}SeedItem> GetAll{EntityName}s()`
|
|
11
|
-
-
|
|
11
|
+
- ALWAYS use `Guid.NewGuid()` — avoids conflicts between projects/tenants/environments
|
|
12
|
+
- Idempotence is handled by `AnyAsync()` checks (by Code/Name), NOT by fixed IDs
|
|
12
13
|
- Include 3-5 sample entities with varied, realistic data
|
|
13
14
|
- For multi-tenant: organize by tenant using `GetTenant1{EntityName}s()` helpers
|
|
14
15
|
|
|
15
16
|
```csharp
|
|
16
17
|
public static class {EntityName}SeedData
|
|
17
18
|
{
|
|
18
|
-
public static readonly Guid Sample1Id = Guid.
|
|
19
|
-
public static readonly Guid Sample2Id = Guid.
|
|
20
|
-
public static readonly Guid Sample3Id = Guid.
|
|
19
|
+
public static readonly Guid Sample1Id = Guid.NewGuid();
|
|
20
|
+
public static readonly Guid Sample2Id = Guid.NewGuid();
|
|
21
|
+
public static readonly Guid Sample3Id = Guid.NewGuid();
|
|
21
22
|
|
|
22
23
|
internal static IEnumerable<{EntityName}SeedItem> GetAll{EntityName}s()
|
|
23
24
|
{
|
|
@@ -59,7 +60,7 @@ private async Task Seed{EntityName}sAsync(CancellationToken cancellationToken)
|
|
|
59
60
|
foreach (var seedItem in {EntityName}SeedData.GetAll{EntityName}s())
|
|
60
61
|
{
|
|
61
62
|
var entity = {EntityName}.Create(/* map seedItem properties */);
|
|
62
|
-
|
|
63
|
+
// Factory method generates a random GUID — no need to override
|
|
63
64
|
_context.{EntityName}s.Add(entity);
|
|
64
65
|
createdCount++;
|
|
65
66
|
}
|
|
@@ -23,7 +23,7 @@ questions:
|
|
|
23
23
|
**If user selects YES:**
|
|
24
24
|
|
|
25
25
|
See [references/backend-entity-seeding.md](../references/backend-entity-seeding.md) for the full seeding pattern:
|
|
26
|
-
1. **SeedData file** (`{EntityName}SeedData.cs`) — static class with
|
|
26
|
+
1. **SeedData file** (`{EntityName}SeedData.cs`) — static class with `Guid.NewGuid()`, 3-5 samples
|
|
27
27
|
2. **DevDataSeeder update** — add `Seed{EntityName}sAsync()` method with idempotent check
|
|
28
28
|
|
|
29
29
|
**If user selects NO:**
|
|
@@ -16,11 +16,10 @@
|
|
|
16
16
|
Glob: **/Persistence/Configurations/Navigation/Navigation*Configuration.cs
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
2. **Read NavigationTranslationConfiguration.cs** -
|
|
19
|
+
2. **Read NavigationTranslationConfiguration.cs** - Check existing translations:
|
|
20
20
|
```
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
Your new translations MUST continue from the next index value.
|
|
21
|
+
All translation IDs use Guid.NewGuid() — no sequence to track.
|
|
22
|
+
Just verify no duplicate EntityType+EntityId+LanguageCode combinations.
|
|
24
23
|
```
|
|
25
24
|
|
|
26
25
|
3. **Read Navigation{Level}Configuration.cs** - Check existing entities:
|
|
@@ -51,18 +50,16 @@ Read the parent SeedData class and find the GUID matching `{parent_path}`.
|
|
|
51
50
|
|
|
52
51
|
## F3. Generate Navigation Entity GUID
|
|
53
52
|
|
|
54
|
-
Generate a
|
|
53
|
+
Generate a random GUID for the new navigation entity:
|
|
55
54
|
|
|
56
55
|
```csharp
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes("navigation-{level}-{full_path}"));
|
|
60
|
-
var guid = new Guid(hash.Take(16).ToArray());
|
|
56
|
+
// ALWAYS use Guid.NewGuid() — avoids conflicts between projects/tenants/environments
|
|
57
|
+
var guid = Guid.NewGuid();
|
|
61
58
|
```
|
|
62
59
|
|
|
63
60
|
**Rules:**
|
|
64
|
-
-
|
|
65
|
-
-
|
|
61
|
+
- ALWAYS use `Guid.NewGuid()` — no deterministic, no sequential, no fixed values
|
|
62
|
+
- Idempotence is handled by Code-based lookups, not by fixed IDs
|
|
66
63
|
- Store result as `{navigation_guid}`
|
|
67
64
|
|
|
68
65
|
## F4. Write Navigation Entity Seed
|
|
@@ -128,7 +125,7 @@ Add 4 translation entries to `NavigationTranslationConfiguration.cs`:
|
|
|
128
125
|
|
|
129
126
|
// {level}: {code} translations
|
|
130
127
|
translations.Add(new {
|
|
131
|
-
Id =
|
|
128
|
+
Id = Guid.NewGuid(),
|
|
132
129
|
EntityType = NavigationEntityType.{Level},
|
|
133
130
|
EntityId = Navigation{Level}SeedData.{PascalCode}Id, // or Guid.Parse("{navigation_guid}")
|
|
134
131
|
LanguageCode = "fr",
|
|
@@ -137,7 +134,7 @@ translations.Add(new {
|
|
|
137
134
|
CreatedAt = seedDate
|
|
138
135
|
});
|
|
139
136
|
translations.Add(new {
|
|
140
|
-
Id =
|
|
137
|
+
Id = Guid.NewGuid(),
|
|
141
138
|
EntityType = NavigationEntityType.{Level},
|
|
142
139
|
EntityId = Navigation{Level}SeedData.{PascalCode}Id,
|
|
143
140
|
LanguageCode = "en",
|
|
@@ -146,7 +143,7 @@ translations.Add(new {
|
|
|
146
143
|
CreatedAt = seedDate
|
|
147
144
|
});
|
|
148
145
|
translations.Add(new {
|
|
149
|
-
Id =
|
|
146
|
+
Id = Guid.NewGuid(),
|
|
150
147
|
EntityType = NavigationEntityType.{Level},
|
|
151
148
|
EntityId = Navigation{Level}SeedData.{PascalCode}Id,
|
|
152
149
|
LanguageCode = "it",
|
|
@@ -155,7 +152,7 @@ translations.Add(new {
|
|
|
155
152
|
CreatedAt = seedDate
|
|
156
153
|
});
|
|
157
154
|
translations.Add(new {
|
|
158
|
-
Id =
|
|
155
|
+
Id = Guid.NewGuid(),
|
|
159
156
|
EntityType = NavigationEntityType.{Level},
|
|
160
157
|
EntityId = Navigation{Level}SeedData.{PascalCode}Id,
|
|
161
158
|
LanguageCode = "de",
|
|
@@ -175,9 +172,9 @@ translations.Add(new {
|
|
|
175
172
|
## F7. Validation Checklist
|
|
176
173
|
|
|
177
174
|
Before proceeding, verify:
|
|
178
|
-
- [ ]
|
|
175
|
+
- [ ] Random GUID generated via Guid.NewGuid()
|
|
179
176
|
- [ ] 4 languages present (fr, en, it, de)
|
|
180
|
-
- [ ]
|
|
177
|
+
- [ ] No duplicate EntityType+EntityId+LanguageCode combinations
|
|
181
178
|
- [ ] Parent GUID correctly references existing entity
|
|
182
179
|
- [ ] Route path matches `/{app}/{module}` pattern
|
|
183
180
|
- [ ] DisplayOrder is consistent with existing entities
|
|
@@ -86,11 +86,11 @@ public class {AppPascalName}SeedDataProvider : IClientSeedDataProvider
|
|
|
86
86
|
// t => t.EntityId == secEntry.Id && t.EntityType == NavigationEntityType.Section, ct))
|
|
87
87
|
|
|
88
88
|
// --- Resources (idempotent — use ACTUAL section IDs from DB) ---
|
|
89
|
-
// CRITICAL: NavigationSection.Create() generates its own ID.
|
|
90
|
-
//
|
|
91
|
-
//
|
|
89
|
+
// CRITICAL: NavigationSection.Create() generates its own random ID.
|
|
90
|
+
// MUST query actual section from DB to get real ID for FK references:
|
|
91
|
+
// var actualSection = await context.NavigationSections
|
|
92
92
|
// .FirstAsync(s => s.Code == secEntry.Code && s.ModuleId == modEntity.Id, ct);
|
|
93
|
-
// Then use actualSection.Id
|
|
93
|
+
// Then use actualSection.Id for NavigationResource.Create() and AnyAsync
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
public async Task SeedRolesAsync(ICoreDbContext context, CancellationToken ct)
|
|
@@ -172,6 +172,6 @@ services.AddScoped<IClientSeedDataProvider, {AppPascalName}SeedDataProvider>();
|
|
|
172
172
|
3. **Translation guard pattern**: `if (!await context.NavigationTranslations.AnyAsync(t => t.EntityId == {id} && t.EntityType == ..., ct))` — MANDATORY before every translation batch insert
|
|
173
173
|
4. **SaveChangesAsync per group**: Navigation → save → Roles → save → Permissions → save → RolePermissions → save
|
|
174
174
|
4. **Execution order**: `SeedRolesAsync()` MUST be called BEFORE `SeedRolePermissionsAsync()` (roles must exist before mapping)
|
|
175
|
-
5. **
|
|
175
|
+
5. **Random GUIDs**: ALWAYS use `Guid.NewGuid()` — resolve FKs by Code lookup, not fixed IDs
|
|
176
176
|
6. **Resolve FK by Code**: Parent modules and roles are found by `Code`, not hardcoded GUID
|
|
177
177
|
7. **Order property**: Use `100` as default. If multiple providers exist, they run in Order sequence
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
See [references/application-roles-template.md](../references/application-roles-template.md) for the complete template.
|
|
16
16
|
|
|
17
17
|
**Key requirements:**
|
|
18
|
-
-
|
|
18
|
+
- Random GUIDs via `Guid.NewGuid()` — roles are resolved by Code at runtime
|
|
19
19
|
- 4 roles: Admin, Manager, Contributor, Viewer
|
|
20
20
|
- Each role has a valid `Code` property ("admin", "manager", "contributor", "viewer")
|
|
21
21
|
- `ApplicationId` references the navigation application GUID
|
|
@@ -24,26 +24,21 @@ Read the file to determine:
|
|
|
24
24
|
|
|
25
25
|
| Role | GUID |
|
|
26
26
|
|------|------|
|
|
27
|
-
| SuperAdmin |
|
|
28
|
-
| PlatformAdmin |
|
|
29
|
-
| TenantAdmin |
|
|
30
|
-
| StandardUser |
|
|
27
|
+
| SuperAdmin | Resolved by Code at runtime (NEVER hardcoded) |
|
|
28
|
+
| PlatformAdmin | Resolved by Code at runtime (NEVER hardcoded) |
|
|
29
|
+
| TenantAdmin | Resolved by Code at runtime (NEVER hardcoded) |
|
|
30
|
+
| StandardUser | Resolved by Code at runtime (NEVER hardcoded) |
|
|
31
31
|
|
|
32
32
|
**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.
|
|
33
33
|
|
|
34
|
-
**Application-scoped roles** (
|
|
34
|
+
**Application-scoped roles** (resolved by Code at runtime):
|
|
35
35
|
|
|
36
36
|
```csharp
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
var input = $"{applicationId}-{roleType}";
|
|
43
|
-
var hash = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(input));
|
|
44
|
-
return new Guid(hash);
|
|
45
|
-
}
|
|
46
|
-
// roleType values: "admin", "manager", "contributor", "viewer"
|
|
37
|
+
// ALWAYS resolve roles by Code at runtime — NEVER use deterministic/hardcoded GUIDs
|
|
38
|
+
// Role IDs are generated via Guid.NewGuid() at seed time
|
|
39
|
+
// Lookups use:
|
|
40
|
+
var role = await context.Roles.FirstOrDefaultAsync(r => r.Code == "admin", ct);
|
|
41
|
+
// roleCode values: "admin", "manager", "contributor", "viewer"
|
|
47
42
|
```
|
|
48
43
|
|
|
49
44
|
Find the `applicationId` from `NavigationApplicationSeedData.cs` matching `{full_path}`.
|
|
@@ -141,7 +141,7 @@ The SeedData files will be consumed by the `IClientSeedDataProvider` generated a
|
|
|
141
141
|
See [references/nav-fallback-procedure.md](../references/nav-fallback-procedure.md) for the complete 8-step fallback:
|
|
142
142
|
- **F1:** Read existing Configuration/SeedData files to determine state
|
|
143
143
|
- **F2:** Determine parent GUID by level
|
|
144
|
-
- **F3:** Generate
|
|
144
|
+
- **F3:** Generate random GUID via Guid.NewGuid()
|
|
145
145
|
- **F4:** Write navigation entity seed (SeedData class or inline HasData)
|
|
146
146
|
- **F5:** Write 4 translation entries (continue existing index sequence)
|
|
147
147
|
- **F6-F8:** Store result, validation checklist, summary
|
|
@@ -63,7 +63,7 @@ Args:
|
|
|
63
63
|
The tool returns:
|
|
64
64
|
- Permissions.cs nested class structure
|
|
65
65
|
- PermissionConfiguration.cs HasData() entries
|
|
66
|
-
-
|
|
66
|
+
- Random GUIDs via Guid.NewGuid() for each permission
|
|
67
67
|
|
|
68
68
|
### 4. Present Permissions.cs Output
|
|
69
69
|
|
|
@@ -130,7 +130,7 @@ Write it as usual.
|
|
|
130
130
|
Instead, create:
|
|
131
131
|
- `Infrastructure/Persistence/Seeding/Data/{Domain}/{Module}PermissionSeedData.cs`
|
|
132
132
|
|
|
133
|
-
Content: static class with
|
|
133
|
+
Content: static class with `Guid.NewGuid()` and method `GetPermissionEntries()`.
|
|
134
134
|
These entries will be consumed by the `IClientSeedDataProvider` at step 03b.
|
|
135
135
|
|
|
136
136
|
### 4. Store Permission GUIDs
|
|
@@ -179,7 +179,7 @@ If MCP call fails:
|
|
|
179
179
|
- MCP generate_permissions called successfully
|
|
180
180
|
- Permissions.cs code displayed
|
|
181
181
|
- PermissionConfiguration.cs HasData displayed
|
|
182
|
-
-
|
|
182
|
+
- Random GUIDs via Guid.NewGuid() (not placeholders, not deterministic)
|
|
183
183
|
- Permission GUIDs stored for role assignment
|
|
184
184
|
- Proceeded to step-03-roles.md
|
|
185
185
|
|
|
@@ -84,6 +84,7 @@ Adapt the template:
|
|
|
84
84
|
- Replace `{app_code}` with the kebab-case application code
|
|
85
85
|
- Fill in the actual navigation, permission, and role-permission creation logic
|
|
86
86
|
using the helper methods from the SeedData classes
|
|
87
|
+
- **CRITICAL:** Follow the 7 critical rules: factory methods, idempotence, execution order, SaveChangesAsync order, random GUIDs via Guid.NewGuid(), FK by Code, Order property
|
|
87
88
|
|
|
88
89
|
### 3. Register in DI
|
|
89
90
|
|