feat(runner): tier-1 production follow-up — status/logs/doctor/--detach/multi-instance/launchctl-modern/runner_shutdown #28
merged
cole.christensen@gmail.com wants to merge
feat/runner-production-ready
into main
No CI
Summary
Lands the tier-1 production-ready follow-up work that PR #22 deferred, all in one PR per the "each PR is a release" convention.
- `runner status` / `runner logs` / `runner doctor` subcommands
- `runner start –detach` with size-based log rotation + new session (setsid via pre_exec defends the SIGHUP startup-race window)
- launchctl bootstrap/bootout/print with fallback to legacy load/unload/list; idempotent across casing and EEXIST(17)
- gui/ → user/ fallback for headless ssh
- `–service-name ` everywhere; per-instance state at `~/.anvil-runner/services/.json`; per-instance PID files; `runner service list`
- systemd ExecStart embeds `–pid-file` + `–parallel`; scope-aware `ProtectHome` (read-only for user, full for system+StateDirectory)
- Atomic-rename PID file with drop-only-if-still-ours
- `EndReason` (Finished / RunnerShutdown / Timeout) flows into the job-status update as `cancel_reason`, but only when the runner actually cancelled the job
122 tests pass; `cargo fmt && cargo clippy –all-targets – -D warnings` clean.
Closes #23.
Test plan
- `anvil runner doctor` returns non-zero when the PID is stale
- `anvil runner doctor –json` machine-readable on success and failure
- `anvil runner start –detach` survives `exit` on the launching terminal; `runner logs` tails the same file
- `anvil runner service install –service-name gpu1` co-installs next to a `default` install without colliding on PID file or unit
- `anvil runner service start –service-name gpu1` survives ssh logout
- `anvil runner stop –service-name gpu1` only stops the named instance
- On macOS: bootstrap fails over from gui/ to user/ on a headless ssh login
- Server receives `cancel_reason: runner_shutdown` when the runner is drained mid-job; does NOT receive cancel_reason on a job that fails normally or times out
🤖 Generated with Claude Code
Created Jun 05, 2026 at 01:56 UTC
| Merged Jun 07, 2026 at 00:41 UTC
by
cole.christensen@gmail.com