| name | migrate |
| description | Execute cross-cutting migrations — library swaps, pattern changes, API version upgrades, or architecture refactors. Handles phased rollout, coexistence, and rollback planning. |
Migrate
Execute a migration from one pattern, library, or approach to another across the codebase.
Parameters: migration description (optional if spec provides it)
Context (optional)
The user may provide additional context in three ways — all are optional:
- Bare — short description, e.g.,
/migrate order repo from in-memory to ktor.
@file reference — e.g., /migrate @specs/auth-to-oauth2.md. The CLI resolves the file and includes its content. Extract from/to states, scope, phasing, and rollback plan from the spec. Template: .agents/templates/migrate-spec.md.
- Inline description — free text describing the migration.
Pre-flight: Grill the Spec
When the user provides a spec via @file, scan it for unresolved markers before scaffolding (see the grill-me skill for the full marker list): <!-- TODO --> placeholders, empty - [ ] AC items, empty required header fields, raw template placeholder prose, ... table cells, or unconfirmed reverse-spec presumptions (presumably / appears to). If any are present, stop and run /grill-me @{spec} first, then resume this skill.
The conversion skills (/ac-to-spec, /reverse-spec) grill inline before producing output, so a marker-laden spec usually means the spec was hand-authored from a template or has gone stale. Skip the pre-flight only if the user explicitly says "skip the grill" — in that case, surface unresolved markers as // TODO comments in the generated code and call them out in the final summary.
Pre-flight: Subagent dispatch (callsite inventory)
Migrations are the canonical "find every caller of X and decide what to do with each" task. Always dispatch one Explore agent BEFORE step 1 to inventory every callsite — including indirect ones (test fakes, fixtures, DI bindings, AGENTS.md references). Even tightly scoped migrations benefit from the upfront survey; serial grep cycles compound across 7 migration phases.
Recommended prompt template:
Inventory every callsite of `{old API / type / library}` in the codebase. Search:
- All `.kt` files (commonMain, androidMain, iosMain, commonTest, androidDeviceTest)
- All `.swift` files in `iosApp/`
- `build.gradle.kts` dependency declarations
- `gradle/libs.versions.toml`
- Per-module `AGENTS.md` files
Report a punch list: `file:line — context (one line)`. Group by migration
phase order: (1) core modules, (2) impl/data, (3) impl/domain,
(4) impl/presentation, (5) test/, (6) iOS, (7) test suites.
See .agents/standards/ways-of-working.md → "When to Spawn Subagents". The only case to skip is a single-file rename where direct grep is trivially complete.
Reference Standards
- Architecture & module structure:
.agents/standards/architecture.md
- DI patterns:
.agents/standards/dependency-injection.md
- Convention plugins:
.agents/standards/convention-plugins.md
- CenterPost interactors:
.agents/standards/centerpost.md
Steps
1. Audit Current State
- Read all files involved in the "from" state
- Map the full dependency graph of the thing being migrated
- Identify all consumers (features, core modules, tests, iOS)
- Document current behavior as a baseline for verification
2. Assess Migration Strategy
| Strategy | When to Use | Risk |
|---|
| Big-bang | Small scope, low risk, no phasing needed | Must be correct in one pass |
| Phased | Large scope, can ship incrementally | Coexistence complexity |
| Strangler-fig | Critical path, zero-downtime required | Bridge/adapter overhead |
Choose based on scope and risk. Default to phased for anything touching more than one feature.
3. Introduce New Pattern (if phased/strangler)
- Add new abstractions alongside existing ones
- Create bridge/adapter if old and new must coexist
- Wire feature flag to toggle between old and new paths if applicable. Use the namespaced
{feature}.{flag} key convention and contribute a FeatureFlagDefinition so the debug-menu enumerates the migration flag:
object {Feature}Flags {
val useNewThing = FeatureFlag(key = "{feature}.use_new_thing", defaultValue = false)
}
@ContributesIntoSet(AppScope::class)
class UseNewThingFlagDefinition : FeatureFlagDefinition {
override val flag = {Feature}Flags.useNewThing
override val description = "Routes {thing} through the new impl during migration"
override val owner = "{feature}"
override val lifecycle = FlagLifecycle.Experiment
}
Remove both the flag and its definition in step 6 once the migration lands.
4. Migrate Consumers
Work through consumers one at a time, verifying each:
For each consumer:
- Update imports and type references
- Adapt to new API surface
- Update tests to cover new path
- Update fakes if abstractions changed
- Run
verify to confirm the consumer still works
Order of migration:
- Core modules (upstream dependencies first)
- Feature
impl/data layers
- Feature
impl/domain layers
- Feature
impl/presentation layers
- Feature
test/ fakes
- iOS layer
- Test suites (unit, UI, navint, e2e)
5. Verify Behavioral Equivalence
After migrating all consumers:
- All existing tests must pass without modification (behavior preserved)
- Any test modifications must be intentional and documented
- Run full
verify to confirm cross-module consistency
6. Remove Old Code
Only after all consumers are migrated and verified:
- Delete old implementation files
- Remove old dependency declarations
- Remove bridge/adapter code
- Remove migration feature flag
- Remove old DTOs, mappers, data sources
- Update AGENTS.md files
7. Update Documentation
- Update
AGENTS.md for affected features and core modules
- Update
.agents/standards/ if the migration changes a documented convention
- Update
CLAUDE.md if build commands or key versions changed
Common Migration Types
Library Swap
From: library-a → To: library-b
1. Add library-b dependency
2. Create new impl using library-b
3. Migrate consumers
4. Remove library-a dependency
Pattern Change
From: pattern-a → To: pattern-b
1. Document both patterns
2. Create new pattern alongside old
3. Migrate file by file
4. Remove old pattern
API Version Upgrade
From: /v1/resource → To: /v2/resource
1. Add v2 DTO alongside v1
2. Add v2 data source
3. Update repository to use v2
4. Remove v1 DTO and data source
In-Memory to Real Implementation
From: flowOf(fakeData) → To: HttpClient + real API
1. Create RemoteDataSource interface + impl
2. Create DTO + mapper
3. Update RepositoryImpl to use data source
4. Update tests with fake data source
Dependency Changes
When adding or removing libraries:
| Action | Where | Convention |
|---|
| Add dependency | build-logic/src/main/.../ or module build.gradle.kts | Use version catalog (libs.{name}) |
| Remove dependency | Same | Verify zero imports before removing |
| Version bump | gradle/libs.versions.toml | Update version catalog entry |
Key Rules
- Never break the build mid-migration — each step must compile and pass tests
- Migrate one consumer at a time — verify after each, not at the end
- Preserve behavior — migration changes implementation, not functionality (unless explicitly requested)
- Feature-flag large migrations — if it takes more than one session, gate with a flag
- Update AGENTS.md — migration changes are exactly the kind of thing AGENTS.md must reflect
- Check iOS parity — Kotlin migrations often require SwiftUI-side changes
Post-Change Verification — MANDATORY
Work is NEVER complete until verification passes. Run the full verify skill since migrations are cross-cutting. The skill will run: lint, unit tests, architecture tests (Konsist + Harmonize), and full build.
If ANY check fails, fix the issue and re-run. Do not declare the task complete until verification passes.