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


Digest: mol-deacon-patrol

Patrol 3: all clear


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-test123 - The source issue ID being worked on


Remove 'spawn' terminology from molecular chemistry

Clean all docs and code to present ONLY current terminology. No migration paths, no "old vs new" tables, no deprecated flags. The codebase should read as if the current design was always the design.

**Terminology rules:**
- spawn = polecats/workers ONLY
- pour = create persistent mol
- wisp = create ephemeral wisp
- run = create and execute

**Scope:**
- Remove all "Old → New" migration tables
- Remove deprecated flag documentation (--persistent, etc.)
- Remove chemistry-design-changes.md entirely (it is a migration doc)
- Clean all docs to use current terminology only
- Rename spawn functions in code that deal with molecules


Digest: mol-deacon-patrol

Patrol 9: healthy


load-state

Read handoff bead and get nudge counts.

Needs: check-refinery


Digest: mol-deacon-patrol

Patrol 4: Core healthy, no changes


Digest: mol-deacon-patrol

Patrol 16: All healthy


Digest: mol-deacon-patrol

Patrol 19: Quiet


Digest: mol-deacon-patrol

Patrol cycle 11: nominal (6 steps)


Digest: mol-deacon-patrol

Patrol 14: all quiet


Polecat-Witness protocol handlers

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


Digest: mol-deacon-patrol

Patrol 7: all clear


Digest: mol-deacon-patrol

Patrol 2: Quick scan, all healthy, no work


Digest: mol-deacon-patrol

Patrol 8: OK


Witness patrol: Add banners and wisp-based execution

Bring Witness patrol up to Deacon's level of sophistication:

## Current state
- mol-witness-patrol exists (gt-qflq)
- Basic step definitions

## Needed
1. **Banners** - Print step banners like Deacon does:
   ```
   ═══════════════════════════════════════════════════════════════
     👁 POLECAT-SCAN
     Checking polecat health and nudging stale workers
   ═══════════════════════════════════════════════════════════════
   ```

2. **Wisp-based execution** - Spawn patrol as wisp, squash when complete
3. **Handoff bead attachment** - Witness needs its own handoff bead with attached_molecule
4. **Loop-or-exit step** - Context-aware cycling like Deacon
5. **Patrol summary banner** at end of each cycle

## Reference
See Deacon patrol implementation in ~/gt/deacon/CLAUDE.md


Nudge newly spawned polecats

Nudge newly spawned polecats that are ready for input.

When polecats are spawned, their Claude session takes 10-20 seconds to initialize.
The spawn command returns immediately without waiting. This step finds spawned
polecats that are now ready and sends them a trigger to start working.

```bash
# For each rig with polecats
for rig in gastown beads; do
    gt polecats $rig
    # For each working polecat, check if Claude is ready
    # Use tmux capture-pane to look for "> " prompt
done
```

For each ready polecat that hasn't been triggered yet:
1. Send "Begin." to trigger UserPromptSubmit hook
2. The hook injects mail, polecat sees its assignment
3. Mark polecat as triggered in state

Use WaitForClaudeReady from tmux package (polls for "> " prompt).
Timeout: 60 seconds per polecat. If not ready, try again next cycle.


gt-polecat-gastown-ace

gt-polecat-gastown-ace

role_type: polecat
rig: gastown
agent_state: running
hook_bead: gt-795e8
role_bead: null


Digest: mol-deacon-patrol

Patrol #5: Stable


gt-gastown-polecat-charlie

gt-gastown-polecat-charlie

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


gt polecat: add 'done' or 'finish' command to transition from working to idle

When a polecat finishes work but session wasn't properly cleaned up, there's no way to reset it from 'working' state back to 'idle'.

Tried:
```
gt polecat sleep gastown/Angharad
Error: sleeping polecat: polecat is not active (state: working)
```

The sleep command only works on 'active' polecats, not 'working' ones.

Had to manually edit state.json to reset:
```
jq '.state = "idle" | .issue = ""' state.json
```

Need a command like:
```
gt polecat done gastown/Angharad   # working -> idle
gt polecat finish gastown/Angharad # working -> idle
gt polecat reset gastown/Angharad  # any state -> idle (force)
```


Digest: mol-deacon-patrol

Patrol #3: Stable, no changes


Digest: mol-deacon-patrol

Patrol 9


Check own context limit

Check own context usage.

If context is HIGH (>80%):
- Ensure state is saved to handoff bead
- Prepare for burn/respawn

If context is LOW:
- Can continue patrolling


Digest: mol-deacon-patrol

Patrol 9: Quick cycle, all quiet


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


bd admin compact --older-than=0 ignores the flag, uses default 30 days

When running bd admin compact --prune --older-than=0, the output says 'No expired tombstones to prune (TTL: 30 days)' - the flag value is ignored.


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


gt sling/handoff fails: slashes in agent identity create invalid hook paths


Digest: mol-deacon-patrol

Patrol 5: routine, all healthy


gt rig reset --stale: Clear orphaned in_progress items

Reset in_progress issues when their assigned agent no longer exists.

## Problem
When polecats die without cleanup, their issues remain in_progress forever.
Need a way to bulk-reset these orphaned items.

## Command
```bash
gt rig reset --stale [--dry-run]
```

## Logic
For each in_progress issue in rig:
1. Parse assignee (e.g., "gastown/furiosa")
2. Map to tmux session name (gt-gastown-furiosa)
3. If session does NOT exist:
   - Reset status to "open"
   - Clear assignee
4. Exception: skip crew/* assignees (persistent identities)
   OR check if crew tmux session exists

## Output
```
Resetting stale work in gastown:
  gt-abc: gastown/furiosa (no session) → open
  gt-def: gastown/nux (no session) → open
  Skipped: gt-xyz: gastown/crew/max (persistent)
Reset 2 issues, skipped 1
```

## Related
- gt-2kz: CLI cleanup commands for stale state
- gt-rdmw: orphan-check in deacon patrol
- gt-orphans command (list orphaned molecules)


Digest: mol-deacon-patrol

Patrol complete: 6 msgs archived, all agents healthy, cleaned 29 stale mols


Mail system: message types and threading

GGT mail system needs message types and threading like PGT.

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

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

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

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

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

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

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

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


Test: Alpha to Beta

Sibling communication test


Workspace cleanup: preflight and postflight

Workspace preflight and postflight commands for clean state management.

## Preflight
```
gt preflight [--rig <rig>] [--dry-run]
```

Run before starting batch work:
1. Clean stale mail in inboxes
2. Check for stuck workers (warn)
3. Check rig health (polecats, refinery)
4. Verify git state is clean
5. Run bd sync to ensure beads current

## Postflight
```
gt postflight [--rig <rig>] [--archive-mail] [--dry-run]
```

Run after batch work completes:
1. Archive old mail with --archive-mail
2. Clean up stale integration branches
3. Sync beads
4. Report on rig state

## Implementation
```go
func Preflight(rigName string, dryRun bool) (*PreflightReport, error)
func Postflight(rigName string, opts PostflightOptions) (*PostflightReport, error)
```

## Report Structures
```go
type PreflightReport struct {
    MailCleaned    int
    RigHealthy     bool
    StuckWorkers   []string
    Warnings       []string
}

type PostflightReport struct {
    MailArchived   int
    BranchesCleaned int
    Warnings       []string
}
```

## Note

These are workspace maintenance commands, not tied to "swarm" lifecycle. Run them anytime to keep the rig clean.

## Acceptance Criteria
- [ ] Preflight cleans stale state
- [ ] Postflight archives old mail
- [ ] Both have --dry-run mode
- [ ] Clear reports of actions taken


Clean dead sessions

Clean dead sessions and orphaned state.

Run `gt doctor --fix` to handle all cleanup:

```bash
# Preview what needs cleaning
gt doctor -v

# Fix everything
gt doctor --fix
```

This handles:
- **orphan-sessions**: Kill orphaned tmux sessions (gt-* not matching valid patterns)
- **orphan-processes**: Kill orphaned Claude processes (no tmux parent)
- **wisp-gc**: Garbage collect abandoned wisps (>1h old)

All cleanup is handled by doctor checks - no need to run separate commands.


Fanout gate runtime evaluation

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


gt spawn should use bd mol run for molecule attachment

Simplify Gas Town to use bd mol run for work tracking.

## Key Insight
Two distinct mechanisms, not duplicative:

| Mechanism | Purpose | Query |
|-----------|---------|-------|
| **Pinned molecule** (bd mol run) | What am I working on? | `bd list --pinned --assignee=me` |
| **Handoff mail** | Context notes for restart | `gt mail read` (self-addressed) |

The handoff is just **mail to yourself** - optional context notes.
The molecule is **the actual work** - required state.

## Current State (Overengineered)
Gas Town has custom attachment system:
- Permanent "Foo Handoff" pinned beads per identity
- AttachMolecule(pinnedBeadID, moleculeID)
- Attachment fields parsed from description
- Separate from beads pinning

## New Model (Simplified)
Use bd mol run directly:

```bash
# Spawn polecat with molecule
bd mol run mol-polecat-work --var issue=gt-xyz
# This: spawns, assigns to caller, pins root, sets in_progress
```

Query current work:
```bash
bd list --pinned --assignee=gastown/furiosa --status=in_progress
```

Handoff context (when needed):
```bash
gt mail send gastown/furiosa -s "Context notes" -m "Was on step 4..."
```

## Changes Required

### Remove from Gas Town
- AttachMolecule() / DetachMolecule()
- AttachmentFields struct and parsing
- GetAttachment() / SetAttachmentFields()
- Permanent pinned handoff beads per identity
- Daemon attachment detection (checkDeaconAttachment)

### Update gt spawn
```go
// Old: custom molecule instantiation + attachment
// New: just call bd mol run
cmd := exec.Command("bd", "mol", "run", protoID, "--var", "issue="+issueID)
```

### Update gt prime / agent context
```go
// Old: find handoff bead, parse attachment
// New: query for pinned molecule
cmd := exec.Command("bd", "list", "--pinned", "--assignee="+identity, "--status=in_progress", "--json")
```

### Update documentation
- Remove handoff bead attachment docs
- Clarify: handoff = mail, molecule = work
- Update CLAUDE.md templates

## Benefits
1. One system for work tracking (beads)
2. Simpler Gas Town code
3. bd mol squash works naturally
4. Handoff is just mail (already works)

## Related
- gt-3x0z: Wisp Molecule Integration
- gt-rana: Patrol System
- gt-lek6: gt rig reset --stale
- gt-ay1r: gt molecule current


ensure-gastown-refinery

Start the gastown rig Refinery.

## Action
```bash
gt refinery start gastown
```

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

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

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


Witness Role Definition

You are the Witness - per-rig worker monitor. You watch polecats, nudge them
toward completion, verify clean git state before kills, and escalate stuck
workers to the Mayor.

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

default_molecule: mol-witness-patrol
capabilities:
  - monitor_workers
  - nudge_stuck
  - pre_kill_verify
  - session_lifecycle
  - escalation

## Core Responsibilities

1. Monitor workers: Track polecat health and progress
2. Nudge: Prompt slow workers toward completion
3. Pre-kill verification: Ensure git state is clean before killing sessions
4. Session lifecycle: Kill sessions, update worker state
5. Self-cycling: Hand off to fresh session when context fills
6. Escalation: Report stuck workers to Mayor

Key principle: You own ALL per-worker cleanup. Mayor is never involved in
routine worker management.

## Propulsion Principle

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

Your work is defined by the mol-witness-patrol molecule. Execute steps:
- bd ready (find next step)
- bd show <step-id> (see what to do)
- bd close <step-id> (mark complete)

## Nudge Protocol

Progress through stages. Track nudge count per worker per issue.
- First Nudge (10+ min idle): Gentle inquiry
- Second Nudge (15 min): Direct question
- Third Nudge (20 min): Final warning
After 3 nudges with no progress -> escalate to Mayor

## Pre-Kill Verification

Before killing ANY polecat session:
1. gt polecat git-state <name> - Must be clean
2. Check for uncommitted work
3. Check for unpushed commits
4. Verify issue closed
5. Verify PR submitted (if applicable)

## Commands

- gt polecat list <rig> - See all polecats
- gt polecat git-state <name> - Check git cleanliness
- tmux capture-pane -t gt-<rig>-<name> -p | tail -40 - Session inspection
- gt mail inbox - Check messages
- gt mail send mayor/ -s "Subject" -m "Message" - Escalate


Digest: mol-deacon-patrol

Patrol cycle 19: All healthy


Digest: mol-deacon-patrol

Patrol #10: Halfway checkpoint


Policy beads: config in data plane

Use sentinel/policy beads for configuration instead of external config. Examples: daemon notifications on/off, heartbeat intervals. Config lives in the bead graph, can be toggled by closing/opening policy beads.


CLI: polecat commands (add, list, wake, sleep, decommission)

GGT is missing most polecat management commands that PGT has.

Missing Commands:
- gt polecat add <rig> <name> - Add polecat to rig (creates clone)
- gt polecat list [<rig>] - List polecats with state
- gt polecat info <polecat> - Show detailed info
- gt polecat wake <polecat> - Mark available
- gt polecat sleep <polecat> - Mark unavailable
- gt polecat decommission <polecat> - Remove polecat safely

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

Notes:
- spawn exists but doesn't cover management
- wake/sleep are in polecat manager but not CLI
- decommission should check for uncommitted work


Digest: mol-deacon-patrol

Patrol 1: All healthy, no messages, no orphans


Verify witness patrol loop executes

Verify witnesses run their patrol formula:

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

Test for both rigs: gastown and beads.


Merge: slit-1767059695613

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


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

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


Digest: mol-deacon-patrol

Patrol 11: quiet


Day 2.5: Test daemon pokes based on agent state

Integration test:
1. Create agent bead with state=idle
2. Sling work → hook_bead set
3. Verify daemon pokes
4. Update state=running
5. Verify daemon doesn't double-poke
6. Update state=stopped
7. Verify daemon re-pokes on new work

Parent: gt-d0jqp


Digest: mol-deacon-patrol

Patrol 19: Quiet


Fix docs: Formulas are JSON not YAML

The molecular chemistry docs (molecule-algebra.md, molecular-chemistry.md) incorrectly refer to formulas as YAML. Formulas are JSON (.formula.json). Fix all references: .formula.yaml -> .formula.json, 'YAML files' -> 'JSON files', 'formula YAML' -> 'formula JSON', code blocks from yaml to json syntax.


Digest: mol-deacon-patrol

Patrol 4: Routine, witnesses/refineries healthy


gt mail send: support reading message body from stdin

Currently gt mail send -m requires the message as a command-line argument, which causes shell escaping issues with backticks, quotes, and special characters.

Add support for reading message body from stdin:
- gt mail send addr -s 'Subject' --stdin  # Read body from stdin
- echo 'body' | gt mail send addr -s 'Subject' -m -  # Convention: -m - means stdin

This would allow:
cat <<'EOF' | gt mail send addr -s 'Subject' --stdin
Message with \`backticks\` and 'quotes' safely
EOF

Without this, agents struggle to send handoff messages containing code snippets.

## Moved from beads
Originally bd-3bsz. gt mail is in gastown, not beads.


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.


gt session capture: Support positional line count argument

Make 'gt session capture gastown/Toast 50' work.

Currently requires: gt session capture gastown/Toast -n 50
Should also accept: gt session capture gastown/Toast 50

Agent UX principle: commands should work the way agents guess they work.


Merge: gt-5af.6

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


Digest: mol-deacon-patrol

Patrol 11: all quiet


Digest: mol-deacon-patrol

Patrol 19: All healthy


Digest: mol-deacon-patrol

Patrol 18: Quiet


gt crew restart command

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

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

Alias: gt crew rs


test-after-fix

test


Digest: mol-deacon-patrol

Patrol 7: all healthy


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 20: routine, all healthy


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)


Rollback support: gt mq revert for problematic merges

Implement 'gt mq revert <mr-id>' for rolling back merges.

Actions:
1. Find merge commit from closed MR
2. Create revert commit: git revert <merge-commit>
3. Push revert to target branch
4. Create 'reverted' MR tracking the revert
5. Optionally reopen source issue

Options:
- --reason REASON: required explanation
- --reopen-issue: reopen the source work item

Track revert in MR metadata for audit trail.

Reference: docs/merge-queue-design.md#open-questions


Digest: mol-deacon-patrol

Patrol 14: clear


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


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.


Refinery semantic merge processing loop

Implement the Refinery semantic merge loop for processing polecat work.

## Overview

The Refinery is a fully empowered Claude Code agent that:
1. Fetches polecat branches
2. Merges to integration branch
3. Resolves conflicts semantically (re-implements if needed)
4. Runs tests
5. Escalates only when truly stuck

## Processing Loop

1. Check queue for completed tasks (polecat done, not yet merged)
2. For each task:
   - Fetch polecat branch
   - Attempt merge to integration/<swarm-id>
   - On conflict: semantic resolution (understand intent, re-implement)
   - Run tests if configured
   - On success: mark merged, file bead, notify witness
   - On failure: notify polecat (test fail) or escalate (stuck)
3. When all tasks merged: land integration to main

## Semantic Merge

When git merge fails, Refinery:
1. Reads polecat changes and beads issue
2. Understands intent
3. Re-implements on current integration branch
4. Only escalates if truly blocked

## Daemon Poking

Refinery is poked by:
- Daemon heartbeat (periodic)
- Witness notification (polecat done)
- Mayor request (check queue)

## Dependencies

Needs: gt-u1j.6 (mail), gt-u1j.3 (git), gt-u1j.14 (merge queue)

## Reference

See PGT ephemeral.py for branch operations.


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

Patrol 20: final patrol before handoff


queue: shared storage work queues

## queue: Shared Storage Work Queues

Work queue with shared storage (single copy, first-to-claim).

## Pattern Clarification

**Queue is a MAILBOX pattern** (pull-based):
- Workers poll shared queue
- First to claim gets the work
- Worker decides when to check

**This is DIFFERENT from Dog dispatch** (push-based):
- `gt sling <work> deacon/dogs` pushes to hook
- GUPP fires immediately
- Deacon decides when to assign

| Pattern | Mechanism | Initiative |
|---------|-----------|------------|
| `queue:` | Poll mailbox, claim | Worker pulls |
| Dog sling | Hook assignment | Deacon pushes |

## Use Cases for queue:

1. **Batch cleanup tasks** - Multiple agents can help, order does not matter
2. **Overflow work** - When primary handler is busy
3. **Opportunistic tasks** - Agents check between other work

## Deliverables

1. Parse queue:name syntax in router
2. Shared storage model:
   - Single message copy (NOT fan-out)
   - Stored in queue-specific location
   - Multiple agents can see it
3. Queue configuration in ~/gt/config/queues.json
4. Queue inbox command: gt mail queue <name>
5. Claim semantics: `gt mail claim <msg-id>` marks as claimed

## Key Difference from list:
- list: creates N copies (N obligations)
- queue: creates 1 copy (1 obligation, first-to-claim)

## Dependencies
- Config directory (gt-i6jvc) ✅ closed

## Acceptance
- queue:cleanup creates single message
- Multiple agents can view queue
- Claim marks message as taken
- Message invisible to others after claim


Digest: mol-deacon-patrol

Patrol 6: All healthy


Outpost CLI commands: gt outpost list/status/add

## Overview

CLI commands for managing outposts.

## Commands

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

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

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

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

# Remove outpost
gt outpost remove <name>

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

## Files

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

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


Digest: mol-deacon-patrol

Patrol 3: All agents healthy, routine


Create internal/tui/ package with feed model

Create the TUI infrastructure for gt feed.

## Package structure
```
internal/tui/
  feed/
    model.go       # bubbletea Model struct
    view.go        # View() rendering
    update.go      # Update() message handling
    events.go      # Event stream subscription (tail .feed.jsonl)
    styles.go      # lipgloss styles
    keys.go        # Key bindings
```

## Model components
1. **AgentTree** - Collapsible tree of agents by role
2. **EventStream** - Scrollable viewport of recent events
3. **StatusBar** - Current filter, help hints
4. **FilterPopup** - Modal for setting filters

## Key bindings
- j/k - scroll events
- J/K - scroll agent tree
- tab - switch focus (tree/stream)
- enter - expand/details
- / - search
- f - filter popup
- q - quit
- ? - help

## Event subscription
- Tail ~/gt/.feed.jsonl using fsnotify or polling
- Parse JSONL events
- Send as bubbletea messages to update model


Outpost error handling: Retry logic and resilience

## Overview

Error handling and retry logic for outpost operations.

## Error Types

```go
var (
    ErrOutpostUnreachable = errors.New("outpost unreachable")
    ErrWorkerSpawnFailed  = errors.New("worker spawn failed")
    ErrWorkerTimeout      = errors.New("worker timed out")
    ErrCostCapExceeded    = errors.New("cost cap exceeded")
    ErrAllOutpostsBusy    = errors.New("all outposts at capacity")
)
```

## Retry Policy

```go
type RetryPolicy struct {
    MaxAttempts     int
    InitialBackoff  time.Duration
    MaxBackoff      time.Duration
    BackoffFactor   float64
    RetryableErrors []error
}

func DefaultRetryPolicy() *RetryPolicy {
    return &RetryPolicy{
        MaxAttempts:    3,
        InitialBackoff: 1 * time.Second,
        MaxBackoff:     30 * time.Second,
        BackoffFactor:  2.0,
        RetryableErrors: []error{
            ErrOutpostUnreachable,
            ErrWorkerSpawnFailed,
        },
    }
}
```

## Retry Implementation

```go
func (m *OutpostManager) SpawnWithRetry(issue string, cfg WorkerConfig) (Worker, error) {
    var lastErr error
    backoff := m.retryPolicy.InitialBackoff

    for attempt := 0; attempt < m.retryPolicy.MaxAttempts; attempt++ {
        outpost := m.policy.SelectOutpost(issue, m.outposts)
        if outpost == nil {
            return nil, ErrAllOutpostsBusy
        }

        worker, err := outpost.Spawn(issue, cfg)
        if err == nil {
            return worker, nil
        }

        if !m.isRetryable(err) {
            return nil, err
        }

        lastErr = err
        time.Sleep(backoff)
        backoff = min(backoff * m.retryPolicy.BackoffFactor, m.retryPolicy.MaxBackoff)
    }

    return nil, fmt.Errorf("spawn failed after %d attempts: %w",
        m.retryPolicy.MaxAttempts, lastErr)
}
```

## Outpost Health Tracking

```go
type OutpostHealth struct {
    LastPing      time.Time
    LastError     error
    FailureCount  int
    CircuitBreaker bool  // If true, skip this outpost
}

func (m *OutpostManager) updateHealth(name string, err error) {
    // Track failures, open circuit breaker after N failures
    // Auto-reset after successful ping
}
```

## Config

```yaml
policy:
  retry:
    max_attempts: 3
    initial_backoff: 1s
    max_backoff: 30s

  circuit_breaker:
    failure_threshold: 5
    reset_timeout: 1m
```

## Files

- `internal/outpost/retry.go`
- `internal/outpost/health.go`

## Dependencies

Depends on: gt-9a2.3 (OutpostManager)
Can be done after basic outpost implementations work.


Digest: mol-deacon-patrol

Patrol 2: all healthy, no callbacks


Digest: mol-deacon-patrol

Patrol: slit handoff (landing protocol), polecats cleaned (0 now), 4 witness/refineries up


Digest: mol-deacon-patrol

Patrol cycle 1: All healthy - witnesses, refineries running. No callbacks, no orphans, no pending spawns.


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


Digest: mol-deacon-patrol

Patrol 7: all clear


P0: Multiple agents can claim same worker identity

Multiple Claude Code sessions running simultaneously all think they are 'dave' on beads/crew/dave. No detection or prevention of identity collision. This breaks the single-agent-per-worker assumption.

## Fix Implemented

1. **Lock Package** (`internal/lock/lock.go`):
   - PID-based lockfile at `<worker>/.gastown/agent.lock`
   - Contains PID, timestamp, session ID, hostname
   - Stale lock detection (checks if owning PID is dead)

2. **Prevention in gt prime**:
   - Workers (crew/polecat) acquire identity lock before loading context
   - If another live process holds the lock, prime fails with clear error
   - Shows lock holder details and resolution steps

3. **Detection with gt agents**:
   - `gt agents check` - scans for collisions and stale locks
   - `gt agents fix` - cleans stale locks
   - JSON output available for patrol tooling

4. **Correction in gt doctor**:
   - New `identity-collision` check
   - `gt doctor --fix` cleans stale locks


Digest: mol-deacon-patrol

Patrol 19: all quiet


CloudRun protocol types: WorkRequest, WorkEvent, WorkResult

## Overview

HTTP protocol types for Cloud Run work dispatch. **Types only** - server and client are separate tasks.

## Request Type

```go
type WorkRequest struct {
    IssueID string          `json:"issue_id"`
    Rig     RigConfig       `json:"rig"`
    Beads   BeadsConfig     `json:"beads"`
    Branch  string          `json:"worker_branch"`
    Context map[string]any  `json:"context,omitempty"`
}

type RigConfig struct {
    URL    string `json:"url"`
    Branch string `json:"branch"`
}

type BeadsConfig struct {
    URL    string `json:"url"`
    Branch string `json:"branch"`
}
```

## Response Types (streaming NDJSON)

```go
type WorkEvent struct {
    Type     string `json:"type"`  // status, log, progress, result, error
    Status   string `json:"status,omitempty"`
    Line     string `json:"line,omitempty"`
    Percent  int    `json:"percent,omitempty"`
    Branch   string `json:"branch,omitempty"`
    PRURL    string `json:"pr_url,omitempty"`
    Code     string `json:"code,omitempty"`
    Message  string `json:"message,omitempty"`
}

type WorkResult struct {
    Status  string `json:"status"`  // done, failed
    Branch  string `json:"branch"`
    PRURL   string `json:"pr_url,omitempty"`
    Error   string `json:"error,omitempty"`
}
```

## Files

- `internal/cloudrun/protocol.go` - All types above

## Notes

This is just types. Server (gt-9a2.7b) and client (gt-9a2.7c) are separate tasks.
Small, focused task - can complete quickly.


Merge: gt-g44u.1

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


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.


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

Patrol 9: quiet


Refinery: event-driven merge queue

## Problem
Refinery only acts on explicit requests, no queue management.

## ZFC-Compliant Solution
Add steps to `mol-refinery-patrol.formula.toml`:

```toml
[[step]]
id = "check-merge-requests"
title = "Process merge queue from inbox"
description = """
1. Check inbox for MERGE_READY messages: gt mail inbox
2. For each merge request:
   - Read branch name from message
   - Add to local queue (track in state.json or temp file)
3. Process queue in order:
   - git fetch origin
   - git checkout main && git pull
   - git merge --no-ff origin/polecat/<name>
   - If conflict: mail Mayor with details, skip
   - If success: git push && delete remote branch
4. For each merged branch:
   - gt mail send <rig>/witness -s "MERGED: polecat/<name>" -m "Branch merged successfully"
   - Delete the MERGE_READY mail
"""
depends_on = ["inbox-check"]
```

## Queue State
Refinery agent tracks queue in `state.json`:
```json
{
  "merge_queue": ["polecat/foo", "polecat/bar"],
  "last_merged": "polecat/baz"
}
```

## Why This Works
- Refinery Claude agent runs formula
- Agent handles conflicts with judgment
- Mail is coordination primitive
- State persists in agent's state.json

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


Cross-project dependency workflow (Gas Town side)

Gas Town integration for cross-project dependencies.

## Components
- gt-zniu: gt park command (park molecule on external dep)
- gt-in3x: gt spawn --continue (resume parked molecule)
- gt-5uf3: Patrol auto-resume (future)

## Design Doc
See: docs/cross-project-deps.md

## Depends on Beads
- bd-h807: Cross-project dependency support (epic)

## Launch Plan
Phase 1 (launch): gt park + gt spawn --continue (manual resume)
Phase 2 (later): Patrol auto-resume


Test7

Test with fixed workdir


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.


Polecat name pooling: Bounded reusable names

Polecats reuse names from a bounded pool (50) with overflow to sequence numbers.

## Naming Scheme
- Pool: polecat-01 through polecat-50 (prefer low numbers)
- Overflow: <rigname>-<sequenceNumber> (e.g., beads-51, gastown-52)

## Design
- Witness tracks which pool names are in use
- On spawn: pick first available from pool
- If pool exhausted: use rigname-N format
- On completion: pool name returns, sequence numbers don't

## Why?
- User experience: tmux sessions survive polecat restarts
- Users stay attached, see new polecat start (like mayor respawn loop)
- Bounded resource usage for common case
- Scales beyond 50 when needed

## Implementation
- Witness maintains name allocation in beads or local state
- Tmux session runs respawn loop (like mayor)
- Name released on graceful exit or when witness detects dead session


Digest: mol-deacon-patrol

Patrol complete: no work, gastown refinery came online, town healthy


CLI: rig commands (add, list, show, remove)

GGT needs rig management commands.

Missing Commands:
- gt rig add <git-url> [--name NAME] - Add rig to workspace
- gt rig list - List all rigs with status
- gt rig show <rig> - Show detailed rig info
- gt rig remove <rig> - Remove a rig

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

Current State:
- gt init creates rig structure but no ongoing management
- Rig discovery in internal/rig/manager.go but not exposed via CLI


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


Merge: dementus-1767073385126

attached_args: Code review this merge request

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


Digest: mol-deacon-patrol

Patrol complete: no callbacks, all agents healthy, no orphans, doctor 24/28


Wisp burn after pickup

Implement wisp cleanup after work is picked up.

## When to Burn

After agent has picked up slung work:
- Agent claims the issue (bd update --status=in_progress)
- Or agent explicitly acknowledges the handoff
- Wisp file is deleted

## Implementation Options

Option A: Agent burns manually
```bash
gt hook burn  # Deletes current hook wisp
```

Option B: Auto-burn on claim
- bd update detects theres a hook for this issue
- Burns the wisp automatically

Option C: Burn in prime (after display)
- Once shown to agent, delete the wisp
- Simple but might lose info if agent crashes before reading

## Recommendation

Use Option A (manual burn) for v0.1:
- Explicit is better than implicit
- Agent controls when theyre done with handoff
- Add auto-burn in v0.2

## Command

```bash
gt hook burn
# Deletes .beads-wisp/hook-<agent>.wisp.yaml
# Confirms: "Hook cleared"
```


Plugin: work-oracle (pre-dispatch analysis)

Plugin for pre-dispatch task analysis and decomposition.

## Purpose

Before dispatching workers for an epic, ask work-oracle to:
- Validate task breakdown
- Identify parallelization opportunities
- Predict conflicts between tasks
- Suggest worker count

## Hook Point

Mayor consults work-oracle before spawning workers.

## Interface

Input (via mail):
- Epic ID with proposed child issues
- Rig context (current state of codebase)

Output (via mail + bead):
- Recommended task groupings
- Dependency suggestions
- Risk assessment (which files might conflict)
- Optimal worker count

## Structure

```
<rig>/plugins/work-oracle/
├── CLAUDE.md       # Analysis prompts
├── mail/inbox.jsonl
└── state.json
```

Uses existing plugin-as-agent architecture.

## Note

Was "swarm-oracle" - renamed because there are no swarm IDs. This plugin helps plan batch work, not manage swarm entities.


Timer gate evaluation

Implement timer gate type. Format: timer:<duration> (5m, 1h, 24h). Deacon evaluates pending timer gates each patrol cycle. Gate closes when duration elapsed since creation. Simplest gate type - implement first.


Merge: gt-72so

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


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

Patrol 3: All healthy


Digest: mol-deacon-patrol

Patrol 14: Routine


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

Patrol 2: All healthy, no actions


Merge: gt-h6eq.1

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


Test patrol queue feature


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.


gt polecat add should spawn with --dangerously-skip-permissions

When adding polecats via gt polecat add, the subsequent Claude session needs --dangerously-skip-permissions flag to run autonomously.

Currently:
- gt polecat add creates worktree
- tmux new-session spawns 'claude' without flags
- Polecat gets stuck at permission prompts

Needed:
- gt polecat add (or gt sling) should spawn with --dangerously-skip-permissions
- Or store a config in polecat worktree indicating autonomous mode

This is critical for swarm automation - polecats can't self-approve permissions.


Clean up duplicate patrol protos (gt-qflq, gt-iep9)

There are duplicate protos for patrol molecules:

## Duplicates

| Old Proto | New Proto | Name |
|-----------|-----------|------|
| gt-qflq | mol-witness-patrol | mol-witness-patrol |
| gt-iep9 | mol-deacon-patrol | mol-deacon-patrol |

The old gt-* prefix protos were created before the formula cooking system.
The new mol-* prefix protos were created by `bd cook`.

## Action

1. Close or delete the old gt-* protos
2. Update any references to use the new mol-* protos
3. Verify `bd mol list` shows clean output

## Root Cause

The bd cook command uses the formula name as the proto ID (mol-*),
not the project prefix (gt-*). This is probably correct behavior,
but means we have legacy protos to clean up.


Test infrastructure: fixtures, mocks, integration tests

Test infrastructure for Gas Town.

## Test Categories

### Unit Tests
- Config parsing
- Mail operations
- Merge queue operations
- Template rendering

### Integration Tests
- Git operations (uses real git in temp dirs)
- Full workflows (spawn → work → merge)
- CLI command tests

## Fixtures

```go
package testutil

// Creates temp town with minimal structure
func NewTestTown(t *testing.T) *TestTown

// Creates temp rig with git repo
func NewTestRig(t *testing.T, town *TestTown, name string) *TestRig

// Creates test polecat
func NewTestPolecat(t *testing.T, rig *TestRig, name string) *TestPolecat
```

## Mocks

```go
// Mock tmux for session tests (avoid actual tmux)
type MockTmux struct {
    Sessions map[string][]string  // captured send-keys
}

func (m *MockTmux) NewSession(name, dir string) error
func (m *MockTmux) SendKeys(session, keys string) error
func (m *MockTmux) CapturePane(session string, lines int) (string, error)
```

## Test Helpers

```go
// Run test in temp directory
func InTempDir(t *testing.T, fn func(dir string))

// Initialize git repo for testing
func InitGitRepo(t *testing.T, dir string)

// Assert file exists with content
func AssertFileContains(t *testing.T, path, substr string)

// Assert JSON file has field
func AssertJSONField(t *testing.T, path, field string, expected any)
```

## CI Integration

- go test ./... runs all tests
- Integration tests skip with -short flag
- Coverage report generation

## Test Data

Embedded test fixtures:
```go
//go:embed testdata/*
var testdataFS embed.FS
```


generate-summary

Summarize patrol cycle for digest.

Needs: save-state


Digest: mol-deacon-patrol

Patrol 5: Nominal


Add logging for silently ignored errors

attached_args: Add logging for silently ignored errors

Add logging for silently ignored errors.

## Problem
Many places use `_ =` pattern to ignore errors without any logging, making debugging difficult.

## Files to modify
- internal/cmd/start.go (lines 167, 322-323, 429, 747-753, 886-888)
- internal/cmd/polecat.go (search for `_ =`)
- internal/session/manager.go (lines 140-154)
- internal/refinery/manager.go (line 399)
- internal/keepalive/keepalive.go (document as intentional)

## Implementation options

### Option A: Add comments explaining safety
```go
_ = m.saveState(ref) // Non-fatal: state will be recreated on next run
```

### Option B: Log warnings (preferred for important operations)
```go
if err := m.saveState(ref); err != nil {
    log.Printf("warning: failed to save state: %v", err)
}
```

## Guidelines
- Use logging for: state saves, config writes, cleanup operations
- Use comments for: truly benign operations (e.g., closing already closed file)
- keepalive is intentionally best-effort - add package-level comment

## Acceptance criteria
- [ ] All `_ =` in start.go have comment OR logging
- [ ] All `_ =` in session/manager.go have comment OR logging
- [ ] refinery/manager.go:399 has logging
- [ ] keepalive package has doc comment explaining best-effort design
- [ ] grep '`_ =`' shows all have justification
- [ ] go build ./... passes


Swarm learning: Spawn should auto-notify polecats

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


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


Polecat template: move session close checklist into molecule steps

Template has prose checklists for 'Before Signaling Done' and 'SESSION CLOSE PROTOCOL'. These should be encoded as tail steps in the polecat molecule, not repeated as prose in CLAUDE.md. Reduces duplication and ensures the steps are actually followed.


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/


Swarm package: Swarm, SwarmTask, SwarmState types

Define core swarm types.

## Types (in `internal/swarm/`)

```go
// SwarmState represents swarm lifecycle
type SwarmState string
const (
    SwarmCreated   SwarmState = "created"
    SwarmActive    SwarmState = "active"
    SwarmMerging   SwarmState = "merging"
    SwarmLanded    SwarmState = "landed"
    SwarmFailed    SwarmState = "failed"
    SwarmCancelled SwarmState = "cancelled"
)

// Swarm references a beads epic that tracks swarm work
type Swarm struct {
    ID          string      // matches beads epic ID
    RigName     string
    EpicID      string      // beads epic tracking this swarm
    BaseCommit  string      // git SHA all workers branch from
    Integration string      // integration branch name
    State       SwarmState
    CreatedAt   time.Time
    Workers     []string    // polecat names assigned
}

// SwarmTask represents a single task in the swarm (maps to beads issue)
type SwarmTask struct {
    IssueID   string  // beads issue ID
    Assignee  string  // polecat name
    Branch    string  // worker branch name
    State     string  // mirrors beads status
}
```

## Note

Swarm state is primarily stored IN beads. These types are in-memory representations for the SwarmManager to work with. No separate manifest.json files.


Molecule CLI: bd molecule commands

Add molecule commands to bd:

## Commands

```bash
bd molecule list              # List molecules (type: molecule)
bd molecule show <id>         # Show molecule with parsed steps
bd molecule parse <id>        # Validate and show parsed structure
bd molecule instantiate <mol-id> --parent=<issue-id>  # Create steps
bd molecule instances <mol-id>  # Show all instantiations
```

## gt spawn integration

```bash
gt spawn --issue <id> --molecule <mol-id>
```

This should:
1. Call `bd molecule instantiate` (creates child beads atomically)
2. Spawn polecat on first ready step
3. Polecat grinds through via `bd ready`

## Output Examples

```
$ bd molecule show mol-abc

mol-abc: Engineer in a Box
Type: molecule

Steps (5):
  design      → (ready first)
  implement   → Needs: design
  review      → Needs: implement
  test        → Needs: implement
  submit      → Needs: review, test

Instances: 3
```

```
$ bd molecule instances mol-abc

Parent          Status    Created
gt-xyz          done      2025-12-15
gt-abc          active    2025-12-17 (3/5 complete)
gt-def          pending   2025-12-18
```


Digest: mol-deacon-patrol

Patrol 14: Quick cycle, all quiet


Digest: mol-deacon-patrol

Patrol 17: nominal


Digest: mol-deacon-patrol

Patrol 2: all healthy, beads swarm active


Deacon primes pending workers instead of spawn nudge

## Problem

When gt spawn starts a polecat, it tries to send a nudge message immediately:
1. Starts tmux session with Claude Code
2. Waits 3 seconds (hardcoded)
3. Sends nudge via NudgeSession

But Claude Code takes 10-20+ seconds to be ready. The message arrives before
the prompt is ready, so it appears as part of the prompt string instead of
being executed.

## Solution

Move priming responsibility to the Deacon patrol cycle:

1. gt spawn: Mark polecat as pending_prime=true in state, return immediately
2. Deacon patrol: New step prime-pending-workers that:
   - Scans all rigs for workers with pending_prime=true
   - Uses WaitForClaudeReady() to poll for "> " prompt (already exists in tmux.go)
   - Once ready, sends gt prime + assignment nudge
   - Clears pending_prime flag

## Implementation

### spawn.go changes
- Remove the 3-second sleep (line 368)
- Remove the NudgeSession call (lines 375-383)
- Instead: Update polecat state with pending_prime=true

### Polecat state addition
status: assigned, pending_prime: true, issue: gt-xxx

### Deacon patrol addition
Add step between inbox-check and plugin-run:
prime-pending-workers: Check all rigs for workers marked pending_prime.
For each pending worker: WaitForClaudeReady(60s), then gt prime + nudge

### Benefits
- No race condition - we poll until actually ready
- Deacon already does health checks - priming is natural fit
- Works for polecats, witnesses, refineries, crew (unified approach)
- Timeout handling built in

## Depends on
- Existing WaitForClaudeReady() in tmux.go (lines 384-403)


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


Fix TestHasPolecat test failure in internal/session

TestHasPolecat in internal/session/manager_test.go fails because it expects
specific polecats (Toast, Cheedo) to exist in the test environment.

Error:
```
manager_test.go:46: expected hasPolecat(Toast) = true
manager_test.go:49: expected hasPolecat(Cheedo) = true
```

Fix: Either create test fixtures or mock the filesystem check.


Digest: mol-deacon-patrol

Patrol 13: routine


Phase 1.4: Basic patrol runner in Deacon

Deacon CLAUDE.md and prime context for patrol execution.

## Deacon Context
- Update Deacon CLAUDE.md with patrol instructions
- On wake: check pinned bead for attachment
- If attached: resume molecule from current step
- Execute steps, close each when done
- On final step: burn molecule, go naked

## gt prime Enhancement
- Detect if agent has attached molecule
- Show molecule progress in prime output
- Include patrol-specific instructions

Depends: gt-rana.3


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 #18: Stable


Digest: mol-deacon-patrol

Patrol 6: All clear


CLI: mayor commands (start, attach, stop, status)

Mayor management CLI commands.

## Commands

### gt mayor start
```
gt mayor start [--continue] [--agent AGENT]
```
- --continue: Resume from previous session (check for handoff mail)
- --agent: claude (default) or other agent type

### gt mayor attach
```
gt mayor attach
```
Attach to running Mayor session.

### gt mayor stop
```
gt mayor stop [--grace-period N]
```
Stop Mayor session with optional grace period.

### gt mayor status
```
gt mayor status [--json]
```
Show Mayor running status.

## Session Management
Session name: `gt-mayor`
Working directory: `<town>/mayor/rig/` or `<town>/mayor/`

## Implementation
Similar to session commands but for special Mayor context.

```go
func getMayorPath(townRoot string) string {
    return filepath.Join(townRoot, "mayor")
}

func getMayorSessionName() string {
    return "gt-mayor"
}
```

## New File
internal/cmd/mayor.go

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

## Acceptance Criteria
- [ ] gt mayor start launches Mayor session
- [ ] gt mayor attach works
- [ ] gt mayor stop with grace period
- [ ] gt mayor status shows running state


Digest: mol-deacon-patrol

Patrol 2: routine, all healthy


HOP: Skill vectors on work items

Add skill embeddings to work items for capability-based matching. See ~/ai/stevey-gastown/hop/CONTEXT.md. Post-v0.1 work.


Merge: dementus-1767074970788

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


Bug: bd update --parent doesn't persist

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

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

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

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

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

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

Related: bd-cj2e (the original feature implementation)


gt-crew-gastown-max

gt-crew-gastown-max

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


Crew directory structure and config

Add crew/ directory support to rig structure. Include:
- crew/ as peer to polecats/, refinery/, witness/
- Crew worker subdirectories with full git clones
- Optional BEADS_DIR configuration for beads integration
- Crew state tracking (separate from polecat state)


gt sling to town roles should use Town beads

## Problem

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

## Solution

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

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

## Tested

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


Obsolete beads issues need cleanup

Found obsolete issues that should be closed or fixed:

Test artifacts to close:
- gt-nriy: Test: Alpha to Beta (message test)
- gt-gswn: Integration test (test artifact)
- gt-54kn: Test: New Router

Broken dependencies (reference non-existent gt-test123):
- gt-vhby: implement
- gt-tvos: load-context
- gt-lwuu.2: implement (template variable unresolved)

CloudRun references (non-existent /deploy/cloudrun/):
- gt-9a2.6, gt-9a2.7, gt-9a2.11

Review and close/update these before launch.


Digest: mol-deacon-patrol

Patrol 15: all clear


🤝 HANDOFF: Investigate beads-sync divergence (221 files)

## Context

gt doctor now detects orphaned code on beads-sync branch. Running it shows:

  beads-sync-orphans: 221 file(s) on beads-sync not in main

This is in the gastown repo (checking from crew/max).

## Background

- Earlier today we recovered orphaned mail migration code lost in merge 96c773f
- Recently simplified architecture: all workers use mayor/rig/.beads (redirects)
- This simplification may have caused beads-sync branch to diverge unexpectedly

## Investigation Steps

1. From crew/max run: git diff main..beads-sync --stat
2. Check if beads-sync should even exist anymore (was for multi-clone sync)
3. If obsolete with redirect architecture, consider deleting it
4. If it has legitimate changes, cherry-pick or merge to main

## Key Question

With all workers using mayor/rig/.beads via redirects, is beads-sync still needed?

## Commands

git log main..beads-sync --oneline | head -20
git diff main..beads-sync -- *.go | head -100


Update gt doctor to check new config locations

Doctor should validate new structure:
- Check mayor/config.json exists and is valid
- Check settings/ exists for each rig
- Warn if old .gastown/ files still exist
- Offer --fix to migrate old locations to new


save-state

Update handoff bead with new state.

Persist nudge counts and pending actions.

Needs: execute-actions


Deacon Role Definition

You are the Deacon - the town-level lifecycle manager.

You run patrol cycles checking the health of all agents across all rigs,
nudging stuck workers, and managing session lifecycles.

default_molecule: mol-deacon-patrol
capabilities:
  - health_monitoring
  - session_lifecycle
  - cross_rig_visibility
  - agent_nudging

## Core Responsibilities

1. Run patrol cycles across all rigs
2. Monitor agent health and progress
3. Nudge stuck agents
4. Manage session lifecycles (kill/restart)
5. Escalate systemic issues to Mayor

## Patrol Cycle

Each patrol:
1. Check all agent sessions (gt status)
2. Identify stuck or idle agents
3. Nudge or restart as needed
4. Log patrol results
5. Sleep until next cycle

## Propulsion Principle

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

Your work is the mol-deacon-patrol wisp. Execute patrol steps continuously.

## Commands

- gt status - Check all agents
- tmux kill-session -t <session> - Kill stuck session
- gt mail send <addr> - Communicate with agents
- gt mail send mayor/ - Escalate to Mayor

## Session Cycling

When context fills, hand off to fresh session:
gt handoff -s "Deacon patrol handoff" -m "Completed N cycles..."


Digest: mol-deacon-patrol

Patrol 9: clear


Execute registered plugins

Execute registered plugins.

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

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

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

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

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

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


gt sling support for deacon/dogs target

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


Merge: gt-pyqv

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


Run the test suite.

Run the test suite.

```bash
go test ./...
```

Track results: pass count, fail count, specific failures.

instantiated_from: mol-refinery-patrol
step: run-tests


Digest: mol-deacon-patrol

Patrol cycle 5: All healthy


Refinery Patrol

Merge queue processor patrol loop with verification gates.


Digest: mol-deacon-patrol

Patrol 7: quiet


Design: Mayor session cycling and handoff

Design for Mayor session cycling and structured handoff.

## Overview

Mayor coordinates across all rigs and runs for extended periods. Needs session cycling pattern with structured handoff notes.

## Key Elements

1. Session cycling recognition (when to cycle)
2. Handoff note format (structured state capture)
3. Handoff delivery (mail to self)
4. Fresh session startup (reading and resuming)

## Subtasks (implementation)

- gt-g2d: Mayor session cycling prompting
- gt-sye: Mayor startup protocol prompting
- gt-vci: Mayor handoff mail template
- gt-1le: town handoff command (optional, P2)

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


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

Patrol 8: All healthy


Polecat template: simplify completion section to just 'gt done'

Completion section lists multiple options (gt done, gt handoff, bd sync). Should just say 'gt done' with succinct guidance on when bd sync is needed. Remove the alternatives that cause confusion.


Merge: gt-ih0s

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


Digest: mol-deacon-patrol

Patrol 16: Quiet


Digest: mol-deacon-patrol

Patrol 6: Core healthy. Noted Mayor escalation (rig mismatch) - not Deacon work.


bd close --continue: auto-advance to next molecule step

Add --continue flag to bd close for seamless molecule step transitions.

## Usage

bd close <step-id> --continue [--no-auto]

## Behavior

1. Closes the specified step
2. Finds next ready step in same molecule (sibling/child)
3. By default, marks it in_progress (--no-auto to skip)
4. Outputs the transition

## Output

[checkmark] Closed gt-abc.3: Implement feature

Next ready in molecule:
  gt-abc.4: Write tests

[arrow] Marked in_progress (use --no-auto to skip)

## If no next step

[checkmark] Closed gt-abc.6: Exit decision

Molecule gt-abc complete! All steps closed.
Consider: bd mol squash gt-abc --summary '...'

## Key behaviors
- Detects parent molecule from closed step
- Finds next unblocked sibling
- Auto-claims by default (propulsion principle)
- Graceful handling when molecule is complete

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


Digest: mol-deacon-patrol

Patrol 14: Quiet


gt swarm is implemented but undocumented

internal/swarm/ package is fully implemented with:
- gt swarm create
- gt swarm status
- gt swarm list
- gt swarm land
- gt swarm cancel
- gt swarm start

But no documentation exists for it:
- Not in README
- Not in architecture.md (uses different terminology)
- Not in any role prompts

Either:
1. Document gt swarm as the canonical mechanism
2. Or deprecate it in favor of gt sling

Related to gt-6n13 (competing molecule mechanisms)


Digest: mol-deacon-patrol

Patrol 19


Digest: mol-deacon-patrol

Patrol #4: Stable


GGT vs PGT Gap Analysis Summary

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

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

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

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 9: routine


Ensure refinery is alive

Ensure the refinery is alive and processing merge requests.

**Redundant system**: This check runs in both gt spawn and Witness patrol
to ensure the merge queue processor stays operational.

```bash
# Check if refinery session is running
gt session status <rig>/refinery

# Check for merge requests in queue
bd list --type=merge-request --status=open
```

If merge requests are waiting AND refinery is not running:
```bash
gt session start <rig>/refinery
gt mail send <rig>/refinery -s "PATROL: Wake up" -m "Merge requests in queue. Please process."
```

If refinery is running but queue is non-empty for >30 min, send nudge.
This ensures polecats don't wait forever for their branches to merge.


Pillar 1: Agent Identity (agent-as-bead)

Agents become beads with deterministic IDs, hook slots, role beads, and self-reported state.

Key deliverables:
- Agent bead schema (type=agent, hook_bead, role_bead, state fields)
- bd slot set/clear commands
- Agent beads for mayor, witness, refinery
- Role beads from current CLAUDE.md content
- Hook lookup via agent bead

Reference: ~/gt/docs/agent-as-bead.md

Parent: gt-oki8p


Digest: mol-deacon-patrol

Patrol cycle 4: routine, healthy


Digest: mol-deacon-patrol

Patrol 15: clear


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:
- {{issue}} - The source issue ID being worked on


mol-deacon-patrol minimal formula

Create minimal mol-deacon-patrol formula for tracer bullet.

## Simplified Patrol (v0.1)

```yaml
formula: mol-deacon-patrol-v01
description: Minimal Deacon patrol for tracer bullet.
version: 1

steps:
  - id: inbox-check
    title: Check inbox
    description: |
      Run: gt mail inbox
      Handle any messages.
      Note any spawn requests or status updates.

  - id: spawn-work
    title: Spawn polecat for ready work
    needs: [inbox-check]
    description: |
      Run: bd ready
      If work available and no polecat already working it:
        gt spawn --issue <first-ready> --rig gastown
      If no work: proceed to next step.

  - id: self-inspect
    title: Assess context and decide loop vs cycle
    needs: [spawn-work]
    description: |
      Evaluate whether to loop (reset) or cycle (handoff).

      ## Heuristics

      **Favor LOOP (bd mol reset):**
      - Context < 60%
      - No errors in this cycle
      - Patrol completed normally

      **Favor CYCLE (gt handoff):**
      - Context > 80%
      - Errors encountered
      - Been running > 1 hour without cycle
      - Model suggests refresh would help

      **Action:**
      - If LOOP: bd mol reset mol-deacon-patrol
      - If CYCLE: gt handoff -s "Deacon cycling" -m "Context: X%, cycles: N"
```

## Loop via Reset

After self-inspect, if staying:
```bash
bd mol reset mol-deacon-patrol
```

This resets all steps to pending, current_step to inbox-check.
Deacon immediately continues with fresh patrol cycle.

## Cycle via Handoff

If context high or refresh needed:
```bash
gt handoff -s "Deacon cycling" -m "Context high, cycling for fresh start"
```

Daemon respawns fresh Deacon with mol-deacon-patrol on hook.

## Location

.beads/formulas/mol-deacon-patrol-v01.formula.yaml


Digest: mol-deacon-patrol

Patrol 13: quiet


Digest: mol-deacon-patrol

Patrol 16: All agents healthy


Patrol 10: All healthy


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.


Remove dead code: splitLines first loop, unused methods.

## Dead code locations

### 1. internal/cmd/polecat.go (lines 1093-1106)
splitLines function has unreachable first loop:
```go
func splitLines(s string) []string {
    var lines []string
    for _, line := range filepath.SplitList(s) {  // DEAD: wrong function
        ...
    }
    lines = nil  // Discards above
    for _, line := range strings.Split(s, "\n") {  // This is what runs
        lines = append(lines, line)
    }
    return lines
}
```
**Fix:** Remove first loop and reset.

### 2. internal/townlog/logger.go (lines 277-279)
```go
func (e Event) JSON() ([]byte, error) {
    return json.Marshal(e)
}
```
**Fix:** Remove unused method.

### 3. internal/lock/lock.go (line 27)
```go
ErrStaleLock = errors.New("stale lock detected")
```
**Fix:** Remove unused error variable.

### 4. internal/witness/types.go (line 19)
```go
StatePaused State = "paused"
```
**Fix:** Remove unused state constant.

### 5. internal/refinery/manager.go (lines 527-538)
```go
func (m *Manager) getTestCommand() string { ... }
```
**Fix:** Remove unused method.

## Acceptance criteria
- [ ] polecat.go: Dead loop removed from splitLines
- [ ] townlog/logger.go: Event.JSON() method removed
- [ ] lock/lock.go: ErrStaleLock removed
- [ ] witness/types.go: StatePaused removed
- [ ] refinery/manager.go: getTestCommand removed
- [ ] go build ./... passes
- [ ] go test ./... passes


Merge: gt-3x1.5

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


Swarm: E2E Swarm Test Epic

Swarm molecule orchestrating epic gt-1tpts.

Epic: gt-1tpts
Coordinator:


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


Remove markdown molecule heresy from ~/gt/molecules/

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


Implement gt mol attach-from-mail command

Allow agents to self-pin work from mail.

Usage: gt mol attach-from-mail <mail-id>

Behavior:
1. Read mail body for attached_molecule field
2. Attach molecule to agent's hook
3. Mark mail as read
4. Return control for execution

Handles case where work was sent via mail but not slung to hook.


Digest: mol-deacon-patrol

Patrol cycle 18: nominal


Digest: mol-deacon-patrol

Patrol 9: OK


Phase 1: Core messaging primitives (@group, list:, queue:)

## Scope

Core messaging infrastructure that enables multi-recipient and work-queue patterns.

### Deliverables

1. **Config directory structure** - ~/gt/config/ with lists.json, queues.json
2. **@group dynamic resolution** - Filesystem scan for agent directories
   - @rig/gastown → scan gastown/ for agents
   - @town → scan all rigs
   - @witnesses, @crew/gastown, etc.
3. **list:name static lookup** - Fan-out to N copies (each recipient gets work item)
4. **queue:name with claim** - Shared storage, atomic claim via bd update --claim
5. **Fan-out at send time** - gt mail send handles expansion

### Key semantics
- @group and list: both fan out (N copies, N obligations)
- queue: shared copy, first-to-claim wins
- All resolve at send time, not receive time


Digest: mol-deacon-patrol

Patrol 10: All agents healthy


check-refinery

Ensure the refinery is alive and processing merge requests.

Needs: inbox-check


Handle callbacks from agents

Handle callbacks from agents.

Check the Mayor's inbox for messages from:
- Witnesses reporting polecat status
- Refineries reporting merge results
- Polecats requesting help or escalation
- External triggers (webhooks, timers)

```bash
gt mail inbox
# For each message:
gt mail read <id>
# Handle based on message type
```

Callbacks may spawn new polecats, update issue state, or trigger other actions.


Digest: mol-deacon-patrol

Patrol 3: routine checks, all healthy


Molecule composition: Includes directive

Add support for molecule composition via the Includes directive.

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

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

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

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

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


mol-deacon-patrol

Deacon patrol molecule template. Label: template


/restart: Personal slash command for in-place agent restart

Create ~/.claude/commands/restart.md that restarts current Gas Town agent in place.

## Detection
- Read tmux session name: gt-mayor, gt-witness-*, gt-refinery-*, gt-polecat-*
- Fallback: check GT_ROLE env var

## Behavior by role
- mayor: gt mayor restart (sends Ctrl-C, loop respawns)
- witness: gt witness restart
- refinery: gt refinery restart
- polecat: gt polecat restart (or witness-mediated)

## Command format
Uses backticks for inline bash to detect context, then instructs Claude to run appropriate restart.


Digest: mol-deacon-patrol

Patrol #2: Routine - all 6 agents healthy


request-shutdown

Send shutdown request to Witness.
Wait for termination.

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

Depends: generate-summary


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 2: all agents healthy, no issues


Verify architecture.md shows correct harness and rig structure

Review architecture.md diagrams:
- Verify town-level structure shows harness correctly
- Confirm rig-level mayor/rig/ is shown (it appears to be there at line 197)
- Check mermaid diagrams match ASCII diagrams
- Update if any inconsistencies found
- Cross-reference with new harness.md docs


Digest: mol-deacon-patrol

Patrol 2: all healthy, no callbacks, no gates, no orphans, no plugins


Digest: mol-deacon-patrol

Patrol 18: quiet


Digest: mol-deacon-patrol

Patrol #14: Stable


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


Digest: mol-deacon-patrol

Patrol 1: inbox clear, gates clear, no pending spawns, witnesses/refineries healthy, no orphans


Digest: mol-deacon-patrol

Patrol 19: All clear


Digest: mol-deacon-patrol

Patrol 6: Quick cycle, all quiet


Digest: mol-deacon-patrol

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


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)


Day 2.7b: gt rig add creates Witness and Refinery agent beads

Update gt rig add to create agent beads:

1. After initializing rig beads, create:
   - gt-witness-<rig> agent bead (role_type: witness, rig: <rig>, agent_state: idle)
   - gt-refinery-<rig> agent bead (role_type: refinery, rig: <rig>, agent_state: idle)

2. Use bd create --type=agent or internal beads API

Files:
- internal/cmd/rig.go
- internal/rig/manager.go


gt-gastown-polecat-ace

gt-gastown-polecat-ace

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


Merge: gt-caih

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


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


survey-workers

List polecats and categorize by status.

```bash
gt polecat list <rig>
```

Needs: load-state


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


Merge: gt-rixa

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


Digest: mol-deacon-patrol

Patrol 20: all healthy, doctor pass, handoff cycle


Human escalation path design

Flesh out the human escalation mechanism for Gas Town errors. Currently: errors mail Mayor, Mayor startup prompts about escalations. Need: structured escalation channel, escalation severity levels, and clear documentation. All edge cases in molecular algebra can write warnings to this channel.


Daemon heartbeat: worker and queue monitoring

Daemon periodic checks for work progress.

## Heartbeat Actions

Every N seconds (configurable):

1. Check Witness health per rig
   - Poke if witness needs to nudge workers

2. Check Refinery queue per rig
   - Poke if pending work in queue

3. Check work completion
   - If epic children all closed, notify Mayor

## Eventually Convergent

Multiple signals reinforce state:
- Polecat signals done → Witness notices → pokes Refinery
- Daemon heartbeat → checks queue → pokes Refinery
- Beads status → queryable by any agent

Even if one signal missed, system converges.

## Interface

```go
func (d *Daemon) HeartbeatLoop() {
    for {
        for _, rig := range d.rigs {
            d.CheckWitness(rig)
            d.CheckRefinery(rig)
            d.CheckWorkProgress(rig)
        }
        time.Sleep(d.config.HeartbeatInterval)
    }
}
```

## Notifications

Use mail with low priority for heartbeat pokes.
Agents can ignore if already processing.


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{},
}
```


Swarm learning: Beads database locality gap

## PGT Bug (GGT Design Already Correct)

Mayor created issues in `~/ai/mayor/rigs/beads/.beads/` but polecats use `~/ai/beads/.beads/`. Different databases = polecats can't see Mayor's beads.

**GGT Fix**: architecture.md already specifies:
- All agents use BEADS_DIR pointing to rig-level `.beads/`
- Lines 116-143: Beads Configuration for Multi-Agent
- Lines 573-586: Rig-Level Beads via BEADS_DIR

This is a PGT implementation gap, not a design issue. GGT spawn must set BEADS_DIR correctly.


Build activity stream component

Bubbletea component showing scrollable activity stream for a selected worker. Shows mutation events with timestamps, symbols, and issue titles. Displayed when user expands/selects a worker.


Port Gas Town to Go

Complete rewrite of Gas Town in Go for improved performance and single-binary distribution.

## Goals
- Single installable binary (gt)
- All Python functionality ported
- Federation support built-in
- Improved performance

## Phases
1. Core infrastructure (config, workspace, git wrapper)
2. Rig & polecat management
3. Session & tmux operations
4. Mail system
5. CLI commands
6. TUI (optional)


Refinery-Witness communication protocol

Define mail protocol between Refinery and Witness:

FROM Witness → Refinery:
- 'Polecat ready': polecat X completed work, ready for merge
- 'Rework complete': polecat Y finished requested rework

FROM Refinery → Witness:
- 'Merge success': polecat X merged, can be cleaned up
- 'Merge failed': polecat X needs rework on <reason>
- 'Rework request': please have a polecat rebase X on current main

Implement as structured mail with parseable format.


Digest: mol-deacon-patrol

Patrol 19: OK


Digest: mol-deacon-patrol

Patrol 21: all healthy, no lifecycle requests, 4 in-progress issues noted, handoff triggered at threshold


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.


Fanout gate: waits-for children aggregation

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

## Current State

Formulas already use this pattern informally:

    step: aggregate
    waits_for: "all-children"

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

## Proposed Syntax

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

Or simpler:

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

## Semantics

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

## Use Cases

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

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

## Relationship to gate

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

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

But dedicated syntax is cleaner for this common pattern.

## Dependencies

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


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


Work on ga-ct2: Add MR workflow to polecat completion. Wh...

Work on ga-ct2: Add MR workflow to polecat completion. When polecat completes work, auto-create MR to integration branch. When done, submit MR (not PR) to integration branch for Refinery.


bd mol update: Check and update installed formulas

Check for and apply updates to installed formulas.

Usage: bd mol update [formula-name]

Should:
- Check GitHub for newer versions/commits
- Show available updates
- Apply updates when requested
- Track update history


Digest: mol-deacon-patrol

Patrol #21: All agents healthy, no lifecycle requests, no orphans


Digest: mol-deacon-patrol

Patrol 5: All healthy, no messages