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)
- 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 therequiredBylist 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--jsonfor the machine version to store inmigration/report.json. - Confirm the session capabilities (the script cannot see the session): for every
sessionSkillsentry and every MCP inrequirements.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);context7before phases 3/5 and before any UI;frontend-designbefore writing a rewritten UI;dataviz+artifact-designbefore any dashboard (audit or report). - Documented degradation, never silent: every absent recommended capability is recorded in the report with the fallback used (
preflight.sh --jsonoutput + the session confirmations; same rule as the audit's RoselineMCP fallback).
Hard rules
- 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). - Preview first. Every RoselineMCP mutation (
apply_fixes,edit_member,rename_symbol) runs in preview mode first; inspect the diff, then re-run withpreviewOnly: false. - 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.
- 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. - No behavior changes. The migration preserves observable behavior. Behavior fixes discovered along the way are recorded in the report as follow-ups, not applied.
- 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.mdand git history. (In-code comments that encode a maintenance constraint — "verbatim port, do not modernize" — stay.) - 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. - 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 thereview-followupsskill (<kit>/scripts/followups.pyover 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'screate-issueskill (see thereview-followupsskill). 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'slessonsentry 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 alessonsentry is incomplete. - 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 theverdict(ALREADY_MODERN | RED_BY_TFM_LAG | NORMAL) that/migratebranches 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 — seereferences/phase-6-verify.md) and thelessonsentry (rule 8). Single source rendered by<kit>/scripts/report-dashboard.pyintoreport.html, and whatfollowups.pyreads.
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
/migratebranches on it:ALREADY_MODERN(already at target, in support, no obsolete-API cluster) stops after phase 1 like/migrate-assessand offers/migrate-verify(modern ≠ clean) — never a netN→netNretarget;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);NORMALruns the standard path. - Resume —
/migrateon a repo that already carries amigration/folder or amigration/<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 exceptmigration/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. 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.