---
name:        migration-strategy
description: "Reads AUDIT.md, selects a migration strategy from a decision table, and produces a committed MIGRATION-PLAN.md that all Phase 3 and 4 skills execute against."
metadata:
  phase:           2
  source_stack:    "Any — stack-agnostic; all inputs come from AUDIT.md"
  target_stack:    "Any — defined by the engineer before this skill runs"
  effort_estimate: M
  last_updated:    2026-04-05
---

# 1. Purpose

This skill selects the safest migration strategy for a given codebase by reading the four
signals already recorded in `AUDIT.md` — module coupling (fan-out), test coverage, consumer
count, and LOC — and running them through a decision table that produces one of three concrete
strategies: Big Bang, Strangler Fig, or Parallel Run. The output is `MIGRATION-PLAN.md`,
a committed document that defines the strategy, the ordered sequence of Phase 3 and 4 skill
invocations, the skill dependency graph, a risk register with mitigations, a rollback decision
tree naming specific roles, and the go/no-go criteria that gate Phase 6 cut-over. This skill
runs after `codebase-audit` (Phase 1) and before any Phase 3 preparation or Phase 4 migration
work begins — every downstream skill reads `MIGRATION-PLAN.md` to understand its execution
context. The strategy is not chosen by intuition or preference; it is derived mechanically from
audit data, and the derivation is recorded in the plan so any engineer can audit the reasoning.

---

# 2. Trigger Conditions

**Use when:**
- `AUDIT.md` exists at the repo root, all seven numbered sections are populated, and no section contains a `<!-- TODO -->` placeholder — run `grep -c '<!-- TODO' AUDIT.md` and confirm the result is `0`.
- The `## Sign-off` section in `AUDIT.md` has Status `APPROVED` and at least one row with a filled Date field — the audit findings have been reviewed by a human before strategy decisions are made.
- At least one engineer has read `AUDIT.md §3 Coupling Map` and can confirm the fan-in/fan-out scores are accurate for the modules being migrated — strategy selection depends on these numbers being real, not auto-generated defaults.
- The target stack has been named — the engineer can answer "We are migrating to X version Y" before this skill runs. The skill does not select the target stack; it accepts it as an input.

**Do NOT use when:**
- `AUDIT.md` is absent, incomplete (missing sections), or its Sign-off Status is `PENDING` — run `skills/01-audit/codebase-audit/` first and get sign-off before planning.
- `AUDIT.md` is more than 90 days old — the coupling map, coverage data, and CVE list may no longer reflect the codebase. Re-run `codebase-audit` and get a fresh sign-off.
- `AUDIT.md §6.3` CVE Risk Verdict is `Red` (critical CVEs with a fix available) and no remediation PR has been merged — migrating on top of known critical CVEs moves them to the new stack without fixing them. Resolve CVEs first.
- `MIGRATION-PLAN.md` already exists at the repo root for this migration goal — update the plan in place rather than regenerating from scratch, to preserve the sign-off history. If the scope has changed substantially, archive the old plan to `output/MIGRATION-PLAN-archived-<timestamp>.md` before running this skill.
- Any Phase 3 or Phase 4 work has already begun — strategy must be settled before preparation starts. If Phase 4 is already in flight, this skill will produce a plan that contradicts work already done. Stop Phase 4, assess the current state against the decision table, and reconcile.

---

# 3. Inputs

**Required:**

| Input | Type | Description |
|-------|------|-------------|
| `audit_file` | file-path | Path to `AUDIT.md`. Almost always `AUDIT.md` (repo root). Must exist and pass the 7-section check before this skill reads it. |
| `migration_goal` | string | One sentence stating what the migration achieves and why. This becomes the opening sentence of `MIGRATION-PLAN.md`. Example: `"Replace Express 4.x with Fastify 4.x to reduce p99 latency and enable typed request schemas."` |
| `target_stack` | string | The technology stack being migrated TO, with version numbers. Example: `"Fastify 4.x, @fastify/session, TypeScript 5.x strict"`. |
| `primary_engineer` | string | Name or handle of the engineer owning this migration. This person is named in the rollback decision tree as the first escalation contact. |
| `stakeholders` | string | Comma-separated names or handles who must sign `MIGRATION-PLAN.md § Sign-off` before Phase 3 begins. May overlap with `AUDIT.md` sign-off signatories. |

**Optional:**

| Input | Type | Default | Description |
|-------|------|---------|-------------|
| `scope` | string | All modules flagged in `AUDIT.md §4.2` (coverage gaps) and §3 (coupling hot spots) | Comma-separated list of `src/` paths or module names to include in the migration scope. Override when the migration is intentionally scoped to a subset of what the audit covers. |
| `hard_constraints` | string | none | Newline-separated list of constraints that override strategy selection. Example: `"Auth routes must not change before 2026-06-01 due to SOC2 audit."` Each constraint is recorded in `MIGRATION-PLAN.md §6` and may force a more conservative strategy than the decision table recommends. |
| `risk_appetite` | enum(`low`, `medium`, `high`) | `medium` | Shifts the decision table's thresholds. `low`: treat Medium fan-out as High, reducing the likelihood of Big Bang selection. `high`: treat Medium fan-out as Low, allowing Strangler Fig where Parallel Run would otherwise be selected. Document the setting and reason in the plan. |
| `output_plan_file` | file-path | `MIGRATION-PLAN.md` | Where to write the plan. Override only when managing multiple concurrent migration plans in one repo. |

<!--
  STOP CONDITIONS:
  - If `audit_file` does not exist or `grep -c '<!-- TODO' <audit_file>` returns >0, halt:
    "AUDIT.md is missing or has unpopulated sections. Run codebase-audit first."
  - If AUDIT.md § Sign-off Status is not APPROVED, halt:
    "AUDIT.md has not been signed off (Status: PENDING). Get stakeholder sign-off before planning."
  - If `migration_goal` is not provided, halt and ask:
    "What is the migration goal? Describe it in one sentence: what changes and why."
  - If `target_stack` is not provided, halt and ask:
    "What is the target stack, including version numbers?"
-->

---

# 4. Steps

1. Read `audit_file` in full. Extract and record the four strategy-selection signals for each module in `scope`. Write them to `output/migration-strategy-signals-<timestamp>.json`:

   ```json
   {
     "extracted_at": "<ISO-8601>",
     "audit_file": "<audit_file>",
     "modules": [
       {
         "path": "<module path>",
         "fan_out": "<Low | Medium | High>",
         "fan_out_count": "<integer from AUDIT.md §3.1>",
         "coverage_pct": "<integer from AUDIT.md §4.1>",
         "consumer_count": "<fan-in count from AUDIT.md §3.2 — internal importers>",
         "loc": "<estimated LOC>",
         "source_section": "<AUDIT.md section cited>"
       }
     ],
     "cve_verdict": "<Green | Yellow | Red from AUDIT.md §6.3>",
     "performance_baselines": {
       "coverage_pct": "<overall from AUDIT.md §4.1>",
       "bundle_size_gzip_kb": "<from AUDIT.md §5.1 or null>",
       "startup_ms": "<from AUDIT.md §5.2 or null>",
       "p99_latency_ms": "<from AUDIT.md §5.3 or null>",
       "cve_count_critical": "<from AUDIT.md §6.1>",
       "cve_count_high": "<from AUDIT.md §6.1>"
     }
   }
   ```

   **Signal classification rules** — apply to each module:
   - `fan_out`: Low = fan-out count ≤ 3; Medium = 4–8; High = ≥ 9. Source: `AUDIT.md §3.1`.
   - `coverage_pct`: read directly from `AUDIT.md §4.1` for the module's row. If absent, treat as 0% and flag as `[UNCERTAIN — not in audit coverage table]`.
   - `consumer_count`: fan-in count for the module from `AUDIT.md §3.2`. Counts internal importers only; external API consumers (HTTP clients, SDK users) are listed separately in `AUDIT.md §7.4` and added on top.
   - `loc`: estimate from the coupling report in `output/codebase-audit-coupling-*.json` if available; otherwise run `wc -l <module path>/**/*.{ts,js,py,rb,go}` and record the result.

   If any signal cannot be extracted from `AUDIT.md` (missing row, section not populated, or marked `[UNCERTAIN]`), record the signal as `null` and treat the module as requiring **Parallel Run** — missing data is a risk, not a free pass to a lighter strategy.

2. Apply the strategy decision table to each module. Derive the recommended strategy for the overall migration scope by taking the most conservative recommendation across all modules.

   | Fan-out | Coverage | Consumer count | LOC | Recommended strategy |
   |---------|----------|----------------|-----|----------------------|
   | Low | ≥ 70% | ≤ 3 | Any | **Big Bang** |
   | Medium | ≥ 70% | Any | < 5,000 | **Strangler Fig** |
   | High | Any | Any | Any | **Strangler Fig** |
   | Any | < 70% | Any | Any | **Parallel Run** |
   | Any | Any | > 10 | Any | **Parallel Run** |
   | Any signal = null | — | — | — | **Parallel Run** |

   **Tie-breaking rule:** when multiple rows match, select the row that appears latest in the table (most conservative). A module that matches both "Medium / ≥70% / Any / <5000 → Strangler Fig" and "Any / <70% / Any / Any → Parallel Run" gets Parallel Run.

   **`risk_appetite` adjustment:** if `risk_appetite` is `low`, promote Medium fan-out to High before applying the table. If `high`, demote Medium fan-out to Low. Log the adjustment and reason.

   **`hard_constraints` override:** if any constraint explicitly restricts a module (e.g., "auth routes must not change before Q3"), override the table result for that module to **Parallel Run** regardless of signals, and record the constraint reference.

   Record each module's derivation — the specific row that matched and the resulting strategy — in `output/migration-strategy-signals-<timestamp>.json` under `"strategy_derivation"`.

3. Determine the **overall migration strategy** as the most conservative strategy across all modules in scope:
   - If any module requires Parallel Run → overall is Parallel Run.
   - If no module requires Parallel Run but any requires Strangler Fig → overall is Strangler Fig.
   - If all modules permit Big Bang → overall is Big Bang.

   Write the strategy selection and derivation to the migration log.

4. Write `output_plan_file` (`MIGRATION-PLAN.md`) using the template below. Replace every `<!-- placeholder -->` with real content derived from `audit_file`, the signals JSON, and the strategy derivation. Do not leave any section as "TBD" or copy placeholder text verbatim.

   `````markdown
   # Migration Plan — <!-- project name from AUDIT.md header -->

   > Produced by `skills/02-strategize/migration-strategy` · agent-revamp-skills
   > Based on: `AUDIT.md` (audit date: <!-- ISO-8601 date from AUDIT.md header table -->)
   > Skill version: 0.1.0

   | Field | Value |
   |-------|-------|
   | **Migration goal** | <!-- migration_goal input --> |
   | **Source stack** | <!-- detected from AUDIT.md §1 or user-provided --> |
   | **Target stack** | <!-- target_stack input --> |
   | **Selected strategy** | <!-- Big Bang \| Strangler Fig \| Parallel Run --> |
   | **Primary engineer** | <!-- primary_engineer input --> |
   | **Plan date** | <!-- ISO-8601 --> |
   | **Overall risk level** | <!-- Critical \| High \| Medium \| Low — highest severity in §4 Risk Register --> |
   | **Sign-off status** | PENDING |

   ---

   ## 1. Strategy Selection

   ### 1.1 Audit Signals

   <!-- One row per module in scope. Source every value with the AUDIT.md section it came from. -->

   | Module | Fan-out | Fan-out Count | Coverage % | Consumer Count | LOC | Signal Source |
   |--------|---------|--------------|------------|----------------|-----|---------------|
   | <!-- path --> | Low \| Medium \| High | <!-- N --> | <!-- N% --> | <!-- N --> | <!-- N --> | AUDIT.md §3.1, §4.1 |

   **Performance baselines** (reference point for Phase 5 equivalence gates):

   | Metric | Baseline | Source | Date |
   |--------|---------|--------|------|
   | Line coverage (overall) | <!-- N% --> | AUDIT.md §4.1 | <!-- date --> |
   | Bundle size (gzip) | <!-- N KB or N/A --> | AUDIT.md §5.1 | <!-- date --> |
   | Startup time (median cold start) | <!-- N ms or N/A --> | AUDIT.md §5.2 | <!-- date --> |
   | p99 latency (highest-traffic route) | <!-- N ms or N/A --> | AUDIT.md §5.3 | <!-- date --> |
   | Critical CVE count | <!-- N --> | AUDIT.md §6.1 | <!-- date --> |
   | High CVE count | <!-- N --> | AUDIT.md §6.1 | <!-- date --> |

   ### 1.2 Decision Table Application

   <!-- For each module, show which table row matched and the resulting strategy. -->

   | Module | Matching Row | Resulting Strategy | Override Applied? |
   |--------|-------------|-------------------|------------------|
   | <!-- path --> | <!-- e.g., "High fan-out → Strangler Fig" --> | <!-- strategy --> | None \| <!-- constraint text --> |

   ### 1.3 Selected Strategy: <!-- Big Bang | Strangler Fig | Parallel Run -->

   <!-- 1 paragraph. State the strategy, cite the signals that drove it (fan-out counts,
        coverage %, consumer counts), and explain why this is the safest choice given the
        audit findings. Reference specific AUDIT.md sections. Do not use vague justifications
        like "seems appropriate" — every claim must point to a number. -->

   #### What this strategy means concretely

   <!-- Fill in the block for the selected strategy. Delete the other two. -->

   **Big Bang:** The entire migration unit is replaced in a single release. No coexistence
   period. The old stack is removed the same day the new stack is deployed. This is safe
   only when: fan-out is Low, coverage is ≥ 70%, and consumer count is ≤ 3 — meaning the
   blast radius is small and detectable in tests. Pre-conditions: all Phase 3 preparation
   complete, all Phase 5 equivalence tests passing in staging. Rollback is a git revert
   plus one deploy; the window is measured in minutes.

   **Strangler Fig:** The new stack is built alongside the old. Traffic is shifted
   incrementally by path prefix, feature flag, or percentage rollout. Both stacks coexist
   for the duration of Phase 4 (days to weeks). Each migrated route or module is validated
   independently before the next is migrated. The old stack is removed only when all routes
   have migrated and Phase 5 shadow mode shows 0.0% divergence over 24 hours. Rollback per
   route is a proxy config change; full rollback is a feature flag flip.

   **Parallel Run:** Both stacks process the same production inputs simultaneously. The old
   stack's output is served to users; the new stack's output is logged and compared
   asynchronously. No user-facing traffic cut-over occurs until divergence rate reaches
   exactly 0.0% over a sustained window (minimum 24 hours). This strategy is mandatory when
   coverage is below 70% — the test suite cannot detect all regressions, so production
   traffic comparison is the only reliable equivalence signal. Rollback at any time is
   stopping the new stack's parallel processing; users are never exposed to new-stack output
   during this phase.

   ---

   ## 2. Phase Sequence

   <!-- The ordered list of skill invocations to execute this migration.
        Rules:
        - Phase 3 skills always precede Phase 4 skills for the same module.
        - test-coverage-baseline must precede any Phase 4 skill for a module with <70% coverage.
        - behavioral-equivalence (Phase 5) must follow every Phase 4 skill before Phase 6 begins.
        - Skills within the same phase may be parallelized only if they have no dependency edge in §3.
        - For Parallel Run: add a "parallel-run-setup" step in Phase 3 to configure traffic mirroring. -->

   | Order | Phase | Skill | Target Module / Unit | Depends On (order #) | Parallelizable With |
   |-------|-------|-------|----------------------|----------------------|---------------------|
   | 1 | 3 | `test-coverage-baseline` | <!-- module --> | — | — |
   | 2 | 3 | <!-- e.g., feature-flag-setup --> | <!-- target --> | 1 | — |
   | 3 | 4 | <!-- e.g., js-to-typescript --> | <!-- module --> | 1, 2 | <!-- order # or "none" --> |
   | 4 | 5 | `behavioral-equivalence` | <!-- module --> | 3 | — |

   ---

   ## 3. Skill Dependency Graph

   <!-- Adjacency list: each skill invocation (by order number from §2) and the order numbers
        it must wait for. This is the machine-readable form of the sequence table.
        The graph must be acyclic — if you find a cycle, the sequencing in §2 is wrong. -->

   ```
   1 →  (no dependencies)
   2 →  1
   3 →  1, 2
   4 →  3
   ```

   **Cycle check:** <!-- State explicitly: "No cycles detected" or list the cycle and explain
   how to break it. A plan with a cycle cannot be executed. -->

   ---

   ## 4. Risk Register

   <!-- Minimum 3 entries, ordered by severity descending (Critical → High → Medium → Low).
        Likelihood × Impact scoring: both on a 1–3 scale (1=Low, 2=Medium, 3=High).
        Score = Likelihood × Impact. Score ≥ 6 with no mitigation is a halt condition —
        return to Phase 1 for more information.
        Every row must cite the AUDIT.md section that surfaces the risk. -->

   | # | Risk | Source | Likelihood (1–3) | Impact (1–3) | Score | Mitigation | Residual Score |
   |---|------|--------|-----------------|-------------|-------|------------|----------------|
   | 1 | <!-- description --> | AUDIT.md §<!-- N --> | <!-- 1–3 --> | <!-- 1–3 --> | <!-- L×I --> | <!-- specific action --> | <!-- score after mitigation --> |
   | 2 | <!-- description --> | AUDIT.md §<!-- N --> | <!-- 1–3 --> | <!-- 1–3 --> | <!-- L×I --> | <!-- specific action --> | <!-- score after mitigation --> |
   | 3 | <!-- description --> | AUDIT.md §<!-- N --> | <!-- 1–3 --> | <!-- 1–3 --> | <!-- L×I --> | <!-- specific action --> | <!-- score after mitigation --> |

   **Halt check:** <!-- State explicitly: "No risk has a score ≥ 6 with no mitigation" or
   list which risks triggered the halt condition and what information would resolve them. -->

   ---

   ## 5. Rollback Decision Tree

   <!-- Rollback decisions must name specific roles, not "the team" or "someone".
        Each node is a condition; each leaf is an action with a named actor. -->

   ```
   Is the migration in progress?
   ├── No → No rollback needed.
   └── Yes
       ├── Is this a Big Bang migration?
       │   ├── Yes → Is the new stack returning 5xx for >1% of requests?
       │   │         ├── Yes → <!-- primary_engineer --> executes git revert + deploy within 15 min.
       │   │         │         Notify: <!-- stakeholders -->
       │   │         └── No  → Continue monitoring. Re-evaluate at next 15-min checkpoint.
       │   └── No (Strangler Fig or Parallel Run)
       │       ├── Is shadow mode divergence rate >0.1% over any 1-hour window?
       │       │   ├── Yes → <!-- primary_engineer --> sets feature flag to old stack within 5 min.
       │       │   │         File incident. Notify: <!-- stakeholders -->
       │       │   │         Do not re-enable new stack until divergence root cause is identified.
       │       │   └── No  → Continue. Re-evaluate at next 15-min checkpoint.
       │       └── Has any migrated route returned 5xx in staging that was 200 in old stack?
       │           ├── Yes → <!-- primary_engineer --> reverts the proxy config for that route only.
       │           │         Does not roll back other routes. Files a regression report.
       │           └── No  → Continue.
   ```

   **Rollback authority:**
   - **Route-level rollback** (Strangler Fig / Parallel Run): `<!-- primary_engineer -->` can execute without approval. Must notify `<!-- stakeholders -->` within 30 minutes.
   - **Full rollback** (all routes / Big Bang revert): requires sign-off from one stakeholder in `<!-- stakeholders -->` unless the incident is P0 (complete outage), in which case `<!-- primary_engineer -->` acts immediately and notifies within 15 minutes.
   - **Rollback window:** tracked from the moment Phase 4 begins for each unit. The window closes when Phase 5 `behavioral-equivalence` returns `PASS` and Phase 6 cut-over is complete.

   ---

   ## 6. Hard Constraints Applied

   <!-- Explicit record of how hard_constraints changed the strategy or sequencing.
        If none were provided, write "None." -->

   | Constraint | Effect on Strategy or Sequencing |
   |-----------|----------------------------------|
   | <!-- constraint text --> | <!-- effect --> |

   ---

   ## 7. Go / No-Go Criteria for Phase 6 Cut-over

   <!-- Binary checklist. Every item must be objectively verifiable.
        Phase 6 (traffic-cutover) does not begin until all items are checked. -->

   - [ ] `behavioral-equivalence` VALIDATION-REPORT.md verdict is `PASS` for all migrated units — grep `VALIDATION-REPORT.md` for `PASS`.
   - [ ] Shadow mode ran ≥ 24 continuous hours with 0.0% divergence rate — read shadow log timestamps and compute rate.
   - [ ] All golden fixtures in `tests/golden/` are committed and passing — `npx jest tests/golden/` exits 0.
   - [ ] p99 latency for all migrated routes is ≤ baseline recorded in §1.1 + 5ms tolerance — read APM or k6 output.
   - [ ] No CRITICAL CVEs introduced by new dependencies — `npm audit --audit-level=critical` exits 0 (or language equivalent).
   - [ ] <!-- Add migration-specific go/no-go criterion from AUDIT.md risk register § -->
   - [ ] <!-- Add migration-specific go/no-go criterion -->
   - [ ] All stakeholders in § Sign-off below have confirmed readiness for production cut-over.

   ---

   ## Sign-off

   > This section must be completed before Phase 3 begins.
   > No preparation or migration work may start until Status is APPROVED.

   | Role | Name | Date | Notes |
   |------|------|------|-------|
   | Primary engineer | <!-- primary_engineer --> | | |
   | <!-- stakeholder role --> | <!-- name --> | | |
   | <!-- stakeholder role --> | <!-- name --> | | |

   **Status:** PENDING

   > To approve: fill in your Date and Notes above, then change Status to APPROVED.
   `````

5. Run a halt check on the risk register. Read the completed risk table in `MIGRATION-PLAN.md §4`. For any row where Score ≥ 6 AND the Mitigation column contains only "TBD", "none", or is blank:
   - Do not commit `MIGRATION-PLAN.md`.
   - Write a blocking finding to the migration log: `"Risk #N (score <N>) has no mitigation. Return to Phase 1 to gather: <specific information that would lower likelihood or impact>."` The specific information must name a concrete artifact (e.g., "test coverage report for `src/auth/`", "load test results for `/api/payments`") not a vague action ("investigate further").
   - STOP. Do not proceed to Step 6.

6. Commit `MIGRATION-PLAN.md` to the repo root. The commit message must be: `plan: establish migration strategy for <migration_goal> (Phase 2)`. This commit is a distinct, standalone point in git history — do not squash it with Phase 3 preparation work, because it is the reference point for "what was decided before we started."

7. Present `MIGRATION-PLAN.md` to `primary_engineer` and `stakeholders` for review. Ask each stakeholder to fill in their Date and Notes in `§ Sign-off` and change Status from `PENDING` to `APPROVED`. Do not begin Phase 3 until Status is `APPROVED` and every stakeholder row has a Date.
   - If a stakeholder rejects the plan (declines to sign, or adds a Note indicating disagreement): record the rejection reason in the migration log, archive the current plan to `output/MIGRATION-PLAN-rejected-<timestamp>.md`, and STOP with: "Plan rejected by <name>: <reason>. Address the feedback and re-run this skill."

8. Write all outputs declared in Section 7. Run every Equivalence Test in Section 6 and record results in `output/migration-strategy-equiv-<timestamp>.md`. Evaluate every item in Section 9 Done Criteria; report pass/fail inline, then print the final verdict.

---

# 5. Agent Handoffs

## migration-planner

- **File:** `agents/migration-planner.md`
- **Triggered by:** Step 4 (writing the phase sequence and dependency graph)
- **Prompt template:**
  ```
  GOAL:              <migration_goal>
  AUDIT_FILE:        <audit_file>
  REPO_ROOT:         <repo_root>
  CONSTRAINTS:       <hard_constraints — one per line, or "None">
  EXCLUDE_UNITS:     <any modules explicitly out of scope, or "None">
  MANIFEST_FORMAT:   json
  OUTPUT_PLAN_FILE:  <output_plan_file>
  OUTPUT_MANIFEST_FILE: output/migration-manifest-<timestamp>.json
  ```
  The planner populates §2 Phase Sequence and §3 Skill Dependency Graph in `MIGRATION-PLAN.md`.
  The skill writes the strategy selection (§1) and risk register (§4) itself — do not delegate
  those to the planner. Only delegate the sequencing and dependency ordering.

---

# 6. Equivalence Tests

<!--
  This skill produces a plan and baseline record — no code changes occur.
  Section 6 verifies that the plan artifact is complete, internally consistent,
  and committed; and that the baseline metrics from AUDIT.md are faithfully recorded.
  These become the reference numbers Phase 5 compares against.
-->

| Test Name | Input | Expected Output | Tool |
|-----------|-------|-----------------|------|
| `plan-file-exists` | `ls <output_plan_file>` | File exists and is >2 KB — a file smaller than 2 KB is the skeleton template, not a completed plan. | Bash: `wc -c <output_plan_file>` returns a value >2048. |
| `no-placeholders-remain` | `grep -c '<!-- placeholder\|<!-- Fill in\|TBD\|<!-- path -->' <output_plan_file>` | Returns 0 — no unfilled placeholder text remains in the committed plan. | Bash: count must be 0. |
| `strategy-cited` | Read `MIGRATION-PLAN.md §1.3` | Section names the strategy (Big Bang, Strangler Fig, or Parallel Run), cites at least one fan-out count, one coverage %, and one AUDIT.md section reference (e.g., `AUDIT.md §3.1`). | Read — grep for `AUDIT.md §` in §1.3; count must be ≥ 1. |
| `decision-table-recorded` | Read `MIGRATION-PLAN.md §1.2` | Table has one row per module in scope, each row names the matching decision-table row and the resulting strategy. No row says "intuition" or omits a matching row. | Read |
| `risk-register-≥3-rows` | Read `MIGRATION-PLAN.md §4` risk table | Table has ≥ 3 data rows. Every row has a non-empty Mitigation column. No row has Score ≥ 6 with Mitigation = "TBD" or blank. | Read + Bash: `grep -c '| [0-9]' MIGRATION-PLAN.md` ≥ 3 in the risk table block. |
| `dependency-graph-acyclic` | Read `MIGRATION-PLAN.md §3` adjacency list | No order number appears in its own dependency list, directly or transitively. A cycle would mean a skill waits for itself. | Read — manually verify or run topological sort: `python3 -c "import sys; ..."` against the adjacency list. |
| `rollback-names-role` | Read `MIGRATION-PLAN.md §5` | Every leaf action in the rollback decision tree names a specific person or role (e.g., `@alice`, `primary_engineer`, `on-call engineer`). No leaf says "the team", "someone", or "engineering". | Read — grep for `the team\|someone\|engineering` in §5; count must be 0. |
| `baselines-recorded` | Read `MIGRATION-PLAN.md §1.1` performance baselines table | Coverage %, bundle size, startup time, p99 latency, and CVE counts are all populated (or explicitly marked `N/A` with a reason — not left blank). | Read |
| `plan-committed` | `git log --oneline -1 -- <output_plan_file>` | The plan appears in git history with a commit message matching `plan: establish migration strategy`. | Bash |

---

# 7. Outputs

| Artifact | Path Pattern | Format | Description |
|----------|-------------|--------|-------------|
| Migration plan | `<output_plan_file>` (default: `MIGRATION-PLAN.md` at repo root) | markdown | Primary deliverable. Committed to repo root so it is visible on the project landing page. Human-readable, stakeholder-facing. Read by all Phase 3 and 4 skills at session startup via CLAUDE.md Session Startup rules. |
| Migration manifest | `output/migration-manifest-<timestamp>.json` | json | Machine-readable manifest produced by the migration-planner agent. Consumed by `/migrate` to route to the correct Phase 4 skill for each unit. |
| Strategy signals | `output/migration-strategy-signals-<timestamp>.json` | json | The four extracted signals per module and their decision-table derivation. Evidence that strategy selection was data-driven, not intuition-driven. |
| Migration log | `output/migration-strategy-log-<timestamp>.md` | markdown | Halt check results, any rejected plans with rejection reasons, `risk_appetite` adjustments, `hard_constraints` overrides, confidence level, and numbered assumptions list. |
| Equivalence test results | `output/migration-strategy-equiv-<timestamp>.md` | markdown | Pass/fail verdict for every row in Section 6. Required by Done Criteria. |

---

# 8. References

- `skills/01-audit/codebase-audit/SKILL.md` — produces `AUDIT.md`, the mandatory input for this skill; must be complete and signed off before running migration-strategy.
- `skills/03-prepare/test-coverage-baseline/SKILL.md` — the first Phase 3 skill scheduled for any module where signals show coverage < 70%; referenced in §2 Phase Sequence.
- `skills/04-migrate/frontend/cra-to-vite/SKILL.md` — example Phase 4 consumer of `MIGRATION-PLAN.md`; reads the plan at session startup via CLAUDE.md Session Startup rules.
- `skills/04-migrate/backend/express-to-fastify/SKILL.md` — same; reads `MIGRATION-PLAN.md` for rollback decision authority and strategy coexistence details.
- `skills/05-validate/behavioral-equivalence/SKILL.md` — the go/no-go criteria in §7 are the same gates this skill enforces; the two documents must agree.
- `agents/migration-planner.md` — invoked in Step 4 to produce the phase sequence and dependency graph; its `MIGRATION-PLAN.md` sections are incorporated into the plan written by this skill.
- `references/strangler-fig-pattern.md` — the coexistence mechanics for the Strangler Fig strategy (dual-running, proxy routing, feature-flag shifting) are documented here; cross-reference when writing the "what this strategy means concretely" paragraph in §1.3.
- `references/migration-anti-patterns.md` — "The Big Bang Rewrite" (§1) explains exactly why the decision table requires Low fan-out, ≥70% coverage, and ≤3 consumers before allowing Big Bang; cite this in the plan's §1.3 rationale when Big Bang is selected, so future engineers understand the guard rails.
- `references/stack-compatibility-matrix.md` — the Skill Composability section identifies which Phase 4 skills must not be combined in the same sprint; the phase sequence in §2 must respect these constraints.

---

# 9. Done Criteria

<!--
  Claude evaluates each item and reports pass/fail before declaring this skill complete.
  Any unchecked item means Phase 3 may NOT begin.
  This is the gate between understanding the problem (Phase 1) and acting on it (Phase 3+).
-->

- [ ] AUDIT.md signals (fan-out, coverage %, consumer count, LOC) extracted for every module in scope and recorded in `output/migration-strategy-signals-<timestamp>.json` with a source citation for each value — `strategy-cited` equivalence test recorded as pass.
- [ ] Strategy selected using the decision table with the derivation recorded per-module — `decision-table-recorded` equivalence test recorded as pass. No module's strategy is described as "seemed right" or selected without a matching table row.
- [ ] `MIGRATION-PLAN.md` committed to `<output_plan_file>` in a standalone git commit — `plan-committed` equivalence test recorded as pass. The commit is not squashed with Phase 3 work.
- [ ] `MIGRATION-PLAN.md` contains no unfilled placeholder text — `no-placeholders-remain` equivalence test recorded as pass.
- [ ] Skill dependency graph in `MIGRATION-PLAN.md §3` is acyclic — `dependency-graph-acyclic` equivalence test recorded as pass. A cyclic graph means the sequencing is logically impossible to execute.
- [ ] Risk register has ≥ 3 entries, each with a non-empty Mitigation column and a Residual Score — `risk-register-≥3-rows` equivalence test recorded as pass. No row has Score ≥ 6 with no mitigation (that is the halt condition from Step 5).
- [ ] Rollback decision tree in `MIGRATION-PLAN.md §5` names `primary_engineer` by handle or name at every leaf action — `rollback-names-role` equivalence test recorded as pass. "The team" is not an acceptable actor.
- [ ] Performance baselines from `AUDIT.md` (coverage %, bundle size, startup time, p99 latency, CVE counts) recorded in `MIGRATION-PLAN.md §1.1` — `baselines-recorded` equivalence test recorded as pass. These are the reference numbers Phase 5 compares against; if they are absent, Phase 5 cannot establish a confidence level higher than Low.
- [ ] `MIGRATION-PLAN.md § Sign-off` Status is `APPROVED` and every stakeholder row has a Date — Step 7 complete. No Phase 3 skill may be invoked before this gate passes.
- [ ] `migration-manifest-<timestamp>.json` exists at its declared output path and contains at least one unit entry with a `skill` field that resolves to a real file in `skills/` — verify with a file read of the manifest and a path check on each `skill` value.
- [ ] All output files listed in Section 7 exist at their declared paths — verify each with a file read.
- [ ] Every equivalence test in Section 6 has a recorded result in `output/migration-strategy-equiv-<timestamp>.md` — no test name is missing.
- [ ] No equivalence test in Section 6 is recorded as **fail** — grep the results file for `fail`; zero matches required.
- [ ] The migration log includes a confidence level (High / Medium / Low) — grep `output/migration-strategy-log-<timestamp>.md` for `Confidence:`.
- [ ] The migration log includes a numbered assumptions list — grep `output/migration-strategy-log-<timestamp>.md` for `Assumptions:`.
