---
name: step-03-execute
description: Orchestrate code creation via existing skills and MCP tools
model: opus
prev_step: steps/step-02-plan.md
next_step: steps/step-04-examine.md
---

# Step 3: Execute

/apex ORCHESTRATES. It does not write SmartStack code directly.
All code goes through skills (/controller, /application, /ui-components, /efcore) or MCP tools.

## LOAD CONDITIONALLY

> 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.

- Read `references/core-seed-data.md` — comprehensive seed data templates (needed for Layer 1, released after)
- Defer `references/smartstack-frontend.md` until Layer 3 — lazy loading, i18n, page structure, CSS variables, EntityLookup (sections 1-6)
- Defer `references/smartstack-frontend-compliance.md` until Layer 3 — documentation, form testing, compliance gates (sections 7-9)
- If NOT `{economy_mode}` AND a layer has multi-entity work: read `references/parallel-execution.md`
- If build failure during execution: read `references/error-classification.md` — error diagnosis categories A-F

---

## Foundation Mode Detection

**IF `{foundation_mode}` == true:**

This is a foundation-only execution (called by ralph-loop Phase 0). Execute ONLY:
- Layer 0: Domain entities + EF configs + Migration

Skip all other layers:
- Layer 1: Seed Data → SKIP
- Layer 2: Backend → SKIP
- Layer 3: Frontend → SKIP
- Layer 4: DevData → SKIP

After Layer 0 completes and builds successfully:
- Commit with message: `chore(foundation): entities for {module_code}`
- Jump to step-04 for POST-CHECKs (domain + infrastructure only)
- End execution

**IF `{foundation_mode}` == false:**
Execute all layers (Layer 0 → Layer 1 → Layer 2 → Layer 3 → Layer 4).

---

## Context Recovery Protocol

> **Run this check BEFORE each layer** to survive context compression.

```
BEFORE starting Layer N:
  Verify these variables are still accessible:
    {app_name}, {module_code}, {sections}, {entities}, {code_patterns}

  IF any variable is missing or empty:
    1. Read .claude/output/apex/{task_id}/state.json (if exists)
       → Restore: completed_layers, completed_entities, files_created
    2. IF state.json missing:
       → Re-derive from filesystem:
         - {app_name}: Glob("docs/business/*/") → first directory name
         - {module_code}: Glob("src/**/Domain/Entities/*/") → target module directory
         - {entities}: Glob("src/**/Domain/Entities/{module_code}/*.cs") → entity names
         - {sections}: Glob("src/**/Seeding/Data/{module_code}/*NavigationSeedData.cs") → parse GetSectionEntries
    3. IF Layer N-1 was already completed (check state.json or git log):
       → Skip to Layer N directly

  Cost: ~5 tool calls. Only triggered if context was compressed.
```

---

## Layer 0 — Domain + Infrastructure (sequential, agent principal)

### Task Progress
TaskUpdate(taskId: layer0_task_id, status: "in_progress")
TaskUpdate(taskId: progress_tracker_id,
  description: "Module: {module_code}. Current: step-03 (Execute), Layer 0",
  activeForm: "Executing Layer 0")

### Domain Entities

```
For each entity to create/modify:
  → MCP scaffold_extension (type: "entity", target: entity_name)
  → Verify: inherits BaseEntity, implements ITenantEntity + IAuditableEntity
  → Verify entity matches patterns in references/smartstack-api.md
```

### Person Extension Detection

**If entity has personRoleConfig (mandatory or optional UserId link):**
See `references/person-extension-pattern.md` for full entity, EF config, service, DTO, and frontend patterns.

```
1. scaffold_extension with options: { isPersonRole: true, userLinkMode: 'mandatory' | 'optional' }
2. Verify: EF config has unique index on (TenantId, UserId)
   → Mandatory variant: plain .IsUnique()
   → Optional variant: .IsUnique().HasFilter("[UserId] IS NOT NULL")
3. Verify all build checks pass before continuing
```

### EF Core Configurations

```
For each entity:
  → Create IEntityTypeConfiguration<T> manually per smartstack-api.md patterns
  → Verify: table name, relationships, indexes
  → Register DbSet in ExtensionsDbContext if new entity
```

### Migration

> 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.

```
1. Verify all entities have been added as DbSet in ExtensionsDbContext
2. Verify all EF configurations are registered (ApplyConfigurationsFromAssembly or individual)
3. MCP suggest_migration → get standardized name
4. dotnet ef migrations add {Name} --project src/{Infra}.csproj --startup-project src/{Api}.csproj -o Persistence/Migrations
5. dotnet ef database update (if local DB)
6. dotnet build
7. Verify: dotnet ef migrations has-pending-model-changes → must report "No pending model changes"
```

If build fails after migration, fix EF configs before proceeding.
If `has-pending-model-changes` reports pending changes, entities are missing from the migration — create a new migration.

### Post-Layer 0 Build Gate

```bash
dotnet build
# Note: WSL bin\Debug cleanup handled by PostToolUse hook (wsl-dotnet-cleanup.sh)
```

Must pass before Layer 1. If NuGet error, run `dotnet restore` first. If file lock (MSB3021), use `--output /tmp/{project}_build`.

TaskUpdate(taskId: layer0_task_id, status: "completed",
  metadata: { build_gate: "pass" })

### Layer 0 Commit

```
feat({module}): [domain+infra] {short description}
```

---

## Layer 1 — Seed Data (DEDICATED LAYER — sequential, agent principal)

### Task Progress
TaskUpdate(taskId: layer1_task_id, status: "in_progress")
TaskUpdate(taskId: progress_tracker_id,
  description: "Module: {module_code}. Current: step-03 (Execute), Layer 1",
  activeForm: "Executing Layer 1")

> 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.

### Application-Level Seed Data (ONCE per application)

```
1. NavigationApplicationSeedData.cs
   → Application-level navigation entry (MUST be first)
   → 4 language translations (fr, en, it, de)

2. ApplicationRolesSeedData.cs
   → 4 roles: admin, manager, contributor, viewer
   → References NavigationApplicationSeedData.ApplicationId
```

### Per-Module Seed Data

```
3. NavigationModuleSeedData.cs
   → 4 languages (fr, en, it, de), GetModuleEntry() + GetTranslationEntries()
   → If sections defined: GetSectionEntries() + GetSectionTranslationEntries()
   → If resources defined: GetResourceEntries() + resource translations
   → Query actual parent from DB for FK (NOT deterministic GUID)

4. Permissions.cs
   → MCP generate_permissions (PRIMARY tool)
   → Static constants: public static class {Module} { public const string Read = "..."; }
   → Permission paths MUST use kebab-case matching NavRoute codes

5. PermissionsSeedData.cs
   → MCP generate_permissions first, fallback template
   → Paths match Permissions.cs, wildcard + CRUD

6. RolesSeedData.cs
   → Admin=wildcard(*), Manager=CRU, Contributor=CR, Viewer=R
   → Code-based role mapping (not deterministic GUIDs for roles)
   → Look up roles by Code at runtime
```

### Infrastructure Provider (ONCE per application)

```
7. {App}SeedDataProvider.cs
   → Implements IClientSeedDataProvider
   → SeedNavigationAsync(): Application → Module → Section → Resource + translations
   → SeedRolesAsync(): application-scoped roles from ApplicationRolesSeedData
   → SeedPermissionsAsync(): permission entries from PermissionsSeedData
   → SeedRolePermissionsAsync(): maps roles to permissions (by Code, NOT by GUID)
   → DI: services.AddScoped<IClientSeedDataProvider, {App}SeedDataProvider>()
```

### Post-Layer 1 Build Gate

```bash
dotnet build
```

Must pass before Layer 2.

TaskUpdate(taskId: layer1_task_id, status: "completed",
  metadata: { build_gate: "pass" })

### Layer 1 Commit

```
feat({module}): [seed] navigation, permissions, roles
```

> **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+.

---

## Layer 2 — Backend (Services + Controllers)

### Task Progress
TaskUpdate(taskId: layer2_task_id, status: "in_progress")
TaskUpdate(taskId: progress_tracker_id,
  description: "Module: {module_code}. Current: step-03 (Execute), Layer 2",
  activeForm: "Executing Layer 2")

> **Layer 2 rules** are in `references/smartstack-layers.md` (already in context from step-02):
> - NavRoute and permission kebab-case (Layer 2 - API section)
> - Controller route attributes (FORBIDDEN: [Route] alongside [NavRoute])
> - Validators DI registration
> - DateOnly vs string for DTO date fields
> - Code generation patterns (ICodeGenerator<T> registration, see references/code-generation.md)

### Backend Tasks (sequential or parallel within layer)

- Services/DTOs: MCP scaffold_extension
- Controllers: use /controller skill for complex, MCP scaffold_extension for simple
- Important: All GetAll endpoints must support `?search=` query parameter (enables EntityLookup on frontend)

### Skill Delegation

| Skill | When | Context to provide |
|-------|------|--------------------|
| /controller | Custom routes, complex auth, file upload | entity, CRUD actions, permissions, routes |
| /application | Full app/module from scratch | app, module names |
| /ui-components | Complex pages (tables, grids, dashboards) | entity, fields, page type |
| /efcore | Complex EF configs, inheritance | relationships, indexes |
| /notification | In-app or email notifications | trigger, recipients, template |
| /workflow | Automated workflows | trigger, steps, conditions |

### If NOT economy_mode AND multiple entities: Parallel Agents (within layer)

> **Protocol:** See `references/parallel-execution.md`

```
IF NOT economy_mode AND entities.length > 1:
  For each entity, launch in parallel (single message):
    Agent(subagent_type='Snipper', model='opus',
      prompt='Execute Layer 2 backend for {EntityName}:
        - Application service/DTO: MCP scaffold_extension
        - Controller: /controller skill or MCP scaffold_extension
        - IMPORTANT: GetAll endpoint MUST support ?search= parameter
        - Validators: FluentValidation + DI registration
        - Your task ID is {task_id}. Call TaskUpdate(status: "in_progress") before starting.
        - Call TaskUpdate(status: "completed", metadata: { files_created: [...] }) when done.')
  # All agents launched in parallel

ELSE:
  # Agent principal handles all entities sequentially
```

### Parallel Agents + TaskCreate Integration

When launching agents for multi-entity layers:
- Each agent receives its task ID and metadata context in the prompt
- Agent must call TaskUpdate(status: "in_progress") before starting
- Agent must call TaskUpdate(status: "completed", metadata: { files_created: [...] }) when done
- Agent principal monitors via TaskList() after all agents complete
- Agent principal updates activeForm after each entity completion:
  `TaskUpdate(taskId: layer2_task_id, activeForm: "Building {EntityName} backend (2/5 entities)")`

### Post-Layer 2 Build Gate

```bash
dotnet build
```

Must pass before backend tests.

TaskUpdate(taskId: layer2_task_id, status: "completed",
  metadata: { build_gate: "pass" })
TaskUpdate(taskId: progress_tracker_id,
  activeForm: "Running build gate (Layer 2)")

### Backend Tests Inline

> **Tests are scaffolded and run WITHIN Layer 2, not deferred to step-07.**

```
1. Scaffold backend tests:
   → MCP scaffold_tests (target_layer: "domain", module: "{module_code}", test_type: "unit")
   → MCP scaffold_tests (target_layer: "application", module: "{module_code}", test_type: "unit")
   → MCP scaffold_tests (target_layer: "api", module: "{module_code}", test_type: "integration")

2. Run tests:
   dotnet test --no-build --verbosity normal

3. Fix loop (max 3 iterations):
   IF tests fail:
     → Identify root cause (ALWAYS code bug, not test bug)
     → Fix production CODE via appropriate skill/MCP
     → dotnet build --no-restore
     → dotnet test --no-build
     → Repeat until pass or max 3

4. If still failing after 3 iterations: note failures, continue to Layer 3.
   Step-07 "Final Test Sweep" will handle remaining failures.
```

### Layer 2 Commits

```
feat({module}): [app+api] {short description}
test({module}): backend unit and integration tests
```

> **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`).

---

## Layer 3 — Frontend (Pages + I18n + Documentation)

### Load Frontend References (deferred from top of step)

- Read `references/smartstack-frontend.md` now — lazy loading, i18n, page structure, CSS variables, EntityLookup (sections 1-6)
- Read `references/smartstack-frontend-compliance.md` now — documentation, form testing, compliance gates (sections 7-9)

### Task Progress
TaskUpdate(taskId: layer3_task_id, status: "in_progress")
TaskUpdate(taskId: progress_tracker_id,
  description: "Module: {module_code}. Current: step-03 (Execute), Layer 3",
  activeForm: "Executing Layer 3")

> **Frontend patterns** are in `references/smartstack-frontend.md` (already loaded above):
> - API client generation (MCP scaffold_api_client)
> - Route scaffolding (MCP scaffold_routes) — section 1 + 3b
> - Page types (ListPage, DetailPage, CreatePage, EditPage) — section 3
> - FK field handling (EntityLookup component) — section 6
> - Form structure (no modals — all full pages) — section 3b
> - Detail page tabs (local state only) — section 3 "Tab Behavior Rules"
> - Testing patterns (co-located form tests) — section 8
> - Section-level routes and permissions — section 3b
> - I18n JSON structure and registration — section 2
> - Compliance gates (5 mandatory checks) — section 9

### Frontend Tasks (sequential or parallel within layer)

For each module:
- API client: MCP scaffold_api_client
- Routes: MCP scaffold_routes (outputFormat: 'applicationRoutes') → generates lazy imports + Suspense
- Wire Routes to App.tsx: After scaffold_routes, routes must be wired into App.tsx:
  → Read App.tsx and detect the routing pattern
  → Pattern A (`applicationRoutes: ApplicationRouteExtensions`): add routes to `applicationRoutes['{application_kebab}'][]` with RELATIVE paths
  → Pattern B (JSX `<Route>`): nest routes inside `<Route path="/{application}" element={<AppLayout />}>` + duplicate in tenant block
  → Do not add business routes to `clientRoutes[]` — it is only for non-app routes (`/about`, `/pricing`)
  → All business applications use `<AppLayout />` as layout wrapper
  → See `references/frontend-route-wiring-app-tsx.md` for full Pattern A/B detection and examples
  → Verify: `mcp__smartstack__validate_frontend_routes (scope: 'routes')`
- Pages: use /ui-components skill — follow smartstack-frontend.md patterns:
  → React.lazy() for all page imports (named export wrapping)
  → `<Suspense fallback={<PageLoader />}>` around all lazy components
  → Page structure: hooks → useEffect(load) → loading → error → content
  → CSS variables only: bg-[var(--bg-card)], text-[var(--text-primary)]
  → DataTable/EntityCard (not raw HTML `<table>`)
  → If entity has FK Guid fields: generate EntityLookup component in @/components/ui/EntityLookup (see smartstack-frontend.md section 6)
  → Dashboard pages: generate StatCard + ChartCard locally (see ui-components patterns/dashboard-chart.md)
- Form pages: Create/Edit forms are full pages with own routes:
  → EntityCreatePage.tsx with route /{module}/create
  → EntityEditPage.tsx with route /{module}/:id/edit
  → Zero modals/popups/drawers/dialogs for forms
  → Back button with navigate(-1) on every form page
  → See smartstack-frontend.md section 3b for templates
- Tabs on detail pages: Tabs must switch content locally:
  → Tab click handler: setActiveTab(tabKey) only — do not navigate() to another page
  → Tab content: render inline with {activeTab === 'tabKey' && <TabContent />}
  → Sub-resource data: fetch via API filtered by parent ID, display in tab panel
  → Do not use navigate('../leaves?employee=${id}') in tab handler
  → See smartstack-frontend.md section 3 'Tab Behavior Rules'
- FK fields: Foreign key Guid fields (e.g., EmployeeId) must use EntityLookup:
  → Do not render FK fields as `<input>` — users cannot type GUIDs
  → Do not render FK fields as `<select>` — even with API-loaded options, `<select>` is not acceptable
  → Only use `<EntityLookup />` from @/components/ui/EntityLookup for searchable selection
  → Each FK field needs: apiEndpoint, mapOption (display name), search support on backend
  → Do not use `<select value={formData.departmentId}>`, `<option value={dept.id}>`, `e.target.value` for FK fields
  → See smartstack-frontend.md section 6 for the full EntityLookup pattern
- I18n: Generate 4 JSON files per module namespace (fr, en, it, de)
  → Follow JSON template from smartstack-frontend.md
  → Keys: actions, labels, errors, validation, columns, form, messages, empty
  → All t() calls must use namespace prefix + fallback: t('ns:key', 'Default text')
  → **Register namespace in i18n config** (critical — POST-CHECK C39 catches this, but fix it now):
    1. Find i18n config: `Glob("src/**/i18n/config.ts")` or `index.ts` or `i18n.ts`
    2. Read the config → find the resources/ns registration pattern
    3. Add the new namespace import + registration for all 4 languages
    4. If config uses dynamic imports: add namespace to the `ns` array
    5. Verify: `grep -q "{module_namespace}" src/**/i18n/config.ts` → must match
- Permissions: Call MCP generate_permissions for the module permission root (2 segments: {app}.{module}),
  then also call MCP generate_permissions for each section (3 segments: {app}.{module}.{section}).
- Section routes: Add section child routes to the module's children array.
  Wire PermissionGuard for section routes with section-level permissions.
- MUST use src/pages/{App}/{Module}/ hierarchy (NOT flat)

### Documentation (after frontend pages exist)

> **After frontend pages are created, generate module documentation via `/documentation` skill.**

```
Invoke /documentation {module_code} --type user

This generates:
- src/pages/docs/business/{app}/{module}/doc-data.ts (data file)
- src/pages/docs/business/{app}/{module}/index.tsx (page wrapper)
- src/i18n/locales/fr/docs-{app}-{module}.json (French doc translations)
- Updates App.tsx routing for doc page
- Updates docs-manifest.json
```

**Verify DocToggleButton presence in list/detail pages:**
- Every `*ListPage.tsx` and `*DetailPage.tsx` MUST import and render `DocToggleButton` in their header
- Import: `import { DocToggleButton } from '@/components/docs/DocToggleButton';`
- Placement: inside the header actions area (see `smartstack-frontend.md` section 7)

### If NOT economy_mode AND multiple entities: Parallel Agents (within layer)

> **Protocol:** See `references/parallel-execution.md`

```
IF NOT economy_mode AND entities.length > 1:
  For each entity, launch in parallel (single message):
    Agent(subagent_type='Snipper', model='opus',
      prompt='Execute Layer 3 frontend for {EntityName}:
        **MANDATORY: Read references/smartstack-frontend.md FIRST**
        - API client: MCP scaffold_api_client
        - Routes: MCP scaffold_routes (outputFormat: "applicationRoutes")
        - Wire Routes to App.tsx (BLOCKING): detect Pattern A/B, wire accordingly
          → See references/frontend-route-wiring-app-tsx.md for full patterns
          → Verify: mcp__smartstack__validate_frontend_routes (scope: "routes")
        - Pages: /ui-components skill (ALL 4 types: List, Detail, Create, Edit)
        - I18n: 4 JSON files (fr, en, it, de) + REGISTER namespace in i18n config
        - FORM PAGES: Full pages with own routes (no modals)
        - FK FIELDS: EntityLookup for all FK Guid fields
        - TABS: Local state only (NEVER navigate())
        - FORM TESTS: Co-located .test.tsx for Create and Edit pages
        - Your task ID is {task_id}. Call TaskUpdate(status: "in_progress") before starting.
        - Call TaskUpdate(status: "completed", metadata: { files_created: [...] }) when done.')
  # All agents launched in parallel

ELSE:
  # Agent principal handles all entities sequentially
```

### Parallel Agents + TaskCreate Integration

When launching agents for multi-entity layers:
- Each agent receives its task ID and metadata context in the prompt
- Agent must call TaskUpdate(status: "in_progress") before starting
- Agent must call TaskUpdate(status: "completed", metadata: { files_created: [...] }) when done
- Agent principal monitors via TaskList() after all agents complete
- Agent principal updates activeForm after each entity completion:
  `TaskUpdate(taskId: layer3_task_id, activeForm: "Building {EntityName} frontend (2/5 entities)")`

### Frontend Compliance Gate

> See `references/smartstack-frontend.md` section 9 "Compliance Gates" for all 5 required checks:
> 1. CSS Variables (theme system)
> 2. Forms as Pages (zero modals/drawers/slide-overs)
> 3. I18n File Structure (4 languages, separate JSON files)
> 4. Lazy Loading (React.lazy() — no static imports)
> 5. useTranslation in Pages (all text translated)

Do not commit frontend changes until all 5 gates pass.

When delegating to `/ui-components` skill, include explicit instructions:
- "CSS: Use CSS variables ONLY — `bg-[var(--bg-card)]`, `text-[var(--text-primary)]`."
- "Forms: Create/Edit forms are FULL PAGES with own routes (e.g., `/create`, `/:id/edit`)."
- "I18n: ALL text must use `t('namespace:key', 'Fallback')`. Generate JSON files in `src/i18n/locales/`."

### Frontend Tests Inline

> **Tests are scaffolded and run WITHIN Layer 3, not deferred to step-07.**

```
1. Scaffold frontend tests:
   → For each module with create/edit pages:
     → Generate EntityCreatePage.test.tsx and EntityEditPage.test.tsx
     → Co-located next to the page component (NOT in __tests__/ folder)
     → Cover: rendering, validation, submit, pre-fill, navigation, errors
     → Tools: Vitest + React Testing Library + @testing-library/user-event
     → Mock API: vi.mock() or MSW — NEVER real API calls

2. Run tests:
   WEB_DIR=$(find . -name "vitest.config.ts" -not -path "*/node_modules/*" -exec dirname {} \; 2>/dev/null | head -1)
   if [ -n "$WEB_DIR" ]; then
     (cd "$WEB_DIR" && npm run test)
   fi

3. Fix loop (max 3 iterations):
   IF tests fail:
     → Identify root cause (ALWAYS code bug, not test bug)
     → Fix production CODE
     → Re-run tests
     → Repeat until pass or max 3

4. If still failing after 3 iterations: note failures, continue.
   Step-07 "Final Test Sweep" will handle remaining failures.
```

### Typecheck Gate

```bash
npm run typecheck
```

Must pass before commit.

TaskUpdate(taskId: layer3_task_id, status: "completed",
  metadata: { build_gate: "pass" })

### Layer 3 Commits

```
feat({module}): [frontend] pages, routes, i18n, documentation
test({module}): frontend form tests
```

---

## Layer 4 — DevData (optional)

### Task Progress
IF layer4 task exists: TaskUpdate(taskId: layer4_task_id, status: "in_progress")
IF layer4 task exists: TaskUpdate(taskId: progress_tracker_id,
  description: "Module: {module_code}. Current: step-03 (Execute), Layer 4",
  activeForm: "Executing Layer 4")

> **Business test data for development/demo environments.**
> Skip this layer if no meaningful test data is needed.

```
1. Create DevDataSeeder:
   → Infrastructure/Persistence/Seeding/DevData/{Module}DevDataSeeder.cs
   → ALL entities MUST include TenantId
   → Realistic business data for demo purposes

2. Build gate:
   dotnet build → MUST PASS
```

IF layer4 task exists: TaskUpdate(taskId: layer4_task_id, status: "completed",
  metadata: { build_gate: "pass" })

### Layer 4 Commit (if applicable)

```
feat({module}): [devdata] test data for development
```

---

## PRD State Update (delegate mode only)

> **After completing EACH layer**, update the PRD file so `/ralph-loop` can track progress.

```
IF delegate_mode:
  After each layer completes successfully:
    Read {delegate_prd_path}
    For each task executed in this layer:
      task.status = 'completed'  (or 'failed' if errors)
      task.completed_at = new Date().toISOString()
      task.iteration = 1  (delegate mode = single iteration)
      task.commit_hash = $(git rev-parse --short HEAD)
      task.files_changed = { created: [...files created...], modified: [...files modified...] }
      task.validation = "APEX_PASS" (or error details)
    prd.updated_at = new Date().toISOString()
    Write back to {delegate_prd_path}
```

---

## Commits Per Layer

After each layer completes, gates pass, and builds pass:

```
Layer 0: feat({module}): [domain+infra] {short description}
Layer 1: feat({module}): [seed] navigation, permissions, roles
Layer 2: feat({module}): [app+api] {short description}
Layer 2: test({module}): backend unit and integration tests
Layer 3: feat({module}): [frontend] pages, routes, i18n, documentation
Layer 3: test({module}): frontend form tests
Layer 4: feat({module}): [devdata] test data for development  # if applicable
```

**Maximum 7 commits per module.**

---

## State Auto-Save (after each layer)

> **Automatic** — not dependent on `-s` flag. Enables reliable resume after context loss.

After each layer's build gate passes, write state to `.claude/output/apex/{task_id}/state.json`:

```json
{
  "step": 3,
  "layer": 2,
  "completed_layers": [0, 1, 2],
  "completed_entities": ["Employee", "Department"],
  "files_created": ["Employee.cs", "EmployeeConfiguration.cs", "..."],
  "build_gates": { "layer0": "pass", "layer1": "pass", "layer2": "pass" },
  "commits": ["abc1234", "def5678", "ghi9012"],
  "timestamp": "2026-03-06T14:30:00Z"
}
```

```
IF .claude/output/apex/{task_id}/ does not exist:
  Generate {task_id} from timestamp (e.g., "20260306-143000")
  Create directory

After EACH layer build gate passes:
  Read existing state.json (or create new)
  Update: layer, completed_layers, completed_entities, files_created, build_gates, commits
  Write back to state.json
```

This file is consumed by resume mode (`-r`) to skip completed layers instead of re-deriving from git.

---

## Save Output (if save_mode)

Write to `{output_dir}/03-execute.md` with execution log per layer.

---

## NEXT STEP

Load `steps/step-04-examine.md`
