@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.
Files changed (47) hide show
  1. package/dist/mcp-entry.mjs +201 -22
  2. package/dist/mcp-entry.mjs.map +1 -1
  3. package/package.json +1 -1
  4. package/templates/agents/efcore/conflicts.md +22 -2
  5. package/templates/agents/efcore/migration.md +11 -0
  6. package/templates/agents/efcore/rebase-snapshot.md +7 -0
  7. package/templates/agents/efcore/scan.md +24 -2
  8. package/templates/agents/efcore/squash.md +7 -0
  9. package/templates/agents/gitflow/init.md +195 -12
  10. package/templates/skills/apex/SKILL.md +14 -9
  11. package/templates/skills/apex/_shared.md +3 -0
  12. package/templates/skills/apex/references/analysis-methods.md +1 -1
  13. package/templates/skills/apex/references/challenge-questions.md +21 -0
  14. package/templates/skills/apex/references/core-seed-data.md +59 -104
  15. package/templates/skills/apex/references/post-checks.md +289 -225
  16. package/templates/skills/apex/references/smartstack-api.md +33 -35
  17. package/templates/skills/apex/references/smartstack-frontend.md +99 -3
  18. package/templates/skills/apex/references/smartstack-layers.md +145 -23
  19. package/templates/skills/apex/steps/step-00-init.md +2 -2
  20. package/templates/skills/apex/steps/step-01-analyze.md +1 -0
  21. package/templates/skills/apex/steps/step-02-plan.md +4 -3
  22. package/templates/skills/apex/steps/step-03-execute.md +24 -24
  23. package/templates/skills/apex/steps/step-04-examine.md +64 -24
  24. package/templates/skills/apex/steps/step-05-deep-review.md +1 -1
  25. package/templates/skills/apex/steps/step-08-run-tests.md +21 -13
  26. package/templates/skills/application/references/application-roles-template.md +10 -15
  27. package/templates/skills/application/references/backend-entity-seeding.md +6 -5
  28. package/templates/skills/application/references/backend-seeding-and-dto-output.md +1 -1
  29. package/templates/skills/application/references/nav-fallback-procedure.md +14 -17
  30. package/templates/skills/application/references/provider-template.md +5 -5
  31. package/templates/skills/application/references/roles-client-project-handling.md +1 -1
  32. package/templates/skills/application/references/roles-fallback-procedure.md +10 -15
  33. package/templates/skills/application/steps/step-01-navigation.md +1 -1
  34. package/templates/skills/application/steps/step-02-permissions.md +3 -3
  35. package/templates/skills/application/steps/step-03b-provider.md +1 -0
  36. package/templates/skills/application/templates-seed.md +41 -47
  37. package/templates/skills/business-analyse/references/team-orchestration.md +2 -2
  38. package/templates/skills/controller/steps/step-04-perms.md +1 -1
  39. package/templates/skills/efcore/references/troubleshooting.md +2 -2
  40. package/templates/skills/efcore/steps/rebase-snapshot/step-00-init.md +2 -2
  41. package/templates/skills/efcore/steps/squash/step-00-init.md +2 -2
  42. package/templates/skills/apex/references/examine-build-validation.md +0 -82
  43. package/templates/skills/apex/references/execution-frontend-gates.md +0 -177
  44. package/templates/skills/apex/references/execution-frontend-patterns.md +0 -105
  45. package/templates/skills/apex/references/execution-layer1-rules.md +0 -96
  46. package/templates/skills/apex/references/initialization-challenge-flow.md +0 -110
  47. package/templates/skills/apex/references/planning-layer-mapping.md +0 -151
@@ -7,17 +7,15 @@
7
7
  ## GUID GENERATION RULES
8
8
 
9
9
  ```csharp
10
- // NEVER generate sequential GUIDs with NewGuid()
11
- // ALWAYS use the deterministic method
10
+ // ALWAYS use Guid.NewGuid() for ALL seed data IDs
11
+ // This avoids conflicts between different projects/tenants/environments
12
+ // Idempotence is handled by Code-based lookups, NOT by fixed IDs
12
13
 
13
- private static Guid GenerateGuid(int index)
14
- {
15
- // Format: 11111111-1111-1111-1111-{index:D12}
16
- return Guid.Parse($"11111111-1111-1111-1111-{index:D12}");
17
- }
14
+ // For HasData() in Configuration files:
15
+ Id = Guid.NewGuid()
18
16
 
19
- // For existing seeds, continue the sequence
20
- // Check the last index used in NavigationTranslationConfiguration.cs
17
+ // For SeedData classes:
18
+ public static readonly Guid Sample1Id = Guid.NewGuid();
21
19
  ```
22
20
 
23
21
  ---
@@ -49,7 +47,7 @@ new {
49
47
  ```csharp
50
48
  // Application translations
51
49
  translations.Add(new {
52
- Id = GenerateGuid(index++),
50
+ Id = Guid.NewGuid(),
53
51
  EntityType = NavigationEntityType.Application,
54
52
  EntityId = $APP_GUID,
55
53
  LanguageCode = "fr",
@@ -58,7 +56,7 @@ translations.Add(new {
58
56
  CreatedAt = seedDate
59
57
  });
60
58
  translations.Add(new {
61
- Id = GenerateGuid(index++),
59
+ Id = Guid.NewGuid(),
62
60
  EntityType = NavigationEntityType.Application,
63
61
  EntityId = $APP_GUID,
64
62
  LanguageCode = "en",
@@ -67,7 +65,7 @@ translations.Add(new {
67
65
  CreatedAt = seedDate
68
66
  });
69
67
  translations.Add(new {
70
- Id = GenerateGuid(index++),
68
+ Id = Guid.NewGuid(),
71
69
  EntityType = NavigationEntityType.Application,
72
70
  EntityId = $APP_GUID,
73
71
  LanguageCode = "it",
@@ -76,7 +74,7 @@ translations.Add(new {
76
74
  CreatedAt = seedDate
77
75
  });
78
76
  translations.Add(new {
79
- Id = GenerateGuid(index++),
77
+ Id = Guid.NewGuid(),
80
78
  EntityType = NavigationEntityType.Application,
81
79
  EntityId = $APP_GUID,
82
80
  LanguageCode = "de",
@@ -114,7 +112,7 @@ new {
114
112
  ```csharp
115
113
  // Module translations
116
114
  translations.Add(new {
117
- Id = GenerateGuid(index++),
115
+ Id = Guid.NewGuid(),
118
116
  EntityType = NavigationEntityType.Module,
119
117
  EntityId = $MODULE_GUID,
120
118
  LanguageCode = "fr",
@@ -123,7 +121,7 @@ translations.Add(new {
123
121
  CreatedAt = seedDate
124
122
  });
125
123
  translations.Add(new {
126
- Id = GenerateGuid(index++),
124
+ Id = Guid.NewGuid(),
127
125
  EntityType = NavigationEntityType.Module,
128
126
  EntityId = $MODULE_GUID,
129
127
  LanguageCode = "en",
@@ -132,7 +130,7 @@ translations.Add(new {
132
130
  CreatedAt = seedDate
133
131
  });
134
132
  translations.Add(new {
135
- Id = GenerateGuid(index++),
133
+ Id = Guid.NewGuid(),
136
134
  EntityType = NavigationEntityType.Module,
137
135
  EntityId = $MODULE_GUID,
138
136
  LanguageCode = "it",
@@ -141,7 +139,7 @@ translations.Add(new {
141
139
  CreatedAt = seedDate
142
140
  });
143
141
  translations.Add(new {
144
- Id = GenerateGuid(index++),
142
+ Id = Guid.NewGuid(),
145
143
  EntityType = NavigationEntityType.Module,
146
144
  EntityId = $MODULE_GUID,
147
145
  LanguageCode = "de",
@@ -179,7 +177,7 @@ new {
179
177
  ```csharp
180
178
  // Section translations
181
179
  translations.Add(new {
182
- Id = GenerateGuid(index++),
180
+ Id = Guid.NewGuid(),
183
181
  EntityType = NavigationEntityType.Section,
184
182
  EntityId = $SECTION_GUID,
185
183
  LanguageCode = "fr",
@@ -188,7 +186,7 @@ translations.Add(new {
188
186
  CreatedAt = seedDate
189
187
  });
190
188
  translations.Add(new {
191
- Id = GenerateGuid(index++),
189
+ Id = Guid.NewGuid(),
192
190
  EntityType = NavigationEntityType.Section,
193
191
  EntityId = $SECTION_GUID,
194
192
  LanguageCode = "en",
@@ -197,7 +195,7 @@ translations.Add(new {
197
195
  CreatedAt = seedDate
198
196
  });
199
197
  translations.Add(new {
200
- Id = GenerateGuid(index++),
198
+ Id = Guid.NewGuid(),
201
199
  EntityType = NavigationEntityType.Section,
202
200
  EntityId = $SECTION_GUID,
203
201
  LanguageCode = "it",
@@ -206,7 +204,7 @@ translations.Add(new {
206
204
  CreatedAt = seedDate
207
205
  });
208
206
  translations.Add(new {
209
- Id = GenerateGuid(index++),
207
+ Id = Guid.NewGuid(),
210
208
  EntityType = NavigationEntityType.Section,
211
209
  EntityId = $SECTION_GUID,
212
210
  LanguageCode = "de",
@@ -510,9 +508,9 @@ new {
510
508
 
511
509
  ```
512
510
  $APP = sales
513
- $APP_GUID = e2e2e2e2-2222-2222-2222-222222222222
511
+ $APP_GUID = (generated via Guid.NewGuid() at seed time)
514
512
  $MODULE = products
515
- $MODULE_GUID = e3e3e3e3-3333-3333-3333-333333333333
513
+ $MODULE_GUID = (generated via Guid.NewGuid() at seed time)
516
514
 
517
515
  $LABEL_FR = Produits
518
516
  $LABEL_EN = Products
@@ -570,12 +568,13 @@ namespace SmartStack.Infrastructure.Persistence.Seeding.Data.{Domain};
570
568
  public static class {EntityName}SeedData
571
569
  {
572
570
  // ============================================================
573
- // DETERMINISTIC IDs - for referencing in other seed data
571
+ // RANDOM IDs - generated at class load, unique per deployment
572
+ // Idempotence is handled by AnyAsync() checks, NOT by fixed IDs
574
573
  // ============================================================
575
574
 
576
- public static readonly Guid Sample1Id = Guid.Parse("$SEED_GUID_1");
577
- public static readonly Guid Sample2Id = Guid.Parse("$SEED_GUID_2");
578
- public static readonly Guid Sample3Id = Guid.Parse("$SEED_GUID_3");
575
+ public static readonly Guid Sample1Id = Guid.NewGuid();
576
+ public static readonly Guid Sample2Id = Guid.NewGuid();
577
+ public static readonly Guid Sample3Id = Guid.NewGuid();
579
578
 
580
579
  /// <summary>
581
580
  /// Returns all demo {EntityName} entities.
@@ -641,7 +640,7 @@ private static IEnumerable<{EntityName}SeedItem> GetTenant1{EntityName}s()
641
640
  {
642
641
  new {EntityName}SeedItem
643
642
  {
644
- Id = Guid.Parse("$GUID"),
643
+ Id = Guid.NewGuid(),
645
644
  TenantId = tenantId,
646
645
  CreatedByUserId = userId,
647
646
  // ... properties
@@ -688,8 +687,8 @@ private async Task Seed{EntityName}sAsync(CancellationToken cancellationToken)
688
687
  // Map seedItem properties to factory method parameters
689
688
  );
690
689
 
691
- // Set deterministic ID (factory generates random GUID)
692
- typeof({EntityName}).GetProperty("Id")?.SetValue(entity, seedItem.Id);
690
+ // Factory method already generates a random GUID via Guid.NewGuid()
691
+ // No need to override — each seed run creates fresh unique IDs
693
692
 
694
693
  _context.{EntityName}s.Add(entity);
695
694
  createdCount++;
@@ -707,30 +706,25 @@ private async Task Seed{EntityName}sAsync(CancellationToken cancellationToken)
707
706
  ### Seed Data GUID Generation
708
707
 
709
708
  ```csharp
710
- // Option 1: Hardcoded descriptive GUIDs (preferred for small sets)
711
- public static readonly Guid ProductAlphaId = Guid.Parse("aa000001-0000-0000-0000-000000000001");
712
- public static readonly Guid ProductBetaId = Guid.Parse("aa000001-0000-0000-0000-000000000002");
709
+ // ALWAYS use Guid.NewGuid() avoids conflicts between projects/tenants/environments
710
+ public static readonly Guid ProductAlphaId = Guid.NewGuid();
711
+ public static readonly Guid ProductBetaId = Guid.NewGuid();
713
712
 
714
- // Option 2: SHA256-based for larger sets
715
- private static Guid GenerateEntityGuid(string uniqueKey)
716
- {
717
- using var sha256 = System.Security.Cryptography.SHA256.Create();
718
- var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes($"{EntityName}-{uniqueKey}"));
719
- return new Guid(hash.Take(16).ToArray());
720
- }
713
+ // FORBIDDEN: Deterministic GUIDs (SHA256, sequential, hardcoded)
714
+ // These create conflicts when multiple projects seed into the same database
721
715
  ```
722
716
 
723
717
  ### Best Practices
724
718
 
725
719
  | Practice | Description |
726
720
  |----------|-------------|
727
- | Deterministic IDs | NEVER use `Guid.NewGuid()` - seeds must be idempotent |
728
- | Idempotent | Always check `if exists` before creating |
721
+ | Random IDs | ALWAYS use `Guid.NewGuid()` avoids cross-project/tenant conflicts |
722
+ | Idempotent | Always check `if exists` before creating (by Code, not by ID) |
729
723
  | Use factory methods | Call `Entity.Create(...)` not `new Entity()` |
730
- | Set ID via reflection | Factory methods generate random IDs; override after creation |
724
+ | FK by Code lookup | Resolve foreign keys by Code at runtime, not by hardcoded GUID |
731
725
  | Realistic data | Use plausible names, descriptions, amounts |
732
726
  | Cover all states | Include entities in different statuses (active, closed, etc.) |
733
- | Reference existing seeds | Use IDs from TenantSeedData, UserSeedData for foreign keys |
727
+ | Reference existing seeds | Resolve FKs via Code/Name lookups, not static GUIDs |
734
728
  | SaveChanges per batch | Call SaveChanges after each entity group |
735
729
 
736
730
  ---
@@ -895,7 +889,7 @@ public class {AppPascalName}SeedDataProvider : IClientSeedDataProvider
895
889
  | Factory methods | `NavigationModule.Create(...)`, `NavigationSection.Create(...)`, `NavigationResource.Create(...)`, `Permission.CreateForModule(...)` - NEVER `new Entity()` |
896
890
  | Idempotence | Each Seed method checks existence before inserting |
897
891
  | SaveChanges per group | Navigation -> save -> Roles -> save -> Permissions -> save -> RolePermissions -> save |
898
- | Deterministic GUIDs | Use IDs from SeedData classes (not `Guid.NewGuid()`) |
892
+ | Random GUIDs | ALWAYS use `Guid.NewGuid()` — resolve FKs by Code lookup |
899
893
  | FK resolution by Code | Parent modules found by `Code`, not hardcoded GUID |
900
894
  | Section/Resource conditionality | Only generate if `seedDataCore.navigationSections` / `seedDataCore.navigationResources` exist in feature.json |
901
895
 
@@ -905,9 +899,9 @@ public class {AppPascalName}SeedDataProvider : IClientSeedDataProvider
905
899
 
906
900
  | Check | Status |
907
901
  |-------|--------|
908
- | ☐ Deterministic GUID (not NewGuid) | |
902
+ | ☐ Random GUIDs via Guid.NewGuid() (no deterministic/sequential/fixed) | |
909
903
  | ☐ 4 languages for each navigation entity (modules, sections, resources) | |
910
- | ☐ Index translations continue existing sequence | |
904
+ | ☐ Translation IDs use Guid.NewGuid() | |
911
905
  | ☐ Route aligned with permission path | |
912
906
  | ☐ DisplayOrder consistent | |
913
907
  | ☐ CRUD permissions created (Level 2 - Module) | |
@@ -512,8 +512,8 @@ IF layer.modules.length >= 2 AND all modules approved:
512
512
  3. i18n keys: Do keys follow consistent patterns? No collisions?
513
513
  → {module1}.labels.name vs {module2}.labels.name (OK — namespaced)
514
514
 
515
- 4. Seed data IDs: Are deterministic GUIDs unique across modules?
516
- Check DeterministicGuid inputs don't collide
515
+ 4. Seed data IDs: All GUIDs use Guid.NewGuid() (no deterministic/fixed values)
516
+ Verify no hardcoded or deterministic GUID patterns
517
517
 
518
518
  5. Entity attribute types: Are shared concepts typed consistently?
519
519
  → If both modules define "Status", is it the same enum type?
@@ -20,7 +20,7 @@ Add permissions to both Permissions.cs (constants) and PermissionConfiguration.c
20
20
 
21
21
  See [references/permission-sync-templates.md](../references/permission-sync-templates.md) for the C# templates:
22
22
  - **Permissions.cs**: Nested class with 4 constants (View, Create, Update, Delete) + path-to-class mapping
23
- - **PermissionConfiguration.cs**: 4 HasData Permission entries with deterministic GUIDs
23
+ - **PermissionConfiguration.cs**: 4 HasData Permission entries with Guid.NewGuid()
24
24
 
25
25
  ### 3. Generate Migration (Required)
26
26
 
@@ -41,7 +41,7 @@ Option 1: HasData() in Configuration (RECOMMENDED)
41
41
  --------------------------------------------------
42
42
  // In UserConfiguration.cs
43
43
  builder.HasData(new User {
44
- Id = Guid.Parse("7f3c9a2e-8d1b-4e5f-a6c8-9b4d2f7e1a3c"),
44
+ Id = Guid.NewGuid(),
45
45
  Name = "Admin",
46
46
  Email = "admin@example.com"
47
47
  });
@@ -74,7 +74,7 @@ INSERT INTO Users (Id, Name) VALUES (1, 'Admin');
74
74
 
75
75
  // AFTER (CORRECT): UserConfiguration.cs
76
76
  builder.HasData(new User {
77
- Id = Guid.Parse("7f3c9a2e-8d1b-4e5f-a6c8-9b4d2f7e1a3c"),
77
+ Id = Guid.NewGuid(),
78
78
  Name = "Admin"
79
79
  });
80
80
  // Then: dotnet ef migrations add SeedData
@@ -80,10 +80,10 @@ PARENT_MIGRATIONS=$(git ls-tree -r --name-only "origin/$BASE_BRANCH" -- "$MIGRAT
80
80
  # Get local migrations
81
81
  LOCAL_MIGRATIONS=$(find "$MIGRATIONS_DIR" -name "*.cs" 2>/dev/null | grep -v "Designer\|Snapshot" | xargs -I{} basename {} 2>/dev/null || echo "")
82
82
 
83
- # Find branch-only migrations
83
+ # Find branch-only migrations (exact match, not substring)
84
84
  BRANCH_MIGRATIONS=()
85
85
  for mig in $LOCAL_MIGRATIONS; do
86
- if ! echo "$PARENT_MIGRATIONS" | grep -q "$(basename "$mig")"; then
86
+ if ! echo "$PARENT_MIGRATIONS" | grep -qx "$(basename "$mig")"; then
87
87
  BRANCH_MIGRATIONS+=("$mig")
88
88
  fi
89
89
  done
@@ -38,10 +38,10 @@ BASE_MIGRATIONS=$(git ls-tree -r --name-only "origin/$BASE_BRANCH" -- "$MIGRATIO
38
38
  # Get local migrations
39
39
  LOCAL_MIGRATIONS=$(find "$MIGRATIONS_DIR" -name "*.cs" 2>/dev/null | grep -v "Designer\|Snapshot" | xargs -I{} basename {} 2>/dev/null || echo "")
40
40
 
41
- # Find migrations unique to this branch
41
+ # Find migrations unique to this branch (exact match, not substring)
42
42
  BRANCH_ONLY_MIGRATIONS=()
43
43
  for mig in $LOCAL_MIGRATIONS; do
44
- if ! echo "$BASE_MIGRATIONS" | grep -q "$(basename "$mig")"; then
44
+ if ! echo "$BASE_MIGRATIONS" | grep -qx "$(basename "$mig")"; then
45
45
  BRANCH_ONLY_MIGRATIONS+=("$mig")
46
46
  fi
47
47
  done
@@ -1,82 +0,0 @@
1
- # Examine Build & Migration Validation
2
-
3
- > **Loaded by:** step-04-examine.md (sections 4-5)
4
- > **Purpose:** Build verification, migration validation, database testing procedures.
5
-
6
- ---
7
-
8
- ## Build Verification
9
-
10
- ```bash
11
- # Backend
12
- dotnet clean && dotnet restore && dotnet build
13
- # Note: WSL bin\Debug cleanup handled by PostToolUse hook (wsl-dotnet-cleanup.sh)
14
-
15
- # Frontend (if applicable)
16
- npm run typecheck
17
- ```
18
-
19
- **BLOCKING:** Both must pass. If failure, classify error per `references/error-classification.md`:
20
- - Category A (missing package) → `dotnet add package` → rebuild
21
- - Category B (assembly conflict) → resolve version → rebuild
22
- - Category C (DI missing) → fix DI registration → rebuild
23
- - Category D (migration broken) → fix migration → rebuild
24
- - Category E (config) → fix config → rebuild
25
- - Category F (source code) → fix code → rebuild
26
-
27
- ---
28
-
29
- ## Migration Validation (if needs_migration)
30
-
31
- ### Pending Model Changes Check
32
-
33
- ```bash
34
- INFRA_PROJECT=$(ls src/*Infrastructure*/*.csproj 2>/dev/null | head -1)
35
- API_PROJECT=$(ls src/*Api*/*.csproj 2>/dev/null | head -1)
36
-
37
- dotnet ef migrations has-pending-model-changes \
38
- --project "$INFRA_PROJECT" \
39
- --startup-project "$API_PROJECT"
40
- ```
41
-
42
- **BLOCKING** if pending changes detected → migration is missing.
43
-
44
- ### Migration Application Test (SQL Server LocalDB)
45
-
46
- ```bash
47
- DB_NAME="SmartStack_Apex_Examine_$(date +%s)"
48
- CONN_STRING="Server=(localdb)\\MSSQLLocalDB;Database=$DB_NAME;Integrated Security=true;TrustServerCertificate=true;Connect Timeout=120;"
49
-
50
- dotnet ef database update \
51
- --connection "$CONN_STRING" \
52
- --project "$INFRA_PROJECT" \
53
- --startup-project "$API_PROJECT"
54
- ```
55
-
56
- **BLOCKING** if migration fails on SQL Server. Common issues:
57
- - SQLite-only syntax in migrations (fix: regenerate migration)
58
- - Column type mismatches (fix: update EF configuration)
59
- - Missing foreign key targets (fix: reorder migrations)
60
-
61
- ### Integration Tests on Real SQL Server
62
-
63
- ```bash
64
- # Integration tests use DatabaseFixture → real SQL Server LocalDB
65
- # This validates: LINQ→SQL, multi-tenant isolation, soft delete, EF configs
66
- INT_TEST_PROJECT=$(ls tests/*Tests.Integration*/*.csproj 2>/dev/null | head -1)
67
- if [ -n "$INT_TEST_PROJECT" ]; then
68
- dotnet test "$INT_TEST_PROJECT" --no-build --verbosity normal
69
- fi
70
- ```
71
-
72
- Tests running against SQL Server catch issues that SQLite misses:
73
- - Case sensitivity in string comparisons
74
- - Date/time function differences
75
- - IDENTITY vs AUTOINCREMENT behavior
76
- - Global query filter translation to T-SQL
77
-
78
- ### Cleanup
79
-
80
- ```bash
81
- 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
82
- ```
@@ -1,177 +0,0 @@
1
- # Frontend Compliance Gate — 5 Mandatory Checks
2
-
3
- > **Loaded by:** step-03-execute.md (FRONTEND COMPLIANCE GATE section)
4
- > **Condition:** MANDATORY before any frontend commit
5
- > **Purpose:** Automated checks that catch the most common failures in generated code.
6
-
7
- ---
8
-
9
- ## Gate 1: CSS Variables (Theme System)
10
-
11
- Check for hardcoded Tailwind colors — MUST use CSS variables.
12
-
13
- ```bash
14
- ALL_PAGES=$(find src/pages/ src/components/ -name "*.tsx" 2>/dev/null | grep -v node_modules | grep -v "\.test\.")
15
- if [ -n "$ALL_PAGES" ]; then
16
- HARDCODED=$(grep -Pn '(bg|text|border)-(?!\[)(red|blue|green|gray|white|black|slate|zinc|neutral|stone)-' $ALL_PAGES 2>/dev/null)
17
- if [ -n "$HARDCODED" ]; then
18
- echo "FAIL: Hardcoded Tailwind colors found — must use CSS variables"
19
- echo "$HARDCODED"
20
- else
21
- echo "PASS: CSS variables"
22
- fi
23
- fi
24
- ```
25
-
26
- **If hardcoded colors found, replace BEFORE committing:**
27
-
28
- | Old | New |
29
- |-----|-----|
30
- | `bg-white` | `bg-[var(--bg-card)]` |
31
- | `bg-gray-50` | `bg-[var(--bg-primary)]` |
32
- | `text-gray-900` | `text-[var(--text-primary)]` |
33
- | `text-gray-500/600` | `text-[var(--text-secondary)]` |
34
- | `border-gray-200` | `border-[var(--border-color)]` |
35
- | `bg-blue-600` / `text-blue-600` | `bg-[var(--color-accent-500)]` / `text-[var(--color-accent-500)]` |
36
- | `hover:bg-blue-700` | `hover:bg-[var(--color-accent-600)]` |
37
- | `text-red-500` | `text-[var(--error-text)]` |
38
- | `bg-green-500` | `bg-[var(--success-bg)]` |
39
-
40
- ---
41
-
42
- ## Gate 2: Forms as Pages (ZERO Modals/Drawers/Slide-overs)
43
-
44
- Check for modal/dialog/drawer/slide-over imports and inline form patterns — FORBIDDEN.
45
-
46
- ```bash
47
- PAGE_FILES=$(find src/pages/ -name "*.tsx" 2>/dev/null)
48
- if [ -n "$PAGE_FILES" ]; then
49
- FAIL=false
50
-
51
- # 2a. Component imports
52
- MODAL_IMPORTS=$(grep -Pn "import.*(?:Modal|Dialog|Drawer|Popup|Sheet|SlideOver|Overlay)" $PAGE_FILES 2>/dev/null)
53
- if [ -n "$MODAL_IMPORTS" ]; then
54
- echo "FAIL: Modal/Dialog/Drawer component imports — forms MUST be full pages"
55
- echo "$MODAL_IMPORTS"
56
- FAIL=true
57
- fi
58
-
59
- # 2b. State variables for inline forms (catches drawers/slide-overs without imports)
60
- MODAL_STATE=$(grep -Pn "useState.*(?:isOpen|showModal|showDialog|showCreate|showEdit|showForm|isCreating|isEditing|showDrawer|showPanel|showSlideOver|selectedEntity|editingEntity)" $PAGE_FILES 2>/dev/null)
61
- if [ -n "$MODAL_STATE" ]; then
62
- echo "FAIL: Inline form state detected — forms MUST be separate page components with own routes"
63
- echo "$MODAL_STATE"
64
- FAIL=true
65
- fi
66
-
67
- if [ "$FAIL" = true ]; then
68
- echo "Fix: Create EntityCreatePage.tsx (route: /create) and EntityEditPage.tsx (route: /:id/edit)"
69
- echo "See smartstack-frontend.md section 3b"
70
- else
71
- echo "PASS: No modals/drawers"
72
- fi
73
- fi
74
- ```
75
-
76
- **If modals/drawers found:** Replace with separate `EntityCreatePage.tsx` (route: `/{module}/create`) and `EntityEditPage.tsx` (route: `/{module}/:id/edit`). See `smartstack-frontend.md` section 3b.
77
-
78
- ---
79
-
80
- ## Gate 3: I18n File Structure
81
-
82
- Verify translation files exist as separate JSON per language.
83
-
84
- ```bash
85
- if [ ! -d "src/i18n/locales" ]; then
86
- echo "FAIL: Missing src/i18n/locales/ directory — create it with 4 languages"
87
- else
88
- for LANG in fr en it de; do
89
- JSON_FILES=$(find "src/i18n/locales/$LANG" -name "*.json" 2>/dev/null | wc -l)
90
- if [ "$JSON_FILES" -eq 0 ]; then
91
- echo "FAIL: No JSON files in src/i18n/locales/$LANG/"
92
- else
93
- echo "PASS: $LANG ($JSON_FILES files)"
94
- fi
95
- done
96
- fi
97
- ```
98
-
99
- **If i18n structure wrong:** Create `src/i18n/locales/{fr,en,it,de}/{module}.json` following the template in `smartstack-frontend.md` section 2. NEVER embed translations in a single `.ts` file.
100
-
101
- **Correct structure:**
102
- ```
103
- src/i18n/locales/
104
- ├── fr/{module}.json ← French (primary)
105
- ├── en/{module}.json ← English
106
- ├── it/{module}.json ← Italian
107
- └── de/{module}.json ← German
108
- ```
109
-
110
- Each file MUST contain: `title`, `description`, `actions`, `labels`, `columns`, `form`, `errors`, `validation`, `messages`, `empty`.
111
-
112
- ---
113
-
114
- ## Gate 4: Lazy Loading
115
-
116
- Check for static page imports in route/App files.
117
-
118
- ```bash
119
- APP_TSX=$(find src/ -name "App.tsx" -not -path "*/node_modules/*" 2>/dev/null | head -1)
120
- ROUTE_FILES=$(find src/routes/ -name "*.tsx" -o -name "*.ts" 2>/dev/null)
121
- if [ -n "$APP_TSX" ]; then
122
- STATIC_IMPORTS=$(grep -Pn "^import .+ from '@/pages/" "$APP_TSX" $ROUTE_FILES 2>/dev/null)
123
- if [ -n "$STATIC_IMPORTS" ]; then
124
- echo "FAIL: Static page imports in App.tsx/routes — MUST use React.lazy()"
125
- echo "$STATIC_IMPORTS"
126
- else
127
- echo "PASS: Lazy loading"
128
- fi
129
- fi
130
- ```
131
-
132
- ---
133
-
134
- ## Gate 5: useTranslation in Pages
135
-
136
- Verify pages use i18n.
137
-
138
- ```bash
139
- PAGE_FILES=$(find src/pages/ -name "*.tsx" 2>/dev/null | grep -v "\.test\." | grep -v node_modules)
140
- if [ -n "$PAGE_FILES" ]; then
141
- TOTAL=$(echo "$PAGE_FILES" | wc -l)
142
- WITH_I18N=$(grep -l "useTranslation" $PAGE_FILES 2>/dev/null | wc -l)
143
- if [ "$WITH_I18N" -eq 0 ]; then
144
- echo "FAIL: No pages use useTranslation — all text must be translated"
145
- else
146
- echo "PASS: $WITH_I18N/$TOTAL pages use useTranslation"
147
- fi
148
- fi
149
- ```
150
-
151
- ---
152
-
153
- ## Explicit I18n File Creation
154
-
155
- When creating i18n files, generate EXACTLY this structure:
156
-
157
- ```
158
- src/i18n/locales/
159
- ├── fr/{module}.json ← French (primary)
160
- ├── en/{module}.json ← English
161
- ├── it/{module}.json ← Italian
162
- └── de/{module}.json ← German
163
- ```
164
-
165
- Each file MUST contain these keys: `title`, `description`, `actions`, `labels`, `columns`, `form`, `errors`, `validation`, `messages`, `empty`. See `smartstack-frontend.md` section 2 for the complete JSON template.
166
-
167
- ---
168
-
169
- ## ALL 5 Gates MUST PASS
170
-
171
- **Before creating the frontend commit:**
172
- 1. Do NOT commit frontend changes until ALL checks pass
173
- 2. If ANY gate fails, fix the issues first
174
- 3. When delegating to `/ui-components` skill, include explicit instructions:
175
- - "CSS: Use CSS variables ONLY — `bg-[var(--bg-card)]`, `text-[var(--text-primary)]`. NEVER use hardcoded Tailwind colors."
176
- - "Forms: Create/Edit forms are FULL PAGES with own routes (e.g., `/create`, `/:id/edit`). NEVER use modals/dialogs."
177
- - "I18n: ALL text must use `t('namespace:key', 'Fallback')`. Generate JSON files in `src/i18n/locales/`."
@@ -1,105 +0,0 @@
1
- # Frontend Patterns — Economy Mode Guidelines
2
-
3
- > **Loaded by:** step-03-execute.md (Layer 1 section: economy_mode frontend tasks)
4
- > **Purpose:** Quick reference for frontend creation in sequential (economy) mode.
5
- > **Detailed patterns:** See `smartstack-frontend.md` (referenced from smartstack-layers.md)
6
-
7
- ---
8
-
9
- ## Frontend Tasks — Sequential Execution (economy_mode)
10
-
11
- For each frontend task in the plan (Layer 1):
12
-
13
- 1. **API Client:** `MCP scaffold_api_client` → API client + types + React Query hook
14
- 2. **Routes:** `MCP scaffold_routes` with `outputFormat: 'clientRoutes'` for lazy imports
15
- 3. **Pages:** **INVOKE `/ui-components` skill** (read SKILL.md + ALL patterns) — MANDATORY for ALL page types
16
-
17
- ---
18
-
19
- ## Required Page Types Per Module
20
-
21
- Create ALL 4 page types per module:
22
- - `ListPage.tsx` — entity list with SmartTable/EntityCard
23
- - `DetailPage.tsx` — entity detail view
24
- - `EntityCreatePage.tsx` (route: `/create`) — FULL PAGE form, NEVER modal
25
- - `EntityEditPage.tsx` (route: `/:id/edit`) — FULL PAGE form, NEVER modal
26
-
27
- **Wire ALL routes in App.tsx:** `index` (ListPage), `:id` (DetailPage), `create` (CreatePage), `:id/edit` (EditPage)
28
-
29
- ---
30
-
31
- ## Foreign Key (FK) Fields — CRITICAL
32
-
33
- Any Guid FK property (e.g., EmployeeId, DepartmentId) MUST use `EntityLookup` component:
34
- - **NEVER** a `<select>` dropdown
35
- - **NEVER** a `<input type="text">`
36
- - **A `<select>` loaded from API state is NOT a substitute for EntityLookup**
37
-
38
- See `smartstack-frontend.md` section 6 for the full EntityLookup pattern. Backend GetAll endpoints MUST support `?search=` parameter (enables EntityLookup on frontend).
39
-
40
- ---
41
-
42
- ## Forms: ZERO Modals/Popups/Drawers/Slide-overs
43
-
44
- **ALL forms are full pages with their own URL:**
45
- - Create forms: route `/{module}/create`
46
- - Edit forms: route `/{module}/:id/edit`
47
- - NEVER embed forms as drawers, panels, or slide-overs
48
- - Back button with `navigate(-1)` on every form page
49
-
50
- ---
51
-
52
- ## Detail Pages: Tab Behavior (CRITICAL)
53
-
54
- **Tabs MUST switch content LOCALLY via `setActiveTab()` — NEVER `navigate()` to another page.**
55
-
56
- Sub-resource data (e.g., employee's leaves) loads inline via API call filtered by parent entity ID. See `smartstack-frontend.md` section 3 "Tab Behavior Rules".
57
-
58
- ---
59
-
60
- ## Tests (MANDATORY)
61
-
62
- Generate form tests as co-located files:
63
- - `EntityCreatePage.test.tsx` (next to CreatePage.tsx)
64
- - `EntityEditPage.test.tsx` (next to EditPage.tsx)
65
-
66
- Cover: rendering, validation, submit, pre-fill, navigation, errors. See `smartstack-frontend.md` section 8 for test templates.
67
-
68
- ---
69
-
70
- ## Section-Level Routes (if sections exist)
71
-
72
- **SECTION PERMISSIONS:** After calling `MCP generate_permissions` for the module navRoute (2 segments: `{app}.{module}`), also call it for EACH section navRoute (3 segments: `{app}.{module}.{section}`)
73
-
74
- **SECTION ROUTES:** After generating module routes, add section child routes to the module's `children` array. Wire `PermissionGuard` for section routes with section-level permissions.
75
-
76
- ---
77
-
78
- ## Sub-Resource Handling
79
-
80
- If a section controller has sub-resource endpoints (e.g., `[HttpGet("types")]` for LeaveTypes inside LeavesController), you MUST EITHER:
81
- 1. Create dedicated frontend pages for the sub-resource (ListPage, CreatePage, EditPage) with routes wired in App.tsx, OR
82
- 2. NOT include any `navigate()` button that links to those sub-resource pages
83
-
84
- **Prefer separate controllers** with `[NavRoute(..., Suffix = "types")]` — see `smartstack-api.md` Sub-Resource Pattern. A dead link (navigate to a route with no page) is a BLOCKING issue (POST-CHECK 42).
85
-
86
- ---
87
-
88
- ## I18n (Translations)
89
-
90
- Generate i18n JSON files for all 4 languages (fr, en, it, de):
91
- - Location: `src/i18n/locales/{lang}/{module}.json`
92
- - Keys: `actions`, `labels`, `errors`, `validation`, `columns`, `form`, `messages`, `empty`
93
- - ALL `t()` calls MUST use namespace prefix + fallback: `t('ns:key', 'Default text')`
94
-
95
- **CRITICAL:** After creating i18n JSON files, register EACH new namespace in the i18n config file (config.ts/index.ts/i18n.ts). Unregistered namespaces → `useTranslation(['module'])` returns empty strings at runtime. POST-CHECK 45 validates this.
96
-
97
- ---
98
-
99
- ## Folder Structure
100
-
101
- MUST use: `src/pages/{App}/{Module}/` hierarchy (NOT flat).
102
-
103
- All pages must follow: **hooks → useEffect(load) → loading state → error state → content**
104
-
105
- Use **CSS variables ONLY** for styling — hardcoded Tailwind colors are BLOCKING (POST-CHECK 13).