fix: Hook persists across session interruption via in_progress lookup (gt-ttn3h)

The hook query now falls back to checking in_progress beads assigned to the
agent when no hooked beads are found. This ensures work is not lost when
a session is interrupted after claiming work.

Previously, gt hook only looked for status=hooked beads, so work that had
been claimed but not completed appeared lost. The fix extends the query to
also include in_progress beads assigned to the agent.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rockryder
2026-01-04 16:52:42 -08:00
committed by Steve Yegge
parent 095a426ebf
commit 8592098036
3 changed files with 49 additions and 3 deletions

View File

@@ -368,6 +368,7 @@ func runMoleculeStatus(cmd *cobra.Command, args []string) error {
}
} else {
// FALLBACK: Query for hooked beads (work on agent's hook)
// First try status=hooked (work that's been slung but not yet claimed)
hookedBeads, err := b.List(beads.ListOptions{
Status: beads.StatusHooked,
Assignee: target,
@@ -377,6 +378,21 @@ func runMoleculeStatus(cmd *cobra.Command, args []string) error {
return fmt.Errorf("listing hooked beads: %w", err)
}
// If no hooked beads found, also check in_progress beads assigned to this agent.
// This handles the case where work was claimed (status changed to in_progress)
// but the session was interrupted before completion. The hook should persist.
if len(hookedBeads) == 0 {
inProgressBeads, err := b.List(beads.ListOptions{
Status: "in_progress",
Assignee: target,
Priority: -1,
})
if err == nil && len(inProgressBeads) > 0 {
// Use the first in_progress bead (should typically be only one)
hookedBeads = inProgressBeads
}
}
// For town-level roles (mayor, deacon), scan all rigs if nothing found locally
if len(hookedBeads) == 0 && isTownLevelRole(target) {
hookedBeads = scanAllRigsForHookedBeads(townRoot, target)
@@ -887,6 +903,8 @@ func scanAllRigsForHookedBeads(townRoot, target string) []*beads.Issue {
}
b := beads.New(rigBeadsDir)
// First check for hooked beads
hookedBeads, err := b.List(beads.ListOptions{
Status: beads.StatusHooked,
Assignee: target,
@@ -899,6 +917,20 @@ func scanAllRigsForHookedBeads(townRoot, target string) []*beads.Issue {
if len(hookedBeads) > 0 {
return hookedBeads
}
// Also check for in_progress beads (work that was claimed but session interrupted)
inProgressBeads, err := b.List(beads.ListOptions{
Status: "in_progress",
Assignee: target,
Priority: -1,
})
if err != nil {
continue
}
if len(inProgressBeads) > 0 {
return inProgressBeads
}
}
return nil

View File

@@ -977,11 +977,25 @@ func checkSlungWork(ctx RoleContext) bool {
Assignee: agentID,
Priority: -1,
})
if err != nil || len(hookedBeads) == 0 {
// No hooked beads - no slung work
if err != nil {
return false
}
// If no hooked beads found, also check in_progress beads assigned to this agent.
// This handles the case where work was claimed (status changed to in_progress)
// but the session was interrupted before completion. The hook should persist.
if len(hookedBeads) == 0 {
inProgressBeads, err := b.List(beads.ListOptions{
Status: "in_progress",
Assignee: agentID,
Priority: -1,
})
if err != nil || len(inProgressBeads) == 0 {
return false
}
hookedBeads = inProgressBeads
}
// Use the first hooked bead (agents typically have one)
hookedBead := hookedBeads[0]

View File

@@ -11,7 +11,7 @@ import (
// Generate formulas directory from canonical source at .beads/formulas/
//go:generate sh -c "rm -rf formulas && mkdir -p formulas && cp ../../.beads/formulas/*.formula.toml ../../.beads/formulas/*.formula.json formulas/ 2>/dev/null || cp ../../.beads/formulas/*.formula.toml formulas/"
//go:embed formulas/*.formula.toml formulas/*.formula.json
//go:embed formulas/*.formula.toml
var formulasFS embed.FS
// ProvisionFormulas creates the .beads/formulas/ directory with embedded formulas.