From 30984dcf958ca7320143d66cbe417409696b2fbb Mon Sep 17 00:00:00 2001 From: max Date: Sat, 10 Jan 2026 12:39:53 -0800 Subject: [PATCH] fix(priming): use bootstrap pointers instead of full CLAUDE.md templates Fresh installs and rig adds were creating full CLAUDE.md files (285 lines for mayor, ~100 lines for other roles), causing gt doctor to fail the priming check immediately. Per the priming architecture, CLAUDE.md should be a minimal bootstrap pointer (<30 lines) that tells agents to run gt prime. Full context is injected ephemerally at session start. Changes: - install.go: createMayorCLAUDEmd now writes 12-line bootstrap pointer - manager.go: createRoleCLAUDEmd now writes role-specific bootstrap pointers for mayor, refinery, crew, and polecat roles Note: The AGENTS.md issue mentioned in #316 could not be reproduced - the code does not appear to create AGENTS.md at rig level. May be from an older version or different configuration. Partial fix for #316 Co-Authored-By: Claude Opus 4.5 --- internal/cmd/install.go | 26 ++++++++---- internal/rig/manager.go | 93 ++++++++++++++++++++++++++--------------- 2 files changed, 77 insertions(+), 42 deletions(-) diff --git a/internal/cmd/install.go b/internal/cmd/install.go index 8da51075..036777a9 100644 --- a/internal/cmd/install.go +++ b/internal/cmd/install.go @@ -16,7 +16,6 @@ import ( "github.com/steveyegge/gastown/internal/config" "github.com/steveyegge/gastown/internal/deps" "github.com/steveyegge/gastown/internal/formula" - "github.com/steveyegge/gastown/internal/session" "github.com/steveyegge/gastown/internal/shell" "github.com/steveyegge/gastown/internal/state" "github.com/steveyegge/gastown/internal/style" @@ -310,14 +309,23 @@ func runInstall(cmd *cobra.Command, args []string) error { } func createMayorCLAUDEmd(mayorDir, townRoot string) error { - townName, _ := workspace.GetTownName(townRoot) - return templates.CreateMayorCLAUDEmd( - mayorDir, - townRoot, - townName, - session.MayorSessionName(), - session.DeaconSessionName(), - ) + // Create a minimal bootstrap pointer instead of full context. + // Full context is injected ephemerally by `gt prime` at session start. + // This keeps the on-disk file small (<30 lines) per priming architecture. + bootstrap := `# Mayor Context + +> **Recovery**: Run ` + "`gt prime`" + ` after compaction, clear, or new session + +Full context is injected by ` + "`gt prime`" + ` at session start. + +## Quick Reference + +- Check mail: ` + "`gt mail inbox`" + ` +- Check rigs: ` + "`gt rig list`" + ` +- Start patrol: ` + "`gt patrol start`" + ` +` + claudePath := filepath.Join(mayorDir, "CLAUDE.md") + return os.WriteFile(claudePath, []byte(bootstrap), 0644) } func writeJSON(path string, data interface{}) error { diff --git a/internal/rig/manager.go b/internal/rig/manager.go index 8199f31d..e1e332c1 100644 --- a/internal/rig/manager.go +++ b/internal/rig/manager.go @@ -16,8 +16,6 @@ import ( "github.com/steveyegge/gastown/internal/constants" "github.com/steveyegge/gastown/internal/config" "github.com/steveyegge/gastown/internal/git" - "github.com/steveyegge/gastown/internal/templates" - "github.com/steveyegge/gastown/internal/workspace" ) // Common errors @@ -900,46 +898,75 @@ func (m *Manager) ListRigNames() []string { return names } -// createRoleCLAUDEmd creates a CLAUDE.md file with role-specific context. -// This ensures each workspace (crew, refinery, mayor) gets the correct prompting, -// overriding any CLAUDE.md that may exist in the cloned repository. +// createRoleCLAUDEmd creates a minimal bootstrap pointer CLAUDE.md file. +// Full context is injected ephemerally by `gt prime` at session start. +// This keeps on-disk files small (<30 lines) per the priming architecture. func (m *Manager) createRoleCLAUDEmd(workspacePath string, role string, rigName string, workerName string) error { - tmpl, err := templates.New() - if err != nil { - return err - } + // Create role-specific bootstrap pointer + var bootstrap string + switch role { + case "mayor": + bootstrap = `# Mayor Context (` + rigName + `) - // Get town name for session names - townName, _ := workspace.GetTownName(m.townRoot) +> **Recovery**: Run ` + "`gt prime`" + ` after compaction, clear, or new session - // Get default branch from rig config (default to "main" if not set) - defaultBranch := "main" - if rigName != "" { - rigPath := filepath.Join(m.townRoot, rigName) - if rigCfg, err := LoadRigConfig(rigPath); err == nil && rigCfg.DefaultBranch != "" { - defaultBranch = rigCfg.DefaultBranch +Full context is injected by ` + "`gt prime`" + ` at session start. +` + case "refinery": + bootstrap = `# Refinery Context (` + rigName + `) + +> **Recovery**: Run ` + "`gt prime`" + ` after compaction, clear, or new session + +Full context is injected by ` + "`gt prime`" + ` at session start. + +## Quick Reference + +- Check MQ: ` + "`gt mq list`" + ` +- Process next: ` + "`gt mq process`" + ` +` + case "crew": + name := workerName + if name == "" { + name = "worker" } - } + bootstrap = `# Crew Context (` + rigName + `/` + name + `) - data := templates.RoleData{ - Role: role, - RigName: rigName, - TownRoot: m.townRoot, - TownName: townName, - WorkDir: workspacePath, - DefaultBranch: defaultBranch, - Polecat: workerName, // Used for crew member name as well - MayorSession: fmt.Sprintf("gt-%s-mayor", townName), - DeaconSession: fmt.Sprintf("gt-%s-deacon", townName), - } +> **Recovery**: Run ` + "`gt prime`" + ` after compaction, clear, or new session - content, err := tmpl.RenderRole(role, data) - if err != nil { - return err +Full context is injected by ` + "`gt prime`" + ` at session start. + +## Quick Reference + +- Check hook: ` + "`gt hook`" + ` +- Check mail: ` + "`gt mail inbox`" + ` +` + case "polecat": + name := workerName + if name == "" { + name = "worker" + } + bootstrap = `# Polecat Context (` + rigName + `/` + name + `) + +> **Recovery**: Run ` + "`gt prime`" + ` after compaction, clear, or new session + +Full context is injected by ` + "`gt prime`" + ` at session start. + +## Quick Reference + +- Check hook: ` + "`gt hook`" + ` +- Report done: ` + "`gt done`" + ` +` + default: + bootstrap = `# Agent Context + +> **Recovery**: Run ` + "`gt prime`" + ` after compaction, clear, or new session + +Full context is injected by ` + "`gt prime`" + ` at session start. +` } claudePath := filepath.Join(workspacePath, "CLAUDE.md") - return os.WriteFile(claudePath, []byte(content), 0644) + return os.WriteFile(claudePath, []byte(bootstrap), 0644) } // createPatrolHooks creates .claude/settings.json with hooks for patrol roles.