# SmartStack Layers — Execution Rules & Skill/MCP Mapping

> **Loaded by:** step-02 (planning) and step-03 (execution)
> **Purpose:** Defines per-layer folder conventions, skill/MCP tools, and verification rules.

---

## Layer Isolation Rules (Clean Architecture)

### Dependency Graph (allowed imports)

```
Domain         → (nothing)
Application    → Domain
Infrastructure → Domain, Application
Api            → Application, Infrastructure
Web            → Application (via API clients)
```

### Forbidden Patterns

| Layer | Do not | Why |
|-------|--------|-----|
| Domain | import Application, Infrastructure, Api | Domain is the core, depends on nothing |
| Application | import Infrastructure, Api | Application defines interfaces, Infrastructure implements them |
| Controller | inject DbContext directly | Controllers use Application services, not database directly |
| Controller | inject IRepository directly | Controllers use Application services, not repositories |
| Domain | use EF Core attributes (`[Table]`, `[Column]`, `[Index]`) | Domain must be persistence-ignorant; use `IEntityTypeConfiguration<T>` in Infrastructure |
| Domain | use `using Microsoft.EntityFrameworkCore` | Infrastructure concern leaking into Domain |
| API response | return Domain entity (e.g., `ActionResult<Employee>`) | Return DTOs (`EmployeeResponseDto`), never Domain entities |

### Enforcement

- **MCP `validate_conventions(checks: ['architecture'])`** — scans `.cs` files for forbidden imports, DbContext injection, entity exposure, and service interface placement
- **MCP `review_code(checks: ['architecture'])`** — detects layer violations, DbContext in controllers, EF Core attributes in Domain, entity exposure in API responses
- **POST-CHECKs A1–A7** — bash-based defense-in-depth (see `post-checks.md`)

---

## Layer 0 — Domain (sequential)

**Entities:** `Domain/Entities/{App}/{Module}/`
**Enums:** `Domain/Enums/{App}/{Module}/`
**Exceptions:** `Domain/Exceptions/{App}/{Module}/`

| Action | Tool |
|--------|------|
| Create entity | MCP `scaffold_extension` (type: entity) |
| Validate | MCP `validate_conventions` |

**Rules:**
- Inherit `BaseEntity`, choose tenant interface based on data isolation requirements:
  - `ITenantEntity` (strict mode, default): User can only access their tenant's data. Implement `Guid TenantId { get; set; }`. Service must filter by `_currentTenant.TenantId` with guard clause.
  - `IOptionalTenantEntity` (optional mode): Cross-tenant data (shared lookup tables, public content). Implement `Guid? TenantId { get; set; }`. Service may accept null TenantId. EF global filter handles both shared (null) and tenant-scoped (TenantId == current) rows automatically.
  - `IScopedTenantEntity` (scoped mode): Data scoped by Scope + TenantId combination. Validate both match current context in service layer.
  - No tenant interface (none mode): Cross-tenant system data (no isolation). No TenantId property, no filtering needed.
- Also implement `IAuditableEntity` for CreatedBy/UpdatedBy tracking
- See `references/smartstack-api.md` for exact BaseEntity API (Id, CreatedAt, UpdatedAt only)
- No Code/IsDeleted/RowVersion in BaseEntity — add business properties yourself
- Domain events for state changes
- Value objects for composite values
- **Enum serialization:** All enums in DTOs must serialize as strings. Check if `JsonStringEnumConverter` is configured globally. If not, add `[JsonConverter(typeof(JsonStringEnumConverter))]` on each enum. Frontend sends/receives strings (e.g., `"Active"`, not `0`).
- **Person Extension Pattern:** If entity has `personRoleConfig` (from PRD or feature.json):
  - Load `references/person-extension-pattern.md` for full patterns
  - Mandatory variant (`userLinkMode: 'mandatory'`): `Guid UserId` (non-nullable), ZERO person fields (`FirstName`, `LastName`, `Email`), unique index `(TenantId, UserId)`
  - Optional variant (`userLinkMode: 'optional'`): `Guid? UserId` (nullable), own person fields + computed `Display*` properties, filtered unique index `(TenantId, UserId) WHERE UserId IS NOT NULL`
  - EF Config: `builder.Ignore()` on computed `Display*` properties (optional variant only)

---

## Layer 0 — Infrastructure: EF Core (sequential)

**Configs:** `Infrastructure/Persistence/Configurations/{App}/{Module}/`
**Migrations:** `Infrastructure/Persistence/Migrations/` (not in subdirectories)

| Action | Tool |
|--------|------|
| Create config | Manual: `IEntityTypeConfiguration<T>` per `smartstack-api.md` patterns |
| Create migration | MCP `suggest_migration` → `dotnet ef migrations add` |
| Validate | MCP `check_migrations` |

**Rules:**
- One config per entity, table name = plural
- All relationships explicitly configured
- Indexes on FKs and frequent queries
- Register DbSet in ExtensionsDbContext
- Migration name ALWAYS from MCP `suggest_migration`
- Migration flag: `-o Persistence/Migrations`

Build requirement: `dotnet build --no-restore` must pass after migration.

> **Migration must cover ALL entities (POST-CHECK C38, C42):**
> Create/update migration AFTER ALL entities and EF configs are registered in DbContext.
> Verify with `dotnet ef migrations has-pending-model-changes` — must report NO pending changes.
> If entities are added incrementally across modules, create a NEW migration for each batch.

---

## Layer 1 — Seed Data (DEDICATED LAYER — sequential)

**Folder:** `Infrastructure/Persistence/Seeding/Data/{ModulePascal}/`

> **Detailed templates:** See `references/core-seed-data.md` for complete C# code templates.
> Navigation hierarchy: Application → Module → Section → Resource (all levels need seed data).

| Action | Tool |
|--------|------|
| Generate permissions | MCP `generate_permissions` (PRIMARY) |
| Navigation seed | Templates below |
| Roles seed | Templates below |
| Provider | Templates below |

### Seed Data Chain (9 files minimum)

**Application-level (created ONCE, shared across modules):**
1. **NavigationApplicationSeedData.cs** — Application-level navigation entry (MUST be first)
2. **ApplicationRolesSeedData.cs** — Application-scoped roles (admin, manager, contributor, viewer) with random GUIDs (`Guid.NewGuid()`). Provides role entries for SeedRolesAsync().

**Per-module:**
3. **NavigationModuleSeedData.cs** — Random GUIDs (`Guid.NewGuid()`), 4 languages (fr, en, it, de)
4. **NavigationSectionSeedData.cs** — Section-level navigation (if sections defined)
5. **NavigationResourceSeedData.cs** — Resource-level navigation (if resources defined)
6. **Permissions.cs** — Static permission constants: `public static class {Module} { public const string Read = "..."; }`. Referenced by `[RequirePermission(Permissions.{Module}.Read)]`.
7. **PermissionsSeedData.cs** — MCP `generate_permissions` first, fallback template
8. **RolesSeedData.cs** — Code-based role mapping: Admin=wildcard(*), Manager=CRU, Contributor=CR, Viewer=R. Do not use deterministic GUIDs for roles — look up by Code at runtime.

**Infrastructure (created ONCE per application):**
9. **{App}SeedDataProvider.cs** — Implements IClientSeedDataProvider
   - `SeedNavigationAsync()` — seeds Application → Module → Section → Resource + translations
   - `SeedRolesAsync()` — seeds application-scoped roles from ApplicationRolesSeedData
   - `SeedPermissionsAsync()` — seeds permission entries from PermissionsSeedData
   - `SeedRolePermissionsAsync()` — maps roles to permissions (roles resolved by Code, NOT by GUID)
   - DI: `services.AddScoped<IClientSeedDataProvider, {App}SeedDataProvider>()`

### GUID Generation Rule

All seed data IDs MUST use `Guid.NewGuid()` — NEVER deterministic/sequential/fixed GUIDs.
> See `references/core-seed-data.md` section 1b for the complete GUID rule and template.

### Route Convention (Full Paths Required)

> **Routes stored in DB drive the platform menu AND application-tracking.**
> Short routes (e.g., `humanresources`) cause errors on every page navigation.

**Route format: `/{app-kebab}/{module-kebab}/{section-kebab}`**

| Level | Code (C#) | Route (DB) |
|-------|-----------|-----------|
| Application | `human-resources` | `/human-resources` |
| Module | `employees` | `/human-resources/employees` |
| Section | `departments` | `/human-resources/employees/departments` |
| Resource | `export` | `/human-resources/employees/departments/export` |

**Examples (verified from SmartStack.app):**
- `/administration` (not `administration` without `/`)
- `/administration/users` (not `users`)

- PascalCase for C# code identifiers (`HumanResources`)
- **kebab-case** for ALL URL routes in seed data (`human-resources`)
- **Routes MUST start with `/`** and include full parent hierarchy
- Helper: `ToKebabCase()` transforms PascalCase → kebab-case

```csharp
private static string ToKebabCase(string value)
    => System.Text.RegularExpressions.Regex.Replace(value, "([a-z])([A-Z])", "$1-$2").ToLowerInvariant();

// Route construction:
var appRoute = $"/{ToKebabCase(appCode)}";
// → "/human-resources"

var moduleRoute = $"{appRoute}/{ToKebabCase(moduleCode)}";
// → "/human-resources/employees"

var sectionRoute = $"{moduleRoute}/{ToKebabCase(sectionCode)}";
// → "/human-resources/employees/departments"
```

**Route convention:**
> ALL sections use a UNIFORM route pattern: module route + `/{section-code}`.
> - `list` section route = module route + `/list` (e.g., `/human-resources/employees/list`)
> - `detail` is an implicit route (not a section): module route + `/:id` (e.g., `/human-resources/employees/:id`)
> - Other sections (dashboard, approve, import) = module route + `/{section-kebab}` (same rule)
> - Do NOT use: `/employees/detail/:id` (detail is implicit, not a section code)

**Do not:**
- Use deterministic/sequential/fixed GUIDs — always use `Guid.NewGuid()`
- Skip translations (must have fr, en, it, de)
- Create empty seed classes with no seeding logic
- Use PascalCase in route URLs — always kebab-case
- Omit NavigationApplicationSeedData — menu becomes invisible
- Use short routes without `/` prefix — `"humanresources"` must be `"/human-resources"`
- Create routes without parent hierarchy — `"employees"` must be `"/human-resources/employees"`

---

## Layer 2 — Application (parallel with API within layer)

**Services:** `Application/Services/{App}/{Module}/` (interface)
**Service impls:** `Infrastructure/Services/{App}/{Module}/` (implementation)
**DTOs:** `Application/DTOs/{App}/{Module}/`
**Validators:** `Application/Validators/{App}/{Module}/`

| Action | Tool |
|--------|------|
| Create service/DTO | MCP `scaffold_extension` |
| Complex service logic | /application skill |
| Validate | MCP `validate_conventions` |

**Rules:**
- **Tenant injection depends on entity tenant mode** (see entity rules below):
  - strict mode (default): Inject `ICurrentTenantService` + guard clause: `var tenantId = _currentTenant.TenantId ?? throw new TenantContextRequiredException();` (returns 400 if missing)
  - optional mode: Inject `ICurrentTenantService`, no guard: `var tenantId = _currentTenant.TenantId;` (nullable). EF global filter handles shared+tenant data.
  - scoped mode: Inject `ICurrentTenantService`, validate Scope + TenantId consistency. Tenant scope requires TenantId.
  - none mode: No `ICurrentTenantService` injection needed (cross-tenant data, no filtering)
- All services must inject `ICurrentUserService` for audit trails
- All services must inject `ILogger<T>` for production diagnostics
- CQRS with MediatR
- FluentValidation for all commands — must register validators via DI:
  `services.AddValidatorsFromAssemblyContaining<Create{Entity}DtoValidator>();`
  Without DI registration, `[FromBody]` DTOs are never validated (POST-CHECK C40)
- Date fields in DTOs must use `DateOnly`, not `string` (POST-CHECK C41). See `smartstack-api.md` DTO Type Mapping.
- DTOs separate from domain entities
- Service interfaces in Application, implementations in Infrastructure
- Do not use: `tenantId: Guid.Empty`, `TenantId!.Value`, queries without TenantId filter, `ICurrentUser` (does not exist — use `ICurrentUserService` + `ICurrentTenantService`), `string` type for date fields
- **Person Extension services:** If entity has `personRoleConfig`:
  - `Include(x => x.User)` on all queries (required)
  - Inject `ICoreDbContext` for User existence checks
  - Required: search on `User.FirstName`, `User.LastName`, `User.Email`
  - Optional: search fallback `User.FirstName ?? x.FirstName`
  - CreateAsync: verify User exists + no duplicate `(TenantId, UserId)`
  - ResponseDto: include `Display*` fields resolved from User join

---

## Layer 2 — API (parallel with Application within layer)

**Controllers:** `Api/Controllers/{AppPascal}/{Entity}Controller.cs`

| Action | Tool |
|--------|------|
| Simple CRUD controller | MCP `scaffold_extension` |
| Complex controller | /controller skill |
| Validate | MCP `validate_security` |

**Rules:**
- `[RequirePermission(Permissions.{Module}.{Action})]` on every endpoint
- NavRoute minimum 2 segments (application.module):
  - `[NavRoute("human-resources.employees")]` (CORRECT — module-level, 2 segments)
  - `[NavRoute("human-resources.employees.departments")]` (CORRECT — section-level, 3 segments)
  - `[NavRoute("administration.ai", Suffix = "prompts")]` (CORRECT — sub-resource with Suffix)
- NavRoute values must use kebab-case for all multi-word segments:
  - `[NavRoute("human-resources.employees")]` (CORRECT)
  - `[NavRoute("humanresources.employees")]` (WRONG — missing hyphens)
  - `[NavRoute("project-management.projects")]` (CORRECT)
  - `[NavRoute("projectmanagement.projects")]` (WRONG)
- Permission paths must use kebab-case matching NavRoute codes (e.g., `human-resources.employees.read`)
- Do not use concatenated segments like `humanresources` — must be `human-resources`
- POST-CHECK C34 detects non-kebab-case NavRoute values. POST-CHECK C35 detects non-kebab-case permissions
- Do not combine `[Route("api/...")]` with `[NavRoute]` — causes 404s (POST-CHECK C43)
- `[NavRoute]` is the only route attribute needed — resolves routes from DB at startup
- **NavRoute uniqueness:** Each controller MUST have a unique NavRoute value. Two controllers sharing the same NavRoute causes routing conflicts and 404s (POST-CHECK C50)
- **NavRoute segment count vs hierarchy:** If a controller lives in a section subfolder (e.g., `Controllers/{App}/Employees/ContractsController.cs`), its NavRoute MUST have 3 segments (`app.module.section`), not 2. A 2-segment NavRoute on a section controller causes API 404s because the resolved route doesn't match the expected path (POST-CHECK C51)
- Do not use `[Authorize]` without specific permission
- Swagger XML documentation
- Return DTOs, never domain entities
- **Person Extension DTOs:** ResponseDto must include `Display*` fields (`DisplayFirstName`, `DisplayLastName`, `DisplayEmail`) resolved from User join. Required CreateDto: `Guid UserId` only. Optional CreateDto: `Guid? UserId` + person fields.

---

## Layer 3 — Frontend (parallel with I18n within layer)

> **Detailed patterns:** See `references/smartstack-frontend.md` for complete code templates.

**Pages:** `src/pages/{AppPascal}/{Module}/`
**Components:** `src/components/{Module}/`

| Action | Tool |
|--------|------|
| API client | MCP `scaffold_api_client` |
| Routes | MCP `scaffold_routes` (outputFormat: `componentRegistry`) |
| Complex pages | /ui-components skill + `smartstack-frontend.md` patterns |
| Validate routes | MCP `validate_frontend_routes` |

**Layout mapping:** Configured per application in the routing configuration.

### Lazy Loading (Required)

> See `references/smartstack-frontend.md` section 1 for complete patterns (React.lazy + Suspense + named export wrapping).

### Page Structure Pattern

All pages MUST follow: hooks → useEffect(load) → loading state → error state → content.

```tsx
// 1. Hooks at top
const { t } = useTranslation(['{module}']);
// 2. State
const [loading, setLoading] = useState(true);
// 3. useCallback + useEffect for data loading
// 4. if (loading) return <Loader2 spinner />
// 5. if (error) return <error UI with retry>
// 6. return <content with CSS variables>
```

### Components & CSS

**Components:** DataTable, EntityCard, EntityLookup (do not use raw HTML `<table>`)
**CSS:** Variables only — hardcoded Tailwind colors cause failures in POST-CHECK C9.
> See `references/smartstack-frontend.md` section 4 for the full CSS variable mapping table.

**Loader:** `Loader2` from `lucide-react` for spinners, `PageLoader` for Suspense fallback

### RULE — Frontend Route Ordering

> **Note (v3.7+):** Route ordering is handled automatically by DynamicRouter.
> The ordering rules below apply only to legacy Pattern A/B projects.

> **CRITICAL:** React Router matches routes top-to-bottom. Dynamic routes (`:id`, `:id/edit`) placed BEFORE static routes (`dashboard`, `create`) will swallow the static paths — e.g., `/employees/dashboard` matches `:id` with `id="dashboard"` → 404.

**Order (MANDATORY):**

1. Index/list route (path: `'{module}/{section}'`)
2. Create route (`'{module}/{section}/create'`)
3. Static sections (`'{module}/{section}/dashboard'`, etc.)
4. Dynamic routes (`'{module}/{section}/:id'`, `'{module}/{section}/:id/edit'`)
5. Module redirect (`'{module}'` → `'{module}/{first_section}'`)
6. Application redirect (`''` → `'{first_module}/{first_section}'`) — ALWAYS LAST

```tsx
// ✅ CORRECT — module segment included, static before dynamic
// (inside applicationRoutes['human-resources'], paths are relative to /human-resources/)
{ path: 'employee-management/employees', element: <EmployeesPage /> },
{ path: 'employee-management/employees/create', element: <CreateEmployeePage /> },
{ path: 'employee-management/employees/dashboard', element: <EmployeeDashboardPage /> },
{ path: 'employee-management/employees/:id', element: <EmployeeDetailPage /> },
{ path: 'employee-management/employees/:id/edit', element: <EditEmployeePage /> },
{ path: 'employee-management', element: <Navigate to="employee-management/employees" replace /> },
{ path: '', element: <Navigate to="employee-management/employees" replace /> },

// ❌ WRONG — missing module segment (resolves to /human-resources/employees instead of /human-resources/employee-management/employees)
{ path: 'employees', element: <EmployeesPage /> },

// ❌ WRONG — :id before dashboard → dashboard is unreachable (matched as id="dashboard")
{ path: 'employee-management/employees/:id', element: <EmployeeDetailPage /> },
{ path: 'employee-management/employees/dashboard', element: <EmployeeDashboardPage /> },  // NEVER REACHED
```

POST-CHECK C49 detects this anti-pattern and BLOCKS.

### Section Routes (when module has sections)

> **Note (v3.7+):** With DynamicRouter, section routes are resolved automatically from navigation
> seed data + PageRegistry. The manual wiring below applies only to legacy Pattern A/B projects.

If the module defines `{sections}`, ensure navigation seed data has ComponentKey matching PageRegistry keys.
ComponentKey convention: `{app_code}.{module_code}.{section_code}` (dot-separated, lowercase).
DynamicRouter generates the URL paths from seed data Route field.

Legacy (Pattern A/B): generate frontend routes for EACH section as children of the application route.
Route paths MUST include the module segment: `{module-kebab}/{section-kebab}`.

```tsx
// Routes in applicationRoutes['human-resources'] — RELATIVE paths include module segment
// IMPORTANT: static routes BEFORE dynamic routes (see Route Ordering rule above)
// Example: app=human-resources, module=employee-management, section=employees
{ path: '{module-kebab}/{section-kebab}', element: <Suspense fallback={<PageLoader />}><{Section}Page /></Suspense> },
{ path: '{module-kebab}/{section-kebab}/create', element: <Suspense fallback={<PageLoader />}><Create{Section}Page /></Suspense> },
{ path: '{module-kebab}/{section-kebab}/:id', element: <Suspense fallback={<PageLoader />}><{Section}DetailPage /></Suspense> },
{ path: '{module-kebab}/{section-kebab}/:id/edit', element: <Suspense fallback={<PageLoader />}><Edit{Section}Page /></Suspense> },

// Concrete example:
// { path: 'employee-management/employees', element: ... },         → /human-resources/employee-management/employees ✅
// { path: 'employees', element: ... },                             → /human-resources/employees ❌ WRONG (missing module)
```

Section pages live in `src/pages/{AppPascal}/{Module}/{Section}/`.

### FK Fields in Forms

FK Guid fields (e.g., `EmployeeId`) must use `EntityLookup` — do not use `<select>` or `<input>`.
Backend: GetAll endpoint must accept `?search=` parameter.
> See `references/smartstack-frontend.md` section 6 for the full EntityLookup component, code examples, and usage patterns.

**Do not:**
- Use `src/pages/{Module}/` structure (flat, missing App)
- Import axios directly — use `@/services/api/apiClient`
- Use raw `<table>` element — use DataTable
- Use `<input type="text">` for FK Guid fields — use `EntityLookup`
- Use `<select>` for FK Guid fields — use `EntityLookup` (even with API-loaded options)
- Add placeholders like "Enter ID" or "Enter GUID" — use `EntityLookup`
- Hardcode Tailwind colors (`bg-white`, `text-gray-900`) — use CSS variables
- Import pages statically in route files — use `React.lazy()`
- Create `<Suspense>` without `fallback` prop
- Support only fr/en — must have 4 languages
- Use `t('key')` without namespace prefix — use `t('ns:key', 'fallback')`
- Hardcode user-facing strings in JSX — use `t()`

---

## Layer 3 — I18n

> **Template:** See `references/smartstack-frontend.md` Section 2 for complete JSON template.

4 JSON files per module namespace: fr, en, it, de — identical key structures.

**Location:** `src/i18n/locales/{lang}/{module}.json`

**Required keys:** `title`, `description`, `actions`, `labels`, `columns`, `form`, `errors`, `validation`, `messages`, `empty`

**Usage pattern:**
```tsx
const { t } = useTranslation(['{module}']);
t('{module}:title', 'Module Title')           // ALWAYS with fallback
t('{module}:actions.create', 'Create entity') // ALWAYS with namespace prefix
```

---

## Layer 3 — Documentation (after frontend pages exist)

> **After frontend pages are created, generate module documentation.**

| Action | Tool |
|--------|------|
| Generate doc-data.ts + page wrapper | `/documentation` skill (type: user) |
| Verify DocToggleButton in pages | POST-CHECK C26 |

**Output files:**
- `src/pages/docs/business/{app}/{module}/doc-data.ts` — data-driven documentation
- `src/pages/docs/business/{app}/{module}/index.tsx` — page wrapper using `DocRenderer`
- `src/i18n/locales/fr/docs-{app}-{module}.json` — French doc translations

**DocToggleButton (Required):** Every list/detail page must include `DocToggleButton` in its header.
See `references/smartstack-frontend.md` section 7 for the component pattern.

---

## Layer 4 — DevData (optional)

**Folder:** `Infrastructure/Persistence/Seeding/DevData/`

| Action | Tool |
|--------|------|
| Create DevDataSeeder | Manual / template |

**Rules:**
- Business test data for development/demo environments
- All DevData entities must include `TenantId`
- DevData depends on seed data (navigation, permissions) being in place
- Optional — skip if no meaningful test data needed

---

## Planning Template — Per-File Plan Format

> **Used by step-02 when creating the execution plan.** Each entity MUST include:

```yaml
Entity: {EntityName}
  - tenantMode: strict | optional | scoped | none
  - codePattern: auto-generated strategy (if applicable)
  - fkFields: [{field, targetEntity}] (if applicable)
  - acceptance criteria: [AC1, AC2, ...]
```

For EACH file in the plan, specify HOW it will be created/modified:

**Layer 0 — Domain + Infrastructure (sequential):**

| # | File | Action | Tool |
|---|------|--------|------|
| 1 | Domain/Entities/.../Entity.cs | create | MCP scaffold_extension |
| 2 | Infrastructure/.../EntityConfiguration.cs | create | MCP scaffold_extension |
| 3 | Infrastructure/Migrations/ | create | MCP suggest_migration + dotnet ef |

**Layer 1 — Seed Data (sequential):**

| # | File | Action | Tool |
|---|------|--------|------|
| 4 | Seeding/Data/NavigationApplicationSeedData.cs | create | Reference core-seed-data.md (once per app) |
| 4b | Seeding/Data/ApplicationRolesSeedData.cs | create | Reference core-seed-data.md (once per app) |
| 5 | Seeding/Data/.../NavigationModuleSeedData.cs | create | Reference core-seed-data.md (4 langs) |
| 5b | Application/Authorization/Permissions.cs | create | MCP generate_permissions |
| 6 | Seeding/Data/.../PermissionsSeedData.cs | create | MCP generate_permissions |
| 7 | Seeding/Data/.../RolesSeedData.cs | create | Reference core-seed-data.md |
| 7b | Seeding/{App}SeedDataProvider.cs | create | Reference core-seed-data.md (IClientSeedDataProvider + DI) |

**Layer 2 — Backend (parallel within layer):**

| # | File | Action | Tool |
|---|------|--------|------|
| 8 | Application/Services/.../Service.cs | create | MCP scaffold_extension |
| 9 | Application/DTOs/.../Dto.cs | create | MCP scaffold_extension |
| 9b | Infrastructure/Services/CodeGeneration/ | create | Reference code-generation.md (if codePattern != manual) |
| 10 | Api/Controllers/.../Controller.cs | create | /controller skill or MCP scaffold_extension |

**Layer 3 — Frontend + I18n + Documentation (parallel within layer):**

| # | File | Action | Tool |
|---|------|--------|------|
| 11 | src/pages/{App}/{Mod}/ListPage.tsx | create | /ui-components skill |
| 11b | src/pages/{App}/{Mod}/CreatePage.tsx | create | /ui-components skill (FK: EntityLookup) |
| 11c | src/pages/{App}/{Mod}/EditPage.tsx | create | /ui-components skill (FK: EntityLookup) |
| 12 | src/services/api/{module}Api.ts | create | MCP scaffold_api_client |
| 13 | src/routes/{module}.tsx | create | MCP scaffold_routes |
| 14 | src/i18n/locales/{lang}/{module}.json | create | Reference smartstack-frontend.md (4 languages) |
| 14b | src/pages/docs/business/{app}/{module}/doc-data.ts | create | /documentation skill |
| 14c | src/pages/docs/business/{app}/{module}/index.tsx | create | /documentation skill |

**FK Field Guidance:** If step-01 identified `fkFields[]`, every Create/Edit page MUST use `EntityLookup` for those fields (see `smartstack-frontend.md` section 6).

---

## Parallelization Strategy (Agent tool)

> **Do not parallelize across layers.** Layers execute sequentially: 0 → 1 → 2 → 3 → 4.
> The Agent tool is used **within** a layer to parallelize multi-entity work.

If NOT economy_mode AND a layer has multiple entities to process:

**Launch parallel Agent subagents WITHIN the layer for multi-entity work.**

- **Layer 2 (Backend):** If multiple entities, launch one Snipper agent per entity (each handles service + controller for one entity)
- **Layer 3 (Frontend):** If multiple entities, launch one Snipper agent per entity (each handles pages + i18n for one entity)

| Condition | Action |
|-----------|--------|
| economy_mode = true | NO parallel agents, all sequential |
| Single entity | NO parallel agents, agent principal handles all layers |
| Multiple entities, Layer 2 | Parallel: one Snipper agent per entity (service + controller) |
| Multiple entities, Layer 3 | Parallel: one Snipper agent per entity (pages + i18n) |
| Analysis phase (step-01) | Parallel: 2-3 Explore agents (backend + frontend + context) |

See `references/parallel-execution.md` for agent launch patterns, task coordination, and decision matrix.

---

## Delegate Mode Fast Path

When `/business-analyse-develop` invokes `/apex -d {prd_path}`, PRD tasks already define the scope.

Map each PRD task to a layer based on `task.category`:
- `domain` → Layer 0
- `infrastructure` → Layer 0
- `seedData` → Layer 1
- `application` → Layer 2
- `api` → Layer 2
- `frontend` → Layer 3
- `test` → inline (Layer 2 backend tests, Layer 3 frontend tests)

For each task: infer file_path, action, and tool from category. SKIP user checkpoint. Jump to "Estimated Commits" section.
