From 9729e05f86d9fdc44a15be8473b8a2f5ccfa4880 Mon Sep 17 00:00:00 2001 From: capable Date: Tue, 6 Jan 2026 19:55:17 -0800 Subject: [PATCH] feat(rig): add operational state to gt rig status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Status line showing operational state (OPERATIONAL/PARKED/DOCKED) with source indication (local/global - synced/default). The state is looked up using the property layer system: 1. Wisp layer (local/ephemeral): .beads-wisp/config/.json 2. Rig bead labels (global/synced): status:parked or status:docked 3. Default: OPERATIONAL Example output: gastown Status: PARKED (local) Path: /Users/stevey/gt/gastown ... Closes: gt-5l7h4 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- internal/cmd/rig.go | 57 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/internal/cmd/rig.go b/internal/cmd/rig.go index 7d80d8f4..d6ad1075 100644 --- a/internal/cmd/rig.go +++ b/internal/cmd/rig.go @@ -20,6 +20,7 @@ import ( "github.com/steveyegge/gastown/internal/session" "github.com/steveyegge/gastown/internal/style" "github.com/steveyegge/gastown/internal/tmux" + "github.com/steveyegge/gastown/internal/wisp" "github.com/steveyegge/gastown/internal/witness" "github.com/steveyegge/gastown/internal/workspace" ) @@ -1047,6 +1048,17 @@ func runRigStatus(cmd *cobra.Command, args []string) error { // Header fmt.Printf("%s\n", style.Bold.Render(rigName)) + + // Operational state + opState, opSource := getRigOperationalState(townRoot, rigName) + if opState == "OPERATIONAL" { + fmt.Printf(" Status: %s\n", style.Success.Render(opState)) + } else if opState == "PARKED" { + fmt.Printf(" Status: %s (%s)\n", style.Warning.Render(opState), opSource) + } else if opState == "DOCKED" { + fmt.Printf(" Status: %s (%s)\n", style.Dim.Render(opState), opSource) + } + fmt.Printf(" Path: %s\n", r.Path) if r.Config != nil && r.Config.Prefix != "" { fmt.Printf(" Beads prefix: %s-\n", r.Config.Prefix) @@ -1472,3 +1484,48 @@ func runRigRestart(cmd *cobra.Command, args []string) error { return nil } + +// getRigOperationalState returns the operational state and source for a rig. +// It checks the wisp layer first (local/ephemeral), then rig bead labels (global). +// Returns state ("OPERATIONAL", "PARKED", or "DOCKED") and source ("local", "global - synced", or "default"). +func getRigOperationalState(townRoot, rigName string) (state string, source string) { + // Check wisp layer first (local/ephemeral overrides) + wispConfig := wisp.NewConfig(townRoot, rigName) + if status := wispConfig.GetString("status"); status != "" { + switch strings.ToLower(status) { + case "parked": + return "PARKED", "local" + case "docked": + return "DOCKED", "local" + } + } + + // Check rig bead labels (global/synced) + // Rig identity bead ID: -rig- + // Look for status:docked or status:parked labels + rigPath := filepath.Join(townRoot, rigName) + rigBeadsDir := beads.ResolveBeadsDir(rigPath) + bd := beads.NewWithBeadsDir(rigPath, rigBeadsDir) + + // Try to find the rig identity bead + // Convention: -rig- + if rigCfg, err := rig.LoadRigConfig(rigPath); err == nil && rigCfg.Beads != nil { + rigBeadID := fmt.Sprintf("%s-rig-%s", rigCfg.Beads.Prefix, rigName) + if issue, err := bd.Show(rigBeadID); err == nil { + for _, label := range issue.Labels { + if strings.HasPrefix(label, "status:") { + statusValue := strings.TrimPrefix(label, "status:") + switch strings.ToLower(statusValue) { + case "docked": + return "DOCKED", "global - synced" + case "parked": + return "PARKED", "global - synced" + } + } + } + } + } + + // Default: operational + return "OPERATIONAL", "default" +}