Wie Bun nach Rust portiert wurde — und was wir daraus über die Zusammenarbeit mit Agenten lernen
Nachdem der Agent sich mit background command "Build Bun debug binary" completed (exit code 0) zurückgemeldet hatte und ich ein aus Rust kompiliertes Bun-Binary in den Händen hielt, gab ich Claude die nächste Aufgabe: diesen PR zu analysieren und einen Blogbeitrag darüber zu schreiben, wie Jarred Sumner den Zig-zu-Rust-Port hingekriegt hat — damit alle anderen lernen können, besser mit Agenten zu arbeiten. Was folgt, ist das, was zurückkam, möglicherweise mit Spuren des Mythos-Modells.
Am 14. Mai 2026 mergte Jarred Sumner PR #30412 — "Rewrite Bun in Rust" in oven-sh/bun. Es war einer der grössten einzelnen PRs in der Geschichte des öffentlichen Open Source: 2.188 Dateien geändert, 1.009.257 Zeilen hinzugefügt. Eine komplette JavaScript-Runtime — Bundler, Package Manager, HTTP/2/3-Stack, Dev Server, Transpiler, alles — wurde von Zig auf Rust neu implementiert, mit unveränderter Architektur und weiterhin grüner Testsuite.
Ohne Claude-Agenten wäre das mit hoher Wahrscheinlichkeit nicht möglich gewesen. Der PR sagt es nicht offen — im Beschreibungstext steht nur "compiler-assisted tools for catching & preventing memory bugs" — aber die Commit-Historie verrät die Orchestrierung. Zehn Minuten nach dem Merge pushte Jarred einen weiteren Commit mit dem Titel "Delete stray files", der 34 Dateien aus .claude/workflows/ entfernte. Diese Dateien, kurzzeitig in der Git-Historie öffentlich sichtbar, erzählen die Geschichte.
In diesem Beitrag geht es darum, was wir aus dieser Geschichte lernen können — nicht über Bun im Speziellen, sondern darüber, wie man eine Codebase, Instruktionen und Tools so aufsetzt, dass Agenten Arbeit erledigen, die Menschen allein nicht ausliefern könnten.
Die geleakten Beweise🔗
Das gelöschte Verzeichnis enthielt Workflow-Skripte, deren Namen einer bewussten Phasenreihenfolge folgten:
phase-a-port.workflow.js
phase-b0-cyclebreak / movein / moveout / verify.workflow.js
phase-b1-tier.workflow.js
phase-b2-cycle / fill / fix-bugs / keystone / ungate-tier / verify.workflow.js
phase-c-panic-swarm.workflow.js
phase-d-build-queue / crate-shard / todo-sweep / unsafe-audit /
blocked-on-resolve / bundler-perfile / bundler-shard /
recursive-ungate / subtree-batch.workflow.js
phase-e-body-port / mass-ungate / proper-port / test-bringup /
scopeguard-sweep.workflow.js
phase-f-probe-swarm / test-swarm.workflow.js
phase-g-mega-swarm / test-swarm.workflow.js
phase-h-ci-tasks / dedup / deep-dive / diff-review / idioms-audit /
libuv-audit / main-parity / unsafe-wrap.workflow.js
lifetime-classify.workflow.js
Und noch eine Datei, .apply-lock-guard.sh:
#!/bin/bash
# Serialize Apply-phase agents on /root/bun-5 working tree.
# Usage: flock /root/bun-5/.apply-lock bash .apply-lock-guard.sh -- <command...>
exec "$@"
Ein vierzeiliger flock-Wrapper, um parallel laufende Agenten zu serialisieren, die auf einem gemeinsamen Working Tree unter /root/bun-5 schreiben. Wer auch immer das ausgeführt hat, liess viele Agenten gleichzeitig am selben Checkout arbeiten und benutzte Dateisystem-Locks, damit sie nicht kollidieren.
Aufschlussreicher ist der Inhalt von phase-a-port.workflow.js:
export const meta = {
name: "phase-a-port",
description: "Phase A: draft .rs for a batch of .zig files (implement → verify → fix)",
phases: [
{ title: "Implement", detail: "one agent per .zig writes draft .rs per PORTING.md" },
{ title: "Verify", detail: "adversarial check of .rs against .zig + PORTING.md rules" },
{ title: "Fix", detail: "apply verifier findings to .rs" },
],
};
Ein Agent pro Zig-Datei. Ein zweiter, adversarialer Agent verifiziert gegen einen PORTING.md-Styleguide. Ein dritter wendet die Befunde des Verifiers an. Alle Ausgaben werden gegen ein JSON-Schema validiert (IMPL_SCHEMA, VERIFY_SCHEMA werden weiter unten definiert), damit die nächste Phase sie maschinell verarbeiten kann.
Was im Git Repo noch sichtbar ist🔗
Die Workflows wurden gelöscht, doch ihre Fingerabdrücke sind überall:
-
1.292
.zig-Dateien bleiben neben 1.432.rs-Dateien erhalten. DieCLAUDE.mdim Root wird deutlich (Zeile 141): "These are the original Zig implementation, kept only as a porting reference — they are not compiled and not shipped. New code goes in.rs. When fixing a bug or porting a behavior, the.zigsibling is the source of truth for intended semantics." -
10.841
PORT NOTE:/PERF(port):/TODO(port):Kommentare im Rust-Quellcode. Das sind im Schnitt sieben bis acht pro Datei. Sie sind nicht dekorativ. Schau dirsrc/bundler/defines.rsan:// PORT NOTE: Zig pre-counted `key_buf_len`/`e_strings_to_allocate` to size // two bump allocations, then `iter.reset()` and re-walked. With per-entry // copies the pre-sizing pass is dead — emit directly. // PERF(port): was single-buffer key arena; now per-entry Vec reuse.Jede Annotation erklärt, warum der Rust-Code vom Zig-Code abweicht — für den nächsten Agenten, der die Datei liest.
-
108 Cargo-Crates in einem einzigen Workspace, mit Präfixen
bun_core,bun_sys,bun_paths,bun_jsc,bun_runtime,bun_js_parser,bun_js_printer,bun_bundler,bun_install, … Winzige Crates sind nicht bloss eine architektonische Entscheidung — sie sind eine Parallelisierungsgrenze. Jedes Crate ist eine geshardete Arbeitseinheit, die ein Agent übernehmen, prüfen und unabhängig mitcargo check -p <crate>fertigstellen kann. -
Fest verdrahtete Branch-Namen.
CLAUDE.mdZeile 266: "Branch names must start withclaude/. This is a requirement for the CI to work." Das CI unterscheidet Agenten-Commits von menschlichen. -
Ein vorschreibender Styleguide, getarnt als Entwicklerdokumentation.
src/CLAUDE.md(14 KB) enthält eine sechsspaltige Tabelle, die festlegt, welchestd::-APIs verboten sind und was stattdessen zu verwenden ist —bun_sys::Filestattstd::fs::File,bun_paths::dirnamestattPath::parent,bun_core::env_var::*::get()stattstd::env::var, und so weiter. Keine Recherche, kein Ermessen. Die Agenten sollen kein "rustiges Äquivalent" suchen. Sie bekommen es vorgeschrieben. -
Ein "Code-Review-Selbstcheck"-Abschnitt in derselben Datei:
Before writing code that makes a non-obvious choice, pre-emptively ask "why this and not the alternative?" If you can't answer, research until you can — don't write first and justify later.
Und, ganz unverblümt, im Abschnitt Important Notes:
Be humble & honest — NEVER overstate what you got done or what actually works in commits, PRs or in messages to the user.
Den letzten Satz gibt es nur, weil jemand — viele Jemands — übertrieben haben.
Die Phasen als Denkmuster🔗
Lies die Phasennamen als Satz, nicht als Liste:
- A — einen Erstentwurf implementieren (ein Agent pro Datei)
- B0–B2 — Abhängigkeitszyklen aufbrechen, Typen zwischen Crates verschieben, sie in Schichten sortieren, damit jedes Crate kompiliert
- C — Panics aus der Codebase fegen ("panic-swarm")
- D — Arbeit pro Crate, pro Teilbaum, pro Bundler-Datei auffächern; TODOs abarbeiten und
unsafeauditieren - E — echte Funktionsrümpfe auf das nun kompilierende Skelett portieren
- F–G — Schwarm-Probes und Tests
- H — Diff-Review, Deduplizierung, Idiom-Audit, libuv-Parität,
unsafeaufräumen, CI-Cleanup
Das Muster lautet: erst ein Entwurf, dann ein struktureller Durchgang, damit alles kompiliert, dann die Funktionsrümpfe, dann die Tests, dann die Audits. Jede Phase hat andere Invarianten, andere Eingaben, andere Verifier-Prompts. Ein einziger "Portiere diese Codebase"-Mega-Prompt hätte Slop produziert — so heruntergebrochen wird jede Phase zu einem Problem, das klein genug ist, um es einem Agenten mit einem engen Kontrakt zu übergeben.
Übertragbare Lektionen🔗
Wenn du nur eine Sache mitnimmst, dann diese: Die Workflow-Skripte sind geleakt, weil die Agenten-Infrastruktur projektspezifisch war, nicht generisch. Jarred liess nicht einfach "Claude, portiere mein Repo" laufen. Er baute eine Portierungs-Harness für sein Repo, mit seinen Konventionen, seinem Crate-Layout und seinem Styleguide, und liess die Agenten dann dort hindurchlaufen. Das Modell ist dasselbe, das jeder benutzen kann; die Hebelwirkung kam von der Harness drumherum.
Konkrete Muster, die es zu klauen lohnt:
1. Behalte den alten Code als Grundwahrheit im Tree. Lösche den Zig-Quellcode nicht an dem Tag, an dem du den Rust-Code eincheckst. Agenten verlieren zwischen Sessions den Faden, was eigentlich beabsichtigt war; das Original ist die einzige stabile Referenz dafür, "was soll das hier eigentlich tun". Die 1.292 .zig-Dateien waren kein totes Gewicht — sie waren die Spezifikation.
2. Annotiere jede Abweichung mit dem WARUM, nicht dem WAS. PORT NOTE: reshaped for borrowck — drop deflate borrow before re-borrowing self. Dem nächsten Agenten muss man nicht sagen, dass Rust einen Borrow-Checker hat. Man muss ihm sagen, dass genau diese Zeile existiert, weil sich ein vorheriger Agent bereits durch die offensichtliche naive Variante gekämpft hat — und die nicht funktionierte. Ohne das entdeckt jeder Agent dieselben Workarounds neu und verhandelt sie wieder durch.
3. Schreibe einen vorschreibenden Styleguide, keinen aspirativen. Wenn du eine Meinung hast, codiere sie als Tabelle mit "verboten/Ersatz". Agenten sind ausgezeichnet im Regelnbefolgen und miserabel im Geschmackhaben. Lass sie nicht zwischen std::fs::File und deinem Wrapper wählen — sage du musst den Wrapper benutzen, hier ist der Wrapper, hier ist, wann welche Methode aufgerufen wird.
4. Trenne Implementieren, Verifizieren und Anwenden. Der Phase-A-Workflow hatte drei Rollen pro Datei: Entwerfer, adversarialer Reviewer, Korrektor. Der adversariale Agent sieht nicht das Reasoning des Entwerfers, nur dessen Ausgabe und die Regeln. Das zwingt den Entwerfer zur Explizitheit (weil der Reviewer implizite Annahmen aufdeckt) und stoppt jene Art von selbstbestätigendem Drift, die Single-Agent-Loops produzieren.
5. Verlange strukturierte Ausgaben zwischen Phasen. IMPL_SCHEMA und VERIFY_SCHEMA waren nicht optional — der Workflow validiert sie. Wenn Agenten an andere Agenten übergeben, geht in freier Prosa Information verloren. Lass sie ein JSON-Objekt mit rs_path, confidence, todos, rs_loc, note ausfüllen. Die nächste Phase parst das, anstatt erneut Aufsätze zu lesen.
6. Sharde die Arbeit für Parallelität, sperre die Schreibzugriffe. ~200 kleine Crates machen Parallelität billig, weil die Granularität von cargo check der Granularität der Agenten entspricht. Ein flock-Guard um den Working Tree macht die Parallelität sicher. Beides braucht es — die Architektur und den Mechanismus.
7. Kennzeichne Agentenarbeit im CI. Branches mit claude/-Präfix geben dir eine Möglichkeit, von Agenten erzeugte Änderungen getrennt von menschlichen zu gaten, zu zählen und rückgängig zu machen. Das ist die langweilige Art von Infrastruktur, die in dem Moment unverzichtbar wird, in dem dein Agentenvolumen dein Review-Volumen übersteigt.
8. Schreibe explizite Regeln gegen Konfabulation. "Übertreibe NIEMALS, was du geschafft hast." "Frage vorab: warum dies und nicht die Alternative?" Das liest sich wie Führungsratgeber, ist im Kontext aber tragend — die Regeln existieren, weil der Fehlermodus "alle Tests grün" eines Agenten, während sieben Crates gar nicht kompiliert haben, real ist und im System-Prompt adressiert werden muss, nicht erst im Code-Review.
9. Mache build und test zu einem einzigen Befehl. bun bd test <file> baut und führt aus. Agenten müssen keine Zweischritt-Beschwörungsformel im Kopf behalten, also können sie nicht scheitern, weil sie Schritt zwei vergessen. "Bring deine Tests zum Bestehen. Wenn du die Tests nicht ausgeführt hast, funktioniert dein Code nicht." — diese Anweisung wirkt nur, wenn Tests ausführen ein einziger Tool-Call ist.
10. Behandle die Meta-Infrastruktur als Teil der Arbeit. Das geleakte .apply-lock-guard.sh umfasst vier Zeilen. Die geleakten Workflows umfassen 4.000. Die CLAUDE.md-Dateien im Repo nochmals 30 KB. Nichts davon wird an die User ausgeliefert. Aber alles davon hat Bun ausgeliefert.
Die Fussnote, die du eigentlich willst🔗
Die Rust-Binary von Bun ist 3–8 MB kleiner, die Tests laufen weiterhin grün, die Benchmarks sind neutral bis schneller, und die Codebase ist jetzt in einer Sprache, in der der Compiler genau jene Bug-Klasse erkennt, die das Team historisch am meisten Zeit gekostet hat. Der Beweis für den Ansatz sind nicht die Workflows — es ist, dass eine der feindseligsten Software-Engineering-Aufgaben überhaupt (eine systemnahe Codebase von einer Million Zeilen in eine Sprache mit komplett anderem Speichermodell umzuschreiben, ohne die Kompatibilität zu brechen) ausgeliefert wurde, mit allen Tests grün, in einem einzigen PR.
Wenn du dich fragst, ob Agenten echte Arbeit verrichten können: Sie können. Die Frage ist, ob du bereit bist, den Agenten im Zaum zu halten und ihm ein Geschirr wie für Rösser zu bauen.