docs/ARCHITECTURE.md
This document describes bd's overall architecture - the data model, sync mechanism, and how components fit together. For internal implementation details (FlushManager, Blocked Cache), see INTERNALS.md.
bd's core design enables a distributed, Dolt-powered issue tracker that feels like a centralized database. The architecture has two synchronized layers:
┌─────────────────────────────────────────────────────────────────┐
│ CLI Layer │
│ │
│ bd create, list, update, close, ready, show, dep, sync, ... │
│ - Cobra commands in cmd/bd/ │
│ - All commands support --json for programmatic use │
│ - Direct DB access (server mode via dolt sql-server) │
└──────────────────────────────┬──────────────────────────────────┘
│
v
┌─────────────────────────────────────────────────────────────────┐
│ Dolt Database │
│ (.beads/dolt/) │
│ │
│ - Version-controlled SQL database with cell-level merge │
│ - Server mode via dolt sql-server (multi-writer capable) │
│ - Fast queries, indexes, foreign keys │
│ - Issues, dependencies, labels, comments, events │
│ - Automatic Dolt commits on every write │
│ - Native push/pull to Dolt remotes │
└──────────────────────────────┬──────────────────────────────────┘
│
Dolt push/pull
(or federation peer sync)
│
v
┌─────────────────────────────────────────────────────────────────┐
│ Remote (Dolt or Git) │
│ │
│ - Dolt remotes (DoltHub, S3, GCS, filesystem) │
│ - All collaborators share the same issue database │
│ - Cell-level merge for conflict resolution │
│ - Protected branch support via separate sync branch │
└─────────────────────────────────────────────────────────────────┘
Dolt for versioned SQL: Queries complete in milliseconds with full SQL support. Dolt adds native version control — every write is automatically committed to Dolt history, providing a complete audit trail. Cell-level merge resolves conflicts automatically.
Dolt for distribution: Native push/pull to Dolt remotes (DoltHub, S3, GCS). No special sync server needed. Issues travel with your code. Offline work just works.
Export and backup: bd export outputs issue JSONL for data migration and interoperability. Use bd backup init / bd backup sync to push Dolt-native backups (preserving full commit history) to a filesystem path or DoltHub, and bd backup restore to restore from them.
When you create or modify an issue:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ CLI Command │───▶│ Dolt Write │───▶│ Dolt Commit │
│ (bd create) │ │ (immediate) │ │ (automatic) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
bd create "New feature" writes to Dolt immediatelybd dolt push to share changes with Dolt remotesKey implementation:
internal/storage/dolt/cmd/bd/export.goAll queries run directly against the local Dolt database:
┌─────────────────┐ ┌─────────────────┐
│ CLI Query │───▶│ Dolt Query │
│ (bd ready) │ │ (SQL) │
└─────────────────┘ └─────────────────┘
bd dolt pull to fetch updates from Dolt remotesKey implementation:
cmd/bd/backup_restore.gocmd/bd/init.gointernal/storage/dolt/The key insight that enables distributed operation: content-based hashing for deduplication.
Sequential IDs (bd-1, bd-2, bd-3) cause collisions when multiple agents create issues concurrently:
Branch A: bd create "Add OAuth" → bd-10
Branch B: bd create "Add Stripe" → bd-10 (collision!)
Hash-based IDs derived from random UUIDs ensure uniqueness:
Branch A: bd create "Add OAuth" → bd-a1b2
Branch B: bd create "Add Stripe" → bd-f14c (no collision)
┌─────────────────────────────────────────────────────────────────┐
│ Merge Logic │
│ (used by Dolt pull and init --from-jsonl) │
│ │
│ For each issue in incoming data: │
│ 1. Compute content hash │
│ 2. Look up existing issue by ID │
│ 3. Compare hashes: │
│ - Same hash → skip (already present) │
│ - Different hash → update (newer version) │
│ - No match → create (new issue) │
└─────────────────────────────────────────────────────────────────┘
This eliminates the need for central coordination while ensuring all machines converge to the same state.
See COLLISION_MATH.md for birthday paradox calculations on hash length vs collision probability.
Each workspace can run its own Dolt server for multi-writer access:
┌─────────────────────────────────────────────────────────────────┐
│ Dolt Server Mode │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ RPC Server │ │ dolt │ │
│ │ │ │ sql-server │ │
│ └─────────────┘ └─────────────┘ │
│ │ │ │
│ └──────────────────┘ │
│ │ │
│ v │
│ ┌─────────────┐ │
│ │ Dolt │ │
│ │ Database │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
CLI commands ───SQL───▶ dolt sql-server ───▶ Database
or
CLI commands ───SQL───▶ Database (embedded mode)
Server mode:
dolt sql-server (multi-writer, high-concurrency).beads/dolt-server.pid.beads/dolt-server.log~/.beads/shared-server/ instead of per-project servers. Enable via
dolt.shared-server: true in config.yaml or BEADS_DOLT_SHARED_SERVER=1.Embedded mode:
Communication:
internal/rpc/protocol.goCore types in internal/types/types.go:
| Type | Description | Key Fields |
|---|---|---|
| Issue | Work item | ID, Title, Description, Status, Priority, Type |
| Dependency | Relationship | FromID, ToID, Type (blocks/related/parent-child/discovered-from) |
| Label | Tag | Name, Color, Description |
| Comment | Discussion | IssueID, Author, Content, Timestamp |
| Event | Audit trail | IssueID, Type, Data, Timestamp |
| Type | Semantic | Affects bd ready? |
|---|---|---|
blocks | Issue X must close before Y starts | Yes |
parent-child | Hierarchical (epic/subtask) | Yes (children blocked if parent blocked) |
related | Soft link for reference | No |
discovered-from | Found during work on parent | No |
open ──▶ in_progress ──▶ closed
│ │
└────────────────────────┘
(reopen)
Each issue in the Dolt database (and in JSONL exports via bd export) has the following fields. Fields marked with (optional) use omitempty and are excluded when empty/zero.
Core Identification:
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier (e.g., bd-a1b2) |
Issue Content:
| Field | Type | Description |
|---|---|---|
title | string | Issue title (required) |
description | string | Detailed description (optional) |
design | string | Design notes (optional) |
acceptance_criteria | string | Acceptance criteria (optional) |
notes | string | Additional notes (optional) |
Status & Workflow:
| Field | Type | Description |
|---|---|---|
status | string | Current status: open, in_progress, blocked, deferred, closed, tombstone, pinned, hooked (optional, defaults to open) |
priority | int | Priority 0-4 where 0=critical, 4=backlog |
issue_type | string | Type: bug, feature, task, epic, chore, message, merge-request, molecule, gate, agent, role, convoy (optional, defaults to task) |
Assignment:
| Field | Type | Description |
|---|---|---|
assignee | string | Assigned user/agent (optional) |
estimated_minutes | int | Time estimate in minutes (optional) |
Timestamps:
| Field | Type | Description |
|---|---|---|
created_at | RFC3339 | When issue was created |
created_by | string | Who created the issue (optional) |
updated_at | RFC3339 | Last modification time |
closed_at | RFC3339 | When issue was closed (optional, set when status=closed) |
close_reason | string | Reason provided when closing (optional) |
External Integration:
| Field | Type | Description |
|---|---|---|
external_ref | string | External reference (e.g., gh-9, jira-ABC) (optional) |
Relational Data:
| Field | Type | Description |
|---|---|---|
labels | []string | Tags attached to the issue (optional) |
dependencies | []Dependency | Relationships to other issues (optional) |
comments | []Comment | Discussion comments (optional) |
Tombstone Fields (soft-delete):
| Field | Type | Description |
|---|---|---|
deleted_at | RFC3339 | When deleted (optional, set when status=tombstone) |
deleted_by | string | Who deleted (optional) |
delete_reason | string | Why deleted (optional) |
original_type | string | Issue type before deletion (optional) |
Note: Fields with json:"-" tags (like content_hash, source_repo, id_prefix) are internal and not included in exports.
.beads/
├── dolt/ # Dolt database, sql-server.pid, sql-server.log (gitignored)
├── metadata.json # Backend config (local, gitignored)
└── config.yaml # Project config (optional)
| Area | Files |
|---|---|
| CLI entry | cmd/bd/main.go |
| Storage interface | internal/storage/storage.go |
| Dolt implementation | internal/storage/dolt/ |
| RPC protocol | internal/rpc/protocol.go, server_*.go |
| Export logic (portability) | cmd/bd/export.go |
| Backup restore | cmd/bd/backup_restore.go |
| Issue bootstrap/migration | cmd/bd/init.go |
Molecules are template work items that define structured workflows. When spawned, they create wisps - ephemeral child issues that track execution steps.
For full documentation on the molecular chemistry metaphor (protos, pour, bond, squash, burn), see MOLECULES.md.
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ bd mol wisp │───▶│ Wisp Issues │───▶│ bd mol squash │
│ (from template) │ │ (local-only) │ │ (→ digest) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Wisps are intentionally local-only:
This design enables:
| Aspect | Regular Issues | Wisps |
|---|---|---|
| Synced to remotes | Yes | No |
| Tombstone on delete | Yes | No |
| Can resurrect | Yes (without tombstone) | No (never synced) |
| Deletion method | CreateTombstone() | DeleteIssue() (hard delete) |
The bd mol squash command uses hard delete intentionally - tombstones would be wasted overhead for data that never leaves the local database.