fix(hook): enable cross-prefix bead hooking with database routing

The `gt hook` command was failing to persist hooks for beads from different
prefixes (e.g., hooking an hq-* bead from a rig worker). The issue was that
`runHook` used `b.Update()` which always writes to the local beads database,
regardless of the bead's prefix.

Changes:
- Use `beads.ResolveHookDir()` to determine the correct database directory
  for the bead being hooked, based on its prefix
- Execute `bd update` with the correct working directory for cross-prefix routing
- Call `updateAgentHookBead()` to set the agent's hook_bead slot, enabling
  `gt hook status` to find cross-prefix hooked beads
- Add integration test for cross-prefix hooking scenarios

This matches the pattern already used by `gt sling` for cross-prefix dispatch.

Fixes: gt-rphsv

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nux
2026-01-26 11:19:12 -08:00
committed by John Ogle
parent 5ab01f383a
commit c66dc4594c
2 changed files with 141 additions and 5 deletions
+22 -3
View File
@@ -12,6 +12,7 @@ import (
"github.com/steveyegge/gastown/internal/beads"
"github.com/steveyegge/gastown/internal/events"
"github.com/steveyegge/gastown/internal/style"
"github.com/steveyegge/gastown/internal/workspace"
)
var hookCmd = &cobra.Command{
@@ -147,6 +148,12 @@ func runHook(_ *cobra.Command, args []string) error {
return fmt.Errorf("detecting agent identity: %w", err)
}
// Find town root (needed for cross-prefix bead resolution)
townRoot, err := workspace.FindFromCwd()
if err != nil {
return fmt.Errorf("finding workspace: %w", err)
}
// Find beads directory
workDir, err := findLocalBeadsDir()
if err != nil {
@@ -225,9 +232,15 @@ func runHook(_ *cobra.Command, args []string) error {
return nil
}
// Hook the bead using beads package (uses RPC when daemon available)
status := beads.StatusHooked
if err := b.Update(beadID, beads.UpdateOptions{Status: &status, Assignee: &agentID}); err != nil {
// Hook the bead using bd update with cross-prefix routing.
// The bead may be in a different beads database than the agent's local one
// (e.g., hooking an hq-* bead from a rig worker). Use ResolveHookDir to
// find the correct database directory based on the bead's prefix.
// See: https://github.com/steveyegge/gastown/issues/gt-rphsv
hookCmd := exec.Command("bd", "--no-daemon", "update", beadID, "--status=hooked", "--assignee="+agentID)
hookCmd.Dir = beads.ResolveHookDir(townRoot, beadID, workDir)
hookCmd.Stderr = os.Stderr
if err := hookCmd.Run(); err != nil {
return fmt.Errorf("hooking bead: %w", err)
}
@@ -235,6 +248,12 @@ func runHook(_ *cobra.Command, args []string) error {
fmt.Printf(" Use 'gt handoff' to restart with this work\n")
fmt.Printf(" Use 'gt hook' to see hook status\n")
// Update agent bead's hook_bead slot for status queries.
// This enables `gt hook status` to find cross-prefix hooked beads.
// The agent bead has a hook_bead database field that tracks current work.
townBeadsDir := filepath.Join(townRoot, ".beads")
updateAgentHookBead(agentID, beadID, workDir, townBeadsDir)
// Log hook event to activity feed (non-fatal)
if err := events.LogFeed(events.TypeHook, agentID, events.HookPayload(beadID)); err != nil {
fmt.Fprintf(os.Stderr, "%s Warning: failed to log hook event: %v\n", style.Dim.Render("⚠"), err)