@atlashub/smartstack-cli 2.7.3 → 2.8.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 (56) hide show
  1. package/.documentation/agents.html +0 -4
  2. package/.documentation/business-analyse.html +0 -4
  3. package/.documentation/cli-commands.html +0 -4
  4. package/.documentation/commands.html +0 -77
  5. package/.documentation/css/styles.css +0 -8
  6. package/.documentation/efcore.html +0 -4
  7. package/.documentation/gitflow.html +0 -4
  8. package/.documentation/hooks.html +0 -4
  9. package/.documentation/index.html +2 -28
  10. package/.documentation/init.html +8 -14
  11. package/.documentation/installation.html +0 -11
  12. package/.documentation/js/app.js +2 -16
  13. package/.documentation/ralph-loop.html +0 -4
  14. package/.documentation/test-web.html +0 -4
  15. package/README.md +0 -1
  16. package/dist/index.js +100 -23
  17. package/dist/index.js.map +1 -1
  18. package/package.json +2 -3
  19. package/templates/agents/docs-sync-checker.md +2 -2
  20. package/templates/hooks/docs-drift-check.md +4 -5
  21. package/templates/skills/_resources/context-digest-template.md +2 -2
  22. package/templates/skills/_resources/doc-context-cache.md +0 -2
  23. package/templates/skills/_resources/docs-manifest-schema.md +1 -3
  24. package/templates/skills/_resources/mcp-validate-documentation-spec.md +1 -3
  25. package/templates/skills/_shared.md +24 -25
  26. package/templates/skills/application/steps/step-04-backend.md +185 -11
  27. package/templates/skills/application/steps/step-06-migration.md +41 -2
  28. package/templates/skills/application/templates-seed.md +151 -0
  29. package/templates/skills/business-analyse/steps/step-05-handoff.md +59 -17
  30. package/templates/skills/controller/steps/step-01-analyze.md +1 -1
  31. package/templates/skills/ralph-loop/steps/step-01-task.md +21 -1
  32. package/templates/skills/ralph-loop/steps/step-02-execute.md +75 -3
  33. package/.documentation/apex.html +0 -1027
  34. package/templates/skills/apex/SKILL.md +0 -297
  35. package/templates/skills/apex/steps/step-00-init.md +0 -212
  36. package/templates/skills/apex/steps/step-01-analyze.md +0 -263
  37. package/templates/skills/apex/steps/step-02-plan.md +0 -255
  38. package/templates/skills/apex/steps/step-03-execute.md +0 -217
  39. package/templates/skills/apex/steps/step-04-validate.md +0 -273
  40. package/templates/skills/apex/steps/step-04b-doc-sync.md +0 -162
  41. package/templates/skills/apex/steps/step-05-examine.md +0 -214
  42. package/templates/skills/apex/steps/step-06-resolve.md +0 -181
  43. package/templates/skills/apex/steps/step-07-tests.md +0 -206
  44. package/templates/skills/apex/steps/step-08-run-tests.md +0 -207
  45. package/templates/skills/apex/templates/00-context.md +0 -46
  46. package/templates/skills/apex/templates/01-analyze.md +0 -63
  47. package/templates/skills/apex/templates/02-plan.md +0 -63
  48. package/templates/skills/apex/templates/03-execute.md +0 -34
  49. package/templates/skills/apex/templates/04-validate.md +0 -61
  50. package/templates/skills/apex/templates/04b-doc-sync.md +0 -31
  51. package/templates/skills/apex/templates/05-examine.md +0 -58
  52. package/templates/skills/apex/templates/06-resolve.md +0 -39
  53. package/templates/skills/apex/templates/07-tests.md +0 -56
  54. package/templates/skills/apex/templates/08-run-tests.md +0 -41
  55. package/templates/skills/apex/templates/README.md +0 -69
  56. package/templates/skills/apex/templates/context-digest.md +0 -35
@@ -960,3 +960,154 @@ Level 5: RESOURCE (finest granularity)
960
960
  └─ Path: {context}.{application}.{module}.{section}.{resource}.{action}
961
961
  └─ E.g.: platform.administration.ai.prompts.blocks.delete
962
962
  ```
963
+
964
+ ---
965
+
966
+ ## SQL OBJECTS INFRASTRUCTURE
967
+
968
+ > **Usage:** SQL Server functions (TVFs, scalar), views, and stored procedures managed as embedded resources
969
+ > **Pattern:** Same as SmartStack.app — `CREATE OR ALTER` for idempotency, applied via migrations
970
+ > **Location:** `Infrastructure/Persistence/SqlObjects/`
971
+
972
+ ### Directory Structure
973
+
974
+ ```
975
+ Infrastructure/Persistence/SqlObjects/
976
+ ├── SqlObjectHelper.cs ← Helper to apply SQL from embedded resources
977
+ ├── Functions/ ← Table-Valued & Scalar functions
978
+ │ └── fn_{FunctionName}.sql ← CREATE OR ALTER FUNCTION [schema].[fn_Name]
979
+ ├── Views/ ← Future: SQL views
980
+ │ └── vw_{ViewName}.sql
981
+ └── StoredProcedures/ ← Future: Stored procedures
982
+ └── sp_{ProcName}.sql
983
+ ```
984
+
985
+ ### SqlObjectHelper Pattern
986
+
987
+ ```csharp
988
+ // Infrastructure/Persistence/SqlObjects/SqlObjectHelper.cs
989
+
990
+ using System.Reflection;
991
+ using Microsoft.EntityFrameworkCore.Migrations;
992
+
993
+ namespace {BaseNamespace}.Infrastructure.Persistence.SqlObjects;
994
+
995
+ /// <summary>
996
+ /// Helper for applying SQL objects from embedded resources.
997
+ /// SQL files use CREATE OR ALTER for idempotency — safe to re-run on any migration or squash.
998
+ /// </summary>
999
+ public static class SqlObjectHelper
1000
+ {
1001
+ private static readonly Assembly Assembly = typeof(SqlObjectHelper).Assembly;
1002
+ private const string ResourcePrefix = "{BaseNamespace}.Infrastructure.Persistence.SqlObjects.";
1003
+
1004
+ /// <summary>
1005
+ /// Applies all SQL objects (functions, views, stored procedures).
1006
+ /// Call in Up() method of a migration, especially after a squash.
1007
+ /// </summary>
1008
+ public static void ApplyAll(MigrationBuilder migrationBuilder)
1009
+ {
1010
+ ApplyAllFunctions(migrationBuilder);
1011
+ }
1012
+
1013
+ public static void ApplyAllFunctions(MigrationBuilder migrationBuilder)
1014
+ {
1015
+ ApplyCategory(migrationBuilder, "Functions");
1016
+ }
1017
+
1018
+ public static void ApplyOne(MigrationBuilder migrationBuilder, string resourceName)
1019
+ {
1020
+ var sql = ReadSqlObject(resourceName);
1021
+ migrationBuilder.Sql(sql);
1022
+ }
1023
+
1024
+ public static string ReadSqlObject(string resourceName)
1025
+ {
1026
+ var fullName = ResourcePrefix + resourceName;
1027
+ using var stream = Assembly.GetManifestResourceStream(fullName)
1028
+ ?? throw new InvalidOperationException(
1029
+ $"SQL object resource '{fullName}' not found. " +
1030
+ $"Ensure the .sql file is marked as EmbeddedResource in the .csproj. " +
1031
+ $"Available: {string.Join(", ", Assembly.GetManifestResourceNames().Where(n => n.EndsWith(".sql")))}");
1032
+ using var reader = new StreamReader(stream);
1033
+ return reader.ReadToEnd();
1034
+ }
1035
+
1036
+ private static void ApplyCategory(MigrationBuilder migrationBuilder, string category)
1037
+ {
1038
+ var prefix = ResourcePrefix + category + ".";
1039
+ var resources = Assembly.GetManifestResourceNames()
1040
+ .Where(n => n.StartsWith(prefix, StringComparison.Ordinal)
1041
+ && n.EndsWith(".sql", StringComparison.Ordinal))
1042
+ .OrderBy(n => n, StringComparer.Ordinal);
1043
+
1044
+ foreach (var resource in resources)
1045
+ {
1046
+ using var stream = Assembly.GetManifestResourceStream(resource)!;
1047
+ using var reader = new StreamReader(stream);
1048
+ migrationBuilder.Sql(reader.ReadToEnd());
1049
+ }
1050
+ }
1051
+ }
1052
+ ```
1053
+
1054
+ ### .csproj Configuration (REQUIRED)
1055
+
1056
+ ```xml
1057
+ <ItemGroup>
1058
+ <EmbeddedResource Include="Persistence\SqlObjects\**\*.sql" />
1059
+ </ItemGroup>
1060
+ ```
1061
+
1062
+ ### SQL File Convention
1063
+
1064
+ All `.sql` files MUST use `CREATE OR ALTER` for idempotency:
1065
+
1066
+ ```sql
1067
+ -- Infrastructure/Persistence/SqlObjects/Functions/fn_GetUserGroupHierarchy.sql
1068
+ CREATE OR ALTER FUNCTION [extensions].[fn_GetEntityHierarchy](@EntityId UNIQUEIDENTIFIER)
1069
+ RETURNS TABLE
1070
+ AS
1071
+ RETURN
1072
+ (
1073
+ WITH Hierarchy AS (
1074
+ SELECT Id, ParentId, 0 AS Level
1075
+ FROM [extensions].[fb_Entities]
1076
+ WHERE Id = @EntityId
1077
+ UNION ALL
1078
+ SELECT child.Id, child.ParentId, parent.Level + 1
1079
+ FROM [extensions].[fb_Entities] child
1080
+ INNER JOIN Hierarchy parent ON child.ParentId = parent.Id
1081
+ WHERE parent.Level < 10
1082
+ )
1083
+ SELECT DISTINCT Id FROM Hierarchy
1084
+ );
1085
+ ```
1086
+
1087
+ ### Migration Integration
1088
+
1089
+ In a migration's `Up()` method, call SqlObjectHelper to apply/re-apply SQL objects:
1090
+
1091
+ ```csharp
1092
+ protected override void Up(MigrationBuilder migrationBuilder)
1093
+ {
1094
+ // ... table creation, indexes, etc.
1095
+
1096
+ // Apply all SQL objects (idempotent — CREATE OR ALTER)
1097
+ SqlObjectHelper.ApplyAll(migrationBuilder);
1098
+ }
1099
+ ```
1100
+
1101
+ Or apply a single function:
1102
+ ```csharp
1103
+ SqlObjectHelper.ApplyOne(migrationBuilder, "Functions.fn_GetEntityHierarchy.sql");
1104
+ ```
1105
+
1106
+ ### When to Use SQL Objects
1107
+
1108
+ | Need | Solution |
1109
+ |------|----------|
1110
+ | Recursive hierarchy traversal | TVF with CTE (fn_GetXxxHierarchy) |
1111
+ | Complex aggregation for reports | SQL View (vw_XxxSummary) |
1112
+ | Bulk operations with business logic | Stored Procedure (sp_XxxBatch) |
1113
+ | Simple CRUD, filtering, sorting | EF Core LINQ (no SQL needed) |
@@ -313,43 +313,43 @@ From `specification.uiWireframes[]`, `specification.dashboards[]` and `analysis.
313
313
  ```json
314
314
  "seedData": [
315
315
  {
316
- "path": "src/Infrastructure/Data/SeedData/{ModuleName}/NavigationModuleConfiguration.cs",
317
- "type": "HasData",
316
+ "path": "src/Infrastructure/Persistence/Seeding/Data/{ModuleName}/NavigationModuleSeedData.cs",
317
+ "type": "SeedData",
318
318
  "category": "core",
319
319
  "source": "specification.seedDataCore.navigationModules",
320
320
  "module": "{moduleCode}"
321
321
  },
322
322
  {
323
- "path": "src/Infrastructure/Data/SeedData/{ModuleName}/PermissionsConfiguration.cs",
324
- "type": "HasData",
323
+ "path": "src/Infrastructure/Persistence/Seeding/Data/{ModuleName}/PermissionsSeedData.cs",
324
+ "type": "SeedData",
325
325
  "category": "core",
326
326
  "source": "specification.seedDataCore.permissions",
327
327
  "module": "{moduleCode}"
328
328
  },
329
329
  {
330
- "path": "src/Infrastructure/Data/SeedData/{ModuleName}/RolesConfiguration.cs",
331
- "type": "HasData",
330
+ "path": "src/Infrastructure/Persistence/Seeding/Data/{ModuleName}/RolesSeedData.cs",
331
+ "type": "SeedData",
332
332
  "category": "core",
333
333
  "source": "specification.seedDataCore.roles",
334
334
  "module": "{moduleCode}"
335
335
  },
336
336
  {
337
- "path": "src/Infrastructure/Data/SeedData/{ModuleName}/TenantConfiguration.cs",
338
- "type": "HasData",
337
+ "path": "src/Infrastructure/Persistence/Seeding/Data/{ModuleName}/TenantSeedData.cs",
338
+ "type": "SeedData",
339
339
  "category": "core",
340
340
  "source": "specification.seedDataCore.tenants",
341
341
  "module": "{moduleCode}"
342
342
  },
343
343
  {
344
- "path": "src/Infrastructure/Data/SeedData/{ModuleName}/UserConfiguration.cs",
345
- "type": "HasData",
344
+ "path": "src/Infrastructure/Persistence/Seeding/Data/{ModuleName}/UserSeedData.cs",
345
+ "type": "SeedData",
346
346
  "category": "core",
347
347
  "source": "specification.seedDataCore.users",
348
348
  "module": "{moduleCode}"
349
349
  },
350
350
  {
351
- "path": "src/Infrastructure/Data/SeedData/{ModuleName}/{Entity}SeedData.cs",
352
- "type": "HasData",
351
+ "path": "src/Infrastructure/Persistence/Seeding/Data/{ModuleName}/{Entity}SeedData.cs",
352
+ "type": "SeedData",
353
353
  "category": "business",
354
354
  "source": "specification.seedDataBusiness.{module}",
355
355
  "module": "{moduleCode}"
@@ -357,12 +357,51 @@ From `specification.uiWireframes[]`, `specification.dashboards[]` and `analysis.
357
357
  ]
358
358
  ```
359
359
 
360
+ **For client projects (ExtensionsDbContext), add these MANDATORY infrastructure files:**
361
+
362
+ ```json
363
+ "seedData": [
364
+ // ... 5 core + business entries above ...
365
+ {
366
+ "path": "src/Infrastructure/Persistence/Seeding/{AppPascalName}SeedDataProvider.cs",
367
+ "type": "IClientSeedDataProvider",
368
+ "category": "infrastructure",
369
+ "description": "Runtime provider that injects core seed data (navigation, permissions, roles) into Core schema",
370
+ "source": "specification.seedDataCore",
371
+ "module": "{moduleCode}"
372
+ },
373
+ {
374
+ "path": "src/Infrastructure/Persistence/Seeding/DevDataSeeder.cs",
375
+ "type": "DevDataSeeder",
376
+ "category": "infrastructure",
377
+ "description": "Seeds development/demo data for domain entities at startup",
378
+ "module": "{moduleCode}"
379
+ },
380
+ {
381
+ "path": "src/Infrastructure/Persistence/Seeding/Data/SeedConstants.cs",
382
+ "type": "SeedConstants",
383
+ "category": "infrastructure",
384
+ "description": "Shared constants for deterministic seed data",
385
+ "module": "{moduleCode}"
386
+ }
387
+ ]
388
+ ```
389
+
390
+ **Path convention:** All seed data files MUST be under `Persistence/Seeding/Data/` (matching SmartStack.app architecture).
391
+ NEVER use `Data/SeedData/` or `Infrastructure/Data/SeedData/`.
392
+
393
+ **IClientSeedDataProvider (MANDATORY for client projects):** This provider injects core seed data
394
+ (navigation, permissions, roles) into the Core schema at runtime. Generated at
395
+ `src/Infrastructure/Persistence/Seeding/{AppPascalName}SeedDataProvider.cs`.
396
+ See `/application` skill step-03b-provider for the full pattern.
397
+ Without this provider, the 5 core SeedData files are DEAD CODE and will have no effect.
398
+
360
399
  Core categories (ALWAYS 5):
361
- 1. NavigationModuleConfiguration (navigation items for module)
362
- 2. PermissionsConfiguration (RBAC permissions)
363
- 3. RolesConfiguration (roles using permissions)
364
- 4. TenantConfiguration (test tenants)
365
- 5. UserConfiguration (test users)
400
+ 1. NavigationModuleSeedData (navigation items for module)
401
+ 2. PermissionsSeedData (RBAC permissions)
402
+ 3. RolesSeedData (roles using permissions)
403
+ 4. TenantSeedData (test tenants)
404
+ 5. UserSeedData (test users)
366
405
 
367
406
  Business categories (from specification.seedDataBusiness):
368
407
  - Domain-specific reference data
@@ -635,6 +674,8 @@ Generate `.ralph/progress.txt` as main task tracker:
635
674
  □ Create {Entity2}Repository
636
675
  □ Configure DbContext for module
637
676
  □ Setup specifications for complex queries
677
+ □ Create IClientSeedDataProvider (client projects ONLY - injects core seeds at runtime)
678
+ □ Create DevDataSeeder + SeedConstants (if first module)
638
679
  Total: A tasks
639
680
 
640
681
  [API] REST Endpoints & Controllers
@@ -652,6 +693,7 @@ Generate `.ralph/progress.txt` as main task tracker:
652
693
  □ Create custom hook use{ModuleName}
653
694
  □ Implement pagination, filtering, sorting
654
695
  □ Validate all pages match their wireframe layout
696
+ □ ⚠️ Routes MUST be INSIDE Layout wrapper (AdminLayout/BusinessLayout/UserLayout)
655
697
  Total: C tasks
656
698
 
657
699
  [I18N] Internationalization Keys
@@ -35,7 +35,7 @@ Glob: "src/SmartStack.Domain/**/{entity}s.cs"
35
35
  **If Entity NOT found:**
36
36
  ```
37
37
  STOP - Entity {entity} not found in Domain layer.
38
- Create entity first: /apex add {entity} entity to Domain
38
+ Create entity first using /application skill to add the entity to Domain
39
39
  ```
40
40
 
41
41
  ### 2. Check DbContext
@@ -131,7 +131,27 @@ function transformPrdJsonToRalphV2(prdJson, moduleCode) {
131
131
  }
132
132
  }
133
133
 
134
- // 2. Add a final validation task
134
+ // 2. Add IClientSeedDataProvider task for client projects (ExtensionsDbContext)
135
+ // This is MANDATORY - without it, core seed data (navigation, permissions, roles) is dead code
136
+ const hasClientSeedData = filesToCreate["seedData"]?.some(f =>
137
+ f.type === "IClientSeedDataProvider" || f.path?.includes("SeedDataProvider"));
138
+ if (!hasClientSeedData && filesToCreate["seedData"]?.length > 0) {
139
+ tasks.push({
140
+ id: taskId,
141
+ description: `[infrastructure] Create IClientSeedDataProvider to inject core seed data at runtime`,
142
+ status: "pending",
143
+ category: "infrastructure",
144
+ dependencies: [lastIdByCategory["infrastructure"] || lastIdByCategory["domain"]].filter(Boolean),
145
+ acceptance_criteria: "IClientSeedDataProvider implements SeedNavigationAsync, SeedPermissionsAsync, SeedRolePermissionsAsync; registered in DI",
146
+ started_at: null, completed_at: null, iteration: null, commit_hash: null,
147
+ files_changed: { created: ["src/Infrastructure/Persistence/Seeding/{AppPascalName}SeedDataProvider.cs"], modified: ["src/Infrastructure/DependencyInjection.cs"] },
148
+ validation: null, error: null, module: moduleCode
149
+ });
150
+ lastIdByCategory["infrastructure"] = taskId;
151
+ taskId++;
152
+ }
153
+
154
+ // 3. Add a final validation task
135
155
  tasks.push({
136
156
  id: taskId,
137
157
  description: `[validation] Build, test, and MCP validate module ${moduleCode}`,
@@ -74,13 +74,85 @@ writeJSON('.ralph/prd.json', prd);
74
74
  |----------|-----------|----------|
75
75
  | `domain` | `validate_conventions` | Entity base class, IHasData, audit fields |
76
76
  | `application` | `validate_conventions`, `scaffold_extension` | CQRS, MediatR, FluentValidation |
77
- | `infrastructure` | `check_migrations`, `suggest_migration` | EF Core config, HasData seeding |
78
- | `api` | `scaffold_routes`, `validate_security` | Controller conventions, authorization |
79
- | `frontend` | `validate_frontend_routes`, `scaffold_frontend_extension` | React patterns, hooks |
77
+ | `infrastructure` | `check_migrations`, `suggest_migration` | EF Core config, HasData seeding, IClientSeedDataProvider, SqlObjects |
78
+ | `api` | `scaffold_routes`, `validate_security` | Controller conventions, authorization, context-based folder hierarchy |
79
+ | `frontend` | `validate_frontend_routes`, `scaffold_frontend_extension` | React patterns, hooks, Layout wrapper |
80
80
  | `i18n` | - | 4-language JSON structure |
81
81
  | `test` | `scaffold_tests`, `analyze_test_coverage` | xUnit, test naming conventions |
82
82
  | `validation` | `validate_conventions` | Build, test, lint checks |
83
83
 
84
+ **Infrastructure task guidance (MANDATORY directory conventions):**
85
+
86
+ When executing `infrastructure` category tasks, follow these rules strictly:
87
+
88
+ 1. **Seed data files** MUST go under `Infrastructure/Persistence/Seeding/Data/{Module}/`
89
+ - NEVER use `Infrastructure/Data/SeedData/` or `Data/SeedData/`
90
+ - Each module gets 5 core files: NavigationModuleSeedData.cs, PermissionsSeedData.cs, RolesSeedData.cs, TenantSeedData.cs, UserSeedData.cs
91
+
92
+ 2. **IClientSeedDataProvider** (client projects with `extensions` DbContext):
93
+ - Generate at `Infrastructure/Persistence/Seeding/{AppPascalName}SeedDataProvider.cs`
94
+ - Register in DI: `services.AddScoped<IClientSeedDataProvider, {AppPascalName}SeedDataProvider>()`
95
+ - Must implement: SeedNavigationAsync, SeedPermissionsAsync, SeedRolePermissionsAsync
96
+ - See `/application` skill step-03b-provider for the full pattern
97
+
98
+ 3. **DevDataSeeder** for domain entity seed data:
99
+ - Located at `Infrastructure/Persistence/Seeding/DevDataSeeder.cs`
100
+ - Implements `IDevDataSeeder`, registered in DI
101
+
102
+ 4. **Migrations** MUST all be in `Infrastructure/Persistence/Migrations/`
103
+ - NEVER create subdirectories under Migrations/
104
+ - Always use `-o Persistence/Migrations` flag with `dotnet ef migrations add`
105
+
106
+ 5. **SQL Objects** go under `Infrastructure/Persistence/SqlObjects/`
107
+ - `Functions/` for TVFs and scalar functions (.sql files)
108
+ - Use `SqlObjectHelper.cs` for embedded resource loading
109
+ - Use `CREATE OR ALTER` in SQL files for idempotency
110
+
111
+ **Frontend task guidance (MANDATORY Layout wrapper):**
112
+
113
+ When executing `frontend` category tasks, follow these rules strictly:
114
+
115
+ 1. **Routes MUST be inside Layout wrapper** (CRITICAL - violating this breaks the entire UI shell)
116
+ - All routes MUST be nested inside the appropriate context Layout component
117
+ - `platform.*` → `<Route path="/platform" element={<AdminLayout />}>` children
118
+ - `business.*` → `<Route path="/business" element={<BusinessLayout />}>` children
119
+ - `personal.*` → `<Route path="/personal/myspace" element={<UserLayout />}>` children
120
+ - **If routes are placed OUTSIDE the layout wrapper: header, sidebar, and AvatarMenu will NOT render**
121
+ - NEVER create flat routes at the top level of `<Routes>`
122
+
123
+ 2. **Nested routes, NOT flat routes**
124
+ - Use `<Route path="application"><Route path="module" element={...} /></Route>` pattern
125
+ - NEVER use `<Route path="/context/application/module" element={...} />` (flat)
126
+ - Use `<Route index element={<Navigate to="default" replace />} />` for default redirects
127
+
128
+ 3. **Tenant-prefixed routes** - If `/t/:slug/` block exists, add routes there too
129
+
130
+ 4. **See `/application` skill templates-frontend.md** for full template patterns
131
+
132
+ **API/Controller task guidance (MANDATORY folder hierarchy):**
133
+
134
+ When executing `api` category tasks, follow these rules strictly:
135
+
136
+ 1. **Controllers MUST be organized by context and application folders:**
137
+ - Path: `Api/Controllers/{ContextShort}/{Application}/{EntityName}Controller.cs`
138
+ - If no application sub-level: `Api/Controllers/{ContextShort}/{EntityName}Controller.cs`
139
+
140
+ 2. **Context-to-folder mapping (from navRoute):**
141
+
142
+ | NavRoute Prefix | Controller Folder |
143
+ |-----------------|-------------------|
144
+ | `platform.administration` | `Admin` |
145
+ | `platform.support` | `Support` |
146
+ | `business.*` | `Business` |
147
+ | `personal.*` | `User` |
148
+
149
+ 3. **Sub-folders for applications/modules with multiple controllers:**
150
+ - `Admin/Tenants/` for tenant-related controllers
151
+ - `Admin/AI/` for AI-related controllers
152
+ - `Admin/Communications/` for communication controllers
153
+
154
+ 4. **Reference:** SmartStack.app `Api/Controllers/` for the canonical structure
155
+
84
156
  ### 4. Explore Context (if needed)
85
157
 
86
158
  **Use subagents sparingly:**