oven-sh/bun
Read `CLAUDE.md`, `REVIEW.md`, `src/AGENTS.md`, and `src/js/AGENTS.md` before writing anything — they encode rules that are enforced, not suggestions. The single most important operational fact: never run `bun test` or `bun <file>` directly; use `bun bd` (which builds the debug binary and execs it with trailing args) so your native changes are actually exercised. Prefer in-tree helpers over `std` — `clippy.toml` mechanically bans `std::fs::*`, `std::env::var`, `std::thread::spawn`, and `std::mem::zeroed`. Add tests to the existing file for the module you changed, not a new file, and reserve `test/regression/issue/<n>.test.ts` for real regressions with a real issue number. Branch names must start with `claude/`. The one documented exception to the build requirement is `packages/bun-types/**/*.d.ts`, which can be tested with the system Bun.
- Build and run with `bun bd` — it builds the debug binary and passes trailing args through to it; `bun bd test <file>` is the only valid way to run tests against your changes, and `bun bd` must not be given a timeout.
- Every build script supports build-then-exec with the same arg routing: build flags first, then the first unrecognized arg (or an explicit `--`) starts the exec args, and build output is suppressed unless the build fails.
- `clippy.toml` is a hard boundary: it lists disallowed methods with replacement guidance — `std::fs::*` → `bun_sys::file`/`dir`, `std::env::var` → `bun_core::env_var`, `std::thread::spawn` → `bun_threading::spawn_named`, `std::mem::zeroed` → `MaybeUninit` (zeroed is UB for most Bun types).
- Error handling follows a fixed shape: each crate defines a `thiserror` enum at `<crate>/error.rs` re-exported as `crate::Error`/`crate::Result`; the syscall boundary uses `bun_sys::Maybe<T>`; OOM routes through `bun_alloc::AllocError`; and `.unwrap()` is only for invariants you can prove, never for anything user input or the OS can reach.
- `mordant.toml` runs a ratcheting Rust lint pass against `mordant-baseline.toml`: a PR fails only when it adds a new finding for an existing (lint, file) pair, so expect to either fix or regenerate the baseline via `bun run rust:mordant:baseline`.
- Platform-gated code is not type-checked unless that target is built — run `bun run rust:check-all` (which drives `scripts/rust-check-all.ts` across linux/macos/windows × x64/aarch64) after touching any `#[cfg(...)]` branch.
- Built-in JS in `src/js/` must be tamper-resistant: `$`-prefixed intrinsics and primordial-safe calls (`$isJSArray`, `map.$get`, `$call`), globals captured at module load, `node:`-prefixed requires, and never routing internal logic through user-overridable machinery like `Array.isArray`.
- Tests must copy harness conventions exactly: `tempDir` from `"harness"` (never `tmpdirSync` or `fs.mkdtempSync`), `port: 0` always, `normalizeBunSnapshot` for snapshot output, `Promise.all` to drain stdout/stderr/exited concurrently, and stdout assertions before the exit-code assertion.
- Never use `setTimeout`/`sleep` to wait for a condition in a test — poll with a deadline or await the event; reviewers treat a bare sleep of 50ms or more outside a bounded poll loop as requiring a comment naming why no observable signal exists.
- The single most-repeated trap in this repo's instructions: the JSC conservative stack scanner is never the cause of a bug you are debugging; a surviving object is held by a real reference, and blaming the scanner requires a heap snapshot plus a debugger session as PR artifacts.
- Arena allocation is a known memory trap: values in a `bumpalo`/`Arena` do not run `Drop` on reset, so anything owning a heap allocation or refcount must be freed explicitly first, mirroring the original Zig `deinit()` ordering.
- Verify exception-check correctness with `BUN_JSC_validateExceptionChecks=1 BUN_JSC_dumpSimulatedThrows=1 bun bd <command>` rather than adding suppression-list entries, and silence debug logging with `BUN_DEBUG_QUIET_LOGS=1` or enable one scope with `BUN_DEBUG_<SCOPE>=1`.
- PR feedback is read with `bun run pr:comments` (which paginates past 100 and filters bot noise) rather than `gh`, and CI failures with `bun run ci:errors` / `ci:status` / `ci:logs`, which wrap the BuildKite CLI via `scripts/find-build.ts`.
- Situational review rules are deliberately kept out of the main file: `.claude/docs/landing-prs.md` holds the Node/Web-compat, API-design, performance, cross-platform, vendoring, docs/types, and PR-process sections, to be read before the work each covers.
- Repo-specific skills exist for the recurring hard tasks and should be preferred over improvising: implementing JSC classes in C++ or Rust, the JSC garbage-collector reference, `bun_sys` system calls, and writing bundler and dev-server tests.