Adds RigBeadsCheck to gt doctor to verify rig identity beads exist. These beads track rig metadata (git URL, prefix, state) and are created by gt rig add. The check scans routes.jsonl and verifies each rig has an identity bead, with --fix to create missing ones. Recovered from furiosa's uncommitted work after worker interruption. Co-Authored-By: furiosa <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
185 lines
6.3 KiB
Go
185 lines
6.3 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/steveyegge/gastown/internal/doctor"
|
|
"github.com/steveyegge/gastown/internal/workspace"
|
|
)
|
|
|
|
var (
|
|
doctorFix bool
|
|
doctorVerbose bool
|
|
doctorRig string
|
|
doctorRestartSessions bool
|
|
)
|
|
|
|
var doctorCmd = &cobra.Command{
|
|
Use: "doctor",
|
|
GroupID: GroupDiag,
|
|
Short: "Run health checks on the workspace",
|
|
Long: `Run diagnostic checks on the Gas Town workspace.
|
|
|
|
Doctor checks for common configuration issues, missing files,
|
|
and other problems that could affect workspace operation.
|
|
|
|
Workspace checks:
|
|
- town-config-exists Check mayor/town.json exists
|
|
- town-config-valid Check mayor/town.json is valid
|
|
- rigs-registry-exists Check mayor/rigs.json exists (fixable)
|
|
- rigs-registry-valid Check registered rigs exist (fixable)
|
|
- mayor-exists Check mayor/ directory structure
|
|
|
|
Infrastructure checks:
|
|
- daemon Check if daemon is running (fixable)
|
|
- repo-fingerprint Check database has valid repo fingerprint (fixable)
|
|
- boot-health Check Boot watchdog health (vet mode)
|
|
|
|
Cleanup checks (fixable):
|
|
- orphan-sessions Detect orphaned tmux sessions
|
|
- orphan-processes Detect orphaned Claude processes
|
|
- wisp-gc Detect and clean abandoned wisps (>1h)
|
|
|
|
Clone divergence checks:
|
|
- persistent-role-branches Detect crew/witness/refinery not on main
|
|
- clone-divergence Detect clones significantly behind origin/main
|
|
|
|
Crew workspace checks:
|
|
- crew-state Validate crew worker state.json files (fixable)
|
|
- crew-worktrees Detect stale cross-rig worktrees (fixable)
|
|
|
|
Rig checks (with --rig flag):
|
|
- rig-is-git-repo Verify rig is a valid git repository
|
|
- git-exclude-configured Check .git/info/exclude has Gas Town dirs (fixable)
|
|
- witness-exists Verify witness/ structure exists (fixable)
|
|
- refinery-exists Verify refinery/ structure exists (fixable)
|
|
- mayor-clone-exists Verify mayor/rig/ clone exists (fixable)
|
|
- polecat-clones-valid Verify polecat directories are valid clones
|
|
- beads-config-valid Verify beads configuration (fixable)
|
|
|
|
Routing checks (fixable):
|
|
- routes-config Check beads routing configuration
|
|
- prefix-mismatch Detect rigs.json vs routes.jsonl prefix mismatches (fixable)
|
|
|
|
Session hook checks:
|
|
- session-hooks Check settings.json use session-start.sh
|
|
- claude-settings Check Claude settings.json match templates (fixable)
|
|
|
|
Patrol checks:
|
|
- patrol-molecules-exist Verify patrol molecules exist
|
|
- patrol-hooks-wired Verify daemon triggers patrols
|
|
- patrol-not-stuck Detect stale wisps (>1h)
|
|
- patrol-plugins-accessible Verify plugin directories
|
|
- patrol-roles-have-prompts Verify role prompts exist
|
|
|
|
Use --fix to attempt automatic fixes for issues that support it.
|
|
Use --rig to check a specific rig instead of the entire workspace.`,
|
|
RunE: runDoctor,
|
|
}
|
|
|
|
func init() {
|
|
doctorCmd.Flags().BoolVar(&doctorFix, "fix", false, "Attempt to automatically fix issues")
|
|
doctorCmd.Flags().BoolVarP(&doctorVerbose, "verbose", "v", false, "Show detailed output")
|
|
doctorCmd.Flags().StringVar(&doctorRig, "rig", "", "Check specific rig only")
|
|
doctorCmd.Flags().BoolVar(&doctorRestartSessions, "restart-sessions", false, "Restart patrol sessions when fixing stale settings (use with --fix)")
|
|
rootCmd.AddCommand(doctorCmd)
|
|
}
|
|
|
|
func runDoctor(cmd *cobra.Command, args []string) error {
|
|
// Find town root
|
|
townRoot, err := workspace.FindFromCwdOrError()
|
|
if err != nil {
|
|
return fmt.Errorf("not in a Gas Town workspace: %w", err)
|
|
}
|
|
|
|
// Create check context
|
|
ctx := &doctor.CheckContext{
|
|
TownRoot: townRoot,
|
|
RigName: doctorRig,
|
|
Verbose: doctorVerbose,
|
|
RestartSessions: doctorRestartSessions,
|
|
}
|
|
|
|
// Create doctor and register checks
|
|
d := doctor.NewDoctor()
|
|
|
|
// Register workspace-level checks first (fundamental)
|
|
d.RegisterAll(doctor.WorkspaceChecks()...)
|
|
|
|
// Register built-in checks
|
|
d.Register(doctor.NewTownGitCheck())
|
|
d.Register(doctor.NewDaemonCheck())
|
|
d.Register(doctor.NewRepoFingerprintCheck())
|
|
d.Register(doctor.NewBootHealthCheck())
|
|
d.Register(doctor.NewBeadsDatabaseCheck())
|
|
d.Register(doctor.NewBdDaemonCheck())
|
|
d.Register(doctor.NewPrefixConflictCheck())
|
|
d.Register(doctor.NewPrefixMismatchCheck())
|
|
d.Register(doctor.NewRoutesCheck())
|
|
d.Register(doctor.NewOrphanSessionCheck())
|
|
d.Register(doctor.NewOrphanProcessCheck())
|
|
d.Register(doctor.NewWispGCCheck())
|
|
d.Register(doctor.NewBranchCheck())
|
|
d.Register(doctor.NewBeadsSyncOrphanCheck())
|
|
d.Register(doctor.NewCloneDivergenceCheck())
|
|
d.Register(doctor.NewIdentityCollisionCheck())
|
|
d.Register(doctor.NewLinkedPaneCheck())
|
|
d.Register(doctor.NewThemeCheck())
|
|
|
|
// Patrol system checks
|
|
d.Register(doctor.NewPatrolMoleculesExistCheck())
|
|
d.Register(doctor.NewPatrolHooksWiredCheck())
|
|
d.Register(doctor.NewPatrolNotStuckCheck())
|
|
d.Register(doctor.NewPatrolPluginsAccessibleCheck())
|
|
d.Register(doctor.NewPatrolRolesHavePromptsCheck())
|
|
d.Register(doctor.NewAgentBeadsCheck())
|
|
d.Register(doctor.NewRigBeadsCheck())
|
|
|
|
// NOTE: StaleAttachmentsCheck removed - staleness detection belongs in Deacon molecule
|
|
|
|
// Config architecture checks
|
|
d.Register(doctor.NewSettingsCheck())
|
|
d.Register(doctor.NewSessionHookCheck())
|
|
d.Register(doctor.NewRuntimeGitignoreCheck())
|
|
d.Register(doctor.NewLegacyGastownCheck())
|
|
d.Register(doctor.NewClaudeSettingsCheck())
|
|
|
|
// Crew workspace checks
|
|
d.Register(doctor.NewCrewStateCheck())
|
|
d.Register(doctor.NewCrewWorktreeCheck())
|
|
d.Register(doctor.NewCommandsCheck())
|
|
|
|
// Lifecycle hygiene checks
|
|
d.Register(doctor.NewLifecycleHygieneCheck())
|
|
|
|
// Hook attachment checks
|
|
d.Register(doctor.NewHookAttachmentValidCheck())
|
|
d.Register(doctor.NewHookSingletonCheck())
|
|
d.Register(doctor.NewOrphanedAttachmentsCheck())
|
|
|
|
// Rig-specific checks (only when --rig is specified)
|
|
if doctorRig != "" {
|
|
d.RegisterAll(doctor.RigChecks()...)
|
|
}
|
|
|
|
// Run checks
|
|
var report *doctor.Report
|
|
if doctorFix {
|
|
report = d.Fix(ctx)
|
|
} else {
|
|
report = d.Run(ctx)
|
|
}
|
|
|
|
// Print report
|
|
report.Print(os.Stdout, doctorVerbose)
|
|
|
|
// Exit with error code if there are errors
|
|
if report.HasErrors() {
|
|
return fmt.Errorf("doctor found %d error(s)", report.Summary.Errors)
|
|
}
|
|
|
|
return nil
|
|
}
|