schedule: every 5m
strategy: greenfield
source-language: python
target-languages: [go]
target-metric: 1.0
metric_direction: higher
completion-framework: deletion-grade-cli-parity-v2
APM CLI: Python to Go -- Full Rewrite
Complete rewrite of the APM CLI from Python to Go. The Python implementation
stays in place as the authoritative reference until the Go CLI proves complete
black-box equivalence. The end state is 100% Go for the shipping apm command,
but Python must not be removed until the deletion-grade parity gates below pass.
Current guidance supersedes prior completion claims.
Previous Crane iterations reported migration_score: 1.0 and marked this
migration complete, but that was not deletion-grade proof. The current Go CLI
still contains simplified command bodies and approved parity exceptions. This
issue is not complete until the stricter framework in
#96 is implemented and all gates here
are green.
Source
- Language: Python 3.10+ with Click/Rich
- Paths:
src/apm_cli/ -- authoritative Python implementation
tests/ -- Python reference test suite
- Role during migration: runnable oracle for CLI behavior, filesystem
mutations, generated artifacts, lockfiles, config, cache, and benchmark
comparison.
Target
- Language: Go
- Paths:
cmd/apm/ -- CLI entry point
internal/ -- internal packages mirroring Python behavior
pkg/ -- public library APIs if needed
- Bridge: none for the final shipping CLI. Temporary test-time use of
Python is only for parity verification.
Completion Definition
migration_score: 1.0 must mean the Python CLI can be removed from the
shipping path. It is not enough for TestParity* tests to pass, for help text
to match, or for Go command stubs to return successful exit codes.
Crane may mark this migration complete only when every gate is true:
python_reference_required = true
surface_parity = 100%
help_parity = 100%
functional_contracts = 100%
state_diff_contracts = 100%
known_exceptions = 0
go_tests = pass
python_tests = pass
benchmarks = pass
- release/install/cutover path ships the Go CLI or documents a temporary shim
with explicit removal criteria.
If any gate is false, the score must be less than 1.0.
Required Crane Framework Update
Implement the framework described in #96 before accepting further completion
claims:
- Missing
APM_PYTHON_BIN is a hard failure, never a warning or vacuous pass.
- Empty or cached Go test event streams must fail scoring.
- The score script must not derive completion from test names alone.
- "Approved exceptions", "simplified help", and "Go is more lenient" logs are
allowed only as temporary progress notes; final completion requires zero.
- Every PR commit on a Crane branch must run parity and benchmarks, then post
the benchmark and parity summary back to the PR.
Surface Parity Gate
Generate a CLI inventory for both Python and Go, then diff them.
The inventory must include:
- commands and subcommands
- aliases, including hidden aliases
- positional arguments
- options and flags
- defaults
- required/repeated/multiple-value behavior
- hidden/deprecated status
- invalid usage behavior
Fail on any missing or mismatched public behavior unless the issue explicitly
records a temporary non-cutover exception.
Help And Usage Gate
For every command and subcommand, compare Python and Go:
apm --help
apm <command> --help
apm <command> <subcommand> --help
- invalid option
- missing required argument
- unknown subcommand
Compare exit code, stdout, and stderr after only deterministic normalization.
Final cutover requires no help truncation exceptions.
Functional State-Diff Gate
Crane must use a black-box contract harness:
- Create two identical temp homes and repos.
- Run Python in one.
- Run Go in the other.
- Compare observable results.
Compare:
- exit code
- stdout/stderr
- files created, removed, and modified
- file contents
apm.yml
apm.lock.yaml
.apm/ package directories
- managed-file manifests
- generated
AGENTS.md, CLAUDE.md, Copilot, Codex, Gemini, Cursor,
Windsurf, and OpenCode files
- marketplace config
- user config
- cache layout where deterministic
- audit artifacts such as JSON, SARIF, and markdown
Mutating commands do not count as complete until their filesystem and config
effects match Python, not just their exit code.
Required Command Contract Matrix
Crane must cover the full supported Python CLI surface, including:
init: defaults, explicit project name, explicit targets, plugin mode,
marketplace mode, existing apm.yml, non-interactive behavior.
compile: all canonical targets, --target, --all, --clean,
--validate, --dry-run, generated file parity, orphan cleanup.
install: local bundle, local plugin directory, local skill bundle, fixture
git repo, transitive deps, skill subset, MCP deps, global scope, --frozen,
policy, collisions, --force, lockfile writes, managed-file writes.
uninstall: direct deps, transitive orphan cleanup, global scope, dry-run,
stale integrated files, manifest mutation.
update and deps update: no-op, changed refs, selected packages,
target-specific update, lockfile mutation, dry-run.
deps: list, tree, info, clean, global scope, insecure-only views.
pack and unpack: bundle contents, marketplace artifacts, manifest fields,
hidden-character audit, JSON output, dry-run, --force, --skip-verify.
marketplace: add/list/remove/update/browse/validate,
init/check/outdated/doctor/publish dry-run/migrate, package add/set/remove,
config mutation, fixture refs.
audit: package scan, arbitrary file scan, hidden Unicode findings, --strip,
--dry-run, text/json/sarif/markdown, --ci,
policy/no-policy/no-cache/no-drift.
policy: status, check, local policy, inherited policy, no-cache, output
format parity.
mcp: list, search, inspect/show, install, fixture registry, client config
mutation.
run, preview, and list: scripts, params, missing script, default script,
prompt output parity, verbose output.
config: get, set, unset, config path handling, missing config, invalid
key/value.
runtime: list, setup, remove, status, fixture runtime adapters.
targets: auto-detection, explicit apm.yml targets, --json, --all,
ambiguous/missing target behavior.
view: installed package metadata, missing package, versions using fixture
refs, global scope.
cache: info, clean, prune, cache path/env behavior.
prune: no-op, undeclared package removal, dry-run, integration cleanup.
outdated: missing lockfile, no outdated deps, outdated dependency fixture,
prerelease behavior where applicable.
self-update: --check, platform-specific messaging, no real install or
network in parity tests.
Fixture Policy
Parity tests must not depend on live GitHub, registries, or external services.
Use local git repos, local tarballs, local fixture registries, local HTTP
fixture servers, fake homes, fake config paths, and fake cache roots.
Real-network smoke tests may exist, but they are not cutover evidence.
Python Tests
Keep running the original Python unit suite while Python exists. Python tests
prove the reference still works; they do not prove Go parity. Cutover readiness
comes from black-box contracts that both Python and Go satisfy.
Benchmarks
Benchmarks must run on every Crane PR commit and post results to the PR. A
benchmark pass is required for final completion, but speed cannot compensate for
missing parity.
Crane Operating Rule
For every accepted iteration:
- Pick one Python CLI behavior.
- Add a black-box Python-vs-Go contract test for it.
- See the test fail for Go.
- Implement Go behavior.
- Confirm the contract passes.
- Update the PR/issue report with the exact behavior now covered and the
remaining gates still false.
No final cutover exceptions. No vacuous parity. No deleting Python until the
deletion-grade gate is green.
Verification
Minimum local checks:
uv sync --extra dev
go test ./...
APM_PYTHON_BIN="$PWD/.venv/bin/apm" go test -count=1 ./...
APM_PYTHON_BIN="$PWD/.venv/bin/apm" go test -count=1 -json ./... | go run .crane/scripts/score.go
python scripts/ci/migration_cli_benchmark.py \
--python-bin "$PWD/.venv/bin/apm" \
--go-bin /tmp/apm-go \
--json-out /tmp/migration-cli-benchmark.json \
--markdown-out /tmp/migration-cli-benchmark.md
The exact harness can be richer, but final completion must execute both CLIs
and compare observable behavior. A skipped Python invocation, a Go-only unit
test, or a help-only fixture is not completion evidence.
Out Of Scope During Migration
- Do not delete
src/apm_cli/ until all deletion-grade gates pass.
- Do not remove the existing Python tests while they are still the reference.
- Do not claim completion from benchmark speed alone.
- Do not rely on live external services for parity evidence.
schedule: every 5m
strategy: greenfield
source-language: python
target-languages: [go]
target-metric: 1.0
metric_direction: higher
completion-framework: deletion-grade-cli-parity-v2
APM CLI: Python to Go -- Full Rewrite
Complete rewrite of the APM CLI from Python to Go. The Python implementation
stays in place as the authoritative reference until the Go CLI proves complete
black-box equivalence. The end state is 100% Go for the shipping
apmcommand,but Python must not be removed until the deletion-grade parity gates below pass.
Source
src/apm_cli/-- authoritative Python implementationtests/-- Python reference test suitemutations, generated artifacts, lockfiles, config, cache, and benchmark
comparison.
Target
cmd/apm/-- CLI entry pointinternal/-- internal packages mirroring Python behaviorpkg/-- public library APIs if neededPython is only for parity verification.
Completion Definition
migration_score: 1.0must mean the Python CLI can be removed from theshipping path. It is not enough for
TestParity*tests to pass, for help textto match, or for Go command stubs to return successful exit codes.
Crane may mark this migration complete only when every gate is true:
python_reference_required = truesurface_parity = 100%help_parity = 100%functional_contracts = 100%state_diff_contracts = 100%known_exceptions = 0go_tests = passpython_tests = passbenchmarks = passwith explicit removal criteria.
If any gate is false, the score must be less than
1.0.Required Crane Framework Update
Implement the framework described in #96 before accepting further completion
claims:
APM_PYTHON_BINis a hard failure, never a warning or vacuous pass.allowed only as temporary progress notes; final completion requires zero.
the benchmark and parity summary back to the PR.
Surface Parity Gate
Generate a CLI inventory for both Python and Go, then diff them.
The inventory must include:
Fail on any missing or mismatched public behavior unless the issue explicitly
records a temporary non-cutover exception.
Help And Usage Gate
For every command and subcommand, compare Python and Go:
apm --helpapm <command> --helpapm <command> <subcommand> --helpCompare exit code, stdout, and stderr after only deterministic normalization.
Final cutover requires no help truncation exceptions.
Functional State-Diff Gate
Crane must use a black-box contract harness:
Compare:
apm.ymlapm.lock.yaml.apm/package directoriesAGENTS.md,CLAUDE.md, Copilot, Codex, Gemini, Cursor,Windsurf, and OpenCode files
Mutating commands do not count as complete until their filesystem and config
effects match Python, not just their exit code.
Required Command Contract Matrix
Crane must cover the full supported Python CLI surface, including:
init: defaults, explicit project name, explicit targets, plugin mode,marketplace mode, existing
apm.yml, non-interactive behavior.compile: all canonical targets,--target,--all,--clean,--validate,--dry-run, generated file parity, orphan cleanup.install: local bundle, local plugin directory, local skill bundle, fixturegit repo, transitive deps, skill subset, MCP deps, global scope,
--frozen,policy, collisions,
--force, lockfile writes, managed-file writes.uninstall: direct deps, transitive orphan cleanup, global scope, dry-run,stale integrated files, manifest mutation.
updateanddeps update: no-op, changed refs, selected packages,target-specific update, lockfile mutation, dry-run.
deps: list, tree, info, clean, global scope, insecure-only views.packandunpack: bundle contents, marketplace artifacts, manifest fields,hidden-character audit, JSON output, dry-run,
--force,--skip-verify.marketplace: add/list/remove/update/browse/validate,init/check/outdated/doctor/publish dry-run/migrate, package add/set/remove,
config mutation, fixture refs.
audit: package scan, arbitrary file scan, hidden Unicode findings,--strip,--dry-run, text/json/sarif/markdown,--ci,policy/no-policy/no-cache/no-drift.
policy: status, check, local policy, inherited policy, no-cache, outputformat parity.
mcp: list, search, inspect/show, install, fixture registry, client configmutation.
run,preview, andlist: scripts, params, missing script, default script,prompt output parity, verbose output.
config: get, set, unset, config path handling, missing config, invalidkey/value.
runtime: list, setup, remove, status, fixture runtime adapters.targets: auto-detection, explicitapm.ymltargets,--json,--all,ambiguous/missing target behavior.
view: installed package metadata, missing package, versions using fixturerefs, global scope.
cache: info, clean, prune, cache path/env behavior.prune: no-op, undeclared package removal, dry-run, integration cleanup.outdated: missing lockfile, no outdated deps, outdated dependency fixture,prerelease behavior where applicable.
self-update:--check, platform-specific messaging, no real install ornetwork in parity tests.
Fixture Policy
Parity tests must not depend on live GitHub, registries, or external services.
Use local git repos, local tarballs, local fixture registries, local HTTP
fixture servers, fake homes, fake config paths, and fake cache roots.
Real-network smoke tests may exist, but they are not cutover evidence.
Python Tests
Keep running the original Python unit suite while Python exists. Python tests
prove the reference still works; they do not prove Go parity. Cutover readiness
comes from black-box contracts that both Python and Go satisfy.
Benchmarks
Benchmarks must run on every Crane PR commit and post results to the PR. A
benchmark pass is required for final completion, but speed cannot compensate for
missing parity.
Crane Operating Rule
For every accepted iteration:
remaining gates still false.
No final cutover exceptions. No vacuous parity. No deleting Python until the
deletion-grade gate is green.
Verification
Minimum local checks:
The exact harness can be richer, but final completion must execute both CLIs
and compare observable behavior. A skipped Python invocation, a Go-only unit
test, or a help-only fixture is not completion evidence.
Out Of Scope During Migration
src/apm_cli/until all deletion-grade gates pass.