Files
gastown/internal/cmd/rig_dock.go
mayor b316239d12 chore(gastown): scorched-earth SQLite removal from codebase
Remove all bd sync references and SQLite-specific code from gastown:

**Formulas (agent priming):**
- mol-polecat-work: Remove bd sync step from prepare-for-review
- mol-sync-workspace: Replace sync-beads step with verify-beads (Dolt check)
- mol-polecat-conflict-resolve: Remove bd sync from close-beads
- mol-polecat-code-review: Remove bd sync from summarize-review and complete-and-exit
- mol-polecat-review-pr: Remove bd sync from complete-and-exit
- mol-convoy-cleanup: Remove bd sync from archive-convoy
- mol-digest-generate: Remove bd sync from send-digest
- mol-town-shutdown: Replace sync-state step with verify-state
- beads-release: Replace restart-daemons with verify-install (no daemons with Dolt)

**Templates (role priming):**
- mayor.md.tmpl: Update session end checklist to remove bd sync steps
- crew.md.tmpl: Remove bd sync references from workflow and checklist
- polecat.md.tmpl: Update self-cleaning model and session close docs
- spawn.md.tmpl: Remove bd sync from completion steps
- nudge.md.tmpl: Remove bd sync from completion steps

**Go code:**
- session_manager.go: Remove syncBeads function and call
- rig_dock.go: Remove bd sync calls from dock/undock
- crew/manager.go: Remove runBdSync, update Pristine function
- crew_maintenance.go: Remove bd sync status output
- crew.go: Update pristine command help text
- polecat.go: Make sync command a no-op with deprecation message
- daemon/lifecycle.go: Remove bd sync from startup sequence
- doctor/beads_check.go: Update fix hints and Fix to use bd import not bd sync
- doctor/rig_check.go: Remove sync status check, simplify BeadsConfigValidCheck
- beads/beads.go: Update primeContent to remove bd sync references

With Dolt backend, beads changes are persisted immediately to the sql-server.
There is no separate sync step needed.

Part of epic: hq-e4eefc (SQLite removal)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 14:08:53 -08:00

261 lines
7.3 KiB
Go

package cmd
import (
"fmt"
"os/exec"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/beads"
"github.com/steveyegge/gastown/internal/refinery"
"github.com/steveyegge/gastown/internal/style"
"github.com/steveyegge/gastown/internal/tmux"
"github.com/steveyegge/gastown/internal/witness"
)
// RigDockedLabel is the label set on rig identity beads when docked.
const RigDockedLabel = "status:docked"
var rigDockCmd = &cobra.Command{
Use: "dock <rig>",
Short: "Dock a rig (global, persistent shutdown)",
Long: `Dock a rig to persistently disable it across all clones.
Docking a rig:
- Stops the witness if running
- Stops the refinery if running
- Sets status:docked label on the rig identity bead
- Syncs via git so all clones see the docked status
This is a Level 2 (global/persistent) operation:
- Affects all clones of this rig (via git sync)
- Persists until explicitly undocked
- The daemon respects this status and won't auto-restart agents
Use 'gt rig undock' to resume normal operation.
Examples:
gt rig dock gastown
gt rig dock beads`,
Args: cobra.ExactArgs(1),
RunE: runRigDock,
}
var rigUndockCmd = &cobra.Command{
Use: "undock <rig>",
Short: "Undock a rig (remove global docked status)",
Long: `Undock a rig to remove the persistent docked status.
Undocking a rig:
- Removes the status:docked label from the rig identity bead
- Syncs via git so all clones see the undocked status
- Allows the daemon to auto-restart agents
- Does NOT automatically start agents (use 'gt rig start' for that)
Examples:
gt rig undock gastown
gt rig undock beads`,
Args: cobra.ExactArgs(1),
RunE: runRigUndock,
}
func init() {
rigCmd.AddCommand(rigDockCmd)
rigCmd.AddCommand(rigUndockCmd)
}
func runRigDock(cmd *cobra.Command, args []string) error {
rigName := args[0]
// Check we're on main branch - docking on other branches won't persist
branchCmd := exec.Command("git", "branch", "--show-current")
branchOutput, err := branchCmd.Output()
if err == nil {
currentBranch := string(branchOutput)
currentBranch = currentBranch[:len(currentBranch)-1] // trim newline
if currentBranch != "main" && currentBranch != "master" {
return fmt.Errorf("cannot dock: must be on main branch (currently on %s)\n"+
"Docking on other branches won't persist. Run: git checkout main", currentBranch)
}
}
// Get rig
_, r, err := getRig(rigName)
if err != nil {
return err
}
// Get rig prefix for bead ID
prefix := "gt" // default
if r.Config != nil && r.Config.Prefix != "" {
prefix = r.Config.Prefix
}
// Find the rig identity bead
rigBeadID := beads.RigBeadIDWithPrefix(prefix, rigName)
bd := beads.New(r.BeadsPath())
// Check if rig bead exists, create if not
rigBead, err := bd.Show(rigBeadID)
if err != nil {
// Rig identity bead doesn't exist (legacy rig) - create it
fmt.Printf(" Creating rig identity bead %s...\n", rigBeadID)
rigBead, err = bd.CreateRigBead(rigBeadID, rigName, &beads.RigFields{
Repo: r.GitURL,
Prefix: prefix,
State: "active",
})
if err != nil {
return fmt.Errorf("creating rig identity bead: %w", err)
}
}
// Check if already docked
for _, label := range rigBead.Labels {
if label == RigDockedLabel {
fmt.Printf("%s Rig %s is already docked\n", style.Dim.Render("•"), rigName)
return nil
}
}
fmt.Printf("Docking rig %s...\n", style.Bold.Render(rigName))
var stoppedAgents []string
t := tmux.NewTmux()
// Stop witness if running
witnessSession := fmt.Sprintf("gt-%s-witness", rigName)
witnessRunning, _ := t.HasSession(witnessSession)
if witnessRunning {
fmt.Printf(" Stopping witness...\n")
witMgr := witness.NewManager(r)
if err := witMgr.Stop(); err != nil {
fmt.Printf(" %s Failed to stop witness: %v\n", style.Warning.Render("!"), err)
} else {
stoppedAgents = append(stoppedAgents, "Witness stopped")
}
}
// Stop refinery if running
refinerySession := fmt.Sprintf("gt-%s-refinery", rigName)
refineryRunning, _ := t.HasSession(refinerySession)
if refineryRunning {
fmt.Printf(" Stopping refinery...\n")
refMgr := refinery.NewManager(r)
if err := refMgr.Stop(); err != nil {
fmt.Printf(" %s Failed to stop refinery: %v\n", style.Warning.Render("!"), err)
} else {
stoppedAgents = append(stoppedAgents, "Refinery stopped")
}
}
// Set docked label on rig identity bead
if err := bd.Update(rigBeadID, beads.UpdateOptions{
AddLabels: []string{RigDockedLabel},
}); err != nil {
return fmt.Errorf("setting docked label: %w", err)
}
// Output
fmt.Printf("%s Rig %s docked (global)\n", style.Success.Render("✓"), rigName)
fmt.Printf(" Label added: %s\n", RigDockedLabel)
for _, msg := range stoppedAgents {
fmt.Printf(" %s\n", msg)
}
return nil
}
func runRigUndock(cmd *cobra.Command, args []string) error {
rigName := args[0]
// Check we're on main branch - undocking on other branches won't persist
branchCmd := exec.Command("git", "branch", "--show-current")
branchOutput, err := branchCmd.Output()
if err == nil {
currentBranch := string(branchOutput)
currentBranch = currentBranch[:len(currentBranch)-1] // trim newline
if currentBranch != "main" && currentBranch != "master" {
return fmt.Errorf("cannot undock: must be on main branch (currently on %s)\n"+
"Undocking on other branches won't persist. Run: git checkout main", currentBranch)
}
}
// Get rig and town root
_, r, err := getRig(rigName)
if err != nil {
return err
}
// Get rig prefix for bead ID
prefix := "gt" // default
if r.Config != nil && r.Config.Prefix != "" {
prefix = r.Config.Prefix
}
// Find the rig identity bead
rigBeadID := beads.RigBeadIDWithPrefix(prefix, rigName)
bd := beads.New(r.BeadsPath())
// Check if rig bead exists, create if not
rigBead, err := bd.Show(rigBeadID)
if err != nil {
// Rig identity bead doesn't exist (legacy rig) - can't be docked
fmt.Printf("%s Rig %s has no identity bead and is not docked\n", style.Dim.Render("•"), rigName)
return nil
}
// Check if actually docked
isDocked := false
for _, label := range rigBead.Labels {
if label == RigDockedLabel {
isDocked = true
break
}
}
if !isDocked {
fmt.Printf("%s Rig %s is not docked\n", style.Dim.Render("•"), rigName)
return nil
}
// Remove docked label from rig identity bead
if err := bd.Update(rigBeadID, beads.UpdateOptions{
RemoveLabels: []string{RigDockedLabel},
}); err != nil {
return fmt.Errorf("removing docked label: %w", err)
}
fmt.Printf("%s Rig %s undocked\n", style.Success.Render("✓"), rigName)
fmt.Printf(" Label removed: %s\n", RigDockedLabel)
fmt.Printf(" Daemon can now auto-restart agents\n")
fmt.Printf(" Use '%s' to start agents immediately\n", style.Dim.Render("gt rig start "+rigName))
return nil
}
// IsRigDocked checks if a rig is docked by checking for the status:docked label
// on the rig identity bead. This function is exported for use by the daemon.
func IsRigDocked(townRoot, rigName, prefix string) bool {
// Construct the rig beads path
rigPath := townRoot + "/" + rigName
beadsPath := rigPath + "/mayor/rig"
if _, err := exec.Command("test", "-d", beadsPath).CombinedOutput(); err != nil {
beadsPath = rigPath
}
bd := beads.New(beadsPath)
rigBeadID := beads.RigBeadIDWithPrefix(prefix, rigName)
rigBead, err := bd.Show(rigBeadID)
if err != nil {
return false
}
for _, label := range rigBead.Labels {
if label == RigDockedLabel {
return true
}
}
return false
}