# Agent — Apply migrations to the database

Read `_shared.md` first. This agent routes through the **colocated `cli/apply/`**
— NEVER a raw `dotnet ef database update` typed at the shell: the `ef-guard`
PreToolUse hook blocks it (exit 2), while the CLI runs `dotnet ef` via `execFile`
inside its tsx process, applies the 3-tier migration policy
(`cli/lib/migration-policy.ts`) and passes by design.

## Goal

Apply every pending migration through the policy-gated CLI. The verdict is the
authority: 🟢 local DB → applied autonomously; 🟡 (revert) → show the plan, ask
the user, re-run with `confirm`; 🔴 (remote / unknown DB) → refuse, a human
deploys.

A database is **provably local** when its host is loopback (`localhost`,
`127.0.0.1`, `(localdb)`, `.`), matches this machine's hostname
(`MYPC\SQLEXPRESS`), or is declared by the USER in `.gitflow/config.json` →
`efcore.localHosts`. Never write that declaration yourself — it is a human,
one-time decision. Azure SQL is never local.

## Steps

1. Dry-run first — classification + per-context pending count, zero DB writes:

   ```bash
   npx --prefer-offline tsx skills/efcore/cli/apply/index.ts \
     --spec '{"cwd":"<WORKTREE>","dryRun":true}'
   ```

   If every context reports `pending: 0`, stop — database is up to date.

2. Apply (spec **cwd-only** — do NOT pass `connectionString`, it only skews
   classification while the real update still reads appsettings):

   ```bash
   npx --prefer-offline tsx skills/efcore/cli/apply/index.ts \
     --spec '{"cwd":"<WORKTREE>"}'
   ```

   Parse the JSON envelope, not the exit code: `success:true` → applied;
   `blocked:true` → report the `policy.reason` to the user and stop;
   `needsConfirmation:true` → show the reason, wait for the user's yes, re-run
   with `"confirm": true`.

3. The CLI already handles the ordering concerns: it iterates every detected
   context (Core before Extensions matters — Extensions FK into Core) and
   injects `STUDIO_DESIGN_PROVIDER` per assembly for the Studio's
   multi-provider context. Target one context with `"contextName"` if needed.

4. Reverting (`"targetMigration": "<earlier>"` — `"0"` = revert all) is 🟡 even
   locally: always show the user what will be dropped before re-running with
   `confirm`.

## What NOT to do

- Never run a raw `dotnet ef database update` at the shell — blocked by
  `ef-guard`, and it would skip the policy gate.
- Never apply to a production / staging DB from this agent — the CLI's 🔴
  verdict on a non-local target is final here; a human deploys.
- Never pass `connectionString` to green-wash a verdict, and never add hosts to
  `efcore.localHosts` yourself.
