Design: Failure modes and recovery

Document failure modes and recovery strategies for Gas Town operations.

## Critical Failure Modes

### 1. Agent Crash Mid-Operation

**Scenario**: Polecat crashes while committing, Witness crashes while verifying

**Detection**:
- Session suddenly gone (tmux check fails)
- State shows 'working' but no session
- Heartbeat stops (for Witness)

**Recovery**:
- Doctor detects via ZombieSessionCheck
- Capture any recoverable state
- Reset agent state to 'idle'
- For Witness: auto-restart via supervisor or manual gt witness start

### 2. Git State Corruption

**Scenario**: Merge conflict, failed rebase, detached HEAD

**Detection**:
- Git commands fail
- Dirty state that won't commit
- Branch diverged from origin

**Recovery**:
- gt doctor reports git health issues
- Manual intervention recommended
- Severe cases: remove clone, re-clone

### 3. Beads Sync Conflict

**Scenario**: Two polecats modify same issue

**Detection**:
- bd sync fails with conflict
- Beads tombstone mechanism handles most cases

**Recovery**:
- Beads has last-write-wins semantics
- bd sync --force in extreme cases
- Issues may need manual dedup

### 4. Tmux Failure

**Scenario**: Tmux server crashes, socket issues

**Detection**:
- All sessions inaccessible
- "no server running" errors

**Recovery**:
- Kill any orphan processes
- tmux kill-server && tmux start-server
- All agent states reset to idle
- Re-spawn active work

### 5. Claude API Issues

**Scenario**: Rate limits, outages, context limits

**Detection**:
- Sessions hang or produce errors
- Repeated failure patterns

**Recovery**:
- Exponential backoff (handled by Claude Code)
- For context limits: session cycling (mail-to-self)
- For outages: wait and retry

### 6. Disk Full

**Scenario**: Clones, logs, or beads fill disk

**Detection**:
- Write operations fail
- git/bd commands error

**Recovery**:
- Clean up logs: rm ~/.gastown/logs/*
- Remove old polecat clones
- gt doctor --fix can clean some cruft

### 7. Network Failure

**Scenario**: Can't reach GitHub, API servers

**Detection**:
- git fetch/push fails
- Claude sessions hang

**Recovery**:
- Work continues locally
- Queue pushes for later
- Sync when connectivity restored

## Recovery Principles

1. **Fail safe**: Prefer stopping over corrupting
2. **State is recoverable**: Git and beads have recovery mechanisms
3. **Doctor heals**: gt doctor --fix handles common issues
4. **Emergency stop**: gt stop --all as last resort
5. **Human escalation**: Some failures need Overseer intervention

## Implementation

- Document each failure mode in architecture.md
- Ensure doctor checks cover detection
- Add recovery hints to error messages
- Log all failures for debugging


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.


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.


Phase 4.1: mol-deacon-patrol uses ephemeral

Deacon patrol cycles run as ephemeral molecules.

## Current Deacon Loop

```
while running:
    check_heartbeat()
    check_mail()
    sleep(interval)
```

## Molecule-Based Loop

```
while running:
    mol = bd mol bond mol-deacon-patrol-cycle --ephemeral
    execute_cycle(mol):
        check_heartbeat()
        check_mail()
        log_status()
    summary = generate_cycle_summary()
    bd squash mol --summary summary
    sleep(interval)
```

## Benefits

- Each cycle is tracked
- Digests show daemon health over time
- Can query: 'show me patrol cycles from last hour'
- Crash mid-cycle → ephemeral shows where we were


Pinned Beads: Ongoing concerns and anchors

Pinned beads represent persistent concerns that do not close traditionally. Stay out of bd ready. Examples: Monitor production, Weekly syncs. Implementation: pinned: true field on bead.


Batch work landing report generation

Generate reports on batch work completion.

## Trigger

When all children of an epic are closed, generate a completion report.

## Report Contents

- Epic metadata (ID, title, created by, duration)
- Task summary (completed counts, timing)
- Per-task details (issue, assignee, time taken)
- Git stats (commits merged, lines changed)
- Cleanup stats (branches deleted)

## Output Locations

1. Mail to Mayor - summary report
2. Optional: save to file with --save flag

## Command

```bash
gt report --epic <epic-id> [--save report.md]
```

## Format

Markdown with sections:
- Summary
- Tasks
- Workers
- Timeline

## Note

This replaces the swarm-based reporting (gt-662). Reports are generated from epic/issue data, not from separate swarm state files.

## Reference

PGT: swarm/report.py (for format ideas, ignore swarm ID patterns)


load-context

Run gt prime and bd prime. Verify issue assignment.
Check inbox for any relevant messages.

Read the assigned issue (gt-test123) and understand the requirements.
Identify any blockers or missing information.


Test from Mayor

This is a test message via GGT mail


Merge: gt-pyqv

branch: polecat/dementus
target: main
source_issue: gt-pyqv
rig: gastown


Agent monitoring and status inference

Agent monitoring with status inference from activity, like PGT.

## Agent Status Enum
```go
type AgentStatus string
const (
    StatusAvailable AgentStatus = "available"
    StatusWorking   AgentStatus = "working"
    StatusThinking  AgentStatus = "thinking"
    StatusBlocked   AgentStatus = "blocked"
    StatusWaiting   AgentStatus = "waiting"
    StatusReviewing AgentStatus = "reviewing"
    StatusIdle      AgentStatus = "idle"
    StatusPaused    AgentStatus = "paused"
    StatusError     AgentStatus = "error"
    StatusOffline   AgentStatus = "offline"
)
```

## Status Sources (priority order)
```go
type StatusSource string
const (
    SourceBossOverride StatusSource = "boss"      // Witness/Mayor sets
    SourceSelfReported StatusSource = "self"      // Agent reports own status
    SourceInferred     StatusSource = "inferred"  // Detected from activity
)
```

## Activity Detection

### Pattern Registry
```go
var activityPatterns = []struct {
    Pattern string
    Status  AgentStatus
}{
    {"Thinking...", StatusThinking},
    {"BLOCKED:", StatusBlocked},
    {"Error:", StatusError},
    // etc
}
```

### Idle Detection
No pane output for N seconds → StatusIdle

### Resource Monitoring (optional)
CPU/memory via os.Process

## New Package
internal/monitoring/
├── types.go      # AgentStatus, StatusReport
├── detector.go   # PatternRegistry, detect from output
├── tracker.go    # Per-agent status tracking
└── idle.go       # Idle timeout detection

## Integration
- Session capture output → monitoring detector
- Status shown in gt status, gt session list

## PGT Reference
gastown-py/src/gastown/monitoring/

## Acceptance Criteria
- [ ] Status enum with 10 states
- [ ] Pattern-based detection from pane output
- [ ] Idle detection with configurable timeout
- [ ] Status visible in CLI output


Test molecule

Test


Phase 5.2: Ephemeral error handling and recovery

Handle edge cases in ephemeral molecule lifecycle.

## Scenarios

### Agent dies mid-molecule
- Ephemeral state persists
- Witness detects stall (no heartbeat)
- Options:
  a. Nudge agent to resume
  b. Reassign to new agent
  c. Escalate to Mayor

### Squash fails
- Retry with exponential backoff
- If persistent failure, escalate
- Don't lose the ephemeral state until squash succeeds

### Orphaned molecules
- Molecule started, never completed
- gt doctor detects (>24h old with no activity)
- Options:
  a. Manual review
  b. Auto-abandon with 'abandoned' digest
  c. Reassign

### Ephemeral repo corruption
- Re-init from scratch
- Active molecules are lost
- Main beads is source of truth for assigned work

## Implementation

Add error handling to:
- bd mol bond (handle init failures)
- bd squash (retry logic)
- gt doctor (recovery suggestions)


self-review

Review your own changes. Look for:
- Bugs and edge cases
- Style issues
- Missing error handling
- Security concerns

Fix any issues found before proceeding.

Depends: implement


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/
```


bd create should support molecule type

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


Clarify Mayor vs Witness cleanup responsibilities

Document the cleanup authority model: Witness owns ALL per-worker cleanup, Mayor never involved.

## The Rule

**Witness handles ALL per-worker cleanup. Mayor is never involved.**

## Why This Matters

1. Separation of concerns: Mayor strategic, Witness operational
2. Reduced coordination overhead: No back-and-forth for routine cleanup
3. Faster shutdown: Witness kills workers immediately upon verification
4. Cleaner escalation: Mayor only hears about problems

## What Witness Handles

- Verifying worker git state before kill
- Nudging workers to fix dirty state
- Killing worker sessions
- Updating worker state (sleep/wake)
- Logging verification results

## What Mayor Handles

- Receiving swarm complete notifications
- Deciding whether to start new swarms
- Handling escalations (stuck workers after 3 retries)
- Cross-rig coordination

## Escalation Path

Worker stuck -> Witness nudges (up to 3x) -> Witness escalates to Mayor -> Mayor decides: force kill, reassign, or human

## Anti-Patterns

DO NOT: Mayor asks Witness if worker X is clean
DO: Witness reports swarm complete, all workers verified

DO NOT: Mayor kills worker sessions directly
DO: Mayor tells Witness to abort swarm, Witness handles cleanup

DO NOT: Workers report done to Mayor
DO: Workers report to Witness, Witness aggregates and reports up


Merge: gt-3x1.4

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


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.


Test infrastructure: fixtures, mocks, integration tests

Test infrastructure for Gas Town.

## Test Categories

### Unit Tests
- Config parsing
- Mail operations
- Merge queue operations
- Template rendering

### Integration Tests
- Git operations (uses real git in temp dirs)
- Full workflows (spawn → work → merge)
- CLI command tests

## Fixtures

```go
package testutil

// Creates temp town with minimal structure
func NewTestTown(t *testing.T) *TestTown

// Creates temp rig with git repo
func NewTestRig(t *testing.T, town *TestTown, name string) *TestRig

// Creates test polecat
func NewTestPolecat(t *testing.T, rig *TestRig, name string) *TestPolecat
```

## Mocks

```go
// Mock tmux for session tests (avoid actual tmux)
type MockTmux struct {
    Sessions map[string][]string  // captured send-keys
}

func (m *MockTmux) NewSession(name, dir string) error
func (m *MockTmux) SendKeys(session, keys string) error
func (m *MockTmux) CapturePane(session string, lines int) (string, error)
```

## Test Helpers

```go
// Run test in temp directory
func InTempDir(t *testing.T, fn func(dir string))

// Initialize git repo for testing
func InitGitRepo(t *testing.T, dir string)

// Assert file exists with content
func AssertFileContains(t *testing.T, path, substr string)

// Assert JSON file has field
func AssertJSONField(t *testing.T, path, field string, expected any)
```

## CI Integration

- go test ./... runs all tests
- Integration tests skip with -short flag
- Coverage report generation

## Test Data

Embedded test fixtures:
```go
//go:embed testdata/*
var testdataFS embed.FS
```


Merge: gt-a95

branch: polecat/Ace
target: main
source_issue: gt-a95
rig: gastown


request-shutdown

Send shutdown request to Witness.
Wait for termination.

The polecat is now ready to be cleaned up.
Do not exit directly - wait for Witness to kill the session.

Depends: generate-summary


gt swarm start: Does not spawn polecat sessions

gt swarm start marks swarm as 'active' but doesn't start any polecat sessions.

Repro:
1. gt swarm create gastown --epic gt-hw6 --worker Toast --worker Nux
2. gt swarm start gt-hw6
3. gt session list - shows no new sessions

Expected: Polecat sessions should start for each worker.
Actual: No sessions started, workers sit idle.


Engineer main loop: poll for ready merge-requests

Implement the Engineer's main processing loop.

Loop structure:
1. Query: bd ready --type=merge-request
2. If empty: sleep(poll_interval), continue
3. Select highest priority, oldest MR
4. Claim: bd update <id> --status=in_progress
5. Process (delegate to other subtasks)
6. Repeat

Configuration:
- poll_interval: from rig config (default 30s)
- max_concurrent: from rig config (default 1)

The loop should be interruptible and handle graceful shutdown.

Reference: docs/merge-queue-design.md#engineer-processing-loop


Create rig .runtime/ for rig runtime state

Move ephemeral rig state to .runtime/:
- Create <rig>/.runtime/ directory
- Move .gastown/witness.json → .runtime/witness.json
- Move .gastown/refinery.json → .runtime/refinery.json
- Move .gastown/namepool.json (state portion) → .runtime/namepool-state.json
- Update .gitignore to ignore .runtime/
- Update all code references


Crew directory structure and config

Add crew/ directory support to rig structure. Include:
- crew/ as peer to polecats/, refinery/, witness/
- Crew worker subdirectories with full git clones
- Optional BEADS_DIR configuration for beads integration
- Crew state tracking (separate from polecat state)


Swarm package: Swarm, SwarmTask, SwarmState types

Define core swarm types.

## Types (in `internal/swarm/`)

```go
// SwarmState represents swarm lifecycle
type SwarmState string
const (
    SwarmCreated   SwarmState = "created"
    SwarmActive    SwarmState = "active"
    SwarmMerging   SwarmState = "merging"
    SwarmLanded    SwarmState = "landed"
    SwarmFailed    SwarmState = "failed"
    SwarmCancelled SwarmState = "cancelled"
)

// Swarm references a beads epic that tracks swarm work
type Swarm struct {
    ID          string      // matches beads epic ID
    RigName     string
    EpicID      string      // beads epic tracking this swarm
    BaseCommit  string      // git SHA all workers branch from
    Integration string      // integration branch name
    State       SwarmState
    CreatedAt   time.Time
    Workers     []string    // polecat names assigned
}

// SwarmTask represents a single task in the swarm (maps to beads issue)
type SwarmTask struct {
    IssueID   string  // beads issue ID
    Assignee  string  // polecat name
    Branch    string  // worker branch name
    State     string  // mirrors beads status
}
```

## Note

Swarm state is primarily stored IN beads. These types are in-memory representations for the SwarmManager to work with. No separate manifest.json files.


Session management: start, stop, attach, capture

Polecat session lifecycle management.

## Interface

```go
type SessionManager struct {
    tmux *Tmux
    rig  *Rig
}

func NewSessionManager(tmux *Tmux, rig *Rig) *SessionManager

// Lifecycle
func (sm *SessionManager) Start(polecat string, opts StartOptions) error
func (sm *SessionManager) Stop(polecat string) error
func (sm *SessionManager) IsRunning(polecat string) (bool, error)
func (sm *SessionManager) List() ([]SessionInfo, error)

// Interaction
func (sm *SessionManager) Attach(polecat string) error
func (sm *SessionManager) Capture(polecat string, lines int) (string, error)
func (sm *SessionManager) Inject(polecat string, message string) error

type StartOptions struct {
    WorkDir   string   // defaults to polecat clone dir
    Issue     string   // optional: issue to work on
    Command   string   // optional: override claude command
}

type SessionInfo struct {
    Polecat   string
    SessionID string
    Running   bool
    StartedAt time.Time
}
```

## Session Naming

`gt-<rig>-<polecat>` (e.g., `gt-wyvern-Toast`)

## Start Flow

1. Verify polecat clone exists
2. Check no existing session
3. Create tmux session in polecat workdir
4. Send initial command: `claude` or custom
5. If issue provided, inject initial prompt

## Stop Flow

1. Capture final output (for logging)
2. Send exit/quit command
3. Wait briefly for graceful shutdown
4. Kill session if still running

## Capture

Uses tmux capture-pane to get recent output. Returns last N lines.

## Inject

Sends text to session via tmux send-keys. Used for:
- Mail notifications
- Witness nudges
- User messages


Human escalation: notify overseer when self-heal fails

Lightweight escalation extracted from Deacon epic (gt-5af).

**Implementation**: Config in town.json or similar:
```yaml
escalation:
  contact: steve@example.com  # or slack webhook
  triggers:
    - daemon_cant_restart
    - session_missing_5min
```

**Trigger points**:
- Go daemon can't restart a session after N attempts
- Agent detects it's stuck and can't recover
- Witness can't reach polecat

**Mechanism**:
- Simple: `gt mail send --human` already exists
- Enhanced: email/slack via external script

**Weight**: Small config + one code path in daemon
**Value**: High for unattended operation - human gets notified instead of silent failure


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.


Track merge requests as beads (type=merge-request)

Currently MRs are tracked in .gastown/refinery.json. For HOP audit trail and entity CV tracking, merge requests should be Beads entries with type=merge-request. This enables:
- Full audit trail of what was merged, when, by whom
- Entity chain contributions (who validated what)
- Cross-rig visibility of merge activity

The refinery would create an MR bead when work enters the queue, update status as it progresses (open → in_progress → merged/rejected).

Part of Beads-as-data-plane vision from HOP.


Heartbeat convention: simple liveness signal for agents

Lightweight liveness signal extracted from Deacon epic (gt-5af).

**Implementation**: Each agent writes a timestamp file on activity:
```bash
echo '{"ts":"'$(date -Iseconds)'"}' > ~/gt/<role>/heartbeat.json
```

**Integration points**:
- SessionStart hook writes heartbeat
- Periodic activity (mail check, work completion) refreshes it
- `gt status` shows staleness (e.g., 'mayor: 5m ago')

**Weight**: ~5 lines per agent
**Value**: Quick debugging - see which agents are active at a glance

No monitoring daemon needed - human checks `gt status` when curious.


Phase 1.2: Daemon attachment detection

Daemon polls Deacon's pinned bead to detect naked state.

## Implementation
- Daemon knows Deacon's pinned bead ID
- Every heartbeat cycle: bd show <pinned> --json
- Parse attached_molecule field
- If null/empty: Deacon is naked

## Actions on Naked
- Spawn mol-deacon-patrol
- Attach to Deacon's pinned
- Nudge Deacon to start

## Failsafes
- Keepalive file monitoring
- Stale detection (>10min no progress)
- Force nudge on stale

Depends: gt-rana.1


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


gt mail reply/thread: Conversation support

Add mail conversation features:

- gt mail reply <id> -m 'message' - Reply to a message
- gt mail thread <id> - Show all messages in a thread

Enables back-and-forth communication between agents.


Swarm: report generation

Generate markdown reports for completed swarms.

## Command
```
gt swarm report <swarm-id> [--save <file>]
```

## Report Content

### Header
- Swarm ID and title
- Created/completed timestamps
- Duration
- Rig name

### Task Summary
| Task | Assignee | Status | Duration |
|------|----------|--------|----------|
| gt-xxx | Toast | merged | 15m |
| gt-yyy | Nux | merged | 22m |

### Worker Contributions
- Commits per worker
- Issues closed per worker
- Lines changed (optional)

### Timeline
- Chronological events from events.jsonl
- Key milestones (started, first merge, landing)

### Issues Encountered
- Conflicts resolved
- Failed tasks (if any)
- Escalations

## Implementation
```go
func GenerateReport(swarmID string) (*SwarmReport, error)
func (r *SwarmReport) ToMarkdown() string
```

## Storage
- Save to <rig>/.gastown/swarms/<id>/report.md
- Or user-specified path with --save

## PGT Reference
gastown-py/src/gastown/swarm/manager.py generate_report()

## Acceptance Criteria
- [ ] Markdown report generated
- [ ] Includes all sections above
- [ ] Auto-saved to swarm directory
- [ ] --save allows custom path


CLI: gt swarm commands (create, status, list, land)

CLI commands for swarm management.

## Commands

### gt swarm create
Create a new swarm in a rig.

```
gt swarm create <rig> --title "Fix all P1 bugs" \
  --task gt-abc --task gt-def \
  --worker Toast --worker Nux
```

Options:
- --title: Swarm title (required)
- --task: Beads issue IDs to include (repeatable)
- --worker: Polecat names (repeatable, or --workers=3 to auto-assign)
- --start: Start swarm immediately after creation

### gt swarm status <swarm-id>
Show swarm status and progress.

Output:
- Swarm metadata (title, created, workers)
- Task status (pending/in_progress/completed/merged)
- Integration branch status
- Overall progress percentage

### gt swarm list [rig]
List swarms, optionally filtered by rig or status.

```
gt swarm list --status=active
gt swarm list gastown --status=landed
```

### gt swarm land <swarm-id>
Trigger manual landing (normally automatic).

### gt swarm cancel <swarm-id>
Cancel a swarm in progress.

## Output Format

Support --json for machine-readable output.

## Dependencies

Needs: gt-kmn.1 (types), gt-kmn.2 (manager)


mol-polecat-work

Full polecat lifecycle from assignment to decommission.

This proto enables nondeterministic idempotence for polecat work.
A polecat that crashes after any step can restart, read its molecule state,
and continue from the last completed step. No work is lost.

Variables:
- gt-test123 - The source issue ID being worked on


Pinned beads should appear first in mail inbox

Currently bd mail inbox sorts by priority then date, but pinned beads (handoff context) should always appear at the top.

**Current behavior:**
- Pinned beads mixed in with regular mail based on priority/date

**Expected behavior:**
- Pinned beads always first in inbox (before priority sorting)
- This enables handoff beads to be the default first item an agent sees

**Implementation:**
1. In mail.go runMailInbox(), after filtering, sort pinned beads to top
2. Within pinned and non-pinned groups, maintain priority/date sort

**Related:**
- gt-r8ej: Implement pinned beads for handoff state
- gt-8h4: Pinned Beads epic

**Acceptance criteria:**
- bd mail inbox shows pinned beads first
- Pinned beads still sorted by priority/date among themselves
- Non-pinned mail sorted normally after pinned section


Merge: gt-rixa

branch: polecat/furiosa
target: main
source_issue: gt-rixa
rig: gastown


Merge: gt-g44u.2

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


Merge: gt-rana.3

branch: polecat/dementus
target: main
source_issue: gt-rana.3
rig: gastown


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


Update gt doctor to check new config locations

Doctor should validate new structure:
- Check mayor/config.json exists and is valid
- Check settings/ exists for each rig
- Warn if old .gastown/ files still exist
- Offer --fix to migrate old locations to new


Digest: mol-deacon-patrol

Test patrol cycle - verifying wisp flow


Deacon event/timer callbacks: reactive triggers

Implement event-triggered and timer-based callbacks in the Deacon.

## Timer Callbacks

Agents can schedule future wakes by mailing the Deacon:
```
Subject: TIMER: gastown/witness wake at 2025-12-20T16:00:00Z
Body: Please nudge me - I'm waiting for external dependency
```

Deacon processing:
1. Parse timer mail
2. Check if time has passed
3. If yes: mail the agent "WAKE: Timer fired"
4. Close the timer mail

## Event Subscriptions

Plugins/agents can subscribe to events:
```yaml
# In kernel bead or plugin config
event_triggers:
  - event: "issue.created"
    action: mail
    target: "plugins/work-oracle"

  - event: "mr.submitted"
    action: run
    plugin: "review-oracle"

  - event: "agent.stuck"
    action: escalate
    target: human
```

## Event Sources

Events come through mail to deacon/:
```
Subject: EVENT: issue.created gt-abc123
Body: New issue created in gastown rig
```

## Event Types

Initial event types:
- `issue.created` - New bead created
- `issue.closed` - Bead closed
- `mr.submitted` - Merge request submitted
- `mr.merged` - Merge request merged
- `agent.stuck` - Agent appears stuck (from health scan)
- `agent.failed` - Agent remediation failed

## Event Processing

During events step:
1. Read event mail from inbox
2. Match against subscriptions
3. Execute actions:
   - `mail`: Send mail to target
   - `run`: Execute plugin inline
   - `spawn`: Spawn subagent for plugin
   - `escalate`: Mail human

## Relation to Mail

Mail IS the event bus. No separate event system.
Events are just specially-formatted mail to deacon/.


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


Phase 3.1: Summary generation protocol

Define how agents generate summaries for squash.

## The Problem

Beads doesn't make AI calls (inversion of control). Gas Town agents must:
1. Generate their own summary before calling squash
2. Pass summary to bd squash command

## Summary Template

```
Task: <source issue title>
Action: <what was done - fix/implement/refactor/etc>
Outcome: <result - tests pass, committed, needs follow-up, etc>
Details: <1-2 sentences of specifics if needed>
```

## Example

```
Task: Fix lifecycle parser matching bug (gt-rixa)
Action: Reordered conditional checks in parseLifecycleRequest
Outcome: Tests passing, committed to polecat/furiosa
Details: The 'cycle' keyword was matching 'lifecycle:' prefix. Now checks restart/shutdown first, uses word boundary for cycle.
```

## Command

```bash
bd squash eph-abc123 --summary "Task: Fix lifecycle parser..."
```


Phase 1.3: gt doctor checks for ephemeral health

Add doctor checks for ephemeral beads repo.

## Checks

1. **ephemeral-exists**: .beads-ephemeral/ directory exists for each rig
2. **ephemeral-git**: It's a valid git repo
3. **ephemeral-orphans**: Molecules started but never squashed (>24h old)
4. **ephemeral-size**: Warn if ephemeral repo is >100MB (should be cleaned)
5. **ephemeral-stale**: Molecules with no activity in last hour

## Auto-fix

--fix can:
- Create missing ephemeral repo
- Clean up old completed molecules (already squashed)
- NOT auto-squash incomplete molecules (needs AI summary)


Swarm: ephemeral rig support

PGT has ephemeral rigs for swarms - temporary worker groups that are destroyed after landing.

Missing Features:
- gt swarm init [--rig <name>|--git-url <url>] [--num-workers N]
- gt swarm worker add/remove/list <rig-id>
- gt swarm rigs - List ephemeral rigs
- gt swarm destroy <rig-id> - Destroy ephemeral rig

Directory structure:
<workspace>/mayor/workers/<rig-id>/
├── rig.json (metadata)
├── alice/ (git clone)
├── bob/
└── carol/

PGT Reference: gastown-py/src/gastown/ephemeral.py

Note: Beads issue gt-kmn.12 mentions this but implementation is missing.


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)


generate-summary

Generate a summary for molecule squash.
File any remaining work as issues.

Document any important context for the squash digest.

Depends: submit-merge


Retry logic: exponential backoff for transient failures

Implement retry logic for transient failures.

Retryable failures:
- Push failed (network, auth transient)
- Test flaky (config: retry_flaky_tests)
- Fetch failed (network)

Retry strategy:
- Max retries: 3 (configurable)
- Backoff: 1s, 2s, 4s (exponential)
- On final failure: treat as permanent, assign back

Non-retryable failures:
- Merge conflict
- Test consistently fails
- Build error

Track retry count in MR metadata for debugging.

Reference: docs/merge-queue-design.md#failure-recovery


Update documentation for new config architecture

Update docs to reflect new structure:
- docs/architecture.md: Directory structure section
- CLAUDE.md files: Config references
- Add docs/configuration.md with full reference


Auto-handoff: self-cycling with guaranteed work pickup

## Summary

A mechanism for agents to self-cycle: send handoff mail, request Deacon to kill the process, and automatically restart to pick up the work.

## Use Case

User says: `auto-handoff: gt-bug3`

The agent:
1. Sends handoff mail documenting current state
2. Requests Deacon to terminate the session
3. Deacon restarts the agent in background
4. New session picks up the handoff and continues working

When the user returns to terminal, the old session is gone but work continues in background. User can reattach.

## Key Requirements

- **Guaranteed pickup**: Something in the hook and/or handoff must ensure the new session picks up the work (not just a passive "check inbox")
- **Background execution**: Work continues without user presence
- **Reattachability**: User can reconnect to see progress

## Design Questions

1. Can molecules help coordinate this? (e.g., molecule binds the handoff + auto-restart)
2. What hook handles the pickup guarantee?
3. How does Deacon know to restart vs just kill?
4. How does user reattach to a background agent?

## Related

- Molecules system
- Deacon lifecycle management
- Handoff hooks


Update polecat CLAUDE.md with molecule workflow

Add molecule execution guidance to polecat context:

## What to Add

### Molecule Awareness
- Polecats execute Wisps (ephemeral molecule instances)
- The work assignment mail includes molecule context
- Current step is tracked in the wisp

### Workflow Protocol
1. Read assignment (includes molecule ID and current step)
2. Execute current step
3. Update step status via bd mol step
4. Generate summary when all steps complete
5. Run bd mol squash to compress wisp into digest

### Key Commands
- bd mol show <wisp-id> - view current molecule state
- bd mol step <wisp-id> --status=complete - mark step done
- bd mol squash <wisp-id> --summary='...' - complete molecule

### Summary Generation
When completing work, generate a summary that:
- Lists what was accomplished
- Notes any deviations from the plan
- Captures key decisions made
- This becomes the permanent digest


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.


Post-work discovery: AI analysis finds follow-on issues

AI analyzes completed work to discover: bugs, punted work, follow-on tasks.

**From VC**: Supervisor.AnalyzeResult() with iterative refinement. ~300 lines.

**Gas Town implementation**: Post-work hook in molecule:
```yaml
post_work:
  discover:
    - bugs
    - punted_items
    - follow_on_work
  file_as: beads
```

Polecat output gets analyzed by AI, discovered work becomes beads issues.

**Value**: Nothing gets forgotten. VC found ~25% more issues with refinement.

**Key**: Use semantic deduplication (gt-xxx) to avoid pollution.


Test merge request

branch: polecat/Test/gt-test
target: main
source_issue: gt-test
worker: TestWorker
rig: gastown


CLI: session commands (start, stop, at, list, capture, inject)

Session management CLI commands.

## Commands

### gt session start
Start a polecat session.
```
gt session start <rig>/<polecat> [--issue <id>]
```
- Creates tmux session
- Launches claude in polecat workdir
- Optionally injects initial issue context

### gt session stop
Stop a polecat session.
```
gt session stop <rig>/<polecat> [--force]
```
- Graceful shutdown by default
- --force kills immediately

### gt session at (attach)
Attach to running session.
```
gt session at <rig>/<polecat>
```
- Attaches tmux session to current terminal
- Detach with Ctrl-B D

### gt session list
List all sessions.
```
gt session list [--rig <rig>] [--json]
```
- Shows running/stopped status
- Optionally filter by rig

### gt session capture
Capture recent output from session.
```
gt session capture <rig>/<polecat> [--lines <n>]
```
- Default: last 100 lines
- Useful for checking polecat progress

### gt session inject
Send message to session.
```
gt session inject <rig>/<polecat> -m "message"
gt session inject <rig>/<polecat> -f <file>
```
- Injects text via tmux send-keys
- Used for nudges, notifications

## Address Format

`<rig>/<polecat>` e.g., `wyvern/Toast`

## Implementation

Session subcommand group:
```go
var sessionCmd = &cobra.Command{Use: "session"}
sessionCmd.AddCommand(startCmd, stopCmd, atCmd, listCmd, captureCmd, injectCmd)
```


Merge fix/spawn-beads-path branch to main

Branch has gt nudge command; main has notification deduplication. Both valuable.

Key insight: Only ~10 commits diverged each way in mayor/rig.
- Branch: 9509afa feat(nudge): Add gt nudge command (MISSING from main)
- Main: d2fccd5 slot-based notification deduplication (KEEP)

Merge plan:
1. git merge fix/spawn-beads-path --no-ff
2. Keep nudge.go from branch
3. Keep notification.go from main
4. Merge tmux.go (both NudgeSession AND SendKeysReplace)
5. go build && go test
6. Deploy to ~/.local/bin/gt


Merge polecat/rictus: Add molecule phase lifecycle diagram

Branch: polecat/rictus

Adds molecule phase lifecycle diagram to architecture.md showing Proto → Mol/Wisp → Digest state transitions with the 'states of matter' metaphor. Also documents when to use Mol (durable) vs Wisp (ephemeral).

Closes: gt-c6zs


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.


Phase 2.2: gt prime shows ephemeral molecule context

Update gt prime to detect and display ephemeral molecule state.

## Detection

1. Check for active ephemeral molecule assigned to current identity
2. Parse molecule progress (current step, total steps)
3. Show in prime output

## Output

```
🔧 Polecat furiosa, checking in.

📦 Molecule: mol-polecat-work (eph-abc123)
   Step 3/8: do-work
   Source: gt-rixa
   Started: 10 minutes ago

Run 'bd mol status' for full molecule state.
```


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


Phase 4: Polish and observability

Production readiness.

## Commands
- gt patrol status - Show patrol state for all agents
- gt patrol history - Recent patrol activity

## Observability
- Metrics collection
- Alert thresholds
- Dashboard (optional)

## Tuning
- Cooldown optimization
- Wake SLA verification
- Error threshold tuning

Depends: Phase 3


Merge: gt-odvf

branch: polecat/slit
target: main
source_issue: gt-odvf
rig: gastown


Implement bd mol bond --ephemeral flag

Add --ephemeral flag to bd mol bond command to support ephemeral molecule bonding.

## Context
Phase 1.2 of Wisp Molecule Integration (gt-3x0z.2) discovered that this flag doesn't exist.

## Requirements

1. Add --ephemeral flag to mol bond command
2. When --ephemeral is set:
   - Mark spawned issues with wisp=true
   - Optionally write to separate .beads-ephemeral/ storage (Phase 2)

## Architecture Reference
See gastown architecture.md lines 487-491 for the full ephemeral storage design.

## Related
- gt-3x0z.2: Configure bd for ephemeral molecule bonding (closed - blocked on this)


BUG: gt done doesn't push branch to origin - work lost when worktree deleted

## Problem

`gt done` creates a merge-request record in beads but **never pushes the branch to origin**.

When polecats are cleaned up (worktrees deleted), the branch is lost forever. The MR record exists but there's no actual branch to merge.

## Evidence

12 MQ items had `ready` status but no corresponding branches on origin. All polecat worktrees were deleted, losing all work.

## Impact

**CRITICAL**: All polecat work is silently lost unless polecats manually push (which they don't know to do).

## Fix

In `done.go`, before creating the MR:
1. `git push origin <branch>` to push the branch to remote
2. Only create MR record if push succeeds

## Location
`mayor/rig/internal/cmd/done.go:124-135`


Witness tmux status: show polecat count under management

Add witness-specific status line showing:
- Number of polecats under management
- Active/idle status
- Maybe: last nudge time, blocked count

Implement in runWitnessStatusLine() in internal/cmd/statusline.go


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).


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


Test: Alpha to Beta

Sibling communication test


Daemon heartbeat: worker and queue monitoring

Daemon periodic checks for work progress.

## Heartbeat Actions

Every N seconds (configurable):

1. Check Witness health per rig
   - Poke if witness needs to nudge workers

2. Check Refinery queue per rig
   - Poke if pending work in queue

3. Check work completion
   - If epic children all closed, notify Mayor

## Eventually Convergent

Multiple signals reinforce state:
- Polecat signals done → Witness notices → pokes Refinery
- Daemon heartbeat → checks queue → pokes Refinery
- Beads status → queryable by any agent

Even if one signal missed, system converges.

## Interface

```go
func (d *Daemon) HeartbeatLoop() {
    for {
        for _, rig := range d.rigs {
            d.CheckWitness(rig)
            d.CheckRefinery(rig)
            d.CheckWorkProgress(rig)
        }
        time.Sleep(d.config.HeartbeatInterval)
    }
}
```

## Notifications

Use mail with low priority for heartbeat pokes.
Agents can ignore if already processing.


Split PGT/GGT harness or migrate to GGT-only

Current ~/ai harness is shared by PGT and GGT with confusing overlap. Options:
1. Keep separate (document the coexistence)
2. Migrate fully to GGT structure
3. Create separate harnesses

This affects the beads redirect, Mayor home location, and rig structure.


Epic: Wisp Molecule Integration

## Vision

Integrate Beads wisp molecules into Gas Town. All orchestration work runs as wisp molecules - patrols, polecat workflows, batch operations. Only digests reach main beads.

## The Steam Engine Metaphor

```
Engine does work → generates steam
Steam wisps rise → execution trace
Steam condenses → digest (distillate)
Steam dissipates → cleaned up (burned)
```

## Architecture

```
Proto Molecules (templates)
    ↓ bd mol bond
Wisps (.beads-wisps/ or inline)
    ↓ bd mol squash + AI summary
Main Beads (digests only)
```

## Vocabulary

| Term | Meaning |
|------|---------|
| bond | Attach proto to work (creates wisps) |
| wisp | Temporary execution step (steam rising) |
| squash | Condense wisps into digest |
| burn | Destroy wisps without record |
| digest | Permanent condensed record (distillate) |

## Key Design Decisions

1. **Wisp location**: Per-rig or inline in main beads with wisp flag
2. **Summary generation**: Agent that did work generates summary (inversion of control)
3. **Squash timing**: Final step of molecule workflow, before signaling done
4. **Crash recovery**: Wisps persist, Witness detects stalls, new agent resumes
5. **Patrols**: Each cycle is fresh wisp molecule, squashed on completion

## Digest Contents

- Molecule type and instance ID
- Assignee, start/end times
- Source issue reference
- AI-generated summary
- Outcomes (issues closed, commits, branches)

## Integration Points

- gt rig init: Configure wisp storage
- gt spawn --molecule: Bond creates wisps
- gt prime: Show wisp molecule context
- Polecat CLAUDE.md: Summary + squash protocol
- gt doctor: Wisp health checks
- Deacon/Witness/Refinery: Patrol molecules

## Phases

Phase 1: Wisp setup infrastructure
Phase 2: Spawn integration
Phase 3: Completion flow (summary + squash)
Phase 4: Patrol integration
Phase 5: Documentation and polish


Witness swarm landing protocol

Witness responsibilities for swarm landing cleanup.

## Trigger

Witness receives "swarm-landed" mail from Refinery containing:
- swarm_id
- merge_commit
- action: "cleanup"

## Landing Protocol

### Phase 1: Preflight
Verify all polecat sessions stopped:
- Check tmux for active sessions
- Kill any remaining sessions
- Wait for graceful shutdown

### Phase 2: Git Audit
For each polecat in swarm:
- Check for uncommitted changes
- Check for unpushed commits
- Check for stashes
- Classify as "beads-only" (safe) or "code at risk"
- If code at risk: escalate, abort landing

### Phase 3: Cleanup
For each polecat:
- Clear state entry (mark as idle)
- Delete inbox (or archive)
- Delete worker branch (local + remote)

### Phase 4: Final Report
- Update swarm state to "landed"
- File landing report bead
- Mail Mayor with summary

## Code at Risk Handling

If git audit finds code at risk:
1. Abort cleanup for that polecat
2. Mail Mayor with details
3. Human intervention required

## Reference

PGT: swarm/landing.py (6-phase protocol), swarm/git_audit.py


gt crew at/attach: Start session in crew workspace

Implement 'gt crew at <rig>/<name>' and 'gt crew attach <name>' commands:
- Start tmux session (optional - could just print cd instructions)
- Launch claude code in the workspace
- Deliver any pending mail
- Support --no-tmux to just print directory path
- 'attach' infers rig from cwd


Polecat name pooling: Bounded reusable names

Polecats reuse names from a bounded pool (50) with overflow to sequence numbers.

## Naming Scheme
- Pool: polecat-01 through polecat-50 (prefer low numbers)
- Overflow: <rigname>-<sequenceNumber> (e.g., beads-51, gastown-52)

## Design
- Witness tracks which pool names are in use
- On spawn: pick first available from pool
- If pool exhausted: use rigname-N format
- On completion: pool name returns, sequence numbers don't

## Why?
- User experience: tmux sessions survive polecat restarts
- Users stay attached, see new polecat start (like mayor respawn loop)
- Bounded resource usage for common case
- Scales beyond 50 when needed

## Implementation
- Witness maintains name allocation in beads or local state
- Tmux session runs respawn loop (like mayor)
- Name released on graceful exit or when witness detects dead session


Molecule schema: YAML format for workflow definitions

Define the YAML schema for molecule definitions:

```yaml
molecule: <name>
version: 1
description: "Human description"
steps:
  - id: <step-id>
    title: "Step title"
    prompt: "Instructions for agent"
    depends: [<other-step-ids>]  # optional
    tier: haiku|sonnet|opus      # optional, default from config
    timeout: 30m                 # optional
```

Store molecules in:
- `<rig>/molecules/<name>.yaml` for rig-specific
- `<town>/molecules/<name>.yaml` for town-wide

Built-in molecules to ship:
- engineer-in-box: design→code→review→test→submit
- quick-fix: implement→test→submit
- research: investigate→document


Refinery agent: merge queue processing loop

The Refinery agent processes the merge queue, merging polecat work to main.

## Interface

```go
type Refinery struct {
    rig    *Rig
    queue  *MergeQueue
    git    *Git
    mail   *Mailbox
    config RefineryConfig
}

type RefineryConfig struct {
    AutoMerge     bool          // Auto-merge passing MRs
    RunTests      bool          // Run tests before merge
    TestCommand   string        // Command to run tests
    RequireReview bool          // Require review before merge (future)
}

func NewRefinery(rig *Rig, ...) *Refinery

// Lifecycle
func (r *Refinery) Start() error
func (r *Refinery) Stop() error
func (r *Refinery) IsRunning() bool

// Processing
func (r *Refinery) ProcessQueue() error
func (r *Refinery) ProcessMR(mr *MergeRequest) error
```

## Processing Loop

1. Check queue for pending MRs
2. For each pending MR:
   a. Fetch polecat branch
   b. Attempt merge to refinery/rig (local main)
   c. Run tests if configured
   d. If pass: push to origin, mark merged
   e. If fail: mark rejected, notify polecat
3. Sleep, repeat

## Merge Strategy

- Fast-forward when possible
- Merge commit when not
- On conflict: reject MR, polecat must rebase

## Test Integration

If tests configured:
```bash
cd refinery/rig
git merge polecat/branch
<test_command>  # e.g., go test ./...
```
Result determines merge/reject.

## Notifications

On merge success:
- Mail to polecat: "Your work merged"
- Update bead if issue tracked

On merge failure:
- Mail to polecat: "Merge failed: <reason>"
- Include conflict details if applicable

## Dependencies

Needs: Rig management, Git wrapper, Mail system, Merge queue


Merge: gt-h5n.5

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


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


Merge: gt-3x0z.2

branch: polecat/nux
target: main
source_issue: gt-3x0z.2
rig: gastown


Work assignment mail not received by polecat

When gt spawn sends a work assignment:

1. gt spawn says '✓ Work assignment sent'
2. Polecat runs 'gt mail inbox'
3. Shows '0 messages, 0 unread'

The work assignment mail never arrived at the polecat's inbox.

## Observed in session
- Spawned polecat dementus on gt-th7
- Polecat checked inbox: empty
- Polecat couldn't find issue (separate sync bug)

## Possible causes
- Mail routing issue for polecat addresses
- gt spawn not actually sending mail
- Mail sent to wrong address format

This was likely caused by stale beads in old polecats. With gt-9nf (fresh polecats), polecats now use shared rig beads via redirect file, eliminating sync issues. Mail routing uses town beads correctly.


Molecule format bridge: convert embedded markdown to child issues

## Problem

Two molecule formats exist:
1. **Old (gastown builtin)**: Steps embedded as markdown in Description field, parsed by ParseMoleculeSteps()
2. **New (bd mol)**: Steps as child issues in proper beads DAG

The daemon's InstantiateMolecule() uses the old format. bd mol spawn uses the new format.

## Options

### Option A: Require child issues (new format)
- Update daemon to use bd mol spawn instead of InstantiateMolecule
- Deprecate embedded markdown format
- Pro: One format, simpler
- Con: Breaking change for existing molecules

### Option B: Build a bridge
- In InstantiateMolecule, detect format:
  - If Description has ## Step: markers → parse to child issues
  - If issue has children with template label → use directly
- Convert old format to new format on instantiation
- Pro: Backward compatible
- Con: More complexity

## Recommendation

Option B - build the bridge. On instantiation, convert markdown steps to child issues. This unifies execution while preserving authoring flexibility.


Polecats should not push branches to remote

## Current Behavior

Polecats push their branches to origin (e.g., `polecat/furiosa`), which pollutes the remote with many short-lived branches.

## Desired Behavior

Polecats should only commit locally. The Refinery handles all remote pushes:
1. Polecat works on local `polecat/<name>` branch
2. Polecat signals done (state → idle)
3. Refinery pulls from local polecat branch
4. Refinery runs tests, merges to main
5. Refinery pushes main to remote
6. If PR review needed, Refinery creates the PR

## Benefits

- Clean remote (no branch pollution)
- Clear responsibility (Refinery is the quality gate)
- Simpler cleanup (local branches deleted with worktree)
- Less noise in GitHub UI

## Trade-offs

- If polecat crashes before Refinery merges, code is lost locally
- But beads issue remains open, another polecat can redo the work
- This is acceptable for ephemeral workers

## Implementation

In polecat CLAUDE.md or landing protocol:
- Remove `git push origin HEAD` from workflow
- Replace with just `git commit` + signal done
- Refinery handles the rest


Merge: gt-5af.2

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


Implement pinned beads for handoff state

Add pinned bead support to beads:
- Pinned beads never close, only update
- Use for persistent state like Refinery handoffs
- bd create --pinned flag
- bd list --pinned to find them
- Update description to change state


Refactor: Eliminate state.json, use beads assignee field for polecat state

## Problem

state.json in each polecat worktree duplicates data that should be in beads:
- `issue` field duplicates assignment (should be issue.assignee)
- `state` field duplicates status (derivable from issue.status)
- `name/rig/branch/clone_path` are all derivable from filesystem/git

This violates 'Beads as the data plane' (HOP Decision 001).

## Current State

```json
{
  "name": "Angharad",        // Derivable: basename(cwd)
  "rig": "gastown",          // Derivable: parent dir name
  "state": "working",        // Should be: issue.status
  "clone_path": "/path",     // Derivable: cwd
  "branch": "polecat/X",     // Derivable: git branch
  "issue": "gt-xyz",         // Should be: issue.assignee
  "timestamps": "..."        // Should be in beads history
}
```

## Target State

**Polecat identity**: Derived from worktree path
**Polecat assignment**: `issue.assignee = 'gastown/Angharad'`
**Polecat state**: Derived from issue.status (in_progress = working, closed = done)
**Polecat existence**: Worktree directory exists

## Benefits

1. Single source of truth (no sync issues)
2. Queryable: `bd list --assignee=gastown/Angharad`
3. History tracking via beads
4. Cross-agent coordination via beads sync
5. Simpler code (no state file management)

## Implementation

1. Update `gt spawn` to set issue.assignee instead of writing state.json
2. Update polecat list/status commands to query beads
3. Update Witness to query beads for polecat state
4. Remove state.json read/write code
5. Keep state.json as optional bootstrap cache (perf optimization only)


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


Ephemeral polecats: spawn fresh, delete on completion

## Design Decision

Switch from pooled/idle polecats to ephemeral model:
- Spawn creates fresh worktree from main
- Polecat requests shutdown when done (bottom-up)
- Witness verifies handoff, kills session, deletes worktree
- No 'idle' state - polecats exist only while working

## Rationale

1. **Git worktrees are fast** - pooling optimization is obsolete
2. **Pooling creates maintenance burden:**
   - Git stashes accumulate
   - Untracked artifacts pile up
   - Branches drift from main
   - Beads DB gets stale
3. **PGT sync problems** came from persistent branches
4. **Support infrastructure exists** - Witness, Refinery, Mayor handle continuity
5. **Simpler mental model** - polecat exists = work in progress

## Lifecycle

```
Spawn:
  gt spawn --issue <id>
  → Creates fresh worktree: git worktree add polecats/<name> -b polecat/<name>
  → Initializes beads in worktree
  → Starts session, assigns work

Working:
  Polecat does task
  → Pushes to polecat/<name> branch
  → Submits to merge queue when ready

Completion (POLECAT-INITIATED):
  Polecat runs: gt handoff
  → Verifies git state clean
  → Sends mail to Witness: "Ready for shutdown"
  → Marks itself done, waits for termination

Cleanup (WITNESS-OWNED):
  Witness receives shutdown request
  → Verifies PR merged or in queue
  → Verifies no uncommitted changes
  → Kills session: gt session stop <rig>/<polecat>
  → Deletes worktree: git worktree remove polecats/<name>
  → Deletes branch: git branch -d polecat/<name>
  → Optionally: Notifies Mayor of completion
```

## Key Insight: Bottom-Up Shutdown

**Old model (wrong)**: Top-down batch shutdown - "cancel the swarm"
**New model (right)**: Bottom-up individual shutdown - polecat requests, Witness executes

This enables streaming:
- Workers come and go continuously
- No "swarm end" to trigger cleanup
- Each worker manages its own lifecycle
- Witness is the lifecycle authority

## Implementation

1. Add `gt handoff` command for polecats to request shutdown
2. Modify gt spawn to always create fresh worktree
3. Run bd init in new worktree (beads needs initialization)
4. Add shutdown request handler to Witness
5. Witness verifies handoff, then cleans up:
   - Kill session
   - Remove worktree
   - Delete branch
6. Remove 'idle' state from polecat state machine
7. Simplify gt polecat list (only shows active)

## Impact on Other Tasks

- gt-17r (Zombie cleanup): Becomes trivial - orphan worktrees
- gt-4my (Worker health): Simpler - no idle/stuck ambiguity
- gt-f9x.5/f9x.6 (Doctor): Fewer states to validate
- gt-eu9 (Witness handoff): Witness receives polecat shutdown requests


Add 'wit' alias for witness command

ref works as alias for refinery, but wit doesn't work for witness. Add Aliases: []string{"wit"} to witnessCmd.


Daemon: proper rig discovery

Currently discovers rigs by scanning tmux session names for gt-*-witness pattern. Should instead:
- Read rigs from mayor/rigs.json
- Or scan town directory for .gastown subdirs
- Handle rigs that exist but don't have running witnesses


Auto-rebase on conflict: optional automatic rebase

Implement automatic rebase as conflict resolution option.

When on_conflict=auto_rebase and conflicts detected:
1. Attempt: git rebase <target> <source-branch>
2. If clean rebase: update source branch, continue merge
3. If conflicts in rebase: abort, fall back to assign_back
4. Force push rebased branch (worker's branch)

Requires:
- Worker branch must be force-pushable
- Config: on_conflict: auto_rebase

Risks:
- Force push can confuse workers
- Complex conflicts still need manual resolution

Reference: docs/merge-queue-design.md#conflict-resolution-strategies


Plugin system: subprocess JSON protocol

Language-agnostic plugin system via subprocess JSON protocol.

## Concept

Allow extending Gas Town with external scripts/programs. Plugins communicate via stdin/stdout JSON.

## Protocol

Request (gt → plugin):
```json
{"method": "check", "params": {"rig": "wyvern", "polecat": "Toast"}}
```

Response (plugin → gt):
```json
{"result": {...}, "error": null}
```

Error response:
```json
{"result": null, "error": {"code": 1, "message": "..."}}
```

## Interface

```go
type Plugin struct {
    Name    string
    Path    string  // executable path
    Methods []string
}

type PluginManager struct {
    plugins map[string]*Plugin
}

func NewPluginManager() *PluginManager
func (pm *PluginManager) Register(plugin *Plugin) error
func (pm *PluginManager) Call(plugin, method string, params any) (any, error)
func (pm *PluginManager) Discover(dir string) error  // auto-discover plugins
```

## Plugin Discovery

Look in:
1. `<town>/plugins/`
2. `~/.config/gastown/plugins/`
3. System path with `gt-plugin-*` prefix

## Built-in Hooks

Plugins can register for events:
- `polecat.started` - Polecat session started
- `polecat.done` - Polecat signals done
- `merge.requested` - MR submitted
- `merge.completed` - MR merged

## Use Cases

- Custom health checks
- Integration with external systems (Slack, etc.)
- Custom merge policies
- Metrics collection

## Future

Consider MCP integration for richer plugin capabilities.


self-review

Review your own changes. Look for:
- Bugs and edge cases
- Style issues
- Missing error handling
- Security concerns

Fix any issues found before proceeding.

Depends: implement


Witness Patrol Molecule

Create mol-witness-patrol molecule for witness heartbeat loop.

Similar to DeaconPatrolMolecule but for per-rig witness duties.

## Steps
1. inbox-check - Process witness mail
2. survey-workers - List polecats, check status
3. inspect-workers - Capture pane output, assess state
4. plugin-run - Execute plugins from <rig>/plugins/witness/
5. decide-actions - Determine nudges, escalations
6. execute-actions - Send nudges, process shutdowns
7. context-check - Check own context, handoff if needed
8. loop-or-exit - Continue or cycle

The plugin-run step enables user customization of the patrol loop.


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`.


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)


Verify architecture.md shows correct harness and rig structure

Review architecture.md diagrams:
- Verify town-level structure shows harness correctly
- Confirm rig-level mayor/rig/ is shown (it appears to be there at line 197)
- Check mermaid diagrams match ASCII diagrams
- Update if any inconsistencies found
- Cross-reference with new harness.md docs


health-scan

Ping Witnesses and Refineries. Run gt status --health. Remediate if down.


Merge Queue in Beads: Universal chit system for all work

**Design doc**: docs/merge-queue-design.md


Update lifecycle mail targets to deacon/

Update Mayor, Witness, and Crew handoff code to send lifecycle requests to deacon/ instead of daemon/. Update internal/cmd/handoff.go and related code.


Implement gt sling command

The unified work dispatch command.

```bash
gt sling <thing> <target> [options]
```

Implements spawn + assign + pin in one operation. See sling-design.md.

Acceptance:
- [ ] Parse thing (proto name, issue ID, epic ID)
- [ ] Parse target (agent address)
- [ ] Spawn molecule if proto
- [ ] Assign to target agent
- [ ] Pin to agent's hook (pinned bead)
- [ ] Support --wisp flag for ephemeral
- [ ] Support --molecule flag for issue+workflow
- [ ] Error if hook already occupied (unless --force)


Integration branch management for swarms

Manage integration branches for swarm merging strategy.

## Branch Naming

- Integration branch: `integration/<swarm-id>` (e.g., integration/sw-1)
- Worker branches: `<swarm-id>/<polecat>` (e.g., sw-1/Toast)

## Operations

```go
// Create integration branch at swarm start
func (m *SwarmManager) CreateIntegrationBranch(swarmID string) error
// - Creates from BaseCommit
// - Pushes to origin

// Merge worker branch to integration
func (r *Refinery) MergeToIntegration(swarmID, workerBranch string) error
// - Fetches worker branch
// - Merges to integration/<swarm-id>
// - Handles conflicts (semantic merge)

// Land integration to main
func (r *Refinery) LandToMain(swarmID string) error
// - Merges integration/<swarm-id> to main
// - Pushes to origin
// - Triggers cleanup

// Cleanup branches after landing
func (m *SwarmManager) CleanupBranches(swarmID string) error
// - Deletes integration/<swarm-id> (local + remote)
// - Deletes all <swarm-id>/<polecat> branches
```

## All Workers Same Base

When swarm starts:
1. Record current main HEAD as BaseCommit
2. All polecats branch from BaseCommit
3. Integration branch also starts at BaseCommit

This ensures clean merges within the swarm.

## Reference

PGT: ephemeral.py (create_integration_branch, merge_integration_to_main)


Go scaffolding: cmd/gt, go.mod, Cobra setup

Set up Go project structure with CLI framework.

**Stack:**
- Cobra for command/flag handling
- Lipgloss for styled terminal output

**Deliverables:**
- cmd/gt/main.go with Cobra root command
- Basic subcommands: version, help
- Lipgloss styles for status output (success, warning, error)
- go.mod with dependencies


gt handoff: Unified agent lifecycle command

## Summary

Unified `gt handoff` command for ALL agent types to request lifecycle actions.

## Usage

gt handoff              # Context-aware default
gt handoff --shutdown   # Terminate, cleanup, don't restart
gt handoff --cycle      # Restart with handoff mail
gt handoff --restart    # Fresh restart, no handoff

## Context-Aware Defaults

| Agent Type | Default | Reason |
|------------|---------|--------|
| Polecat | --shutdown | Ephemeral, work is done |
| Witness | --cycle | Long-running, context full |
| Refinery | --cycle | Long-running, context full |
| Mayor | --cycle | Long-running, context full |
| Crew | (sends mail only) | Human-managed |

## What gt handoff Does

1. **Verify safe to stop**
   - Git state clean (no uncommitted changes)
   - Work handed off (PR exists for polecats)

2. **Send handoff mail to self** (for cycle/restart)
   - Captures current state
   - New session will read this

3. **Send lifecycle request to manager**
   - Polecats/Refinery → Witness
   - Witness/Mayor → Daemon
   - Format: mail to <manager> with action type

4. **Set state: requesting_<action>**
   - Lifecycle manager checks this before acting

5. **Wait for termination**
   - Don't self-exit - let manager kill session
   - Ensures clean handoff

## Lifecycle Request Flow

Agent                           Lifecycle Manager
  |                                    |
  | 1. gt handoff --cycle              |
  |    a. Verify git clean             |
  |    b. Send handoff mail to self    |
  |    c. Set requesting_cycle=true    |
  |    d. Send lifecycle request       |
  |------------------------------------→|
  |                                    |
  |                   2. Receive request
  |                   3. Verify state  |
  |                   4. Kill session  |
  |                   5. Start new     |
  |                      (for cycle)   |
  |                                    |
  |         New session reads handoff  |
  |         Resumes work               |

## Who Manages Whom

| Agent | Sends lifecycle request to |
|-------|---------------------------|
| Polecat | <rig>/witness |
| Refinery | <rig>/witness |
| Witness | daemon/ |
| Mayor | daemon/ |

## Implementation

1. Detect current role (polecat, witness, refinery, mayor, crew)
2. Apply context-aware default if no flag specified
3. Run pre-flight checks (git clean, work handed off)
4. Send handoff mail to self (if cycling)
5. Send lifecycle request to appropriate manager
6. Set requesting_<action> in state.json
7. Wait (manager will kill us)

## For Polecats (--shutdown)

Additional cleanup after kill:
- Witness removes worktree
- Witness deletes polecat branch
- Polecat ceases to exist

## Related Issues

- gt-99m: Daemon (handles Mayor/Witness lifecycle)
- gt-7ik: Ephemeral polecats (polecat cleanup)
- gt-eu9: Witness session cycling


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


Worker cleanup: Beads sync on shutdown

Add beads sync verification to worker cleanup checklist and Witness verification.

## Update to Decommission Checklist (gt-sd6)

Add to pre-done verification:
- bd sync --status must show 'Up to date'
- git status .beads/ must show no changes

## Beads Edge Cases

Uncommitted beads changes:
  bd sync
  git add .beads/
  git commit -m 'beads: final sync'

Beads sync conflict (rare):
  git fetch origin main
  git checkout main -- .beads/
  bd sync --force
  git add .beads/
  git commit -m 'beads: resolve sync conflict'

## Update to Witness Verification (gt-f8v)

When capturing worker state:
  town capture <polecat> "bd sync --status && git status .beads/"

Check for:
- bd sync --status shows 'Up to date'
- git status .beads/ shows no changes

If beads not synced, nudge:
  WITNESS CHECK: Beads not synced. Run 'bd sync' then commit .beads/. Signal done when complete.


Work request: gt-976 crew lifecycle support

Hey Max, filed gt-976 for you - adding crew lifecycle support to Deacon.

Currently crew is 'human-managed' and can't request automated refresh. Would be useful for molecules that need fresh sessions mid-workflow.

Key changes:
- getManager(RoleCrew) → return deacon/ instead of human
- Teach Deacon crew session patterns
- Test with gt nudge

Priority 2, no rush but would unblock molecule automation for crew.

- Dave (beads/crew/dave)


MQ config schema: merge_queue section in rig config.json

Define and implement merge_queue configuration in rig config.

Schema:
{
  "merge_queue": {
    "enabled": true,
    "target_branch": "main",
    "integration_branches": true,
    "on_conflict": "assign_back",  // or "auto_rebase"
    "run_tests": true,
    "test_command": "go test ./...",
    "delete_merged_branches": true,
    "retry_flaky_tests": 1,
    "poll_interval": "30s",
    "max_concurrent": 1
  }
}

Implement:
- Config loading in rig package
- Default values
- Validation

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


Rig doctor checks: Refinery health, clones, gitignore

Rig-level doctor checks.

## Checks

### RigIsGitRepo
- Verify .git/ directory exists
- Verify git status works
- Fix: Cannot auto-fix

### GitExcludeConfigured
- Check .git/info/exclude contains Gas Town dirs
- Required entries: polecats/ witness/ refinery/ mayor/
- Fix: Append missing entries

### WitnessExists
- Verify witness/ directory exists
- Verify witness/rig/ is a git clone
- Verify witness/mail/inbox.jsonl exists
- Fix: Create structure, clone repo

### RefineryExists
- Verify refinery/ directory exists
- Verify refinery/rig/ is a git clone
- Verify refinery/mail/inbox.jsonl exists
- Fix: Create structure, clone repo

### MayorCloneExists
- Verify mayor/ directory exists
- Verify mayor/rig/ is a git clone
- Fix: Create structure, clone repo

### PolecatClonesValid
- For each polecat in polecats/:
  - Verify is a git clone
  - Verify on polecat branch
  - Warn if has uncommitted changes
- Fix: Cannot auto-fix (data loss risk)

### BeadsConfigValid (if applicable)
- If .beads/ exists, verify bd commands work
- Check beads sync status
- Warn if out of sync
- Fix: Run bd sync

## Implementation

```go
var RigChecks = []Check{
    &RigIsGitRepoCheck{},
    &GitExcludeConfiguredCheck{},
    &WitnessExistsCheck{},
    &RefineryExistsCheck{},
    &MayorCloneExistsCheck{},
    &PolecatClonesValidCheck{},
    &BeadsConfigValidCheck{},
}
```


Phase 1.4: Basic patrol runner in Deacon

Deacon CLAUDE.md and prime context for patrol execution.

## Deacon Context
- Update Deacon CLAUDE.md with patrol instructions
- On wake: check pinned bead for attachment
- If attached: resume molecule from current step
- Execute steps, close each when done
- On final step: burn molecule, go naked

## gt prime Enhancement
- Detect if agent has attached molecule
- Show molecule progress in prime output
- Include patrol-specific instructions

Depends: gt-rana.3


gt prime: Refinery context detection and output

Update gt prime to detect Refinery role:
- Detect from directory: refinery/rig/ = Refinery
- Show handoff bead reference
- Show merge queue status
- Show polecat branches with unmerged commits
- Show last actions summary


Witness session cycling and handoff

Add session cycling and handoff protocol to Witness CLAUDE.md template.

## Session Cycling Protocol

```markdown
## Session Cycling

Your context will fill over long swarms. Proactively cycle when:
- Running for many hours
- Losing track of which workers you've checked
- Responses getting slower
- About to start complex operation

### Handoff Protocol

1. **Capture current state**:
```bash
town list .           # Worker states
town all beads        # Pending verifications
town inbox            # Unprocessed messages
```

2. **Compose handoff note**:
```
[HANDOFF_TYPE]: witness_cycle
[TIMESTAMP]: <now>
[RIG]: <rig>

## Active Workers
<list workers and status>

## Pending Verifications
<workers signaled done but not verified>

## Recent Actions
<last 3-5 actions>

## Warnings/Notes
<anything next session should know>

## Next Steps
<what should happen next>
```

3. **Send handoff**:
```bash
town mail send <rig>/witness -s "Session Handoff" -m "<note>"
```

4. **Exit cleanly**: End session, daemon spawns fresh one.

### On Fresh Session Start

1. Check for handoff: `town inbox | grep "Session Handoff"`
2. If found, read it and resume from handoff state
3. If not found, do full status check
```

## Implementation

Add to WITNESS_CLAUDE.md template.


gt mq list: doesn't show submitted MRs

After submitting MRs with gt mq submit, gt mq list gastown shows empty queue.

## Reproduction
1. gt mq submit --issue gt-h5n.5 --branch polecat/Scabrous
2. gt mq list gastown → (empty)
3. bd list --type=merge-request → shows the MR

## Expected
gt mq list should show submitted MRs

## MR example
gt-ts4u has rig: gastown in description, type=merge-request, status=open


LocalConnection: Local file/exec/tmux operations

LocalConnection implementation for local machine operations.

## Implementation

```go
type LocalConnection struct {
    tmux *Tmux
}

func NewLocalConnection() *LocalConnection

func (c *LocalConnection) Name() string { return "local" }
func (c *LocalConnection) IsLocal() bool { return true }
```

## File Operations

Direct passthrough to os package:
```go
func (c *LocalConnection) ReadFile(path string) ([]byte, error) {
    return os.ReadFile(path)
}
// etc.
```

## Command Execution

Uses exec.Command:
```go
func (c *LocalConnection) Exec(cmd string, args ...string) ([]byte, error) {
    return exec.Command(cmd, args...).Output()
}
```

## Tmux

Delegates to Tmux wrapper:
```go
func (c *LocalConnection) TmuxNewSession(name, dir string) error {
    return c.tmux.NewSession(name, dir)
}
```

## Notes

Straightforward implementation - this is the "baseline" connection that SSHConnection will mirror remotely.


implement

Implement the solution for gt-test. Follow codebase conventions.
File discovered work as new issues with bd create.

Make regular commits with clear messages.
Keep changes focused on the assigned issue.

Depends: load-context


Slim down rig root config.json to identity only

Rig root config.json should only contain identity:
- type, version, name, git_url, beads.prefix, created_at
- Remove any behavioral config (move to settings/config.json)
- Update RigConfig type to separate identity from settings
- Ensure loader reads identity from root, settings from settings/


Overhaul gt mol to match bd mol chemistry interface

## The Sling: Unified Work Dispatch

This issue tracks the overhaul of `gt molecule` to align with chemistry metaphor and introduce the **Universal Gas Town Propulsion Principle**.

### The Propulsion Principle

> **If you find something on your hook, YOU RUN IT.**

This is the one rule that drives all Gas Town agents.

### The Sling Operation

`gt sling <thing> <target> [options]` - unified command for spawn + assign + pin.

See: `gastown/mayor/rig/docs/sling-design.md`

### Implementation Tasks

| Issue | Title | Priority |
|-------|-------|----------|
| gt-4ev4 | Implement gt sling command | P1 |
| gt-uym5 | Implement gt mol status command | P1 |
| gt-i4kq | Update templates for Propulsion Principle | P1 |
| gt-7hor | Document the Propulsion Principle | P2 |

### Command Changes

| Old | New |
|-----|-----|
| `gt molecule instantiate` | `gt sling` |
| `gt molecule attach` | `gt sling --force` |
| `gt molecule detach` | `gt mol burn` |
| `gt molecule progress` | `gt mol status` |
| `gt molecule list` | `gt mol catalog` |
| `gt spawn --molecule` | `gt sling` |

### Acceptance Criteria

- [ ] `gt sling` works for protos, issues, and epics
- [ ] `gt mol status` shows hook state
- [ ] Templates updated for propulsion principle
- [ ] Old commands deprecated with warnings
- [ ] Documentation complete


Support numeric shortcuts in mail read (e.g., 'mail read 1')

When inbox shows numbered messages like:
* 1. gm-19b29031...  2025-12-16  mayor  Subject...
* 2. gm-19b26d51...  2025-12-16         Subject...

Users should be able to run 'gt mail read 1' instead of needing the full message ID 'gt mail read gm-19b29031f6a172206'.

Implementation:
- Track inbox message order in display
- Map numeric indices to actual message IDs
- Accept both numeric shortcuts and full IDs in 'mail read' command


Refactor: Extract common manager creation boilerplate

Five nearly identical functions exist for creating managers:
- getPolecatManager (polecat.go:241)
- getSessionManager (session.go:183)
- getCrewManager (crew_helpers.go:44)
- getRefineryManager (refinery.go:116)
- getWitnessManager (witness.go:102)

All follow the same pattern: find workspace, load config, get rig, create manager. Should extract to a common helper that takes a factory function.


Test message

Test body


implement

Implement the solution for {{issue}}. Follow codebase conventions.
File discovered work as new issues with bd create.

Make regular commits with clear messages.
Keep changes focused on the assigned issue.

Depends: load-context


Add molecules.jsonl as separate catalog file for template molecules


session stop: --force flag is defined but not used


Tech Debt: Code Review December 2024

Tech debt identified during code review on 2024-12-21. Contains 11 issues ranging from P2-P4 covering:

- Code duplication (manager creation boilerplate)
- Test coverage gaps (cmd 6.8%, mail 3.6%)
- Magic strings needing constants
- Error handling inconsistencies
- Large files needing splitting
- Unused code removal

Work through these incrementally to improve codebase maintainability.


Add deacon/ mail identity support

Ensure deacon/ works as a mail identity in town-level beads. Test with bd mail send deacon/ and bd mail inbox --identity deacon/.


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


Polecats should auto-cleanup after MR submission

Currently polecats must manually run 'gt handoff --shutdown' after completing work. This is error-prone and leaves stale polecats around.

## Desired Flow

1. Polecat completes work
2. Polecat runs 'gt mq submit' (or similar)
3. MR is added to integration queue
4. **Polecat automatically cleans up** (no manual handoff needed)

## Implementation Options

### Option A: mq submit triggers cleanup
In 'gt mq submit':
1. Submit MR to queue
2. Automatically run cleanup (same as gt handoff --shutdown)
3. Polecat session terminates

### Option B: Refinery triggers cleanup
When Refinery picks up MR:
1. Refinery processes MR
2. Sends message to Witness: 'CLEANUP: <polecat>'
3. Witness cleans up polecat

### Option C: Molecule-driven
Define cleanup as final phase of polecat-work molecule:
1. code → test → submit-mr → cleanup

## Note
This reinforces the ephemeral model: polecats exist only for the duration of a single task.


bd create --dedup: semantic deduplication before issue creation

Before creating issue, check for semantic duplicates using AI similarity.

**From VC**: internal/deduplication/ - AI-powered batch comparison. ~300 lines.
VC had issue pollution problem: 438 issues with ~350+ spam because no early dedup.

**Gas Town implementation**: CLI flag on bd create:
```bash
bd create --dedup --title="Fix auth bug" --description="..."
```

Checks recent issues (7-day window) with AI similarity. If confidence >0.85, warns or blocks.

**Value**: Prevents pollution from parallel workers discovering same issues.

**VC lesson**: 115 issues filed in single day (Nov 2) because supervisor over-discovered without dedup. Rate limiting + dedup are essential.


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


Deacon patrol banners: visual feedback on atom transitions

Print large ASCII banners when transitioning between patrol atoms.

## Problem

When the deacon progresses through patrol atoms (steps), there is no visual feedback.
The operator cannot easily see what the deacon is doing without reading the full output.

## Desired Behavior

Print banners on step start and completion:

  INBOX-CHECK - Checking for lifecycle requests, escalations, timers
  INBOX-CHECK COMPLETE - Processed 3 messages, 0 lifecycle requests

## Benefits

1. Scanability: Operator can glance at tmux and see what is happening
2. Progress tracking: Easy to see where in the patrol loop we are
3. Debugging: Clear demarcation between steps for troubleshooting

## Implementation Options

1. In deacon CLAUDE.md: Instruct agent to print banners
2. gt patrol step start/end: Commands that print banners
3. bd mol step hooks: Automatically on step transitions

## Related

- gt-id36: Deacon Kernel
- gt-rana: Patrol System


Refinery semantic merge processing loop

Implement the Refinery semantic merge loop for processing polecat work.

## Overview

The Refinery is a fully empowered Claude Code agent that:
1. Fetches polecat branches
2. Merges to integration branch
3. Resolves conflicts semantically (re-implements if needed)
4. Runs tests
5. Escalates only when truly stuck

## Processing Loop

1. Check queue for completed tasks (polecat done, not yet merged)
2. For each task:
   - Fetch polecat branch
   - Attempt merge to integration/<swarm-id>
   - On conflict: semantic resolution (understand intent, re-implement)
   - Run tests if configured
   - On success: mark merged, file bead, notify witness
   - On failure: notify polecat (test fail) or escalate (stuck)
3. When all tasks merged: land integration to main

## Semantic Merge

When git merge fails, Refinery:
1. Reads polecat changes and beads issue
2. Understands intent
3. Re-implements on current integration branch
4. Only escalates if truly blocked

## Daemon Poking

Refinery is poked by:
- Daemon heartbeat (periodic)
- Witness notification (polecat done)
- Mayor request (check queue)

## Dependencies

Needs: gt-u1j.6 (mail), gt-u1j.3 (git), gt-u1j.14 (merge queue)

## Reference

See PGT ephemeral.py for branch operations.


context-check

Assess own context usage. If high, prepare for handoff.


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


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.


Default crew name should be something better than 'main'

When creating a new rig, the default crew worker name is 'main'. This is confusing since:

1. 'main' is also the git branch name
2. It doesn't convey that this is the user's personal workspace
3. Crew names should feel more personal/distinct

Consider alternatives like:
- 'home' - the user's home base
- 'desk' - their personal desk
- 'joe/max/etc' - actual name-based defaults
- Let user pick during rig init


Merge: gt-70b3

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


Self-test

Testing gt mail works


session-gc

Clean dead sessions. Run gt gc --sessions.


Enhanced polecat decommission prompting

Add decommission checklist to polecat AGENTS.md.template. Make crystal clear: verify ALL before signaling done.

## Checklist for AGENTS.md.template

```markdown
## Decommission Checklist

**CRITICAL**: Before signaling done, you MUST complete this checklist.
The Witness will verify each item and bounce you back if dirty.

### Pre-Done Verification

```bash
# 1. Git status - must be clean
git status
# Expected: "nothing to commit, working tree clean"

# 2. Stash list - must be empty
git stash list
# Expected: (empty output)

# 3. Beads sync - must be up to date
bd sync --status
# Expected: "Up to date" or "Nothing to sync"

# 4. Branch merged - your work must be on main
git log main --oneline -1
git log HEAD --oneline -1
# Expected: Same commit
```

### If Any Check Fails

- **Uncommitted changes**: Commit them or discard if unnecessary
- **Stashes**: Pop and commit, or drop if obsolete
- **Beads out of sync**: Run `bd sync`
- **Branch not merged**: Complete the merge workflow

### Signaling Done

Only after ALL checks pass:

```bash
bd close <issue-id>
bd sync
town mail send <rig>/witness -s "Work Complete" -m "Issue <id> done."
```
```

## Implementation

Add to AGENTS.md.template in the polecat prompting section.


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.


mol-christmas-launch: 3-day execution plan

---

## Wisp Integration Wave Plan (added 2025-12-21)

Dependency-ordered execution for gt-3x0z (Wisp Molecules) + gt-rana (Patrol System):

### Wave 1 (parallel - no blockers)
- gt-3x0z.1: gt rig init creates .beads-ephemeral/
- gt-3x0z.2: Configure bd for ephemeral molecule bonding
- gt-3x0z.3: gt doctor checks for ephemeral health
- gt-rana.1: Attachment field on pinned beads
- gt-rana.3: mol-deacon-patrol definition

### Wave 2 (after Wave 1)
- gt-3x0z.4: gt spawn --molecule bonds ephemeral (GATE 1)
- gt-rana.2: Daemon attachment detection

### Wave 3-5 (sequential)
- gt-3x0z.5, gt-3x0z.6 → gt-3x0z.7 → gt-3x0z.8 (GATE 2: squash)

### Wave 6+ (patrol integration)
- gt-rana.4: Basic patrol runner (needs gt-rana.3 + gt-3x0z.8)
- gt-3x0z.9: mol-deacon-patrol uses wisp (needs gt-rana.3 + gt-3x0z.8)
- Then: gt-rana.5-7, gt-3x0z.10-12

### Key Gates
1. gt-3x0z.4 - spawn/bond unlocks Phase 2
2. gt-3x0z.8 - squash unlocks patrol integration
3. gt-rana.4 - patrol runner unlocks Phase 2+ patrol


Track operational stats as entity/audit beads

Witness and refinery track operational stats in JSON files:
- total_merged, today_merged, total_failed (refinery)
- total_checks, total_nudges, total_escalations (witness)

For HOP entity CV tracking, these could become:
- type=audit beads (daily roll-ups of operational metrics)
- Entity chain entries showing validator activity

This provides observability and contributes to entity reputation tracking (e.g., refinery X has merged 500 PRs with 2% failure rate).

Not critical for launch but aligns with HOP Platform of Platforms vision.