---
name: step-03a-layer0-domain
description: "Layer 0: Domain entities, EF configs, migration"
model: opus
parent_step: steps/step-03-execute.md
---

**ULTRA THINK about domain entities: which are NEW (create) vs which EXIST (modify/extend)?**

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

### Companion Specs Loading (delegate mode)

```javascript
if (delegate_mode && specification_loading_plan) {
  const specsDir = path.dirname(delegate_prd_path);
  // Layer 0 loads: entities companion
  for (const file of specification_loading_plan.layer0_domain) {
    const specPath = path.join(specsDir, file);
    if (fileExists(specPath)) {
      const specData = readJSON(specPath);
      // specData.entities[] contains full attribute definitions:
      //   { name, type, required, validation, defaultValue, relationships[] }
      // Use these for entity scaffolding instead of just entity names
      console.log(`Loaded ${specPath}: ${(specData.entities || []).length} entities with full attributes`);
    }
  }
}
```

> When companion specs are loaded, use `specData.entities[].attributes[]` for entity property definitions (type, required, validation, defaultValue) instead of inferring from task descriptions. This ensures oneshot generation with correct types and constraints.

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

### Code Generation Integration

```
For each entity with auto-generated code ({code_patterns} from step-00):
  IF {code_patterns} has entry for this entity AND strategy != "manual":
    → Pass codePattern in scaffold_extension options:
      MCP scaffold_extension (type: "entity", target: entity_name, options: {
        codePattern: {
          strategy: {code_patterns[entity].strategy},
          prefix: {code_patterns[entity].prefix},
          digits: {code_patterns[entity].digits},
          includeTenantSlug: {code_patterns[entity].includeTenantSlug},
          separator: {code_patterns[entity].separator}
        }
      })
    → Verify: Code property exists on entity but is NOT in CreateDto
    → Verify: ICodeGenerator<{Entity}> is ready for DI registration (Layer 2)
  ELSE:
    → Default behavior (strategy: "manual", Code in CreateDto)
```

### Enum Serialization Check

```
For each enum type created in Domain/Enums/:
  1. Grep("JsonStringEnumConverter", "src/**/Program.cs")
  2. IF global config exists → no action
  3. IF no global config → add [JsonConverter(typeof(JsonStringEnumConverter))] on each enum
```

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

<!-- TODO A6: Replace with MCP scaffold_extension(type: "ef-config") when B2 is ready -->

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

---

## NEXT SUB-STEP (MANDATORY — IMMEDIATE)

> **DO NOT STOP.** DO NOT ask the user. DO NOT present options. IMMEDIATELY load the next sub-step.
> Stopping here is a VIOLATION of the execution guarantee.

Load `steps/step-03b-layer1-seed.md`
