Add audit trail for hook detach operations

Log detachment events for debugging and monitoring.

When work is detached from hook:
- Log timestamp, agent, molecule ID, reason
- If abnormal (not completion), notify Witness
- Consider adding to wisp digest for patrol cycles


Refactor: Extract common manager creation boilerplate

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

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


gt nudge: support no-arg invocation with default message

Currently gt nudge requires a message argument:

```
gt nudge <rig/polecat> <message>
```

Would be nice to support:
```
gt nudge <rig/polecat>
```

With a sensible default message like 'nudge' or 'continue' or checking for pending work automatically.


Digest: mol-deacon-patrol

Patrol: 4 msgs (2 blocked on bd mol current, 2 handoffs). 11 polecats, 18 sessions. Swarm in progress.


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)


Phase 4.2: Witness/Refinery patrol molecules

Extend ephemeral patrol pattern to Witness and Refinery.

## Witness Patrol Cycle

mol-witness-patrol-cycle:
1. scan-polecats: Check each polecat status
2. detect-stalls: Find stuck/idle polecats
3. nudge-or-escalate: Take action on stalls
4. log-status: Record cycle results
→ Squash with summary

## Refinery Patrol Cycle

mol-refinery-patrol-cycle:
1. scan-queue: Check merge queue
2. process-ready: Merge ready MRs
3. handle-failures: Deal with conflicts/failures
4. log-status: Record cycle results
→ Squash with summary

## Quiescent Mode

When no work: just log 'no activity' summary.
Witness and Refinery can sleep between cycles until triggered.


Create handoff beads for Witness and Refinery roles

Each patrol role needs a pinned handoff bead to track attached molecules.

## Pattern (from Deacon)
- Title: '\<role\> Handoff' (e.g., 'witness Handoff', 'refinery Handoff')
- Status: pinned
- Description contains:
  ```
  attached_molecule: \<mol-id\>
  attached_at: \<timestamp\>
  ```

## Tasks
1. Create 'witness Handoff' bead in each rig's beads
2. Create 'refinery Handoff' bead in each rig's beads
3. Update Witness/Refinery startup to check handoff bead for attached work
4. Update templates to document handoff bead usage

## Note
These are rig-level beads (in gastown/.beads/, beads/.beads/), not town-level like deacon Handoff (in ~/gt/.beads/).


Merge execution: merge, test, push

Implement the actual merge execution.

Steps:
1. git checkout <mr.target>
2. git merge <mr.branch> --no-ff -m 'Merge <branch>: <title>'
3. If config.run_tests:
   - Run test_command (from config)
   - If failed: git reset --hard HEAD~1, return Failure(tests_failed)
4. git push origin <mr.target>
5. Return Success(merge_commit=HEAD)

Configuration:
- run_tests: bool (default true)
- test_command: string (default 'go test ./...')

Handle push failures with retry logic.

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


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


Success handling: close MR, close source issue, cleanup

Handle successful merge completion.

Steps:
1. Update MR with merge_commit SHA:
   bd update <id> --body='...\nmerge_commit: <sha>'
2. Close MR with reason:
   bd close <id> --reason='merged'
3. Close source issue (the work item):
   bd close <source_issue> --reason='Merged in <mr_id>'
4. Delete source branch (if configured):
   git push origin --delete <mr.branch>
5. Log success

Configuration:
- delete_merged_branches: bool (default true)

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


Merge polecat/rictus: Add molecule phase lifecycle diagram

Branch: polecat/rictus

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

Closes: gt-c6zs


Outpost configuration: YAML config and OutpostManager

## Overview

Configuration file for outposts and manager to load/track them.

## Config File

`~/ai/config/outposts.yaml`:

```yaml
outposts:
  - name: local
    type: local
    max_workers: 4

  - name: gce-burst
    type: ssh
    host: 10.0.0.5
    user: steve
    ssh_key: ~/.ssh/gce_worker
    town_path: /home/steve/ai
    max_workers: 8

  - name: cloudrun-burst
    type: cloudrun
    project: my-gcp-project
    region: us-central1
    service: gastown-worker
    max_workers: 20
    cost_cap_hourly: 5.00

policy:
  default_preference: [local, gce-burst, cloudrun-burst]
  overrides:
    - condition: "priority >= P3"
      prefer: cloudrun-burst
```

## OutpostManager

```go
type OutpostManager struct {
    outposts map[string]Outpost
    policy   AssignmentPolicy
}

func NewOutpostManager(configPath string) (*OutpostManager, error)
func (m *OutpostManager) Get(name string) (Outpost, bool)
func (m *OutpostManager) List() []Outpost
func (m *OutpostManager) SelectOutpost(issue Issue) Outpost
```

## Files

- `internal/outpost/manager.go`
- `internal/outpost/config.go` (extend)
- `internal/outpost/policy.go`

Depends on: gt-9a2.1 (interfaces)


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


Crew worker CLAUDE.md prompting

Create CLAUDE.md template for crew workers:
- Explain crew worker role (overseer's personal workspace)
- Include mail-to-self handoff instructions
- Document gt crew refresh for context cycling
- Explain no witness monitoring (user-managed)
- Include beads usage if BEADS_DIR configured


Update swarm terminology to streams

Gas Town moved from batch swarms to continuous streaming of polecats. Update:
- Docs and designs
- Code comments
- CLI flags (remove --swarm if present, or reframe)
- Variable/function names where appropriate

Streams reflect the reality: polecats flow continuously, not in discrete batches.


Merge: gt-3x1

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


Implement await type handlers (gh:run, gh:pr, timer, human, mail)

Implement condition checking for each await type.

## Handlers Needed
- gh:run:<id> - Check GitHub Actions run status via gh CLI
- gh:pr:<id> - Check PR merged/closed status via gh CLI
- timer:<duration> - Simple elapsed time check
- human:<prompt> - Check for human approval (via mail?)
- mail:<pattern> - Check for mail matching pattern

## Interface
```go
type AwaitHandler interface {
    Check(awaitID string) (completed bool, result string, err error)
}
```

## Moved from beads
Originally bd-2l03. Deacon handlers belong in gastown.


Implement pinned beads for handoff state

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


Digest: mol-deacon-patrol

Patrol cycle 1: inbox clear, 8 in-progress issues, sessions healthy, gc not implemented


Test issue for spawn molecule


mol-outpost-assign: Intelligent work routing to outposts

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

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

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

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

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

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

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

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

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


Maintenance: Regular cleanup of closed MR/gate beads

Closed merge-request and gate beads accumulate over time. Need regular cleanup.

## Cleanup Candidates
- type=merge-request with status=closed
- type=gate with status=closed
- type=molecule with status=closed (completed workflows)

## Options
1. **Deacon patrol step** - squash closed MRs periodically
2. **Refinery post-merge** - close and tombstone MR after successful merge
3. **bd doctor check** - warn when stale MR count exceeds threshold

## Consideration
Keep some audit trail? Or just tombstone after N days?


Work on ga-ct2: Add MR workflow to polecat completion. Wh...

Work on ga-ct2: Add MR workflow to polecat completion. When polecat completes work, auto-create MR to integration branch. When done, submit MR (not PR) to integration branch for Refinery.


Design: Polecat Beads write access

Design for granting polecats direct beads write access.

## Background

With Beads v0.30.0 tombstone-based rearchitecture, we have solid multi-agent support. Reversing the original read-only decision.

## Benefits

- Simplifies architecture (no mail-based issue filing proxy)
- Empowers polecats to file discovered work
- Beads handles work-disavowal

## Complications

For OSS projects where you cannot commit to project .beads/, need per-rig beads repo configuration.

## Subtasks (implementation)

- gt-zx3: Per-rig beads configuration schema
- gt-e1y: Worker prompting updates for beads access
- gt-cjb: Witness proxy removal
- gt-082: Beads sync in decommission checklist

**Design complete.** Each subtask has full specification in its description.


mol-ready-work: graceful GitHub/label handling

## Summary

mol-ready-work assumes:
- GitHub repo exists with gh CLI configured
- Labels 'untriaged' and 'triaged' exist

Should handle gracefully:
1. No GitHub repo (beads-only project) → skip GH backlogs
2. Missing labels → skip that backlog tier, don't error
3. gh CLI not authenticated → warn and skip GH backlogs

## Implementation
Add checks in scan-backlogs step:
```bash
# Check if gh is available and authenticated
if gh auth status &>/dev/null; then
  # scan GH backlogs
fi
```

## Parent
Part of gt-tnca (mol-ready-work epic)


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)


Deacon Kernel: event processing molecule architecture

Extend the Deacon from "health orchestrator" to "event processing kernel" that runs a
molecule (rounds) each wake cycle, like an OS kernel scheduler.

## Core Insight

The Deacon already has:
- Wake cycle (daemon poke, timer, lifecycle request)
- Mail as event queue
- Health scanning
- Lifecycle processing

But it is REACTIVE. This epic makes it PROACTIVE with a structured molecule:

```
Deacon Wake
├── Step 0: Session Check (should I cycle myself?)
├── Step 1: Health Scan (Mayor, Witnesses, Crew, Refineries)
├── Step 2: Lifecycle Processing (cycle/restart/shutdown requests)
├── Step 3: Plugin Execution (scheduled, event-triggered, human-requested)
├── Step 4: Event Callbacks (timers, subscriptions)
└── Step 5: Heartbeat + Wait
```

## Key Design Decisions

1. **Session cycling first** - Token costs are quadratic with session length.
   Deacon should aggressively cycle to fresh sessions. First step of every
   wake is "do I need a new session?" based on context budget.

2. **Pinned bead with marching orders** - A pinned bead (like gt prime for
   agents) defines the Deacon's rounds molecule. This is the Deacon's
   "kernel" - its operating loop.

3. **Keepalive file for daemon quieting** - Instead of daemon tmux-poking
   the Deacon (which interrupts work), Deacon writes a keepalive file.
   Daemon reads it and backs off. Worst case: Deacon forgets, gets one
   extra heartbeat it squelches.

4. **Molecule-based rounds** - The wake cycle becomes a molecule with steps.
   Each step can have plugins (inline attention). Steps can be added/modified
   by updating the pinned bead.

## Architecture

```
Go Daemon (minimal, dumb)
├── Reads {townRoot}/deacon/keepalive.json
├── If fresh: do nothing
├── If stale: poke Deacon (fallback)
└── Process lifecycle requests (still daemon's job)

Deacon (Claude agent, smart)
├── Wakes: poke, timer, mail event
├── Writes keepalive (quiets daemon for duration of rounds)
├── Runs rounds molecule:
│   ├── session-check: cycle if context budget low
│   ├── health-scan: check agents, remediate
│   ├── lifecycle: process requests
│   ├── plugins: run scheduled/triggered plugins
│   ├── events: process timer/event callbacks
│   └── complete: update heartbeat, wait
└── Reads marching orders from pinned bead
```

## Relation to Existing Work

- Extends gt-5af (Deacon infrastructure) - CLOSED
- Relates to gt-axz (Plugin architecture) - P3
- Fits into phase3-deacon of gt-ngpz (Christmas Plan)
- Enables gt-976 (Crew lifecycle) as part of health-scan step


gt nudge should accept partial/fuzzy session names

Currently gt nudge requires the exact tmux session name (e.g., gt-gastown-crew-max). Should be more forgiving:

1. Accept partial matches when unambiguous (e.g., 'max' → gt-gastown-crew-max)
2. Accept shorthand like 'gastown/max' or 'crew/max'
3. Show helpful error with suggestions when ambiguous

Examples that should work:
- gt nudge max '...' → matches gt-gastown-crew-max
- gt nudge gastown/max '...' → matches gt-gastown-crew-max
- gt nudge beads-dave '...' → matches gt-beads-crew-dave


Polecats should not push branches to remote

## Current Behavior

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

## Desired Behavior

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

## Benefits

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

## Trade-offs

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

## Implementation

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


Add gt rig rename command

Allow renaming a rig after creation. Similar to gt crew rename, this should:

- Update the directory name
- Update mayor/rigs.json entry
- Update the rig's config.json
- Handle running agents gracefully (require shutdown first or --force)

Use case: User creates a rig, later wants to change its name for better organization.


LocalOutpost: Refactor current polecat spawning

## Overview

Refactor current local polecat spawning to implement the Outpost interface.

## Current State

Polecat spawning is ad-hoc. This task wraps it in LocalOutpost.

## Implementation

```go
type LocalOutpost struct {
    name       string
    maxWorkers int
    tmux       *Tmux
    workers    map[string]*LocalWorker
}

func NewLocalOutpost(config OutpostConfig) *LocalOutpost

func (o *LocalOutpost) Type() OutpostType { return OutpostLocal }

func (o *LocalOutpost) Spawn(issue string, cfg WorkerConfig) (Worker, error) {
    // Create tmux session
    // Start claude in pane
    // Return LocalWorker
}
```

## LocalWorker

```go
type LocalWorker struct {
    id      string
    session string  // tmux session name
    issue   string
    status  WorkerStatus
}

func (w *LocalWorker) Attach() error {
    // tmux attach-session
}
```

## Notes

- This should be a refactor, not new functionality
- Existing polecat code becomes LocalOutpost internals
- Tests should pass before and after

Depends on: gt-9a2.1 (interfaces)


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

## Problem

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

## Current Behavior

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

## What Works

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

## Proposed Fix

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

## Considerations

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

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


Plugin: merge-oracle (merge queue analysis)

Example plugin that analyzes changesets before Refinery processes them. Builds overlap graph, classifies disjointness (parallel-safe vs needs-sequencing), uses LLM for semantic complexity, identifies high-risk patterns. Based on merge-orchestration proposal. See docs/architecture.md.


Consolidate molecular-chemistry.md with rig-cook-run.md

Merge rig-cook-run.md into molecular-chemistry.md as the canonical chemical algebra spec:
- Rig/Cook/Run as the lifecycle backbone
- Full generation graph: Formula → Compound Formula → Proto → Mol/Wisp
- Bond table at artifact level (symmetric)
- Rig operator at source level
- Unified vocabulary
- Archive or redirect rig-cook-run.md


Create rig settings/ directory for rig configuration

Replace .gastown/ with visible settings/ at rig level:
- Create <rig>/settings/ directory
- Move .gastown/config.json → settings/config.json (theme, merge_queue, max_workers)
- Create settings/namepool.json for pool settings (style, max - not state)
- Create settings/roles/ for per-role overrides (optional)
- Update RigConfig type to reference settings/
- Update loader to read from settings/


Merge: gt-rana.3

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


Work reconciliation module

Port reconcile.py work verification to Go.

## Purpose

Verify assigned issues are completed before landing.

## Interface

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

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

## Logic

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

## Handling Incomplete Work

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

## Reference

PGT: swarm/reconcile.py


Review: Beads Universal Data Plane documentation

Review new docs/beads-data-plane.md (275 lines) for accuracy.

## Commit
- 54c8269: Add Beads Universal Data Plane documentation

## Review focus
- Accuracy of beads architecture description
- Correct field mappings (mail → beads fields)
- Two-level architecture (town vs rig beads) correctly explained


Merge: gt-3x0z.2

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


gt spawn beads sync warning is misleading for redirect-based polecats

## Problem

When spawning a polecat, gt spawn shows a warning:
```
Warning: beads sync: exit status 1
```

This is misleading because polecats using the redirect architecture (`.beads/redirect`) share the canonical database at `mayor/rig/.beads/beads.db`. The 'stale beads' indicated by the warning is just git branch divergence (main vs beads-sync), not actual data staleness.

## Expected Behavior

gt spawn should either:
1. Skip the beads sync check for polecats using redirects (they share the canonical DB)
2. Or provide a clearer message like 'beads redirect active, using shared database'

## Reproduction

```bash
gt spawn --issue gt-xxx --rig gastown --create
# Shows 'Warning: beads sync: exit status 1' even though beads are current
```

## Root Cause

spawn.go calls beads sync and treats any non-zero exit as a warning. But with redirects, the polecat doesn't need its own beads - it uses the canonical source via the redirect chain:
```
polecat/.beads/redirect -> ../../.beads -> gastown/.beads/redirect -> mayor/rig/.beads
```

## Fix Options

1. Check for .beads/redirect before calling sync
2. Have bd sync return 0 when redirect is present
3. Suppress the warning in spawn when redirect exists


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/


Adaptive backoff for daemon heartbeat

Track agent responsiveness and adjust heartbeat frequency.

## Per-Agent State

```go
type AgentBackoff struct {
    BaseInterval    time.Duration  // 60s default
    CurrentInterval time.Duration  // grows when busy
    MaxInterval     time.Duration  // 10min cap
    ConsecutiveMiss int            // pokes with no response
}
```

## Strategy Options

- **Fixed**: Always 60s (current, simple)
- **Geometric**: 60s → 90s → 135s → 202s (factor 1.5)
- **Exponential**: 60s → 120s → 240s (factor 2, aggressive)

## Recovery

When agent responds (runs a command):
- Reset ConsecutiveMiss to 0
- Return to BaseInterval immediately

## Benefits

- Reduces noise for busy agents
- Saves resources during quiet periods
- Still catches stuck agents (max interval cap)


Daemon: configurable heartbeat interval

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

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


gt spawn: polecats report success but no work actually happens

## Problem

gt spawn polecats have multiple silent failure modes:

### Failure Mode 1: No gt prime
- Claude starts at prompt and waits for input
- SessionStart hook doesn't auto-run 'gt prime'
- Without gt prime, polecats don't pick up work

### Failure Mode 2: No git push (CRITICAL)
- Polecat commits code locally
- Polecat closes the beads issue
- Polecat sends POLECAT_DONE
- **But code is never pushed to remote branch**
- When worktree is cleaned up, commits are lost forever
- Issue appears closed but no code exists

## Evidence

testcat on bd-d28c:
- Reported 'Work Complete' with commits 7d3447b9, cd87e541
- Closed bd-d28c
- Submitted MR bd-yx22
- But: `grep -r 'TestCreateTombstone' cmd/bd/*.go` returns nothing
- No polecat/testcat branch on remote

## Required Fixes

1. Polecats MUST push their branch before closing issues
2. Witness MUST verify branch exists on remote before cleanup
3. gt spawn should auto-nudge 'gt prime'
4. Consider: Polecat handoff should fail if unpushed commits exist

## Verification Protocol for Witness

Before accepting POLECAT_DONE:
```bash
# 1. Verify branch pushed
git branch -r | grep polecat/<name>

# 2. Verify code exists
grep -r '<expected_function>' path/to/files

# 3. Only then cleanup
```


gt rig info: Show detailed rig information

Add 'gt rig info <rig>' command to show detailed rig status.

Should show:
- Rig path and git URL
- Active polecats with status
- Refinery status
- Witness status
- Recent activity
- Beads summary (open issues count)


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


Design: Work flow simulation and validation

Validate GGT designs through simulation before implementation.

## Validation Approaches

### 1. Dry-Run Simulation (Recommended First)
Mayor walks through scenarios mentally/on paper:
- "If polecat Toast signals done with dirty git state, what happens?"
- "If Witness context fills mid-verification, what state is lost?"
- "If two polecats try to close same issue, what happens?"

Create beads for any gaps discovered.

### 2. Real Work in gastown-py
Use Python Gas Town to stress-test assumptions:
- Run actual batch work on test repos
- Observe edge cases in practice
- Document issues found

### 3. Edge Case Analysis
Systematic review of failure modes:
- Agent crashes mid-operation
- Network failures during sync
- Concurrent access to shared state
- Context limits hit at bad times

## Key Scenarios to Validate

- [ ] Witness session cycling (state preservation)
- [ ] Polecat decommission with dirty state
- [ ] Merge conflicts in queue
- [ ] Beads sync conflicts between workers
- [ ] Escalation path (stuck worker -> Mayor)
- [ ] Cross-rig communication
- [ ] Federation mail routing (future)

## Success Criteria

- No data loss scenarios identified
- Clear recovery paths for all failure modes
- Edge cases either handled or documented as limitations
- Design improves as model cognition improves

## Output

For each scenario validated:
1. Document in relevant bead if issue found
2. Create new beads for missing functionality
3. Update architecture.md if design changes


Beads as Universal Data Plane

## Vision

Beads is the data plane for all Gas Town operations. Everything flows through beads:
- Work items (issues, tasks, epics)
- Mail (messages between agents)
- Merge requests (queue entries)
- Workflows (composable execution patterns)
- Resources (leases, locks, quotas)
- Schedules (timed activities)

## New Bead Categories

### Molecules (Composable Workflows)
Crystallized workflow patterns that can be attached to work items.

### Timed Beads (Scheduled Work)
Beads that wake up periodically via daemon.

### Pinned Beads (Ongoing Concerns)
Beads representing persistent concerns, not discrete tasks.

### Resource Beads (Leases/Locks)
Beads representing reserved resources.

## v1 Priority

- **P0**: Molecules (enables engineer-in-box)
- **P2**: Timed, Pinned, Resource beads (post-v1)


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.


mol-code-review: Self-improving code review molecule

Create a code review molecule that enables Gas Town to self-improve through automated PR review. Flywheel: review generates quality checks, discovered issues, pattern learning.


Polecat Mood Plugin (standard)

Standard witness plugin molecule that assesses polecat emotional state.

## Molecule Definition

```json
{
  "id": "mol-polecat-mood",
  "title": "Assess mood for {{polecat_name}}",
  "description": "Analyze captured output and determine emotional state.\n\nVars:\n- {{polecat_name}} - The polecat to assess\n- {{captured_output}} - Recent tmux capture\n\nOutput: gt polecat mood {{polecat_name}} <emoji>",
  "labels": ["template", "plugin", "witness", "tier:haiku"],
  "issue_type": "task"
}
```

## Mood Emojis

```
😺 working     Active tool calls, making progress
😸 productive  Completing tasks, tests passing
🐱 idle        Waiting at prompt, no recent activity
😼 confident   Self-reviewing, about to submit
😿 struggling  Repeated errors, test failures
🙀 stuck       No progress for 10+ min
😽 done        Work complete, requesting shutdown
😾 blocked     Explicitly waiting on dependency
```

## Bonding

During witness patrol plugin-run step:

```bash
bd mol bond mol-polecat-mood $PATROL_WISP \
  --ref mood-{{polecat_name}} \
  --var polecat_name=ace \
  --var captured_output="$TMUX_CAPTURE"
```

## Installation

```bash
bd mol install mol-polecat-mood  # From Mol Mall
```

## Customization

Fork the molecule and modify description to change assessment criteria or add custom moods. Install your fork to override.


Refinery-Witness communication protocol

Define mail protocol between Refinery and Witness:

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

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

Implement as structured mail with parseable format.


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)


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

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


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.


Merge: gt-5af.6

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


gt spawn: pasted work assignment needs manual Enter to start

## Problem

After `gt spawn` pastes the work assignment into Claude, the session waits for Enter.

## Current Behavior

1. `gt spawn gastown/Rictus --issue gt-xxx`
2. Session starts, work is pasted
3. Claude shows 'Pasted text #1 +53 lines' but doesn't start
4. Must manually send Enter or attach and press Enter

## Expected

The spawn should send Enter after pasting to kick off the work.

## Related

Same debounce issue as tmux notifications (gt-vnp9).


/restart: Personal slash command for in-place agent restart

Create ~/.claude/commands/restart.md that restarts current Gas Town agent in place.

## Detection
- Read tmux session name: gt-mayor, gt-witness-*, gt-refinery-*, gt-polecat-*
- Fallback: check GT_ROLE env var

## Behavior by role
- mayor: gt mayor restart (sends Ctrl-C, loop respawns)
- witness: gt witness restart
- refinery: gt refinery restart
- polecat: gt polecat restart (or witness-mediated)

## Command format
Uses backticks for inline bash to detect context, then instructs Claude to run appropriate restart.


Cross-project dependency workflow (Gas Town side)

Gas Town integration for cross-project dependencies.

## Components
- gt-zniu: gt park command (park molecule on external dep)
- gt-in3x: gt spawn --continue (resume parked molecule)
- gt-5uf3: Patrol auto-resume (future)

## Design Doc
See: docs/cross-project-deps.md

## Depends on Beads
- bd-h807: Cross-project dependency support (epic)

## Launch Plan
Phase 1 (launch): gt park + gt spawn --continue (manual resume)
Phase 2 (later): Patrol auto-resume


Bug: refinery --foreground detects parent session as already running

When gt refinery start gastown runs:
1. Creates tmux session gt-gastown-refinery
2. Sends 'gt refinery start gastown --foreground' into the session
3. The foreground command checks HasSession() - finds the session it's inside
4. Returns 'already running' error

The foreground mode check should either:
- Skip the tmux session check (only check PID)
- Use a different indicator that the daemon loop is running
- Pass a flag to indicate we're being called from the background starter

Workaround: Manually run 'gt refinery start gastown --foreground' from a fresh session.


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.


Test

Body


verify-town-health

Final verification that Gas Town is healthy.

## Action
```bash
gt status
```

## Verify
1. Daemon running: Shows daemon status OK
2. Deacon active: Shows deacon in patrol mode
3. All witnesses: Each rig witness shows active
4. All refineries: Each rig refinery shows active

## OnStall
N/A - this is a read-only verification step.

## OnFail
Log degraded state but consider boot complete. Some agents may need manual recovery.
Run `gt doctor` for detailed diagnostics.


Daemon: integration test with real lifecycle

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

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


Swarms of One: Lightweight single-worker task dispatch

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

## Context

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

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

## Design Questions

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

## Possible Interface

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

## Related

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


Test Task 1: Add comment to gt.go


Implement handoffs using pinned beads

Replace the current mail-based handoff system with pinned beads.

## Current Problem

Handoff messages get closed before the successor can read them because:
1. `gt mail read` auto-acks (closes) messages
2. `bd mail inbox` only shows open messages
3. Successor sees empty inbox

## Solution

Use pinned beads for handoffs:
- One pinned bead per role: `mayor-handoff`, `<rig>-refinery-handoff`, etc.
- Predecessor updates the content before cycling
- Successor reads on startup via `gt prime`
- Never closes - always available

## Implementation

### 1. Create handoff beads on first cycle
- `bd create --title='Mayor Handoff' --type=task --status=pinned --assignee=mayor`
- Store ID in role config or use well-known naming convention

### 2. Update gt handoff command
- Instead of `bd mail send`, update the pinned handoff bead
- `bd update <handoff-id> --description='...handoff content...'`

### 3. Update gt prime
- Read the role's handoff bead
- Display content to successor

### 4. Compression/reset
- `gt rig reset` clears handoff content
- Or manual: `bd update <handoff-id> --description=''`

## Dependencies

Requires beads-6v2 (StatusPinned) to be implemented first.


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


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.


Add deacon/ mail identity support

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


Molecule converge: iterate until AI says done

Work isn't 'done' until AI is confident it's done. Fixes the 85% problem.

**From VC**: internal/iterative/converge.go - 3-7 iterations with AI convergence detection. ~600 lines.
Target: 20%+ more issues discovered. Observed: ~25% average improvement.

**Gas Town implementation**: Molecule converge config:
```yaml
converge:
  strategy: ai
  min_iterations: 3
  max_iterations: 7
  confidence: 0.85
```

Polecat iterates, AI checks convergence. Stop when AI is confident or max reached.

**Value**: Raw agents get to ~85% and stop. Iteration catches the remaining 15%.

**VC metrics**: Convergence rate >70%, mean 4-5 iterations for complex work.


Step recovery: timeout and release

Implement recovery mechanism for stuck molecule steps.

## Problem
When a worker dies mid-step, the step stays in_progress forever.
Need timeout/release mechanism for nondeterministic idempotence.

## Solution
1. Track step start time (claimed_at timestamp)
2. Timeout: After 30 min in_progress, step returns to pending
3. Manual release: bd release <step-id>

## Implementation Options

### Option A: Beads-level timeout
- Add claimed_at field to issues
- bd ready excludes items in_progress < 30 min
- bd ready includes items in_progress > 30 min (auto-recovery)

### Option B: Daemon-level timeout
- Daemon watches in_progress items
- Moves back to pending after timeout

### Option C: Manual only (MVP)
- bd release <id> manually moves in_progress → pending
- Document recovery procedure
- Witness can automate for polecats

## Recommendation
Start with Option C (manual) for Christmas. Add Option A later.

## Acceptance
- [ ] bd release command works
- [ ] Stuck steps can be recovered
- [ ] Documented recovery procedure


P0: Multiple agents can claim same worker identity

Multiple Claude Code sessions running simultaneously all think they are 'dave' on beads/crew/dave. No detection or prevention of identity collision. This breaks the single-agent-per-worker assumption.

## Fix Implemented

1. **Lock Package** (`internal/lock/lock.go`):
   - PID-based lockfile at `<worker>/.gastown/agent.lock`
   - Contains PID, timestamp, session ID, hostname
   - Stale lock detection (checks if owning PID is dead)

2. **Prevention in gt prime**:
   - Workers (crew/polecat) acquire identity lock before loading context
   - If another live process holds the lock, prime fails with clear error
   - Shows lock holder details and resolution steps

3. **Detection with gt agents**:
   - `gt agents check` - scans for collisions and stale locks
   - `gt agents fix` - cleans stale locks
   - JSON output available for patrol tooling

4. **Correction in gt doctor**:
   - New `identity-collision` check
   - `gt doctor --fix` cleans stale locks


bd close hooks: context check and notifications

Add hook system to bd close for context checking and notifications.

## Concept

Register hooks that run on every bd close:

# .beads/config.yaml
hooks:
  on_close:
    - command: 'gt context-check'
    - notify: 'Next step ready'

## Use cases

### Context check on close
After closing a step, check if context is getting full.
If >80%, output warning suggesting session cycling.

### Next step notification
Automatically show next ready step (complements --continue).

### Custom actions
User-defined scripts for workflow automation.

## Hook types

- command: Run shell command with env vars (BEAD_ID, BEAD_TITLE, etc)
- notify: Send message to agent
- webhook: POST to URL (future)

## Integration with context detection

The context-check hook could:
1. Capture tmux pane (if in tmux session)
2. Estimate context usage (turn count, output length)
3. If high, output: 'Context at ~85%. Consider cycling.'

## Priority
P2 - nice to have, not blocking launch.

## Related
- gt-qswb (bd mol current)
- gt-fly0 (bd close --continue)


Merge: gt-99a

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


Conditional aspects (feature flags)

Apply aspects conditionally based on environment or config. Example: apply security-audit only when env.SECURITY_SCANNING == enabled. Adds 'when' clause to aspect composition.


Merge: gt-j6s8

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


Create .runtime/ for town-level runtime state

Move ephemeral town state to .runtime/:
- Create ~/gt/.runtime/ directory structure
- Move daemon/state.json → .runtime/daemon.json
- Move deacon/heartbeat.json → .runtime/deacon.json
- Move .gastown/agent-state.json → .runtime/agent-requests.json
- Move .gastown/keepalive.json → .runtime/keepalive.json
- Update .gitignore to ignore .runtime/
- Update all code references


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


Define mol-gastown-boot proto structure

Create the proto molecule definition with all steps.

## Proto Definition

Each step has:
- Title (step name)
- Description with Action/Verify/OnStall/OnFail sections
- Dependencies (Needs: directive)

## Steps

1. ensure-daemon
   - Action: gt daemon status || gt daemon start
   - Verify: daemon PID exists and responding

2. ensure-deacon
   - Action: gt deacon start
   - Verify: session exists, not stalled, heartbeat fresh
   - OnStall: gt nudge deacon/ "Start patrol."

3. ensure-witnesses (parallel container)
   - ensure-gastown-witness
   - ensure-beads-witness
   - Verify each: session exists, not stalled

4. ensure-refineries (parallel container)
   - ensure-gastown-refinery
   - ensure-beads-refinery
   - Verify each: session exists, not stalled

5. verify-town-health
   - Action: gt status
   - Verify: all expected agents shown

## Output

Proto stored in beads as molecule type issue.


gt sling: use mail queue for patrol roles (witness, refinery, deacon)

When slinging work to patrol agents, queue via mail instead of replacing hook.

**Rationale:**
Patrol agents run continuous loops. Replacing their hook with discrete work breaks patrol continuity - when the task completes, the patrol stops.

**New behavior for patrol roles (witness, refinery, deacon):**
1. Check if patrol is running (hook has patrol molecule attached)
2. If patrol running:
   - Don't touch hook (patrol stays pinned)
   - Send work assignment mail
   - Print 'Queued for next patrol cycle'
3. If patrol NOT running:
   - Start default patrol for that role (mol-witness-patrol, mol-refinery-patrol, mol-deacon-patrol)
   - Send work assignment mail
   - Print 'Started patrol and queued work'

**New flags:**
- --urgent: Interrupt current patrol cycle, process this work immediately
- --replace: Explicitly terminate patrol and pin discrete work (break-glass)

**No change for:**
- Polecat, Crew, Mayor (discrete task agents - current behavior)

**Dependencies:**
- Patrol templates must have 'check inbox' step (verify/add)
- Need to know default patrol molecule for each role


Merge: gt-y5o

branch: polecat/Doof
target: main
source_issue: gt-y5o
rig: gastown


TIDY UP: Your previous work (patrol runner) was already m...

TIDY UP: Your previous work (patrol runner) was already merged to main. Check your git status is clean, sync beads, and if nothing to do, just run 'gt done'.


Phase 1.2: Configure bd for ephemeral molecule bonding

Ensure bd mol bond --ephemeral works with Gas Town setup.

## Questions for Dave

1. Does bd automatically find .beads-ephemeral/ or need explicit path?
2. How does bd mol bond --ephemeral know which repo to use?
3. Is there a redirect mechanism for ephemeral like main beads?

## Integration

From polecat/crew working directory:
```bash
bd mol bond mol-polecat-work --ephemeral --assignee $(gt whoami)
```

Should create molecule in rig's .beads-ephemeral/, not main beads.


Refinery not processing merge-requests - stale MRs accumulating

Discovered 15 stale merge-request issues in beads rig that were never processed by the refinery. These appear to be from polecat branches that completed work but the refinery didn't pick up the merge-requests.

Closed issues:
- bd-r06v, bd-bhg7, bd-754r, bd-fcl1, bd-3zzh
- bd-bijf, bd-5rj1, bd-kptp, bd-rdzk, bd-ibl9
- bd-gfo3, bd-aq3s, bd-x2bd, bd-s1pz, bd-h27p

Need to investigate:
1. Why refinery isn't picking up merge-requests
2. Whether the polecat branches have valid work to merge
3. Add monitoring/alerting for stale MRs


Digest: mol-deacon-patrol

Patrol: slit handoff (landing protocol), polecats cleaned (0 now), 4 witness/refineries up


Engineer session restart protocol

Implement session restart flow for when the Engineer needs to split work:

1. Engineer creates subtask(s) in Beads assigned to self
2. Engineer sends handoff mail to self (🤝 HANDOFF)
3. Engineer sends restart request to Witness
4. Witness verifies:
   - Handoff mail exists in Engineer outbox/sent
   - Subtasks filed in Beads
5. Witness restarts the Refinery session (new Engineer context)

This enables "occasionally consistent, eventually convergent" work patterns.
The Refinery continues; the Engineer gets fresh context.


Fix blocking bugs (gt-dsfi, gt-n7z7, gm-c6b)

Fix bugs blocking Witness functionality:

1. gt-dsfi: handoff deadlock
   - Polecats hang when trying to exit
   - Blocks shutdown request handler

2. gt-n7z7: refinery foreground race condition
   - Sometimes detects parent as already running
   - Blocks reliable Refinery startup

3. gm-c6b: mail coordination
   - Cross-rig mail should use town-level database
   - Affects Witness <-> Mayor communication

These should be fixed early as they'll block integration testing.

Fixed gt-dsfi and gt-n7z7. Issue gm-c6b not found in database.


session-gc

Clean dead sessions. Run gt gc --sessions.


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


gt spawn should verify daemon is running for polecat triggering

gt spawn notifies the Deacon (via mail) that a polecat was started, expecting the Deacon to trigger it once Claude is ready. But if the daemon isn't running, the mail sits unread and the polecat never gets triggered.

## Current Behavior
1. gt spawn starts polecat session
2. gt spawn sends POLECAT_STARTED to deacon/
3. (assumes daemon will trigger polecat)

## Problem
If gt daemon isn't running, step 3 never happens and polecat sits at prompt.

## Solution
In gt spawn, after session start:
1. Check if daemon is running (gt daemon status)
2. If not running, either:
   a. Start daemon: gt daemon start
   b. Or warn user: 'Daemon not running, polecat may not auto-start'

## Alternative
The user can manually trigger with gt nudge, but automated flow should work.


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


Test patrol queue feature


Phase 3: Callbacks and plugins

Mail-based callback protocol and plugin surface.

## Callbacks
- Polecat → Witness: shutdown requests
- Witness → Deacon: escalations, status
- Crew → Deacon: recycle requests
- Deacon → Mayor: escalations

## Plugins
- .beads/plugins.yaml registry
- Cooldown tracking
- Plugin runner in deacon patrol

Depends: Phase 2


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.


Create mol-install-go-binary molecule

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

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

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

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

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

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

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


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


Merge: gt-0ei3

branch: polecat/capable
target: main
source_issue: gt-0ei3
rig: gastown


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


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.


Update architecture documentation

Update all documentation to reflect refinery-as-worktree model.

## Files to update
- docs/architecture.md (if exists)
- CLAUDE.md references to refinery
- mayor/rig/docs/*.md - any refinery docs
- wisp-architecture.md - add MR wisp details
- README files mentioning clone vs worktree

## Key messages
- Refinery is worktree, not clone
- Polecat branches stay local
- Only main pushed to origin
- MRs are wisps (ephemeral)


Integration branch management for swarms

Manage integration branches for swarm merging strategy.

## Branch Naming

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

## Operations

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

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

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

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

## All Workers Same Base

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

This ensures clean merges within the swarm.

## Reference

PGT: ephemeral.py (create_integration_branch, merge_integration_to_main)


mol-gastown-boot: Mayor-driven town bootstrap

Mayor bootstraps Gas Town via a verification-gated lifecycle molecule.

## Vision

When user says "boot up gas town", Mayor slings `mol-gastown-boot` and executes it.
Each step has **action + verification** - steps stay open until outcome is confirmed.

## Key Principles

1. **No daemon-based timeouts** - Mayor keeps trying until verified
2. **Verification-gated steps** - Not "command ran" but "outcome confirmed"
3. **gt peek for verification** - Capture session output to detect stalls
4. **gt nudge for recovery** - Reliable message delivery to unstick agents
5. **Parallel where possible** - Witnesses can start in parallel
6. **Ephemeral execution** - Boot is a wisp, squashed to digest

## Proto Catalog

Town maintains a catalog of protos at `~/gt/molecules/`:

```
~/gt/molecules/
├── lifecycle/
│   ├── gastown-boot/       # Full town bootstrap
│   ├── rig-spinup/         # Add a rig at runtime
│   ├── rig-spindown/       # Remove a rig gracefully
│   └── agent-restart/      # Restart single agent
├── patrol/
│   ├── deacon-patrol/      # Deacon health loop
│   ├── witness-patrol/     # Witness polecat loop
│   └── refinery-patrol/    # Refinery merge loop
└── work/
    ├── polecat-work/       # Standard issue workflow
    ├── code-review/        # Pluggable review
    └── feature/            # Feature development
```

## mol-gastown-boot Structure

```
mol-gastown-boot (proto)
├── ensure-daemon       # Verify daemon running
├── ensure-deacon       # Start deacon, verify patrol active
├── ensure-witnesses    # Parallel: all rig witnesses
│   ├── ensure-gastown-witness
│   └── ensure-beads-witness
├── ensure-refineries   # Parallel: all rig refineries
│   ├── ensure-gastown-refinery
│   └── ensure-beads-refinery
└── verify-town-health  # Final gt status check
```

## Step Template

Each step in description includes action + verification:

```markdown
## Step: ensure-deacon

### Action
gt deacon start

### Verify
1. Session gt-deacon exists: `tmux has-session -t gt-deacon`
2. Not stalled: `gt peek deacon/` does NOT show "> Try"
3. Recent heartbeat: deacon/heartbeat.json < 2 min old

### On Stall
gt nudge deacon/ "Start patrol."
Wait 30s, re-verify.

### On Fail
Log error, continue to next step (town can run with degraded deacon)
```

## Event-Triggered Lifecycle

Beyond manual slinging, events can trigger lifecycle wisps:

| Event | Triggers |
|-------|----------|
| `gt town start` | mol-gastown-boot wisp |
| `gt rig add <name>` | mol-rig-spinup wisp |
| `gt rig remove <name>` | mol-rig-spindown wisp |
| Agent crash detected | mol-agent-restart wisp |

## Parameterization (Future)

For rig-spinup/spindown, need to pass rig name:

```bash
gt sling rig-spinup mayor/ --param rig=newproject
```

Molecule steps interpolate `${rig}` in their descriptions.

## Related Issues

- gt-lx3n: Witness startup bond
- gt-j6s8: Refinery startup bond
- gt-rana: Patrol System epic
- gt-sye: Mayor startup protocol


Silent error handling with _ = err patterns

Multiple locations intentionally ignore errors with comments like 'Ignore errors'. While some are valid (best-effort operations), others should be audited:

- witness/manager.go:542 - Ignores cmd.Run() error
- refinery/manager.go:411, 438, 509 - Ignores loadState and git pull errors
- swarm/manager.go:47 - Ignores getGitHead error
- polecat/manager.go:69, 284 - Ignores pool.Load and DeleteBranch errors
- swarm/integration.go:101, 137 - Ignores git pull and push errors

Each should be evaluated: log it, handle it, or confirm ignoring is intentional.


Batch work landing report generation

Generate reports on batch work completion.

## Trigger

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

## Report Contents

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

## Output Locations

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

## Command

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

## Format

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

## Note

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

## Reference

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


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


Test: New Router

Testing bd create for mail


gt done: Notify Witness with exit outcome

When polecat runs gt done, it should send mail to Witness:

```
gt mail send <rig>/witness -s 'POLECAT_DONE <name>' -m 'Exit: COMPLETED
Issue: <issue-id>
MR: <mr-id>
Branch: <branch>'
```

Exit types:
- COMPLETED: Work done, MR submitted
- ESCALATED: Hit blocker, needs human
- DEFERRED: Work paused, issue still open

This enables Witness patrol to:
1. See completion in inbox-check step
2. Verify git state is clean
3. Kill session and prune worktree
4. Close the polecat lease in its patrol wisp

Paired with gt-r6td (spawn notification) - together they bracket polecat lifecycle.


Test7

Test with fixed workdir


gt handoff: Deadlock bug in runHandoff

Running 'gt handoff' from a polecat causes a deadlock:

Stack trace shows:
- goroutine 1 [select (no cases)] in runHandoff
- File: internal/cmd/handoff.go:125

The command successfully sends the shutdown request but then hangs with 'fatal error: all goroutines are asleep - deadlock!'


Polecat template: move startup announcement into molecule

Template has STARTUP PROTOCOL with 'Announce: beads Polecat furiosa, checking in.' This should be a startup step in the polecat molecule, not prose instruction in CLAUDE.md. The molecule drives behavior, not the template prose.


Polecat template: fix 'git clone' to 'git worktree'

Template says 'You're in a project git clone' but polecats use worktrees, not clones. Fix terminology.


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.


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.


test message


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.


Refinery Patrol Cycle


Merge: gt-h6eq.7

branch: polecat/dag
target: main
source_issue: gt-h6eq.7
rig: gastown


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)


Design GGT prompt architecture

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


gt mq list shows empty when MRs exist

The gt mq list command returns empty results even when merge requests exist in the queue.


Merge: gt-7923

branch: polecat/rictus
target: main
source_issue: gt-7923
rig: gastown