diff --git a/internal/beads/beads.go b/internal/beads/beads.go index c1811e22..1ec4f2f6 100644 --- a/internal/beads/beads.go +++ b/internal/beads/beads.go @@ -387,6 +387,25 @@ func (b *Beads) CloseWithReason(reason string, ids ...string) error { return err } +// Pin pins an issue to an agent's hook. +// This sets the pinned boolean field and optionally the assignee. +// If agent is empty, only sets the pinned field. +func (b *Beads) Pin(id string, agent string) error { + args := []string{"--no-daemon", "pin", id} + if agent != "" { + args = append(args, "--for="+agent) + } + _, err := b.run(args...) + return err +} + +// Unpin removes the pinned flag from an issue. +func (b *Beads) Unpin(id string) error { + args := []string{"unpin", id, "--json"} + _, err := b.run(args...) + return err +} + // Release moves an in_progress issue back to open status. // This is used to recover stuck steps when a worker dies mid-task. // It clears the assignee so the step can be claimed by another worker. diff --git a/internal/cmd/sling.go b/internal/cmd/sling.go index 73614bcf..5e34cd54 100644 --- a/internal/cmd/sling.go +++ b/internal/cmd/sling.go @@ -1057,11 +1057,21 @@ func pinToHook(beadsPath, agentAddress, issueID string, moleculeCtx *MoleculeCon attachedMolecule = moleculeCtx.RootIssueID } - // Attach molecule to handoff bead + // Attach molecule to handoff bead (stores in description) _, err = b.AttachMolecule(handoff.ID, attachedMolecule) if err != nil { return fmt.Errorf("attaching molecule: %w", err) } + // Also pin the work issue itself to the agent + // This sets the pinned boolean field AND assignee so bd hook can find it + // NOTE: There's a known issue (gt-o3is) where bd pin via subprocess doesn't + // actually set the pinned field, even though it reports success. + if err := b.Pin(attachedMolecule, role); err != nil { + // Non-fatal - the handoff bead attachment is the primary mechanism + // This just enables bd hook visibility + fmt.Printf(" %s pin work issue: %v\n", style.Dim.Render("Note: could not"), err) + } + return nil }