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


Phase 2.3: Polecat CLAUDE.md molecule workflow protocol

Update polecat prompting for molecule-based work.

## CLAUDE.md Updates

Add section on molecule workflow:

```markdown
## Molecule Workflow

When assigned a molecule (check gt prime output):

1. Follow molecule steps in order
2. Mark steps complete: bd mol step complete <step-id>
3. Before signaling done:
   a. Generate summary of work performed
   b. Run: bd squash <molecule-root> --summary "<your summary>"
4. Then signal done as normal

### Summary Guidelines

Your summary should include:
- What was the task?
- What did you do?
- What was the outcome?
- Any issues or follow-ups?

Keep it to 2-4 sentences. This becomes the permanent record.
```


mol-deacon-patrol

Mayor's daemon patrol loop.

The Deacon is the Mayor's background process that runs continuously, handling callbacks, monitoring rig health, and performing cleanup. Each patrol cycle runs these steps in sequence, then loops or exits.


Digest: mol-deacon-patrol

Patrol 7: routine


Blocked issues: gt-um6q, gt-lz13, gt-5xph depend on missing beads features

Template/docs updates blocked on: bd-nurq (bd mol current), bd-29fb (bd close --continue). These gastown issues should be marked blocked on the beads issues once cross-rig deps work.


Phase 2.2: gt prime shows ephemeral molecule context

Update gt prime to detect and display ephemeral molecule state.

## Detection

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

## Output

```
🔧 Polecat furiosa, checking in.

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

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


Check own context usage.

Check own context usage.

If context is HIGH (>80%):
- Write handoff summary
- Prepare for burn/respawn

If context is LOW:
- Can continue processing

instantiated_from: mol-refinery-patrol
step: context-check


Digest: mol-deacon-patrol

Patrol 4: All healthy


Remove 'spawn' terminology from molecular chemistry

Clean all docs and code to present ONLY current terminology. No migration paths, no "old vs new" tables, no deprecated flags. The codebase should read as if the current design was always the design.

**Terminology rules:**
- spawn = polecats/workers ONLY
- pour = create persistent mol
- wisp = create ephemeral wisp
- run = create and execute

**Scope:**
- Remove all "Old → New" migration tables
- Remove deprecated flag documentation (--persistent, etc.)
- Remove chemistry-design-changes.md entirely (it is a migration doc)
- Clean all docs to use current terminology only
- Rename spawn functions in code that deal with molecules


Digest: mol-deacon-patrol

P16: stable


Digest: mol-deacon-patrol

Patrol 20: Final patrol, all healthy, handoff triggered


implement

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

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

Depends: load-context


Merge: gt-5af.5

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


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

This molecule is bonded dynamically by mol-witness-patrol's survey-workers step.
Each polecat being monitored gets one arm that runs in parallel with other arms.

## Variables

| Variable | Required | Description |
|----------|----------|-------------|
| polecat_name | Yes | Name of the polecat to inspect |
| rig | Yes | Rig containing the polecat |

## Step: capture
Capture recent tmux output for toast.

```bash
tmux capture-pane -t gt-gastown-toast -p | tail -50
```

Record:
- Last activity timestamp (when was last tool call?)
- Visible errors or stack traces
- Completion indicators ("Done", "Finished", etc.)

## Step: assess
Categorize polecat state based on captured output.

States:
- **working**: Recent tool calls, active processing
- **idle**: At prompt, no recent activity
- **error**: Showing errors or stack traces
- **requesting_shutdown**: Sent LIFECYCLE/Shutdown mail
- **done**: Showing completion indicators

Calculate: minutes since last activity.
Needs: capture

## Step: load-history
Read nudge history for toast from patrol state.

```
nudge_count = state.nudges[toast].count
last_nudge_time = state.nudges[toast].timestamp
```

This data was loaded by the parent patrol's load-state step and passed
to the arm via the bonding context.
Needs: assess

## Step: decide
Apply the nudge matrix to determine action for toast.

| State | Idle Time | Nudge Count | Action |
|-------|-----------|-------------|--------|
| working | any | any | none |
| idle | <10min | any | none |
| idle | 10-15min | 0 | nudge-1 (gentle) |
| idle | 15-20min | 1 | nudge-2 (direct) |
| idle | 20+min | 2 | nudge-3 (final) |
| idle | any | 3 | escalate |
| error | any | any | assess-severity |
| requesting_shutdown | any | any | pre-kill-verify |
| done | any | any | pre-kill-verify |

Nudge text:
1. "How's progress? Need any help?"
2. "Please wrap up soon. What's blocking you?"
3. "Final check. Will escalate in 5 min if no response."

Record decision and rationale.
Needs: load-history

## Step: execute
Take the decided action for toast.

**nudge-N**:
```bash
tmux send-keys -t gt-gastown-toast "{{nudge_text}}" Enter
```

**pre-kill-verify**:
```bash
cd polecats/toast
git status                    # Must be clean
git log origin/main..HEAD     # Check for unpushed
bd show <assigned-issue>      # Verify closed/deferred
```
If clean: kill session, remove worktree, delete branch
If dirty: record failure, retry next cycle

**escalate**:
```bash
gt mail send mayor/ -s "Escalation: toast stuck" -m "..."
```

**none**: No action needed.

Record: action taken, result, updated nudge count.
Needs: decide

## Output

The arm completes with:
- action_taken: none | nudge-1 | nudge-2 | nudge-3 | killed | escalated
- result: success | failed | pending
- updated_state: New nudge count and timestamp for toast

This data feeds back to the parent patrol's aggregate step.
---
bonded_from: mol-polecat-arm
bonded_to: gt-na7y
bonded_ref: arm-toast
bonded_at: 2025-12-23T10:00:00Z


mol-deacon-patrol


mol-deacon-patrol

Mayor's daemon patrol loop.

The Deacon is the Mayor's background process that runs continuously,
handling callbacks, monitoring rig health, and performing cleanup.
Each patrol cycle runs these steps in sequence, then loops or exits.


Merge: gt-rp0k

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


test pin fix


Phase 4.1: mol-deacon-patrol uses ephemeral

Deacon patrol cycles run as ephemeral molecules.

## Current Deacon Loop

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

## Molecule-Based Loop

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

## Benefits

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


Digest: mol-deacon-patrol

Patrol cycle 18: nominal


Convert formula files from YAML to JSON

Formulas currently use .formula.yaml but the project avoids YAML. Convert to .formula.json for consistency with the rest of the codebase. This blocks implementing compose operators (advice, expand, etc.) since the schema needs to be right first.


Test Patrol Parent

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

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

Original description:
Test parent for Christmas Ornament pattern


Template and Formula Distribution: Embed in binary, detect staleness

## Problem

Two types of Gas Town infrastructure live in `.beads/` but have unclear distribution:

1. **Role templates** (`*.md.tmpl`) - embedded in binary, rendered to CLAUDE.md on setup
2. **Patrol formulas** (`*.formula.json`) - currently just exist, no clear seeding path

When `gt` is rebuilt with updated templates/formulas, existing installations don't auto-update.

## Current State

- Templates: embedded via `//go:embed`, written on `gt install`/`gt rig init`
- Formulas: exist in `.beads/formulas/` but no seeding mechanism
- `bd init` creates empty structure (correct - bd shouldn't know Gas Town)
- No staleness detection for either

## Proposed Solution

### 1. Embed formulas in `gt` binary

```go
//go:embed formulas/*.formula.json
var formulaFS embed.FS
```

### 2. Seed formulas during setup

`gt install` and `gt rig init` write embedded formulas to `.beads/formulas/`.

### 3. Hash-based staleness detection

`gt doctor` checks:
- CLAUDE.md hash vs embedded template hash
- Formula files hash vs embedded formula hash

If stale → warn. `gt doctor --fix` refreshes.

### 4. Keep bd/gt boundary clean

- `bd` stays generic: `bd cook <path>` takes any formula path
- `gt` knows where formulas live, handles distribution
- Patrol formulas are Gas Town infrastructure, not beads concern

## Tasks

1. Move formulas to embedded FS in gt binary
2. Add formula seeding to `gt install` / `gt rig init`
3. Add hash storage for templates and formulas
4. Add staleness detection to `gt doctor`
5. Add `--fix` flag to refresh stale files


Refactor formula transformation pipeline to shared helper

The formula transformation pipeline (control flow, advice, expansions, aspects) is duplicated between cook.go and mol_bond.go. Should be extracted to a shared function in the formula package.

Current duplication in:
- cmd/bd/cook.go:96-136
- cmd/bd/mol_bond.go:598-633

Suggested approach:
- Add formula.Transform(resolved *Formula, parser *Parser) (*Formula, error)
- Handles all transformation steps in order
- Both cook.go and mol_bond.go call this single function


Draft mol-ready-work protomolecule definition

Draft the actual protomolecule definition in markdown format.

## Molecule Structure

```markdown
## Molecule: ready-work
Autonomous backlog processing patrol for crew workers.

Phase: vapor (wisp) - ephemeral patrol cycles
Squash: after each work item or context threshold

## Step: orient
Load context and check for interrupts:
- Read mail for overseer instructions
- Check for predecessor handoff
- Load current context state

## Step: scan-backlogs
Survey all backlogs in priority order:
1. gh pr list --state open
2. gh issue list --state open --label untriaged (or no label)
3. bd ready
4. gh issue list --state open --label triaged

Capture counts and candidates.

Needs: orient

## Step: select-work
Apply ROI heuristic to select best work item:
- Estimate size (tokens needed)
- Check remaining context capacity
- Weight by impact (priority, type)
- Select highest ROI achievable item
- If empty: exit patrol

Needs: scan-backlogs

## Step: execute-work
Work the selected item:
- For PRs: review, request changes, or merge
- For untriaged: triage and label
- For beads: implement and close
- For triaged GH: implement fix

Commit, push, close/update as appropriate.

Needs: select-work

## Step: check-context
Assess context state:
- Estimate remaining capacity
- If < 20%: goto handoff
- If ok: loop to scan-backlogs

Needs: execute-work

## Step: handoff
Prepare for session transition:
- Summarize work completed this cycle
- Note any in-progress items
- Send handoff mail to self
- Squash wisp to digest
- Exit for fresh session

Needs: check-context
```

## Variables

- `backlog_priority`: Override backlog scan order
- `context_threshold`: Percentage at which to handoff (default: 20)
- `max_items`: Maximum items to process per session

## Notes

- This is a vapor-phase molecule (wisp)
- Each work item should squash to a digest
- The patrol itself squashes at handoff


Silent error handling with _ = err patterns

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

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

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


Digest: mol-deacon-patrol

Patrol 11: quiet


Digest: mol-deacon-patrol

Patrol 14: clear


gt context --usage: estimate remaining context via tmux line count

## Summary

Implement `gt context --usage` to estimate remaining context capacity.

## Approach

Use tmux scrollback buffer line count as proxy for token usage:
- `tmux capture-pane -p | wc -l` gives current line count
- Heuristics (tunable per model):
  - Opus 4.5: ~1800 lines = warning threshold
  - ~2000+ lines = critical, need handoff

## Output Format
```
Context Usage: 1642 lines (~82%)
Status: WARNING - consider handoff soon
```

## Notes
- Heuristics are model-specific, need config
- Line count is proxy, not exact token count
- Good enough for autonomous patrol decisions


Digest: mol-deacon-patrol

Patrol 18: All healthy


Digest: mol-deacon-patrol

Patrol 4: Routine


Digest: mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 16: all healthy


mol-deacon-patrol

Mayor's daemon patrol loop.

The Deacon is the Mayor's background process that runs continuously, handling callbacks, monitoring rig health, and performing cleanup. Each patrol cycle runs these steps in sequence, then loops or exits.


Add gt crew rename command


Agent protocol integration test

End-to-end test: polecat completes work → sends WORK_COMPLETE → witness receives and sends POLECAT_READY → refinery merges → sends MERGE_SUCCESS → witness cleans up polecat. All via structured protocol messages.


mol-deacon-patrol


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


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.


Work on ga-yp3: Polecat inbox system for reliable work as...

Work on ga-yp3: Polecat inbox system for reliable work assignment. This is P1 priority. See bd show ga-yp3 for full design spec.


Patrol hygiene: Witness/Refinery/Deacon inbox cleanup

## Summary
Patrol roles should clean up their inboxes as part of normal operation, not let messages accumulate.

## Witness Patrol Cleanup
- POLECAT_STARTED: archive after acknowledging
- POLECAT_DONE: archive after cleanup complete
- LIFECYCLE requests: archive after processing
- Keep only: active work, unprocessed requests

## Refinery Patrol Cleanup
- MR Ready: archive after merge/reject decision
- Swarm notices: archive after dispatch
- Keep only: pending MRs in queue

## Deacon Patrol Cleanup
- Rotate daemon.log when >10MB
- Prune state.json of dead sessions
- Archive old daemon logs (gzip, date-suffix)

## Implementation
Each patrol molecule should have a 'cleanup' phase at end of cycle.

## Related
- mol-town-shutdown (nuclear cleanup)
- mol-witness-patrol, mol-refinery-patrol, mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol complete: fixed EMERGENCY clone divergence (zoey 103 behind, refinery 40 behind), cleaned orphan processes, created patrol molecules, fixed invalid hook attachments


Agent monitoring and status inference

Agent monitoring with status inference from activity, like PGT.

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

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

## Activity Detection

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

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

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 9: clear


Test gate for verification


Witness handoff bead state persistence

Implement state persistence for Witness across wisp cycles.

## Problem
Wisps burn between cycles, but Witness needs to remember:
- Which workers have been nudged
- How many times (nudge_count)
- When was last nudge
- Last observed activity

## Solution
Witness handoff bead with worker_states field:

```json
{
  "id": "gt-witness-state",
  "type": "handoff",
  "assignee": "<rig>/witness",
  "pinned": true,
  "worker_states": {
    "furiosa": {
      "issue": "gt-123",
      "nudge_count": 2,
      "last_nudge": "2024-12-22T10:00:00Z"
    }
  },
  "last_patrol": "2024-12-22T10:05:00Z"
}
```

## Implementation
1. On patrol start: bd show <witness-handoff-id> to load state
2. During patrol: update in-memory state
3. On save-state step: bd update to persist
4. State survives wisp burn/squash

## Depends on
- gt-83k0 (mol-witness-patrol definition)


Deacon plugin scheduler: cron-like execution

Implement scheduled plugin execution in the Deacon's plugins step.

## Concept

Plugins are just directories with config. Deacon checks which are due
and executes them during the plugins step of rounds.

## Plugin Registration

Plugins are registered in:
1. Pinned kernel bead (preferred)
2. `{townRoot}/plugins/` directory scan (fallback)

Each plugin has:
```json
{
  "name": "beads-hygiene",
  "schedule": "0 2 * * *",
  "last_run": "2025-12-20T02:00:00Z",
  "enabled": true,
  "attention_budget": "low",
  "max_duration_minutes": 30
}
```

## Schedule Format

Standard cron format: `minute hour day month weekday`
- `0 2 * * *` - 2 AM daily
- `0 4 * * 0` - 4 AM Sundays
- `*/15 * * * *` - Every 15 minutes

## Execution

During plugins step:
1. Load plugin schedules from kernel bead + directory
2. For each plugin:
   a. Parse schedule, check if due (last_run + schedule)
   b. If due, execute:
      - Read `plugins/{name}/CLAUDE.md` for context
      - Run plugin logic (inline or spawn subagent)
      - Record outcome and update last_run
3. Continue to next step

## Plugin Execution Modes

- **inline**: Deacon runs the plugin logic directly (simple, fast)
- **subagent**: Spawn a polecat-like agent for the plugin (complex, parallel)
- **mail**: Send mail to another agent to do the work (delegation)

## State Tracking

`{townRoot}/deacon/plugin-state.json`:
```json
{
  "plugins": {
    "beads-hygiene": {
      "last_run": "2025-12-20T02:00:00Z",
      "last_outcome": "success",
      "run_count": 42,
      "failure_count": 2
    }
  }
}
```

## Relation to gt-axz

This implements the execution side of the plugin architecture (gt-axz).
gt-axz defines the format; this task implements the scheduler.


Day 3.2: Witness reads agent beads for polecat state

Witness patrol reads polecat state from agent beads:
- bd list --type=agent --role_type=polecat
- For each, check state field
- If state=running, check progress
- If state=stuck, handle stuck protocol

No more PID/tmux inference.

Parent: gt-hwka3


SSHOutpost: Full Gas Town clone on remote VM

## Overview

Implement SSHOutpost for running workers on remote VMs via SSH.

## Model

Remote VM has a full Gas Town clone. SSHOutpost:
- Connects via SSH
- Spawns tmux sessions on remote
- Uses remote git clone for code/beads
- Syncs via git push/pull

## Implementation

```go
type SSHOutpost struct {
    name       string
    host       string
    user       string
    keyPath    string
    townPath   string  // e.g., /home/steve/ai
    maxWorkers int
    conn       *ssh.Client
}

func NewSSHOutpost(config OutpostConfig) (*SSHOutpost, error)

func (o *SSHOutpost) Spawn(issue string, cfg WorkerConfig) (Worker, error) {
    // SSH: create tmux session on remote
    // SSH: start claude in session
    // Return SSHWorker
}

func (o *SSHOutpost) Ping() error {
    // Test SSH connectivity
}
```

## SSHWorker

```go
type SSHWorker struct {
    outpost *SSHOutpost
    id      string
    session string
    issue   string
}

func (w *SSHWorker) Attach() error {
    // SSH + tmux attach (opens terminal)
}

func (w *SSHWorker) Logs() (io.Reader, error) {
    // SSH: tmux capture-pane
}
```

## Prerequisites on Remote VM

1. Gas Town clone at townPath
2. Claude Code installed
3. Git credentials configured
4. SSH key access

## Integration with gt-f9x.7-8

This builds on the Connection interface from gt-f9x.7/8. SSHOutpost uses SSHConnection internally for remote operations.

Depends on: gt-9a2.1 (interfaces), gt-f9x.7 (Connection interface)


town handoff command (optional)

CLI commands for session handoff workflow (optional convenience).

## Commands

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

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

## Implementation

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

## Priority

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

## Notes

Part of session cycling workflow designed in [deleted:gt-u82].


Inconsistent error wrapping patterns

Error handling is inconsistent across the codebase:

1. Some functions use fmt.Errorf with %w for wrapping, others don't
2. return nil, nil patterns (40+ occurrences) sometimes represent 'not found' and sometimes 'error but continue' - should use sentinel errors
3. Some places return nil for errors when they should propagate them

Examples of nil,nil that might need review:
- internal/git/git.go:272, 308
- internal/crew/manager.go:227
- internal/witness/manager.go:517, 523, 530, 775
- internal/beads/beads.go:225, 492


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


Digest: mol-deacon-patrol

Patrol 9: all clear


Test: New Router

Testing bd create for mail


Digest: mol-deacon-patrol

Patrol 10: routine


Polecats should not bd sync on startup

Polecats all share the same beads database at the rig level. The refinery and mayor/witness manage syncing beads.

## Current Behavior
Polecat startup runs bd sync, causing:
- Contention when multiple polecats spawn simultaneously
- Unnecessary sync operations
- Potential race conditions

## Desired Behavior
- Polecats should NOT run bd sync on startup
- They read from the shared beads database
- Only refinery/witness/mayor sync beads

## Implementation
Remove bd sync from polecat spawn/startup sequence.


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

Implement condition checking for each await type.

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

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

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


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

This molecule is bonded dynamically by mol-witness-patrol's survey-workers step.
Each polecat being monitored gets one arm that runs in parallel with other arms.

## Variables

| Variable | Required | Description |
|----------|----------|-------------|
| polecat_name | Yes | Name of the polecat to inspect |
| rig | Yes | Rig containing the polecat |

## Step: capture
Capture recent tmux output for toast.

```bash
tmux capture-pane -t gt-gastown-toast -p | tail -50
```

Record:
- Last activity timestamp (when was last tool call?)
- Visible errors or stack traces
- Completion indicators ("Done", "Finished", etc.)

## Step: assess
Categorize polecat state based on captured output.

States:
- **working**: Recent tool calls, active processing
- **idle**: At prompt, no recent activity
- **error**: Showing errors or stack traces
- **requesting_shutdown**: Sent LIFECYCLE/Shutdown mail
- **done**: Showing completion indicators

Calculate: minutes since last activity.
Needs: capture

## Step: load-history
Read nudge history for toast from patrol state.

```
nudge_count = state.nudges[toast].count
last_nudge_time = state.nudges[toast].timestamp
```

This data was loaded by the parent patrol's load-state step and passed
to the arm via the bonding context.
Needs: assess

## Step: decide
Apply the nudge matrix to determine action for toast.

| State | Idle Time | Nudge Count | Action |
|-------|-----------|-------------|--------|
| working | any | any | none |
| idle | <10min | any | none |
| idle | 10-15min | 0 | nudge-1 (gentle) |
| idle | 15-20min | 1 | nudge-2 (direct) |
| idle | 20+min | 2 | nudge-3 (final) |
| idle | any | 3 | escalate |
| error | any | any | assess-severity |
| requesting_shutdown | any | any | pre-kill-verify |
| done | any | any | pre-kill-verify |

Nudge text:
1. "How's progress? Need any help?"
2. "Please wrap up soon. What's blocking you?"
3. "Final check. Will escalate in 5 min if no response."

Record decision and rationale.
Needs: load-history

## Step: execute
Take the decided action for toast.

**nudge-N**:
```bash
tmux send-keys -t gt-gastown-toast "{{nudge_text}}" Enter
```

**pre-kill-verify**:
```bash
cd polecats/toast
git status                    # Must be clean
git log origin/main..HEAD     # Check for unpushed
bd show <assigned-issue>      # Verify closed/deferred
```
If clean: kill session, remove worktree, delete branch
If dirty: record failure, retry next cycle

**escalate**:
```bash
gt mail send mayor/ -s "Escalation: toast stuck" -m "..."
```

**none**: No action needed.

Record: action taken, result, updated nudge count.
Needs: decide

## Output

The arm completes with:
- action_taken: none | nudge-1 | nudge-2 | nudge-3 | killed | escalated
- result: success | failed | pending
- updated_state: New nudge count and timestamp for toast

This data feeds back to the parent patrol's aggregate step.
---
bonded_from: mol-polecat-arm
bonded_to: gt-vhn1
bonded_ref: arm-toast
bonded_at: 2025-12-23T10:00:00Z


Day 4.3: Polecat self-manages session handoffs

**REVISED**: Session cadence is polecat-managed, not mechanically forced by Witness.

Polecats decide when to cycle their own sessions:
1. Work through molecule steps sequentially in one session
2. Close each step as completed (bd close, for activity feed)
3. Self-initiate handoff when: context filling, logical chunk done, or stuck
4. Use: gt handoff -s 'Continuing molecule' -m '<context notes>'

If polecat forgets to handoff:
- Compaction will force it eventually
- Work continues from hook (molecule state preserved)
- No work is lost

Witness role changes:
- Does NOT force recycle between steps
- DOES monitor for stuck (long idle on same step)
- DOES use recycle for stuck recovery (not routine step transitions)
- DOES verify clean state before nuke

This is more ZFC-compliant: polecats report their own needs.


gt-deacon

Deacon (daemon beacon) - receives mechanical heartbeats, runs town plugins and monitoring.

role_type: deacon
rig: null
agent_state: idle
hook_bead: null
role_bead: gt-deacon-role


Delete hook functions from internal/wisp

Delete from internal/wisp/io.go:
- WriteSlungWork
- ReadHook
- BurnHook
- HasHook
- ListHooks
- HookPath

Keep: EnsureDir, WispPath, writeJSON (used by other things)


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.


Digest: mol-deacon-patrol

Patrol 14


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)


Digest: mol-deacon-patrol

Patrol 8: routine


Define meta-formula YAML schema

Define the YAML schema for meta-formulas that generate multiple formulas.

## Proposed Schema

    type: meta-formula
    name: multi-lang-shiny
    generates:
      for-each:
        var: lang
        values: [go, python, typescript]
      formula:
        name: "shiny-{lang}"
        extends: base-shiny
        vars:
          language: "{lang}"

## Key Decisions

- Use "generates" key to distinguish from regular formulas
- for-each at generation level (not runtime)
- Output is N formula files or in-memory formulas

## Deliverables

1. Add MetaFormula type to types.go
2. Add ForEachGenerate struct
3. Document schema in comments


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

This molecule is bonded dynamically by mol-witness-patrol's survey-workers step.
Each polecat being monitored gets one arm that runs in parallel with other arms.

## Variables

| Variable | Required | Description |
|----------|----------|-------------|
| polecat_name | Yes | Name of the polecat to inspect |
| rig | Yes | Rig containing the polecat |

## Step: capture
Capture recent tmux output for toast.

```bash
tmux capture-pane -t gt-gastown-toast -p | tail -50
```

Record:
- Last activity timestamp (when was last tool call?)
- Visible errors or stack traces
- Completion indicators ("Done", "Finished", etc.)

## Step: assess
Categorize polecat state based on captured output.

States:
- **working**: Recent tool calls, active processing
- **idle**: At prompt, no recent activity
- **error**: Showing errors or stack traces
- **requesting_shutdown**: Sent LIFECYCLE/Shutdown mail
- **done**: Showing completion indicators

Calculate: minutes since last activity.
Needs: capture

## Step: load-history
Read nudge history for toast from patrol state.

```
nudge_count = state.nudges[toast].count
last_nudge_time = state.nudges[toast].timestamp
```

This data was loaded by the parent patrol's load-state step and passed
to the arm via the bonding context.
Needs: assess

## Step: decide
Apply the nudge matrix to determine action for toast.

| State | Idle Time | Nudge Count | Action |
|-------|-----------|-------------|--------|
| working | any | any | none |
| idle | <10min | any | none |
| idle | 10-15min | 0 | nudge-1 (gentle) |
| idle | 15-20min | 1 | nudge-2 (direct) |
| idle | 20+min | 2 | nudge-3 (final) |
| idle | any | 3 | escalate |
| error | any | any | assess-severity |
| requesting_shutdown | any | any | pre-kill-verify |
| done | any | any | pre-kill-verify |

Nudge text:
1. "How's progress? Need any help?"
2. "Please wrap up soon. What's blocking you?"
3. "Final check. Will escalate in 5 min if no response."

Record decision and rationale.
Needs: load-history

## Step: execute
Take the decided action for toast.

**nudge-N**:
```bash
tmux send-keys -t gt-gastown-toast "{{nudge_text}}" Enter
```

**pre-kill-verify**:
```bash
cd polecats/toast
git status                    # Must be clean
git log origin/main..HEAD     # Check for unpushed
bd show <assigned-issue>      # Verify closed/deferred
```
If clean: kill session, remove worktree, delete branch
If dirty: record failure, retry next cycle

**escalate**:
```bash
gt mail send mayor/ -s "Escalation: toast stuck" -m "..."
```

**none**: No action needed.

Record: action taken, result, updated nudge count.
Needs: decide

## Output

The arm completes with:
- action_taken: none | nudge-1 | nudge-2 | nudge-3 | killed | escalated
- result: success | failed | pending
- updated_state: New nudge count and timestamp for toast

This data feeds back to the parent patrol's aggregate step.
---
bonded_from: mol-polecat-arm
bonded_to: gt-ksrt
bonded_ref: arm-toast
bonded_at: 2025-12-23T10:00:00Z


Outpost error handling: Retry logic and resilience

## Overview

Error handling and retry logic for outpost operations.

## Error Types

```go
var (
    ErrOutpostUnreachable = errors.New("outpost unreachable")
    ErrWorkerSpawnFailed  = errors.New("worker spawn failed")
    ErrWorkerTimeout      = errors.New("worker timed out")
    ErrCostCapExceeded    = errors.New("cost cap exceeded")
    ErrAllOutpostsBusy    = errors.New("all outposts at capacity")
)
```

## Retry Policy

```go
type RetryPolicy struct {
    MaxAttempts     int
    InitialBackoff  time.Duration
    MaxBackoff      time.Duration
    BackoffFactor   float64
    RetryableErrors []error
}

func DefaultRetryPolicy() *RetryPolicy {
    return &RetryPolicy{
        MaxAttempts:    3,
        InitialBackoff: 1 * time.Second,
        MaxBackoff:     30 * time.Second,
        BackoffFactor:  2.0,
        RetryableErrors: []error{
            ErrOutpostUnreachable,
            ErrWorkerSpawnFailed,
        },
    }
}
```

## Retry Implementation

```go
func (m *OutpostManager) SpawnWithRetry(issue string, cfg WorkerConfig) (Worker, error) {
    var lastErr error
    backoff := m.retryPolicy.InitialBackoff

    for attempt := 0; attempt < m.retryPolicy.MaxAttempts; attempt++ {
        outpost := m.policy.SelectOutpost(issue, m.outposts)
        if outpost == nil {
            return nil, ErrAllOutpostsBusy
        }

        worker, err := outpost.Spawn(issue, cfg)
        if err == nil {
            return worker, nil
        }

        if !m.isRetryable(err) {
            return nil, err
        }

        lastErr = err
        time.Sleep(backoff)
        backoff = min(backoff * m.retryPolicy.BackoffFactor, m.retryPolicy.MaxBackoff)
    }

    return nil, fmt.Errorf("spawn failed after %d attempts: %w",
        m.retryPolicy.MaxAttempts, lastErr)
}
```

## Outpost Health Tracking

```go
type OutpostHealth struct {
    LastPing      time.Time
    LastError     error
    FailureCount  int
    CircuitBreaker bool  // If true, skip this outpost
}

func (m *OutpostManager) updateHealth(name string, err error) {
    // Track failures, open circuit breaker after N failures
    // Auto-reset after successful ping
}
```

## Config

```yaml
policy:
  retry:
    max_attempts: 3
    initial_backoff: 1s
    max_backoff: 30s

  circuit_breaker:
    failure_threshold: 5
    reset_timeout: 1m
```

## Files

- `internal/outpost/retry.go`
- `internal/outpost/health.go`

## Dependencies

Depends on: gt-9a2.3 (OutpostManager)
Can be done after basic outpost implementations work.


Digest: mol-deacon-patrol

Patrol 3: all quiet


Digest: mol-deacon-patrol

Patrol 2: All agents healthy, no messages, no orphans


Digest: mol-deacon-patrol

Patrol 3: nux exited (wrong-rig escalation), 11 sessions, all core healthy


Digest: mol-deacon-patrol

Patrol #8


Phase 4: Deacon Patrol Loop

$(cat /tmp/phase4.md)


mol-deacon-patrol

Mayor's daemon patrol loop.

The Deacon is the Mayor's background process that runs continuously, handling callbacks, monitoring rig health, and performing cleanup. Each patrol cycle runs these steps in sequence, then loops or exits.


Shutdown request handler

Handle polecat shutdown requests:

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

Depends on fixing gt-dsfi (handoff deadlock).


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

## Context

The mol-refinery-patrol needs two additional steps:

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

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

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

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

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

Position: After queue-scan, before process-branch

## Implementation

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

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


Unify tmux session lifecycle: prefer exec-over-shell pattern

## Context

When `gt crew at` is run from INSIDE the target session, it uses `execClaude()` which
replaces the shell with Claude directly. This is actually preferable behavior:
- Cleaner exit (no intermediate shell prompt)
- More reliable for programmatic worker recycling
- Better UX for humans

But currently this only happens accidentally when you run `gt crew at` while already
in the session at a shell prompt.

## Current behavior

1. Session created: `tmux new-session` starts with shell
2. Claude started via `send-keys` → shell spawns claude as child
3. Exit claude → return to shell → exit shell → session ends

With exec path (only if already in session):
1. `execClaude()` replaces shell with claude
2. Exit claude → session ends (no intermediate shell)

## Desired behavior

Consider making exec-the-shell the DEFAULT for all crew/persistent sessions:
- Spawn session with shell
- Wait for ready
- Use `respawn-pane -k` with claude command (kills shell, starts claude directly)

This gives the cleaner lifecycle without requiring the user to be inside the session.

## Related

Part of tmux control plane unification - ability to manage workers from any session.


Digest: mol-deacon-patrol

Patrol 13: All clear


mol-deacon-patrol

Mayor's daemon patrol loop.

The Deacon is the Mayor's background process that runs continuously,
handling callbacks, monitoring rig health, and performing cleanup.
Each patrol cycle runs these steps in sequence, then loops or exits.


Digest: mol-deacon-patrol

Patrol 4: 8 sessions active, all healthy


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.


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

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

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

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

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

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


Merge: gt-lnji

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


Witness should verify polecat submitted before stopping session

## Problem

Witness stopped dementus's session before it could call `gt done`, losing the MR submission. I had to manually push and submit the branch.

## Root Cause

Witness cleanup is triggered by nudge/manual check rather than by receiving POLECAT_DONE message. When Witness cleans up based on issue status (closed), it doesn't wait for the polecat to complete its shutdown sequence.

## Expected Behavior

Witness should only stop a polecat session after:
1. Receiving POLECAT_DONE message from that polecat, OR
2. Timeout waiting for POLECAT_DONE after issue is closed

## Current Behavior

Witness stops sessions immediately when asked to check for completions, even if polecat hasn't called `gt done` yet.

## Fix

In mol-witness-patrol inbox-check step:
- Only clean up polecats that have sent POLECAT_DONE
- For polecats with closed issues but no DONE message, nudge them to complete
- Add timeout before force-killing


Digest: mol-deacon-patrol

Patrol 11: healthy


Day 1.1b: Add type=role to bead schema

Add role as a valid bead type in the schema.

Role beads define agent behavior:
- Priming instructions (from current CLAUDE.md content)
- default_molecule field
- capabilities list

This enables Day 1.5 (creating role beads from CLAUDE.md).

Must be done alongside type=agent schema work.


Digest: mol-deacon-patrol

Patrol 13: Quiet


Digest: mol-deacon-patrol

Patrol 1: All agents healthy, no messages, no orphans


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


implement

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

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

Depends: load-context


Digest: mol-deacon-patrol


gt mail read: Support numeric indices for message ID

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

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

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


Worker cleanup: Beads sync on shutdown

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

## Update to Decommission Checklist (gt-sd6)

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

## Beads Edge Cases

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

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

## Update to Witness Verification (gt-f8v)

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

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

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


Merge: gt-3x1.5

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


Digest: mol-deacon-patrol

Patrol cycle 16: routine


Digest: mol-deacon-patrol

Patrol 11


Digest: mol-deacon-patrol

Patrol 18: OK


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.


Workspace cleanup: preflight and postflight

Workspace preflight and postflight commands for clean state management.

## Preflight
```
gt preflight [--rig <rig>] [--dry-run]
```

Run before starting batch work:
1. Clean stale mail in inboxes
2. Check for stuck workers (warn)
3. Check rig health (polecats, refinery)
4. Verify git state is clean
5. Run bd sync to ensure beads current

## Postflight
```
gt postflight [--rig <rig>] [--archive-mail] [--dry-run]
```

Run after batch work completes:
1. Archive old mail with --archive-mail
2. Clean up stale integration branches
3. Sync beads
4. Report on rig state

## Implementation
```go
func Preflight(rigName string, dryRun bool) (*PreflightReport, error)
func Postflight(rigName string, opts PostflightOptions) (*PostflightReport, error)
```

## Report Structures
```go
type PreflightReport struct {
    MailCleaned    int
    RigHealthy     bool
    StuckWorkers   []string
    Warnings       []string
}

type PostflightReport struct {
    MailArchived   int
    BranchesCleaned int
    Warnings       []string
}
```

## Note

These are workspace maintenance commands, not tied to "swarm" lifecycle. Run them anytime to keep the rig clean.

## Acceptance Criteria
- [ ] Preflight cleans stale state
- [ ] Postflight archives old mail
- [ ] Both have --dry-run mode
- [ ] Clear reports of actions taken


Test merge request

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


Digest: mol-deacon-patrol

Patrol 6: clear


Digest: mol-deacon-patrol

Patrol 16


Digest: mol-deacon-patrol

Patrol OK: town quiet


mol-deacon-patrol


load-context

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

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


Digest: mol-deacon-patrol

Patrol 1: cleaned 3 stale locks, 3 orphan processes, doctor passed


mol-deacon-patrol

Deacon patrol molecule template. Label: template


Digest: mol-deacon-patrol

Patrol 2 complete: all healthy, no issues


Update gt doctor to check new config locations

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


Digest: mol-deacon-patrol

Patrol 17: All healthy, 2 mayor handoffs observed


gt swarm start: Does not spawn polecat sessions

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

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

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


mol-deacon-patrol

Mayor's daemon patrol loop.

The Deacon is the Mayor's background process that runs continuously, handling callbacks, monitoring rig health, and performing cleanup. Each patrol cycle runs these steps in sequence, then loops or exits.


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


Digest: mol-deacon-patrol

Patrol 1: inbox empty, Mayor OK, 2 witnesses, 2 refineries healthy, no orphans


Digest: mol-deacon-patrol

Patrol 16: routine


mol-deacon-patrol

Mayor's daemon patrol loop.

The Deacon is the Mayor's background process that runs continuously,
handling callbacks, monitoring rig health, and performing cleanup.
Each patrol cycle runs these steps in sequence, then loops or exits.


Check Witness and Refinery health

Check Witness and Refinery health for each rig.

**ZFC Principle**: You (Claude) make the judgment call about what is "stuck" or "unresponsive" - there are no hardcoded thresholds in Go. Read the signals, consider context, and decide.

For each rig, run:
```bash
gt witness status <rig>
gt refinery status <rig>
```

**Signals to assess:**

| Component | Healthy Signals | Concerning Signals |
|-----------|-----------------|-------------------|
| Witness | State: running, recent activity | State: not running, no heartbeat |
| Refinery | State: running, queue processing | Queue stuck, merge failures |

**Tracking unresponsive cycles:**

Maintain in your patrol state (persisted across cycles):
```
health_state:
  <rig>:
    witness:
      unresponsive_cycles: 0
      last_seen_healthy: <timestamp>
    refinery:
      unresponsive_cycles: 0
      last_seen_healthy: <timestamp>
```

**Decision matrix** (you decide the thresholds based on context):

| Cycles Unresponsive | Suggested Action |
|---------------------|------------------|
| 1-2 | Note it, check again next cycle |
| 3-4 | Attempt restart: gt witness restart <rig> |
| 5+ | Escalate to Mayor with context |

**Restart commands:**
```bash
gt witness restart <rig>
gt refinery restart <rig>
```

**Escalation:**
```bash
gt mail send mayor/ -s "Health: <rig> <component> unresponsive" \
  -m "Component has been unresponsive for N cycles. Restart attempts failed.
      Last healthy: <timestamp>
      Error signals: <details>"
```

Reset unresponsive_cycles to 0 when component responds normally.


Create rig settings/ directory for rig configuration

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


Digest: mol-deacon-patrol

Patrol 3: All nominal


Digest: mol-deacon-patrol

P18: stable


Digest: mol-deacon-patrol

Patrol 11


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 13: All clear


Polecat Mood Plugin (standard)

Standard witness plugin molecule that assesses polecat emotional state.

## Molecule Definition

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

## Mood Emojis

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

## Bonding

During witness patrol plugin-run step:

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

## Installation

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

## Customization

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


Digest: mol-deacon-patrol

Patrol complete: reset 3 orphaned polecats (furiosa, nux, slit) to idle, all agents healthy


Digest: mol-deacon-patrol

Patrol 8: healthy


Day 4.1: Implement gt polecat recycle command

Add gt polecat recycle <name> command:
- Kill the Claude session (tmux kill-session)
- Preserve the sandbox (branch/worktree)
- Update agent bead state to 'stopped'
- Leave for respawn on next step

This enables session-per-step.

Parent: gt-4a2qt


test pin fix 2


Digest: mol-deacon-patrol

Patrol 3: All clear


Digest: mol-deacon-patrol

Patrol 6: Routine


Add tmux crash detection hooks

Use tmux hooks to detect when agent panes die unexpectedly.

## tmux Hooks to Use
- pane-died: fires when a pane exits
- session-closed: fires when session ends

## Implementation
```bash
set-hook -g pane-died 'run-shell "gt log pane-died #{pane_id} #{pane_dead_status}"'
set-hook -g session-closed 'run-shell "gt log session-closed #{session_name}"'
```

## Integration
- gt spawn should set these hooks on agent sessions
- Distinguish expected exits (handoff, done) from crashes
- Capture last N lines of output on crash
- Record exit code/signal for forensics

## gt log crash command
- Parse pane-died events
- Show crash history
- Filter by agent, time range


Digest: mol-deacon-patrol

Patrol 19: All clear


Digest: mol-deacon-patrol

Patrol #20: Final before handoff


Witness swarm landing protocol

Witness responsibilities for swarm landing cleanup.

## Trigger

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

## Landing Protocol

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

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

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

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

## Code at Risk Handling

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

## Reference

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


Digest: mol-deacon-patrol

P15: stable


Digest: mol-deacon-patrol

Patrol 1: All healthy, no messages, no orphans


Day 2.6: Define bd agent state command

Add command for agents to write their own state to agent beads.

```bash
bd agent state <agent-id> <state>
# Example: bd agent state gt-mayor running
# States: idle | running | stuck | stopped | dead
```

Implementation:
1. Lookup agent bead by ID
2. Update state field
3. Update last_activity timestamp
4. Trigger bd sync if configured

Also add:
- bd agent heartbeat <agent-id> - just updates last_activity
- bd agent show <agent-id> - show agent bead details

This is the ZFC-compliant way for agents to self-report state.

Delegated to beads repo: bd-uxlb. Assign to beads/crew/dave.


Digest: mol-deacon-patrol

Patrol 4: Routine, all healthy


Test molecule

Test


Create PolecatArmMolecule() for per-polecat inspection

Add PolecatArmMolecule() to builtin_molecules.go.

Steps:
- capture: Capture tmux output
- assess: Categorize state
- load-history: Get nudge counts
- decide: Apply nudge matrix
- execute: Take action

Uses variable substitution:
- {{polecat_name}}
- {{rig}}

This proto is bonded dynamically by survey-workers step.


Add molecule to update local go binary on push to main

When pushing to main branch, automatically rebuild and install the gt binary on the local machine so changes are immediately available.


Digest: mol-deacon-patrol

Patrol #13


Remove legacy .beads-wisp/ infrastructure

The wisp simplification (gt-fgms + bd-bkul) changed wisps to be just a flag on issues in the main .beads/ database. JSONL export filters Wisp=true issues. No separate directory needed.

But legacy .beads-wisp/ infrastructure still exists and should be removed:

## Gas Town (gt) cleanup:
- `internal/rig/manager.go`: Remove `initWispBeads()` call from `AddRig()`
- `internal/wisp/io.go`: Delete entire file (old abstraction layer)
- `internal/cmd/install.go`: Remove any .beads-wisp setup
- `docs/wisp-architecture.md`: Update to reflect 'just a flag' model

## Beads (bd) cleanup:
- `cmd/bd/doctor/wisp_check.go`: Remove WispExistsCheck, WispGitCheck, WispSizeCheck (check .beads-wisp dirs)
- Keep WispOrphansCheck and WispStaleCheck but rewrite to query main DB for Wisp=true issues

## Filesystem cleanup:
- Delete ~/gt/.beads-wisp/
- Delete ~/gt/gastown/.beads-wisp/
- Delete all per-rig .beads-wisp/ directories
- Remove .beads-wisp from .gitignore entries

## Verify:
- `gt mail send --wisp` still works (creates issue with Wisp=true in main DB)
- `bd sync` still filters wisps from JSONL
- `bd mol squash/burn` still work on Wisp=true issues


Digest: mol-deacon-patrol

Patrol 8: All green


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

This molecule is bonded dynamically by mol-witness-patrol's survey-workers step.
Each polecat being monitored gets one arm that runs in parallel with other arms.

## Variables

| Variable | Required | Description |
|----------|----------|-------------|
| polecat_name | Yes | Name of the polecat to inspect |
| rig | Yes | Rig containing the polecat |

## Step: capture
Capture recent tmux output for toast.

```bash
tmux capture-pane -t gt-gastown-toast -p | tail -50
```

Record:
- Last activity timestamp (when was last tool call?)
- Visible errors or stack traces
- Completion indicators ("Done", "Finished", etc.)

## Step: assess
Categorize polecat state based on captured output.

States:
- **working**: Recent tool calls, active processing
- **idle**: At prompt, no recent activity
- **error**: Showing errors or stack traces
- **requesting_shutdown**: Sent LIFECYCLE/Shutdown mail
- **done**: Showing completion indicators

Calculate: minutes since last activity.
Needs: capture

## Step: load-history
Read nudge history for toast from patrol state.

```
nudge_count = state.nudges[toast].count
last_nudge_time = state.nudges[toast].timestamp
```

This data was loaded by the parent patrol's load-state step and passed
to the arm via the bonding context.
Needs: assess

## Step: decide
Apply the nudge matrix to determine action for toast.

| State | Idle Time | Nudge Count | Action |
|-------|-----------|-------------|--------|
| working | any | any | none |
| idle | <10min | any | none |
| idle | 10-15min | 0 | nudge-1 (gentle) |
| idle | 15-20min | 1 | nudge-2 (direct) |
| idle | 20+min | 2 | nudge-3 (final) |
| idle | any | 3 | escalate |
| error | any | any | assess-severity |
| requesting_shutdown | any | any | pre-kill-verify |
| done | any | any | pre-kill-verify |

Nudge text:
1. "How's progress? Need any help?"
2. "Please wrap up soon. What's blocking you?"
3. "Final check. Will escalate in 5 min if no response."

Record decision and rationale.
Needs: load-history

## Step: execute
Take the decided action for toast.

**nudge-N**:
```bash
tmux send-keys -t gt-gastown-toast "{{nudge_text}}" Enter
```

**pre-kill-verify**:
```bash
cd polecats/toast
git status                    # Must be clean
git log origin/main..HEAD     # Check for unpushed
bd show <assigned-issue>      # Verify closed/deferred
```
If clean: kill session, remove worktree, delete branch
If dirty: record failure, retry next cycle

**escalate**:
```bash
gt mail send mayor/ -s "Escalation: toast stuck" -m "..."
```

**none**: No action needed.

Record: action taken, result, updated nudge count.
Needs: decide

## Output

The arm completes with:
- action_taken: none | nudge-1 | nudge-2 | nudge-3 | killed | escalated
- result: success | failed | pending
- updated_state: New nudge count and timestamp for toast

This data feeds back to the parent patrol's aggregate step.
---
bonded_from: mol-polecat-arm
bonded_to: gt-kgk5
bonded_ref: arm-toast
bonded_at: 2025-12-23T10:00:00Z


Digest: mol-deacon-patrol

Patrol 3: Routine


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


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 7: Nominal


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 spawn --issue should auto-attach mol-polecat-work

When spawning a polecat with --issue but no --molecule, the polecat gets an issue assigned and work assignment mail, but no molecule on their hook. They check 'gt mol status', see nothing, and are stuck.

Fix: If --issue is provided but --molecule is not, auto-attach mol-polecat-work (or a configurable default) to the polecat's hook.

Current behavior:
- Issue assigned in beads ✓
- Mail sent ✓
- No molecule attached ✗

Expected:
- Issue assigned in beads ✓
- Mail sent ✓
- mol-polecat-work attached to hook ✓


Handle callbacks from agents

Handle callbacks from agents.

Check the Mayor's inbox for messages from:
- Witnesses reporting polecat status
- Refineries reporting merge results
- Polecats requesting help or escalation
- External triggers (webhooks, timers)

```bash
gt mail inbox
# For each message:
gt mail read <id>
# Handle based on message type
```

Callbacks may spawn new polecats, update issue state, or trigger other actions.


gt mail: Cross-level routing is broken

**Problem:**
When Mayor (at ~/gt) sends mail to a rig worker (gastown/crew/max), the message lands in town-level beads (~/.beads/) but the recipient checks rig-level beads (crew/max/.beads/).

**Reproduction:**
```bash
cd ~/gt
gt mail send gastown/crew/max -s 'Test' -m 'body'
# Message goes to ~/gt/.beads/ with prefix hq-*

cd ~/gt/gastown/crew/max
gt mail inbox
# Does NOT see the message - it's looking in crew/max/.beads/
```

**Root cause:**
`findBeadsWorkDir()` walks up from CWD to find .beads. `Router.Send()` runs `bd create` in that directory. This means messages always go to the sender's beads, not the recipient's.

**Fix options:**
1. Route based on recipient address - if sending to rig/*, use that rig's .beads
2. Use a single shared beads database for all mail (simpler but less isolated)
3. Teach agents to check both levels (workaround, not fix)

**Related:**
- gt-ngu1: Pinned beads sorting (done but pointless if mail doesn't route)
- This blocks all cross-level mail functionality


mol-deacon-patrol

Mayor's daemon patrol loop.

The Deacon is the Mayor's background process that runs continuously, handling callbacks, monitoring rig health, and performing cleanup. Each patrol cycle runs these steps in sequence, then loops or exits.


Digest: mol-deacon-patrol

Patrol 2: all healthy, no action needed


Digest: mol-deacon-patrol @ 2025-12-24 19:24

Patrol 9: quiet


Test Patrol Parent

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

Original description:
Test parent for Christmas Ornament pattern


gt sling --force: return displaced work to ready pool

When slinging with --force to an agent with occupied hook, the displaced molecule should be returned to the ready pool rather than silently orphaned.

Current behavior:
- --force overwrites the hook attachment
- Previous work becomes orphaned (still assigned but not pinned)

Desired behavior:
1. Unpin the displaced molecule (clear assignee, set pinned=false)
2. Print warning in tool output: 'Warning: displaced gt-xxx back to ready pool'
3. Proceed with new sling

This ensures:
- No silent data loss
- Agent sees the warning and can act on it
- Human caller sees what happened
- Displaced work is discoverable via 'bd ready'

Implementation location: checkHookCollision() or the sling handlers themselves (after the --force check passes)


Digest: mol-deacon-patrol

Patrol 18: all quiet


Digest: mol-deacon-patrol

Patrol 8: All clear


Digest: mol-deacon-patrol

Patrol 18: All clear


test message


Clean up deprecated gt mol command examples in molecule.go

## Task

The molecule.go file contains example strings that reference deprecated gt mol commands.

## Deprecated References Found

1. `gt molecule instantiate mol-xyz` → should reference `bd mol pour`
2. `gt mol list --catalog` → should reference `bd formula list`
3. `gt mol bond mol-polecat-arm` → should reference `bd mol bond`

## Location

`internal/cmd/molecule.go` (multiple lines)

## Context

The gt mol help already correctly redirects:
```
For beads data operations (listing, showing, creating molecules), use bd:
  bd formula list    List molecule protos (replaces gt mol catalog)
  bd mol show        Show molecule details (replaces gt mol show)
  bd mol pour        Instantiate molecule (replaces gt mol instantiate)
  bd mol bond        Bond molecules together (replaces gt mol bond)
```

But the example strings in the code still show the old patterns.

## Fix

Update the example strings to use the current bd mol patterns.


Integration branch management for swarms

Manage integration branches for swarm merging strategy.

## Branch Naming

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

## Operations

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

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

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

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

## All Workers Same Base

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

This ensures clean merges within the swarm.

## Reference

PGT: ephemeral.py (create_integration_branch, merge_integration_to_main)


Digest: mol-deacon-patrol

Patrol 12: Quick cycle, all quiet


Update all documentation to remove hook references

14 docs reference hooks:
- docs/architecture.md
- docs/beads-data-plane.md
- docs/molecular-chemistry.md
- docs/molecules.md
- docs/pinned-beads-design.md
- docs/polecat-lifecycle.md
- docs/polecat-wisp-architecture.md
- docs/propulsion-principle.md
- docs/session-lifecycle.md
- docs/sling-design.md
- docs/vision.md
- docs/wisp-architecture.md
- docs/witness-patrol-design.md
- ~/gt/CLAUDE.md

Update all to reflect pinned beads as the sole propulsion mechanism.


Patrol step: check parked molecules for unblock

Add patrol step to Deacon for checking parked molecules:

```yaml
- step: check-parked-molecules
  action: |
    For each molecule with:
      - status: in_progress
      - assignee: null
      - has step with external: blocked_by
    Check if external deps are now satisfied.
    If yes: spawn polecat to resume the molecule.
```

This automates the resume process - no manual intervention needed when
upstream dependencies ship.

Part of cross-project dependency system.
See: docs/cross-project-deps.md

Depends on: gt-in3x (spawn --continue)
Priority: P3 (future automation, not required for launch)


mol-deacon-patrol

Mayor's daemon patrol loop.

The Deacon is the Mayor's background process that runs continuously,
handling callbacks, monitoring rig health, and performing cleanup.
Each patrol cycle runs these steps in sequence, then loops or exits.


Config package: Config, State types and JSON serialization

Config and state types with JSON serialization.

## Town Config (config/town.json)

```go
type TownConfig struct {
    Type      string    `json:"type"`       // "town"
    Version   int       `json:"version"`    // schema version
    Name      string    `json:"name"`       // town identifier
    CreatedAt time.Time `json:"created_at"`
}
```

## Rigs Registry (config/rigs.json)

```go
type RigsConfig struct {
    Version int                `json:"version"`
    Rigs    map[string]RigEntry `json:"rigs"`
}

type RigEntry struct {
    GitURL   string    `json:"git_url"`
    AddedAt  time.Time `json:"added_at"`
    BeadsConfig *BeadsConfig `json:"beads,omitempty"`
}

type BeadsConfig struct {
    Repo   string `json:"repo"`   // "local" | path | git-url
    Prefix string `json:"prefix"` // issue prefix
}
```

## Agent State (*/state.json)

```go
type AgentState struct {
    Role       string    `json:"role"`       // "mayor", "witness", etc.
    LastActive time.Time `json:"last_active"`
    Session    string    `json:"session,omitempty"`
    Extra      map[string]any `json:"extra,omitempty"`
}
```

## Interface

```go
func LoadTownConfig(path string) (*TownConfig, error)
func SaveTownConfig(path string, config *TownConfig) error

func LoadRigsConfig(path string) (*RigsConfig, error)
func SaveRigsConfig(path string, config *RigsConfig) error

func LoadAgentState(path string) (*AgentState, error)
func SaveAgentState(path string, state *AgentState) error
```

## Validation

- Version compatibility checks
- Required field validation
- Path existence verification


Digest: mol-deacon-patrol

Patrol 5: quiet


Refinery: Add lifecycle-notify step to patrol

After successful merge in handleSuccess(), send lifecycle notification to Witness.

## Implementation

1. Add new mail type: `LIFECYCLE: work merged`
2. In engineer.go handleSuccess(), after closing MR and source issue:
   ```
   gt mail send <rig>/witness -s "LIFECYCLE: work merged" -m "
   Worker: <polecat>
   MR: <mr-id>
   Issue: <source-issue>
   Commit: <merge-commit>
   Action: Polecat work merged. Worktree eligible for recycling.
   "
   ```

3. Update mol-refinery-patrol to include lifecycle-notify step between merge-push and loop-check

## Acceptance
- Successful merges send mail to Witness
- Mail includes worker name, MR id, source issue, commit SHA


Digest: mol-deacon-patrol

Patrol 2: all healthy, no incidents


Day 4.2: Implement gt polecat nuke command

Add gt polecat nuke <name> command:
- Kill the Claude session
- Delete the worktree
- Delete the branch
- Remove agent bead
- Full cleanup post-merge

Parent: gt-4a2qt


mol-gastown-boot

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

## Purpose
When Mayor executes "boot up gas town", this proto provides the workflow.
Each step has action + verification - steps stay open until outcome is confirmed.

## Key Principles
1. **Verification-gated steps** - Not "command ran" but "outcome confirmed"
2. **gt peek for verification** - Capture session output to detect stalls
3. **gt nudge for recovery** - Reliable message delivery to unstick agents
4. **Parallel where possible** - Witnesses and refineries can start in parallel
5. **Ephemeral execution** - Boot is a wisp, squashed to digest after completion

## Step Structure
Each step has Action/Verify/OnStall/OnFail sections.

## Execution
```bash
bd mol spawn mol-gastown-boot  # Create wisp
bd mol run <wisp-id>           # Execute
```


Digest: mol-deacon-patrol

Patrol 10: Nominal - halfway point


gt-witness-gastown

Witness - per-rig polecat monitor for gastown.

role_type: witness
rig: gastown
agent_state: idle
hook_bead: null
role_bead: gt-witness-role


Digest: mol-deacon-patrol

Patrol 7: all clear


Digest: mol-deacon-patrol

Patrol 11: Quick cycle, all quiet


bd create --dedup: semantic deduplication before issue creation

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

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

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

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

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

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


mol-deacon-patrol


Digest: mol-deacon-patrol

P12: stable


Digest: mol-deacon-patrol

P11: stable


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

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

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

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


Plugin: plan-oracle (work decomposition)

Plugin that helps decompose epics/issues into sub-tasks. Analyzes scope, identifies dependencies, estimates complexity for parallelization, creates beads for sub-tasks with dependency links.


Digest: mol-deacon-patrol

Patrol 4: All healthy


Day 1.2: Add agent-specific fields to schema

Add fields for agent beads:
- hook_bead: reference to current work (0..1 cardinality)
- role_bead: reference to role definition (1)
- state: agent-reported state (idle|running|stuck|stopped)

Parent: gt-d0jqp

Re-opened: Close reason referenced non-existent commit b5c69507. Agent fields not yet implemented in bd CLI.


Work reconciliation module

Port reconcile.py work verification to Go.

## Purpose

Verify assigned issues are completed before landing.

## Interface

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

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

## Logic

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

## Handling Incomplete Work

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

## Reference

PGT: swarm/reconcile.py


Phase 3: Callbacks and plugins

Mail-based callback protocol and plugin surface.

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

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

Depends: Phase 2


TODO: Detect cycles in molecule dependency graph

molecule.go:302 has a TODO to detect cycles in the dependency graph. Currently, cyclical dependencies could cause issues.


Digest: mol-deacon-patrol

Patrol 6: all healthy


Configuration in Beads: use pinned beads for rig/agent config

## Summary

Move configuration from JSON files into Beads data plane using pinned beads.

## Motivation

- Config becomes versionable and syncable like issues
- Agents can read config via `bd show`
- Unified data model for everything
- Supports the 'Beads as Universal Data Plane' vision (gt-aqm)

## Config Types to Migrate

### Rig-level (pinned per rig)
- Theme/colors for tmux status line
- Default branch naming conventions
- Merge queue settings
- Witness thresholds

### Agent-level (pinned per role)
- Handoffs (already planned - gt-cu7r)
- Agent preferences
- Current focus/context

### Town-level (pinned at town root)
- Cross-rig settings
- Federation config
- Global themes

## Implementation

1. Implement StatusPinned (beads-6v2) ✓ in progress
2. Create config pinned beads on rig init
3. Add `bd config get/set` commands that read/write pinned beads
4. Migrate existing config.json fields

## Naming Convention

```
<prefix>-cfg-<scope>
gt-cfg-theme      # gastown theme
bd-cfg-merge      # beads merge settings
gm-cfg-town       # town-level config
```

## Dependencies

- beads-6v2: Add StatusPinned to beads schema
- gm-w13: Pinned Beads epic


Remove lifecycle intent parsing from Go

daemon/lifecycle.go parses mail subjects with regex looking for restart/shutdown/cycle keywords, then executes actions. Fix: use structured message types in mail body instead of parsing subjects. Go reads action field, does not interpret text.


Digest: mol-deacon-patrol

Patrol 4: stable


Polecats should not push branches to remote

## Current Behavior

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

## Desired Behavior

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

## Benefits

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

## Trade-offs

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

## Implementation

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


test with labels


Digest: mol-deacon-patrol

Patrol 17: Green


Add Gas Town theory of operation to all role primings

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


mol-deacon-patrol

Mayor's daemon patrol loop.

The Deacon is the Mayor's background process that runs continuously,
handling callbacks, monitoring rig health, and performing cleanup.
Each patrol cycle runs these steps in sequence, then loops or exits.


Digest: mol-deacon-patrol

Patrol 14


Persist patrol state

Update handoff bead with new states.

Persist state to the witness handoff bead:
- Updated worker statuses from all arms
- Current nudge counts per worker
- Nudge timestamps
- Actions taken this cycle
- Pending items for next cycle

```bash
bd update <handoff-bead-id> --description="<serialized state>"
```

This state survives wisp burns and session cycles.


Digest: mol-deacon-patrol @ 2025-12-24 19:28

Patrol 18


Test Polecat Arm

Test child for bonding pattern


Merge: gt-oiv0

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


Control flow: loop, gate, branch

Implement control flow operators for the molecule algebra.

## Core Operators

| Operator | Syntax | Purpose |
|----------|--------|---------|
| loop (fixed) | loop: { count: N, body: [steps] } | Fixed iteration |
| loop (conditional) | loop: { until: COND, max: N, body: [steps] } | Bounded conditional iteration |
| gate | gate: { before: step, condition: COND } | Wait for condition before proceeding |
| branch | branch: { from: step, steps: [a,b,c], join: step } | Parallel paths that rejoin |

Conditions evaluated mechanically (step status, output fields) per gt-8tmz.7.

## Design Review (2025-12-25)

Analyzed patrol patterns (witness, deacon, refinery) to verify these constructs
can express real Gas Town workflows. Key findings:

### Insight: Patrol Loop is OUTSIDE the Algebra

The daemon respawns patrols. Each formula describes ONE cycle. This means:
- Formulas don't need "infinite loop with burn-and-respawn"
- loop is only for **inner loops** (refinery's per-branch processing)
- "burn-or-loop" = complete cycle; daemon decides next action
- Context cycling is external, not algebraic

This significantly simplifies the algebra.

### Gap: for-each is Missing

The current proposal doesn't include for-each, but it's essential for patrol patterns:

| Pattern | Use Case |
|---------|----------|
| Witness survey-workers | For each polecat, bond an arm (parallel) |
| Refinery process-branch | For each branch in queue, run merge pipeline (sequential) |
| Deacon plugin-run | For each plugin where gate open, execute |

Proposed syntax:

    for-each:
      collection: "survey-workers.output.polecats"
      bond: mol-polecat-arm
      vars:
        polecat_name: "{item.name}"
      parallel: true

This overlaps with gt-8tmz.8 (runtime dynamic expansion). Decision needed:
- Include for-each here as control flow, OR
- Ensure gt-8tmz.8 covers these patterns adequately

### Gap: waits-for Gate Variant

The "fanout gate" pattern (aggregate waits for all dynamically-bonded children)
needs formalization:

    step: aggregate
    waits-for: children-of(survey-workers)

Currently exists in formulas as waits_for: "all-children" but not in the algebra spec.

### What This Covers

With these operators (plus for-each), patrols become fully expressible:

**Witness Patrol:**
- for-each polecat -> bond mol-polecat-arm (parallel)
- gate with waits-for: all-children for aggregation

**Refinery Patrol:**
- for-each branch -> process pipeline (sequential)
- gate for test verification before merge

**Deacon Patrol:**
- for-each plugin -> execute if gate open
- Simple sequence otherwise

### Scope Decision

**Option A**: Implement loop, gate, branch now. Track for-each and waits-for separately.

**Option B**: Expand scope to include for-each and waits-for formalization.

Recommendation: Option A - implement core constructs, create child issues for gaps.


Digest: mol-deacon-patrol

Test patrol cycle - verifying wisp flow


Create .runtime/ for town-level runtime state

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


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)


Digest: mol-deacon-patrol

Patrol 11: All healthy


mol-deacon-patrol


Digest: mol-deacon-patrol @ 2025-12-24 19:26

Patrol 13: quiet


Digest: mol-deacon-patrol

Patrol 19: All agents healthy


gt session capture: Support positional line count argument

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

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

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


Test Patrol for Bonding

Parent issue for mol bond CLI test


Digest: mol-deacon-patrol

Patrol 3: all healthy


Digest: mol-deacon-patrol

Patrol 6: all clear


Digest: mol-deacon-patrol

Patrol 6: All healthy


Digest: mol-deacon-patrol

Final patrol cycle 20: all systems healthy


gt rig shutdown: Gracefully stop all rig agents

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

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


Digest: mol-deacon-patrol

Patrol 15: healthy


Nudge newly spawned polecats

Nudge newly spawned polecats that are ready for input.

When polecats are spawned, their Claude session takes 10-20 seconds to initialize.
The spawn command returns immediately without waiting. This step finds spawned
polecats that are now ready and sends them a trigger to start working.

```bash
# For each rig with polecats
for rig in gastown beads; do
    gt polecats $rig
    # For each working polecat, check if Claude is ready
    # Use tmux capture-pane to look for "> " prompt
done
```

For each ready polecat that hasn't been triggered yet:
1. Send "Begin." to trigger UserPromptSubmit hook
2. The hook injects mail, polecat sees its assignment
3. Mark polecat as triggered in state

Use WaitForClaudeReady from tmux package (polls for "> " prompt).
Timeout: 60 seconds per polecat. If not ready, try again next cycle.


MQ conflict prediction: file hotspot tracking

Track which files change frequently to predict conflicts before spawn.

## Goal
Before spawning a polecat on an issue, warn if the target files are "hot" (recently changed by other MRs).

## Approach
1. Track file change frequency per MR (sliding window, e.g., last 24h)
2. At spawn time, analyze issue description / planned changes
3. If touching hot files, either:
   - Warn and proceed
   - Suggest waiting for queue to clear
   - Auto-assign to ownership zone (future)

## Implementation
- Add file_changes log to refinery state (or separate hotspot.json)
- After each merge, record changed files with timestamp
- gt spawn checks hotspots before assignment
- Optional: Witness patrol aggregates hotspot data

## Example
```
$ gt spawn --issue gt-xyz
Warning: This issue likely touches auth/login.go
  - Changed 3 times in last 2 hours
  - 2 MRs pending that also touch this file
Proceed anyway? [y/N]
```

## Parent
gt-lxxh2


gt rig add: Auto-configure HQ routes.jsonl for new rigs

## Summary

When `gt rig add <rig-name>` adds a new rig to the town, it should automatically
add a routing entry to `~/gt/.beads/routes.jsonl` so that beads with that rig's
prefix can be looked up from the town root.

## Current Behavior

After running `gt rig add myproject`:
- Rig is cloned and set up
- No routing entry is added to HQ beads

## Desired Behavior

After running `gt rig add myproject`:
- Rig is cloned and set up
- Detect the rig's beads prefix (from config.yaml or existing issues)
- Add entry to `~/gt/.beads/routes.jsonl`:
  `{"prefix": "mp-", "path": "myproject/mayor/rig"}`

## Implementation Notes

1. After rig setup, check for `<rig>/mayor/rig/.beads/config.yaml`
2. Read `issue-prefix` if set, or sample an issue ID from the database
3. Append to `~/gt/.beads/routes.jsonl` if the prefix isn't already routed
4. Create routes.jsonl if it doesn't exist

## Edge Cases

- Rig has no beads yet (no prefix known) - skip routing setup, let doctor fix later
- Prefix already exists in routes.jsonl - warn and skip (or update path?)
- routes.jsonl doesn't exist - create it


Remove schedule/phase terminology from code

Temporal/schedule language in identifiers is an anti-pattern.

## Found Issues

### beads repo
- `examples/multi-phase-development/` directory
  - Rename to `examples/dependency-sequenced-work/` or similar

### gastown repo
- `internal/cmd/start.go` lines 178-195
  - Uses "Phase 1:", "Phase 2:", etc. in printf output
  - Change to descriptive names: "Interrupting agents...", "Requesting handoff...", etc.

- `internal/swarm/landing.go` lines ~50-70
  - Comments say "// Phase 1: Stop all polecat sessions"
  - Change to "// Step: Stop all polecat sessions" or just remove phase labels

## Why This Matters

Phase/version language in names:
1. Implies a fixed schedule (antithetical to dependency-driven work)
2. Creates confusion with molecule "phases" (solid/liquid/gas)
3. Encourages temporal thinking that leads to backwards dependencies

## What To Keep

- Molecule phase terminology (pour/wisp/proto) - this is chemistry, not scheduling
- Version fields inside files (version: 1) - this is semantic versioning


Molecule format bridge: convert embedded markdown to child issues

## Problem

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

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

## Options

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

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

## Recommendation

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


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

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

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

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

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


Clean dead sessions

Clean dead sessions and orphaned state.

Run `gt doctor --fix` to handle all cleanup:

```bash
# Preview what needs cleaning
gt doctor -v

# Fix everything
gt doctor --fix
```

This handles:
- **orphan-sessions**: Kill orphaned tmux sessions (gt-* not matching valid patterns)
- **orphan-processes**: Kill orphaned Claude processes (no tmux parent)
- **wisp-gc**: Garbage collect abandoned wisps (>1h old)

All cleanup is handled by doctor checks - no need to run separate commands.


Digest: mol-deacon-patrol

Patrol #5: Stable


End-to-end integration test

Create and run end-to-end integration test.

## Test Scenario

1. Clean state: no polecats, fresh beads
2. Create test issue: simple documentation fix
3. Verify Deacon sees it: bd ready shows issue
4. Trigger Deacon: send heartbeat or wait
5. Verify spawn: gt polecats shows new polecat
6. Watch polecat work: tmux attach
7. Verify completion: issue closed, MR created

## Test Script

```bash
#!/bin/bash
# e2e-test.sh

# 1. Clean state
gt polecat remove --all
bd create "Test: update README typo" --type=task --priority=1

# 2. Verify ready
bd ready | grep "update README"

# 3. Wait for Deacon or trigger
# (manual for v0.1)

# 4. Watch
gt status
gt polecats gastown

# 5. Wait for completion
# (manual observation for v0.1)

# 6. Verify
bd list --status=closed | grep "update README"
```

## Success Criteria

- Issue created and visible in bd ready
- Polecat spawned and working
- Issue closed with commit reference
- No manual intervention (except observation)


survey-workers

List polecats and categorize by status.

```bash
gt polecat list <rig>
```

Needs: load-state


Day 2.5: Test daemon pokes based on agent state

Integration test:
1. Create agent bead with state=idle
2. Sling work → hook_bead set
3. Verify daemon pokes
4. Update state=running
5. Verify daemon doesn't double-poke
6. Update state=stopped
7. Verify daemon re-pokes on new work

Parent: gt-d0jqp


Update deacon.md.tmpl with correct molecule commands

The deacon prompt references commands that don't exist:
- gt mol bond → should be bd mol run or gt mol arm
- gt mol status → needs gt mol command tree first

Update after gt mol command tree is implemented.

Depends on: gt mol command tree issue


Digest: mol-deacon-patrol


gt mq submit: create MR from current branch

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

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

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

Creates merge-request bead and prints MR ID.

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


Layered context onboarding pattern

Pattern from handoff discussion:

## Pattern: Layered Context Onboarding

Town CLAUDE.md (user/org) -> Rig CLAUDE.md (project) -> Role priming

## Ultra-compressed HOP for workers (no reveal)

- Permanent record: All work tracked. Outcomes matter.
- Quality gates: Molecule steps exist for a reason.
- Attribution: Completions build your track record.
- Handoff clean: Leave state any worker can continue.

## Recommendation

Create Town @AGENTS.md for shared worker context that all workers see.
This provides common behavioral guidance without revealing full HOP context.


Consider splitting large files (800+ lines)

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

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


Computed range expansion (for-each over expressions)

Support for-each expansion over computed ranges, not just static lists:

for-each:
  var: move_num
  range: "1..2^{disks}"  # Computed at cook time

This enables formulas like Towers of Hanoi where the step count is a function of input variables. The range expression is evaluated during cooking.

Related: towers-of-hanoi.formula.yaml demonstrates the need.
Depends on: gt-8tmz.8 (Runtime dynamic expansion)