# Core Seed Data - Execution Reference

> **Loaded by:** step-02-execute and step-04 compact loop
> **Condition:** Infrastructure task involves core seed data or IClientSeedDataProvider
> **Applies to:** Client projects only (seeding_strategy = "provider", ExtensionsDbContext)
>
> **Source of truth:** `/application` skill `templates-seed.md` (lines 608-916)

---

## 1. Parameter Extraction

Extract navigation hierarchy from the task's `_seedDataMeta` (populated by guardrail 1e):

```javascript
const task = currentTask;
const meta = task._seedDataMeta || task._providerMeta || {};
const coreSeedData = meta.coreSeedData || {};

// Navigation hierarchy
const navModules = coreSeedData.navigationModules || coreSeedData.navigation || [];
const navSections = coreSeedData.navigationSections || [];
const navResources = coreSeedData.navigationResources || [];
const permissions = coreSeedData.permissions || [];
const rolePerms = coreSeedData.rolePermissions || [];

// Derived context (from guardrail or PRD)
const navRoute = meta.navRoute;         // e.g. "business.humanresources.projects"
const contextCode = meta.contextCode;   // e.g. "business"
const appCode = meta.appCode;           // e.g. "humanresources"
const moduleCode = task.module;         // e.g. "projects"

// If _seedDataMeta is absent, fallback to PRD source
if (!navRoute) {
  const prd = readJSON('.ralph/prd.json');
  const navRoute = `${prd.source?.context || 'business'}.${prd.source?.application || prd.metadata?.module}.${task.module}`;
}
```

**State variables after extraction:**

| Variable | Example | Source |
|----------|---------|--------|
| `navRoute` | `business.humanresources.projects` | `_seedDataMeta.navRoute` |
| `contextCode` | `business` | `_seedDataMeta.contextCode` |
| `appCode` | `humanresources` | `_seedDataMeta.appCode` |
| `moduleCode` | `projects` | `task.module` |
| `navModules[]` | `[{code, label, icon, route, translations}]` | `coreSeedData.navigationModules` |
| `navSections[]` | `[{code, label, icon, route, parentCode, permission, sort}]` | `coreSeedData.navigationSections` |
| `navResources[]` | `[{code, type, entity, parentCode, permission}]` | `coreSeedData.navigationResources` |
| `permissions[]` | `[{path, action, description}]` | `coreSeedData.permissions` |
| `rolePerms[]` | `[{role, permissions[]}]` | `coreSeedData.rolePermissions` |

---

## 2. NavigationModuleSeedData.cs

**File:** `Infrastructure/Persistence/Seeding/Data/{ModulePascal}/NavigationModuleSeedData.cs`

### GUID Generation Rule

```csharp
// NEVER use Guid.NewGuid() — ALWAYS deterministic
private static Guid GenerateDeterministicGuid(string seed)
{
    using var sha256 = System.Security.Cryptography.SHA256.Create();
    var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(seed));
    return new Guid(hash.Take(16).ToArray());
}

// Usage: GUIDs are derived from the navigation path
public static readonly Guid ModuleId = GenerateDeterministicGuid("navigation-module-{navRoute}");
// Example: GenerateDeterministicGuid("navigation-module-business.humanresources.projects")
```

### Template

```csharp
using SmartStack.Domain.Navigation;

namespace {BaseNamespace}.Infrastructure.Persistence.Seeding.Data.{ModulePascal};

/// <summary>
/// Navigation seed data for {ModuleLabel} module.
/// Consumed by IClientSeedDataProvider at application startup.
/// </summary>
public static class {ModulePascal}NavigationSeedData
{
    // Deterministic GUID for this module
    public static readonly Guid {ModulePascal}ModuleId =
        GenerateDeterministicGuid("navigation-module-{navRoute}");

    /// <summary>
    /// Returns navigation module entry for seeding into core.nav_Modules.
    /// </summary>
    public static NavigationModuleSeedEntry GetModuleEntry(Guid applicationId)
    {
        return new NavigationModuleSeedEntry
        {
            Id = {ModulePascal}ModuleId,
            ApplicationId = applicationId,
            Code = "{moduleCode}",
            Label = "{label_en}",
            Description = "{desc_en}",
            Icon = "{icon}",              // Lucide React icon name
            IconType = IconType.Lucide,
            Route = "/{contextCode}/{appCode}/{moduleCode}",
            DisplayOrder = {displayOrder},
            IsActive = true
        };
    }

    /// <summary>
    /// Returns 4-language translations for this module.
    /// </summary>
    public static IEnumerable<NavigationTranslationSeedEntry> GetTranslationEntries()
    {
        var moduleId = {ModulePascal}ModuleId;
        return new[]
        {
            new NavigationTranslationSeedEntry
            {
                EntityType = NavigationEntityType.Module,
                EntityId = moduleId,
                LanguageCode = "fr",
                Label = "{label_fr}",
                Description = "{desc_fr}"
            },
            new NavigationTranslationSeedEntry
            {
                EntityType = NavigationEntityType.Module,
                EntityId = moduleId,
                LanguageCode = "en",
                Label = "{label_en}",
                Description = "{desc_en}"
            },
            new NavigationTranslationSeedEntry
            {
                EntityType = NavigationEntityType.Module,
                EntityId = moduleId,
                LanguageCode = "it",
                Label = "{label_it}",
                Description = "{desc_it}"
            },
            new NavigationTranslationSeedEntry
            {
                EntityType = NavigationEntityType.Module,
                EntityId = moduleId,
                LanguageCode = "de",
                Label = "{label_de}",
                Description = "{desc_de}"
            }
        };
    }

    private static Guid GenerateDeterministicGuid(string seed)
    {
        using var sha256 = System.Security.Cryptography.SHA256.Create();
        var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(seed));
        return new Guid(hash.Take(16).ToArray());
    }
}

/// <summary>Seed entry DTO for navigation module.</summary>
public class NavigationModuleSeedEntry
{
    public Guid Id { get; init; }
    public Guid ApplicationId { get; init; }
    public string Code { get; init; } = null!;
    public string Label { get; init; } = null!;
    public string Description { get; init; } = null!;
    public string Icon { get; init; } = null!;
    public IconType IconType { get; init; }
    public string Route { get; init; } = null!;
    public int DisplayOrder { get; init; }
    public bool IsActive { get; init; }
}

/// <summary>Seed entry DTO for navigation translation.</summary>
public class NavigationTranslationSeedEntry
{
    public NavigationEntityType EntityType { get; init; }
    public Guid EntityId { get; init; }
    public string LanguageCode { get; init; } = null!;
    public string Label { get; init; } = null!;
    public string Description { get; init; } = null!;
}
```

**Replace placeholders** with values from `navModules[]` and PRD `project` metadata.

---

## 2b. Navigation Sections & Resources (in same NavigationSeedData.cs)

> **CONDITIONAL:** Only generate if `seedDataCore.navigationSections[]` exists and is non-empty in feature.json.
> Sections and resources are added as additional methods in the **same** `{ModulePascal}NavigationSeedData.cs` file.

### GUID Generation Rules

```csharp
// Section GUID: deterministic from navRoute + section code
public static readonly Guid {SectionPascal}SectionId =
    GenerateDeterministicGuid("navigation-section-{navRoute}.{sectionCode}");
// Example: GenerateDeterministicGuid("navigation-section-business.ressourceshumaines.employees.list")

// Resource GUID: deterministic from navRoute + section code + resource code
public static readonly Guid {ResourcePascal}ResourceId =
    GenerateDeterministicGuid("navigation-resource-{navRoute}.{sectionCode}.{resourceCode}");
// Example: GenerateDeterministicGuid("navigation-resource-business.ressourceshumaines.employees.list.employees-grid")
```

### Section Methods (add to {ModulePascal}NavigationSeedData.cs)

```csharp
// --- Add AFTER GetTranslationEntries() in {ModulePascal}NavigationSeedData.cs ---

// Deterministic GUIDs for sections
public static readonly Guid {Section1Pascal}SectionId =
    GenerateDeterministicGuid("navigation-section-{navRoute}.{section1Code}");
// Repeat for each section...

/// <summary>
/// Returns navigation section entries for seeding into core.nav_Sections.
/// </summary>
public static IEnumerable<NavigationSectionSeedEntry> GetSectionEntries(Guid moduleId)
{
    return new[]
    {
        new NavigationSectionSeedEntry
        {
            Id = {Section1Pascal}SectionId,
            ModuleId = moduleId,
            Code = "{section1Code}",
            Label = "{section1_label_en}",
            Description = "{section1_desc_en}",
            Icon = "{section1_icon}",
            IconType = IconType.Lucide,
            Route = "/{contextCode}/{appCode}/{moduleCode}/{section1Code}",
            DisplayOrder = {section1_sort},
            IsActive = true
        }
        // Repeat for each section...
    };
}

/// <summary>
/// Returns 4-language translations for sections.
/// </summary>
public static IEnumerable<NavigationTranslationSeedEntry> GetSectionTranslationEntries()
{
    var entries = new List<NavigationTranslationSeedEntry>();

    // Section: {section1Code}
    var sec1Id = {Section1Pascal}SectionId;
    entries.AddRange(new[]
    {
        new NavigationTranslationSeedEntry
        {
            EntityType = NavigationEntityType.Section,
            EntityId = sec1Id,
            LanguageCode = "fr",
            Label = "{section1_label_fr}",
            Description = "{section1_desc_fr}"
        },
        new NavigationTranslationSeedEntry
        {
            EntityType = NavigationEntityType.Section,
            EntityId = sec1Id,
            LanguageCode = "en",
            Label = "{section1_label_en}",
            Description = "{section1_desc_en}"
        },
        new NavigationTranslationSeedEntry
        {
            EntityType = NavigationEntityType.Section,
            EntityId = sec1Id,
            LanguageCode = "it",
            Label = "{section1_label_it}",
            Description = "{section1_desc_it}"
        },
        new NavigationTranslationSeedEntry
        {
            EntityType = NavigationEntityType.Section,
            EntityId = sec1Id,
            LanguageCode = "de",
            Label = "{section1_label_de}",
            Description = "{section1_desc_de}"
        }
    });
    // Repeat for each section...

    return entries;
}
```

### Resource Methods (add to {ModulePascal}NavigationSeedData.cs)

> **CONDITIONAL:** Only generate if `seedDataCore.navigationResources[]` exists and is non-empty.

```csharp
// --- Add AFTER GetSectionTranslationEntries() ---

// Deterministic GUIDs for resources
public static readonly Guid {Resource1Pascal}ResourceId =
    GenerateDeterministicGuid("navigation-resource-{navRoute}.{parentSectionCode}.{resource1Code}");
// Repeat for each resource...

/// <summary>
/// Returns navigation resource entries for a given section.
/// </summary>
public static IEnumerable<NavigationResourceSeedEntry> GetResourceEntries(Guid sectionId)
{
    var entries = new List<NavigationResourceSeedEntry>();

    // Resources for section: {section1Code}
    if (sectionId == {Section1Pascal}SectionId)
    {
        entries.AddRange(new[]
        {
            new NavigationResourceSeedEntry
            {
                Id = {Resource1Pascal}ResourceId,
                SectionId = sectionId,
                Code = "{resource1Code}",
                Label = "{resource1_label_en}",
                EntityType = "{resource1_entity}",
                Route = "/{contextCode}/{appCode}/{moduleCode}/{section1Code}/{resource1Code}",
                DisplayOrder = 1
            }
            // Repeat for each resource in this section...
        });
    }
    // Repeat if-block for each section with resources...

    return entries;
}
```

### Additional DTO Classes (add to bottom of file)

```csharp
/// <summary>Seed entry DTO for navigation section.</summary>
public class NavigationSectionSeedEntry
{
    public Guid Id { get; init; }
    public Guid ModuleId { get; init; }
    public string Code { get; init; } = null!;
    public string Label { get; init; } = null!;
    public string Description { get; init; } = null!;
    public string Icon { get; init; } = null!;
    public IconType IconType { get; init; }
    public string Route { get; init; } = null!;
    public int DisplayOrder { get; init; }
    public bool IsActive { get; init; }
}

/// <summary>Seed entry DTO for navigation resource.</summary>
public class NavigationResourceSeedEntry
{
    public Guid Id { get; init; }
    public Guid SectionId { get; init; }
    public string Code { get; init; } = null!;
    public string Label { get; init; } = null!;
    public string? EntityType { get; init; }
    public string? Route { get; init; }
    public int DisplayOrder { get; init; }
}
```

### Placeholder Values Source

| Placeholder | Source in feature.json |
|-------------|----------------------|
| `{sectionCode}` | `seedDataCore.navigationSections[].code` |
| `{section_label_xx}` | `specification.navigation.entries[]` where `level == "section"` → `labels.xx` |
| `{section_icon}` | `seedDataCore.navigationSections[].icon` |
| `{section_sort}` | `seedDataCore.navigationSections[].sort` |
| `{section_route}` | `seedDataCore.navigationSections[].route` |
| `{resourceCode}` | `seedDataCore.navigationResources[].code` |
| `{resource_entity}` | `seedDataCore.navigationResources[].entity` |
| `{parentSectionCode}` | `seedDataCore.navigationResources[].parentCode` |

---

## 3. PermissionsSeedData.cs — MCP-First

### CRITICAL: PermissionAction Safety Rules

> **NEVER use `Enum.Parse<PermissionAction>("...")` — this causes runtime crashes if the string is invalid.**
> The error only manifests at application startup, not at compile time.

**Valid PermissionAction values (from SmartStack.Domain.Authorization):**

| Enum Value | Int | Description |
|------------|-----|-------------|
| `PermissionAction.Access` | 0 | Wildcard permissions only (IsWildcard = true) |
| `PermissionAction.Read` | 1 | GET/HEAD — View data |
| `PermissionAction.Create` | 2 | POST — Create new records |
| `PermissionAction.Update` | 3 | PUT/PATCH — Modify existing records |
| `PermissionAction.Delete` | 4 | DELETE — Remove records |
| `PermissionAction.Export` | 5 | Export data (CSV, Excel, etc.) |
| `PermissionAction.Import` | 6 | Import data |
| `PermissionAction.Approve` | 7 | Approve workflow items |
| `PermissionAction.Reject` | 8 | Reject workflow items |
| `PermissionAction.Assign` | 9 | Assign items to users |
| `PermissionAction.Execute` | 10 | Execute actions (sync, run, etc.) |

**Anti-patterns (FORBIDDEN):**

```csharp
// FORBIDDEN — Runtime crash if string is not a valid enum value
Enum.Parse<PermissionAction>("Validate");          // ArgumentException at startup
(PermissionAction)Enum.Parse(typeof(PermissionAction), "Validate"); // Same crash

// FORBIDDEN — String-based action in anonymous objects
new { Action = "read" };  // Not type-safe, silent mismatch possible
```

**Correct patterns (MANDATORY):**

```csharp
// ALWAYS use the typed enum directly — compile-time safe
Action = PermissionAction.Read      // Compile-time checked
Action = PermissionAction.Create    // Compile-time checked
Action = PermissionAction.Approve   // Compile-time checked

// For custom actions beyond standard CRUD, pick from the enum:
// Export, Import, Approve, Reject, Assign, Execute
```

**MCP validation:** `validate_conventions` with `checks: ["permissions"]` will detect and flag these anti-patterns.

### Step A: Call MCP (PRIMARY)

```
Tool: mcp__smartstack__generate_permissions
Args:
  navRoute: "{navRoute}"
  includeStandardActions: true
  includeWildcard: true
```

MCP returns:
- `Permissions.cs` nested class (Application layer constants)
- Permission seed entries with deterministic GUIDs

### Step B: Write Permissions.cs (Application layer)

```csharp
// Add to Application/Common/Authorization/Permissions.cs
public static class {ContextPascal}
{
    public const string Access = "{contextCode}.{appCode}";

    public static class {ModulePascal}
    {
        public const string View = "{navRoute}.read";
        public const string Create = "{navRoute}.create";
        public const string Update = "{navRoute}.update";
        public const string Delete = "{navRoute}.delete";
    }
}
```

### Step C: Write PermissionsSeedData.cs (Infrastructure layer)

**File:** `Infrastructure/Persistence/Seeding/Data/{ModulePascal}/PermissionsSeedData.cs`

```csharp
namespace {BaseNamespace}.Infrastructure.Persistence.Seeding.Data.{ModulePascal};

/// <summary>
/// Permission seed data for {ModuleLabel} module.
/// Consumed by IClientSeedDataProvider at application startup.
/// </summary>
public static class {ModulePascal}PermissionsSeedData
{
    // Deterministic GUIDs for permissions
    public static readonly Guid WildcardPermId = GenerateGuid("{navRoute}.*");
    public static readonly Guid ReadPermId = GenerateGuid("{navRoute}.read");
    public static readonly Guid CreatePermId = GenerateGuid("{navRoute}.create");
    public static readonly Guid UpdatePermId = GenerateGuid("{navRoute}.update");
    public static readonly Guid DeletePermId = GenerateGuid("{navRoute}.delete");

    public static IEnumerable<PermissionSeedEntry> GetPermissionEntries(Guid moduleId)
    {
        return new[]
        {
            new PermissionSeedEntry { Id = WildcardPermId, Path = "{navRoute}.*", Level = PermissionLevel.Module, Action = PermissionAction.Access, IsWildcard = true, ModuleId = moduleId, Description = "Full {moduleLabel} access" },
            new PermissionSeedEntry { Id = ReadPermId, Path = "{navRoute}.read", Level = PermissionLevel.Module, Action = PermissionAction.Read, IsWildcard = false, ModuleId = moduleId, Description = "View {moduleLabel}" },
            new PermissionSeedEntry { Id = CreatePermId, Path = "{navRoute}.create", Level = PermissionLevel.Module, Action = PermissionAction.Create, IsWildcard = false, ModuleId = moduleId, Description = "Create {moduleLabel}" },
            new PermissionSeedEntry { Id = UpdatePermId, Path = "{navRoute}.update", Level = PermissionLevel.Module, Action = PermissionAction.Update, IsWildcard = false, ModuleId = moduleId, Description = "Update {moduleLabel}" },
            new PermissionSeedEntry { Id = DeletePermId, Path = "{navRoute}.delete", Level = PermissionLevel.Module, Action = PermissionAction.Delete, IsWildcard = false, ModuleId = moduleId, Description = "Delete {moduleLabel}" }
        };
    }

    private static Guid GenerateGuid(string path)
    {
        using var sha256 = System.Security.Cryptography.SHA256.Create();
        var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes($"permission-{path}"));
        return new Guid(hash.Take(16).ToArray());
    }
}

public class PermissionSeedEntry
{
    public Guid Id { get; init; }
    public string Path { get; init; } = null!;
    public PermissionLevel Level { get; init; }
    public PermissionAction Action { get; init; }
    public bool IsWildcard { get; init; }
    public Guid ModuleId { get; init; }
    public string Description { get; init; } = null!;
}
```

### Step D: MCP Fallback

If MCP `generate_permissions` fails, use the template above directly with values derived from the PRD `coreSeedData.permissions[]`.

---

## 4. ApplicationRolesSeedData.cs (Application-Level, Once per Application)

**File:** `Infrastructure/Persistence/Seeding/Data/ApplicationRolesSeedData.cs`

### Purpose

Creates the 4 standard application-scoped roles: Admin, Manager, Contributor, Viewer.

**CRITICAL:** This file is created **ONCE per application** (not per module).
Without these roles, role-permission mappings in `SeedRolePermissionsAsync()` will fail silently.

### GUID Generation Rule

```csharp
// Deterministic GUID from application ID + role type
private static Guid GenerateRoleGuid(string roleType)
{
    using var sha256 = System.Security.Cryptography.SHA256.Create();
    var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes($"role-{ApplicationId}-{roleType}"));
    return new Guid(hash.Take(16).ToArray());
}
// roleType values: "admin", "manager", "contributor", "viewer"
```

### Template

```csharp
using SmartStack.Domain.Platform.Administration.Roles;

namespace {BaseNamespace}.Infrastructure.Persistence.Seeding.Data;

/// <summary>
/// Application-scoped role seed data for {AppLabel}.
/// Defines the 4 standard application roles: Admin, Manager, Contributor, Viewer.
/// Consumed by IClientSeedDataProvider at application startup.
/// </summary>
public static class ApplicationRolesSeedData
{
    // Application ID from NavigationApplicationSeedData
    private static readonly Guid ApplicationId = {ApplicationGuid};

    public static readonly Guid AdminRoleId = GenerateRoleGuid("admin");
    public static readonly Guid ManagerRoleId = GenerateRoleGuid("manager");
    public static readonly Guid ContributorRoleId = GenerateRoleGuid("contributor");
    public static readonly Guid ViewerRoleId = GenerateRoleGuid("viewer");

    public static IEnumerable<ApplicationRoleSeedEntry> GetRoleEntries()
    {
        yield return new ApplicationRoleSeedEntry
        {
            Id = AdminRoleId,
            Code = "admin",
            Name = "{AppLabel} Admin",
            Description = "Full administrative access to {AppLabel}",
            ApplicationId = ApplicationId,
            IsSystem = false,
            IsActive = true,
            DisplayOrder = 1
        };

        yield return new ApplicationRoleSeedEntry
        {
            Id = ManagerRoleId,
            Code = "manager",
            Name = "{AppLabel} Manager",
            Description = "Management access to {AppLabel} (Create, Read, Update)",
            ApplicationId = ApplicationId,
            IsSystem = false,
            IsActive = true,
            DisplayOrder = 2
        };

        yield return new ApplicationRoleSeedEntry
        {
            Id = ContributorRoleId,
            Code = "contributor",
            Name = "{AppLabel} Contributor",
            Description = "Contributor access to {AppLabel} (Create, Read)",
            ApplicationId = ApplicationId,
            IsSystem = false,
            IsActive = true,
            DisplayOrder = 3
        };

        yield return new ApplicationRoleSeedEntry
        {
            Id = ViewerRoleId,
            Code = "viewer",
            Name = "{AppLabel} Viewer",
            Description = "Read-only access to {AppLabel}",
            ApplicationId = ApplicationId,
            IsSystem = false,
            IsActive = true,
            DisplayOrder = 4
        };
    }

    private static Guid GenerateRoleGuid(string roleType)
    {
        using var sha256 = System.Security.Cryptography.SHA256.Create();
        var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes($"role-{ApplicationId}-{roleType}"));
        return new Guid(hash.Take(16).ToArray());
    }
}

public class ApplicationRoleSeedEntry
{
    public Guid Id { get; init; }
    public string Code { get; init; } = null!;
    public string Name { get; init; } = null!;
    public string Description { get; init; } = null!;
    public Guid ApplicationId { get; init; }
    public bool IsSystem { get; init; }
    public bool IsActive { get; init; }
    public int DisplayOrder { get; init; }
}
```

**Replace placeholders** with values from PRD and navigation metadata.

---

## 5. {Module}RolesSeedData.cs (Per Module)

**File:** `Infrastructure/Persistence/Seeding/Data/{ModulePascal}/RolesSeedData.cs`

### Context-Based Role Mapping

| Context | Admin | Manager | Contributor | Viewer |
|---------|-------|---------|-------------|--------|
| `platform.*` | CRUD | CRU | CR | R |
| `business.*` | CRUD | CRU | CR | R |
| `personal.*` | CRUD | CRU | CR | R |

### Template

```csharp
namespace {BaseNamespace}.Infrastructure.Persistence.Seeding.Data.{ModulePascal};

/// <summary>
/// Role-permission mapping seed data for {ModuleLabel} module.
/// Maps permissions to application-scoped roles (Admin, Manager, Contributor, Viewer).
/// Consumed by IClientSeedDataProvider at application startup.
/// </summary>
public static class {ModulePascal}RolesSeedData
{
    /// <summary>
    /// Returns role-permission mappings for this module.
    /// Roles are resolved at runtime by Code (not hardcoded GUIDs).
    /// </summary>
    public static IEnumerable<RolePermissionSeedEntry> GetRolePermissionEntries()
    {
        // Admin: wildcard access
        yield return new RolePermissionSeedEntry { RoleCode = "admin", PermissionPath = "{navRoute}.*" };

        // Manager: CRUD
        yield return new RolePermissionSeedEntry { RoleCode = "manager", PermissionPath = "{navRoute}.read" };
        yield return new RolePermissionSeedEntry { RoleCode = "manager", PermissionPath = "{navRoute}.create" };
        yield return new RolePermissionSeedEntry { RoleCode = "manager", PermissionPath = "{navRoute}.update" };

        // Contributor: CR
        yield return new RolePermissionSeedEntry { RoleCode = "contributor", PermissionPath = "{navRoute}.read" };
        yield return new RolePermissionSeedEntry { RoleCode = "contributor", PermissionPath = "{navRoute}.create" };

        // Viewer: R
        yield return new RolePermissionSeedEntry { RoleCode = "viewer", PermissionPath = "{navRoute}.read" };
    }
}

public class RolePermissionSeedEntry
{
    public string RoleCode { get; init; } = null!;
    public string PermissionPath { get; init; } = null!;
}
```

---

## 6. IClientSeedDataProvider Implementation

**File:** `Infrastructure/Persistence/Seeding/{AppPascalName}SeedDataProvider.cs`

### Critical Rules

| Rule | Description |
|------|-------------|
| Factory methods | `NavigationModule.Create(...)`, `Role.Create(...)`, `Permission.CreateForModule(...)`, `RolePermission.Create(...)` — NEVER `new Entity()` |
| Idempotence | Each Seed method checks existence before inserting |
| Execution order | Navigation → Roles → Permissions → RolePermissions (roles MUST exist before mapping) |
| SaveChanges per group | Navigation -> save -> Roles -> save -> Permissions -> save -> RolePermissions -> save |
| FK resolution by Code | Parent entities (modules, roles) found by `Code`, not hardcoded GUID |
| DI registration | `services.AddScoped<IClientSeedDataProvider, {AppPascalName}SeedDataProvider>()` |

### Template

```csharp
using Microsoft.EntityFrameworkCore;
using SmartStack.Application.Common.Interfaces;
using SmartStack.Application.Common.Interfaces.Seeding;
using SmartStack.Domain.Navigation;
using SmartStack.Domain.Platform.Administration.Roles;
using {BaseNamespace}.Infrastructure.Persistence.Seeding.Data;
using {BaseNamespace}.Infrastructure.Persistence.Seeding.Data.{Module1Pascal};
// using {BaseNamespace}.Infrastructure.Persistence.Seeding.Data.{Module2Pascal}; // Add per module

namespace {BaseNamespace}.Infrastructure.Persistence.Seeding;

/// <summary>
/// Seeds {AppLabel} navigation, roles, permissions, and role-permission data
/// into the SmartStack Core schema at application startup.
/// </summary>
public class {AppPascalName}SeedDataProvider : IClientSeedDataProvider
{
    public int Order => 100;

    public async Task SeedNavigationAsync(ICoreDbContext context, CancellationToken ct)
    {
        // --- Application ---
        var exists = await context.NavigationApplications
            .AnyAsync(a => a.Code == "{appCode}", ct);
        if (exists) return;

        var parentContext = await context.NavigationContexts
            .FirstAsync(c => c.Code == "{contextCode}", ct);

        var app = NavigationApplication.Create(
            parentContext.Id, "{appCode}", "{appLabel_en}",
            "{appDesc_en}", "{appIcon}", IconType.Lucide,
            "/{contextCode}/{appCode}", 1);
        context.NavigationApplications.Add(app);
        await ((DbContext)context).SaveChangesAsync(ct);

        // --- Application translations (4 languages) ---
        var appTranslations = new[]
        {
            NavigationTranslation.Create(NavigationEntityType.Application, app.Id, "fr", "{appLabel_fr}", "{appDesc_fr}"),
            NavigationTranslation.Create(NavigationEntityType.Application, app.Id, "en", "{appLabel_en}", "{appDesc_en}"),
            NavigationTranslation.Create(NavigationEntityType.Application, app.Id, "it", "{appLabel_it}", "{appDesc_it}"),
            NavigationTranslation.Create(NavigationEntityType.Application, app.Id, "de", "{appLabel_de}", "{appDesc_de}")
        };
        foreach (var t in appTranslations) context.NavigationTranslations.Add(t);
        await ((DbContext)context).SaveChangesAsync(ct);

        // --- Modules ---
        // Module: {Module1}
        var mod1Entry = {Module1Pascal}NavigationSeedData.GetModuleEntry(app.Id);
        var mod1 = NavigationModule.Create(
            mod1Entry.ApplicationId, mod1Entry.Code, mod1Entry.Label,
            mod1Entry.Description, mod1Entry.Icon, mod1Entry.IconType,
            mod1Entry.Route, mod1Entry.DisplayOrder);
        context.NavigationModules.Add(mod1);

        // Repeat for each module...
        await ((DbContext)context).SaveChangesAsync(ct);

        // --- Module translations ---
        foreach (var t in {Module1Pascal}NavigationSeedData.GetTranslationEntries())
        {
            context.NavigationTranslations.Add(
                NavigationTranslation.Create(t.EntityType, t.EntityId, t.LanguageCode, t.Label, t.Description));
        }
        // Repeat for each module...
        await ((DbContext)context).SaveChangesAsync(ct);

        // --- Sections (if seedDataCore.navigationSections exists) ---
        // Module 1 sections
        foreach (var secEntry in {Module1Pascal}NavigationSeedData.GetSectionEntries(mod1.Id))
        {
            var sec = NavigationSection.Create(
                secEntry.ModuleId, secEntry.Code, secEntry.Label,
                secEntry.Description, secEntry.Icon, secEntry.IconType,
                secEntry.Route, secEntry.DisplayOrder);
            context.NavigationSections.Add(sec);
        }
        // Repeat for each module that has sections...
        await ((DbContext)context).SaveChangesAsync(ct);

        // --- Section translations ---
        foreach (var t in {Module1Pascal}NavigationSeedData.GetSectionTranslationEntries())
        {
            context.NavigationTranslations.Add(
                NavigationTranslation.Create(t.EntityType, t.EntityId, t.LanguageCode, t.Label, t.Description));
        }
        // Repeat for each module that has sections...
        await ((DbContext)context).SaveChangesAsync(ct);

        // --- Resources (if seedDataCore.navigationResources exists) ---
        // Module 1 resources (resolved per section)
        foreach (var secEntry in {Module1Pascal}NavigationSeedData.GetSectionEntries(mod1.Id))
        {
            foreach (var resEntry in {Module1Pascal}NavigationSeedData.GetResourceEntries(secEntry.Id))
            {
                var res = NavigationResource.Create(
                    resEntry.SectionId, resEntry.Code, resEntry.Label,
                    resEntry.EntityType, resEntry.Route, resEntry.DisplayOrder);
                context.NavigationResources.Add(res);
            }
        }
        // Repeat for each module that has resources...
        await ((DbContext)context).SaveChangesAsync(ct);
    }

    public async Task SeedRolesAsync(ICoreDbContext context, CancellationToken ct)
    {
        // Check idempotence
        var applicationId = ApplicationRolesSeedData.ApplicationId;
        var exists = await context.Roles
            .AnyAsync(r => r.ApplicationId == applicationId, ct);
        if (exists) return;

        // Create application-scoped roles (Admin, Manager, Contributor, Viewer)
        foreach (var entry in ApplicationRolesSeedData.GetRoleEntries())
        {
            var role = Role.Create(
                entry.Code,
                entry.Name,
                entry.Description,
                entry.ApplicationId,
                entry.IsSystem);
            context.Roles.Add(role);
        }
        await ((DbContext)context).SaveChangesAsync(ct);
    }

    public async Task SeedPermissionsAsync(ICoreDbContext context, CancellationToken ct)
    {
        var exists = await context.Permissions
            .AnyAsync(p => p.Path == "{contextCode}.{appCode}.*", ct);
        if (exists) return;

        // Application-level wildcard
        var appWildcard = Permission.CreateWildcard(
            "{contextCode}.{appCode}.*", PermissionLevel.Application,
            "Full {appLabel_en} access");
        context.Permissions.Add(appWildcard);

        // Module permissions
        var mod1 = await context.NavigationModules
            .FirstAsync(m => m.Code == "{module1Code}", ct);
        foreach (var entry in {Module1Pascal}PermissionsSeedData.GetPermissionEntries(mod1.Id))
        {
            var perm = entry.IsWildcard
                ? Permission.CreateWildcard(entry.Path, entry.Level, entry.Description)
                : Permission.CreateForModule(entry.Path, entry.Level, entry.Action, false, entry.ModuleId, entry.Description);
            context.Permissions.Add(perm);
        }

        // Repeat for each module...
        await ((DbContext)context).SaveChangesAsync(ct);
    }

    public async Task SeedRolePermissionsAsync(ICoreDbContext context, CancellationToken ct)
    {
        var exists = await context.RolePermissions
            .AnyAsync(rp => rp.Permission!.Path.StartsWith("{contextCode}.{appCode}."), ct);
        if (exists) return;

        // Resolve roles by Code
        var roles = await context.Roles
            .Where(r => r.ApplicationId != null || r.IsSystem)
            .ToListAsync(ct);

        // Resolve permissions
        var permissions = await context.Permissions
            .Where(p => p.Path.StartsWith("{contextCode}.{appCode}."))
            .ToListAsync(ct);

        // Apply role-permission mappings from all modules
        var allMappings = new List<RolePermissionSeedEntry>();
        allMappings.AddRange({Module1Pascal}RolesSeedData.GetRolePermissionEntries());
        // allMappings.AddRange({Module2Pascal}RolesSeedData.GetRolePermissionEntries()); // per module

        foreach (var mapping in allMappings)
        {
            var role = roles.FirstOrDefault(r => r.Code == mapping.RoleCode);
            var perm = permissions.FirstOrDefault(p => p.Path == mapping.PermissionPath);
            if (role != null && perm != null)
            {
                context.RolePermissions.Add(RolePermission.Create(role.Id, perm.Id, "system"));
            }
        }

        await ((DbContext)context).SaveChangesAsync(ct);
    }
}
```

### DI Registration

```csharp
// In Infrastructure/DependencyInjection.cs — add:
using SmartStack.Application.Common.Interfaces.Seeding;

// In the registration method:
services.AddScoped<IClientSeedDataProvider, {AppPascalName}SeedDataProvider>();
```

---

## 7. Multi-Module Handling

When processing multiple modules in the same ralph-loop run:

### Module 1 (first): Creates everything from scratch

1. `ApplicationRolesSeedData.cs` (application-level, once per app)
2. `{Module1}NavigationSeedData.cs` (module + sections + resources + all translations)
3. `{Module1}PermissionsSeedData.cs`
4. `{Module1}RolesSeedData.cs`
5. `{AppPascalName}SeedDataProvider.cs` (new, with 4 methods — including section/resource seeding)
6. DI registration (new)

### Module 2+ (subsequent): Append to existing provider

1. `ApplicationRolesSeedData.cs` (already exists — skip)
2. `{Module2}NavigationSeedData.cs` (new file — include sections + resources if defined in feature.json)
3. `{Module2}PermissionsSeedData.cs` (new file)
4. `{Module2}RolesSeedData.cs` (new file)
5. `{AppPascalName}SeedDataProvider.cs` (**modify** — add using, add entries in Navigation/Permissions/RolePermissions methods, **including section/resource seeding for the new module**)
6. DI registration (already exists — skip)

**Detection:** Check if `{AppPascalName}SeedDataProvider.cs` exists. If yes, READ it and ADD the new module's entries to the appropriate methods (Navigation, Permissions, RolePermissions). Do NOT modify SeedRolesAsync().

### Section/Resource Conditionality

Sections and resources are **optional per module**. When processing a module's feature.json:

| Condition | Action |
|-----------|--------|
| `seedDataCore.navigationSections` absent or empty | Skip `GetSectionEntries()` / `GetSectionTranslationEntries()` / section seeding in provider |
| `seedDataCore.navigationResources` absent or empty | Skip `GetResourceEntries()` / resource seeding in provider |
| Both present | Generate all section + resource methods and provider code |

---

## 8. Verification Checklist (BLOCKING)

Before marking the task as completed, verify ALL:

- [ ] Deterministic GUIDs (NEVER `Guid.NewGuid()`) — SHA256 of path
- [ ] 4 languages for each navigation entity (fr, en, it, de)
- [ ] `ApplicationRolesSeedData.cs` created (once per application)
- [ ] 4 application roles defined: Admin, Manager, Contributor, Viewer
- [ ] Each role has a valid `Code` value ("admin", "manager", "contributor", "viewer")
- [ ] `Permissions.cs` constants match seed data paths
- [ ] MCP `generate_permissions` called (or fallback used)
- [ ] Role-permission mappings assigned (Admin, Manager, Contributor, Viewer)
- [ ] `IClientSeedDataProvider` generated with 4 methods (Navigation, Roles, Permissions, RolePermissions)
- [ ] Execution order: Navigation (modules → sections → resources) → Roles → Permissions → RolePermissions
- [ ] Each Seed method is idempotent (checks existence before inserting)
- [ ] Factory methods used throughout (NEVER `new Entity()`)
- [ ] `SaveChangesAsync` called per group (Navigation → Roles → Permissions → RolePermissions)
- [ ] DI registration added: `services.AddScoped<IClientSeedDataProvider, ...>()`
- [ ] NavigationSections seeded (if `seedDataCore.navigationSections` present in feature.json)
- [ ] NavigationResources seeded (if `seedDataCore.navigationResources` present in feature.json)
- [ ] Section/Resource translations created (4 languages each, EntityType = Section/Resource)
- [ ] `dotnet build` passes after generation
- [ ] NO `Enum.Parse<PermissionAction>` usage anywhere in seeding code (use typed enum directly)
- [ ] ALL PermissionAction values are from the valid enum: Access, Read, Create, Update, Delete, Export, Import, Approve, Reject, Assign, Execute

**If ANY check fails, the task status = 'failed'.**

---

## 9. Business Seed Data (DevDataSeeder) — TenantId Rules

> **Applies to:** Seed data for business entities (reference types, categories, statuses).
> **NOT the same as** core seed data above (navigation, permissions, roles).

### Rules

| Rule | Description |
|------|-------------|
| TenantId MANDATORY | ALL business seed entities MUST set `TenantId` |
| Deterministic TenantId | Use `SeedConstants.DefaultTenantId` (NEVER inline GUID) |
| DevDataSeeder pattern | Implement `IDevDataSeeder` with `SeedAsync()` method |
| Idempotency | Each seeder MUST check `AnyAsync()` before inserting |
| Order | DevDataSeeder `Order >= 200` (after core seed data at Order 100) |

### Template (Reference Types)

```csharp
public class {Module}DevDataSeeder : IDevDataSeeder
{
    public int Order => 200; // After core seed data (Order 100)

    public async Task SeedAsync(ExtensionsDbContext context, CancellationToken ct)
    {
        if (await context.Set<{EntityType}>().AnyAsync(ct)) return;

        var items = new[]
        {
            new {EntityType}
            {
                Id = GenerateDeterministicGuid("{entity-type}-{code}"),
                Code = "{code}",
                Name = "{name}",
                TenantId = SeedConstants.DefaultTenantId,  // MANDATORY
                IsActive = true
            }
        };

        context.Set<{EntityType}>().AddRange(items);
        await context.SaveChangesAsync(ct);
    }

    private static Guid GenerateDeterministicGuid(string seed)
    {
        using var sha256 = System.Security.Cryptography.SHA256.Create();
        var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(seed));
        return new Guid(hash.Take(16).ToArray());
    }
}
```

### FORBIDDEN

- Seeding business entities WITHOUT `TenantId`
- Using `Guid.NewGuid()` for TenantId
- Omitting idempotency check (`AnyAsync`)
- Hardcoding TenantId inline (use `SeedConstants.DefaultTenantId`)
