---
name: efcore
description: >
  EF Core migration workflow — create, squash, rebase-snapshot, apply, list,
  status. Applies the best practices of the global /efcore agents without
  MCP or external dependencies. Works against a single-assembly generated
  app (CoreDbContext + ExtensionsDbContext) or the multi-assembly Studio
  (StudioDbContext with Sqlite / Postgres / SqlServer migration projects).
phase: infrastructure
cli: cli/
allowed-tools: [Read, Glob, Grep, Bash]  # Bash: CLI invocation
---

# efcore — EF Core migration workflow

This skill manages EF Core migrations for any .NET project in a worktree:
SmartStack-generated apps (dual context: Core + Extensions) or the Studio
itself (single context, three provider assemblies).

All intelligence lives in this skill + the agent prompts under `agents/`.
The colocated CLIs in `cli/` are pure executors — they never decide what to
do, they only do what the spec tells them.

## Routing table

| User intent | Agent prompt | CLI |
|-------------|--------------|-----|
| "Show me the migrations" | `agents/list.md` | `cli/list/` |
| "Is anything pending?" | `agents/status.md` | `cli/status/` |
| "Create a new migration" | `agents/create.md` | `cli/create/` |
| "Squash branch migrations before merge" | `agents/squash.md` | `cli/squash/` |
| "Fix snapshot after a rebase" | `agents/rebase-snapshot.md` | `cli/rebase-snapshot/` |
| "Apply migrations to the local DB" | `agents/db-update.md` | — (runs `dotnet ef database update`) |
| "Drop + recreate the local DB (destructive)" | `agents/recreate-db.md` | — (runs `database drop --force` then `database update`) |

**Always read `_shared.md` before executing any operation.** It documents
WSL git preflight, the `dotnet-ef` PATH trick on Git Bash, the naming
convention, and the dual-DbContext detection heuristic.

## Invariants

- Never create migrations manually named — always let `cli/create/` compute
  the name. The convention is `{contextPrefix}_v{version}_{seq3}_{PascalCaseDescription}`.
- Never edit `ModelSnapshot.cs` by hand — it is regenerated by EF.
- Never run `squash` or `rebase-snapshot` on `main` / `master` — the CLI
  refuses.
- Squash always creates a backup under `.efcore-squash-backup/<timestamp>/`
  before deleting anything. If something goes wrong, the backup is the
  source of truth.
- For multi-provider contexts (the Studio itself), `create` adds the same
  migration to every provider assembly by default. Pass `assembly` to
  target only one.

## Invocation contract for agents

When Claude decides which agent to use, it invokes the corresponding CLI
with a single `--spec '<JSON>'` argument. Every CLI returns a single JSON
object on stdout. Exit code 0 = success, 1 = failure (inspect `error` or
`errors[]`).

Example — create a migration on the current worktree:

```
npx tsx skills/efcore/cli/create/index.ts --spec '{
  "cwd": "D:/projets/app/features/ba-002",
  "contextName": "CoreDbContext",
  "description": "AddEmployeeAvatar"
}'
```

## Consumer: DevRunner Migrations tab

The Studio's DevRunner page (see `src/frontend/components/dev-runner/MigrationsTab.tsx`)
exposes these operations through buttons. Deterministic reads (list,
status, detect-contexts) go through the IPC router `/migrations/*`; any
operation that mutates the worktree (create, squash, rebase-snapshot,
db-update) is invoked via `claude -p @skills/efcore/SKILL.md ...` so that
the agent prompts drive Claude. This keeps all business logic in Markdown
and away from TypeScript.

## When something goes wrong

- **dotnet-ef not on PATH** — follow `_shared.md` "dotnet-ef PATH setup".
- **Snapshot conflicts after rebase** — use `rebase-snapshot`.
- **Squash produced a build error** — copy files back from the backup
  under `.efcore-squash-backup/<timestamp>/` and investigate.
- **Multiple contexts detected but operation only matches one** — pass the
  `contextName` and optionally `assembly` to disambiguate.
