---
name: step-03b-layer1-seed
description: "Layer 1: Seed data (navigation, permissions, roles)"
model: opus
parent_step: steps/step-03-execute.md
---

**ULTRA THINK about seed data completeness: is this a NEW module (create all seed files from scratch) or an UPDATE to an existing module (modify existing seed files to add new sections/resources/permissions)? Read existing files BEFORE deciding what to generate.**

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

### Companion Specs Loading (delegate mode)

```javascript
if (delegate_mode && specification_loading_plan) {
  const specsDir = path.dirname(delegate_prd_path);
  // Layer 1 loads: entities + permissions companions
  for (const file of specification_loading_plan.layer1_seed) {
    const specPath = path.join(specsDir, file);
    if (fileExists(specPath)) {
      const specData = readJSON(specPath);
      if (specData.entities) {
        // Use entities[].seedValues[] for business seed data generation
        console.log(`Loaded entities: ${specData.entities.length} entities`);
      }
      if (specData.permissionPaths || specData.roles) {
        // Use full permission structure for PermissionsSeedData and RolesSeedData
        console.log(`Loaded permissions: ${(specData.permissionPaths || []).length} paths, ${(specData.roles || []).length} roles`);
      }
    }
  }
}
```

> When companion specs are loaded, use `specData.permissionPaths[]` for exact permission paths and `specData.roles[]` for role definitions instead of generating from conventions. This ensures permission seed data matches BA specifications exactly.

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

> **CONTEXT COMPRESSION SAFEGUARD (delegate mode):**
> In delegate mode (`-d`), `references/core-seed-data.md` may have been evicted from context by compression
> before Layer 1 begins (it was loaded at step-03 entry but is ~1464 lines).
>
> **CHECK:** Can you see Section 6 "IClientSeedDataProvider Implementation" from `core-seed-data.md` in your context?
> - **YES** → Proceed normally using the templates from that section.
> - **NO** → **Re-read** `references/core-seed-data.md` lines 1004-1296 NOW (the IClientSeedDataProvider template).
>   This section contains the complete C# template for the provider class with all 4 Seed methods.
>
> **WARNING — ANTI-STUB RULE:**
> The SeedDataProvider MUST contain real implementation code. Each of the 4 methods
> (`SeedNavigationAsync`, `SeedRolesAsync`, `SeedPermissionsAsync`, `SeedRolePermissionsAsync`)
> MUST have actual entity creation logic with `context.{DbSet}.Add()` calls.
>
> **BLOCKING:** If ANY Seed method body is just `return Task.CompletedTask;` or `{ }` (empty),
> the seed data layer is INVALID. The module will be invisible in the UI (menu vide / empty menu).
> Re-read the template from `references/core-seed-data.md` Section 6 and generate proper implementation.

---

### Mode Detection: CREATE vs UPDATE

> **CRITICAL — Run this BEFORE generating any seed data.**

```
Glob("**/Seeding/Data/{module_code}/*SeedData.cs") → existing_seed_files
Glob("**/Seeding/Data/NavigationApplicationSeedData.cs") → app_seed_exists
Glob("**/Seeding/*SeedDataProvider.cs") → provider_exists

IF existing_seed_files is EMPTY:
  SEED_MODE = "CREATE"
  → Generate ALL seed data from scratch (full templates below)

IF existing_seed_files is NOT EMPTY:
  SEED_MODE = "UPDATE"
  → Read EACH existing file
  → Compare content against {sections} and {entities} from step-00
  → ADD only what's MISSING — do NOT recreate or duplicate existing entries
```

---

### CREATE Mode — Application-Level Seed Data (ONCE per application)

> Skip if `app_seed_exists` (files already present from previous APEX run).

```
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 (CREATE or UPDATE)

#### If SEED_MODE = "CREATE"

Generate all files from scratch using `references/core-seed-data.md` templates:

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

#### If SEED_MODE = "UPDATE"

> **Read each existing file FIRST, then surgically add only what's missing.**

```
3. NavigationModuleSeedData.cs — READ EXISTING FILE
   → Extract existing section codes (look for GetSectionEntries or section GUID definitions)
   → Extract existing resource codes
   → For each section in {sections} NOT already present:
     - Add new section entry in GetSectionEntries() with new Guid.NewGuid()
     - Add section translations (4 languages) in GetSectionTranslationEntries()
     - Add route entry: /{app-kebab}/{module-kebab}/{new-section-kebab}
   → For each resource in {resources} NOT already present:
     - Add resource entry in GetResourceEntries()
     - Add resource translations
   → PRESERVE all existing entries unchanged

4. Permissions.cs — READ EXISTING FILE
   → Extract existing permission constants
   → For each NEW section:
     - Add section-level constants: Wildcard, Read, Create, Update, Delete
     - Path format: {app}.{module}.{new-section}.{action}
   → MCP generate_permissions for validation (compare output vs file)
   → PRESERVE all existing constants unchanged

5. PermissionsSeedData.cs — READ EXISTING FILE
   → Extract existing HasData() permission entries
   → Add HasData() entries for each NEW permission from step 4
   → PRESERVE all existing entries unchanged

6. RolesSeedData.cs — READ EXISTING FILE
   → Extract existing role-permission mappings
   → For each NEW permission:
     - Admin → wildcard or all new permissions
     - Manager → Read + Create + Update for new section
     - Contributor → Read + Create for new section
     - Viewer → Read for new section
   → PRESERVE all existing mappings unchanged
```

---

### Infrastructure Provider (CREATE or UPDATE)

#### If SEED_MODE = "CREATE"

```
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>()
```

#### If SEED_MODE = "UPDATE"

```
7. {App}SeedDataProvider.cs — READ EXISTING FILE
   → Verify SeedNavigationAsync() calls the module's NavigationModuleSeedData
   → If new sections were added: verify they are seeded (new section entries are part of existing methods)
   → Verify SeedPermissionsAsync() covers new PermissionsSeedData entries
   → Verify SeedRolePermissionsAsync() covers new RolesSeedData entries
   → Add any missing calls or registrations
   → PRESERVE all existing logic unchanged
```

<!-- TODO A4: Replace direct seed generation with MCP scaffold_seed_data when B1 is ready. This will eliminate the need for references/core-seed-data.md (1464 lines). -->

---

### Startup Pipeline Wiring (ONCE per project)

> **CRITICAL:** Seed data files are useless without startup execution.
> Reference: SmartStack.app `SmartStackExtensions.cs` → `InitializeSmartStackAsync()` → `RunClientSeedDataProvidersAsync()`

```
Glob("**/Program.cs") → Read Program.cs

IF Program.cs calls `InitializeSmartStackAsync()`:
  → Seed data pipeline is already wired ✅

ELSE IF Program.cs ONLY calls `MigrateAsync()` without seed data execution:
  → Add seed data provider execution AFTER MigrateAsync():

  After `await extDb.Database.MigrateAsync();`:
    var seedProviders = scope.ServiceProvider
        .GetServices<IClientSeedDataProvider>()
        .OrderBy(s => s.Order).ToList();
    foreach (var provider in seedProviders)
    {
        var coreDb = scope.ServiceProvider.GetRequiredService<ICoreDbContext>();
        await provider.SeedNavigationAsync(coreDb, CancellationToken.None);
        await provider.SeedRolesAsync(coreDb, CancellationToken.None);
        await provider.SeedPermissionsAsync(coreDb, CancellationToken.None);
        await provider.SeedRolePermissionsAsync(coreDb, CancellationToken.None);
    }

  Add using: SmartStack.Application.Common.Interfaces.Seeding
  → ONE-TIME wiring. Future modules just add DI registrations.
```

---

### Post-Layer 1 Verification Checklist

> **Before the build gate, verify ALL new scope elements are covered:**

```
For each section in {sections} from step-00:
  ✓ NavigationModuleSeedData has section entry + translations (4 langs)
  ✓ NavigationModuleSeedData has route: /{app}/{module}/{section}
  ✓ Permissions.cs has constants for this section (Read/Create/Update/Delete/Wildcard)
  ✓ PermissionsSeedData has HasData() for each permission
  ✓ RolesSeedData maps all 4 roles to section permissions
  ✓ {App}SeedDataProvider references all seed data classes
  ✓ Program.cs executes seed data providers after MigrateAsync()
  ✓ DI registers: services.AddScoped<IClientSeedDataProvider, {App}SeedDataProvider>()

IF any check fails → fix BEFORE build gate
```

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

---

## 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-03c-layer2-backend.md`
