Migration Skill is a Codex-native, local-first protocol for behavior-preserving codebase migration.
It turns a migration request into an auditable sequence:
Discover → Contract → Judge → Freeze → Plan → Resume Preflight → Rewrite → Verify → Ratchet
The project is intentionally not another gpt-migrate Agent Runtime. Codex supplies repository reading, cross-file reasoning, editing, command execution, build, test, and debugging. This package supplies the pieces that need to stay deterministic across sessions:
- read-only inventory and migration-readiness evidence;
- a JSON Behavior Contract with atomic public operations;
- a separate parity Corpus of concrete inputs;
- a portable Source/Target adapter protocol;
- targeted Judge mutation validation;
- a frozen verifier bundle and source evidence digest;
- adaptive completion gates and atomic milestone checkpoints.
The current package is a v1.2 protocol implementation. It does not claim that an arbitrary repository can be migrated automatically or that a subprocess wrapper is a network sandbox.
Python 3.11 or newer is required. The deterministic helpers use the standard-library tomllib parser introduced in Python 3.11; the offline test path has no third-party Python dependency.
This repository has two equivalent distribution paths:
- Install the skills-only Codex Plugin described in
.codex-plugin/plugin.json. - Use
skills/migration-skill/directly as a repository-scoped raw Skill.
Both paths use the same canonical Skill package. The Plugin adds discovery and lifecycle management; it does not add an LLM SDK, MCP server, Docker orchestrator, or third-party Python package.
-
Run the read-only inventory:
python skills/migration-skill/scripts/inventory_project.py --root SOURCE --output .migration/inventory.json -
Have Codex turn the evidence into
.migration/migration.jsonschema v2 and.migration/parity-corpus.json. Every required operation must have evidence, and every required Corpus case must name itsoperation_id. -
Validate the Contract and Corpus:
python skills/migration-skill/scripts/validate_contract.py \ --contract .migration/migration.json \ --corpus .migration/parity-corpus.json -
Capture the Source baseline and execute the Source/Target adapters with
run_parity.py. If a Contract contains placeholders such as${PYTHON}, pass them explicitly with--var; validate a positive Source Judge and targeted negative controls withvalidate_judge.py. -
Freeze the source evidence and complete verifier bundle, then write
.migration/migration-plan.jsonand validate it withvalidate_plan.py. -
Before every new edit, run
verify_resume.py. After one bounded milestone, runevaluate_milestone.pyand accept its proof set only throughadvance_milestone.py. Run the finalevaluate_migration.pyonly after all milestones are complete.
The full command sequence and recovery rules are in skills/migration-skill/references/migration-workflow.md. Contract examples are in skills/migration-skill/references/behavior-contract.md.
The repository root is a skills-only Codex Plugin. Its manifest is
.codex-plugin/plugin.json, and its only Plugin component is the canonical
skills/migration-skill/ Skill. No MCP configuration is included. Follow the
official Codex Plugin documentation
when adding this repository to a Codex Plugin marketplace, and prefer a
reviewed release tag or Git ref for reproducible installation.
The current Plugin release candidate is 0.2.0-rc.1. Its compatibility policy is recorded in
docs/plugin-compatibility.json, and the
install, upgrade, rollback, uninstall, and frozen-verifier rules are recorded
in docs/adr/0001-plugin-distribution.md.
The release-candidate scope is recorded in
docs/releases/v0.2.0-rc.1.md.
Updating the Plugin cannot silently replace the verifier bundle frozen for an
in-progress migration; resume preflight must verify the recorded bundle first.
The host-level lifecycle probe and its current evidence are documented in
docs/plugin-lifecycle-test-report.md.
The probe is manual-only by default; use --execute-host only when changing
the local Codex marketplace and Plugin cache is explicitly authorized.
The isolated host replay evidence is published at
benchmarks/plugin-runs/20260901-plugin-python-node-001/.
It records successful marketplace/install/discovery and verifier-bundle
upgrade/rollback/uninstall checks, while keeping model-backed new-session
invocation explicitly blocked when the host transport is unavailable.
For each Corpus case, run_parity.py starts the selected Surface adapter with shell=False, sends one JSON object on stdin, and expects one JSON object on stdout:
{
"case_id": "health",
"surface_id": "public-http",
"operation_id": "GET-/health",
"input": {"method": "GET", "path": "/health"}
}The adapter returns:
{
"status": "passed",
"observed": {"status": 200, "body": {"ok": true}}
}The same Corpus is run against Source and Target. compare_results.py applies the frozen whole or fields comparator, including explicit text normalization and JSON semantic comparison.
Checks and adapters receive a minimum environment by default; host credentials are not implicitly inherited. Explicitly declared environment names that look like keys, tokens, passwords, credentials, or private keys are rejected. Secrets must not be placed in Contract, Corpus, target, logs, or result JSON.
The runners use shell=False, but this is not network isolation. Package installation, native code, external services, unknown scripts, and high-risk repositories require a real sandbox/Docker boundary or a deliberate PLAN_ONLY result. The source remains read-only and the target is isolated by default.
Read skills/migration-skill/references/safety.md before executing a repository with external side effects.
The deterministic evaluator emits exactly one of:
VERIFIED
PARTIALLY_VERIFIED
BLOCKED
PLAN_ONLY
INVALIDATED
VERIFIED requires an intact freeze, no new Source regression, all configured required Target checks, all required parity cases, complete required operation coverage, a valid Judge, all milestones in the validated plan, and no required gaps. Scores and percentages are informational only. A milestone can be eligible while future cases are missing; that intermediate result is not final VERIFIED.
.codex-plugin/plugin.json
skills/migration-skill/
├── SKILL.md
├── agents/openai.yaml
├── references/
└── scripts/
tests/
benchmarks/
docs/
README.md
LICENSE
The deterministic helper scripts use Python's standard library only.
skills/migration-skill/scripts/validate_skill.py validates the raw Skill
package locally; the Plugin Creator validator validates the Plugin manifest.
Run the same checks used by CI:
python -m unittest discover -s tests
python -c "from pathlib import Path; import py_compile; [py_compile.compile(str(path), doraise=True) for path in Path('skills/migration-skill/scripts').glob('*.py')]"
python skills/migration-skill/scripts/validate_skill.py --root skills/migration-skill
python benchmarks/run_regression.py --root . --run-id 20260831-python-cli-to-node-cli-001 --run-id 20260831-commonjs-to-esm-001 --output regression-smoke.json
GitHub Actions runs these checks on both Ubuntu and Windows. The regular matrix
job remains dependency-free; a separate Ubuntu Python 3.12
benchmark-regression job installs the exact benchmark-only requirements from
benchmarks/cases/flask-to-fastapi/requirements.txt
and gates on all three published runs returning VERIFIED. The Windows matrix
job keeps the core unit, compile, and Skill validation checks, but skips the
historical Python CLI replay smoke because its frozen Source test decodes
Unicode through the host Windows code page; run that replay locally with a
UTF-8-compatible environment when needed. Tests use unittest and temporary
directories and do not require Docker, network access, or third-party packages
outside the dedicated benchmark job.
The benchmark plan covers Python CLI → Node CLI, Flask → FastAPI, and CommonJS → ESM. Benchmark cases are blind: they publish Source, Contract, Corpus, plan, and mutation metadata but no pre-made Target. Complete VERIFIED runs with broken-Target rejection are published at benchmarks/runs/20260831-python-cli-to-node-cli-001/, benchmarks/runs/20260831-flask-to-fastapi-001/, and benchmarks/runs/20260831-commonjs-to-esm-001/. See benchmarks/README.md.
The dated runs also have an offline regression replay harness:
python benchmarks/run_regression.py \
--root . \
--matrix benchmarks/regression-matrix.json \
--output benchmarks/regression-report.json
It replays each published run from a temporary copy using the frozen verifier
bundle, checks the final VERIFIED result, and verifies the named broken-Target
controls. It needs no model, network, or credentials. If a benchmark's
documented third-party runtime is not installed, that run is reported as
blocked; use the explicit runtime options described in the
Migration Benchmark Rulebook. The harness uses
shell=False and a minimum environment but does not claim OS-level network
isolation.
Out of scope for v1 are production deployment, production database writes, cloud mutation, a GUI, a long-running Agent Runtime, a GitHub PR bot, automatic network isolation, arbitrary monorepo one-click migration, and in-place migration without a branch/worktree boundary.
The architectural lineage from gpt-migrate, Anthropic's Code Migration Kit, GitHub Next Crane, and sandboxed migration examples is documented in skills/migration-skill/references/upstream-lineage.md. No implementation code is copied from those projects.
Please report reproducible defects, unsafe behavior, or adapter compatibility issues through the repository's GitHub Issues. Maintenance should keep the verifier bundle, schema validation, mutation tests, cross-platform CI, and benchmark evidence in sync whenever a protocol rule changes.
The benchmark maintenance and evidence rules are documented in
docs/migration-rulebook.md.