From deb58838adadbb8d914ed380f6a68247ae214ec7 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Mon, 29 Dec 2025 23:42:30 -0800 Subject: [PATCH] fix: Use SQLite columns for hook_bead instead of description text (gt-9v52) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, UpdateAgentState embedded hook_bead in the description text field, while bd slot commands read/write from the SQLite hook_bead column. This caused gt sling to report hooks as occupied when bd slot show showed them empty. Fix: Change UpdateAgentState to use proper bd commands: - `bd agent state` for agent_state (updates column directly) - `bd slot set/clear` for hook_bead (updates column directly) This ensures consistency between gastown and beads commands. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- internal/beads/beads.go | 48 +++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/internal/beads/beads.go b/internal/beads/beads.go index 4555c98e..13f8fa91 100644 --- a/internal/beads/beads.go +++ b/internal/beads/beads.go @@ -642,24 +642,50 @@ func (b *Beads) CreateAgentBead(id, title string, fields *AgentFields) (*Issue, // UpdateAgentState updates the agent_state field in an agent bead. // Optionally updates hook_bead if provided. +// +// IMPORTANT: This function uses the proper bd commands to update agent fields: +// - `bd agent state` for agent_state (uses SQLite column directly) +// - `bd slot set/clear` for hook_bead (uses SQLite column directly) +// +// This ensures consistency with `bd slot show` and other beads commands. +// Previously, this function embedded these fields in the description text, +// which caused inconsistencies with bd slot commands (see GH #gt-9v52). func (b *Beads) UpdateAgentState(id string, state string, hookBead *string) error { - // First get current issue to preserve other fields - issue, err := b.Show(id) + // Update agent state using bd agent state command + // This updates the agent_state column directly in SQLite + _, err := b.run("agent", "state", id, state) if err != nil { - return err + return fmt.Errorf("updating agent state: %w", err) } - // Parse existing fields - fields := ParseAgentFields(issue.Description) - fields.AgentState = state + // Update hook_bead if provided if hookBead != nil { - fields.HookBead = *hookBead + if *hookBead != "" { + // Set the hook using bd slot set + // This updates the hook_bead column directly in SQLite + _, err = b.run("slot", "set", id, "hook", *hookBead) + if err != nil { + // If slot is already occupied, clear it first then retry + // This handles re-slinging scenarios where we're updating the hook + errStr := err.Error() + if strings.Contains(errStr, "already occupied") { + _, _ = b.run("slot", "clear", id, "hook") + _, err = b.run("slot", "set", id, "hook", *hookBead) + } + if err != nil { + return fmt.Errorf("setting hook: %w", err) + } + } + } else { + // Clear the hook + _, err = b.run("slot", "clear", id, "hook") + if err != nil { + return fmt.Errorf("clearing hook: %w", err) + } + } } - // Format new description - description := FormatAgentDescription(issue.Title, fields) - - return b.Update(id, UpdateOptions{Description: &description}) + return nil } // UpdateAgentCleanupStatus updates the cleanup_status field in an agent bead.