@atlashub/smartstack-cli 4.32.0 → 4.33.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/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/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
|
@@ -6,11 +6,13 @@ prev_step: steps/step-02-plan.md
|
|
|
6
6
|
next_step: steps/step-04-examine.md
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
# Step 3: Execute
|
|
9
|
+
# Step 3: Execute — Orchestrator
|
|
10
10
|
|
|
11
11
|
/apex ORCHESTRATES. It does not write SmartStack code directly.
|
|
12
12
|
All code goes through skills (/controller, /application, /ui-components, /efcore) or MCP tools.
|
|
13
13
|
|
|
14
|
+
**This step dispatches to layer-specific sub-steps.** Each sub-step is loaded sequentially.
|
|
15
|
+
|
|
14
16
|
## LOAD CONDITIONALLY
|
|
15
17
|
|
|
16
18
|
> CONTEXT REUSE: `smartstack-api.md` (loaded in step-01) and `smartstack-layers.md` (loaded in step-02) are already in context. Do not re-read them.
|
|
@@ -26,20 +28,8 @@ All code goes through skills (/controller, /application, /ui-components, /efcore
|
|
|
26
28
|
## Foundation Mode Detection
|
|
27
29
|
|
|
28
30
|
**IF `{foundation_mode}` == true:**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- Layer 0: Domain entities + EF configs + Migration
|
|
32
|
-
|
|
33
|
-
Skip all other layers:
|
|
34
|
-
- Layer 1: Seed Data → SKIP
|
|
35
|
-
- Layer 2: Backend → SKIP
|
|
36
|
-
- Layer 3: Frontend → SKIP
|
|
37
|
-
- Layer 4: DevData → SKIP
|
|
38
|
-
|
|
39
|
-
After Layer 0 completes and builds successfully:
|
|
40
|
-
- Commit with message: `chore(foundation): entities for {module_code}`
|
|
41
|
-
- Jump to step-04 for POST-CHECKs (domain + infrastructure only)
|
|
42
|
-
- End execution
|
|
31
|
+
Execute ONLY Layer 0 (domain entities + EF configs + migration). Skip all other layers.
|
|
32
|
+
After Layer 0 completes: commit `chore(foundation): entities for {module_code}`, jump to step-04.
|
|
43
33
|
|
|
44
34
|
**IF `{foundation_mode}` == false:**
|
|
45
35
|
Execute all layers (Layer 0 → Layer 1 → Layer 2 → Layer 3 → Layer 4).
|
|
@@ -57,734 +47,33 @@ BEFORE starting Layer N:
|
|
|
57
47
|
|
|
58
48
|
IF any variable is missing or empty:
|
|
59
49
|
1. Read .claude/output/apex/{task_id}/state.json (if exists)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
→
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
3. IF Layer N-1 was already completed (check state.json or git log):
|
|
69
|
-
→ Skip to Layer N directly
|
|
50
|
+
2. IF state.json missing → re-derive from filesystem:
|
|
51
|
+
- {app_name}: Glob("docs/business/*/") → first directory name
|
|
52
|
+
- {module_code}: Glob("src/**/Domain/Entities/*/") → target module directory
|
|
53
|
+
- {entities}: Glob("src/**/Domain/Entities/{module_code}/*.cs") → entity names
|
|
54
|
+
- {sections}: Glob("src/**/Seeding/Data/{module_code}/*NavigationSeedData.cs") → parse
|
|
55
|
+
- {code_patterns}: Read state.json or re-derive from DependencyInjection.cs
|
|
56
|
+
3. IF recovered: verify consistency with naming derivation rules (step-00 §4f)
|
|
57
|
+
4. IF Layer N-1 already completed: skip to Layer N directly
|
|
70
58
|
|
|
71
59
|
Cost: ~5 tool calls. Only triggered if context was compressed.
|
|
72
60
|
```
|
|
73
61
|
|
|
74
62
|
---
|
|
75
63
|
|
|
76
|
-
## Layer
|
|
77
|
-
|
|
78
|
-
### Task Progress
|
|
79
|
-
TaskUpdate(taskId: layer0_task_id, status: "in_progress")
|
|
80
|
-
TaskUpdate(taskId: progress_tracker_id,
|
|
81
|
-
description: "Module: {module_code}. Current: step-03 (Execute), Layer 0",
|
|
82
|
-
activeForm: "Executing Layer 0")
|
|
83
|
-
|
|
84
|
-
### Domain Entities
|
|
85
|
-
|
|
86
|
-
```
|
|
87
|
-
For each entity to create/modify:
|
|
88
|
-
→ MCP scaffold_extension (type: "entity", target: entity_name)
|
|
89
|
-
→ Verify: inherits BaseEntity, implements ITenantEntity + IAuditableEntity
|
|
90
|
-
→ Verify entity matches patterns in references/smartstack-api.md
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Code Generation Integration
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
For each entity with auto-generated code ({code_patterns} from step-00):
|
|
97
|
-
IF {code_patterns} has entry for this entity AND strategy != "manual":
|
|
98
|
-
→ Pass codePattern in scaffold_extension options:
|
|
99
|
-
MCP scaffold_extension (type: "entity", target: entity_name, options: {
|
|
100
|
-
codePattern: {
|
|
101
|
-
strategy: {code_patterns[entity].strategy},
|
|
102
|
-
prefix: {code_patterns[entity].prefix},
|
|
103
|
-
digits: {code_patterns[entity].digits},
|
|
104
|
-
includeTenantSlug: {code_patterns[entity].includeTenantSlug},
|
|
105
|
-
separator: {code_patterns[entity].separator}
|
|
106
|
-
}
|
|
107
|
-
})
|
|
108
|
-
→ Verify: Code property exists on entity but is NOT in CreateDto
|
|
109
|
-
→ Verify: ICodeGenerator<{Entity}> is ready for DI registration (Layer 2)
|
|
110
|
-
ELSE:
|
|
111
|
-
→ Default behavior (strategy: "manual", Code in CreateDto)
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### Enum Serialization Check
|
|
115
|
-
|
|
116
|
-
```
|
|
117
|
-
For each enum type created in Domain/Enums/:
|
|
118
|
-
1. Grep("JsonStringEnumConverter", "src/**/Program.cs")
|
|
119
|
-
2. IF global config exists → no action
|
|
120
|
-
3. IF no global config → add [JsonConverter(typeof(JsonStringEnumConverter))] on each enum
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Person Extension Detection
|
|
124
|
-
|
|
125
|
-
**If entity has personRoleConfig (mandatory or optional UserId link):**
|
|
126
|
-
See `references/person-extension-pattern.md` for full entity, EF config, service, DTO, and frontend patterns.
|
|
127
|
-
|
|
128
|
-
```
|
|
129
|
-
1. scaffold_extension with options: { isPersonRole: true, userLinkMode: 'mandatory' | 'optional' }
|
|
130
|
-
2. Verify: EF config has unique index on (TenantId, UserId)
|
|
131
|
-
→ Mandatory variant: plain .IsUnique()
|
|
132
|
-
→ Optional variant: .IsUnique().HasFilter("[UserId] IS NOT NULL")
|
|
133
|
-
3. Verify all build checks pass before continuing
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### EF Core Configurations
|
|
137
|
-
|
|
138
|
-
```
|
|
139
|
-
For each entity:
|
|
140
|
-
→ Create IEntityTypeConfiguration<T> manually per smartstack-api.md patterns
|
|
141
|
-
→ Verify: table name, relationships, indexes
|
|
142
|
-
→ Register DbSet in ExtensionsDbContext if new entity
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### Migration
|
|
146
|
-
|
|
147
|
-
> 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.
|
|
148
|
-
|
|
149
|
-
```
|
|
150
|
-
1. Verify all entities have been added as DbSet in ExtensionsDbContext
|
|
151
|
-
2. Verify all EF configurations are registered (ApplyConfigurationsFromAssembly or individual)
|
|
152
|
-
3. MCP suggest_migration → get standardized name
|
|
153
|
-
4. dotnet ef migrations add {Name} --project src/{Infra}.csproj --startup-project src/{Api}.csproj -o Persistence/Migrations
|
|
154
|
-
5. dotnet ef database update (if local DB)
|
|
155
|
-
6. dotnet build
|
|
156
|
-
7. Verify: dotnet ef migrations has-pending-model-changes → must report "No pending model changes"
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
If build fails after migration, fix EF configs before proceeding.
|
|
160
|
-
If `has-pending-model-changes` reports pending changes, entities are missing from the migration — create a new migration.
|
|
161
|
-
|
|
162
|
-
### Post-Layer 0 Build Gate
|
|
163
|
-
|
|
164
|
-
```bash
|
|
165
|
-
dotnet build
|
|
166
|
-
# Note: WSL bin\Debug cleanup handled by PostToolUse hook (wsl-dotnet-cleanup.sh)
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
Must pass before Layer 1. If NuGet error, run `dotnet restore` first. If file lock (MSB3021), use `--output /tmp/{project}_build`.
|
|
170
|
-
|
|
171
|
-
TaskUpdate(taskId: layer0_task_id, status: "completed",
|
|
172
|
-
metadata: { build_gate: "pass" })
|
|
173
|
-
|
|
174
|
-
### Layer 0 Commit
|
|
175
|
-
|
|
176
|
-
```
|
|
177
|
-
feat({module}): [domain+infra] {short description}
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
## Layer 1 — Seed Data (DEDICATED LAYER — sequential, agent principal)
|
|
183
|
-
|
|
184
|
-
### Task Progress
|
|
185
|
-
TaskUpdate(taskId: layer1_task_id, status: "in_progress")
|
|
186
|
-
TaskUpdate(taskId: progress_tracker_id,
|
|
187
|
-
description: "Module: {module_code}. Current: step-03 (Execute), Layer 1",
|
|
188
|
-
activeForm: "Executing Layer 1")
|
|
189
|
-
|
|
190
|
-
> 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.
|
|
191
|
-
|
|
192
|
-
### Application-Level Seed Data (ONCE per application)
|
|
193
|
-
|
|
194
|
-
```
|
|
195
|
-
1. NavigationApplicationSeedData.cs
|
|
196
|
-
→ Application-level navigation entry (MUST be first)
|
|
197
|
-
→ 4 language translations (fr, en, it, de)
|
|
198
|
-
|
|
199
|
-
2. ApplicationRolesSeedData.cs
|
|
200
|
-
→ 4 roles: admin, manager, contributor, viewer
|
|
201
|
-
→ References NavigationApplicationSeedData.ApplicationId
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### Per-Module Seed Data
|
|
205
|
-
|
|
206
|
-
```
|
|
207
|
-
3. NavigationModuleSeedData.cs
|
|
208
|
-
→ 4 languages (fr, en, it, de), GetModuleEntry() + GetTranslationEntries()
|
|
209
|
-
→ If sections defined: GetSectionEntries() + GetSectionTranslationEntries()
|
|
210
|
-
→ If resources defined: GetResourceEntries() + resource translations
|
|
211
|
-
→ Query actual parent from DB for FK (NOT deterministic GUID)
|
|
212
|
-
|
|
213
|
-
4. Permissions.cs
|
|
214
|
-
→ MCP generate_permissions (PRIMARY tool)
|
|
215
|
-
→ Static constants: public static class {Module} { public const string Read = "..."; }
|
|
216
|
-
→ Permission paths MUST use kebab-case matching NavRoute codes
|
|
217
|
-
|
|
218
|
-
5. PermissionsSeedData.cs
|
|
219
|
-
→ MCP generate_permissions first, fallback template
|
|
220
|
-
→ Paths match Permissions.cs, wildcard + CRUD
|
|
221
|
-
|
|
222
|
-
6. RolesSeedData.cs
|
|
223
|
-
→ Admin=wildcard(*), Manager=CRU, Contributor=CR, Viewer=R
|
|
224
|
-
→ Code-based role mapping (not deterministic GUIDs for roles)
|
|
225
|
-
→ Look up roles by Code at runtime
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### Infrastructure Provider (ONCE per application)
|
|
229
|
-
|
|
230
|
-
```
|
|
231
|
-
7. {App}SeedDataProvider.cs
|
|
232
|
-
→ Implements IClientSeedDataProvider
|
|
233
|
-
→ SeedNavigationAsync(): Application → Module → Section → Resource + translations
|
|
234
|
-
→ SeedRolesAsync(): application-scoped roles from ApplicationRolesSeedData
|
|
235
|
-
→ SeedPermissionsAsync(): permission entries from PermissionsSeedData
|
|
236
|
-
→ SeedRolePermissionsAsync(): maps roles to permissions (by Code, NOT by GUID)
|
|
237
|
-
→ DI: services.AddScoped<IClientSeedDataProvider, {App}SeedDataProvider>()
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### Post-Layer 1 Build Gate
|
|
241
|
-
|
|
242
|
-
```bash
|
|
243
|
-
dotnet build
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
Must pass before Layer 2.
|
|
247
|
-
|
|
248
|
-
TaskUpdate(taskId: layer1_task_id, status: "completed",
|
|
249
|
-
metadata: { build_gate: "pass" })
|
|
250
|
-
|
|
251
|
-
### Layer 1 Commit
|
|
252
|
-
|
|
253
|
-
```
|
|
254
|
-
feat({module}): [seed] navigation, permissions, roles
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
> **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+.
|
|
258
|
-
|
|
259
|
-
---
|
|
260
|
-
|
|
261
|
-
## Layer 2 — Backend (Services + Controllers)
|
|
262
|
-
|
|
263
|
-
### Task Progress
|
|
264
|
-
TaskUpdate(taskId: layer2_task_id, status: "in_progress")
|
|
265
|
-
TaskUpdate(taskId: progress_tracker_id,
|
|
266
|
-
description: "Module: {module_code}. Current: step-03 (Execute), Layer 2",
|
|
267
|
-
activeForm: "Executing Layer 2")
|
|
268
|
-
|
|
269
|
-
> **Layer 2 rules** are in `references/smartstack-layers.md` (already in context from step-02):
|
|
270
|
-
> - NavRoute and permission kebab-case (Layer 2 - API section)
|
|
271
|
-
> - Controller route attributes (FORBIDDEN: [Route] alongside [NavRoute])
|
|
272
|
-
> - Validators DI registration
|
|
273
|
-
> - DateOnly vs string for DTO date fields
|
|
274
|
-
> - Code generation patterns (ICodeGenerator<T> registration, see references/code-generation.md)
|
|
275
|
-
|
|
276
|
-
### Backend Tasks (sequential or parallel within layer)
|
|
277
|
-
|
|
278
|
-
- Services/DTOs: MCP scaffold_extension
|
|
279
|
-
- Controllers: use /controller skill for complex, MCP scaffold_extension for simple
|
|
280
|
-
- Important: All GetAll endpoints must support `?search=` query parameter (enables EntityLookup on frontend)
|
|
281
|
-
- 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).
|
|
282
|
-
|
|
283
|
-
### Code Generation Service Registration (per entity)
|
|
284
|
-
|
|
285
|
-
```
|
|
286
|
-
For each entity where {code_patterns} defines strategy != "manual":
|
|
287
|
-
1. Verify DI registration in DependencyInjection.cs:
|
|
288
|
-
→ services.AddScoped<ICodeGenerator<{Entity}>>(sp => new CodeGenerator<{Entity}>(...))
|
|
289
|
-
→ Use CodePatternConfig matching {code_patterns[entity]} values
|
|
290
|
-
→ See references/code-generation.md "DI Registration Pattern"
|
|
291
|
-
→ Guard: Do NOT duplicate if ICodeGenerator<{Entity}> is already registered
|
|
292
|
-
2. Verify service injection:
|
|
293
|
-
→ {Entity}Service constructor receives ICodeGenerator<{Entity}>
|
|
294
|
-
→ CreateAsync uses _codeGenerator.NextCodeAsync(ct) instead of dto.Code
|
|
295
|
-
→ See references/code-generation.md "Service Integration Pattern"
|
|
296
|
-
3. Verify CreateDto:
|
|
297
|
-
→ Code property MUST NOT be in Create{Entity}Dto
|
|
298
|
-
→ Code property MUST be in {Entity}ResponseDto
|
|
299
|
-
→ See references/code-generation.md "CreateDto Changes"
|
|
300
|
-
4. Verify Validator:
|
|
301
|
-
→ Create{Entity}Validator has NO Code rule (auto-generated)
|
|
302
|
-
→ Update{Entity}Validator has Code rule with regex ^[a-z0-9_-]+$ (if Code is mutable)
|
|
303
|
-
```
|
|
304
|
-
- 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.
|
|
305
|
-
|
|
306
|
-
### Skill Delegation
|
|
307
|
-
|
|
308
|
-
| Skill | When | Context to provide |
|
|
309
|
-
|-------|------|--------------------|
|
|
310
|
-
| /controller | Custom routes, complex auth, file upload | entity, CRUD actions, permissions, routes |
|
|
311
|
-
| /application | Full app/module from scratch | app, module names |
|
|
312
|
-
| /ui-components | Complex pages (tables, grids, dashboards) | entity, fields, page type |
|
|
313
|
-
| /efcore | Complex EF configs, inheritance | relationships, indexes |
|
|
314
|
-
| /notification | In-app or email notifications | trigger, recipients, template |
|
|
315
|
-
| /workflow | Automated workflows | trigger, steps, conditions |
|
|
316
|
-
|
|
317
|
-
### If NOT economy_mode AND multiple entities: Parallel Agents (within layer)
|
|
318
|
-
|
|
319
|
-
> **Protocol:** See `references/parallel-execution.md`
|
|
320
|
-
|
|
321
|
-
```
|
|
322
|
-
IF NOT economy_mode AND entities.length > 1:
|
|
323
|
-
For each entity, launch in parallel (single message):
|
|
324
|
-
Agent(subagent_type='Snipper', model='opus',
|
|
325
|
-
prompt='Execute Layer 2 backend for {EntityName}:
|
|
326
|
-
- Application service/DTO: MCP scaffold_extension
|
|
327
|
-
- Controller: /controller skill or MCP scaffold_extension
|
|
328
|
-
- IMPORTANT: GetAll endpoint MUST support ?search= parameter
|
|
329
|
-
- Validators: FluentValidation + DI registration
|
|
330
|
-
- CODE GENERATION: {code_patterns[EntityName] summary — e.g., "strategy: sequential, prefix: emp, digits: 5" or "manual"}
|
|
331
|
-
If strategy != "manual": read references/code-generation.md, then:
|
|
332
|
-
→ Register ICodeGenerator<{EntityName}> in DI (DependencyInjection.cs) with CodePatternConfig matching {code_patterns}
|
|
333
|
-
→ Inject ICodeGenerator<{EntityName}> in {EntityName}Service, use _codeGenerator.NextCodeAsync(ct) in CreateAsync
|
|
334
|
-
→ Remove Code from Create{EntityName}Dto (auto-generated, not user-provided)
|
|
335
|
-
→ Keep Code in {EntityName}ResponseDto
|
|
336
|
-
→ Create{EntityName}Validator: NO Code rule. Update{EntityName}Validator: Code rule with regex ^[a-z0-9_-]+$
|
|
337
|
-
- Your task ID is {task_id}. Call TaskUpdate(status: "in_progress") before starting.
|
|
338
|
-
- Call TaskUpdate(status: "completed", metadata: { files_created: [...] }) when done.')
|
|
339
|
-
# All agents launched in parallel
|
|
340
|
-
|
|
341
|
-
ELSE:
|
|
342
|
-
# Agent principal handles all entities sequentially
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
### Parallel Agents + TaskCreate Integration
|
|
346
|
-
|
|
347
|
-
When launching agents for multi-entity layers:
|
|
348
|
-
- Each agent receives its task ID and metadata context in the prompt
|
|
349
|
-
- Agent must call TaskUpdate(status: "in_progress") before starting
|
|
350
|
-
- Agent must call TaskUpdate(status: "completed", metadata: { files_created: [...] }) when done
|
|
351
|
-
- Agent principal monitors via TaskList() after all agents complete
|
|
352
|
-
- Agent principal updates activeForm after each entity completion:
|
|
353
|
-
`TaskUpdate(taskId: layer2_task_id, activeForm: "Building {EntityName} backend (2/5 entities)")`
|
|
354
|
-
|
|
355
|
-
### Controller NavRoute Attribute (MANDATORY)
|
|
356
|
-
|
|
357
|
-
After controller generation, verify `[NavRoute]` attribute is present on every controller:
|
|
358
|
-
- Expected: `[NavRoute("{app_name}.{module_code}.{section_code}")]` on the controller class
|
|
359
|
-
- If missing: Add it manually above `[Authorize]`
|
|
360
|
-
- When calling `scaffold_extension(type: "controller")`, always pass `navRoute` in options
|
|
361
|
-
- This is REQUIRED for `scaffold_routes` to auto-detect routes in Layer 3
|
|
362
|
-
|
|
363
|
-
### Guard: NavRoute Uniqueness and Segment Count (MANDATORY)
|
|
364
|
-
|
|
365
|
-
**BEFORE proceeding past Layer 2**, verify for EACH controller:
|
|
366
|
-
|
|
367
|
-
1. **Unique NavRoute:** No two controllers may share the same `[NavRoute("...")]` value. Duplicate NavRoutes cause routing conflicts → 404s on one of the controllers.
|
|
368
|
-
|
|
369
|
-
2. **Segment count matches hierarchy:** Count the dots in the NavRoute value:
|
|
370
|
-
- 1 dot = 2 segments (module-level, e.g., `human-resources.employees`) — controller is at `Controllers/{App}/`
|
|
371
|
-
- 2 dots = 3 segments (section-level, e.g., `human-resources.employees.contracts`) — controller is at `Controllers/{App}/{Module}/` or in a section subfolder
|
|
372
|
-
- **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.
|
|
373
|
-
- 0 dots = INVALID → BLOCK
|
|
374
|
-
|
|
375
|
-
```bash
|
|
376
|
-
# Quick validation
|
|
377
|
-
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
|
|
378
|
-
for f in $CTRL_FILES; do
|
|
379
|
-
NAVROUTE=$(grep -oP '\[NavRoute\("\K[^"]+' "$f")
|
|
380
|
-
if [ -n "$NAVROUTE" ]; then
|
|
381
|
-
DOTS=$(echo "$NAVROUTE" | tr -cd '.' | wc -c)
|
|
382
|
-
if [ "$DOTS" -eq 0 ]; then
|
|
383
|
-
echo "BLOCKING: NavRoute '$NAVROUTE' has only 1 segment (need minimum 2): $f"
|
|
384
|
-
exit 1
|
|
385
|
-
fi
|
|
386
|
-
# Check if controller is in a section subfolder but NavRoute has only 2 segments
|
|
387
|
-
DEPTH=$(echo "$f" | grep -oP 'Controllers/[^/]+/[^/]+/' | wc -l)
|
|
388
|
-
if [ "$DEPTH" -gt 0 ] && [ "$DOTS" -eq 1 ]; then
|
|
389
|
-
echo "WARNING: Controller in section subfolder but NavRoute has only 2 segments: $f"
|
|
390
|
-
echo " NavRoute: $NAVROUTE — expected 3 segments (app.module.section)"
|
|
391
|
-
fi
|
|
392
|
-
fi
|
|
393
|
-
done
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
```bash
|
|
397
|
-
# Quick check: all controllers must have [NavRoute] (not just [Route])
|
|
398
|
-
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
|
|
399
|
-
for f in $CTRL_FILES; do
|
|
400
|
-
if ! grep -q "\[NavRoute" "$f" && grep -q "\[Route" "$f"; then
|
|
401
|
-
echo "WARNING: $f has [Route] but no [NavRoute] — add [NavRoute] for route auto-detection"
|
|
402
|
-
fi
|
|
403
|
-
done
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
### Post-Layer 2 Build Gate
|
|
407
|
-
|
|
408
|
-
```bash
|
|
409
|
-
dotnet build
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
Must pass before backend tests.
|
|
413
|
-
|
|
414
|
-
TaskUpdate(taskId: layer2_task_id, status: "completed",
|
|
415
|
-
metadata: { build_gate: "pass" })
|
|
416
|
-
TaskUpdate(taskId: progress_tracker_id,
|
|
417
|
-
activeForm: "Running build gate (Layer 2)")
|
|
418
|
-
|
|
419
|
-
### Backend Tests Inline
|
|
420
|
-
|
|
421
|
-
> **Tests are scaffolded and run WITHIN Layer 2, not deferred to step-07.**
|
|
422
|
-
|
|
423
|
-
```
|
|
424
|
-
1. Scaffold backend tests:
|
|
425
|
-
→ MCP scaffold_tests (target_layer: "domain", module: "{module_code}", test_type: "unit")
|
|
426
|
-
→ MCP scaffold_tests (target_layer: "application", module: "{module_code}", test_type: "unit")
|
|
427
|
-
→ MCP scaffold_tests (target_layer: "api", module: "{module_code}", test_type: "integration")
|
|
428
|
-
|
|
429
|
-
2. Run tests:
|
|
430
|
-
dotnet test --no-build --verbosity normal
|
|
431
|
-
|
|
432
|
-
3. Fix loop (max 3 iterations):
|
|
433
|
-
IF tests fail:
|
|
434
|
-
→ Identify root cause (ALWAYS code bug, not test bug)
|
|
435
|
-
→ Fix production CODE via appropriate skill/MCP
|
|
436
|
-
→ dotnet build --no-restore
|
|
437
|
-
→ dotnet test --no-build
|
|
438
|
-
→ Repeat until pass or max 3
|
|
439
|
-
|
|
440
|
-
4. If still failing after 3 iterations: note failures, continue to Layer 3.
|
|
441
|
-
Step-07 "Final Test Sweep" will handle remaining failures.
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
### Layer 2 Commits
|
|
445
|
-
|
|
446
|
-
```
|
|
447
|
-
feat({module}): [app+api] {short description}
|
|
448
|
-
test({module}): backend unit and integration tests
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
> **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`).
|
|
452
|
-
|
|
453
|
-
---
|
|
454
|
-
|
|
455
|
-
## Layer 3 — Frontend (Pages + I18n + Documentation)
|
|
456
|
-
|
|
457
|
-
### ⛔ HARD RULE — /ui-components is NON-NEGOTIABLE (read BEFORE any Layer 3 action)
|
|
458
|
-
|
|
459
|
-
> **VIOLATION CHECK:** If ANY .tsx page file was created by Write tool WITHOUT
|
|
460
|
-
> a prior Skill("ui-components") call in this execution, the frontend layer is INVALID.
|
|
461
|
-
>
|
|
462
|
-
> **You MUST NOT:**
|
|
463
|
-
> - Generate .tsx page code in Agent prompts and dispatch to Snipper agents
|
|
464
|
-
> - Write page content directly via the Write tool
|
|
465
|
-
> - Copy-paste page templates from your knowledge
|
|
466
|
-
>
|
|
467
|
-
> **You MUST:**
|
|
468
|
-
> - Call Skill("ui-components") which loads the style guide, responsive guidelines,
|
|
469
|
-
> accessibility rules, and pattern files (entity-card, data-table, dashboard-chart, grid-layout, kanban)
|
|
470
|
-
> - Let /ui-components generate all pages with correct conventions
|
|
471
|
-
>
|
|
472
|
-
> **Why this matters:** Without the skill, pages miss CSS variables, DataTable/EntityCard,
|
|
473
|
-
> memo()/useCallback, responsive mobile-first design, and accessibility patterns.
|
|
474
|
-
>
|
|
475
|
-
> **Agent boundary rule:** Snipper sub-agents DO NOT have access to the Skill tool.
|
|
476
|
-
> Therefore, .tsx page generation MUST NEVER be delegated to Snipper agents.
|
|
477
|
-
> Pages are ALWAYS generated by the principal agent via Skill("ui-components").
|
|
478
|
-
> Snipper agents handle: API clients, routes, wiring, i18n, tests — NOT pages.
|
|
479
|
-
|
|
480
|
-
### Load Frontend References (deferred from top of step)
|
|
481
|
-
|
|
482
|
-
- Read `references/smartstack-frontend.md` now — lazy loading, i18n, page structure, CSS variables, EntityLookup (sections 1-6)
|
|
483
|
-
- Read `references/smartstack-frontend-compliance.md` now — documentation, form testing, compliance gates (sections 7-9)
|
|
484
|
-
|
|
485
|
-
### Pre-flight: Shared component existence check
|
|
486
|
-
|
|
487
|
-
Before generating any page, verify shared components exist:
|
|
488
|
-
|
|
489
|
-
```
|
|
490
|
-
Glob("src/components/ui/DataTable.*")
|
|
491
|
-
Glob("src/components/ui/EntityCard.*")
|
|
492
|
-
Glob("src/components/ui/PageLoader.*")
|
|
493
|
-
Glob("src/components/ui/EntityLookup.*")
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
If ANY shared component is MISSING:
|
|
497
|
-
→ Log warning: "Shared component {Component} not found — will be generated locally"
|
|
498
|
-
→ When invoking Skill("ui-components"), add instruction:
|
|
499
|
-
"MISSING SHARED COMPONENTS: {list}. Generate these locally in src/components/ui/"
|
|
500
|
-
|
|
501
|
-
**EntityLookup special handling (FK fields):**
|
|
502
|
-
If ANY entity has FK Guid fields (e.g., EmployeeId, DepartmentId) AND EntityLookup is missing:
|
|
503
|
-
→ Generate EntityLookup FIRST, BEFORE any Create/Edit pages
|
|
504
|
-
→ Use the EXACT implementation from `references/smartstack-frontend.md` section 6 (EntityLookup Component Pattern)
|
|
505
|
-
→ Critical: response parsing MUST use `(res.data.items || res.data).map(mapOption)` to handle both paginated and array responses
|
|
506
|
-
→ Do NOT improvise — copy the reference implementation verbatim
|
|
507
|
-
→ Do NOT write a simplified version with `result.data` or `Array.isArray(result.data)` — the reference handles both `PaginatedResult<T>` (`.items`) and raw array responses
|
|
508
|
-
→ Verify after generation: the component MUST contain the expression `(res.data.items || res.data).map(mapOption)` — if it does not, replace with the reference version
|
|
509
|
-
|
|
510
|
-
### Task Progress
|
|
511
|
-
TaskUpdate(taskId: layer3_task_id, status: "in_progress")
|
|
512
|
-
TaskUpdate(taskId: progress_tracker_id,
|
|
513
|
-
description: "Module: {module_code}. Current: step-03 (Execute), Layer 3",
|
|
514
|
-
activeForm: "Executing Layer 3")
|
|
515
|
-
|
|
516
|
-
> **Frontend patterns** are in `references/smartstack-frontend.md` (already loaded above):
|
|
517
|
-
> - API client generation (MCP scaffold_api_client)
|
|
518
|
-
> - Route scaffolding (MCP scaffold_routes) — section 1 + 3b
|
|
519
|
-
> - Page types (ListPage, DetailPage, CreatePage, EditPage) — section 3
|
|
520
|
-
> - FK field handling (EntityLookup component) — section 6
|
|
521
|
-
> - Form structure (no modals — all full pages) — section 3b
|
|
522
|
-
> - Detail page tabs (local state only) — section 3 "Tab Behavior Rules"
|
|
523
|
-
> - Testing patterns (co-located form tests) — section 8
|
|
524
|
-
> - Section-level routes and permissions — section 3b
|
|
525
|
-
> - I18n JSON structure and registration — section 2
|
|
526
|
-
> - Compliance gates (5 mandatory checks) — section 9
|
|
527
|
-
|
|
528
|
-
### Frontend Tasks (sequential or parallel within layer)
|
|
529
|
-
|
|
530
|
-
For each module:
|
|
531
|
-
- API client: MCP scaffold_api_client
|
|
532
|
-
- Routes — TWO mandatory steps:
|
|
533
|
-
1. Generate registry: MCP scaffold_routes (source: 'controllers', outputFormat: 'applicationRoutes', dryRun: false)
|
|
534
|
-
→ Creates navRoutes.generated.ts (required by POST-CHECK C2)
|
|
535
|
-
2. Wire to App.tsx (see below)
|
|
536
|
-
- Wire Routes to App.tsx: After scaffold_routes, routes must be wired into App.tsx:
|
|
537
|
-
→ Read App.tsx and detect the routing pattern
|
|
538
|
-
→ Pattern A (`applicationRoutes: ApplicationRouteExtensions`): add routes to `applicationRoutes['{application_kebab}'][]` with RELATIVE paths
|
|
539
|
-
→ Pattern B (JSX `<Route>`): nest routes inside `<Route path="/{application}" element={<AppLayout />}>` + duplicate in tenant block
|
|
540
|
-
→ **CRITICAL — Module Segment Required:** Route paths MUST include the module segment.
|
|
541
|
-
For a 3-level hierarchy (app → module → sections), paths are `{module_kebab}/{section_kebab}`.
|
|
542
|
-
Example: `employee-management/employees` NOT just `employees`.
|
|
543
|
-
Without the module segment, routes resolve to `/{app}/{section}` instead of `/{app}/{module}/{section}`,
|
|
544
|
-
causing mismatch with backend navigation seed data → nav links produce 404s.
|
|
545
|
-
→ **BEFORE wiring:** Verify route ordering — static routes (`create`, `dashboard`, `departments`) MUST come BEFORE dynamic routes (`:id`, `:id/edit`). Redirect routes (`Navigate`) MUST be LAST. See `references/smartstack-layers.md` "RULE — Frontend Route Ordering".
|
|
546
|
-
→ Do not add business routes to `clientRoutes[]` — it is only for non-app routes (`/about`, `/pricing`)
|
|
547
|
-
→ All business applications use `<AppLayout />` as layout wrapper
|
|
548
|
-
→ See `references/frontend-route-wiring-app-tsx.md` for full Pattern A/B detection and examples
|
|
549
|
-
→ Verify: `mcp__smartstack__validate_frontend_routes (scope: 'routes')`
|
|
550
|
-
- Pages per section: When a section exists (e.g., "list"), generate ALL 4 page types:
|
|
551
|
-
→ {Section}ListPage.tsx (index route)
|
|
552
|
-
→ {Section}DetailPage.tsx (/:id route) — click on list item navigates here
|
|
553
|
-
→ Create{Section}Page.tsx (/create route)
|
|
554
|
-
→ Edit{Section}Page.tsx (/:id/edit route)
|
|
555
|
-
"detail" is NEVER a separate section — it's the /:id route of the list section.
|
|
556
|
-
- Pages: **MANDATORY — INVOKE Skill("ui-components")** for ALL page generation.
|
|
557
|
-
⚠ BLOCKING REQUIREMENT: You MUST call Skill("ui-components") for EVERY page (.tsx).
|
|
558
|
-
NEVER generate .tsx page code directly. NEVER write page content in Agent prompts.
|
|
559
|
-
NEVER use Write tool to create pages without first calling Skill("ui-components").
|
|
560
|
-
The skill loads the full style guide + patterns (entity-card, data-table, dashboard-chart, grid-layout).
|
|
561
|
-
Follow smartstack-frontend.md patterns:
|
|
562
|
-
→ React.lazy() for all page imports (named export wrapping)
|
|
563
|
-
→ `<Suspense fallback={<PageLoader />}>` around all lazy components
|
|
564
|
-
→ Page structure: hooks → useEffect(load) → loading → error → content
|
|
565
|
-
→ CSS variables only: bg-[var(--bg-card)], text-[var(--text-primary)]
|
|
566
|
-
→ DataTable/EntityCard (not raw HTML `<table>`)
|
|
567
|
-
→ If entity has FK Guid fields: generate EntityLookup component in @/components/ui/EntityLookup (see smartstack-frontend.md section 6)
|
|
568
|
-
→ Dashboard pages: generate StatCard + ChartCard locally (see ui-components patterns/dashboard-chart.md)
|
|
569
|
-
- Form pages: Create/Edit forms are full pages with own routes:
|
|
570
|
-
→ EntityCreatePage.tsx with route /{module}/create
|
|
571
|
-
→ EntityEditPage.tsx with route /{module}/:id/edit
|
|
572
|
-
→ Zero modals/popups/drawers/dialogs for forms
|
|
573
|
-
→ Back button with navigate(-1) on every form page
|
|
574
|
-
→ See smartstack-frontend.md section 3b for templates
|
|
575
|
-
- Tabs on detail pages: Tabs must switch content locally:
|
|
576
|
-
→ Tab click handler: setActiveTab(tabKey) only — do not navigate() to another page
|
|
577
|
-
→ Tab content: render inline with {activeTab === 'tabKey' && <TabContent />}
|
|
578
|
-
→ Sub-resource data: fetch via API filtered by parent ID, display in tab panel
|
|
579
|
-
→ Do not use navigate('../leaves?employee=${id}') in tab handler
|
|
580
|
-
→ See smartstack-frontend.md section 3 'Tab Behavior Rules'
|
|
581
|
-
- FK fields: Foreign key Guid fields (e.g., EmployeeId) must use EntityLookup:
|
|
582
|
-
→ Do not render FK fields as `<input>` — users cannot type GUIDs
|
|
583
|
-
→ Do not render FK fields as `<select>` — even with API-loaded options, `<select>` is not acceptable
|
|
584
|
-
→ Only use `<EntityLookup />` from @/components/ui/EntityLookup for searchable selection
|
|
585
|
-
→ Each FK field needs: apiEndpoint, mapOption (display name), search support on backend
|
|
586
|
-
→ Do not use `<select value={formData.departmentId}>`, `<option value={dept.id}>`, `e.target.value` for FK fields
|
|
587
|
-
→ See smartstack-frontend.md section 6 for the full EntityLookup pattern
|
|
588
|
-
- I18n: Generate 4 JSON files per module namespace (fr, en, it, de)
|
|
589
|
-
→ Follow JSON template from smartstack-frontend.md
|
|
590
|
-
→ Keys: actions, labels, errors, validation, columns, form, messages, empty
|
|
591
|
-
→ All t() calls must use namespace prefix + fallback: t('ns:key', 'Default text')
|
|
592
|
-
→ **Register namespace in i18n config** (critical — POST-CHECK C39 catches this, but fix it now):
|
|
593
|
-
1. Find i18n config: `Glob("src/**/i18n/config.ts")` or `index.ts` or `i18n.ts`
|
|
594
|
-
2. Read the config → find the resources/ns registration pattern
|
|
595
|
-
3. Add the new namespace import + registration for all 4 languages
|
|
596
|
-
4. If config uses dynamic imports: add namespace to the `ns` array
|
|
597
|
-
5. Verify: `grep -q "{module_namespace}" src/**/i18n/config.ts` → must match
|
|
598
|
-
- Permissions: Call MCP generate_permissions for the module permission root (2 segments: {app}.{module}),
|
|
599
|
-
then also call MCP generate_permissions for each section (3 segments: {app}.{module}.{section}).
|
|
600
|
-
- Section routes: Add section child routes to `applicationRoutes['{app_kebab}']`.
|
|
601
|
-
Route paths MUST include the module segment: `{module_kebab}/{section_kebab}` (e.g., `employee-management/employees`).
|
|
602
|
-
Do NOT use just `{section_kebab}` (e.g., `employees`) — this omits the module level and causes 404s.
|
|
603
|
-
Wire PermissionGuard for section routes with section-level permissions.
|
|
604
|
-
- MUST use src/pages/{App}/{Module}/ hierarchy (NOT flat)
|
|
605
|
-
|
|
606
|
-
### Documentation (after frontend pages exist)
|
|
607
|
-
|
|
608
|
-
> **After frontend pages are created, generate module documentation via `/documentation` skill.**
|
|
609
|
-
|
|
610
|
-
```
|
|
611
|
-
Invoke /documentation {module_code} --type user
|
|
612
|
-
|
|
613
|
-
This generates:
|
|
614
|
-
- src/pages/docs/business/{app}/{module}/doc-data.ts (data file)
|
|
615
|
-
- src/pages/docs/business/{app}/{module}/index.tsx (page wrapper)
|
|
616
|
-
- src/i18n/locales/fr/docs-{app}-{module}.json (French doc translations)
|
|
617
|
-
- Updates App.tsx routing for doc page
|
|
618
|
-
- Updates docs-manifest.json
|
|
619
|
-
```
|
|
620
|
-
|
|
621
|
-
**Verify DocToggleButton presence in list/detail pages:**
|
|
622
|
-
- Every `*ListPage.tsx` and `*DetailPage.tsx` MUST import and render `DocToggleButton` in their header
|
|
623
|
-
- Import: `import { DocToggleButton } from '@/components/docs/DocToggleButton';`
|
|
624
|
-
- Placement: inside the header actions area (see `smartstack-frontend.md` section 7)
|
|
625
|
-
|
|
626
|
-
### If NOT economy_mode AND multiple entities: Parallel Agents (within layer)
|
|
627
|
-
|
|
628
|
-
> **Protocol:** See `references/parallel-execution.md`
|
|
629
|
-
|
|
630
|
-
```
|
|
631
|
-
IF NOT economy_mode AND entities.length > 1:
|
|
632
|
-
# PHASE A — Parallel: infrastructure frontend (NO page generation)
|
|
633
|
-
# Snipper agents DO NOT have access to the Skill tool, so they CANNOT call /ui-components.
|
|
634
|
-
# Pages MUST be generated by the principal agent in Phase B.
|
|
635
|
-
For each entity, launch in parallel (single message):
|
|
636
|
-
Agent(subagent_type='Snipper', model='opus',
|
|
637
|
-
prompt='Execute Layer 3 INFRASTRUCTURE for {EntityName}:
|
|
638
|
-
**MANDATORY: Read references/smartstack-frontend.md FIRST**
|
|
639
|
-
- API client: MCP scaffold_api_client
|
|
640
|
-
- Routes: MCP scaffold_routes (outputFormat: "applicationRoutes", dryRun: false) → MUST generate navRoutes.generated.ts
|
|
641
|
-
- Wire Routes to App.tsx (BLOCKING): detect Pattern A/B, wire accordingly
|
|
642
|
-
→ CRITICAL: Route paths MUST include module segment: {module_kebab}/{section_kebab} (e.g., employee-management/employees, NOT just employees)
|
|
643
|
-
→ See references/frontend-route-wiring-app-tsx.md for full patterns
|
|
644
|
-
→ Verify: mcp__smartstack__validate_frontend_routes (scope: "routes")
|
|
645
|
-
- I18n: 4 JSON files (fr, en, it, de) + REGISTER namespace in i18n config
|
|
646
|
-
- DO NOT generate any .tsx page files — pages are handled by the principal agent
|
|
647
|
-
- Your task ID is {task_id}. Call TaskUpdate(status: "in_progress") before starting.
|
|
648
|
-
- Call TaskUpdate(status: "completed", metadata: { files_created: [...] }) when done.')
|
|
649
|
-
# Wait for all agents to complete
|
|
650
|
-
|
|
651
|
-
# PHASE B — Sequential: pages via /ui-components (principal agent)
|
|
652
|
-
# Snipper agents cannot call Skill() — only the principal agent can.
|
|
653
|
-
For each entity (sequentially):
|
|
654
|
-
**INVOKE Skill("ui-components")** — pass entity context:
|
|
655
|
-
- Entity: {EntityName}, Module: {ModuleName}, App: {AppName}
|
|
656
|
-
- Page types: List, Detail, Create, Edit (+ Dashboard if applicable)
|
|
657
|
-
- "CSS: Use CSS variables ONLY — bg-[var(--bg-card)], text-[var(--text-primary)]"
|
|
658
|
-
- "Forms: Create/Edit are FULL PAGES with own routes (/create, /:id/edit)"
|
|
659
|
-
- "FK FIELDS: EntityLookup for ALL FK Guid fields"
|
|
660
|
-
- "I18n: ALL text uses t('namespace:key', 'Fallback')"
|
|
661
|
-
Generate form tests: co-located .test.tsx for Create and Edit pages
|
|
662
|
-
|
|
663
|
-
ELSE:
|
|
664
|
-
# Economy mode: Agent principal handles all entities SEQUENTIALLY.
|
|
665
|
-
# MANDATORY: You MUST still call Skill("ui-components") for page generation.
|
|
666
|
-
# economy_mode only disables parallel agents — it does NOT skip /ui-components.
|
|
667
|
-
For each entity (sequentially):
|
|
668
|
-
1. MCP scaffold_api_client
|
|
669
|
-
2. MCP scaffold_routes (outputFormat: 'applicationRoutes')
|
|
670
|
-
3. Wire routes to App.tsx (Pattern A/B — see references/frontend-route-wiring-app-tsx.md)
|
|
671
|
-
CRITICAL: paths MUST include module segment: {module_kebab}/{section_kebab} (e.g., employee-management/employees)
|
|
672
|
-
4. **INVOKE Skill("ui-components")** — pass entity context:
|
|
673
|
-
- Entity: {EntityName}, Module: {ModuleName}, App: {AppName}
|
|
674
|
-
- Page types: List, Detail, Create, Edit (+ Dashboard if applicable)
|
|
675
|
-
- "CSS: Use CSS variables ONLY — bg-[var(--bg-card)], text-[var(--text-primary)]"
|
|
676
|
-
- "Forms: Create/Edit are FULL PAGES with own routes (/create, /:id/edit)"
|
|
677
|
-
- "I18n: ALL text uses t('namespace:key', 'Fallback')"
|
|
678
|
-
5. I18n: 4 JSON files (fr, en, it, de) + register namespace in i18n config
|
|
679
|
-
6. Form tests: co-located .test.tsx for Create and Edit pages
|
|
680
|
-
```
|
|
681
|
-
|
|
682
|
-
### Parallel Agents + TaskCreate Integration
|
|
683
|
-
|
|
684
|
-
When launching agents for multi-entity layers:
|
|
685
|
-
- Each agent receives its task ID and metadata context in the prompt
|
|
686
|
-
- Agent must call TaskUpdate(status: "in_progress") before starting
|
|
687
|
-
- Agent must call TaskUpdate(status: "completed", metadata: { files_created: [...] }) when done
|
|
688
|
-
- Agent principal monitors via TaskList() after all agents complete
|
|
689
|
-
- Agent principal updates activeForm after each entity completion:
|
|
690
|
-
`TaskUpdate(taskId: layer3_task_id, activeForm: "Building {EntityName} frontend (2/5 entities)")`
|
|
691
|
-
|
|
692
|
-
### Frontend Compliance Gate
|
|
693
|
-
|
|
694
|
-
> See `references/smartstack-frontend-compliance.md` section 9 "Compliance Gates" for all 6 required checks:
|
|
695
|
-
> 1. CSS Variables (theme system)
|
|
696
|
-
> 2. Forms as Pages (zero modals/drawers/slide-overs)
|
|
697
|
-
> 3. I18n File Structure (4 languages, separate JSON files)
|
|
698
|
-
> 4. Lazy Loading (React.lazy() — no static imports)
|
|
699
|
-
> 5. useTranslation in Pages (all text translated)
|
|
700
|
-
> 6. SmartStack Components (no raw HTML tables — DataTable/EntityCard required)
|
|
701
|
-
|
|
702
|
-
Do not commit frontend changes until all 6 gates pass.
|
|
703
|
-
|
|
704
|
-
When delegating to `/ui-components` skill, include explicit instructions:
|
|
705
|
-
- "CSS: Use CSS variables ONLY — `bg-[var(--bg-card)]`, `text-[var(--text-primary)]`."
|
|
706
|
-
- "Forms: Create/Edit forms are FULL PAGES with own routes (e.g., `/create`, `/:id/edit`)."
|
|
707
|
-
- "I18n: ALL text must use `t('namespace:key', 'Fallback')`. Generate JSON files in `src/i18n/locales/`."
|
|
708
|
-
|
|
709
|
-
### Frontend Tests Inline
|
|
710
|
-
|
|
711
|
-
> **Tests are scaffolded and run WITHIN Layer 3, not deferred to step-07.**
|
|
712
|
-
|
|
713
|
-
```
|
|
714
|
-
1. Scaffold frontend tests:
|
|
715
|
-
→ For each module with create/edit pages:
|
|
716
|
-
→ Generate EntityCreatePage.test.tsx and EntityEditPage.test.tsx
|
|
717
|
-
→ Co-located next to the page component (NOT in __tests__/ folder)
|
|
718
|
-
→ Cover: rendering, validation, submit, pre-fill, navigation, errors
|
|
719
|
-
→ Tools: Vitest + React Testing Library + @testing-library/user-event
|
|
720
|
-
→ Mock API: vi.mock() or MSW — NEVER real API calls
|
|
721
|
-
|
|
722
|
-
2. Run tests:
|
|
723
|
-
WEB_DIR=$(find . -name "vitest.config.ts" -not -path "*/node_modules/*" -exec dirname {} \; 2>/dev/null | head -1)
|
|
724
|
-
if [ -n "$WEB_DIR" ]; then
|
|
725
|
-
(cd "$WEB_DIR" && npm run test)
|
|
726
|
-
fi
|
|
727
|
-
|
|
728
|
-
3. Fix loop (max 3 iterations):
|
|
729
|
-
IF tests fail:
|
|
730
|
-
→ Identify root cause (ALWAYS code bug, not test bug)
|
|
731
|
-
→ Fix production CODE
|
|
732
|
-
→ Re-run tests
|
|
733
|
-
→ Repeat until pass or max 3
|
|
734
|
-
|
|
735
|
-
4. If still failing after 3 iterations: note failures, continue.
|
|
736
|
-
Step-07 "Final Test Sweep" will handle remaining failures.
|
|
737
|
-
```
|
|
738
|
-
|
|
739
|
-
### Typecheck Gate
|
|
740
|
-
|
|
741
|
-
```bash
|
|
742
|
-
npm run typecheck
|
|
743
|
-
```
|
|
744
|
-
|
|
745
|
-
Must pass before commit.
|
|
746
|
-
|
|
747
|
-
TaskUpdate(taskId: layer3_task_id, status: "completed",
|
|
748
|
-
metadata: { build_gate: "pass" })
|
|
749
|
-
|
|
750
|
-
### Layer 3 Commits
|
|
751
|
-
|
|
752
|
-
```
|
|
753
|
-
feat({module}): [frontend] pages, routes, i18n, documentation
|
|
754
|
-
test({module}): frontend form tests
|
|
755
|
-
```
|
|
756
|
-
|
|
757
|
-
---
|
|
758
|
-
|
|
759
|
-
## Layer 4 — DevData (optional)
|
|
760
|
-
|
|
761
|
-
### Task Progress
|
|
762
|
-
IF layer4 task exists: TaskUpdate(taskId: layer4_task_id, status: "in_progress")
|
|
763
|
-
IF layer4 task exists: TaskUpdate(taskId: progress_tracker_id,
|
|
764
|
-
description: "Module: {module_code}. Current: step-03 (Execute), Layer 4",
|
|
765
|
-
activeForm: "Executing Layer 4")
|
|
766
|
-
|
|
767
|
-
> **Business test data for development/demo environments.**
|
|
768
|
-
> Skip this layer if no meaningful test data is needed.
|
|
769
|
-
|
|
770
|
-
```
|
|
771
|
-
1. Create DevDataSeeder:
|
|
772
|
-
→ Infrastructure/Persistence/Seeding/DevData/{Module}DevDataSeeder.cs
|
|
773
|
-
→ ALL entities MUST include TenantId
|
|
774
|
-
→ Realistic business data for demo purposes
|
|
775
|
-
|
|
776
|
-
2. Build gate:
|
|
777
|
-
dotnet build → MUST PASS
|
|
778
|
-
```
|
|
64
|
+
## Layer Dispatch
|
|
779
65
|
|
|
780
|
-
|
|
781
|
-
metadata: { build_gate: "pass" })
|
|
66
|
+
Load each sub-step sequentially:
|
|
782
67
|
|
|
783
|
-
|
|
68
|
+
| Layer | Sub-step | Content |
|
|
69
|
+
|-------|----------|---------|
|
|
70
|
+
| 0 | `steps/step-03a-layer0-domain.md` | Domain entities, EF configs, migration |
|
|
71
|
+
| 1 | `steps/step-03b-layer1-seed.md` | Seed data (navigation, permissions, roles) |
|
|
72
|
+
| 2 | `steps/step-03c-layer2-backend.md` | Services, controllers, backend tests |
|
|
73
|
+
| 3 | `steps/step-03d-layer3-frontend.md` | Pages, i18n, routes, frontend tests |
|
|
74
|
+
| 4 | `steps/step-03e-layer4-devdata.md` | Development test data (optional) |
|
|
784
75
|
|
|
785
|
-
|
|
786
|
-
feat({module}): [devdata] test data for development
|
|
787
|
-
```
|
|
76
|
+
**FIRST ACTION:** Load `steps/step-03a-layer0-domain.md`
|
|
788
77
|
|
|
789
78
|
---
|
|
790
79
|
|
|
@@ -801,7 +90,7 @@ IF delegate_mode:
|
|
|
801
90
|
task.completed_at = new Date().toISOString()
|
|
802
91
|
task.iteration = 1 (delegate mode = single iteration)
|
|
803
92
|
task.commit_hash = $(git rev-parse --short HEAD)
|
|
804
|
-
task.files_changed = { created: [...
|
|
93
|
+
task.files_changed = { created: [...], modified: [...] }
|
|
805
94
|
task.validation = "APEX_PASS" (or error details)
|
|
806
95
|
prd.updated_at = new Date().toISOString()
|
|
807
96
|
Write back to {delegate_prd_path}
|
|
@@ -811,8 +100,6 @@ IF delegate_mode:
|
|
|
811
100
|
|
|
812
101
|
## Commits Per Layer
|
|
813
102
|
|
|
814
|
-
After each layer completes, gates pass, and builds pass:
|
|
815
|
-
|
|
816
103
|
```
|
|
817
104
|
Layer 0: feat({module}): [domain+infra] {short description}
|
|
818
105
|
Layer 1: feat({module}): [seed] navigation, permissions, roles
|
|
@@ -835,8 +122,7 @@ After each layer's build gate passes, write state to `.claude/output/apex/{task_
|
|
|
835
122
|
|
|
836
123
|
```json
|
|
837
124
|
{
|
|
838
|
-
"step": 3,
|
|
839
|
-
"layer": 2,
|
|
125
|
+
"step": 3, "layer": 2,
|
|
840
126
|
"completed_layers": [0, 1, 2],
|
|
841
127
|
"completed_entities": ["Employee", "Department"],
|
|
842
128
|
"files_created": ["Employee.cs", "EmployeeConfiguration.cs", "..."],
|
|
@@ -846,19 +132,6 @@ After each layer's build gate passes, write state to `.claude/output/apex/{task_
|
|
|
846
132
|
}
|
|
847
133
|
```
|
|
848
134
|
|
|
849
|
-
```
|
|
850
|
-
IF .claude/output/apex/{task_id}/ does not exist:
|
|
851
|
-
Generate {task_id} from timestamp (e.g., "20260306-143000")
|
|
852
|
-
Create directory
|
|
853
|
-
|
|
854
|
-
After EACH layer build gate passes:
|
|
855
|
-
Read existing state.json (or create new)
|
|
856
|
-
Update: layer, completed_layers, completed_entities, files_created, build_gates, commits
|
|
857
|
-
Write back to state.json
|
|
858
|
-
```
|
|
859
|
-
|
|
860
|
-
This file is consumed by resume mode (`-r`) to skip completed layers instead of re-deriving from git.
|
|
861
|
-
|
|
862
135
|
---
|
|
863
136
|
|
|
864
137
|
## Save Output (if save_mode)
|