Deacon patrol banners: visual feedback on atom transitions

Print large ASCII banners when transitioning between patrol atoms.

## Problem

When the deacon progresses through patrol atoms (steps), there is no visual feedback.
The operator cannot easily see what the deacon is doing without reading the full output.

## Desired Behavior

Print banners on step start and completion:

  INBOX-CHECK - Checking for lifecycle requests, escalations, timers
  INBOX-CHECK COMPLETE - Processed 3 messages, 0 lifecycle requests

## Benefits

1. Scanability: Operator can glance at tmux and see what is happening
2. Progress tracking: Easy to see where in the patrol loop we are
3. Debugging: Clear demarcation between steps for troubleshooting

## Implementation Options

1. In deacon CLAUDE.md: Instruct agent to print banners
2. gt patrol step start/end: Commands that print banners
3. bd mol step hooks: Automatically on step transitions

## Related

- gt-id36: Deacon Kernel
- gt-rana: Patrol System


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)


Polecat state tracking: Add mr_submitted and merged states

Track polecat lifecycle state separate from session state.

## New States

| State | Meaning |
|-------|---------|
| active | Working on assigned issue |
| mr_submitted | MR submitted, waiting for merge |
| idle | No current work, session may be alive |
| merged | Work successfully merged by Refinery |
| retired | Session killed, worktree removed |

## Implementation

1. Add state field to polecat tracking (state.json or beads)
2. `gt polecat update <name> --state=<state>`
3. `gt polecat list` shows state column
4. State transitions:
   - active → mr_submitted (on gt mq submit)
   - mr_submitted → merged (on Refinery notification)
   - merged → retired (on worktree cleanup)

## Acceptance
- Polecat state tracked and queryable
- State transitions happen at correct lifecycle points
- `gt polecat list` shows current state


mol-witness-patrol

Per-rig worker monitor patrol loop using the Christmas Ornament pattern.

The Witness is the Pit Boss for your rig. You watch polecats, nudge them toward
completion, verify clean git state before kills, and escalate stuck workers.

**You do NOT do implementation work.** Your job is oversight, not coding.

This molecule uses dynamic bonding to spawn mol-polecat-arm for each worker,
enabling parallel inspection with a fanout gate for aggregation.

## The Christmas Ornament Shape

```
                     ★ mol-witness-patrol (trunk)
                    /|\
          ┌────────┘ │ └────────┐
       PREFLIGHT  DISCOVERY  CLEANUP
          │          │           │
      inbox-check  survey    aggregate (WaitsFor: all-children)
      check-refnry   │       save-state
      load-state     │       generate-summary
                     ↓       context-check
             ┌───────┼───────┐  burn-or-loop
             ●       ●       ●   mol-polecat-arm (dynamic)
            ace     nux    toast
```


Fix namepool Save() to use write lock

attached_args: Fix namepool Save() mutex for thread safety

Fix incorrect mutex usage in namepool Save() method.

## Files to modify
- internal/polecat/namepool.go

## Problem (lines 179-194)
Save() uses RLock() but performs file I/O:
```go
func (p *NamePool) Save() error {
    p.mu.RLock()  // WRONG: file write needs write lock
    defer p.mu.RUnlock()
    // ... file operations ...
}
```

## Fix
Change to write lock:
```go
func (p *NamePool) Save() error {
    p.mu.Lock()
    defer p.mu.Unlock()
    // ... file operations ...
}
```

## Acceptance criteria
- [ ] Save() uses Lock() instead of RLock()
- [ ] go test -race ./internal/polecat/... passes
- [ ] Existing namepool tests still pass


Merge: gt-7923

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


Digest: mol-deacon-patrol

Patrol 3: all healthy


gt convoy create/status/list commands

CLI for convoy management in Gas Town.

Commands:
- gt convoy create <name> <issues...> [--molecule <mol>] [--notify <addr>]
- gt convoy status [id]
- gt convoy list

Creates convoy beads in town-level beads (hq-* prefix).
Convoys track issues across project chains.

Depends on: bd-3roq (tracks relation), bd-hj0s (convoy type)
Related: hq-7h8jx (Convoy System epic in town beads)


Witness patrol wisp with polecat leases

Witness should maintain a rolling patrol wisp that tracks active polecats:

```
wisp-witness-patrol
├── lease: furiosa (boot → working → done)
├── lease: nux (working)
└── lease: slit (done, closed)
```

When POLECAT_STARTED arrives:
- bd mol bond mol-polecat-lease wisp-patrol --var polecat=X

Patrol loop iterates leases:
- gt peek $polecat
- If idle: gt nudge
- If shutdown received: close lease

When all leases closed:
- bd mol squash wisp-xxx --summary='N polecats processed'

Requires mol-polecat-lease proto definition.


Test polecat lifecycle end-to-end

Test the full polecat lifecycle:

1. **Spawn**: Sling a test issue to a polecat
```bash
bd create --title="Test issue for polecat" --type=task
gt sling gt-<id> gastown
```

2. **Execute**: Watch polecat work
```bash
tmux attach -t gt-gastown-<name>
```

3. **Complete**: Polecat should:
   - Close the issue: `bd close gt-<id>`
   - Commit work: `git commit && git push`
   - Send POLECAT_DONE mail to witness

4. **Cleanup**: Witness should:
   - Receive POLECAT_DONE
   - Verify git state clean
   - Kill session and remove worktree

Success: Issue closed, PR merged, polecat cleaned up.


Deacon Kernel: event processing molecule architecture

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

## Core Insight

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

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

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

## Key Design Decisions

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

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

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

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

## Architecture

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

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

## Relation to Existing Work

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


Digest: mol-deacon-patrol

P17: stable


Design: Failure modes and recovery

Document failure modes and recovery strategies for Gas Town operations.

## Critical Failure Modes

### 1. Agent Crash Mid-Operation

**Scenario**: Polecat crashes while committing, Witness crashes while verifying

**Detection**:
- Session suddenly gone (tmux check fails)
- State shows 'working' but no session
- Heartbeat stops (for Witness)

**Recovery**:
- Doctor detects via ZombieSessionCheck
- Capture any recoverable state
- Reset agent state to 'idle'
- For Witness: auto-restart via supervisor or manual gt witness start

### 2. Git State Corruption

**Scenario**: Merge conflict, failed rebase, detached HEAD

**Detection**:
- Git commands fail
- Dirty state that won't commit
- Branch diverged from origin

**Recovery**:
- gt doctor reports git health issues
- Manual intervention recommended
- Severe cases: remove clone, re-clone

### 3. Beads Sync Conflict

**Scenario**: Two polecats modify same issue

**Detection**:
- bd sync fails with conflict
- Beads tombstone mechanism handles most cases

**Recovery**:
- Beads has last-write-wins semantics
- bd sync --force in extreme cases
- Issues may need manual dedup

### 4. Tmux Failure

**Scenario**: Tmux server crashes, socket issues

**Detection**:
- All sessions inaccessible
- "no server running" errors

**Recovery**:
- Kill any orphan processes
- tmux kill-server && tmux start-server
- All agent states reset to idle
- Re-spawn active work

### 5. Claude API Issues

**Scenario**: Rate limits, outages, context limits

**Detection**:
- Sessions hang or produce errors
- Repeated failure patterns

**Recovery**:
- Exponential backoff (handled by Claude Code)
- For context limits: session cycling (mail-to-self)
- For outages: wait and retry

### 6. Disk Full

**Scenario**: Clones, logs, or beads fill disk

**Detection**:
- Write operations fail
- git/bd commands error

**Recovery**:
- Clean up logs: rm ~/.gastown/logs/*
- Remove old polecat clones
- gt doctor --fix can clean some cruft

### 7. Network Failure

**Scenario**: Can't reach GitHub, API servers

**Detection**:
- git fetch/push fails
- Claude sessions hang

**Recovery**:
- Work continues locally
- Queue pushes for later
- Sync when connectivity restored

## Recovery Principles

1. **Fail safe**: Prefer stopping over corrupting
2. **State is recoverable**: Git and beads have recovery mechanisms
3. **Doctor heals**: gt doctor --fix handles common issues
4. **Emergency stop**: gt stop --all as last resort
5. **Human escalation**: Some failures need Overseer intervention

## Implementation

- Document each failure mode in architecture.md
- Ensure doctor checks cover detection
- Add recovery hints to error messages
- Log all failures for debugging


Add org formula examples and tests

Create example organizational formula hierarchy and tests.

## Examples

Create in .beads/formulas/:
- acme-base.formula.yaml: Company-wide (security-audit aspect)
- acme-frontend.formula.yaml: extends acme-base, adds accessibility-audit
- acme-backend.formula.yaml: extends acme-base, adds sql-injection-scan

## Tests

1. Three-level inheritance resolves correctly
2. Aspect merge order is correct
3. exclude-aspects removes parent aspect
4. Cooking org formula produces expected steps
5. Error on missing parent formula


Simplify and reorganize molecular chemistry docs

The Gas Town molecular chemistry docs (molecules.md, molecular-chemistry.md, molecule-algebra.md) are too dramatic and verbose. They need a simplification pass to be more reference-manual style.

**Current problems:**
1. Too much metaphor and philosophy, not enough practical reference
2. Start with the chemistry abstraction instead of what users care about (patrols, workflows)
3. Layer cake is presented top-down (formulas → protos → molecules) instead of bottom-up (issues → dependencies → execution)

**Proposed structure (inverted pyramid):**

1. **Start with execution semantics:**
   - Work = issues with dependencies
   - Dependencies control execution (blocks = sequential, no dep = parallel)
   - Agents traverse dependency graphs until blocked
   - Multi-day workflows via compound bonding

2. **Move to molecules:**
   - Molecules are just epics with workflow intent
   - Bonding = creating dependencies between work graphs
   - No protos required for ad-hoc workflows

3. **Then templates (protos/wisps):**
   - Phase metaphor for storage locations
   - pour/wisp/squash/burn operations
   - When to use each phase

4. **Finally formulas (for advanced users):**
   - YAML compile-time composition
   - extends, compose, aspects
   - Only needed for complex reusable patterns

**Style guidelines:**
- Reference manual with quick examples
- Written like agent priming (context they need to execute)
- Less dramatic, more straightforward
- TL;DR at top of each doc

**See also:**
- beads/docs/MOLECULES.md has a simplified version to use as template
- The execution model section explains multi-day traversal clearly


Deacon tmux status bar: heartbeat timing and patrol state

## Problem

The deacon shows patrol output but lacks real-time observability:
- No indication of when last heartbeat occurred
- No indication of when next heartbeat is scheduled
- No visibility into current patrol step

## Desired Behavior

The tmux status bar for gt-deacon should show:

```
⛪ Deacon | ❤ 2m ago | ⏰ 3m | 📍 health-scan (3/7)
```

Components:
- **Role icon**: ⛪ (deacon identity)
- **Last heartbeat**: ❤ 2m ago (time since last heartbeat file update)
- **Next heartbeat**: ⏰ 3m (time until daemon would poke)
- **Current step**: 📍 health-scan (3/7) (current patrol atom, step N of M)

## Implementation

1. **Read heartbeat.json** for last update time
2. **Calculate next poke** based on daemon interval (default 5m)
3. **Read current wisp** from .beads-wisp/ to get patrol progress
4. **Update tmux status** periodically or on state change

Options:
- tmux status-right with shell script
- gt deacon status --tmux for formatted output
- Hook into patrol step completion

## Related

- gt-id36: Deacon Kernel epic
- gt-3x0z: Wisp Molecule Integration


Digest: mol-deacon-patrol

Patrol 6: All agents healthy


Digest: mol-deacon-patrol

Patrol 9: All healthy


Digest: mol-deacon-patrol

Patrol 16: all clear


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.


Simplify mail wisps: remove dual-routing, use ephemeral flag

## Context

gt-lg66 implemented dual-inbox architecture with separate .beads-wisp/ directory.
This was over-engineered. See bd-bkul for the simpler approach.

## Work

Once beads implements bd-bkul (ephemeral flag in single db):

1. Revert/simplify router.go:
   - Remove resolveWispDir()
   - Remove shouldBeEphemeral() auto-detection (beads handles this)
   - Send() just passes --ephemeral flag to bd create

2. Revert/simplify mailbox.go:
   - Remove wispDir field
   - Remove listFromDir/getFromDir dual-source logic
   - Remove closeInDir dual-source logic
   - Single query, beads returns both types with Source marked

3. Keep in mail.go:
   - --ephemeral flag (passed through to bd)
   - (ephemeral) display indicator

4. Keep in spawn.go:
   - Ephemeral: true on lifecycle messages

## Depends On

bd-bkul: Simplify wisp architecture in beads


E2E Swarm Test Epic


Digest: mol-deacon-patrol

Patrol 8: healthy


Digest: mol-deacon-patrol

Patrol cycle 4: All healthy


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


Swarm: ephemeral rig support

PGT has ephemeral rigs for swarms - temporary worker groups that are destroyed after landing.

Missing Features:
- gt swarm init [--rig <name>|--git-url <url>] [--num-workers N]
- gt swarm worker add/remove/list <rig-id>
- gt swarm rigs - List ephemeral rigs
- gt swarm destroy <rig-id> - Destroy ephemeral rig

Directory structure:
<workspace>/mayor/workers/<rig-id>/
├── rig.json (metadata)
├── alice/ (git clone)
├── bob/
└── carol/

PGT Reference: gastown-py/src/gastown/ephemeral.py

Note: Beads issue gt-kmn.12 mentions this but implementation is missing.


gt session start --issue doesn't pin work to hook

When starting a polecat with --issue, the work isn't pinned to the hook:
  gt session start beads/Toast --issue bd-oxgi

After session starts, 'gt mol status' shows empty hook.

The polecat had to manually discover the work via bd ready. The --issue flag should call 'gt hook attach' or equivalent to pin the work.


Merge: keeper-1767074342207

branch: polecat/keeper-1767074342207
target: main
source_issue: keeper-1767074342207
rig: gastown


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

Patrol 1: All healthy, no actions


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


Digest: mol-deacon-patrol

Patrol 15: All clear


Digest: mol-deacon-patrol

Patrol 6: Quiet


Merge: gt-g44u.3

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


Deacon patrol refactor: detect → dispatch

Refactor Deacon patrol to delegate heavy work to dogs:
- check-conditions → sling to dog (not inline execution)
- Pool maintenance step (spawn/retire/refresh)
- Completion mail handling from dogs
- Keep patrol lightweight (detection only)


bd formula list/show commands

Implement formula management commands:

  bd formula list
  # Lists formulas from all search paths

  bd formula show rule-of-five
  # Shows formula details, steps, compose rules

Search paths (in order):
1. .beads/formulas/ (project)
2. ~/gt/.beads/formulas/ (town)
3. ~/.beads/formulas/ (user)
4. Built-in formulas (embedded)


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.


Step recovery: timeout and release

Implement recovery mechanism for stuck molecule steps.

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

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

## Implementation Options

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

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

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

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

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


Pick next branch. Rebase on current main.

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

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

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


Add charmbracelet TUI dependencies (bubbletea, bubbles)

Add charmbracelet TUI framework dependencies.

## Dependencies to add
```
go get github.com/charmbracelet/bubbletea
go get github.com/charmbracelet/bubbles
```

## Already have
- github.com/charmbracelet/lipgloss (styling)

## Verify
- go mod tidy
- go build ./...

Quick task, just adding deps.


Digest: mol-deacon-patrol

Patrol complete: inbox empty, 2 polecats working, all agents healthy


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


Polecat beads not synced with rig beads

When gt spawn assigns an issue to a polecat, the polecat cannot find the issue because:

1. Rig beads at /gt/gastown/.beads contains gt-th7
2. Polecat beads at /gt/gastown/polecats/dementus/.beads does NOT have gt-th7
3. bd sync in polecat dir pulls from beads-sync branch which doesn't have the new issue
4. Rig bd sync uses 'main' branch, causing a branch mismatch

The polecat gets a work assignment for an issue it literally cannot see.

## Root Cause

- Rig beads and polecat beads are separate git-tracked copies
- No mechanism to propagate newly created issues from rig to polecats before assignment
- Sync branch configuration mismatch between rig and polecats

## Fix Options

1. gt spawn should sync rig beads before assigning work
2. gt spawn should sync polecat beads after assignment
3. Use shared beads (symlink or same DB) instead of copies
4. Push new issues immediately on create

Note: This sync mismatch is resolved by gt-9nf (fresh polecats). Rather than fixing sync between stale clones, we'll always create fresh worktrees. This issue documents the root cause for posterity.


Deacon: Hierarchical health-check orchestrator

Replace daemon heartbeats with Deacon - an AI agent that keeps Gas Town running.

## Core Concept

The Deacon is a **Claude agent** (not just a Go process) that:
- Lives at town root in gt-deacon session
- Has its own mailbox (deacon/ identity in town beads)
- Is poked by a minimal Go daemon every ~60s (if idle)
- Monitors Mayor and Witnesses proactively
- Handles lifecycle requests (restart/cycle) from Mayor, Witnesses, AND Crew

## Architecture

```
Minimal Go Daemon (just watches Deacon)
         |
         v
     Deacon (Claude agent)
         |
    +----+----+
    v         v
  Mayor    Witnesses --> Polecats (Witness-managed)
    |         |
    +----+----+
         |
      Crew (lifecycle only, not monitored)
```

## Deacon Responsibilities

**Proactive monitoring:**
- Mayor health (tmux session, keepalive freshness)
- Witness health (tmux sessions, keepalive freshness)

**Reactive lifecycle:**
- Process restart/cycle requests from Mayor, Witnesses, Crew
- Kill session, create new, prime, verify startup

**Escalation:**
- Mail human (configurable) if issues can't be resolved

## Key Files

- ~/gt/DEACON.md - Role context
- ~/gt/deacon/ - State directory
  - heartbeat.json - Written each wake cycle (daemon checks this)
  - state.json - Health tracking, last scan results
- ~/gt/.beads/ - Town beads with deacon/ mail identity

## Wake Cycle

1. Write heartbeat (prevents daemon from poking)
2. Check mail (lifecycle requests)
3. Quick health scan (Mayor, Witnesses)
4. Process lifecycle requests
5. Remediate unhealthy agents
6. Update state

## Session Patterns

- Deacon: gt-deacon
- Mayor: gt-mayor
- Witness: gt-<rig>-witness
- Crew: gt-<rig>-<name> (e.g., gt-gastown-max)

## Relation to Policy Beads

Future: Deacon behavior configurable via policy beads (gt-3zw).
Initial implementation uses sensible defaults.


Witness startup: bond mol-witness-patrol on start

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

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

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


Digest: mol-deacon-patrol

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


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

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

## Two Points to Add Instructions

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

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

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

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

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


Phase 2.1: gt spawn --molecule bonds in ephemeral

Make gt spawn molecule-aware with ephemeral bonding.

## New Flag

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

## Behavior

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

## Work Assignment Mail

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

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

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

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


Digest: mol-deacon-patrol

Patrol #5: Routine - 6 sessions healthy


Replace WaitForClaudeReady with gt peek for steady-state agent observation

## Problem

WaitForClaudeReady uses regex to detect Claude's prompt, which is a ZFC violation.

## Architectural Fix

**Bootstrap (ZFC violation acceptable):**
During cold town startup, no AI is available. Regex to get Deacon online is acceptable.

**Steady State (proper ZFC):**
Once any agent is running, AI should observe AI:
- Deacon starting polecats → Deacon uses gt peek
- Deacon restarting → Mayor watches via gt peek
- Mayor restarting → Deacon watches via gt peek

## Implementation

1. Keep WaitForClaudeReady for daemon bootstrap only
2. Update gt deacon trigger-pending to use gt peek
3. Document bootstrap vs steady-state distinction


Polecat-Witness protocol handlers

Implement handlers for: WORK_COMPLETE (polecat→witness: done with issue), NEED_HELP (polecat→witness: stuck, requesting intervention). Witness patrol steps should parse and act on these.


GT_ROLE env var not set correctly for crew workers


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

Patrol 13: All healthy


Day 2.1: Daemon reads agent bead state field

Daemon queries agent beads for state instead of inferring from PID/tmux.

Change getAgentState() to:
1. Look up agent bead by ID (gt-mayor, gt-witness, etc.)
2. Read state field (idle|running|stuck|stopped)
3. Trust the value

Parent: gt-d0jqp


ensure-beads-refinery

Start the beads rig Refinery.

## Action
```bash
gt refinery start beads
```

## Verify
1. Session exists: `tmux has-session -t beads-refinery 2>/dev/null`
2. Not stalled: `gt peek beads/refinery` does NOT show "> Try" prompt
3. Queue processing: Refinery can receive merge requests

## OnStall
```bash
gt nudge beads/refinery "Start patrol."
sleep 30
# Re-verify
```

## OnFail
Log error. Completed polecat work will queue until Refinery recovers.


Digest: mol-deacon-patrol

Patrol 15: Routine


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


Witness: active polecat monitoring and nudging

## Problem
Witness runs but doesn't actively monitor polecats during patrol.

## ZFC-Compliant Solution
Add step to `mol-witness-patrol.formula.toml`:

```toml
[[step]]
id = "survey-polecats"
title = "Survey polecat health"
description = """
For each polecat in this rig:
1. Run: gt peek <rig>/<polecat> 20
2. Check if work is pinned: gt mol status --rig <rig>/<polecat>
3. If pinned but idle (no recent output), nudge: gt nudge <rig>/<polecat> "You have work on hook"
4. If stuck >15min with no progress, escalate to Mayor via mail
"""
depends_on = ["inbox-check"]
```

## Why This Works
- Witness Claude agent reads formula step
- Agent decides what "stuck" means (uses judgment)
- Agent runs gt commands (CLI can exist)
- No Go code makes decisions

## Files
- formulas/mol-witness-patrol.formula.toml


Digest: mol-deacon-patrol

Patrol 3: all clear, hook fix applied


Phase 5.1: Document ephemeral architecture

Update docs with ephemeral molecule architecture.

## Files to Update

### docs/architecture.md
- Add Ephemeral Molecules section
- Diagram: Proto → Ephemeral → Digest flow
- Explain inversion of control for summaries

### docs/molecules.md (new or update)
- Proto molecule catalog
- Ephemeral vs main beads
- Molecule lifecycle
- Summary generation guidelines

### Agent CLAUDE.md files
- Polecat: molecule workflow protocol
- Deacon: patrol cycle pattern
- Witness: patrol cycle pattern
- Refinery: patrol cycle pattern

## Key Concepts to Document

1. Everything is a molecule
2. Orchestration molecules are ephemeral
3. Only digests reach main beads
4. Agents generate their own summaries
5. Crash recovery via ephemeral persistence


gt-mayor

gt-mayor

role_type: mayor
rig: null
agent_state: stopped
hook_bead: null
role_bead: gt-mayor-role
cleanup_status: null


Merge: gt-70b3

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


Merge: gt-99a

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


Test Polecat Arm

Test child for bonding pattern


Account management for multi-account Claude Code usage

Enable Gas Town to manage multiple Claude Code accounts (e.g., personal vs work) with easy switching. Core mechanism: CLAUDE_CONFIG_DIR env var per account. See docs/design/account-management.md for full design.


Digest: mol-deacon-patrol

Patrol 4: Routine


Digest: mol-deacon-patrol

Patrol 2: all clear, 9 sessions healthy


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

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


Verify witness patrol loop executes

Verify witnesses run their patrol formula:

1. Check witness session: `gt session status gastown/witness`
2. If not running: `gt session start gastown/witness`
3. Watch witness: `tmux attach -t gt-gastown-witness`
4. Verify it cycles through:
   - inbox-check
   - process-cleanups
   - check-refinery
   - survey-workers
   - context-check
   - loop-or-exit

Test for both rigs: gastown and beads.


Digest: mol-deacon-patrol

Patrol 10: Routine


bd pin command fails: invalid field for update: pinned

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

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

Expected: Issue should be pinned.

This blocks gt mail send --pinned from working.


Refinery: Send MERGED notification to witness after merge

During liftoff test (gt-j0gx2), observed that refinery:
1. Received MERGE_READY for nux
2. Fetched, rebased, resolved conflicts correctly
3. Ran tests (passed)
4. Merged to main and pushed
5. Deleted polecat branch

But did NOT:
- Send MERGED mail to witness
- Close the MR bead (gt-5qkah)

The mol-refinery-patrol.formula.toml documents this step but the agent didn't execute it.

Impact:
- Witness never receives MERGED signal
- Polecat worktree never gets nuked
- MR beads accumulate as open

Fix:
After successful merge+push, refinery must:
```bash
gt mail send <rig>/witness -s "MERGED <polecat>" -m "..."
bd close <mr-bead> --reason "Merged to main"
```


test message


Digest: mol-deacon-patrol

Patrol 12: all healthy


Digest: mol-deacon-patrol

Patrol 19: all quiet


Digest: mol-deacon-patrol

Patrol 13: Nominal


Digest: mol-deacon-patrol

Patrol cycle 15: routine


Merge: gt-ldk8

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


Cooking metadata: source tracing

Add source_formula and source_location metadata to cooked steps so you can trace a step in a proto back to its origin formula. Useful for debugging complex compositions.


Merge: dementus-1767074970788

branch: polecat/dementus-1767074970788
target: main
source_issue: dementus-1767074970788
rig: gastown


Consolidate configuration architecture: three-tier model

Rationalize Gas Town's scattered configuration into a clean three-tier model:

## Current Problems
- Config split across mayor/, .gastown/, and rig/config.json
- Runtime state mixed with configuration
- Hidden directories (.gastown/) are missed by agents
- Category confusion: identity vs config vs runtime state

## Proposed Architecture

### Tier 1: Town Config (mayor/)
```
mayor/
├── town.json      # Town identity (unchanged)
├── rigs.json      # Rig registry (unchanged)
└── config.json    # NEW: Town-level configuration
    # theme defaults, daemon settings, etc.
```

### Tier 2: Town Runtime (.runtime/)
```
~/gt/.runtime/                    # NEW: Gitignored
├── daemon.json                   # {pid, started_at, heartbeat}
├── deacon.json                   # {cycle, last_action}
└── agent-requests.json           # Lifecycle requests
```

### Tier 3: Rig Config (settings/)
```
<rig>/
├── config.json                   # Rig identity only (type, git_url, beads.prefix)
├── settings/                     # NEW: Visible, git-tracked
│   ├── config.json               # theme, merge_queue, max_workers
│   ├── namepool.json             # pool settings (style, max)
│   └── roles/                    # Per-role overrides (optional)
└── .runtime/                     # NEW: Gitignored
    ├── witness.json              # {state, started_at, stats}
    ├── refinery.json             # {state, started_at, stats}
    └── namepool-state.json       # {in_use, overflow_next}
```

## Key Principles
1. **Visible > Hidden** for config agents need to find
2. **Git-tracked** for identity and config; **gitignored** for runtime
3. **Separation of concerns**: identity, config, runtime are distinct
4. **Locality**: town config at town root, rig config at rig root

## Migration
- Move .gastown/ contents to appropriate new locations
- Update all code that reads/writes these files
- Update .gitignore patterns
- Update documentation (architecture.md, CLAUDE.md)


mol-witness-patrol molecule definition

Create mol-witness-patrol in builtin_molecules.go.

## Steps (10 total)
1. inbox-check - Process witness mail (lifecycle, help requests)
2. load-state - Read handoff bead, get nudge counts
3. survey-workers - gt polecat list, categorize by status
4. inspect-workers - tmux capture-pane for each 'working' polecat
5. decide-actions - Apply nudge matrix, queue actions
6. execute-actions - Nudge, kill, or escalate as decided
7. save-state - Update handoff bead with new states
8. generate-summary - Summarize cycle for digest
9. context-check - Check own context usage
10. burn-or-loop - Squash wisp, then loop or cycle session

## Key Behaviors
- Uses wisp storage (.beads-wisp/)
- Reads/writes witness handoff bead for state persistence
- Progressive nudging (3 levels before escalate)
- Pre-kill verification before killing polecats

## Reference
- See prompts/roles/witness.md for protocol details
- See mol-refinery-patrol for similar structure
- Parent epic: gt-aqd8


Digest: mol-deacon-patrol

Patrol 8: Witnesses healthy


Digest: mol-deacon-patrol

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


Work on ga-rd4: Add gt polecat status command. Show detai...

Work on ga-rd4: Add gt polecat status command. Show detailed polecat status including current issue, session state, last activity time. When done, submit MR (not PR) to integration branch for Refinery.


Digest: mol-deacon-patrol

Patrol 11: OK


Digest: mol-deacon-patrol

Patrol 20: routine, handoff triggered


Crew worker grip in gastown - human-managed persistent workspace.

role_type: crew
rig: gastown
agent_state: idle
hook_bead: null
role_bead: gt-crew-gastown-grip-role
cleanup_status: null


bd ready --type: missing type filter for MQ integration

bd ready lacks --type flag that engineer.go expects.

## Code (internal/refinery/engineer.go)
readyMRs, err := e.beads.ReadyWithType("merge-request")

## Actual bd ready
No --type flag - only supports assignee, label, priority filters.

## Impact
Refinery can't find merge-requests in queue, so MQ doesn't process anything.

## Fix Options
1. Add --type flag to bd ready
2. Use bd list --type=merge-request --status=open instead
3. Both (ready filters for unblocked, list for all)

This is blocking the entire MQ pipeline.


Witness: detect polecat completion and trigger merge

## Problem
When polecat pushes completed work, witness needs to detect it and signal refinery.

## ZFC-Compliant Solution
Add step to `mol-witness-patrol.formula.toml`:

```toml
[[step]]
id = "check-ready-branches"
title = "Check for branches ready to merge"
description = """
1. List polecat branches: git branch -r | grep origin/polecat/
2. For each branch ahead of main:
   - Check if polecat session is done (no session or at handoff prompt)
   - Check if bead is closed: bd show <bead-id>
3. For ready branches, mail refinery:
   gt mail send <rig>/refinery -s "MERGE_READY: polecat/<name>" -m "Branch ready for merge"
"""
depends_on = ["survey-polecats"]
```

## Alternative: Polecat signals explicitly
Add to polecat workflow (end of work):
```
gt mail send <rig>/refinery -s "MERGE_READY" -m "polecat/<name> ready"
gt mail send <rig>/witness -s "WORK_COMPLETE" -m "<bead-id> done"
```

## Why This Works
- Witness/polecat agents make decisions
- Mail is the coordination primitive
- No daemon code needed

## Files
- formulas/mol-witness-patrol.formula.toml
- formulas/mol-polecat-work.formula.toml (add completion signal)


Review: Witness patrol template changes (gt-h1n5)

Polecat rictus implemented significant witness.md.tmpl changes: banners, wisp-based execution, handoff bead attachment, loop-or-exit, patrol summary. Review for correctness and consistency with deacon pattern.


gt crew at: auto-prime when exec'ing Claude in-session

When running 'gt crew at <name>' from inside the target session, we exec Claude directly. But this means we can't send 'gt prime' afterward since we ARE the process.

Possible solutions:
1. Claude startup hook that runs gt prime
2. Pass prompt as argument to claude CLI
3. Wrapper script approach

Related: crew resume prompt also can't be sent in this path.


Digest: mol-deacon-patrol

Patrol 12: All agents healthy


Refactor: Eliminate state.json, use beads assignee field for polecat state

## Problem

state.json in each polecat worktree duplicates data that should be in beads:
- `issue` field duplicates assignment (should be issue.assignee)
- `state` field duplicates status (derivable from issue.status)
- `name/rig/branch/clone_path` are all derivable from filesystem/git

This violates 'Beads as the data plane' (HOP Decision 001).

## Current State

```json
{
  "name": "Angharad",        // Derivable: basename(cwd)
  "rig": "gastown",          // Derivable: parent dir name
  "state": "working",        // Should be: issue.status
  "clone_path": "/path",     // Derivable: cwd
  "branch": "polecat/X",     // Derivable: git branch
  "issue": "gt-xyz",         // Should be: issue.assignee
  "timestamps": "..."        // Should be in beads history
}
```

## Target State

**Polecat identity**: Derived from worktree path
**Polecat assignment**: `issue.assignee = 'gastown/Angharad'`
**Polecat state**: Derived from issue.status (in_progress = working, closed = done)
**Polecat existence**: Worktree directory exists

## Benefits

1. Single source of truth (no sync issues)
2. Queryable: `bd list --assignee=gastown/Angharad`
3. History tracking via beads
4. Cross-agent coordination via beads sync
5. Simpler code (no state file management)

## Implementation

1. Update `gt spawn` to set issue.assignee instead of writing state.json
2. Update polecat list/status commands to query beads
3. Update Witness to query beads for polecat state
4. Remove state.json read/write code
5. Keep state.json as optional bootstrap cache (perf optimization only)


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


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 12: Quick cycle, all quiet


Add crew session pattern to lifecycle handling

Update identityToSession and restartSession to handle crew patterns: gastown/max -> gt-gastown-max. Crew workers can request lifecycle but are not proactively monitored.


Merge: slit-1767059695613

branch: polecat/slit-1767059695613
target: main
source_issue: slit-1767059695613
rig: gastown


Digest: mol-deacon-patrol

Patrol 6: all clear, agents healthy


Digest: mol-deacon-patrol

Patrol 18: Mayor OK, 11 sessions


Clean up stale children on deacon handoff bead

The deacon handoff bead hq-8r8 has 7 stale children (hq-8r8.1 through hq-8r8.7) from a previous patrol that got incorrectly parented. These should be cleaned up.

The handoff bead should only contain the attached_molecule reference, not instantiated steps.


loop-or-exit

Decision: burn and loop if context low, exit for respawn if context high.


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

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol complete: 0 messages, agents healthy, no cleanup needed


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


Nested loop support in control flow

Currently, nested loops (a loop inside a loop body) are not supported.

In expandLoopIteration(), the Loop field is intentionally not copied:
```go
// Note: Loop field intentionally not copied - nested loops need explicit support
```

To support nested loops:
1. After expanding the outer loop iteration, recursively call ApplyLoops on the body steps
2. Handle nested iteration ID prefixing (e.g., `outer.iter1.inner.iter2.step`)
3. Add tests for nested loop scenarios

This is a lower-priority enhancement since most real-world formulas use single-level loops.


Bubbletea TUI dashboard

Interactive TUI using Charm Bubbletea. Root model, view routing, Lipgloss theme. Includes: dashboard, rig explorer, polecat detail with live tail, mail view, beads view, spawn wizard.


Merge Queue in Beads: Universal chit system for all work

**Design doc**: docs/merge-queue-design.md


Prompt templates: role contexts, nudge messages

Prompt templates for role contexts and messages.

## Template Types

### Role CLAUDE.md Templates

For gt prime and CLAUDE.md generation:
- Mayor template
- Witness template
- Refinery template
- Polecat template

### Message Templates

For notifications and nudges:
- Spawn assignment
- Witness nudge
- Escalation to Mayor
- Session handoff

## Implementation

```go
//go:embed templates/*.md.tmpl
var templateFS embed.FS

type Templates struct {
    fs embed.FS
}

func NewTemplates() *Templates
func (t *Templates) RenderRole(role string, data RoleData) (string, error)
func (t *Templates) RenderMessage(name string, data any) (string, error)
```

## Template Data

```go
type RoleData struct {
    Role      string
    RigName   string
    TownRoot  string
    Polecats  []string
    Commands  []CommandHelp
}

type SpawnData struct {
    Issue       string
    Title       string
    Priority    int
    Description string
}

type NudgeData struct {
    Polecat    string
    Reason     string
    NudgeCount int
    MaxNudges  int
}
```

## Template Location

Embedded in binary via go:embed:
```
internal/templates/
├── roles/
│   ├── mayor.md.tmpl
│   ├── witness.md.tmpl
│   ├── refinery.md.tmpl
│   └── polecat.md.tmpl
└── messages/
    ├── spawn.md.tmpl
    ├── nudge.md.tmpl
    └── escalation.md.tmpl
```

## Usage

gt prime uses role templates.
gt spawn uses spawn template.
Witness uses nudge/escalation templates.


gt sling: Set status=hooked instead of pinned

Update gt sling to use the new 'hooked' status when attaching work to an agent's hook.

## Current Behavior

gt sling sets status='pinned' on the work item.

## New Behavior

gt sling sets status='hooked' on the work item.

## Files to Update

- internal/cmd/sling.go (or wherever sling logic lives)
- Any tests that expect pinned status after sling

## CROSS-RIG DEPENDENCY

**BLOCKED BY: bd-s00m.1** (Schema: Add 'hooked' as valid status)

This task cannot start until the beads schema change is deployed. The 'hooked' status must be valid before gt can use it.

## Verification

1. gt sling gt-xyz gastown/furiosa
2. bd show gt-xyz → status should be 'hooked'
3. bd ready should NOT show gt-xyz's dependents


gt-gastown-polecat-keeper

gt-gastown-polecat-keeper

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 complete: no callbacks, agents healthy, 1 stale lock cleaned


gt spawn: beads sync warnings on fresh worktree

When spawning fresh polecats, seeing 'Warning: beads sync: exit status 1' and 'Warning: beads push: exit status 1'. Worktrees are created but beads state may not be properly synced.


Add crew lifecycle support to Deacon

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

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

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

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

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

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


Add agent abstraction layer to support non-Claude agents

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

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

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


request-shutdown

Send shutdown request to Witness.
Wait for termination.

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

Depends: generate-summary


Digest: mol-deacon-patrol

Patrol 10: Quick cycle, 50% to handoff


Digest: mol-deacon-patrol

Patrol 19: routine


Digest: mol-deacon-patrol

Patrol 14: all clear


Digest: mol-deacon-patrol

Patrol 4: stable


Digest: mol-deacon-patrol

Patrol 13: Routine


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.


Spawn --molecule integration

Implement gt spawn --molecule flag for molecule-based polecat workflows.

Usage: gt spawn --issue gt-xyz --molecule mol-gastown-polecat

Behavior:
1. Validate molecule exists and is well-formed
2. Create molecule instance (child beads) under the issue
3. Find first ready step(s) in the instance
4. Spawn polecat with first ready step as initial work

Implementation:
1. Add --molecule flag to spawn command
2. Call molecule.Instantiate()
3. Query ready steps from instance
4. Pass first ready step to polecat context

Files: internal/cmd/spawn.go

Acceptance:
- --molecule flag works
- Creates proper molecule instance
- Polecat starts on first ready step
- End-to-end test with actual polecat


Use Beads issue status as spawn source of truth

Currently witness tracks spawned issues in .gastown/witness.json to prevent re-spawning:

{"spawned_issues": ["gt-abc1", "gt-def2", ...]}

This is redundant with Beads issue status (in_progress). The witness could query:
  bd list --status=in_progress

The local list serves as a performance cache to avoid querying beads every cycle. Consider:
1. Use Beads as source of truth
2. Keep local cache with TTL
3. Sync cache on witness start

This simplifies state management and ensures consistency.


Bug: bd update --parent doesn't persist

The --parent flag on bd update reports success but doesn't persist the parent field.

**Confirmed behavior:**
```bash
bd update gt-9kp3t --parent=gt-d0jqp --no-daemon -v
# Output: ✓ Updated issue: gt-9kp3t

bd show gt-9kp3t --json | jq '.[0].parent'
# Returns: null

grep gt-9kp3t .beads/issues.jsonl | head -1 | jq '.parent'
# Returns: null (field not present)
```

**Root cause hypothesis:**
The update handler processes the --parent flag but either:
1. Doesn't write it to the Issue struct
2. Doesn't include it in the JSONL export
3. The field name doesn't match what export expects

**Files to check:**
- cmd/bd/show.go:638-640 (flag handling)
- cmd/bd/show.go:810-814 (reparenting logic)
- internal/rpc/server_issues_epics.go (RPC handler)
- internal/rpc/protocol.go (UpdateArgs.Parent field)

Related: bd-cj2e (the original feature implementation)


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

## Problem

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

## Current Behavior

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

## What Works

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

## Proposed Fix

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

## Considerations

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

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


Check own context limit

Check own context limit.

The Deacon runs in a Claude session with finite context. Check if approaching the limit:

```bash
gt context --usage
```

If context is high (>80%), prepare for handoff:
- Summarize current state
- Note any pending work
- Write handoff to molecule state

This enables the Deacon to burn and respawn cleanly.


Merge polecat/slit: docs bd mol bond/squash/burn CLI

Branch: polecat/slit

## Summary
Added comprehensive CLI reference documentation for the three molecule lifecycle commands to molecules.md:

- **bd mol bond**: Instantiate proto into Mol (durable) or Wisp (ephemeral)
- **bd mol squash**: Complete molecule and generate digest
- **bd mol burn**: Abandon molecule without digest

Includes argument tables, behavior descriptions, examples, and a lifecycle diagram showing the steam engine metaphor mapping.

Closes: gt-odvf


Swarm status doesn't update after task completions

During swarm bd-784c, the swarm status remained at '9% (1/11 tasks merged)' even after 5 tasks were completed and closed.

**Expected:**
Swarm status should reflect actual completion count, either by:
1. Polling issue status from beads
2. Updating when polecats close issues
3. Updating when commits are pushed to integration branch

**Impact:**
Coordinator had to manually check bd list to know actual progress.


Digest: mol-deacon-patrol

Patrol OK: 0 polecats, 6 core sessions up, town quiet


Implement

Implement the solution for gt-8tmz.10. 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.


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 16: Quiet


Patrol exponential backoff for cost savings

Agent-side backoff state tracking in patrol molecules.

## State

Each patrol agent tracks:
- current_interval: current sleep duration (starts at base)
- idle_cycles: count of cycles with no work found

## Backoff Logic

```
on await-signal timeout (no nudge):
    idle_cycles++
    current_interval = min(base * 2^idle_cycles, max_interval)

on nudge received:
    idle_cycles = 0
    current_interval = base
    wake immediately

on work found:
    idle_cycles = 0
    current_interval = base
```

## Where State Lives

Options:
A. Agent bead fields (persistent across sessions)
B. In-memory during patrol (resets on restart)
C. Wisp fields (ephemeral but survives handoff)

Option B is simplest - backoff resets when agent restarts anyway.

## Depends On

- gt-l6ro3.3: await-signal step type


Merge: gt-caih

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


Digest: mol-deacon-patrol


Fanout gate runtime evaluation

Aggregate step uses waits-for: all-children. Deacon must evaluate this gate by checking if all dynamically-bonded arm children are complete. If no children bonded, gate passes immediately.


MQ summary in gt status

Add per-rig MQ summary to gt status output.

Current gt status shows:
  gastown
    refinery  ✓ running

Add:
  gastown
    refinery  ✓ running
    MQ: 4 pending, 1 in-flight

Implementation:
- Query beads for merge-request type issues
- Count by status (open=pending, in_progress=in-flight)
- Display in rig section


Git safety audit module

Port git_audit.py safety checks to Go.

## Purpose

Before destroying polecat clones, verify no code is at risk.

## Checks

1. Uncommitted changes (staged and unstaged)
2. Unpushed commits
3. Git stashes
4. Local-only branches (not tracking remote)

## Beads-Only Classification

Some files are safe to discard:
- .beads/*
- .claude/settings.json

Changes only affecting these = "beads-only" = safe to destroy.

## Interface

```go
type GitAuditResult struct {
    Polecat              string
    Path                 string
    UncommittedFiles     []string
    UncommittedIsBeadsOnly bool
    UnpushedCommits      int
    Stashes              []StashInfo
    StashesAreBeadsOnly  bool
    LocalBranches        []string
    Error                error
}

func (r *GitAuditResult) HasCodeAtRisk() bool

func AuditPolecat(name string, path string) *GitAuditResult
func AuditPolecats(polecats []PolecatInfo) []*GitAuditResult
```

## Reference

PGT: swarm/git_audit.py


Merge: slit-1767073379145

attached_args: Code review

branch: polecat/slit-1767073379145
target: main
source_issue: slit-1767073379145
rig: gastown


ensure-beads-witness

Start the beads rig Witness.

## Action
```bash
gt witness start beads
```

## Verify
1. Session exists: `tmux has-session -t beads-witness 2>/dev/null`
2. Not stalled: `gt peek beads/witness` does NOT show "> Try" prompt
3. Heartbeat fresh: Last patrol cycle < 5 min ago

## OnStall
```bash
gt nudge beads/witness "Start patrol."
sleep 30
# Re-verify
```

## OnFail
Log error. Rig polecats will be unmonitored until Witness recovers.


Digest: mol-deacon-patrol

Patrol complete: inbox empty, all agents healthy (mayor, 2 witnesses, 2 refineries), no orphans


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.


Add orphaned-attachments doctor check

Add gt doctor check for molecules attached to non-existent agents.

Check: orphaned-attachments
Warning if: Handoff bead exists for agent that no longer has worktree
Fix suggestion: Re-sling to active agent or close molecule


Merge: gt-h6eq.3

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


Expansion var overrides not implemented

ExpandRule.Vars field exists in schema but is ignored during expansion.

## Current State

The vars map in expand rules should override the expansion formula default variables, but currently the Vars field is not used.

## Implementation

1. In ApplyExpansions, pass rule.Vars to expandStep
2. Merge vars with formula defaults (rule.Vars wins)
3. Substitute vars in template placeholders
4. Add test: expansion with var overrides

## Files

- internal/formula/expand.go: ApplyExpansions, expandStep
- internal/formula/expand_test.go: add test case


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

Patrol 17


Hook system for event extensibility

GGT needs hook system for extensibility like PGT.

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

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

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

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

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

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

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

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

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

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

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


Phase 1.1: gt rig init creates .beads-ephemeral/

Add ephemeral beads repo creation to rig initialization.

## Implementation

In `gt rig init` (or equivalent setup):
1. Create `<rig>/.beads-ephemeral/` directory
2. Initialize as git repo
3. Create minimal beads config (no sync-branch needed)
4. Add to .gitignore if not already

## Config

```yaml
# .beads-ephemeral/config.yaml
ephemeral: true
# No sync-branch - ephemeral is local only
```

## Verification

```bash
gt rig init gastown
ls gastown/.beads-ephemeral/  # Should exist
```


gt sling command implementation

Implement the gt sling command for handoff with attached work.

## Command Signature

```bash
gt sling <bead-id> [-s subject] [-m message]
```

## Behavior

1. Validate the bead exists: bd show <bead-id>
2. Create wisp on agents hook:
   - File: .beads-wisp/hook-<agent>.wisp.yaml
   - Content: bead_id, slung_at, optional context
3. Optionally send handoff mail with subject/message
4. Exit cleanly (or signal restart)

## Wisp Format

```yaml
type: slung-work
bead_id: gt-xxx
slung_at: 2025-12-24T...
slung_by: crew/joe
context: "Optional message"
```

## Files to Create/Modify

- cmd/gt/sling.go (new)
- internal/wisp/wisp.go (may need creation)
- internal/wisp/hook.go (hook reading)

## Notes

- Keep it simple - this is connector code
- The wisp is burned after pickup
- Sling should work for any role (crew, polecat, deacon)


Refinery patrol: Add sibling-check step

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

## Implementation

1. Add `sibling-check` step to mol-refinery-patrol (early in cycle)
2. Use `gt peek <rig>/witness 20` to check Witness output
3. Look for signs of health:
   - Recent activity (tool calls, log messages)
   - No error stack traces
   - Responsive to pings
4. If Witness seems stuck:
   - Send status request mail
   - Note for potential escalation
   - Don't block Refinery work

## Acceptance
- Refinery checks Witness each patrol cycle
- Anomalies logged
- Non-blocking (Refinery continues even if Witness is down)


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

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

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


ZFC #7: Make message age filtering configurable

**ZFC Violation:** internal/daemon/lifecycle.go:64-75

Go hardcodes 6-hour max message age:
```go
const MaxLifecycleMessageAge = 6 * time.Hour
```

**ZFC-compliant solution:**
- Make this configurable per message type
- Or let agents decide message relevance
- Low priority - not blocking liftoff

Reference: ~/gt/docs/zfc-violations-audit.md #7


Witness: aggregate completion and notify Mayor

## Problem
No signal when swarm completes. Mayor must manually check.

## ZFC-Compliant Solution
Add step to `mol-witness-patrol.formula.toml`:

```toml
[[step]]
id = "check-swarm-completion"
title = "Report swarm completion to Mayor"
description = """
1. Check state.json for active swarm tracking:
   - swarm_id, start_time, assigned_beads[], completed_beads[]
2. For each completed merge (from cleanup step):
   - Add bead to completed_beads
3. If all assigned_beads are in completed_beads:
   - Calculate duration
   - Send summary to Mayor:
     gt mail send mayor/ -s "SWARM_COMPLETE: <swarm_id>" -m "All N polecats merged in Xm"
   - Clear swarm tracking from state.json
"""
depends_on = ["cleanup-merged-polecats"]
```

## Swarm Initialization
When Mayor slings a batch, they should:
```bash
# Mayor creates swarm tracking
gt mail send <rig>/witness -s "SWARM_START" -m '{"swarm_id": "batch-123", "beads": ["bd-a", "bd-b", "bd-c"]}'
```

Witness parses SWARM_START and initializes tracking in state.json.

## Why This Works
- Mayor initiates batch with SWARM_START mail
- Witness tracks progress in its own state
- Witness signals completion via mail
- All coordination is agent-to-agent via mail

## Files
- formulas/mol-witness-patrol.formula.toml


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

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


Digest: mol-deacon-patrol

Patrol 5: Quick cycle, all quiet


Refinery needs branch visibility: Convert to worktree or push branches

The refinery cannot see polecat branches because it is a separate clone, not a worktree.

## Problem
- Polecats are worktrees of mayor/rig (correct, per gt-4u5z)
- Refinery is still a separate clone at refinery/rig (not converted)
- Polecat branches are local to mayor/rig, invisible to refinery

## Decision (2025-12-25)
**Use a shared bare repo as the single git source of truth.**

```
gastown/
├── .repo.git/           ← Bare repo (no working tree, invisible base)
├── mayor/rig/           ← Worktree (human workspace)
├── polecats/
│   └── Toast/           ← Worktree on polecat/Toast
├── refinery/rig/        ← Worktree on main (can see all branches!)
└── crew/max/            ← Worktree or separate clone (TBD)
```

**Why bare repo:**
- No working directory to accidentally work in
- Standard git pattern (how servers work)
- All worktrees share branch visibility
- Refinery on main can merge polecat branches directly

## Implementation
1. Create `.repo.git` as bare clone
2. Convert refinery to worktree of .repo.git on main
3. Convert polecats to worktrees of .repo.git
4. Optionally convert mayor/rig (or keep as worktree)
5. Update gt rig init for new rigs
6. Write migration for existing rigs

## Evidence
Tracer bullet 2025-12-23: Refinery could not see polecat/tracer until pushed to origin.


Merge: dementus-1767073385126

branch: polecat/dementus-1767073385126
target: main
source_issue: dementus-1767073385126
rig: gastown


Crew worker max in gastown - human-managed persistent workspace.

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


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 complete: all healthy, cleaned 2 orphan processes + 3 stale locks


Digest: mol-deacon-patrol

Patrol 6: 9 sessions healthy


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

Patrol 3: all healthy, no work


gt mail send: add stdin and file input support for complex messages

Current -m flag is fragile with multi-line messages containing backticks, quotes, or shell special chars. Add: 1) --stdin flag to read message body from stdin (heredoc-friendly), 2) -f/--file flag to read from file. This sidesteps shell quoting nightmares for handoff messages with code samples.


Digest: mol-deacon-patrol

Patrol 4: nominal


Digest: mol-deacon-patrol

Patrol 11: nominal


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


Feed-Based Wake Model for Patrol Agents

## The Paradigm Shift

> **Backoff is not about polling less. It is about waiting for signals with a timeout.**

This epic captures the shift from time-driven to event-driven patrol wake.

## Key Insight (Recovered from 2025-12-29 session crash)

The Beads activity feed (bd activity --follow) is the universal data plane for
wake signals. Every meaningful action creates a beads mutation; agents subscribe to
the feed and wake within ~500ms. The timeout is just a safety net.

**OLD**: Time-driven. Wake because time passed. Poll less when idle.
**NEW**: Event-driven. Wake because signal arrived. Timeout is safety net.

## Architecture

ANY MUTATION:
  bd create/update/close -> issues.jsonl mutation
                        -> bd activity --follow sees it (~500ms)
                        -> All subscribed agents wake immediately

PATROL AGENT:
  Runs bd activity --follow as background source
  Any output line = wake signal
  Timeout expiry = safety net (not primary wake)

## Wake Sources (Priority Order)

| Source | Latency | Purpose |
|--------|---------|---------|
| Feed | ~500ms | Primary - any beads mutation |
| Nudge | Instant | Explicit wake (startup hooks) |
| Timeout | Backoff | Safety net for missed signals |
| Daemon | 5-60min | GUPP backstop, session recovery |

## Key Design Decisions

1. No wake_on conditions - signals carry no semantic meaning. Just wake up.
   Agent discovers reality by checking mail, beads, hook, git state.

2. Read-only commands do not wake - gt status, bd show create no mutations.

3. Daemon is recovery-focused - catches edge cases (GUPP violations, dead sessions).

4. Keepalive file deprecated - the feed replaces the keepalive mechanism.

## Reference

- ~/gt/docs/patrol-system-design.md (Sleeping Agents section)
- ~/gt/docs/PRIMING.md (Feed Is the Signal insight)

## Related

- gt-l6ro3: Patrol Exponential Backoff System (backoff timing, complements this)


Digest: mol-deacon-patrol

Patrol 14: all healthy


Digest: mol-deacon-patrol

Patrol 9: all quiet


Remove stale migration docs from gastown-py

The gastown-py repo has migration-related documentation that is now misinformation since we have made design decisions. Remove or clearly mark as obsolete: any docs about migration paths, old architecture assumptions, or superseded designs.


Merge: nux-1766959759345

branch: polecat/nux-1766959759345
target: main
source_issue: nux-1766959759345
rig: gastown


Digest: mol-deacon-patrol

Patrol 6: all quiet


Digest: mol-deacon-patrol

Patrol 13: All clear


Digest: mol-deacon-patrol

Patrol 11: All healthy


Digest: mol-deacon-patrol

Patrol 4: All healthy


Digest: mol-deacon-patrol

Patrol 12: Quiet


Add crew auto-start config to gt start

gt start needs a config file to specify:
- Which crew members auto-start per rig
- Which rigs to auto-start (instead of requiring --all or --rigs flags)

Requested defaults:
- beads rig: dave
- gastown rig: max, joe

This would allow 'gt start' to bring up the configured crew without manual flags.


gt mq integration land: merge integration branch to main

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

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

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

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

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


Messaging infrastructure: lists, queues, and channels

## Problem

Current messaging is point-to-point only:
- Mail: A → B (durable, polling)
- Nudge: A → B (ephemeral, real-time)

No support for broadcast, work queues, or pub/sub patterns.

## Design

### Addressing Syntax

| Prefix | System | Resolution | Storage | Claim? |
|--------|--------|------------|---------|--------|
| `agent/` | Mail | Direct | Recipient inbox | N/A |
| `@group` | Mail | Dynamic (filesystem) | Fan-out (N copies) | No |
| `list:name` | Mail | Static config | Fan-out (N copies) | No |
| `queue:name` | Mail | Static config | Shared (1 copy) | Yes |
| `announce:name` | Mail | Static config | Shared (1 copy) | No |
| `#channel` | Nudge | Dynamic (tmux) | None (ephemeral) | No |

### Dynamic Aliases

- `@rig/gastown` → scan ~/gt/gastown/ for agent dirs (witness, refinery, crew/*, polecats/*)
- `@town` → scan ~/gt/ for all agent dirs
- `@witnesses` → for each rig, include <rig>/witness
- `@crew/gastown` → scan ~/gt/gastown/crew/*
- `#rig/gastown` → scan tmux for gastown/* sessions
- `#town` → scan tmux for all Gas Town sessions

**Mail** resolves against filesystem (agents that exist)
**Channels** resolve against tmux (agents that are running)

### Static Config

Location: `~/gt/config/` (JSON format, machine-edited)

```json
// lists.json
{
  "oncall": ["mayor/", "gastown/witness", "beads/witness"],
  "cleanup/gastown": ["gastown/witness", "deacon/"]
}
```

### Work Queue Semantics

- `gt mail send queue:cleanup/gastown -s "Task" -m "..."`
- `bd update <msg-id> --claim` → atomic claim (sets assignee + in_progress)
- If already claimed, returns error "already claimed by X"

### Use Cases

1. **Polecat spawn**: `gt mail send @rig/gastown` → Witness + Deacon both notified
2. **Polecat cleanup**: `gt mail send queue:cleanup/gastown` → first to claim handles it
3. **Town broadcast**: `gt mail send @town -s "Handoff now"` → everyone gets copy
4. **Real-time alert**: `gt channel publish #witnesses "Swarm incoming"`

## Implementation Tasks

1. Create ~/gt/config/ directory structure
2. Implement dynamic alias resolution (filesystem scan)
3. Implement static list lookup (config/lists.json)
4. Add fan-out at send time for @/list: addresses
5. Implement queue: with shared storage
6. Add `bd update --claim` for work queue semantics
7. Implement channel resolution (tmux scan)
8. Add `gt channel publish` command


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


Digest: mol-deacon-patrol

Patrol 5: Nominal


Day 1.4b: Create Deacon agent bead

Create gt-deacon agent bead for the Deacon (daemon beacon).

The Deacon is a separate Claude Code agent from Mayor:
- Receives the ONLY mechanical daemon heartbeats in the system
- Runs town plugins, cleanup, and monitoring
- Has own top-level directory: ~/gt/deacon/
- No rig preassignment (town-level, like Mayor)
- role_type: deacon

Agent bead structure:
```yaml
id: gt-deacon
type: agent
entity_platform: gastown
entity_org: steveyegge
entity_id: deacon
hook_bead: null  # or current patrol wisp
role_bead: gt-deacon-role
state: idle
role_type: deacon
```

Must be created alongside mayor, witness, refinery agent beads.


Patrol System: Agent lifecycle loops with attachments

Enable Gas Town agents to run continuous patrols, survive crashes, and hand off work across sessions.

## Core Concepts
- **Attachments**: Molecules bound to agent's pinned bead until complete
- **Patrols**: Cyclic molecules that loop (deacon, witness, refinery)
- **Quiescent**: Agents that sleep until triggered (witness, refinery)

## Design Doc
See docs/patrol-system-design.md

## Phases
Phase 1: Foundation (attachment field, daemon detection, mol-deacon-patrol)
Phase 2: Quiescent Agents (wake triggers, witness/refinery patrols)
Phase 3: Callbacks and Plugins (mail protocol, plugin runner)
Phase 4: Polish (gt patrol status, metrics, tuning)

## Key Decisions
- Attachment as field on pinned bead (not edge type, for now)
- Mail-based orchestration for all callbacks
- Queue replacement for heartbeat delivery (not pile-up)
- Burn and respawn for patrol loops (not in-place reset)


Task D: Integration


Digest: mol-deacon-patrol

Patrol 11: all clear


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.


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


gt status: Show agent hook_bead (current work) in status output

attached_args: Implement the hook_bead display in gt status. The agent bead lifecycle is complete - just need to read hook_bead and display it.

## Current State

gt status shows agents with their agent_state from beads:
```
   crew/max       ✓ running [running]
```

## Enhancement

Show what each agent is working on (hook_bead field from agent beads):
```
   crew/max       ✓ running [running] → gt-abc (Fix auth bug)
   crew/joe       ✓ running [running] → gt-xyz (Add tests)
   refinery       ✓ running → refinery Handoff
```

This gives the overseer a quick view of:
1. Who is running
2. What each agent is working on
3. Progress at a glance

## Implementation

In status.go, when displaying agents:
1. Look up agent bead by ID (e.g., gt-crew-gastown-max)
2. Parse hook_bead from description
3. If hook_bead set, fetch its title for display
4. Show as: agent_state → hook_bead (title truncated)

## Related

The agent bead lifecycle is now complete:
- prime.go creates agent beads
- sling.go sets hook_bead
- done.go clears hook_bead and sets final state


Merge: valkyrie-1767074338488

attached_args: Code review

branch: polecat/valkyrie-1767074338488
target: main
source_issue: valkyrie-1767074338488
rig: gastown

CODE REVIEW (furiosa): REJECT

Issue: Commit modifies CLAUDE.md which is gitignored on main.

Findings:
1. CLAUDE.md is in .gitignore (clone-specific file)
2. CLAUDE.md is NOT tracked on main branch
3. Polecat manager has installCLAUDETemplate() to generate this file locally per worktree
4. The commit adds a file that should not be committed

Recommendation: Do not merge. CLAUDE.md should be locally generated, not committed.

This merge request has no valid changes to merge.


Slim down rig root config.json to identity only

Rig root config.json should only contain identity:
- type, version, name, git_url, beads.prefix, created_at
- Remove any behavioral config (move to settings/config.json)
- Update RigConfig type to separate identity from settings
- Ensure loader reads identity from root, settings from settings/


Digest: mol-deacon-patrol

Patrol complete: all agents healthy, no callbacks, cleaned 1 stale lock


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

Patrol 13: quiet


Swarm learning: Witness needs automated context cycling

Furiosa hit 2% context during swarm work. Witness role needs automated detection of low context and should trigger session cycling before agents get stuck. Add to Witness responsibilities in prompts.md.


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

Patrol 17: All healthy


Swarms of One: Lightweight single-worker task dispatch

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

## Context

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

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

## Design Questions

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

## Possible Interface

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

## Related

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


mol-polecat-lease: Semaphore proto for tracking polecat lifecycle

Define a small proto for tracking a single polecat in the Witness patrol wisp:

```markdown
## Molecule: polecat-lease
Semaphore tracking a single polecat's lifecycle.
Vars: {{polecat}}, {{issue}}

## Step: boot
Spawned. Verify it starts working.
gt peek {{polecat}} - if idle, gt nudge.
Timeout: 60s before escalation.

## Step: working
Actively working. Monitor for stuck.
Wait for SHUTDOWN mail.
Needs: boot

## Step: done
Exit received. Ready for cleanup.
Kill session, prune worktree.
Needs: working
```

Used by Witness: bd mol bond mol-polecat-lease wisp-patrol --var polecat=X


Add harness overview to Mayor priming

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


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

Patrol 15


test pin fix


Digest: mol-deacon-patrol

Patrol complete: town quiet, all healthy


Digest: mol-deacon-patrol

Patrol #1: No issues found. All 6 agents healthy. No orphans.


Digest: mol-deacon-patrol

Patrol 20: all quiet - handoff cycle


Digest: mol-deacon-patrol

Patrol 4: all healthy


Digest: mol-deacon-patrol

Patrol 7: all clear