gt polecat: add 'done' or 'finish' command to transition from working to idle

When a polecat finishes work but session wasn't properly cleaned up, there's no way to reset it from 'working' state back to 'idle'.

Tried:
```
gt polecat sleep gastown/Angharad
Error: sleeping polecat: polecat is not active (state: working)
```

The sleep command only works on 'active' polecats, not 'working' ones.

Had to manually edit state.json to reset:
```
jq '.state = "idle" | .issue = ""' state.json
```

Need a command like:
```
gt polecat done gastown/Angharad   # working -> idle
gt polecat finish gastown/Angharad # working -> idle
gt polecat reset gastown/Angharad  # any state -> idle (force)
```


Ephemeral rig support for ad-hoc batch work

Support for temporary worker rigs for ad-hoc batch work.

## Use Case

Ad-hoc batch work where you want temporary workers:
- gt spawn --epic <epic-id> --ephemeral --workers 3
- Creates temp rig with 3 clones
- Workers process epic children
- Work merges through queue
- Clones cleaned up when done

## Interface

```go
type EphemeralRig struct {
    ID               string
    GitURL           string
    BaseCommit       string
    Workers          []string
    IntegrationBranch string
    EpicID           string  // associated epic (for cleanup trigger)
    CreatedAt        time.Time
}

func InitEphemeralRig(gitURL string, numWorkers int) (*EphemeralRig, error)
func (r *EphemeralRig) AddWorker(name string) error
func (r *EphemeralRig) Destroy(force bool) error
```

## Directory Structure

```
<rig>/ephemeral/
└── rig-a3f7/
    ├── config.json
    ├── Alice/     # clone
    ├── Bob/       # clone
    └── Carol/     # clone
```

## Cleanup Trigger

When all issues in the associated epic are closed AND merged, the ephemeral rig can be destroyed.

## Note

No swarm ID needed - the epic ID provides the grouping. Ephemeral rig is just a convenience for temporary clones.

## Reference

PGT: ephemeral.py (for structure, ignore swarm ID coupling)


MR state transitions: validate open→in_progress→closed

Enforce valid state transitions for merge-requests:

Valid transitions:
- open → in_progress (Engineer claims MR)
- in_progress → closed (merge success or rejection)
- in_progress → open (failure, reassign to worker)
- open → closed (manual rejection)

Invalid:
- closed → anything (immutable once closed)

Implement validation in MR update operations.

Reference: docs/merge-queue-design.md#state-machine


CLI: witness commands (start, stop, status)

Witness daemon CLI commands.

## Commands

### gt witness start
Start the witness daemon for a rig.
```
gt witness start <rig> [--interval <seconds>]
```
- Starts background daemon process
- Default heartbeat interval: 30s
- Logs to <rig>/witness/witness.log

### gt witness stop
Stop the witness daemon.
```
gt witness stop <rig>
```
- Graceful shutdown
- Completes current heartbeat cycle

### gt witness status
Show witness status.
```
gt witness status <rig> [--json]
```
Output:
- Running/stopped
- Last heartbeat time
- Polecats being monitored
- Recent nudges/escalations

### gt witness logs
View witness logs.
```
gt witness logs <rig> [--follow] [--lines <n>]
```
- --follow: Continuous output (like tail -f)
- --lines: Number of lines (default 50)

## Daemon Management

Witness runs as background process:
- PID stored in <rig>/witness/witness.pid
- Uses nohup for persistence
- Logs structured JSON events

## Implementation

```go
var witnessCmd = &cobra.Command{Use: "witness"}
witnessCmd.AddCommand(startCmd, stopCmd, statusCmd, logsCmd)
```

Start launches daemon via exec, status reads PID file and pings process.


Failure handling: assign back to worker, add labels

Handle merge failures appropriately.

Failure types and actions:
| Failure     | Action                                      |
|-------------|---------------------------------------------|
| conflict    | Add needs-rebase label, assign to worker    |
| tests_fail  | Add needs-fix label, assign to worker       |
| build_fail  | Add needs-fix label, assign to worker       |
| flaky_test  | Retry once, then treat as tests_fail        |
| push_fail   | Retry with backoff, escalate if persistent  |

Actions:
1. bd update <id> --status=open --assignee=<worker>
2. bd update <id> --labels=<failure-label>
3. Send mail to worker explaining failure
4. Log failure details

Reference: docs/merge-queue-design.md#handling-failures


Merge conflicts: Buzzard/mq-status, Dementus/harness-docs


Epic: Refinery Engineer Autonomy

Make Refinery Engineer fully autonomous:

## Goal
Refinery can run indefinitely, processing all polecat work through merge queue, cycling sessions as needed, without human intervention.

## Key Components
1. Role prompting (CLAUDE.md, templates)
2. Handoff mechanism (pinned beads)
3. Context detection (gt prime)
4. Communication protocol (Witness, Deacon)
5. Future: Merge orchestration plugins

## Success Criteria
- Refinery starts, reads handoff, knows what to do
- Processes merges sequentially with conflict resolution
- Cycles sessions cleanly via Deacon
- Communicates results to Witness
- No work left behind


Mayor session cycling prompting

Add session cycling section to Mayor CLAUDE.md template.

## When to Cycle

Cycle proactively when:
- Running for several hours
- Context feels crowded (losing track of earlier state)
- Major phase completed
- About to start complex new work

## Composing Handoff Notes

1. Gather information:
   town status    # Overall health
   town rigs      # Each rig state
   town inbox     # Pending messages
   bd ready       # Work items

2. Compose note with this structure:

[HANDOFF_TYPE]: mayor_cycle
[TIMESTAMP]: <current time>
[SESSION_DURATION]: <how long running>

## Active Swarms
<per-rig swarm status>

## Rig Status
<table of rig health>

## Pending Escalations
<issues needing your decision>

## In-Flight Decisions
<decisions being made>

## Recent Actions
<last 5-10 things you did>

## Delegated Work
<work sent to refineries>

## User Requests
<pending user asks>

## Next Steps
<what next session should do>

## Warnings/Notes
<critical info for next session>

3. Send handoff:
   town mail send mayor/ -s "Session Handoff" -m "<note>"

4. End session - next instance picks up from handoff.


Mayor handoff mail template

Add MAYOR_HANDOFF mail template to templates.py.

## Template Function

def mayor_handoff(
    active_swarms: List[SwarmStatus],
    rig_status: Dict[str, RigStatus],
    pending_escalations: List[Escalation],
    in_flight_decisions: List[Decision],
    recent_actions: List[str],
    delegated_work: List[DelegatedItem],
    user_requests: List[str],
    next_steps: List[str],
    warnings: Optional[str] = None,
    session_duration: Optional[str] = None,
) -> Message:
    metadata = {
        'template': 'MAYOR_HANDOFF',
        'timestamp': datetime.utcnow().isoformat(),
        'session_duration': session_duration,
        'active_swarm_count': len(active_swarms),
        'pending_escalation_count': len(pending_escalations),
    }
    # ... format sections ...
    return Message.create(
        sender='mayor/',
        recipient='mayor/',
        subject='Session Handoff',
        body=body,
        priority='high',
    )

## Metadata Fields

- template: MAYOR_HANDOFF
- timestamp: ISO format
- session_duration: Human readable
- active_swarm_count: Number of active swarms
- pending_escalation_count: Number of escalations

## Mail Priority

Use priority='high' to ensure handoff is seen on startup.


Merge: gt-frs

branch: polecat/Slit
target: main
source_issue: gt-frs
rig: gastown


gt spawn: Not injecting work instructions to session

gt spawn starts session but doesn't inject the issue assignment.

Repro:
1. gt spawn gastown/Toast --issue gt-2ux
2. Session starts but polecat just sees Claude prompt
3. No issue context injected

Expected: Polecat should receive issue details automatically.
Actual: Polecat sits at blank prompt, needs manual injection.


Replaceable notifications via Claude Code queue

Leverage Claude Code's ability to replace queued text for notifications that supersede previous ones.

## Problem

If daemon sends 10 heartbeats while agent is busy, agent returns to see 10 stacked messages. Wasteful and noisy.

## Solution

Use Claude Code's queue replacement for:
- Heartbeat messages (only latest matters)
- Status updates that supersede previous
- Progress notifications

## Implementation

Notifications get a 'slot' identifier. New notification in same slot replaces old one:
- Slot: 'heartbeat' → only one heartbeat queued at a time
- Slot: 'status-<rig>' → latest status per rig
- No slot → stacks normally (for unique messages)

## Research Needed

- How does Claude Code expose queue replacement?
- tmux send-keys behavior with pending input
- Alternative: clear + resend pattern


gt molecule seed: create built-in molecules as beads

The molecule infrastructure is complete but built-in molecules (engineer-in-box, quick-fix, research) need to be seeded into the beads database.

## Current State
- `gt molecule list` works but shows 0 molecules
- BuiltinMolecules() in internal/beads/builtin_molecules.go has 3 molecules defined
- No way to create them as beads

## Needed
Add `gt molecule seed` command that:
1. Reads BuiltinMolecules()
2. Creates each as a bead with type: molecule
3. Uses well-known IDs (mol-engineer-in-box, mol-quick-fix, mol-research)
4. Idempotent (skip if already exists)

## Acceptance Criteria
```
gt molecule seed
gt molecule list   # Shows 3 built-in molecules
```

## Parent
gt-4nn: Molecules epic


Town & Rig Management: install, doctor, federation

Reify the Gas Town installation as a first-class concept.

## Goals
- Installable: gt install [path] creates complete installation
- Diagnosable: gt doctor checks and fixes issues
- Federable: Clone town to VMs with central control

## Architecture Reference

See docs/architecture.md for full design, especially:
- Directory structure (Town Level / Rig Level sections)
- Configuration (town.json, rigs.json schemas)
- Key design decisions (visible config dir, decentralized agents)


Outpost integration tests: Mock and E2E testing

## Overview

Integration tests for the outpost system.

## Mock Outpost

```go
type MockOutpost struct {
    name        string
    maxWorkers  int
    spawnDelay  time.Duration
    spawnError  error
    workers     []*MockWorker
}

func (o *MockOutpost) Spawn(issue string, cfg WorkerConfig) (Worker, error) {
    if o.spawnError != nil {
        return nil, o.spawnError
    }
    time.Sleep(o.spawnDelay)
    worker := &MockWorker{id: uuid.New().String(), issue: issue}
    o.workers = append(o.workers, worker)
    return worker, nil
}
```

## Unit Tests

```go
func TestOutpostManager_SelectOutpost(t *testing.T) {
    // Test policy-based selection
}

func TestOutpostManager_SpawnWithRetry(t *testing.T) {
    // Test retry logic with transient failures
}

func TestCloudRunOutpost_CostTracking(t *testing.T) {
    // Test cost calculation and caps
}
```

## Integration Tests (with mocked Cloud Run)

```go
func TestCloudRunOutpost_Integration(t *testing.T) {
    // Start mock HTTP server
    server := httptest.NewServer(mockWorkHandler())
    defer server.Close()

    outpost := NewCloudRunOutpost(OutpostConfig{
        ServiceURL: server.URL,
    })

    worker, err := outpost.Spawn("test-issue", WorkerConfig{})
    // Assert worker created, events streamed
}
```

## E2E Tests (requires real Cloud Run)

```go
// +build e2e

func TestCloudRunOutpost_E2E(t *testing.T) {
    if os.Getenv("CLOUDRUN_SERVICE_URL") == "" {
        t.Skip("CLOUDRUN_SERVICE_URL not set")
    }
    // Test against real Cloud Run service
}
```

## Files

- `internal/outpost/mock_test.go`
- `internal/outpost/manager_test.go`
- `internal/outpost/cloudrun_test.go`
- `internal/cloudrun/integration_test.go`

## Dependencies

Depends on: gt-9a2.8 (CloudRunOutpost), gt-9a2.15 (error handling)
Lower priority - can be done after implementations work.


gt session restart/status: Complete session management

Add missing session subcommands:

- gt session restart <rig> <polecat> - Restart a session (stop + start)
- gt session status <rig> <polecat> - Show session status details

status should show:
- Running state
- Uptime
- Current activity
- Last output timestamp


Swarm learning: Refinery merge queue automation

Manually merging 15 polecat branches was painful and error-prone. Refinery should automate: detect completed work, run tests, merge to main, handle conflicts. This is core Refinery value prop.


Interactive prompts for destructive operations

Add interactive confirmations for destructive operations.

## Operations Needing Confirmation
- `gt swarm cancel` - Cancels active swarm
- `gt swarm land --force` - Force landing
- `gt polecat decommission` - Removes polecat
- `gt rig remove` - Removes rig
- `gt stop --all` - Stops all sessions
- `gt mail purge` - Permanently deletes mail

## Implementation Options

### Option 1: promptui library
```go
import "github.com/manifoldco/promptui"

prompt := promptui.Prompt{
    Label:     "Delete polecat Toast",
    IsConfirm: true,
}
result, err := prompt.Run()
```

### Option 2: Simple stdin
```go
func confirm(prompt string) bool {
    fmt.Printf("%s [y/N]: ", prompt)
    var response string
    fmt.Scanln(&response)
    return strings.ToLower(response) == "y"
}
```

## Bypass Flags
All confirmations skippable with --force or --yes:
```go
if !force && !confirm("Really cancel swarm?") {
    return nil
}
```

## Acceptance Criteria
- [ ] Destructive ops prompt by default
- [ ] --force or --yes bypasses
- [ ] Clear prompt text explaining action
- [ ] Non-interactive mode (piped input) auto-fails


Update Mayor prompting in gastown-py

The Mayor CLAUDE.md and related prompting in gastown-py (still in production use) needs to reflect current design decisions: session cycling, handoff protocol, cleanup responsibilities, beads access model. Sync prompting with GGT design work.


Add gt deacon start/stop/status commands

Add CLI commands: gt deacon start (spawn gt-deacon session), gt deacon stop (kill session), gt deacon status (show health state). Similar to gt mayor commands.


Implement gt mq CLI commands

Add gt mq subcommands as sugar over bd:

- gt mq submit: Create MR for current branch
- gt mq list: Show open merge requests
- gt mq next: Show next MR ready to process
- gt mq process: Engineer processes the queue
- gt mq reorder <id> --after <x>: Change ordering via deps
- gt mq status <id>: Show MR details

All commands should work with the Beads data plane.


Add gt decommission command for clean swarm/worker shutdown

Single command to cleanly shut down a swarm and its workers: cancel swarm, stop sessions, optionally remove polecats. E.g. gt decommission gt-hw6 --cleanup


CLI: all command for batch polecat operations

Batch operations across multiple polecats.

## Commands

### gt all start
```
gt all start [--awake-only] [<specs>...]
```
Start sessions for multiple polecats.
- --awake-only: Only start awake polecats
- specs: Polecat names, rig/polecat patterns

### gt all stop
```
gt all stop [<specs>...] [--force]
```
Stop sessions for multiple polecats.

### gt all status
```
gt all status [<specs>...] [--json]
```
Show status of multiple polecats.

### gt all attach
```
gt all attach [<specs>...]
```
Attach to multiple sessions in tmux panes/windows.

### gt all run
```
gt all run <command> [<specs>...]
```
Run command in multiple polecat sessions.

## Spec Patterns
- `Toast`: Specific polecat (in default/current rig)
- `gastown/Toast`: Specific rig/polecat
- `gastown/*`: All polecats in rig
- `*`: All polecats everywhere

## Implementation
```go
func expandSpecs(specs []string, awakeOnly bool) ([]*polecat.Polecat, error) {
    // Expand patterns to list of polecats
}

func runForAll(polecats []*polecat.Polecat, action func(*polecat.Polecat) error) error {
    // Run action for each, collect errors
}
```

## New File
internal/cmd/all.go

## PGT Reference
gastown-py/src/gastown/cli/all_cmd.py

## Acceptance Criteria
- [ ] Pattern expansion works
- [ ] Parallel execution where safe
- [ ] Aggregate error reporting
- [ ] --awake-only filter works


File locking for concurrent access

Add file locking for concurrent access safety.

## At-Risk Files
- .gastown/swarms.json (or per-swarm state.json)
- .gastown/refinery.json
- polecats/<name>/state.json
- inbox.jsonl files

## Go File Locking
Use syscall.Flock for advisory locking:
```go
type FileLock struct {
    file *os.File
}

func AcquireLock(path string, timeout time.Duration) (*FileLock, error) {
    f, err := os.OpenFile(path+".lock", os.O_CREATE|os.O_RDWR, 0644)
    if err != nil {
        return nil, err
    }
    // Use syscall.Flock with timeout
}

func (l *FileLock) Release() error
```

## Integration Pattern
```go
func (m *Manager) saveState(ref *Refinery) error {
    lock, err := AcquireLock(m.stateFile(), 5*time.Second)
    if err != nil {
        return fmt.Errorf("could not acquire lock: %w", err)
    }
    defer lock.Release()

    // Read-modify-write cycle
}
```

## New Package
internal/filelock/
├── lock.go     # FileLock, AcquireLock
└── lock_test.go

## Apply To
- internal/refinery/manager.go: loadState/saveState
- internal/cmd/swarm.go: SwarmStore
- internal/mail/mailbox.go: Append, rewrite
- internal/polecat/manager.go: state operations

## Timeout Handling
Default 5 second timeout. Return error if lock not acquired.

## Acceptance Criteria
- [ ] Lock files created (.lock extension)
- [ ] Timeout on lock contention
- [ ] All state files protected
- [ ] Locks released on error paths


Git wrapper: shell out to git

Wrapper for git operations via subprocess.

## Interface

```go
type Git struct {
    workDir string
}

func NewGit(workDir string) *Git

// Core operations
func (g *Git) Clone(url, dest string) error
func (g *Git) Checkout(ref string) error
func (g *Git) Fetch(remote string) error
func (g *Git) Pull(remote, branch string) error
func (g *Git) Push(remote, branch string, force bool) error

// Commit operations
func (g *Git) Add(paths ...string) error
func (g *Git) Commit(message string) error
func (g *Git) CommitAll(message string) error

// Query operations
func (g *Git) Status() (*GitStatus, error)
func (g *Git) CurrentBranch() (string, error)
func (g *Git) HasUncommittedChanges() (bool, error)
func (g *Git) RemoteURL(remote string) (string, error)

// Merge operations
func (g *Git) Merge(branch string) error
func (g *Git) Rebase(onto string) error
func (g *Git) AbortMerge() error
func (g *Git) AbortRebase() error
```

## Implementation

Shell out to git binary via exec.Command. Parse output where needed (Status, CurrentBranch).

## Error Handling

- Wrap git stderr in error messages
- Detect specific failures (merge conflict, auth failure, not a repo)
- Return structured errors for callers to handle

## Testing

- Use temp directories with real git repos
- Test both success and failure paths


Harness Design & Documentation

The harness (Gas Town installation directory) needs design cleanup, documentation, and tooling.

## Current Problems

1. **Shared harness confusion**: ~/ai is shared by PGT and GGT with overlapping structures
   - PGT uses ~/ai/mayor/ as town-level Mayor home
   - GGT Mayor works in ~/ai/mayor/rigs/gastown/
   - ~/ai/gastown/ has both .gastown/ (PGT) and mayor/ (git clone)

2. **Beads redirect**: ~/ai/.beads/redirect → mayor/rigs/gastown/.beads
   - This is specific to GGT's decentralized structure
   - Should be documented as an example

3. **architecture.md**: Verify rig-level mayor/rig/ is shown correctly

4. **No harness creation tooling**: Users must manually set up

## Proposed Work

- Document what a harness IS (installation directory)
- Create harness creation command or template repo
- Update architecture.md if needed
- Create example harness configuration for docs
- Resolve PGT/GGT sharing issue


Molecule instantiation: create child beads from template

When instantiating a molecule on a work bead:

## Transaction Flow

1. Parse molecule's `## Step:` sections from description
2. Begin SQLite transaction
3. For each step, create child issue:
   - ID: `{parent-id}.{step-ref}` or generated
   - Title: step title (from header or first line)
   - Description: step prose instructions
   - Type: task
   - Priority: inherit from parent
4. Add `instantiated-from` edge from each step to molecule:
   ```sql
   INSERT INTO dependencies (issue_id, depends_on_id, type, metadata)
   VALUES (step_id, mol_id, 'instantiated-from', '{"step": "implement"}');
   ```
5. Wire inter-step dependencies from `Needs:` lines
6. Commit transaction (atomic - all or nothing)

## Parsing Conventions

```markdown
## Step: <ref>
<prose instructions>
Needs: <step>, <step>  # optional
Tier: haiku|sonnet|opus  # optional hint
```

## Parameterization

Steps can have `{{variable}}` placeholders:
```markdown
## Step: implement
Implement {{feature_name}} in {{target_file}}.
```

Context map provided at instantiation time.

## API

```go
func (s *Store) InstantiateMolecule(mol *Issue, parent *Issue, ctx map[string]string) ([]*Issue, error)
func ParseMoleculeSteps(description string) ([]MoleculeStep, error)
```

Implementation lives in `internal/beads/molecule.go`.


Haiku-based smart stuck detection

Use Haiku to analyze tmux state when signals are ambiguous.

## When to Invoke

Only as escalation tier:
1. Keepalive is stale (> 2 min)
2. Tmux shows claude is running (not idle shell)
3. Heuristics can't determine state

## Prompt

Capture last 50 lines of tmux pane, ask Haiku:
'Is this Claude agent: WORKING | STUCK | IDLE | WAITING_FOR_HUMAN?'

## Cost

~$0.001 per check. At 1 check/min worst case = $0.06/hour.
In practice, most checks avoided by keepalive signal.

## Configuration

```toml
[daemon]
smart_detection = true
smart_model = "haiku"
smart_threshold = "2m"  # only check if stale > 2min
```


Bug: LIFECYCLE messages use literal <rig> placeholder

Found stale LIFECYCLE messages addressed to @<rig>/witness instead of actual rig names like @gastown/witness. Template substitution not happening.

Examples found:
- gm-bay: LIFECYCLE: polecat requesting shutdown (to @<rig>/witness)
- gm-7du, gm-94e, gm-u5x, gm-89b: same issue

Fix: Ensure rig name is substituted in lifecycle mail templates.


gt rig shutdown: Gracefully stop all rig agents

Add 'gt rig shutdown <rig>' command to gracefully stop all agents in a rig.

Should:
- Stop all polecat sessions
- Stop refinery
- Stop witness
- Optionally wait for graceful shutdown with timeout


Add crew session pattern to lifecycle handling

Update identityToSession and restartSession to handle crew patterns: gastown/max -> gt-gastown-max. Crew workers can request lifecycle but are not proactively monitored.


Merge: gt-g44u.3

branch: polecat/Ace
target: main
source_issue: gt-g44u.3
rig: gastown


Work on ga-lzh: Add gt witness attach command. Allow atta...

Work on ga-lzh: Add gt witness attach command. Allow attaching to witness session for a rig, similar to gt mayor attach. When done, submit MR (not PR) to integration branch for Refinery.


test-after-fix

test


Automatic spawn for ready work

Auto-spawn polecats for ready work:

When Witness has capacity (active_workers < max_workers):
1. Query bd ready for unblocked issues
2. Filter to rig-appropriate work (by prefix or epic)
3. For each ready issue up to capacity:
   - gt spawn --issue <id>
   - Track that we spawned for this issue

Configuration (in rig config.json):
- max_workers: 4 (default)
- spawn_delay: 5s between spawns
- auto_spawn: true/false

This enables 'fire and forget' swarming:
- Mayor creates epic with children
- Mayor tells Witness to work epic
- Witness spawns polecats automatically
- Witness cleans up as they complete


gt crew at: auto-prime when exec'ing Claude in-session

When running 'gt crew at <name>' from inside the target session, we exec Claude directly. But this means we can't send 'gt prime' afterward since we ARE the process.

Possible solutions:
1. Claude startup hook that runs gt prime
2. Pass prompt as argument to claude CLI
3. Wrapper script approach

Related: crew resume prompt also can't be sent in this path.


Document Overseer role (human operator)

Document the Overseer role in Gas Town architecture.

## The Overseer

The **Overseer** is the human operator of Gas Town. Not an agent - a person.

## Responsibilities

| Area | Overseer Does | Mayor/Agents Do |
|------|---------------|-----------------|
| Strategy | Define project goals | Execute toward goals |
| Priorities | Set priority order | Work in priority order |
| Escalations | Final decision on stuck work | Escalate to Overseer |
| Resources | Provision machines | Use allocated resources |
| Quality | Review & approve swarm output | Produce output |
| Operations | Run gt commands, monitor dashboards | Do the work |

## Key Interactions

### Overseer → Mayor
- Start/stop Mayor sessions
- Direct Mayor via conversation
- Review Mayor recommendations
- Approve cross-rig decisions

### Mayor → Overseer (Escalations)
- Stuck workers after retries
- Resource decisions (add machines, polecats)
- Ambiguous requirements
- Architecture decisions

## Operating Cadence

Typical Overseer workflow:
1. Morning: Check status, review overnight work
2. During day: Monitor, respond to escalations, adjust priorities
3. End of day: Review progress, plan next batch

## Commands for Overseers

```bash
gt status              # Quick health check
gt doctor              # Detailed diagnostics
gt doctor --fix        # Auto-repair issues
gt inbox               # Messages from agents
gt stop --all          # Emergency halt
```

## Documentation Updates

Add to docs/architecture.md:
- Overseer section under Agent Roles
- Clarify Mayor reports to Overseer
- Add Overseer to workflow diagrams


Design GGT prompt architecture

Audit PGT prompts and design canonical prompt system for GGT. Create docs/prompts.md with inventory, gap analysis, and Witness prompt design.


Refinery-Witness communication protocol

Define mail protocol between Refinery and Witness:

FROM Witness → Refinery:
- 'Polecat ready': polecat X completed work, ready for merge
- 'Rework complete': polecat Y finished requested rework

FROM Refinery → Witness:
- 'Merge success': polecat X merged, can be cleaned up
- 'Merge failed': polecat X needs rework on <reason>
- 'Rework request': please have a polecat rebase X on current main

Implement as structured mail with parseable format.


Daemon: integration test with real lifecycle

Need an end-to-end test that:
1. Starts daemon
2. Starts a test agent session
3. Sends lifecycle request to daemon
4. Verifies session was killed and restarted
5. Cleans up

Could use a mock 'agent' that's just a shell script.


gt mq integration land: merge integration branch to main

Implement 'gt mq integration land <epic>' command.

Actions:
1. Verify all MRs targeting integration/<epic> are merged
2. Verify integration branch exists
3. Merge integration/<epic> to main (--no-ff)
4. Run tests on main
5. Push to origin
6. Delete integration branch
7. Update epic status

Options:
- --force: land even if some MRs still open
- --skip-tests: skip test run
- --dry-run: preview only

This creates a single merge commit for the entire epic's work.

Reference: docs/merge-queue-design.md#integration-branches


town handoff command (optional)

CLI commands for session handoff workflow (optional convenience).

## Commands

### gt handoff
Generate handoff interactively.
```
gt handoff [--send]
```
- Collects current state (status, inbox, beads)
- Prompts for additional notes
- --send: Mail to self and exit

### gt resume
Check for and display pending handoff.
```
gt resume
```
- Checks inbox for handoff message
- Displays formatted handoff if found
- Suggests next actions

## Implementation

These are convenience wrappers. The same workflow can be done manually:
```bash
# Manual handoff
town status > /tmp/handoff
town inbox >> /tmp/handoff
bd ready >> /tmp/handoff
# Edit and send
town mail send mayor/ -s "Session Handoff" -f /tmp/handoff
```

## Priority

P2 - Optional. Manual workflow works fine. Nice to have for UX.

## Notes

Part of session cycling workflow designed in gt-u82.


Complete gt crew commands (list, attach, remove, refresh, status)

Add remaining crew subcommands to internal/cmd/crew.go:

1. gt crew list - List crew workspaces with status
2. gt crew at/attach - Start tmux session in crew workspace
3. gt crew remove - Remove crew workspace (with safety checks)
4. gt crew refresh - Context cycling with mail-to-self handoff
5. gt crew status - Show detailed workspace status

Build on existing crew add implementation in internal/cmd/crew.go.
Reference closed issues gt-cik.3-7 for original requirements.


Fix role detection for nested rig structures

When gastown/ has its own mayor/ dir, workspace detection finds it as town root instead of ~/ai. This breaks polecat/refinery/witness detection when running from within a rig.


Batch Work System: Integration branch, merge queue, landing

## Overview

This epic tracks batch work coordination in GGT. Key insight: **work is a stream, not discrete swarms**.

There are no "swarm IDs" - the epic IS the grouping, the merge queue IS the coordination. "Swarming an epic" is colloquial for spawning multiple workers on its children.

## Architecture

- **Epics** group related work
- **Issues** are individual tasks
- **Dependencies** encode ordering (multi-wave emerges automatically)
- **Merge queue** coordinates completed work
- **Workers** process issues independently

See Key Decision #11 in docs/architecture.md: "Work is a Stream (No Swarm IDs)"

## What This Epic Covers

1. Integration branch management for batch merges
2. Refinery semantic merge processing
3. Witness landing protocol (cleanup)
4. Report generation (epic-based, not swarm-ID-based)
5. Git safety auditing
6. Work reconciliation

## What Was Removed

- Swarm IDs (sw-1, sw-2, etc.)
- Per-swarm directories (.gastown/swarms/<id>/)
- Swarm manifest/state/events files
- swarm-started/swarm-landed beads

These were redundant - beads already provides hierarchy, status, dependencies, and history.

## References

- Architecture: docs/architecture.md (Key Decision #11, Multi-Wave Work Processing)
- PGT: swarm/manager.py, swarm/landing.py (behavioral reference, but ignore swarm ID patterns)


Test message

Testing GGT mail via beads


Timed Beads: Scheduled recurring work

## Summary

Timed beads wake up periodically and get injected into the ready queue by the daemon.

## Schema Extension

```yaml
id: gt-weekly-sync
type: task  # or sentinel
schedule: "0 9 * * 1"  # cron: Monday 9am
# OR
interval: 24h          # every 24 hours
tier: haiku            # cheap model for routine checks
next_run: 2025-12-20T09:00:00Z
```

## Daemon Integration

Daemon heartbeat loop:
1. Check timed beads where `next_run <= now`
2. For each due bead:
   - Inject into ready queue (set status to open if needed)
   - Update `next_run` based on schedule/interval
3. Witnesses pick up work via `bd ready`

## Use Cases

- Weekly team sync reminders
- Daily health checks
- Periodic cleanup tasks
- Scheduled reports

## Interaction with Pinned Beads

A pinned bead can be timed - it wakes up periodically but never closes.
This is how you model "background services" in Gas Town.


Test Task 1: Add comment to gt.go


MR: gt-svi.1 (polecat/Furiosa)

branch: polecat/Furiosa
target: main
source_issue: gt-svi.1


Design: Collapse mail into beads

## Proposal

Replace the separate mail system (JSONL inboxes) with beads issues using a naming convention.

## Rationale

If all state should be in beads (work items, swarm state, dependencies), why have a separate system for messages? Mail is just:
- Handoffs (agent → self)
- Commands (Mayor → Refinery)
- Escalations (Witness → Mayor)

These can all be beads issues with a special prefix.

## Design

### Convention
- Messages use `@-` prefix: `@-witness-1734012345`
- Assignee = recipient
- Status: open = unread, closed = read/acknowledged
- Priority 0 = urgent

### Commands (thin wrappers)
```bash
gt mail send witness -s "Subject" -m "Body"
  → bd create --prefix=@ --title="Subject" --assignee=witness --description="Body"

gt mail inbox
  → bd list --prefix=@ --assignee=$(gt whoami) --status=open

gt mail read @-abc
  → bd show @-abc && bd close @-abc
```

### Notification
Daemon watches for new `@-` issues and pokes relevant sessions.
Or: agents poll on heartbeat (simpler).

## What We Remove
- `mail/inbox.jsonl` files
- Mail JSONL read/write code
- Separate delivery mechanism

## What We Keep
- `gt mail` CLI (as wrapper)
- Handoff semantics
- Notification (via daemon or polling)

## Benefits
- One system, one sync, one query interface
- All communication in git history
- Simpler architecture

## Risks
- Beads prefix filtering must be efficient
- Namespace collision with user prefixes
- Performance for high-frequency messages (probably fine for handoffs)

## Decision Point
Do we need first-class mail support in beads (`bd mail` commands) or is convention sufficient?


Swarms of One: Lightweight single-worker task dispatch

Design and implement a lightweight pattern for firing off single workers to handle tasks without full swarm overhead.

## Context

Currently we have:
- town spawn: Creates a polecat in a rig with issue assignment
- Swarms (sw-*): Full lifecycle tracking with manifest, state, events, reports
- Ephemeral rigs (rig-*): Temporary worker groups for swarms

What's missing: A simple way to say 'fire off a worker to do X' without swarm ceremony.

## Design Questions

1. Should this be a new command like 'gt fire' or an option on existing commands?
2. Should single tasks still get swarm IDs (sw-N) for consistency/queryability?
3. Should it default to creating new workers or support --reuse for idle polecats?
4. How does this relate to crew workers (gt-cik)?

## Possible Interface

gt fire --rig gastown --issue gt-xyz [--prompt '...']
gt fire gastown/QuickTask --issue gt-xyz

## Related

- gt-cik: Overseer Crew (user-managed persistent workspaces)
- gt-kmn: Swarm System epic


Create mol-install-go-binary molecule

Create a single-step molecule for building and installing the gt binary.

## Molecule Definition
\`\`\`markdown
## Molecule: install-go-binary
Single step to rebuild and install the gt binary after code changes.

## Step: install
Build and install the gt binary locally.

Run from the rig directory:
\`\`\`
go build -o gt ./cmd/gt
go install ./cmd/gt
\`\`\`

Verify the installed binary is updated:
\`\`\`
which gt
gt --version  # if we have version command
\`\`\`
\`\`\`

## Implementation
1. Add to builtin_molecules.go
2. Update SeedBuiltinMolecules to include it
3. Run gt molecule seed

## Acceptance
- [ ] mol-install-go-binary exists in beads
- [ ] Can be instantiated standalone
- [ ] Can be included by other molecules


Fix gt prime to give crew workers crew context

gt prime currently gives Mayor context to all agents. Crew workers should get crew-specific context. Also extract shared theory of operation from mayor priming into shared context for all roles.


Update config location: .gastown/ → config/

Move rig configuration from hidden .gastown/ to visible config/:
- config/rig.json: Rig configuration
- config/engineer.json: Engineer settings (test command, etc.)
- config/witness.json: Witness settings (heartbeat interval, etc.)

Hidden directories are poorly discovered by AI agents.


Merge: gt-3x1.3

branch: polecat/Doof
target: main
source_issue: gt-3x1.3
rig: gastown


Work on gt-o9j: Fix tmux status bar polecat count - exclu...

Work on gt-o9j: Fix tmux status bar polecat count - exclude static roles (mayor, deacon, witnesses, refineries, docs, hop). Run 'bd show gt-o9j' for details.


gt mq integration status: show integration branch status

Implement 'gt mq integration status <epic>' command.

Display:
- Integration branch name and creation date
- Commits ahead of main
- MRs merged to integration (closed)
- MRs pending (open, targeting integration)
- Comparison: main..integration/<epic>

Output example:
  Integration: integration/gt-auth-epic
  Created: 2025-12-17
  Ahead of main: 5 commits

  Merged MRs (3):
    gt-mr-001  Fix login timeout
    gt-mr-002  Fix session expiry
    gt-mr-003  Update auth config

  Pending MRs (1):
    gt-mr-004  Update auth tests (in_progress)

Reference: docs/merge-queue-design.md#integration-branches


gt mail send: --priority flag should work like bd mail send

UX inconsistency: gt mail send passes flags that bd mail send doesn't support.

## Root Cause

router.go line 42 passes `--priority` to bd:
```go
args = append(args, "--priority", fmt.Sprintf("%d", beadsPriority))
```

But `bd mail send` only has `--urgent` (boolean), not `--priority`.

## Fix Options

1. Add `--priority` flag to `bd mail send` (preferred - more expressive)
2. Change router to only use `--urgent` when priority=0

## Also Affected

- `--type` flag (line 46) - bd mail send doesn't have this
- `--thread-id` flag (line 51) - bd mail send doesn't have this
- `--reply-to` flag (line 56) - bd mail send doesn't have this

The router assumes bd mail send has features it doesn't have.


Port Gas Town to Go

Complete rewrite of Gas Town in Go for improved performance and single-binary distribution.

## Goals
- Single installable binary (gt)
- All Python functionality ported
- Federation support built-in
- Improved performance

## Phases
1. Core infrastructure (config, workspace, git wrapper)
2. Rig & polecat management
3. Session & tmux operations
4. Mail system
5. CLI commands
6. TUI (optional)


VM outpost setup: Terraform and documentation

## Overview

Documentation and optional Terraform for setting up VM outposts.

## Manual Setup Guide

```markdown
# Setting Up a VM Outpost

## Prerequisites
- GCE VM (or any Linux machine with SSH access)
- SSH key pair
- Git configured on VM

## Steps

1. Install Claude Code on VM:
   ```bash
   ssh user@vm
   npm install -g @anthropic-ai/claude-code
   ```

2. Clone Gas Town:
   ```bash
   mkdir -p ~/ai
   cd ~/ai
   gt install .
   ```

3. Configure Git credentials:
   ```bash
   git config --global user.name "Your Name"
   git config --global credential.helper store
   ```

4. Add outpost to local config:
   ```bash
   gt outpost add ssh \
     --name gce-burst \
     --host 10.0.0.5 \
     --user steve \
     --key ~/.ssh/gce_worker \
     --town-path /home/steve/ai \
     --max-workers 8
   ```

5. Test connectivity:
   ```bash
   gt outpost ping gce-burst
   ```
```

## Terraform Module (Optional)

```hcl
# deploy/terraform/vm-outpost/main.tf

variable "project" {}
variable "zone" { default = "us-central1-a" }
variable "machine_type" { default = "e2-standard-4" }

resource "google_compute_instance" "outpost" {
  name         = "gastown-outpost"
  machine_type = var.machine_type
  zone         = var.zone

  boot_disk {
    initialize_params {
      image = "ubuntu-2204-lts"
      size  = 100
    }
  }

  network_interface {
    network = "default"
    access_config {}
  }

  metadata_startup_script = file("${path.module}/startup.sh")
}

output "external_ip" {
  value = google_compute_instance.outpost.network_interface[0].access_config[0].nat_ip
}
```

## Files

- `docs/vm-outpost-setup.md`
- `deploy/terraform/vm-outpost/` (optional)

Depends on: gt-9a2.5 (SSHOutpost)


Swarm learning: Beads database locality gap

## PGT Bug (GGT Design Already Correct)

Mayor created issues in `~/ai/mayor/rigs/beads/.beads/` but polecats use `~/ai/beads/.beads/`. Different databases = polecats can't see Mayor's beads.

**GGT Fix**: architecture.md already specifies:
- All agents use BEADS_DIR pointing to rig-level `.beads/`
- Lines 116-143: Beads Configuration for Multi-Agent
- Lines 573-586: Rig-Level Beads via BEADS_DIR

This is a PGT implementation gap, not a design issue. GGT spawn must set BEADS_DIR correctly.


Add merge-request type to Beads schema

Define the merge-request bead type with fields:
- branch: source branch (e.g., polecat/Nux/gt-xxx)
- target_branch: destination (main or integration/xxx)
- source_issue: the work being merged (gt-xxx)
- worker: who did the work
- rig: which rig
- merge_commit: (filled on close) SHA of merge commit
- close_reason: (filled on close) success/failure details

This may require beads schema changes or just convention.


Architecture: Decentralized rig structure with per-rig agents

## Decision

Adopt decentralized architecture where each rig contains all its agents (mayor/, witness/, refinery/, polecats/) rather than centralizing mayor clones at town level.

## Town Level Structure

```
~/ai/                              # Town root
├── config/                        # Town config (VISIBLE, not hidden)
│   ├── town.json                  # {"type": "town"}
│   ├── rigs.json                  # Registry of managed rigs
│   └── federation.json            # Wasteland config (future)
│
├── mayor/                         # Mayor's HOME at town level
│   ├── CLAUDE.md
│   ├── mail/inbox.jsonl
│   └── state.json
│
└── <rigs>/                        # Managed projects
```

## Rig Level Structure (e.g., wyvern)

```
wyvern/                            # Rig = clone of project repo
├── .git/info/exclude              # Gas Town adds: polecats/ refinery/ witness/ mayor/
├── .beads/                        # Beads (if project uses it)
├── [project files]                # Clean project code on main
│
├── polecats/                      # Worker clones
│   └── <name>/                    # Each is a git clone
│
├── refinery/
│   ├── rig/                       # Refinery's clone
│   ├── state.json
│   └── mail/inbox.jsonl
│
├── witness/                       # NEW: Per-rig pit boss
│   ├── rig/                       # Witness's clone
│   ├── state.json
│   └── mail/inbox.jsonl
│
└── mayor/
    ├── rig/                       # Mayor's clone for this rig
    └── state.json
```

## Key Decisions

1. **Visible config dir**: `config/` not `.gastown/` (models don't find hidden dirs)
2. **Witness per-rig**: Each rig has its own Witness (pit boss) with its own clone
3. **Mayor decentralized**: Mayor's clones live IN each rig at `<rig>/mayor/rig/`
4. **Minimal invasiveness**: Only `.git/info/exclude` modified, no commits to project
5. **Clone subdir name**: Keep `rig/` for consistency (refinery/rig/, witness/rig/, mayor/rig/)

## Role Detection

- Town root or mayor/ → Mayor (town level)
- Rig root → Mayor (canonical main)
- <rig>/mayor/rig/ → Mayor (rig-specific)
- <rig>/refinery/rig/ → Refinery
- <rig>/witness/rig/ → Witness
- <rig>/polecats/<name>/ → Polecat

## Migration from PGT

- `mayor/rigs/<rig>/` → `<rig>/mayor/rig/`
- `<rig>/town/` → eliminated (rig root IS the clone)
- Add `witness/` to each rig


Refinery Engineer: Role prompting and CLAUDE.md

Create refinery/CLAUDE.md with Chief Merge Engineer role context. Include:
- Role identity and responsibilities
- Decision authority (merge order, test frequency, binary rebuilds)
- Communication patterns (Witness, Deacon)
- Session lifecycle (handoff bead protocol)
- Key commands reference


gt mq retry: retry a failed MR

Implement 'gt mq retry <id>' to retry a failed merge request.

Actions:
1. Verify MR exists and is in failed state (open with failure labels)
2. Remove failure labels (needs-rebase, needs-fix)
3. Reset to ready state
4. Optionally re-run immediately (--now flag)

Options:
- --now: immediately process (instead of waiting for Engineer loop)

Reference: docs/merge-queue-design.md#cli-commands


CLI: mail commands (send, inbox, read)

CLI: mail commands wrapping bd mail.

## Status

Current implementation works with JSONL files. Refactor to wrap bd mail CLI.

## Commands

### gt mail inbox
```
gt mail inbox [--unread] [--json]
```
Wraps: `bd mail inbox [--json]`

### gt mail read
```
gt mail read <id>
```
Wraps: `bd mail read <id> && bd mail ack <id>`
Note: bd mail read + ack marks as read.

### gt mail send
```
gt mail send <address> -s "Subject" -m "Body" [--notify]
```
Wraps: `bd mail send <recipient> -s "Subject" -m "Body"`
If --notify: inject tmux notification after send.

### gt mail delete
```
gt mail delete <id>
```
Wraps: `bd mail ack <id>` (closes the message issue)

## Address Translation

Same as gt-u1j.6:
- `mayor/` → `mayor`
- `<rig>/refinery` → `<rig>-refinery`
- `<rig>/<polecat>` → `<rig>-<polecat>`

## Dependencies

- gt-u1j.6: Mail system backend


Work on gt-e11: gt mail send priority flag is incompatibl...

Work on gt-e11: gt mail send priority flag is incompatible with bd mail send. Run 'bd show gt-e11' for details.


Spawn inject: Enter not submitted after multiline paste

When gt spawn injects a multiline context (85+ lines), Claude Code shows
'[Pasted text #1 +85 lines]' but the prompt is not submitted.

The tmux SendKeys function does include 'Enter' but it appears to not work
for long pasted text:

```go
func (t *Tmux) SendKeys(session, keys string) error {
    _, err := t.run("send-keys", "-t", session, keys, "Enter")
    return err
}
```

Possible fixes:
1. Use tmux paste-buffer instead of send-keys for long text
2. Send Enter separately after the text
3. Use bracketed paste mode correctly

Reproduction:
```bash
gt spawn gastown/Nux --issue gt-u1j.13 --create
# Session shows pasted text but waits at prompt
```


CLI: doctor diagnostics and auto-repair

GGT completely lacks the doctor command which is critical for debugging.

Required Commands:
- gt doctor [<rig>] - Run diagnostic checks
- gt doctor --fix - Auto-repair common issues

Checks to Implement:
Workspace Level: Config validity, Mayor mailbox, Rig registry
Rig Level: Git state, clone health, witness/refinery presence, beads sync
Swarm Level: Stuck detection, zombie sessions, heartbeat health

PGT Reference: gastown-py/src/gastown/cli/dashboard_cmd.py


Review and audit all GGT beads

Thorough review of all filed beads in gastown GGT repo. Check for: consistency, completeness, correct dependencies, accurate descriptions, proper prioritization. Ensure beads are self-contained and dont rely on external docs.


Enhance Mayor CLAUDE.md with GGT milestone tracking

Update ~/ai/CLAUDE.md (Mayor startup context) with:

1. GGT Self-Hosting Milestone section
   - Track progress toward gt replacing town
   - Key blockers: gt-h5n (merge queue), gt-974 (daemon)
   - What "self-hosting" means (GGT working on itself)

2. Recent Architecture Decisions
   - Engineer = role, Refinery = place
   - Merge queue in Beads (not branch discovery)
   - Session restart protocol

3. Transition Plan
   - Current: use town commands (PGT)
   - Target: use gt commands (GGT)
   - Switch criteria: gt-b1g closed

Keep it concise - Mayor context should be quick to scan.


Mail coordination should use town-level database

gt mail send/inbox should use ~/gt/.beads (town root) not rig-local beads. Cross-rig mail coordination (Witness <-> Mayor, polecat <-> Witness) needs to be in a shared location.


Witness role template + CLAUDE.md

Create the Witness agent's role context:

1. internal/templates/roles/witness.md.tmpl
   - Witness responsibilities (polecat lifecycle)
   - Core loop description
   - Commands available (gt polecats, bd ready, etc)
   - Escalation protocol

2. Generate CLAUDE.md for witness/ directories
   - Context about the rig
   - Mail address: <rig>/witness
   - Startup protocol (gt prime, check inbox)

The Witness is the per-rig 'pit boss' - NOT a global coordinator. It manages polecats for ONE rig only.


Work reconciliation module

Port reconcile.py work verification to Go.

## Purpose

Verify assigned issues are completed before landing.

## Interface

```go
type ReconcileResult struct {
    CompletedIssues  []IssueInfo
    IncompleteIssues []IssueInfo
    AllComplete      bool
    PolecatsChecked  []string
    Errors           []string
}

func ReconcileWork(rig string, polecats []string) *ReconcileResult
func GetAssignedIssues(assignee string) []IssueInfo
func UnassignIssue(issueID string, resetStatus bool) error
```

## Logic

1. Get list of polecats in swarm
2. For each polecat, query beads for assigned issues
3. Categorize as completed (closed) or incomplete (open/in_progress)
4. Return summary for landing decision

## Handling Incomplete Work

If issues incomplete:
- Abort landing (default)
- Or force landing (leaves work unfinished)
- Unassign incomplete issues for future work

## Reference

PGT: swarm/reconcile.py


🤝 HANDOFF: Timing fixes applied

Fixed mayor attach timing bug. Completed: timing fix, gt-tulx, Emma beads-6v2. Remaining P1: gt-17zr, gt-kcee, gt-szsq. Run gt prime on startup.


Beads epic templates for batch work patterns

Optional: Define templates for common batch work patterns.

## Concept

A template encodes a workflow pattern that can be instantiated as beads:

```yaml
# templates/batch-basic.yaml
name: basic-batch
description: Simple batch work pattern
phases:
  - name: startup
    issues:
      - title: "Verify workers ready"
  - name: working
    # Actual work issues added separately
  - name: cleanup
    issues:
      - title: "Merge all branches"
      - title: "Clean up workers"
      - title: "Report to Mayor"
```

## Usage

```bash
gt spawn --template basic-batch --epic gt-u1j --workers 3
```

Creates beads epic with template phases + actual work from gt-u1j children.

## Decision Point

Templates are OPTIONAL. The core design (beads as state, multi-wave orchestration) works without templates. Templates are sugar for common patterns.

Consider deferring to P3 or dropping entirely if beads epics with dependencies suffice.

## Note

No "swarm IDs" involved - templates just pre-populate epic/issue structure.


Mail system: message types and threading

GGT mail system needs message types and threading like PGT.

## 1. Message Types
Add to internal/mail/types.go:
```go
type MessageType string
const (
    TypeTask         MessageType = "task"         // Required processing
    TypeScavenge     MessageType = "scavenge"     // Optional first-come work
    TypeNotification MessageType = "notification" // Informational
    TypeReply        MessageType = "reply"        // Response to message
)

// Update Message struct
type Message struct {
    // existing fields...
    Type     MessageType `json:"type"`
    ThreadID string      `json:"thread_id,omitempty"`
    ReplyTo  string      `json:"reply_to,omitempty"`
}
```

## 2. Priority Levels
Expand from 2 to 4:
```go
type Priority string
const (
    PriorityLow    Priority = "low"
    PriorityNormal Priority = "normal"
    PriorityHigh   Priority = "high"
    PriorityUrgent Priority = "urgent"
)
```

## 3. CLI Updates
internal/cmd/mail.go:
- Add --type flag to send: `gt mail send ... --type task`
- Add --reply-to flag: `gt mail send ... --reply-to <msg-id>`
- Add thread command: `gt mail thread <thread-id>`

## 4. Threading Logic
NewMessage() should auto-generate thread_id if not a reply.
Reply messages inherit thread_id from original.

## Files to Modify
- internal/mail/types.go: Add types, expand Priority
- internal/mail/mailbox.go: Thread filtering
- internal/cmd/mail.go: CLI flags and thread command

## PGT Reference
gastown-py/src/gastown/mail/message.py

## Acceptance Criteria
- [ ] Messages have type field (default: notification)
- [ ] 4 priority levels supported
- [ ] Reply creates thread with shared thread_id
- [ ] gt mail thread <id> shows conversation


Session cycling UX: smooth transitions via TUI wrapper

## Problem

Current CLI agent session cycling is painful:
- Shell → CC starts → priming → context loads → ready → work → exit/crash → repeat
- Each cycle is 30-60 seconds of cold boot
- No continuity between shell and agent's inner state
- Raw "session not running, starting..." loop is the baseline

## GGT Advantages (already have)

- Beads: Work state survives session death completely
- Mail: Handoff notes from past-self to future-self
- Prime commands: Structured context reload

## Gap: Transition Mechanics

Ideas to explore when actively using CLI:

1. **In-band cycling** - `/restart` or `/cycle` command, agent handles own restart without dropping to shell

2. **Hot standby** - TUI maintains pre-warmed session in background, switch to already-primed agent

3. **Persistent wrapper** - Bubbletea TUI stays running across session cycles, CC sessions come/go inside it

4. **Session pooling** - Keep 2-3 primed sessions ready, never wait for cold start

## Deferred

Deliberately P4 until we're actively using the simpler CLI and feel the pain firsthand.


gt session capture: Support positional line count argument

Make 'gt session capture gastown/Toast 50' work.

Currently requires: gt session capture gastown/Toast -n 50
Should also accept: gt session capture gastown/Toast 50

Agent UX principle: commands should work the way agents guess they work.


gt refinery attach: Attach to refinery session

Add 'gt refinery attach [rig]' command to attach to refinery tmux session.

Mirrors 'gt mayor attach' pattern. If rig not specified, use current rig context.


Outpost CLI commands: gt outpost list/status/add

## Overview

CLI commands for managing outposts.

## Commands

```bash
# List configured outposts
gt outpost list
NAME            TYPE       WORKERS   STATUS
local           local      2/4       healthy
gce-burst       ssh        0/8       healthy
cloudrun-burst  cloudrun   0/20      healthy

# Detailed status
gt outpost status [name]
Outpost: cloudrun-burst
Type: cloudrun
Project: my-gcp-project
Region: us-central1
Service: gastown-worker
Active Workers: 3/20
Cost (today): $0.42
Status: healthy

# Add new outpost interactively
gt outpost add
? Outpost type: [local/ssh/cloudrun]
> cloudrun
? Name: cloudrun-burst
? GCP Project: my-gcp-project
...

# Add via flags
gt outpost add cloudrun --name burst --project my-proj --region us-central1

# Remove outpost
gt outpost remove <name>

# Test connectivity
gt outpost ping <name>
```

## Files

- `cmd/gt/outpost.go` - Cobra command group
- `cmd/gt/outpost_list.go`
- `cmd/gt/outpost_status.go`
- `cmd/gt/outpost_add.go`

Depends on: gt-9a2.3 (config/manager)


Town-level beads: Real DB for coordination mail

Implement Option A from mail redesign: Town gets real beads DB for coordination.

## Background

Mail is now Beads. But currently:
- Town .beads/redirect points to rig beads
- mayor/mail/ has legacy JSONL files
- Cross-rig coordination has no clear home

## Design

Town beads = coordination, cross-rig mail, mayor inbox, handoffs
Rig beads = project issues, work items

Matches HOP hierarchy: platform > project > worker

## Structure

~/gt/
  .beads/           # REAL beads DB (prefix: gm-)
  mayor/
    town.json
    state.json      # NO mail/ directory
  gastown/
    .beads/         # Rig beads (prefix: ga-)

## Tasks

1. Delete ~/gt/.beads/redirect
2. Run bd init --prefix gm at ~/gt/ (town beads)
3. Delete ~/gt/mayor/mail/ directory
4. Update gt mail to use beads not JSONL
5. Add mail fields (thread_id, reply_to, msg_type)
6. Update gt prime for two-tier model
7. Update docs/architecture.md

## Addressing

- mayor/ -> town beads
- rig/agent -> rig beads
- Cross-rig -> town beads


EXTERNAL: Beads Messaging & Knowledge Graph (bd-kwro)

Tracking issue for external dependency on Beads v0.30.2 messaging features.

Beads epic: bd-kwro in ~/src/beads (steveyegge/beads repo)

This blocks GGT work that depends on:
- bd mail send/inbox/read/ack commands
- message issue type
- replies_to threading
- Hooks system for notifications
- Identity configuration

GGT mail commands will be thin wrappers around bd mail once available.

When bd-kwro ships in Beads v0.30.2, close this and unblock dependent work.


Error handling improvements: retry logic and recovery hints

Improve error handling across GGT codebase.

## Issues Found

### 1. Silent Failures in Refinery
manager.go ProcessMR():
- git pull errors ignored
- Continues processing even on partial failures

### 2. Missing Beads CLI
Many operations silently continue if `bd` not found:
```go
// Current
tasks, err := m.loadTasksFromBeads(epicID)
if err != nil {
    // Non-fatal - swarm can start without tasks
}

// Better
if err != nil {
    return nil, fmt.Errorf("beads required: %w", err)
}
```

### 3. No Retry Logic
Network/lock errors should retry:
```go
func withRetry(attempts int, delay time.Duration, fn func() error) error {
    for i := 0; i < attempts; i++ {
        if err := fn(); err == nil {
            return nil
        }
        time.Sleep(delay)
    }
    return fmt.Errorf("failed after %d attempts", attempts)
}
```

### 4. Recovery Hints
Error messages should include fix suggestions:
```go
// Current
return fmt.Errorf("polecat not found")

// Better
return fmt.Errorf("polecat '%s' not found. Use 'gt polecat list' to see available polecats", name)
```

## Domain Error Types
```go
type SessionError struct {
    Op      string
    Polecat string
    Err     error
}

func (e *SessionError) Error() string {
    return fmt.Sprintf("session %s for %s: %v", e.Op, e.Polecat, e.Err)
}
```

## Files to Review
- internal/refinery/manager.go
- internal/swarm/manager.go
- internal/session/manager.go
- All cmd/*.go files

## Acceptance Criteria
- [ ] No silent error swallowing
- [ ] Retry logic for transient failures
- [ ] Helpful error messages with hints
- [ ] Consistent error types per domain


Spawn --molecule integration

Implement gt spawn --molecule flag for molecule-based polecat workflows.

Usage: gt spawn --issue gt-xyz --molecule mol-gastown-polecat

Behavior:
1. Validate molecule exists and is well-formed
2. Create molecule instance (child beads) under the issue
3. Find first ready step(s) in the instance
4. Spawn polecat with first ready step as initial work

Implementation:
1. Add --molecule flag to spawn command
2. Call molecule.Instantiate()
3. Query ready steps from instance
4. Pass first ready step to polecat context

Files: internal/cmd/spawn.go

Acceptance:
- --molecule flag works
- Creates proper molecule instance
- Polecat starts on first ready step
- End-to-end test with actual polecat


Recreate beads rigs with fresh clones

## Problem

Beads rigs have schema mismatches (missing thread_id column, etc.) from development iteration.

## Tasks

1. Shut down any active polecats
2. Delete existing beads rigs: mayor/rig, refinery/rig, witness/rig, crew/*
3. Re-clone from beads repo
4. Run bd init in each new clone

## Rigs to recreate

- /Users/stevey/gt/beads/mayor/rig
- /Users/stevey/gt/beads/refinery/rig
- /Users/stevey/gt/beads/crew/* (if any)

## Source

Clone from beads repo (need to confirm remote URL)


Doctor check: Worker health and stuck detection

Detect and report stuck workers via gt doctor.

## Checks

### WorkerHealthCheck
- List all active workers (polecats with state=working)
- Check last activity timestamp for each
- Flag as potentially stuck if no progress for configurable threshold (default: 30 min)
- Check if Witness is running for the rig
- Verify Witness last heartbeat time

### Stuck Detection Criteria
- Polecat state=working but session not running
- Polecat state=working but output unchanged for threshold
- Witness not responding to health checks
- Multiple polecats in same rig all stuck

## Output

```
[WARN] Workers in rig 'wyvern' may be stuck:
  - Toast: working for 45m, no recent output
  - Capable: working for 52m, session not found
  - Witness: last heartbeat 20m ago

  Suggestions:
  - gt witness status wyvern
  - gt capture wyvern/Toast 50
  - gt stop --rig wyvern (kill all)
```

## Auto-Fix

Cannot auto-fix stuck workers (risk of data loss), but can:
- Restart Witness daemon if crashed
- Send warning mail to Mayor


mol-outpost-assign: Intelligent work routing to outposts

The federation design shows outposts.yaml with a static policy section. Simple preference ordering is fine as config, but intelligent work routing should be a molecule.

Current static approach:
```yaml
policy:
  default_preference: [local, gce-burst, cloudrun-burst]
```

Cases requiring cognition:
- "This is a long-running research task, route to VM not CloudRun"
- "This touches sensitive code, keep local"
- "These 5 issues are related, batch them to same outpost"
- "CloudRun cost is high today, prefer local even if slower"

## Molecule: outpost-assign
Intelligent work-to-outpost routing.

## Step: classify-work
Analyze the issue/work item:
- Expected duration (quick fix vs multi-hour)
- Resource requirements
- Sensitivity/security tier
- Related work (same epic?)

## Step: check-capacity
Query outpost status:
- Current load on each
- Cost accrued today
- Health status

## Step: select-outpost
Choose optimal outpost based on:
- Work classification
- Capacity/cost
- Policy constraints
Needs: classify-work, check-capacity

## Step: emit-assignment
Record decision in beads for audit.
Needs: select-outpost

## Notes
- This molecule is invoked by Mayor/Witness when spawning
- Simple cases can short-circuit to static policy
- Full analysis only for ambiguous cases


Direct landing integration: gt land uses MR or bypasses

Integrate direct landing with merge queue.

'gt land --direct' should:
1. Check if MR exists for the polecat's work
2. If MR exists: process it directly (skip queue)
3. If no MR: create temporary MR, process, close

Options:
- --direct: bypass queue (existing)
- --via-queue: ensure goes through queue (new)
- --force: skip safety checks (existing)

This ensures direct landing still creates proper records.

Reference: docs/merge-queue-design.md#direct-landing-bypass-queue


Design: Additional design issues (placeholder)

Placeholder for additional design issues the user wants to raise and work through. Convert to specific subtasks as issues are identified.


gt mq submit: creates task type instead of merge-request type

## Problem

`gt mq submit` creates an issue with `type: task` but should be `type: merge-request`.

## Evidence

```
$ bd show gt-n508
gt-n508: Merge: gt-70b3
Status: open
Priority: P1
Type: task      <-- WRONG, should be merge-request
...
Description:
type: merge-request   <-- Correct type is in description, not in actual type field
```

## Impact

`gt mq list` shows empty queue because it queries for `type: merge-request`

## Fix

`gt mq submit` should set `--type merge-request` when creating the bead.


Swarm learning: Witness needs automated context cycling

Furiosa hit 2% context during swarm work. Witness role needs automated detection of low context and should trigger session cycling before agents get stuck. Add to Witness responsibilities in prompts.md.


Tmux status line: rig color themes and worker identity display

## Summary

Customize tmux status line for Gas Town workers with:
1. Per-rig configurable color themes
2. Clear worker name and role visibility

## Current Problem

- Only mayor shows in status line (and truncated)
- Can't tell which rig/worker you're looking at
- All sessions look the same

## Proposed Design

### Per-rig colors
```yaml
# In rig config or beads
theme:
  primary: '#ff6600'    # Orange for gastown
  secondary: '#333333'
  accent: '#ffcc00'
```

### Status line format
```
[gastown/Rictus] polecat | gt-70b3 | branch: polecat/Rictus
[beads/emma] crew | working | branch: main
[mayor] coordinator | idle
```

### Components
- Rig name with rig color
- Worker name
- Role (polecat/crew/mayor/witness/refinery)
- Current issue or status
- Branch name

## Configuration

Could use pinned beads for this (see gm-w13, beads-6v2):
- `bd show <rig>-theme` returns theme config
- Stored as pinned bead, always available
- Part of 'config in beads data plane' initiative

Implementation complete. Core features: per-rig color themes, dynamic status line with issue/mail indicators, gt theme/issue commands. Ready for testing.


Mail CLI: archive, purge, search, mark

GGT mail CLI needs more commands for mail management.

## Commands to Add

### gt mail check
Check for new mail without full inbox display.
```
gt mail check [--quiet] [--inject]
```
- --quiet: Only output if new mail (for scripts)
- --inject: Send notification to running session

### gt mail mark
Change read status.
```
gt mail mark <id> --read
gt mail mark <id> --unread
```

### gt mail delete
Remove message from inbox.
```
gt mail delete <id> [--force]
```
- Confirm unless --force

### gt mail archive
Move old/read messages to archive.
```
gt mail archive [--older-than DAYS] [--all-read] [--dry-run]
```
- Creates inbox.jsonl.archive or separate archive.jsonl

### gt mail purge
Permanently delete archived messages.
```
gt mail purge [--older-than DAYS] [--dry-run] [--force]
```

### gt mail search
Find messages by content.
```
gt mail search <query> [--from SENDER] [--subject] [--body]
```

### gt mail reply
Reply to a message.
```
gt mail reply <id> -m BODY
```
- Auto-sets reply_to and thread_id
- Auto-addresses to original sender

## Files to Modify
- internal/cmd/mail.go: Add commands
- internal/mail/mailbox.go: Add Archive(), Search(), Delete()

## Acceptance Criteria
- [ ] All commands implemented
- [ ] Archive stores in separate file
- [ ] Search supports regex patterns
- [ ] Delete confirms by default


gt refinery start: doesn't actually start a session

## Problem

`gt refinery start gastown` reports success but doesn't start a tmux session.

## Evidence

```
$ gt refinery start gastown
Starting refinery for gastown...
✓ Refinery started for gastown

$ tmux list-sessions | grep refinery
(nothing)

$ gt refinery status gastown
State: ○ stopped
```

## Expected

Should start a tmux session (e.g., gt-gastown-refinery) with Claude processing the merge queue.

## Related

- gt-kcee: Witness commands also need implementation
- The refinery 'start' may just be updating state.json without spawning a session


Molecule Workflow Engine: Composable Crystallized Workflows

# Epic: Molecule Workflow Engine

**Vision**: Molecules are crystallized, composable, nondeterministic-idempotent workflow templates. Any worker can pick up where any other worker was interrupted and continue along the molecule.

**Christmas Target**: Full molecule-based workflow engine operational by Dec 25, 2025.

## The Core Concepts

1. **Molecule**: Read-only workflow template (beads issue with type=molecule)
2. **Atom/Step**: Individual work unit with prose instructions
3. **Bond**: Dependency between steps
4. **Polymer/Derived**: Molecule composed from other molecules
5. **Instance**: Concrete beads created when molecule is attached to work

## Key Features Needed

### 1. Molecule Composition (Includes Directive)
Molecules can include other molecules:
\`\`\`markdown
## Molecule: gastown-polecat
Includes: mol-engineer-in-box

## Step: install-binary
Build and install the local gt binary.
Needs: submit
\`\`\`

### 2. Standard Molecules
- mol-install-go-binary: Single step to build/install gt
- mol-gastown-polecat: engineer-in-box + install-binary

### 3. Spawn Integration
\`gt spawn --issue <id> --molecule <mol-id>\` creates molecule instance then starts polecat on first ready step.

### 4. Nondeterministic Idempotence
- Steps are atomic (pending → in_progress → completed)
- Any worker can pick up any ready step
- Step timeout/recovery for stuck workers

## Success Criteria
- [ ] Polecats can be spawned with mol-gastown-polecat
- [ ] Derived molecules work end-to-end
- [ ] 10+ polecat swarm completes molecule workflows
- [ ] install-go-binary step runs after successful merges


Rig management: discover, load, create rigs

Rig discovery, loading, and creation.

## Interface

```go
type Rig struct {
    Name      string
    Path      string
    GitURL    string
    Config    RigConfig
    Polecats  []string
    HasWitness bool
    HasRefinery bool
}

type RigManager struct {
    townRoot string
    config   *Config
    git      *Git
}

func NewRigManager(townRoot string, config *Config, git *Git) *RigManager

// Discovery
func (rm *RigManager) DiscoverRigs() ([]*Rig, error)
func (rm *RigManager) GetRig(name string) (*Rig, error)
func (rm *RigManager) RigExists(name string) bool

// Creation
func (rm *RigManager) AddRig(name, gitURL string) (*Rig, error)
func (rm *RigManager) RemoveRig(name string) error

// Rig state
func (rm *RigManager) LoadRigConfig(rig *Rig) error
func (rm *RigManager) SaveRigConfig(rig *Rig) error
```

## Discovery Logic

1. Read rigs.json from config/
2. For each registered rig, verify directory exists
3. Load rig-level config if present
4. Scan for polecats/ subdirectory
5. Check for witness/, refinery/ directories

## Rig Creation

1. Clone repo to town root
2. Add entry to rigs.json
3. Create agent directories (polecats/, witness/, refinery/, mayor/)
4. Update .git/info/exclude to ignore agent dirs
5. Initialize rig config

## Rig Structure

Per docs/architecture.md:
```
<rig>/
├── polecats/
├── refinery/rig/
├── witness/rig/
└── mayor/rig/
```


Add bulk session stop command (gt session stop --all)

When decommissioning a rig, need to stop multiple sessions one at a time. A --all or --rig flag would allow: gt session stop --rig gastown


Mail orchestrator daemon

Background mail orchestrator daemon.

## Command
```
gt mail orchestrate [--interval N] [--once] [--verbose]
```

## Purpose
Background process that:
1. Monitors outbox for pending mail
2. Delivers to recipient inboxes
3. Handles offline recipients (retry later)
4. Cleans delivered messages from outbox

## Why Needed?
Current mail is synchronous. If recipient is offline or mailbox locked, send fails.
Orchestrator enables async delivery with retry.

## Implementation
```go
func (o *Orchestrator) Run(interval time.Duration) error {
    ticker := time.NewTicker(interval)
    for range ticker.C {
        o.processOutbox()
    }
}

func (o *Orchestrator) processOutbox() {
    // List outbox/*.json
    // For each, attempt delivery
    // On success, delete from outbox
    // On failure, increment retry count
}
```

## Outbox Structure
```
<town>/mayor/mail/outbox/
├── msg-abc123.json
└── msg-def456.json
```

## Lower Priority
Current synchronous delivery works. Orchestrator is optimization.

## Acceptance Criteria
- [ ] Background daemon mode
- [ ] Retry failed deliveries
- [ ] --once for single pass
- [ ] Configurable interval


gt spawn: Enter key needs debounce delay after paste

## Problem

When spawning polecats via `gt spawn`, instructions are pasted into tmux but workers often sit idle at prompt because the Enter key arrives before the paste is fully processed.

## Observed Behavior

- Instructions pasted via tmux send-keys
- Enter key sent immediately after
- Worker session shows prompt with no input (paste not submitted)
- Requires manual intervention to nudge workers

## Expected Behavior

- Paste completes fully
- Enter key submits the pasted instructions
- Worker begins executing immediately

## Root Cause

Likely a race condition between paste buffer processing and keypress handling. Need either:
1. Debounce delay before sending Enter
2. Longer delay (Tmax) to ensure paste completes
3. Alternative submission mechanism

## Impact

- Swarm of 10 workers had multiple stalls at startup
- Required manual tmux send-keys to unstick them
- Defeats purpose of automated spawning

## References

- Observed during Mad Max swarm (Dec 18, 2025)
- Affects gt spawn command in internal/cmd/spawn.go


Witness pre-kill verification protocol

Add pre-kill verification protocol to Witness CLAUDE.md template.

## Protocol for Witness Prompting

```markdown
## Pre-Kill Verification Protocol

Before killing any worker session, verify workspace is clean.

### Verification Steps

When a worker signals done:

1. **Capture worker state**:
```bash
town capture <polecat> "git status && git stash list && bd sync --status"
```

2. **Assess the output** (use your judgment):
- Is working tree clean?
- Is stash list empty?
- Is beads synced?

3. **Decision**:
- **CLEAN**: Proceed to kill session
- **DIRTY**: Send nudge with specific issues

### Nudge Templates

**Uncommitted Changes**:
```
town inject <polecat> "WITNESS CHECK: Uncommitted changes found. Please commit or discard: <files>. Signal done when clean."
```

**Beads Not Synced**:
```
town inject <polecat> "WITNESS CHECK: Beads not synced. Run 'bd sync' then commit. Signal done when complete."
```

### Kill Sequence

Only after verification passes:
```bash
town kill <polecat>
town sleep <polecat>
```

### Escalation

If worker fails verification 3+ times:
```bash
town mail send mayor/ -s "Escalation: <polecat> stuck" -m "Cannot complete cleanup after 3 attempts. Issues: <list>."
```
```

## Implementation

Add to WITNESS_CLAUDE.md template.


CLI: names commands for polecat naming pool

Polecat naming pool for auto-generated names.

## Commands

### gt names generate
```
gt names generate [--count N]
```
Generate N names from pool.

### gt names add
```
gt names add <name>...
```
Add custom names to pool.

### gt names list
```
gt names list
```
Show available and used names.

### gt names reset
```
gt names reset [--keep-used]
```
Reset pool to defaults.

## Config File
<rig>/town/naming.json:
```json
{
  "enabled": true,
  "auto_refill": true,
  "refill_threshold": 5,
  "pool": {
    "available": ["Toast", "Nux", "Capable", ...],
    "used": ["Alice", "Bob"]
  }
}
```

## Default Pool
Mad Max themed: Toast, Nux, Capable, Furiosa, Immortan, etc.

## Integration
gt polecat add calls naming pool if no name given:
```go
if name == "" {
    name, err = naming.Generate(rigPath)
}
```

## New Package
internal/naming/
├── pool.go     # Pool management
└── defaults.go # Default name lists

## Acceptance Criteria
- [ ] Auto-generate names on polecat add
- [ ] Track used vs available
- [ ] Auto-refill when low
- [ ] Custom names addable


gt spawn --create should auto-add polecat if missing

## Problem

`gt spawn gastown --issue gt-xxx --create` fails if no polecats exist.

## Current Behavior

```
Error: auto-select polecat: no available polecats in rig 'gastown'
```

## Expected Behavior

`--create` should:
1. Create a new polecat if none exist
2. Or create the specified polecat if `gt spawn gastown/NewName --create`

## Workaround

Must manually run `gt polecat add gastown Name` first.

## Principle

Agent UX should be forgiving - if I say spawn with --create, make it work.


Remove stale migration docs from gastown-py

The gastown-py repo has migration-related documentation that is now misinformation since we have made design decisions. Remove or clearly mark as obsolete: any docs about migration paths, old architecture assumptions, or superseded designs.


Resource Beads: Leases, locks, and quotas

Resource beads represent reserved resources. Types: vm, lock, slot, quota. Fields: holder, expires, renewable. Daemon monitors for expiry and manages contention.


Bug: parseLifecycleRequest always matches 'cycle' due to LIFECYCLE prefix

In lifecycle.go, parseLifecycleRequest checks strings.Contains(title, "cycle") first, but the prefix "LIFECYCLE:" contains the word "cycle". This means ALL lifecycle messages match the cycle action, making restart and shutdown unreachable. Fix: check for restart/shutdown before cycle, or use word boundaries.


Update harness beads redirect for GGT

Change ~/ai/.beads/redirect from mayor/rigs/gastown/.beads to gastown/mayor/.beads for the GGT directory structure


bd create should support molecule type

gt molecule commands expect type=molecule but bd validates against bug|feature|task|epic|chore|merge-request only


Merge: gt-5af.2

branch: polecat/Doof
target: main
source_issue: gt-5af.2
rig: gastown


Test: release command verification


CloudRun protocol types: WorkRequest, WorkEvent, WorkResult

## Overview

HTTP protocol types for Cloud Run work dispatch. **Types only** - server and client are separate tasks.

## Request Type

```go
type WorkRequest struct {
    IssueID string          `json:"issue_id"`
    Rig     RigConfig       `json:"rig"`
    Beads   BeadsConfig     `json:"beads"`
    Branch  string          `json:"worker_branch"`
    Context map[string]any  `json:"context,omitempty"`
}

type RigConfig struct {
    URL    string `json:"url"`
    Branch string `json:"branch"`
}

type BeadsConfig struct {
    URL    string `json:"url"`
    Branch string `json:"branch"`
}
```

## Response Types (streaming NDJSON)

```go
type WorkEvent struct {
    Type     string `json:"type"`  // status, log, progress, result, error
    Status   string `json:"status,omitempty"`
    Line     string `json:"line,omitempty"`
    Percent  int    `json:"percent,omitempty"`
    Branch   string `json:"branch,omitempty"`
    PRURL    string `json:"pr_url,omitempty"`
    Code     string `json:"code,omitempty"`
    Message  string `json:"message,omitempty"`
}

type WorkResult struct {
    Status  string `json:"status"`  // done, failed
    Branch  string `json:"branch"`
    PRURL   string `json:"pr_url,omitempty"`
    Error   string `json:"error,omitempty"`
}
```

## Files

- `internal/cloudrun/protocol.go` - All types above

## Notes

This is just types. Server (gt-9a2.7b) and client (gt-9a2.7c) are separate tasks.
Small, focused task - can complete quickly.


tmux notifications: display-message too subtle, use send-keys instead

## Problem

`tmux display-message` notifications are not visible enough - they appear briefly in the status bar and are easy to miss.

## Current Behavior

router.go uses:
```go
r.tmux.DisplayMessageDefault(sessionID, notification)
```

## What Works

Sending echo commands directly to the terminal:
```bash
tmux send-keys -t <session> "echo '📬 NEW MAIL from mayor'" Enter
```

## Proposed Fix

Change notification method to send visible output to the terminal, perhaps with a box/banner:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📬 NEW MAIL from mayor
Subject: <subject>
Run: bd mail inbox
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

## Considerations

- This interrupts the terminal output (acceptable for important mail)
- Could check if Claude is mid-response and queue notification
- Or use tmux popup if available

Additional issues:
1. Enter key not sent properly when chained with send-keys
2. Need to debounce and send Enter separately
3. Correct pattern: send text, sleep briefly, then send Enter


Add unit tests for daemon package

The daemon package has no unit tests. Need tests for:
- Config and state serialization
- Session name pattern matching (isWitnessSession)
- Lifecycle request parsing
- Identity to session mapping


MQ dashboard widget: queue status in TUI

Add merge queue status widget to the Bubbletea TUI dashboard.

Display:
┌─────────────────────────────────────────────────┐
│              MERGE QUEUE STATUS                  │
├─────────────────────────────────────────────────┤
│ Pending: 3   In Progress: 1   Merged (24h): 12  │
├─────────────────────────────────────────────────┤
│ QUEUE:                                          │
│   ► gt-mr-004  P0  Nux/gt-xyz     Processing... │
│     gt-mr-005  P1  Toast/gt-abc   Ready         │
│     gt-mr-006  P1  Capable/gt-def Blocked (005) │
└─────────────────────────────────────────────────┘

Interactive features:
- Navigate queue entries
- View MR details
- Retry/reject from dashboard

Depends on: gt-u1j.2 (Bubbletea TUI dashboard)

Reference: docs/merge-queue-design.md#dashboard


Architecture: beads config and direct landing docs

Added to architecture.md:
- Beads multi-agent configuration table (daemon, worktree, sync-branch)
- ASCII directory layout for non-mermaid rendering
- Direct landing workflow (bypass merge queue)
- Design decisions 9 and 10 for direct landing and daemon awareness
- CLI commands for gt land


Consolidate design docs into beads descriptions

The markdown design docs (swarm-shutdown-design.md, polecat-beads-access-design.md, mayor-handoff-design.md) will decay. Extract key decisions and prompting templates into the beads descriptions themselves, then archive or remove the markdown files. Beads are the source of truth.


Implement gt harness create command

Add scaffolding command to create a new harness:
- gt harness create [path]
- Creates config/, mayor/, .beads/redirect (optional)
- Optionally initializes git
- Generates CLAUDE.md with Mayor role
- Could also offer a template repo alternative


bd mail send: pinned column missing from schema

gt mail send fails with: sqlite3: SQL logic error: table issues has no column named pinned

Likely a schema migration issue - bd schema has 'pinned' field but SQLite table doesn't.

## Reproduction
gt mail send gastown/Immortan -s 'test' -m 'test'

## Error
table issues has no column named pinned

## Fix
Either:
1. Add migration to add pinned column
2. Remove pinned from insert if not present
3. Regenerate DB from JSONL


Bubbletea TUI dashboard

Interactive TUI using Charm Bubbletea. Root model, view routing, Lipgloss theme. Includes: dashboard, rig explorer, polecat detail with live tail, mail view, beads view, spawn wizard.


Daemon: configurable heartbeat interval

Heartbeat interval is hardcoded to 60s. Should be configurable via:
- town.json config
- Command line flag
- Environment variable

Default 60s is reasonable but some deployments may want faster/slower.


Plugin surface for daemon lifecycle hooks

Allow rigs to customize daemon behavior via hooks/plugins.

## Hook Points

- on_heartbeat: Called during daemon heartbeat cycle
- on_worker_idle: Called when worker goes idle
- on_worker_stuck: Called when stuck detection triggers
- on_lifecycle_request: Called before processing lifecycle

## Configuration

```
<rig>/config/daemon-hooks.json
{
  "on_heartbeat": "./hooks/check-ci-status.sh",
  "on_worker_idle": "./hooks/maybe-assign-work.sh",
  "idle_threshold": "5m",
  "custom_signals": [
    {"name": "ci_status", "command": "./hooks/get-ci.sh"}
  ]
}
```

## Signal Contribution

Hooks can return signals that feed into decision engine:
- SKIP_POKE: Don't poke this agent
- FORCE_POKE: Override backoff, poke now
- CUSTOM_DATA: Extra context for logging

## Use Cases

- Check CI status before interrupting test runs
- Auto-assign work when worker becomes idle
- Custom stuck detection for specific workflows


Merge: gt-70b3

type: merge-request
branch: polecat/Rictus
target: main
source_issue: gt-70b3
rig: gastown


Merge: gt-h5n.5

branch: polecat/Scabrous
target: main
source_issue: gt-h5n.5
rig: gastown


CLI: gt stop --all (emergency swarm kill)

Emergency kill command for swarm operations.

## Commands

```bash
gt stop --all              # Kill ALL sessions across all rigs
gt stop --rig <name>       # Kill all sessions in one rig
gt stop --rig <name> --graceful  # Try graceful first, then force
```

## Implementation

### --all flag
1. List all tmux sessions matching gt-*
2. For each: capture final output, kill session
3. Update all polecat states to idle
4. Log all killed sessions
5. Send mail to Mayor: "Emergency stop executed"

### --rig flag
1. List sessions for specific rig
2. Same as above but scoped

### --graceful flag
1. First, try to inject "exit" command
2. Wait 5 seconds
3. If still running, force kill

## Output

```
Stopping all Gas Town sessions...
  [wyvern] Toast: captured output, killed
  [wyvern] Capable: captured output, killed
  [beads] Nux: captured output, killed

3 sessions stopped.
All polecat states set to 'idle'.
Mail sent to mayor/.
```

## Safety

- Always capture output before killing
- Update state files to reflect reality
- Log all operations to gt.log
- Warn if uncommitted changes detected (but still kill)

## Dependencies

Needs: Tmux wrapper, Session management, Polecat management


Merge: gt-g44u.1

branch: polecat/Ace
target: main
source_issue: gt-g44u.1
rig: gastown


Test message

Testing GGT mail system


MR: gt-test (main)

branch: main
target: main
source_issue: gt-test


Crew workers getting wrong CLAUDE.md (shows Refinery)

## Problem

Crew workers (emma, dave) have CLAUDE.md that says they're the Refinery.

## Evidence

```
$ head -5 /Users/stevey/gt/beads/crew/emma/CLAUDE.md
# Claude: Beads Refinery
You are the **Refinery** for the **beads** rig...
```

## Expected

Should use crew.md.tmpl which correctly says:
```
# Crew Worker Context
You are a **crew worker** - the overseer's (human's) personal workspace...
```

## Impact

- Crew workers have wrong identity in static context
- `gt prime` correctly outputs crew context, but CLAUDE.md conflicts
- Confusing role information

## Fix

Check `gt crew create` or whatever populates CLAUDE.md for crew workers.


gt crew refresh: Context cycling with handoff

Implement 'gt crew refresh <name>' command:
- Send handoff mail to self (context summary)
- Kill current session cleanly
- Start new session
- New session reads handoff mail and resumes
- Support --message to add custom handoff notes


Shutdown request handler

Handle polecat shutdown requests:

When polecat runs 'gt handoff --shutdown':
1. Polecat sends mail to <rig>/witness requesting shutdown
2. Witness receives mail
3. Witness verifies:
   - Git working tree is clean
   - Work is submitted (MR exists or in queue)
   - No uncommitted beads changes
4. If clean:
   - gt session stop <rig>/<polecat>
   - git worktree remove polecats/<name>
   - git branch -d polecat/<name>
5. If not clean:
   - Send nudge back to polecat
   - Track retry count

Depends on fixing gt-dsfi (handoff deadlock).