beads: simplify claim — open only, orphan recovery via unclaim

Claim now rejects anything not in status=open. Dead-assignee takeover
is handled by unclaiming the bead (which fires BeadReady), letting any
waiting bot claim it normally. Removes isAlive/SetSessionLiveness.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Levi Neely 2026-03-14 20:51:22 +01:00
parent d0acd32f8f
commit b52f0b5e85
2 changed files with 2 additions and 19 deletions

View File

@ -223,10 +223,6 @@ func start(daemonize bool) {
// Wire up event bus to session manager and beads filesystem
mgr.SetEventBus(srv.Events())
beadsFS.SetEventBus(srv.Events())
beadsFS.SetSessionLiveness(func(agentID string) bool {
sess := mgr.Get(agentID)
return sess != nil
})
// Start maildir writer for message persistence
mailDir := filepath.Join(os.Getenv("HOME"), ".local", "share", "anvillm", "mail")

View File

@ -84,13 +84,6 @@ type BeadsFS struct {
mounts map[string]*MountedProject
mountsMu sync.RWMutex
eventbus *eventbus.Bus
isAlive func(agentID string) bool // nil means no liveness check (all agents assumed dead)
}
// SetSessionLiveness wires a function that reports whether an agent session is alive.
// Used by the claim handler to allow orphan takeover when the current assignee is dead.
func (b *BeadsFS) SetSessionLiveness(fn func(agentID string) bool) {
b.isAlive = fn
}
// NewBeadsFS creates a beads filesystem handler from an existing store.
@ -826,14 +819,8 @@ func (b *BeadsFS) executeCtlOnStore(store bd.Storage, mountName, cmd string) err
if issue == nil {
return fmt.Errorf("bead not found: %s", beadID)
}
if issue.Status == bd.StatusInProgress && issue.Assignee != "" {
alive := b.isAlive != nil && b.isAlive(issue.Assignee)
if alive {
return fmt.Errorf("%w: assigned to %s", ErrAlreadyClaimed, issue.Assignee)
}
// assignee is dead — orphan takeover proceeds
} else if issue.Status != bd.StatusOpen {
return fmt.Errorf("bead %s is not claimable (status: %s)", beadID, issue.Status)
if issue.Status != bd.StatusOpen {
return fmt.Errorf("%w: %s (status: %s)", ErrAlreadyClaimed, beadID, issue.Status)
}
return tx.UpdateIssue(b.ctx, beadID, map[string]any{
"assignee": assignee,