fix(beads): align agent bead prefixes and force multi-hyphen IDs (#482)

* fix(beads): align agent bead prefixes and force multi-hyphen IDs

* fix(checkpoint): treat threshold as stale at boundary
This commit is contained in:
JJ
2026-01-16 15:33:51 -05:00
committed by GitHub
parent 03213a7307
commit b1a5241430
17 changed files with 141 additions and 26 deletions
+4
View File
@@ -16,6 +16,7 @@ import (
tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/beads"
"github.com/steveyegge/gastown/internal/style"
"github.com/steveyegge/gastown/internal/tui/convoy"
"github.com/steveyegge/gastown/internal/workspace"
@@ -319,6 +320,9 @@ func runConvoyCreate(cmd *cobra.Command, args []string) error {
"--description=" + description,
"--json",
}
if beads.NeedsForceForID(convoyID) {
createArgs = append(createArgs, "--force")
}
createCmd := exec.Command("bd", createArgs...)
createCmd.Dir = townBeads
+8 -1
View File
@@ -211,7 +211,14 @@ func TestQuerySessionEvents_FindsEventsFromAllLocations(t *testing.T) {
if wsErr != nil {
t.Fatalf("workspace.FindFromCwdOrError failed: %v", wsErr)
}
if foundTownRoot != townRoot {
normalizePath := func(path string) string {
resolved, err := filepath.EvalSymlinks(path)
if err != nil {
return filepath.Clean(path)
}
return resolved
}
if normalizePath(foundTownRoot) != normalizePath(townRoot) {
t.Errorf("workspace.FindFromCwdOrError returned %s, expected %s", foundTownRoot, townRoot)
}
+10
View File
@@ -11,6 +11,7 @@ import (
"strings"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/beads"
"github.com/steveyegge/gastown/internal/config"
"github.com/steveyegge/gastown/internal/style"
"github.com/steveyegge/gastown/internal/workspace"
@@ -335,6 +336,9 @@ func executeConvoyFormula(f *formulaData, formulaName, targetRig string) error {
"--title=" + convoyTitle,
"--description=" + description,
}
if beads.NeedsForceForID(convoyID) {
createArgs = append(createArgs, "--force")
}
createCmd := exec.Command("bd", createArgs...)
createCmd.Dir = townBeads
@@ -365,6 +369,9 @@ func executeConvoyFormula(f *formulaData, formulaName, targetRig string) error {
"--title=" + leg.Title,
"--description=" + legDesc,
}
if beads.NeedsForceForID(legBeadID) {
legArgs = append(legArgs, "--force")
}
legCmd := exec.Command("bd", legArgs...)
legCmd.Dir = townBeads
@@ -405,6 +412,9 @@ func executeConvoyFormula(f *formulaData, formulaName, targetRig string) error {
"--title=" + f.Synthesis.Title,
"--description=" + synDesc,
}
if beads.NeedsForceForID(synthesisBeadID) {
synArgs = append(synArgs, "--force")
}
synCmd := exec.Command("bd", synArgs...)
synCmd.Dir = townBeads
+3 -3
View File
@@ -957,7 +957,7 @@ func runPolecatCheckRecovery(cmd *cobra.Command, args []string) error {
// We need to read it directly from beads since manager doesn't expose it
rigPath := r.Path
bd := beads.New(rigPath)
agentBeadID := beads.PolecatBeadID(rigName, polecatName)
agentBeadID := polecatBeadIDForRig(r, rigName, polecatName)
_, fields, err := bd.GetAgentBead(agentBeadID)
status := RecoveryStatus{
@@ -1158,7 +1158,7 @@ func runPolecatNuke(cmd *cobra.Command, args []string) error {
fmt.Printf(" - Kill session: gt-%s-%s\n", p.rigName, p.polecatName)
fmt.Printf(" - Delete worktree: %s/polecats/%s\n", p.r.Path, p.polecatName)
fmt.Printf(" - Delete branch (if exists)\n")
fmt.Printf(" - Close agent bead: %s\n", beads.PolecatBeadID(p.rigName, p.polecatName))
fmt.Printf(" - Close agent bead: %s\n", polecatBeadIDForRig(p.r, p.rigName, p.polecatName))
displayDryRunSafetyCheck(p)
fmt.Println()
@@ -1214,7 +1214,7 @@ func runPolecatNuke(cmd *cobra.Command, args []string) error {
}
// Step 5: Close agent bead (if exists)
agentBeadID := beads.PolecatBeadID(p.rigName, p.polecatName)
agentBeadID := polecatBeadIDForRig(p.r, p.rigName, p.polecatName)
closeArgs := []string{"close", agentBeadID, "--reason=nuked"}
if sessionID := runtime.SessionIDFromEnv(); sessionID != "" {
closeArgs = append(closeArgs, "--session="+sessionID)
+12 -2
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"path/filepath"
"strings"
"github.com/steveyegge/gastown/internal/beads"
@@ -104,7 +105,7 @@ func checkPolecatSafety(target polecatTarget) *SafetyCheckResult {
// Check 1: Unpushed commits via cleanup_status or git state
bd := beads.New(target.r.Path)
agentBeadID := beads.PolecatBeadID(target.rigName, target.polecatName)
agentBeadID := polecatBeadIDForRig(target.r, target.rigName, target.polecatName)
agentIssue, fields, err := bd.GetAgentBead(agentBeadID)
if err != nil || fields == nil {
@@ -176,6 +177,15 @@ func checkPolecatSafety(target polecatTarget) *SafetyCheckResult {
return result
}
func rigPrefix(r *rig.Rig) string {
townRoot := filepath.Dir(r.Path)
return beads.GetPrefixForRig(townRoot, r.Name)
}
func polecatBeadIDForRig(r *rig.Rig, rigName, polecatName string) string {
return beads.PolecatBeadIDWithPrefix(rigPrefix(r), rigName, polecatName)
}
// displaySafetyCheckBlocked prints blocked polecats and guidance.
func displaySafetyCheckBlocked(blocked []*SafetyCheckResult) {
fmt.Printf("%s Cannot nuke the following polecats:\n\n", style.Error.Render("Error:"))
@@ -202,7 +212,7 @@ func displayDryRunSafetyCheck(target polecatTarget) {
fmt.Printf("\n Safety checks:\n")
polecatInfo, infoErr := target.mgr.Get(target.polecatName)
bd := beads.New(target.r.Path)
agentBeadID := beads.PolecatBeadID(target.rigName, target.polecatName)
agentBeadID := polecatBeadIDForRig(target.r, target.rigName, target.polecatName)
agentIssue, fields, err := bd.GetAgentBead(agentBeadID)
// Check 1: Git state
+9 -9
View File
@@ -232,7 +232,7 @@ func runPolecatIdentityAdd(cmd *cobra.Command, args []string) error {
// Check if identity already exists
bd := beads.New(r.Path)
beadID := beads.PolecatBeadID(rigName, polecatName)
beadID := polecatBeadIDForRig(r, rigName, polecatName)
existingIssue, _, _ := bd.GetAgentBead(beadID)
if existingIssue != nil && existingIssue.Status != "closed" {
return fmt.Errorf("identity bead %s already exists", beadID)
@@ -385,7 +385,7 @@ func runPolecatIdentityShow(cmd *cobra.Command, args []string) error {
// Get identity bead
bd := beads.New(r.Path)
beadID := beads.PolecatBeadID(rigName, polecatName)
beadID := polecatBeadIDForRig(r, rigName, polecatName)
issue, fields, err := bd.GetAgentBead(beadID)
if err != nil {
return fmt.Errorf("getting identity bead: %w", err)
@@ -414,10 +414,10 @@ func runPolecatIdentityShow(cmd *cobra.Command, args []string) error {
if polecatIdentityShowJSON {
output := struct {
IdentityInfo
Title string `json:"title"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
CV *CVSummary `json:"cv,omitempty"`
Title string `json:"title"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
CV *CVSummary `json:"cv,omitempty"`
}{
IdentityInfo: IdentityInfo{
Rig: rigName,
@@ -563,8 +563,8 @@ func runPolecatIdentityRename(cmd *cobra.Command, args []string) error {
}
bd := beads.New(r.Path)
oldBeadID := beads.PolecatBeadID(rigName, oldName)
newBeadID := beads.PolecatBeadID(rigName, newName)
oldBeadID := polecatBeadIDForRig(r, rigName, oldName)
newBeadID := polecatBeadIDForRig(r, rigName, newName)
// Check old identity exists
oldIssue, oldFields, err := bd.GetAgentBead(oldBeadID)
@@ -631,7 +631,7 @@ func runPolecatIdentityRemove(cmd *cobra.Command, args []string) error {
}
bd := beads.New(r.Path)
beadID := beads.PolecatBeadID(rigName, polecatName)
beadID := polecatBeadIDForRig(r, rigName, polecatName)
// Check identity exists
issue, fields, err := bd.GetAgentBead(beadID)
+4
View File
@@ -9,6 +9,7 @@ import (
"path/filepath"
"strings"
"github.com/steveyegge/gastown/internal/beads"
"github.com/steveyegge/gastown/internal/style"
"github.com/steveyegge/gastown/internal/workspace"
)
@@ -80,6 +81,9 @@ func createAutoConvoy(beadID, beadTitle string) (string, error) {
"--title=" + convoyTitle,
"--description=" + description,
}
if beads.NeedsForceForID(convoyID) {
createArgs = append(createArgs, "--force")
}
createCmd := exec.Command("bd", append([]string{"--no-daemon"}, createArgs...)...)
createCmd.Dir = townBeads