Codebase Guidelines
August 22, 2026 · View on GitHub
Simplicity First
- When renaming a domain concept, search project-wide for stale names in variables, files, query keys, constants, tests, and docs. TypeScript only catches type references.
Types And Contracts
- Validate and parse data at system boundaries, then pass typed values internally.
- Avoid
unknownandas Xcasts inside the system. Use them only at genuinely unknowable boundaries such as freeform tool input, then narrow immediately. - Keep one-off types near the code that uses them. Move types to a shared package only for a real cross-package contract.
- Optional contract fields are allowed only when omission has real semantic meaning. Do not use optional or nullable fields to hide defaults.
- If a field has a default, fill it in once at the server boundary and pass the explicit value through internal routes, commands, and persisted events.
- Accepted-but-ignored route or command fields are forbidden. Delete them or implement them end to end.
- Add or update route and command documentation only when behavior is non-obvious.
Server And Daemon
- The server owns product policy: defaults, instructions, manager behavior, tool lists, and thread behavior.
- The host daemon owns host-local primitives, provider translation, runtime/session management, and workspace execution.
- If the server needs host-local data, the daemon should return raw data and the server should assemble product behavior.
- Do not move responsibility across the server/daemon boundary unless the current change requires it.
- Always increment
HOST_DAEMON_PROTOCOL_VERSIONwhen a change can alter anything sent between the server and host daemon. This includes adding, removing, renaming, or changing the type, requiredness, default, or meaning of fields in session payloads, WebSocket messages, host RPC commands, or host RPC results. A shared TypeScript build passing is not evidence of wire compatibility: enrolled machines can still be running an older daemon. The version mismatch is what triggers their automatic update; without a bump, an old daemon may connect successfully and then enter aninvalid-messagereconnect loop. If compatibility with the previously shipped daemon has not been deliberately preserved and tested, bump the version.
CLI, Guide, And Skill
- When you add or change a
bbCLI command, flag, or a user-facing configuration knob (env var,.bb/workspace file, settings field), update its discoverable surfaces in the same change. See docs/cli-guide-and-skill.md for which surfaces to update. - Every end-user feature must also be usable by agents through both the SDK and the
bbCLI; ship and document those surfaces in the same change as the UI.
Plugin API
- Any new public plugin API member (a
@get-bb/plugin-sdk/appexport, anapp.slots.*method, or aBbPluginApiproperty) ships with anexperimental_name prefix and an entry in docs/api_to_audit.md describing what it does and what to audit before stabilizing. Dropping the prefix is the deliberate stabilization step: audit the entry, rename project-wide, and remove it from the doc in the same change.
Data Access
- Do not load all rows and filter in JavaScript when a targeted query with
WHEREorJOINis possible. - Add indexes only when they are required by the new or changed query.
- Do not manually edit Drizzle snapshot JSON. Change the schema, then regenerate migrations/snapshots with Drizzle so the snapshot chain stays consistent.
- Never mock the database in tests. Use in-memory SQLite via
createConnection(":memory:")plusmigrate(db).
UI
- Prefer sanctioned typography tokens over arbitrary
text-[Npx]classes. - Derive theme color tokens from the
--canvas/--inkanchors (color-mix(in oklch, var(--ink) N%, var(--canvas))) or from another derived token — never hand-set anoklch(L 0 0)literal. Achromatic literals don't follow custom palettes (Nord, Dracula, …), which re-anchor only--canvas/--ink, so a hardcoded token strands a neutral-gray element in an otherwise tinted UI. Mix opaque stepsin oklch; mix translucent steps (atransparentpole)in oklabso the hue survives.apps/app/src/components/ui/theme.cssis the source of truth andtheme.test.tsguards it. - Never scope styles with the CSS
@scopeat-rule. WebKit resolves scope containment per element per scoped rule with no selector bucketing, so a rule set inside@scopecostselements × ruleson every style recalculation. Measured on a 2,635-element page: one plugin's Tailwind utilities layer inside@scopetook 306ms per recalculation, 7ms after rewriting to a:where()prefix, against a 6ms floor for the whole document. Blink shows none of it, so this is invisible in Chrome and dominant in Safari. To confine rules to a subtree, prefix each selector with a zero-specificity:where(<roots>)arm plus a:where(<roots>)compound arm —packages/plugin-build/src/scope-plugin-utilities.tsdoes this for every plugin's compiled stylesheet and explains why both arms are required. When style recalculation is slow, bisect it: disable stylesheets one at a time and timegetComputedStyleafter invalidating a custom property on:root. - Use the shared persistent responsive drawer for every compact slide-out menu, picker, popover, and dialog. Do not use modal drawer primitives that add
inertoraria-hiddento the app root: iOS Safari can recalculate styles for the full app tree and stall the interaction. Start the drawer transform before heavy content, realize that content after two animation frames with a timeout fallback, and retain it after the first open. Verify representative drawers in iOS Simulator Safari and protect the app-root and deferred-realization behavior with tests.
Build And Typecheck
- Always use Turbo when building and typechecking:
pnpm exec turbo run <task> --filter=@bb/<pkg>. Turbo ensures upstream^builddependencies run first. - Typecheck with
pnpm exec turbo run typecheck --filter=@bb/<pkg>. - Do not run package scripts directly, such as
pnpm --filter @bb/foo test, or rawnpx tsc --noEmitunless you are deliberately bypassing repo orchestration for investigation. - Generated modules are not committed:
packages/templates/src/generated/,packages/plugin-build/src/generated/, andpackages/plugin-sdk/bundled-types/are gitignored. Turbo tasks (@bb/templates#generate:*,@bb/plugin-build#generate,@get-bb/plugin-sdk#build:types) produce them before every dependent build, typecheck, and test task, andpnpm installruns the cheap ones. If your editor cannot resolve@get-bb/plugin-sdkinside a plugin, runpnpm exec turbo run build:types --filter=@get-bb/plugin-sdkonce. Never commit a generated module and never add a--checkmode for one; when you add a generated module, add a turbo task with explicitinputs/outputsand edges from its consumers.
Testing
- Only write high quality tests that verify where there could be potential bugs. Avoid testing trivial getters/setters, framework wiring, or other code that is unlikely to break.
- Pipe slow test output to a file, then read the file. Example:
pnpm exec turbo run test --filter=@bb/integration-tests --force > /tmp/test-out.txt 2>&1. - Package
vitest.config.tsfiles build theirprojectswithsharedWorkerProjectsfromvitest.shared.ts. It runs node-environment test files in shared workers (isolate: false) and gives a file its own worker when it runs in a DOM environment (jsdom) or when the file, or a test helper it imports, mutates worker-global state (vi.mock,vi.stubGlobal,process.env,globalThis.*assignments,Object.definePropertyon a global). Re-importing the module graph per file was 80–90% of the big suites' CPU. Restore what a test changes anyway; the scan is a safety net, not a license.
GitHub Issues And Pull Requests
-
Follow docs/filing-issues.md when you file an issue. Reproduce first; give versions, minimal copy-pasteable steps, expected vs actual output pasted verbatim, evidence with commit permalinks, and what you ruled out. Use the issue form's sections. Do not file from a single symptom or log line, and do not open a duplicate — add evidence to the existing issue instead.
-
Follow
.github/PULL_REQUEST_TEMPLATE.mdwhen you open a pull request: what was wrong (root cause), what changed, how you verified (tests that fail before and pass after),Fixes #N. -
When an agent creates a GitHub issue or pull request, add this line at the end of the body:
> AGENT GENERATED -
Add this line to each new issue and pull request. It shows the readers that an agent made the content.
Debugging And QA
- Do not assume. Inspect logs, query the database, call server APIs, or use the CLI to observe real state.
- See docs/debugging-and-qa.md for dev ports/data dirs, entity-ID lookups, and the
scripts/bb-dev-applocal dev QA launcher.