Crane: verified code migration
Crane is a migration assistant for GitHub repositories that plans, executes, and verifies code migrations in small agentic steps while keeping humans in control.
Introducing Crane, our newest Agentic Workflow, designed to make code migrations fast and sure-footed. To get started right away:
Install Crane using https://github.com/githubnext/crane/blob/main/install.md Migrating existing code to more modern or more performant languages is one of the highest-return projects you can undertake. It’s also one where agents are especially capable.
Agents are excellent at executing work against a detailed spec, and when you already have a working, tested implementation, that’s about as good a spec as you can ask for. Migrating that project to another language becomes a matter of porting the existing tests, possibly writing new tests for more complete coverage, and then doing the work to make them pass. Because agents can quickly get deterministic feedback on their progress, they know exactly what work remains to be done, and they can fix their errors as they go.
Migration as a ratchet
The central idea in Crane is simple: migration progress should advance verifiably.
Every Crane migration defines four things:
- Source: the language, runtime, and paths being migrated from
- Target: the language or languages, runtime, and paths being migrated to
- Strategy:
in-place,greenfield, orauto - Verification: a command that prints a JSON health score
On the first run, Crane inventories the source, picks a strategy if you left it on auto, and writes a living plan broken into milestones. On later runs, it reads the plan, chooses the next milestone, implements one bounded step, runs verification, and accepts the change only if the new health score is at least as good as the previous one.
If verification passes and CI is green, Crane commits to a long-running branch for that migration, updates the plan, and advances the draft pull request. If verification fails, the change is discarded and the failed approach is recorded so the next iteration can try something else.
The health score
Crane expects each migration to define a verification command that outputs JSON with a migration_score between 0.0 and 1.0. The recommended convention is:
migration_score = correctness_gate * progress
The correctness gate is 1.0 only when the important correctness checks pass: source-side tests, target-side tests, parity tests, or whatever other project-specific evidence proves behavior is preserved. If correctness fails, the gate collapses to zero and the iteration is rejected.
Progress is the fraction of the migration that has been completed and verified. That can be measured by modules ported, APIs covered, parity cases passing, routes migrated, or a custom evaluator for the project.
A typical verifier might print something like this:
{
"migration_score": 0.42,
"progress": 0.55,
"source_tests_passing": true,
"target_tests_passing": true,
"parity_passing": 18,
"parity_total": 32,
"perf_ratio": 1.4
}
This is intentionally strict. If a migration has no useful tests, Crane will not pretend otherwise. The first useful milestone is often to characterize the existing behavior with tests or a parity corpus, then migrate against that evidence.
In-place or greenfield
Crane supports two migration strategies, with auto as the default.
| Strategy | Best when | How it moves |
|---|---|---|
in-place | The system is live, large, or has external consumers | Port one unit at a time, route callers through the new implementation, and remove the old unit only after verification passes |
greenfield | The source is small, self-contained, or too tangled to interleave safely | Build the target in parallel, prove parity against the source, and cut over when the target is complete |
State management
All of Crane’s migration state lives in markdown on a dedicated memory/crane branch. It is a human-readable, version-controlled artifact with inventory, strategy rationale, milestones, current focus, lessons learned, blockers, and iteration history.
Each migration also gets its own long-running branch:
crane/<migration-name>
The branch accumulates accepted work. The draft pull request gives humans the familiar review surface: diffs, CI, comments, and merge control.
Starting a migration
Crane migrations can be defined in three ways:
- A GitHub issue with the
crane-migrationlabel - A markdown file under
.crane/migrations/ - A directory under
.crane/migrations/<name>/with a migration definition, evaluator, fixtures, and parity corpus
The issue path is the quickest way to begin. A migration issue defines the source, target, strategy, verification command, and out-of-scope paths. Crane discovers labeled migration issues and runs them on schedule.
The directory path is better when verification needs supporting code: a parity corpus, custom evaluator, fixtures, or staged source and target trees.
Crane also registers a /crane slash command, so you can steer a migration from an issue or pull request comment:
/crane stats_py_to_ts: port the quantile family next
That instruction becomes part of the next iteration’s context, without requiring you to manually edit workflow files.
What Crane is for
Crane is for migrations with a clear before, a clear after, and an executable definition of “still works.”
Good candidates include:
- language migrations, like Python to TypeScript or Ruby to Go
- runtime migrations, like CommonJS to ESM or Python 2 to Python 3
- framework migrations, like Flask to FastAPI or Express to Fastify
- polyglot extractions, like moving hot loops from TypeScript or Python into Rust, Go, or C++ behind a stable boundary
Begin your migration today
Just ask your coding agent:
Install Crane using https://github.com/githubnext/crane/blob/main/install.md