migrate-legacy · diff
git:20260831.d1e53de to git:20260902.3d3a820
1 added, 1 removed. Audit A to A.
---
name: migrate-legacy
description: >-
Use when upgrading, migrating, or modernizing a legacy application — outdated target framework,
out-of-support runtime, obsolete APIs, old packages, or legacy idioms. Triggers on "upgrade this
app", "migrate to .NET 10", "modernize this codebase", "this project targets an old framework", «
mets à niveau cette app », « migre vers .NET 10 », « modernise ce code legacy », /migrate,
/migrate-assess, /migrate-verify. Drives a seven-phase, gate-verified pipeline (assessment →
verified production) in which RoselineMCP performs all C# analysis and transformation.
license: MIT
compatibility: >-
Requires the RoselineMCP server (Roslyn) for all C# analysis and mutation, a .NET SDK >= 8, git
and python3. Recommended: context7 MCP, gh CLI, node, headless Chrome. Canonical manifest:
requirements.json at the kit root, verified by scripts/preflight.sh (phase 0).
metadata:
author: Philippe Matray
suite: ai-migration-kit
---
- # Legacy Upgrade Pipeline
+ # Migrate a legacy application — the seven-phase pipeline
Upgrade a legacy application completely, verifiably, easily, and fast. You are the orchestrator: drive the phases **in order**, never advancing past a red gate.
Throughout this skill and its references, **`<kit>`** is the plugin root — resolve it as
`<skill-dir>/../..`, where `<skill-dir>` is this skill's base directory (given when the skill
loads). Every kit script and template path (`<kit>/scripts/…`, `<kit>/templates/…`) resolves from
there — never from the current working directory, which is the *target* repo.
## Phase 0 — Preflight (before anything else)
1. **Run `<kit>/scripts/preflight.sh`** (deterministic; a missing REQUIRED item = stop). The canonical prerequisite list — tools, MCP servers, session skills, each required or recommended, plus the `requiredBy` list where a specific skill hard-requires an entry — lives in **`<kit>/requirements.json`**: the script reads it, nothing is duplicated here. To add or remove a prerequisite, edit THAT file. Re-run with `--json` for the machine version to store in `migration/report.json`.
2. **Confirm the session capabilities** (the script cannot see the session): for every `sessionSkills` entry and every MCP in `requirements.json`, verify presence in YOUR list of skills/tools. The usage moments stay hard rules: `mcp__roseline__*` **mandatory** for any C# (otherwise stop and ask for the configuration); `context7` before phases 3/5 and before any UI; `frontend-design` **before writing a rewritten UI**; `dataviz` + `artifact-design` **before any dashboard** (audit or report).
3. **Documented degradation, never silent**: every absent recommended capability is recorded in the report with the fallback used (`preflight.sh --json` output + the session confirmations; same rule as the audit's RoselineMCP fallback).
## Hard rules
1. **RoselineMCP is mandatory for C#.** Every C# analysis (diagnostics, references, call graphs, symbol lookup) and every C# code mutation (bulk fixes, member edits, renames) goes through RoselineMCP tools — see `references/roseline-playbook.md`. Use plain Read/Grep/Edit only for non-C# files (csproj, config, docs).
2. **Preview first.** Every RoselineMCP mutation (`apply_fixes`, `edit_member`, `rename_symbol`) runs in preview mode first; inspect the diff, then re-run with `previewOnly: false`.
3. **Red gate stops the pipeline.** If a gate fails (build, tests, diagnostics regression), fix it or roll back the phase. Never continue past a failing gate, never weaken a gate to pass it.
4. **Branch and commit discipline.** Work on a dedicated `migration/<yyyy-mm-dd>` branch in the target repo. Commit at every green gate with a message naming the phase.
5. **No behavior changes.** The migration preserves observable behavior. Behavior fixes discovered along the way are recorded in the report as follow-ups, not applied.
6. **The deliverable never narrates its migration.** No banner, footer, meta tag or user-facing string mentions the port, the tooling or the process — the end user gets a product, not a case study. Provenance lives in the README, `migration/report.md` and git history. (In-code comments that encode a maintenance constraint — "verbatim port, do not modernize" — stay.)
7. **Kit scripts and templates are mandatory.** When the kit ships a tool for a step, improvising is forbidden: inventory → `<kit>/scripts/audit-inventory.sh`; report → `<kit>/scripts/report-dashboard.py` (never hand-written HTML); CI → `<kit>/templates/ci-dotnet.yml`; Blazor deployment → `<kit>/templates/deploy-pages-blazor.yml`. This is what makes migrations reproducible and comparable.
8. **Delivered = in production.** The pipeline does not stop at local green: follow `references/delivery-playbook.md` (default branch, workflows from the kit templates, Pages, production verified with a deep route + a reviewed screenshot). Phase 7 closes with a pass of the `review-followups` skill (`<kit>/scripts/followups.py` over the migrated repos): the follow-up queue — owner decisions, tasks, deliberate deferrals — is presented up to date before leaving the repo. A follow-up that deserves a real ticket becomes a GitHub issue via the kit's `create-issue` skill (see the `review-followups` skill). An app with **no production target** closes phase 7 by recording that owner decision in the report — documented, never silent. Phase 7 also closes the loop on the kit itself: the report's `lessons` entry either points at the kit change this wave produced, or states "nothing to learn from this wave" explicitly (delivery playbook, step 9) — a wave without a `lessons` entry is incomplete.
9. **Remediation must converge.** Track the error count after every phase-4 pass; if two consecutive passes do not reduce it, stop the loop — roll back to the last green-gate commit, record the blockage in the report (remaining diagnostics grouped by id, what was tried), and hand the decision to the owner. Burning passes on a flat error count is a failure mode, not progress.
## The pipeline
| # | Phase | Purpose | Exit gate | Reference |
|---|-------|---------|-----------|-----------|
| 1 | Assess | Read-only inventory, risk map + **verdict** | `migration/assessment.md` written with a `verdict` (`ALREADY_MODERN` stops the pipeline here); zero files modified | `references/phase-1-assess.md` |
| 2 | Baseline | Prove the app is green before touching it | Build + tests green; `migration/baseline.md` committed | `references/phase-2-baseline.md` |
| 3 | Retarget | New TFM + updated packages, dependency order | Full solution builds on the new TFM | `references/phase-3-retarget.md` |
| 4 | Remediate | Drive diagnostics to zero errors | 0 errors, warnings ≤ baseline, tests green | `references/phase-4-remediate.md` |
| 5 | Modernize | Opt-in idiom upgrades | Build + tests green after each item | `references/phase-5-modernize.md` |
| 6 | Verify | Final gate + report | `migration/report.html` generated + `report.md`; all gates green | `references/phase-6-verify.md` |
| 7 | Deliver | Production (CI, Pages, verification) | Public URL verified (deep route + reviewed screenshot) | `references/delivery-playbook.md` |
Load each phase's reference file **when you enter that phase**, not before — keep context small.
## Artifact contract
All pipeline artifacts live in a `migration/` folder at the target repo root:
- `migration/assessment.md` — phase 1 output (inventory, diagnostics histogram, risk map, recommended target) led by the **`verdict`** (`ALREADY_MODERN | RED_BY_TFM_LAG | NORMAL`) that `/migrate` branches on (phase-1-assess step 6).
- `migration/baseline.md` — phase 2 output (build/test/diagnostic counts that later gates compare against).
- `migration/report.md` — phase 6 output (before/after evidence, changes, follow-ups).
- `migration/report.json` — the report's data: KPIs, gates, next steps / follow-up queue, the **phase timeline** (`phases[]`, derived from the gate commits — see `references/phase-6-verify.md`) and the **`lessons` entry** (rule 8). Single source rendered by `<kit>/scripts/report-dashboard.py` into `report.html`, and what `followups.py` reads.
## Scope variants
- `/migrate` — the full pipeline, phases 1–7 (assess → deliver). It ends in verified production (hard rule 8) — or with the recorded owner decision when no production target exists.
- **Verdict gate** — phase 1 classifies the target (phase-1-assess step 6) and `/migrate` branches on it: `ALREADY_MODERN` (already at target, in support, no obsolete-API cluster) **stops after phase 1** like `/migrate-assess` and offers `/migrate-verify` (modern ≠ clean) — never a net`N`→net`N` retarget; `RED_BY_TFM_LAG` (baseline red because a bot pushed packages past the TFM — `NU1202`) defers the phase-2 baseline so the retarget can produce the first green (phase 3 records it); `NORMAL` runs the standard path.
- **Resume** — `/migrate` on a repo that already carries a `migration/` folder or a `migration/<yyyy-mm-dd>` branch never starts over. Locate the last green gate: the gate commits name their phase (rule 4) and the artifacts confirm it (`assessment.md` → phase 1 done, `baseline.md` → phase 2, `report.md`/`report.html` → phase 6). Announce "in-progress migration detected — resuming at phase N", then re-enter at the phase after that gate. A green phase is never replayed.
- `/migrate-assess` — phase 1 only. Absolute guarantee: no file in the target repo is created or modified except `migration/assessment.md`.
- `/migrate-verify` — phase 6 only; re-runnable at any time after a migration.
- Non-.NET legacy apps: the same seven-phase methodology applies, but phases 3–5 use the ecosystem's own tooling; RoselineMCP covers the C# path.
## Common issues (error → cause → solution)
| Error | Cause | Solution |
|---|---|---|
| Preflight prints `PREFLIGHT FAILED` on a required line | Required tool absent (dotnet/git/python3) or RoselineMCP not connected | Install the tool / `claude mcp add roseline …`, re-run `<kit>/scripts/preflight.sh` — never start phase 1 on red |
| `mcp__roseline__*` missing from the session's tool list | Server configured but this session started without it (or it died) | Check `claude mcp list`, restart the session; without roseline, stop — hard rule 1 forbids C# work without it |
| Build red right after the phase 3 retarget | Packages bumped out of dependency order | Roll back to the last green-gate commit; re-bump following the dependency graph, building between bumps |
| Phase 4 gate: warnings above baseline | Bulk fixes introduced new diagnostics | `list_diagnostics` grouped by id, fix by code — never widen the baseline to pass the gate |
| `dotnet test` fails locally on a missing prerequisite (workload, DB) | The app has a CI-only prerequisite | Run per-project builds / filtered suites and record the degradation in the report — documented, never silent |
| Phase 4 loops — error count stopped dropping | Fixes introduce as many diagnostics as they resolve (pathological repo) | Hard rule 9: after two passes without reduction, stop — roll back to the last green gate, record the remaining diagnostics (grouped by id) in the report, escalate to the owner |
| Session interrupted mid-pipeline | Crash, context loss, or user stop between gates | Re-run `/migrate`: it detects the `migration/` folder and the gate commits, announces the resume point, and re-enters at the phase after the last green gate (see Scope variants — Resume) |
## Recap
Close with the shared recap shape — [`../_shared/recap.md`](../_shared/recap.md). It owns the four
blocks (verdict · **What happened** · **Artifacts** · **Assumed · skipped · unverified**, where
`None` is a required answer rather than an omission) and the **Next** line, which is read off this
skill's row in that file's hand-off table instead of being decided again here. Everything below is
only what **migrate-legacy** adds on top of them.
- Name the phase reached and every gate's verdict — a pipeline that stopped at a green gate and one
that stopped at a red one are different outcomes, and hard rule 8 makes "verified production" a
claim rather than a formality.
- **Artifacts** lists the `migration/` folder's contents by path (`assessment.md`, `baseline.md`,
`report.md`, `report.html`, `report.json`) plus the gate commits, so the next run's resume logic
and the owner read the same evidence.
- A documented degradation (a test suite skipped for a CI-only prerequisite, a verdict of
`ALREADY_MODERN`, diagnostics left after hard rule 9's two-pass limit) belongs in **Assumed ·
skipped · unverified** — recorded, never silent.