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


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.


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


Rollback support: gt mq revert for problematic merges

Implement 'gt mq revert <mr-id>' for rolling back merges.

Actions:
1. Find merge commit from closed MR
2. Create revert commit: git revert <merge-commit>
3. Push revert to target branch
4. Create 'reverted' MR tracking the revert
5. Optionally reopen source issue

Options:
- --reason REASON: required explanation
- --reopen-issue: reopen the source work item

Track revert in MR metadata for audit trail.

Reference: docs/merge-queue-design.md#open-questions


Pick next branch. Rebase on current main.

```bash
git checkout -b temp origin/<polecat-branch>
git rebase origin/main
```

If rebase conflicts and unresolvable:
- git rebase --abort
- Notify polecat to fix and resubmit
- Skip to loop-check for next branch

instantiated_from: mol-refinery-patrol
step: process-branch


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


GGT Command Parity: Complete gt command coverage

Complete gt command set to match/exceed PGT town commands.

Covers: uninstall, rig info, refinery attach, witness, session mgmt, mail UX, daemon.


Phase 1.1: Attachment field on pinned beads

Add attached_molecule field to pinned bead schema.

## Schema Change
Add to handoff/pinned bead issues:
- attached_molecule: string (root issue ID of attached mol)
- attached_at: timestamp

## Implementation
- Update beads Issue struct if needed
- Or: use labels/metadata field
- Ensure bd show displays attachment info

## Acceptance
- Can set/clear attachment on a bead
- bd show displays attachment status


gt mail: Cross-level routing is broken

When Mayor sends mail to rig worker, message lands in wrong beads database. Sender's beads vs recipient's.


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.


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


Update config location: .gastown/ → config/

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

Hidden directories are poorly discovered by AI agents.


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


Consider splitting large files (800+ lines)

Several files are getting large and may benefit from splitting:
- internal/beads/beads.go (990 lines)
- internal/cmd/molecule.go (981 lines)
- internal/refinery/manager.go (934 lines)
- internal/beads/beads_test.go (883 lines)
- internal/cmd/polecat.go (836 lines)
- internal/witness/manager.go (808 lines)
- internal/cmd/mail.go (804 lines)

For .go files, consider extracting logical subsystems. For test files, this is lower priority.


bd pin command fails: invalid field for update: pinned

In beads v0.32.0, `bd pin <id>` fails with 'invalid field for update: pinned'. The pinned field exists in the schema but update logic doesn't handle it.

Repro:
```
bd create 'test issue'
bd pin <id>
# Error: invalid field for update: pinned
```

Expected: Issue should be pinned.

This blocks gt mail send --pinned from working.


Merge: gt-8v8

branch: polecat/nux
target: main
source_issue: gt-8v8
rig: gastown


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.


Phase 1.3: mol-deacon-patrol definition

Define the Deacon patrol molecule in builtin_molecules.go.

## Steps
1. inbox-check - Handle callbacks from agents
2. health-scan - Ping Witnesses and Refineries
3. plugin-run - Execute registered plugins
4. orphan-check - Find abandoned work
5. session-gc - Clean dead sessions
6. context-check - Check own context limit
7. loop-or-exit - Burn and let daemon respawn, or exit if context high

## Implementation
- Add DeaconPatrolMolecule() to builtin_molecules.go
- Add to BuiltinMolecules() list
- Test with bd mol show mol-deacon-patrol


Witness patrol: cleanup idle orphan polecats

Add patrol step to find and cleanup polecats that are idle with no assigned issue. These orphans occur when polecats crash before sending DONE or Witness misses the message. Patrol should verify git is clean before removing worktree. Part of gt-rana.


Implement gt mol status command

Show what's on an agent's hook.

```bash
gt mol status [target]
```

Output:
- What's slung (molecule name, associated issue)
- Current phase and progress
- Whether it's a wisp
- Next action hint

If no target, shows current agent's status.

Acceptance:
- [ ] Read pinned bead attachment
- [ ] Display molecule/issue info
- [ ] Show phase progress
- [ ] Indicate wisp vs durable


CLI: worker status reporting commands

Worker status reporting CLI for polecats to report progress.

## Commands

### gt worker started
```
gt worker started <issue-id> [-m MESSAGE]
```
Reports work started on issue.

### gt worker progress
```
gt worker progress <issue-id> <0-100> [-m MESSAGE]
```
Reports percentage complete.

### gt worker blocked
```
gt worker blocked <issue-id> <reason> [-m MESSAGE]
```
Reports blocked status with reason.

### gt worker completed
```
gt worker completed <issue-id> [-m MESSAGE]
```
Reports task completion.

### gt worker failed
```
gt worker failed <issue-id> <reason> [-m MESSAGE]
```
Reports task failure.

## Implementation
Each command sends mail to refinery with structured content:
```go
type WorkerStatusReport struct {
    IssueID    string
    Status     string  // started|progress|blocked|completed|failed
    Progress   int     // 0-100 for progress
    Reason     string  // for blocked/failed
    Message    string  // optional detail
    ReportedAt time.Time
}
```

## Message Format
Subject: "[STATUS] <issue-id>: <status>"
Body: JSON-encoded WorkerStatusReport

## Default Recipient
```go
// Determine from context
func getDefaultRecipient() string {
    rig := os.Getenv("GT_RIG")
    if rig != "" {
        return rig + "/refinery"
    }
    return "refinery/"
}
```

## New File
internal/cmd/worker.go

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

## Acceptance Criteria
- [ ] All 5 commands implemented
- [ ] Status sent as mail to refinery
- [ ] Structured JSON body for parsing
- [ ] Works from polecat session context


Town root .beads has schema mismatch with bd

The .beads directory at town root (/Users/stevey/gt/.beads) has an incompatible schema:

```
Error: failed to open database: failed to initialize schema: sqlite3: SQL logic error: no such column: thread_id
```

Meanwhile, gastown/.beads (symlinked to mayor/rig/.beads) works fine.

## Impact

- gt mail inbox fails at town root
- gt handoff sends mail to broken db
- Daemon can't check its inbox

## Options

1. Delete town root .beads/beads.db and let it recreate
2. Symlink town root .beads to gastown/.beads
3. Run schema migration on existing db

## Root Cause

Likely a beads version upgrade that added thread_id column, but the town root db was created before that.


Design: Plugin architecture (agents-as-plugins)

Plugin system where plugins are just additional agents with identities, mailboxes, and beads access. See docs/architecture.md Plugins section. No special framework - just directory conventions and mail-based invocation.


Create DEACON.md role context

Create ~/gt/DEACON.md with the Deacon's role context, wake cycle instructions, and command reference. Model after existing CLAUDE.md for Mayor.


Crew workers getting wrong CLAUDE.md (shows Refinery)

## Problem

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

## Evidence

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

## Expected

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

## Impact

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

## Fix

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


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/


Document the Propulsion Principle

Write canonical documentation for the Universal Gas Town Propulsion Principle.

Location: gastown/mayor/rig/docs/propulsion-principle.md

Content:
- The One Rule (hook has work → work happens)
- Why it works (stateless agents, molecule-driven)
- The sling lifecycle diagram
- Agent startup protocol
- Examples and anti-patterns

This is foundational theory-of-operation documentation.


load-context

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

Read the assigned issue ({{issue}}) and understand the requirements.
Identify any blockers or missing information.


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


Use cmd.OutOrStdout instead of fmt.Print in refinery

refinery/manager.go and refinery/engineer.go use fmt.Print/Println directly for user output (30+ occurrences). This breaks testability and doesn't follow cobra best practices. Should use cmd.OutOrStdout() or pass an io.Writer.

Affected files:
- internal/refinery/manager.go (lines 222, 360-361, 369, 387-393, 519, 537, 672)
- internal/refinery/engineer.go (lines 190-211, 249, 294-297, 325-353, 362-366)


Recover stale polecat work: 4 branches with unpushed commits

During observation of polecat workflow, found 4 polecats with unpushed work:

## Branches Pushed (preserved)
- polecat/capable: 3 commits (molecule catalog, doctor orphan detection, gt done)
- polecat/dementus: 4 commits (Witness MVP, handoff fixes)
- polecat/furiosa: 2 commits (bulk polecat removal, spawn handoff)
- polecat/rictus: 1 commit (molecule docs)

## Action Required
1. Review each branch for merge-worthiness
2. Either:
   a. Create PRs for valuable work
   b. OR discard if superseded
3. After decision, clean up polecats properly

## Root Cause
Polecats were not cleaned up after previous work sessions. This is exactly why we need:
- gt-u1k: gt shutdown should fully cleanup polecats
- gt-8v8: Refuse to lose uncommitted work
- gt-9nf: Always create fresh polecats


gt spawn should auto-start refinery/witness if not running

When spawning a polecat, the infrastructure (refinery, witness) should already be running.

## Current Behavior
spawn.go only:1. Assigns issue to polecat2. Sends work mail3. Starts polecat session

The refinery and witness must be started separately.

## Expected Behavior (per user feedback)
When spawning a polecat, if the rig's refinery or witness is not running, auto-start them.

## Options

### Option A: spawn auto-starts infrastructure
Check if refinery/witness running before spawn, start if not.

### Option B: gt swarm start <rig> command
Explicit command that:
1. Starts refinery
2. Starts witness
3. Optionally spawns polecats on bd ready issues

### Option C: gt rig start <rig>
Similar to Option B but as rig lifecycle command.

## Related
- gt-n7z7: refinery --foreground race condition bug
- gt-u1j.18: witness CLI commands


gt molecule seed: create built-in molecules as beads

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

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

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

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

## Parent
gt-4nn: Molecules epic


Update architecture.md: Engineer role and Beads merge queue

Update docs/architecture.md with recent design decisions:

1. Agent table: Change "Refinery" role to "Engineer"
   - Refinery = place/module/directory
   - Engineer = role (agent that works in the Refinery)

2. Merge Queue section: Document Beads-native model
   - MRs are beads issues with --type=merge-request
   - gt mq commands (submit, list, next, process, reorder)
   - Ordering via depends-on links

3. CLI section: Add gt mq commands

4. Key Design Decisions: Add decisions for:
   - #15: Merge Queue in Beads
   - #16: Engineer role (distinct from Refinery place)
   - #17: Session restart protocol for Engineer


Refinery startup: bond mol-refinery-patrol on start

Wire up Refinery to automatically bond its patrol molecule on startup.

## Current state
- mol-refinery-patrol exists in builtin_molecules.go
- prompts/roles/refinery.md describes the protocol
- Refinery doesn't auto-bond on startup

## Desired behavior
On Refinery session start:
1. gt prime detects RoleRefinery
2. Check for existing in-progress patrol: bd list --status=in_progress --assignee=refinery
3. If found: resume from current step
4. If not found: bd mol bond mol-refinery-patrol --wisp
5. Output patrol context to agent

## Implementation options
A) Add to gt prime (outputRefineryPatrolContext)
B) Add startup hook in refinery CLAUDE.md
C) Both (prime detects, template reinforces)

## Testing
- Start refinery session
- Verify patrol bonds automatically
- Kill mid-patrol, restart, verify resumes

## Depends on
- gt-3x0z.10 (existing issue for Refinery patrol)


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.


Speed up test suite

Tests are running slow during MQ processing. Investigate and optimize:
- Profile test execution time
- Look for slow tests (network, file I/O, sleeps)
- Consider parallel test execution
- Cache expensive setup where possible


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

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


Design molecule step hooks

Hooks that fire between molecule steps. When a bead in a molecule closes, trigger hook that can spawn agent attention to prompts/requests. This enables reactive orchestration - the molecule drives, hooks respond. Gas Town feature built on Beads data plane.


Add crew lifecycle support to Deacon

Currently crew sessions (like gastown/crew/max, beads/crew/dave) are marked as 'human-managed' and excluded from automated lifecycle.

## Current State
- `getManager(RoleCrew)` returns `"human"` (handoff.go:273)
- `gt handoff` for crew just prints a message, no lifecycle request sent
- Crew cannot request automated refresh from Deacon

## Desired State
Crew members should be able to request lifecycle actions from Deacon:
- `gt handoff --cycle` sends request to Deacon, which kills/restarts the crew session
- Useful when: context full, running molecules that need fresh session, automation

## Implementation
1. Change `getManager(RoleCrew)` to return `"deacon/"` (or new crew manager)
2. Teach Deacon to recognize crew session naming pattern
3. Add crew to Deacon's respawn loop handling
4. Test with `gt nudge` for reliable message delivery

## Use Case
Running a molecule (e.g., version-bump) and discovering mid-workflow that a fresh session is needed. Agent should be able to request refresh automatically rather than requiring human intervention.

## Related
- gt nudge: reliable message delivery to Claude sessions
- bd mol bond: molecule instantiation (coming in beads)


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

## Problem

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

## Evidence

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

## Impact

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

## Fix

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


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


Update Mayor startup protocol for bootstrap

Teach Mayor to respond to "boot up gas town" by slinging mol-gastown-boot.

## Current Protocol

1. Announce: "Mayor, checking in."
2. Check mail
3. If handoff, continue
4. Await user instruction

## Enhanced Protocol

1. Announce: "Mayor, checking in."
2. Check mail
3. If handoff, continue
4. **If user says "boot"/"startup"/"bootstrap":**
   - Sling mol-gastown-boot as wisp
   - Execute verification-gated steps
   - Report town status when complete
5. Otherwise await instruction

## Trigger Phrases

- "boot up gas town"
- "bootstrap the town"
- "start gas town"
- "bring up the town"

## Execution

Mayor runs the molecule manually (not via subagent):

```
1. Bond the proto: bd mol bond mol-gastown-boot --wisp
2. For each step:
   a. Run action command
   b. Loop verification with backoff
   c. On stall, run recovery
   d. Close step when verified
3. Squash wisp with summary
4. Report: "Gas Town is up. All agents healthy."
```

## CLAUDE.md Update

Add to Mayor CLAUDE.md:

```markdown
## Bootstrap Command

When user requests town bootstrap:
1. gt sling gastown-boot mayor/ --wisp
2. Execute molecule steps with verification
3. Keep trying until all agents healthy
4. No timeouts - you are the town engineer
```


Harness & Priming: Document architecture and update all role contexts


gt mail read: Support numeric indices for message ID

Allow 'gt mail read 1' to read the first message in inbox.

Current behavior requires full message ID like 'msg-abc123'.
Should support:
- Numeric index: 'gt mail read 1' reads first/newest message
- Partial ID match: 'gt mail read abc' matches 'msg-abc123'

This is a UX improvement - agents frequently type 'gt mail read 1'.


Workspace detection: Find() walking up directory tree

Find workspace root by walking up directory tree looking for Gas Town markers.

## Detection Logic

Walk up from current directory, looking for:
1. `config/town.json` - Primary marker (visible config dir per gt-iib)
2. `mayor/` directory at town level - Secondary marker

Stop at filesystem root if neither found.

## Interface

```go
// Find locates the town root from the given directory
func Find(startDir string) (string, error)

// FindOrError is like Find but returns a user-friendly error
func FindOrError(startDir string) (string, error)
```

## Return Values

- Success: Absolute path to town root
- Not found: Empty string + specific error
- Error: Empty string + wrapped error

## Edge Cases

- Symlinks: Follow them (use filepath.EvalSymlinks)
- Permissions: Return error if can't read directory
- Nested towns: Return nearest ancestor (shouldn't happen in practice
EOF
)


Fetch and conflict check: git operations for MR

Implement git operations for MR processing.

Steps:
1. git fetch origin <mr.branch>
2. git checkout <mr.target> (main or integration/xxx)
3. git merge --no-commit --no-ff <mr.branch> (test merge)
4. Check for conflicts
5. If conflicts: abort and return Failure(conflict, files)
6. If clean: abort (actual merge in next step)

Helper functions:
- FetchBranch(branch string) error
- CheckConflicts(source, target string) ([]string, error)

Reference: docs/merge-queue-design.md#process-merge-steps


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 message

Testing GGT mail integration


gt molecule current: Show what agent should be working on

Query what an agent identity is supposed to be working on via breadcrumb trail.

## Command
```bash
gt molecule current <identity>
gt molecule current gastown/furiosa
gt molecule current deacon
```

## Logic
1. Find handoff bead for identity (pinned bead titled "<role> Handoff")
2. Parse attachment field → molecule ID
3. If no attachment → "naked" (no active molecule)
4. If attached → load molecule, find current step:
   - bd ready --parent=<mol-id> → next unblocked step
   - Or first in_progress step

## Output
```
Identity: gastown/furiosa
Handoff:  gt-8v2 (Furiosa Handoff)
Molecule: gt-mol-abc (mol-polecat-work)
Progress: 3/8 steps complete
Current:  gt-mol-abc.4 - verify-tests
```

Or if naked:
```
Identity: gastown/angharad
Handoff:  gt-9x1 (Angharad Handoff)
Molecule: (none attached)
Status:   naked - awaiting work assignment
```

## Use Cases
- Mayor checking what polecats are doing
- Witness verifying polecat progress
- Debug: "why isnt this polecat working?"
- Deacon patrol: track all agent states


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.


Merge: gt-3x1.3

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


mol-swarm-cleanup: Post-swarm debris cleanup molecule

After a 20+ worker swarm completed, found significant beads debris:
- 18 stale messages (work assignments, lifecycle requests, swarm instructions)
- 3 completed issues still open/in_progress
- Test messages accumulated

Need: Document a post-swarm checklist or create gt swarm cleanup command that:
1. Closes stale work assignment messages
2. Reviews in_progress issues for completion
3. Closes orphaned lifecycle messages
4. Optionally archives test messages


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


VM outpost setup: Terraform and documentation

## Overview

Documentation and optional Terraform for setting up VM outposts.

## Manual Setup Guide

```markdown
# Setting Up a VM Outpost

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

## Steps

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

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

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

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

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

## Terraform Module (Optional)

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

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

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

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

  network_interface {
    network = "default"
    access_config {}
  }

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

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

## Files

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

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


Fix failing beads tests: TestIntegration and TestPolecatWorkMolecule


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


Implement gt mq CLI commands

Add gt mq subcommands as sugar over bd:

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

All commands should work with the Beads data plane.


Port Gas Town to Go

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

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

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


gt mq submit: create MR from current branch

Implement 'gt mq submit' command that creates a merge-request bead.

Auto-detection logic:
1. Branch: current git branch
2. Issue: parse from branch name (polecat/Nux/gt-xyz → gt-xyz)
3. Target: main (or integration branch if --epic specified)
4. Worker: parse from branch name
5. Rig: current rig

Options:
- --branch BRANCH: explicit source branch
- --issue ISSUE: explicit source issue
- --epic EPIC: target integration/EPIC instead of main
- --priority P: override priority (default: inherit from source issue)

Creates merge-request bead and prints MR ID.

Reference: docs/merge-queue-design.md#creating-merge-requests


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


CLI: rig commands (add, list, show, remove)

GGT needs rig management commands.

Missing Commands:
- gt rig add <git-url> [--name NAME] - Add rig to workspace
- gt rig list - List all rigs with status
- gt rig show <rig> - Show detailed rig info
- gt rig remove <rig> - Remove a rig

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

Current State:
- gt init creates rig structure but no ongoing management
- Rig discovery in internal/rig/manager.go but not exposed via CLI


Add Gas Town theory of operation to all role primings

All roles (Mayor, Witness, Refinery, Polecat) should get basic GT architecture context: harness, rigs, agents, mail, beads workflow


gt swarm: Not discovering tasks from epic dependents

gt swarm create/start shows '0 tasks loaded' even when epic has dependents.

Repro:
1. Create epic gt-hw6
2. Create tasks and add deps: bd dep add gt-xxx gt-hw6
3. gt swarm create gastown --epic gt-hw6 --worker Toast
4. Swarm shows 'Tasks: 0'

Expected: Swarm should discover tasks that depend on the epic.
Actual: Shows '(no tasks loaded)'


detectSender() doesn't recognize crew workers

## Problem

`detectSender()` in mail.go only detects polecats, not crew workers.

## Current Code (line 445)

```go
// If in a rig's polecats directory, extract address
if strings.Contains(cwd, "/polecats/") {
    // extract rig/polecat
}

// Default to mayor
return "mayor/"
```

## Symptom

Emma (crew worker at `/Users/stevey/gt/beads/crew/emma`) runs:
- `gt mail inbox` → checks `mayor/` inbox (wrong!)
- Should check `beads/emma` or `beads/crew/emma`

## Fix

Add crew detection:
```go
// If in a rig's crew directory, extract address
if strings.Contains(cwd, "/crew/") {
    parts := strings.Split(cwd, "/crew/")
    if len(parts) >= 2 {
        rigPath := parts[0]
        crewName := strings.Split(parts[1], "/")[0]
        rigName := filepath.Base(rigPath)
        return fmt.Sprintf("%s/%s", rigName, crewName)
    }
}
```

## Also Check

- `gt prime` correctly detects role, so there may be another detection function that works
- Should unify detection logic


Add await-work and plugin-run steps to mol-refinery-patrol

## Context

The mol-refinery-patrol needs two additional steps:

### 1. await-work (Decision Point)

Not just 'wait for signal' but a crossroads:
- Check for pending signals (mail, nudge, human)
- Check plugin gates (any plugins need to run?)
- Check maintenance schedule (daily/weekly due?)
- Evaluate context and decide next action

Position: Between burn-or-loop and inbox-check (the loop point)

### 2. plugin-run (Like Deacon)

Refinery needs plugin support for:
- **Monitors**: Flag PRs touching sensitive code
- **Gates**: Block merges under certain conditions
- **Schedulers**: Reorder queue based on priority/urgency
- **Maintenance**: Weekly cleanup, audits, stats
- **Audits**: Log merge statistics, track patterns

Position: After queue-scan, before process-branch

## Implementation

Update RefineryPatrolMolecule() in builtin_molecules.go to include:
- await-work step with decision tree
- plugin-run step with gate types

## Related
- gt-7920 (original mol-refinery-patrol)
- docs/deacon-plugins.md (existing plugin model)


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


Add no-PR instructions to mol-polecat-work at two points

Update mol-polecat-work in builtin_molecules.go to explicitly forbid GitHub PRs.

## Two Points to Add Instructions

### 1. submit-work step
When polecat is ready to submit:
- Push branch to origin
- Create beads merge-request issue
- DO NOT use gh pr create or GitHub PRs

### 2. CLAUDE.md polecat context
Add to polecat role instructions:
- Never use gh pr create
- Never create GitHub pull requests
- The Refinery processes merges via beads MR issues

## Why Two Points
- Molecule step description guides the workflow
- CLAUDE.md reinforces at context level
- Belt and suspenders approach

## Implementation
1. Update PolecatWorkMolecule() submit-work step description
2. Update prompts/roles/polecat.md with explicit prohibition

Related: gt-44wh (general no-PR bug)


Add event-triggered lifecycle wisps

Commands like `gt town start` should auto-trigger lifecycle wisps.

## Event → Wisp Mapping

| Command | Triggers Wisp |
|---------|---------------|
| gt town start | mol-gastown-boot |
| gt rig add <name> | mol-rig-spinup (param: rig=<name>) |
| gt rig remove <name> | mol-rig-spindown (param: rig=<name>) |
| gt daemon restart | mol-gastown-boot |

## Implementation

1. Command implementation checks for lifecycle proto
2. Auto-slings wisp to Mayor with --wisp flag
3. Mayor picks up via molecule attachment

## Alternative: Explicit Sling

Keep commands simple, require explicit:

```bash
gt town start        # Just starts sessions
gt sling gastown-boot mayor/ --wisp   # Bootstrap with verification
```

This is more transparent - user sees the sling happen.

## Recommendation

Start with explicit sling. Add auto-trigger later once pattern proven.
`gt town start --verified` could trigger the wisp.


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


Outpost integration tests: Mock and E2E testing

## Overview

Integration tests for the outpost system.

## Mock Outpost

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

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

## Unit Tests

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

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

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

## Integration Tests (with mocked Cloud Run)

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

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

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

## E2E Tests (requires real Cloud Run)

```go
// +build e2e

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

## Files

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

## Dependencies

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


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


Clean up ~/gt root directory cruft

The town root has accumulated various directories and files that need review:

**To evaluate:**
- daemon/ - old daemon code? move or delete?
- deacon/ - WIP deacon work? consolidate with gastown?
- mayor/ - old mayor structure?
- AGENTS.md - is this still used?
- gt binary at root - should be gitignored

**Already correct:**
- .beads/, .claude/, .gastown/, .runtime/ - runtime dirs
- beads/, gastown/ - rig directories
- docs/hop/ - strategic docs
- CLAUDE.md - mayor context

Clean up, consolidate, update .gitignore as needed.


Overseer Crew: User-managed persistent workspaces

## Overview

Crew workers are the overseer's (human's) personal workspaces within a rig. Unlike polecats which are witness-managed and ephemeral, crew workers are:

- **Persistent**: Not auto-garbage-collected
- **User-managed**: Overseer controls lifecycle
- **Long-lived identities**: dave, emma, fred - recognizable names
- **Gas Town integrated**: Mail, handoff mechanics work
- **Tmux optional**: Can work in terminal directly

## Directory Structure

```
<rig>/
  polecats/       # Managed workers (witness controls)
  refinery/       # Merge queue processor
  witness/        # Pit boss
  crew/           # Overseer's personal workspaces
    dave/         # Full clone, persistent
    emma/         # Full clone, persistent
    fred/         # Full clone, persistent
```

## Key Differences from Polecats

- Location: crew/ instead of polecats/
- Lifecycle: User-managed, not witness-managed
- Auto-cleanup: Never (polecats auto-cleanup on swarm land)
- Issue assignment: Optional (polecats require it)
- Tmux: Optional (polecats require it)
- Mail & Handoff: Yes for both
- Identity: Persistent (polecats are ephemeral)

## CLI Commands

- gt crew add <name> [--rig <rig>] - Create crew workspace
- gt crew list [--rig <rig>] - List crew workspaces
- gt crew at <rig>/<name> - Attach to workspace (start session)
- gt crew attach <name> - Attach (infer rig from cwd)
- gt crew refresh <name> - Handoff + restart (context cycling)
- gt crew remove <name> [--force] - Remove workspace
- gt crew status [<name>] - Show workspace status

## Design Notes

- Crew workers use full git clones (not worktrees)
- Optional beads integration via BEADS_DIR
- Mail-to-self handoff works for context cycling
- No witness monitoring or nudging
- No automatic issue assignment required

## Background

Users often maintain separate repo clones for serial agent work. This is tedious to set up manually. Crew workspaces bring these into Gas Town's infrastructure while keeping user control.


Test2

Testing gt mail


Define Engineer as the Refinery agent role

Clarify the distinction:

- **Refinery** = place/module/directory/workspace
  - `<rig>/refinery/` directory structure
  - `gt refinery start/stop/status` commands
  - tmux session name: `refinery` or `<rig>-refinery`

- **Engineer** = role/agent who works in the Refinery
  - CLAUDE.md prompting: "You are an Engineer..."
  - Documentation: "The Engineer processes merge requests..."
  - Mail address: `<rig>/engineer` (or `<rig>/refinery`?)

Updates needed:
- Add Engineer role description to docs
- Update CLAUDE.md templates for refinery agents
- Keep `gt refinery` commands as-is (they manage the place)
- Internal code stays `internal/refinery/` (the module)

Future consideration: Multiple Engineers in one Refinery for parallel merge processing.


Merge to main and push immediately.

Merge to main and push immediately.

```bash
git checkout main
git merge --ff-only temp
git push origin main
git branch -d temp
git push origin --delete <polecat-branch>
```

Main has moved. Any remaining branches need rebasing on new baseline.

instantiated_from: mol-refinery-patrol
step: merge-push


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.


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)


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


Merge: gt-odvf

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


gt crew add: Create crew workspace

Implement 'gt crew add <name> [--rig <rig>]' command:
- Clone repo into <rig>/crew/<name>/
- Create feature branch (optional, or stay on main)
- Register for mail delivery
- Initialize CLAUDE.md with crew worker prompting
- Do NOT register with witness (user-managed)


Phase 2: Quiescent agents (Witness, Refinery)

Enable Witness and Refinery to sleep until triggered.

## Scope
- Wake triggers: gt spawn, MR submit, mail, Deacon ping
- Wake SLA: <1 minute
- mol-witness-patrol and mol-refinery-patrol definitions
- Quiescent entry/exit protocol

## Implementation
- Kill tmux session on quiescent entry
- Preserve sandbox (worktree, state files)
- Restart session on wake trigger
- Agent checks pinned, spawns default patrol if naked

Depends: Phase 1 complete


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

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


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


Remove gt mail dependency on bd mail commands

Replaced bd mail send/inbox/read/ack with bd create/list/show/close. Messages are now stored as beads issues with type=message.


Pinned bead naming convention for all roles

Establish consistent naming convention for pinned beads across all roles.

## Problem

Every role needs a pinned bead for molecule attachment, handoff state, and identity.
We need a consistent naming convention so gt tools can find the pinned bead for any role.

## Proposed Convention

  {role}.pinned                    - Singleton roles (deacon, mayor)
  {rig}-{role}.pinned              - Per-rig roles (witness, refinery)
  {rig}-{name}.pinned              - Named agents (polecats, crew)

Examples:
- deacon.pinned
- mayor.pinned
- gastown-witness.pinned
- gastown-refinery.pinned
- gastown-furiosa.pinned (polecat)
- gastown-max.pinned (crew)

## Storage Location

| Role | Pinned Bead Location |
|------|---------------------|
| Mayor | ~/gt/.beads/ (town level) |
| Deacon | ~/gt/.beads/ (town level) |
| Witness | {rig}/.beads/ (rig level) |
| Refinery | {rig}/.beads/ (rig level) |
| Polecat | {rig}/polecats/{name}/.beads/ |
| Crew | {rig}/crew/{name}/.beads/ |

## Why This Matters

1. Name recycling: Polecats reuse names so their pinned beads persist
2. Molecule attachment: gt mol bond needs to find the pinned bead
3. gt prime: Can show you are working on X from pinned bead
4. Crash recovery: Resume from pinned beads attached molecule

## Tasks

- Document naming convention in architecture.md
- Update gt prime to find pinned bead by convention
- Update gt mol bond to use conventional name
- Create pinned beads on role startup if missing
- Add gt pinned command to show/manage pinned beads

## Related

- gt-rana.1: Attachment field on pinned beads (closed)
- gt-id36.2: Deacon marching orders (pinned bead)


gt polecat add: should handle existing branch gracefully

## Problem

`gt polecat add gastown Nux` fails if the branch `polecat/Nux` already exists.

## Current Behavior

```
fatal: a branch named 'polecat/Nux' already exists
```

## Expected Behavior

Should either:
1. Reuse the existing branch
2. Or prompt to delete/recreate
3. Or auto-suffix: polecat/Nux-2

## Context

Branch may exist from previous polecat that was removed but branch wasn't cleaned up.


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

Emergency kill command for swarm operations.

## Commands

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

## Implementation

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

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

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

## Output

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

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

## Safety

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

## Dependencies

Needs: Tmux wrapper, Session management, Polecat management


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.


Merge: gt-70b3

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


Daemon: verify requesting_cycle before kill

Per gt-gby spec, daemon should verify agent state shows requesting_cycle=true before killing session. Currently kills on any lifecycle request without verification.

Requires state.json in agent workspace with requesting_cycle field.


Merge: gt-rana.1

branch: polecat/rictus
target: main
source_issue: gt-rana.1
rig: gastown


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


Workspace doctor checks: Config, state, mail, Mayor, rigs

Workspace-level doctor checks.

## Checks

### ConfigExists
- Verify config/ directory exists
- Verify config/town.json exists
- Fix: Cannot auto-fix (need gt install)

### ConfigValid
- Parse town.json
- Verify required fields (type, version, name)
- Verify type == "town"
- Fix: Cannot auto-fix

### RigsRegistryValid
- Parse config/rigs.json
- Verify each registered rig directory exists
- Warn on missing rigs
- Fix: Remove missing rigs from registry

### MayorExists
- Verify mayor/ directory exists
- Verify mayor/CLAUDE.md exists
- Fix: Create from template

### MayorMailboxExists
- Verify mayor/mail/ directory exists
- Verify mayor/mail/inbox.jsonl exists (can be empty)
- Fix: Create directory and empty file

### MayorStateValid
- Parse mayor/state.json if exists
- Verify valid JSON
- Fix: Reset to default state

## Implementation

```go
var WorkspaceChecks = []Check{
    &ConfigExistsCheck{},
    &ConfigValidCheck{},
    &RigsRegistryValidCheck{},
    &MayorExistsCheck{},
    &MayorMailboxExistsCheck{},
    &MayorStateValidCheck{},
}
```


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


Witness startup: bond mol-witness-patrol on start

Wire up Witness to automatically bond its patrol molecule on startup.

## Desired behavior
On Witness session start:
1. gt prime detects RoleWitness
2. Check for existing in-progress patrol
3. If found: resume from current step
4. If not found: bd mol bond mol-witness-patrol --wisp
5. Output patrol context to agent

## Depends on
- gt-83k0 (mol-witness-patrol definition)
- gt-caih (handoff bead state persistence)


Refinery background daemon mode

The refinery 'gt refinery start' command only works in foreground mode (--foreground). Need to implement background daemon mode for production use.

Options:
1. Use a separate tmux session for the refinery
2. Implement proper daemonization
3. Use Claude Code session for the refinery agent

For MVP, option 1 (tmux session) is probably simplest.


Create mol-refinery-patrol with test failure gates

## Problem

The Refinery role lacks:
1. A formal patrol molecule (Deacon has mol-deacon-patrol)
2. Engineer identity/philosophy guidance
3. Structural enforcement of failure handling (not just documentation)

When tests fail during merge processing, the current guidance doesn't prevent 'disavowal' - noting a problem exists and proceeding without tracking it.

## The Structural Solution

Create `mol-refinery-patrol` with **verification gates** that make disavowal structurally impossible:

```markdown
## Molecule: mol-refinery-patrol

## Step: inbox-check
Check mail for MR submissions, escalations, messages.
Process any urgent items first.

## Step: queue-scan
Fetch remote, identify polecat branches waiting.
If queue empty, skip to context-check.
Track branch list for this cycle.
Needs: inbox-check

## Step: process-branch
Pick next branch. Rebase on current main.
If rebase conflicts: notify polecat, skip to next branch.
Needs: queue-scan

## Step: run-tests
Run go test ./...
Track results (pass/fail count).
Needs: process-branch

## Step: handle-failures
**GATE**: If tests passed, this step auto-completes.
If tests failed:
- Diagnose: branch regression or pre-existing?
- Branch regression → abort, notify polecat
- Pre-existing → EITHER fix OR \`bd create --type=bug\`
**VERIFY**: Fix committed OR bead filed before proceeding.
Needs: run-tests

## Step: merge-push
Merge to main (ff-only preferred).
Push immediately.
Delete polecat branch.
Needs: handle-failures

## Step: loop-check
More branches? Return to process-branch.
Otherwise continue to summary.
Needs: merge-push

## Step: generate-summary
Summarize cycle: branches processed, tests results, issues filed.
Needs: loop-check

## Step: context-check
Check context usage.
If high: write handoff, prepare for burn/respawn.
Needs: generate-summary

## Step: burn-or-loop
If context LOW and queue non-empty: loop (return to queue-scan)
If context HIGH or queue empty: burn and exit
Needs: context-check
```

## Implementation Tasks

### 1. Add RefineryPatrolMolecule() to builtin_molecules.go
- Copy pattern from DeaconPatrolMolecule
- Include all steps with proper `Needs:` dependencies
- Emphasize handle-failures gate in description

### 2. Update Refinery CLAUDE.md
Add sections:
- **The Engineer Mindset** (identity/philosophy)
- **The Scotty Test** (Would Scotty walk past this?)
- **Patrol Molecule** reference (mol-refinery-patrol)
- **Test Failure Protocol** (explicit decision tree)

### 3. Create prompts/roles/refinery.md
- Follow pattern of deacon.md
- Include patrol execution loop diagram
- Reference the molecule

### 4. Consider mol-witness-patrol
- Witness has detailed heartbeat protocol but no formal molecule
- Similar pattern could apply

## Why This Matters

GUPP: 'If you have work on your hook, you have to run it.'
The patrol molecule puts failure handling ON THE HOOK as a mandatory step.
You can't skip handle-failures to get to merge-push.
The structure enforces good behavior.

## Related
- gt-7919: Fix failing beads tests (discovered during this session)
- mol-deacon-patrol (existing pattern to follow)
- docs/molecular-chemistry.md (chemistry concepts)
- docs/wisp-architecture.md (wisp vs mol decision)


Immediate daemon notification on lifecycle request

When gt handoff sends a lifecycle request to the daemon (via bd mail send deacon/), the daemon only discovers it on its next heartbeat poll (every 5 min). Workers wait unnecessarily for retirement.

## Current Behavior
1. Worker runs gt handoff
2. Lifecycle mail sent to deacon/ via beads
3. Worker blocks waiting for retirement
4. Daemon heartbeat runs (up to 5 min later)
5. Daemon processes lifecycle request and kills session

## Expected Behavior
Lifecycle requests should be processed immediately, not on poll interval.

## Proposed Solutions

### Option A: SIGUSR1 Signal (simplest)
1. Add SIGUSR1 handler in daemon that calls ProcessLifecycleRequests()
2. gt handoff reads daemon.pid and sends SIGUSR1 after mail send

### Option B: Unix Socket
1. Daemon listens on ~/gt/daemon/lifecycle.sock
2. gt handoff connects and sends 'process' message

### Option C: Watchfile + fsnotify
1. gt handoff touches ~/gt/daemon/lifecycle.trigger
2. Daemon uses fsnotify to detect and process

Recommend Option A for simplicity.

## Affected Files
- internal/daemon/daemon.go - add signal handler
- internal/cmd/handoff.go - send signal after mail


CLI: core commands (status, prime, version, init)

Essential CLI commands for Gas Town operation.

## Commands

### gt status
Show overall town status.
```
gt status [--json]
```
Output:
- Town name and location
- Number of rigs
- Active polecats across all rigs
- Witness status per rig
- Recent activity summary

### gt prime
Output role context for current directory.
```
gt prime
```
Detects role from directory:
- Town root or mayor/ → Mayor context
- <rig>/witness/rig/ → Witness context
- <rig>/refinery/rig/ → Refinery context
- <rig>/polecats/<name>/ → Polecat context

### gt version
Show version information.
```
gt version [--short]
```
Output: version, git commit, build date.

### gt init
Initialize current rig for Gas Town (alternative to gt install for existing repos).
```
gt init [--force]
```
Creates Gas Town structure in existing git repo.

## Implementation

Each command is a Cobra subcommand under root:
```go
var statusCmd = &cobra.Command{...}
var primeCmd = &cobra.Command{...}
var versionCmd = &cobra.Command{...}
var initCmd = &cobra.Command{...}
```

Register in cmd/gt/main.go.


Document harness concept in docs/harness.md

Create comprehensive harness documentation:
- What is a harness (installation directory)
- Recommended structure and naming
- .beads/redirect for default project
- config/ contents (rigs.json, town.json)
- Mayor home vs rig-level mayor/
- Example configurations
- Relationship to rigs


Add human escalation configuration

Add overseer contact config to mayor/town.json. Deacon uses this for escalation when it cannot resolve issues. Support email and/or other notification methods.


Implement shared beads architecture for rig

Implement redirect-based shared beads to eliminate git sync overhead within a rig.

## Background
Each polecat currently has its own .beads/ directory synced via git. This burns tokens on sync operations.

## Solution
Use bd's redirect feature:
1. Create single shared .beads/ at rig root
2. Polecats get redirect files pointing to shared location
3. All agents connect to same daemon
4. SQLite WAL + daemon serialization handles concurrency

## Implementation
1. Create shared .beads/ at rig root (e.g., ~/gt/gastown/.beads/)
2. Update gt spawn to create redirect files:
   mkdir -p polecats/nux/.beads
   echo ../../.beads > polecats/nux/.beads/redirect
3. Test that all polecats connect to same daemon
4. Remove git sync from intra-rig workflow
5. Keep JSONL export for backup/cross-rig only

## Reference
beads/polecats/rictus/internal/beads/beads.go:45 - followRedirect()


Add agent abstraction layer to support non-Claude agents

Currently Gas Town hardcodes 'claude --dangerously-skip-permissions' throughout the codebase for spawning agents. We should add an abstraction layer to support other AI agents (e.g., Gemini CLI, OpenAI agents, local models).

Locations that spawn Claude:
- internal/cmd/mayor.go:131
- internal/cmd/deacon.go:150
- internal/cmd/witness.go:280
- internal/cmd/crew.go (multiple locations)
- internal/cmd/up.go:190, 229
- internal/session/manager.go:146
- internal/refinery/manager.go:207

Suggested approach:
1. Create an agent package with an interface
2. Add configuration for agent type in town/rig config
3. Replace hardcoded claude commands with agent.Spawn() calls
4. Support agents: claude, gemini, openai, local (ollama, etc.)


Test: Alpha to Beta

Sibling communication test


Define constants for magic strings

Several magic strings are hardcoded throughout the codebase:
- "mayor" appears in 20+ places as a path component
- "main" branch name appears in 20+ places
- "beads-sync" branch name in multiple files
- "rigs.json" filename

These should be constants in a central location for maintainability.


Per-rig beads repo configuration

Add per-rig beads configuration to rig config schema.

## Config Schema

In each rig's config.json:

```json
{
  "version": 1,
  "name": "wyvern",
  "git_url": "https://github.com/steveyegge/wyvern",
  "beads": {
    "repo": "local",    // "local" | "<path>" | "<git-url>"
    "root": null,       // Override bd --root (optional)
    "prefix": "wyv"     // Issue prefix for this rig
  }
}
```

## Repo Options

| Value | Meaning | Use Case |
|-------|---------|----------|
| `"local"` | Use project's `.beads/` | Own projects, full commit access |
| `"<path>"` | Use beads at path | OSS contributions |
| `"<git-url>"` | Clone and use repo | Team shared beads |

## Environment Injection

When spawning polecats, Gas Town sets:
```bash
export BEADS_ROOT="<resolved-path>"
```

## Resolution Logic

```go
func ResolveBeadsRoot(rigConfig *RigConfig, rigPath string) (string, error) {
    beads := rigConfig.Beads
    switch {
    case beads.Root != "":
        return beads.Root, nil
    case beads.Repo == "local" || beads.Repo == "":
        return filepath.Join(rigPath, ".beads"), nil
    case strings.HasPrefix(beads.Repo, "/"):
        return beads.Repo, nil
    case strings.Contains(beads.Repo, "://"):
        return cloneAndResolve(beads.Repo)
    default:
        return filepath.Join(rigPath, beads.Repo), nil
    }
}
```

## Backwards Compatibility

If `beads` section missing, assume `"repo": "local"`.


Test molecule

Test


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.


Mayor session cycling prompting

Add session cycling section to Mayor CLAUDE.md template.

## When to Cycle

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

## Composing Handoff Notes

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

2. Compose note with this structure:

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

## Active Swarms
<per-rig swarm status>

## Rig Status
<table of rig health>

## Pending Escalations
<issues needing your decision>

## In-Flight Decisions
<decisions being made>

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

## Delegated Work
<work sent to refineries>

## User Requests
<pending user asks>

## Next Steps
<what next session should do>

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

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

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


Mail system: message types and threading

GGT mail system needs message types and threading like PGT.

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

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

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

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

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

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

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

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


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.


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


Swarm state persistence: manifest + state + events

Upgrade swarm persistence to match PGT pattern.

## Current State
Single .gastown/swarms.json with all swarms.

## Target State (per-swarm)
```
.gastown/swarms/
└── <swarm-id>/
    ├── manifest.json  # Immutable config
    ├── state.json     # Mutable progress
    ├── events.jsonl   # Audit log
    └── report.md      # Generated report
```

## File Schemas

### manifest.json (immutable after creation)
```json
{
  "id": "sw-1",
  "title": "Epic description",
  "epic_id": "gt-abc",
  "rig": "gastown",
  "base_commit": "abc123",
  "integration_branch": "swarm/sw-1",
  "target_branch": "main",
  "workers": ["Toast", "Nux"],
  "tasks": [{"id": "gt-xyz", "title": "..."}],
  "created_at": "2024-01-01T00:00:00Z"
}
```

### state.json (mutable)
```json
{
  "state": "active",
  "task_states": {
    "gt-xyz": {"state": "merged", "assignee": "Toast"}
  },
  "updated_at": "2024-01-01T01:00:00Z",
  "error": null
}
```

### events.jsonl (append-only audit)
```jsonl
{"event": "created", "ts": "...", "data": {...}}
{"event": "task_assigned", "ts": "...", "data": {...}}
{"event": "task_merged", "ts": "...", "data": {...}}
```

## Benefits
- Audit trail via events.jsonl
- Manifest immutability prevents corruption
- Cleaner separation of concerns
- Per-swarm isolation

## Migration
Keep backward compat with swarms.json during transition.

## Files to Modify
- internal/swarm/manager.go: Refactor persistence
- internal/cmd/swarm.go: SwarmStore → directory-based

## Acceptance Criteria
- [ ] Per-swarm directory structure
- [ ] Events logged to JSONL
- [ ] Manifest immutable after creation
- [ ] List command aggregates from directories


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


Batch wisp squashing in Deacon maintenance

Add wisp squashing to Deacon's maintenance duties:
- Patrol plugin to find orphaned/completed wisps across all rigs
- Squash completed patrol wisps to digests
- Burn abandoned wisps that have no audit value
- Part of the hygiene/cleanup system

Also: Witness polecat shutdown should spawn its own short wisp for the cleanup protocol.


Session cycling UX: smooth transitions via TUI wrapper

## Problem

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

## GGT Advantages (already have)

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

## Gap: Transition Mechanics

Ideas to explore when actively using CLI:

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

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

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

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

## Deferred

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


Self-test

Testing gt mail works


gt spawn: add --polecat flag for explicit worker selection

Currently gt spawn requires positional arg format:
```
gt spawn gastown/Angharad --issue gt-xyz
```

But I tried the more intuitive flag form:
```
gt spawn --issue gt-xyz --polecat Angharad
```

This failed with 'unknown flag: --polecat'.

The flag form is more discoverable and consistent with other commands. Add --polecat flag as alternative to positional arg.


Rename .beads-ephemeral to .beads-wisps in docs

The wisp storage directory should be named `.beads-wisps/` not `.beads-ephemeral/`.

Also fix the architecture: Witness and Refinery share mayor/rig's beads and wisps.
They don't have separate ephemeral stores.

## Files to update
- docs/wisp-architecture.md
- docs/architecture.md
- ~/gt/docs/patrol-system-design.md
- ~/gt/CLAUDE.md

## Changes
1. Rename `.beads-ephemeral/` → `.beads-wisps/` everywhere
2. Remove incorrect references to witness/.beads-ephemeral/ and refinery/rig/.beads-ephemeral/
3. Clarify that all rig-level patrols use mayor/rig/.beads-wisps/


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

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


mol-deacon-patrol

[RESURRECTED] This issue was deleted but recreated as a tombstone to preserve hierarchical structure.

Original description:
Deacon patrol molecule template. Label: template


Document merge queue architecture

Update docs/architecture.md with:

- Merge Queue section explaining Beads-native approach
- Engineer role (renamed from Refinery)
- Session restart protocol
- gt mq command reference
- Federation considerations for queue

Also update any references to .gastown/ → config/.


CLI: all command for batch polecat operations

Batch operations across multiple polecats.

## Commands

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

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

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

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

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

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

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

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

## New File
internal/cmd/all.go

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

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


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)


Witness session startup (gt witness start)

Implement gt witness start <rig>

Should:
1. Verify rig exists and has witness/ directory
2. Check for existing witness session (don't double-start)
3. Create tmux session: gt-<rig>-witness
4. Start Claude Code with --dangerously-skip-permissions
5. Send gt prime to load context
6. Send startup prompt

Similar pattern to refinery startup in internal/refinery/manager.go


beads: Stop searching upward when .beads found

## Problem

When running bd commands in a nested directory structure with multiple .beads directories, bd shows a confusing warning:

```
╔══════════════════════════════════════════════════════════════════════════╗
║ WARNING: 2 beads databases detected in directory hierarchy              ║
╠══════════════════════════════════════════════════════════════════════════╣
║ Multiple databases can cause confusion and database pollution.          ║
║                                                                          ║
║   /Users/stevey/gt/gastown/.beads (261 issues)                           ║
║   /Users/stevey/gt/.beads (21 issues)                                    ║
║                                                                          ║
║ WARNING: Not using the closest database! Check your BEADS_DB setting.   ║
║                                                                          ║
║ RECOMMENDED: Consolidate or remove unused databases to avoid confusion. ║
╚══════════════════════════════════════════════════════════════════════════╝
```

## Why This Is Wrong

In Gas Town, nested .beads directories are **intentional and necessary**:
- Town level: /Users/stevey/gt/.beads (mail, town-level issues)
- Rig level: /Users/stevey/gt/gastown/.beads (gastown project issues)
- Worker level: polecats have their own beads in worktrees

These are **unrelated** beads instances for different scopes. They should never be consolidated.

## Expected Behavior

When bd finds a .beads directory, it should:
1. Use that directory (closest ancestor wins)
2. **Stop searching upward** - do not look for parent .beads directories
3. **No warning** about multiple databases

## Current Behavior

bd searches all the way up to root, finds all .beads directories, and warns about "multiple databases" even though they are separate, intentional instances.

## Fix

In the database discovery code, stop the upward search as soon as a .beads directory is found. The first .beads found is the one to use, and parent directories are out of scope.

## Note

This is a beads issue, filed here for tracking. Should be implemented in the beads codebase.


Digest: mol-deacon-patrol

Old test patrol cycle - cleanup


Merge: gt-3x1

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


Merge: gt-3x1.4

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


gt nudge should accept flexible session identifiers

Currently `gt nudge` requires the exact tmux session name (e.g., `gt-gastown-furiosa`).

It should also accept:
- `gastown/furiosa` (rig/polecat format)
- `furiosa` (polecat name, infer rig from cwd or require if ambiguous)

The session list command shows `gastown/furiosa` format, but nudge rejects it:
```
gt session list → shows 'gastown/furiosa'
gt nudge gastown/furiosa 'msg' → 'session not found'
gt nudge gt-gastown-furiosa 'msg' → works
```

Should normalize all these formats to the tmux session name internally.


MR field parsing: extract structured fields from description

Parse merge-request beads to extract structured fields:
- branch: source branch name
- target: target branch (main or integration/xxx)
- source_issue: the work item being merged
- worker: who did the work
- rig: which rig
- merge_commit: (set on close)
- close_reason: (set on close)

Fields stored in description as YAML block or key: value lines.
Provide helper functions: ParseMRFields(issue) and SetMRFields(issue, fields).

Reference: docs/merge-queue-design.md#merge-request-schema


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


Document bd mol bond/squash/burn CLI

Create CLI reference documentation for molecule commands:

## bd mol bond

Instantiate a proto into a runnable molecule.

```bash
bd mol bond <proto-id> [--wisp] [--assignee=<addr>]
```

- Default: creates a Mol (durable, in main beads)
- --wisp: creates a Wisp (ephemeral, in .beads-ephemeral/)
- --assignee: who will execute this molecule

## bd mol squash

Complete a molecule and generate digest.

```bash
bd mol squash <mol-id> --summary='...'
```

- For Mol: creates digest in git history
- For Wisp: evaporates (no permanent record)
- --summary: required summary of what was accomplished

## bd mol burn

Abandon a molecule without completing.

```bash
bd mol burn <mol-id> [--reason='...']
```

- Discards molecule state
- No digest created
- Use when molecule is no longer needed


Merge: gt-5wb7

branch: polecat/nux
target: main
source_issue: gt-5wb7
rig: gastown


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


Polecat mood command and status line display

CLI and status line support for polecat mood.

## CLI Command
```
gt polecat mood <name> <emoji>   # Set mood
gt polecat mood <name>           # Get mood
```

Sets GT_MOOD environment variable in the polecat's tmux session.

## Status Line Integration
Update runWorkerStatusLine() to read mood:
```go
mood, _ := t.GetEnvironment(session, "GT_MOOD")
if mood != "" {
    icon = mood  // Use mood emoji instead of default
} else {
    icon = AgentTypeIcons[AgentPolecat]  // Fallback to 😺
}
```

## Storage
Mood stored in tmux session environment (ephemeral, per-session).
No persistence needed - mood is reassessed each patrol cycle.

Depends on: gt-u818 (plugin system)


Remove redundant gt prime SendKeys from crew commands

The crew commands (gt crew at, gt crew restart) still send 'gt prime' via SendKeys after session creation:
- internal/cmd/crew_at.go:97, 121
- internal/cmd/crew_lifecycle.go:202

This is now redundant because SessionStart hook handles priming automatically.

**Low priority** - these aren't causing bugs (just duplicate work), unlike the spawn race condition that was fixed. Can be cleaned up when convenient.


Merge: gt-5af.6

branch: polecat/Slit
target: main
source_issue: gt-5af.6
rig: gastown


Patrols: Cyclic molecules for autonomous maintenance

## Vision

Patrols are **cyclic molecules** - workflow loops that give Gas Town its autonomous nervous system. While regular molecules are DAGs that terminate, patrols loop forever, performing maintenance tasks at varying cadences.

This is the "steam engine" of Gas Town: converting episodic Claude sessions into continuous autonomous operation.

## Core Concepts

### 1. Cyclic Molecules

Regular molecule:     A -> B -> C -> done
Patrol molecule:      A -> B -> C --+
                      ^             |
                      +-------------+

Patrols have a loop_to field specifying where to restart.

### 2. Cooldown-Aware Steps (Atoms)

Steps encode their own cadence. When patrol reaches a step:
1. Check: now - last_run > cooldown?
2. If yes: execute, update last_run
3. If no: skip (immediately close)

The patrol runner is simple - steps self-skip. Complexity distributed into atoms.

### 3. The Beacon

The heartbeat that fires patrol triggers:
- Internal ticker in Deacon (goroutine)
- Or external cron firing gt deacon tick
- Or mail-based triggers

Without beacon, nothing proactive happens.

### 4. Session Reset as Patrol Step

Connects to auto-handoff (gt-bcwn). Session reset is a patrol step, not a separate mechanism.

### 5. Multi-Role Patrols

Each supervisor has its own patrol:

**Deacon patrol:** health-check (30s), session-gc (5m), beacon-tick (10s)
**Witness patrol:** orphan-scan (10m), stuck-check (2m), molecule-progress (1m)
**Refinery patrol:** queue-check (30s), pr-status (1m), merge-ready (30s)

### 6. Cadence Tiers

- Critical (10-30s): Health checks
- Active (1-5m): Progress, nudges
- Maintenance (10-30m): Orphans, GC
- Periodic (1h+): Reports

### 7. Best-Effort Scheduling

Not real-time - more like cron. No hard deadlines. Catch-up, dont pile-up.
Priority preemption (mail interrupts patrol). Graceful degradation under load.

## Open Questions

1. State persistence: Beads (self-describing) or file (faster)?
2. Interruption: How does urgent mail preempt patrol?
3. Error recovery: Backoff? Escalate? Circuit breaker?
4. Coordination: Can patrols send mail to trigger other patrols?

## Related

- gt-bcwn: Auto-handoff (session reset is a patrol step)
- Molecule system (patrols extend molecules with loops)
- Deacon lifecycle management

## Metaphor

Claude was fire. Claude Code was steam. Gas Town is the steam engine. Beads is the train tracks.

The steam engine converts episodic combustion into continuous rotary motion.
Gas Town converts episodic Claude sessions into continuous autonomous work.


Work on gt-role-template: Refine witness/CLAUDE.md role t...

Work on gt-role-template: Refine witness/CLAUDE.md role template. Add clearer heartbeat protocol, mail checking procedure, nudge decision criteria, escalation thresholds. Run 'bd show gt-role-template' for details.


load-context

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

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


mol-deacon-patrol

[RESURRECTED] This issue was deleted but recreated as a tombstone to preserve hierarchical structure.

Original description:
Deacon patrol molecule template. Label: template


verify-tests

Run existing tests. Add new tests for new functionality.
Ensure adequate coverage.

go test ./...

Fix any test failures before proceeding.

Depends: implement


Doctor framework: Check interface, Result types, Report

Framework for gt doctor health checks.

## Check Interface

```go
type Check interface {
    Name() string
    Description() string
    Run(ctx *CheckContext) *CheckResult
    Fix(ctx *CheckContext) error  // optional auto-fix
    CanFix() bool
}

type CheckContext struct {
    TownRoot string
    RigName  string  // empty for town-level checks
    Verbose  bool
}

type CheckResult struct {
    Status  CheckStatus
    Message string
    Details []string  // additional info
    FixHint string    // suggestion if not auto-fixable
}

type CheckStatus int
const (
    StatusOK CheckStatus = iota
    StatusWarning
    StatusError
)
```

## Report

```go
type Report struct {
    Timestamp time.Time
    Checks    []CheckResult
    Summary   ReportSummary
}

type ReportSummary struct {
    Total    int
    OK       int
    Warnings int
    Errors   int
}

func (r *Report) Print(w io.Writer, verbose bool)
```

## Doctor Runner

```go
type Doctor struct {
    checks []Check
}

func NewDoctor() *Doctor
func (d *Doctor) Register(check Check)
func (d *Doctor) Run(ctx *CheckContext) *Report
func (d *Doctor) Fix(ctx *CheckContext) *Report  // run with auto-fix
```

## Built-in Checks

Town-level (gt-f9x.5):
- ConfigExists, ConfigValid
- StateExists, StateValid
- MayorMailboxExists
- RigsRegistryValid

Rig-level (gt-f9x.6):
- RigCloneExists
- GitExcludeConfigured
- WitnessExists, RefineryExists
- PolecatClonesValid


CLI: plugin commands (list, status)

Add gt plugins <rig> to list plugins and gt plugin status <name> to check plugin state. Simple directory scan of <rig>/plugins/.


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


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)


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.


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