fix: ZFC improvements - query tmux directly instead of marker TTL
Two ZFC fixes: 1. Boot marker file (hq-zee5n): Changed IsRunning() to query tmux.HasSession() directly instead of checking marker file freshness with TTL. Removed stale marker check from doctor. 2. Branch pattern matching (hq-zwuh6): Replaced hardcoded "polecat/" strings with constants.BranchPolecatPrefix for consistency. Also removed 60-second WaitForCommand blocking from crew Start() which was causing gt crew start to hang. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
e0858096f6
commit
c94d59dca7
@@ -23,15 +23,12 @@ import (
|
|||||||
// to return true when only Boot is running.
|
// to return true when only Boot is running.
|
||||||
const SessionName = "gt-boot"
|
const SessionName = "gt-boot"
|
||||||
|
|
||||||
// MarkerFileName is the file that indicates Boot is currently running.
|
// MarkerFileName is the lock file for Boot startup coordination.
|
||||||
const MarkerFileName = ".boot-running"
|
const MarkerFileName = ".boot-running"
|
||||||
|
|
||||||
// StatusFileName stores Boot's last execution status.
|
// StatusFileName stores Boot's last execution status.
|
||||||
const StatusFileName = ".boot-status.json"
|
const StatusFileName = ".boot-status.json"
|
||||||
|
|
||||||
// DefaultMarkerTTL is how long a marker is considered valid before it's stale.
|
|
||||||
const DefaultMarkerTTL = 5 * time.Minute
|
|
||||||
|
|
||||||
// Status represents Boot's execution status.
|
// Status represents Boot's execution status.
|
||||||
type Status struct {
|
type Status struct {
|
||||||
Running bool `json:"running"`
|
Running bool `json:"running"`
|
||||||
@@ -78,22 +75,9 @@ func (b *Boot) statusPath() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsRunning checks if Boot is currently running.
|
// IsRunning checks if Boot is currently running.
|
||||||
// Returns true if marker exists and isn't stale, false otherwise.
|
// Queries tmux directly for observable reality (ZFC principle).
|
||||||
func (b *Boot) IsRunning() bool {
|
func (b *Boot) IsRunning() bool {
|
||||||
info, err := os.Stat(b.markerPath())
|
return b.IsSessionAlive()
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if marker is stale (older than TTL)
|
|
||||||
age := time.Since(info.ModTime())
|
|
||||||
if age > DefaultMarkerTTL {
|
|
||||||
// Stale marker - clean it up
|
|
||||||
_ = os.Remove(b.markerPath())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSessionAlive checks if the Boot tmux session exists.
|
// IsSessionAlive checks if the Boot tmux session exists.
|
||||||
@@ -106,7 +90,7 @@ func (b *Boot) IsSessionAlive() bool {
|
|||||||
// Returns error if Boot is already running.
|
// Returns error if Boot is already running.
|
||||||
func (b *Boot) AcquireLock() error {
|
func (b *Boot) AcquireLock() error {
|
||||||
if b.IsRunning() {
|
if b.IsRunning() {
|
||||||
return fmt.Errorf("boot is already running (marker exists)")
|
return fmt.Errorf("boot is already running (session exists)")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := b.EnsureDir(); err != nil {
|
if err := b.EnsureDir(); err != nil {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/steveyegge/gastown/internal/beads"
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
|
"github.com/steveyegge/gastown/internal/constants"
|
||||||
"github.com/steveyegge/gastown/internal/git"
|
"github.com/steveyegge/gastown/internal/git"
|
||||||
"github.com/steveyegge/gastown/internal/rig"
|
"github.com/steveyegge/gastown/internal/rig"
|
||||||
"github.com/steveyegge/gastown/internal/style"
|
"github.com/steveyegge/gastown/internal/style"
|
||||||
@@ -32,7 +33,7 @@ func parseBranchName(branch string) branchInfo {
|
|||||||
info := branchInfo{Branch: branch}
|
info := branchInfo{Branch: branch}
|
||||||
|
|
||||||
// Try polecat/<worker>/<issue> format
|
// Try polecat/<worker>/<issue> format
|
||||||
if strings.HasPrefix(branch, "polecat/") {
|
if strings.HasPrefix(branch, constants.BranchPolecatPrefix) {
|
||||||
parts := strings.SplitN(branch, "/", 3)
|
parts := strings.SplitN(branch, "/", 3)
|
||||||
if len(parts) == 3 {
|
if len(parts) == 3 {
|
||||||
info.Worker = parts[1]
|
info.Worker = parts[1]
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/steveyegge/gastown/internal/beads"
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
"github.com/steveyegge/gastown/internal/claude"
|
"github.com/steveyegge/gastown/internal/claude"
|
||||||
"github.com/steveyegge/gastown/internal/config"
|
"github.com/steveyegge/gastown/internal/config"
|
||||||
"github.com/steveyegge/gastown/internal/constants"
|
|
||||||
"github.com/steveyegge/gastown/internal/git"
|
"github.com/steveyegge/gastown/internal/git"
|
||||||
"github.com/steveyegge/gastown/internal/rig"
|
"github.com/steveyegge/gastown/internal/rig"
|
||||||
"github.com/steveyegge/gastown/internal/session"
|
"github.com/steveyegge/gastown/internal/session"
|
||||||
@@ -536,8 +535,10 @@ func (m *Manager) Start(name string, opts StartOptions) error {
|
|||||||
// Set up C-b n/p keybindings for crew session cycling (non-fatal)
|
// Set up C-b n/p keybindings for crew session cycling (non-fatal)
|
||||||
_ = t.SetCrewCycleBindings(sessionID)
|
_ = t.SetCrewCycleBindings(sessionID)
|
||||||
|
|
||||||
// Wait for Claude to start (non-fatal: session continues even if this times out)
|
// Note: We intentionally don't wait for Claude to start here.
|
||||||
_ = t.WaitForCommand(sessionID, constants.SupportedShells, constants.ClaudeStartTimeout)
|
// The session is created in detached mode, and blocking for 60 seconds
|
||||||
|
// serves no purpose. If the caller needs to know when Claude is ready,
|
||||||
|
// they can check with IsClaudeRunning().
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package doctor
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/steveyegge/gastown/internal/boot"
|
"github.com/steveyegge/gastown/internal/boot"
|
||||||
@@ -84,24 +83,9 @@ func (c *BootHealthCheck) Run(ctx *CheckContext) *CheckResult {
|
|||||||
details = append(details, "No previous run recorded")
|
details = append(details, "No previous run recorded")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check 4: Marker file freshness (stale marker indicates crash)
|
// Check 4: Currently running (uses tmux session state per ZFC principle)
|
||||||
markerPath := filepath.Join(bootDir, boot.MarkerFileName)
|
if sessionAlive {
|
||||||
if info, err := os.Stat(markerPath); err == nil {
|
details = append(details, "Currently running (tmux session active)")
|
||||||
age := time.Since(info.ModTime())
|
|
||||||
if age > boot.DefaultMarkerTTL {
|
|
||||||
return &CheckResult{
|
|
||||||
Name: c.Name(),
|
|
||||||
Status: StatusWarning,
|
|
||||||
Message: "Boot marker is stale (possible crash)",
|
|
||||||
Details: []string{
|
|
||||||
fmt.Sprintf("Marker age: %s", age.Round(time.Second)),
|
|
||||||
fmt.Sprintf("TTL: %s", boot.DefaultMarkerTTL),
|
|
||||||
},
|
|
||||||
FixHint: "Stale marker will be cleaned on next daemon tick",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Marker exists and is fresh - Boot is currently running
|
|
||||||
details = append(details, fmt.Sprintf("Currently running (marker age: %s)", age.Round(time.Second)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All checks passed
|
// All checks passed
|
||||||
|
|||||||
@@ -742,8 +742,8 @@ func (c *PolecatClonesValidCheck) Run(ctx *CheckContext) *CheckResult {
|
|||||||
branchOutput, err := cmd.Output()
|
branchOutput, err := cmd.Output()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
branch := strings.TrimSpace(string(branchOutput))
|
branch := strings.TrimSpace(string(branchOutput))
|
||||||
if !strings.HasPrefix(branch, "polecat/") {
|
if !strings.HasPrefix(branch, constants.BranchPolecatPrefix) {
|
||||||
warnings = append(warnings, fmt.Sprintf("%s: on branch '%s' (expected polecat/*)", polecatName, branch))
|
warnings = append(warnings, fmt.Sprintf("%s: on branch '%s' (expected %s*)", polecatName, branch, constants.BranchPolecatPrefix))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -663,7 +663,7 @@ func (m *Manager) FindMR(idOrBranch string) (*MergeRequest, error) {
|
|||||||
if item.MR.Branch == idOrBranch {
|
if item.MR.Branch == idOrBranch {
|
||||||
return item.MR, nil
|
return item.MR, nil
|
||||||
}
|
}
|
||||||
if "polecat/"+idOrBranch == item.MR.Branch {
|
if constants.BranchPolecatPrefix+idOrBranch == item.MR.Branch {
|
||||||
return item.MR, nil
|
return item.MR, nil
|
||||||
}
|
}
|
||||||
// Match by worker name (partial match for convenience)
|
// Match by worker name (partial match for convenience)
|
||||||
|
|||||||
Reference in New Issue
Block a user