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-f17b
bonded_ref: arm-toast
bonded_at: 2025-12-23T10:00:00Z


Mail system: message types and threading

GGT mail system needs message types and threading like PGT.

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

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

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

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 9: nominal


Digest: mol-deacon-patrol

Cycle 5: 1 msg processed, all healthy


Implement gt harness create command

Add scaffolding command to create a new harness:
- gt harness create [path]
- Creates config/, mayor/, .beads/redirect (optional)
- Optionally initializes git
- Generates CLAUDE.md with Mayor role
- Could also offer a template repo alternative


Digest: mol-deacon-patrol

Patrol 3: stable


Outpost CLI commands: gt outpost list/status/add

## Overview

CLI commands for managing outposts.

## Commands

```bash
# List configured outposts
gt outpost list
NAME            TYPE       WORKERS   STATUS
local           local      2/4       healthy
gce-burst       ssh        0/8       healthy
cloudrun-burst  cloudrun   0/20      healthy

# Detailed status
gt outpost status [name]
Outpost: cloudrun-burst
Type: cloudrun
Project: my-gcp-project
Region: us-central1
Service: gastown-worker
Active Workers: 3/20
Cost (today): $0.42
Status: healthy

# Add new outpost interactively
gt outpost add
? Outpost type: [local/ssh/cloudrun]
> cloudrun
? Name: cloudrun-burst
? GCP Project: my-gcp-project
...

# Add via flags
gt outpost add cloudrun --name burst --project my-proj --region us-central1

# Remove outpost
gt outpost remove <name>

# Test connectivity
gt outpost ping <name>
```

## Files

- `cmd/gt/outpost.go` - Cobra command group
- `cmd/gt/outpost_list.go`
- `cmd/gt/outpost_status.go`
- `cmd/gt/outpost_add.go`

Depends on: gt-9a2.3 (config/manager)


CloudRun cost tracking: Usage monitoring and limits

## Overview

Track Cloud Run usage costs and enforce spending limits.

## Cost Model

Cloud Run pricing (approximate):
- CPU: ~$0.000024/vCPU-second
- Memory: ~$0.0000025/GiB-second
- Requests: ~$0.40/million

For 5-minute worker (2 vCPU, 4GB):
- CPU: 300 × 2 × $0.000024 = $0.0144
- Memory: 300 × 4 × $0.0000025 = $0.003
- **Total: ~$0.017 per session**

## Implementation

```go
type CostTracker struct {
    cpuRate    float64  // per vCPU-second
    memRate    float64  // per GiB-second
    sessions   []SessionCost
    mu         sync.RWMutex
}

type SessionCost struct {
    WorkerID  string
    StartTime time.Time
    EndTime   time.Time
    CPUs      float64
    MemoryGiB float64
}

func (t *CostTracker) RecordSession(workerID string, start, end time.Time, cpus, mem float64) {
    t.mu.Lock()
    defer t.mu.Unlock()
    t.sessions = append(t.sessions, SessionCost{...})
}

func (t *CostTracker) CurrentCost() float64 {
    t.mu.RLock()
    defer t.mu.RUnlock()
    var total float64
    for _, s := range t.sessions {
        duration := s.EndTime.Sub(s.StartTime).Seconds()
        total += duration * s.CPUs * t.cpuRate
        total += duration * s.MemoryGiB * t.memRate
    }
    return total
}
```

## Cost Cap Enforcement

```go
func (o *CloudRunOutpost) Spawn(...) (Worker, error) {
    if o.costCap > 0 && o.costTracker.CurrentCost() >= o.costCap {
        return nil, ErrCostCapExceeded
    }
    // ... spawn worker
}
```

## Config

```yaml
outposts:
  - name: cloudrun-burst
    type: cloudrun
    cost_cap_hourly: 5.00   # Stop spawning if hourly cost exceeds $5
    cost_cap_daily: 50.00   # Daily limit
```

## CLI

```bash
gt outpost status cloudrun-burst
# Shows: Cost (today): $0.42 / $50.00 cap
```

## Files

- `internal/cloudrun/cost.go`

## Dependencies

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


End-to-end swarm integration test

Full integration test for swarm lifecycle.

Test scenario:
1. Create a small epic with 3-4 issues and dependencies
2. Run bd swarm validate - should pass
3. Run gt swarm create with the epic
4. Verify swarm molecule created with mol_type=swarm
5. Run gt swarm status - verify ready front
6. Simulate issue completion (bd close)
7. Verify ready front advances
8. Complete all issues
9. Verify swarm auto-closes

Can be manual test documented in test file, or automated.

Dependencies: gt-kc7yj.2 (Witness integration), gt-kc7yj.3 (completion detection)


Digest: mol-deacon-patrol

Patrol 10: All healthy, 1 in_progress issue (max working)


Inline step expansion (Step.Expand field)

Step.Expand and Step.ExpandVars fields exist in schema but are not implemented.

## Current State

Steps can declare inline expansion:

    steps:
      - id: design
        expand: rule-of-five
        expand_vars:
          iterations: 3

This is more convenient than compose.expand for single-step expansions.

## Difference from compose.expand

- compose.expand: Centralized, applies after all steps parsed
- Step.Expand: Inline, step declares its own expansion

## Implementation

1. During cooking, detect steps with Expand field
2. Load referenced expansion formula
3. Replace step with expanded template (like compose.expand)
4. Pass ExpandVars as variable overrides
5. Handle recursion depth (reuse DefaultMaxExpansionDepth)

## Files

- internal/formula/types.go: Step.Expand, Step.ExpandVars (already defined)
- internal/formula/expand.go: Add inline expansion handling
- internal/formula/expand_test.go: Add test cases

## Depends On

- gt-8tmz.34 (var overrides) should be done first


MQ Submit: Notify Witness on MR submission

When `gt mq submit` is run, notify the Witness so it can verify the submission.

## Implementation

1. In mq_submit.go after successful MR creation:
   ```
   gt mail send <rig>/witness -s "LIFECYCLE: mr submitted" -m "
   Worker: <polecat>
   MR: <mr-id>
   Branch: <branch-name>
   Target: <target-branch>
   Action: Verify MR submission, help polecat if needed.
   "
   ```

2. This happens BEFORE the existing polecat auto-cleanup logic

## Acceptance
- MR submission sends notification to Witness
- Witness receives mail with all relevant details
- Works for both polecat and crew submissions


Implement handoffs using pinned beads

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

## Current Problem

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

## Solution

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

## Implementation

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

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

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

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

## Dependencies

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


Molecule await-signal step type

Add await-signal as a molecule step type for patrol agents.

## Step Definition

```toml
[[steps]]
id = "await-signal"
type = "wait"
backoff = { base = "30s", multiplier = 2, max = "10m" }
description = "Wait for signal, then check reality"
```

## Behavior

1. Sleep for current backoff interval
2. Wake immediately on nudge (or timeout expires)
3. Proceed to next step (check-reality)
4. If no work found, loop back with increased backoff
5. If work found, reset backoff to base

## Implementation Options

A. Formula-level: Define in TOML, agent interprets
B. Code-level: Agent implements wait loop with backoff

Option A is cleaner but needs formula parser support.
Option B works now but less declarative.

## Used By

- mol-witness-patrol (first step)
- mol-refinery-patrol (first step)


Proto debugging and inspection tools

Proto debugging and inspection tools.

With ephemeral protos (gt-4v1eo), this becomes:
- `bd cook <formula> --dry-run` - preview cooked output without persisting
- `bd cook <formula> --json` - output proto as JSON for inspection
- `bd cook <formula> --graph` - visualize step DAG

No persistent proto beads to inspect - formulas are the source of truth.


Witness MVP: Automated Polecat Lifecycle

Implement the Witness agent - per-rig 'pit boss' that manages polecat lifecycles.

The Witness enables hands-free swarming by automating:
- Spawning polecats for ready work
- Monitoring worker health
- Processing shutdown requests
- Cleaning up worktrees when done
- Escalating stuck workers to Mayor

Without Witness, humans must manually spawn, monitor, and kill each polecat.

## Core Loop

```
while True:
  # Handle pending shutdowns
  for polecat in polecats where state == pending_shutdown:
    verify git clean
    kill session
    remove worktree
    delete branch

  # Spawn for ready work
  ready = bd ready --parent=<epic> if epic else bd ready
  for issue in ready:
    if active_workers < max_workers:
      gt spawn --issue <id>

  # Check worker health
  for polecat in active polecats:
    if stuck (no progress for 30 min):
      nudge or escalate

  sleep 60
```

## Blocking Bugs to Fix
- gt-dsfi: handoff deadlock
- gt-n7z7: refinery foreground race condition
- gm-c6b: mail coordination (town vs rig beads)


Instrument gt commands to appear in activity feed

## Architecture: Two-tier event system

```
┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│ gt commands │  │ bd mutations│  │ agent output│
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       └────────────────┼────────────────┘
                        ▼
              ~/gt/.events.jsonl (raw, audit)
                        │
              ┌─────────────────┐
              │  Feed daemon    │ ← goroutine in gt daemon
              │  - filters      │
              │  - aggregates   │
              │  - AI curates   │
              └────────┬────────┘
                       ▼
              ~/gt/.feed.jsonl (curated, user-facing)
```

## Event format

```json
{
  "ts": "2025-12-28T10:30:15Z",
  "source": "gt",
  "type": "sling",
  "actor": "mayor",
  "payload": {"bead": "gt-xyz", "target": "gastown/Toast"},
  "visibility": "feed"
}
```

Visibility: `audit` | `feed` | `both`

## Implementation phases

1. **Event logging** - gt commands write to ~/gt/.events.jsonl
2. **Feed daemon** - goroutine in gt daemon tails events, writes curated feed
3. **Feed readers** - gt feed, gt dashboard read from .feed.jsonl
4. **AI curation** - future: haiku summarization for complex activity

Part of epic gt-u7dxq


Digest: mol-deacon-patrol

Patrol 4: routine


Digest: mol-deacon-patrol

Patrol 11: All healthy, dave now running


Witness session cycling and handoff

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

## Session Cycling Protocol

```markdown
## Session Cycling

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

### Handoff Protocol

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

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

## Active Workers
<list workers and status>

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

## Recent Actions
<last 3-5 actions>

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

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

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

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

### On Fresh Session Start

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

## Implementation

Add to WITNESS_CLAUDE.md template.


Digest: mol-deacon-patrol

Patrol 9: routine, all healthy


mol-theme-generate: Custom name pool generation via cognition

Currently polecat name themes are hardcoded in namepool.go. Simple theme selection is fine as static config, but custom name generation should be a molecule.

Examples requiring cognition:
- "Generate 50 polecat names in the style of [book/movie/game]"
- "Create names that match the project's domain (medical, financial, etc.)"
- "Extend the mad-max theme with 20 more obscure characters"

## Molecule: theme-generate
Generate custom polecat names for a rig.

## Step: analyze-request
Understand the naming request:
- Theme style or source material
- Number of names needed
- Any constraints (length, format)

## Step: generate-candidates
Generate candidate names matching the style.
Aim for 2x the requested count.

## Step: validate-names
Filter candidates:
- No duplicates with existing themes
- Appropriate length (3-15 chars)
- No problematic terms

## Step: create-theme
Write the custom theme to rig config or beads.
Register in name pool.
Needs: validate-names

## Notes
- Output is a new theme entry in BuiltinThemes equivalent
- Could be stored as a pinned bead for persistence


Extended addressing: Parse [machine:]rig/polecat

Extended addressing for cross-machine operations.

## Address Format

```
[machine:]rig/polecat
```

Examples:
- `wyvern/Toast` - Local machine, wyvern rig, Toast polecat
- `gcp-vm-1:wyvern/Toast` - Remote machine
- `wyvern/` - Broadcast to rig
- `gcp-vm-1:wyvern/` - Broadcast to remote rig

## Parser

```go
type Address struct {
    Machine  string  // empty = local
    Rig      string
    Polecat  string  // empty = broadcast
}

func ParseAddress(s string) (*Address, error)
func (a *Address) String() string
func (a *Address) IsLocal() bool
func (a *Address) IsBroadcast() bool
```

## Validation

```go
func (a *Address) Validate(registry *MachineRegistry) error
```
- Machine must exist in registry (if specified)
- Rig must exist on machine
- Polecat must exist (if specified)

## Usage

All CLI commands that take addresses use ParseAddress:
```go
// gt mail send gcp-vm-1:wyvern/Toast -s "Hello"
addr, _ := ParseAddress("gcp-vm-1:wyvern/Toast")
conn, _ := registry.Connection(addr.Machine)
// Use conn for operations
```

## Backward Compatibility

Existing `rig/polecat` addresses work unchanged (Machine defaults to local).


Digest: mol-deacon-patrol

Patrol 9: all healthy


Digest: mol-deacon-patrol

Patrol 10: all healthy, doctor pass


Remove markdown molecule heresy from ~/gt/molecules/

The ~/gt/molecules/lifecycle/*.md files are outdated heresy - molecule definitions should only exist as formula.toml files in .beads/formulas/. Remove the markdown specs and update any docs that reference them.


Digest: mol-deacon-patrol

Patrol 20: all healthy, handoff threshold reached


CRITICAL: ProcessMRFromQueue not implemented - Refinery merge logic stubbed

File: internal/refinery/engineer.go:384
The ProcessMRFromQueue() function returns hardcoded failure with TODO comment.
This is a core refinery function that cannot process merges in current state.
Must implement actual merge logic before OSS launch.


Digest: mol-deacon-patrol

Patrol 11: All healthy


crew_add.go: Agent bead ID uses wrong format

In internal/cmd/crew_add.go:84, the agent bead ID is constructed as:

  crewID := fmt.Sprintf("gt-crew-%s-%s", rigName, name)

This produces 'gt-crew-gastown-joe' but existing beads and the session name format use:

  gt-<rig>-crew-<name>  (e.g., gt-gastown-crew-joe)

This causes agent beads to not be found/matched properly.

Fix: Change line 84 to:

  crewID := fmt.Sprintf("gt-%s-crew-%s", rigName, name)


Refinery background daemon mode

Refinery currently only works in foreground mode. Background daemon is stubbed.

## Current State
manager.go line 128-129:
```go
// Background mode: spawn a new process
// For MVP, we just mark as running - actual daemon implementation in gt-ov2
return nil
```

## Requirements

### 1. Background Process Spawning
```go
func (m *Manager) Start(foreground bool) error {
    if !foreground {
        // Spawn gt refinery start --foreground as subprocess
        cmd := exec.Command(os.Args[0], "refinery", "start", m.rig.Name, "--foreground")
        cmd.Start()  // Don't wait
        // Record PID
    }
}
```

### 2. PID File Management
- Write PID to .gastown/refinery.pid
- Check PID validity on status
- Clean up stale PID files

### 3. Log Output
- Redirect stdout/stderr to .gastown/refinery.log
- Log rotation (optional for MVP)

### 4. Graceful Shutdown
- Handle SIGTERM/SIGINT
- Complete current merge before exit
- Update state to stopped

### 5. Health Check
- Process existence check via kill -0
- Optional: heartbeat file with timestamp

## Files to Modify
- internal/refinery/manager.go: Start(), Status(), process spawning

## Acceptance Criteria
- [ ] gt refinery start <rig> spawns background process
- [ ] gt refinery status shows running with PID
- [ ] gt refinery stop sends SIGTERM and waits
- [ ] Logs written to .gastown/refinery.log
- [ ] Survives terminal close


Request shutdown

Send shutdown request to Witness.
Wait for termination.

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


Witness patrol: Add sibling-check for Refinery

Witness should check on Refinery health as part of patrol (village sibling watch).

## Implementation

1. Add `sibling-check` step to mol-witness-patrol
2. Use `gt peek <rig>/refinery 20` to check Refinery output
3. Check Refinery queue status: `gt mq list <rig>`
4. Look for:
   - Refinery session alive
   - Queue processing (if items present)
   - No stuck MRs (same MR in_progress for too long)
5. If Refinery seems stuck:
   - Send nudge or status request
   - Consider escalation to Deacon

## Acceptance
- Witness checks Refinery each patrol cycle
- Can detect stuck Refinery
- Provides backup oversight for merge queue


mol-polecat-work

Full polecat lifecycle from assignment to decommission.

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

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


GGT vs PGT Gap Analysis Summary

Summary of gaps comparing GGT (~4,500 LOC) to PGT (~27,700 LOC).

## Critical Gaps (P1) - Existing Issues
- gt-u1j.17: Polecat CLI (add, list, wake, sleep) - DETAILED
- gt-u1j.16: Rig CLI (add, list, show, remove) - DETAILED
- gt-u1j.18: Witness CLI (start, stop, status) - DETAILED
- gt-f9x.4: Doctor framework - DETAILED
- gt-f9x.5: Workspace doctor checks - DETAILED
- gt-f9x.6: Rig doctor checks - DETAILED

## Critical Gaps (P1) - New Issues
- gt-a95: Refinery background daemon mode - ENHANCED
- gt-hgk: Mail message types and threading - ENHANCED

## Significant Gaps (P2) - New Issues
- gt-d46: Mail CLI archive/purge/search - ENHANCED
- gt-e9k: Swarm preflight/postflight - ENHANCED
- gt-662: Swarm report generation - ENHANCED
- gt-69l: Hook system - ENHANCED
- gt-3yj: Agent monitoring - ENHANCED
- gt-1ky: Workspace CLI (may overlap f9x.3) - ENHANCED
- gt-9j9: Worker status reporting - ENHANCED
- gt-qao: Mayor CLI - ENHANCED
- gt-7o7: Session pre-shutdown checks - ENHANCED
- gt-c92: Batch all command - ENHANCED
- gt-lno: Swarm state persistence - ENHANCED
- gt-a9y: File locking - ENHANCED
- gt-30o: Error handling improvements - ENHANCED

## Significant Gaps (P2) - Existing Issues
- gt-kmn.12: Ephemeral rig support - DETAILED

## Lower Priority (P3) - New Issues
- gt-3fm: Mail orchestrator daemon - ENHANCED
- gt-2kz: Cleanup commands - ENHANCED
- gt-ebl: Names commands - ENHANCED
- gt-1u9: Interactive prompts - ENHANCED
- gt-8lz: Help text improvements - ENHANCED

## Closed as Duplicates
- gt-3tz → gt-u1j.17 (polecat CLI)
- gt-e1r → gt-u1j.16 (rig CLI)
- gt-86w → gt-f9x.4/5/6 (doctor)
- gt-alx → gt-kmn.12 (ephemeral rigs)

## Execution Order Recommendation
1. P1 CLI commands (existing detailed issues ready to implement)
2. gt-a95: Refinery daemon (blocks autonomous operation)
3. gt-7o7: Pre-shutdown checks (prevents data loss)
4. gt-a9y: File locking (prevents corruption)
5. gt-hgk + gt-d46: Mail improvements
6. gt-69l: Hook system (enables extensibility)
7. Remaining P2s in any order
8. P3s as time permits


Work on ga-xxp: Define mol-polecat-work standard molecule...

Work on ga-xxp: Define mol-polecat-work standard molecule. See bd show ga-xxp for full details.


Digest: mol-deacon-patrol

Patrol cycle 16: nominal


Fix CLI mismatches from bd-2fs7 (wisp->ephemeral migration)

bd-2fs7 moved wisp/pour under bd mol, but gastown wasn't updated.

Fixed:
- router.go: --wisp -> --ephemeral for bd create
- patrol_helpers.go: bd wisp create -> bd mol wisp create
- wisp_check.go: bd wisp gc -> bd mol wisp gc

This was causing handoff failures (gt mail send errored with 'unknown flag: --wisp').


Remove shell respawn loops from witness and deacon

Currently witness and deacon use shell loops (`while true; do claude; done`) to auto-respawn when Claude exits. This bypasses the proper lifecycle architecture.

## Current Behavior
- witness.go: ensureWitnessSession() wraps Claude in a shell loop
- deacon.go: runDeaconStart() wraps Claude in a shell loop
- When Claude exits, shell automatically restarts it

## Desired Behavior
- Agents should request lifecycle changes via LIFECYCLE mail to deacon
- Daemon processes lifecycle requests and handles restarts
- No shell loops - daemon and deacon's health-scan handle respawns

## Why Change
1. Shell loops bypass state verification (requesting_cycle flag)
2. Shell loops bypass handoff mail protocol
3. Shell loops make lifecycle tracking harder
4. Daemon already has infrastructure for this

## Migration
1. Remove shell loop from witness (daemon/deacon handles restart)
2. Remove shell loop from deacon (daemon handles restart via ensureDeaconRunning)
3. Verify daemon's health check properly restarts dead agents
4. Update templates if needed

## Risk
Low - we have gt witness restart and gt deacon restart as fallbacks


Digest: mol-deacon-patrol

Patrol 16: all healthy


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

Patrol 10: quiet


Digest: mol-deacon-patrol

Routine patrol, town stable


Digest: mol-deacon-patrol

Patrol 16: all clear


Tmux wrapper: session operations

Wrapper for tmux session operations via subprocess.

## Interface

```go
type Tmux struct{}

func NewTmux() *Tmux

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

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

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

## Session Naming

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

## Implementation

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

## Error Handling

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

## Environment

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


Digest: mol-deacon-patrol

Patrol 11: All clear


CLI: names commands for polecat naming pool

Polecat naming pool for auto-generated names.

## Commands

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

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

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

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

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 18: OK


Implement gt hooks command

New command to show all hooks across a rig.

Usage: gt hooks [rig]

Output shows:
- All polecats and their hook status
- Witness hook status
- Refinery hook status
- Crew member hook status
- Progress bars for attached molecules


Digest: mol-deacon-patrol

Patrol complete: routine cycle


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

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


Merge: gt-ih0s

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


Update docs with molecule navigation workflow

Update architecture and workflow docs with new molecule navigation.

## Docs to update

### docs/molecules.md
- Add 'Navigating Molecules' section
- Document bd mol current usage
- Document bd close --continue workflow
- Show the propulsion pattern

### docs/propulsion-principle.md
- Add molecule navigation as key enabler
- Show before/after workflow comparison

### docs/polecat-wisp-architecture.md
- Update step execution section
- Show bd close --continue in examples

## New section content

### Molecule Navigation

Finding your place:
  bd mol current              # Where am I?
  bd mol current gt-abc       # Status of specific molecule

Seamless transitions:
  bd close gt-abc.3 --continue   # Close and advance
  bd close gt-abc.3 --no-auto    # Close but don't auto-claim next

The old way (3 commands):
  bd close gt-abc.3
  bd ready --parent=gt-abc
  bd update gt-abc.4 --status=in_progress

The new way (1 command):
  bd close gt-abc.3 --continue

## Blocked by (Beads features)
- bd-sal9: bd mol current
- bd-ieyy: bd close --continue


Update lifecycle mail targets to deacon/

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


Digest: mol-deacon-patrol

Patrol 14: routine


Digest: mol-deacon-patrol

Patrol #2: Routine - all 6 agents healthy


gt-gastown-polecat-slit

gt-gastown-polecat-slit

role_type: polecat
rig: gastown
agent_state: running
hook_bead: gt-scjyc
role_bead: gt-polecat-role
cleanup_status: clean


Digest: mol-deacon-patrol


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

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


Digest: mol-deacon-patrol

Patrol cycle 18: nominal


Mol Mall: Molecule marketplace distribution

Design the Mol Mall distribution mechanism for sharing molecules.

## Core Insight

Mol Mall is just a registry of molecules.jsonl fragments. No special format needed.

## Distribution Model

```
Mol Mall (registry)
    │
    │ bd mol search "security"
    ▼
Molecule catalog (searchable index)
    │
    │ bd mol install mol-security-scan
    ▼
Download molecules.jsonl fragment
    │
    │ Append to ~/.beads/molecules.jsonl
    ▼
Available locally: bd mol list
```

## Registry Format

Simple JSON index served from HTTPS:

```json
{
  "molecules": [
    {
      "id": "mol-security-scan",
      "title": "Security Scan",
      "description": "OWASP Top 10 vulnerability checker",
      "labels": ["plugin", "code-review", "tier:sonnet"],
      "author": "anthropic",
      "version": "1.0.0",
      "url": "https://mol-mall.anthropic.com/molecules/mol-security-scan.jsonl"
    }
  ]
}
```

## CLI Commands

```bash
bd mol search <query>           # Search registry
bd mol info <id>                # Show molecule details
bd mol install <id>             # Download and install
bd mol uninstall <id>           # Remove from catalog
bd mol update                   # Update all installed molecules
```

## Installation Locations

```
~/.beads/molecules.jsonl        # User-level (default install location)
~/gt/.beads/molecules.jsonl     # Town-level (shared across rigs)
.beads/molecules.jsonl          # Project-level (team-specific)
```

## Version Management

Molecules can have versions via labels: `version:1.0.0`
Install specific version: `bd mol install mol-security-scan@1.0.0`

## Auth (Future)

Private registries could require auth token.
Enterprise Mol Malls for internal distribution.

## Related

- gt-u818: Plugin System (plugins ARE molecules)
- molecular-chemistry.md: Format documentation


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.

**ZFC-Compliant Observation** (AI observes AI):

```bash
# View pending spawns with captured terminal output
gt deacon pending
```

For each pending session, analyze the captured output:
- Look for Claude's prompt indicator "> " at the start of a line
- If prompt is visible, Claude is ready for input
- Make the judgment call yourself - you're the AI observer

For each ready polecat:
```bash
# 1. Trigger the polecat
gt nudge <session> "Begin."

# 2. Clear from pending list
gt deacon pending <session>
```

This triggers the UserPromptSubmit hook, which injects mail so the polecat sees its assignment.

**Bootstrap mode** (daemon-only, no AI available):
The daemon uses `gt deacon trigger-pending` with regex detection. This ZFC violation is acceptable during cold startup when no AI agent is running yet.


Document the Propulsion Principle

Write canonical documentation for the Universal Gas Town Propulsion Principle.

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

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

This is foundational theory-of-operation documentation.


Session pre-shutdown checks

Session stop should verify clean state before killing, like PGT.

## Pre-Shutdown Checks

### 1. Git Working Tree Clean
```go
func checkGitClean(clonePath string) error {
    // git status --porcelain
    // Fail if any output
}
```

### 2. All Commits Pushed
```go
func checkCommitsPushed(clonePath string) error {
    // git log origin/HEAD..HEAD
    // Fail if any unpushed commits
}
```

### 3. Assigned Issues Handled
```go
func checkIssuesHandled(polecat *Polecat) error {
    // If polecat.Issue != "", check if closed or reassigned
}
```

### 4. Beads Synced
```go
func checkBeadsSynced(clonePath string) error {
    // bd sync --status in clone directory
}
```

## Behavior on Failure
1. First attempt: Nudge worker to fix
2. Retry up to 3 times with delay
3. After retries: Escalate to Witness/Mayor

## Integration
Modify internal/session/manager.go Stop():
```go
func (m *Manager) Stop(polecat string, force bool) error {
    if !force {
        if err := m.runPreShutdownChecks(polecat); err != nil {
            return fmt.Errorf("pre-shutdown checks failed: %w", err)
        }
    }
    // existing stop logic
}
```

## Flags
- --force: Skip checks
- --grace-period N: Time to wait for fixes

## Dependencies
- Ties into gt-69l (hook system) - can be hook-based
- Ties into gt-f8v (Witness pre-kill verification)

## Acceptance Criteria
- [ ] Stop fails if uncommitted changes (without --force)
- [ ] Stop fails if unpushed commits
- [ ] Clear error messages with fix instructions
- [ ] --force bypasses all checks


Error handling improvements: retry logic and recovery hints

Improve error handling across GGT codebase.

## Issues Found

### 1. Silent Failures in Refinery
manager.go ProcessMR():
- git pull errors ignored
- Continues processing even on partial failures

### 2. Missing Beads CLI
Many operations silently continue if `bd` not found:
```go
// Current
tasks, err := m.loadTasksFromBeads(epicID)
if err != nil {
    // Non-fatal - swarm can start without tasks
}

// Better
if err != nil {
    return nil, fmt.Errorf("beads required: %w", err)
}
```

### 3. No Retry Logic
Network/lock errors should retry:
```go
func withRetry(attempts int, delay time.Duration, fn func() error) error {
    for i := 0; i < attempts; i++ {
        if err := fn(); err == nil {
            return nil
        }
        time.Sleep(delay)
    }
    return fmt.Errorf("failed after %d attempts", attempts)
}
```

### 4. Recovery Hints
Error messages should include fix suggestions:
```go
// Current
return fmt.Errorf("polecat not found")

// Better
return fmt.Errorf("polecat '%s' not found. Use 'gt polecat list' to see available polecats", name)
```

## Domain Error Types
```go
type SessionError struct {
    Op      string
    Polecat string
    Err     error
}

func (e *SessionError) Error() string {
    return fmt.Sprintf("session %s for %s: %v", e.Op, e.Polecat, e.Err)
}
```

## Files to Review
- internal/refinery/manager.go
- internal/swarm/manager.go
- internal/session/manager.go
- All cmd/*.go files

## Acceptance Criteria
- [ ] No silent error swallowing
- [ ] Retry logic for transient failures
- [ ] Helpful error messages with hints
- [ ] Consistent error types per domain


Phase 1.2: Configure bd for ephemeral molecule bonding

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

## Questions for Dave

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

## Integration

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

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


Document bd mol bond/squash/burn CLI

Create CLI reference documentation for molecule commands:

## bd mol bond

Instantiate a proto into a runnable molecule.

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

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

## bd mol squash

Complete a molecule and generate digest.

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

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

## bd mol burn

Abandon a molecule without completing.

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

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


Recovery: Detect orphaned merged polecats

Safety net for when Refinery misses sending lifecycle notification.

## Detection Points

**Witness polecat-scan**:
- For each polecat in `mr_submitted` state:
  - Check MR status: `gt mq status <mr-id>`
  - If MR is closed/merged but polecat state is still mr_submitted:
    - Update polecat state to `merged`
    - Log recovery action

**Deacon orphan-check**:
- Query all closed MRs
- Cross-reference with polecat states
- If merged MR but polecat not in merged/retired state:
  - Send correction to Witness
  - Log for metrics (indicates Refinery notification missed)

## Acceptance
- Orphaned polecats detected and corrected
- Recovery is logged for monitoring
- No work lost due to missed notifications


Digest: mol-deacon-patrol

Patrol 8: nominal


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 sling to town roles should use Town beads

## Problem

When Mayor starts from ~/gt (town root), `gt mol status` couldn't find beads pinned in rig beads.

## Solution

Added cross-rig scanning for town-level roles (mayor, deacon):
1. First check local beads directory
2. If nothing found AND target is a town-level role, scan all registered rigs
3. Return first pinned bead found

This makes `gt mol status` work correctly regardless of where Mayor is when checking hook status.

## Tested

1. `gt sling gt-552hb mayor` from gastown/mayor/rig/ - pins in rig beads ✓
2. `gt mol status` from ~/gt - finds the pinned bead via cross-rig scan ✓


Daemon should auto-start as part of town bootstrap

The Go daemon should be started automatically when the town boots up (mol-gastown-boot). Currently it requires manual 'gt daemon start'.

The daemon is critical infrastructure - it monitors the Deacon and nudges it when naked. Without it, the Deacon patrol loop doesn't run.


gt-gastown-polecat-ace

gt-gastown-polecat-ace

role_type: polecat
rig: gastown
agent_state: done
hook_bead: null
role_bead: gt-polecat-role
cleanup_status: has_stash


Digest: mol-deacon-patrol

Patrol 14: all quiet


Digest: mol-deacon-patrol

Patrol 15


Digest: mol-deacon-patrol

Patrol 15: all healthy, doctor pass


gt-crew-gastown-max

gt-crew-gastown-max

role_type: crew
rig: gastown
agent_state: running
hook_bead: null
role_bead: gt-crew-gastown-max-role
cleanup_status: null


gt crew restart command

Add a 'gt crew restart' command that kills the tmux session and restarts fresh. Useful when a crew member gets confused or needs a clean slate.

Should:
- Kill existing tmux session if running
- Start fresh session with claude
- Run gt prime to reinitialize context

Alias: gt crew rs


Hardcoded session ID prefixes and paths

Multiple hardcoded values throughout code:
- internal/mail/router.go: 'gt-mayor', 'gt-' session prefixes
- internal/lock/lock.go: '.runtime', 'agent.lock' paths
- Session ID generation uses string literals

Should move to:
- constants package for prefixes
- config for customizable paths

Low priority but improves maintainability.


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


Digest: mol-deacon-patrol

Patrol cycle 17: routine


Digest: mol-deacon-patrol

Patrol 19: all quiet


Digest: mol-deacon-patrol

Patrol 6: Quick cycle, all quiet


Digest: mol-deacon-patrol

Patrol 13: All healthy, no messages


Digest: mol-deacon-patrol

Patrol 17: Nominal


Standardize session end: gt handoff for all roles

## Summary

Standardize session ending across all agent roles to use `gt handoff` as the canonical command. This is critical for the beads propulsion cycle - turning agent sessions from pets into cattle.

## Current State (Inconsistent)

| Role | Current Guidance | Command |
|------|-----------------|---------|
| Mayor | Manual mail send | `gt mail send mayor/ -s 'HANDOFF:...' -m '...'` |
| Crew | Manual mail send | `gt mail send <rig>/crew/<name> -s 'HANDOFF:...' -m '...'` |
| Witness | Manual mail send | `gt mail send <rig>/witness -s 'HANDOFF:...' -m '...'` |
| Refinery | Manual mail send | `gt mail send <rig>/refinery -s 'HANDOFF:...' -m '...'` |
| Deacon | Exit on high context | (implicit) |
| Polecat | `gt done` | `gt done [--exit TYPE]` |

## Target State (Unified)

All roles use `gt handoff`:
- `gt handoff` - Hand off current session to fresh instance
- `gt handoff -s 'context' -m 'details'` - Hand off with custom message
- For polecats: `gt handoff` internally calls `gt done`

## Changes Required

### 1. Code Changes
- [ ] Update `gt handoff` to detect polecat role and call `gt done` internally
- [ ] Consider adding `--exit` flag to `gt handoff` for polecat compatibility

### 2. CLAUDE.md Updates (gastown)
- [ ] ~/gt/CLAUDE.md (Mayor)
- [ ] gastown/crew/max/CLAUDE.md
- [ ] gastown/crew/joe/CLAUDE.md
- [ ] gastown/witness/CLAUDE.md
- [ ] gastown/refinery/CLAUDE.md (and rig/)
- [ ] deacon/CLAUDE.md

### 3. CLAUDE.md Updates (beads)
- [ ] beads/mayor/rig/CLAUDE.md
- [ ] beads/crew/dave/CLAUDE.md
- [ ] beads/crew/zoey/CLAUDE.md
- [ ] beads/witness/CLAUDE.md
- [ ] beads/refinery/CLAUDE.md (and rig/)

### 4. Architecture Docs
- [ ] docs/patrol-system-design.md
- [ ] gastown/mayor/rig/docs/prompts.md
- [ ] gastown/mayor/rig/docs/session-management.md

## New Session End Checklist (Universal)

```
# SESSION CLOSE PROTOCOL

[ ] 1. git status              (check uncommitted changes)
[ ] 2. git add <files>         (stage changes)
[ ] 3. git commit -m '...'     (commit with issue ID)
[ ] 4. bd sync                 (sync beads)
[ ] 5. git push                (push to remote - CRITICAL)
[ ] 6. gt handoff              (hand off to fresh session)
       OR gt handoff -s 'Context' -m 'Details for next session'
```

## Why This Matters

The handoff mechanism is what turns agent sessions from **pets** (precious, long-lived) into **cattle** (disposable, replaceable). At any time, any agent can:
1. Send itself a detailed handoff mail (or sling itself a mol)
2. System shuts them down and restarts them
3. Fresh session runs priming and reads mail
4. Work continues seamlessly

This enables:
- Unlimited context through automatic cycling
- Clean recovery from any state
- Consistent behavior across all roles
- Simplified agent instructions


gt session start --issue doesn't inject work to prompt

When starting a polecat session with --issue flag:
  gt session start beads/Toast --issue bd-oxgi

The session starts but the polecat doesn't receive the issue context. They sit at the prompt awaiting input. Had to manually nudge them.

Expected: Session start should either nudge or inject the issue context so polecat starts working immediately (propulsion principle).


Digest: mol-deacon-patrol

Patrol 2: All agents healthy, no issues.


Digest: mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 18: clear


Digest: mol-deacon-patrol

Patrol 20: Final patrol, 11 sessions healthy, no incidents


Digest: mol-deacon-patrol

Patrol 11: Nominal


Refinery Patrol Cycle


Witness swarm integration

Add swarm coordination to Witness patrol.

The Witness should be able to:
1. Receive swarm assignments via mail (SWARM: subject prefix)
2. Query ready front and dispatch to available polecats
3. Monitor polecat progress on swarm tasks
4. Detect completion and close swarm molecule
5. Use Christmas Ornament pattern for arm-bonding (optional)

Implementation:
- Add swarm handling to mol-witness-patrol formula
- Mail trigger: SWARM in subject
- Dispatch: bd ready --parent=<epic> to find ready issues
- Assign: gt sling <issue> to available polecat
- Monitor: Check polecat status during patrol
- Complete: When bd ready --parent returns empty and all closed

Reference: ~/gt/docs/swarm-architecture.md - Witness Integration section

Dependencies: gt-kc7yj.1 (gt swarm refactor)


Digest: mol-deacon-patrol

Patrol cycle 12: routine


Update role prompts with hook protocol

Update role prompt templates to include hook-first protocol.

Templates to update:
- polecat.md.tmpl
- deacon.md.tmpl
- witness.md.tmpl
- refinery.md.tmpl
- crew.md.tmpl

Each should include:
1. Check hook first (gt mol status)
2. If empty, check mail for attached work
3. Self-pin protocol if needed


Async Coordination Gates

Agents wait for external events without polling.

## Problem
Agents cannot wait for external conditions (CI completion, human approval, timers) without wasteful polling or losing state on handoff.

## Requirements
- Gate issue type in beads (type=gate)
- bd gate create --await timer:5m / gh:run:123 / human:approve / mail:pattern
- Deacon evaluates pending gates each patrol cycle
- Agents park work on gate, resume when condition met
- Gate timeout and notification

## Gate Types
- timer:<duration> - Simple delay (5m, 1h, 24h)
- gh:run:<id> - GitHub Actions run completion
- gh:pr:<id> - PR merged/closed
- human:<prompt> - Human approval required
- mail:<pattern> - Wait for mail matching pattern

## Success Criteria
- Agent can create gate and suspend work
- Deacon wakes agent when gate condition met
- Gates survive session cycling

Consolidates gt-31eg. Parallel work with no dependencies.


Digest: mol-deacon-patrol

Patrol 5: All green


Mayor Role Definition

You are the Mayor - global coordinator of Gas Town. You sit above all rigs,
coordinating work across the entire workspace.

default_molecule: mol-mayor-patrol
capabilities:
  - dispatch_work
  - cross_rig_coordination
  - escalation_handling

## Responsibilities

- Work dispatch: Spawn workers for issues, coordinate batch work on epics
- Cross-rig coordination: Route work between rigs when needed
- Escalation handling: Resolve issues Witnesses can't handle
- Strategic decisions: Architecture, priorities, integration planning

NOT your job: Per-worker cleanup, session killing, nudging workers (Witness handles that)

## Propulsion Principle

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

Your pinned molecule persists across sessions. Hook has work -> Run it.
Hook empty -> Check mail. Nothing anywhere -> Wait for user.

## Key Commands

### Communication
- gt mail inbox - Check your messages
- gt mail read <id> - Read a specific message
- gt mail send <addr> -s "Subject" -m "Message" - Send mail

### Status
- gt status - Overall town status
- gt rigs - List all rigs
- gt polecats <rig> - List polecats in a rig

### Work Management
- bd ready - Issues ready to work (no blockers)
- gt sling <bead> <rig> - Assign work to polecat in rig

## Session End Protocol

- git status, git add, bd sync, git commit, git push
- gt handoff - hand off to fresh session


Digest: mol-deacon-patrol

Patrol 19: routine


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

## Problem

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

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

## Expected Behavior

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

## Reproduction

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

## Root Cause

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

## Fix Options

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


Digest: mol-deacon-patrol

Patrol 9: all clear


@group dynamic resolution in mail router

## @group Dynamic Resolution in Mail Router

Wire agent bead queries into mail router for @group addresses.

## Updated Approach (post agent-as-bead + Dogs)

Agent discovery uses bead queries:

```bash
bd list --type=agent --rig=gastown         # All agents in rig
bd list --type=agent --role_type=witness   # All witnesses
bd list --type=agent --role_type=dog       # All dogs
```

## Supported Patterns

| Pattern | Resolution | Scope |
|---------|------------|-------|
| `@rig/gastown` | `--rig=gastown` | All gastown agents |
| `@town` | `--rig=""` (no filter) | All town-level agents |
| `@witnesses` | `--role_type=witness` | All witnesses across rigs |
| `@crew/gastown` | `--role_type=crew --rig=gastown` | Gastown crew |
| `@dogs` | `--role_type=dog` | All Deacon dogs |
| `@overseer` | Special case (not bead query) | Human operator |

## Deliverables

1. Parse @group syntax in router
2. Resolve via `bd list --type=agent` queries
3. Fan-out at send time (N copies, one per recipient)
4. Special handling for `@overseer` (single recipient, uses overseer.json)

## @overseer Special Case

The overseer is the human operator - NOT an agent bead. Resolution:
- Load `mayor/overseer.json`
- Use `overseer/` as recipient address
- No bead query needed

## Dependencies
- Agent bead schema (gt-ikyo1) ✅ closed
- Dog agent beads need role_type=dog (new prereq)

## Acceptance
- @rig/gastown sends to all gastown agents
- @dogs sends to all Deacon dogs
- @overseer sends to human operator
- Each recipient gets own message copy

Beads dependency: bd-jsk7 (Agent beads: structured labels for filtering)


Replaceable notifications via Claude Code queue

Leverage Claude Code's ability to replace queued text for notifications that supersede previous ones.

## Problem

If daemon sends 10 heartbeats while agent is busy, agent returns to see 10 stacked messages. Wasteful and noisy.

## Solution

Use Claude Code's queue replacement for:
- Heartbeat messages (only latest matters)
- Status updates that supersede previous
- Progress notifications

## Implementation

Notifications get a 'slot' identifier. New notification in same slot replaces old one:
- Slot: 'heartbeat' → only one heartbeat queued at a time
- Slot: 'status-<rig>' → latest status per rig
- No slot → stacks normally (for unique messages)

## Research Needed

- How does Claude Code expose queue replacement?
- tmux send-keys behavior with pending input
- Alternative: clear + resend pattern


Digest: mol-deacon-patrol

Patrol 9


Digest: mol-deacon-patrol

Patrol 8


Digest: mol-deacon-patrol

Patrol #15: 3/4 through


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.


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


Digest: mol-deacon-patrol

Patrol cycle 4: routine check, all nominal


Self-review

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

Fix any issues found before proceeding.


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)


gt doctor: detect tmux session anomalies (linked panes, etc.)

Add a tmux health check to gt doctor that detects:

1. **Linked panes between sessions** - The bug where gt-deacon and gt-mayor shared pane @283, causing heartbeat crosstalk
2. **Session naming anomalies** - Sessions that don't match expected patterns
3. **Orphaned panes** - Panes in gt-* sessions with no running process

Detection approach:
- List all gt-* sessions
- For each session, get window/pane IDs
- Check for duplicate pane IDs across different sessions
- If found, report which sessions are linked and suggest fix

Auto-fix: Could offer to kill and recreate the offending session.

Reference: gt-rt6g documented a case where this caused the daemon's Deacon heartbeats to appear in the Mayor's input, eating user prompts.


Molecule-based Witness Patrol

Wire up mol-witness-patrol as a tracking ledger for the Go-based witness.

## Key Insight

The molecule is NOT an executor - it's a **tracking structure**. The existing
Go code in internal/witness/manager.go continues to run the patrol loop.
The molecule instance provides visibility, audit trail, and polecat tracking.

## Current State

- Witness runs as Go code with direct patrol logic
- mol-witness-patrol and mol-polecat-arm exist as molecule definitions
- gt mol bond creates dynamic children
- No molecule tracking of actual witness operations

## Target State

1. When witness starts → instantiate mol-witness-patrol on its hook
2. When Go code discovers polecat → bond mol-polecat-arm child
3. When polecat completes/dies → close that arm issue
4. gt mol progress shows current patrol state
5. Molecule survives wisp burns via handoff bead

## Benefits

- Visibility: gt mol status gastown/witness shows active arms
- Audit: Each polecat inspection is a trackable issue
- Consistency: Same tracking model as polecats with molecules
- Progress: gt mol progress works for witness too

## Non-Goals

- NOT replacing Go code with Claude session
- NOT running molecule steps as prompts
- NOT requiring Claude for witness operation


Merge: gt-pyqv

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


Test

Body


Deacon trigger-pending-spawns: Auto-nudge polecats when ready

The Deacon's mol-deacon-patrol defines a trigger-pending-spawns step, but it's not implemented.

## Problem
After gt spawn creates a polecat session, Claude initializes for 10-20s. Nobody sends the initial 'Begin.' message to start work, so polecats sit at prompts forever.

## Current Workaround
Manual: gt nudge gt-<rig>-<polecat> 'Begin.'

## Required Implementation
1. Track pending spawns (polecats spawned but not triggered)
2. In Deacon patrol, poll WaitForClaudeReady for each pending spawn
3. When ready, send 'Begin.' via NudgeSession
4. Mark as triggered

## Evidence
Tracer bullet 2025-12-23: Had to manually nudge polecat/tracer to start work.


Digest: mol-deacon-patrol

Patrol OK: town quiet


Digest: mol-deacon-patrol

Patrol 18: routine


Digest: mol-deacon-patrol

Patrol 9: All clear


Merge: gt-594l2

branch: polecat/morsov-1766966180179
target: main
source_issue: gt-594l2
rig: gastown


MR ID generation: prefix-mr-hash convention

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

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

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

Implement: GenerateMRID(prefix, branch string) string

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


CLI: rig commands (list, show, add)

Rig management CLI commands.

## Commands

### gt rig list
List all registered rigs.
```
gt rig list [--json]
```
Output:
- Rig name
- Git URL
- Polecat count
- Witness/Refinery status

### gt rig show
Show details for a specific rig.
```
gt rig show <name> [--json]
```
Output:
- Full rig info
- Polecats with status
- Recent activity
- Beads summary (open issues)

### gt rig add
Add a new rig to the town.
```
gt rig add <name> <git-url>
gt rig add <name> --local <path>
```
Steps:
- Clone repo to town root (or link local)
- Register in rigs.json
- Create agent directories
- Configure git exclude

### gt rig remove
Remove a rig from the town.
```
gt rig remove <name> [--force]
```
- Warns if active polecats
- --force required if uncommitted changes

## Implementation

```go
var rigCmd = &cobra.Command{Use: "rig"}
rigCmd.AddCommand(rigListCmd, rigShowCmd, rigAddCmd, rigRemoveCmd)
```


Digest: mol-deacon-patrol

Patrol 20: Green - routine cycle complete, handing off


Digest: mol-deacon-patrol

Patrol 5: Routine


bd mol advance command

## Cross-Repo Reference

This work is tracked in beads repo as **bd-3zm7**.

The gastown task gt-qvn7.2.1 is a placeholder.
Actual implementation happens in beads.

See: bd show bd-3zm7 (from beads/crew/dave)


Digest: mol-deacon-patrol

Patrol 8: All clear


Digest: mol-deacon-patrol

Cycle 13: quiet


Fanout gate: waits-for children aggregation

Formalize the fanout gate pattern where a step waits for all dynamically-bonded children.

## Current State

Formulas already use this pattern informally:

    step: aggregate
    waits_for: "all-children"

But it is not in the algebra spec or implemented in the cooker.

## Proposed Syntax

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

Or simpler:

    step: aggregate
    needs: [survey-workers]
    waits-for: all-children

## Semantics

- Step cannot start until ALL children of the referenced step have completed
- Children are dynamically bonded via for-each (gt-8tmz.8)
- This is the "aggregation" half of the Christmas Ornament pattern
- If no children were bonded, the gate passes immediately

## Use Cases

**Witness Patrol:**
- survey-workers bonds N polecat-arms
- aggregate waits for all arms to complete
- Then proceeds to save-state

**Refinery Patrol:**
- Less common (sequential processing), but useful for parallel test runs

## Relationship to gate

This is a variant of gate where the condition is "all children complete" rather than
an arbitrary expression. Could be implemented as:

    gate:
      condition: "children-of(survey-workers).all(status == complete)"

But dedicated syntax is cleaner for this common pattern.

## Dependencies

- Depends on gt-8tmz.8 (for-each creates the children)
- Depends on gt-8tmz.4 (gate infrastructure)


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-f14c
bonded_ref: arm-toast
bonded_at: 2025-12-23T10:00:00Z


Merge: gt-rixa

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


Day 3.0: Document mail protocol for patrol coordination

Document the mail protocol used for patrol coordination.

**Mail types:**
- POLECAT_DONE: Polecat → Witness (work complete, ready for merge)
- MERGE_READY: Witness → Refinery (branch ready for merge queue)
- MERGED: Refinery → Witness (merge complete, safe to nuke)
- WITNESS_PING: Witness → Deacon (health check for second-order monitoring)
- HELP: Any → escalation target (stuck, need intervention)
- HANDOFF: Any → self (session continuity)

**Format conventions:**
- Subject prefix indicates type (e.g., 'POLECAT_DONE: nux')
- Body contains structured data (branch, bead ID, etc.)

This documentation enables consistent implementation across all patrol formulas.


Deacon patrol wisps should use town beads, not gastown rig beads

## Problem

The deacon is a town-level role but stores patrol wisps in gastown/mayor/rig/.beads/ (see internal/cmd/prime.go:691). This fails for users who don't have the gastown rig installed.

## Correct Architecture

Deacon -> ~/gt/.beads-wisp/ (town-level, no rig dependency)

Town-level ephemeral work (deacon patrols) should use town wisp storage, just like rig-level ephemeral work (witness/refinery patrols) uses rig wisp storage.

## Implementation Tasks

### 1. Town Setup (gt install / gt town init)

- Create ~/gt/.beads-wisp/ directory during town initialization
- Initialize as git repo (for local commits, not synced)
- Add config.yaml with wisp: true
- Ensure .beads-wisp/ is gitignored at town level

Files:
- internal/cmd/install.go (or equivalent town init)
- internal/town/manager.go (if exists)

### 2. Rig Setup (gt rig init)

Already creates <rig>/.beads-wisp/ - verify this is working:
- internal/rig/manager.go:375 (beadsDir := filepath.Join(rigPath, ".beads-wisp"))
- internal/rig/manager.go:394 (ensureGitignoreEntry)

### 3. Deacon Code Fixes

Update deacon to use town wisp storage:
- internal/cmd/prime.go:691 - change rigBeadsDir to townWispDir
- internal/cmd/sling.go - route deacon wisps to town level
- internal/daemon/daemon.go - any deacon-specific wisp handling

### 4. gt doctor Checks

Add/update doctor checks for wisp directories:
- internal/doctor/wisp_check.go - already checks rig wisps
- Add TownWispCheck to verify ~/gt/.beads-wisp/ exists
- Add TownWispGitCheck to verify it is a valid git repo
- Update existing checks to handle both town and rig levels

Files:
- internal/doctor/wisp_check.go (extend for town level)
- internal/doctor/checks.go (register new checks)

### 5. Documentation

- docs/beads-data-plane.md - already updated with three-tier architecture
- Verify wisp-architecture.md reflects town-level wisps

## Testing

1. Fresh gt install should create ~/gt/.beads-wisp/
2. gt doctor should pass with no rig installed
3. Deacon patrol should work without gastown rig
4. gt sling patrol deacon/ --wisp should route to town wisps


Digest: mol-deacon-patrol

Patrol 16: All healthy


Test Task 1: Add comment to gt.go


Digest: mol-deacon-patrol

Patrol 10


Digest: mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 9: All clear


Wire GT_ACCOUNT env var into spawn/attach

When spawning Claude Code (gt spawn, gt crew attach), check GT_ACCOUNT env var. If set, look up account in config and set CLAUDE_CONFIG_DIR accordingly.


Town-level beads: Real DB for coordination mail

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

## Background

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

## Design

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

Matches HOP hierarchy: platform > project > worker

## Structure

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

## Tasks

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

## Addressing

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


Digest: mol-deacon-patrol

Patrol 19: OK


Implement town activity logging

Add centralized logging for Gas Town agent lifecycle events.

## Events to Log
- spawn: new agent created
- wake: agent resumed
- nudge: message injected
- handoff: agent handed off (intentional restart)
- done: agent finished work
- crash: agent exited unexpectedly
- kill: agent killed intentionally

## Implementation
- Add internal/log/town.go with LogEvent() function
- Log to ~/gt/logs/town.log or .beads/town.log
- Add gt log command to view/tail/filter logs
- Include timestamps, agent identity, context

## Log Format
```
2025-12-26 15:30:45 [spawn] gastown/crew/max spawned for gt-xyz
2025-12-26 15:31:02 [nudge] gastown/crew/max nudged with "start work"
2025-12-26 15:45:33 [crash] gastown/crew/max exited unexpectedly (signal 9)
```


Deacon convoy patrol steps

New Deacon patrol steps for convoy orchestration.

Steps:
1. check-convoy-completion
   - Watch town feed for tracked issue closures
   - Auto-close convoys when all tracked issues complete

2. resolve-external-deps
   - When external issue closes, propagate to dependents in other rigs
   - Update blocked status across project chain boundaries

3. fire-notifications
   - Convoy complete → notify Overseer inbox
   - Cross-rig dep resolved → notify affected rigs

Depends on: gt feed, bd-hj0s (convoy type)
Related: hq-7h8jx (Convoy System epic in town beads)