plan-v2-migration
Use this skill when user requests to migrate, upgrade, or plan migration to Crossplane v2. Analyzes an existing v1 configuration package and writes a migration plan covering XRD updates, provider API
SKILL.mdskills/plan-v2-migration/SKILL.md
GitHubCrossplane v2 migration planner
Analyze a Crossplane v1 configuration package and write the plan that execute-v2-migration
carries out: .agents/plans/CROSSPLANE_V2_MIGRATION.md.
Before you start
Load control-plane-project-charter first, or read its SKILL.md beside this skill's
directory; this skill does not load it. The only questions are whether to replace an existing
plan (Phase 1) and Phase 5's decisions, asked only when interactive (charter §1).
- Analysis only: write the plan file and nothing else — no code changes, builds, tests or commits.
- Every change you plan comes from breaking-changes.md. Point at its sections; do not restate them in the plan. Upstream: the Crossplane v2 upgrade guide and What's new in v2.
- The plan changes the project, not a control plane. Moving a control plane that runs the v1 package is a rollout risk you record, never a step you plan (Installed v1 APIs).
Phase 1: Confirm a v1 project
First, if .agents/plans/CROSSPLANE_V2_MIGRATION.md exists, a migration may be under way, and
its ticks are where execute-v2-migration continue resumes. Interactive: ask whether to keep it
or replace it; to keep it, stop and point to execute-v2-migration continue. Unattended: do not
overwrite it; stop and report that it exists.
grep -n '^apiVersion:' upbound.yaml # meta.dev.upbound.io/v1alpha1
grep -rl 'kind: CompositeResourceDefinition' apis/ | xargs grep -l 'apiextensions.crossplane.io/v1$'
The second command lists the XRDs still on v1, filtering on the kind because Compositions are
apiextensions.crossplane.io/v1 in both versions. Functions can lag behind both: run Phase 4's
first grep over functions/ too. If nothing is v1 — upbound.yaml, XRDs or functions — report
that and stop. If the project is partly migrated, plan only what is left and say so.
Phase 2: Map the project
yq '.metadata.name' upbound.yaml
find apis -name definition.yaml | sort # XRDs
find apis -name composition.yaml | sort # compositions
ls -1d functions/*/ tests/test-*/ tests/e2etest-*/ 2>/dev/null
ls -1 examples/ 2>/dev/null
Detect each function's language and the test language with
control-plane-project-charter/references/languages/README.md, and read that language file: it
gives the import spelling and XR bootstrap you will plan. Map which composition calls which
function (functionRef.name) and which tests cover which composition.
Phase 3: Check the dependencies
A sub-agent keeps the large marketplace pages out of your context. Hand it this brief and wait
for its report; if your harness has no sub-agents, follow the brief yourself
(control-plane-project-charter §1).
Read upbound.yaml in <project root> and list every entry of spec.dependsOn.
For each provider: note its name and version, then open
https://marketplace.upbound.io/providers/<org>/<provider>/<version>#managedResources
and read the "Namespace Scoped (<count>)" figure. A count above 0 serves the .m. API groups.
If it is 0, find the oldest version whose count is above 0.
For each configuration: find its source repository from its marketplace page and check
whether its XRDs are apiextensions.crossplane.io/v2 with scope: Namespaced. If not, find a
version that is.
If a page cannot be fetched, say so for that package; never guess a version.
Report, per package: current version, v2-ready yes/no, the target version if not, and the
URL you verified it on.
Phase 4: Analyze each component
Check every file against breaking-changes.md and note which sections apply to each.
# XRDs: version, scope, Kind, claim and connection-secret fields
yq '[.apiVersion, .spec.scope, .spec.names.kind, .spec.claimNames, .spec.connectionSecretKeys, .spec.defaultCompositeDeletePolicy]' apis/<path>/definition.yaml
# Compositions
yq '[.spec.mode, .spec.writeConnectionSecretsToNamespace, .spec.compositeTypeRef, .spec.pipeline[].functionRef.name]' apis/<path>/composition.yaml
# Examples
yq '[.kind, .metadata.namespace, .spec.compositionSelector, .spec.compositionRef, .spec.writeConnectionSecretToRef]' examples/<file>.yaml
# Functions and tests: legacy provider groups and model paths, in any language
grep -rnE '(aws|azure|gcp)\.upbound\.io|io[./]upbound[./](aws|azure|gcp)[./]|(kubernetes|helm)\.crossplane\.io|io[./]crossplane[./](kubernetes|helm)[./]' functions/ tests/
# ... and the fields whose v2 meaning differs
grep -rnE 'deletionPolicy|providerConfigRef|managementPolicies|[Ss]ecretRef|namespace\s*[:=]' functions/ tests/
grep -rniE 'connection_?details|writeConnectionSecret' functions/ tests/
The first grep matches the legacy forms only (ec2.aws.upbound.io, Python
models.io.upbound.aws, Go io/upbound/aws/); extend it for any other provider the project
uses. Judge each hit of the field grep: a reference naming default goes, one naming another
config stays, a deletionPolicy parameter gets mapped.
Phase 5: Decide
Four questions have no answer in the code. Interactive: ask the user the open ones. Unattended: take the default and record it in the plan as an assumption.
| Decision | Default |
|---|---|
| Keep or rename each X-prefixed Kind | keep (Kind and the X prefix) |
A deletionPolicy parameter |
keep it and map it (deletionPolicy) |
Each non-default providerConfigRef |
keep it as a ClusterProviderConfig reference (providerConfigRef) |
| Is the v1 package installed on a control plane? | assume yes: record the rollout risk |
Phase 6: Write the plan
Write .agents/plans/CROSSPLANE_V2_MIGRATION.md (overwrite an existing one only if the user
chose to replace it in Phase 1) from
checklist-template.md: real file paths, the target version
for every dependency, and, for stage 5, which tests cover each function and what each test
should fail on before its function is migrated. A stage that does not apply keeps its heading with
None: <why>.
Phase 7: Report
## v2 migration plan: [project]
Plan: `.agents/plans/CROSSPLANE_V2_MIGRATION.md`
Scope: [n] XRDs, [n] compositions, [n] functions ([languages]), [n] tests, [n] examples;
[n] dependencies to bump
Breaking changes that matter here: [3–5, most consequential first]
Decisions: [each, and whether the user made it or you assumed it]
Rollout risk: [one line, or "none: not installed anywhere"]
Next: review the plan, then run `execute-v2-migration`.
Success criteria
Checks for you before you report, not a report format (control-plane-project-charter §4:
report the effect, not the intent).
- Every XRD, composition, function, test and example is in the plan, or named as needing no change.
- Every dependency has a verified target version, or is marked unverified with the reason.
- Every assumed decision is labelled as an assumption.
- No file outside
.agents/plans/changed (git status).