Wire up --wisp flag for ephemeral molecules

The --wisp flag exists in gt sling but isn't wired through to bd mol.

Need:
1. bd mol run --wisp - spawn to .beads-wisp/ instead of .beads/
2. Automatic burn on molecule completion
3. Optional squash to digest for audit trail

Currently:
- sling.go sets thing.IsWisp but never uses it
- bd mol run has no --wisp flag
- .beads-wisp/ exists but nothing writes to it


gt swarm: Not discovering tasks from epic dependents

gt swarm create/start shows '0 tasks loaded' even when epic has dependents.

Repro:
1. Create epic gt-hw6
2. Create tasks and add deps: bd dep add gt-xxx gt-hw6
3. gt swarm create gastown --epic gt-hw6 --worker Toast
4. Swarm shows 'Tasks: 0'

Expected: Swarm should discover tasks that depend on the epic.
Actual: Shows '(no tasks loaded)'


Recreate beads rigs with fresh clones

## Problem

Beads rigs have schema mismatches (missing thread_id column, etc.) from development iteration.

## Tasks

1. Shut down any active polecats
2. Delete existing beads rigs: mayor/rig, refinery/rig, witness/rig, crew/*
3. Re-clone from beads repo
4. Run bd init in each new clone

## Rigs to recreate

- /Users/stevey/gt/beads/mayor/rig
- /Users/stevey/gt/beads/refinery/rig
- /Users/stevey/gt/beads/crew/* (if any)

## Source

Clone from beads repo (need to confirm remote URL)


Digest: mol-deacon-patrol

Patrol 2: Routine, all healthy


Digest: mol-deacon-patrol

Patrol 12: clear


Merge: gt-70b3

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


Deacon initialization with patrol molecule

Ensure Deacon always has mol-deacon-patrol on hook.

## The Problem

When Deacon starts fresh, it needs:
1. mol-deacon-patrol bonded
2. Molecule pinned to hook
3. Ready to execute

## Solution: Bootstrap in gt prime

When gt prime runs for role=deacon:

1. Check if mol-deacon-patrol exists on hook
2. If not: bond it and pin it
3. Then show execution prompt

```go
// In prime.go for deacon role
if role == "deacon" && !hasPatrolOnHook() {
    // Bond the patrol formula
    bondDeaconPatrol()
    // Pin to hook
    pinToHook("deacon", patrolMolID)
}
```

## Alternative: Deacon Startup Hook

Or handle in Deacon SessionStart hook:
- Check hook
- If empty: gt mol bond mol-deacon-patrol && gt mol pin

## Files

- cmd/gt/prime.go (if handling in prime)
- deacon/hooks/session-start.sh (if handling in hook)


Digest: mol-deacon-patrol

Patrol 8


Error handling improvements: retry logic and recovery hints

Improve error handling across GGT codebase.

## Issues Found

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

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

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

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

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

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

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

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

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

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


Polecat identity confusion leads to direct main commits

When polecats spawn without proper identity (due to gt-si6am and gt-y41ep):
1. They think they're mayor
2. They may commit from wrong directory (mayor/rig instead of polecats/<name>)
3. Work goes directly to main, bypassing merge queue

Observed: slit pushed 'gt polecat recycle' directly to main instead of its branch

Root cause:
- gt-si6am: Wrong CLAUDE.md template (says 'Mayor Rig Context')
- gt-y41ep: Env vars not exported (falls back to mayor detection)

Impact: CRITICAL - merge queue entirely bypassed, no refinery review


Deacon rounds molecule: the kernel execution loop

Define and implement mol-deacon-rounds - the molecule the Deacon executes each wake.

## Molecule Definition

```markdown
# mol-deacon-rounds

The Deacon kernel - executed on every wake cycle.

## Step: session-check
Check context budget. If low, cycle to fresh session.
Needs: (none - always runs first)

## Step: health-scan
Check health of all monitored agents:
- Mayor (gt-mayor session)
- Witnesses (gt-*-witness sessions)
- Refineries (gt-*-refinery sessions)
- Crew (gt-*-* sessions, lifecycle only)

Remediate unhealthy agents:
- Restart dead sessions
- Nudge stuck agents
- Escalate if remediation fails

Needs: session-check

## Step: lifecycle
Process pending lifecycle requests from mail:
- LIFECYCLE: <identity> requesting cycle
- LIFECYCLE: <identity> requesting restart
- LIFECYCLE: <identity> requesting shutdown

Needs: health-scan

## Step: plugins
Run due plugins:
1. Check scheduled plugins (cron-like)
2. Check event-triggered plugins (from mail events)
3. Check human-requested plugins (from mail)
4. Execute each, record outcomes

Needs: lifecycle

## Step: events
Process timer and event callbacks:
- TIMER: <identity> wake at <time>
- EVENT: <type> <payload>

Notify relevant agents or trigger actions.

Needs: plugins

## Step: complete
Finalize the cycle:
1. Update heartbeat.json
2. Clear/update keepalive.json
3. Log summary
4. Return to prompt (wait for next wake)

Needs: events
```

## Execution

The Deacon reads this molecule from its pinned bead and executes steps in order.
Each step:
1. Announces "Starting <step>"
2. Performs the step logic
3. Records outcome
4. Proceeds to next step

## Step Plugins

Each step can have inline plugins (additional attention):
```yaml
step: health-scan
  plugins:
    - advanced-diagnostics  # Run if basic scan finds issues
```

## Relation to bd mol

Uses the same molecule format as bd mol for polecats.
Deacon molecule is special: it's the kernel, not work.


gt refinery attach: Attach to refinery session

Add 'gt refinery attach [rig]' command to attach to refinery tmux session.

Mirrors 'gt mayor attach' pattern. If rig not specified, use current rig context.


Digest: mol-deacon-patrol

Patrol: 2 completions (valkyrie gt-yd98 MQ, scrotus gt-mzal.1 boot proto). 8 polecats working.


Digest: mol-deacon-patrol

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


gt crew add: Agent bead uses hardcoded gt- prefix instead of rig prefix

When running 'gt crew add <name> --rig beads', the command tries to create an agent bead with ID 'gt-crew-beads-<name>' but beads rig uses 'bd' prefix.

Error: issue ID 'gt-crew-beads-grip' does not match configured prefix 'bd'

The agent bead ID should use the target rig's prefix, not hardcode 'gt-'.


Patrol hygiene: Witness/Refinery/Deacon inbox cleanup

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

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 18: Nominal


ensure-beads-refinery

Start the beads rig Refinery.

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

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

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

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


Digest: mol-deacon-patrol

Patrol #19: Stable


gt-gastown-polecat-valkyrie

gt-gastown-polecat-valkyrie

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


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.


Batch wisp squashing in Deacon maintenance

Add wisp squashing to Deacon's maintenance duties:
- Patrol plugin to find orphaned/completed wisps across all rigs
- Squash completed patrol wisps to digests
- Burn abandoned wisps that have no audit value
- Part of the hygiene/cleanup system

Also: Witness polecat shutdown should spawn its own short wisp for the cleanup protocol.


Test issue 1 for displacement


Add agent abstraction layer to support non-Claude agents

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

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

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


implement

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

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

Depends: load-context


Digest: mol-deacon-patrol

Patrol cycle 9: nominal


Digest: mol-deacon-patrol

Patrol 11: nominal


Digest: mol-deacon-patrol

Patrol 11


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

Patrol 17: All healthy


Merge: rictus-1766959789036

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


Automatic spawn for ready work

Auto-spawn polecats for ready work:

When Witness has capacity (active_workers < max_workers):
1. Query bd ready for unblocked issues
2. Filter to rig-appropriate work (by prefix or epic)
3. For each ready issue up to capacity:
   - gt spawn --issue <id>
   - Track that we spawned for this issue

Configuration (in rig config.json):
- max_workers: 4 (default)
- spawn_delay: 5s between spawns
- auto_spawn: true/false

This enables 'fire and forget' swarming:
- Mayor creates epic with children
- Mayor tells Witness to work epic
- Witness spawns polecats automatically
- Witness cleans up as they complete


Immediate daemon notification on lifecycle request

When gt handoff sends a lifecycle request to the daemon (via bd mail send deacon/), the daemon only discovers it on its next heartbeat poll (every 5 min). Workers wait unnecessarily for retirement.

## Current Behavior
1. Worker runs gt handoff
2. Lifecycle mail sent to deacon/ via beads
3. Worker blocks waiting for retirement
4. Daemon heartbeat runs (up to 5 min later)
5. Daemon processes lifecycle request and kills session

## Expected Behavior
Lifecycle requests should be processed immediately, not on poll interval.

## Proposed Solutions

### Option A: SIGUSR1 Signal (simplest)
1. Add SIGUSR1 handler in daemon that calls ProcessLifecycleRequests()
2. gt handoff reads daemon.pid and sends SIGUSR1 after mail send

### Option B: Unix Socket
1. Daemon listens on ~/gt/daemon/lifecycle.sock
2. gt handoff connects and sends 'process' message

### Option C: Watchfile + fsnotify
1. gt handoff touches ~/gt/daemon/lifecycle.trigger
2. Daemon uses fsnotify to detect and process

Recommend Option A for simplicity.

## Affected Files
- internal/daemon/daemon.go - add signal handler
- internal/cmd/handoff.go - send signal after mail


Daemon: integration test with real lifecycle

Need an end-to-end test that:
1. Starts daemon
2. Starts a test agent session
3. Sends lifecycle request to daemon
4. Verifies session was killed and restarted
5. Cleans up

Could use a mock 'agent' that's just a shell script.


Digest: mol-deacon-patrol

Patrol 20: All agents healthy - triggering handoff


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

## Summary

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

## Current Behavior

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

## Desired Behavior

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

## Implementation Notes

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

## Edge Cases

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


Crew CLAUDE.md overwritten by polecat template

The crew worker CLAUDE.md files (crew/max, crew/joe, crew/jack) have been overwritten with the polecat-CLAUDE.md template content.

**Expected**: Crew workers should have mayor-like context (they share the rig repo, work on main branch, plan work)

**Actual**: Crew CLAUDE.md says 'Polecat ace' and includes polecat-specific instructions (don't push to main, wait for Witness to kill session, etc.)

**Impact**:
- Crew workers getting wrong identity context
- Ready Front planning guidance in gotchas section was lost
- Crew workers told not to push to main (but they should)

**Root cause**: Likely gt polecat template deployment hit crew directories

**Fix needed**:
- Restore crew CLAUDE.md template (or create one if missing)
- Ensure template deployment only targets polecats/ directories
- Add Ready Front planning section to crew template


Daemon sends heartbeat to Mayor instead of Deacon


Digest: mol-deacon-patrol

Patrol 20: routine, handing off


Digest: mol-deacon-patrol

Patrol cycle 16: nominal


Digest: mol-deacon-patrol

Patrol 20: Mayor OK, 11 sessions


CLI: cleanup commands for stale state

Cleanup commands for recovering from stale state.

## Commands

### gt cleanup <polecat>
Clean stale state for specific polecat.
```
gt cleanup <rig>/<polecat> [--dry-run]
```

Actions:
- Remove orphaned state.json if clone missing
- Clear stale session references
- Reset stuck state (working → idle after timeout)

### gt cleanup --all
Clean all polecats in workspace.
```
gt cleanup --all [--dry-run]
```

## Implementation
```go
func CleanupPolecat(rigName, polecatName string, dryRun bool) (*CleanupResult, error)

type CleanupResult struct {
    OrphanedStateRemoved bool
    SessionsCleared      int
    StateReset           bool
    Warnings             []string
}
```

## Overlap with Doctor
- Doctor diagnoses and offers --fix
- Cleanup is more aggressive state recovery
- Consider merging into doctor --fix

## New File
internal/cmd/cleanup.go

## Acceptance Criteria
- [ ] Removes orphaned state files
- [ ] Clears stale session refs
- [ ] --dry-run shows what would happen
- [ ] Reports all actions taken


Swarm learning: Refinery merge queue automation

Manually merging 15 polecat branches was painful and error-prone. Refinery should automate: detect completed work, run tests, merge to main, handle conflicts. This is core Refinery value prop.


Digest: mol-deacon-patrol

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


gt spawn pins mol-polecat-work

Enhance gt spawn to automatically pin mol-polecat-work to new polecats.

## Current Behavior

gt spawn --issue gt-xxx creates polecat with issue in mail but no molecule.

## New Behavior

gt spawn should:
1. Create fresh worktree (existing)
2. Initialize polecat beads (existing)
3. Bond mol-polecat-work with issue as variable
4. Pin the molecule to polecat hook
5. Create polecat session

## Molecule Bonding

```bash
bd mol bond mol-polecat-work --var issue_id=gt-xxx --var rig=gastown
```

Creates .beads/molecules/mol-polecat-work-<hash>.state.yaml

## Hook Pinning

```bash
bd pin mol-polecat-work-<hash> --for polecat/<name>
```

Creates .beads-wisp/hook-polecat-<name>.wisp.yaml pointing to molecule

## Files

- internal/spawn/spawn.go - Add mol-polecat-work bonding
- Or: internal/cmd/spawn.go depending on structure


Check own context limit

Check own context limit.

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

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

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

This enables the Deacon to burn and respawn cleanly.


Digest: mol-deacon-patrol

Patrol 13: Quiet


Witness role describes incorrect commands

prompts/roles/witness.md line 44 shows:
  gt polecat list {{ rig }}

But polecats are worktrees managed by Refinery/Mayor's clone,
not a separate polecat manager. The witness shouldn't manage
polecats directly.

Review and update witness role to match actual architecture.


Digest: mol-deacon-patrol

Patrol 18: all clear


Design: Deacon exponential backoff when town is idle

## Problem

When the user goes to sleep and all workers have stopped, the Deacon continues
polling at full speed (every daemon heartbeat). This wastes resources and creates
unnecessary log noise.

## Requirements

1. **Gradual slowdown**: Deacon patrol frequency should exponentially back off
   when there's no activity in the town.

2. **Wake on activity**: Any gt or bd command (human or agent) should reset
   the backoff immediately.

3. **Daemon coordination**: Deacon needs to "program" the daemon for next
   heartbeat duration.

## Design Considerations

### Option A: Daemon tracks last-activity timestamp
- Daemon mechanically slows heartbeat based on time since last activity
- Activity signal comes from gt/bd command hooks
- Simple, no Deacon state needed
- But: Daemon is "dumb" by design - adding intelligence here feels wrong

### Option B: Deacon tracks patrol-count-since-activity
- Deacon maintains counter (via label on patrol molecule or state.json)
- Each idle patrol increments counter, calculates next sleep
- Deacon tells daemon "wake me in X minutes"
- More complex but keeps intelligence in AI agent

### Option C: Discovery-based (preferred if feasible)
- Can we detect "no activity" without explicit counters?
- Ideas:
  - Compare current time to last git commit across all clones?
  - Check tmux activity timestamps?
  - Look at beads modification times?
- Pros: No state to manage
- Cons: May be expensive to compute, could miss activity

### Wake mechanism
- Hook on gt commands (gt.hooks.pre-command or similar)
- Hook on bd commands (bd.hooks.pre-command)
- Both should signal daemon (SIGUSR2?) or write to activity file
- Daemon reads activity file on each heartbeat, resets backoff if fresh

### Backoff schedule (strawman)
| Idle patrols | Next heartbeat |
|--------------|----------------|
| 0-5          | 5 min (default) |
| 6-10         | 10 min |
| 11-20        | 30 min |
| 21+          | 60 min (max) |

## Open Questions

1. Should tmux activity count as "activity"? (cursor movement, typing)
2. How does Deacon communicate next-heartbeat to daemon?
3. Where to store backoff state? (daemon state.json? deacon state.json? label?)
4. Should wake be instant (SIGUSR) or next-heartbeat?

## Implementation Sketch

Activity file written by hooks at ~/gt/daemon/activity.json:
- last_command: timestamp
- command: the gt/bd command that ran
- actor: who ran it


Multiple merge/landing models documented without clarification

Architecture describes three different merge models:

1. Refinery-only model (traditional)
2. Direct landing model (Mayor bypass)
3. Swarm integration branch model (internal/swarm)

These appear to conflict. Documentation should clarify:
- Which model is canonical?
- When to use which model?
- Or are some deprecated?

This is confusing for users trying to understand the system.


Digest: mol-deacon-patrol

Patrol complete: checked inbox (0 msgs), health check passed (all rigs healthy), cleaned 3 stale locks


Digest: mol-deacon-patrol

Patrol 5: Nominal


Digest: mol-deacon-patrol

Patrol 6: clear


Digest: mol-deacon-patrol

Patrol 12: Quiet


Swarm: ephemeral rig support

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 14: all healthy


Agent Communication Protocol

Structured mail between agents for coordination.

## Problem
Agents communicate via freeform mail. This works but lacks parseable structure for automated handling.

## Protocols

### Witness to Refinery
- POLECAT_READY: worker X completed, branch ready for merge
- REWORK_COMPLETE: worker Y finished requested rework

### Refinery to Witness
- MERGE_SUCCESS: worker X merged, can be cleaned up
- MERGE_FAILED: worker X needs rework (reason attached)
- REWORK_REQUEST: please have worker X rebase on current main

### Polecat to Witness
- WORK_COMPLETE: done with assigned issue
- NEED_HELP: stuck, requesting intervention

### Any to Deacon
- ESCALATION: problem requiring Mayor attention

## Mail Format
Subject: [PROTOCOL_TYPE] brief description
Body: Structured YAML or JSON payload

## Success Criteria
- Patrol steps parse protocol messages automatically
- Handlers exist for each protocol type
- Integration test: polecat completes -> witness notifies -> refinery merges -> witness cleans up

Consolidates gt-0qki.


mol-polecat-work minimal formula

Create minimal mol-polecat-work formula for tracer bullet.

## Simplified Steps

For v0.1, just the essentials:

```yaml
formula: mol-polecat-work-v01
description: Minimal polecat work cycle for tracer bullet.
version: 1

variables:
  - name: issue_id
    required: true
  - name: rig
    required: true

steps:
  - id: claim
    title: Claim the issue
    description: |
      Run: bd update {{issue_id}} --status=in_progress
      Read the issue: bd show {{issue_id}}
      Understand what needs to be done.

  - id: work
    title: Implement the fix
    needs: [claim]
    description: |
      Do the work described in the issue.
      Make your changes.
      Test them.
      Commit with issue ID: git commit -m "description ({{issue_id}})"

  - id: submit
    title: Submit for review
    needs: [work]
    description: |
      Push your branch: git push -u origin <branch>
      Create MR: bd create "Merge: {{issue_id}}" --type=merge-request
      Close issue: bd close {{issue_id}}
      Sync: bd sync
      Signal done: gt mail send witness/ -s "LIFECYCLE: Done"
```

## Location

.beads/formulas/mol-polecat-work-v01.formula.yaml

## Notes

- Keep it simple for tracer bullet
- Full mol-polecat-work can be more elaborate
- This proves the pattern works


Digest: mol-deacon-patrol

Patrol 16: Quiet


Add bulk session stop command (gt session stop --all)

When decommissioning a rig, need to stop multiple sessions one at a time. A --all or --rig flag would allow: gt session stop --rig gastown


Digest: mol-deacon-patrol

Patrol 11: routine


mol-deacon-patrol

Mayor's daemon patrol loop.

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

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


Witness patrol: cleanup idle orphan polecats

Add patrol step to find and cleanup polecats that are idle with no assigned issue. These orphans occur when polecats crash before sending DONE or Witness misses the message. Patrol should verify git is clean before removing worktree. Part of gt-rana.


Digest: mol-deacon-patrol

Patrol: no callbacks, no polecats, all agents healthy, cleaned 2 orphan processes + 2 stale locks


MR state transitions: validate open→in_progress→closed

Enforce valid state transitions for merge-requests:

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

Invalid:
- closed → anything (immutable once closed)

Implement validation in MR update operations.

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


Optimize: Share stepMap across control flow operators

In ApplyControlFlow, both ApplyBranches and ApplyGates call buildStepMap() separately:

```go
func ApplyBranches(steps []*Step, compose *ComposeRules) ([]*Step, error) {
    stepMap := buildStepMap(steps)  // Called here
    ...
}

func ApplyGates(steps []*Step, compose *ComposeRules) ([]*Step, error) {
    stepMap := buildStepMap(steps)  // Called again here
    ...
}
```

This is inefficient for large formulas. Options:

1. Pass stepMap as a parameter to ApplyBranches/ApplyGates
2. Create an internal version that accepts stepMap, keep public API unchanged
3. Build once in ApplyControlFlow and pass down

Low priority since formula sizes are typically small.


Per-epic config overrides: custom merge settings

Allow per-epic merge configuration overrides.

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

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

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

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


Add godoc comments to exported functions

Several exported functions lack godoc comments. While not critical, adding documentation would improve code maintainability. Focus on:

- Public API functions in each package
- Exported types and their methods
- Functions that have non-obvious behavior

Can be addressed incrementally as code is touched.


Merge: gt-r6td

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


Merge: capable-1767074169502

attached_args: Code review this merge request

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


Digest: mol-deacon-patrol

Patrol #21: No messages, all agents healthy, cleaned 1 stale lock, burned 1 orphan wisp


Digest: mol-deacon-patrol

Patrol 7: Healthy. Furiosa resolved rig mismatch.


Digest: mol-deacon-patrol

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


MQ Submit: Notify Witness on MR submission

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

## Implementation

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

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

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


Mol Mall: Formula Package Registry

A package registry for sharing and discovering formulas, schematics, and protocols.

## Concept

Like npm/Maven/Go modules but for workflow definitions.

```
mol-mall.io/
├── @gastown/           # Official Gas Town formulas
│   ├── shiny
│   ├── polecat-work
│   └── release
├── @acme-corp/         # Enterprise org (private)
│   └── compliance-review
├── @linus/             # Individual publisher
│   └── kernel-review
└── community/          # Unscoped public formulas
    └── towers-of-hanoi
```

## Resolution Hierarchy

```
1. Project:  ./.beads/formulas/          # Project-specific
2. Rig:      ~/gt/<rig>/.formulas/       # Rig-wide
3. Town:     ~/gt/.formulas/             # Town-wide (mayor stdlib)
4. User:     ~/.gastown/formulas/        # Personal collection
5. Mall:     mol-mall.io/                # Published packages
```

## Package Manifest

```toml
[package]
name = "shiny"
version = "1.2.0"
description = "Engineer in a Box"
authors = ["Gas Town <gastown@example.com>"]
license = "MIT"

[dependencies]
"@gastown/review-patterns" = "^2.0"
```

## Distribution Scenarios

1. **Celebrity Formulas**: Expert-curated workflows (e.g., Linus kernel review)
2. **Enterprise Compliance**: Private company formulas
3. **Standard Library**: @gastown/* blessed defaults
4. **Community Ecosystem**: Varying quality, needs ratings/verification

## Open Questions

1. Versioning semantics - SemVer? Breaking change rules?
2. Security model - sandboxing? signing? permissions?
3. Private registries - self-hosted? authentication?
4. Lock files - formulas.lock format?

## Related

- docs/formula_evolution.md - package ecosystem parallel section
- gt-8ws7o - Schematic (can be published)
- gt-y3jb9 - Protocol (can be published)


Digest: mol-deacon-patrol

Patrol 6: routine


Refinery: Add lifecycle-notify step to patrol

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

## Implementation

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

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

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


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

Patrol 18: All healthy


Add unit tests for internal/mail router and mailbox

attached_args: Add unit tests for mail router and mailbox

Add tests for mail routing and mailbox functionality.

## Files to create
- internal/mail/router_test.go
- internal/mail/mailbox_test.go

## Test cases for router
1. Route() resolves addresses correctly
2. Route() handles rig/role format
3. Route() handles rig/crew/name format
4. Invalid addresses return error

## Test cases for mailbox
1. Send() writes message to mailbox
2. Inbox() lists messages
3. Read() retrieves message by ID
4. Delete() removes message
5. Messages persist correctly

## Acceptance criteria
- [ ] router_test.go with 4+ tests
- [ ] mailbox_test.go with 5+ tests
- [ ] Tests pass: go test ./internal/mail/...
- [ ] Coverage > 60%


Digest: mol-deacon-patrol

Patrol 13: Quick cycle, all quiet


Review: Refinery patrol template changes (gt-qz2l)

Polecat dementus implemented refinery template changes: banners, wisp-based execution, propulsion protocol. Review for correctness and consistency with witness/deacon patterns.


Digest: mol-deacon-patrol

Patrol #14


Digest: mol-deacon-patrol

Patrol 11: all clear


Digest: mol-deacon-patrol

Patrol 10: all clear


Merge: nux-1767059693217

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


Merge: dementus-1767073385126

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


Create molecules catalog directory structure

Establish the proto catalog at ~/gt/molecules/.

## Structure

```
~/gt/molecules/
├── README.md           # Catalog overview
├── lifecycle/
│   ├── gastown-boot/
│   │   └── PROTO.md    # Proto definition in markdown
│   ├── rig-spinup/
│   ├── rig-spindown/
│   └── agent-restart/
├── patrol/
│   ├── deacon-patrol/
│   ├── witness-patrol/
│   └── refinery-patrol/
└── work/
    ├── polecat-work/
    ├── code-review/
    └── feature/
```

## PROTO.md Format

```markdown
---
type: lifecycle
parallel: [ensure-witnesses, ensure-refineries]
ephemeral: true
---

# mol-gastown-boot

Town bootstrap molecule.

## Step: ensure-daemon
...
```

## Catalog Loading

`gt sling gastown-boot` should find the proto by:
1. Check beads for existing proto issue
2. If not found, scan ~/gt/molecules/lifecycle/gastown-boot/
3. Auto-create proto issue from PROTO.md

This enables file-based proto development with beads tracking.


Digest: mol-deacon-patrol

Patrol 5: all healthy


Schematic: Domain-Level Formula Composition

A Schematic is a coordinated set of formulas defining an entire workflow domain.

## Concept

Like an orchestra score to instrument parts - says how formulas fit together.

```toml
# shiny-engineering.schematic
[schematic]
name = "The Shiny Way"
description = "Full engineering workflow from issue to production"

[formulas]
planning = "@gastown/shiny"
development = "@gastown/polecat-work"
review = "@gastown/review"
release = "@gastown/release"

[flow]
default = "planning >> development >> review >> release"

[constraints]
all_code_reviewed = true
tests_before_deploy = true
```

## Relationship to Other Concepts

- **Schematic** (workflow dimension) = composition of Formulas
- **Campaign** (work dimension) = composition of Epics
- Schematic compiles to Campaign, like Formula compiles to Molecule

## Use Cases

1. Define "The Shiny Way" for a team/org
2. Encode compliance requirements
3. Standardize workflows across projects
4. Enable workflow reuse and sharing

## Open Questions

1. File format - TOML? YAML? Extend formula syntax?
2. Validation - how to verify schematic consistency?
3. Overrides - can project override org schematic?

## Related

- docs/formula_evolution.md
- gt-8tmz - molecule algebra


Merge: gt-unrd

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


Task D: Integration


Add multi-lang-shiny example and tests

Create example meta-formula and test suite.

## Example

Create .beads/formulas/multi-lang-shiny.meta.yaml:
- Generates shiny-go, shiny-python, shiny-typescript
- Each extends base-shiny with language-specific vars

## Tests

1. Test basic expansion (3 languages -> 3 formulas)
2. Test variable substitution in names and content
3. Test error on duplicate generated names
4. Test empty values list
5. Integration: cook a generated formula


Define mol-gastown-boot proto structure

Create the proto molecule definition with all steps.

## Proto Definition

Each step has:
- Title (step name)
- Description with Action/Verify/OnStall/OnFail sections
- Dependencies (Needs: directive)

## Steps

1. ensure-daemon
   - Action: gt daemon status || gt daemon start
   - Verify: daemon PID exists and responding

2. ensure-deacon
   - Action: gt deacon start
   - Verify: session exists, not stalled, heartbeat fresh
   - OnStall: gt nudge deacon/ "Start patrol."

3. ensure-witnesses (parallel container)
   - ensure-gastown-witness
   - ensure-beads-witness
   - Verify each: session exists, not stalled

4. ensure-refineries (parallel container)
   - ensure-gastown-refinery
   - ensure-beads-refinery
   - Verify each: session exists, not stalled

5. verify-town-health
   - Action: gt status
   - Verify: all expected agents shown

## Output

Proto stored in beads as molecule type issue.


Refinery background daemon mode

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

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

## Requirements

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

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

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

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

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

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

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


Add unit tests for internal/mrqueue package

attached_args: mrqueue package tests

Add comprehensive tests for the merge request queue package.

## Files to create
- internal/mrqueue/mrqueue_test.go

## Test cases to cover
1. NewQueue creates queue directory
2. Add() persists MR to queue
3. Get() retrieves MR by ID
4. List() returns all MRs
5. Remove() deletes MR
6. MR serialization/deserialization
7. Queue handles concurrent access

## Acceptance criteria
- [ ] internal/mrqueue/mrqueue_test.go created
- [ ] At least 6 test functions
- [ ] Tests pass: go test ./internal/mrqueue/...
- [ ] Coverage > 70%


Digest: mol-deacon-patrol

Patrol 2: all clear, 9 sessions healthy


Merge: valkyrie-1767074338488

attached_args: Code review

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

CODE REVIEW (furiosa): REJECT

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

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

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

This merge request has no valid changes to merge.


Speed up test suite

Tests are running slow during MQ processing. Investigate and optimize:
- Profile test execution time
- Look for slow tests (network, file I/O, sleeps)
- Consider parallel test execution
- Cache expensive setup where possible


Check own context limit

Check own context limit.

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

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

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

This enables the Deacon to burn and respawn cleanly.


Digest: mol-deacon-patrol

Patrol 18: Routine


attached_args: Standardize warning output to use style.Warning

Unify warning message formatting across codebase.

## Files to modify
- internal/cmd/start.go
- internal/cmd/polecat.go
- internal/cmd/mail.go
- Any other files with inconsistent warning output

## Current inconsistency
```go
fmt.Printf("%s Warning: ...", style.Dim.Render("⚠"))
fmt.Printf("Warning: %s", ...)
fmt.Fprintf(os.Stderr, "warning: ...")
```

## Standard pattern
Create helper if needed:
```go
// In internal/style/style.go
func PrintWarning(format string, args ...interface{}) {
    msg := fmt.Sprintf(format, args...)
    fmt.Printf("%s %s\n", Warning.Render("⚠ Warning:"), msg)
}
```

## Acceptance criteria
- [ ] PrintWarning helper in style package
- [ ] All warning outputs use consistent format
- [ ] Warnings go to stdout (not stderr) for consistency
- [ ] grep -rn 'Warning:' internal/cmd/ shows consistent format
- [ ] go build ./... passes


Digest: mol-deacon-patrol

Patrol 2: All healthy, routine


Delete hook types from internal/wisp

Delete from internal/wisp/types.go:
- WispType type
- TypeSlungWork constant
- HookPrefix, HookSuffix constants
- Wisp struct
- SlungWork struct
- NewSlungWork function
- HookFilename function
- AgentFromHookFilename function

The entire file may become empty or minimal.


Digest: mol-deacon-patrol

Patrol 6: quiet


Template and Formula Distribution: Embed in binary, detect staleness

## Problem

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

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

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

## Current State

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

## Proposed Solution

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

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

### 2. Seed formulas during setup

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

### 3. Hash-based staleness detection

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

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

### 4. Keep bd/gt boundary clean

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

## Tasks

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


Add merge-request type to Beads schema

Define the merge-request bead type with fields:
- branch: source branch (e.g., polecat/Nux/gt-xxx)
- target_branch: destination (main or integration/xxx)
- source_issue: the work being merged (gt-xxx)
- worker: who did the work
- rig: which rig
- merge_commit: (filled on close) SHA of merge commit
- close_reason: (filled on close) success/failure details

This may require beads schema changes or just convention.


gt swarm start: Does not spawn polecat sessions

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

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

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


Molecule-based Witness Patrol

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

## Key Insight

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

## Current State

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

## Target State

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

## Benefits

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

## Non-Goals

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


Add event-triggered lifecycle wisps

Commands like `gt town start` should auto-trigger lifecycle wisps.

## Event → Wisp Mapping

| Command | Triggers Wisp |
|---------|---------------|
| gt town start | mol-gastown-boot |
| gt rig add <name> | mol-rig-spinup (param: rig=<name>) |
| gt rig remove <name> | mol-rig-spindown (param: rig=<name>) |
| gt daemon restart | mol-gastown-boot |

## Implementation

1. Command implementation checks for lifecycle proto
2. Auto-slings wisp to Mayor with --wisp flag
3. Mayor picks up via molecule attachment

## Alternative: Explicit Sling

Keep commands simple, require explicit:

```bash
gt town start        # Just starts sessions
gt sling gastown-boot mayor/ --wisp   # Bootstrap with verification
```

This is more transparent - user sees the sling happen.

## Recommendation

Start with explicit sling. Add auto-trigger later once pattern proven.
`gt town start --verified` could trigger the wisp.


Test Patrol Parent

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

Original description:
Test parent for Christmas Ornament pattern


LocalConnection: Local file/exec/tmux operations

LocalConnection implementation for local machine operations.

## Implementation

```go
type LocalConnection struct {
    tmux *Tmux
}

func NewLocalConnection() *LocalConnection

func (c *LocalConnection) Name() string { return "local" }
func (c *LocalConnection) IsLocal() bool { return true }
```

## File Operations

Direct passthrough to os package:
```go
func (c *LocalConnection) ReadFile(path string) ([]byte, error) {
    return os.ReadFile(path)
}
// etc.
```

## Command Execution

Uses exec.Command:
```go
func (c *LocalConnection) Exec(cmd string, args ...string) ([]byte, error) {
    return exec.Command(cmd, args...).Output()
}
```

## Tmux

Delegates to Tmux wrapper:
```go
func (c *LocalConnection) TmuxNewSession(name, dir string) error {
    return c.tmux.NewSession(name, dir)
}
```

## Notes

Straightforward implementation - this is the "baseline" connection that SSHConnection will mirror remotely.


Digest: mol-deacon-patrol

Patrol cycle 4: All healthy


Test activity feed enhancement


Machine registry: Store and manage machine configs

Registry for managing remote machines in federation.

## Data Model

```go
type Machine struct {
    Name     string `json:"name"`
    Type     string `json:"type"`      // "local", "ssh", "gcp"
    Host     string `json:"host"`      // for ssh: user@host
    KeyPath  string `json:"key_path"`  // SSH key path
    TownPath string `json:"town_path"` // Path to town on remote
}
```

## Interface

```go
type MachineRegistry struct {
    path     string  // config/federation.json
    machines map[string]*Machine
}

func NewMachineRegistry(configPath string) *MachineRegistry
func (r *MachineRegistry) Get(name string) (*Machine, error)
func (r *MachineRegistry) Add(m *Machine) error
func (r *MachineRegistry) Remove(name string) error
func (r *MachineRegistry) List() []*Machine
func (r *MachineRegistry) Connection(name string) (Connection, error)
```

## Storage

federation.json in config/:
```json
{
  "version": 1,
  "machines": {
    "local": {"type": "local"},
    "gcp-vm-1": {
      "type": "ssh",
      "host": "user@10.0.0.1",
      "key_path": "~/.ssh/gcp_key",
      "town_path": "/home/user/ai"
    }
  }
}
```

## Connection Factory

```go
func (r *MachineRegistry) Connection(name string) (Connection, error) {
    m := r.machines[name]
    switch m.Type {
    case "local":
        return NewLocalConnection(), nil
    case "ssh":
        return NewSSHConnection(m.Host, m.KeyPath), nil
    }
}
```


mol-polecat-arm

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.

## Variables

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

## Steps

### capture
Capture recent tmux output for this polecat.

```bash
tmux capture-pane -t gt-{{rig}}-{{polecat_name}} -p | tail -50
```

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

### assess
Categorize polecat state.

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

### load-history
Read nudge history for this polecat from patrol state.

```
nudge_count = state.nudges[{{polecat_name}}].count
last_nudge_time = state.nudges[{{polecat_name}}].timestamp
```

Needs: assess

### decide
Apply the nudge matrix to determine action.

| 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

### execute
Take the decided action.

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

**pre-kill-verify**:
```bash
cd polecats/{{polecat_name}}
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: {{polecat_name}} 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

This data feeds back to the patrol's aggregate step.

Labels: [template, christmas-ornament, polecat-arm]


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


Digest: mol-deacon-patrol

Patrol complete: Restarted 5 downed agents (Mayor, 2 Witnesses, 2 Refineries). All systems now healthy.


Condition evaluator for gates and loops

Implement mechanical condition evaluation:
- step.status == 'complete'
- step.output.field == value
- children(step).all(status == 'complete')
- file.exists(path), env.VAR

Keep decidable: no arbitrary code, bounded evaluation.


Digest: mol-deacon-patrol

Patrol 8: quiet


load-state

Read handoff bead and get nudge counts.

Needs: check-refinery


test pin fix


Merge: slit-1767073379145

attached_args: Code review

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


Test Patrol Parent


Standardize error handling and warning output patterns

Inconsistent patterns across codebase:

Error wrapping:
- Some: fmt.Errorf("context: %w", err) (good)
- Some: return err (loses context)
- Some: fmt.Errorf("message", rigName) without wrapping original

Exit handling in Cobra commands:
- Some use os.Exit(1) inside RunE (bad - loses error chain)
- Some properly return errors

Warning output styles:
- fmt.Printf("%s Warning: ...", style.Dim.Render("⚠"))
- fmt.Printf("Warning: %s", ...)
- fmt.Fprintf(os.Stderr, "warning: ...")

Suggestion:
1. Always wrap errors with %w
2. Never call os.Exit() in RunE handlers
3. Standardize on style.Warning.Render("Warning:") pattern


Merge: toast-1767074299802

attached_args: Code review this merge request

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


Load context

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

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


Digest: mol-deacon-patrol

Patrol cycle 14: nominal


Polecat template: fix 'git clone' to 'git worktree'

Template says 'You're in a project git clone' but polecats use worktrees, not clones. Fix terminology.


Document Rig/Cook/Run lifecycle

The rig-cook-run.md spec is written. Propagate terminology to all relevant docs: - molecule-algebra.md (done) - molecular-chemistry.md (done) - vision.md (done) - propulsion-principle.md - sling-design.md - Any agent templates that reference cooking/instantiation


Digest: mol-deacon-patrol

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


Merge: gt-h5n.5

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


gt orphans: Find lost polecat work

Add a command to find orphaned commits that were never merged to main.

## Problem
Polecat work can get lost when:
- Session killed before merge
- Refinery fails to process
- Network issues during push

## Solution
Add `gt orphans` command that:
1. Uses `git fsck --unreachable` to find dangling commits
2. Filters to recent commits (default: 7 days, configurable)
3. Excludes stash/index entries (WIP on, index on)
4. Shows commit details and suggests recovery

## Usage
```
gt orphans              # Last 7 days
gt orphans --days=14    # Last 2 weeks
gt orphans --recover    # Interactive cherry-pick
```

## Found orphans (Dec 16-20, 2025)
- 3b146c11: Fix mail read auto-ack and add Mayor startup directive
- b6fdc561: Add persistent theme config and fix crew session theming
- 97aba756: Unify gt prime to call bd prime and mail check
- ce769ca5: Add mol-bootstrap molecule for Gas Town installation
- b2219de7: Add bootstrap documentation for new Gas Town installations
- bc82348: feat: Add gt done command (already recovered)


Digest: mol-deacon-patrol

Patrol 14: All agents healthy


Digest: mol-deacon-patrol

Patrol 8: All clear


Digest: mol-deacon-patrol

Patrol 15: routine


Digest: mol-deacon-patrol

Patrol 20: All healthy, routine cycle complete


Digest: mol-deacon-patrol

Patrol 8: All healthy


Digest: mol-deacon-patrol

Patrol 13: All clear


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


Digest: mol-deacon-patrol

Patrol 18: Quiet


Extended addressing: Parse [machine:]rig/polecat

Extended addressing for cross-machine operations.

## Address Format

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

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

## Parser

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

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

## Validation

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

## Usage

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

## Backward Compatibility

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


Polecat auto-start: Claude doesn't process initial nudge

After gt sling spawns a polecat, the 'Work slung' message is sent to tmux but Claude sits idle at the prompt. The SessionStart hook doesn't fire because no user input was received.

Observed:
- gt sling successfully spawns polecat, creates worktree, starts Claude
- Sends 'Work slung: gt-xxx. Start working on it now...' to the pane
- Claude shows idle prompt 'Try refactor...' and doesn't process the message

Expected:
- Polecat should auto-start working on the slung issue

Root cause hypothesis:
- The nudge is sent as tmux send-keys which goes to the input line
- Claude needs Enter to be pressed to submit the prompt
- SessionStart hook only fires after first user message

Workaround:
- Send extra Enter after the nudge message
- Or use daemon's triggerPendingSpawns to send Enter after Claude is ready


gt shutdown should fully cleanup polecats (worktrees, branches, inboxes)

Current gt shutdown only kills tmux sessions but leaves:

1. Git worktrees in polecats/ directory
2. Polecat branches (polecat/<name>)
3. Inbox messages
4. Beads assignee state

This causes stale polecats to be reused on next startup.

## Expected Behavior

gt shutdown should:
1. Kill all polecat tmux sessions (current behavior)
2. For each polecat with StateIdle or StateDone:
   - Remove worktree
   - Delete branch
   - Clear inbox
3. For polecats with uncommitted work:
   - REFUSE to clean up
   - Report which polecats have uncommitted changes
   - Require --force or --nuclear to proceed

## Current Code

- start.go:runImmediateShutdown() only calls t.KillSession()
- Witness cleanupPolecat() does full cleanup but isn't called by shutdown

## Fix

Either:
1. Call Witness cleanup for each polecat during shutdown
2. OR add rig.ShutdownPolecats() that does the full cleanup


Digest: mol-deacon-patrol

Patrol cycle 20: All healthy


Use atomic write pattern for state files

attached_args: Atomic write pattern for state files

Prevent data loss from concurrent state file writes using atomic write pattern.

## Files to modify
- internal/witness/manager.go
- internal/refinery/manager.go
- internal/crew/manager.go
- internal/swarm/manager.go

## Problem
State is saved directly to file - if process crashes mid-write, file corrupts.

## Implementation
Replace direct writes with atomic pattern:
```go
func atomicWriteJSON(path string, v interface{}) error {
    data, err := json.MarshalIndent(v, "", "  ")
    if err != nil {
        return err
    }
    tmpFile := path + ".tmp"
    if err := os.WriteFile(tmpFile, data, 0644); err != nil {
        return err
    }
    return os.Rename(tmpFile, path)  // Atomic on POSIX
}
```

## Acceptance criteria
- [ ] atomicWriteJSON helper created (in internal/util or similar)
- [ ] All 4 manager saveState methods use atomic writes
- [ ] .tmp files cleaned up on success
- [ ] go test ./... passes
- [ ] Manual test: state files survive kill -9


Merge: cheedo-1767074334708

attached_args: Code review

branch: polecat/cheedo-1767074334708
target: main
source_issue: cheedo-1767074334708
rig: gastown


Timed Beads: Scheduled recurring work

## Summary

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

## Schema Extension

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

## Daemon Integration

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

## Use Cases

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

## Interaction with Pinned Beads

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


Bug: MRs appearing in bd ready output

merge-request beads are showing up in `bd ready` output alongside actual work items.

## Problem
`bd ready` is meant to show work available for polecats to claim. MRs are internal workflow items processed by the Refinery, not polecat work.

## Example
```
| P1 | merge-request | bd-3zzh | Merge: bd-tvu3 |
| P1 | merge-request | bd-fcl1 | Merge: bd-au0.5 |
```

## Possible Fixes
1. **Filter by type** - `bd ready` excludes type=merge-request, type=gate, type=molecule
2. **Set assignee on submit** - MR assigned to refinery/ when created, so not 'unassigned'
3. **Set status=in_progress** - MR starts in_progress since it's queued for processing

## Related
- gate beads also showing (bd-hyp6, bd-wu62) - same issue


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

## Context

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

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

## Current behavior

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

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

## Desired behavior

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

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

## Related

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


Merge: gt-g44u.1

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


Phase 3.2: mol-polecat-work squash step

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

## Current Steps

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

## Updated Steps

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

## Step Definitions

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

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

## Update Location

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


Polecats should spawn with auto-accept mode enabled

During swarm bd-784c, polecats (Toast, Nux) were spawned without --dangerously-skip-permissions or equivalent auto-accept mode.

**Problem:**
Every edit, bash command, and tool use required manual confirmation via tmux send-keys. This defeats the purpose of autonomous polecat operation.

**Expected:**
Polecats in a swarm should run with permissions bypassed so they can work autonomously.

**Suggestion:**
- gt sling should pass --dangerously-skip-permissions by default for polecats
- Or polecats should have a .claude/settings.local.json pre-configured for auto-accept


Digest: mol-deacon-patrol

Patrol 13


Refinery patrol: Add sibling-check step

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

## Implementation

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

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


announce: shared bulletin board

Announcement channel with shared single-copy storage.

## Deliverables

1. Parse announce:name syntax
2. Shared storage model (like queue, but no claim):
   - Single message copy
   - Multiple readers can view
   - Informational, not work-creating
3. Configuration in ~/gt/config/announce.json
4. Read tracking (optional): who has seen it

## Key semantics
- Informational broadcast
- Missing is OK (not critical)
- Use case: "Bob refactoring logging, be aware"

## Dependencies
- Config directory (gt-i6jvc)
- queue: implementation (pattern reference)

## Acceptance
- announce:status creates single shared message
- Multiple agents can read
- No claim semantics (read-only)


Merge: morsov-1766966180179

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


Merge: gt-99a

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


Digest: mol-deacon-patrol

Patrol 3: all quiet


Bug: refinery --foreground detects parent session as already running

When gt refinery start gastown runs:
1. Creates tmux session gt-gastown-refinery
2. Sends 'gt refinery start gastown --foreground' into the session
3. The foreground command checks HasSession() - finds the session it's inside
4. Returns 'already running' error

The foreground mode check should either:
- Skip the tmux session check (only check PID)
- Use a different indicator that the daemon loop is running
- Pass a flag to indicate we're being called from the background starter

Workaround: Manually run 'gt refinery start gastown --foreground' from a fresh session.


Digest: mol-deacon-patrol

Patrol 6: All healthy


mol-witness-patrol

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

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

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

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

## The Christmas Ornament Shape

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


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 cycle 8: All healthy


Witness role template + CLAUDE.md

Create the Witness agent's role context:

1. internal/templates/roles/witness.md.tmpl
   - Witness responsibilities (polecat lifecycle)
   - Core loop description
   - Commands available (gt polecats, bd ready, etc)
   - Escalation protocol

2. Generate CLAUDE.md for witness/ directories
   - Context about the rig
   - Mail address: <rig>/witness
   - Startup protocol (gt prime, check inbox)

The Witness is the per-rig 'pit boss' - NOT a global coordinator. It manages polecats for ONE rig only.


Digest: mol-deacon-patrol

Patrol 20: Nominal - Handoff threshold reached


Digest: mol-deacon-patrol

Patrol 18: healthy


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

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

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

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

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

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

This is blocking the entire MQ pipeline.


Consolidate design docs into beads descriptions

The markdown design docs (swarm-shutdown-design.md, polecat-beads-access-design.md, mayor-handoff-design.md) will decay. Extract key decisions and prompting templates into the beads descriptions themselves, then archive or remove the markdown files. Beads are the source of truth.


Digest: mol-deacon-patrol

Patrol 9: Quick cycle, all quiet


Fix failing beads tests: TestIntegration and TestPolecatWorkMolecule


Merge: gt-0ei3

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


Digest: mol-deacon-patrol

Patrol 12: All healthy


Digest: mol-deacon-patrol

Quick patrol: no messages, all healthy


Create mol-install-go-binary molecule

Create a single-step molecule for building and installing the gt binary.

## Molecule Definition
\`\`\`markdown
## Molecule: install-go-binary
Single step to rebuild and install the gt binary after code changes.

## Step: install
Build and install the gt binary locally.

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

Verify the installed binary is updated:
\`\`\`
which gt
gt --version  # if we have version command
\`\`\`
\`\`\`

## Implementation
1. Add to builtin_molecules.go
2. Update SeedBuiltinMolecules to include it
3. Run gt molecule seed

## Acceptance
- [ ] mol-install-go-binary exists in beads
- [ ] Can be instantiated standalone
- [ ] Can be included by other molecules


CLI: session commands (start, stop, at, list, capture, inject)

Session management CLI commands.

## Commands

### gt session start
Start a polecat session.
```
gt session start <rig>/<polecat> [--issue <id>]
```
- Creates tmux session
- Launches claude in polecat workdir
- Optionally injects initial issue context

### gt session stop
Stop a polecat session.
```
gt session stop <rig>/<polecat> [--force]
```
- Graceful shutdown by default
- --force kills immediately

### gt session at (attach)
Attach to running session.
```
gt session at <rig>/<polecat>
```
- Attaches tmux session to current terminal
- Detach with Ctrl-B D

### gt session list
List all sessions.
```
gt session list [--rig <rig>] [--json]
```
- Shows running/stopped status
- Optionally filter by rig

### gt session capture
Capture recent output from session.
```
gt session capture <rig>/<polecat> [--lines <n>]
```
- Default: last 100 lines
- Useful for checking polecat progress

### gt session inject
Send message to session.
```
gt session inject <rig>/<polecat> -m "message"
gt session inject <rig>/<polecat> -f <file>
```
- Injects text via tmux send-keys
- Used for nudges, notifications

## Address Format

`<rig>/<polecat>` e.g., `wyvern/Toast`

## Implementation

Session subcommand group:
```go
var sessionCmd = &cobra.Command{Use: "session"}
sessionCmd.AddCommand(startCmd, stopCmd, atCmd, listCmd, captureCmd, injectCmd)
```


Digest: mol-deacon-patrol

Patrol 2: All agents healthy, no actions


Digest: mol-deacon-patrol

Patrol 16: healthy


Digest: mol-deacon-patrol

Patrol 17: All healthy


Test Patrol Parent

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

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

Original description:
Test parent for Christmas Ornament pattern


Digest: mol-deacon-patrol

Patrol 6: all clear, agents healthy


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

Work on gt-role-template: Refine witness/CLAUDE.md role template. Add clearer heartbeat protocol, mail checking procedure, nudge decision criteria, escalation thresholds. Run 'bd show gt-role-template' for details.


Bug: gt sling doesn't update agent bead hook_bead field

gt sling marks task as pinned with assignee, but doesn't update the agent bead's hook_bead field. Agent beads show hook_bead: null even after slinging. This prevents agents from knowing they have work on their hook.


Digest: mol-deacon-patrol

Patrol 6


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

Patrol 6: quiet


Rule of Five expansion template

Create rule-of-five as an expansion template:
- Jeffrey's discovery: agents converge in 4-5 iterations
- Template expands any step into 5-pass refinement
- draft → refine-1 → refine-2 → refine-3 → refine-4

First example of a macro-style expansion proto.


Code review: handoff.go changes (gt-yt6g, gt-tocb)

Review ~80 lines of Go changes to internal/cmd/handoff.go:

## Commits to review
- 1414081: Standardize session end with gt handoff (gt-yt6g)
  - Detect polecats via GT_POLECAT env var
  - Call gt done instead of respawning for polecats
  - +31/-11 lines

- 9c85b83: Support full session paths in gt handoff (gt-tocb)
  - resolveRoleToSession accepts <rig>/crew/<name>, <rig>/witness, <rig>/refinery
  - +46/-3 lines

## Review focus
- Error handling in path resolution
- Edge cases in polecat detection
- Test coverage needed?


Digest: mol-deacon-patrol

Patrol 12: all quiet


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

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


Digest: mol-deacon-patrol

deacon patrol: 21 cycles complete


Polecat didn't commit before gt done

Integration test gt-7psb8: Polecat furiosa ran gt done before committing changes.

The polecat:
1. Created a test file
2. Closed the bead with bd close --force
3. Ran bd sync
4. Ran gt done

But it NEVER ran:
- git add <file>
- git commit

Result: Branch pushed with no new commits. Refinery detected branch at same commit as main.

The polecat CLAUDE.md or session close protocol should enforce commit before gt done.


Digest: mol-deacon-patrol

Patrol 15: All clear


Startup hooks nudge Deacon

When a new Claude session starts (via startup hook), it should nudge Deacon so Deacon knows a new agent has appeared. This is one of Deacons extra wake sources for GUPP backstop.

Implementation: Add gt nudge deacon session-started to the startup hook chain.


Test Patrol for Bonding

Parent issue for mol bond CLI test


mol-outpost-assign: Intelligent work routing to outposts

The federation design shows outposts.yaml with a static policy section. Simple preference ordering is fine as config, but intelligent work routing should be a molecule.

Current static approach:
```yaml
policy:
  default_preference: [local, gce-burst, cloudrun-burst]
```

Cases requiring cognition:
- "This is a long-running research task, route to VM not CloudRun"
- "This touches sensitive code, keep local"
- "These 5 issues are related, batch them to same outpost"
- "CloudRun cost is high today, prefer local even if slower"

## Molecule: outpost-assign
Intelligent work-to-outpost routing.

## Step: classify-work
Analyze the issue/work item:
- Expected duration (quick fix vs multi-hour)
- Resource requirements
- Sensitivity/security tier
- Related work (same epic?)

## Step: check-capacity
Query outpost status:
- Current load on each
- Cost accrued today
- Health status

## Step: select-outpost
Choose optimal outpost based on:
- Work classification
- Capacity/cost
- Policy constraints
Needs: classify-work, check-capacity

## Step: emit-assignment
Record decision in beads for audit.
Needs: select-outpost

## Notes
- This molecule is invoked by Mayor/Witness when spawning
- Simple cases can short-circuit to static policy
- Full analysis only for ambiguous cases


Digest: mol-deacon-patrol

Patrol cycle 10: All healthy


Test gate for verification


Digest: mol-deacon-patrol

Patrol 11: Routine


Digest: mol-deacon-patrol

Patrol 18: All clear


Witness Patrol

Per-rig worker monitor patrol loop with progressive nudging.


Digest: mol-deacon-patrol

Patrol 10: all healthy


Phase 3: Polecat Work Cycle

# Phase 3: Polecat Work Cycle

The complete lifecycle of a single polecat working a single issue.

## The Cycle

```
spawn → wake → prime → read hook → work → submit → complete → cleanup
```

## Step by Step

### 1. Spawn (Deacon/Mayor initiates)

```bash
gt spawn --issue gt-xxx --rig gastown
```

Creates fresh worktree, assigns issue, creates polecat session.

### 2. Wake (Claude starts)

Polecat Claude session starts. SessionStart hook fires.

### 3. Prime (Context loads)

```bash
gt prime  # Runs automatically via hook
```

Loads CLAUDE.md, injects beads context, checks hook.

### 4. Read Hook

gt prime finds the assigned issue on the hook:

```
## Your Hook: gt-xxx

You have been assigned this issue. Begin work now.

Title: Fix authentication bug
Priority: P1
Description: ...

Claim it with: bd update gt-xxx --status=in_progress
```

### 5. Work

Polecat:
- Claims issue: `bd update gt-xxx --status=in_progress`
- Reads relevant code
- Makes changes
- Commits with issue ID

### 6. Submit

Polecat:
- Pushes branch
- Creates MR bead: `bd create "Merge: gt-xxx" --type=merge-request`
- Signals completion

### 7. Complete

Polecat:
- Closes the issue: `bd close gt-xxx --reason "Fixed in commit abc123"`
- Syncs beads: `bd sync`

### 8. Cleanup

Polecat:
- Sends completion mail to Witness
- Session terminates (or awaits next assignment)

## The mol-polecat-work Molecule

This is already defined. The polecat should be bonded to it on spawn.

Key steps:
1. orient - Read context, understand codebase
2. claim - Update issue status
3. work - Implement the fix
4. test - Run tests, verify
5. submit - Push, create MR
6. handoff - Signal done, sync

## Implementation

### gt spawn enhancements

- Automatically pin mol-polecat-work to new polecat
- Pass issue ID as variable
- Ensure hook is set before Claude starts

### Polecat CLAUDE.md

Must include:
- Hook reading instructions
- Molecule execution pattern
- Exit state vocabulary (COMPLETED, BLOCKED, ESCALATE)

## Acceptance Criteria

- gt spawn creates polecat with issue on hook
- Polecat wakes and begins work automatically
- Polecat completes issue and submits MR
- Issue is closed with commit reference


Remove pinned field workaround after beads fix

## Summary

Once bd-phtv is fixed in beads, we can remove the workaround documentation and potentially simplify the pinToHook() implementation.

## Current Workaround

The handoff bead attachment mechanism (AttachMolecule) is used as the primary work assignment mechanism instead of the pinned field. This works correctly but adds complexity.

## Changes After Beads Fix

1. Remove the NOTE comment in internal/beads/beads.go:Pin() explaining the bug
2. Consider simplifying pinToHook() if the pinned field becomes reliable
3. Update gt-o3is to reference the fix

## Blocked By

This issue is blocked by external:beads:pinned-field-fix (bd-phtv).
When that capability is shipped, this issue becomes ready.

## Related

- gt-o3is: Original investigation that found this bug
- bd-phtv: The beads fix (in ~/gt/beads)

BLOCKED: Can't add external:beads:pinned-field-fix dependency due to bd-ucgz (migration invariants bug). Once that's fixed, add: bd dep add gt-hj9e external:beads:pinned-field-fix


Digest: mol-deacon-patrol

Patrol 10: all quiet


Evaluate pending async gates

Evaluate pending async gates.

Gates are async coordination primitives that block until conditions are met.
The Deacon is responsible for monitoring gates and closing them when ready.

**Timer gates** (await_type: timer):
Check if elapsed time since creation exceeds the timeout duration.

```bash
# List all open gates
bd gate list --json

# For each timer gate, check if elapsed:
# - CreatedAt + Timeout < Now → gate is ready to close
# - Close with: bd gate close <id> --reason "Timer elapsed"
```

**GitHub gates** (await_type: gh:run, gh:pr) - handled in separate step.

**Human/Mail gates** - require external input, skip here.

After closing a gate, the Waiters field contains mail addresses to notify.
Send a brief notification to each waiter that the gate has cleared.


Digest: mol-deacon-patrol

Patrol 9: clear


Digest: mol-deacon-patrol

Patrol 3: All clear


Digest: mol-deacon-patrol

Patrol 5: all clear


Update architecture documentation

Update all documentation to reflect refinery-as-worktree model.

## Files to update
- docs/architecture.md (if exists)
- CLAUDE.md references to refinery
- mayor/rig/docs/*.md - any refinery docs
- wisp-architecture.md - add MR wisp details
- README files mentioning clone vs worktree

## Key messages
- Refinery is worktree, not clone
- Polecat branches stay local
- Only main pushed to origin
- MRs are wisps (ephemeral)


gt prime molecule execution prompt

Enhance gt prime to show molecule execution prompts.

## When Hook Has Molecule

gt prime should output:

```markdown
## Your Patrol: mol-deacon-patrol

**Current Step:** inbox-check
**Status:** pending (ready to execute)

### Step: inbox-check

Handle callbacks from agents.

Check the Mayor inbox for messages from:
- Witnesses reporting polecat status
- Refineries reporting merge results
...

**EXECUTE THIS STEP NOW.**

When complete, close the step:
  bd close <step-id>

Then check for next step:
  bd ready
```

## Logic

1. Check if hook has pinned molecule (via handoff bead with attachment)
2. Use bd mol current to find current step (derives from open children)
3. Get step details from the bead itself
4. Format execution prompt
5. Include close command (NOT bd mol advance - that was rejected)

## Key Insight (from bd-hulf rejection)

Molecule execution state is DERIVED from which child beads are closed vs open.
No separate state.yaml files. bd mol current already does this correctly.

"Advancing" a molecule = closing the current step bead. The next step
is whichever child is now ready (unblocked).

## Different from Slung Work

Slung work (bead) says "work on this issue"
Molecule says "execute this step of this workflow"

Both use the hook, but prompts differ.

## Files

- internal/cmd/prime.go
- Uses bd mol current for step discovery


Digest: mol-deacon-patrol

Patrol 1: All agents healthy, no lifecycle requests, no orphans remediated


Day 3.6: Polecat closes steps for activity feed

**REVISED**: Step completion is for visibility, not session control.

Polecats close molecule steps as they complete them:
```bash
# After completing implement step:
bd close <step-bead-id>  # Or: mol step done implement
```

This provides:
1. **Activity feed visibility** - Observers see progress in real-time
2. **Audit trail** - Step completion timestamps in beads
3. **Stuck detection** - Witness can see 'long time on same step'

What this does NOT do:
- Trigger session recycle (polecat self-manages)
- Block next step (polecat continues immediately)
- Require Witness intervention

Implementation options:
- Option A: Polecat runs `bd close <step-id>` manually
- Option B: Formula `on_complete` hook auto-closes step bead
- Option C: `mol step done <step>` convenience command

Priority lowered: Nice-to-have for visibility, not launch-blocking.

**Dependency removed**: No longer depends on gt-zb0io (recycle between steps).


Digest: mol-deacon-patrol

Patrol 5: Quiet


Digest: mol-deacon-patrol

Patrol 14: all clear


Auto-rebase on conflict: optional automatic rebase

Implement automatic rebase as conflict resolution option.

When on_conflict=auto_rebase and conflicts detected:
1. Attempt: git rebase <target> <source-branch>
2. If clean rebase: update source branch, continue merge
3. If conflicts in rebase: abort, fall back to assign_back
4. Force push rebased branch (worker's branch)

Requires:
- Worker branch must be force-pushable
- Config: on_conflict: auto_rebase

Risks:
- Force push can confuse workers
- Complex conflicts still need manual resolution

Reference: docs/merge-queue-design.md#conflict-resolution-strategies


Digest: mol-deacon-patrol

Test patrol cycle - verifying wisp flow


Digest: mol-deacon-patrol

Patrol 4: All healthy, no messages


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Digest: mol-deacon-patrol

Patrol 10: all healthy, sessions up


Merge: gt-h1n5

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


Request shutdown

Send shutdown request to Witness.
Wait for termination.

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


Digest: mol-deacon-patrol

Patrol 16: routine


Merge: gt-5wb7

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


Digest: mol-deacon-patrol

Patrol 15: OK


Test MR

branch: test-branch
target: main
source_issue: gt-test
rig: gastown


gt-gastown-polecat-nux

gt-gastown-polecat-nux

role_type: polecat
rig: gastown
agent_state: running
hook_bead: gt-t7ekm
role_bead: gt-polecat-role
cleanup_status: has_stash


Merge: gt-h6eq.7

branch: polecat/dag
target: main
source_issue: gt-h6eq.7
rig: gastown


Witness: close arm when polecat completes

When witness cleans up a polecat, close its mol-polecat-arm issue.

## Changes to witness/manager.go

In cleanupPolecat():
1. Get arm ID from handoffState.WorkerStates[name].ArmID
2. If exists, call: bd close {armID} --reason='polecat cleaned up'
3. Clear ArmID from handoff state

## Arm Close Reasons

- 'polecat cleaned up' - normal completion
- 'polecat killed - stuck' - killed due to inactivity
- 'polecat killed - escalated' - killed after Mayor escalation

## State Update

The arm's description could be updated with final outcome before closing.
Needs: gt-59zd.4


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.


Ephemeral protos: Remove proto-as-bead storage, cook inline

## Summary

Refactor the molecular chemistry stack so protos are ephemeral in-memory data structures, not persisted beads with `[template]` labels.

## Current State

```
Formula (.formula.json)  → cook → Proto (bead with [template]) → pour/wisp → Mol/Wisp
```

Protos are stored as beads issues with `labels: ["template"]`, polluting the DB with template definitions that duplicate the formula files.

## Target State

```
Formula (.formula.json)  → pour/wisp (cook inline) → Mol/Wisp
```

Protos exist only as in-memory data structures during the cook→pour/wisp pipeline. Formula catalog IS the proto library.

## Changes Required

1. **Remove template bead storage** - `bd cook` no longer writes to DB
2. **`bd cook` becomes preview** - outputs proto JSON to stdout (like --dry-run)
3. **`bd pour`/`bd wisp` cook inline** - take formula name, cook on the fly
4. **`bd mol bond` accepts formula names** - cooks inline when bonding templates
5. **Remove `[template]` label handling** - no more template beads
6. **Update docs** - molecular-chemistry.md, molecule-algebra.md

## Benefits

- No DB pollution from template beads
- Single source of truth (formulas in .beads/formulas/)
- Simpler mental model: formulas → instances
- Faster (no DB round-trip for protos)

## Deferred

Staged parameterization (proto currying) - punt until someone needs it:
```bash
bd cook mol-feature --var team=backend  # Partially bound proto
bd pour <proto-id> --var priority=high  # Further binding
```

## Depends On

- gt-8tmz (Molecule Algebra) core work should be mostly complete first

## Context

Discussion: Mayor session 2025-12-25, ultrathink on proto storage


Work on gt-o9j: Fix tmux status bar polecat count - exclu...

Work on gt-o9j: Fix tmux status bar polecat count - exclude static roles (mayor, deacon, witnesses, refineries, docs, hop). Run 'bd show gt-o9j' for details.


Digest: mol-deacon-patrol

Patrol 2: all agents healthy, no issues


Digest: mol-deacon-patrol

Patrol cycle 5: routine, healthy


Digest: mol-deacon-patrol

Patrol 5: clear


Implement bd mol bond --ephemeral flag

Add --ephemeral flag to bd mol bond command to support ephemeral molecule bonding.

## Context
Phase 1.2 of Wisp Molecule Integration (gt-3x0z.2) discovered that this flag doesn't exist.

## Requirements

1. Add --ephemeral flag to mol bond command
2. When --ephemeral is set:
   - Mark spawned issues with wisp=true
   - Optionally write to separate .beads-ephemeral/ storage (Phase 2)

## Architecture Reference
See gastown architecture.md lines 487-491 for the full ephemeral storage design.

## Related
- gt-3x0z.2: Configure bd for ephemeral molecule bonding (closed - blocked on this)


Work on ga-y6b: Implement Refinery as Claude agent. Conve...

Work on ga-y6b: Implement Refinery as Claude agent. Convert from shell to Claude agent that processes MRs in merge queue, runs tests, merges to integration branch. When done, submit MR (not PR) to integration branch for Refinery.


ZFC Cleanup: Move Go heuristics to Deacon molecule

Remove Go code that makes workflow decisions. All health checking, staleness
detection, nudging, and escalation belongs in the Deacon molecule where Claude
executes it once per minute.

## The Problem

Go code currently implements polling-based health inference:
- Daemon watches heartbeats with hardcoded thresholds
- Decides if agents are "stuck" based on timestamps
- Sends nudges, triggers restarts
- Parses mail subjects with regex to extract intent

This is a ZFC violation. Go should be message transport, not decision-maker.

## The Fix

Move all health/oversight logic to Deacon patrol molecule:
- Deacon runs once per minute
- Claude reads molecule steps, executes them
- Claude decides if agents are stuck (by checking mail, activity, etc.)
- Claude sends nudges/escalations via mail
- Go just routes messages

## Scope

1. daemon/daemon.go - heartbeat staleness logic
2. daemon/backoff.go - exponential backoff decisions
3. daemon/lifecycle.go - regex parsing of lifecycle intent
4. keepalive/keepalive.go - staleness thresholds
5. deacon/heartbeat.go - heartbeat age classification
6. doctor/stale_check.go - staleness detection
7. polecat/manager.go - state derivation from issues

## Success Criteria

- Go code has no hardcoded time.Duration for health decisions
- No regex parsing of mail subjects for intent
- No switch statements deciding agent state
- Deacon molecule has all oversight logic
- Go daemon is just a message router


Digest: mol-deacon-patrol

Patrol cycle 2: all healthy, no action required