# 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: 🟢 → applied autonomously; 🟡 → show the plan, ask the user, re-run
with `confirm`; 🔴 → refuse — there is NO token; the envelope's `remediation`
block names the structural exit, report it to the user and stop.

The policy grades by **PROVENANCE before topology** — WHICH configuration layer
named the database (`configSource` in the envelope), mirroring the app's own
precedence (environment > `appsettings.Local.json` > `appsettings.Development.json`
> `appsettings.json`):

| `configSource.kind` | Host | Branch | Verdict |
|---|---|---|---|
| any | loopback (`localhost`, `127.0.0.1`, `(localdb)`, `.`), this machine's hostname (`MYPC\SQLEXPRESS`), or a host the USER declared in `.gitflow/config.json` → `efcore.localHosts` | any | 🟢 |
| `local-override` — `appsettings.Local.json` (gitignored, seeded per branch by `ss dev configure`: the developer's OWN working database) | any other (LAN, `10.x`, Azure SQL) | `feature/*` | 🟢 — journaled |
| `local-override` | any other | `develop` / `release/*` / `hotfix/*` / unknown | 🟡 `confirm` — an integration DB |
| `local-override` | any other | `main` | 🔴 |
| `environment` — `ConnectionStrings__DefaultConnection` in the shell (a deployment context; `dotnet ef` inherits it) | — | any | 🔴 |
| `committed` (`appsettings.json` / `.Development.json`) or `spec` | non-local | any | 🔴 |
| `none` | — | any | 🔴 |

`revert` (a down-migration) is 🟡 on the two 🟢 lanes and 🔴 elsewhere.

## Steps

1. Dry-run first — it runs BEFORE the policy gate, so it measures on EVERY
   tier: `configSource`, per-context pending count, `wouldApply`, and the
   `remediation` block when the verdict is not 🟢. Read-only for the database
   (it still builds the project and opens a read connection for
   `dotnet ef migrations list`); a failed list is `success: false`, never
   `pending: 0`:

   ```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 targets what the app resolves):

   ```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 (a
   `historyLogPath` says the run was journaled); `blocked:true` → report
   `policy.reason` AND `remediation.message` to the user and stop —
   `remediation.confirmable` is `false`, no re-run will pass;
   `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`.

## Lifting a 🔴 — the user's decisions, never yours

`remediation.kind` says which one applies:

- `declare-working-db` — the string comes from a committed file (or the spec) and
  names a non-local host. If it is the user's working database, THE USER names it
  in `appsettings.Local.json` (`ss dev configure` seeds it per branch) and the
  run is re-launched from a `feature/*` branch. `remediation.localHosts` carries
  the exact `.gitflow/config.json` fragment for the rarer one-time declaration.
- `unset-environment-connection` — `ConnectionStrings__DefaultConnection` is set
  in this shell: a deployment setting. The user unsets it (or runs from a clean
  shell); `dotnet ef` would have targeted it.
- `configure-connection` — nothing resolves: `ss dev configure`.
- `deploy-app` — Azure SQL named by a committed file: a deployed environment is
  migrated by deploying the app (it applies pending migrations at startup, in
  every environment).
- `protected-branch` — `main`: switch to the owning feature branch.

## 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 edit `appsettings.Local.json`, never set
  `ConnectionStrings__DefaultConnection`, never add hosts to
  `efcore.localHosts`, and never pass `connectionString` to green-wash a
  verdict. Naming a working database is a USER decision — you report the
  `remediation`, you do not perform it.
- Never treat a 🔴 as "ask for a token": there is none. Report and stop.
