@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.
Files changed (46) hide show
  1. package/.documentation/index.html +2 -2
  2. package/.documentation/init.html +358 -174
  3. package/dist/index.js +45 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/mcp-entry.mjs +271 -44
  6. package/dist/mcp-entry.mjs.map +1 -1
  7. package/package.json +1 -1
  8. package/templates/mcp-scaffolding/controller.cs.hbs +54 -128
  9. package/templates/project/README.md +19 -0
  10. package/templates/project/claude-md/api.CLAUDE.md.template +315 -0
  11. package/templates/project/claude-md/application.CLAUDE.md.template +181 -0
  12. package/templates/project/claude-md/domain.CLAUDE.md.template +125 -0
  13. package/templates/project/claude-md/infrastructure.CLAUDE.md.template +168 -0
  14. package/templates/project/claude-md/root.CLAUDE.md.template +339 -0
  15. package/templates/project/claude-md/web.CLAUDE.md.template +339 -0
  16. package/templates/skills/apex/SKILL.md +16 -10
  17. package/templates/skills/apex/_shared.md +1 -1
  18. package/templates/skills/apex/references/checks/architecture-checks.sh +154 -0
  19. package/templates/skills/apex/references/checks/backend-checks.sh +194 -0
  20. package/templates/skills/apex/references/checks/frontend-checks.sh +448 -0
  21. package/templates/skills/apex/references/checks/infrastructure-checks.sh +255 -0
  22. package/templates/skills/apex/references/checks/security-checks.sh +153 -0
  23. package/templates/skills/apex/references/checks/seed-checks.sh +536 -0
  24. package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +49 -192
  25. package/templates/skills/apex/references/post-checks.md +124 -2156
  26. package/templates/skills/apex/references/smartstack-api.md +160 -957
  27. package/templates/skills/apex/references/smartstack-frontend.md +134 -1022
  28. package/templates/skills/apex/references/smartstack-layers.md +12 -6
  29. package/templates/skills/apex/steps/step-00-init.md +81 -238
  30. package/templates/skills/apex/steps/step-03-execute.md +25 -752
  31. package/templates/skills/apex/steps/step-03a-layer0-domain.md +118 -0
  32. package/templates/skills/apex/steps/step-03b-layer1-seed.md +91 -0
  33. package/templates/skills/apex/steps/step-03c-layer2-backend.md +240 -0
  34. package/templates/skills/apex/steps/step-03d-layer3-frontend.md +300 -0
  35. package/templates/skills/apex/steps/step-03e-layer4-devdata.md +44 -0
  36. package/templates/skills/apex/steps/step-04-examine.md +70 -150
  37. package/templates/skills/application/references/frontend-i18n-and-output.md +2 -2
  38. package/templates/skills/application/references/frontend-route-naming.md +5 -1
  39. package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +49 -198
  40. package/templates/skills/application/references/frontend-verification.md +11 -11
  41. package/templates/skills/application/steps/step-05-frontend.md +26 -15
  42. package/templates/skills/application/templates-frontend.md +4 -0
  43. package/templates/skills/cli-app-sync/SKILL.md +2 -2
  44. package/templates/skills/cli-app-sync/references/comparison-map.md +1 -1
  45. package/templates/skills/controller/references/controller-code-templates.md +70 -67
  46. 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
- This is a foundation-only execution (called by ralph-loop Phase 0). Execute ONLY:
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
- Restore: completed_layers, completed_entities, files_created
61
- 2. IF state.json missing:
62
- Re-derive from filesystem:
63
- - {app_name}: Glob("docs/business/*/") → first directory name
64
- - {module_code}: Glob("src/**/Domain/Entities/*/") → target module directory
65
- - {entities}: Glob("src/**/Domain/Entities/{module_code}/*.cs") entity names
66
- - {sections}: Glob("src/**/Seeding/Data/{module_code}/*NavigationSeedData.cs") parse GetSectionEntries
67
- - {code_patterns}: Read state.json code_patterns field, OR re-derive from existing ICodeGenerator registrations in DependencyInjection.cs, OR default to "manual" for all entities
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 0 — Domain + Infrastructure (sequential, agent principal)
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
- IF layer4 task exists: TaskUpdate(taskId: layer4_task_id, status: "completed",
781
- metadata: { build_gate: "pass" })
66
+ Load each sub-step sequentially:
782
67
 
783
- ### Layer 4 Commit (if applicable)
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: [...files created...], modified: [...files modified...] }
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)