---
name: step-03-roles
description: Generate application roles and role-permission mappings using MCP scaffold_role_permissions (with fallback)
prev_step: steps/step-02-permissions.md
next_step: steps/step-03b-provider.md
---

# Step 3: Application Roles & Role-Permission Mapping

## MANDATORY EXECUTION RULES

- For **client projects** (`seeding_strategy = "provider"`): Generate ApplicationRolesSeedData.cs and module role mappings
- For **core projects** (`seeding_strategy = "hasdata"`): Use RolePermissionConfiguration.cs
- PREFER MCP `scaffold_role_permissions` tool as the primary method
- If MCP is unavailable or the call fails, use the FALLBACK PROCEDURE below
- ALWAYS assign permissions to default roles
- NEVER leave permissions without role assignments
- ALWAYS WRITE generated code to the actual files

## YOUR TASK

For **client projects**:
1. **ApplicationRolesSeedData.cs** (once per application) — defines the 4 application-scoped roles
2. **{Module}RolePermissionSeedData.cs** (per module) — maps permissions to roles by Code

For **core projects**:
1. RolePermissionConfiguration.cs HasData() entries
2. Default role assignments (SuperAdmin, PlatformAdmin, TenantAdmin, StandardUser)
3. Application-scoped role assignments (Admin, Manager, Contributor, Viewer)

---

## AVAILABLE STATE

From previous steps:

| Variable | Description |
|----------|-------------|
| `{full_path}` | Complete navigation path (navRoute) |
| `{level}` | context, application, module, or section |
| `{permission_guids}` | GUIDs for generated permissions |
| `{mcp_available}` | Boolean - MCP connectivity status |
| `{project_type}` | "core" or "client" |
| `{seeding_strategy}` | "hasdata" or "provider" |

---

## EXECUTION SEQUENCE (MCP Primary)

### 1. Determine Default Role Assignments

Based on navigation context, apply default role mappings:

| Context | PlatformAdmin | TenantAdmin | StandardUser |
|---------|---------------|-------------|--------------|
| `platform.*` | Full CRUD | Read only | None |
| `business.*` | Full CRUD | Full CRUD | Read only |
| `personal.*` | None | Full CRUD | Full CRUD |

### 2. Call MCP scaffold_role_permissions

```
Tool: mcp__smartstack__scaffold_role_permissions
Args:
  navRoute: "{full_path}"
  roles:
    platformAdmin: ["read", "create", "update", "delete"]  # Adjust based on context
    tenantAdmin: ["read", "create", "update"]              # Adjust based on context
    standardUser: ["read"]                                  # Adjust based on context
  includeWildcard: true
```

### 3. Parse MCP Response

The tool returns:
- RolePermissionConfiguration.cs HasData() entries
- Permission ID variable references
- Role ID variable references

### 4. Write Code to Files

**CRITICAL:** WRITE the generated code to the actual RolePermissionConfiguration.cs file.

### 5. Present Summary

```markdown
## Role-Permission Mappings

| Role | Permissions |
|------|-------------|
| SuperAdmin | `{full_path}.*` (via wildcard) |
| PlatformAdmin | `{full_path}.read`, `.create`, `.update`, `.delete` |
| TenantAdmin | `{full_path}.read`, `.create`, `.update` |
| StandardUser | `{full_path}.read` |
```

### 6. Confirm with User (Optional)

```yaml
questions:
  - header: "Role Access"
    question: "Adjust role permissions for {full_path}?"
    options:
      - label: "Keep defaults (Recommended)"
        description: "PlatformAdmin: CRUD, TenantAdmin: CRU, StandardUser: R"
      - label: "All roles full access"
        description: "All roles get full CRUD access"
      - label: "Custom"
        description: "I'll specify custom permissions"
    multiSelect: false
```

---

## MCP RESPONSE HANDLING

### Success Case

If MCP returns successfully:
- Write RolePermission HasData code to file
- Show role-permission summary table
- Proceed to step-04-backend.md

### Error Case

If MCP call fails or `{mcp_available}` = false:
- Log the error for reference
- Proceed to FALLBACK PROCEDURE below
- Do NOT stop the workflow

---

## CLIENT PROJECT HANDLING

> **Condition:** `{seeding_strategy}` = "provider"

**For core (`{seeding_strategy}` = "hasdata"):** Write in RolePermissionConfiguration.cs (existing pattern)

**For client (`{seeding_strategy}` = "provider"):** DO NOT write in RolePermissionConfiguration.cs (does not exist in client projects).

Instead, create TWO files:

### 1. ApplicationRolesSeedData.cs (ONCE per application)

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

**Purpose:** Defines the 4 standard application-scoped roles (Admin, Manager, Contributor, Viewer) with valid `Code` values.

**CRITICAL:** Without this file, role-permission mappings in `SeedRolePermissionsAsync()` will fail silently because `roles.FirstOrDefault(r => r.Code == mapping.RoleCode)` will return null.

See [references/application-roles-template.md](../references/application-roles-template.md) for the complete template.

**Key requirements:**
- Deterministic GUIDs based on `role-{applicationId}-{roleType}`
- 4 roles: Admin, Manager, Contributor, Viewer
- Each role has a valid `Code` property ("admin", "manager", "contributor", "viewer")
- `ApplicationId` references the navigation application GUID
- `IsSystem = false` (application-scoped, not system roles)

**Detection:** Check if ApplicationRolesSeedData.cs exists. If yes, skip creation (already exists from Module 1). If no, create it.

### 2. {Module}RolePermissionSeedData.cs (PER module)

**File:** `Infrastructure/Persistence/Seeding/Data/{Domain}/{Module}RolePermissionSeedData.cs`

**Purpose:** Maps permissions to roles by Code (e.g., "admin" → "{navRoute}.*").

Content: static class with method `GetRolePermissionEntries()` that returns the role-permission mapping data.
These entries will be consumed by the `IClientSeedDataProvider` at step 03b.

**After creating both files:** Proceed to step-03b-provider.md (which will skip for core projects).

---

## FALLBACK PROCEDURE (When MCP Unavailable)

> This procedure generates role-permission HasData entries following SmartStack.app patterns.

### F1. Read Existing RolePermissionConfiguration.cs

```
Glob: **/Persistence/Configurations/Authorization/RolePermissionConfiguration.cs
```

Read the file to determine:
- Existing role-permission mappings
- The GetSeedData() method structure
- Which roles already have which permissions
- The GUID generation method used (deterministic or hardcoded)

### F2. Read Role GUIDs

**System-level roles** (well-known GUIDs):

| Role | GUID |
|------|------|
| SuperAdmin | `11111111-1111-1111-1111-111111111111` |
| PlatformAdmin | `22222222-2222-2222-2222-222222222222` |
| TenantAdmin | `33333333-3333-3333-3333-333333333333` |
| StandardUser | `44444444-4444-4444-4444-444444444444` |

**IMPORTANT:** Read the actual `RoleSeedData.cs` or `RoleConfiguration.cs` in the target project to confirm the actual role GUIDs. The above are defaults; the project may use different values.

**Application-scoped roles** (deterministic GUIDs based on application):

```csharp
// Read the existing GenerateDeterministicGuid method in RolePermissionConfiguration.cs
// Typically uses MD5 hash:
private static Guid GenerateDeterministicGuid(Guid applicationId, string roleType)
{
    using var md5 = System.Security.Cryptography.MD5.Create();
    var input = $"{applicationId}-{roleType}";
    var hash = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(input));
    return new Guid(hash);
}
// roleType values: "admin", "manager", "contributor", "viewer"
```

Find the `applicationId` from `NavigationApplicationSeedData.cs` matching `{full_path}`.

### F3. Determine Context-Based Default Mappings

Based on `{full_path}` prefix:

| Context Prefix | SuperAdmin | PlatformAdmin | App Admin | App Manager | App Contributor | App Viewer |
|----------------|------------|---------------|-----------|-------------|-----------------|------------|
| `platform.*` | wildcard | Full CRUD | Full CRUD | CRU | CR | R |
| `business.*` | wildcard | Full CRUD | Full CRUD | CRU | CR | R |
| `personal.*` | wildcard | None | Full CRUD | CRU | CR | R |

### F4. Generate RolePermission HasData Entries

Using `{permission_guids}` from step-02:

```csharp
// In RolePermissionConfiguration.cs - GetSeedData() method
var seedDate = SeedConstants.SeedDate;

// ============================================================
// {MODULE_NAME} PERMISSIONS
// ============================================================

// SuperAdmin: already has *.* wildcard - no individual entries needed

// PlatformAdmin (for platform.* context)
rolePermissions.Add(new { RoleId = platformAdminRoleId, PermissionId = {permission_guids.read}, AssignedAt = seedDate });
rolePermissions.Add(new { RoleId = platformAdminRoleId, PermissionId = {permission_guids.create}, AssignedAt = seedDate });
rolePermissions.Add(new { RoleId = platformAdminRoleId, PermissionId = {permission_guids.update}, AssignedAt = seedDate });
rolePermissions.Add(new { RoleId = platformAdminRoleId, PermissionId = {permission_guids.delete}, AssignedAt = seedDate });

// Application-scoped: Admin → wildcard
rolePermissions.Add(new { RoleId = appAdminRoleId, PermissionId = {permission_guids.wildcard}, AssignedAt = seedDate });

// Application-scoped: Manager → CRU (read + create + update — no delete)
rolePermissions.Add(new { RoleId = appManagerRoleId, PermissionId = {permission_guids.read}, AssignedAt = seedDate });
rolePermissions.Add(new { RoleId = appManagerRoleId, PermissionId = {permission_guids.create}, AssignedAt = seedDate });
rolePermissions.Add(new { RoleId = appManagerRoleId, PermissionId = {permission_guids.update}, AssignedAt = seedDate });

// Application-scoped: Contributor → CR
rolePermissions.Add(new { RoleId = appContributorRoleId, PermissionId = {permission_guids.read}, AssignedAt = seedDate });
rolePermissions.Add(new { RoleId = appContributorRoleId, PermissionId = {permission_guids.create}, AssignedAt = seedDate });

// Application-scoped: Viewer → R
rolePermissions.Add(new { RoleId = appViewerRoleId, PermissionId = {permission_guids.read}, AssignedAt = seedDate });
```

### F5. Write Code to RolePermissionConfiguration.cs

**CRITICAL:** Do NOT just display code. WRITE it to the actual file.

1. Open `RolePermissionConfiguration.cs`
2. Find the `GetSeedData()` method
3. Add the new role-permission entries to the list
4. Add necessary permission GUID references (import from PermissionConfiguration or use inline)
5. Add comments grouping the new entries: `// {MODULE_NAME} PERMISSIONS`

### F6. Present Summary

```markdown
## Role-Permission Mappings Generated (Fallback)

| Role | Permissions |
|------|-------------|
| SuperAdmin | Already has wildcard access |
| PlatformAdmin | {full_path}.read, .create, .update, .delete |
| App Admin | {full_path}.* (wildcard) |
| App Manager | {full_path}.read, .create, .update |
| App Contributor | {full_path}.read, .create |
| App Viewer | {full_path}.read |

Written to: RolePermissionConfiguration.cs
```

### F7. Offer User Adjustment

```yaml
questions:
  - header: "Role Access"
    question: "Default role-permission mappings have been applied. Adjust?"
    options:
      - label: "Keep defaults (Recommended)"
        description: "Standard role hierarchy applied"
      - label: "Custom adjustments"
        description: "I want to change specific role permissions"
    multiSelect: false
```

If user selects "Custom adjustments", ask which roles/permissions to change and update the file accordingly.

---

## SUCCESS METRICS

**For client projects:**
- ApplicationRolesSeedData.cs created (once per application)
- {Module}RolePermissionSeedData.cs created with role-permission mappings
- All 4 application roles defined with valid Code values
- Proceeded to step-03b-provider.md

**For core projects:**
- Role-permission mappings generated (via MCP or fallback)
- RolePermissionConfiguration.cs WRITTEN with new entries
- All default roles have appropriate access
- Proceeded to step-04-backend.md

## FAILURE MODES

- Permission GUIDs not available from step-02 (return to step-02)
- Role GUIDs not found in project (ask user, use well-known defaults)
- Invalid navRoute format (return to step-00)

---

## NEXT STEP

After role-permission mappings are generated (via MCP or fallback) and written to files,
proceed to `./step-03b-provider.md`

> **Note:** step-03b will automatically skip if `{seeding_strategy}` = "hasdata" and proceed directly to step-04-backend.
