diff --git a/internal/polecat/manager.go b/internal/polecat/manager.go index 0b4074ee..48fb2c2d 100644 --- a/internal/polecat/manager.go +++ b/internal/polecat/manager.go @@ -6,7 +6,6 @@ import ( "os" "path/filepath" "strconv" - "strings" "time" "github.com/steveyegge/gastown/internal/beads" @@ -203,10 +202,10 @@ func (m *Manager) Add(name string) (*Polecat, error) { return nil, fmt.Errorf("creating worktree: %w", err) } - // Install polecat CLAUDE.md template (non-fatal if template missing) - if err := m.installCLAUDETemplate(polecatPath, name); err != nil { - fmt.Printf("Warning: could not install CLAUDE.md template: %v\n", err) - } + // NOTE: We intentionally do NOT write to CLAUDE.md here. + // Gas Town context is injected ephemerally via SessionStart hook (gt prime). + // Writing to CLAUDE.md would overwrite project instructions and could leak + // Gas Town internals into the project repo if merged. // Set up shared beads: polecat uses rig's .beads via redirect file. // This eliminates git sync overhead - all polecats share one database. @@ -416,10 +415,8 @@ func (m *Manager) Recreate(name string, force bool) (*Polecat, error) { return nil, fmt.Errorf("creating fresh worktree: %w", err) } - // Install polecat CLAUDE.md template (non-fatal if template missing) - if err := m.installCLAUDETemplate(polecatPath, name); err != nil { - fmt.Printf("Warning: could not install CLAUDE.md template: %v\n", err) - } + // NOTE: We intentionally do NOT write to CLAUDE.md here. + // Gas Town context is injected ephemerally via SessionStart hook (gt prime). // Set up shared beads if err := m.setupSharedBeads(polecatPath); err != nil { @@ -725,79 +722,6 @@ func (m *Manager) loadFromBeads(name string) (*Polecat, error) { }, nil } -// installCLAUDETemplate copies the polecat CLAUDE.md template into the worktree. -// Template variables {{rig}} and {{name}} are substituted with actual values. -// This provides polecats with context about their role and available commands. -func (m *Manager) installCLAUDETemplate(polecatPath, name string) error { - // Try multiple template locations in order of precedence: - // 1. Rig-specific template: /mayor/rig/templates/polecat-CLAUDE.md - // 2. Gastown (canonical) template: /gastown/mayor/rig/templates/polecat-CLAUDE.md - templatePaths := []string{ - filepath.Join(m.rig.Path, "mayor", "rig", "templates", "polecat-CLAUDE.md"), - } - - // Add gastown fallback if we can find the town root - if townRoot, err := findTownRoot(m.rig.Path); err == nil && townRoot != "" { - gasTownTemplate := filepath.Join(townRoot, "gastown", "mayor", "rig", "templates", "polecat-CLAUDE.md") - // Only add fallback if different from rig-specific path - if gasTownTemplate != templatePaths[0] { - templatePaths = append(templatePaths, gasTownTemplate) - } - } - - var content []byte - for _, templatePath := range templatePaths { - var err error - content, err = os.ReadFile(templatePath) - if err == nil { - break // Found a template - } - if !os.IsNotExist(err) { - return fmt.Errorf("reading template %s: %w", templatePath, err) - } - } - - if content == nil { - // No template found in any location - warn and skip - fmt.Printf("Warning: polecat template not found (checked %d locations)\n", len(templatePaths)) - return nil - } - - // Substitute template variables - output := string(content) - output = strings.ReplaceAll(output, "{{rig}}", m.rig.Name) - output = strings.ReplaceAll(output, "{{name}}", name) - - // Write to polecat's CLAUDE.md - claudePath := filepath.Join(polecatPath, "CLAUDE.md") - if err := os.WriteFile(claudePath, []byte(output), 0644); err != nil { - return fmt.Errorf("writing CLAUDE.md: %w", err) - } - - return nil -} - -// findTownRoot locates the Gas Town root directory by walking up from startDir. -func findTownRoot(startDir string) (string, error) { - absDir, err := filepath.Abs(startDir) - if err != nil { - return "", err - } - - current := absDir - for { - // Check for primary marker (mayor/town.json) - if _, err := os.Stat(filepath.Join(current, "mayor", "town.json")); err == nil { - return current, nil - } - parent := filepath.Dir(current) - if parent == current { - return "", fmt.Errorf("town root not found") - } - current = parent - } -} - // setupSharedBeads creates a redirect file so the polecat uses the rig's shared .beads database. // This eliminates the need for git sync between polecat clones - all polecats share one database. // diff --git a/internal/polecat/manager_test.go b/internal/polecat/manager_test.go index 276c4307..53293dbb 100644 --- a/internal/polecat/manager_test.go +++ b/internal/polecat/manager_test.go @@ -3,7 +3,6 @@ package polecat import ( "os" "path/filepath" - "strings" "testing" "github.com/steveyegge/gastown/internal/git" @@ -266,135 +265,7 @@ func TestClearIssueWithoutAssignment(t *testing.T) { } } -// TestInstallCLAUDETemplate verifies the polecat CLAUDE.md template is installed -// from mayor/rig/templates/ (not rig root) with correct variable substitution. -// This is a regression test for gt-si6am. -func TestInstallCLAUDETemplate(t *testing.T) { - root := t.TempDir() - - // Create polecat directory - polecatDir := filepath.Join(root, "polecats", "testcat") - if err := os.MkdirAll(polecatDir, 0755); err != nil { - t.Fatalf("mkdir polecat: %v", err) - } - - // Create template at mayor/rig/templates/ (the correct location) - templateDir := filepath.Join(root, "mayor", "rig", "templates") - if err := os.MkdirAll(templateDir, 0755); err != nil { - t.Fatalf("mkdir templates: %v", err) - } - - // Write a template with variables - templateContent := `# Polecat Context - -**YOU ARE IN: {{rig}}/polecats/{{name}}/** - This is YOUR worktree. - -Your Role: POLECAT -Your rig: {{rig}} -Your name: {{name}} -` - templatePath := filepath.Join(templateDir, "polecat-CLAUDE.md") - if err := os.WriteFile(templatePath, []byte(templateContent), 0644); err != nil { - t.Fatalf("write template: %v", err) - } - - // Also create a WRONG template at rig root (the old buggy location) - // This should NOT be used - wrongTemplateDir := filepath.Join(root, "templates") - if err := os.MkdirAll(wrongTemplateDir, 0755); err != nil { - t.Fatalf("mkdir wrong templates: %v", err) - } - wrongContent := `# Mayor Context - THIS IS WRONG` - if err := os.WriteFile(filepath.Join(wrongTemplateDir, "polecat-CLAUDE.md"), []byte(wrongContent), 0644); err != nil { - t.Fatalf("write wrong template: %v", err) - } - - // Create manager and install template - r := &rig.Rig{ - Name: "gastown", - Path: root, - } - m := NewManager(r, git.NewGit(root)) - - err := m.installCLAUDETemplate(polecatDir, "testcat") - if err != nil { - t.Fatalf("installCLAUDETemplate: %v", err) - } - - // Read the installed CLAUDE.md - installedPath := filepath.Join(polecatDir, "CLAUDE.md") - content, err := os.ReadFile(installedPath) - if err != nil { - t.Fatalf("read installed CLAUDE.md: %v", err) - } - - // Verify it's the polecat template (not mayor) - if !strings.Contains(string(content), "Polecat Context") { - t.Error("CLAUDE.md should contain 'Polecat Context'") - } - if strings.Contains(string(content), "Mayor Context") { - t.Error("CLAUDE.md should NOT contain 'Mayor Context' (wrong template used)") - } - - // Verify variables were substituted - if strings.Contains(string(content), "{{rig}}") { - t.Error("{{rig}} should be substituted") - } - if strings.Contains(string(content), "{{name}}") { - t.Error("{{name}} should be substituted") - } - if !strings.Contains(string(content), "gastown/polecats/testcat/") { - t.Error("CLAUDE.md should contain substituted path 'gastown/polecats/testcat/'") - } - if !strings.Contains(string(content), "Your rig: gastown") { - t.Error("CLAUDE.md should contain 'Your rig: gastown'") - } - if !strings.Contains(string(content), "Your name: testcat") { - t.Error("CLAUDE.md should contain 'Your name: testcat'") - } -} - -// TestInstallCLAUDETemplateNotAtRigRoot verifies that templates at rig root -// (the old buggy location) are NOT used. -func TestInstallCLAUDETemplateNotAtRigRoot(t *testing.T) { - root := t.TempDir() - - // Create polecat directory - polecatDir := filepath.Join(root, "polecats", "testcat") - if err := os.MkdirAll(polecatDir, 0755); err != nil { - t.Fatalf("mkdir polecat: %v", err) - } - - // Only create template at rig root (wrong location) - // Do NOT create at mayor/rig/templates/ - wrongTemplateDir := filepath.Join(root, "templates") - if err := os.MkdirAll(wrongTemplateDir, 0755); err != nil { - t.Fatalf("mkdir wrong templates: %v", err) - } - wrongContent := `# Mayor Context - THIS IS WRONG` - if err := os.WriteFile(filepath.Join(wrongTemplateDir, "polecat-CLAUDE.md"), []byte(wrongContent), 0644); err != nil { - t.Fatalf("write wrong template: %v", err) - } - - // Create manager and try to install template - r := &rig.Rig{ - Name: "gastown", - Path: root, - } - m := NewManager(r, git.NewGit(root)) - - // Should not error (missing template is OK) but should NOT install wrong one - err := m.installCLAUDETemplate(polecatDir, "testcat") - if err != nil { - t.Fatalf("installCLAUDETemplate: %v", err) - } - - // CLAUDE.md should NOT exist (template not found at correct location) - installedPath := filepath.Join(polecatDir, "CLAUDE.md") - if _, err := os.Stat(installedPath); err == nil { - content, _ := os.ReadFile(installedPath) - if strings.Contains(string(content), "Mayor Context") { - t.Error("Template from rig root was incorrectly used - should use mayor/rig/templates/") - } - } -} +// NOTE: TestInstallCLAUDETemplate tests were removed. +// We no longer write CLAUDE.md to worktrees - Gas Town context is injected +// ephemerally via SessionStart hook (gt prime) to prevent leaking internal +// architecture into project repos.