@atlashub/smartstack-cli 4.32.0 → 4.34.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/.documentation/index.html +2 -2
- package/.documentation/init.html +358 -174
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +271 -44
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/mcp-scaffolding/controller.cs.hbs +54 -128
- package/templates/project/README.md +19 -0
- package/templates/project/claude-md/api.CLAUDE.md.template +315 -0
- package/templates/project/claude-md/application.CLAUDE.md.template +181 -0
- package/templates/project/claude-md/domain.CLAUDE.md.template +125 -0
- package/templates/project/claude-md/infrastructure.CLAUDE.md.template +168 -0
- package/templates/project/claude-md/root.CLAUDE.md.template +339 -0
- package/templates/project/claude-md/web.CLAUDE.md.template +339 -0
- package/templates/skills/apex/SKILL.md +16 -10
- package/templates/skills/apex/_shared.md +1 -1
- package/templates/skills/apex/references/checks/architecture-checks.sh +154 -0
- package/templates/skills/apex/references/checks/backend-checks.sh +194 -0
- package/templates/skills/apex/references/checks/frontend-checks.sh +448 -0
- package/templates/skills/apex/references/checks/infrastructure-checks.sh +255 -0
- package/templates/skills/apex/references/checks/security-checks.sh +153 -0
- package/templates/skills/apex/references/checks/seed-checks.sh +536 -0
- package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +49 -192
- package/templates/skills/apex/references/post-checks.md +124 -2156
- package/templates/skills/apex/references/smartstack-api.md +160 -957
- package/templates/skills/apex/references/smartstack-frontend.md +134 -1022
- package/templates/skills/apex/references/smartstack-layers.md +12 -6
- package/templates/skills/apex/steps/step-00-init.md +81 -238
- package/templates/skills/apex/steps/step-03-execute.md +25 -752
- package/templates/skills/apex/steps/step-03a-layer0-domain.md +118 -0
- package/templates/skills/apex/steps/step-03b-layer1-seed.md +91 -0
- package/templates/skills/apex/steps/step-03c-layer2-backend.md +240 -0
- package/templates/skills/apex/steps/step-03d-layer3-frontend.md +300 -0
- package/templates/skills/apex/steps/step-03e-layer4-devdata.md +44 -0
- package/templates/skills/apex/steps/step-04-examine.md +70 -150
- package/templates/skills/application/references/frontend-i18n-and-output.md +2 -2
- package/templates/skills/application/references/frontend-route-naming.md +5 -1
- package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +49 -198
- package/templates/skills/application/references/frontend-verification.md +11 -11
- package/templates/skills/application/steps/step-05-frontend.md +26 -15
- package/templates/skills/application/templates-frontend.md +4 -0
- package/templates/skills/cli-app-sync/SKILL.md +2 -2
- package/templates/skills/cli-app-sync/references/comparison-map.md +1 -1
- package/templates/skills/controller/references/controller-code-templates.md +70 -67
- package/templates/skills/controller/references/mcp-scaffold-workflow.md +5 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: step-03a-layer0-domain
|
|
3
|
+
description: "Layer 0: Domain entities, EF configs, migration"
|
|
4
|
+
model: opus
|
|
5
|
+
parent_step: steps/step-03-execute.md
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Layer 0 — Domain + Infrastructure (sequential, agent principal)
|
|
9
|
+
|
|
10
|
+
### Task Progress
|
|
11
|
+
TaskUpdate(taskId: layer0_task_id, status: "in_progress")
|
|
12
|
+
TaskUpdate(taskId: progress_tracker_id,
|
|
13
|
+
description: "Module: {module_code}. Current: step-03 (Execute), Layer 0",
|
|
14
|
+
activeForm: "Executing Layer 0")
|
|
15
|
+
|
|
16
|
+
### Domain Entities
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
For each entity to create/modify:
|
|
20
|
+
→ MCP scaffold_extension (type: "entity", target: entity_name)
|
|
21
|
+
→ Verify: inherits BaseEntity, implements ITenantEntity + IAuditableEntity
|
|
22
|
+
→ Verify entity matches patterns in references/smartstack-api.md
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Code Generation Integration
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
For each entity with auto-generated code ({code_patterns} from step-00):
|
|
29
|
+
IF {code_patterns} has entry for this entity AND strategy != "manual":
|
|
30
|
+
→ Pass codePattern in scaffold_extension options:
|
|
31
|
+
MCP scaffold_extension (type: "entity", target: entity_name, options: {
|
|
32
|
+
codePattern: {
|
|
33
|
+
strategy: {code_patterns[entity].strategy},
|
|
34
|
+
prefix: {code_patterns[entity].prefix},
|
|
35
|
+
digits: {code_patterns[entity].digits},
|
|
36
|
+
includeTenantSlug: {code_patterns[entity].includeTenantSlug},
|
|
37
|
+
separator: {code_patterns[entity].separator}
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
→ Verify: Code property exists on entity but is NOT in CreateDto
|
|
41
|
+
→ Verify: ICodeGenerator<{Entity}> is ready for DI registration (Layer 2)
|
|
42
|
+
ELSE:
|
|
43
|
+
→ Default behavior (strategy: "manual", Code in CreateDto)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Enum Serialization Check
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
For each enum type created in Domain/Enums/:
|
|
50
|
+
1. Grep("JsonStringEnumConverter", "src/**/Program.cs")
|
|
51
|
+
2. IF global config exists → no action
|
|
52
|
+
3. IF no global config → add [JsonConverter(typeof(JsonStringEnumConverter))] on each enum
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Person Extension Detection
|
|
56
|
+
|
|
57
|
+
**If entity has personRoleConfig (mandatory or optional UserId link):**
|
|
58
|
+
See `references/person-extension-pattern.md` for full entity, EF config, service, DTO, and frontend patterns.
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
1. scaffold_extension with options: { isPersonRole: true, userLinkMode: 'mandatory' | 'optional' }
|
|
62
|
+
2. Verify: EF config has unique index on (TenantId, UserId)
|
|
63
|
+
→ Mandatory variant: plain .IsUnique()
|
|
64
|
+
→ Optional variant: .IsUnique().HasFilter("[UserId] IS NOT NULL")
|
|
65
|
+
3. Verify all build checks pass before continuing
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### EF Core Configurations
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
For each entity:
|
|
72
|
+
→ Create IEntityTypeConfiguration<T> manually per smartstack-api.md patterns
|
|
73
|
+
→ Verify: table name, relationships, indexes
|
|
74
|
+
→ Register DbSet in ExtensionsDbContext if new entity
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
<!-- TODO A6: Replace with MCP scaffold_extension(type: "ef-config") when B2 is ready -->
|
|
78
|
+
|
|
79
|
+
### Migration
|
|
80
|
+
|
|
81
|
+
> Migration must cover ALL entities. Root cause (test-apex-007): Migration was created once for 3 entities, then 4 more entities were added later without re-running → 4 entities had no tables. Create/update migration AFTER ALL entities and EF configs are registered in DbContext. If entities are added incrementally, create a new migration for each batch.
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
1. Verify all entities have been added as DbSet in ExtensionsDbContext
|
|
85
|
+
2. Verify all EF configurations are registered (ApplyConfigurationsFromAssembly or individual)
|
|
86
|
+
3. MCP suggest_migration → get standardized name
|
|
87
|
+
4. dotnet ef migrations add {Name} --project src/{Infra}.csproj --startup-project src/{Api}.csproj -o Persistence/Migrations
|
|
88
|
+
5. dotnet ef database update (if local DB)
|
|
89
|
+
6. dotnet build
|
|
90
|
+
7. Verify: dotnet ef migrations has-pending-model-changes → must report "No pending model changes"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
If build fails after migration, fix EF configs before proceeding.
|
|
94
|
+
If `has-pending-model-changes` reports pending changes, entities are missing from the migration — create a new migration.
|
|
95
|
+
|
|
96
|
+
### Post-Layer 0 Build Gate
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
dotnet build
|
|
100
|
+
# Note: WSL bin\Debug cleanup handled by PostToolUse hook (wsl-dotnet-cleanup.sh)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Must pass before Layer 1. If NuGet error, run `dotnet restore` first. If file lock (MSB3021), use `--output /tmp/{project}_build`.
|
|
104
|
+
|
|
105
|
+
TaskUpdate(taskId: layer0_task_id, status: "completed",
|
|
106
|
+
metadata: { build_gate: "pass" })
|
|
107
|
+
|
|
108
|
+
### Layer 0 Commit
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
feat({module}): [domain+infra] {short description}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## NEXT SUB-STEP
|
|
117
|
+
|
|
118
|
+
Load `steps/step-03b-layer1-seed.md`
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: step-03b-layer1-seed
|
|
3
|
+
description: "Layer 1: Seed data (navigation, permissions, roles)"
|
|
4
|
+
model: opus
|
|
5
|
+
parent_step: steps/step-03-execute.md
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Layer 1 — Seed Data (DEDICATED LAYER — sequential, agent principal)
|
|
9
|
+
|
|
10
|
+
### Task Progress
|
|
11
|
+
TaskUpdate(taskId: layer1_task_id, status: "in_progress")
|
|
12
|
+
TaskUpdate(taskId: progress_tracker_id,
|
|
13
|
+
description: "Module: {module_code}. Current: step-03 (Execute), Layer 1",
|
|
14
|
+
activeForm: "Executing Layer 1")
|
|
15
|
+
|
|
16
|
+
> This layer is required. Seed data makes modules visible in the UI. Without it, the module exists in code but is invisible to users. Reference: `references/core-seed-data.md` (loaded above) for complete C# templates.
|
|
17
|
+
|
|
18
|
+
### Application-Level Seed Data (ONCE per application)
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
1. NavigationApplicationSeedData.cs
|
|
22
|
+
→ Application-level navigation entry (MUST be first)
|
|
23
|
+
→ 4 language translations (fr, en, it, de)
|
|
24
|
+
|
|
25
|
+
2. ApplicationRolesSeedData.cs
|
|
26
|
+
→ 4 roles: admin, manager, contributor, viewer
|
|
27
|
+
→ References NavigationApplicationSeedData.ApplicationId
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Per-Module Seed Data
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
3. NavigationModuleSeedData.cs
|
|
34
|
+
→ 4 languages (fr, en, it, de), GetModuleEntry() + GetTranslationEntries()
|
|
35
|
+
→ If sections defined: GetSectionEntries() + GetSectionTranslationEntries()
|
|
36
|
+
→ If resources defined: GetResourceEntries() + resource translations
|
|
37
|
+
→ Query actual parent from DB for FK (NOT deterministic GUID)
|
|
38
|
+
|
|
39
|
+
4. Permissions.cs
|
|
40
|
+
→ MCP generate_permissions (PRIMARY tool)
|
|
41
|
+
→ Static constants: public static class {Module} { public const string Read = "..."; }
|
|
42
|
+
→ Permission paths MUST use kebab-case matching NavRoute codes
|
|
43
|
+
|
|
44
|
+
5. PermissionsSeedData.cs
|
|
45
|
+
→ MCP generate_permissions first, fallback template
|
|
46
|
+
→ Paths match Permissions.cs, wildcard + CRUD
|
|
47
|
+
|
|
48
|
+
6. RolesSeedData.cs
|
|
49
|
+
→ Admin=wildcard(*), Manager=CRU, Contributor=CR, Viewer=R
|
|
50
|
+
→ Code-based role mapping (not deterministic GUIDs for roles)
|
|
51
|
+
→ Look up roles by Code at runtime
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Infrastructure Provider (ONCE per application)
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
7. {App}SeedDataProvider.cs
|
|
58
|
+
→ Implements IClientSeedDataProvider
|
|
59
|
+
→ SeedNavigationAsync(): Application → Module → Section → Resource + translations
|
|
60
|
+
→ SeedRolesAsync(): application-scoped roles from ApplicationRolesSeedData
|
|
61
|
+
→ SeedPermissionsAsync(): permission entries from PermissionsSeedData
|
|
62
|
+
→ SeedRolePermissionsAsync(): maps roles to permissions (by Code, NOT by GUID)
|
|
63
|
+
→ DI: services.AddScoped<IClientSeedDataProvider, {App}SeedDataProvider>()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
<!-- TODO A4: Replace direct seed generation with MCP scaffold_seed_data when B1 is ready. This will eliminate the need for references/core-seed-data.md (1464 lines). -->
|
|
67
|
+
|
|
68
|
+
### Post-Layer 1 Build Gate
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
dotnet build
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Must pass before Layer 2.
|
|
75
|
+
|
|
76
|
+
TaskUpdate(taskId: layer1_task_id, status: "completed",
|
|
77
|
+
metadata: { build_gate: "pass" })
|
|
78
|
+
|
|
79
|
+
### Layer 1 Commit
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
feat({module}): [seed] navigation, permissions, roles
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
> **Context release:** `references/core-seed-data.md` is no longer needed after Layer 1. Its templates have been consumed. Do not reference it in Layer 2+.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## NEXT SUB-STEP
|
|
90
|
+
|
|
91
|
+
Load `steps/step-03c-layer2-backend.md`
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: step-03c-layer2-backend
|
|
3
|
+
description: "Layer 2: Backend services, controllers, and tests"
|
|
4
|
+
model: opus
|
|
5
|
+
parent_step: steps/step-03-execute.md
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Layer 2 — Backend (Services + Controllers)
|
|
9
|
+
|
|
10
|
+
### Task Progress
|
|
11
|
+
TaskUpdate(taskId: layer2_task_id, status: "in_progress")
|
|
12
|
+
TaskUpdate(taskId: progress_tracker_id,
|
|
13
|
+
description: "Module: {module_code}. Current: step-03 (Execute), Layer 2",
|
|
14
|
+
activeForm: "Executing Layer 2")
|
|
15
|
+
|
|
16
|
+
> **Layer 2 rules** are in `references/smartstack-layers.md` (already in context from step-02):
|
|
17
|
+
> - NavRoute and permission kebab-case (Layer 2 - API section)
|
|
18
|
+
> - Controller route attributes (FORBIDDEN: [Route] alongside [NavRoute])
|
|
19
|
+
> - Validators DI registration
|
|
20
|
+
> - DateOnly vs string for DTO date fields
|
|
21
|
+
> - Code generation patterns (ICodeGenerator<T> registration, see references/code-generation.md)
|
|
22
|
+
|
|
23
|
+
### Backend Tasks (sequential or parallel within layer)
|
|
24
|
+
|
|
25
|
+
- Services/DTOs: MCP scaffold_extension
|
|
26
|
+
- Controllers: use /controller skill for complex, MCP scaffold_extension for simple
|
|
27
|
+
- Important: All GetAll endpoints must support `?search=` query parameter (enables EntityLookup on frontend)
|
|
28
|
+
- Guard: DTO mapping in services MUST use inline construction (`new ResponseDto(...)`) inside IQueryable `.Select()`. Never use helper methods (MapToDto, ToDto) inside `.Select()` — EF Core cannot translate them. Helper methods are allowed only after materialization (ToListAsync, FirstAsync).
|
|
29
|
+
|
|
30
|
+
### Code Generation Service Registration (per entity)
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
For each entity where {code_patterns} defines strategy != "manual":
|
|
34
|
+
1. Verify DI registration in DependencyInjection.cs:
|
|
35
|
+
→ services.AddScoped<ICodeGenerator<{Entity}>>(sp => new CodeGenerator<{Entity}>(...))
|
|
36
|
+
→ Use CodePatternConfig matching {code_patterns[entity]} values
|
|
37
|
+
→ See references/code-generation.md "DI Registration Pattern"
|
|
38
|
+
→ Guard: Do NOT duplicate if ICodeGenerator<{Entity}> is already registered
|
|
39
|
+
2. Verify service injection:
|
|
40
|
+
→ {Entity}Service constructor receives ICodeGenerator<{Entity}>
|
|
41
|
+
→ CreateAsync uses _codeGenerator.NextCodeAsync(ct) instead of dto.Code
|
|
42
|
+
→ See references/code-generation.md "Service Integration Pattern"
|
|
43
|
+
3. Verify CreateDto:
|
|
44
|
+
→ Code property MUST NOT be in Create{Entity}Dto
|
|
45
|
+
→ Code property MUST be in {Entity}ResponseDto
|
|
46
|
+
→ See references/code-generation.md "CreateDto Changes"
|
|
47
|
+
4. Verify Validator:
|
|
48
|
+
→ Create{Entity}Validator has NO Code rule (auto-generated)
|
|
49
|
+
→ Update{Entity}Validator has Code rule with regex ^[a-z0-9_-]+$ (if Code is mutable)
|
|
50
|
+
```
|
|
51
|
+
- FK inter-entity pattern: When an entity has FK to another business entity (e.g., Absence → Employee), use navigation properties directly inside `.Select()` (e.g., `x.Employee.Code`, `x.Employee.User!.LastName`). EF Core translates navigation access to SQL JOINs automatically. Do NOT use `.Include()` with `.Select()` — it is ignored. See `references/smartstack-api.md` "Service Pattern — Entity with FK to another business entity" for the full pattern.
|
|
52
|
+
|
|
53
|
+
### Skill Delegation
|
|
54
|
+
|
|
55
|
+
| Skill | When | Context to provide |
|
|
56
|
+
|-------|------|--------------------|
|
|
57
|
+
| /controller | Custom routes, complex auth, file upload | entity, CRUD actions, permissions, routes |
|
|
58
|
+
| /application | Full app/module from scratch | app, module names |
|
|
59
|
+
| /ui-components | Complex pages (tables, grids, dashboards) | entity, fields, page type |
|
|
60
|
+
| /efcore | Complex EF configs, inheritance | relationships, indexes |
|
|
61
|
+
| /notification | In-app or email notifications | trigger, recipients, template |
|
|
62
|
+
| /workflow | Automated workflows | trigger, steps, conditions |
|
|
63
|
+
|
|
64
|
+
### If NOT economy_mode AND multiple entities: Parallel Agents (within layer)
|
|
65
|
+
|
|
66
|
+
> **Protocol:** See `references/parallel-execution.md`
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
IF NOT economy_mode AND entities.length > 1:
|
|
70
|
+
For each entity, launch in parallel (single message):
|
|
71
|
+
Agent(subagent_type='Snipper', model='opus',
|
|
72
|
+
prompt='Execute Layer 2 backend for {EntityName}:
|
|
73
|
+
- Application service/DTO: MCP scaffold_extension
|
|
74
|
+
- Controller: /controller skill or MCP scaffold_extension
|
|
75
|
+
- IMPORTANT: GetAll endpoint MUST support ?search= parameter
|
|
76
|
+
- Validators: FluentValidation + DI registration
|
|
77
|
+
- CODE GENERATION: {code_patterns[EntityName] summary — e.g., "strategy: sequential, prefix: emp, digits: 5" or "manual"}
|
|
78
|
+
If strategy != "manual": read references/code-generation.md, then:
|
|
79
|
+
→ Register ICodeGenerator<{EntityName}> in DI (DependencyInjection.cs) with CodePatternConfig matching {code_patterns}
|
|
80
|
+
→ Inject ICodeGenerator<{EntityName}> in {EntityName}Service, use _codeGenerator.NextCodeAsync(ct) in CreateAsync
|
|
81
|
+
→ Remove Code from Create{EntityName}Dto (auto-generated, not user-provided)
|
|
82
|
+
→ Keep Code in {EntityName}ResponseDto
|
|
83
|
+
→ Create{EntityName}Validator: NO Code rule. Update{EntityName}Validator: Code rule with regex ^[a-z0-9_-]+$
|
|
84
|
+
- Your task ID is {task_id}. Call TaskUpdate(status: "in_progress") before starting.
|
|
85
|
+
- Call TaskUpdate(status: "completed", metadata: { files_created: [...] }) when done.')
|
|
86
|
+
# All agents launched in parallel
|
|
87
|
+
|
|
88
|
+
ELSE:
|
|
89
|
+
# Agent principal handles all entities sequentially
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Parallel Agents + TaskCreate Integration
|
|
93
|
+
|
|
94
|
+
When launching agents for multi-entity layers:
|
|
95
|
+
- Each agent receives its task ID and metadata context in the prompt
|
|
96
|
+
- Agent must call TaskUpdate(status: "in_progress") before starting
|
|
97
|
+
- Agent must call TaskUpdate(status: "completed", metadata: { files_created: [...] }) when done
|
|
98
|
+
- Agent principal monitors via TaskList() after all agents complete
|
|
99
|
+
- Agent principal updates activeForm after each entity completion:
|
|
100
|
+
`TaskUpdate(taskId: layer2_task_id, activeForm: "Building {EntityName} backend (2/5 entities)")`
|
|
101
|
+
|
|
102
|
+
### Controller NavRoute Attribute (MANDATORY)
|
|
103
|
+
|
|
104
|
+
After controller generation, verify `[NavRoute]` attribute is present on every controller:
|
|
105
|
+
- Expected: `[NavRoute("{app_name}.{module_code}.{section_code}")]` on the controller class
|
|
106
|
+
- If missing: Add it manually above `[Authorize]`
|
|
107
|
+
- When calling `scaffold_extension(type: "controller")`, always pass `navRoute` in options
|
|
108
|
+
- This is REQUIRED for `scaffold_routes` to auto-detect routes in Layer 3
|
|
109
|
+
|
|
110
|
+
### Guard: NavRoute Uniqueness and Segment Count (MANDATORY)
|
|
111
|
+
|
|
112
|
+
**BEFORE proceeding past Layer 2**, verify for EACH controller:
|
|
113
|
+
|
|
114
|
+
1. **Unique NavRoute:** No two controllers may share the same `[NavRoute("...")]` value. Duplicate NavRoutes cause routing conflicts → 404s on one of the controllers.
|
|
115
|
+
|
|
116
|
+
2. **Segment count matches hierarchy:** Count the dots in the NavRoute value:
|
|
117
|
+
- 1 dot = 2 segments (module-level, e.g., `human-resources.employees`) — controller is at `Controllers/{App}/`
|
|
118
|
+
- 2 dots = 3 segments (section-level, e.g., `human-resources.employees.contracts`) — controller is at `Controllers/{App}/{Module}/` or in a section subfolder
|
|
119
|
+
- **If a controller is in a section subfolder** (e.g., `Controllers/{App}/Employees/ContractsController.cs`) **but has only 2 segments** → the API route will be wrong → 404. It MUST have 3 segments.
|
|
120
|
+
- 0 dots = INVALID → BLOCK
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Quick validation
|
|
124
|
+
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
|
|
125
|
+
for f in $CTRL_FILES; do
|
|
126
|
+
NAVROUTE=$(grep -oP '\[NavRoute\("\K[^"]+' "$f")
|
|
127
|
+
if [ -n "$NAVROUTE" ]; then
|
|
128
|
+
DOTS=$(echo "$NAVROUTE" | tr -cd '.' | wc -c)
|
|
129
|
+
if [ "$DOTS" -eq 0 ]; then
|
|
130
|
+
echo "BLOCKING: NavRoute '$NAVROUTE' has only 1 segment (need minimum 2): $f"
|
|
131
|
+
exit 1
|
|
132
|
+
fi
|
|
133
|
+
# Check if controller is in a section subfolder but NavRoute has only 2 segments
|
|
134
|
+
DEPTH=$(echo "$f" | grep -oP 'Controllers/[^/]+/[^/]+/' | wc -l)
|
|
135
|
+
if [ "$DEPTH" -gt 0 ] && [ "$DOTS" -eq 1 ]; then
|
|
136
|
+
echo "WARNING: Controller in section subfolder but NavRoute has only 2 segments: $f"
|
|
137
|
+
echo " NavRoute: $NAVROUTE — expected 3 segments (app.module.section)"
|
|
138
|
+
fi
|
|
139
|
+
fi
|
|
140
|
+
done
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Quick check: all controllers must have [NavRoute] (not just [Route])
|
|
145
|
+
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
|
|
146
|
+
for f in $CTRL_FILES; do
|
|
147
|
+
if ! grep -q "\[NavRoute" "$f" && grep -q "\[Route" "$f"; then
|
|
148
|
+
echo "WARNING: $f has [Route] but no [NavRoute] — add [NavRoute] for route auto-detection"
|
|
149
|
+
fi
|
|
150
|
+
done
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Layer 2 Build Gate — NavRoute Verification
|
|
154
|
+
|
|
155
|
+
> Fail-fast check: detect MCP generation errors BEFORE Layer 3.
|
|
156
|
+
> Does NOT auto-fix — flags as BLOCKING for manual correction.
|
|
157
|
+
> Full validation in step-04 POST-CHECK C43 + C56.
|
|
158
|
+
|
|
159
|
+
After all controllers are generated and build passes:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# Check only controllers generated in this run (match against {entities})
|
|
163
|
+
for ENTITY in $ENTITIES; do
|
|
164
|
+
CTRL=$(find src/ -path "*/Controllers/*" -name "${ENTITY}*Controller.cs" 2>/dev/null | head -1)
|
|
165
|
+
[ -z "$CTRL" ] && continue
|
|
166
|
+
|
|
167
|
+
HAS_NAVROUTE=$(grep -c "\[NavRoute" "$CTRL")
|
|
168
|
+
HAS_ROUTE=$(grep -cP "^\s*\[Route\(" "$CTRL") # class-level only
|
|
169
|
+
|
|
170
|
+
if [ "$HAS_NAVROUTE" -eq 0 ] && [ "$HAS_ROUTE" -gt 0 ]; then
|
|
171
|
+
echo "BLOCKING: $CTRL has [Route] but NO [NavRoute]"
|
|
172
|
+
echo "MCP scaffold_extension generated [Route] instead of [NavRoute]"
|
|
173
|
+
echo "Fix: Replace [Route(\"api/...\")] with [NavRoute(\"{app}.{module}.{section}\")]"
|
|
174
|
+
echo " NavRoute value: derive from naming rules (section 4f)"
|
|
175
|
+
echo " Add: using SmartStack.Api.Routing;"
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
if [ "$HAS_NAVROUTE" -eq 0 ] && [ "$HAS_ROUTE" -eq 0 ]; then
|
|
179
|
+
echo "BLOCKING: $CTRL has NO route attribute at all"
|
|
180
|
+
echo "Fix: Add [NavRoute(\"{app}.{module}.{section}\")] from naming rules"
|
|
181
|
+
fi
|
|
182
|
+
done
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
> IF any BLOCKING found: fix the controller(s) BEFORE proceeding to Layer 3.
|
|
186
|
+
> The fix is: remove [Route], add [NavRoute] with the correct dot-separated value,
|
|
187
|
+
> add `using SmartStack.Api.Routing;`.
|
|
188
|
+
|
|
189
|
+
### Post-Layer 2 Build Gate
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
dotnet build
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Must pass before backend tests.
|
|
196
|
+
|
|
197
|
+
TaskUpdate(taskId: layer2_task_id, status: "completed",
|
|
198
|
+
metadata: { build_gate: "pass" })
|
|
199
|
+
TaskUpdate(taskId: progress_tracker_id,
|
|
200
|
+
activeForm: "Running build gate (Layer 2)")
|
|
201
|
+
|
|
202
|
+
### Backend Tests Inline
|
|
203
|
+
|
|
204
|
+
> **Tests are scaffolded and run WITHIN Layer 2, not deferred to step-07.**
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
1. Scaffold backend tests:
|
|
208
|
+
→ MCP scaffold_tests (target_layer: "domain", module: "{module_code}", test_type: "unit")
|
|
209
|
+
→ MCP scaffold_tests (target_layer: "application", module: "{module_code}", test_type: "unit")
|
|
210
|
+
→ MCP scaffold_tests (target_layer: "api", module: "{module_code}", test_type: "integration")
|
|
211
|
+
|
|
212
|
+
2. Run tests:
|
|
213
|
+
dotnet test --no-build --verbosity normal
|
|
214
|
+
|
|
215
|
+
3. Fix loop (max 3 iterations):
|
|
216
|
+
IF tests fail:
|
|
217
|
+
→ Identify root cause (ALWAYS code bug, not test bug)
|
|
218
|
+
→ Fix production CODE via appropriate skill/MCP
|
|
219
|
+
→ dotnet build --no-restore
|
|
220
|
+
→ dotnet test --no-build
|
|
221
|
+
→ Repeat until pass or max 3
|
|
222
|
+
|
|
223
|
+
4. If still failing after 3 iterations: note failures, continue to Layer 3.
|
|
224
|
+
Step-07 "Final Test Sweep" will handle remaining failures.
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Layer 2 Commits
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
feat({module}): [app+api] {short description}
|
|
231
|
+
test({module}): backend unit and integration tests
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
> **Context release:** `references/smartstack-api.md` entity patterns and `references/smartstack-layers.md` Layer 2 rules have been consumed. Layer 3 only needs their frontend sections (already covered by `smartstack-frontend.md`).
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## NEXT SUB-STEP
|
|
239
|
+
|
|
240
|
+
Load `steps/step-03d-layer3-frontend.md`
|