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

---

### Reserved Section Code Guard (MANDATORY — runs before ANY section seeding)

> **CRITICAL RULE:** Reserved section codes must NOT be seeded as menu-visible sections.

```
RESERVED_SECTION_CODES = ["detail", "create", "edit"]

For each section in {sections} (from PRD or entity_section_map):
  IF section.code IN RESERVED_SECTION_CODES OR section.code contains "-detail":
    → Do NOT seed this section in NavigationSeedData at all
    → "detail" is the /:id route of its parent list section (DynamicRouter convention)
    → "create" is the /create route (DynamicRouter convention)
    → "edit" is the /:id/edit route (DynamicRouter convention)
    → "*-detail" (e.g., "department-detail") is the /:id route of its parent section
    → Set permissionMode = "inherit" (no dedicated permissions)
    → LOG: "Skipped reserved section '{section.code}' — resolved by DynamicRouter convention"

  ELSE:
    → Seed normally with IsActive = true

In delegate mode (-d): PRD data may contain reserved codes that were not challenged.
This guard catches them regardless of mode.
```

---

### 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 — MODULE-LEVEL
   → MCP generate_permissions (PRIMARY tool)
   → Static constants: public static class {Module} { public const string Read = "..."; }
   → Permission paths MUST use kebab-case matching NavRoute codes

4b. Permissions.cs — SECTION-LEVEL (for each entity in {entity_section_map} where level == "section")
   → Add nested class per section inside the module class:
     public static class {SectionPascal} {
       public const string View = "{navRoute}.{section_code}.read";
       public const string Create = "{navRoute}.{section_code}.create";
       ...
     }
   → Apply permissionMode inference (core-seed-data.md Step C2):
     - section code == "detail" → inherit (SKIP — no section permissions)
     - section code == "dashboard" → read-only (read permission only)
     - section code == "list" or "kanban" or "calendar" or "weekly" → inherit (SKIP — view of module entity)
     - otherwise → crud (wildcard + read + create + update + delete)
   → Reference: core-seed-data.md lines 736-799 for full template

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

5b. PermissionsSeedData.cs — SECTION-LEVEL (for each entity where level == "section" AND permissionMode != "inherit")
   → Add CreateSectionPermissions(Guid {sectionCode}SectionId) method
   → Section-level entries: wildcard + CRUD matching Permissions.cs nested class
   → Reference: core-seed-data.md Step C2 lines 758-779 for template

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

6b. RolesSeedData.cs — SECTION-LEVEL (for each entity where level == "section" AND permissionMode != "inherit")
   → Add section-level mappings in GetRolePermissionMappings():
     Admin: section wildcard ({navRoute}.{section_code}.*)
     Manager: read + create + update for section
     Contributor: read + create for section
     Viewer: read for section
```

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

For each entity in {entity_section_map} where level == "section" AND permissionMode != "inherit":
  ✓ Permissions.cs has SECTION-LEVEL nested class {SectionPascal} with constants
  ✓ PermissionsSeedData has CreateSectionPermissions(Guid {section}SectionId) method
  ✓ RolesSeedData.GetRolePermissionMappings() includes section-level role mappings

Module-level (always):
  ✓ Permissions.cs has MODULE-LEVEL constants (Read/Create/Update/Delete/Wildcard)
  ✓ PermissionsSeedData has module-level HasData() for each permission
  ✓ RolesSeedData maps all 4 roles to module 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`
