package cmd

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/spf13/cobra"
	"github.com/steveyegge/gastown/internal/beads"
	"github.com/steveyegge/gastown/internal/lock"
	"github.com/steveyegge/gastown/internal/style"
	"github.com/steveyegge/gastown/internal/templates"
	"github.com/steveyegge/gastown/internal/wisp"
	"github.com/steveyegge/gastown/internal/workspace"
)

// Role represents a detected agent role.
type Role string

const (
	RoleMayor    Role = "mayor"
	RoleDeacon   Role = "deacon"
	RoleWitness  Role = "witness"
	RoleRefinery Role = "refinery"
	RolePolecat  Role = "polecat"
	RoleCrew     Role = "crew"
	RoleUnknown  Role = "unknown"
)

var primeCmd = &cobra.Command{
	Use:     "prime",
	GroupID: GroupDiag,
	Short:   "Output role context for current directory",
	Long: `Detect the agent role from the current directory and output context.

Role detection:
  - Town root or mayor/rig/ → Mayor context
  - <rig>/witness/rig/ → Witness context
  - <rig>/refinery/rig/ → Refinery context
  - <rig>/polecats/<name>/ → Polecat context

This command is typically used in shell prompts or agent initialization.`,
	RunE: runPrime,
}

func init() {
	rootCmd.AddCommand(primeCmd)
}

// RoleContext is an alias for RoleInfo for backward compatibility.
// New code should use RoleInfo directly.
type RoleContext = RoleInfo

func runPrime(cmd *cobra.Command, args []string) error {
	cwd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("getting current directory: %w", err)
	}

	// Find town root
	townRoot, err := workspace.FindFromCwd()
	if err != nil {
		return fmt.Errorf("finding workspace: %w", err)
	}
	if townRoot == "" {
		return fmt.Errorf("not in a Gas Town workspace")
	}

	// Get role using env-aware detection
	roleInfo, err := GetRoleWithContext(cwd, townRoot)
	if err != nil {
		return fmt.Errorf("detecting role: %w", err)
	}

	// Warn prominently if there's a role/cwd mismatch
	if roleInfo.Mismatch {
		fmt.Printf("\n%s\n", style.Bold.Render("⚠️  ROLE/LOCATION MISMATCH"))
		fmt.Printf("You are %s (from $GT_ROLE) but your cwd suggests %s.\n",
			style.Bold.Render(string(roleInfo.Role)),
			style.Bold.Render(string(roleInfo.CwdRole)))
		fmt.Printf("Expected home: %s\n", roleInfo.Home)
		fmt.Printf("Actual cwd:    %s\n", cwd)
		fmt.Println()
		fmt.Println("This can cause commands to misbehave. Either:")
		fmt.Println("  1. cd to your home directory, OR")
		fmt.Println("  2. Use absolute paths for gt/bd commands")
		fmt.Println()
	}

	// Build RoleContext for compatibility with existing code
	ctx := RoleContext{
		Role:     roleInfo.Role,
		Rig:      roleInfo.Rig,
		Polecat:  roleInfo.Polecat,
		TownRoot: townRoot,
		WorkDir:  cwd,
	}

	// Check and acquire identity lock for worker roles
	if err := acquireIdentityLock(ctx); err != nil {
		return err
	}

	// Ensure beads redirect exists for worktree-based roles
	ensureBeadsRedirect(ctx)

	// Output context
	if err := outputPrimeContext(ctx); err != nil {
		return err
	}

	// Output handoff content if present
	outputHandoffContent(ctx)

	// Output attachment status (for autonomous work detection)
	outputAttachmentStatus(ctx)

	// Check for slung work on hook (from gt sling)
	// If found, we're in autonomous mode - skip normal startup directive
	hasSlungWork := checkSlungWork(ctx)

	// Output molecule context if working on a molecule step
	outputMoleculeContext(ctx)

	// Run bd prime to output beads workflow context
	runBdPrime(cwd)

	// Run gt mail check --inject to inject any pending mail
	runMailCheckInject(cwd)

	// Output startup directive for roles that should announce themselves
	// Skip if in autonomous mode (slung work provides its own directive)
	if !hasSlungWork {
		outputStartupDirective(ctx)
	}

	return nil
}

func detectRole(cwd, townRoot string) RoleInfo {
	ctx := RoleInfo{
		Role:     RoleUnknown,
		TownRoot: townRoot,
		WorkDir:  cwd,
		Source:   "cwd",
	}

	// Get relative path from town root
	relPath, err := filepath.Rel(townRoot, cwd)
	if err != nil {
		return ctx
	}

	// Normalize and split path
	relPath = filepath.ToSlash(relPath)
	parts := strings.Split(relPath, "/")

	// Check for mayor role
	// At town root, or in mayor/ or mayor/rig/
	if relPath == "." || relPath == "" {
		ctx.Role = RoleMayor
		return ctx
	}
	if len(parts) >= 1 && parts[0] == "mayor" {
		ctx.Role = RoleMayor
		return ctx
	}

	// Check for deacon role: deacon/
	if len(parts) >= 1 && parts[0] == "deacon" {
		ctx.Role = RoleDeacon
		return ctx
	}

	// At this point, first part should be a rig name
	if len(parts) < 1 {
		return ctx
	}
	rigName := parts[0]
	ctx.Rig = rigName

	// Check for witness: <rig>/witness/rig/
	if len(parts) >= 2 && parts[1] == "witness" {
		ctx.Role = RoleWitness
		return ctx
	}

	// Check for refinery: <rig>/refinery/rig/
	if len(parts) >= 2 && parts[1] == "refinery" {
		ctx.Role = RoleRefinery
		return ctx
	}

	// Check for polecat: <rig>/polecats/<name>/
	if len(parts) >= 3 && parts[1] == "polecats" {
		ctx.Role = RolePolecat
		ctx.Polecat = parts[2]
		return ctx
	}

	// Check for crew: <rig>/crew/<name>/
	if len(parts) >= 3 && parts[1] == "crew" {
		ctx.Role = RoleCrew
		ctx.Polecat = parts[2] // Use Polecat field for crew member name
		return ctx
	}

	// Default: could be rig root - treat as unknown
	return ctx
}

func outputPrimeContext(ctx RoleContext) error {
	// Try to use templates first
	tmpl, err := templates.New()
	if err != nil {
		// Fall back to hardcoded output if templates fail
		return outputPrimeContextFallback(ctx)
	}

	// Map role to template name
	var roleName string
	switch ctx.Role {
	case RoleMayor:
		roleName = "mayor"
	case RoleDeacon:
		roleName = "deacon"
	case RoleWitness:
		roleName = "witness"
	case RoleRefinery:
		roleName = "refinery"
	case RolePolecat:
		roleName = "polecat"
	case RoleCrew:
		roleName = "crew"
	default:
		// Unknown role - use fallback
		return outputPrimeContextFallback(ctx)
	}

	// Build template data
	data := templates.RoleData{
		Role:     roleName,
		RigName:  ctx.Rig,
		TownRoot: ctx.TownRoot,
		WorkDir:  ctx.WorkDir,
		Polecat:  ctx.Polecat,
	}

	// Render and output
	output, err := tmpl.RenderRole(roleName, data)
	if err != nil {
		return fmt.Errorf("rendering template: %w", err)
	}

	fmt.Print(output)
	return nil
}

func outputPrimeContextFallback(ctx RoleContext) error {
	switch ctx.Role {
	case RoleMayor:
		outputMayorContext(ctx)
	case RoleWitness:
		outputWitnessContext(ctx)
	case RoleRefinery:
		outputRefineryContext(ctx)
	case RolePolecat:
		outputPolecatContext(ctx)
	case RoleCrew:
		outputCrewContext(ctx)
	default:
		outputUnknownContext(ctx)
	}
	return nil
}

func outputMayorContext(ctx RoleContext) {
	fmt.Printf("%s\n\n", style.Bold.Render("# Mayor Context"))
	fmt.Println("You are the **Mayor** - the global coordinator of Gas Town.")
	fmt.Println()
	fmt.Println("## Responsibilities")
	fmt.Println("- Coordinate work across all rigs")
	fmt.Println("- Delegate to Refineries, not directly to polecats")
	fmt.Println("- Monitor overall system health")
	fmt.Println()
	fmt.Println("## Key Commands")
	fmt.Println("- `gt mail inbox` - Check your messages")
	fmt.Println("- `gt mail read <id>` - Read a specific message")
	fmt.Println("- `gt status` - Show overall town status")
	fmt.Println("- `gt rigs` - List all rigs")
	fmt.Println("- `bd ready` - Issues ready to work")
	fmt.Println()
	fmt.Println("## Startup")
	fmt.Println("Check for handoff messages with 🤝 HANDOFF in subject - continue predecessor's work.")
	fmt.Println()
	fmt.Printf("Town root: %s\n", style.Dim.Render(ctx.TownRoot))
}

func outputWitnessContext(ctx RoleContext) {
	fmt.Printf("%s\n\n", style.Bold.Render("# Witness Context"))
	fmt.Printf("You are the **Witness** for rig: %s\n\n", style.Bold.Render(ctx.Rig))
	fmt.Println("## Responsibilities")
	fmt.Println("- Monitor polecat health via heartbeat")
	fmt.Println("- Spawn replacement agents for stuck polecats")
	fmt.Println("- Report rig status to Mayor")
	fmt.Println()
	fmt.Println("## Key Commands")
	fmt.Println("- `gt witness status` - Show witness status")
	fmt.Println("- `gt polecats` - List polecats in this rig")
	fmt.Println()
	fmt.Printf("Rig: %s\n", style.Dim.Render(ctx.Rig))
}

func outputRefineryContext(ctx RoleContext) {
	fmt.Printf("%s\n\n", style.Bold.Render("# Refinery Context"))
	fmt.Printf("You are the **Refinery** for rig: %s\n\n", style.Bold.Render(ctx.Rig))
	fmt.Println("## Responsibilities")
	fmt.Println("- Process the merge queue for this rig")
	fmt.Println("- Merge polecat work to integration branch")
	fmt.Println("- Resolve merge conflicts")
	fmt.Println("- Land completed swarms to main")
	fmt.Println()
	fmt.Println("## Key Commands")
	fmt.Println("- `gt merge queue` - Show pending merges")
	fmt.Println("- `gt merge next` - Process next merge")
	fmt.Println()
	fmt.Printf("Rig: %s\n", style.Dim.Render(ctx.Rig))
}

func outputPolecatContext(ctx RoleContext) {
	fmt.Printf("%s\n\n", style.Bold.Render("# Polecat Context"))
	fmt.Printf("You are polecat **%s** in rig: %s\n\n",
		style.Bold.Render(ctx.Polecat), style.Bold.Render(ctx.Rig))
	fmt.Println("## Startup Protocol")
	fmt.Println("1. Run `gt prime` - loads context and checks mail automatically")
	fmt.Println("2. Check inbox - if mail shown, read with `gt mail read <id>`")
	fmt.Println("3. Look for '📋 Work Assignment' messages for your task")
	fmt.Println("4. If no mail, check `bd list --status=in_progress` for existing work")
	fmt.Println()
	fmt.Println("## Key Commands")
	fmt.Println("- `gt mail inbox` - Check your inbox for work assignments")
	fmt.Println("- `bd show <issue>` - View your assigned issue")
	fmt.Println("- `bd close <issue>` - Mark issue complete")
	fmt.Println("- `gt done` - Signal work ready for merge")
	fmt.Println()
	fmt.Printf("Polecat: %s | Rig: %s\n",
		style.Dim.Render(ctx.Polecat), style.Dim.Render(ctx.Rig))
}

func outputCrewContext(ctx RoleContext) {
	fmt.Printf("%s\n\n", style.Bold.Render("# Crew Worker Context"))
	fmt.Printf("You are crew worker **%s** in rig: %s\n\n",
		style.Bold.Render(ctx.Polecat), style.Bold.Render(ctx.Rig))
	fmt.Println("## About Crew Workers")
	fmt.Println("- Persistent workspace (not auto-garbage-collected)")
	fmt.Println("- User-managed (not Witness-monitored)")
	fmt.Println("- Long-lived identity across sessions")
	fmt.Println()
	fmt.Println("## Key Commands")
	fmt.Println("- `gt mail inbox` - Check your inbox")
	fmt.Println("- `bd ready` - Available issues")
	fmt.Println("- `bd show <issue>` - View issue details")
	fmt.Println("- `bd close <issue>` - Mark issue complete")
	fmt.Println()
	fmt.Printf("Crew: %s | Rig: %s\n",
		style.Dim.Render(ctx.Polecat), style.Dim.Render(ctx.Rig))
}

func outputUnknownContext(ctx RoleContext) {
	fmt.Printf("%s\n\n", style.Bold.Render("# Gas Town Context"))
	fmt.Println("Could not determine specific role from current directory.")
	fmt.Println()
	if ctx.Rig != "" {
		fmt.Printf("You appear to be in rig: %s\n\n", style.Bold.Render(ctx.Rig))
	}
	fmt.Println("Navigate to a specific agent directory:")
	fmt.Println("- `<rig>/polecats/<name>/` - Polecat role")
	fmt.Println("- `<rig>/witness/rig/` - Witness role")
	fmt.Println("- `<rig>/refinery/rig/` - Refinery role")
	fmt.Println("- Town root or `mayor/` - Mayor role")
	fmt.Println()
	fmt.Printf("Town root: %s\n", style.Dim.Render(ctx.TownRoot))
}

// outputHandoffContent reads and displays the pinned handoff bead for the role.
func outputHandoffContent(ctx RoleContext) {
	if ctx.Role == RoleUnknown {
		return
	}

	// Get role key for handoff bead lookup
	roleKey := string(ctx.Role)

	bd := beads.New(ctx.TownRoot)
	issue, err := bd.FindHandoffBead(roleKey)
	if err != nil {
		// Silently skip if beads lookup fails (might not be a beads repo)
		return
	}
	if issue == nil || issue.Description == "" {
		// No handoff content
		return
	}

	// Display handoff content
	fmt.Println()
	fmt.Printf("%s\n\n", style.Bold.Render("## 🤝 Handoff from Previous Session"))
	fmt.Println(issue.Description)
	fmt.Println()
	fmt.Println(style.Dim.Render("(Clear with: gt rig reset --handoff)"))
}

// runBdPrime runs `bd prime` and outputs the result.
// This provides beads workflow context to the agent.
func runBdPrime(workDir string) {
	cmd := exec.Command("bd", "prime")
	cmd.Dir = workDir

	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = nil // Ignore stderr

	if err := cmd.Run(); err != nil {
		// Silently skip if bd prime fails (beads might not be available)
		return
	}

	output := strings.TrimSpace(stdout.String())
	if output != "" {
		fmt.Println()
		fmt.Println(output)
	}
}

// outputStartupDirective outputs role-specific instructions for the agent.
// This tells agents like Mayor to announce themselves on startup.
func outputStartupDirective(ctx RoleContext) {
	switch ctx.Role {
	case RoleMayor:
		fmt.Println()
		fmt.Println("---")
		fmt.Println()
		fmt.Println("**STARTUP PROTOCOL**: You are the Mayor. Please:")
		fmt.Println("1. Announce: \"Mayor, checking in.\"")
		fmt.Println("2. Check mail: `gt mail inbox` - look for 🤝 HANDOFF messages")
		fmt.Println("3. Check for attached work: `gt mol status`")
		fmt.Println("   - If mol attached → **RUN IT** (no human input needed)")
		fmt.Println("   - If no mol → await user instruction")
	case RoleWitness:
		fmt.Println()
		fmt.Println("---")
		fmt.Println()
		fmt.Println("**STARTUP PROTOCOL**: You are the Witness. Please:")
		fmt.Println("1. Announce: \"Witness, checking in.\"")
		fmt.Println("2. Check mail: `gt mail inbox` - look for 🤝 HANDOFF messages")
		fmt.Println("3. Check for attached patrol: `gt mol status`")
		fmt.Println("   - If mol attached → **RUN IT** (resume from current step)")
		fmt.Println("   - If no mol → create patrol: `bd wisp mol-witness-patrol`")
	case RolePolecat:
		fmt.Println()
		fmt.Println("---")
		fmt.Println()
		fmt.Println("**STARTUP PROTOCOL**: You are a polecat. Please:")
		fmt.Printf("1. Announce: \"%s Polecat %s, checking in.\"\n", ctx.Rig, ctx.Polecat)
		fmt.Println("2. Check mail: `gt mail inbox`")
		fmt.Println("3. If there's a 🤝 HANDOFF message, read it for context")
		fmt.Println("4. Check for attached work: `gt mol status`")
		fmt.Println("   - If mol attached → **RUN IT** (you were spawned with this work)")
		fmt.Println("   - If no mol → ERROR: polecats must have work attached; escalate to Witness")
	case RoleRefinery:
		fmt.Println()
		fmt.Println("---")
		fmt.Println()
		fmt.Println("**STARTUP PROTOCOL**: You are the Refinery. Please:")
		fmt.Println("1. Announce: \"Refinery, checking in.\"")
		fmt.Println("2. Check mail: `gt mail inbox` - look for 🤝 HANDOFF messages")
		fmt.Println("3. Check for attached patrol: `gt mol status`")
		fmt.Println("   - If mol attached → **RUN IT** (resume from current step)")
		fmt.Println("   - If no mol → create patrol: `bd wisp mol-refinery-patrol`")
	case RoleCrew:
		fmt.Println()
		fmt.Println("---")
		fmt.Println()
		fmt.Println("**STARTUP PROTOCOL**: You are a crew worker. Please:")
		fmt.Printf("1. Announce: \"%s Crew %s, checking in.\"\n", ctx.Rig, ctx.Polecat)
		fmt.Println("2. Check mail: `gt mail inbox`")
		fmt.Println("3. If there's a 🤝 HANDOFF message, read it and continue the work")
		fmt.Println("4. Check for attached work: `gt mol status`")
		fmt.Println("   - If attachment found → **RUN IT** (no human input needed)")
		fmt.Println("   - If no attachment → await user instruction")
	case RoleDeacon:
		fmt.Println()
		fmt.Println("---")
		fmt.Println()
		fmt.Println("**STARTUP PROTOCOL**: You are the Deacon. Please:")
		fmt.Println("1. Announce: \"Deacon, checking in.\"")
		fmt.Println("2. Signal awake: `gt deacon heartbeat \"starting patrol\"`")
		fmt.Println("3. Check mail: `gt mail inbox` - look for 🤝 HANDOFF messages")
		fmt.Println("4. Check for attached patrol: `gt mol status`")
		fmt.Println("   - If mol attached → **RUN IT** (resume from current step)")
		fmt.Println("   - If no mol → create patrol: `bd wisp mol-deacon-patrol`")
	}
}

// runMailCheckInject runs `gt mail check --inject` and outputs the result.
// This injects any pending mail into the agent's context.
func runMailCheckInject(workDir string) {
	cmd := exec.Command("gt", "mail", "check", "--inject")
	cmd.Dir = workDir

	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = nil // Ignore stderr

	if err := cmd.Run(); err != nil {
		// Silently skip if mail check fails
		return
	}

	output := strings.TrimSpace(stdout.String())
	if output != "" {
		fmt.Println()
		fmt.Println(output)
	}
}

// outputAttachmentStatus checks for attached work molecule and outputs status.
// This is key for the autonomous overnight work pattern.
// The Propulsion Principle: "If you find something on your hook, YOU RUN IT."
func outputAttachmentStatus(ctx RoleContext) {
	if ctx.Role != RoleCrew && ctx.Role != RolePolecat {
		return
	}

	// Check for pinned beads with attachments
	b := beads.New(ctx.WorkDir)

	// Build assignee string based on role
	var assignee string
	switch ctx.Role {
	case RoleCrew:
		assignee = fmt.Sprintf("%s/crew/%s", ctx.Rig, ctx.Polecat)
	case RolePolecat:
		assignee = fmt.Sprintf("%s/%s", ctx.Rig, ctx.Polecat)
	}

	// Find pinned beads for this agent
	pinnedBeads, err := b.List(beads.ListOptions{
		Status:   beads.StatusPinned,
		Assignee: assignee,
		Priority: -1,
	})
	if err != nil || len(pinnedBeads) == 0 {
		// No pinned beads - interactive mode
		return
	}

	// Check first pinned bead for attachment
	attachment := beads.ParseAttachmentFields(pinnedBeads[0])
	if attachment == nil || attachment.AttachedMolecule == "" {
		// No attachment - interactive mode
		return
	}

	// Has attached work - output prominently with current step
	fmt.Println()
	fmt.Printf("%s\n\n", style.Bold.Render("## 🎯 ATTACHED WORK DETECTED"))
	fmt.Printf("Pinned bead: %s\n", pinnedBeads[0].ID)
	fmt.Printf("Attached molecule: %s\n", attachment.AttachedMolecule)
	if attachment.AttachedAt != "" {
		fmt.Printf("Attached at: %s\n", attachment.AttachedAt)
	}
	fmt.Println()

	// Show current step from molecule
	showMoleculeExecutionPrompt(ctx.WorkDir, attachment.AttachedMolecule)
}

// MoleculeCurrentOutput represents the JSON output of bd mol current.
type MoleculeCurrentOutput struct {
	MoleculeID    string `json:"molecule_id"`
	MoleculeTitle string `json:"molecule_title"`
	NextStep      *struct {
		ID          string `json:"id"`
		Title       string `json:"title"`
		Description string `json:"description"`
		Status      string `json:"status"`
	} `json:"next_step"`
	Completed int `json:"completed"`
	Total     int `json:"total"`
}

// showMoleculeExecutionPrompt calls bd mol current and shows the current step
// with execution instructions. This is the core of the Propulsion Principle.
func showMoleculeExecutionPrompt(workDir, moleculeID string) {
	// Call bd mol current with JSON output
	cmd := exec.Command("bd", "--no-daemon", "mol", "current", moleculeID, "--json")
	cmd.Dir = workDir
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	if err := cmd.Run(); err != nil {
		// Fall back to simple message if bd mol current fails
		fmt.Println(style.Bold.Render("→ PROPULSION PRINCIPLE: Work is on your hook. RUN IT."))
		fmt.Println("  Begin working on this molecule immediately.")
		fmt.Printf("  Check status with: bd mol current %s\n", moleculeID)
		return
	}

	// Parse JSON output - it's an array with one element
	var outputs []MoleculeCurrentOutput
	if err := json.Unmarshal(stdout.Bytes(), &outputs); err != nil || len(outputs) == 0 {
		// Fall back to simple message
		fmt.Println(style.Bold.Render("→ PROPULSION PRINCIPLE: Work is on your hook. RUN IT."))
		fmt.Println("  Begin working on this molecule immediately.")
		return
	}
	output := outputs[0]

	// Show molecule progress
	fmt.Printf("**Progress:** %d/%d steps complete\n\n",
		output.Completed, output.Total)

	// Show current step if available
	if output.NextStep != nil {
		step := output.NextStep
		fmt.Printf("%s\n\n", style.Bold.Render("## 🎬 CURRENT STEP: "+step.Title))
		fmt.Printf("**Step ID:** %s\n", step.ID)
		fmt.Printf("**Status:** %s (ready to execute)\n\n", step.Status)

		// Show step description if available
		if step.Description != "" {
			fmt.Println("### Instructions")
			fmt.Println()
			// Indent the description for readability
			lines := strings.Split(step.Description, "\n")
			for _, line := range lines {
				fmt.Printf("%s\n", line)
			}
			fmt.Println()
		}

		// The propulsion directive
		fmt.Println(style.Bold.Render("→ EXECUTE THIS STEP NOW."))
		fmt.Println()
		fmt.Println("When complete:")
		fmt.Printf("  1. Close the step: bd close %s\n", step.ID)
		fmt.Println("  2. Check for next step: bd ready")
		fmt.Println("  3. Continue until molecule complete")
	} else {
		// No next step - molecule may be complete
		fmt.Println(style.Bold.Render("✓ MOLECULE COMPLETE"))
		fmt.Println()
		fmt.Println("All steps are done. You may:")
		fmt.Println("  - Report completion to supervisor")
		fmt.Println("  - Check for new work: bd ready")
	}
}

// outputMoleculeContext checks if the agent is working on a molecule step and shows progress.
func outputMoleculeContext(ctx RoleContext) {
	// Applies to polecats, crew workers, deacon, witness, and refinery
	if ctx.Role != RolePolecat && ctx.Role != RoleCrew && ctx.Role != RoleDeacon && ctx.Role != RoleWitness && ctx.Role != RoleRefinery {
		return
	}

	// For Deacon, use special patrol molecule handling
	if ctx.Role == RoleDeacon {
		outputDeaconPatrolContext(ctx)
		return
	}

	// For Witness, use special patrol molecule handling (auto-bonds on startup)
	if ctx.Role == RoleWitness {
		outputWitnessPatrolContext(ctx)
		return
	}

	// For Refinery, use special patrol molecule handling (auto-bonds on startup)
	if ctx.Role == RoleRefinery {
		outputRefineryPatrolContext(ctx)
		return
	}

	// Check for in-progress issues
	b := beads.New(ctx.WorkDir)
	issues, err := b.List(beads.ListOptions{
		Status:   "in_progress",
		Assignee: ctx.Polecat,
		Priority: -1,
	})
	if err != nil || len(issues) == 0 {
		return
	}

	// Check if any in-progress issue is a molecule step
	for _, issue := range issues {
		moleculeID := parseMoleculeMetadata(issue.Description)
		if moleculeID == "" {
			continue
		}

		// Get the parent (root) issue ID
		rootID := issue.Parent
		if rootID == "" {
			continue
		}

		// This is a molecule step - show context
		fmt.Println()
		fmt.Printf("%s\n\n", style.Bold.Render("## 🧬 Molecule Workflow"))
		fmt.Printf("You are working on a molecule step.\n")
		fmt.Printf("  Current step: %s\n", issue.ID)
		fmt.Printf("  Molecule: %s\n", moleculeID)
		fmt.Printf("  Root issue: %s\n\n", rootID)

		// Show molecule progress by finding sibling steps
		showMoleculeProgress(b, rootID)

		fmt.Println()
		fmt.Println("**Molecule Work Loop:**")
		fmt.Println("1. Complete current step, then `bd close " + issue.ID + "`")
		fmt.Println("2. Check for next steps: `bd ready --parent " + rootID + "`")
		fmt.Println("3. Work on next ready step(s)")
		fmt.Println("4. When all steps done, run `gt done`")
		break // Only show context for first molecule step found
	}
}

// parseMoleculeMetadata extracts molecule info from a step's description.
// Looks for lines like:
//
//	instantiated_from: mol-xyz
func parseMoleculeMetadata(description string) string {
	lines := strings.Split(description, "\n")
	for _, line := range lines {
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "instantiated_from:") {
			return strings.TrimSpace(strings.TrimPrefix(line, "instantiated_from:"))
		}
	}
	return ""
}

// showMoleculeProgress displays the progress through a molecule's steps.
func showMoleculeProgress(b *beads.Beads, rootID string) {
	if rootID == "" {
		return
	}

	// Find all children of the root issue
	children, err := b.List(beads.ListOptions{
		Parent:   rootID,
		Status:   "all",
		Priority: -1,
	})
	if err != nil || len(children) == 0 {
		return
	}

	total := len(children)
	done := 0
	inProgress := 0
	var readySteps []string

	for _, child := range children {
		switch child.Status {
		case "closed":
			done++
		case "in_progress":
			inProgress++
		case "open":
			// Check if ready (no open dependencies)
			if len(child.DependsOn) == 0 {
				readySteps = append(readySteps, child.ID)
			}
		}
	}

	fmt.Printf("Progress: %d/%d steps complete", done, total)
	if inProgress > 0 {
		fmt.Printf(" (%d in progress)", inProgress)
	}
	fmt.Println()

	if len(readySteps) > 0 {
		fmt.Printf("Ready steps: %s\n", strings.Join(readySteps, ", "))
	}
}

// outputDeaconPatrolContext shows patrol molecule status for the Deacon.
// Deacon uses wisps (Wisp:true issues in main .beads/) for patrol cycles.
// bd wisp creates wisp-marked issues that are auto-deleted on squash.
func outputDeaconPatrolContext(ctx RoleContext) {
	fmt.Println()
	fmt.Printf("%s\n\n", style.Bold.Render("## 🔄 Patrol Status (Wisp-based)"))

	// Check for active mol-deacon-patrol molecules in rig beads
	// A patrol is "active" if it has open wisp children (steps to execute)
	// After squash, the root stays open but has no open children - that's "completed"
	rigBeadsDir := filepath.Join(ctx.TownRoot, "gastown", "mayor", "rig")

	// First find mol-deacon-patrol molecules (exclude template)
	cmdList := exec.Command("bd", "list", "--status=open", "--type=epic")
	cmdList.Dir = rigBeadsDir
	var stdoutList bytes.Buffer
	cmdList.Stdout = &stdoutList
	cmdList.Stderr = nil
	errList := cmdList.Run()

	// Find a patrol molecule with open children
	hasPatrol := false
	var patrolLine string
	var patrolID string
	if errList == nil {
		lines := strings.Split(stdoutList.String(), "\n")
		for _, line := range lines {
			if strings.Contains(line, "mol-deacon-patrol") && !strings.Contains(line, "[template]") {
				// Extract the ID (first word)
				parts := strings.Fields(line)
				if len(parts) > 0 {
					molID := parts[0]
					// Check if this molecule has open children using bd show
					cmdShow := exec.Command("bd", "show", molID)
					cmdShow.Dir = rigBeadsDir
					var stdoutShow bytes.Buffer
					cmdShow.Stdout = &stdoutShow
					cmdShow.Stderr = nil
					if cmdShow.Run() == nil {
						showOutput := stdoutShow.String()
						// Check for "- open]" in children section (open child steps)
						if strings.Contains(showOutput, "- open]") {
							hasPatrol = true
							patrolLine = line
							patrolID = molID
							break
						}
					}
				}
			}
		}
	}

	if !hasPatrol {
		// No active patrol - AUTO-SPAWN one
		fmt.Println("Status: **No active patrol** - creating mol-deacon-patrol...")
		fmt.Println()

		// Find the proto ID for mol-deacon-patrol
		cmdCatalog := exec.Command("bd", "--no-daemon", "mol", "catalog")
		cmdCatalog.Dir = rigBeadsDir
		var stdoutCatalog bytes.Buffer
		cmdCatalog.Stdout = &stdoutCatalog
		cmdCatalog.Stderr = nil

		if cmdCatalog.Run() != nil {
			fmt.Println(style.Dim.Render("Failed to list molecule catalog. Run `bd mol catalog` to troubleshoot."))
			return
		}

		// Find mol-deacon-patrol in catalog
		var protoID string
		catalogLines := strings.Split(stdoutCatalog.String(), "\n")
		for _, line := range catalogLines {
			if strings.Contains(line, "mol-deacon-patrol") {
				parts := strings.Fields(line)
				if len(parts) > 0 {
					// Strip trailing colon from ID (catalog format: "gt-xxx: title")
					protoID = strings.TrimSuffix(parts[0], ":")
					break
				}
			}
		}

		if protoID == "" {
			fmt.Println(style.Dim.Render("Proto mol-deacon-patrol not found in catalog. Run `bd mol register` first."))
			return
		}

		// Create the patrol wisp
		cmdSpawn := exec.Command("bd", "--no-daemon", "wisp", protoID, "--assignee", "deacon")
		cmdSpawn.Dir = rigBeadsDir
		var stdoutSpawn, stderrSpawn bytes.Buffer
		cmdSpawn.Stdout = &stdoutSpawn
		cmdSpawn.Stderr = &stderrSpawn

		if err := cmdSpawn.Run(); err != nil {
			fmt.Printf("Failed to create patrol wisp: %s\n", stderrSpawn.String())
			fmt.Println(style.Dim.Render("Run manually: bd --no-daemon wisp" + protoID))
			return
		}

		// Parse the created molecule ID from output
		spawnOutput := stdoutSpawn.String()
		fmt.Printf("✓ Created patrol wisp\n")

		// Extract molecule ID from output (format: "Created molecule: gt-xxxx")
		for _, line := range strings.Split(spawnOutput, "\n") {
			if strings.Contains(line, "molecule:") || strings.Contains(line, "Created") {
				parts := strings.Fields(line)
				for _, p := range parts {
					if strings.HasPrefix(p, "gt-") {
						patrolID = p
						break
					}
				}
			}
		}

		if patrolID != "" {
			fmt.Printf("Patrol ID: %s\n\n", patrolID)
		}
	} else {
		// Has active patrol - show status
		fmt.Println("Status: **Patrol Active**")
		fmt.Printf("Patrol: %s\n\n", strings.TrimSpace(patrolLine))
	}

	// Show patrol work loop instructions
	fmt.Println("**Deacon Patrol Work Loop:**")
	fmt.Println("Run from gastown/mayor/rig/:")
	fmt.Println("1. Check next step: `bd ready`")
	fmt.Println("2. Execute the step (heartbeat, mail, health checks, etc.)")
	fmt.Println("3. Close step: `bd close <step-id>`")
	fmt.Println("4. Check next: `bd ready`")
	fmt.Println("5. At cycle end (loop-or-exit step):")
	fmt.Println("   - Generate summary of patrol cycle")
	fmt.Println("   - Squash: `bd --no-daemon mol squash <mol-id> --summary \"<summary>\"`")
	fmt.Println("   - Loop back to create new wisp, or exit if context high")
	if patrolID != "" {
		fmt.Println()
		fmt.Printf("Current patrol ID: %s\n", patrolID)
	}
}

// outputWitnessPatrolContext shows patrol molecule status for the Witness.
// Witness AUTO-BONDS its patrol molecule on startup if one isn't already running.
// This ensures polecat health is always monitored.
func outputWitnessPatrolContext(ctx RoleContext) {
	fmt.Println()
	fmt.Printf("%s\n\n", style.Bold.Render("## 👁 Witness Patrol Status"))

	// Witness works from its own rig clone: <rig>/witness/rig/
	// Beads are in the current WorkDir
	witnessBeadsDir := ctx.WorkDir

	// Find mol-witness-patrol molecules (exclude template)
	// Look for in-progress patrol first (resumable)
	cmdList := exec.Command("bd", "--no-daemon", "list", "--status=in_progress", "--type=epic")
	cmdList.Dir = witnessBeadsDir
	var stdoutList bytes.Buffer
	cmdList.Stdout = &stdoutList
	cmdList.Stderr = nil
	errList := cmdList.Run()

	hasPatrol := false
	var patrolID string
	var patrolLine string

	if errList == nil {
		lines := strings.Split(stdoutList.String(), "\n")
		for _, line := range lines {
			if strings.Contains(line, "mol-witness-patrol") && !strings.Contains(line, "[template]") {
				parts := strings.Fields(line)
				if len(parts) > 0 {
					patrolID = parts[0]
					patrolLine = line
					hasPatrol = true
					break
				}
			}
		}
	}

	// Also check for open patrols with open children (active wisp)
	if !hasPatrol {
		cmdOpen := exec.Command("bd", "--no-daemon", "list", "--status=open", "--type=epic")
		cmdOpen.Dir = witnessBeadsDir
		var stdoutOpen bytes.Buffer
		cmdOpen.Stdout = &stdoutOpen
		cmdOpen.Stderr = nil
		if cmdOpen.Run() == nil {
			lines := strings.Split(stdoutOpen.String(), "\n")
			for _, line := range lines {
				if strings.Contains(line, "mol-witness-patrol") && !strings.Contains(line, "[template]") {
					parts := strings.Fields(line)
					if len(parts) > 0 {
						molID := parts[0]
						// Check if this molecule has open children
						cmdShow := exec.Command("bd", "--no-daemon", "show", molID)
						cmdShow.Dir = witnessBeadsDir
						var stdoutShow bytes.Buffer
						cmdShow.Stdout = &stdoutShow
						cmdShow.Stderr = nil
						if cmdShow.Run() == nil {
							showOutput := stdoutShow.String()
							if strings.Contains(showOutput, "- open]") || strings.Contains(showOutput, "- in_progress]") {
								hasPatrol = true
								patrolID = molID
								patrolLine = line
								break
							}
						}
					}
				}
			}
		}
	}

	if !hasPatrol {
		// No active patrol - AUTO-SPAWN one
		fmt.Println("Status: **No active patrol** - creating mol-witness-patrol...")
		fmt.Println()

		// Find the proto ID for mol-witness-patrol
		cmdCatalog := exec.Command("bd", "--no-daemon", "mol", "catalog")
		cmdCatalog.Dir = witnessBeadsDir
		var stdoutCatalog bytes.Buffer
		cmdCatalog.Stdout = &stdoutCatalog
		cmdCatalog.Stderr = nil

		if cmdCatalog.Run() != nil {
			fmt.Println(style.Dim.Render("Failed to list molecule catalog. Run `bd mol catalog` to troubleshoot."))
			return
		}

		// Find mol-witness-patrol in catalog
		var protoID string
		catalogLines := strings.Split(stdoutCatalog.String(), "\n")
		for _, line := range catalogLines {
			if strings.Contains(line, "mol-witness-patrol") {
				parts := strings.Fields(line)
				if len(parts) > 0 {
					// Strip trailing colon from ID (catalog format: "gt-xxx: title")
					protoID = strings.TrimSuffix(parts[0], ":")
					break
				}
			}
		}

		if protoID == "" {
			fmt.Println(style.Dim.Render("Proto mol-witness-patrol not found in catalog. Run `bd mol register` first."))
			return
		}

		// Create the patrol wisp
		cmdSpawn := exec.Command("bd", "--no-daemon", "wisp", protoID, "--assignee", ctx.Rig+"/witness")
		cmdSpawn.Dir = witnessBeadsDir
		var stdoutSpawn, stderrSpawn bytes.Buffer
		cmdSpawn.Stdout = &stdoutSpawn
		cmdSpawn.Stderr = &stderrSpawn

		if err := cmdSpawn.Run(); err != nil {
			fmt.Printf("Failed to create patrol wisp: %s\n", stderrSpawn.String())
			fmt.Println(style.Dim.Render("Run manually: bd --no-daemon wisp" + protoID))
			return
		}

		// Parse the created molecule ID from output
		spawnOutput := stdoutSpawn.String()
		fmt.Printf("✓ Created patrol wisp\n")

		// Extract molecule ID from output (format: "Created molecule: gt-xxxx")
		for _, line := range strings.Split(spawnOutput, "\n") {
			if strings.Contains(line, "molecule:") || strings.Contains(line, "Created") {
				parts := strings.Fields(line)
				for _, p := range parts {
					if strings.HasPrefix(p, "gt-") {
						patrolID = p
						break
					}
				}
			}
		}

		if patrolID != "" {
			fmt.Printf("Patrol ID: %s\n\n", patrolID)
		}
	} else {
		// Has active patrol - show status
		fmt.Println("Status: **Patrol Active**")
		fmt.Printf("Patrol: %s\n\n", strings.TrimSpace(patrolLine))
	}

	// Show patrol work loop instructions
	fmt.Println("**Witness Patrol Work Loop:**")
	fmt.Println("1. Check inbox: `gt mail inbox`")
	fmt.Println("2. Check next step: `bd ready`")
	fmt.Println("3. Execute the step (survey polecats, inspect, nudge, etc.)")
	fmt.Println("4. Close step: `bd close <step-id>`")
	fmt.Println("5. Check next: `bd ready`")
	fmt.Println("6. At cycle end (burn-or-loop step):")
	fmt.Println("   - Generate summary of patrol cycle")
	fmt.Println("   - Squash: `bd --no-daemon mol squash <mol-id> --summary \"<summary>\"`")
	fmt.Println("   - Loop back to create new wisp, or exit if context high")
	if patrolID != "" {
		fmt.Println()
		fmt.Printf("Current patrol ID: %s\n", patrolID)
	}
}

// outputRefineryPatrolContext shows patrol molecule status for the Refinery.
// Unlike other patrol roles, Refinery AUTO-BONDS its patrol molecule on startup
// if one isn't already running. This ensures the merge queue is always monitored.
func outputRefineryPatrolContext(ctx RoleContext) {
	fmt.Println()
	fmt.Printf("%s\n\n", style.Bold.Render("## 🔧 Refinery Patrol Status"))

	// Refinery works from its own rig clone: <rig>/refinery/rig/
	// Beads are in the current WorkDir
	refineryBeadsDir := ctx.WorkDir

	// Find mol-refinery-patrol molecules (exclude template)
	// Look for in-progress patrol first (resumable)
	cmdList := exec.Command("bd", "--no-daemon", "list", "--status=in_progress", "--type=epic")
	cmdList.Dir = refineryBeadsDir
	var stdoutList bytes.Buffer
	cmdList.Stdout = &stdoutList
	cmdList.Stderr = nil
	errList := cmdList.Run()

	hasPatrol := false
	var patrolID string
	var patrolLine string

	if errList == nil {
		lines := strings.Split(stdoutList.String(), "\n")
		for _, line := range lines {
			if strings.Contains(line, "mol-refinery-patrol") && !strings.Contains(line, "[template]") {
				parts := strings.Fields(line)
				if len(parts) > 0 {
					patrolID = parts[0]
					patrolLine = line
					hasPatrol = true
					break
				}
			}
		}
	}

	// Also check for open patrols with open children (active wisp)
	if !hasPatrol {
		cmdOpen := exec.Command("bd", "--no-daemon", "list", "--status=open", "--type=epic")
		cmdOpen.Dir = refineryBeadsDir
		var stdoutOpen bytes.Buffer
		cmdOpen.Stdout = &stdoutOpen
		cmdOpen.Stderr = nil
		if cmdOpen.Run() == nil {
			lines := strings.Split(stdoutOpen.String(), "\n")
			for _, line := range lines {
				if strings.Contains(line, "mol-refinery-patrol") && !strings.Contains(line, "[template]") {
					parts := strings.Fields(line)
					if len(parts) > 0 {
						molID := parts[0]
						// Check if this molecule has open children
						cmdShow := exec.Command("bd", "--no-daemon", "show", molID)
						cmdShow.Dir = refineryBeadsDir
						var stdoutShow bytes.Buffer
						cmdShow.Stdout = &stdoutShow
						cmdShow.Stderr = nil
						if cmdShow.Run() == nil {
							showOutput := stdoutShow.String()
							if strings.Contains(showOutput, "- open]") || strings.Contains(showOutput, "- in_progress]") {
								hasPatrol = true
								patrolID = molID
								patrolLine = line
								break
							}
						}
					}
				}
			}
		}
	}

	if !hasPatrol {
		// No active patrol - AUTO-SPAWN one
		fmt.Println("Status: **No active patrol** - creating mol-refinery-patrol...")
		fmt.Println()

		// Find the proto ID for mol-refinery-patrol
		cmdCatalog := exec.Command("bd", "--no-daemon", "mol", "catalog")
		cmdCatalog.Dir = refineryBeadsDir
		var stdoutCatalog bytes.Buffer
		cmdCatalog.Stdout = &stdoutCatalog
		cmdCatalog.Stderr = nil

		if cmdCatalog.Run() != nil {
			fmt.Println(style.Dim.Render("Failed to list molecule catalog. Run `bd mol catalog` to troubleshoot."))
			return
		}

		// Find mol-refinery-patrol in catalog
		var protoID string
		catalogLines := strings.Split(stdoutCatalog.String(), "\n")
		for _, line := range catalogLines {
			if strings.Contains(line, "mol-refinery-patrol") {
				parts := strings.Fields(line)
				if len(parts) > 0 {
					// Strip trailing colon from ID (catalog format: "gt-xxx: title")
					protoID = strings.TrimSuffix(parts[0], ":")
					break
				}
			}
		}

		if protoID == "" {
			fmt.Println(style.Dim.Render("Proto mol-refinery-patrol not found in catalog. Run `bd mol register` first."))
			return
		}

		// Create the patrol wisp
		cmdSpawn := exec.Command("bd", "--no-daemon", "wisp", protoID, "--assignee", ctx.Rig+"/refinery")
		cmdSpawn.Dir = refineryBeadsDir
		var stdoutSpawn, stderrSpawn bytes.Buffer
		cmdSpawn.Stdout = &stdoutSpawn
		cmdSpawn.Stderr = &stderrSpawn

		if err := cmdSpawn.Run(); err != nil {
			fmt.Printf("Failed to create patrol wisp: %s\n", stderrSpawn.String())
			fmt.Println(style.Dim.Render("Run manually: bd --no-daemon wisp" + protoID))
			return
		}

		// Parse the created molecule ID from output
		spawnOutput := stdoutSpawn.String()
		fmt.Printf("✓ Created patrol wisp\n")

		// Extract molecule ID from output (format: "Created molecule: gt-xxxx")
		for _, line := range strings.Split(spawnOutput, "\n") {
			if strings.Contains(line, "molecule:") || strings.Contains(line, "Created") {
				parts := strings.Fields(line)
				for _, p := range parts {
					if strings.HasPrefix(p, "gt-") {
						patrolID = p
						break
					}
				}
			}
		}

		if patrolID != "" {
			fmt.Printf("Patrol ID: %s\n\n", patrolID)
		}
	} else {
		// Has active patrol - show status
		fmt.Println("Status: **Patrol Active**")
		fmt.Printf("Patrol: %s\n\n", strings.TrimSpace(patrolLine))
	}

	// Show patrol work loop instructions
	fmt.Println("**Refinery Patrol Work Loop:**")
	fmt.Println("1. Check inbox: `gt mail inbox`")
	fmt.Println("2. Check next step: `bd ready`")
	fmt.Println("3. Execute the step (queue scan, process branch, tests, merge)")
	fmt.Println("4. Close step: `bd close <step-id>`")
	fmt.Println("5. Check next: `bd ready`")
	fmt.Println("6. At cycle end (burn-or-loop step):")
	fmt.Println("   - Generate summary of patrol cycle")
	fmt.Println("   - Squash: `bd --no-daemon mol squash <mol-id> --summary \"<summary>\"`")
	fmt.Println("   - Loop back to create new wisp, or exit if context high")
	if patrolID != "" {
		fmt.Println()
		fmt.Printf("Current patrol ID: %s\n", patrolID)
	}
}

// checkSlungWork checks for slung work on the agent's hook.
// If found, displays it prominently and tells the agent to execute it.
// The wisp is burned after the agent acknowledges it.
// Returns true if slung work was found (caller should skip normal startup directive).
func checkSlungWork(ctx RoleContext) bool {
	// Determine agent identity for hook lookup
	agentID := getAgentIdentity(ctx)
	if agentID == "" {
		return false
	}

	// Get the git clone root (hooks are stored at clone root, not cwd)
	cloneRoot, err := getGitRoot()
	if err != nil {
		// Not in a git repo - can't have hooks
		return false
	}

	sw, err := wisp.ReadHook(cloneRoot, agentID)
	if err != nil {
		if errors.Is(err, wisp.ErrNoHook) {
			// No hook - normal case, nothing to do
			return false
		}
		// Log other errors (permission, corruption) but continue
		fmt.Printf("%s Warning: error reading hook: %v\n", style.Dim.Render("⚠"), err)
		return false
	}

	// Verify bead exists before showing autonomous mode
	// Try multiple beads locations: cwd, clone root, and rig's beads dir
	var stdout bytes.Buffer
	beadExists := false
	beadSearchDirs := []string{ctx.WorkDir, cloneRoot}
	// For Mayor, also try the gastown rig's beads location
	if ctx.Role == RoleMayor {
		beadSearchDirs = append(beadSearchDirs, filepath.Join(ctx.TownRoot, "gastown", "mayor", "rig"))
	}
	for _, dir := range beadSearchDirs {
		cmd := exec.Command("bd", "show", sw.BeadID)
		cmd.Dir = dir
		stdout.Reset()
		cmd.Stdout = &stdout
		cmd.Stderr = nil
		if cmd.Run() == nil {
			beadExists = true
			break
		}
	}

	if !beadExists {
		fmt.Println()
		fmt.Printf("%s\n\n", style.Bold.Render("## 🎯 SLUNG WORK ON HOOK"))
		fmt.Printf("  Bead ID: %s\n", style.Bold.Render(sw.BeadID))
		fmt.Printf("  %s Bead %s not found! It may have been deleted.\n",
			style.Bold.Render("⚠ WARNING:"), sw.BeadID)
		fmt.Println("  The hook will NOT be burned. Investigate this issue.")
		fmt.Println()
		// Don't burn - leave hook for debugging
		return false
	}

	// Build the role announcement string
	roleAnnounce := buildRoleAnnouncement(ctx)

	// Found slung work! Display AUTONOMOUS MODE prominently
	fmt.Println()
	fmt.Printf("%s\n\n", style.Bold.Render("## 🚨 AUTONOMOUS WORK MODE 🚨"))
	fmt.Println("Work is slung on your hook. After announcing your role, begin IMMEDIATELY.")
	fmt.Println()
	fmt.Println("1. Announce: \"" + roleAnnounce + "\" (ONE line, no elaboration)")
	fmt.Printf("2. Then IMMEDIATELY run: `bd show %s`\n", sw.BeadID)
	fmt.Println("3. Begin execution - no waiting for user input")
	fmt.Println()
	fmt.Println("**DO NOT:**")
	fmt.Println("- Wait for user response after announcing")
	fmt.Println("- Ask clarifying questions")
	fmt.Println("- Describe what you're going to do")
	fmt.Println()

	// Show the slung work details
	fmt.Printf("%s\n\n", style.Bold.Render("## Slung Work"))
	fmt.Printf("  Bead ID: %s\n", style.Bold.Render(sw.BeadID))
	if sw.Subject != "" {
		fmt.Printf("  Subject: %s\n", sw.Subject)
	}
	if sw.Context != "" {
		fmt.Printf("  Context: %s\n", sw.Context)
	}
	fmt.Printf("  Slung by: %s at %s\n", sw.CreatedBy, sw.CreatedAt.Format("2006-01-02 15:04:05"))
	fmt.Println()

	// Show bead preview (first 15 lines)
	lines := strings.Split(stdout.String(), "\n")
	maxLines := 15
	if len(lines) > maxLines {
		lines = lines[:maxLines]
		lines = append(lines, "...")
	}
	fmt.Println("**Bead preview:**")
	for _, line := range lines {
		fmt.Printf("  %s\n", line)
	}
	fmt.Println()

	// Burn the hook now that it's been read and verified
	if err := wisp.BurnHook(cloneRoot, agentID); err != nil {
		fmt.Printf("%s Warning: could not burn hook: %v\n", style.Dim.Render("⚠"), err)
	}

	return true
}

// buildRoleAnnouncement creates the role announcement string for autonomous mode.
func buildRoleAnnouncement(ctx RoleContext) string {
	switch ctx.Role {
	case RoleMayor:
		return "Mayor, checking in."
	case RoleDeacon:
		return "Deacon, checking in."
	case RoleWitness:
		return fmt.Sprintf("%s Witness, checking in.", ctx.Rig)
	case RoleRefinery:
		return fmt.Sprintf("%s Refinery, checking in.", ctx.Rig)
	case RolePolecat:
		return fmt.Sprintf("%s Polecat %s, checking in.", ctx.Rig, ctx.Polecat)
	case RoleCrew:
		return fmt.Sprintf("%s Crew %s, checking in.", ctx.Rig, ctx.Polecat)
	default:
		return "Agent, checking in."
	}
}

// getGitRoot returns the root of the current git repository.
func getGitRoot() (string, error) {
	cmd := exec.Command("git", "rev-parse", "--show-toplevel")
	out, err := cmd.Output()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(out)), nil
}

// getAgentIdentity returns the agent identity string for hook lookup.
func getAgentIdentity(ctx RoleContext) string {
	switch ctx.Role {
	case RoleCrew:
		return fmt.Sprintf("%s/crew/%s", ctx.Rig, ctx.Polecat)
	case RolePolecat:
		return fmt.Sprintf("%s/polecats/%s", ctx.Rig, ctx.Polecat)
	case RoleMayor:
		return "mayor"
	case RoleDeacon:
		return "deacon"
	case RoleWitness:
		return fmt.Sprintf("%s/witness", ctx.Rig)
	case RoleRefinery:
		return fmt.Sprintf("%s/refinery", ctx.Rig)
	default:
		return ""
	}
}

// acquireIdentityLock checks and acquires the identity lock for worker roles.
// This prevents multiple agents from claiming the same worker identity.
// Returns an error if another agent already owns this identity.
func acquireIdentityLock(ctx RoleContext) error {
	// Only lock worker roles (polecat, crew)
	// Infrastructure roles (mayor, witness, refinery, deacon) are singletons
	// managed by tmux session names, so they don't need file-based locks
	if ctx.Role != RolePolecat && ctx.Role != RoleCrew {
		return nil
	}

	// Create lock for this worker directory
	l := lock.New(ctx.WorkDir)

	// Determine session ID from environment or context
	sessionID := os.Getenv("TMUX_PANE")
	if sessionID == "" {
		// Fall back to a descriptive identifier
		sessionID = fmt.Sprintf("%s/%s", ctx.Rig, ctx.Polecat)
	}

	// Try to acquire the lock
	if err := l.Acquire(sessionID); err != nil {
		if errors.Is(err, lock.ErrLocked) {
			// Another agent owns this identity
			fmt.Printf("\n%s\n\n", style.Bold.Render("⚠️  IDENTITY COLLISION DETECTED"))
			fmt.Printf("Another agent already claims this worker identity.\n\n")

			// Show lock details
			if info, readErr := l.Read(); readErr == nil {
				fmt.Printf("Lock holder:\n")
				fmt.Printf("  PID: %d\n", info.PID)
				fmt.Printf("  Session: %s\n", info.SessionID)
				fmt.Printf("  Acquired: %s\n", info.AcquiredAt.Format("2006-01-02 15:04:05"))
				fmt.Println()
			}

			fmt.Printf("To resolve:\n")
			fmt.Printf("  1. Find the other session and close it, OR\n")
			fmt.Printf("  2. Run: gt doctor --fix (cleans stale locks)\n")
			fmt.Printf("  3. If lock is stale: rm %s/.runtime/agent.lock\n", ctx.WorkDir)
			fmt.Println()

			return fmt.Errorf("cannot claim identity %s/%s: %w", ctx.Rig, ctx.Polecat, err)
		}
		return fmt.Errorf("acquiring identity lock: %w", err)
	}

	return nil
}

// ensureBeadsRedirect ensures the .beads/redirect file exists for worktree-based roles.
// This handles cases where git clean or other operations delete the redirect file.
func ensureBeadsRedirect(ctx RoleContext) {
	// Only applies to crew and polecat roles (they use shared beads)
	if ctx.Role != RoleCrew && ctx.Role != RolePolecat {
		return
	}

	// Check if redirect already exists
	beadsDir := filepath.Join(ctx.WorkDir, ".beads")
	redirectPath := filepath.Join(beadsDir, "redirect")

	if _, err := os.Stat(redirectPath); err == nil {
		// Redirect exists, nothing to do
		return
	}

	// Determine the correct redirect path based on role and rig structure
	var redirectContent string

	// Get the rig root (parent of crew/ or polecats/)
	var rigRoot string
	relPath, err := filepath.Rel(ctx.TownRoot, ctx.WorkDir)
	if err != nil {
		return
	}
	parts := strings.Split(filepath.ToSlash(relPath), "/")
	if len(parts) >= 1 {
		rigRoot = filepath.Join(ctx.TownRoot, parts[0])
	} else {
		return
	}

	// Check for shared beads locations in order of preference:
	// 1. rig/mayor/rig/.beads/ (if mayor rig clone exists)
	// 2. rig/.beads/ (rig root beads)
	mayorRigBeads := filepath.Join(rigRoot, "mayor", "rig", ".beads")
	rigRootBeads := filepath.Join(rigRoot, ".beads")

	if _, err := os.Stat(mayorRigBeads); err == nil {
		// Use mayor/rig/.beads
		if ctx.Role == RoleCrew {
			// crew/<name>/.beads -> ../../mayor/rig/.beads
			redirectContent = "../../mayor/rig/.beads"
		} else {
			// polecats/<name>/.beads -> ../../mayor/rig/.beads
			redirectContent = "../../mayor/rig/.beads"
		}
	} else if _, err := os.Stat(rigRootBeads); err == nil {
		// Use rig root .beads
		if ctx.Role == RoleCrew {
			// crew/<name>/.beads -> ../../.beads
			redirectContent = "../../.beads"
		} else {
			// polecats/<name>/.beads -> ../../.beads
			redirectContent = "../../.beads"
		}
	} else {
		// No shared beads found, nothing to redirect to
		return
	}

	// Create .beads directory if needed
	if err := os.MkdirAll(beadsDir, 0755); err != nil {
		// Silently fail - not critical
		return
	}

	// Write redirect file
	if err := os.WriteFile(redirectPath, []byte(redirectContent+"\n"), 0644); err != nil {
		// Silently fail - not critical
		return
	}

	// Note: We don't print a message here to avoid cluttering prime output
	// The redirect is silently restored
}
