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

**CRITICAL PRINCIPLE: /apex ORCHESTRATES. It NEVER writes SmartStack code directly.**
All code goes through skills (/controller, /application, /ui-components, /efcore) or MCP tools.

## LOAD CONDITIONALLY

- **ALWAYS** read `references/smartstack-api.md` — BaseEntity API, entity/config/controller patterns
- **ALWAYS** read `references/smartstack-frontend.md` — lazy loading, i18n, page structure, CSS variables
- Read `references/smartstack-layers.md` for execution rules per layer
- If NOT `{economy_mode}` AND Layer 1 has parallel work: read `references/agent-teams-protocol.md`
- If `{delegate_mode}` AND seedData tasks exist: read `references/core-seed-data.md` — comprehensive seed data templates
- If build failure during execution: read `references/error-classification.md` — error diagnosis categories A-F

---

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

### 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
```

### EF Core Configurations

```
For each entity:
  → MCP scaffold_extension (type: "configuration", target: entity_name)
  → Verify: table name, relationships, indexes
  → Register DbSet in ExtensionsDbContext if new entity
```

### Migration (BLOCKING)

```
1. MCP suggest_migration → get standardized name
2. dotnet ef migrations add {Name} --project src/{Infra}.csproj --startup-project src/{Api}.csproj -o Persistence/Migrations
3. Cleanup corrupted EF Core artifacts:
   for d in src/*/bin?Debug; do [ -d "$d" ] && rm -rf "$d"; done
4. dotnet ef database update (if local DB)
5. dotnet build → MUST PASS
```

**BLOCKING:** If build fails after migration, fix EF configs before proceeding.

### Post-Layer 0 Build Gate

```bash
dotnet build
```

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

---

## Layer 1 — Application + API + Seed Data

### If economy_mode: Sequential execution

Execute each item from the plan sequentially using skills and MCP.

### Code Generation (if entities have codePattern != "manual")

For each entity with auto-generated code pattern (from feature.json or step-02 decisions):

```
1. Create CodePatternConfig for the entity (strategy, prefix, digits from codePattern)
2. Register ICodeGenerator<TEntity> in DependencyInjection.cs (Infrastructure layer)
   → See references/code-generation.md for DI registration pattern
3. Update CreateDto: REMOVE Code property (auto-generated, not user-provided)
4. Update CreateDtoValidator: REMOVE Code regex rule (not in DTO anymore)
5. Update service CreateAsync: inject ICodeGenerator<TEntity>, call NextCodeAsync()
   → Code is auto-generated BEFORE entity creation
   → See references/code-generation.md for service integration pattern
6. Keep Code in ResponseDto (returned to frontend after creation)
7. Keep Code in UpdateDto ONLY if code is mutable (rare — discuss with user)
```

**CRITICAL:** If `codePattern.strategy == "manual"` (or no codePattern), keep the current behavior:
Code stays in CreateDto, user provides it, validator has regex rule.

**For frontend tasks (economy_mode):** Follow the same rules as exec-frontend above:
- `MCP scaffold_api_client` → API client + types + React Query hook
- `MCP scaffold_routes` with `outputFormat: 'clientRoutes'` for lazy imports
- **INVOKE `/ui-components` skill** (read SKILL.md + ALL patterns) — MANDATORY for ALL page types
- **Create ALL 4 page types per module:**
  - `ListPage.tsx` — entity list with SmartTable/EntityCard
  - `DetailPage.tsx` — entity detail view
  - `EntityCreatePage.tsx` (route: `/create`) — FULL PAGE form, NEVER modal
  - `EntityEditPage.tsx` (route: `/:id/edit`) — FULL PAGE form, NEVER modal
- **Wire ALL routes in App.tsx:** `index` (ListPage), `:id` (DetailPage), `create` (CreatePage), `:id/edit` (EditPage)
- **FK FIELDS (CRITICAL):** Any Guid FK property (e.g., EmployeeId, DepartmentId) MUST use `EntityLookup` component — NEVER a `<select>` dropdown, NEVER a `<input type="text">`. A `<select>` loaded from API state is NOT a substitute for EntityLookup. See `smartstack-frontend.md` section 6.
- **ZERO modals/popups/drawers for forms — ALL forms are full pages with their own URL**
- **TABS ON DETAIL PAGES (CRITICAL):** Tabs MUST switch content LOCALLY via `setActiveTab()` — NEVER `navigate()` to another page. Sub-resource data (e.g., employee's leaves) loads inline via API call filtered by parent entity ID. See `smartstack-frontend.md` section 3 "Tab Behavior Rules".
- **Form tests: Generate `EntityCreatePage.test.tsx` and `EntityEditPage.test.tsx` (co-located)**
- **SECTION PERMISSIONS:** After calling `MCP generate_permissions` for the module navRoute (3 segments: `{context}.{app}.{module}`), also call it for EACH section navRoute (4 segments: `{context}.{app}.{module}.{section}`)
- **SECTION ROUTES:** After generating module routes, add section child routes to the module's `children` array. Wire `PermissionGuard` for section routes with section-level permissions.
- **SUB-RESOURCE COMPLETENESS:** If a section controller has sub-resource endpoints (e.g., `[HttpGet("types")]` for LeaveTypes inside LeavesController), you MUST EITHER:
  - Create dedicated frontend pages for the sub-resource (ListPage, CreatePage, EditPage) with routes wired in App.tsx, OR
  - NOT include any `navigate()` button that links to those sub-resource pages
  - **Prefer separate controllers** with `[NavRoute(..., Suffix = "types")]` — see `smartstack-api.md` Sub-Resource Pattern
  - A dead link (navigate to a route with no page) is a BLOCKING issue (POST-CHECK 42)
- Read `references/smartstack-frontend.md` for mandatory patterns (sections 3b + 8)
- Generate i18n JSON files for all 4 languages (fr, en, it, de) — `src/i18n/locales/{lang}/{module}.json`
- All pages must follow loading → error → content pattern with CSS variables

### If NOT economy_mode: Agent Teams (parallel)

> **Protocol:** See `references/agent-teams-protocol.md`

```
TeamCreate("apex-exec", description: "Execute Layer 1 in parallel")

Spawn 2 teammates (Opus, full tools):

1. exec-backend (team_name: "apex-exec", name: "exec-backend"):
   "Execute these Layer 1 backend tasks for module {module_code}:
    {list of application + api + seed data tasks from plan}

    For each task:
    - Application 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)
    - Seed data: MCP generate_permissions, then follow smartstack-layers.md templates

    After ALL tasks done:
    SendMessage(type:'message', recipient:'team-lead', content:'BACKEND_COMPLETE', summary:'Backend layer done')"

2. exec-frontend (team_name: "apex-exec", name: "exec-frontend"):
   "Execute these Layer 1 frontend tasks for module {module_code}:
    {list of frontend + i18n tasks from plan}

    **MANDATORY: Read references/smartstack-frontend.md FIRST** — contains all required patterns.

    For each task:
    - API client: MCP scaffold_api_client
    - Routes: MCP scaffold_routes (outputFormat: 'clientRoutes') → generates lazy imports + Suspense
    - Pages: use /ui-components skill — MUST 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)]
      → SmartTable/SmartFilter/EntityCard (NEVER raw HTML)
    - **FORM PAGES (CRITICAL):** 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 (CRITICAL):** Tabs MUST switch content LOCALLY:
      → Tab click handler: setActiveTab(tabKey) ONLY — NEVER 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
      → FORBIDDEN: navigate('../leaves?employee=${id}') in tab handler
      → See smartstack-frontend.md section 3 'Tab Behavior Rules'
    - **FK FIELDS (CRITICAL):** Foreign key Guid fields (e.g., EmployeeId) MUST use EntityLookup:
      → NEVER render FK fields as `<input>` — users cannot type GUIDs
      → NEVER 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
      → FORBIDDEN: `<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
    - **FORM TESTS (MANDATORY):**
      → EntityCreatePage.test.tsx (co-located next to page)
      → EntityEditPage.test.tsx (co-located next to page)
      → Cover: rendering, validation, submit, pre-fill, navigation, errors
      → See smartstack-frontend.md section 8 for test templates
    - **SECTION PERMISSIONS:** After MCP generate_permissions for the module navRoute (3 segments),
      also call MCP generate_permissions for EACH section navRoute (4 segments: {context}.{app}.{module}.{section})
    - **SECTION ROUTES:** Add section child routes to the module's children array.
      Wire PermissionGuard for section routes with section-level permissions.
    - 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')
    - MUST use src/pages/{Context}/{App}/{Module}/ hierarchy (NOT flat)

    After ALL tasks done:
    SendMessage(type:'message', recipient:'team-lead', content:'FRONTEND_COMPLETE', summary:'Frontend layer done')"

Wait for both teammates to report completion.

Build verification:
  dotnet build  (backend)
  npm run typecheck           (frontend, if applicable)

shutdown_request → shutdown_response → TeamDelete("apex-exec")
```

---

## Layer 1 — Skill Delegation

| Skill | When | Context to provide |
|-------|------|--------------------|
| /controller | Custom routes, complex auth, file upload | entity, CRUD actions, permissions, routes |
| /application | Full context/app/module from scratch | context, 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 |

---

## Layer 2b — 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)

---

## Layer 2 — Final Seed Data + Validation (sequential)

After Layer 1 completes:

```
1. Verify seed data completeness:
   - NavigationModuleSeedData.cs with 4 languages
   - PermissionsSeedData.cs with deterministic GUIDs
   - RolesSeedData.cs with context-based role mapping
   - IClientSeedDataProvider updated
   - DI registration added

2. Build gate:
   dotnet build → MUST PASS
   npm run typecheck → MUST PASS (if frontend)
```

---

## FRONTEND COMPLIANCE GATE (MANDATORY before commit)

> **BLOCKING:** Do NOT commit frontend changes until ALL checks pass.
> These issues are the most common failures in generated code — verify EVERY item.

**Run these checks on ALL generated/modified `.tsx` files BEFORE creating the frontend commit:**

### Gate 1: CSS Variables (Theme System)

```bash
# Check for hardcoded Tailwind colors — MUST use CSS variables
ALL_PAGES=$(find src/pages/ src/components/ -name "*.tsx" 2>/dev/null | grep -v node_modules | grep -v "\.test\.")
if [ -n "$ALL_PAGES" ]; then
  HARDCODED=$(grep -Pn '(bg|text|border)-(?!\[)(red|blue|green|gray|white|black|slate|zinc|neutral|stone)-' $ALL_PAGES 2>/dev/null)
  if [ -n "$HARDCODED" ]; then
    echo "FAIL: Hardcoded Tailwind colors found — must use CSS variables"
    echo "$HARDCODED"
  else
    echo "PASS: CSS variables"
  fi
fi
```

**If hardcoded colors found, replace BEFORE committing:**
- `bg-white` → `bg-[var(--bg-card)]`
- `bg-gray-50` → `bg-[var(--bg-primary)]`
- `text-gray-900` → `text-[var(--text-primary)]`
- `text-gray-500/600` → `text-[var(--text-secondary)]`
- `border-gray-200` → `border-[var(--border-color)]`
- `bg-blue-600` / `text-blue-600` → `bg-[var(--color-accent-500)]` / `text-[var(--color-accent-500)]`
- `hover:bg-blue-700` → `hover:bg-[var(--color-accent-600)]`
- `text-red-500` → `text-[var(--error-text)]`
- `bg-green-500` → `bg-[var(--success-bg)]`

### Gate 2: Forms as Pages (ZERO Modals/Drawers/Slide-overs)

```bash
# Check for modal/dialog/drawer/slide-over imports and inline form patterns — FORBIDDEN
PAGE_FILES=$(find src/pages/ -name "*.tsx" 2>/dev/null)
if [ -n "$PAGE_FILES" ]; then
  FAIL=false

  # 2a. Component imports
  MODAL_IMPORTS=$(grep -Pn "import.*(?:Modal|Dialog|Drawer|Popup|Sheet|SlideOver|Overlay)" $PAGE_FILES 2>/dev/null)
  if [ -n "$MODAL_IMPORTS" ]; then
    echo "FAIL: Modal/Dialog/Drawer component imports — forms MUST be full pages"
    echo "$MODAL_IMPORTS"
    FAIL=true
  fi

  # 2b. State variables for inline forms (catches drawers/slide-overs without imports)
  MODAL_STATE=$(grep -Pn "useState.*(?:isOpen|showModal|showDialog|showCreate|showEdit|showForm|isCreating|isEditing|showDrawer|showPanel|showSlideOver|selectedEntity|editingEntity)" $PAGE_FILES 2>/dev/null)
  if [ -n "$MODAL_STATE" ]; then
    echo "FAIL: Inline form state detected — forms MUST be separate page components with own routes"
    echo "$MODAL_STATE"
    FAIL=true
  fi

  if [ "$FAIL" = true ]; then
    echo ""
    echo "Fix: Create EntityCreatePage.tsx (route: /create) and EntityEditPage.tsx (route: /:id/edit)"
    echo "See smartstack-frontend.md section 3b"
  else
    echo "PASS: No modals/drawers"
  fi
fi
```

**If modals/drawers found:** Replace with separate `EntityCreatePage.tsx` (route: `/{module}/create`) and `EntityEditPage.tsx` (route: `/{module}/:id/edit`). See `smartstack-frontend.md` section 3b.

### Gate 3: I18n File Structure

```bash
# Verify translation files exist as separate JSON per language
if [ ! -d "src/i18n/locales" ]; then
  echo "FAIL: Missing src/i18n/locales/ directory — create it with 4 languages"
else
  for LANG in fr en it de; do
    JSON_FILES=$(find "src/i18n/locales/$LANG" -name "*.json" 2>/dev/null | wc -l)
    if [ "$JSON_FILES" -eq 0 ]; then
      echo "FAIL: No JSON files in src/i18n/locales/$LANG/"
    else
      echo "PASS: $LANG ($JSON_FILES files)"
    fi
  done
fi
```

**If i18n structure wrong:** Create `src/i18n/locales/{fr,en,it,de}/{module}.json` following the template in `smartstack-frontend.md` section 2. NEVER embed translations in a single `.ts` file.

### Gate 4: Lazy Loading

```bash
# Check for static page imports in route/App files
APP_TSX=$(find src/ -name "App.tsx" -not -path "*/node_modules/*" 2>/dev/null | head -1)
ROUTE_FILES=$(find src/routes/ -name "*.tsx" -o -name "*.ts" 2>/dev/null)
if [ -n "$APP_TSX" ]; then
  STATIC_IMPORTS=$(grep -Pn "^import .+ from '@/pages/" "$APP_TSX" $ROUTE_FILES 2>/dev/null)
  if [ -n "$STATIC_IMPORTS" ]; then
    echo "FAIL: Static page imports in App.tsx/routes — MUST use React.lazy()"
    echo "$STATIC_IMPORTS"
  else
    echo "PASS: Lazy loading"
  fi
fi
```

### Gate 5: useTranslation in Pages

```bash
# Verify pages use i18n
PAGE_FILES=$(find src/pages/ -name "*.tsx" 2>/dev/null | grep -v "\.test\." | grep -v node_modules)
if [ -n "$PAGE_FILES" ]; then
  TOTAL=$(echo "$PAGE_FILES" | wc -l)
  WITH_I18N=$(grep -l "useTranslation" $PAGE_FILES 2>/dev/null | wc -l)
  if [ "$WITH_I18N" -eq 0 ]; then
    echo "FAIL: No pages use useTranslation — all text must be translated"
  else
    echo "PASS: $WITH_I18N/$TOTAL pages use useTranslation"
  fi
fi
```

> **ALL 5 gates MUST pass before creating the frontend commit.** If ANY gate fails, fix the issues first.

### Explicit I18n File Creation

When creating i18n files, generate EXACTLY this structure:

```
src/i18n/locales/
├── fr/{module}.json    ← French (primary)
├── en/{module}.json    ← English
├── it/{module}.json    ← Italian
└── de/{module}.json    ← German
```

Each file MUST contain these keys: `title`, `description`, `actions`, `labels`, `columns`, `form`, `errors`, `validation`, `messages`, `empty`. See `smartstack-frontend.md` section 2 for the complete JSON template.

**When delegating to `/ui-components`:** ALWAYS include these explicit instructions in the delegation context:
- "CSS: Use CSS variables ONLY — `bg-[var(--bg-card)]`, `text-[var(--text-primary)]`. NEVER use hardcoded Tailwind colors like `bg-white` or `text-gray-900`."
- "Forms: Create/Edit forms are FULL PAGES with own routes (e.g., `/create`, `/:id/edit`). NEVER use modals/dialogs."
- "I18n: ALL text must use `t('namespace:key', 'Fallback')`. Generate JSON files in `src/i18n/locales/`."

---

## 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}): [app+api] {short description}
Layer 1: feat({module}): [frontend] {short description}  # if applicable — AFTER all 5 gates pass
Layer 2: feat({module}): [seed] {short description}       # if applicable
```

---

## 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`
