From b52f0b5e8503434522dee36ee4067678b36ff95f Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Sat, 14 Mar 2026 20:51:22 +0100 Subject: [PATCH] =?UTF-8?q?beads:=20simplify=20claim=20=E2=80=94=20open=20?= =?UTF-8?q?only,=20orphan=20recovery=20via=20unclaim?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- cmd/anvilsrv/main.go | 4 ---- internal/p9/beads.go | 17 ++--------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/cmd/anvilsrv/main.go b/cmd/anvilsrv/main.go index 260d696..5a30a73 100644 --- a/cmd/anvilsrv/main.go +++ b/cmd/anvilsrv/main.go @@ -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") diff --git a/internal/p9/beads.go b/internal/p9/beads.go index 35cfff2..d6eb5e8 100644 --- a/internal/p9/beads.go +++ b/internal/p9/beads.go @@ -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,