ADR-009: First-class Bun runtime support (runtime + compile target only)
Date: 2026-07-02 Status: Proposed (per bun-runtime/RD-01, drafted) Source:
codeops/features/bun-runtime/requirements/RD-01-bun-runtime-support.md(AR-1…AR-10)
Context
jsvision apps should be shippable as single self-contained executables (the Claude Code distribution model), which requires bun build --compile and therefore the Bun runtime. An empirical analysis (2026-07-02, Bun 1.3.14) showed the whole stack already runs on Bun unmodified: all 1,105 unit tests pass on the Bun runtime, the interactive host lifecycle (raw mode, alt-screen, mouse, SIGWINCH resize, SIGTSTP/SIGCONT suspend/resume, restore-on-every-exit-path, /dev/tty stream construction, exit-hook fs.writeSync) was verified under a real PTY, and compiled binaries behave byte-identically to interpreted runs. This works because the runtime surface is deliberately small and seam-guarded (ADR-003's injectable RuntimeAdapter; ADR-001's zero dependencies) and Bun implements every touched Node API compatibly. But nothing guaranteed it: no CI ran Bun, no version was supported, no consumer-facing surface declared it.
Options Considered
Option A: First-class runtime — merge-blocking Bun CI lane
- Pros: Real guarantee; regressions caught at merge time; matches the goal of shipping binaries.
- Cons: CI cost (3 extra jobs); exposure to upstream Bun regressions (mitigated by a triage policy + temporary pin escape hatch).
Option B: Best-effort — non-blocking informational lane
- Pros: Cheaper; immune to Bun-release flakiness.
- Cons: A Bun regression can land silently, defeating the shipping story.
Option C: Compile-target only — Node stays the sole supported runtime
- Pros: Minimal surface.
- Cons: Weakest promise; the compile target still needs the runtime to work, so the coverage gap is illusory.
(Sub-decision) Bun-native APIs: adopt now vs benchmarked spike vs never
Full adoption (Bun.stdin, bun:ffi, Bun test runner) would fork the codebase and violate ADR-001/check:deps's spirit; never-investigate leaves potential wins unknown.
Decision
Option A — Bun is a first-class, merge-blocking runtime and compile target, scoped strictly to runtime: yarn 1.x stays the package manager, vitest the test runner, tsc the build. Supported floor Bun ≥ 1.3; CI runs latest stable on 3 OSes (unit suites on the Bun runtime + per-OS compile smokes + 5-target cross-build verification + a PTY-driven compiled-binary acceptance e2e wired into yarn gate). Bun-native fast paths only via a benchmarked spike behind the RuntimeAdapter seam with a ≥ 20% median-improvement adoption bar (pure JS, runtime-detected, behavior-identical); bun:ffi and the Bun test runner are excluded outright.
Rationale
The empirical result made Option A cheap: first-class support needs zero code changes, only guarantees. Shipping binaries is the stated product goal, and a non-blocking lane (B) or compile-only promise (C) cannot back that goal — a silent runtime regression breaks every shipped executable. The single-codebase constraint (one I/O path, Node canonical) preserves ADR-001/ADR-003; the ≥ 20% bar ensures a second codepath is only ever bought with real performance rent.
Consequences
Positive
- "Runs on Bun" and "compiles to a self-contained binary" become continuously proven properties, not dev-box anecdotes.
- The restore/signal safety oracles (the properties that keep terminals usable) gain a second-runtime execution, a net coverage gain.
- The recipe is dogfooded: the docs quote the same script the e2e executes.
Negative
- Three more required CI jobs; a latest-stable policy means upstream Bun regressions can redden CI until triaged (pin escape hatch documented).
- Windows interactive-TTY behavior remains manually verified (ConPTY cannot be driven under the no-new-deps rule, ADR-004) — a documented checklist, not CI.
Risks
- Bun's Node-API compatibility could drift in a subtle, untested corner — bounded by running the full unit suites + restore e2es on Bun every merge.
- The ~90–100 MB binary size (embedded runtime) may surprise adopters — stated in the shipping guide with the embedded-source and unsigned-binary caveats.