Anthropic RUSTed the BUN

1,009,257 lines of Rust · 6,755 commits · 10 days

Branch Topology Animation

6,755 commits · 276 merges · 10 days

loading…

How this was orchestrated

● Agent main (purple)

Claude Code writes Rust on claude/phase-a-port — ~9s per commit, no compilation, just write and record. 5,439 trunk commits, all timestamped UTC.

● Phase-f worktrees (cyan)

May 7 local git worktrees — parallel audit campaigns (unsafe-audit, send-sync, transmute, mem-zeroed, etc.) spun up locally and merged back. 651 commits, all before CI automation existed.

● unsafe-5k (blue)

The claude/unsafe-5k campaign: eliminating unsafe blocks across the codebase. Merged into main 27 times on May 12–13. 255 commits.

● Bench-until-green (amber)

Dedicated benchmark-repair branch, respawned every time CI flagged a regression. 218 commits across 19 merge cycles.

● CI auto-fix (red)

autofix.ci bot opens numbered branches (ci-auto-fix-53599, etc.) and patches failures. 49 commits — the outer verification loop.

● Divergence fix (violet)

divergence-fix-all, divergence-top5, zig-rust-divergence-audit — branches that caught Zig→Rust behavioral divergences. 47 commits.

● Flaky fix (pink)

flaky-stabilize v1/v2/v3 and fix-hot-h3-flakes. Targeted test instability rather than compile failures. 8 commits.

● Jarred (green)

88 commits from Jarred's Mac (timezone -0700) — architecture, CMake wiring, test pins, and the final merge. Every green dot is a human decision.

◯ Ringed dots are merge commits. Curved arrows show sub-branch tips flowing into the agent main lane — 276 total.

TL;DR

Speed Paradox

Commits land every nine seconds. Rust compilation takes minutes. The agent writes and commits without compiling.

73% of commits land in under 30 seconds. Nine seconds is roughly the inference time for ~430 output tokens at ~150 tok/s — generation latency, not build latency.

Write and verify run on different clocks

Fast inner loop
~9s median

Claude Code generates ~70 lines → git commit. No compilation. Just write and record.

Slow outer loop
hours

CI catches failures → sub-branches (ci-auto-fix, divergence-fix) repair them → merge back.

Writing is fast. Verification is async.

Commit timing — all 6,754 gaps

Gap between consecutive commits across the full PR

Fastest
0 ms
same-second batch (8.6%)
Median
9 s
inference latency, not build
Average
2.1 m
pulled up by CI waits
Slowest
6.15 h
SSLContextCache RAII
Distribution of inter-commit gaps
< 1 s
8.6%
1 – 5 s
28.5%
5 – 10 s
14.3%
10 – 30 s
22.0%
30 – 60 s
8.8%
1 – 5 m
10.6%
5 m – 1 h
6.7%
> 1 h
0.5%
73.4% under 30 s P75 33 s P90 2.9 m P95 8.6 m P99 40.6 m

Agent Architecture

The PR was not one agent typing in a straight line — it was a main porting branch with a fleet of specialized sub-agent branches feeding fixes back into it.

// branch topology — sub-agents merge INTO the PR branch
claude/phase-a-port ← the PR branch (main line of work)
├──< claude/unsafe-5k ×27 unsafe-block elimination campaign
├──< claude/bench-until-green ×19 continuous benchmark-tuning loop
├──< claude/flaky-stabilize* ×3 flaky-test repair
├──< claude/ci-auto-fix-{N} ×4 automated CI-failure handling
├──< claude/divergence-fix-all ×2 Zig→Rust spec-correctness fixes
└──< claude/security-audit-patches ×1 security review
// every branch is namespaced claude/* — 276 merge commits total

Each ×N is the number of times that sub-agent branch merged back into the main port branch — the campaign re-ran until its goal condition was met.

How compile verification evolved

The agent didn't start with CI branches. It ran three distinct verification strategies across the 10 days — each one a response to the scale of what it was building.

May 4 – 6  ·  ~3,000 commits
Local cargo check

Phase-b1 gated each of 96 crates individually — "bun_alloc compiles", "tier-3 — 11/11 crates compile". Phase-b2/d fixed compile errors inline, next commit. A cargo-check daemon ran locally; parallel agent processes read a shared /tmp/cargo-check.log. Fast because cargo check is 10–50× faster than a full build.

May 7  ·  First 52 merges
Local git worktrees

Phase-f introduced parallel local worktrees — one per audit campaign: option-ptr-ffi, send-sync-audit, transmute-static, mem-zeroed. Each ran independently and merged back locally. All 651 phase-f commits happened before any CI automation. Still no remote CI branches.

May 8 – 14  ·  CI era
Remote CI branches

cargo check can't catch link errors, test failures, or platform-specific bugs. Once the full binary was being built by CI, failures appeared that needed async repair. autofix.ci started spawning numbered branches automatically; bench-until-green, flaky-stabilize, and divergence-fix ran as their own parallel campaigns.

Commits by phase

Where the 6,755 commits actually went

What each phase means

phase-a — initial bulk drafts

May 04–05. The initial bulk-drafting phase, where the AI generated large translation batches — 12 to 90 files per commit — before the structured sequential phases began. The runway, before the engineering plan formalized.

phase-b — gating & verify

Wrapping Zig types behind Rust gates and verifying correctness one tier at a time before opening the floodgates.

phase-c — cleanup passes

Intermediate sweeps that tidy the tree between the heavy structural phases.

phase-d — the core port

The mechanical Zig→Rust translation of bun_runtime, the bundler, unsafe blocks, and TODO items. The single largest phase.

phase-e — RAII conversion

Giving Rust ownership of resources that Zig managed by hand — translating manual lifecycles into RAII patterns.

phase-f — further cleanup

A second cleanup wave after RAII conversion settles the ownership model.

phase-g — harvest

Collecting and cataloging remaining work — building the to-do list for the final phase.

phase-h — unsafe-wrap

Systematic wrapping of the 14,000+ remaining unsafe sites — the long tail of memory-safety work that a Rust rewrite is supposed to be about.

Engineering Plan

What makes this rewrite remarkable is what happened before a single line of Rust was written.

commit 2050a922ff00 · 2026-05-05 00:00:27 UTC · First commit of phase-b0
+++ scripts/crate-dag.ts (+175 lines)
#!/usr/bin/env bun
// Compute crate DAG, intended tiers, and back-edges for Phase B-0.
import { readdirSync, readFileSync } from "node:fs";
const TIER: Record<string, number> = {
bun_core: 0, bun_alloc: 0, wyhash: 0, // T0 — zero-dep primitives
js_parser: 4, bundler: 5, // T4/T5 — heavy subsystems
bun_jsc: 6, napi: 6, shell: 6, // T6 — top-level runtime
};

Commit message: phase-b0: add crate DAG analyzer

Before touching a single source file, the agent wrote a 175-line TypeScript program that maps the entire codebase: 96 crates, their intended dependency tiers (T0 zero-dep primitives through T6 subsystems), and which imports create cycles. You cannot safely extract a monolith into a multi-crate Cargo workspace without knowing the dependency graph first.

The migration pipeline

1 Instrument Build the crate-DAG analyzer to map cycles & tiers. 175-line tool
2 Spec Write design docs for dispatch, cycle-break, concurrency. 3 docs: commits
3 Break Cycles Delete spurious back-edges before extraction. 99 edits / 67 files
4 Extract Move verified crates to their tier locations. 44 crates moved
5 Scaffold Generate Cargo workspace with one manifest per crate. 96 manifests
6 Compile ↑ Compile crate-by-crate from lowest tier upward. bottom-up per tier

Six steps, executed in order, all visible in the commit history.

Phase-b1 — the compile loop in real time

Each phase-b1 commit is exactly one crate reaching green — gaps proportional to crate complexity:

Time (UTC) Commit Gap
04:09:11scaffold Cargo workspace (96 crates)
04:10:21prune back-edge deps from Cargo.toml+70s
04:14:12bun_alloc compiles+231s
04:17:40bun_core compiles+208s
04:18:14windows_sys compiles+34s
04:19:35collections compiles+81s
04:20:37unicode compiles+62s
04:22:26base64 + platform compile+109s
04:25:30errno compiles+184s
04:26:39uws_sys + 5 *_sys compile+69s
04:27:48ptr + safety compile+69s
04:30:09paths + string compile+141s
04:31:01sys compiles+52s

231 seconds for bun_alloc (foundational allocator, most complex), 34 seconds for windows_sys (small, mostly declarations) — gaps are irregular and proportional to crate complexity. The natural cadence of a compile→fix→recompile loop.

Migration Status

PR #30412 is a checkpoint, not a finish line. The original Zig stays in the repo as a reference layer while the port continues.

982,554
.rs lines — compiled & shipped

58% of native source. The Rust build owns the binary; Zig no longer compiles.

710,139
.zig lines — reference only

42% of native source. Not compiled, not shipped. Kept as semantic source-of-truth while porting continues.

113
files still pending

Zig files with no .rs sibling yet. The remaining porting queue.

Largest files still in the queue

File Subsystem Zig LOC
js_printer.zigJS code printer6,422
shell.zigShell interpreter4,706
sys.zigOS syscall layer4,703
resolver.zigModule resolver4,388
sys/windows/windows.zigWindows platform4,108
server.zigHTTP server3,855
scripts/port-batch.ts — still on the branch
// Reads manifest of remaining .zig files, checks for .rs sibling,
// emits batches of pending porting work for the next agent run.
const pending = zigFiles.filter(f => !existsSync(f.replace(/\.zig$/, '.rs')));
const batches = chunk(pending, BATCH_SIZE); // emit next wave

The migration did not stop when the PR merged. port-batch.ts is the next agent's instruction set.

Timeline

10 days, structured into clear phases.

Commits per day

May 04 – May 14, 2026 · n = 6,755

Day-by-day

Date What happened
May 04Phase A — initial AI draft batches. 20 commits, 12–90 files each. Work begins.
May 05Phase B2 — verify gates, tier sweeps, cycle fixes. 74 commits.
May 06PEAK — Phase E bulk porting of runtime modules. 2,906 commits.
May 07Phase F/G — merge campaigns, builder daemon, cleanup. 1,568 commits.
May 08Phase D — seam removal (Zig→Rust bridge layers). Jarred wires CMake build. First CI auto-fix branches. 750 commits.
May 09Phase H — Windows-specific porting. 250 commits.
May 10Slow day — Jarred's architectural decisions, test stabilization. 41 commits.
May 11Safety — noalias, unsafe reduction, CI flake fixes. 284 commits.
May 12Perf — unsafe-5k campaign (×27 merges), benchmark loop (bench-until-green ×19). 675 commits.
May 13Tests + docs — flaky stabilization, cleanup, remove internal porting notes. 176 commits.
May 14Final build fixes, merge to main. 11 commits.

Commit heatmap — 5-minute buckets

Click a day to switch. Each cell is one 5-minute bucket; darker = more commits. Hover for details.

fewer more

AI vs. Human

Every git commit object encodes the author's timezone offset. In a 6,755-commit history, that single field shows exactly who was at the keyboard.

6,667
author tz: +0000

Claude Code's automated harness runs in UTC. There is no local timezone because there is no human at a machine — just an agent loop with a system clock.

88
author tz: -0700

Jarred Sumner, committing directly from his Mac. Pacific time — consistent with every other commit he has ever made in this repository.

Jarred's entire pre-PR commit history on oven-sh/bun is overwhelmingly -0700 or -0800. The PR's 6,667 commits are +0000 — the fingerprint of an automated, timezone-agnostic environment. The 88 -0700 commits are the moments where Jarred reached in directly: build wiring, architectural decisions, and reverts.