mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor.

The Deacon's agent bead last_activity timestamp is updated during each patrol
cycle. Witnesses check this timestamp to verify health.


Add unit tests for internal/lock package

attached_args: Lock package tests

Add comprehensive tests for the file locking package.

## Files to create
- internal/lock/lock_test.go

## Test cases to cover
1. Lock() acquires lock successfully
2. Lock() blocks when lock held by another
3. Unlock() releases lock
4. TryLock() returns immediately if locked
5. IsLocked() detects lock state
6. Lock file contains correct PID
7. Stale lock detection (if implemented)

## Acceptance criteria
- [ ] internal/lock/lock_test.go created
- [ ] At least 5 test functions
- [ ] Tests pass: go test ./internal/lock/...
- [ ] Coverage > 70%: go test -cover ./internal/lock/...


gt sling support for deacon/dogs target

Extend gt sling to support dog targets:
- gt sling <work> deacon/dogs - Auto-dispatch to idle dog
- gt sling <work> deacon/dogs/alpha - Specific dog
- Dog pool management (spawn if needed, up to max)
- Completion mail handling


Merge: gt-o3is

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


Merge: gt-nq3pr

branch: polecat/testcat6-mjtkcfjm
target: main
source_issue: gt-nq3pr
rig: gastown


Test: Beads routing and redirects work correctly

Validate beads cross-rig routing:

## Test cases
- [ ] bd show <rig-prefix-id> routes to correct rig from town root
- [ ] bd show <hq-id> routes to town beads
- [ ] Redirect chains (.beads -> actual location) resolve correctly
- [ ] bd list works from polecat/crew directories
- [ ] Prefix conflicts detected

## Key scenarios
- Query from town root
- Query from rig directory
- Query from polecat worktree


Test patrol queue feature


Workspace doctor checks: Config, state, mail, Mayor, rigs

Workspace-level doctor checks.

## Checks

### ConfigExists
- Verify config/ directory exists
- Verify config/town.json exists
- Fix: Cannot auto-fix (need gt install)

### ConfigValid
- Parse town.json
- Verify required fields (type, version, name)
- Verify type == "town"
- Fix: Cannot auto-fix

### RigsRegistryValid
- Parse config/rigs.json
- Verify each registered rig directory exists
- Warn on missing rigs
- Fix: Remove missing rigs from registry

### MayorExists
- Verify mayor/ directory exists
- Verify mayor/CLAUDE.md exists
- Fix: Create from template

### MayorMailboxExists
- Verify mayor/mail/ directory exists
- Verify mayor/mail/inbox.jsonl exists (can be empty)
- Fix: Create directory and empty file

### MayorStateValid
- Parse mayor/state.json if exists
- Verify valid JSON
- Fix: Reset to default state

## Implementation

```go
var WorkspaceChecks = []Check{
    &ConfigExistsCheck{},
    &ConfigValidCheck{},
    &RigsRegistryValidCheck{},
    &MayorExistsCheck{},
    &MayorMailboxExistsCheck{},
    &MayorStateValidCheck{},
}
```


Digest: mol-deacon-patrol

Patrol 2: all healthy, beads swarm active


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 229/1023)


Merge: nux-1767080427756

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


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)


Merge: rictus-mjtlq9xg

branch: polecat/rictus-mjtlq9xg
target: main
source_issue: rictus-mjtlq9xg
rig: gastown


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 157/1023)


Digest: mol-deacon-patrol

Patrol 3: All healthy, 2 convoys in progress


Burn-or-loop step execution

Fix step completion detection in patrol formulas. Ensure done() properly clears the molecule and triggers burn. Currently wisps accumulate because this step doesn't complete.


Merge: organic-mjwjck2f

branch: polecat/organic-mjwjck2f
target: main
source_issue: organic-mjwjck2f
rig: gastown
agent_bead: gt-gastown-polecat-organic


Digest: mol-deacon-patrol

Patrol 89: All healthy


Merge: furiosa-mjxc7zq9

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


Merge: capable-mq

branch: polecat/capable-mq-events
target: main
source_issue: capable-mq
rig: gastown


Digest: mol-deacon-patrol

Patrol 3: Quiet


Digest: mol-deacon-patrol

Patrol 12: All healthy.


Mayor handoff mail template

Add MAYOR_HANDOFF mail template to templates.py.

## Template Function

def mayor_handoff(
    active_swarms: List[SwarmStatus],
    rig_status: Dict[str, RigStatus],
    pending_escalations: List[Escalation],
    in_flight_decisions: List[Decision],
    recent_actions: List[str],
    delegated_work: List[DelegatedItem],
    user_requests: List[str],
    next_steps: List[str],
    warnings: Optional[str] = None,
    session_duration: Optional[str] = None,
) -> Message:
    metadata = {
        'template': 'MAYOR_HANDOFF',
        'timestamp': datetime.utcnow().isoformat(),
        'session_duration': session_duration,
        'active_swarm_count': len(active_swarms),
        'pending_escalation_count': len(pending_escalations),
    }
    # ... format sections ...
    return Message.create(
        sender='mayor/',
        recipient='mayor/',
        subject='Session Handoff',
        body=body,
        priority='high',
    )

## Metadata Fields

- template: MAYOR_HANDOFF
- timestamp: ISO format
- session_duration: Human readable
- active_swarm_count: Number of active swarms
- pending_escalation_count: Number of escalations

## Mail Priority

Use priority='high' to ensure handoff is seen on startup.


Digest: mol-refinery-patrol

Patrol: No MRs in queue. Found 1 pending branch (gt-jq8i4) without MR bead. 8 stale branches detected (work already on main).


Digest: mol-deacon-patrol

Patrol 19: All clear


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor.

The Deacon's agent bead last_activity timestamp is updated during each patrol
cycle. Witnesses check this timestamp to verify health.


mol-gastown-boot: Mayor-driven town bootstrap

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

## Vision

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

## Key Principles

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

## Proto Catalog

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

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

## mol-gastown-boot Structure

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

## Step Template

Each step in description includes action + verification:

```markdown
## Step: ensure-deacon

### Action
gt deacon start

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

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

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

## Event-Triggered Lifecycle

Beyond manual slinging, events can trigger lifecycle wisps:

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

## Parameterization (Future)

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

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

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

## Related Issues

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


mol-deacon-patrol


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 653/1023)


SQL injection in convoy.go:581

User input interpolated directly into SQL via fmt.Sprintf(). convoyID from args[0] used in: fmt.Sprintf(`SELECT depends_on_id, type FROM dependencies WHERE issue_id = '%s'`, convoyID). Fix: Use parameterized queries.


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 783/1023)


Merge: toast-1767088545235

branch: polecat/toast-1767088545235
target: main
source_issue: toast-1767088545235
rig: gastown


Swarm learning: Spawn should auto-notify polecats

town spawn assigns issues but doesn't notify polecats. Required separate 'town session send' to inject prompts. This should be one atomic operation - spawn assigns AND pokes the polecat to start working.


Move disk 4: C → B

Move disk 4 from peg C to peg B. (Move 936/1023)


Digest: mol-deacon-patrol

Patrol 4: routine, all healthy


Implement gt agent state command for label-based state

Add gt agent state command:
- gt agent state <agent-bead> - Get all labels
- gt agent state <agent-bead> --set key=value - Set label
- gt agent state <agent-bead> --incr key - Increment numeric label
- gt agent state <agent-bead> --del key - Delete label

Labels stored on agent beads enable operational state (idle:N, backoff:2m, etc).


Merge: nux-1767083432904

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


Digest: mol-deacon-patrol

Patrol 147 complete


Digest: mol-deacon-patrol

Patrol 9: Quick cycle, all quiet


gt spawn: Not injecting work instructions to session

gt spawn starts session but doesn't inject the issue assignment.

Repro:
1. gt spawn gastown/Toast --issue gt-2ux
2. Session starts but polecat just sees Claude prompt
3. No issue context injected

Expected: Polecat should receive issue details automatically.
Actual: Polecat sits at blank prompt, needs manual injection.


Day 5.3: Declare liftoff or document gaps

Final assessment:
- If E2E works: Close gt-oki8p as complete, declare liftoff
- If gaps remain: Document what's missing, file follow-on beads

Either way, the 4.5 day sprint is complete.

Parent: gt-oki8p


Digest: mol-deacon-patrol

Patrol #2: Routine - all 6 agents healthy


Test: Polecat lifecycle validation (auto-close)

Simple test task: Read this issue, print 'Lifecycle test passed', then close this issue with bd close. No code changes needed.


Digest: mol-deacon-patrol

Patrol cycle 25: quick check, all healthy


Merge: nux-1767059693217

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


mol-deacon-patrol


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 49/1023)


gt sling: Support batch slinging multiple beads

Currently `gt sling` only accepts one bead at a time:

```
gt sling gt-abc gastown        # works
gt sling gt-abc gt-def gastown # fails: accepts between 1 and 2 arg(s)
```

## Proposed

Allow multiple beads in a single command:
```
gt sling gt-abc gt-def gt-ghi gastown
```

This would spawn a polecat for each bead (or reuse existing ones).

## Use case

Slinging related work from a convoy - want to parallelize without running gt sling N times manually.


Digest: mol-deacon-patrol

Patrol 14


gt-gastown-polecat-cheedo

gt-gastown-polecat-cheedo

role_type: polecat
rig: gastown
agent_state: running
hook_bead: null
role_bead: gt-polecat-role
cleanup_status: has_uncommitted
active_mr: gt-2unzu


Replaceable notifications via Claude Code queue

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

## Problem

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

## Solution

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

## Implementation

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

## Research Needed

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


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor.

The Deacon's agent bead last_activity timestamp is updated during each patrol
cycle. Witnesses check this timestamp to verify health.


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 767/1023)


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 83/1023)


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 519/1023)


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 911/1023)


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 819/1023)


Digest: mol-deacon-patrol

Patrol 14: nominal


Mayor startup protocol prompting

Add startup protocol to Mayor CLAUDE.md template.

## On Session Start

1. Check for handoff:
   town inbox | grep "Session Handoff"

2. If handoff found:
   - Read it: town read <msg-id>
   - Process pending escalations (highest priority)
   - Check status of noted swarms
   - Verify rig health matches notes
   - Continue with documented next steps

3. If no handoff:
   town status     # Overall health
   town rigs       # Each rig
   bd ready        # Work items
   town inbox      # Any messages
   Build your own picture of current state.

4. After processing handoff:
   - Archive or delete the handoff message
   - You now own the current state

## Handoff Best Practices

- Be specific: 'Toast has merge conflict in auth/middleware.go' not 'Toast is stuck'
- Include context: Why decisions are pending, what you were thinking
- Prioritize next steps: What is most urgent
- Note time-sensitive items: Anything that might have changed since handoff


Digest: mol-deacon-patrol

Patrol 17: quiet


Digest: mol-deacon-patrol

Patrol complete: healthy


Digest: mol-deacon-patrol

Patrol 4: All healthy, no messages


Digest: mol-deacon-patrol

Patrol cycle 8: All quiet.


Find abandoned work

Find abandoned work.

Scan for orphaned state:
- Issues marked in_progress with no active polecat
- Polecats that stopped responding mid-work
- Merge queue entries with no polecat owner
- Wisp sessions that outlived their spawner

```bash
bd list --status=in_progress
gt polecats --all --orphan
```

For each orphan:
- Check if polecat session still exists
- If not, mark issue for reassignment or retry
- File incident beads if data loss occurred


Migrate startup paths from SessionBeacon to StartupNudge

## Problem
StartupNudge function exists (gt-bgfqy) but no code uses it.
All startup paths still call session.SessionBeacon() + 'gt prime' separately.

SessionBeacon format: [GAS TOWN] address • molID • timestamp
StartupNudge format: [GAS TOWN] recipient <- sender • timestamp • topic

The key difference is SENDER - needed for seance discovery ('who started this session?').

## Files to Update
- internal/cmd/crew_lifecycle.go (lines 354-371, 525-538)
- internal/cmd/start.go (lines 757, 805-810, 946-949)
- internal/cmd/deacon.go (line 307)
- internal/cmd/witness.go (line 350)
- internal/cmd/up.go (lines 288, 335, 559, 660)
- internal/cmd/mayor.go (line 151)
- internal/session/manager.go (lines 205, 215)

## Pattern
Replace:
  beacon := session.SessionBeacon(address, molID)
  t.NudgeSession(sessionID, beacon)
  t.NudgeSession(sessionID, "gt prime")

With:
  cfg := session.StartupNudgeConfig{Recipient: address, Sender: sender, Topic: topic, MolID: molID}
  session.StartupNudge(t, sessionID, cfg)
  // Note: StartupNudge is the session title, still need separate 'gt prime' for priming

## Related
- gt-85whr (parent epic)
- gt-j0546 (did RuntimeConfig refactor, not StartupNudge)


Digest: mol-deacon-patrol

Patrol complete: checked inbox (0 msgs), gates (0), polecats (0), health OK, GC'd 6 old wisps


gt-gastown-polecat-testcat2

gt-gastown-polecat-testcat2

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


Digest: mol-deacon-patrol

Patrol 10: All healthy


Move disk 3: C → A

Move disk 3 from peg C to peg A. (Move 452/1023)


Merge: toast-1767074299802

attached_args: Code review this merge request

branch: polecat/toast-1767074299802
target: main
source_issue: toast-1767074299802
rig: gastown


Digest: mol-deacon-patrol

Patrol cycle 15


Digest: mol-deacon-patrol

Cycle 18


Digest: mol-deacon-patrol

Patrol 16: All healthy


Digest: mol-deacon-patrol

Patrol 2: All healthy. 3 rigs, 6 agents. No incidents.


Deacon keepalive: quiet daemon during rounds

Replace tmux-poke interrupts with a keepalive file the daemon respects.

## Problem

Current daemon pokes Deacon via tmux SendKeys every ~5 minutes if heartbeat
is stale. This can INTERRUPT the Deacon mid-work, especially during long
plugin execution or health remediation.

## Solution

Deacon writes a keepalive file at start of rounds:
```json
{
  "timestamp": "2025-12-20T15:30:00Z",
  "expected_duration_minutes": 10,
  "action": "executing mol-deacon-rounds"
}
```

Daemon reads this and backs off:
- If keepalive is fresh AND within expected_duration: skip poke
- If keepalive is stale OR past expected_duration: poke (fallback)

## Files

- `{townRoot}/deacon/keepalive.json` - Written by Deacon
- Daemon already reads `{townRoot}/deacon/heartbeat.json` - similar pattern

## Deacon Behavior

1. Start of rounds: write keepalive with expected duration
2. End of rounds: write heartbeat (existing), clear/update keepalive
3. If rounds take longer than expected: Deacon gets one poke, squelches it

## Daemon Changes

Modify `pokeDeacon()` in daemon.go:
1. Check keepalive.json freshness and expected_duration
2. If within bounds: skip poke, log "Deacon active"
3. If past bounds: poke as fallback

## Worst Case

Deacon crashes mid-rounds without clearing keepalive:
- Daemon waits until expected_duration expires
- Then pokes, triggering respawn loop
- Max delay: expected_duration (configurable, default 10-15 min)

## Configuration

In pinned kernel bead:
```yaml
config:
  rounds_expected_duration_minutes: 10
```


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


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 679/1023)


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 977/1023)


Move disk 3: B → C

Move disk 3 from peg B to peg C. (Move 492/1023)


Fix golangci-lint errcheck warnings (~160 issues)

Running golangci-lint shows ~160 errcheck warnings for unchecked error returns.

Common patterns:
- t.SetEnvironment() return values
- os.WriteFile(), os.RemoveAll()
- MarkFlagRequired() on cobra commands
- Various manager methods

Run: golangci-lint run ./...

Could batch fix with:
1. Add explicit _ = for intentionally ignored errors
2. Handle errors properly where they matter
3. Consider adding //nolint:errcheck for cobra flag setup


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 505/1023)


OSS Launch Readiness

Track all issues blocking or affecting OSS launch quality.

P0 Blocker:
- gt-ofl2: ProcessMRFromQueue not implemented

P1 Must Fix:
- gt-vzic: README missing prerequisites (tmux)
- gt-xbfw: Missing OSS files (CONTRIBUTING, etc.)
- gt-wexr: Polecat role swarm terminology
- gt-6n13: Competing molecule mechanisms
- gt-3abj: go install may fail

P2 Should Fix:
- gt-qj12: Obsolete beads cleanup
- gt-zn9m: Error suppression patterns
- gt-9uxr: Test coverage gaps
- gt-fm75: os.Exit in library code
- gt-yewf: Mismatched startup protocols
- gt-1z4m: Undocumented gt swarm
- gt-bho9: stderr suppression
- gt-rxsh: Merge model confusion
- gt-5ipl: Witness role commands

P3 Nice to Have:
- gt-t5mz: Hardcoded values


Support nested expansion formulas


Move disk 3: A → B

Move disk 3 from peg A to peg B. (Move 580/1023)


Move disk 5: A → B

Move disk 5 from peg A to peg B. (Move 16/1023)


Move disk 3: B → C

Move disk 3 from peg B to peg C. (Move 36/1023)


Digest: mol-deacon-patrol

Patrol 52: All healthy


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: organic-mjwjck2f

branch: polecat/organic-mjwjck2f
target: main
source_issue: organic-mjwjck2f
rig: gastown
agent_bead: gt-gastown-polecat-organic


Zombie session detection for refinery/witness

A Claude session can crash or hang while still appearing as 'running' in tmux. The current IsRunning checks only verify tmux session exists, not that Claude is actually processing.

## Problem
- Refinery session might be hung (Claude crashed, infinite loop, etc.)
- tmux HasSession returns true, so auto-start doesn't trigger
- MRs pile up with no processing

## Solution Options
1. **Activity-based detection**: Check last output timestamp in tmux
2. **Heartbeat file**: Agents write timestamp to file, check staleness
3. **Prompt detection**: Look for Claude '>' prompt indicating idle/stuck

## Related
- gt-bjft (spawn auto-start) assumes IsRunning means healthy
- Witness patrol assumes running refinery is processing


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 905/1023)


Digest: mol-deacon-patrol

Patrol 94: All healthy


gt-polecat-gastown-testcat

gt-polecat-gastown-testcat

role_type: polecat
rig: gastown
agent_state: running
hook_bead: null
role_bead: null


Fix template syntax in code-review.formula.toml (Handlebars vs Go)

code-review.formula.toml uses Handlebars syntax ({{#if}}, {{#each}}) but Go text/template uses {{if}}, {{range}}. Either switch to Go template syntax or implement a Handlebars-compatible template engine.


Design mol-ready-work patrol for crew workers

## Summary

Design and implement a protomolecule (mol-ready-work) that enables crew workers to autonomously work through backlogs when the overseer is away. This replaces the normal "await instructions" patrol step with productive backlog processing.

## Context

Crew workers like dave/joe are persistent, user-managed agents. Currently their patrol loop has an "await instructions" step that idles. We want an alternative: sling mol-ready-work at them and they crank through backlogs until:
- The overseer interrupts with new instructions
- Context fills up (request handoff)
- Backlogs are empty

## Backlogs (Priority Order)

1. **Open PRs** - Review/merge pending pull requests
2. **Untriaged GH issues** - New issues needing triage
3. **Open beads work** - bd ready items (unblocked issues)
4. **Triaged GH issues** - Bugs/features to implement

## Key Features

### ROI-Based Selection
Instead of strict priority, agent applies ROI heuristic:
- Size estimate (fits in remaining context?)
- Achievability (has all needed info?)
- Impact (priority + type weight)
- Pick highest-value achievable item

### Context Management
- Check token usage after each work item
- Request handoff before running out
- Leave clear handoff notes (mail to self)
- Next session picks up the patrol

### Patrol Loop Structure
```
orient → scan-backlogs → select-work → execute-work → check-context → (loop or handoff)
```

## Design Questions

1. How does gt sling work? Does it exist?
2. How does this interact with the normal crew patrol?
3. Should the molecule go in Gas Town catalog or per-rig?
4. How does bonding work for discovered work during execution?
5. Should this be a wisp (ephemeral) or mol (persistent)?

## Acceptance Criteria

- [ ] mol-ready-work protomolecule defined in catalog
- [ ] gt sling command implemented (or existing mechanism documented)
- [ ] Context-checking step works with agent token awareness
- [ ] Handoff mechanism integrates with gt mail
- [ ] Documentation for crew workers on using the patrol


Digest: mol-deacon-patrol

Patrol 20: routine, handoff triggered


Digest: mol-deacon-patrol

Patrol 10: routine, halfway


Digest: mol-deacon-patrol

Patrol 13: all clear


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 447/1023)


Digest: mol-deacon-patrol

Patrol 20: all healthy, handoff threshold reached


Digest: mol-deacon-patrol

Patrol 19: healthy


Digest: mol-deacon-patrol

Patrol 19: Quiet


Digest: mol-deacon-patrol

Patrol cycle 4: All agents healthy


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 739/1023)


Move disk 2: C → B

Move disk 2 from peg C to peg B. (Move 858/1023)


Code review: session health monitoring (gt-i7wcn)

Review the session health monitoring and auto-restart implementation in internal/cmd/session.go, internal/daemon/daemon.go, and internal/daemon/lifecycle.go


sling pin test 2


Digest: mol-deacon-patrol

Patrol 6: all clear


Digest: mol-deacon-patrol

Patrol cycle 76: All healthy


Digest: mol-deacon-patrol

Patrol 101: All agents healthy, 3 rigs checked, no incidents


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 521/1023)


Merge: toast-mjxb88ev

branch: polecat/toast-mjxb88ev
target: main
source_issue: toast-mjxb88ev
rig: gastown
agent_bead: gt-gastown-polecat-toast


Digest: mol-deacon-patrol

Patrol cycle 6: All 6 agents healthy


Move disk 2: A → C

Move disk 2 from peg A to peg C. (Move 746/1023)


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Idle Town Principle

**The Deacon should be silent/invisible when the town is healthy and idle.**

- Skip HEALTH_CHECK nudges when no active work exists
- Sleep 60+ seconds between patrol cycles (longer when idle)
- Let the feed subscription wake agents on actual events
- The daemon (10-minute heartbeat) is the safety net for dead sessions

This prevents flooding idle agents with health checks every few seconds.

## Second-Order Monitoring

Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor.

The Deacon's agent bead last_activity timestamp is updated during each patrol
cycle. Witnesses check this timestamp to verify health.


Digest: mol-deacon-patrol

Patrol 19: all healthy


Digest: mol-deacon-patrol

Patrol cycle 20: All 3 rigs healthy. No incidents. Handoff threshold reached.


Move disk 3: A → B

Move disk 3 from peg A to peg B. (Move 748/1023)


gt status: check daemon health first, attempt restart

If daemon not running, attempt to start it. If start fails, show warning but don't block status display. Currently falls back to slow direct mode silently.


Digest: mol-deacon-patrol

Patrol 6: quiet


Merge: capable-mjtltnm5

branch: polecat/capable-mjtltnm5
target: main
source_issue: capable-mjtltnm5
rig: gastown


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 295/1023)


Merge: nux-1767082300311

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


E2E Swarm Test Epic


Digest: mol-deacon-patrol

Patrol 153: All healthy, nux respawned


Digest: mol-deacon-patrol

Patrol cycle 10: All healthy


Move disk 5: C → A

Move disk 5 from peg C to peg A. (Move 944/1023)


gt crew add: Create crew workspace

Implement 'gt crew add <name> [--rig <rig>]' command:
- Clone repo into <rig>/crew/<name>/
- Create feature branch (optional, or stay on main)
- Register for mail delivery
- Initialize CLAUDE.md with crew worker prompting
- Do NOT register with witness (user-managed)


Digest: mol-deacon-patrol

Patrol 8: all clear


Burn and respawn or loop

End of patrol cycle decision.

If context is LOW:
- Burn this wisp (no audit trail needed for patrol cycles)
- Sleep briefly to avoid tight loop (30-60 seconds)
- Return to inbox-check step

If context is HIGH:
- Burn wisp with summary digest
- Exit cleanly (daemon will respawn fresh Witness)

```bash
bd mol burn   # Destroy ephemeral wisp
```

The daemon ensures Witness is always running.


Digest: mol-deacon-patrol

Patrol cycle 3: All 3 rigs healthy. 0 messages. No incidents.


Digest: mol-deacon-patrol

Patrol 11: healthy


GitHub gates (gh:run, gh:pr)

Implement GitHub gate types using gh CLI. gh:run:<id> waits for Actions run completion. gh:pr:<id> waits for PR merged/closed. Deacon polls status during patrol cycle.


Digest: mol-deacon-patrol

Patrol 1: All agents healthy, archived 17 routine handoffs, cleaned 1 orphan process and 1 stale lock


Digest: mol-deacon-patrol

Patrol #20: Final cycle - all agents healthy. Beads 5 MQ pending, gastown 1 MQ pending. 1 dog idle. Clean handoff.


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

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


test pin fix 2


test with labels


Digest: mol-deacon-patrol

Patrol cycle 6: All healthy.


Digest: mol-deacon-patrol

Patrol #19


mol-polecat-work

Full polecat lifecycle from assignment to decommission.

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

Variables:
- gt-1wmw - The source issue ID being worked on


Town Doctor molecule for harness health checks

Create a Town Doctor molecule that any Gas Town agent can run to diagnose and repair harness issues.

## Concept

Instead of just `gt doctor` as a CLI command with hardcoded checks, create a **molecule** (checklist workflow) that:
- Any agent (Mayor, Witness, Polecat) can instantiate
- Walks the agent through diagnostic steps
- Agent uses judgment to fix issues found
- Works as a structured troubleshooting guide

## Why a Molecule?

1. **Agent-driven**: The agent running it becomes "the doctor" temporarily
2. **Extensible**: Add new checks by updating the molecule, not code
3. **Contextual**: Agent can reason about issues, not just run scripts
4. **Self-healing**: Agent can fix problems it finds, not just report them

## Proposed Checks (molecule steps)

1. Verify harness structure (mayor/, .beads/, CLAUDE.md exist)
2. Validate config files (town.json, rigs.json parse correctly)
3. Check beads health (bd doctor, redirect validity)
4. Verify git state (clean working tree, proper remotes)
5. Check rig integrity (each registered rig exists, has config.json)
6. Validate agent clones (mayor/rig/, refinery/rig/ exist and are valid)
7. Check for orphaned worktrees/branches
8. Verify daemon state (if running)

## Integration

- `gt doctor` could instantiate the molecule for the current agent
- Or agent can run `bd ready` and pick up doctor tasks when prioritized
- Results logged to beads for audit trail

## Related

- gt-cr9: Harness Design & Documentation (completed)
- Molecules design in architecture.md


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 41/1023)


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 271/1023)


Refinery and witness rigs should redirect .beads to mayor rig

The refinery's rig clone and witness rig clones should have their .beads directories redirect to the mayor's rig beads (like polecats do).

Current state:
- Polecats have .beads symlinked/redirected to mayor rig beads
- Refinery and witness have their own .beads (or none)

Desired state:
- refinery/rig/.beads -> mayor/rig/.beads (or equivalent redirect)
- witness/rig/.beads -> mayor/rig/.beads (or equivalent redirect)

This ensures all rig agents share the same issue tracking state.


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


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 339/1023)


Digest: mol-deacon-patrol

Cycle 6: quiet


Digest: mol-deacon-patrol

Patrol 158 complete


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor.

The Deacon's agent bead last_activity timestamp is updated during each patrol
cycle. Witnesses check this timestamp to verify health.


Digest: mol-deacon-patrol

Patrol 4: Routine, witnesses/refineries healthy


Digest: mol-deacon-patrol

Patrol 18: OK


Digest: mol-deacon-patrol

Patrol 10: healthy


Digest: mol-deacon-patrol

Patrol cycle 20


Digest: mol-deacon-patrol

Patrol 20: quiet, handoff threshold reached


Design: CI test infrastructure and approach

Design decisions for CI test infrastructure.

## Decisions Made

### 1. Test Framework
**Decision**: Go's standard testing with `//go:build integration` tag

Rationale:
- Familiar to Go developers
- Easy to run separately: `go test -tags=integration ./...`
- Can have different timeouts for unit vs integration
- No external test framework dependencies

### 2. Temp Directory Strategy
**Decision**: `t.TempDir()` per test

Rationale:
- Auto-cleanup (no manual defer needed)
- Test isolation - each test gets fresh state
- Easy debugging - can add `t.Setenv("KEEP_TEMP", "1")` to preserve

### 3. Git Repo Handling
**Decision**: Create minimal repos on-the-fly with `git init`

Rationale:
- Faster than cloning fixtures
- No external dependencies
- Tests exactly what users do
- Helper: `createTestGitRepo(t, name)`

### 4. bd Daemon
**Decision**: Run bd commands directly without daemon

Rationale:
- bd CLI works fine for basic operations
- Simpler test setup
- Daemon adds complexity without testing benefit for install flow

### 5. CI Integration
**Decision**: Separate job with path filtering

- Runs on: push to main, PRs touching install/rig/config/routing code
- Timeout: 5 minutes (vs 2 min for unit tests)
- Installs bd from beads repo before running

## Implementation
- Created: `internal/cmd/install_integration_test.go`
- Updated: `.github/workflows/ci.yml` with integration job

## Test Cases Implemented
- TestInstallCreatesCorrectStructure
- TestInstallBeadsHasCorrectPrefix
- TestInstallIdempotent
- Helper: createTestGitRepo


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)


Move disk 2: B → A

Move disk 2 from peg B to peg A. (Move 910/1023)


gt uninstall: Clean removal of Gas Town harness

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

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


Implement gt mq CLI commands

Add gt mq subcommands as sugar over bd:

- gt mq submit: Create MR for current branch
- gt mq list: Show open merge requests
- gt mq next: Show next MR ready to process
- gt mq process: Engineer processes the queue
- gt mq reorder <id> --after <x>: Change ordering via deps
- gt mq status <id>: Show MR details

All commands should work with the Beads data plane.


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 done: MR bead not created

Integration test gt-7psb8 revealed that gt done says 'Work submitted to merge queue' but no MR bead is created.

Observed:
- gt done output: 'MR ID: mr-1766956116-b02fb1e5'
- bd show mr-1766956116-b02fb1e5: 'no issue found'
- gt mq list gastown: '(empty)'

Expected: MR bead should exist with issue_type=merge-request and appear in gt mq list.


Move disk 7: C → A

Move disk 7 from peg C to peg A. (Move 320/1023)


Merge: gt-2hwi9

branch: polecat/furiosa-mjw349y2
target: main
source_issue: gt-2hwi9
rig: gastown
agent_bead: gt-gastown-polecat-furiosa


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 647/1023)


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 125/1023)


Merge: rictus-1767138835254

branch: polecat/rictus-1767138835254
target: main
source_issue: rictus-1767138835254
rig: gastown


Digest: mol-deacon-patrol

Patrol 27: all polecats working, jack session started


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Idle Town Principle

**The Deacon should be silent/invisible when the town is healthy and idle.**

- Skip HEALTH_CHECK nudges when no active work exists
- Sleep 60+ seconds between patrol cycles (longer when idle)
- Let the feed subscription wake agents on actual events
- The daemon (10-minute heartbeat) is the safety net for dead sessions

This prevents flooding idle agents with health checks every few seconds.

## Second-Order Monitoring

Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor.

The Deacon's agent bead last_activity timestamp is updated during each patrol
cycle. Witnesses check this timestamp to verify health.


Remove doctor staleness detection

**File:** doctor/stale_check.go (lines 11-48)

**Current behavior:**
- DefaultStaleThreshold = 1 hour hardcoded
- Scans molecules for in_progress status older than threshold
- Reports "stale" molecules automatically

**Fix:**
- Remove automatic staleness detection from doctor
- Doctor becomes pure diagnostic tool (reports facts, not judgments)
- Deacon molecule step does "orphan-check" instead:
  "Find issues in_progress with no active polecat"

**Lines to remove:**
- stale_check.go:13 (DefaultStaleThreshold constant)
- stale_check.go:243-256 (staleness classification logic)


Digest: mol-deacon-patrol

Patrol cycle 2: all healthy, 2 health acks


Polecat health check loop

Implement the Witness health check loop:

Every 60 seconds:
1. List active polecats (gt polecats <rig>)
2. For each polecat, check:
   - Is tmux session still alive?
   - Is there a state.json with recent update?
   - Has there been git activity recently?
3. If stuck (no activity for configurable threshold):
   - First: nudge (send message asking for status)
   - Second: escalate to Mayor
   - Third: force kill after human confirmation

This is the 'health monitor' part of Witness.


Digest: mol-deacon-patrol

Patrol 11: nominal


Refinery gates: test/lint/build before merge

Before merging polecat work to main, run configurable quality gates.

**From VC**: internal/gates/ - parallel execution with timeout, any failure = overall failure.

**Gas Town implementation**: Refinery config with gate commands:
```yaml
gates:
  test:
    cmd: go test ./...
    timeout: 5m
  lint:
    cmd: golangci-lint run
    timeout: 2m
  build:
    cmd: go build ./...
    timeout: 3m
parallel: true
```

If gates fail, don't merge. Polecat can iterate and retry.

**Value**: Prevents broken code from reaching main. VC had 90.9% gate pass rate.

**VC complexity**: ~200 lines Go
**Gas Town complexity**: ~10 lines YAML


attached_args: Refactor runStart into focused helper functions

Break up the 126-line runStart function in start.go.

## Files to modify
- internal/cmd/start.go

## Current structure (lines 129-255)
runStart does:
1. Crew path detection
2. Workspace verification
3. Mayor session start
4. Deacon session start
5. Optional rig agent startup
6. Configured crew auto-start

## Refactoring plan
Extract these helpers:
```go
func startMayorSession(townRoot string, verbose bool) error
func startDeaconSession(townRoot string) error
func startRigAgents(townRoot string, rigs []string) error
func startConfiguredCrew(townRoot string, config *TownConfig) error
```

## Acceptance criteria
- [ ] runStart reduced to <50 lines
- [ ] 4 helper functions extracted
- [ ] Each helper is <40 lines
- [ ] No change in behavior
- [ ] go test ./internal/cmd/... passes
- [ ] Manual test: gt start works correctly


Digest: mol-deacon-patrol

Patrol cycle 6: All quiet.


Merge: gt-bca67

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


Move disk 2: B → A

Move disk 2 from peg B to peg A. (Move 646/1023)


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor.

The Deacon's agent bead last_activity timestamp is updated during each patrol
cycle. Witnesses check this timestamp to verify health.


Move disk 2: B → A

Move disk 2 from peg B to peg A. (Move 130/1023)


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

Implement condition checking for each await type.

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

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

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


Integration test: simple file creation

Test task for polecat integration test (gt-7psb8).

Simple task: Create a file ~/gt/gastown/test-polecat-integration.txt with content 'Polecat integration test successful'.

This is an intentionally trivial task to verify the polecat → witness → refinery → merged flow works end-to-end.


Digest: mol-deacon-patrol

Patrol 15: Quiet


EXTERNAL: Beads Messaging & Knowledge Graph (bd-kwro)

Tracking issue for external dependency on Beads v0.30.2 messaging features.

Beads epic: bd-kwro in ~/src/beads (steveyegge/beads repo)

This blocks GGT work that depends on:
- bd mail send/inbox/read/ack commands
- message issue type
- replies_to threading
- Hooks system for notifications
- Identity configuration

GGT mail commands will be thin wrappers around bd mail once available.

When bd-kwro ships in Beads v0.30.2, close this and unblock dependent work.


Witness events in gt feed

Wire witness patrol events to gt feed display.

After 'Witness emits activity events' is done:
- gt feed should show patrol activity
- Useful for debugging polecat issues

Example output:
  19:45 PATROL_COMPLETE gastown/witness (checked 3 polecats)
  19:44 POLECAT_NUDGED gastown/polecats/nux (idle 10m)
  19:40 ESCALATION mayor (polecat stuck)


Merge: gt-7hor

branch: polecat/slit
target: main
source_issue: gt-7hor
rig: gastown


MR bead: Add retry_count and conflict metadata

Extend MR beads to track conflict resolution attempts.

## New Fields
- `retry_count`: Number of conflict-resolution cycles
- `last_conflict_sha`: SHA of main when conflict occurred
- `conflict_task_id`: Link to conflict-resolution task (if any)

## Usage
- Refinery increments retry_count on each conflict
- Priority function uses retry_count as input
- Conflict-resolution task links back to MR

## Implementation
- Update MR creation to include fields
- Update refinery to increment on conflict
- Update priority function to read retry_count


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 20


Implement aspect merging across formula chain

Implement merging of aspects when formulas extend each other.

## Current State

extends: already works for steps and vars.
Aspects need similar merge behavior.

## Implementation

1. In Resolve(), collect aspects from entire extends chain
2. Merge lists: parent aspects first, then child
3. Support exclude-aspects field to remove inherited aspects
4. Dedupe by aspect name (child wins on conflict)

## Files

- internal/formula/parser.go: Resolve()
- internal/formula/types.go: add ExcludeAspects field

## Tests

1. Child inherits parent aspects
2. Child adds additional aspects
3. Child excludes parent aspect
4. Three-level chain merges correctly


Digest: mol-deacon-patrol

Patrol cycle 35: All healthy, no incidents


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Idle Town Principle

**The Deacon should be silent/invisible when the town is healthy and idle.**

- Skip HEALTH_CHECK nudges when no active work exists
- Sleep 60+ seconds between patrol cycles (longer when idle)
- Let the feed subscription wake agents on actual events
- The daemon (10-minute heartbeat) is the safety net for dead sessions

This prevents flooding idle agents with health checks every few seconds.

## Second-Order Monitoring

Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor.

The Deacon's agent bead last_activity timestamp is updated during each patrol
cycle. Witnesses check this timestamp to verify health.


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 421/1023)


Digest: mol-deacon-patrol

Patrol 258 complete: no incidents


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

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


Move disk 2: A → B

Move disk 2 from peg A to peg B.


execute-actions

Send nudges, process shutdowns, escalate as decided.

Needs: decide-actions


gt mail inbox doesn't find crew worker mail - identity mismatch

## Problem

Crew workers don't see their handoff messages when running `gt mail inbox` from their working directory.

## Root Cause

gt derives identity from cwd path, but crew workers have a different path structure:
- **cwd**: `/Users/stevey/gt/beads/crew/dave`
- **gt derives**: `beads/crew/dave` (wrong)
- **should be**: `beads/dave` (crew workers use `rig/name` format)

Messages sent to `beads/dave` don't show up because gt is looking for `beads/crew/dave`.

## Workaround

Use explicit identity flag:
```bash
gt mail inbox --identity "beads/dave"
```

## Fix

Identity detection in gt should handle crew/ subdirectory:
- Path `<rig>/crew/<name>` should derive identity as `<rig>/<name>`
- This matches how polecats work: `<rig>/polecats/<name>` → `<rig>/<name>`

## Affected

All crew workers in all rigs.


Digest: mol-deacon-patrol

Patrol 73: Routine


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 369/1023)


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 231/1023)


Clarify formula semantics: read-only reference, not instantiated

## Problem

An agent (likely the Deacon) read the `mol-deacon-patrol.formula.toml` file and created beads from its steps. This is wrong - formulas are reference documents describing a workflow, not templates to be instantiated into trackable work.

## What Happened

1. Formula exists at `.beads/formulas/mol-deacon-patrol.formula.toml`
2. Deacon (or predecessor agent) created an epic + 9 task beads matching the formula steps
3. These showed up in `bd ready` (separate bug filed)

## Correct Model

| Artifact | Purpose | Created How |
|----------|---------|-------------|
| Formula (.toml) | Reference doc describing workflow | Manual/design |
| Wisp | Ephemeral execution context | `bd wisp create` (no child beads) |
| Proto | Template for pour | `bd cook` from formula |

**Formulas should NOT create beads.** They're documentation. When the Deacon runs a patrol, it follows the formula instructions directly - it doesn't need beads for each step.

## Fix Needed

1. Update Deacon template to clarify: "Follow the formula instructions. Do not create beads from formula steps."
2. Audit what command created the beads - was it `bd wisp create` expanding the formula? If so, that command shouldn't create child beads.
3. Consider renaming formulas to `.md` if they're just prose instructions.

(Moved from hq-ufiy)


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.

## The Christmas Ornament Shape

This molecule uses dynamic bonding to create inspection arms per-polecat:

```
                    ★ mol-witness-patrol (trunk)
                   /|\
            ┌─────┘ │ └─────┐
         PREFLIGHT DISCOVERY CLEANUP
            │       │        │
        ┌───┴───┐ ┌─┴─┐  ┌───┴───┐
        │inbox  │ │sur│  │aggreg │
        │refnry │ │vey│  │save   │
        │load   │ └─┬─┘  │summary│
        └───────┘   │    │contxt │
                    │    │loop   │
          ┌─────────┼─────────┐ └───────┘
          │         │         │
          ●         ●         ●     mol-polecat-arm (dynamic)
         ace       nux      toast
          │         │         │
       ┌──┴──┐   ┌──┴──┐   ┌──┴──┐
       │cap  │   │cap  │   │cap  │
       │ass  │   │ass  │   │ass  │
       │dec  │   │dec  │   │dec  │
       │exec │   │exec │   │exec │
       └──┬──┘   └──┬──┘   └──┬──┘
          │         │         │
          └─────────┴─────────┘
                    │
                 ⬣ base (cleanup)
```

## Phases

### PREFLIGHT (fixed steps)
1. inbox-check - Process lifecycle requests, help messages
2. check-refinery - Ensure MQ is alive and processing
3. load-state - Read persistent state (nudge counts, etc.)

### DISCOVERY (spawns dynamic arms)
4. survey-workers - List polecats, bond mol-polecat-arm per polecat
5. run-plugins - Bond mol-plugin-runner for each witness plugin

### CLEANUP (gate + fixed steps)
6. aggregate - GATE: WaitsFor all arms + plugins to complete
7. save-state - Persist nudge counts, action log
8. generate-summary - Create digest content
9. context-check - Check if context is high
10. burn-or-loop - Squash/burn wisp, then loop or exit

## Dynamic Arms

Each polecat gets mol-polecat-arm bonded as a wisp child:
- capture - Capture tmux output
- assess - Categorize state (working/idle/error/done)
- load-history - Get nudge counts for this polecat
- decide - Apply nudge matrix
- execute - Take action (nudge/kill/escalate/none)

Arms run in PARALLEL. The aggregate step waits for all to complete.

## Activity Feed

This design enables real-time visibility:

```
[14:32:08] + patrol-x7k.arm-ace bonded (5 steps)
[14:32:09] → patrol-x7k.arm-ace.capture in_progress
[14:32:10] ✓ patrol-x7k.arm-ace.capture completed
[14:32:14] ✓ patrol-x7k.arm-ace.decide completed (action: nudge-1)
[14:32:17] ✓ patrol-x7k.arm-ace COMPLETE
```

## Storage

- Wisp storage: .beads-wisp/ (ephemeral, gitignored)
- Persistent state: witness handoff bead (nudge counts, etc.)
- Digests: Squashed summaries in permanent beads

## Dependencies

- bd-xo1o: Dynamic Molecule Bonding epic (in beads rig)
  - bd mol bond with variable substitution
  - WaitsFor directive for fanout gates
  - Activity feed query

Labels: [template, christmas-ornament]


Digest: mol-deacon-patrol

Patrol 10: All green - halfway mark


Move disk 2: B → A

Move disk 2 from peg B to peg A. (Move 58/1023)


Digest: mol-deacon-patrol

Patrol #13: Stable


Digest: mol-deacon-patrol

Patrol #3: Routine - 6 agents healthy


Digest: mol-deacon-patrol

Patrol 159 complete


Design: Work flow simulation and validation

Validate GGT designs through simulation before implementation.

## Validation Approaches

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

Create beads for any gaps discovered.

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

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

## Key Scenarios to Validate

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

## Success Criteria

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

## Output

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


Enhanced polecat decommission prompting

Add decommission checklist to polecat AGENTS.md.template. Make crystal clear: verify ALL before signaling done.

## Checklist for AGENTS.md.template

```markdown
## Decommission Checklist

**CRITICAL**: Before signaling done, you MUST complete this checklist.
The Witness will verify each item and bounce you back if dirty.

### Pre-Done Verification

```bash
# 1. Git status - must be clean
git status
# Expected: "nothing to commit, working tree clean"

# 2. Stash list - must be empty
git stash list
# Expected: (empty output)

# 3. Beads sync - must be up to date
bd sync --status
# Expected: "Up to date" or "Nothing to sync"

# 4. Branch merged - your work must be on main
git log main --oneline -1
git log HEAD --oneline -1
# Expected: Same commit
```

### If Any Check Fails

- **Uncommitted changes**: Commit them or discard if unnecessary
- **Stashes**: Pop and commit, or drop if obsolete
- **Beads out of sync**: Run `bd sync`
- **Branch not merged**: Complete the merge workflow

### Signaling Done

Only after ALL checks pass:

```bash
bd close <issue-id>
bd sync
town mail send <rig>/witness -s "Work Complete" -m "Issue <id> done."
```
```

## Implementation

Add to AGENTS.md.template in the polecat prompting section.


Digest: mol-deacon-patrol

Patrol cycle 8: All 3 rigs healthy. No incidents.


Move disk 2: C → B

Move disk 2 from peg C to peg B. (Move 870/1023)


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 279/1023)


Digest: mol-deacon-patrol

Patrol 20: healthy


Digest: mol-deacon-patrol

Patrol cycle 5: All healthy


Digest: mol-deacon-patrol

Patrol 20: Green - routine cycle complete, handing off


Digest: mol-deacon-patrol

Cycle 4: All healthy


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor.

The Deacon's agent bead last_activity timestamp is updated during each patrol
cycle. Witnesses check this timestamp to verify health.


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 985/1023)


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 367/1023)


gt handoff: Unified agent lifecycle command

## Summary

Unified `gt handoff` command for ALL agent types to request lifecycle actions.

## Usage

gt handoff              # Context-aware default
gt handoff --shutdown   # Terminate, cleanup, don't restart
gt handoff --cycle      # Restart with handoff mail
gt handoff --restart    # Fresh restart, no handoff

## Context-Aware Defaults

| Agent Type | Default | Reason |
|------------|---------|--------|
| Polecat | --shutdown | Ephemeral, work is done |
| Witness | --cycle | Long-running, context full |
| Refinery | --cycle | Long-running, context full |
| Mayor | --cycle | Long-running, context full |
| Crew | (sends mail only) | Human-managed |

## What gt handoff Does

1. **Verify safe to stop**
   - Git state clean (no uncommitted changes)
   - Work handed off (PR exists for polecats)

2. **Send handoff mail to self** (for cycle/restart)
   - Captures current state
   - New session will read this

3. **Send lifecycle request to manager**
   - Polecats/Refinery → Witness
   - Witness/Mayor → Daemon
   - Format: mail to <manager> with action type

4. **Set state: requesting_<action>**
   - Lifecycle manager checks this before acting

5. **Wait for termination**
   - Don't self-exit - let manager kill session
   - Ensures clean handoff

## Lifecycle Request Flow

Agent                           Lifecycle Manager
  |                                    |
  | 1. gt handoff --cycle              |
  |    a. Verify git clean             |
  |    b. Send handoff mail to self    |
  |    c. Set requesting_cycle=true    |
  |    d. Send lifecycle request       |
  |------------------------------------→|
  |                                    |
  |                   2. Receive request
  |                   3. Verify state  |
  |                   4. Kill session  |
  |                   5. Start new     |
  |                      (for cycle)   |
  |                                    |
  |         New session reads handoff  |
  |         Resumes work               |

## Who Manages Whom

| Agent | Sends lifecycle request to |
|-------|---------------------------|
| Polecat | <rig>/witness |
| Refinery | <rig>/witness |
| Witness | daemon/ |
| Mayor | daemon/ |

## Implementation

1. Detect current role (polecat, witness, refinery, mayor, crew)
2. Apply context-aware default if no flag specified
3. Run pre-flight checks (git clean, work handed off)
4. Send handoff mail to self (if cycling)
5. Send lifecycle request to appropriate manager
6. Set requesting_<action> in state.json
7. Wait (manager will kill us)

## For Polecats (--shutdown)

Additional cleanup after kill:
- Witness removes worktree
- Witness deletes polecat branch
- Polecat ceases to exist

## Related Issues

- gt-99m: Daemon (handles Mayor/Witness lifecycle)
- gt-7ik: Ephemeral polecats (polecat cleanup)
- gt-eu9: Witness session cycling


Digest: mol-deacon-patrol

Patrol 14: all healthy


Merge: rictus-mjxc967h

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


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


Polecats must not create GitHub PRs

Polecats should never use 'gh pr create' or create GitHub pull requests.

## Correct Workflow
1. Polecat works on polecat/<name> branch
2. Commits and pushes to origin
3. Creates beads MR issue (type: merge-request)
4. Refinery processes the MR and merges to main

## Wrong Workflow
- Using gh pr create
- Creating GitHub pull requests directly

## Why
- Refinery is our merge queue processor
- GitHub PRs bypass our workflow
- Beads MRs are the coordination mechanism


Move disk 4: C → B

Move disk 4 from peg C to peg B. (Move 456/1023)


Move disk 4: B → A

Move disk 4 from peg B to peg A. (Move 472/1023)


End-of-cycle inbox hygiene

Verify inbox hygiene before ending patrol cycle.

**Step 1: Check inbox state**
```bash
gt mail inbox
```

Inbox should be EMPTY or contain only just-arrived unprocessed messages.

**Step 2: Archive any remaining processed messages**

All message types should have been archived during inbox-check processing:
- WITNESS_PING → archived after acknowledging
- HELP/Escalation → archived after handling
- LIFECYCLE → archived after processing

If any were missed:
```bash
# For each stale message found:
gt mail archive <message-id>
```

**Goal**: Inbox should have ≤2 active messages at end of cycle.
Deacon mail should flow through quickly - no accumulation.


Add accounts.yaml config parsing to gt

Parse ~/gt/mayor/accounts.yaml with structure: accounts map (handle -> email, config_dir) and default field. This is the foundational config that other account features depend on. Location follows existing town-level config pattern in mayor/.


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 4: quiet, health OK


Digest: mol-deacon-patrol

Patrol 151: All healthy


Digest: mol-deacon-patrol

Patrol 7: Interrupted, routine


Merge: furiosa-1767073359963

attached_args: Code review this merge request

branch: polecat/furiosa-1767073359963
target: main
source_issue: furiosa-1767073359963
rig: gastown

REVIEW REJECTED by rictus: Commits claim await-signal but no such code exists. Feature removals conflict with rictus branch quality-level work. Needs clarification and reconciliation.


Move disk 2: A → C

Move disk 2 from peg A to peg C. (Move 110/1023)


Move disk 4: C → B

Move disk 4 from peg C to peg B. (Move 792/1023)


attached_args: Refactor runStartCrew into focused helper functions

Break up the 155-line runStartCrew function in start.go.

## Files to modify
- internal/cmd/start.go

## Current structure (lines 638-792)
runStartCrew does:
1. Parse crew target (rig/name)
2. Load rig config
3. Get/create crew workspace
4. Create/attach tmux session
5. Set environment variables
6. Apply theming
7. Start Claude

## Refactoring plan
Extract these helpers:
```go
func parseCrewTarget(args []string) (rigName, crewName string, err error)
func ensureCrewWorkspace(rig *rig.Rig, crewName string) (*crew.CrewWorker, error)
func startCrewSession(worker *crew.CrewWorker, opts CrewStartOpts) error
```

## Acceptance criteria
- [ ] runStartCrew reduced to <60 lines
- [ ] 3 helper functions extracted
- [ ] Each helper is <50 lines
- [ ] No change in behavior
- [ ] go test ./internal/cmd/... passes
- [ ] Manual test: gt start crew works correctly


Update Hanoi formula with agent-actionable instructions

Update towers-of-hanoi.formula.toml description to include:
- Clear execution protocol for agents
- What each step means
- What to do on resume
- Explicit DO/DO NOT guidance


Move disk 2: C → B

Move disk 2 from peg C to peg B. (Move 702/1023)


Digest: mol-deacon-patrol

Patrol cycle 3: All agents healthy


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor.

The Deacon's agent bead last_activity timestamp is updated during each patrol
cycle. Witnesses check this timestamp to verify health.


Digest: mol-deacon-patrol

Patrol cycle 10


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 949/1023)


Digest: mol-deacon-patrol

Cycle 13


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)


Dynamic wisp modification: insert, reorder, bond mid-execution

## Vision

Wisps should be living documents that adapt mid-execution, not static scripts.

## Operations Needed

### bd mol insert
Insert a step into a running wisp:
```bash
bd mol insert <wisp-id> --after <step> --step 'security-gate: Review auth changes'
```

### bd mol reorder
Change step order in a running wisp:
```bash
bd mol reorder <wisp-id> --move process-branch --before current
```

### bd mol bond (to wisp)
Attach an entire molecule to a running wisp:
```bash
bd mol bond mol-weekly-maintenance <wisp-id> --after await-work
```

### bd mol skip
Skip a step (with reason):
```bash
bd mol skip <wisp-id> <step> --reason 'Queue empty, skipping summary'
```

## Use Cases

1. **Hotfix priority**: Reorder queue to process urgent MR first
2. **Security gate**: Insert review step when PR touches sensitive files
3. **Scheduled maintenance**: Bond maintenance mol on schedule
4. **Context adaptation**: Skip steps when they don't apply

## Why This Matters

Engine room problems emerge dynamically. The wisp's flexibility lets agents model situations as they evolve, not execute rigid scripts.

GUPP still applies: whatever's on your hook, you run. But the hook contents can adapt.

## Implementation

This requires Beads changes to support wisp mutation:
- Track step ordering in wisp storage
- Support step insertion/deletion/reordering
- Handle bond-to-wisp (spawn steps into existing wisp)
- Preserve audit trail of modifications


Digest: mol-deacon-patrol

Patrol 19 complete


Polecat doesn't auto-start after spawn inject - requires manual nudge

## Problem

After `gt spawn --issue <id> --create`, the polecat session shows Claude Code started with the injected prompt, but Claude doesn't begin processing. The prompt just sits there until manually nudged.

## Evidence

```
$ gt spawn --issue gt-rixa --rig gastown --create
...
✓ Session started. Attach with: gt session at gastown/furiosa
  Polecat nudged to start working
  Witness notified to monitor startup
```

Session shows:
```
> You have a work assignment. Run 'gt mail inbox' to see it, then start
  working on issue gt-rixa.

  ⏵⏵ bypass permissions on (shift+tab to cycle)
```

But Claude doesn't respond. Manual nudge required:
```
$ gt nudge gt-gastown-furiosa "Please start working..."
```

After nudge, polecat immediately starts working correctly.

## Hypothesis

The spawn inject happens before Claude Code is fully initialized. The text arrives in the input buffer, but Claude hasn't started listening yet. By the time Claude starts, the input has already been 'consumed' as initial prompt text but not submitted.

## Resolution Plan

This will be solved by the **polecat molecule workflow** (mol-polecat-work), which provides structured lifecycle management. The molecule approach handles startup, work, and shutdown as discrete steps with proper state tracking.

**Blocked on**: beads/crew/dave completing ephemeral molecules (bd mol bond, ephemeral beads repo).

## Workaround

For now, use `gt nudge` if a polecat doesn't start within ~30 seconds of spawn.


Digest: mol-deacon-patrol

Patrol 4: Core healthy, no changes


bd mol current: soft cursor showing current/next step

Add bd mol current command for molecule navigation orientation.

## Usage

bd mol current [mol-id]

If mol-id given, show status for that molecule.
If not given, infer from in_progress issues assigned to current agent.

## Output

You're working on molecule gt-abc (Feature X)

  [checkmark] gt-abc.1: Design
  [checkmark] gt-abc.2: Scaffold
  [checkmark] gt-abc.3: Implement
  [arrow] gt-abc.4: Write tests [in_progress] <- YOU ARE HERE
  [circle] gt-abc.5: Documentation
  [circle] gt-abc.6: Exit decision

Progress: 3/6 steps complete

## Key behaviors
- Shows full molecule structure with status indicators
- Highlights current in_progress step
- If no in_progress, highlights first ready step
- Works without explicit cursor tracking (inferred from state)

## Implementation notes
- Query children of mol-id
- Sort by dependency order
- Find first in_progress or first ready
- Format with status indicators

## Beads feature
This is a bd command - needs implementation in beads repo.


Daemon: Implement exponential backoff based on activity file

Modify daemon heartbeat logic to use exponential backoff when idle.

Read ~/gt/daemon/activity.json on each heartbeat.
Compute idle duration: time.Since(activity.Timestamp)
Adjust next ticker interval based on idle duration:

| Idle Duration | Next Heartbeat |
|---------------|----------------|
| 0-5 min       | 5 min (base)   |
| 5-15 min      | 10 min         |
| 15-45 min     | 30 min         |
| 45+ min       | 60 min (max)   |

Implementation:
1. Add activity file reading to daemon heartbeat
2. Replace fixed ticker with dynamic interval calculation
3. Reset to base interval when activity is fresh

No counter tracking needed - pure discovery from single timestamp.

CROSS-RIG DEPENDENCY: Also requires beads:bd-v8ku (assigned to beads/crew/dave)


Create StartupNudge function with seance metadata

## Location
internal/session/startup.go (new file) or internal/agent/startup.go

## Interface
```go
type StartupNudgeConfig struct {
    Recipient   string  // gastown/crew/gus, deacon, gastown/witness
    Sender      string  // mayor, deacon, self (for handoff)
    Topic       string  // mol-id, "cold-start", "handoff", "assigned"
    MolID       string  // Optional: specific molecule being worked
}

func StartupNudge(t *tmux.Tmux, session string, cfg StartupNudgeConfig) error
```

## Nudge Format
```
[GAS TOWN] gastown/crew/gus ← deacon • 2025-12-30T15:42 • assigned:gt-abc12
```

This becomes the session title in Claude Codes `/resume` picker.

## Implementation
1. Build the formatted message from config
2. Call t.NudgeSession(session, message) for reliable delivery
3. Return error if session doesnt exist or nudge fails

## Key Insight
The message content doesnt trigger GUPP - CLAUDE.md and hooks handle that.
The metadata makes sessions identifiable in `/resume`.


implement

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

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

Depends: load-context


load-context

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

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


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 133: All healthy


Move disk 2: B → A

Move disk 2 from peg B to peg A. (Move 286/1023)


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 419/1023)


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

Patrol 13: quiet


Digest: mol-deacon-patrol

Patrol 4: 8 sessions active, all healthy


Haiku-based smart stuck detection

Use Haiku to analyze tmux state when signals are ambiguous.

## When to Invoke

Only as escalation tier:
1. Keepalive is stale (> 2 min)
2. Tmux shows claude is running (not idle shell)
3. Heuristics can't determine state

## Prompt

Capture last 50 lines of tmux pane, ask Haiku:
'Is this Claude agent: WORKING | STUCK | IDLE | WAITING_FOR_HUMAN?'

## Cost

~$0.001 per check. At 1 check/min worst case = $0.06/hour.
In practice, most checks avoided by keepalive signal.

## Configuration

```toml
[daemon]
smart_detection = true
smart_model = "haiku"
smart_threshold = "2m"  # only check if stale > 2min
```


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Idle Town Principle

**The Deacon should be silent/invisible when the town is healthy and idle.**

- Skip HEALTH_CHECK nudges when no active work exists
- Sleep 60+ seconds between patrol cycles (longer when idle)
- Let the feed subscription wake agents on actual events
- The daemon (10-minute heartbeat) is the safety net for dead sessions

This prevents flooding idle agents with health checks every few seconds.

## Second-Order Monitoring

Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor.

The Deacon's agent bead last_activity timestamp is updated during each patrol
cycle. Witnesses check this timestamp to verify health.


Digest: mol-deacon-patrol

Patrol 11: OK


mol-polecat-lease

Semaphore tracking a single polecat's lifecycle.
Vars: {{polecat}}, {{issue}}

Used by Witness to track polecat lifecycle during patrol. The Witness bonds
this proto for each active polecat, creating a lease that tracks the polecat
from spawn through work to cleanup.

## Step: boot
Spawned. Verify it starts working.

Check if the polecat is alive and working:
```bash
gt peek {{polecat}}
```

If idle for too long, nudge:
```bash
gt nudge {{polecat}} "Please start working on your assigned issue."
```

Timeout: 60s before escalation to Mayor.

## Step: working
Actively working. Monitor for stuck.

The polecat is processing its assigned issue ({{issue}}).
Monitor via peek. Watch for:
- Progress on commits
- Status updates in beads
- SHUTDOWN mail when done

Wait for SHUTDOWN signal from the polecat.
Needs: boot

## Step: done
Exit received. Ready for cleanup.

The polecat has completed its work and sent SHUTDOWN.
Perform cleanup:
```bash
gt session kill {{polecat}}
gt worktree prune {{polecat}}
```

Update beads state and close the lease.
Needs: working


gt nudge should accept flexible session identifiers

Currently `gt nudge` requires the exact tmux session name (e.g., `gt-gastown-furiosa`).

It should also accept:
- `gastown/furiosa` (rig/polecat format)
- `furiosa` (polecat name, infer rig from cwd or require if ambiguous)

The session list command shows `gastown/furiosa` format, but nudge rejects it:
```
gt session list → shows 'gastown/furiosa'
gt nudge gastown/furiosa 'msg' → 'session not found'
gt nudge gt-gastown-furiosa 'msg' → works
```

Should normalize all these formats to the tmux session name internally.


Digest: mol-deacon-patrol

Patrol 248 complete: no incidents


Implement town activity logging

Add centralized logging for Gas Town agent lifecycle events.

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

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

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


Execute registered plugins

Execute registered plugins.

Scan ~/gt/plugins/ for plugin directories. Each plugin has a plugin.md with TOML frontmatter defining its gate (when to run) and instructions (what to do).

See docs/deacon-plugins.md for full documentation.

Gate types:
- cooldown: Time since last run (e.g., 24h)
- cron: Schedule-based (e.g., "0 9 * * *")
- condition: Metric threshold (e.g., wisp count > 50)
- event: Trigger-based (e.g., startup, heartbeat)

For each plugin:
1. Read plugin.md frontmatter to check gate
2. Compare against state.json (last run, etc.)
3. If gate is open, execute the plugin

Plugins marked parallel: true can run concurrently using Task tool subagents. Sequential plugins run one at a time in directory order.

Skip this step if ~/gt/plugins/ does not exist or is empty.


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


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 587/1023)


Digest: mol-deacon-patrol

Patrol 16: All healthy


mol-deacon-patrol


Epic: Patrol parity - Witness and Refinery match Deacon sophistication

Bring Witness and Refinery patrols up to Deacon's level of sophistication.

## Deacon has
1. Defined patrol molecule with clear steps
2. Wisp-based execution (spawn, run, squash, loop)
3. Daemon monitoring with auto-nudge on naked state
4. Handoff bead attachment mechanism
5. Banners for observability
6. Context-aware loop-or-exit

## Children
- gt-h1n5: Witness patrol: Add banners and wisp-based execution
- gt-qz2l: Refinery patrol: Add banners and wisp-based execution
- gt-poxd: Create handoff beads for Witness and Refinery roles

## Success criteria
- Tailing a Witness or Refinery session shows clear banners
- Patrols spawn as wisps and squash to digests
- Each role has a handoff bead with attached_molecule
- Daemon monitors Witness/Refinery nakedness (stretch goal)


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 579/1023)


Digest: mol-deacon-patrol

Patrol cycle 1: All 6 agents healthy, no issues, inbox clean


Merge: toecutter-mjwlby4g

branch: polecat/toecutter-mjwlby4g
target: main
source_issue: toecutter-mjwlby4g
rig: gastown
agent_bead: gt-gastown-polecat-toecutter


Digest: mol-deacon-patrol

Patrol 4: MQ cleared (0), all healthy


Digest: mol-deacon-patrol

Cycle 296: All healthy, refinery active


Witness tmux status: show polecat count under management

Add witness-specific status line showing:
- Number of polecats under management
- Active/idle status
- Maybe: last nudge time, blocked count

Implement in runWitnessStatusLine() in internal/cmd/statusline.go


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 561/1023)


Digest: mol-deacon-patrol

Patrol cycle 19: nominal


README: Add beads (bd) as prerequisite

CRITICAL: gt calls bd via exec.Command for sling, cook, gate, etc. but beads is not listed in Prerequisites. Users following Quick Start will fail.

Fix:
1. Add 'beads (bd)' to Prerequisites with install link
2. Move Prerequisites section BEFORE Quick Start
3. Add version requirements for each dep


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


Fetch remote and identify polecat branches waiting.

```bash
git fetch origin
git branch -r | grep polecat
gt refinery queue <rig>
```

If queue empty, skip to context-check step.
Track branch list for this cycle.

instantiated_from: mol-refinery-patrol
step: queue-scan


BUG: Daemon heartbeat too slow (10 min) for stuck agent detection

The daemon's recoveryHeartbeatInterval is set to 10 MINUTES (daemon.go:134).

This means if an agent gets stuck, it can waste up to 10 minutes before Boot is spawned to triage.

Suggested fix: Reduce heartbeat to 2-3 minutes, or add a separate fast-path stuck-detection check.

Related: hq-nsx0g


Digest: mol-deacon-patrol

Patrol cycle 19: All 3 rigs healthy. No incidents.


Digest: mol-deacon-patrol

Patrol 14: All agents healthy


Digest: mol-deacon-patrol

Patrol cycle 7


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor.

The Deacon's agent bead last_activity timestamp is updated during each patrol
cycle. Witnesses check this timestamp to verify health.


Remove gt context --usage estimation

The external context estimation approach doesn't work:
- tmux scrollback trick breaks with --cycle
- Can't see actual context usage from outside
- Only the agent knows its internal state

Remove or deprecate:
- gt context --usage flag
- Any external context monitoring logic

Document why in comments: 'Context management is agent-initiated, not externally monitored.
See self-check guidance in role templates.'

This is P3 because it's cleanup - the feature just doesn't work, not actively harmful.


Session test with rig


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Idle Town Principle

**The Deacon should be silent/invisible when the town is healthy and idle.**

- Skip HEALTH_CHECK nudges when no active work exists
- Sleep 60+ seconds between patrol cycles (longer when idle)
- Let the feed subscription wake agents on actual events
- The daemon (10-minute heartbeat) is the safety net for dead sessions

This prevents flooding idle agents with health checks every few seconds.

## Second-Order Monitoring

Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor.

The Deacon's agent bead last_activity timestamp is updated during each patrol
cycle. Witnesses check this timestamp to verify health.


Restart daemon to activate Boot integration

The running daemon (PID 70670) predates Boot code. Daemon heartbeat() now calls ensureBootRunning() but the stale process never executes it.

Fix: gt shutdown --force && gt start

This is a one-time operational task, not a code change.


Add bash/zsh shell completions for gt CLI

Need shell autocompletions for the gt command to improve usability.

Should support:
- Bash completions
- Zsh completions
- Fish completions (nice to have)

Cobra has built-in completion generation:
  cmd.GenBashCompletion()
  cmd.GenZshCompletion()

Need to:
1. Add 'gt completion bash/zsh/fish' command
2. Document installation in README
3. Consider auto-install during 'gt init' or first run


Move disk 2: C → B

Move disk 2 from peg C to peg B. (Move 354/1023)


Move disk 3: A → B

Move disk 3 from peg A to peg B. (Move 556/1023)


Digest: mol-deacon-patrol

Patrol 105: All agents healthy, no incidents


MQ events in gt feed

Wire MQ lifecycle events to gt feed display.

After 'Refinery emits activity events' is done:
- gt feed should show merge_started, merged, merge_failed events
- Format: timestamp, event type, MR details
- Color coding: green for merged, red for failed

Example output:
  19:45 MERGED gt-abc123 (polecat/nux → main)
  19:42 MERGE_STARTED gt-abc123
  19:40 MERGE_FAILED gt-xyz789 (conflict)


Test refile v2

Testing improved refile

(Refiled from bd-eysh)


Move disk 2: A → C

Move disk 2 from peg A to peg C. (Move 458/1023)


Digest: mol-deacon-patrol

Patrol 15: all healthy


Digest: mol-deacon-patrol

Patrol 9: healthy


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 81/1023)


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 901/1023)


Molecule composition: Includes directive

Add support for molecule composition via the Includes directive.

## Format
\`\`\`markdown
## Molecule: derived-name
Includes: mol-base-molecule

## Step: additional-step
Additional instructions here.
Needs: step-from-base
\`\`\`

## Implementation
1. Add \`Includes:\` parsing to ParseMoleculeSteps()
2. Resolve included molecule by ID
3. Merge steps from included molecule
4. Allow new steps to depend on included steps
5. Support multiple includes (polymers)

## Files to modify
- internal/beads/molecule.go
- internal/beads/molecule_test.go

## Acceptance
- [ ] Parse Includes directive
- [ ] Resolve and merge included steps
- [ ] Dependencies across molecules work
- [ ] Multiple includes supported
- [ ] Tests cover composition scenarios


Daemon: proper rig discovery

Currently discovers rigs by scanning tmux session names for gt-*-witness pattern. Should instead:
- Read rigs from mayor/rigs.json
- Or scan town directory for .gastown subdirs
- Handle rigs that exist but don't have running witnesses


Resolve external dependencies

Resolve external dependencies across rigs.

When an issue in one rig closes, any dependencies in other rigs should be notified. This enables cross-rig coordination without tight coupling.

**Step 1: Check recent closures from feed**
```bash
gt feed --since 10m --plain | grep "✓"
# Look for recently closed issues
```

**Step 2: For each closed issue, check cross-rig dependents**
```bash
bd show <closed-issue>
# Look at 'blocks' field - these are issues that were waiting on this one
# If any blocked issue is in a different rig/prefix, it may now be unblocked
```

**Step 3: Update blocked status**
For blocked issues in other rigs, the closure should automatically unblock them (beads handles this). But verify:
```bash
bd blocked
# Should no longer show the previously-blocked issue if dependency is met
```

**Cross-rig scenarios:**
- bd-xxx closes → gt-yyy that depended on it is unblocked
- External issue closes → internal convoy step can proceed
- Rig A issue closes → Rig B issue waiting on it proceeds

No manual intervention needed if dependencies are properly tracked - this step just validates the propagation occurred.


Digest: mol-deacon-patrol

Cycle 192: Nominal


Digest: mol-deacon-patrol

Patrol 10: All healthy, no messages


Digest: mol-deacon-patrol

Patrol 3: Routine


Move disk 3: A → B

Move disk 3 from peg A to peg B. (Move 772/1023)