* Add arch plugin (architecture + modernization skills) Add the `arch` plugin with two skills: - `arch:document` — produce a single, cited architecture document for a locally-cloned repo, reading files on disk only. - `arch:modernize` — generate a phased modernization plan, auto-running the document workflow first when no architecture doc exists. Skill sources live in top-level skills/ and are referenced declaratively from plugins/arch/.github/plugin/plugin.json, per the repo's plugin model. Regenerated docs/README.plugins.md, docs/README.skills.md and marketplace.json. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Merge arch skills into single doc-and-modernize skill Collapse the document and modernize skills into one standalone skill (doc-and-modernize) with Documentation and Modernization modes, keeping the plugin named arch. Modernization mode now runs the Documentation workflow inline instead of invoking a separate arch:document skill, fixing standalone-install cross-skill references. Reframe Documentation mode as local-first (remote/API lookups are a flagged last resort) rather than local-only. Regenerate docs and marketplace. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review: list skill by bare ID in arch README Use 'doc-and-modernize' (repo convention) instead of the namespaced 'arch:doc-and-modernize', noting it surfaces as arch:doc-and-modernize when installed via the plugin. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review: use non-HTML placeholder in instructions template Change plain-text <PROJECT NAME> to [PROJECT NAME] in the header and first paragraph so Markdown renderers don't parse it as an HTML tag and drop it. The code-span `<N>` on the phase line is unaffected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review + fix codespell CI failure - Fix codespell: pre-empts -> preempts in the instructions template - Consistent terminology: replace 'research step/workflow' with 'Documentation mode' in SKILL.md, README, and plugin.json description - Fix run-on: add 'that' before 'Modernization mode must surface' - Regenerate docs/marketplace for the updated plugin description Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review feedback - Instruct redacting credentials/tokens from git remote URLs before recording - CI enforcement: ask user or mark [UNVERIFIED]; remote lookup is flagged last resort Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: samqbush <samqbush@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
14 KiB
Migration Hazard Catalog
A generic, stack-agnostic catalog of migration hazards that are predictable per ecosystem but are routinely missed in a first-draft modernization plan. Every phase plan should be red-teamed against this list before it is implemented (see Modernization mode's pre-implementation red-team step).
Each hazard is written as: Trigger (when it applies) → Risk (what breaks) → Detection probe (a concrete check you can run now) → Plan action (what to add to the phase) → Examples (per-ecosystem, illustrative only).
These are hazard classes, not a fixed checklist for one stack. The examples (Java/Spring/Mongo/Docker etc.) are illustrations drawn from real runs — map each class onto whatever ecosystem you're actually migrating. If a hazard doesn't apply, say so explicitly rather than silently skipping it.
How to use this catalog
Before implementing any phase:
- Walk every hazard below and ask "does this phase trigger it?"
- For each triggered hazard, run the detection probe against the real repo.
- Fold the plan action into the phase's tasks and exit criteria before writing code — a hazard caught in planning is a task; caught in review it's rework; caught in prod it's an incident.
- Record hazards you checked and cleared, not just the ones that fired — a silent skip is indistinguishable from a miss.
Empirically (the PiggyMetrics run, phases 0–6) an independent critic caught a blocking plan gap in every single phase, and each gap was an instance of one of the classes below. Running this catalog is that critic, institutionalized.
H1 — Incomplete quarantine / removal set
Trigger: You are removing, quarantining, or cutting over an entire dependency family (a dead framework, a removed runtime API, an EOL library set) and only some modules are being touched.
Risk: A module you didn't list still transitively depends on the removed artifact, so the default build/test target fails after the cutover — often in a module the plan never mentioned.
Detection probe: Grep the dead artifact/coordinate across all manifests,
not just the ones you're editing (pom.xml/build.gradle, package.json,
*.csproj, requirements*.txt/pyproject.toml, go.mod, etc.). Every hit is a
module that must move or be quarantined in the same phase.
Plan action: Enumerate the full transitive set of dead-artifact dependents up front and list every one in the phase's reactor/quarantine decision. State the post-cutover build target explicitly (which modules are in, which are quarantined).
Examples:
- JVM: Spring Cloud 2023 removes Netflix Hystrix/Turbine;
monitoringandturbine-stream-servicestill pullednetflix-hystrix-dashboard/netflix-turbine-streamand brokemvn verifyeven though the plan only quarantinedgateway+auth-service. - JS: dropping
request— grep for everyrequire('request')/from 'request', not just the one service you remembered. - Python: removing a C-extension lib — check every
importsite + extras.
H2 — Framework-major mechanical codemods (namespace / API / test-engine)
Trigger: A major version bump of a framework, language, or platform (not a patch/minor). Major bumps carry mechanical breaking changes independent of your own logic.
Risk: The plan treats "bump the version" as one task and misses the whole-tree mechanical migrations the bump forces — namespace renames, removed deprecated APIs, changed test runner/engine, changed config keys — so the build won't even compile.
Detection probe: Read the target version's migration/upgrade guide and identify the known breaking-change recipes. Grep the tree for the old namespace / API surface to size the change. Check whether an ecosystem codemod/recipe tool exists.
Plan action: Enumerate each mechanical migration as its own explicit task, and prefer the ecosystem's automated recipe tool over hand-editing where it's reliable. Decide tool-vs-manual deliberately (small trees may be safer by hand; large trees need the tool). Include test-framework migration in the list — it is routinely forgotten.
Examples:
- Spring Boot 2→3:
javax.* → jakarta.*namespace migration and JUnit 4→5 (addjunit-vintage-engineto keep JUnit 4 tests running, migrateOutputCapture-style Jupiter-only cases). Tool: OpenRewrite. - JS: CommonJS→ESM, or React class→hooks. Tools: jscodeshift,
react-codemod. - Python:
2to3/pyupgrade;asyncAPI renames. - .NET:
Newtonsoft→System.Text.Json,HttpClientpatterns.
H3 — Runtime bump not in lockstep with deployment artifacts
Trigger: You bump the language/runtime major (JDK, Node, Python, Ruby, Go) used to build the app.
Risk: The build now emits artifacts for the new runtime, but the
deployment/runtime surface still pins the old one — base images, CI runner
version, buildpacks, engines fields, lambda runtimes — so the app builds green
and then fails to run (bytecode/ABI/syntax mismatch), often only in a
smoke/e2e step, or silently in prod.
Detection probe: Inventory every place the runtime version is pinned:
Dockerfile/Containerfile base images, docker-compose* build contexts, CI
workflow setup-*/runner images, .tool-versions/.nvmrc/runtime.txt,
engines in package.json, serverless runtime keys. Cross-check each against
the new target.
Plan action: Add an explicit task to move every deployment/runtime pin in the same phase as the build bump. Never let build-runtime and run-runtime drift across a phase boundary. Verify the smoke path actually builds/runs the new artifacts (and doesn't build quarantined modules).
Examples:
- JVM: source-built services emit JDK 21 bytecode but every
DockerfilestillFROM java:8-jre→ containers won't start; dev-compose still built the quarantinedauth-service. - Node: CI bumped to Node 22 but Docker image still
node:14-alpine. - Python: venv on 3.12 but the container base is
python:3.7.
H4 — Traffic-flow / route-class enumeration gap (edge & auth rewrites)
Trigger: Rewriting an edge/gateway, an auth/identity layer, or any request-routing/security boundary.
Risk: The plan pins the obvious authenticated happy path and silently drops one or more other traffic classes — anonymous flows (signup, password reset, public assets, health/metrics scrape), service-to-service calls, webhooks — so those break at cutover.
Detection probe: Enumerate every route class the boundary must serve: (1) anonymous/public, (2) end-user authenticated, (3) service-to-service (machine tokens/scopes), (4) infra/observability (health, readiness, metrics scrape), (5) webhooks/callbacks. For each, confirm the new config has an explicit rule.
Plan action: Make the route-class enumeration an explicit artifact of the phase (a table of route class → new rule → scope). Call out that scanners/security expect infra endpoints (e.g. metrics scrape) to remain reachable. Decide token/scope granularity for s2s calls (narrow scope, not a generic "server" grant).
Examples:
- BFF/OAuth2: SCG BFF + TokenRelay covered login but missed the anonymous
registration POST + signup assets, and
/uaa/userswaspermitAllinstead of requiring a narrowuser.writeservice scope. - Metrics: resource-server security must keep
/actuator/prometheusopen or scraping silently breaks.
H5 — Stateful data-store major upgrade treated as an image/version bump
Trigger: Bumping the major version of a stateful backing service (database, cache, broker, search index) that has persisted data/volumes.
Risk: Engine majors frequently can't start on prior-major on-disk data without a sequential/FCV upgrade path. A naive image bump either fails to start, appears flaky depending on local volume state, or corrupts data.
Detection probe: Check whether the store has persisted volumes/data directories, and read the engine's supported upgrade path (sequential majors? feature-compatibility-version steps? dump/restore only?). Determine whether the data is production/durable or ephemeral/demo.
Plan action: Choose and document one path: (a) sequential/FCV migration with steps + rollback, or (b) an explicit destructive reset (drop volumes, re-seed) when the data is ephemeral/demo — stated as a decision, not an accident. Put this in the migration safety net with a rollback plan.
Examples:
- MongoDB 3→7: direct image bump over an existing anonymous
/data/dbvolume is unsafe; for a demo, the blessed path wasdocker compose down -v+ re-seed via nativeinitdb.d, with the oldmongo:3image retained for rollback. - Postgres major: needs
pg_upgradeor dump/restore, not just a tag change.
H6 — Transitional-insecure state trips scanners & reviewers repeatedly
Trigger: A phase intentionally introduces a temporary, deliberately-weak state to keep the system runnable mid-migration — permit-all security shims, CSRF disabled, an open endpoint, a hard-coded secret placeholder, a disabled check.
Risk: Every code scanner (CodeQL, Dependabot, SAST) and every automated reviewer flags it each review, generating recurring noise and re-litigation, and — worse — a shim that was meant to be temporary silently ships because nobody tracked its closing phase.
Detection probe: For each transitional weakening, ask: which scanner will flag this, and which later phase closes it? Is the weakening minimally scoped (e.g. permit-all chains should still explicitly disable form-login/HTTP-Basic so the surface is exactly what's intended)?
Plan action: Maintain a transitional-insecure-state register: each shim with (a) why it's needed, (b) the phase that closes it, (c) the residual risk, (d) a note to reviewers that it's by-design-until-phase-N. Scope each weakening as tightly as possible. Pre-declaring it converts review churn into a resolved, tracked decision.
Examples:
- Phase-5 permit-all
SecurityConfigshims flagged CodeQL "CSRF disabled" on every service, every review; the fix was to explicitly disable form-login/basic and annotate "stateless permit-all shim, closed in Phase N."
H7 — Stacked-PR trap & trunk split-brain (phased governance)
Trigger: A multi-phase migration where each phase depends on the previous, developed branch-per-phase.
Risk: If phase N+1 is branched off phase N's branch (instead of trunk) and
phase N is never merged to trunk, PRs get merged into each other, the trunk
silently stalls several phases behind, and later phases are forced to stack —
producing the exact bind where the "no stacked PRs" rule has to be overridden.
A parallel failure: a repo with two default-ish branches (master +
main) where phase work targets the wrong one.
Detection probe: Before starting a phase, verify the previous phase's PR is
merged to trunk and the new branch is cut from trunk (git log origin/<trunk>..HEAD should be empty at branch creation). Confirm the repo's
real trunk name and that no legacy default branch is being targeted.
Plan action: Mandate merge each phase PR to trunk before starting the next phase. Never base a phase branch/PR on a sibling phase branch. If genuinely blocked and controlled stacking is unavoidable, require a reconciliation PR that lands the stack onto trunk and an explicit residual-risk note. Pin the trunk name and mark any legacy default branch "history only — do not target."
Examples:
- PiggyMetrics phases 3 & 4 were merged into each other;
mainstuck at Phase 2;master/mainsplit-brain — reconciled only by a dedicated PR #6, after Phase 5 had to break the no-stacking rule.
H8 — Living-doc drift (topology changes outrun the docs)
Trigger: A phase changes system topology — module/reactor membership, removed services, renamed branches, changed commands, new/removed endpoints.
Risk: Only the plan's status markers get updated; the executable/onboarding
docs (.github/copilot-instructions.md, README, module lists, command
tables) go stale and start actively misleading the next agent/human — e.g. a
"quarantined module" list that no longer matches the reactor.
Detection probe: After any topology change, diff the change against every doc that describes topology: agent-instructions file, README, architecture doc's module/command tables, compose/service inventories.
Plan action: Extend the living-plan discipline beyond the plan file:
any phase that changes topology must update copilot-instructions.md, the
README, and any module/topology list in the same PR. Treat these docs as
part of the phase's Definition of Done, not a follow-up.
Examples:
copilot-instructions.mdquarantine text stayed stale aftergatewaymoved into the reactor andmonitoring/turbinewere removed; README hard-coded amasterbranch link that 404'd after the trunk becamemain; actuator exposure omittedrefresh, breaking the@RefreshScopeworkflow.
Quick pre-flight checklist (per phase)
- H1 Grepped the dead artifact across all manifests; full quarantine set listed?
- H2 Enumerated the major-bump codemods (namespace, removed APIs, test engine); recipe tool chosen?
- H3 Every runtime pin (base images, CI runners, engines) moved in lockstep with the build bump?
- H4 Every route class enumerated (anon, authed, s2s, scrape, webhook) with an explicit rule?
- H5 Stateful-store major has a migration path or an explicit destructive-reset decision + rollback?
- H6 Every transitional-insecure state registered with its closing phase + scoped tightly?
- H7 Previous phase merged to trunk; new branch cut from trunk; trunk name confirmed?
- H8 Topology change reflected in copilot-instructions.md + README + module lists in the same PR?