@atlashub/smartstack-cli 3.24.0 → 3.26.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/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +51 -14
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/skills/apex/SKILL.md +26 -5
- package/templates/skills/apex/_shared.md +3 -3
- package/templates/skills/apex/references/agent-teams-protocol.md +8 -8
- package/templates/skills/apex/references/challenge-questions.md +165 -0
- package/templates/skills/apex/references/post-checks.md +457 -0
- package/templates/skills/apex/references/smartstack-api.md +234 -14
- package/templates/skills/apex/references/smartstack-frontend.md +20 -0
- package/templates/skills/apex/references/smartstack-layers.md +16 -4
- package/templates/skills/apex/steps/step-00-init.md +84 -56
- package/templates/skills/apex/steps/step-01-analyze.md +73 -87
- package/templates/skills/apex/steps/step-03-execute.md +6 -4
- package/templates/skills/apex/steps/step-04-examine.md +198 -0
- package/templates/skills/apex/steps/{step-05-examine.md → step-05-deep-review.md} +6 -6
- package/templates/skills/apex/steps/step-06-resolve.md +2 -2
- package/templates/skills/business-analyse/SKILL.md +28 -0
- package/templates/skills/business-analyse/references/agent-module-prompt.md +255 -0
- package/templates/skills/business-analyse/references/agent-pooling-best-practices.md +26 -10
- package/templates/skills/business-analyse/references/team-orchestration.md +437 -0
- package/templates/skills/business-analyse/steps/step-02-decomposition.md +31 -4
- package/templates/skills/business-analyse/steps/step-03a1-setup.md +21 -0
- package/templates/skills/business-analyse/steps/step-03d-validate.md +84 -0
- package/templates/skills/efcore/steps/migration/step-02-create.md +14 -1
- package/templates/skills/ralph-loop/references/category-rules.md +26 -2
- package/templates/skills/ralph-loop/references/compact-loop.md +1 -1
- package/templates/skills/ralph-loop/references/core-seed-data.md +45 -10
- package/templates/skills/ralph-loop/steps/step-02-execute.md +128 -1
- package/templates/skills/validate-feature/steps/step-01-compile.md +4 -1
- package/templates/skills/apex/steps/step-04-validate.md +0 -448
|
@@ -75,11 +75,20 @@ From `seedDataCore.navigationApplications[0]` in feature.json (generated by BA s
|
|
|
75
75
|
|
|
76
76
|
### GUID Generation Rule
|
|
77
77
|
|
|
78
|
+
> **CRITICAL — NavigationContext IDs are NEVER generated as deterministic GUIDs.**
|
|
79
|
+
> Contexts (`business`, `platform`, `personal`) are pre-seeded by SmartStack core with hardcoded GUIDs.
|
|
80
|
+
> The `contextId` parameter in `GetApplicationEntry(Guid contextId)` is resolved at runtime
|
|
81
|
+
> by querying `db.NavigationContexts.FirstOrDefaultAsync(c => c.Code == "business", ct)`.
|
|
82
|
+
> **FORBIDDEN:** `GenerateDeterministicGuid("nav:business")` or any ContextId constant in SeedConstants.
|
|
83
|
+
|
|
78
84
|
```csharp
|
|
79
|
-
// Deterministic GUID
|
|
85
|
+
// Deterministic GUID for APPLICATION (not context — contexts are pre-seeded by SmartStack core)
|
|
80
86
|
public static readonly Guid ApplicationId =
|
|
81
87
|
GenerateDeterministicGuid("navigation-application-{contextCode}.{appCode}");
|
|
82
88
|
// Example: GenerateDeterministicGuid("navigation-application-business.humanresources")
|
|
89
|
+
//
|
|
90
|
+
// FORBIDDEN — Context IDs must NOT be generated:
|
|
91
|
+
// public static readonly Guid BusinessContextId = GenerateDeterministicGuid("nav:business"); // WRONG!
|
|
83
92
|
```
|
|
84
93
|
|
|
85
94
|
### Template
|
|
@@ -723,20 +732,33 @@ If MCP `generate_permissions` fails, use the template above directly with values
|
|
|
723
732
|
|
|
724
733
|
Creates the 4 standard application-scoped roles: Admin, Manager, Contributor, Viewer.
|
|
725
734
|
|
|
735
|
+
> **CRITICAL — SmartStack core MAY already provide system roles (admin, manager, contributor, viewer).**
|
|
736
|
+
> If system roles already exist in `auth_Roles`, do NOT create duplicates.
|
|
737
|
+
> `SeedRolesAsync()` MUST check existence by Code, not just by ApplicationId.
|
|
738
|
+
> **For RolePermission mappings:** ALWAYS look up roles by Code at runtime (in `SeedRolePermissionsAsync()`).
|
|
739
|
+
> **FORBIDDEN:** Using `GenerateRoleGuid()`, `DeterministicGuid("role:admin")`, or any hardcoded role GUID
|
|
740
|
+
> when creating RolePermission entries. The role GUIDs may differ from what's in the database.
|
|
741
|
+
|
|
726
742
|
**CRITICAL:** This file is created **ONCE per application** (not per module).
|
|
727
|
-
Without these roles, role-permission mappings in `SeedRolePermissionsAsync()` will fail silently.
|
|
728
743
|
|
|
729
744
|
### GUID Generation Rule
|
|
730
745
|
|
|
746
|
+
> **WARNING:** These deterministic GUIDs are ONLY used for role creation (if roles don't already exist).
|
|
747
|
+
> They MUST NEVER be used for RolePermission mapping — always look up roles by Code at runtime.
|
|
748
|
+
|
|
731
749
|
```csharp
|
|
732
750
|
// Deterministic GUID from application ID + role type
|
|
751
|
+
// WARNING: Only for role creation. NEVER use these GUIDs for RolePermission mapping.
|
|
752
|
+
// RolePermissions MUST resolve roles by Code at runtime (see SeedRolePermissionsAsync).
|
|
733
753
|
private static Guid GenerateRoleGuid(string roleType)
|
|
734
754
|
{
|
|
735
755
|
using var sha256 = System.Security.Cryptography.SHA256.Create();
|
|
736
756
|
var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes($"role-{ApplicationId}-{roleType}"));
|
|
737
757
|
return new Guid(hash.Take(16).ToArray());
|
|
738
758
|
}
|
|
739
|
-
//
|
|
759
|
+
// FORBIDDEN in RolePermission mapping:
|
|
760
|
+
// var roleId = GenerateRoleGuid("admin"); // WRONG — GUID may not match DB
|
|
761
|
+
// var roleId = DeterministicGuid("role:admin"); // WRONG — use Code lookup instead
|
|
740
762
|
```
|
|
741
763
|
|
|
742
764
|
### Template
|
|
@@ -841,6 +863,11 @@ public class ApplicationRoleSeedEntry
|
|
|
841
863
|
|
|
842
864
|
**File:** `Infrastructure/Persistence/Seeding/Data/{ModulePascal}/RolesSeedData.cs`
|
|
843
865
|
|
|
866
|
+
> **CRITICAL:** This file uses `RoleCode` (string), NOT role GUIDs.
|
|
867
|
+
> Roles are resolved by Code at runtime in `SeedRolePermissionsAsync()`.
|
|
868
|
+
> **FORBIDDEN:** `DeterministicGuid("role:admin")`, `GenerateRoleGuid("admin")`, or any hardcoded Guid for roles.
|
|
869
|
+
> SmartStack core pre-seeds system roles — their IDs are NOT deterministic from the client perspective.
|
|
870
|
+
|
|
844
871
|
### Context-Based Role Mapping
|
|
845
872
|
|
|
846
873
|
| Context | Admin | Manager | Contributor | Viewer |
|
|
@@ -1019,15 +1046,17 @@ public class {AppPascalName}SeedDataProvider : IClientSeedDataProvider
|
|
|
1019
1046
|
|
|
1020
1047
|
public async Task SeedRolesAsync(ICoreDbContext context, CancellationToken ct)
|
|
1021
1048
|
{
|
|
1022
|
-
// Check idempotence
|
|
1023
|
-
var
|
|
1024
|
-
|
|
1025
|
-
.
|
|
1026
|
-
|
|
1049
|
+
// Check idempotence — verify by Code (roles may already exist from SmartStack core)
|
|
1050
|
+
var existingRoleCodes = await context.Roles
|
|
1051
|
+
.Where(r => r.Code == "admin" || r.Code == "manager" || r.Code == "contributor" || r.Code == "viewer")
|
|
1052
|
+
.Select(r => r.Code)
|
|
1053
|
+
.ToListAsync(ct);
|
|
1027
1054
|
|
|
1028
|
-
//
|
|
1055
|
+
// Only create roles that don't already exist (SmartStack core may pre-seed system roles)
|
|
1029
1056
|
foreach (var entry in ApplicationRolesSeedData.GetRoleEntries())
|
|
1030
1057
|
{
|
|
1058
|
+
if (existingRoleCodes.Contains(entry.Code)) continue; // Skip if already exists
|
|
1059
|
+
|
|
1031
1060
|
var role = Role.Create(
|
|
1032
1061
|
entry.Code,
|
|
1033
1062
|
entry.Name,
|
|
@@ -1072,7 +1101,9 @@ public class {AppPascalName}SeedDataProvider : IClientSeedDataProvider
|
|
|
1072
1101
|
.AnyAsync(rp => rp.Permission!.Path.StartsWith("{contextCode}.{appCode}."), ct);
|
|
1073
1102
|
if (exists) return;
|
|
1074
1103
|
|
|
1075
|
-
// Resolve roles by Code
|
|
1104
|
+
// CRITICAL: Resolve roles by Code from DB — NEVER use deterministic GUIDs.
|
|
1105
|
+
// System roles (admin, manager, contributor, viewer) are pre-seeded by SmartStack core
|
|
1106
|
+
// with their own IDs that do NOT match DeterministicGuid("role:admin").
|
|
1076
1107
|
var roles = await context.Roles
|
|
1077
1108
|
.Where(r => r.ApplicationId != null || r.IsSystem)
|
|
1078
1109
|
.ToListAsync(ct);
|
|
@@ -1159,6 +1190,8 @@ Before marking the task as completed, verify ALL:
|
|
|
1159
1190
|
**Application-Level (FIRST — before modules):**
|
|
1160
1191
|
- [ ] `NavigationApplicationSeedData.cs` created (once per application, at `Infrastructure/Persistence/Seeding/Data/`)
|
|
1161
1192
|
- [ ] Application GUID is deterministic (SHA256 of `"navigation-application-{contextCode}.{appCode}"`)
|
|
1193
|
+
- [ ] **NO ContextId constant in SeedConstants** — Context IDs are pre-seeded by SmartStack core (hardcoded GUIDs, NOT deterministic)
|
|
1194
|
+
- [ ] **SeedDataProvider queries context by code at runtime:** `db.NavigationContexts.FirstOrDefaultAsync(c => c.Code == "{contextCode}", ct)`
|
|
1162
1195
|
- [ ] Application translations created (4 languages: fr, en, it, de, EntityType = Application)
|
|
1163
1196
|
- [ ] `IClientSeedDataProvider.SeedNavigationAsync()` uses `NavigationApplicationSeedData` (NO hardcoded `{appLabel_en}` / `{appIcon}` placeholders)
|
|
1164
1197
|
- [ ] `ApplicationRolesSeedData.ApplicationId` references `NavigationApplicationSeedData.ApplicationId` (NO `{ApplicationGuid}` placeholder)
|
|
@@ -1172,6 +1205,8 @@ Before marking the task as completed, verify ALL:
|
|
|
1172
1205
|
- [ ] `Permissions.cs` constants match seed data paths
|
|
1173
1206
|
- [ ] MCP `generate_permissions` called (or fallback used)
|
|
1174
1207
|
- [ ] Role-permission mappings assigned (Admin, Manager, Contributor, Viewer)
|
|
1208
|
+
- [ ] **RolePermission mappings use Code-based lookup** — NEVER `DeterministicGuid("role:admin")` or `GenerateRoleGuid()`
|
|
1209
|
+
- [ ] **SeedRolesAsync checks existence by Code** — SmartStack core may pre-seed system roles
|
|
1175
1210
|
- [ ] `IClientSeedDataProvider` generated with 4 methods (Navigation, Roles, Permissions, RolePermissions)
|
|
1176
1211
|
- [ ] Execution order: Navigation (application → modules → sections → resources) → Roles → Permissions → RolePermissions
|
|
1177
1212
|
- [ ] Each Seed method is idempotent (checks existence before inserting)
|
|
@@ -271,6 +271,17 @@ if [ -n "$LIST_PAGES" ]; then
|
|
|
271
271
|
done
|
|
272
272
|
fi
|
|
273
273
|
|
|
274
|
+
# First check: at least one test file must exist in pages/
|
|
275
|
+
PAGE_FILES=$(find src/pages/ -name "*.tsx" ! -name "*.test.tsx" 2>/dev/null)
|
|
276
|
+
if [ -n "$PAGE_FILES" ]; then
|
|
277
|
+
TEST_FILES=$(find src/pages/ -name "*.test.tsx" 2>/dev/null)
|
|
278
|
+
if [ -z "$TEST_FILES" ]; then
|
|
279
|
+
echo "BLOCKING: No frontend test files found in src/pages/"
|
|
280
|
+
echo "Every module with pages MUST have at least one .test.tsx file"
|
|
281
|
+
exit 1
|
|
282
|
+
fi
|
|
283
|
+
fi
|
|
284
|
+
|
|
274
285
|
# POST-CHECK: Form pages must have companion test files
|
|
275
286
|
FORM_PAGES=$(find src/pages/ -name "*CreatePage.tsx" -o -name "*EditPage.tsx" 2>/dev/null | grep -v test)
|
|
276
287
|
if [ -n "$FORM_PAGES" ]; then
|
|
@@ -286,7 +297,8 @@ fi
|
|
|
286
297
|
# POST-CHECK: FK fields must NOT be plain text inputs — use EntityLookup
|
|
287
298
|
FORM_PAGES=$(find src/pages/ -name "*CreatePage.tsx" -o -name "*EditPage.tsx" 2>/dev/null | grep -v test | grep -v node_modules)
|
|
288
299
|
if [ -n "$FORM_PAGES" ]; then
|
|
289
|
-
|
|
300
|
+
# Match any input with name ending in "Id" (except hidden inputs)
|
|
301
|
+
FK_TEXT_INPUTS=$(grep -Pn '<input[^>]*name=["\x27][a-zA-Z]*Id["\x27]' $FORM_PAGES 2>/dev/null | grep -v 'type=["\x27]hidden["\x27]')
|
|
290
302
|
if [ -n "$FK_TEXT_INPUTS" ]; then
|
|
291
303
|
echo "BLOCKING: FK Guid fields rendered as plain text inputs — MUST use EntityLookup"
|
|
292
304
|
echo "See smartstack-frontend.md section 6 for the EntityLookup pattern"
|
|
@@ -313,6 +325,121 @@ if [ -n "$CTRL_FILES" ]; then
|
|
|
313
325
|
fi
|
|
314
326
|
done
|
|
315
327
|
fi
|
|
328
|
+
|
|
329
|
+
# POST-CHECK: Route seed data vs frontend cross-validation
|
|
330
|
+
SEED_NAV_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*NavigationSeedData.cs" 2>/dev/null)
|
|
331
|
+
APP_TSX=$(find src/ -name "App.tsx" -not -path "*/node_modules/*" 2>/dev/null | head -1)
|
|
332
|
+
if [ -n "$SEED_NAV_FILES" ] && [ -n "$APP_TSX" ]; then
|
|
333
|
+
SEED_ROUTES=$(grep -ohP 'Route\s*=\s*"([^"]+)"' $SEED_NAV_FILES | sed 's/Route\s*=\s*"//' | sed 's/"//' | sort -u)
|
|
334
|
+
CLIENT_PATHS=$(grep -ohP "path:\s*['\"]([^'\"]+)['\"]" "$APP_TSX" | sed "s/path:\s*['\"]//;s/['\"]//" | sort -u)
|
|
335
|
+
if [ -n "$SEED_ROUTES" ] && [ -z "$CLIENT_PATHS" ]; then
|
|
336
|
+
echo "WARNING: Seed data has navigation routes but App.tsx has no client routes defined"
|
|
337
|
+
fi
|
|
338
|
+
fi
|
|
339
|
+
|
|
340
|
+
# POST-CHECK: HasQueryFilter anti-pattern
|
|
341
|
+
CONFIG_FILES=$(find src/ -path "*/Configurations/*" -name "*Configuration.cs" 2>/dev/null)
|
|
342
|
+
if [ -n "$CONFIG_FILES" ]; then
|
|
343
|
+
BAD_FILTER=$(grep -Pn 'HasQueryFilter.*Guid\.Empty' $CONFIG_FILES 2>/dev/null)
|
|
344
|
+
if [ -n "$BAD_FILTER" ]; then
|
|
345
|
+
echo "BLOCKING: HasQueryFilter uses Guid.Empty — must use runtime tenant filter"
|
|
346
|
+
echo "$BAD_FILTER"
|
|
347
|
+
exit 1
|
|
348
|
+
fi
|
|
349
|
+
fi
|
|
350
|
+
|
|
351
|
+
# POST-CHECK: PaginatedResult<T> for GetAll
|
|
352
|
+
SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
|
|
353
|
+
if [ -n "$SERVICE_FILES" ]; then
|
|
354
|
+
BAD_GETALL=$(grep -Pn 'Task<(List|IEnumerable|IList|ICollection)<' $SERVICE_FILES 2>/dev/null | grep -i "getall")
|
|
355
|
+
if [ -n "$BAD_GETALL" ]; then
|
|
356
|
+
echo "BLOCKING: GetAll methods must return PaginatedResult<T>, not List/IEnumerable"
|
|
357
|
+
echo "$BAD_GETALL"
|
|
358
|
+
exit 1
|
|
359
|
+
fi
|
|
360
|
+
fi
|
|
361
|
+
|
|
362
|
+
# POST-CHECK: i18n required key structure
|
|
363
|
+
FR_I18N_FILES=$(find src/i18n/locales/fr/ -name "*.json" 2>/dev/null)
|
|
364
|
+
if [ -n "$FR_I18N_FILES" ]; then
|
|
365
|
+
for f in $FR_I18N_FILES; do
|
|
366
|
+
BASENAME=$(basename "$f")
|
|
367
|
+
case "$BASENAME" in common.json|navigation.json) continue;; esac
|
|
368
|
+
for KEY in "actions" "labels" "errors"; do
|
|
369
|
+
if ! grep -q "\"$KEY\"" "$f"; then
|
|
370
|
+
echo "WARNING: i18n file missing required key '$KEY': $f"
|
|
371
|
+
fi
|
|
372
|
+
done
|
|
373
|
+
done
|
|
374
|
+
fi
|
|
375
|
+
|
|
376
|
+
# POST-CHECK: SeedConstants must NOT contain ContextId (pre-seeded by SmartStack core)
|
|
377
|
+
SEED_CONST_FILES=$(find src/ -path "*/Seeding/*" -name "SeedConstants.cs" 2>/dev/null)
|
|
378
|
+
if [ -n "$SEED_CONST_FILES" ]; then
|
|
379
|
+
BAD_CTX=$(grep -Pn 'ContextId\s*=' $SEED_CONST_FILES 2>/dev/null)
|
|
380
|
+
if [ -n "$BAD_CTX" ]; then
|
|
381
|
+
echo "BLOCKING: SeedConstants must NOT contain a ContextId constant"
|
|
382
|
+
echo "Context IDs are pre-seeded by SmartStack core — look up by code at runtime"
|
|
383
|
+
echo "$BAD_CTX"
|
|
384
|
+
exit 1
|
|
385
|
+
fi
|
|
386
|
+
fi
|
|
387
|
+
SEED_ALL_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*.cs" 2>/dev/null)
|
|
388
|
+
if [ -n "$SEED_ALL_FILES" ]; then
|
|
389
|
+
BAD_CTX_GUID=$(grep -Pn 'DeterministicGuid\("nav:(business|platform|personal)"\)' $SEED_ALL_FILES 2>/dev/null)
|
|
390
|
+
if [ -n "$BAD_CTX_GUID" ]; then
|
|
391
|
+
echo "BLOCKING: Deterministic GUID for NavigationContext detected"
|
|
392
|
+
echo "Fix: Look up context by code at runtime in SeedDataProvider.SeedNavigationAsync()"
|
|
393
|
+
echo "$BAD_CTX_GUID"
|
|
394
|
+
exit 1
|
|
395
|
+
fi
|
|
396
|
+
fi
|
|
397
|
+
|
|
398
|
+
# POST-CHECK: RolePermission seed data must NOT use deterministic role GUIDs
|
|
399
|
+
SEED_ALL_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*.cs" 2>/dev/null)
|
|
400
|
+
SEED_CONST_FILES=$(find src/ -path "*/Seeding/*" -name "SeedConstants.cs" 2>/dev/null)
|
|
401
|
+
if [ -n "$SEED_ALL_FILES" ]; then
|
|
402
|
+
BAD_ROLE_GUID=$(grep -Pn 'DeterministicGuid\("role:' $SEED_ALL_FILES $SEED_CONST_FILES 2>/dev/null)
|
|
403
|
+
if [ -n "$BAD_ROLE_GUID" ]; then
|
|
404
|
+
echo "BLOCKING: Deterministic GUID for role detected"
|
|
405
|
+
echo "System roles are pre-seeded by SmartStack core — look up by Code at runtime"
|
|
406
|
+
echo "$BAD_ROLE_GUID"
|
|
407
|
+
exit 1
|
|
408
|
+
fi
|
|
409
|
+
fi
|
|
410
|
+
|
|
411
|
+
# POST-CHECK: Services must NOT use TenantId!.Value (crashes with 500 instead of 401)
|
|
412
|
+
SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
|
|
413
|
+
if [ -n "$SERVICE_FILES" ]; then
|
|
414
|
+
BAD_PATTERN=$(grep -Pn 'TenantId!\s*\.Value|TenantId!\s*\.ToString|\.TenantId!' $SERVICE_FILES 2>/dev/null)
|
|
415
|
+
if [ -n "$BAD_PATTERN" ]; then
|
|
416
|
+
echo "BLOCKING: TenantId!.Value causes 500 when tenant context is missing"
|
|
417
|
+
echo "Fix: var tenantId = _currentTenant.TenantId ?? throw new UnauthorizedAccessException(\"Tenant context is required\");"
|
|
418
|
+
echo "$BAD_PATTERN"
|
|
419
|
+
exit 1
|
|
420
|
+
fi
|
|
421
|
+
fi
|
|
422
|
+
|
|
423
|
+
# POST-CHECK: IAuditableEntity + Validator pairing
|
|
424
|
+
ENTITY_FILES=$(find src/ -path "*/Domain/Entities/Business/*" -name "*.cs" 2>/dev/null)
|
|
425
|
+
if [ -n "$ENTITY_FILES" ]; then
|
|
426
|
+
for f in $ENTITY_FILES; do
|
|
427
|
+
if grep -q "ITenantEntity" "$f" && ! grep -q "IAuditableEntity" "$f"; then
|
|
428
|
+
echo "WARNING: Entity has ITenantEntity but missing IAuditableEntity: $f"
|
|
429
|
+
fi
|
|
430
|
+
done
|
|
431
|
+
fi
|
|
432
|
+
CREATE_VALIDATORS=$(find src/ -path "*/Validators/*" -name "Create*Validator.cs" 2>/dev/null)
|
|
433
|
+
if [ -n "$CREATE_VALIDATORS" ]; then
|
|
434
|
+
for CV in $CREATE_VALIDATORS; do
|
|
435
|
+
UV=$(echo "$CV" | sed 's/Create/Update/')
|
|
436
|
+
if [ ! -f "$UV" ]; then
|
|
437
|
+
echo "BLOCKING: CreateValidator without matching UpdateValidator: $CV"
|
|
438
|
+
echo "Expected: $UV"
|
|
439
|
+
exit 1
|
|
440
|
+
fi
|
|
441
|
+
done
|
|
442
|
+
fi
|
|
316
443
|
```
|
|
317
444
|
|
|
318
445
|
**Error resolution cycle (ALL categories):**
|
|
@@ -15,9 +15,12 @@ next_step: steps/step-02-unit-tests.md
|
|
|
15
15
|
ls *.sln 2>/dev/null || find . -maxdepth 2 -name "*.sln" -type f
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
### 2.
|
|
18
|
+
### 2. Cleanup & Build
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
+
# Cleanup corrupted EF Core design-time artifacts (Roslyn BuildHost bug on Windows)
|
|
22
|
+
for d in src/*/bin?Debug; do [ -d "$d" ] && echo "Removing corrupted artifact: $d" && rm -rf "$d"; done
|
|
23
|
+
|
|
21
24
|
dotnet build {SolutionFile} --verbosity minimal
|
|
22
25
|
```
|
|
23
26
|
|