Your agent can't quietly wreck this codebase.
VanillaBun is a zero-dependency Bun foundation whose architecture is enforced by tests — and the Claude Code skill that teaches an agent the one rule that keeps it enforced.
/vanillabun
This page is the template, deployed. Same one process, no build step.
Every drift is individually reasonable
An agent doesn't destroy a codebase in one commit. It does it in forty defensible ones — and the last thing it touches is the check that would have told you.
“Just this one dependency.”
Then forty, and a lockfile nobody can audit.
“This file is getting long.”
Then it's 900 lines and every change touches it.
“The cap was too strict anyway.”
The check goes green. The architecture is already gone.
Three files do the work
-
1
CLAUDE.mdThe contract. Auto-loaded into every session, so the agent starts knowing the rules instead of inferring them from the code.
-
2
core/architecture.test.tsThe enforcement. Nine invariants the compiler and test runner check on every run. Prose advises; tests enforce.
-
3
.claude/skills/vanillabunThe playbook. The build loop, the canonical shapes — and the failure-mode rule below, which is the whole point.
When a guard goes red: fix the code, never the guard.
Raising a cap, widening a name exclusion, loosening an assertion, deleting a test — each one makes the build pass. Each one is drift wearing a green check. The skill tells the agent to decompose or rename instead, every time.
If a cap is genuinely wrong, that's a deliberate architecture decision for a human to make out loud — never a side effect of getting a feature to pass.
Nine guards, checked every run
Not conventions in a style guide. Assertions in bun run verify.
Zero runtime npm deps
package.json dependencies must stay {}. Vendor the browser lib, or write it in a few lines.
core/ ≤ 450 LOC
The framework cannot quietly accrete. Over budget means move it to app/, or delete it.
File ≤ 200 LOC
A God file is a design failure, not a formatting one. Over the cap means decompose.
Function ≤ 70 LOC
Measured through the TypeScript compiler API — no new dependency just to count lines.
No junk-drawer names
util, helper, manager, misc. Name the domain, or don't add the file.
No exported mutable state
Exporting a mutable binding creates a hidden global anyone can write. Caches stay module-private.
Parameterized SQL only
No template interpolation into query(), prepare() or duck(). Ever.
Contract stays in sync
Every core/ file must be documented in CLAUDE.md, or the build goes red.
Vendored libs pinned
sha256 + provenance recorded. An unexpected mismatch is a supply-chain signal.
What you're building on
- One Bun process is the whole app — Bun.serve, bun:sqlite, Bun.password, Bun.file.
- No build step. Client TypeScript is transpiled on demand, per request, mtime-cached.
- SSR every page, then boosted navigation swaps <main> — direct links, no-JS and SEO still work.
- Heavy browser libs are vendored and hash-pinned, not installed. Still zero runtime deps.
- Ships an adversarial architecture-reviewer agent for the design judgment tests can't reach.
Start
Open the template in Claude Code and type the skill. It reads the contract,
copies the canonical shape, writes the test first, and won't call it done until
bun run verify is green.
/vanillabun