From 8780ec609738f505233c4352c40bc48987a9c892 Mon Sep 17 00:00:00 2001 From: Travis Cline Date: Tue, 14 Oct 2025 01:08:00 -0700 Subject: [PATCH] examples: Add complete Go extension example with documentation (#15) * examples: Add complete Go extension example with documentation Adds a comprehensive Go extension example demonstrating bd's extension patterns and Go API usage: **New bd-example-extension-go package:** - Complete working example in 116 lines total - main.go (93 lines): Full workflow with embedded schema - schema.sql (23 lines): Extension tables with foreign keys - Comprehensive README.md (241 lines): Documentation and usage guide - Go module with proper dependencies **Key patterns demonstrated:** - Schema extension with namespaced tables (example_executions, example_checkpoints) - Foreign key integration with bd's issues table - Dual-layer access using bd's Go API + direct SQL queries - Complex joined queries across bd and extension tables - Execution tracking with agent assignment and checkpointing **Features:** - Auto-discovery of bd database path - Proper SQLite configuration (WAL mode, busy timeout) - Real-world orchestration patterns - Installation and usage instructions - Integration examples with bd's ready work queue This provides a complete reference implementation for developers building bd extensions, complementing the Go API added in recent commits. * Update go.mod after merge with main, add .gitignore - Fix Go version to 1.21 (matches main module) - Reorganize dependencies properly - Keep replace directive for local development - Add .gitignore for built binary --------- Co-authored-by: Steve Yegge --- examples/bd-example-extension-go/.gitignore | 1 + examples/bd-example-extension-go/README.md | 241 ++++++++++++++++++++ examples/bd-example-extension-go/go.mod | 11 + examples/bd-example-extension-go/go.sum | 2 + examples/bd-example-extension-go/main.go | 93 ++++++++ examples/bd-example-extension-go/schema.sql | 23 ++ 6 files changed, 371 insertions(+) create mode 100644 examples/bd-example-extension-go/.gitignore create mode 100644 examples/bd-example-extension-go/README.md create mode 100644 examples/bd-example-extension-go/go.mod create mode 100644 examples/bd-example-extension-go/go.sum create mode 100644 examples/bd-example-extension-go/main.go create mode 100644 examples/bd-example-extension-go/schema.sql diff --git a/examples/bd-example-extension-go/.gitignore b/examples/bd-example-extension-go/.gitignore new file mode 100644 index 00000000..9d9c4081 --- /dev/null +++ b/examples/bd-example-extension-go/.gitignore @@ -0,0 +1 @@ +bd-example-extension-go diff --git a/examples/bd-example-extension-go/README.md b/examples/bd-example-extension-go/README.md new file mode 100644 index 00000000..818ec5ae --- /dev/null +++ b/examples/bd-example-extension-go/README.md @@ -0,0 +1,241 @@ +# BD Extension Example (Go) + +This example demonstrates how to extend bd with custom tables for application-specific orchestration, following the patterns described in [EXTENDING.md](../../EXTENDING.md). + +## What This Example Shows + +1. **Schema Extension**: Adding custom tables (`example_executions`, `example_checkpoints`) to bd's SQLite database +2. **Foreign Key Integration**: Linking extension tables to bd's `issues` table with proper cascading +3. **Dual-Layer Access**: Using bd's Go API for issue management while directly querying extension tables +4. **Complex Queries**: Joining bd's issues with extension tables for powerful insights +5. **Execution Tracking**: Implementing agent assignment, checkpointing, and crash recovery patterns + +## Key Patterns Illustrated + +### Pattern 1: Namespace Your Tables + +All tables are prefixed with `example_` to avoid conflicts: + +```sql +CREATE TABLE example_executions (...) +CREATE TABLE example_checkpoints (...) +``` + +### Pattern 2: Foreign Key Relationships + +Extension tables link to bd's issues with cascading deletes: + +```sql +FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE +``` + +### Pattern 3: Index Common Queries + +Indexes are created for frequent query patterns: + +```sql +CREATE INDEX idx_executions_status ON example_executions(status); +CREATE INDEX idx_executions_issue ON example_executions(issue_id); +``` + +### Pattern 4: Layer Separation + +- **bd layer**: Issue tracking, dependencies, ready work +- **Extension layer**: Execution state, agent assignments, checkpoints + +### Pattern 5: Join Queries + +Powerful queries join both layers: + +```sql +SELECT i.id, i.title, i.priority, e.status, e.agent_id, COUNT(c.id) +FROM issues i +LEFT JOIN example_executions e ON i.id = e.issue_id +LEFT JOIN example_checkpoints c ON e.id = c.execution_id +GROUP BY i.id, e.id +``` + +## Building and Running + +### Prerequisites + +- Go 1.21 or later +- bd initialized in a directory (run `bd init --prefix demo`) + +### Install + +```bash +# Install from the repository +go install github.com/steveyegge/beads/examples/bd-example-extension-go@latest + +# Or install from local source +cd examples/bd-example-extension-go +go install . +``` + +The binary will be installed as `bd-example-extension-go` in your `$GOPATH/bin` (or `$GOBIN` if set). + +### Running + +```bash +# Auto-discover database and run +bd-example-extension-go + +# Or specify database path +bd-example-extension-go -db .beads/demo.db +``` + +**Output:** +``` +Claiming: demo-5 + ✓ assess + ✓ implement + ✓ test + +Status: + demo-4: Fix memory leak [closed] agent=agent-demo checkpoints=3 + demo-1: Implement auth [in_progress] agent=agent-alice checkpoints=0 + demo-5: Test minimized [closed] agent=demo-agent checkpoints=3 +``` + +## Code Structure + +**Just 116 lines total** - minimal, focused extension example. + +- **main.go** (93 lines): Complete workflow with embedded schema +- **schema.sql** (23 lines): Extension tables (`example_executions`, `example_checkpoints`) with foreign keys and indexes + +Demonstrates: +1. Auto-discover database (`beads.FindDatabasePath`) +2. Dual-layer access (bd API + direct SQL) +3. Execution tracking with checkpoints +4. Complex joined queries across layers + +## Example Queries + +### Find Running Executions with Checkpoint Count + +```go +query := ` + SELECT i.id, i.title, e.status, e.agent_id, COUNT(c.id) as checkpoints + FROM issues i + INNER JOIN example_executions e ON i.id = e.issue_id + LEFT JOIN example_checkpoints c ON e.id = c.execution_id + WHERE e.status = 'running' + GROUP BY i.id, e.id +` +``` + +### Find Failed Executions + +```go +query := ` + SELECT i.id, i.title, e.error, e.completed_at + FROM issues i + INNER JOIN example_executions e ON i.id = e.issue_id + WHERE e.status = 'failed' + ORDER BY e.completed_at DESC +` +``` + +### Get Latest Checkpoint for Recovery + +```go +query := ` + SELECT checkpoint_data + FROM example_checkpoints + WHERE execution_id = ? + ORDER BY created_at DESC + LIMIT 1 +` +``` + +## Integration with bd + +### Using bd's Go API + +```go +// Auto-discover database path +dbPath := beads.FindDatabasePath() +if dbPath == "" { + log.Fatal("No bd database found") +} + +// Open bd storage +store, err := beads.NewSQLiteStorage(dbPath) + +// Find ready work +readyIssues, err := store.GetReadyWork(ctx, beads.WorkFilter{Limit: 10}) + +// Update issue status +updates := map[string]interface{}{"status": beads.StatusInProgress} +err = store.UpdateIssue(ctx, issueID, updates, "agent-name") + +// Close issue +err = store.CloseIssue(ctx, issueID, "Completed", "agent-name") + +// Find corresponding JSONL path (for git hooks, monitoring, etc.) +jsonlPath := beads.FindJSONLPath(dbPath) +``` + +### Direct Database Access + +```go +// Open same database for extension tables +db, err := sql.Open("sqlite3", dbPath) + +// Initialize extension schema +_, err = db.Exec(Schema) + +// Query extension tables +rows, err := db.Query("SELECT * FROM example_executions WHERE status = ?", "running") +``` + +## Testing the Example + +1. **Initialize bd:** + ```bash + bd init --prefix demo + ``` + +2. **Create some test issues:** + ```bash + bd create "Implement authentication" -p 1 -t feature + bd create "Add API documentation" -p 1 -t task + bd create "Refactor database layer" -p 2 -t task + ``` + +3. **Run the demo:** + ```bash + bd-example-extension-go -cmd demo + ``` + +4. **Check the results:** + ```bash + bd list + sqlite3 .beads/demo.db "SELECT * FROM example_executions" + ``` + +## Real-World Usage + +This pattern is used in production by: + +- **VC (VibeCoder)**: Multi-agent orchestration with state machines +- **CI/CD Systems**: Build tracking and artifact management +- **Task Runners**: Parallel execution with dependency resolution + +See [EXTENDING.md](../../EXTENDING.md) for more patterns and the VC implementation example. + +## Next Steps + +1. **Add Your Own Tables**: Extend the schema with application-specific tables +2. **Implement State Machines**: Use checkpoints for resumable workflows +3. **Add Metrics**: Track execution times, retry counts, success rates +4. **Build Dashboards**: Query joined data for visibility +5. **Integrate with Agents**: Use bd's ready work queue for agent orchestration + +## See Also + +- [EXTENDING.md](../../EXTENDING.md) - Complete extension guide +- [../../README.md](../../README.md) - bd documentation +- Run `bd quickstart` for an interactive tutorial diff --git a/examples/bd-example-extension-go/go.mod b/examples/bd-example-extension-go/go.mod new file mode 100644 index 00000000..5f234521 --- /dev/null +++ b/examples/bd-example-extension-go/go.mod @@ -0,0 +1,11 @@ +module bd-example-extension-go + +go 1.21 + +require ( + github.com/mattn/go-sqlite3 v1.14.32 + github.com/steveyegge/beads v0.0.0-00010101000000-000000000000 +) + +// For local development - remove when beads is published +replace github.com/steveyegge/beads => ../.. diff --git a/examples/bd-example-extension-go/go.sum b/examples/bd-example-extension-go/go.sum new file mode 100644 index 00000000..66f7516d --- /dev/null +++ b/examples/bd-example-extension-go/go.sum @@ -0,0 +1,2 @@ +github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= +github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= diff --git a/examples/bd-example-extension-go/main.go b/examples/bd-example-extension-go/main.go new file mode 100644 index 00000000..e7eb19d7 --- /dev/null +++ b/examples/bd-example-extension-go/main.go @@ -0,0 +1,93 @@ +package main + +import ( + "context" + "database/sql" + _ "embed" + "encoding/json" + "flag" + "fmt" + "log" + "time" + + "github.com/steveyegge/beads" +) + +//go:embed schema.sql +var schema string + +func main() { + dbPath := flag.String("db", "", "Database path (default: auto-discover)") + flag.Parse() + + if *dbPath == "" { + *dbPath = beads.FindDatabasePath() + } + if *dbPath == "" { + log.Fatal("No database found. Run 'bd init'") + } + + // Open bd storage + extension database + store, _ := beads.NewSQLiteStorage(*dbPath) + defer store.Close() + db, _ := sql.Open("sqlite3", *dbPath) + defer db.Close() + db.Exec("PRAGMA journal_mode=WAL") + db.Exec("PRAGMA busy_timeout=5000") + db.Exec(schema) // Initialize extension schema + + // Get ready work + ctx := context.Background() + readyIssues, _ := store.GetReadyWork(ctx, beads.WorkFilter{Limit: 1}) + if len(readyIssues) == 0 { + fmt.Println("No ready work") + return + } + + issue := readyIssues[0] + fmt.Printf("Claiming: %s\n", issue.ID) + + // Create execution record + result, _ := db.Exec(`INSERT INTO example_executions (issue_id, status, agent_id, started_at) + VALUES (?, 'running', 'demo-agent', ?)`, issue.ID, time.Now()) + execID, _ := result.LastInsertId() + + // Update issue in bd + store.UpdateIssue(ctx, issue.ID, map[string]interface{}{"status": beads.StatusInProgress}, "demo-agent") + + // Create checkpoints + for _, phase := range []string{"assess", "implement", "test"} { + data, _ := json.Marshal(map[string]interface{}{"phase": phase, "time": time.Now()}) + db.Exec(`INSERT INTO example_checkpoints (execution_id, phase, checkpoint_data) VALUES (?, ?, ?)`, + execID, phase, string(data)) + fmt.Printf(" ✓ %s\n", phase) + } + + // Complete + db.Exec(`UPDATE example_executions SET status='completed', completed_at=? WHERE id=?`, time.Now(), execID) + store.CloseIssue(ctx, issue.ID, "Done", "demo-agent") + + // Show status + fmt.Println("\nStatus:") + rows, _ := db.Query(` + SELECT i.id, i.title, i.status, e.agent_id, COUNT(c.id) + FROM issues i + LEFT JOIN example_executions e ON i.id = e.issue_id + LEFT JOIN example_checkpoints c ON e.id = c.execution_id + GROUP BY i.id, e.id + ORDER BY i.priority + LIMIT 5`) + defer rows.Close() + + for rows.Next() { + var id, title, status string + var agent sql.NullString + var checkpoints int + rows.Scan(&id, &title, &status, &agent, &checkpoints) + agentStr := "-" + if agent.Valid { + agentStr = agent.String + } + fmt.Printf(" %s: %s [%s] agent=%s checkpoints=%d\n", id, title, status, agentStr, checkpoints) + } +} diff --git a/examples/bd-example-extension-go/schema.sql b/examples/bd-example-extension-go/schema.sql new file mode 100644 index 00000000..7d70463d --- /dev/null +++ b/examples/bd-example-extension-go/schema.sql @@ -0,0 +1,23 @@ +CREATE TABLE IF NOT EXISTS example_executions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + issue_id TEXT NOT NULL, + status TEXT NOT NULL, + agent_id TEXT, + started_at DATETIME, + completed_at DATETIME, + error TEXT, + FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS example_checkpoints ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + execution_id INTEGER NOT NULL, + phase TEXT NOT NULL, + checkpoint_data TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (execution_id) REFERENCES example_executions(id) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS idx_executions_issue ON example_executions(issue_id); +CREATE INDEX IF NOT EXISTS idx_executions_status ON example_executions(status); +CREATE INDEX IF NOT EXISTS idx_checkpoints_execution ON example_checkpoints(execution_id);