Implement context-checking for agent sessions

Implement the context-checking step that allows agents to assess their remaining context capacity.

## Challenge

Agents don't have direct access to token counts. Need heuristics:

### Approach 1: Message count heuristic
- Count messages in conversation
- Estimate tokens per message
- Compare to model's context window

### Approach 2: External tool
- Claude Code could expose a /context command
- Returns estimated usage percentage
- Agent queries before each work item

### Approach 3: Conservative fixed limits
- After N work items, always handoff
- Simple but may waste context or handoff too early

### Approach 4: Hook-based injection
- SessionStart hook injects context estimate
- Updated periodically via tool call

## Recommendation

Start with Approach 3 (fixed limits) as MVP:
- Default: 3-5 work items per session
- Agent can override via variable
- Upgrade to smarter heuristics later

## Integration

- Add check-context step to mol-ready-work
- Implement handoff trigger logic
- Test with various context states


Digest: mol-deacon-patrol

Patrol 20: Final cycle, all clear, handoff triggered


Digest: mol-deacon-patrol

Patrol 18: Routine


Doctor check: Zombie session cleanup

Detect and clean up zombie tmux sessions via gt doctor.

## Problem

Zombie sessions occur when:
- Agent crashes without cleanup
- gt kill fails mid-operation
- System restart leaves orphan sessions
- Session naming collision

## Checks

### ZombieSessionCheck
- List all tmux sessions matching gt-* pattern
- Cross-reference with known polecats
- Flag sessions with no corresponding polecat state
- Flag sessions for removed polecats
- Check session age vs polecat creation time

### Detection Criteria
- Session exists but polecat directory doesn't
- Session name doesn't match any registered polecat
- Polecat state=idle but session running
- Multiple sessions for same polecat

## Output

```
[WARN] Zombie tmux sessions detected:
  - gt-wyvern-OldPolecat (polecat removed)
  - gt-beads-Unknown (no matching polecat)
  - gt-wyvern-Toast (duplicate session)

  Run 'gt doctor --fix' to clean up
```

## Auto-Fix (--fix flag)

- Kill orphan tmux sessions
- Update polecat state to match reality
- Log all cleanup actions

## Safety

- Never kill sessions where polecat state=working
- Prompt before killing if --fix used without --force
- Create audit log of killed sessions


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

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

## Variables

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

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

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

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

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

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

Calculate: minutes since last activity.
Needs: capture

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

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

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

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

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

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

Record decision and rationale.
Needs: load-history

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

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

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

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

**none**: No action needed.

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

## Output

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

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


Digest: mol-deacon-patrol

Patrol 8: all healthy


Digest: mol-deacon-patrol

P15: stable


Digest: mol-deacon-patrol

Patrol 8: routine


Implement verification patterns for boot steps

Define and implement the verification logic Mayor uses.

## Verification Utilities

Mayor needs to check:

### Session Exists
```bash
tmux has-session -t <session> 2>/dev/null && echo "exists"
```

### Not Stalled (Claude waiting at prompt)
```bash
gt peek <agent> 10 | grep -q "> Try" && echo "stalled"
```

Pattern: `> Try "..."` indicates Claude started but no message sent.

### Recent Heartbeat
```bash
# For deacon
age=$(( $(date +%s) - $(stat -f %m ~/gt/deacon/heartbeat.json) ))
[ $age -lt 120 ] && echo "fresh"
```

### Active Work Output
```bash
gt peek <agent> 20 | grep -q "⏺" && echo "working"
```

Pattern: `⏺` indicates Claude is executing tools.

## Mayor Execution Pattern

```
for each step in molecule:
  run action
  loop:
    check verification
    if verified: close step, continue
    if stalled: run on_stall recovery
    if timeout (5 attempts): log warning, continue
    sleep 10s
```

No hard timeout - Mayor keeps trying with increasing backoff.


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


SwarmManager: create, start, update, query operations

SwarmManager operations for creating and managing swarms.

## Interface

```go
type SwarmManager struct {
    rig    *Rig
    beads  *BeadsWrapper  // shells out to bd CLI
}

func NewSwarmManager(rig *Rig) *SwarmManager

// Lifecycle
func (m *SwarmManager) Create(epicID string, workers []string) (*Swarm, error)
func (m *SwarmManager) Start(swarmID string) error
func (m *SwarmManager) UpdateState(swarmID string, state SwarmState) error
func (m *SwarmManager) Cancel(swarmID string, reason string) error

// Queries (delegate to beads)
func (m *SwarmManager) GetSwarm(id string) (*Swarm, error)
func (m *SwarmManager) GetReadyTasks(swarmID string) ([]SwarmTask, error)
func (m *SwarmManager) GetActiveTasks(swarmID string) ([]SwarmTask, error)
func (m *SwarmManager) IsComplete(swarmID string) (bool, error)
```

## Implementation Notes

- `GetReadyTasks` wraps `bd ready --parent <epicID>`
- `IsComplete` checks if all child issues are closed
- State transitions update the epic's description or a tag field
- No separate manifest files - beads IS the source of truth


Digest: mol-deacon-patrol

Patrol 16: nominal


gt spawn should create fresh polecat worktree, never reuse

Currently gt spawn tries to reuse existing polecats, which causes:

1. Stale beads database
2. Stale code (behind main/integration)
3. Old inbox messages
4. Git history pollution

## Current Behavior

gt spawn:
1. Looks for idle polecat in pool
2. If found, reuses existing worktree
3. Assigns new issue

## Desired Behavior

gt spawn:
1. Always create FRESH polecat from current main/integration
2. Fresh worktree with clean beads
3. No reuse of old worktrees

## Name Pool Still Useful

Keep name pool for:
- Allocating themed names (mad-max, etc.)
- Tracking which names are in use

But worktrees should be created fresh each time.

## Implementation

In spawn.go, before starting work:
1. If worktree exists: remove it first
2. Create fresh worktree from integration branch
3. Sync beads from rig to polecat
4. Then proceed with work assignment

This ensures polecats always start with latest code and beads.


Digest: mol-deacon-patrol

Patrol 10: all healthy, sessions up


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

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

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


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Merge Queue in Beads: Universal chit system for all work

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


Digest: mol-deacon-patrol

Quick patrol: no messages, all healthy


Digest: mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 4: Routine, witnesses/refineries healthy


Competing molecule instantiation mechanisms need resolution

Documentation describes 4 different molecule instantiation methods:
1. bd mol bond (beads-based, in molecules.md)
2. gt sling (proposed in sling-design.md)
3. gt swarm (implemented in code)
4. gt molecule instantiate (in architecture.md)

Role prompts use different methods:
- Deacon: bd mol spawn
- Refinery: gt mol bond
- Polecat: bd mol current

Need to:
1. Decide canonical mechanism
2. Update all role prompts to use it
3. Remove/deprecate alternatives
4. Update architecture docs


Witness template uses non-existent --assignee flag on bd mol wisp

## Bug

The witness.md.tmpl template uses an invalid flag:

```
bd mol wisp mol-witness-patrol --assignee={{ .RigName }}/witness
```

But `bd mol wisp` does NOT support the `--assignee` flag (only `--dry-run`, `--var`, and global flags).

## Location

`internal/templates/roles/witness.md.tmpl:142`

## Fix

Use the two-step pattern like the deacon template does:

```bash
bd mol wisp create mol-witness-patrol
bd update <wisp-id> --status=pinned --assignee={{ .RigName }}/witness
```

## Impact

Witness agents following the template will get a CLI error when trying to create patrol wisps.


Day 4.2: Implement gt polecat nuke command

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

Parent: gt-4a2qt


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 16: All clear


Federation: Wasteland architecture for cross-town coordination

## Overview

Federation enables Gas Town to scale across machines via **Outposts** - remote compute environments that can run workers.

**Design doc**: docs/federation-design.md

## Outpost Types

| Type | Description | Cost Model |
|------|-------------|------------|
| Local | Current model (tmux panes) | Free |
| SSH/VM | Full Gas Town clone on VM | Always-on |
| CloudRun | Container workers on GCP | Pay-per-use |

## Key Concepts

### Outpost Abstraction
```go
type Outpost interface {
    Name() string
    Type() OutpostType  // local, ssh, cloudrun
    MaxWorkers() int
    Spawn(issue string, config WorkerConfig) (Worker, error)
    Workers() []Worker
    Ping() error
}
```

### Cloud Run Workers
- Persistent HTTP/2 connections solve zero-to-one cold start
- Pay only when working (~$0.017 per 5-min session)
- Scale 0→N automatically
- Git clone via persistent volumes

### SSH/VM Outposts
- Full Gas Town clone on remote machine
- SSH for commands, git for sync
- Good for long-running autonomous work

## Design Principles

1. **Outpost abstraction** - support multiple backends
2. **Local-first** - remote is for overflow/burst
3. **Git as source of truth** - code and beads sync everywhere
4. **HTTP for Cloud Run** - dont force mail onto containers
5. **Graceful degradation** - works with any subset of outposts

## Related

- gt-f9x.7-10: Connection interface (lower-level abstraction)
- docs/federation-design.md: Full architectural analysis


Add gate timeout tracking and notification

Implement timeout and notification logic for gates.

## Timeout Behavior
1. Gate created with timeout (e.g., 30m)
2. Deacon tracks elapsed time during patrol
3. If timeout reached:
   - Notify all waiters: 'Gate timed out'
   - Close gate with timeout reason
   - Waiter can retry, escalate, or fail gracefully

## Notification
- Use gt mail send to notify waiters
- Include gate ID, await type, and reason in message
- Support multiple waiters notification

## Moved from beads
Originally bd-ykqu. Gate notifications are Deacon's job in gastown.


Digest: mol-deacon-patrol

Patrol 6: nominal


MQ dashboard widget: queue status in TUI

Add merge queue status widget to the Bubbletea TUI dashboard.

Display:
┌─────────────────────────────────────────────────┐
│              MERGE QUEUE STATUS                  │
├─────────────────────────────────────────────────┤
│ Pending: 3   In Progress: 1   Merged (24h): 12  │
├─────────────────────────────────────────────────┤
│ QUEUE:                                          │
│   ► gt-mr-004  P0  Nux/gt-xyz     Processing... │
│     gt-mr-005  P1  Toast/gt-abc   Ready         │
│     gt-mr-006  P1  Capable/gt-def Blocked (005) │
└─────────────────────────────────────────────────┘

Interactive features:
- Navigate queue entries
- View MR details
- Retry/reject from dashboard

Depends on: gt-u1j.2 (Bubbletea TUI dashboard)

Reference: docs/merge-queue-design.md#dashboard


Digest: mol-deacon-patrol

Patrol 13: Routine


Add keyboard navigation and filtering

Keyboard navigation: j/k or arrows to move, enter to expand/collapse, q to quit. Filtering: by rig name, worker name, event type. Search within activity stream.


Check Witness and Refinery health

Check Witness and Refinery health for each rig.

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

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

**Signals to assess:**

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

**Tracking unresponsive cycles:**

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

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

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

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

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

Reset unresponsive_cycles to 0 when component responds normally.


Merge: gt-g44u.2

branch: polecat/Doof
target: main
source_issue: gt-g44u.2
rig: gastown


Digest: mol-deacon-patrol

Patrol 12: clear


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

Patrol 17: All healthy


Digest: mol-deacon-patrol

Patrol 18: clear


mol-deacon-patrol


Add crew lifecycle support to Deacon

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

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

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

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

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

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


session-gc

Clean dead sessions. Run gt gc --sessions.


Fetch remote and identify polecat branches waiting.

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

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

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


Digest: mol-deacon-patrol

Patrol 17: All healthy.


Merge: gt-5af.6

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


gt sling: Set status=hooked instead of pinned

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

## Current Behavior

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

## New Behavior

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

## Files to Update

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

## CROSS-RIG DEPENDENCY

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

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

## Verification

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


Witness: active polecat monitoring and nudging

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

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

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

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

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


Create rig settings/ directory for rig configuration

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


Digest: mol-deacon-patrol

Patrol 15: healthy


Digest: mol-deacon-patrol

Patrol 2: All healthy, no messages


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


Digest: mol-deacon-patrol

Patrol 11: OK


Design: Swarm shutdown and worker cleanup

Design for graceful swarm shutdown, worker cleanup, and session cycling.

## Key Decisions

1. Pre-kill verification uses model intelligence (not framework rules)
2. Witness can request restart when context filling (mail self, exit)
3. Mayor NOT involved in per-worker cleanup (Witness responsibility)
4. Clear responsibility boundaries between Mayor/Witness/Polecat

## Subtasks (implementation)

- gt-sd6: Polecat decommission checklist prompting
- gt-f8v: Witness pre-kill verification protocol
- gt-eu9: Witness session cycling and handoff
- gt-gl2: Mayor vs Witness cleanup responsibilities

**Design complete.** Each subtask has full specification in its description.


Digest: mol-deacon-patrol

Patrol #7: Stable


Day 5.1: End-to-end liftoff test

Full integration test:
1. gt sling gt-task123 gastown/nux
2. Watch polecat work (no manual intervention)
3. Watch refinery merge (no manual intervention)
4. Watch witness clean up (no manual intervention)
5. See cycle complete in bd activity or logs

Success = no human touches needed.

Parent: gt-oki8p


Digest: mol-deacon-patrol

Patrol cycle 9: nominal


Polecat prompting: gt mq submit on completion

Update Polecat CLAUDE.md prompting to:

1. On task completion, run: gt mq submit --issue <id>
2. This creates a merge-request bead in the queue
3. Engineer will process it

The Polecat self-reports completion to the merge queue.


Add stale-attachments doctor check

Add gt doctor check for work stale on hook without progress.

Check: stale-attachments
Warning if: Attached molecule has no activity for >24h
Suggestion: Check agent status, consider nudge or reassignment


Digest: mol-deacon-patrol

Patrol 2: all agents healthy, no issues


Digest: mol-deacon-patrol

Patrol 10: all clear


Digest: mol-deacon-patrol

Patrol 18: All healthy


Merge: gt-7hor

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


Molecule schema: YAML format for workflow definitions

Define the YAML schema for molecule definitions:

```yaml
molecule: <name>
version: 1
description: "Human description"
steps:
  - id: <step-id>
    title: "Step title"
    prompt: "Instructions for agent"
    depends: [<other-step-ids>]  # optional
    tier: haiku|sonnet|opus      # optional, default from config
    timeout: 30m                 # optional
```

Store molecules in:
- `<rig>/molecules/<name>.yaml` for rig-specific
- `<town>/molecules/<name>.yaml` for town-wide

Built-in molecules to ship:
- engineer-in-box: design→code→review→test→submit
- quick-fix: implement→test→submit
- research: investigate→document


Fix role detection for nested rig structures

When gastown/ has its own mayor/ dir, workspace detection finds it as town root instead of ~/ai. This breaks polecat/refinery/witness detection when running from within a rig.


Digest: mol-deacon-patrol

Patrol 3: all clear


Digest: mol-deacon-patrol

Patrol 6: quiet


burn-or-loop

Squash wisp and decide: loop or cycle session.

If context low: spawn new wisp and loop
If context high: handoff and request cycle

Needs: generate-summary


Digest: mol-deacon-patrol

Patrol 16: routine


Digest: mol-deacon-patrol

Patrol complete: routine, town stable


Digest: mol-deacon-patrol

Patrol cycle 2: All healthy. No callbacks, orphans, or gates.


Digest: mol-deacon-patrol

Patrol complete: 0 callbacks, no gates, all witnesses/refineries healthy, no orphans


Digest: mol-deacon-patrol

Patrol cycle 14: 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.


Use Beads issue status as spawn source of truth

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

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

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

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

This simplifies state management and ensures consistency.


Digest: mol-deacon-patrol

Patrol #3: Routine - 6 agents healthy


Digest: mol-deacon-patrol

Cycle 3: quiet patrol


Digest: mol-deacon-patrol

Patrol complete: routine cycle


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 13: Quiet


End-to-end swarm integration test

Full integration test for swarm lifecycle.

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

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

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


Digest: mol-deacon-patrol


Async Coordination Gates

Agents wait for external events without polling.

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

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

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

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

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


Merge: gt-3x0z.3

branch: polecat/slit
target: main
source_issue: gt-3x0z.3
rig: gastown


HTTP work server: gt worker serve command

## Overview

HTTP server that runs inside Cloud Run containers, accepting work requests.

## Command

```bash
gt worker serve --port 8080
```

## Implementation

```go
type WorkServer struct {
    port    int
    handler *WorkHandler
}

func (s *WorkServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    switch r.URL.Path {
    case "/work":
        s.handleWork(w, r)
    case "/health":
        s.handleHealth(w, r)
    default:
        http.NotFound(w, r)
    }
}

func (s *WorkServer) handleWork(w http.ResponseWriter, r *http.Request) {
    // 1. Parse WorkRequest
    // 2. Clone/pull repo
    // 3. Start Claude on issue
    // 4. Stream WorkEvents as NDJSON
    // 5. Return WorkResult
}
```

## Streaming Response

```go
func (s *WorkServer) streamEvents(w http.ResponseWriter, events <-chan WorkEvent) {
    flusher, _ := w.(http.Flusher)
    encoder := json.NewEncoder(w)
    for event := range events {
        encoder.Encode(event)
        flusher.Flush()
    }
}
```

## Files

- `internal/cloudrun/server.go`
- `cmd/gt/worker_serve.go` - Cobra command

## Dependencies

Depends on: gt-9a2.7 (protocol types)


Create activity aggregator

Component that connects to multiple rig beads daemons and merges their activity into a unified view. Polls GetMutations and GetWorkerStatus from each rig, groups by rig/worker. Handles connection failures gracefully.

BLOCKED BY (Beads rig):
- bd-gqxd: Enrich MutationEvent with title and assignee
- bd-l13p: Add GetWorkerStatus RPC endpoint
- bd-0oqz: Add GetMoleculeProgress RPC endpoint


Selection operators: glob, filter, children

Implement selection operators for targeting:
- step(id) - specific step
- glob(pattern) - pattern match (*.implement)
- filter(predicate) - status/output predicates
- children(step), descendants(step) - tree traversal

Used by advice, expansion, and aspects for targeting.


gt mq status: detailed MR view

Implement 'gt mq status <id>' command for detailed MR view.

Display:
- All MR fields (branch, target, source_issue, worker, rig)
- Current status with timestamps
- Dependencies (what it's waiting on)
- Blockers (what's waiting on it)
- Processing history (attempts, failures)

Under the hood: bd show <id> with MR-specific formatting.

Reference: docs/merge-queue-design.md#command-details


mol-deacon-patrol

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

Original description:
Deacon patrol molecule template. Label: template


Burn and respawn or loop

Burn and let daemon respawn, or exit if context high.

Decision point at end of patrol cycle:

If context is LOW:
- Sleep briefly (avoid tight loop)
- Return to inbox-check step

If context is HIGH:
- Write state to persistent storage
- Exit cleanly
- Let the daemon orchestrator respawn a fresh Deacon

The daemon ensures Deacon is always running:
```bash
# Daemon respawns on exit
gt daemon status
```

This enables infinite patrol duration via context-aware respawning.


Digest: mol-deacon-patrol

Patrol 20: Final before handoff, all healthy


test pin fix 2


Digest: mol-deacon-patrol

Patrol cycle 5: routine, healthy


Day 3.0: Document mail protocol for patrol coordination

Document the mail protocol used for patrol coordination.

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

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

This documentation enables consistent implementation across all patrol formulas.


Harness Design & Documentation

The harness (Gas Town installation directory) needs design cleanup, documentation, and tooling.

## Current Problems

1. **Shared harness confusion**: ~/ai is shared by PGT and GGT with overlapping structures
   - PGT uses ~/ai/mayor/ as town-level Mayor home
   - GGT Mayor works in ~/ai/mayor/rigs/gastown/
   - ~/ai/gastown/ has both .gastown/ (PGT) and mayor/ (git clone)

2. **Beads redirect**: ~/ai/.beads/redirect → mayor/rigs/gastown/.beads
   - This is specific to GGT's decentralized structure
   - Should be documented as an example

3. **architecture.md**: Verify rig-level mayor/rig/ is shown correctly

4. **No harness creation tooling**: Users must manually set up

## Proposed Work

- Document what a harness IS (installation directory)
- Create harness creation command or template repo
- Update architecture.md if needed
- Create example harness configuration for docs
- Resolve PGT/GGT sharing issue


Test Patrol for Bonding

Parent issue for mol bond CLI test


Day 2.3: Agent writes state on lifecycle events

Agents update their bead state using bd agent state command:
- On startup: bd agent state gt-<role> running
- On shutdown: bd agent state gt-<role> stopped
- On stuck: bd agent state gt-<role> stuck

State is self-reported, not inferred.

Implementation: Wire up agent lifecycle hooks (SessionStart, SessionEnd, health checks) to call bd agent state.

Parent: gt-d0jqp


Digest: mol-deacon-patrol

Patrol #6: Stable


Digest: mol-deacon-patrol

Patrol 5: Routine


Digest: mol-deacon-patrol

Cycle 6: quiet


Witness Patrol Molecule

Create mol-witness-patrol molecule for witness heartbeat loop.

Similar to DeaconPatrolMolecule but for per-rig witness duties.

## Steps
1. inbox-check - Process witness mail
2. survey-workers - List polecats, check status
3. inspect-workers - Capture pane output, assess state
4. plugin-run - Execute plugins from <rig>/plugins/witness/
5. decide-actions - Determine nudges, escalations
6. execute-actions - Send nudges, process shutdowns
7. context-check - Check own context, handoff if needed
8. loop-or-exit - Continue or cycle

The plugin-run step enables user customization of the patrol loop.


mol-gastown-boot

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

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

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

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

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


Demo recording

Record a terminal demo of the tracer bullet.

## Recording Tool

Use asciinema:
```bash
brew install asciinema
asciinema rec gastown-demo.cast
```

## Demo Script

1. Show gt status (the town)
2. Show bd ready (available work)
3. Show Deacon running (tmux session)
4. Trigger spawn (create issue, Deacon picks it up)
5. Show polecat working (tmux attach briefly)
6. Show completion (bd list, issue closed)
7. Show MR created (bd list --type=merge-request)

## Tips

- Practice run first
- Keep it under 3 minutes
- Add commentary (terminal echo or voiceover)
- Show the key moments clearly

## Output

- gastown-demo.cast (asciinema file)
- gastown-demo.gif (optional, for embedding)
- YouTube/Vimeo upload (optional)


Remove spawn - use pour/wisp only (see bd-8y9t)

Remove bd mol spawn entirely from vocabulary.

Replace all references with:
- bd pour <proto> - Create persistent mol (liquid)
- bd wisp <proto> - Create ephemeral wisp (vapor)

'spawn' doesn't fit the chemistry metaphor. Two phase transitions (pour/wisp) are clearer than one command with flags.

See bd-XXX for Beads-side removal.


Digest: mol-deacon-patrol

Patrol 10: full check, all healthy


Convert formula files from YAML to JSON

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


Digest: mol-deacon-patrol

Patrol 19: nominal


Digest: mol-deacon-patrol

Patrol 2: Routine, all healthy


Crew worker CLAUDE.md prompting

Create CLAUDE.md template for crew workers:
- Explain crew worker role (overseer's personal workspace)
- Include mail-to-self handoff instructions
- Document gt crew refresh for context cycling
- Explain no witness monitoring (user-managed)
- Include beads usage if BEADS_DIR configured


Digest: mol-deacon-patrol

Patrol 16: quiet


gt mq integration create: create integration branch for epic

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

Actions:
1. Verify epic exists
2. Create branch: integration/<epic-id> from main
3. Push to origin
4. Store integration branch info in epic metadata

Usage:
  gt mq integration create gt-auth-epic
  # Creates integration/gt-auth-epic from main

Future MRs for this epic's children will auto-target this branch.

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


Test

Body


gt daemon: Background service management

## Summary

ONE daemon for all Gas Town - a simple Go process (not a Claude agent) that:
1. Pokes agents periodically (heartbeat)
2. Processes lifecycle requests
3. Restarts sessions when agents request cycling

## Architecture

Daemon (gt daemon)
- Pokes Mayor: "HEARTBEAT: check your rigs"
- Pokes each Witness: "HEARTBEAT: check your workers"
- Has inbox at daemon/ for lifecycle requests
- Restarts sessions on cycle requests

## NOT the daemon's job:
- Making decisions (Witnesses do that)
- Running Claude (it's a Go process)
- Processing work (polecats do that)
- Direct polecat management (Witnesses do that)

The daemon is a **dumb scheduler** - poke things, execute lifecycle requests. All intelligence is in agents.

## Commands

- gt daemon start: Start daemon (background)
- gt daemon stop: Stop daemon
- gt daemon status: Show daemon status
- gt daemon logs: View daemon logs

## Daemon Loop

Every heartbeat interval:
1. Poke Mayor
2. For each rig: Poke Witness
3. Process lifecycle requests from daemon/ inbox
4. Check for dead sessions, restart if cycle requested

## Lifecycle Request Handling

Daemon checks its inbox (daemon/) for lifecycle requests:
- From Mayor: cycle/restart Mayor session
- From Witnesses: cycle/restart Witness session

On request:
1. Verify agent state shows requesting_cycle
2. Kill session
3. Start new session
4. Clear requesting_cycle flag

## Poke Protocol

Poke = tmux inject "HEARTBEAT: do your job"
- Agent ignores if already working
- Agent wakes up if idle
- Idempotent - multiple pokes are fine

## Lifecycle Hierarchy

- Daemon manages: Mayor, all Witnesses
- Witness manages: Polecats, Refinery (per rig)
- Crew: self-managed (human workspace)

## Related Issues

- gt-kmn.11: Daemon heartbeat details
- gt-gby: gt handoff command (unified lifecycle)
- gt-u1j.9: Fold witness daemon into this


Digest: mol-deacon-patrol

Patrol 14: OK


self-review

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

Fix any issues found before proceeding.

Depends: implement


MR: gt-test (main)

branch: main
target: main
source_issue: gt-test


Add gt deacon start/stop/status commands

Add CLI commands: gt deacon start (spawn gt-deacon session), gt deacon stop (kill session), gt deacon status (show health state). Similar to gt mayor commands.


Digest: mol-deacon-patrol

Patrol 19: All clear


Digest: mol-deacon-patrol

Patrol 14: all healthy


Digest: mol-deacon-patrol

Patrol 12: all healthy


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

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

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


gt spawn: beads sync warnings on fresh worktree

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


mol-christmas-launch: 3-day execution plan

---

## Wisp Integration Wave Plan (added 2025-12-21)

Dependency-ordered execution for gt-3x0z (Wisp Molecules) + gt-rana (Patrol System):

### Wave 1 (parallel - no blockers)
- gt-3x0z.1: gt rig init creates .beads-ephemeral/
- gt-3x0z.2: Configure bd for ephemeral molecule bonding
- gt-3x0z.3: gt doctor checks for ephemeral health
- gt-rana.1: Attachment field on pinned beads
- gt-rana.3: mol-deacon-patrol definition

### Wave 2 (after Wave 1)
- gt-3x0z.4: gt spawn --molecule bonds ephemeral (GATE 1)
- gt-rana.2: Daemon attachment detection

### Wave 3-5 (sequential)
- gt-3x0z.5, gt-3x0z.6 → gt-3x0z.7 → gt-3x0z.8 (GATE 2: squash)

### Wave 6+ (patrol integration)
- gt-rana.4: Basic patrol runner (needs gt-rana.3 + gt-3x0z.8)
- gt-3x0z.9: mol-deacon-patrol uses wisp (needs gt-rana.3 + gt-3x0z.8)
- Then: gt-rana.5-7, gt-3x0z.10-12

### Key Gates
1. gt-3x0z.4 - spawn/bond unlocks Phase 2
2. gt-3x0z.8 - squash unlocks patrol integration
3. gt-rana.4 - patrol runner unlocks Phase 2+ patrol

Postponed to New Year's launch. Christmas was too ambitious.


Proto debugging and inspection tools

Proto debugging and inspection tools.

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

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


CRITICAL: ProcessMRFromQueue not implemented - Refinery merge logic stubbed

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


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Create AgentIdentity type for session name parsing

attached_args: Create AgentIdentity type - depends on completed gt-atqr8

Create AgentIdentity type to parse and construct session names, replacing duplicated logic.

## Files to modify
- internal/session/identity.go (NEW)
- internal/cmd/sling.go (update to use new type)
- internal/cmd/handoff.go (update to use new type)

## Implementation
```go
type AgentIdentity struct {
    Role     string // mayor, deacon, witness, refinery, crew, polecat
    Rig      string // empty for mayor/deacon
    Name     string // crew/polecat name
}

func ParseSessionName(session string) (*AgentIdentity, error)
func (a *AgentIdentity) SessionName() string
func (a *AgentIdentity) Address() string // e.g., "gastown/crew/max"
```

## Acceptance criteria
- [ ] AgentIdentity type with Role, Rig, Name fields
- [ ] ParseSessionName handles all agent types (mayor, deacon, witness, refinery, crew, polecat)
- [ ] SessionName() reconstructs valid session name
- [ ] Address() returns mail-style address
- [ ] sling.go:sessionToAgentID replaced with ParseSessionName
- [ ] handoff.go:sessionToGTRole replaced with ParseSessionName
- [ ] Unit tests for all parsing cases
- [ ] go test ./... passes


Digest: mol-deacon-patrol

Patrol 18: All healthy.


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 18: All agents healthy


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

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

## Variables

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

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

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

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

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

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

Calculate: minutes since last activity.
Needs: capture

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

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

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

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

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

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

Record decision and rationale.
Needs: load-history

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

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

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

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

**none**: No action needed.

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

## Output

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

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


Digest: mol-deacon-patrol

Patrol #19: Stable


CLI: worker status reporting commands

Worker status reporting CLI for polecats to report progress.

## Commands

### gt worker started
```
gt worker started <issue-id> [-m MESSAGE]
```
Reports work started on issue.

### gt worker progress
```
gt worker progress <issue-id> <0-100> [-m MESSAGE]
```
Reports percentage complete.

### gt worker blocked
```
gt worker blocked <issue-id> <reason> [-m MESSAGE]
```
Reports blocked status with reason.

### gt worker completed
```
gt worker completed <issue-id> [-m MESSAGE]
```
Reports task completion.

### gt worker failed
```
gt worker failed <issue-id> <reason> [-m MESSAGE]
```
Reports task failure.

## Implementation
Each command sends mail to refinery with structured content:
```go
type WorkerStatusReport struct {
    IssueID    string
    Status     string  // started|progress|blocked|completed|failed
    Progress   int     // 0-100 for progress
    Reason     string  // for blocked/failed
    Message    string  // optional detail
    ReportedAt time.Time
}
```

## Message Format
Subject: "[STATUS] <issue-id>: <status>"
Body: JSON-encoded WorkerStatusReport

## Default Recipient
```go
// Determine from context
func getDefaultRecipient() string {
    rig := os.Getenv("GT_RIG")
    if rig != "" {
        return rig + "/refinery"
    }
    return "refinery/"
}
```

## New File
internal/cmd/worker.go

## PGT Reference
gastown-py/src/gastown/cli/worker_cmd.py

## Acceptance Criteria
- [ ] All 5 commands implemented
- [ ] Status sent as mail to refinery
- [ ] Structured JSON body for parsing
- [ ] Works from polecat session context


Update refinery to read local branches

Refinery manager needs to access polecat branches locally instead of fetching from origin.

## Changes
- internal/refinery/manager.go - remove origin fetch for MR branches
- internal/refinery/engineer.go - local branch access
- Remove DeleteRemoteBranch calls (branches are local now)
- Update merge workflow to work with local refs

## Key insight
All worktrees share the same .git - refinery can see polecat/nux branch directly.


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


mol-polecat-work

Full polecat lifecycle from assignment to decommission.

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

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


done

Exit received. Ready for cleanup.

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

Update beads state and close the lease.
Variables: {{polecat}}, {{issue}}


gt handoff deadlock at handoff.go:125

When running 'gt handoff -m "message"' after successful MR submit, go panics with 'fatal error: all goroutines are asleep - deadlock\!' at handoff.go:125. The shutdown request still appears to be sent successfully but the command crashes. Stack trace shows issue is in runHandoff select statement.


Witness arm bonding integration test

Test witness with 3 polecats. Verify: arms bonded per worker, each arm captures/assesses independently, aggregation waits for all arms, witness proceeds after aggregation.


Digest: mol-deacon-patrol

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


Merge: gt-oiv0

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


Digest: mol-deacon-patrol


gt status should show runtime state, not just structure

## Problem
`gt status` shows structural info (which crews exist) but not runtime reality (which sessions are running).

## Current Output
- Shows 'Crews: [dave emma zoey]' but doesn't indicate dave is crashed
- Missing Deacon from output entirely
- Hooks shown but unclear if agents are alive

## Expected
- Show which tmux sessions are actually running
- Indicate crashed/missing sessions with warning
- Include all roles (Mayor, Deacon, Witnesses, Refineries, Crews)
- Distinguish between:
  - Structure: what COULD run
  - Runtime: what IS running

## Example Better Output
```
Agents
   mayor          ✓ running (gt-mayor)
   deacon         ✓ running (gt-deacon)

Rigs
   beads
      witness     ✓ running
      refinery    ✓ running
      crew/dave   ✗ CRASHED (session missing)
      crew/emma   ✓ running
   gastown
      witness     ✓ running
      refinery    ✓ running
      crew/max    ✗ CRASHED (session missing)
```


Heartbeat convention: simple liveness signal for agents

Lightweight liveness signal extracted from Deacon epic (gt-5af).

**Implementation**: Each agent writes a timestamp file on activity:
```bash
echo '{"ts":"'$(date -Iseconds)'"}' > ~/gt/<role>/heartbeat.json
```

**Integration points**:
- SessionStart hook writes heartbeat
- Periodic activity (mail check, work completion) refreshes it
- `gt status` shows staleness (e.g., 'mayor: 5m ago')

**Weight**: ~5 lines per agent
**Value**: Quick debugging - see which agents are active at a glance

No monitoring daemon needed - human checks `gt status` when curious.


Use cmd.OutOrStdout instead of fmt.Print in refinery

refinery/manager.go and refinery/engineer.go use fmt.Print/Println directly for user output (30+ occurrences). This breaks testability and doesn't follow cobra best practices. Should use cmd.OutOrStdout() or pass an io.Writer.

Affected files:
- internal/refinery/manager.go (lines 222, 360-361, 369, 387-393, 519, 537, 672)
- internal/refinery/engineer.go (lines 190-211, 249, 294-297, 325-353, 362-366)


Digest: mol-deacon-patrol

Patrol 15: All agents healthy


Day 1.2: Add agent-specific fields to schema

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

Parent: gt-d0jqp

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


Digest: mol-deacon-patrol

Patrol cycle 13: routine


Digest: mol-deacon-patrol

Patrol 14: routine


Swarm-in-Beads: Discovery-based swarm execution

Swarm-in-Beads: Make swarm state derive from beads, not separate JSON. See ~/gt/docs/swarm-architecture.md for full design. Core principles: discovery over tracking, ready front model, nondeterministic idempotence.


Digest: mol-deacon-patrol

Patrol 9: clear


Digest: mol-deacon-patrol

Cycle 12: quiet


Workspace detection: Find() walking up directory tree

Find workspace root by walking up directory tree looking for Gas Town markers.

## Detection Logic

Walk up from current directory, looking for:
1. `config/town.json` - Primary marker (visible config dir per gt-iib)
2. `mayor/` directory at town level - Secondary marker

Stop at filesystem root if neither found.

## Interface

```go
// Find locates the town root from the given directory
func Find(startDir string) (string, error)

// FindOrError is like Find but returns a user-friendly error
func FindOrError(startDir string) (string, error)
```

## Return Values

- Success: Absolute path to town root
- Not found: Empty string + specific error
- Error: Empty string + wrapped error

## Edge Cases

- Symlinks: Follow them (use filepath.EvalSymlinks)
- Permissions: Return error if can't read directory
- Nested towns: Return nearest ancestor (shouldn't happen in practice
EOF
)


Digest: mol-deacon-patrol

Patrol 15: all healthy


Swarm completion detection and auto-close

Detect when all epic children are closed and auto-close the swarm molecule.

Core logic:
1. After any issue closes, check if it was part of a swarm
2. Query bd list --parent=<epic> --status=open
3. If empty (all closed), close the swarm molecule
4. Fire completion event for activity feed

Can be implemented as:
- Part of Witness patrol
- Or as a bd hook triggered on issue close
- Or as a gt swarm check command called periodically

Reference: ~/gt/docs/swarm-architecture.md - Recovery Protocol

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


Test: Polecat lifecycle validation (auto-close)

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


Digest: mol-deacon-patrol

P14: stable


Digest: mol-deacon-patrol

Patrol: mayor handoff (Batch 1 complete, notifications working), 5 polecats now active (Batch 2)


Merge: gt-92l

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


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 5: All agents healthy, routine


Mol Mall: Molecule marketplace distribution

Design the Mol Mall distribution mechanism for sharing molecules.

## Core Insight

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

## Distribution Model

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

## Registry Format

Simple JSON index served from HTTPS:

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

## CLI Commands

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

## Installation Locations

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

## Version Management

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

## Auth (Future)

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

## Related

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


gt sling: support standalone formula slinging

## Summary
Enhance gt sling to support slinging standalone formulas (not just beads or formula-on-bead).

## Current Behavior
- `gt sling <bead> [target]` - works
- `gt sling <formula> --on <bead> [target]` - works (formula shapes bead)
- `gt sling <formula> [target]` - FAILS (tries to find bead named <formula>)

## Proposed Behavior
When first arg is not a bead but IS a formula, enter standalone formula mode:

```bash
gt sling mol-town-shutdown mayor/
gt sling towers-of-hanoi crew --var disks=3
```

Flow:
1. `verifyBeadExists()` fails
2. `verifyFormulaExists()` succeeds
3. Cook formula if needed (`bd cook`)
4. Create wisp instance (`bd wisp`)
5. Attach to target hook
6. Nudge to start

## Implementation
1. Add formula detection fallback in `runSling()`
2. Add `runSlingFormula()` helper
3. Add `--var` flag for formula variables

## Files
- internal/cmd/sling.go


Pillar 3: Polecat Lifecycle

Session-per-step model with automated recycle/nuke.

Key deliverables:
- gt polecat recycle <name> - kill session, preserve sandbox
- gt polecat nuke <name> - kill everything
- Witness calls recycle between molecule steps
- Refinery calls nuke after merge

Subsumes existing work:
- gt-budeb: Witness auto-nuke polecats after merge
- gt-1dbcp: Polecat auto-start nudge fix

Reference: ~/gt/docs/liftoff-plan.md

Parent: gt-oki8p


Build context self-check into patrol molecules

Patrol molecules (witness-patrol, refinery-patrol, deacon-patrol) should have
explicit context self-check as part of each cycle:

After each patrol cycle step:
- Self-assess context pressure
- If high: complete current step, then initiate handoff
- If OK: continue to next cycle

This could be:
1. Explicit step in molecule: 'context-check'
2. Guidance in step instructions: 'Before continuing, assess context...'
3. Protocol baked into the patrol loop description

The key is making it unavoidable - not optional guidance that gets ignored.


Digest: mol-deacon-patrol

Patrol 20: All healthy, handoff triggered


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


Add handoff self-initiation protocol to role templates

Agents need clear protocol for self-initiated handoff:

1. Recognize: 'I should cycle now'
2. Prepare: Summarize current state, what's next
3. Send: gt mail send <self> -s '🤝 HANDOFF: ...' -m '...'
4. Exit: End session cleanly

This replaces external 'you should cycle now' nudging.
The agent owns its lifecycle.

Include examples for each role type.


Merge: gt-ay1r

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


Add self-check context guidance to role CLAUDE.md files

Each role's CLAUDE.md needs guidance for context self-management:

- After each patrol cycle, self-assess context pressure
- Heuristics: feeling compressed, lots of tool calls, long session
- If triggered: initiate handoff before continuing

Roles to update:
- Mayor (~/gt/CLAUDE.md)
- Witness template
- Refinery template
- Polecat template
- Crew template
- Deacon template

This is the 'inversion of control' - agents decide when to cycle, not external monitoring.


Update architecture.md: Engineer role and Beads merge queue

Update docs/architecture.md with recent design decisions:

1. Agent table: Change "Refinery" role to "Engineer"
   - Refinery = place/module/directory
   - Engineer = role (agent that works in the Refinery)

2. Merge Queue section: Document Beads-native model
   - MRs are beads issues with --type=merge-request
   - gt mq commands (submit, list, next, process, reorder)
   - Ordering via depends-on links

3. CLI section: Add gt mq commands

4. Key Design Decisions: Add decisions for:
   - #15: Merge Queue in Beads
   - #16: Engineer role (distinct from Refinery place)
   - #17: Session restart protocol for Engineer


VM outpost setup: Terraform and documentation

## Overview

Documentation and optional Terraform for setting up VM outposts.

## Manual Setup Guide

```markdown
# Setting Up a VM Outpost

## Prerequisites
- GCE VM (or any Linux machine with SSH access)
- SSH key pair
- Git configured on VM

## Steps

1. Install Claude Code on VM:
   ```bash
   ssh user@vm
   npm install -g @anthropic-ai/claude-code
   ```

2. Clone Gas Town:
   ```bash
   mkdir -p ~/ai
   cd ~/ai
   gt install .
   ```

3. Configure Git credentials:
   ```bash
   git config --global user.name "Your Name"
   git config --global credential.helper store
   ```

4. Add outpost to local config:
   ```bash
   gt outpost add ssh \
     --name gce-burst \
     --host 10.0.0.5 \
     --user steve \
     --key ~/.ssh/gce_worker \
     --town-path /home/steve/ai \
     --max-workers 8
   ```

5. Test connectivity:
   ```bash
   gt outpost ping gce-burst
   ```
```

## Terraform Module (Optional)

```hcl
# deploy/terraform/vm-outpost/main.tf

variable "project" {}
variable "zone" { default = "us-central1-a" }
variable "machine_type" { default = "e2-standard-4" }

resource "google_compute_instance" "outpost" {
  name         = "gastown-outpost"
  machine_type = var.machine_type
  zone         = var.zone

  boot_disk {
    initialize_params {
      image = "ubuntu-2204-lts"
      size  = 100
    }
  }

  network_interface {
    network = "default"
    access_config {}
  }

  metadata_startup_script = file("${path.module}/startup.sh")
}

output "external_ip" {
  value = google_compute_instance.outpost.network_interface[0].access_config[0].nat_ip
}
```

## Files

- `docs/vm-outpost-setup.md`
- `deploy/terraform/vm-outpost/` (optional)

Depends on: gt-9a2.5 (SSHOutpost)


Build rig/worker tree view component

Bubbletea component showing hierarchical rig list with workers. Each rig expandable to show workers. Each worker shows: status indicator, current molecule title, step progress (X/Y), time since last activity.


MessagingConfig: Fix error message inconsistency in validation

Negative value errors don't wrap ErrMissingField while empty-list errors do. Should be consistent - either all wrap a sentinel or none do.


Digest: mol-deacon-patrol

Patrol 12: all healthy


Add tests for gt crew restart --all

Add integration tests for the crew restart flow:

- Test getAgentSessions() correctly identifies crew sessions
- Test --rig filtering works correctly
- Test --dry-run outputs correct targets
- Test session kill/recreate cycle (without Claude)

Consider extracting a SessionStarter interface to mock the claude-launching bit.

Related: gt crew restart --all was just implemented.


Role prompts have mismatched startup protocols

Different role prompts describe different startup protocols:

- deacon.md: Check gt mol status, spawn if none: bd mol spawn
- refinery.md: Check bd list --status=in_progress, bond if none: gt mol bond
- polecat.md: Check bd mol current

These should use consistent terminology and commands.
The propulsion principle should be applied uniformly.


VC Pattern Integration: Bring validated ideas to Gas Town

Analysis of ~/src/vc identified 6 validated patterns from the 2nd orchestrator attempt that map cleanly to Gas Town primitives. VC achieved 254 issues closed, 90.9% gate pass rate, and 24 successful missions.

Key insight: VC built ~4300 lines of Go for features that become ~65 lines of YAML + CLI flags in Gas Town's architecture.

Child tasks track each pattern to integrate.


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.


Daemon must delete LIFECYCLE messages after processing

## Problem

Crew workers were being killed every 5 minutes.

## Root Cause

Old stale LIFECYCLE messages from days ago were sitting in the deacon's inbox. The daemon's ProcessLifecycleRequests() was re-finding and re-processing them every 5-minute heartbeat cycle.

The closeMessage() function in lifecycle.go calls 'gt mail delete' but failures are silent - the message stays in the inbox and gets reprocessed on the next heartbeat.

## Fix Required

1. Ensure closeMessage() actually deletes the message
2. If delete fails, log the error AND mark the message as read so it's not reprocessed
3. Consider adding a 'processed' label or moving to a processed folder

## Files

- internal/daemon/lifecycle.go: closeMessage() function
- internal/daemon/lifecycle.go: ProcessLifecycleRequests() loop


Activity stream infrastructure check

Verify activity stream infrastructure exists and is sufficient for MQ/witness events.

Check:
- Does gt feed already have event infrastructure?
- Can we emit custom events from Go code?
- What format do events need?

If infrastructure exists, close this. If not, implement minimal event emission.


Digest: mol-deacon-patrol

Patrol cycle 1: inbox clear, 8 in-progress issues, sessions healthy, gc not implemented


Digest: mol-deacon-patrol

Patrol 20: final cycle


Digest: mol-deacon-patrol

Patrol 6: All clear


Add audit trail for hook detach operations

Log detachment events for debugging and monitoring.

When work is detached from hook:
- Log timestamp, agent, molecule ID, reason
- If abnormal (not completion), notify Witness
- Consider adding to wisp digest for patrol cycles


Find abandoned work

Find abandoned work.

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

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

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


Spawn --molecule integration

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

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

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

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

Files: internal/cmd/spawn.go

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


Merge: gt-rana.1

branch: polecat/rictus
target: main
source_issue: gt-rana.1
rig: gastown


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.


Update role prompts with hook protocol

Update role prompt templates to include hook-first protocol.

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

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


Epic: Patrol parity - Witness and Refinery match Deacon sophistication

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

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

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

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


Digest: mol-deacon-patrol

Patrol 1: All healthy - Mayor OK, 2 witnesses OK, 2 refineries OK, 1 polecat active (furiosa). No lifecycle requests or orphans.


Digest: mol-deacon-patrol

Patrol OK: archived old handoff, all agents up, furiosa on gt-oiv0


mol-deacon-patrol


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

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

## Current State

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

## Enhancement

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

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

## Implementation

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

## Related

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


Campaign: Large-Scale Work Graph Execution

A Campaign is a Molecule-wrapped Epic graph ready for orchestrated execution.

## Concept

Campaign = Molecule + Epics + Dependency Graph + Execution Strategy

Key properties:
1. **Work graph**: Linked epics with dependency edges (possibly from different authors)
2. **Combinators as glue**: >>, |, wrap define bridging/shaping between epics
3. **Molecule shell**: Provides execution state, persistence across sessions
4. **Witness-aware**: Can be parallelized across polecats on independent branches
5. **Single-rig scoped**: One rig executes it (spans sessions/polecats)

## The "Maximum Speed" Model

Witness becomes a scheduler that:
- Watches campaign progress
- Identifies ready nodes (all dependencies satisfied)
- Spawns/assigns polecats to ready nodes
- Respects parallelism constraints
- Handles failures (retry? skip? escalate?)

## Where Campaign Fits in MEOW

Campaign is the convergence point where work-tracking meets workflow-execution:
- An Epic-of-Epics (work dimension)
- A Molecule (workflow dimension)

## Open Questions

1. Campaign creation UX - manual wire vs declare intent?
2. Partial completion - can you commit progress and resume?
3. Distinct entity type or molecule with type="campaign"?
4. Cross-rig campaigns - can graph span multiple rigs?

## Related

- docs/formula_evolution.md - original discussion
- gt-8tmz - molecule algebra (foundation)


Merge: gt-3x1.3

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


Digest: mol-deacon-patrol

Patrol complete: beads witness+refinery came online, both rigs healthy


Add unit tests for internal/lock package

attached_args: Lock package tests

Add comprehensive tests for the file locking package.

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

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

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


load-state

Read handoff bead and get nudge counts.

Needs: check-refinery


Digest: mol-deacon-patrol

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


Town & Rig Management: install, doctor, federation

Reify the Gas Town installation as a first-class concept.

## Goals
- Installable: gt install [path] creates complete installation
- Diagnosable: gt doctor checks and fixes issues
- Federable: Clone town to VMs with central control

## Architecture Reference

See docs/architecture.md for full design, especially:
- Directory structure (Town Level / Rig Level sections)
- Configuration (town.json, rigs.json schemas)
- Key design decisions (visible config dir, decentralized agents)


Digest: mol-deacon-patrol

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


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.


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


Digest: mol-deacon-patrol

Patrol cycle 13: nominal


Phase 2.3: Polecat CLAUDE.md molecule workflow protocol

Update polecat prompting for molecule-based work.

## CLAUDE.md Updates

Add section on molecule workflow:

```markdown
## Molecule Workflow

When assigned a molecule (check gt prime output):

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

### Summary Guidelines

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

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


Digest: mol-deacon-patrol

Patrol 12: nominal


implement

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

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

Depends: load-context


Digest: mol-deacon-patrol

Patrol 2: All healthy


Auto-restart patrol agents after N loops

All patrol roles should auto-restart to keep context usage low. Role-specific heuristics:

**Deacon**:
- 20 patrol loops without major incident
- Immediate restart after ANY extraordinary action (lifecycle request, remediation, escalation)
- Rationale: Keep context short so there's headroom if something big comes up

**Witness**:
- Based on polecats processed (not loop count)
- Restart after processing N polecats (spawns, nudges, decommissions)
- Suggested N: 10-15 polecats

**Refinery**:
- Based on MRs processed (not loop count)
- Restart after processing N merge requests
- Suggested N: 5-10 MRs (merges are context-heavy)

Benefits:
- Keeps context fresh (better focus/performance)
- Reduces cost (quadratic increase vs conversation length)
- Preserves headroom for handling surprises
- Simple and predictable (no context estimation needed)

Each role tracks their metric in state file and hands off when threshold reached.


Witness: aggregate completion and notify Mayor

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

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

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

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

Witness parses SWARM_START and initializes tracking in state.json.

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

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


CLI: doctor diagnostics and auto-repair

GGT completely lacks the doctor command which is critical for debugging.

Required Commands:
- gt doctor [<rig>] - Run diagnostic checks
- gt doctor --fix - Auto-repair common issues

Checks to Implement:
Workspace Level: Config validity, Mayor mailbox, Rig registry
Rig Level: Git state, clone health, witness/refinery presence, beads sync
Swarm Level: Stuck detection, zombie sessions, heartbeat health

PGT Reference: gastown-py/src/gastown/cli/dashboard_cmd.py


Digest: mol-deacon-patrol

Routine patrol, town stable


Define meta-formula YAML schema

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

## Proposed Schema

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

## Key Decisions

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

## Deliverables

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


HTTP work client: CloudRun dispatch client

## Overview

HTTP client for dispatching work to Cloud Run and streaming results back.

## Implementation

```go
type WorkClient struct {
    serviceURL string
    httpClient *http.Client  // HTTP/2 enabled
}

func NewWorkClient(serviceURL string) *WorkClient {
    return &WorkClient{
        serviceURL: serviceURL,
        httpClient: &http.Client{
            Transport: &http2.Transport{},
            Timeout:   0,  // No timeout for streaming
        },
    }
}

func (c *WorkClient) DispatchWork(ctx context.Context, req WorkRequest) (<-chan WorkEvent, error) {
    // 1. POST to /work
    // 2. Return channel that streams WorkEvents
    // 3. Close channel when done/error
}
```

## Streaming Reader

```go
func (c *WorkClient) streamEvents(body io.Reader, events chan<- WorkEvent) {
    defer close(events)
    decoder := json.NewDecoder(body)
    for {
        var event WorkEvent
        if err := decoder.Decode(&event); err != nil {
            return
        }
        events <- event
    }
}
```

## Files

- `internal/cloudrun/client.go`

## Dependencies

Depends on: gt-9a2.7 (protocol types)
Used by: gt-9a2.8 (CloudRunOutpost)


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


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.


Inline step expansion (Step.Expand field)

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

## Current State

Steps can declare inline expansion:

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

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

## Difference from compose.expand

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

## Implementation

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

## Files

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

## Depends On

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


Clean dead sessions

Clean dead sessions.

Garbage collect terminated sessions:
- Remove stale polecat directories
- Clean up wisp session artifacts
- Prune old logs and temp files
- Archive completed molecule state

```bash
gt gc --sessions
gt gc --wisps --age=1h
```

Preserve audit trail. Only clean sessions confirmed dead.


Digest: mol-deacon-patrol

Patrol 3: Quick cycle, all quiet


Phase 5.2: Ephemeral error handling and recovery

Handle edge cases in ephemeral molecule lifecycle.

## Scenarios

### Agent dies mid-molecule
- Ephemeral state persists
- Witness detects stall (no heartbeat)
- Options:
  a. Nudge agent to resume
  b. Reassign to new agent
  c. Escalate to Mayor

### Squash fails
- Retry with exponential backoff
- If persistent failure, escalate
- Don't lose the ephemeral state until squash succeeds

### Orphaned molecules
- Molecule started, never completed
- gt doctor detects (>24h old with no activity)
- Options:
  a. Manual review
  b. Auto-abandon with 'abandoned' digest
  c. Reassign

### Ephemeral repo corruption
- Re-init from scratch
- Active molecules are lost
- Main beads is source of truth for assigned work

## Implementation

Add error handling to:
- bd mol bond (handle init failures)
- bd squash (retry logic)
- gt doctor (recovery suggestions)