diff --git a/internal/cmd/done.go b/internal/cmd/done.go index 8d0c7694..359498ef 100644 --- a/internal/cmd/done.go +++ b/internal/cmd/done.go @@ -456,7 +456,7 @@ notifyWitness: // Notify dispatcher if work was dispatched by another agent if issueID != "" { - if dispatcher := getDispatcherFromBead(cwd, issueID); dispatcher != "" && dispatcher != sender { + if dispatcher := getDispatcherFromBead(townRoot, cwd, issueID); dispatcher != "" && dispatcher != sender { dispatcherNotification := &mail.Message{ To: dispatcher, From: sender, @@ -645,7 +645,7 @@ func updateAgentStateOnDone(cwd, townRoot, exitType, _ string) { // issueID unus if _, err := bd.Run("agent", "state", agentBeadID, "awaiting-gate"); err != nil { fmt.Fprintf(os.Stderr, "Warning: couldn't set agent %s to awaiting-gate: %v\n", agentBeadID, err) } - // ExitCompleted and ExitDeferred don't set state - observable from tmux + // ExitCompleted and ExitDeferred don't set state - observable from tmux } // ZFC #10: Self-report cleanup status @@ -678,12 +678,19 @@ func getIssueFromAgentHook(bd *beads.Beads, agentBeadID string) string { // getDispatcherFromBead retrieves the dispatcher agent ID from the bead's attachment fields. // Returns empty string if no dispatcher is recorded. -func getDispatcherFromBead(cwd, issueID string) string { +// +// BUG FIX (sc-g7bl3): Use townRoot and ResolveHookDir for bead lookup instead of +// ResolveBeadsDir(cwd). When the polecat's worktree is deleted before gt done finishes, +// ResolveBeadsDir(cwd) fails because the redirect file is gone. ResolveHookDir uses +// prefix-based routing via routes.jsonl which works regardless of worktree state. +func getDispatcherFromBead(townRoot, cwd, issueID string) string { if issueID == "" { return "" } - bd := beads.New(beads.ResolveBeadsDir(cwd)) + // Use ResolveHookDir for resilient bead lookup - works even if worktree is deleted + beadsDir := beads.ResolveHookDir(townRoot, issueID, cwd) + bd := beads.New(beadsDir) issue, err := bd.Show(issueID) if err != nil { return ""