Core features:
- Dependency-aware issue tracking with SQLite backend
- Ready work detection (issues with no open blockers)
- Dependency tree visualization
- Cycle detection and prevention
- Full audit trail
- CLI with colored output
Security and correctness fixes applied:
- Fixed SQL injection vulnerability in UpdateIssue (whitelisted fields)
- Fixed race condition in ID generation (added mutex)
- Fixed cycle detection to return full paths (not just issue IDs)
- Added cycle prevention in AddDependency (validates before commit)
- Added comprehensive input validation (priority, status, types, etc.)
- Fixed N+1 query in GetBlockedIssues (using GROUP_CONCAT)
- Improved query building in GetReadyWork (proper string joining)
- Fixed P0 priority filter bug (using Changed() instead of value check)
All critical and major issues from code review have been addressed.
🤖 Generated with Claude Code
14 KiB
Beads Workflow Guide
Complete guide to using Beads for solo development and with AI coding assistants like Claude Code.
Table of Contents
Vibe Coding with Claude Code
The "Let's Continue" Protocol
Start of every session:
# 1. Check for abandoned work
beads list --status in_progress
# 2. If none, get ready work
beads ready --limit 5
# 3. Show top priority
beads show bd-X
Tell Claude: "Let's continue" and it runs these commands.
Full Project Workflow
Session 1: Project Kickoff
You: "Starting a new e-commerce project. Help me plan it."
Claude creates issues:
cd ~/my-project
alias beads="~/src/beads/beads --db ./project.db"
beads create "Set up Next.js project" -p 0 -t task
beads create "Design database schema" -p 0 -t task
beads create "Build authentication system" -p 1 -t feature
beads create "Create API routes" -p 1 -t feature
beads create "Build UI components" -p 2 -t feature
beads create "Add tests" -p 2 -t task
beads create "Deploy to production" -p 3 -t task
Map dependencies:
beads dep add bd-4 bd-2 # API depends on schema
beads dep add bd-3 bd-2 # Auth depends on schema
beads dep add bd-5 bd-4 # UI depends on API
beads dep add bd-6 bd-3 # Tests depend on auth
beads dep add bd-6 bd-5 # Tests depend on UI
beads dep add bd-7 bd-6 # Deploy depends on tests
Visualize:
beads dep tree bd-7
Output:
🌲 Dependency tree for bd-7:
→ bd-7: Deploy to production [P3] (open)
→ bd-6: Add tests [P2] (open)
→ bd-3: Build authentication system [P1] (open)
→ bd-2: Design database schema [P0] (open)
→ bd-5: Build UI components [P2] (open)
→ bd-4: Create API routes [P1] (open)
→ bd-2: Design database schema [P0] (open)
Check ready work:
beads ready
📋 Ready work (2 issues with no blockers):
1. [P0] bd-1: Set up Next.js project
2. [P0] bd-2: Design database schema
Session 2: Foundation
You: "Let's continue"
Claude:
beads ready
# Shows: bd-1, bd-2
You: "Work on bd-2"
Claude:
beads update bd-2 --status in_progress
beads show bd-2
# ... designs schema, creates migrations ...
beads close bd-2 --reason "Schema designed with Prisma, migrations created"
beads ready
Now shows:
📋 Ready work (3 issues):
1. [P0] bd-1: Set up Next.js project
2. [P1] bd-3: Build authentication system ← Unblocked!
3. [P1] bd-4: Create API routes ← Unblocked!
Session 3: Building Features
You: "Let's continue, work on bd-3"
Claude:
beads ready # Confirms bd-3 is ready
beads update bd-3 --status in_progress
# ... implements JWT auth, middleware ...
beads close bd-3 --reason "Auth complete with JWT tokens and protected routes"
Session 4: Discovering Blockers
You: "Let's continue, work on bd-4"
Claude starts working, then:
You: "We need to add OAuth before we can finish the API properly"
Claude:
beads create "Set up OAuth providers (Google, GitHub)" -p 1 -t task
beads dep add bd-4 bd-8 # API now depends on OAuth
beads update bd-4 --status blocked
beads ready
Shows:
📋 Ready work (2 issues):
1. [P0] bd-1: Set up Next.js project
2. [P1] bd-8: Set up OAuth providers ← New blocker must be done first
Claude: "I've blocked bd-4 and created bd-8 as a prerequisite. Should I work on OAuth setup now?"
Session 5: Unblocking
You: "Yes, do bd-8"
Claude completes OAuth setup:
beads close bd-8 --reason "OAuth configured for Google and GitHub"
beads update bd-4 --status open # Manually unblock
beads ready
Now bd-4 is ready again!
Pro Tips for AI Pairing
1. Add context with comments:
beads update bd-5 --status in_progress
# Work session ends mid-task
beads comment bd-5 "Implemented navbar and footer, still need shopping cart icon"
Next session, Claude reads the comment and continues.
2. Break down epics when too big:
beads create "Epic: User Management" -p 1 -t epic
beads create "User registration flow" -p 1 -t task
beads create "User login/logout" -p 1 -t task
beads create "Password reset" -p 2 -t task
beads dep add bd-10 bd-9 --type parent-child
beads dep add bd-11 bd-9 --type parent-child
beads dep add bd-12 bd-9 --type parent-child
3. Use labels for filtering:
beads create "Fix login timeout" -p 0 -l "bug,auth,urgent"
beads create "Add loading spinner" -p 2 -l "ui,polish"
# Later
beads list --status open | grep urgent
4. Track estimates:
beads create "Refactor user service" -p 2 --estimated-minutes 120
beads ready # Shows estimates for planning
Database Structure
What's Inside project.db?
A single SQLite database file (typically 72KB-1MB) containing:
Tables
1. issues - Core issue data
CREATE TABLE issues (
id TEXT PRIMARY KEY, -- "bd-1", "bd-2", etc.
title TEXT NOT NULL,
description TEXT,
design TEXT, -- Solution design
acceptance_criteria TEXT, -- Definition of done
notes TEXT, -- Working notes
status TEXT DEFAULT 'open', -- open|in_progress|blocked|closed
priority INTEGER DEFAULT 2, -- 0-4 (0=highest)
issue_type TEXT DEFAULT 'task', -- bug|feature|task|epic|chore
assignee TEXT,
estimated_minutes INTEGER,
created_at DATETIME,
updated_at DATETIME,
closed_at DATETIME
);
2. dependencies - Relationship graph
CREATE TABLE dependencies (
issue_id TEXT NOT NULL, -- "bd-2"
depends_on_id TEXT NOT NULL, -- "bd-1" (bd-2 depends on bd-1)
type TEXT DEFAULT 'blocks', -- blocks|related|parent-child
created_at DATETIME,
created_by TEXT,
PRIMARY KEY (issue_id, depends_on_id)
);
3. labels - Tags for categorization
CREATE TABLE labels (
issue_id TEXT NOT NULL,
label TEXT NOT NULL,
PRIMARY KEY (issue_id, label)
);
4. events - Complete audit trail
CREATE TABLE events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
issue_id TEXT NOT NULL,
event_type TEXT NOT NULL, -- created|updated|commented|closed|etc
actor TEXT NOT NULL, -- who made the change
old_value TEXT, -- before (JSON)
new_value TEXT, -- after (JSON)
comment TEXT, -- for comments and close reasons
created_at DATETIME
);
5. ready_issues - VIEW (auto-computed)
-- Shows issues with NO open blockers
-- This is the magic that powers "beads ready"
CREATE VIEW ready_issues AS
SELECT i.*
FROM issues i
WHERE i.status = 'open'
AND NOT EXISTS (
SELECT 1 FROM dependencies d
JOIN issues blocked ON d.depends_on_id = blocked.id
WHERE d.issue_id = i.id
AND d.type = 'blocks'
AND blocked.status IN ('open', 'in_progress', 'blocked')
);
6. blocked_issues - VIEW (auto-computed)
-- Shows issues WITH open blockers
CREATE VIEW blocked_issues AS
SELECT
i.*,
COUNT(d.depends_on_id) as blocked_by_count
FROM issues i
JOIN dependencies d ON i.id = d.issue_id
JOIN issues blocker ON d.depends_on_id = blocker.id
WHERE i.status IN ('open', 'in_progress', 'blocked')
AND d.type = 'blocks'
AND blocker.status IN ('open', 'in_progress', 'blocked')
GROUP BY i.id;
Example Data
Issues table:
bd-1|Critical bug|Fix login timeout|||open|0|bug|||2025-10-11 19:23:10|2025-10-11 19:23:10|
bd-2|High priority||Need auth first||open|1|feature|||2025-10-11 19:23:11|2025-10-11 19:23:11|
Dependencies table:
bd-2|bd-1|blocks|2025-10-11 19:23:16|stevey
Translation: "bd-2 depends on bd-1 (blocks type), created by stevey"
Events table:
1|bd-1|created|stevey||{"id":"bd-1","title":"Critical bug",...}||2025-10-11 19:23:10
2|bd-2|created|stevey||{"id":"bd-2","title":"High priority",...}||2025-10-11 19:23:11
3|bd-2|dependency_added|stevey|||Added dependency: bd-2 blocks bd-1|2025-10-11 19:23:16
Inspecting the Database
Show all tables:
sqlite3 project.db ".tables"
View schema:
sqlite3 project.db ".schema issues"
Query directly:
# Find all P0 issues
sqlite3 project.db "SELECT id, title FROM issues WHERE priority = 0;"
# See dependency graph
sqlite3 project.db "SELECT issue_id, depends_on_id FROM dependencies;"
# View audit trail for an issue
sqlite3 project.db "SELECT * FROM events WHERE issue_id = 'bd-5' ORDER BY created_at;"
# Who's working on what?
sqlite3 project.db "SELECT assignee, COUNT(*) FROM issues WHERE status = 'in_progress' GROUP BY assignee;"
# See what's ready (same as beads ready)
sqlite3 project.db "SELECT id, title, priority FROM ready_issues ORDER BY priority;"
Export to CSV:
sqlite3 project.db -header -csv "SELECT * FROM issues;" > issues.csv
Database size:
ls -lh project.db
# Typically: 72KB (empty) to ~1MB (1000 issues)
Git Workflow
Committing the Database
The database IS your project state. Commit it!
# Add database to git
git add project.db
# Commit with meaningful message
git commit -m "Updated tracker: completed auth (bd-3), ready for API work"
# Push
git push
Multi-Machine Workflow
Machine 1:
beads create "New task" -p 1
beads update bd-5 --status in_progress
git add project.db
git commit -m "Started working on bd-5"
git push
Machine 2:
git pull
beads ready # Sees bd-5 is in progress
beads list --status in_progress # See what you were working on
Team Workflow
Each developer has their own database:
# Alice's machine
beads --db alice.db create "Fix bug"
# Bob's machine
beads --db bob.db create "Add feature"
# Merge by convention:
# - Alice handles backend issues (bd-1 to bd-50)
# - Bob handles frontend issues (bd-51 to bd-100)
Or use PostgreSQL for shared state (future feature).
Branching Strategy
Option 1: Database per branch
git checkout -b feature/auth
cp main.db auth.db
beads --db auth.db create "Add OAuth" -p 1
# Work on branch...
git add auth.db
git commit -m "Auth implementation progress"
Option 2: Single database, label by branch
beads create "Add OAuth" -p 1 -l "branch:feature/auth"
beads list | grep "branch:feature/auth"
Advanced Usage
Alias Setup
Add to ~/.bashrc or ~/.zshrc:
# Project-specific
alias b="~/src/beads/beads --db ./project.db"
# Usage
b create "Task" -p 1
b ready
b show bd-5
Scripting Beads
Find all unassigned P0 issues:
#!/bin/bash
beads list --priority 0 --status open | grep -v "Assignee:"
Auto-close issues from git commits:
#!/bin/bash
# In git hook: .git/hooks/commit-msg
COMMIT_MSG=$(cat $1)
if [[ $COMMIT_MSG =~ bd-([0-9]+) ]]; then
ISSUE_ID="bd-${BASH_REMATCH[1]}"
~/src/beads/beads --db ./project.db close "$ISSUE_ID" \
--reason "Auto-closed from commit: $(git rev-parse --short HEAD)"
fi
Weekly report:
#!/bin/bash
echo "Issues closed this week:"
sqlite3 project.db "
SELECT id, title, closed_at
FROM issues
WHERE closed_at > date('now', '-7 days')
ORDER BY closed_at DESC;
"
Multi-Project Management
Use different databases:
# Personal projects
beads --db ~/personal.db create "Task"
# Work projects
beads --db ~/work.db create "Task"
# Client A
beads --db ~/clients/client-a.db create "Task"
Or use labels:
beads create "Task" -l "project:website"
beads create "Task" -l "project:mobile-app"
# Filter by project
sqlite3 ~/.beads/beads.db "
SELECT i.id, i.title
FROM issues i
JOIN labels l ON i.id = l.issue_id
WHERE l.label = 'project:website';
"
Export/Import
Export issues to JSON:
sqlite3 project.db -json "SELECT * FROM issues;" > backup.json
Export dependency graph:
# DOT format for Graphviz
sqlite3 project.db "
SELECT 'digraph G {'
UNION ALL
SELECT ' \"' || issue_id || '\" -> \"' || depends_on_id || '\";'
FROM dependencies
UNION ALL
SELECT '}';
" > graph.dot
dot -Tpng graph.dot -o graph.png
Performance Tips
Vacuum regularly for large databases:
sqlite3 project.db "VACUUM;"
Add custom indexes:
sqlite3 project.db "CREATE INDEX idx_labels_custom ON labels(label) WHERE label LIKE 'project:%';"
Archive old issues:
sqlite3 project.db "
DELETE FROM issues
WHERE status = 'closed'
AND closed_at < date('now', '-6 months');
"
Troubleshooting
Database locked:
# Another process is using it
lsof project.db
# Kill the process or wait for it to finish
Corrupted database:
# Check integrity
sqlite3 project.db "PRAGMA integrity_check;"
# Recover
sqlite3 project.db ".dump" | sqlite3 recovered.db
Reset everything:
rm ~/.beads/beads.db
beads create "Fresh start" -p 1
Summary
Beads is:
- A single binary
- A single database file
- Simple commands
- Powerful dependency tracking
- Perfect for solo dev or AI pairing
The workflow:
- Brain dump all tasks →
beads create - Map dependencies →
beads dep add - Find ready work →
beads ready - Work on it →
beads update --status in_progress - Complete it →
beads close - Commit database →
git add project.db - Repeat
The magic:
- Database knows what's ready
- Git tracks your progress
- AI can query and update
- You never lose track of "what's next"