| title | Drover Feature Review & Design Compliance Analysis | |
|---|---|---|
| description | Drover Feature Review & Design Compliance Analysis | |
| product | drover-orchestrator | |
| audience | platform-operator | |
| doc_type | explanation | |
| topics |
|
|
| surface | repo-docs |
Review Date: January 15, 2026
Branch: init (commit 403ccbc)
Reviewer: Automated Code Review
This review evaluates the Drover codebase against its design specifications for DBOS (Durable Operating System for Workflows) and Beads integration. The implementation demonstrates strong adherence to the architectural vision with a few notable gaps and opportunities for improvement.
| Component | Design Compliance | Test Coverage | Implementation Quality |
|---|---|---|---|
| DBOS Workflow Engine | ⭐⭐⭐⭐ (4/5) | Good (with gaps) | Excellent |
| Beads Integration | ⭐⭐⭐⭐ (4/5) | Minimal | Good |
| Hierarchical Tasks | ⭐⭐⭐⭐⭐ (5/5) | Excellent | Excellent |
| Pluggable Agents | ⭐⭐⭐⭐⭐ (5/5) | Good | Excellent |
| Git Worktree Management | ⭐⭐⭐⭐ (4/5) | Good (1 failure) | Good |
| Dashboard & Telemetry | ⭐⭐⭐⭐ (4/5) | Not tested | Good |
| Requirement | Status | Evidence |
|---|---|---|
| Automatic crash recovery and checkpointing | ✅ Implemented | dbos_workflow.go:366-369 - Steps with retries |
| Built-in retry logic for failed operations | ✅ Implemented | dbos.WithStepMaxRetries(3) pattern |
| Exactly-once execution guarantees | ✅ Implemented | DBOS step functions |
| Queue-based parallel execution | ✅ Implemented | ExecuteTasksWithQueue method |
| SQLite support for development | ✅ Documented | Design docs, CLI help |
| PostgreSQL for production | ✅ Implemented | DBOS_SYSTEM_DATABASE_URL env var |
- Proper Workflow Registration Pattern
type DBOSOrchestrator struct {
config *config.Config
git *git.WorktreeManager
agent executor.Agent // Agent interface for Claude/Codex/Amp
dbosCtx dbos.DBOSContext
queue dbos.WorkflowQueue
store *db.Store // SQLite store for worktree tracking
// ...
}-
Step-Based Checkpointing
- Each major operation (worktree creation, Claude execution, commit, merge) is wrapped as a DBOS step
- Steps have retry logic with configurable max retries
- Exactly-once semantics for each step
-
Dual Workflow Support
- Sequential workflow:
ExecuteAllTasks- good for debugging - Queue-based workflow:
ExecuteTasksWithQueue- production-ready parallelism
- Sequential workflow:
-
Dependency Management
buildDependencyMapcorrectly tracks task dependenciesfindReadyTasksonly enqueues tasks without blockers
- Incomplete Dependent Task Enqueuing
- In
ExecuteTasksWithQueue, only initially-ready tasks are enqueued - The
OnTaskCompleteworkflow exists but is a placeholder (POC) - Impact: Tasks blocked by other tasks won't be automatically enqueued when blockers complete
- In
// OnTaskComplete is called when a task completes and enqueues any dependent tasks
func (o *DBOSOrchestrator) OnTaskComplete(ctx dbos.DBOSContext, completedTaskID string) (int, error) {
// ...
for _, depID := range dependents {
// In a full implementation, we would check if ALL blockers are complete
// before enqueuing the dependent task. For this POC, we just enqueue it.
log.Printf("📤 Enqueuing dependent task %s", depID)
enqueued++
}
return enqueued, nil
}-
DBOS Tests Require PostgreSQL
- All DBOS-specific tests skip without PostgreSQL:
TestDBOSOrchestrator_SingleTask - SKIP (PostgreSQL not available) TestDBOSOrchestrator_MultipleTasks - SKIP (PostgreSQL not available) TestDBOSOrchestrator_QueueParallel - SKIP (PostgreSQL not available)- Recommendation: Add SQLite-based DBOS tests for local development
-
Workflow Recovery Not Tested
func TestDBOSOrchestrator_WorkflowRecovery(t *testing.T) { t.Skip("Workflow recovery test requires more complex setup - skipping for POC") }
| Requirement | Status | Evidence |
|---|---|---|
Hierarchical Task IDs (task-id.subtask) |
✅ Implemented | db/idutil.go, CreateSubTask |
| Sub-task decomposition | ✅ Implemented | db/hierarchy_test.go passes |
| Flat storage with hierarchy | ✅ Implemented | parent_id + sequence_number columns |
| Max depth of 2 levels | ✅ Enforced | CreateSubTask validation |
| Import from Beads format | ✅ Implemented | beads/sync.go:ImportFromBeads |
| Export to Beads format | ✅ Implemented | beads/sync.go:ExportToBeads |
| Bidirectional sync | Export works, import less tested |
-
Hierarchical ID Parsing
- Robust parsing:
ParseHierarchicalIDhandlestask-123.1.2syntax - Depth validation: Rejects IDs deeper than 2 levels
- Well-tested:
TestParseHierarchicalIDcovers all cases
- Robust parsing:
-
Beads Data Structures
type BeadRecord struct {
Type string `json:"type"` // "bead", "epic", "link"
ID string `json:"id"`
Timestamp time.Time `json:"timestamp"`
Data json.RawMessage `json:"data"`
}
type BeadTask struct {
Title string `json:"title"`
Description string `json:"description,omitempty"`
Status string `json:"status"` // "open", "active", "closed"
// ...
}-
Status Mapping
- Correct bidirectional mapping between Drover and Beads statuses
ready/claimed/blocked→openin_progress→activecompleted/failed→closed
-
CLI Integration
drover exportcommand exports to.beads/beads.jsonl- Auto-sync option via
AutoSyncBeadsconfig flag syncToBeadsIfNeeded()called after orchestrator run
-
No Import Command
ImportFromBeadsfunction exists but no CLI command exposes it- Recommendation: Add
drover importcommand
-
No Beads Package Tests
- The
internal/beads/sync.gohas no corresponding test file - Recommendation: Add unit tests for import/export roundtrip
- The
-
BD CLI Integration Untested
- Functions like
BdClose,BdAddexist but aren't used or tested - These call out to the external
bdbinary
- Functions like
TestCreateSubTask - PASS
TestCreateSubTaskMaxDepth - PASS
TestCreateSubTaskNonExistentParent - PASS
TestGetSubTasks - PASS
TestHasSubTasks - PASS
TestGetParentTask - PASS
TestClaimTaskExcludesSubTasks - PASS
TestTaskPersistenceWithHierarchy - PASS
TestSubTaskWorkflow - PASS
TestSubTaskClaimingBehavior - PASS
- Automatic ID Generation:
parent-id.Nformat - Sequence Numbering: Auto-increments within parent
- Max Depth Enforcement: Rejects
task.1.2.3(depth > 2) - Claim Exclusion: Sub-tasks are never claimed directly
- Epic Inheritance: Sub-tasks inherit parent's epic_id
- Ordered Execution: Sub-tasks execute by sequence_number
| Agent | Implementation File | Status |
|---|---|---|
| Claude Code | claude.go |
✅ Full |
| OpenAI Codex | codex_agent.go |
✅ Full |
| Amp | amp_agent.go |
✅ Full |
| OpenCode | opencode_agent.go |
✅ Full |
type Agent interface {
ExecuteWithContext(ctx context.Context, worktreePath string, task *types.Task, parentSpan ...trace.Span) *ExecutionResult
CheckInstalled() error
SetVerbose(bool)
}All agents implement:
- Context-aware execution with timeout support
- OpenTelemetry span integration
- Verbose logging mode
- Installation verification
| Package | Status | Notes |
|---|---|---|
internal/db |
✅ 27/27 PASS | Excellent coverage |
internal/workflow (SQLite) |
✅ 3/3 PASS | Sub-task workflows |
internal/workflow (DBOS) |
⏭️ 7 SKIP | Requires PostgreSQL |
internal/config |
✅ 2/2 PASS | Config parsing |
internal/executor |
✅ 7/7 PASS | Agent execution |
internal/git |
Cleanup issue |
TestWorktreeManager_Cleanup
Worktree task-cleanup-1 still exists after cleanup
Worktree task-cleanup-2 still exists after cleanup
Root Cause: The Cleanup() method parses git worktree list --porcelain output incorrectly. The output format is:
worktree /path/to/worktree
HEAD abc123...
branch refs/heads/branch
But the code expects parts[1] to be the path, when it should parse the line starting with worktree.
Recommendation: Fix the parsing in Cleanup():
// Current (incorrect)
parts := strings.Fields(line)
if len(parts) < 2 {
continue
}
worktreePath := parts[1]
// Should be
if strings.HasPrefix(line, "worktree ") {
worktreePath := strings.TrimPrefix(line, "worktree ")
// ...
}The dashboardCmd() function referenced an undefined runDashboard function, causing build failures:
cmd/drover/commands.go:1426:11: undefined: runDashboard
Resolution: Added the missing runDashboard function that properly initializes and starts the dashboard server:
func runDashboard(store *db.Store, port string, openBrowser bool) error {
dashServer, err := dashboard.New(dashboard.Config{
Addr: ":" + port,
DB: store.DB,
})
// ...
}Also added missing imports for os/exec and internal/dashboard.
┌─────────────────────────────────────────────────────────────┐
│ CLI (Cobra) │
│ drover init | run | add | epic | status | resume │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ DBOS Workflow Engine │
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
│ │ DroverRunWorkflow│ │ ExecuteTaskWorkflow │ │
│ │ (orchestrator) │───►│ (per-task, queued) │ │
│ └─────────────────┘ └─────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Implementation Status:
- ✅ CLI layer using Cobra (commands.go)
- ✅ DBOS Workflow Engine (dbos_workflow.go)
- ✅ SQLite fallback orchestrator (orchestrator.go)
- ✅ Per-task workflows with queuing
- ✅ Git worktree isolation
- ✅ Multiple agent support
All state transitions from design/state-machine.md are implemented:
| Transition | Status |
|---|---|
[*] → ready |
✅ |
[*] → blocked (with dependencies) |
✅ |
ready → claimed |
✅ |
claimed → in_progress |
✅ |
claimed → ready (timeout) |
|
in_progress → completed |
✅ |
in_progress → failed |
✅ |
in_progress → blocked |
|
in_progress → claimed (retry) |
✅ |
blocked → ready |
✅ (via CompleteTask) |
Missing(FIXED)runDashboardFunction- Dashboard command would fail to build
- Added the missing function implementation
-
Fix Worktree Cleanup Test
- Parse
git worktree list --porcelainoutput correctly - Severity: Medium (affects cleanup command)
- Parse
-
Implement Dependent Task Enqueuing in DBOS
- Complete
OnTaskCompleteto actually enqueue dependents - Currently marked as POC placeholder
- Complete
-
Add SQLite-based DBOS Tests
- Current tests skip without PostgreSQL
- Would improve CI/CD coverage
-
Add Beads Import Command
drover importto complementdrover export- Import function exists but isn't exposed
-
Add Beads Package Tests
- Test import/export roundtrip
- Test status mapping
-
Test Claim Timeout Recovery
- The design mentions returning stale claims to ready
- No test verifies this behavior
-
Dashboard Testing
- No unit tests for dashboard handlers
- WebSocket functionality untested
-
Telemetry Integration Testing
- ClickHouse/Grafana stack mentioned but not tested
Per the design document, the following security guidance applies:
| Area | Status | Notes |
|---|---|---|
| Claude Code sandbox | --dangerously-skip-permissions bypasses prompts |
|
| Database access | ✅ SQLite file-based | No network exposure |
| Git operations | ✅ Local worktrees | No remote push by default |
The Drover implementation demonstrates strong adherence to its DBOS and Beads design principles. The codebase is well-structured, with clear separation of concerns between:
- Workflow orchestration (DBOS-based and SQLite fallback)
- Task storage (hierarchical with Beads-compatible IDs)
- Agent execution (pluggable interface for multiple AI backends)
- Git isolation (worktree-per-task model)
- ✅ DBOS integration with durable workflows and queuing
- ✅ Beads-style hierarchical task IDs with max depth 2
- ✅ Pluggable agent interface (Claude, Codex, Amp, OpenCode)
- ✅ Comprehensive test coverage for core database operations
- ✅ Real-time dashboard with WebSocket updates
⚠️ One failing test in worktree cleanup⚠️ DBOS dependent task enqueuing is POC only⚠️ No CLI import command for Beads⚠️ PostgreSQL required for full DBOS test coverage
- ✅ Missing
runDashboardfunction (build error) - FIXED - ✅ Missing imports for dashboard package - FIXED
Overall Grade: B+ (Strong Implementation with Minor Gaps)
| Test Suite | Pass | Fail | Skip | Notes |
|---|---|---|---|---|
internal/db |
27 | 0 | 0 | Complete |
internal/config |
2 | 0 | 0 | Complete |
internal/executor |
7 | 0 | 0 | Complete |
internal/git |
7 | 1 | 0 | Cleanup test fails |
internal/workflow |
6 | 0 | 7 | DBOS tests skip without PostgreSQL |
internal/test |
All | 0 | 0 | Integration tests |
test/ (epic tests) |
All | 0 | 0 | Epic scenario tests |
$ go build ./cmd/drover/...
# Build successful (after fix applied)- Hierarchical Task IDs:
task-123.1,task-123.2 - Max Depth Enforcement: Rejects
task-123.1.2.3 - Sub-task Claiming: Only parents are claimed
- Worker Parallelism: Multiple workers claim independently
- Agent Execution: Mock Claude scripts execute successfully
- Git Worktree Isolation: Each task gets isolated worktree
- Commit & Merge: Changes committed and merged to main