---
name: step-04-examine
description: MCP validation, build verification, seed data check, acceptance criteria
model: opus
prev_step: steps/step-03-execute.md
next_step: steps/step-05-deep-review.md
---

# Step 4: eXamine

**ULTRA THINK about completeness. Did ALL new sections/entities get their navigation entries, permissions, and roles? Cross-check the scope from step-00 against what was actually generated in Layer 1.**

> **This is the X in APEX.** Mandatory automated examination of all generated code.

**Goal:** Verify everything works. MCP conventions, build, seed data, acceptance criteria.

---

## 0. Task Progress

```
TaskUpdate(taskId: progress_tracker_id, description: "Module: {module_code}. Current: step-04 (eXamine)", activeForm: "Validating conventions")
TaskUpdate(taskId: examine_task_id, status: "in_progress")
```

---

## 1. MCP Convention Validation

```
Call: mcp__smartstack__validate_conventions
Expected: 0 errors. Fix and re-validate if any found.
```

## 2. EF Core Migration Check (if needs_migration)

```
Call: mcp__smartstack__check_migrations
Verify: Migration exists, no pending changes, ModelSnapshot matches.
```

## 3. Frontend Route Validation (if frontend modified)

```
Call: mcp__smartstack__validate_frontend_routes

Verify:
- Routes nested in correct Layout wrapper
- Route paths match controller patterns, include module segment
- No orphan routes
- Compare frontend routes (componentRegistry.generated.ts or App.tsx) against backend NavigationSeedData routes
```

> **Page completeness:** POST-CHECKs C4, C5, C6 (dead links, missing Create/Edit, missing tests) executed in section 6b.
> **If ANY check fails → return to step-03.**

---

## 4. Build Verification

```bash
dotnet clean && dotnet restore && dotnet build
npm run typecheck  # if applicable
```

Both must pass. Classify errors per `references/error-classification.md` (A-F).

---

## 5. Migration Validation (if needs_migration)

```bash
INFRA_PROJECT=$(ls src/*Infrastructure*/*.csproj 2>/dev/null | head -1)
API_PROJECT=$(ls src/*Api*/*.csproj 2>/dev/null | head -1)

# Check pending model changes
dotnet ef migrations has-pending-model-changes --project "$INFRA_PROJECT" --startup-project "$API_PROJECT"

# Apply to SQL Server LocalDB
DB_NAME="SmartStack_Apex_Examine_$(date +%s)"
dotnet ef database update --connection "Server=(localdb)\\MSSQLLocalDB;Database=$DB_NAME;Integrated Security=true;TrustServerCertificate=true;Connect Timeout=120;" --project "$INFRA_PROJECT" --startup-project "$API_PROJECT"

# Run integration tests if present
INT_TEST_PROJECT=$(ls tests/*Tests.Integration*/*.csproj 2>/dev/null | head -1)
[ -n "$INT_TEST_PROJECT" ] && dotnet test "$INT_TEST_PROJECT" --no-build --verbosity normal

# Cleanup
sqlcmd -S "(localdb)\MSSQLLocalDB" -Q "IF DB_ID('$DB_NAME') IS NOT NULL BEGIN ALTER DATABASE [$DB_NAME] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE [$DB_NAME]; END" 2>/dev/null
```

If migration fails (SQLite-only syntax, type mismatches, missing FK targets), fix the DbContext or migration first.

---

## 6. Seed Data Completeness Check (if needs_seed_data)

> **CRITICAL — Cross-check against {sections} from step-00.** Every section in scope MUST have navigation, permissions, and roles. This is the most common miss when UPDATING an existing module.

| File | Checks |
|------|--------|
| NavigationApplicationSeedData | 4 lang translations |
| ApplicationRolesSeedData | 4 roles, references NavigationApplicationSeedData.ApplicationId |
| NavigationModuleSeedData | 4 languages, GetModuleEntry + translations |
| ↳ Section methods | GetSectionEntries + translations, 4 languages, random GUIDs |
| ↳ Resource methods | GetResourceEntries + translations, 4 languages, random GUIDs |
| Permissions.cs | Static class with Read/Create/Update/Delete constants, match PermissionsSeedData |
| PermissionsSeedData | MCP generate_permissions, paths match Permissions.cs |
| RolesSeedData | Admin=wildcard(*), Manager=CRU, Contributor=CR, Viewer=R |
| IClientSeedDataProvider | 4 Seed methods, idempotent, SaveChanges per group |
| DI Registration | `services.AddScoped<IClientSeedDataProvider, {App}SeedDataProvider>()` |

### 6a. Section Coverage Matrix (MANDATORY for updates)

```
For EACH section in {sections} from step-00:
  Read NavigationModuleSeedData.cs → has section entry?       YES / NO
  Read NavigationModuleSeedData.cs → has translations (4)?    YES / NO
  Read NavigationModuleSeedData.cs → has route?               YES / NO
  Read Permissions.cs → has section permissions?              YES / NO
  Read PermissionsSeedData.cs → has HasData() entries?        YES / NO
  Read RolesSeedData.cs → has role-permission mappings?       YES / NO

IF ANY cell = NO → BLOCKING — return to step-03b to fix
```

---

## 6b. POST-CHECKs

**MCP FIRST:** Run `validate_conventions`, `validate_security`, `validate_frontend_routes` (covers ~10 checks).

Execute all checks from `references/post-checks.md`. If any fails, fix in step-03 and re-validate.

### 6c. SeedDataProvider Content Verification (BLOCKING)

> **LESSON LEARNED (audit ba-003):** SeedDataProvider existed and had 4 methods (passed C23),
> but all methods were `return Task.CompletedTask` — stubs generated when `core-seed-data.md`
> was evicted from context by compression. Result: empty menu, 0 applications visible.

```javascript
// Find the SeedDataProvider file
const providerFiles = Glob("**/Seeding/*SeedDataProvider.cs");
for (const providerFile of providerFiles) {
  const content = readFile(providerFile);

  // Check 1: No stub methods — each Seed*Async must have real implementation
  const methods = ['SeedNavigationAsync', 'SeedRolesAsync', 'SeedPermissionsAsync', 'SeedRolePermissionsAsync'];
  for (const method of methods) {
    const methodRegex = new RegExp(`${method}[^{]*\\{([^}]*)\\}`, 's');
    const match = content.match(methodRegex);
    if (match) {
      const body = match[1].trim();
      if (body === 'return Task.CompletedTask;' || body === '' || body === 'return;') {
        BLOCKING_ERROR(`${providerFile}: ${method} is a STUB (${body || 'empty body'}). ` +
          `Re-read references/core-seed-data.md Section 6 and generate real implementation. ` +
          `Stub seed methods cause EMPTY MENU — 0 applications visible in UI.`);
      }
    }
  }

  // Check 2: Must contain actual entity creation calls
  if (!content.includes('.Add(') && !content.includes('.AddRange(')) {
    BLOCKING_ERROR(`${providerFile}: No .Add() or .AddRange() calls found. ` +
      `SeedDataProvider must actually insert seed data, not just return.`);
  }
}
```

---

## 7. Acceptance Criteria POST-CHECK

For each AC from step-01:
```
AC1: {criterion} → PASS / FAIL (evidence)
...
```
**All ACs must PASS. If any FAIL, return to step-03.**

---

## 8. eXamine Summary

**APEX SmartStack - eXamine Complete**

| Layer | Status |
|-------|--------|
| Layer 0 — Domain + Infrastructure | PASS / N/A |
| Layer 1 — Seed Data | PASS / N/A |
| Layer 2 — Backend | PASS / N/A |
| Layer 3 — Frontend | PASS / N/A |
| Layer 4 — DevData | PASS / N/A / SKIPPED |

| Category | Status |
|----------|--------|
| MCP conventions | PASS (0 errors) |
| Build (dotnet + npm) | PASS |
| EF Core (migrations, SQL Server) | PASS / N/A |
| Integration tests | PASS / N/A |
| Frontend routes + i18n | PASS / N/A |
| Security checks | PASS / N/A |
| Seed data completeness | PASS / N/A |
| Code quality | PASS / N/A |
| NavRoute kebab-case | PASS / N/A |
| DateOnly DTOs | PASS / N/A |
| I18n registration | PASS / N/A |
| FluentValidation DI | PASS / N/A |
| Route/NavRoute conflict | PASS / N/A |
| Role-permission matrix | PASS / N/A |
| Navigation translations (4 langs) | PASS / N/A |
| Inline tests | PASS / N/A |
| POST-CHECKs | PASS / N/A |
| Acceptance criteria | {X}/{Y} PASS |

---

## 8b. Runtime Validation

For runtime validation (if NOT foundation/economy mode): Run `/validate-feature {EntityName}` (OPTIONAL but RECOMMENDED).

---

## 9. Route to Next Step

```
IF examine_mode: Load step-05 → step-06 → step-07
ELSE: Load step-07-tests.md

TaskUpdate(taskId: examine_task_id, status: "completed")
```

**RULE:** APEX requires steps 07-08 (100% tests, >= 80% coverage).

## 10. PRD Reconciliation (delegate mode)

```
For all tasks in module:
  IF executed AND checks passed: Set task.status = 'completed'
Set prd.updated_at = now
If all done: Set prd.status = 'completed' (single) or 'in_progress' (multi)
```
