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.md

GitHub

Crossplane 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).