Skip to content

Repository files navigation

bdboard

A Python + FastAPI + HTMX dashboard for bd (beads) workspaces.

cd into any directory containing .beads/, run bdboard, and a browser tab opens with a live view of swim lanes, activity, and bead detail with every field bd exposes.

Who it's for: developers using bd who want an at-a-glance, always-fresh web view of a workspace's beads without leaving their terminal-driven workflow.

Prerequisites

Tool Why Notes
bd (beads) Required at runtime — bdboard shells out to the bd binary for every bead read (see src/bdboard/bd.py). Without it on PATH, the board comes up but every lane/detail/memory/history view fails. install from upstream; confirm with bd --version
Python ≥ 3.11 runtime pyproject.toml requires-python
uv venv + installer uv --version
Node + npx only for make duplication (jscpd) bundled toolchains skip this
lychee only for make links brew install lychee

Quick start (fresh clone)

The full setup, in order. A fresh clone has no bead database, so bd bootstrap comes first — skip it and bdboard renders an empty board (see Getting the bead history).

git clone https://github.com/weegens-aaron/bdboard.git
cd bdboard
bd bootstrap --yes          # hydrate the bead DB (one-time; fresh clones ship none)
make install                # uv venv + editable install (resolves against public PyPI)
source .venv/bin/activate   # activate the venv (run once per shell session)
make test                   # optional: confirm the suite is green
bdboard                     # binds 127.0.0.1:7332 and opens a browser

The sections below break these steps out individually.

Install (dev)

cd bdboard
make install           # uv venv + editable install (resolves against public PyPI)
source .venv/bin/activate   # activate the venv (run once per shell session)

Prefer the raw commands? They're equivalent:

cd bdboard
uv venv
uv pip install -e .
source .venv/bin/activate

Behind a private package mirror? make install reads two optional environment variables and passes them through to uv — nothing is hard-coded, so the public default stays clean:

export PY_INDEX_URL=https://mirror.example.com/simple
export PY_TRUSTED_HOST=mirror.example.com   # only if the mirror is self-signed
make install

Run

From within a bd workspace (any directory containing .beads/):

bdboard                        # binds 127.0.0.1:7332 and opens a browser

Or use make run from the bdboard repo (runs against this repo's own .beads/ workspace):

cd bdboard
make run                       # equivalent to .venv/bin/bdboard

bdboard only works inside a bd workspace. From a non-.beads/ directory it reports "workspace not ready" — cd into one or pass --dir /path/to/workspace.

Fresh clone? Hydrate first. A freshly-cloned bdboard workspace ships no bead database (.beads/embeddeddolt/ is gitignored), so bdboard starts but renders an empty board until you run bd bootstrap --yes once. See Getting the bead history (fresh clone) for the one-time hydration step.

Test

make test              # runs the full pytest suite against this repo's own .beads workspace

Code health (CI gates)

The mechanical, deterministic code-health checks run on every pull request and on pushes to main/working via .github/workflows/code-health.yml and gate merges. Run the exact same set locally with:

make code-health      # lint + format-check + dead-code + tests + duplication + audit
make outdated         # advisory only (dependency staleness)

These checks live in CI rather than a bd formula by design — see notes/decisions/0002-dashboard-architecture.md.

Gate Tool Make target Config
Lint + unused-import/dead-code (F401, F811, F841) ruff make lint [tool.ruff] in pyproject.toml
Format check (no rewrites) ruff make fmt-check (make fmt rewrites) [tool.ruff] in pyproject.toml
Dead-code sweep (≥80% confidence) vulture make dead-code inline flag
Tests pytest make test
Copy-paste duplication jscpd make duplication .jscpd.json
Dependency CVE scan pip-audit make audit
Outdated deps (advisory, never fails) uv make outdated

Flags

Flag Default Notes
--addr 127.0.0.1:7332 HTTP listen address
--no-browser off Don't auto-launch a browser
--bd bd Path to the bd binary
--dir cwd Workspace to run in
--strict-port off Fail if the requested port is taken instead of auto-incrementing (default scans 20 ports from the start port)

Behavior highlights

  • Lanes — Deferred / Blocked / Ready / In-Progress / Closed are derived in-process from bd list --json snapshots
  • Activity — derived from updated_at / closed_at / created_at because bd doesn't expose a global audit feed; rendered as a swim lane alongside the bead lanes
  • JSONL freshness — bdboard reads bead state via the bd list ... --json CLI (the dolt-native, always-fresh path), using a three-way split rather than a single --all fetch (see ADR 0004): (1) active issues via bd list --no-pager --limit 0 (no --all, so closed are excluded) — the ~5 KB fast first-paint path; (2) the board Closed lane + header CLOSED KPI via bd list --status closed --closed-after <iso> --sort closed --no-pager --limit 0, date-window-bounded (BOARD_CLOSED_WINDOW_DAYS) — not count-capped — so the lane and KPI count the same set (bdboard-p8v); and (3) the History page via bd list --status closed --sort closed --no-pager --limit 0 [--closed-after <iso>], which is count-uncapped by design so older closures never silently vanish (bdboard-a194) but is window-bounded when a range filter is active (bdboard-gp06). The closed lane is lazy-loaded as a second phase after the active lanes paint (hx-trigger="load"). We do NOT read .beads/issues.jsonl directly — per the upstream COMMUNITY_TOOLS.md, that path is deprecated and may be missing fields. A watchfiles watcher observes each dolt database's noms/ directory (plus .beads/ itself) non-recursively so any bd write triggers a refresh within ~250ms (debounced) + ~1s cooldown. We deliberately avoid recursively watching the whole .beads/ tree: dolt's churning noms/ object store would open a kqueue fd per directory on macOS and exhaust RLIMIT_NOFILE. SSE pushes a single beads_changed event only when the bead list actually changed (structural equality vs the previous cache). No bd export calls; bdboard never writes to .beads/.

Getting the bead history (fresh clone)

Issue history is replicated off-machine using Dolt's git-compatible wire protocol — it rides under refs/dolt/data on the same GitHub origin as the code, not as a committed issues.jsonl. That custom ref is not auto-fetched by a normal git clone, and the local Dolt DB itself (.beads/embeddeddolt/) is gitignored — so a bare clone has no bead database at all. Until you hydrate once, bd list reports no beads database found and bdboard renders an empty board. (See notes/decisions/0003-beads-sync-via-dolt-git-refs.md for the full rationale.)

After cloning, hydrate the bead database with a one-time bd bootstrap:

git clone https://github.com/weegens-aaron/bdboard.git
cd bdboard
bd bootstrap --yes        # auto-detects refs/dolt/data on the git origin and clones it
bd list                   # should now be non-empty — full issue history is local

bd bootstrap auto-detects the dolt data ref on the git origin. If your clone somehow lacks the dolt remote, point it at the code origin (the same repo — per ADR 0003 we do not use a separate dolt repo) before bootstrapping:

bd dolt remote add origin git+https://github.com/weegens-aaron/bdboard.git
bd bootstrap --yes

Gotcha — don't use bd init + bd dolt pull for a fresh clone. bd init seeds an independent Dolt history, so a subsequent bd dolt pull fails with Error 1105: no common ancestor. Use bd bootstrap, which clones the remote history directly. (Hydration of ~200 issues takes ~40s; a raw pull-into-init can churn for minutes before erroring.)

Backup (beads issue data)

Issue data is replicated off-machine. The Dolt remote origin points at the same GitHub origin as the code (weegens-aaron/bdboard.git), and the full issue history rides under refs/dolt/data there via Dolt's git-compatible wire protocol — not as a committed issues.jsonl. See notes/decisions/0003-beads-sync-via-dolt-git-refs.md for the full rationale, and Getting the bead history for how a fresh clone hydrates.

To push local bead writes off-machine:

bd dolt push        # replicates issue history to refs/dolt/data on origin

The bead database lives in .beads/embeddeddolt/ (gitignored) and is the single source of truth, read at runtime through the bd CLI. bd dolt pull brings remote changes back down on another machine.

bdboard never writes to .beads/ and never reads .beads/issues.jsonl; the canonical, runtime source of truth is the Dolt DB as read through the bd CLI.

If any bd command hangs, you may have hit the stale dolt sql-server accumulation issue — run pkill -9 -f 'dolt sql-server' (see Troubleshooting) and retry.

Troubleshooting

Bead detail (or bd itself) hangs

bd embeds dolt; each bd invocation can leave a dolt sql-server subprocess running. Over weeks of use these accumulate and start fighting each other for locks. Symptom: bd commands hang, bdboard's bead-detail panel times out, etc.

Fix:

pkill -9 -f 'dolt sql-server'

Then bd — and bdboard — will be snappy again. If you see this happen often, file an issue upstream against bd.

bdboard says "workspace not ready"

You're not in a directory with .beads/. cd to a bd workspace or pass --dir /path/to/workspace.

Contributing

Contributions are welcome — see CONTRIBUTING.md for the dev setup and the checks CI enforces.

License

Released under the MIT License.

About

No description, website, or topics provided.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages