ADR-0002: Rewrite Caproni in Rust as Xianyun
Status: accepted · Date: 2026-09-28 · Decision makers: James Hebden
Context and Problem Statement
Caproni is a Go CLI that orchestrates local Kubernetes development environments. The fork’s goal is narrower than the original: a developer-focused validation tool and verification driver that an AI agent can run in a loop, read structured results from, and act on without a person parsing terminal text in between. Go’s error strings and best-effort goroutines make that contract harder to guarantee than Rust’s typed errors and join-before-return task sets.
Behaviour that existing users depend on (config files, CLI surface, cluster profile names) has to survive the change.
Decision Drivers
- Typed results and specific error variants that callers can match on
- Concurrency with a guarantee that every supervised task finished before success is reported
- A test-first compatibility contract: each ported behaviour has a test proving the same thing the Go test proved
- Zero migration cost for existing
caproni.yamlprojects - Ability to build and test hermetically with Nix
Considered Options
- Rewrite in Rust as a new workspace beside the Go code, keeping the Go tree as the reference
- Keep Go and add a structured-output mode and an MCP server to the existing code
- Wrap the Go binary from a thin Rust or Python agent shim
Decision Outcome
Chosen option: “Rewrite in Rust beside the Go tree”, because the typed-error and concurrency properties are the point of the fork, and a wrapper (option 3) would inherit Go’s untyped failures. Option 2 is the cheapest, and its main weakness is that join-before-return and exhaustive error matching are conventions in Go rather than properties of the language.
The rewrite proceeds in phases: port the Go test suite as ignored tests (red), implement crate by crate until they pass (green), then add agent-first interface work (structured output, tool surface) once behavioural parity is solid.
Consequences
Good:
- On Linux, 1,119 tests pass, 0 fail, and 23 are ignored (measured 2026-10-13 with
cargo test --workspace); 22 of the ignored have a documented reason and one,compile_schema_lazy_loading, is anunimplemented!stub - The MCP server, typed errors, and
JoinSetsupervision inxianyun runwere possible because of the rewrite
Bad:
- Two implementations live in one repository. Documentation still describes the Go binary almost everywhere (see the all-aboard report for the list).
- Parity isn’t complete:
edit-mode-process-supervisoris a stub;doctorhas 2 of the Go tool’s 5 diagnostics. - The
serde_yamlcrate used for parsing is deprecated
Neutral:
- The binary was renamed from
capronitoxianyun; see ADR-0003
Implementation Location
crates/xianyun-core/src/lib.rscrates/xianyun-cli/src/main.rscrates/README.mdbook/src/project/vision.md