Skip to content

Architecture

BrendanP edited this page Jun 26, 2026 · 1 revision

Architecture

How Bead Me Up, Scotty works under the hood, and the design decisions behind it.

The one big idea: bd is the source of truth

Beads has no HTTP/REST API. Its store is Dolt (a version-controlled SQL database), and in the common single-user case it runs embedded — single-writer, in-process, data in .beads/, no server, no ports.

So the app integrates the only sane way for a local tool: it shells out to the bd CLI with JSON output (bd … --json, BD_JSON_ENVELOPE=1). bd stays the single source of truth for every invariant — ID hashing, ready-queue logic, the audit trail, and Dolt commits.

The consequence is the project's defining constraint: the app adds zero new persisted schema. It is a pure view/controller over bd. The only local state is UI configuration (repo path, attribution, theme), stored under your OS config directory — never in beads.

Browser (React 19, App Router pages)
   │  fetch()
   ▼
Next.js Route Handlers  (app/api/**)
   │  the only server entry points
   ▼
lib/bd.ts   ──► execFile("bd", […, "--json"])  ──►  bd CLI  ──►  Dolt (.beads/)
   ▲
   └── lib/demo-store.ts  (in-memory fallback when bd is absent)

The bd bridge — lib/bd.ts

lib/bd.ts is the only module that talks to bd. Everything else goes through it. It:

  • runs bd with execFile (no shell — arguments are passed as an array),
  • requests the JSON envelope and parses it,
  • validates the output with Zod before it reaches the rest of the app, and
  • serializes writes behind a mutex, because the embedded Dolt backend is a single writer.

lib/store.ts picks the live bridge or the demo store; lib/demo-store.ts is an in-memory dataset seeded from the design export so the app works with no bd installed. See Demo Mode.

The data model (a "bead")

The app mirrors beads' own model — it doesn't invent one. Selected fields:

Field Type Notes
id string hash-based, e.g. bd-a3f8; hierarchical children like bd-a3f8.1
title string required, ≤ 500 chars
description string optional
status enum open, in_progress, blocked, deferred, closed, pinned, hooked
priority int 04 0 = critical … 4 = backlog
issue_type enum bug, feature, task, epic, chore, decision, spike, story, milestone, …
assignee string optional
created_by string free-text actor — the attribution hook (see Configuration)
labels string[]
dependencies object[] typed edges between beads
comments object[] {id, issue_id, author, text, created_at}
parent string | null computed from a parent-child dependency

Key design decisions

These were locked in with the project owner and are documented in design/design.md:

  • Backlog = deferred. The board's "Backlog" column maps to beads' built-in deferred status. "Ready" = open and unblocked. Dragging between columns runs bd update --status / bd close.
  • Epics aren't a separate entity. An epic is a regular bead with issue_type = "epic". Children attach via a parent-child dependency. Epic progress is closed children ÷ total children, computed client-side for live rendering.
  • Attribution is the app's, not beads'. Beads has no human-vs-agent flag, so the UI stamps its own writes with a configured human actor and renders anyone in the human allowlist as 👤, everyone else as 🤖.
  • Archive vs delete. Archive = bd close + an archived label (reversible). Delete = bd delete.

Tech stack

Next.js 16 (App Router) · React 19 · TypeScript · Tailwind v4 · shadcn/ui · TanStack Query (polling + optimistic DnD) · dnd-kit (board) · @xyflow/react (dependency graph) · Zod (validates both bd output and forms).

Project layout

app/                  # pages + API route handlers (the only server entry points)
  api/**              # list/create/patch/delete, status, comments, deps, archive,
                      # plus doctor, config, projects, insights, activity, publish
lib/
  bd.ts               # the ONLY bd CLI bridge (execFile, JSON envelope, write mutex)
  demo-store.ts       # in-memory fallback seeded from the export
  store.ts            # picks bd vs demo
  schema.ts           # Zod schemas + types (the bd data model)
  beads-view.ts       # pure view-model helpers (colors, blocked, epic progress)
  attribution.ts      # human-vs-agent origin
components/           # sidebar, board (dnd), detail drawer, create modal,
                      # epics, graph, settings, command palette, insights, …

See Contributing for how to navigate and verify changes.

Clone this wiki locally