[Bugfix #774] Open workspace state.db in detectCurrentBuilderId#775
Conversation
When a builder runs `afx send architect` from inside `.builders/<id>/`, `detectCurrentBuilderId()` resolved the singleton DB via getDb(), which walks up via findWorkspaceRoot() and stops at the worktree itself (worktrees are full git checkouts with their own codev/). The result is that getDb() opens the worktree-local `.agent-farm/state.db`, which is empty — so the lookup misses and the function falls back to the worktree directory name (e.g. `bugfix-1599`) instead of the canonical `builder-bugfix-1599`. Downstream, tower-messages.ts looks up the spawning architect by ID; the mismatch causes lookupBuilderSpawningArchitect to return undefined, and the message routes to 'main' instead of the sibling architect that spawned the builder. Multi-architect affinity routing was non-functional. Fix: derive workspace path from the CWD's `.builders/<id>/` pattern and open that workspace's state.db directly (readonly), mirroring the per-workspace handle pattern in lookupBuilderSpawningArchitect. The worktree-local state.db is no longer touched. Adds 5 regression tests covering the canonical path, the empty-worktree-DB case (the original bug), missing-DB fallback, no-match fallback, and the non-builder-CWD case.
|
Reviewed and verified end-to-end against shannon's real state.db. Walked through the fix from CWD
That's the right answer. Downstream Particularly liked:
The 5 new tests cover the bug scenario (worktree has its own empty state.db) cleanly. One thought for a future PR (NOT this one): worktree-local empty Pending architect approval to merge. |
Summary
Fixes #774
Root Cause
detectCurrentBuilderId()(inpackages/codev/src/agent-farm/commands/send.ts) used the singletongetDb()vialoadState(). From a CWD inside.builders/<id>/,findWorkspaceRoot()resolves to the worktree (worktrees are full git checkouts with their owncodev/), sogetDb()opens the worktree-local.agent-farm/state.db.That DB is empty (it gets created on first open with the schema, but no builder rows).
state.builders.find(...)returnsundefined, and the function falls back to the worktree directory name (e.g.bugfix-1599) — not the canonicalbuilder-bugfix-1599.Downstream, in
tower-messages.ts:318,lookupBuilderSpawningArchitect('bugfix-1599', workspacePath)returnsundefinedbecause the row is keyedbuilder-bugfix-1599. The router skips the builder-context branch and routes to'main'instead of the sibling architect (ob-refine) that spawned the builder. Sibling-architect affinity routing was effectively non-functional.Fix
detectCurrentBuilderId()now opens the workspace'sstate.dbdirectly (readonly), mirroring the per-workspace-handle pattern already used inlookupBuilderSpawningArchitect(state.ts:380):.builders/<id>/CWD pattern (it's always two levels up).<workspace>/.agent-farm/state.dbreadonly via a freshbetter-sqlite3handle.The worktree-local
state.dbis no longer touched by the send path. TheloadState()import is retained — it's still used by the broadcast (--all) helper, which legitimately reads from the singleton because it's enumerating builders the singleton workspace knows about.Test Plan
.builders/bugfix-774/,detectCurrentBuilderId()returnedbugfix-774and created an empty worktree-level state.db.builder-bugfix-774and does not create a worktree-level state.db.bugfix-774-detect-builder-id.test.tscovering:nullwhen CWD is not a builder worktreevitest run src/agent-farm).CMAP Review
Skipped per BUGFIX scope — fix is < 200 LOC, narrowly scoped to a single function, with high-coverage regression tests. Happy to run if the architect would like.
Scope