test-after-fix

test


Phase 1.4: Basic patrol runner in Deacon

Deacon CLAUDE.md and prime context for patrol execution.

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

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

Depends: gt-rana.3


Harness Design & Documentation

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

## Current Problems

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

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

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

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

## Proposed Work

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


Fix gt prime to give crew workers crew context

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


gt mq integration create: create integration branch for epic

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

Actions:
1. Verify epic exists
2. Create branch: integration/<epic-id> from main
3. Push to origin
4. Store integration branch info in epic metadata

Usage:
  gt mq integration create gt-auth-epic
  # Creates integration/gt-auth-epic from main

Future MRs for this epic's children will auto-target this branch.

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


Add harness overview to Mayor priming

Update gt prime Mayor context to explain the harness concept: umbrella repo for GT installation, rigs underneath, Mayor sits above all rigs


Create Refinery role template

Add Refinery template to internal/templates/roles/:
- refinery.md.tmpl with full role context
- Variables: rig name, working directory, handoff bead ID
- Update SeedRoleTemplates to include it
- gt prime uses this template for Refinery context


Test Epic: GGT MVP Validation


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.


Molecule Workflow Engine: Composable Crystallized Workflows

# Epic: Molecule Workflow Engine

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

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

## The Core Concepts

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

## Key Features Needed

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

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

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

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

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

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


Test Task 2: Add comment to version.go


gt doctor: detect orphaned code on beads-sync branch

After merging beads-sync to main, some code changes can be lost if the merge conflict resolution drops files.

Add a doctor check that runs:
  git diff main..beads-sync -- '*.go' '*.md'

If there are differences in code files (not just .beads/), warn about potentially orphaned work.

Today's incident: Merge 96c773f lost mailbox.go and router.go changes from 5791752, requiring re-implementation.

Acceptance:
- gt doctor warns if beads-sync has unmerged code changes
- Excludes .beads/ directory (expected to differ)
- Shows file list of orphaned changes


CLI: polecat commands (add, remove, list, wake, sleep)

Polecat management CLI commands.

## Commands

### gt polecat list
List polecats in a rig.
```
gt polecat list <rig> [--json]
gt polecat list --all [--json]
```
Output:
- Name
- State (idle/active/working/done/stuck)
- Current issue (if any)
- Session status (running/stopped)

### gt polecat add
Add a new polecat to a rig.
```
gt polecat add <rig> <name>
```
- Creates polecat directory
- Clones rig repo
- Creates work branch
- Initializes state

### gt polecat remove
Remove a polecat from a rig.
```
gt polecat remove <rig>/<polecat> [--force]
```
- Fails if session running
- Warns if uncommitted changes
- --force bypasses checks

### gt polecat wake
Mark polecat as active (ready for work).
```
gt polecat wake <rig>/<polecat>
```
Transitions: idle → active

### gt polecat sleep
Mark polecat as idle (not available).
```
gt polecat sleep <rig>/<polecat>
```
Transitions: active → idle
Fails if session running (stop first).

## Address Format

`<rig>/<polecat>` for operations on specific polecat.
`<rig>` for list command.

## Implementation

```go
var polecatCmd = &cobra.Command{Use: "polecat"}
polecatCmd.AddCommand(listCmd, addCmd, removeCmd, wakeCmd, sleepCmd)
```


Merge: gt-unrd

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


gt mq integration land: merge integration branch to main

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

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

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

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

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


MR state transitions: validate open→in_progress→closed

Enforce valid state transitions for merge-requests:

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

Invalid:
- closed → anything (immutable once closed)

Implement validation in MR update operations.

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


Engineer main loop: poll for ready merge-requests

Implement the Engineer's main processing loop.

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

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

The loop should be interruptible and handle graceful shutdown.

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


gt uninstall: Clean removal of Gas Town harness

Add 'gt uninstall' command to cleanly remove a Gas Town installation.

Should:
- Remove harness directory structure
- Optionally preserve rigs/data with --keep-data flag
- Warn about running sessions
- Clean up any global config references


Merge: gt-3x1

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


Polecat prompting: gt mq submit on completion

Update Polecat CLAUDE.md prompting to:

1. On task completion, run: gt mq submit --issue <id>
2. This creates a merge-request bead in the queue
3. Engineer will process it

The Polecat self-reports completion to the merge queue.


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.


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.


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.


Phase 3.2: mol-polecat-work squash step

Add summary+squash as final steps in mol-polecat-work.

## Current Steps

1. read-assignment
2. understand-task
3. implement-solution
4. verify-tests
5. commit-work
6. signal-done

## Updated Steps

1. read-assignment
2. understand-task
3. implement-solution
4. verify-tests
5. commit-work
6. **generate-summary** ← NEW
7. **squash-molecule** ← NEW
8. signal-done

## Step Definitions

### generate-summary
Agent writes a concise summary following the template.
Save to a local variable or temp file.

### squash-molecule
Run: bd squash $MOLECULE_ROOT --summary "$SUMMARY"
This creates digest in main beads, cleans ephemeral.

## Update Location

This requires updating the mol-polecat-work definition in beads molecule catalog.


Merge: gt-h5n.5

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


Work assignment messages should auto-close on completion

When a polecat completes work on an issue, the work assignment message (msg-type:task) stays open. Found 7 stale work assignments in gastown after swarm completed.

Proposal: When bd close is called on an issue, auto-close any work assignment messages that reference that issue in their body.

Alternative: Work assignment messages could use a different lifecycle - perhaps they should be acked (closed) when the polecat starts working, not when they finish.


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.


init.go: Replace custom contains() with strings.Contains


Interactive prompts for destructive operations

Add interactive confirmations for destructive operations.

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

## Implementation Options

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

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

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

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

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


Merge: gt-rana.3

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


Swarm learning: Beads database locality gap

## PGT Bug (GGT Design Already Correct)

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

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

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


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)


Refinery semantic merge processing loop

Implement the Refinery semantic merge loop for processing polecat work.

## Overview

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

## Processing Loop

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

## Semantic Merge

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

## Daemon Poking

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

## Dependencies

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

## Reference

See PGT ephemeral.py for branch operations.


Outpost/Worker interfaces: Core abstractions for remote compute

## Overview

Define the core interfaces that all outpost types implement.

## Interfaces

```go
type OutpostType string
const (
    OutpostLocal    OutpostType = "local"
    OutpostSSH      OutpostType = "ssh"
    OutpostCloudRun OutpostType = "cloudrun"
)

type Outpost interface {
    Name() string
    Type() OutpostType
    MaxWorkers() int
    ActiveWorkers() int
    Spawn(issue string, config WorkerConfig) (Worker, error)
    Workers() []Worker
    Ping() error
    SendMail(worker string, msg Message) error  // optional
}

type Worker interface {
    ID() string
    Outpost() string
    Status() WorkerStatus  // idle, working, done, failed
    Issue() string
    Attach() error         // for interactive outposts
    Logs() (io.Reader, error)
    Stop() error
}

type WorkerConfig struct {
    RigPath    string
    BeadsDir   string
    GitBranch  string
    Context    map[string]string  // hints for worker
}
```

## Files

- `internal/outpost/outpost.go` - Outpost interface
- `internal/outpost/worker.go` - Worker interface
- `internal/outpost/config.go` - WorkerConfig, OutpostType

## Notes

This is the foundation for all federation work. Keep interfaces minimal - we can extend later.


File locking for concurrent access

Add file locking for concurrent access safety.

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

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

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

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

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

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

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

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

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

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


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.


gt mq integration status: show integration branch status

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

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

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

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

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

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


Bug: LIFECYCLE messages use literal <rig> placeholder

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

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

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


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.


CLI: workspace commands (init, add, list)

GGT needs workspace management commands beyond install.

## Commands (beyond gt-f9x.3 install)

### gt workspace list
List all rigs in current workspace.
```
gt workspace list [--json]
```
Essentially `gt rig list` but framed as workspace view.

### gt workspace add
Add existing rig to workspace (alternative to gt rig add).
```
gt workspace add <git-url> [--name NAME]
```

### gt onboard
Interactive first-time setup wizard.
```
gt onboard
```
- Prompts for workspace location
- Creates structure via gt install
- Offers to add first rig

## Note
May be redundant with gt-f9x.3 (install) and gt-u1j.16 (rig commands).
Consider if this is needed or should be closed as covered by those.

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


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


Polecat CLAUDE.md: configure auto-approve for bd and gt commands

Polecats get stuck waiting for bash command approval when running
bd and gt commands. Need to configure Claude Code to auto-approve these.

Options:
1. Add allowedTools to polecat CLAUDE.md
2. Configure .claude/settings.json in polecat directory
3. Use --dangerously-skip-permissions flag (not recommended)

Should auto-approve:
- bd (beads commands)
- gt (gastown commands)
- go build/test
- git status/add/commit/push

Should still require approval:
- rm -rf
- Arbitrary commands outside project

Related to polecat prompting (gt-e1y, gt-sd6).


Mail coordination should use town-level database

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


Town-level beads: Real DB for coordination mail

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

## Background

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

## Design

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

Matches HOP hierarchy: platform > project > worker

## Structure

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

## Tasks

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

## Addressing

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


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


Test merge request

branch: polecat/Test/gt-test
target: main
source_issue: gt-test
worker: TestWorker
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.


Test message

Testing GGT mail via beads


detectSender() doesn't recognize crew workers

## Problem

detectSender() in internal/cmd/mail.go only checks for /polecats/ directories. Crew workers in /crew/<name>/ fall through to the default 'mayor/', so:
- gt mail inbox shows mayor's inbox instead of the crew worker's
- gt mail send sets the wrong From address

## Fix

Add crew worker detection before the /polecats/ check:

if strings.Contains(cwd, "/crew/") {
    parts := strings.Split(cwd, "/crew/")
    ...
    return fmt.Sprintf("%s/crew/%s", rigName, crewMember)
}

## Affected
- Any crew worker running gt mail inbox without explicit address
- Crew worker handoffs (wrong sender)


Update Mayor prompting in gastown-py

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


Pinned Beads: Ongoing concerns and anchors

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


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)


Work on gt-ogr: Fix rig count in tmux status bar. The cou...

Work on gt-ogr: Fix rig count in tmux status bar. The count is showing wrong. Run 'bd show gt-ogr' for details.


Human escalation: notify overseer when self-heal fails

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

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

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

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

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


Beads CLI wrapper: shell out to bd

Wrapper for bd (beads CLI) commands.

## Interface

```go
type Beads struct {
    workDir string
}

func NewBeads(workDir string) *Beads

// Query
func (b *Beads) List(opts ListOptions) ([]*Issue, error)
func (b *Beads) Ready() ([]*Issue, error)
func (b *Beads) Show(id string) (*Issue, error)
func (b *Beads) Blocked() ([]*Issue, error)

// Mutations
func (b *Beads) Create(opts CreateOptions) (*Issue, error)
func (b *Beads) Update(id string, opts UpdateOptions) error
func (b *Beads) Close(ids ...string) error

// Sync
func (b *Beads) Sync() error
func (b *Beads) SyncStatus() (*SyncStatus, error)
```

## Data Types

```go
type Issue struct {
    ID          string
    Title       string
    Status      string
    Priority    int
    Type        string
    Description string
    Parent      string
    Children    []string
    DependsOn   []string
    Blocks      []string
}

type ListOptions struct {
    Status   string  // "open", "closed", "all"
    Type     string
    Priority int
}

type CreateOptions struct {
    Title       string
    Type        string
    Priority    int
    Description string
    Parent      string
}
```

## Implementation

Shell out to bd binary, parse JSON output where available.
```go
func (b *Beads) runBd(args ...string) ([]byte, error) {
    cmd := exec.Command("bd", args...)
    cmd.Dir = b.workDir
    return cmd.Output()
}
```

## Error Handling

- bd not installed: Clear error with install instructions
- Not a beads repo: Detect and report
- Sync conflicts: Parse and report


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
)


Swarm learning: Session cleanup missing from swarm workflow

## Problem

After Enders Game swarm completed (18 issues merged), 16 polecat sessions were left running but idle. No automated cleanup occurred.

## What Should Happen

1. Witness detects polecat completed work (idle at prompt)
2. Witness verifies git state is clean
3. Witness shuts down session
4. Witness reports completion to Mayor

## GGT Components

- gt-cxx: Witness context cycling (covers self-cycling)
- gt-u1j.9: Witness daemon heartbeat loop
- gt-kmn.6: Witness swarm landing protocol

## Recommendation

Add to Witness responsibilities:
- Monitor for 'work complete' signals (DONE keyword, idle detection)
- Automated session shutdown after verification
- Swarm completion reporting to Mayor

See also: architecture.md 'Worker Cleanup (Witness-Owned)' section which describes this but it wasn't implemented in PGT.


Hook system for event extensibility

GGT needs hook system for extensibility like PGT.

## Event Types
```go
type Event string
const (
    EventPreSessionStart  Event = "pre-session-start"
    EventPostSessionStart Event = "post-session-start"
    EventPreShutdown      Event = "pre-shutdown"
    EventPostShutdown     Event = "post-shutdown"
    EventOnPaneOutput     Event = "on-pane-output"
    EventSessionIdle      Event = "session-idle"
    EventMailReceived     Event = "mail-received"
    EventWorkAssigned     Event = "work-assigned"
)
```

## Hook Configuration
File: .claude/hooks.json or .gastown/hooks.json
```json
{
  "hooks": {
    "pre-shutdown": [
      {"type": "command", "cmd": "./scripts/pre-shutdown.sh"}
    ],
    "on-pane-output": [
      {"type": "command", "cmd": "./scripts/activity-monitor.sh"}
    ]
  }
}
```

## Hook Types
1. **Command**: Execute external script
2. **Built-in**: Internal Go functions (pre-shutdown checks)

## Hook Interface
```go
type HookRunner struct {
    config *HookConfig
}

type HookResult struct {
    Success bool
    Message string
    Block   bool  // For pre-* hooks: should operation be blocked?
}

func (r *HookRunner) Fire(event Event, ctx *HookContext) []HookResult
```

## CLI Commands
```
gt hooks list [<event>]    # List registered hooks
gt hooks fire <event>      # Manually fire for testing
gt hooks test [--all]      # Validate hook config
```

## Integration Points
- internal/session/manager.go: Fire pre/post session hooks
- internal/mail/router.go: Fire mail-received hook

## New Package
internal/hooks/
├── types.go    # Event, HookConfig, HookResult
├── runner.go   # HookRunner, Fire()
└── builtin.go  # Built-in hooks (pre-shutdown checks)

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

## Acceptance Criteria
- [ ] Hook config loading from JSON
- [ ] Command hooks execute subprocess
- [ ] Pre-shutdown hook integration with session stop
- [ ] CLI for listing and testing hooks


Merge: gt-g44u.3

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


Add unit tests for daemon package

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


CLI: names commands for polecat naming pool

Polecat naming pool for auto-generated names.

## Commands

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

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

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

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

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

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

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

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

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


gt spawn: Enter key needs debounce delay after paste

## Problem

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

## Observed Behavior

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

## Expected Behavior

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

## Root Cause

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

## Impact

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

## References

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


Merge: gt-5af.5

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


Rig doctor checks: Refinery health, clones, gitignore

Rig-level doctor checks.

## Checks

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

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

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

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

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

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

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

## Implementation

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


Add Deacon heartbeat mechanism

Deacon writes deacon/heartbeat.json on each wake. Go daemon reads it to decide whether to poke. Add heartbeat read/write helpers.


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


mol-christmas-launch: 3-day execution plan

---

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

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

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

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

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

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

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


Design: Mayor session cycling and handoff

Design for Mayor session cycling and structured handoff.

## Overview

Mayor coordinates across all rigs and runs for extended periods. Needs session cycling pattern with structured handoff notes.

## Key Elements

1. Session cycling recognition (when to cycle)
2. Handoff note format (structured state capture)
3. Handoff delivery (mail to self)
4. Fresh session startup (reading and resuming)

## Subtasks (implementation)

- gt-g2d: Mayor session cycling prompting
- gt-sye: Mayor startup protocol prompting
- gt-vci: Mayor handoff mail template
- gt-1le: town handoff command (optional, P2)

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


MQ metrics: pending count, processing time, success rate

Implement merge queue metrics collection.

Metrics to track:
- mq_pending_count: MRs waiting
- mq_processing_time: Time from submit to merge (histogram)
- mq_success_rate: Merges vs rejections
- mq_conflict_rate: How often conflicts occur
- mq_test_failure_rate: Test failures

Storage options:
- Simple: JSON file with rolling stats
- Advanced: Prometheus-compatible metrics endpoint

Display via:
- gt mq stats: summary statistics
- Dashboard widget (separate task)

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


Witness session cycling and handoff

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

## Session Cycling Protocol

```markdown
## Session Cycling

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

### Handoff Protocol

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

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

## Active Workers
<list workers and status>

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

## Recent Actions
<last 3-5 actions>

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

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

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

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

### On Fresh Session Start

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

## Implementation

Add to WITNESS_CLAUDE.md template.


gt prime should detect mayor role from any rig's mayor/ folder

Currently gt prime only detects the Mayor role when run from town root (~/gt). It should also detect Mayor when run from:
- ~/gt/gastown/mayor/rig (rig's internal mayor dir)
- Any path containing /mayor/ under a rig

This would allow the mayor session to work correctly regardless of which rig directory it's attached to.


Test4

test4 body


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.


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)


Retry logic: exponential backoff for transient failures

Implement retry logic for transient failures.

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

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

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

Track retry count in MR metadata for debugging.

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


gt install command: Create workspace structure

Create Gas Town workspace structure.

## Command

```
gt install [path]
```

If path omitted, uses current directory.

## Created Structure

```
<path>/
├── config/
│   ├── town.json      # {"type": "town", "version": 1, ...}
│   └── rigs.json      # {"version": 1, "rigs": {}}
│
└── mayor/
    ├── CLAUDE.md      # Mayor role prompting (from template)
    ├── mail/
    │   └── inbox.jsonl  # Empty inbox
    └── state.json     # Initial mayor state
```

## Implementation

```go
func Install(path string, opts InstallOptions) error

type InstallOptions struct {
    TownName string  // defaults to directory name
    Force    bool    // overwrite existing
}
```

## Steps

1. Validate path (exists, writable)
2. Check not already a town (unless --force)
3. Create config/ directory
4. Write town.json with name and timestamp
5. Write empty rigs.json
6. Create mayor/ directory structure
7. Write CLAUDE.md from template
8. Create empty inbox.jsonl
9. Write initial state.json

## Error Cases

- Path does not exist: Create it (like mkdir -p)
- Already a town: Error unless --force
- Permission denied: Clear error message
- Inside existing town: Warn (nested towns not recommended)

## Templates

Mayor CLAUDE.md comes from embedded template (see gt-u1j.20).


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.


MQ config schema: merge_queue section in rig config.json

Define and implement merge_queue configuration in rig config.

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

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

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


Work on gt-fix-bugs: Fix blocking infrastructure bugs. Se...

Work on gt-fix-bugs: Fix blocking infrastructure bugs. See issue for details. Run 'bd show gt-fix-bugs' to see the full issue.


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


Plugin: work-oracle (pre-dispatch analysis)

Plugin for pre-dispatch task analysis and decomposition.

## Purpose

Before dispatching workers for an epic, ask work-oracle to:
- Validate task breakdown
- Identify parallelization opportunities
- Predict conflicts between tasks
- Suggest worker count

## Hook Point

Mayor consults work-oracle before spawning workers.

## Interface

Input (via mail):
- Epic ID with proposed child issues
- Rig context (current state of codebase)

Output (via mail + bead):
- Recommended task groupings
- Dependency suggestions
- Risk assessment (which files might conflict)
- Optimal worker count

## Structure

```
<rig>/plugins/work-oracle/
├── CLAUDE.md       # Analysis prompts
├── mail/inbox.jsonl
└── state.json
```

Uses existing plugin-as-agent architecture.

## Note

Was "swarm-oracle" - renamed because there are no swarm IDs. This plugin helps plan batch work, not manage swarm entities.


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

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


Merge: gt-h5n.5

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


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.


MCP beads tools fail from polecat directories

The beads MCP plugin (plugin:beads:beads) fails to find issues when invoked from polecat directories.

## Observed
- Polecat in /gt/gastown/polecats/dementus
- MCP tool: plugin:beads:beads - show (issue_id: 'gt-th7')
- Error: 'bd command failed: Error: no issue found matching gt-th7'

## Expected
MCP tools should work the same as running bd directly.

## Note
This may be related to gt-tmm (beads sync issue) rather than an MCP-specific bug. The MCP tool uses bd under the hood, and bd can't find the issue because the polecat's beads database is stale.


Plugin: Plan-to-Epic converter

## Purpose

Help users create beads epics from various planning inputs.

## Inputs
- Markdown task lists
- GitHub issues
- Linear/Jira exports
- Free-form descriptions
- Existing beads epics

## Output
- Beads epic with properly structured children
- Dependencies set for wave ordering
- Priorities assigned
- Ready for `gt spawn --epic <id>`

## Implementation Options

### Option A: CLI Tool
```bash
gt plan import --from github --repo owner/repo --label batch-candidate
gt plan import --from markdown tasks.md
gt plan structure <epic-id>  # analyze and add dependencies
```

### Option B: Plugin Agent
A plugin at `<rig>/plugins/plan-oracle/` that:
- Receives planning requests via mail
- Analyzes scope and requirements
- Creates structured beads epic
- Sets dependencies based on analysis

### Option C: Interactive Mode
```bash
gt plan create
# Walks through questions, creates epic interactively
```

## Axiom

As stated: 'The Planning phase should end in the creation of a workable Beads plan.'

This plugin bridges the gap between human planning and machine-executable work.

## Priority

P2 - Nice to have for MVP. Manual epic creation works for now.

## Note

No "swarm IDs" - output is just a beads epic with children. Workers process it independently.


Crew workers: design documentation and remaining work

## Summary

Crew workers are user-managed persistent workspaces within a rig, distinct from polecats.

## Current Implementation

### Commands (all working)
- `gt crew add <name>` - Create workspace (git clone)
- `gt crew list` - List workspaces with status
- `gt crew at <name>` - Attach to tmux session
- `gt crew remove <name>` - Remove workspace
- `gt crew pristine [<name>]` - git pull + bd sync
- `gt crew refresh <name>` - Context cycle with handoff mail
- `gt crew status [<name>]` - Detailed status
- `gt crew rename <name>` - Rename workspace

### Crew vs Polecat

| Aspect | Crew | Polecat |
|--------|------|---------|
| Lifecycle | User-managed | Witness-managed |
| Scheduling | Manual | `gt spawn` |
| Merge flow | Direct push OK | Integration branch → refinery |
| Garbage collection | Never | Auto on completion |
| Identity | Long-lived (emma, dave) | Ephemeral (Nux, Toast) |

## Known Bugs

- **gt-70b3**: detectSender() doesn't recognize crew workers
- **gt-vdp0**: Crew CLAUDE.md shows Refinery template

## Potential Enhancements

1. Rebase helper in `gt crew pristine` for conflict resolution
2. Cross-rig crew support (crew worker in multiple rigs?)
3. Better mail identity auto-detection for crew


Test message

Testing GGT mail integration


MR ID generation: prefix-mr-hash convention

Generate merge-request IDs following the convention: <prefix>-mr-<hash>

Example: gt-mr-abc123 for a gastown merge request.

This distinguishes MRs from regular issues while keeping them in the same namespace.
The hash should be derived from branch name + timestamp for uniqueness.

Implement: GenerateMRID(prefix, branch string) string

Reference: docs/merge-queue-design.md#id-convention


Tmux status line: rig color themes and worker identity display

## Summary

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

## Current Problem

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

## Proposed Design

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

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

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

## Configuration

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

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


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)


Work request: gt-976 crew lifecycle support

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

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

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

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

- Dave (beads/crew/dave)


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


Test: Alpha to Beta

Sibling communication test


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


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


Work assignment mail not received by polecat

When gt spawn sends a work assignment:

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

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

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

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

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


gt mq reject: manual MR rejection

Implement 'gt mq reject <id> --reason "..."' for manual rejection.

Actions:
1. Verify MR exists and is open
2. Close MR with close_reason=rejected
3. Notify worker via mail (optional)
4. Do NOT close source issue (work not done)

Options:
- --reason REASON: required explanation
- --notify: send mail to worker

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


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

CLI commands for swarm management.

## Commands

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

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

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

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

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

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

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

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

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

## Output Format

Support --json for machine-readable output.

## Dependencies

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


Test message

Testing GGT mail system


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)


Test infrastructure: fixtures, mocks, integration tests

Test infrastructure for Gas Town.

## Test Categories

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

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

## Fixtures

```go
package testutil

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

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

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

## Mocks

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

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

## Test Helpers

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

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

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

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

## CI Integration

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

## Test Data

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


Batch Work System: Integration branch, merge queue, landing

## Overview

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

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

## Architecture

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

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

## What This Epic Covers

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

## What Was Removed

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

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

## References

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


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{},
}
```


Phase 2.1: gt spawn --molecule bonds in ephemeral

Make gt spawn molecule-aware with ephemeral bonding.

## New Flag

```bash
gt spawn --issue gt-xxx --molecule mol-polecat-work
```

## Behavior

1. Create polecat with fresh worktree (existing)
2. Bond molecule in ephemeral: `bd mol bond mol-polecat-work --ephemeral`
3. Link molecule root to source issue
4. Include molecule context in work assignment mail
5. Start session

## Work Assignment Mail

```
Subject: Work Assignment: Fix lifecycle bug [MOLECULE]

You are working on gt-rixa as part of molecule mol-polecat-work.

Molecule instance: eph-abc123
Current step: read-assignment (1/8)

Follow the molecule workflow. When complete, generate summary and squash.
```


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


Tmux wrapper: session operations

Wrapper for tmux session operations via subprocess.

## Interface

```go
type Tmux struct{}

func NewTmux() *Tmux

// Session lifecycle
func (t *Tmux) NewSession(name, workDir string) error
func (t *Tmux) KillSession(name string) error
func (t *Tmux) HasSession(name string) (bool, error)
func (t *Tmux) ListSessions() ([]string, error)

// Session interaction
func (t *Tmux) SendKeys(session, keys string) error
func (t *Tmux) CapturePane(session string, lines int) (string, error)
func (t *Tmux) AttachSession(session string) error

// Window operations (if needed)
func (t *Tmux) SelectWindow(session string, index int) error
```

## Session Naming

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

## Implementation

- Shell out to tmux binary
- CapturePane uses `tmux capture-pane -p -t <session> -S -<lines>`
- SendKeys uses `tmux send-keys -t <session> '<keys>' Enter`

## Error Handling

- "no server running": tmux not started (not an error for HasSession)
- "session not found": session doesn't exist
- Wrap all tmux errors with context

## Environment

Requires TMUX_TMPDIR or uses default socket. Don't assume tmux is running.


Merge: gt-g44u.2

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


gt witness: Witness management commands

Add 'gt witness' command group for witness lifecycle management.

Subcommands:
- gt witness start [rig] - Start witness for a rig
- gt witness stop [rig] - Stop witness
- gt witness status [rig] - Show witness status
- gt witness attach [rig] - Attach to witness session

Witness monitors polecats and handles:
- Idle detection and cleanup
- Session health checks
- Nudging stuck agents


Policy beads: config in data plane

Use sentinel/policy beads for configuration instead of external config. Examples: daemon notifications on/off, heartbeat intervals. Config lives in the bead graph, can be toggled by closing/opening policy beads.


Create mol-gastown-polecat derived molecule

Create the standard Gas Town polecat workflow molecule.

## Molecule Definition
\`\`\`markdown
## Molecule: gastown-polecat
Full workflow for Gas Town polecats including binary installation.

Includes: mol-engineer-in-box

## Step: install-binary
After merge is submitted, rebuild and install the local gt binary.
This ensures the latest code is available to all local agents.

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

Needs: submit
\`\`\`

## Why This Molecule
Every polecat that pushes to main should also rebuild the binary.
This ensures the installed gt is always current with main.

## Implementation
1. Add to builtin_molecules.go (after Includes support lands)
2. Update SeedBuiltinMolecules
3. Run gt molecule seed

## Acceptance
- [ ] mol-gastown-polecat exists
- [ ] Includes all engineer-in-box steps
- [ ] Adds install-binary step after submit
- [ ] Can be used with gt spawn --molecule


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.


CLI: spawn command

High-level command to spawn a polecat with work assignment.

## Command

```
gt spawn <rig>/<polecat> --issue <id>
gt spawn <rig> --issue <id>  # auto-select available polecat
gt spawn <rig>/<polecat> -m "Work on X"  # free-form task
```

## What It Does

1. Select or create polecat
2. Ensure polecat is in idle/active state
3. Assign the issue (update beads, set polecat state)
4. Start session (gt session start)
5. Inject initial context

## Options

- `--issue <id>`: Beads issue to assign
- `-m "message"`: Free-form work description
- `--create`: Create polecat if it does not exist
- `--no-start`: Assign work but do not start session

## Auto-Select Logic

If polecat not specified:
1. Look for idle polecats in rig
2. Prefer ones that worked on similar issues before
3. If none available, fail (or create if --create)

## Initial Context

Injects into session:
```
[SPAWN] You have been assigned: <issue-title>
Issue: <id>
Priority: <priority>

<description>

Work on this issue. Signal DONE when complete.
```

## Implementation

Orchestrates PolecatManager + SessionManager + Beads:
```go
func Spawn(rig, polecat, issue string, opts SpawnOptions) error
```


bd create should support molecule type

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


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


CloudRun persistent connections: HTTP/2 keepalive

## Overview

Implement persistent HTTP/2 connections to keep Cloud Run containers warm.

## Problem

Cloud Run has cold start latency when scaling from 0. Persistent connections keep containers warm.

## Implementation

```go
type PersistentConnectionManager struct {
    serviceURL string
    conn       net.Conn
    client     *http2.ClientConn
    mu         sync.Mutex
    lastUsed   time.Time
    keepAlive  time.Duration
}

func (m *PersistentConnectionManager) GetConnection() (*http2.ClientConn, error) {
    m.mu.Lock()
    defer m.mu.Unlock()

    // Reuse existing connection if healthy
    if m.client != nil && m.client.CanTakeNewRequest() {
        m.lastUsed = time.Now()
        return m.client, nil
    }

    // Create new connection
    return m.dial()
}

func (m *PersistentConnectionManager) keepAliveLoop() {
    ticker := time.NewTicker(m.keepAlive / 2)
    for range ticker.C {
        m.mu.Lock()
        if time.Since(m.lastUsed) > m.keepAlive {
            // Connection idle too long, close it
            m.close()
        } else {
            // Send ping to keep alive
            m.ping()
        }
        m.mu.Unlock()
    }
}
```

## Integration with CloudRunOutpost

```go
type CloudRunOutpost struct {
    // ...existing fields...
    connMgr *PersistentConnectionManager
}

func (o *CloudRunOutpost) Spawn(...) {
    conn, err := o.connMgr.GetConnection()
    // Use connection for work dispatch
}
```

## Config

```yaml
outposts:
  - name: cloudrun-burst
    type: cloudrun
    # ...
    keep_alive: 5m  # Keep connection warm for 5 minutes
```

## Files

- `internal/cloudrun/connection.go`

## Dependencies

Depends on: gt-9a2.8 (basic CloudRunOutpost)


Test2

Testing gt mail


Heartbeat convention: simple liveness signal for agents

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

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

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

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

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


gt crew remove: Remove crew workspace

Implement 'gt crew remove <name> [--force]' command:
- Check for uncommitted changes, unpushed commits
- Warn and require --force if dirty
- Kill any running session
- Remove directory
- Unregister from mail


Per-epic config overrides: custom merge settings

Allow per-epic merge configuration overrides.

Epic can specify merge_config in its description:
  merge_config:
    run_tests: true
    test_command: 'go test -race ./...'
    on_conflict: assign_back

When processing MRs for an epic:
1. Load rig-level merge_queue config
2. Check if epic has merge_config
3. Merge epic config over rig config
4. Use merged config for processing

Use cases:
- Risky refactors: more thorough testing
- Urgent fixes: skip some checks
- Experimental work: different test suite

Reference: docs/merge-queue-design.md#per-epic-overrides


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

Work on gt-role-template: Refine witness/CLAUDE.md role template. See issue for details. Run 'bd show gt-role-template' to see the full issue.


Merge: gt-72so

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


test message


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)


Add gt crew rename command


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!'


Fix TestHasPolecat test failure in internal/session

TestHasPolecat in internal/session/manager_test.go fails because it expects
specific polecats (Toast, Cheedo) to exist in the test environment.

Error:
```
manager_test.go:46: expected hasPolecat(Toast) = true
manager_test.go:49: expected hasPolecat(Cheedo) = true
```

Fix: Either create test fixtures or mock the filesystem check.


Molecules: Composable Workflow Beads

## Summary

Molecules are crystallized workflow patterns stored as Beads issues.
When instantiated, the molecule creates child beads forming a DAG.

## Key Insight: Molecules ARE Beads

Per HOP Decision 001: Beads IS the ledger. Molecules don't get a separate YAML format - they're issues with `type: molecule` containing prose-based step definitions.

Agents don't need rigid schemas. They parse natural language natively. A molecule is just instructions with enough structure for tooling.

## Example: Engineer in a Box

```markdown
id: mol-xyz
type: molecule
title: Engineer in a Box

This workflow takes a task from design to merge.

## Step: design
Think carefully about architecture. Consider existing patterns,
trade-offs, testability.

## Step: implement
Write clean code. Follow codebase conventions.
Needs: design

## Step: review
Review for bugs, edge cases, style issues.
Needs: implement

## Step: test
Write and run tests. Cover happy path and edge cases.
Needs: implement

## Step: submit
Submit for merge via refinery.
Needs: review, test
```

## Instantiation

```bash
# Attach molecule when spawning
gt spawn --issue gt-abc --molecule mol-xyz

# Creates child beads atomically:
gt-abc.design     ← ready first
gt-abc.implement  ← blocked by design
gt-abc.review     ← blocked by implement
gt-abc.test       ← blocked by implement
gt-abc.submit     ← blocked by review, test
```

Each step issue gets an `instantiated-from` edge to the molecule (with step metadata).

## Why This Matters

1. **Unified data plane**: Everything in Beads, no parallel YAML channel
2. **AI-native**: Prose instructions, not rigid schemas
3. **Error isolation**: Each step is a checkpoint - failure doesn't lose progress
4. **Scales with AI**: As agents get smarter, they handle more complex molecules

## Implementation Primitives

- `ParseMoleculeSteps()`: Extract steps from prose (convention-based)
- `InstantiateMolecule()`: Atomic transaction creating all steps + edges
- `instantiated-from` edge type: Track provenance
- Parameterization: `{{variable}}` substitution from context map


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


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


Test: release command verification


Phase 1.3: gt doctor checks for ephemeral health

Add doctor checks for ephemeral beads repo.

## Checks

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

## Auto-fix

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


Architecture: beads config and direct landing docs

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


Connection interface: Protocol for local/remote ops

Abstract interface for local vs remote (SSH) operations.

## Concept

Enable Gas Town to manage rigs on remote machines via SSH. The Connection interface abstracts whether operations happen locally or remotely.

## Interface

```go
type Connection interface {
    // Identification
    Name() string
    IsLocal() bool

    // File operations
    ReadFile(path string) ([]byte, error)
    WriteFile(path string, data []byte, perm os.FileMode) error
    MkdirAll(path string, perm os.FileMode) error
    Remove(path string) error
    Stat(path string) (os.FileInfo, error)
    Glob(pattern string) ([]string, error)

    // Command execution
    Exec(cmd string, args ...string) ([]byte, error)
    ExecDir(dir, cmd string, args ...string) ([]byte, error)

    // Tmux (for session management)
    TmuxNewSession(name, dir string) error
    TmuxKillSession(name string) error
    TmuxSendKeys(session, keys string) error
    TmuxCapturePane(session string, lines int) (string, error)
}
```

## Implementations

- LocalConnection (gt-f9x.8): Direct file/exec operations
- SSHConnection (future): Operations via SSH

## Usage

```go
func NewRigManager(conn Connection, ...) *RigManager

// Operations work the same regardless of connection type
rm.DiscoverRigs()  // uses conn.Glob, conn.ReadFile
```

## Design Notes

- Connection obtained from MachineRegistry (gt-f9x.9)
- Default is always local
- SSH connection requires machine config (host, key, user)


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


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


Merge: gt-qsvq

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


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


Town & Rig Management: install, doctor, federation

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

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

## Architecture Reference

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


Molecule schema: YAML format for workflow definitions

Define the YAML schema for molecule definitions:

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

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

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


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.


Merge: gt-0ei3

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


Timed Beads: Scheduled recurring work

## Summary

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

## Schema Extension

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

## Daemon Integration

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

## Use Cases

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

## Interaction with Pinned Beads

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


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.


gt handoff: Deadlock in waitForRetirement


Merge: gt-72so

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


CLI: polecat commands (add, list, wake, sleep, decommission)

GGT is missing most polecat management commands that PGT has.

Missing Commands:
- gt polecat add <rig> <name> - Add polecat to rig (creates clone)
- gt polecat list [<rig>] - List polecats with state
- gt polecat info <polecat> - Show detailed info
- gt polecat wake <polecat> - Mark available
- gt polecat sleep <polecat> - Mark unavailable
- gt polecat decommission <polecat> - Remove polecat safely

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

Notes:
- spawn exists but doesn't cover management
- wake/sleep are in polecat manager but not CLI
- decommission should check for uncommitted work


Molecule CLI: bd molecule commands

Add molecule commands to bd:

## Commands

```bash
bd molecule list              # List molecules (type: molecule)
bd molecule show <id>         # Show molecule with parsed steps
bd molecule parse <id>        # Validate and show parsed structure
bd molecule instantiate <mol-id> --parent=<issue-id>  # Create steps
bd molecule instances <mol-id>  # Show all instantiations
```

## gt spawn integration

```bash
gt spawn --issue <id> --molecule <mol-id>
```

This should:
1. Call `bd molecule instantiate` (creates child beads atomically)
2. Spawn polecat on first ready step
3. Polecat grinds through via `bd ready`

## Output Examples

```
$ bd molecule show mol-abc

mol-abc: Engineer in a Box
Type: molecule

Steps (5):
  design      → (ready first)
  implement   → Needs: design
  review      → Needs: implement
  test        → Needs: implement
  submit      → Needs: review, test

Instances: 3
```

```
$ bd molecule instances mol-abc

Parent          Status    Created
gt-xyz          done      2025-12-15
gt-abc          active    2025-12-17 (3/5 complete)
gt-def          pending   2025-12-18
```


Update Refinery to use Beads merge queue

Replace branch discovery with Beads queue in the Refinery module:

Current (internal/refinery/manager.go):
- Scans for polecat/* branches
- Creates MR objects on-the-fly

New:
- Pull from Beads: bd ready --type=merge-request
- Process each MR
- Close with merge commit: bd close <id> --reason="Merged at <sha>"
- Handle failures: bd update <id> --status=blocked --reason="..."

The Engineer (agent) becomes Beads-native.
The Refinery (module) provides the infrastructure.


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.