About a2ui-migration
a2ui-migration is a migration-routing skill that directs tasks to a guide based on language and platform, current SDK and protocol versions, and the target. It helps A2UI maintainers establish migration scope and proceed through dependencies, code layers, and verification steps.
Key features
- 01Identify language, SDK, and protocol versions
- 02Route to a matching migration guide
- 03Provide a layered migration procedure
- 04Cover dependency and change verification
Use cases
- 01Move a genui project to a2ui_flutter
- 02Upgrade an A2UI protocol version
- 03Migrate an A2UI dependency in another repository
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ a2ui-project/ a2ui --skill a2ui-migrationAfter installing, type/a2ui-migrationin Claude Code to confirm it loaded.
Bundled filesreferences/dart_genui_to_1_0.md
Details
From the project READMEA2UI migration
This skill is a router. It identifies what is being migrated and then sends you to the guide for that case. A case is identified by three facts:
- Language / platform: Dart (Flutter), TypeScript (Lit, Angular, React, web core), Python (agent SDKs, ADK, A2A), Swift, Kotlin.
- Source: the SDK or package the code uses today and its version, and the protocol version it speaks on the wire.
- Target: the SDK the code should use afterwards, and the protocol version it should speak.
Do not start editing before all three are written down. A migration that changes the SDK and the wire version at the same time is two migrations; split it when the agent and the renderer are not released together.
Step 1: identify the language and the source SDK
Read the manifests in the repository, not just the file extensions:
| Manifest | Dependency names to look for |
|---|---|
pubspec.yaml, Bazel BUILD with dart_* | genui, a2ui_core, a2ui_flutter, a2ui_agent, a2ui_cli, genai_primitives |
package.json | @a2ui/web_core, @a2ui/lit, @a2ui/angular, @a2ui/react, @a2ui/markdown-it, @a2ui/agent, @a2ui/catalog-* |
pyproject.toml, requirements*.txt, uv.lock | a2ui-core, a2ui-agent-sdk, a2ui-a2a, a2ui-adk, a2ui-catalog-* |
Package.swift | A2UISwiftCore, A2UISwiftUI, BasicCatalog, BasicCatalogSwiftUI (products of this repository's Package.swift) |
build.gradle* | the legacy Kotlin agent SDK under kotlin/agent_sdk_legacy/ |
Record the exact version constraint, and read that package's CHANGELOG.md
in this repository for the release line the code is on.
Step 2: identify the wire protocol version
The SDK version does not always tell the protocol version; read the payloads the code sends or expects (fixtures, goldens, prompts, transport code):
| Evidence in payloads | Protocol |
|---|---|
beginRendering, surfaceUpdate, dataModelUpdate messages | v0.8 |
"version": "v0.9" with createSurface, updateComponents, updateDataModel, deleteSurface; bindings as {"path": ...} and calls as {"call": ...} | v0.9 |
"version": "v0.9.1" | v0.9.1 |
"version": "1.0"; bindings as {"@path": ...}, calls as {"@call": ...}; per-component catalogId; callRendererFunction / callAgentFunction | v1.0 |
Other signs: catalog ids (.../specification/v0_9/catalogs/basic/catalog.json
is v0.9, .../specification/v1_0/catalogs/basic/catalog.json is v1.0; the
v1.0 catalog files live under catalogs/<name>/v1/, but their ids keep the
specification/v1_0 path), the metadata keys for capabilities
(a2uiClientCapabilities is v0.9, a2uiRendererCapabilities is v1.0), and
the names of the schema directories a project validates against
(specification/v0_9/json versus specification/v1_0/json).
Step 3: fix the target
Ask, or infer from the task, whether the target is a new SDK on the same protocol version, a new protocol version on the same SDK, or both. When the task does not say, prefer the smallest step: the current SDK's latest release on the protocol version the agent already speaks. Record the target the same way as the source.
Step 4: route to a case guide
| Language | From | To | Guide |
|---|---|---|---|
| Dart | genui 0.9.x / 0.10.x (A2UI v0.9) | a2ui_core + a2ui_flutter (+ a2ui_agent), v0.9 or v1.0 | references/dart_genui_to_1_0.md |
| any | protocol v0.8 | protocol v0.9 | specification/v0_9/docs/evolution_guide.md; Python payload converter samples/agent/adk/migrate_v08_to_v09.py; Lit renderer example in typescript/renderers/lit/README.md |
| any | protocol v0.9 | protocol v0.9.1 | specification/v0_9_1/docs/evolution_guide.md |
| any | protocol v0.9 / v0.9.1 | protocol v1.0 | specification/v1_0/docs/evolution_guide.md, then the SDK's CHANGELOG.md |
| any | an A2UI SDK release | a later release of the same SDK | that package's CHANGELOG.md (Breaking: entries first), then the generic procedure below |
When a row matches, read the guide in full and follow it. When no row matches, follow the generic procedure and then add a case (see the end of this file).
Generic procedure
Use this when no case guide exists, and as the outer loop around a case guide:
- Inventory. List every file that imports or depends on the source SDK, every fixture or golden that holds payloads, and every prompt that describes the wire format to a model. Group them by the layer they touch: transport, renderer wiring, catalogs and components, functions, tests.
- Dependencies first. Change the manifest, then make the project resolve dependencies. Do not edit source until the new packages are installed.
- One layer at a time. Migrate transport, then renderer wiring, then catalogs and components, then functions, then tests. Compile after each layer; a layer that does not compile hides errors in the next one.
- Payloads and prompts. Update fixtures, goldens and prompt text to the target wire version. Do not blanket-replace keys; the evolution guides list which positions change.
- Behavioral differences. Read the target's behavior notes (a case guide has a section for them) and check each one against the app: error handling, validation strictness, unknown ids, ordering, markdown, theming.
- Verify. Run the analyzer or type checker, unit and widget tests, and one manual round trip against the real agent with a surface that has an input and an action.
- Record. Note in the change description which case was applied, which behavioral differences needed app changes, and anything the guide did not cover.
Migrating a package you do not own
When the migration is a pull request to someone else's repository, such as a
package on pub.dev that depends on genui:
- Read the repository's CI workflows, its task runner and local gate
scripts, its agent instructions (
AGENTS.md,CLAUDE.md,.cursor/rules/and the like), itsCONTRIBUTING.md(some require a signed CLA before a PR is merged) and its commit and PR title conventions first (some repositories check PR titles against Conventional Commits; follow the history's language too). Run the CI steps locally before and after the change, including in examples and in every package of the repository that depends on the migrated one, even when CI skips them. Use the toolchain release CI uses. When the repository pins a toolchain release you cannot install, relax the pin only to run the checks, then restore it everywhere it is recorded. - Keep the package's name and public class names. Keep its API shape where
the new SDK allows, and list each breaking change in its
CHANGELOG.mdunder anUnreleasedheading; leave the version number to the maintainer. - Update the README, examples and agent instructions that show the old SDK's API, so they do not teach code that no longer compiles.
- Commit only the migration:
- Include the manifests, the tracked lock files, and generated files that change because a dependency was added or dropped, such as plugin registrations.
- Leave out edits that tools make on their own (configuration rewrites, lock changes that a plain install on a newer toolchain makes before any edit; mention those in the change description), unrelated generated changes, and generated files that hold local paths. In a repository without a complete ignore file, stage files by name.
- When the change only removes dependencies, the lock loses exactly the packages that nothing else needs; check that a fresh resolve changes nothing more.
- A lock resolved with a newer toolchain can pick releases that the project's own toolchain (CI, version files) cannot use. Check each new package against it, and hold back the ones it cannot use.
- Keep the package's minimum language or SDK version even when the new dependencies need a newer one. The package manager already refuses a toolchain older than a dependency needs, while a higher bound can change language semantics or formatting across the whole package.
- Read the APIs of the published SDK releases (in the package manager's cache), not this repository's sources, which may be ahead of the release.
- Leave behavior the migration did not have to change as it is, including bugs you notice, and mention them in the change description instead.
- In an app repository, update living docs (architecture notes, READMEs, comments in other languages) but leave decision records, specs and plans of their time as they are, and dated experiments outside the workspace and CI when the project's tooling says to. When the repository requires a decision record for a changed dependency, add one as proposed and leave the decision to the owner. When the agent side is in another language and the client translates its records to the wire version, keep that translation, so the agent and its stored data do not change.
A case guide lists what these rules mean on its platform; for Dart and Flutter, see In a package you do not own.
Adding a case
When you migrate something this skill has no guide for, add one so the next migration starts from it:
- Create
references/<language>_<from>_to_<to>.mdnext to this file, named likereferences/dart_genui_to_1_0.md. - Give it the same sections as the existing guide: scope and version assumptions, dependencies, a step per layer with before/after code, checklist, API mapping, recipes, and behavior changes.
- Add a row to the table in Step 4.
- Follow
natural-writingandpr-hygienefor the text and the pull request.