fix: improve workspace detection for nested rig structures

Fixes gt-e5o: When a rig has its own mayor/ directory, workspace
detection now continues searching upward for primary markers.

Changes:
- Add AlternativePrimaryMarker (mayor/config.json) to distinguish
  town-level mayor from rig-level mayor clones
- Continue searching after finding secondary marker (mayor/) to
  prefer primary matches higher in the tree
- Return the first secondary match only if no primary is found

This fixes role detection for polecats/refinery/witness when running
from within a rig that has its own mayor/ clone.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-17 17:03:01 -08:00
parent f04cc6fe15
commit 3f3c7f82b2

View File

@@ -16,13 +16,22 @@ const (
// PrimaryMarker is the main config file that identifies a workspace. // PrimaryMarker is the main config file that identifies a workspace.
PrimaryMarker = "config/town.json" PrimaryMarker = "config/town.json"
// AlternativePrimaryMarker is the town-level mayor config file.
// This distinguishes a town mayor from a rig-level mayor clone.
AlternativePrimaryMarker = "mayor/config.json"
// SecondaryMarker is an alternative indicator at the town level. // SecondaryMarker is an alternative indicator at the town level.
// Note: This can match rig-level mayors too, so we continue searching
// upward after finding this to look for primary markers.
SecondaryMarker = "mayor" SecondaryMarker = "mayor"
) )
// Find locates the town root by walking up from the given directory. // Find locates the town root by walking up from the given directory.
// It looks for config/town.json (primary) or mayor/ directory (secondary). // It looks for config/town.json or mayor/config.json (primary markers)
// Returns the absolute path to the town root, or empty string if not found. // or mayor/ directory (secondary marker).
//
// To avoid matching rig-level mayor directories, we continue searching
// upward after finding a secondary marker, preferring primary matches.
func Find(startDir string) (string, error) { func Find(startDir string) (string, error) {
// Resolve to absolute path and follow symlinks // Resolve to absolute path and follow symlinks
absDir, err := filepath.Abs(startDir) absDir, err := filepath.Abs(startDir)
@@ -35,6 +44,9 @@ func Find(startDir string) (string, error) {
return "", fmt.Errorf("evaluating symlinks: %w", err) return "", fmt.Errorf("evaluating symlinks: %w", err)
} }
// Track the first secondary match in case no primary is found
var secondaryMatch string
// Walk up the directory tree // Walk up the directory tree
current := absDir current := absDir
for { for {
@@ -44,18 +56,28 @@ func Find(startDir string) (string, error) {
return current, nil return current, nil
} }
// Check for secondary marker (mayor/ directory) // Check for alternative primary marker (mayor/config.json)
secondaryPath := filepath.Join(current, SecondaryMarker) // This distinguishes a town-level mayor from a rig-level mayor clone
info, err := os.Stat(secondaryPath) altPrimaryPath := filepath.Join(current, AlternativePrimaryMarker)
if err == nil && info.IsDir() { if _, err := os.Stat(altPrimaryPath); err == nil {
return current, nil return current, nil
} }
// Check for secondary marker (mayor/ directory)
// Don't return immediately - continue searching for primary markers
if secondaryMatch == "" {
secondaryPath := filepath.Join(current, SecondaryMarker)
info, err := os.Stat(secondaryPath)
if err == nil && info.IsDir() {
secondaryMatch = current
}
}
// Move to parent directory // Move to parent directory
parent := filepath.Dir(current) parent := filepath.Dir(current)
if parent == current { if parent == current {
// Reached filesystem root // Reached filesystem root - return secondary match if found
return "", nil return secondaryMatch, nil
} }
current = parent current = parent
} }
@@ -92,6 +114,8 @@ func FindFromCwdOrError() (string, error) {
} }
// IsWorkspace checks if the given directory is a Gas Town workspace root. // IsWorkspace checks if the given directory is a Gas Town workspace root.
// A directory is a workspace if it has primary markers (config/town.json
// or mayor/config.json) or a secondary marker (mayor/ directory).
func IsWorkspace(dir string) (bool, error) { func IsWorkspace(dir string) (bool, error) {
absDir, err := filepath.Abs(dir) absDir, err := filepath.Abs(dir)
if err != nil { if err != nil {
@@ -104,6 +128,12 @@ func IsWorkspace(dir string) (bool, error) {
return true, nil return true, nil
} }
// Check for alternative primary marker
altPrimaryPath := filepath.Join(absDir, AlternativePrimaryMarker)
if _, err := os.Stat(altPrimaryPath); err == nil {
return true, nil
}
// Check for secondary marker // Check for secondary marker
secondaryPath := filepath.Join(absDir, SecondaryMarker) secondaryPath := filepath.Join(absDir, SecondaryMarker)
info, err := os.Stat(secondaryPath) info, err := os.Stat(secondaryPath)