ADR-139: iterion stays Go; other languages enter as sidecars behind seams
- Status: Accepted
- Date: 2026-10-27
- Authors: devthejo (session with Claude), operator-arbitrated
- Code: the seams it protects — pkg/runtime/engine.go (
NodeExecutor), pkg/backend/delegate/delegate.go (delegate.Backend), pkg/eventbus/bus.go (eventbus.Bus); sidecar precedents: rtk (Rust output rewriter, plugins-compression), the agent CLIs (ADR-065/ADR-085), ADR-027 (hostgit)
Context
A proposal reached the operator: with today's coding agents able to write large codebases quickly, migrate iterion from Go to Rust — framed alongside the dependency-internalization proposal this ADR's sibling (ADR-142) disposes of.
The measured shape of the codebase decides most of it:
- iterion is a concurrency engine. The product carries 538
go funcsites, 735selectblocks and ~7,800context.Contextoccurrences; its shutdown budgets are hand-calibrated against the Go scheduler's preemption slice (pkg/eventbus/bus.go), with lossy fan-out and per-subscriber panic isolation as documented contracts. - The proof mass for those contracts is CI's sharded
-racepass over the whole tree plus ~3.3k test files and shared conformance suites (pkg/eventbus,pkg/runwatch,pkg/credusage). That proof verifies behavior against Go's memory model and scheduler; a port inherits none of it and must re-earn it against tokio's, with no differential oracle. - Agents discount the cost of writing a port, not of proving one: the goldens, conformance suites and calibrated budgets are the expensive half, and they must be rebuilt or re-earned rather than translated.
- Velocity is the second half of the cost: ~890 commits/month on a ~7-month old product. A port freezes feature work or forks it across two languages while the DSL, backends and contracts are still moving.
- What Rust would buy here is not measurable: the workload is I/O-bound orchestration; the distribution constant (single static
CGO_ENABLED=0binary, ADR-019, ADR-003) is already met; memory safety is already met by Go; the maintenance pains actually recorded (reliability audit) are distributed-systems reconciliation, independent of language.
Decision
- The engine language is Go. No port, no dual-language core.
- Other languages enter as sidecars behind seams: separate processes holding a process-level contract (CLI arguments, line-delimited JSON on stdio, HTTP), with their own release and lifecycle. rtk (Rust), the agent CLIs and the host
gitbinary are the precedents. A sidecar is an implementation of an existing seam (delegate.Backend, plugins), never a new engineifand never in-process FFI. - Revisiting needs a new ADR with a proof plan: differential goldens against the Go implementation, a race/invariant strategy for the target runtime, and a measured trigger that forced the question — not an anticipated one.
Consequences
- The concurrency contracts keep their proof infrastructure; the golden and conformance corpus stays authoritative for both sides of future seams.
- Hiring and contribution stay on one language; the review loop (adversarial rounds) keeps one vocabulary.
- Accepted cost: sidecars re-introduce the text-boundary work that ADR-023 and ADR-024 paid for on the git seam — each sidecar owns its framing, locale and version pins.
- The paid-knowledge asymmetry stands: seven months of calibrated contracts and recorded traps (the tree) remain assets of this implementation, and a port would reset them.
