## Problem Agent CI still depended on the TypeScript runner as the main execution path. That made it harder to ship a small native binary and harder to prove the Rust path matched the TypeScript path. Review also found places where Rust and TypeScript could drift before the Rust path becomes the default. ## Solution This PR adds the opt-in Rust native `agent-ci` binary and platform npm packages while keeping the TypeScript fallback. It splits the Rust code into `agent-ci-core`, `agent-ci-runtime`, and a thin CLI/orchestration crate. Pure planning, reusable workflow expansion, event contracts, and run-result contracts now live in core; runtime owns Docker, DTU, runner, macOS VM, workspace, execution-plan adapters, and the generic wave worker pool. The fixture checks now use real TypeScript helpers instead of a shadow planner. Shared fixtures cover plans, events, run results, Docker socket resolution, and default job-limit CPU/memory vectors. The Rust runner honors `--jobs`, probes Docker memory for the default job limit, supports macOS VM concurrency limits, avoids nested runner container name collisions, has `--all` smoke coverage, and stages `bin/agent-ci` in every platform package. ## Validation - `cargo check --workspace --all-targets` - `cargo fmt --all -- --check` - `cargo clippy --workspace --all-targets -- -D warnings` - `cargo test --workspace --all-targets` - `pnpm check` - `pnpm fixtures:check` - `pnpm golden:cli` - `pnpm rust:smoke:parity` - `pnpm agent-ci-dev run --all -q -p --json --github-token` ## Notes Rust execution is still opt-in. The TypeScript implementation and fallback remain in place until the later default-switch and removal gates pass.
5.3 KiB
RFC: Rust Native Binary Rewrite
Status
Accepted as the working plan for the Rust rewrite checklist in ../rust-rewrite-tasks.md.
Problem
Agent CI is currently distributed as an npm-installed Node.js CLI. That works well for JavaScript users, but it ties every install path to a Node runtime and npm-compatible package installation. We want Agent CI to become a native binary so users can install it through npm, GitHub Releases, Homebrew, and other package managers without changing the CLI contract.
Goals
- Keep the existing
agent-cicommand and CLI behavior stable. - Keep
npm install @redwoodjs/agent-ciandnpx @redwoodjs/agent-ciworking. - Add direct native binary installs for users who do not want npm.
- Preserve compatibility with the official GitHub Actions runner.
- Preserve current pause/retry, cache, artifact, Docker, and macOS VM behavior.
- Migrate safely with tests that compare the Rust implementation against the TypeScript implementation before switching defaults.
Non-goals
- Do not rewrite the official GitHub Actions runner.
- Do not remove npm distribution during the migration.
- Do not switch the default implementation until smoke workflow parity is proven.
- Do not intentionally change the public CLI interface as part of the rewrite.
Language choice
The native implementation will be written in Rust.
Reasons:
- Mature ecosystem for HTTP servers/clients, async orchestration, Docker clients, YAML parsing, filesystem work, and cross-platform releases.
- Good binary distribution story for Linux and macOS on x64 and arm64.
- Strong type system and memory safety for a long-running orchestration tool.
- Good test tooling for golden CLI tests, integration tests, and benchmarks.
Migration strategy
Use an incremental side-by-side migration:
- Add a Rust workspace and native
agent-cibinary crate. - Implement low-risk CLI behavior first: help text, config/env loading,
clean,retry, andabort. - Add golden tests that compare Rust output to the current TypeScript CLI.
- Port workflow parsing and scheduling.
- Port the DTU server.
- Port Docker and macOS runner execution.
- Run the existing smoke workflows through the Rust binary.
- Switch the npm package launcher to prefer the Rust binary after parity is proven.
- Keep a temporary TypeScript fallback during the first native release window.
- Remove the TypeScript implementation only after the Rust binary has passed the full smoke suite and at least one release cycle.
Supported platforms
Initial native binary targets:
aarch64-apple-darwin— macOS Apple Siliconx86_64-apple-darwin— macOS Intelx86_64-unknown-linux-gnu— Linux x64aarch64-unknown-linux-gnu— Linux arm64
Windows is not an initial target because Agent CI does not currently support Windows workflow execution locally. The Rust code should avoid unnecessary Unix-only assumptions outside runner execution paths so Windows support can be evaluated later.
TypeScript fallback policy
The TypeScript implementation remains available until all of these are true:
- The Rust binary passes golden CLI tests against the TypeScript CLI.
- The Rust binary passes the existing smoke workflow suite.
- npm, direct binary, and Homebrew installs all run the same Rust binary.
- A rollback path is documented.
- A native release has shipped without blocking regressions.
The fallback can then be removed in a later cleanup task.
Install and distribution targets
npm
The npm package remains the compatibility install path.
Planned shape:
@redwoodjs/agent-cikeeps the public package name andagent-cibin entry.- The package ships a small JavaScript launcher.
- The launcher resolves a platform-specific optional package when available.
- If no platform package is available during the migration window, the launcher can fall back to the TypeScript implementation.
GitHub Releases
Each release should attach native binaries and checksums for the supported platforms. The assets are the source of truth for direct downloads, Homebrew, and the shell installer.
Homebrew
A Homebrew formula should install the macOS binaries from GitHub Releases and include a smoke test such as agent-ci --help.
Shell installer
A shell installer is optional, but if added it should:
- Detect OS and architecture.
- Download the matching release asset.
- Verify checksums.
- Install into a user-selected prefix.
Testing gates
Every checklist item should define the smallest meaningful test before being marked complete. Examples:
- CLI tasks: Rust unit tests and golden output comparisons.
- Config tasks: fixture-based environment and
.env.agent-citests. - Workflow tasks: fixture workflows and smoke workflow parity.
- DTU tasks: HTTP route tests and runner protocol fixtures.
- Docker tasks: container config unit tests plus local smoke workflows.
- Packaging tasks: archive/package smoke tests for each supported target.
Do not advance a task checkbox unless its specific tests pass.
Rollback plan
Until the default switch is complete, rollback is simple: keep the npm launcher pointed at the TypeScript implementation or restore the TypeScript fallback path. After the Rust binary becomes the only implementation, rollback should use a normal patch release that restores the previous known-good binary or re-enables the fallback launcher.