---
name: backend-core-seed
description: >
  Phase 0 of /ba-develop — generates one Core Foundation Seed
  bundle per declared application (5 IClientSeedDataProvider classes per app
  covering navigation + roles + permissions + role-permission mappings + dev
  test users) by invoking the deterministic `scaffold-core-seed` CLI. Zero
  creative work for the agent: the spec is built by the Studio backend
  (`prepareCoreSeedContext`) from the BA menu / actors / permissions and
  dropped to a temp file before this skill runs.
phase: development/backend
cli: cli/scaffold-core-seed
allowed-tools: [Read, Glob, Grep, Bash]  # Bash: CLI invocation
---

# Core Foundation Seed — Phase 0 of develop pipeline

This is the FIRST phase of `/ba-develop`. It generates the
per-application foundation that every subsequent phase (Entities, API,
Frontend) depends on:

For each application declared in the BA menu tree (one or more):

- Application + every Module + every Section + every Resource declared in
  the BA menu tree, in
  `Seeding/Applications/{AppPascal}/Core/{AppPascal}CoreNavigationSeedDataProvider.cs`
- One `Role` per actor × `applicationCode`, in
  `{AppPascal}CoreRolesSeedDataProvider.cs`
- One `NavigationPermission` per `structured.permissions[]` row, in
  `{AppPascal}CorePermissionsSeedDataProvider.cs`
- Role → permission wiring in `{AppPascal}CoreRolePermissionsSeedDataProvider.cs`
- One dev test user per role (gated by `IHostEnvironment.IsDevelopment()`)
  in `{AppPascal}CoreTestUsersSeedDataProvider.cs`
- Each application's 5 providers registered in `DependencyInjection.cs`
  between `<<< CORE-SEED-DI-{AppPascal} BEGIN/END >>>` markers (one block
  per app)

## Why this skill exists

The previous per-module seeding pattern allowed a module to ship with 9
entities + 2 controllers but **empty navigation / roles / permissions**
(observed in TestV2/ba-002, Budget module). Each per-module agent only saw
its own slice, so the global navigation tree was never assembled
deterministically.

This skill moves seeding to a **single per-application deterministic generator**
that sees the full BA state for that app in one shot. Each `ApplicationSpec`'s
`modules[]` field is load-bearing: the CLI's `validate.ts` and the runtime gate
`verifyCoreFoundationSeed` both refuse to pass if any declared module lacks
nav + ≥ 1 role + ≥ 2 permissions in its owning application.

Multi-application projects (e.g. CRM + Budgeting in the same .NET solution)
get one Core bundle per app, with class names prefixed by the PascalCase app
code to avoid collisions and DI markers scoped per app for idempotent re-runs.

## Your job

The Studio backend has already assembled the spec and written it to a temp
file. Your job is exactly three steps:

1. **Invoke the CLI** with the spec file path provided in the prompt:

   ```bash
   npx --prefer-offline tsx skills/development/backend/core-seed/cli/scaffold-core-seed/index.ts \
     --spec-file <SPEC_FILE_PATH> \
     --outdir <PROJECT_PATH>
   ```

2. **Verify the output envelope** — the CLI prints a JSON envelope on
   stdout. Confirm `success: true` and that:
   - `filesCreated` lists `5 × N_apps` `{AppPascal}Core*SeedDataProvider.cs`
     files (and the test users manifest, if present)
   - `filesModified` lists the patched `DependencyInjection.cs`
   - `data.diPatched === true` (otherwise the providers will not be
     resolved at startup)
   - `data.appsPatched` lists every application code from the spec
   - `warnings[]` is empty (or only minor — review them)

3. **Emit a SHORT final report**, then STOP:
   ```
   Created: 5 × N apps Core providers + DI patch | Apps: <list> | Warnings: 0
   ```

## What you MUST NOT do

- Do not edit any of the generated `.cs` files. They are deterministic;
  any change you make will be overwritten on the next Phase 0 run.
- Do not regenerate the spec yourself. The backend builds it from the BA
  tables; agent-built specs reintroduce the drift this phase exists to kill.
- Do not run `dotnet build` from this skill — the runtime gate
  (`verifyCoreFoundationSeed` + `dotnet build`) runs after your phase ends.
- Do not add navigation / roles / permissions inline into module-specific
  seed providers in Phase 1. Phase 1's `scaffold-seed` is for **reference
  data only** (lookup tables, enum codes), placed under
  `Seeding/Applications/{AppPascal}/Modules/{ModuleCode}/`.

## Spec contract — what the CLI consumes

See `cli/scaffold-core-seed/types.ts` for the full Zod schema. The shape is:

```jsonc
{
  "appCode": "TestV2",                  // PascalCase — used in src/{AppCode}.Infrastructure/...
  "projectPath": "/path/to/target/app",
  "applications": [
    {
      "code": "crm",                    // kebab-case — must equal the application-level nav entry
      "modules": ["contacts", "prospecting"],   // gate verifies each is covered
      "navigation": [
        { "level": "application", "code": "crm", "label": "CRM", "route": "/crm" },
        { "level": "module", "code": "contacts", "parentCode": "crm", "label": "Contacts", "route": "/crm/contacts" },
        { "level": "section", "code": "directory", "parentCode": "contacts", "label": "Directory", "route": "/crm/contacts/directory" }
      ],
      "roles": [
        { "code": "admin", "name": "Administrator", "applicationCode": "crm", "isDefault": false }
      ],
      "permissions": [
        { "path": "crm.contacts.directory.access", "action": "access", "sectionCode": "directory" },
        { "path": "crm.contacts.directory.read",   "action": "read",   "sectionCode": "directory" }
      ],
      "rolePermissions": [
        { "roleCode": "admin", "permissionPath": "crm.contacts.directory.access" }
      ],
      "testUsers": [{ "roleCode": "admin" }]
    },
    {
      "code": "budgeting",
      "modules": ["budget"],
      "navigation": [/* … */],
      "roles": [/* … */],
      "permissions": [/* … */],
      "rolePermissions": [/* … */],
      "testUsers": [/* … */]
    }
  ],
  "testUserPassword": "DevTest!2026Secure",
  "testUserEmailDomain": "smartstack.local",
  "emitDevCredentialsManifest": true
}
```

## Output structure (in the target app)

```
src/{AppCode}.Infrastructure/
├── DependencyInjection.cs              ← patched: one block per app
└── Persistence/Seeding/Applications/
    ├── {AppPascalA}/Core/
    │   ├── {AppPascalA}CoreNavigationSeedDataProvider.cs       (Order = 10)
    │   ├── {AppPascalA}CoreRolesSeedDataProvider.cs            (Order = 20)
    │   ├── {AppPascalA}CorePermissionsSeedDataProvider.cs      (Order = 30)
    │   ├── {AppPascalA}CoreRolePermissionsSeedDataProvider.cs  (Order = 40)
    │   └── {AppPascalA}CoreTestUsersSeedDataProvider.cs        (Order = 50, IsDevelopment-gated)
    └── {AppPascalB}/Core/
        └── (5 more, prefixed with {AppPascalB}…)
```

C# namespaces are scoped per app:
`{AppCode}.Infrastructure.Persistence.Seeding.Applications.{AppPascal}.Core`.

## Idempotence — safe to re-run

GUIDs are deterministic (`Guid.Parse` with stable hash keys derived from
`nav:{appCode}:{level}:{code}`), so re-running the CLI does NOT churn
database row IDs. The DI patch uses `<<< CORE-SEED-DI-{AppPascal} BEGIN/END >>>`
markers (one pair per application) — each block is replaced wholesale on
re-runs without touching surrounding registrations or other apps' blocks.

## Invocation summary

```bash
npx --prefer-offline tsx skills/development/backend/core-seed/cli/scaffold-core-seed/index.ts \
  --spec-file /tmp/core-seed-spec-<runId>.json \
  --outdir /path/to/target/app
```

Optional: `--dry-run` prints what would be generated without writing.
