From cceacf2b04ff38ac4afaacb9bcfea79439eb3fd5 Mon Sep 17 00:00:00 2001 From: gastown/crew/jack Date: Wed, 31 Dec 2025 11:58:08 -0800 Subject: [PATCH] Fix cross-beads slot references silently failing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When slinging work from town beads (hq-*) to a polecat whose agent bead is in rig beads (gt-*), the hook_bead update was silently failing because bd couldn't find the cross-beads reference. Changes: - sling.go: Use town root for routing instead of cwd, enabling cross-beads resolution via routes.jsonl. Log warnings on failure instead of silent ignore. - done.go: Use townRoot (already available) instead of cwd for beads client. Log warnings on failure for both state and cleanup status updates. Root cause: The beads client was created from current working directory, which may not have access to routes.jsonl for cross-prefix resolution. Town root always has routes.jsonl for proper prefix → rig directory mapping. (gt-ohqxq) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- internal/cmd/done.go | 9 ++++++--- internal/cmd/sling.go | 18 ++++++++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/internal/cmd/done.go b/internal/cmd/done.go index a8c7c9f5..86ea61cd 100644 --- a/internal/cmd/done.go +++ b/internal/cmd/done.go @@ -312,10 +312,12 @@ func updateAgentStateOnDone(cwd, townRoot, exitType, issueID string) { } // Update agent bead with new state and clear hook_bead (work is done) - bd := beads.New(cwd) + // Use town root for routing - ensures cross-beads references work + bd := beads.New(townRoot) emptyHook := "" if err := bd.UpdateAgentState(agentBeadID, newState, &emptyHook); err != nil { - // Silently ignore - beads might not be configured + // Log warning instead of silent ignore - helps debug cross-beads issues + fmt.Fprintf(os.Stderr, "Warning: couldn't update agent %s state on done: %v\n", agentBeadID, err) return } @@ -324,7 +326,8 @@ func updateAgentStateOnDone(cwd, townRoot, exitType, issueID string) { cleanupStatus := computeCleanupStatus(cwd) if cleanupStatus != "" { if err := bd.UpdateAgentCleanupStatus(agentBeadID, cleanupStatus); err != nil { - // Silently ignore + // Log warning instead of silent ignore + fmt.Fprintf(os.Stderr, "Warning: couldn't update agent %s cleanup status: %v\n", agentBeadID, err) return } } diff --git a/internal/cmd/sling.go b/internal/cmd/sling.go index 718720b1..f7c9adc6 100644 --- a/internal/cmd/sling.go +++ b/internal/cmd/sling.go @@ -790,6 +790,11 @@ func runSlingFormula(args []string) error { // updateAgentHookBead updates the agent bead's hook_bead field when work is slung. // This enables the witness to see what each agent is working on. +// +// IMPORTANT: Uses town root for routing so cross-beads references work. +// The agent bead (e.g., gt-gastown-polecat-nux) may be in rig beads, +// while the hook bead (e.g., hq-oosxt) may be in town beads. +// Running from town root gives access to routes.jsonl for proper resolution. func updateAgentHookBead(agentID, beadID string) { // Convert agent ID to agent bead ID // Format examples (canonical: prefix-rig-role-name): @@ -802,15 +807,20 @@ func updateAgentHookBead(agentID, beadID string) { return } - // Find beads directory - try current directory first - workDir, err := os.Getwd() + // Use town root for routing - this ensures cross-beads references work. + // Town beads (hq-*) and rig beads (gt-*) are resolved via routes.jsonl + // which lives at town root. + townRoot, err := workspace.FindFromCwd() if err != nil { + // Not in a Gas Town workspace - can't update agent bead + fmt.Fprintf(os.Stderr, "Warning: couldn't find town root to update agent hook: %v\n", err) return } - bd := beads.New(workDir) + bd := beads.New(townRoot) if err := bd.UpdateAgentState(agentBeadID, "running", &beadID); err != nil { - // Silently ignore - agent bead might not exist yet + // Log warning instead of silent ignore - helps debug cross-beads issues + fmt.Fprintf(os.Stderr, "Warning: couldn't update agent %s hook to %s: %v\n", agentBeadID, beadID, err) return } }