Add CheckMisclassifiedWisps doctor check to detect issues that should be marked as wisps but aren't. This catches merge-requests, patrol molecules, and operational work that lacks the wisp:true flag. Add defense-in-depth wisp filtering to gt ready command. While bd ready should already filter wisps, this provides an additional layer to ensure ephemeral operational work doesn't leak into the ready work display. Changes: - New doctor check: misclassified-wisps (fixable, CategoryCleanup) - gt ready now filters wisps from issues.jsonl in addition to scaffolds - Detects wisp patterns: merge-request type, patrol labels, mol-* IDs Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
210 lines
7.3 KiB
Go
210 lines
7.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
|
|
|
|
Town root protection:
|
|
- town-git Verify town root is under version control
|
|
- town-root-branch Verify town root is on main branch (fixable)
|
|
- pre-checkout-hook Verify pre-checkout hook prevents branch switches (fixable)
|
|
|
|
Infrastructure checks:
|
|
- stale-binary Check if gt binary is up to date with repo
|
|
- 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()...)
|
|
|
|
d.Register(doctor.NewGlobalStateCheck())
|
|
|
|
// Register built-in checks
|
|
d.Register(doctor.NewStaleBinaryCheck())
|
|
d.Register(doctor.NewSqlite3Check())
|
|
d.Register(doctor.NewTownGitCheck())
|
|
d.Register(doctor.NewTownRootBranchCheck())
|
|
d.Register(doctor.NewPreCheckoutHookCheck())
|
|
d.Register(doctor.NewDaemonCheck())
|
|
d.Register(doctor.NewRepoFingerprintCheck())
|
|
d.Register(doctor.NewBootHealthCheck())
|
|
d.Register(doctor.NewBeadsDatabaseCheck())
|
|
d.Register(doctor.NewCustomTypesCheck())
|
|
d.Register(doctor.NewRoleLabelCheck())
|
|
d.Register(doctor.NewFormulaCheck())
|
|
d.Register(doctor.NewBdDaemonCheck())
|
|
d.Register(doctor.NewPrefixConflictCheck())
|
|
d.Register(doctor.NewPrefixMismatchCheck())
|
|
d.Register(doctor.NewRoutesCheck())
|
|
d.Register(doctor.NewRigRoutesJSONLCheck())
|
|
d.Register(doctor.NewRoutingModeCheck())
|
|
d.Register(doctor.NewOrphanSessionCheck())
|
|
d.Register(doctor.NewZombieSessionCheck())
|
|
d.Register(doctor.NewOrphanProcessCheck())
|
|
d.Register(doctor.NewWispGCCheck())
|
|
d.Register(doctor.NewCheckMisclassifiedWisps())
|
|
d.Register(doctor.NewBranchCheck())
|
|
d.Register(doctor.NewBeadsSyncOrphanCheck())
|
|
d.Register(doctor.NewCloneDivergenceCheck())
|
|
d.Register(doctor.NewIdentityCollisionCheck())
|
|
d.Register(doctor.NewLinkedPaneCheck())
|
|
d.Register(doctor.NewThemeCheck())
|
|
d.Register(doctor.NewCrashReportCheck())
|
|
d.Register(doctor.NewEnvVarsCheck())
|
|
|
|
// 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())
|
|
d.Register(doctor.NewRoleBeadsCheck())
|
|
|
|
// 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())
|
|
|
|
// Priming subsystem check
|
|
d.Register(doctor.NewPrimingCheck())
|
|
|
|
// 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
|
|
}
|