Extract duplicate boilerplate in molecule_lifecycle.go.

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

## Current duplication
runMoleculeBurn (lines 17-53) and runMoleculeSquash (lines 121-165) share ~50 lines of identical target detection code:
- Get cwd
- Find workspace
- Parse target argument
- Determine rig from address or role

## Implementation
Extract helper function:
```go
type AgentContext struct {
    TownRoot string
    RigName  string
    Role     string
    Target   string  // polecat name or crew name
}

func getTargetAgentContext(args []string) (*AgentContext, error) {
    cwd, err := os.Getwd()
    if err != nil {
        return nil, fmt.Errorf("getting current directory: %w", err)
    }
    townRoot, err := workspace.FindFromCwd()
    if err != nil {
        return nil, fmt.Errorf("finding workspace: %w", err)
    }
    if townRoot == "" {
        return nil, fmt.Errorf("not in a Gas Town workspace")
    }
    // ... rest of target detection logic ...
}
```

## Acceptance criteria
- [ ] AgentContext struct defined
- [ ] getTargetAgentContext function extracts shared logic
- [ ] runMoleculeBurn uses getTargetAgentContext
- [ ] runMoleculeSquash uses getTargetAgentContext
- [ ] Duplicate code eliminated (diff shows net negative lines)
- [ ] go build ./... passes
- [ ] go test ./internal/cmd/... passes


gt-deacon

Deacon (daemon beacon) - receives mechanical heartbeats, runs town plugins and monitoring.

role_type: deacon
rig: null
agent_state: idle
hook_bead: null
role_bead: gt-deacon-role


P4: Graceful degradation without tmux

Low-priority: Add graceful degradation for environments without tmux.

Current state: Patrol formulas use tmux capture-pane for observing agent state.

tmux-independent alternatives (degraded mode):
1. Tail Claude Code session logs if available
2. Rely solely on agent bead state (less real-time)
3. Use claude --output-file and tail that
4. IPC mechanisms (named pipes, sockets)

NOT launch-blocking. By summer 2025, Claude Code is expected to add signaling and reflection features that will obviate tmux scraping.

For now:
- Keep tmux as primary observation mechanism
- Document the tmux dependency
- Add --no-tmux flag that falls back to bead-only observation

This is P4 backlog - address after liftoff.


Add gate checking to Deacon patrol loop

Integrate gate checking into Deacon's patrol cycle.

## Patrol Integration
```go
func (d *Deacon) checkGates(ctx context.Context) {
    gates, _ := d.store.ListOpenGates(ctx)

    for _, gate := range gates {
        // Check timeout
        if time.Since(gate.CreatedAt) > gate.Timeout {
            d.notifyWaiters(gate, "timeout")
            d.closeGate(gate, "timed out")
            continue
        }

        // Check condition
        if d.checkCondition(gate.AwaitType, gate.AwaitID) {
            d.notifyWaiters(gate, "cleared")
            d.closeGate(gate, "condition met")
        }
    }
}
```

## Moved from beads
Originally bd-is6m. Deacon is in gastown, not beads.


gt handoff deadlock at handoff.go:125

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


Digest: mol-deacon-patrol

Patrol 17: quiet


Digest: mol-deacon-patrol

Patrol 3: all clear


Digest: mol-deacon-patrol

Patrol cycle 2: all quiet, agents healthy


Refactor runStartCrew into focused helper functions

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

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

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

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

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


Review: Deacon town-level wisp storage (gt-ci84)

Polecat slit implemented town-level wisp storage for deacon patrol. Review the changes to ensure deacon wisps are correctly stored in ~/gt/.beads-wisp/ instead of gastown rig beads.


Digest: mol-deacon-patrol

Patrol 11


Update swarm terminology to streams

Gas Town moved from batch swarms to continuous streaming of polecats. Update:
- Docs and designs
- Code comments
- CLI flags (remove --swarm if present, or reframe)
- Variable/function names where appropriate

Streams reflect the reality: polecats flow continuously, not in discrete batches.


Digest: mol-deacon-patrol

Cycle 11: quiet


gt sling: Make target path parsing more forgiving

Current parsing rejects valid-looking paths like 'gastown/nux' or 'gastown/polecats/nux'.

Should accept:
- gastown/nux (polecat)
- gastown/Nux (polecat, case-insensitive)
- gastown/polecats/nux (explicit)
- gastown/crew/max (crew)
- gastown (auto-spawn)

The help says 'gastown/Toast' works but it doesn't. Parser is too strict.

Fixed path parsing to accept polecat shorthand


Remove redundant gt prime SendKeys from crew commands

The crew commands (gt crew at, gt crew restart) still send 'gt prime' via SendKeys after session creation:
- internal/cmd/crew_at.go:97, 121
- internal/cmd/crew_lifecycle.go:202

This is now redundant because SessionStart hook handles priming automatically.

**Low priority** - these aren't causing bugs (just duplicate work), unlike the spawn race condition that was fixed. Can be cleaned up when convenient.


Digest: mol-deacon-patrol

Patrol 13: All healthy.


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


CLI: names commands for polecat naming pool

Polecat naming pool for auto-generated names.

## Commands

### gt names generate
```
gt names generate [--count N]
```
Generate N names from pool.

### gt names add
```
gt names add <name>...
```
Add custom names to pool.

### gt names list
```
gt names list
```
Show available and used names.

### gt names reset
```
gt names reset [--keep-used]
```
Reset pool to defaults.

## Config File
<rig>/town/naming.json:
```json
{
  "enabled": true,
  "auto_refill": true,
  "refill_threshold": 5,
  "pool": {
    "available": ["Toast", "Nux", "Capable", ...],
    "used": ["Alice", "Bob"]
  }
}
```

## Default Pool
Mad Max themed: Toast, Nux, Capable, Furiosa, Immortan, etc.

## Integration
gt polecat add calls naming pool if no name given:
```go
if name == "" {
    name, err = naming.Generate(rigPath)
}
```

## New Package
internal/naming/
├── pool.go     # Pool management
└── defaults.go # Default name lists

## Acceptance Criteria
- [ ] Auto-generate names on polecat add
- [ ] Track used vs available
- [ ] Auto-refill when low
- [ ] Custom names addable


Digest: mol-deacon-patrol

Cycle 5: 1 msg processed, all healthy


Digest: mol-deacon-patrol

Patrol 2: all agents healthy, no issues


Digest: mol-deacon-patrol

Patrol 15: Quiet


Digest: mol-deacon-patrol

Patrol cycle 7: All healthy


Digest: mol-deacon-patrol

Patrol 17: All clear


Digest: mol-deacon-patrol

Patrol 10: All clear


Pinned bead naming convention for all roles

Establish consistent naming convention for pinned beads across all roles.

## Problem

Every role needs a pinned bead for molecule attachment, handoff state, and identity.
We need a consistent naming convention so gt tools can find the pinned bead for any role.

## Proposed Convention

  {role}.pinned                    - Singleton roles (deacon, mayor)
  {rig}-{role}.pinned              - Per-rig roles (witness, refinery)
  {rig}-{name}.pinned              - Named agents (polecats, crew)

Examples:
- deacon.pinned
- mayor.pinned
- gastown-witness.pinned
- gastown-refinery.pinned
- gastown-furiosa.pinned (polecat)
- gastown-max.pinned (crew)

## Storage Location

| Role | Pinned Bead Location |
|------|---------------------|
| Mayor | ~/gt/.beads/ (town level) |
| Deacon | ~/gt/.beads/ (town level) |
| Witness | {rig}/.beads/ (rig level) |
| Refinery | {rig}/.beads/ (rig level) |
| Polecat | {rig}/polecats/{name}/.beads/ |
| Crew | {rig}/crew/{name}/.beads/ |

## Why This Matters

1. Name recycling: Polecats reuse names so their pinned beads persist
2. Molecule attachment: gt mol bond needs to find the pinned bead
3. gt prime: Can show you are working on X from pinned bead
4. Crash recovery: Resume from pinned beads attached molecule

## Tasks

- Document naming convention in architecture.md
- Update gt prime to find pinned bead by convention
- Update gt mol bond to use conventional name
- Create pinned beads on role startup if missing
- Add gt pinned command to show/manage pinned beads

## Related

- gt-rana.1: Attachment field on pinned beads (closed)
- gt-id36.2: Deacon marching orders (pinned bead)


Digest: mol-deacon-patrol

Patrol 17: all clear


Digest: mol-deacon-patrol

Patrol 16: All healthy.


Town & Rig Management: install, doctor, federation

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

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

## Architecture Reference

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


Extended addressing: Parse [machine:]rig/polecat

Extended addressing for cross-machine operations.

## Address Format

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

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

## Parser

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

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

## Validation

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

## Usage

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

## Backward Compatibility

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


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


Rig doctor checks: Refinery health, clones, gitignore

Rig-level doctor checks.

## Checks

### RigIsGitRepo
- Verify .git/ directory exists
- Verify git status works
- Fix: Cannot auto-fix

### GitExcludeConfigured
- Check .git/info/exclude contains Gas Town dirs
- Required entries: polecats/ witness/ refinery/ mayor/
- Fix: Append missing entries

### WitnessExists
- Verify witness/ directory exists
- Verify witness/rig/ is a git clone
- Verify witness/mail/inbox.jsonl exists
- Fix: Create structure, clone repo

### RefineryExists
- Verify refinery/ directory exists
- Verify refinery/rig/ is a git clone
- Verify refinery/mail/inbox.jsonl exists
- Fix: Create structure, clone repo

### MayorCloneExists
- Verify mayor/ directory exists
- Verify mayor/rig/ is a git clone
- Fix: Create structure, clone repo

### PolecatClonesValid
- For each polecat in polecats/:
  - Verify is a git clone
  - Verify on polecat branch
  - Warn if has uncommitted changes
- Fix: Cannot auto-fix (data loss risk)

### BeadsConfigValid (if applicable)
- If .beads/ exists, verify bd commands work
- Check beads sync status
- Warn if out of sync
- Fix: Run bd sync

## Implementation

```go
var RigChecks = []Check{
    &RigIsGitRepoCheck{},
    &GitExcludeConfiguredCheck{},
    &WitnessExistsCheck{},
    &RefineryExistsCheck{},
    &MayorCloneExistsCheck{},
    &PolecatClonesValidCheck{},
    &BeadsConfigValidCheck{},
}
```


Connection interface: Protocol for local/remote ops

Abstract interface for local vs remote (SSH) operations.

## Concept

Enable Gas Town to manage rigs on remote machines via SSH. The Connection interface abstracts whether operations happen locally or remotely.

## Interface

```go
type Connection interface {
    // Identification
    Name() string
    IsLocal() bool

    // File operations
    ReadFile(path string) ([]byte, error)
    WriteFile(path string, data []byte, perm os.FileMode) error
    MkdirAll(path string, perm os.FileMode) error
    Remove(path string) error
    Stat(path string) (os.FileInfo, error)
    Glob(pattern string) ([]string, error)

    // Command execution
    Exec(cmd string, args ...string) ([]byte, error)
    ExecDir(dir, cmd string, args ...string) ([]byte, error)

    // Tmux (for session management)
    TmuxNewSession(name, dir string) error
    TmuxKillSession(name string) error
    TmuxSendKeys(session, keys string) error
    TmuxCapturePane(session string, lines int) (string, error)
}
```

## Implementations

- LocalConnection (gt-f9x.8): Direct file/exec operations
- SSHConnection (future): Operations via SSH

## Usage

```go
func NewRigManager(conn Connection, ...) *RigManager

// Operations work the same regardless of connection type
rm.DiscoverRigs()  // uses conn.Glob, conn.ReadFile
```

## Design Notes

- Connection obtained from MachineRegistry (gt-f9x.9)
- Default is always local
- SSH connection requires machine config (host, key, user)


LocalConnection: Local file/exec/tmux operations

LocalConnection implementation for local machine operations.

## Implementation

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

func NewLocalConnection() *LocalConnection

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

## File Operations

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

## Command Execution

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

## Tmux

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

## Notes

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


Machine registry: Store and manage machine configs

Registry for managing remote machines in federation.

## Data Model

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

## Interface

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

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

## Storage

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

## Connection Factory

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


Digest: mol-deacon-patrol

Patrol 8: All healthy


Add tests for critical untested packages

Several packages have no tests:

High priority (critical functionality):
- internal/lock/ - file locking
- internal/mail/router.go, mailbox.go - agent communication
- internal/mrqueue/ - merge request queue
- internal/refinery/ (manager_test.go missing for complex merge logic)

Medium priority:
- internal/claude/ - settings management
- internal/wisp/ - ephemeral workflow
- internal/constants/ - constant values

Low priority:
- internal/style/ - terminal styling

The refinery package is especially concerning - it has complex merge, conflict, and retry logic with no tests.


os.Exit() calls in library code prevent proper error handling

internal/cmd/mail.go and other files have os.Exit() calls.

Issues:
- Hard to test
- Prevents graceful shutdown
- Can lose unsaved state
- Doesn't compose well in library code

Should return errors instead of calling os.Exit() directly.
Let main() decide how to exit.


Digest: mol-deacon-patrol

Patrol 3: All clear


Add gate timeout tracking and notification

Implement timeout and notification logic for gates.

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

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

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


Extract shared state load/save pattern to generic helper

Nearly identical loadState/saveState methods in:
- internal/witness/manager.go (lines 39-72)
- internal/refinery/manager.go (lines 63-96)
- internal/crew/manager.go (lines 257-306)

All use the same pattern: read JSON file, handle not-exists, unmarshal/marshal.

Suggestion: Create a generic stateStore[T any] helper in a shared package.

Decomposing - generic stateStore is complex, will create simpler focused tasks


Daemon patrol should clean stale locks

The gt daemon patrol cycle should detect and clean up stale locks.

## Problem
If daemon crashes or there are orphaned locks, they can block operations indefinitely.

## Proposed Solution
During patrol cycle:
1. Check for lock files older than threshold (e.g., 5 minutes)
2. Verify owning process is dead (check PID if stored)
3. Remove stale locks
4. Log cleanup for audit


Digest: mol-deacon-patrol

Patrol 5: clear


Standardize warning output to use style.Warning

Unify warning message formatting across codebase.

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

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

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

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


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


Digest: mol-deacon-patrol

Cycle 15: quiet, handoff for fresh context


Day 3.2: Witness reads agent beads for polecat state

Witness patrol reads polecat state from agent beads:
- bd list --type=agent --role_type=polecat
- For each, check state field
- If state=running, check progress
- If state=stuck, handle stuck protocol

No more PID/tmux inference.

Parent: gt-hwka3

Implemented: agents self-report state via beads, witness reads agent beads for polecat state


Digest: mol-deacon-patrol

Patrol 16: quiet


MQ conflict prediction: file hotspot tracking

Track which files change frequently to predict conflicts before spawn.

## Goal
Before spawning a polecat on an issue, warn if the target files are "hot" (recently changed by other MRs).

## Approach
1. Track file change frequency per MR (sliding window, e.g., last 24h)
2. At spawn time, analyze issue description / planned changes
3. If touching hot files, either:
   - Warn and proceed
   - Suggest waiting for queue to clear
   - Auto-assign to ownership zone (future)

## Implementation
- Add file_changes log to refinery state (or separate hotspot.json)
- After each merge, record changed files with timestamp
- gt spawn checks hotspots before assignment
- Optional: Witness patrol aggregates hotspot data

## Example
```
$ gt spawn --issue gt-xyz
Warning: This issue likely touches auth/login.go
  - Changed 3 times in last 2 hours
  - 2 MRs pending that also touch this file
Proceed anyway? [y/N]
```

## Parent
gt-lxxh2


Digest: mol-deacon-patrol

Patrol cycle 18: All healthy


Witness cleanup should know worktree parent repo

Witness had to discover that polecat worktrees are created from mayor/rig, not the rig root.

## Problem
When cleaning up polecat/tracer:
- Witness ran git worktree remove from gastown/ - failed
- Had to discover worktree was from mayor/rig
- Then removed correctly

## Improvement
Witness should know the worktree parent repo path (mayor/rig) and clean up from there directly.

## Evidence
Tracer bullet 2025-12-23: Witness took extra steps to figure out cleanup path.


Optimize: Share stepMap across control flow operators

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

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

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

This is inefficient for large formulas. Options:

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

Low priority since formula sizes are typically small.


Digest: mol-deacon-patrol

Patrol cycle 8: All healthy


Integration test: agent waits for CI via gate

End-to-end test of the gate workflow.

## Test Scenario
1. Agent creates gate: bd gate create --await gh:run:123 --timeout 5m --notify beads/dave
2. Agent writes handoff and exits
3. Deacon patrol checks gate condition
4. (Mock) GitHub run completes
5. Deacon notifies waiter and closes gate
6. New agent session reads mail and resumes

## Test Requirements
- Mock GitHub API responses
- Test timeout path
- Test multiple waiters
- Verify mail notifications sent

## Moved from beads
Originally bd-rl5t. Tests Deacon patrol which is in gastown.


Digest: mol-deacon-patrol

Patrol 16: All clear


Digest: mol-deacon-patrol

Patrol 5: All clear


Day 1.1b: Add type=role to bead schema

Add role as a valid bead type in the schema.

Role beads define agent behavior:
- Priming instructions (from current CLAUDE.md content)
- default_molecule field
- capabilities list

This enables Day 1.5 (creating role beads from CLAUDE.md).

Must be done alongside type=agent schema work.

Implemented: type=agent and type=role added to schema (927e8374)


Add keyboard navigation and filtering

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


bd ready --blockers: prioritize issues that block other work

bd ready should prioritize issues that are blocking other work.

**From VC**: Blocker-first prioritization in GetReadyWork(). ~100 lines.
Algorithm: baseline-failure first, then discovered:blocker, then by priority.

**Gas Town implementation**: CLI flag or default behavior:
```bash
bd ready --blockers-first  # Or make this default
```

Checks dependency graph. Issues with many dependents surface first.

**Value**: Unblocks parallelism faster. Critical path gets cleared.

**VC lesson**: Without blocker priority, work can starve on discovered issues.


Digest: mol-deacon-patrol

Patrol 18: all healthy, handled 1 handoff msg


Day 2.7a: gt install creates Deacon and Mayor agent beads

Update gt install to create agent beads:

1. After initializing town beads, create:
   - gt-deacon agent bead (role_type: deacon, rig: null, agent_state: idle)
   - gt-mayor agent bead (role_type: mayor, rig: null, agent_state: idle)

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

Files:
- internal/cmd/install.go

Moved to gt rig add instead of gt install due to routing constraints. Agent beads need to be in rig beads (not town beads) for daemon to find them via prefix routing. First rig added gets gt-deacon and gt-mayor.


Remove os.Exit calls from Cobra RunE handlers

Replace os.Exit() calls with proper error returns in Cobra commands.

## Files to modify
- internal/cmd/mail.go (lines 546, 554, 561, 808-812)
- Any other files with os.Exit in RunE

## Problem
Some RunE handlers call os.Exit() directly:
```go
RunE: func(cmd *cobra.Command, args []string) error {
    ...
    os.Exit(1)  // BAD: bypasses Cobra error handling
}
```

## Fix
Return errors instead:
```go
RunE: func(cmd *cobra.Command, args []string) error {
    ...
    return fmt.Errorf("not in workspace")
}
```

## Acceptance criteria
- [ ] No os.Exit calls inside RunE functions
- [ ] Errors returned with proper context
- [ ] grep -n 'os.Exit' internal/cmd/*.go shows no RunE matches
- [ ] go build ./... passes
- [ ] Commands still exit with correct codes


Digest: mol-deacon-patrol

Patrol 20: final check, all healthy, handoff threshold


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


Auto-rebase on conflict: optional automatic rebase

Implement automatic rebase as conflict resolution option.

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

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

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

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


Retry logic: exponential backoff for transient failures

Implement retry logic for transient failures.

Retryable failures:
- Push failed (network, auth transient)
- Test flaky (config: retry_flaky_tests)
- Fetch failed (network)

Retry strategy:
- Max retries: 3 (configurable)
- Backoff: 1s, 2s, 4s (exponential)
- On final failure: treat as permanent, assign back

Non-retryable failures:
- Merge conflict
- Test consistently fails
- Build error

Track retry count in MR metadata for debugging.

Reference: docs/merge-queue-design.md#failure-recovery


MQ metrics: pending count, processing time, success rate

Implement merge queue metrics collection.

Metrics to track:
- mq_pending_count: MRs waiting
- mq_processing_time: Time from submit to merge (histogram)
- mq_success_rate: Merges vs rejections
- mq_conflict_rate: How often conflicts occur
- mq_test_failure_rate: Test failures

Storage options:
- Simple: JSON file with rolling stats
- Advanced: Prometheus-compatible metrics endpoint

Display via:
- gt mq stats: summary statistics
- Dashboard widget (separate task)

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


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


Direct landing integration: gt land uses MR or bypasses

Integrate direct landing with merge queue.

'gt land --direct' should:
1. Check if MR exists for the polecat's work
2. If MR exists: process it directly (skip queue)
3. If no MR: create temporary MR, process, close

Options:
- --direct: bypass queue (existing)
- --via-queue: ensure goes through queue (new)
- --force: skip safety checks (existing)

This ensures direct landing still creates proper records.

Reference: docs/merge-queue-design.md#direct-landing-bypass-queue


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


Per-epic config overrides: custom merge settings

Allow per-epic merge configuration overrides.

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

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

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

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


Day 1.3: Implement bd slot set/clear commands

Add slot management commands:
- bd slot set <agent> <slot> <bead> - set slot (error if occupied)
- bd slot clear <agent> <slot> - clear slot
- bd slot show <agent> - show all slots

These enforce cardinality constraints.

Parent: gt-d0jqp

Implemented bd slot set/clear/show commands with cardinality enforcement. Migration 030_agent_fields adds columns.


Digest: mol-deacon-patrol

Patrol 17: Quiet


Track merge requests as beads (type=merge-request)

Currently MRs are tracked in .gastown/refinery.json. For HOP audit trail and entity CV tracking, merge requests should be Beads entries with type=merge-request. This enables:
- Full audit trail of what was merged, when, by whom
- Entity chain contributions (who validated what)
- Cross-rig visibility of merge activity

The refinery would create an MR bead when work enters the queue, update status as it progresses (open → in_progress → merged/rejected).

Part of Beads-as-data-plane vision from HOP.


Digest: mol-deacon-patrol

Patrol 12: quiet


Polecat writes files outside worktree (integration test)

During integration test gt-7psb8, polecat furiosa was asked to create ~/gt/gastown/test-polecat-integration.txt

It used Write tool with relative path '../../test-polecat-integration.txt' which resolved to gastown root instead of its worktree at ~/gt/gastown/polecats/furiosa/

Observed:
- File created at ~/gt/gastown/test-polecat-integration.txt (WRONG)
- Furiosa worktree at ~/gt/gastown/polecats/furiosa/ has no such file
- Furiosa branch has no commits of any new files

Root cause: Claude Code resolves relative paths from cwd, and the polecat may have been in the wrong directory or used incorrect path.

This caused the entire merge to be empty (branch at same commit as main).

Fixed: Added directory discipline section to polecat template


Remove pinned field workaround after beads fix

## Summary

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

## Current Workaround

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

## Changes After Beads Fix

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

## Blocked By

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

## Related

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

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


Day 1.7: Define per-rig agent bead ID pattern

Document and implement the agent bead ID pattern for per-rig agents.

Pattern: gt-<role>-<rig>
Examples:
- gt-witness-gastown
- gt-refinery-gastown
- gt-refinery-beads
- gt-polecat-gastown-nux

Town-level agents (no rig):
- gt-mayor
- gt-deacon

This pattern enables:
- Consistent lookup: bd show gt-witness-gastown
- Routing: routes.jsonl maps prefixes
- Filtering: bd list --type=agent --rig=gastown

Update:
- agent-as-bead.md with examples
- bd create --type=agent to validate pattern

Already done: commit 46cdf075 on beads/main


Digest: mol-deacon-patrol

Patrol 9: all clear


Test Polecat Arm

Test child for bonding pattern


Heartbeat convention: simple liveness signal for agents

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

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

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

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

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


Fix potential race conditions in state file operations

Non-atomic state file operations could cause data loss:

All packages with state files (witness, refinery, crew, swarm):
State is loaded, modified, and saved without locking:
  ref, err := m.loadState()
  // ... modify ref ...
  return m.saveState(ref)

If two processes modify state simultaneously, one update may be lost.

internal/polecat/namepool.go (lines 179-194):
Save() uses RLock() but performs file I/O - should use write lock.

internal/swarm/manager.go:
swarms map[string]*Swarm accessed without mutex but multiple goroutines could call Create/GetSwarm.

Suggestion:
1. Use file locking (flock) for critical state updates
2. Or use atomic write pattern (write to temp, then rename)
3. Add mutex to swarm.Manager.swarms map


Pillar 2: Patrol Ignition

Witness and Refinery patrol loops fire reliably.

Key deliverables:
- Witness patrol reads agent beads for polecat state
- Witness sends MERGE_READY to refinery on completion
- Refinery processes MERGE_READY mail
- Both send completion signals

Subsumes existing work:
- gt-6qyt1: Refinery event-driven merge queue
- gt-qpwv4: Witness detect completion
- gt-dtw9u: Witness active monitoring

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

Parent: gt-oki8p

All 5 children complete: patrol wiring, witness reads agent beads, MERGE_READY flow, refinery processes, integration test passed


Digest: mol-deacon-patrol

Patrol 12: All clear


Crew agent beads never created (dead code in reportAgentState)

## Problem

In prime.go, reportAgentState is called for ALL roles including crew workers.
The function calls bd.UpdateAgentState for crew with ID like gt-crew-gastown-max.

But crew agent beads are NEVER created:
- Polecats: Created on spawn (polecat/manager.go:167)
- Infrastructure: Created by doctor/install (doctor/agent_beads_check.go)
- Crew: Nothing creates them

The error is silently ignored, so this doesn't break anything, but it's dead code.

## Found in commit

f3a6ef6 (feat: Witness reads polecat state from agent beads)

## Decision

**Create crew agent beads** on first gt prime if they don't exist.

Crew workers are human-managed but their state is still valuable for:
- Town-wide status dashboards
- Understanding who's working on what
- Consistent agent bead model across all agent types

## Implementation

In prime.go reportAgentState(), before calling UpdateAgentState:
1. Try to get the agent bead
2. If it doesn't exist, create it with CreateAgentBead
3. Then update state as normal

Implemented: reportAgentState now creates agent beads on first prime for all roles including crew


Digest: mol-deacon-patrol

Patrol cycle 19: All healthy


Code Smell Cleanup: Gas Town Code Quality

Comprehensive code smell cleanup identified in code review session.

## Scope
Fixes for duplication, thread safety, missing tests, inconsistent patterns, and dead code.

## Child issues (18 total)

### Thread Safety & Data Integrity (P2)
- gt-wled7: Atomic write pattern for state files
- gt-xtsb5: Fix namepool Save() mutex
- gt-bp0ht: Add swarm.Manager mutex

### Duplication Removal (P2)
- gt-560ge: Extract processExists function
- gt-ddw3y: Extract molecule_lifecycle boilerplate
- gt-atqr8: Session name helper functions
- gt-ov2uv: AgentIdentity type for parsing

### Missing Tests (P2)
- gt-p2eg5: Lock package tests
- gt-0vu9e: mrqueue package tests
- gt-1pelm: mail router/mailbox tests

### Code Quality (P2)
- gt-gcnnr: Add logging for ignored errors

### Constants Extraction (P3)
- gt-4u682: SupportedShells constant
- gt-795e8: Timing constants

### Refactoring (P3)
- gt-tkbd5: Refactor runStart
- gt-dsqxw: Refactor runStartCrew

### Error Handling (P3)
- gt-h3skz: Remove os.Exit from RunE
- gt-g6kor: Standardize warning output

### Dead Code (P4)
- gt-2g130: Remove unused code

## Acceptance criteria
- [ ] All child issues closed
- [ ] go build ./... passes
- [ ] go test ./... passes
- [ ] go test -race ./... passes


Digest: mol-deacon-patrol

Patrol 6: Routine


Digest: mol-deacon-patrol

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


Config directory and schema for messaging

Create ~/gt/config/ directory structure for messaging configuration.

## Deliverables

1. Create ~/gt/config/ directory
2. Define JSON schema for lists.json:
   ```json
   {
     "oncall": ["mayor/", "gastown/witness"],
     "cleanup/gastown": ["gastown/witness", "deacon/"]
   }
   ```
3. Define JSON schema for queues.json (if separate from lists)
4. Add config loading utility in internal/config/

## Acceptance
- Config directory exists
- Schema documented
- Load/parse functions work

MessagingConfig types and load/save functions implemented. ~/gt/config/messaging.json created.


Digest: mol-deacon-patrol

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


Digest: mol-deacon-patrol

Patrol 11: all clear


Review: Beads Universal Data Plane documentation

Review new docs/beads-data-plane.md (275 lines) for accuracy.

## Commit
- 54c8269: Add Beads Universal Data Plane documentation

## Review focus
- Accuracy of beads architecture description
- Correct field mappings (mail → beads fields)
- Two-level architecture (town vs rig beads) correctly explained


Polecat didn't commit before gt done

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

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

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

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

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

Fixed: Updated completion protocol to emphasize commit before gt done


Digest: mol-deacon-patrol

P13: stable


Digest: mol-deacon-patrol

Patrol 5: Routine


Test Patrol Parent

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

Original description:
Test parent for Christmas Ornament pattern


Digest: mol-deacon-patrol

Patrol 8: All healthy


Formula SDK: Developer Tooling for Workflow Authors

SDK for creating, testing, debugging, and validating formulas.

## Components

### Forge (Creation)
```bash
mol forge new my-workflow        # Scaffold new formula
mol forge extend base-workflow   # Create variant
mol forge lint my-workflow       # Check syntax/style
```

### Lab (Testing)
```bash
mol lab run my-workflow --dry-run   # Mock execution
mol lab test my-workflow            # Run formula tests
mol lab trace my-workflow           # Step-through debug
```

### Validator (Verification)
```bash
mol validate my-workflow                    # Check well-formedness
mol validate my-workflow --protocol=Reviewable  # Check protocol compliance
mol validate my-workflow --schematic=shiny  # Check schematic compatibility
```

### Analyzer (Static Analysis)
```bash
mol analyze my-workflow --deps     # Dependency graph
mol analyze my-workflow --coverage # Step coverage
mol analyze my-workflow --cycles   # Detect cycles
```

## Relationship to Other Concepts

The SDK operates on:
- Formulas (source level)
- Protos (compiled level)
- Molecules (runtime level)

And validates against:
- Protocols (type system)
- Schematics (domain composition)

## Open Questions

1. Bundled vs separate - mol subcommands or formula-kit binary?
2. IDE integration - LSP for formula files?
3. Test harness - how to mock steps? fixtures?

## Related

- docs/formula_evolution.md - "SDK Question" section
- gt-8tmz.30 - Proto debugging tools (subset)
- gt-8tmz.31 - Formula validation (subset)


Day 1.1: Add type=agent to bead schema

Add agent as a valid bead type in the schema. This is the foundation for agent-as-bead.

Parent: gt-d0jqp

Re-opened: Close reason referenced non-existent commit 927e8374. Type=agent not yet implemented in bd CLI.

Already done: commit 927e8374 on beads/main


bd update --claim for work queue semantics

Atomic claim operation for work queue messages.

## Deliverables

1. Add --claim flag to bd update
2. Atomic operation:
   - Sets assignee to claimer
   - Sets status to in_progress
   - Fails if already claimed
3. Conflict detection:
   - If already claimed, return error: "already claimed by X"
4. Integration with gt mail for queue messages

## Example
```bash
gt mail queue cleanup/gastown  # See available work
bd update msg-xxx --claim       # Claim it atomically
# Returns error if someone else claimed first
```

## Dependencies
- queue: shared storage (gt-???)

## Acceptance
- --claim sets assignee + status atomically
- Double-claim returns clear error
- Works with queue messages


Refinery: Add lifecycle-notify step to patrol

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

## Implementation

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

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

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


Witness: Handle LIFECYCLE work-merged mail

Witness should process the `LIFECYCLE: work merged` notification from Refinery.

## Implementation

1. In Witness mail processing, add handler for subject containing "LIFECYCLE: work merged"
2. Parse worker name from mail body
3. Update polecat state to `merged` (new state - see sibling task)
4. Log completion for metrics
5. Worktree is now eligible for cleanup on next gc cycle

## Acceptance
- Witness correctly parses and handles work-merged mail
- Polecat state updated to merged
- No action taken if polecat already cleaned up (idempotent)


MQ Submit: Notify Witness on MR submission

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

## Implementation

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

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

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


Witness: Verify MR submission on notification

When Witness receives `LIFECYCLE: mr submitted`, verify the submission was clean.

## Implementation

1. Add handler for "LIFECYCLE: mr submitted" mail
2. Check MR status: `gt mq status <mr-id>`
3. If MR looks good (status=open, no errors): log success, allow polecat to idle
4. If MR has issues:
   - Check polecat git state
   - Nudge polecat to fix if session still alive
   - Log issue for monitoring

## Acceptance
- Witness verifies MR after submission notification
- Can detect problematic submissions
- Provides early intervention opportunity


Polecat state tracking: Add mr_submitted and merged states

Track polecat lifecycle state separate from session state.

## New States

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

## Implementation

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

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


Refinery patrol: Add sibling-check step

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

## Implementation

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

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


Witness patrol: Add sibling-check for Refinery

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

## Implementation

1. Add `sibling-check` step to mol-witness-patrol
2. Use `gt peek <rig>/refinery 20` to check Refinery output
3. Check Refinery queue status: `gt mq list <rig>`
4. Look for:
   - Refinery session alive
   - Queue processing (if items present)
   - No stuck MRs (same MR in_progress for too long)
5. If Refinery seems stuck:
   - Send nudge or status request
   - Consider escalation to Deacon

## Acceptance
- Witness checks Refinery each patrol cycle
- Can detect stuck Refinery
- Provides backup oversight for merge queue


Recovery: Detect orphaned merged polecats

Safety net for when Refinery misses sending lifecycle notification.

## Detection Points

**Witness polecat-scan**:
- For each polecat in `mr_submitted` state:
  - Check MR status: `gt mq status <mr-id>`
  - If MR is closed/merged but polecat state is still mr_submitted:
    - Update polecat state to `merged`
    - Log recovery action

**Deacon orphan-check**:
- Query all closed MRs
- Cross-reference with polecat states
- If merged MR but polecat not in merged/retired state:
  - Send correction to Witness
  - Log for metrics (indicates Refinery notification missed)

## Acceptance
- Orphaned polecats detected and corrected
- Recovery is logged for monitoring
- No work lost due to missed notifications


Digest: mol-deacon-patrol

Patrol 16: all clear


gt sling: nudge crew/mayor if session is running

When slinging to crew or mayor, if they have an active tmux session, send a nudge notification so they know work has landed on their hook.

Currently only slingToPolecat nudges (lines 514-521 in internal/cmd/sling.go).

Missing nudge in:
- slingToCrew (line ~669)
- slingToMayor (line ~876)

NOT applicable to patrol-based agents (witness, refinery, deacon) - they run on cycles and pick up work via hook check, not interactive nudge.

Implementation:
1. For crew: need to determine session name (e.g., gastown-crew-max)
2. Check if session running via tmux
3. If running, call t.NudgeSession() with appropriate message


Refactor formula transformation pipeline to shared helper

The formula transformation pipeline (control flow, advice, expansions, aspects) is duplicated between cook.go and mol_bond.go. Should be extracted to a shared function in the formula package.

Current duplication in:
- cmd/bd/cook.go:96-136
- cmd/bd/mol_bond.go:598-633

Suggested approach:
- Add formula.Transform(resolved *Formula, parser *Parser) (*Formula, error)
- Handles all transformation steps in order
- Both cook.go and mol_bond.go call this single function


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Digest: mol-deacon-patrol

Patrol 14: all healthy


Missing space in error messages (handoff.go, prime.go)

Minor typo in error messages at two locations:

**handoff.go L895**:
```go
fmt.Println(style.Dim.Render("Run manually: bd --no-daemon wisp" + protoID))
// Missing space before protoID
```

**prime.go L1061**:
Same issue.

**Fix**: Add space before protoID:
```go
fmt.Println(style.Dim.Render("Run manually: bd --no-daemon wisp " + protoID))
```


Day 5.1: End-to-end liftoff test

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

Success = no human touches needed.

Parent: gt-oki8p


Digest: mol-deacon-patrol

Patrol 10: all clear


Digest: mol-deacon-patrol

Patrol 3: All agents healthy.


refinery Handoff

# Merge queue tracking
last_processed_branch: null
branches_merged_this_cycle: 0


Digest: mol-deacon-patrol

Patrol 11: All healthy.


Day 4.1: Implement gt polecat recycle command

Add gt polecat recycle <name> command:
- Kill the Claude session (tmux kill-session)
- Preserve the sandbox (branch/worktree)
- Update agent bead state to 'stopped'
- Leave for respawn on next step

This enables session-per-step.

Parent: gt-4a2qt


Deacon has no rig-level presence - only global gt-deacon

The Deacon patrol role exists only at the global level (~/gt/deacon) but doesn't have per-rig presence like Witness and Refinery.

Current structure:
```
~/gt/
├── deacon/           ← Global deacon only
├── beads/
│   ├── witness/      ← Per-rig witness
│   ├── refinery/     ← Per-rig refinery
│   └── (no deacon)   ← Missing
└── gastown/
    ├── witness/
    ├── refinery/
    └── (no deacon)   ← Missing
```

Question: Should Deacon be per-rig like Witness/Refinery, or is global-only intentional?

If per-rig is needed: Create beads/deacon and gastown/deacon with appropriate CLAUDE.md files.


Digest: mol-deacon-patrol

Cycle 14: quiet


Digest: mol-deacon-patrol

Patrol cycle 12: All healthy


Day 3.0: Document mail protocol for patrol coordination

Document the mail protocol used for patrol coordination.

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

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

This documentation enables consistent implementation across all patrol formulas.


Day 5.0: gt status shows pinned role beads and slots

Enhance gt status to display:
- All pinned role beads (mayor, witness, refinery, polecats)
- Their current hook slot contents (what molecule/bead is attached)
- Agent state if available

This gives a single-command view of who's working on what across the town.

Example output:
```
gt status

Town: gastown
Daemon: running (pid 1234)

Agents:
  mayor/           gt-liftoff (pinned)     idle
  witness/gastown  gt-wisp-abc (patrol)    running
  refinery/gastown (empty)                 idle
  polecat/nux      gt-xyz (in_progress)    working
  polecat/rictus   (empty)                 idle
```

Implemented: gt status now shows hook_bead and agent_state from agent beads for all agents. The HookBead/State fields are read from agent beads (gt-mayor, gt-witness-<rig>, etc.). Currently shows 'idle' state since hook_bead fields aren't being updated by sling/attach commands yet - that's a separate task.


Digest: mol-deacon-patrol

Patrol 9: All healthy


Digest: mol-deacon-patrol

Patrol 18: routine


Build context self-check into patrol molecules

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

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

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

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


Digest: mol-deacon-patrol

Patrol 9: all healthy


Patrol agent state.json not updated on activity

Witness and Refinery state.json files show last_active from Dec 19 even though sessions have been running. The state should update each patrol cycle.

Evidence:
```
$ cat beads/witness/state.json
{"role": "witness", "last_active": "2025-12-19T17:50:00Z"}

$ cat beads/refinery/state.json
{"role": "refinery", "last_active": "2025-12-19T17:50:00Z"}
```

Expected: last_active should update each time the agent runs a patrol cycle.

Impact: Can't tell if patrol agents are actually active vs just having an open tmux session.


Ephemeral rig support for ad-hoc batch work

Support for temporary worker rigs for ad-hoc batch work.

## Use Case

Ad-hoc batch work where you want temporary workers:
- gt spawn --epic <epic-id> --ephemeral --workers 3
- Creates temp rig with 3 clones
- Workers process epic children
- Work merges through queue
- Clones cleaned up when done

## Interface

```go
type EphemeralRig struct {
    ID               string
    GitURL           string
    BaseCommit       string
    Workers          []string
    IntegrationBranch string
    EpicID           string  // associated epic (for cleanup trigger)
    CreatedAt        time.Time
}

func InitEphemeralRig(gitURL string, numWorkers int) (*EphemeralRig, error)
func (r *EphemeralRig) AddWorker(name string) error
func (r *EphemeralRig) Destroy(force bool) error
```

## Directory Structure

```
<rig>/ephemeral/
└── rig-a3f7/
    ├── config.json
    ├── Alice/     # clone
    ├── Bob/       # clone
    └── Carol/     # clone
```

## Cleanup Trigger

When all issues in the associated epic are closed AND merged, the ephemeral rig can be destroyed.

## Note

No swarm ID needed - the epic ID provides the grouping. Ephemeral rig is just a convenience for temporary clones.

## Reference

PGT: ephemeral.py (for structure, ignore swarm ID coupling)


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.


Digest: mol-deacon-patrol

Patrol cycle 17: All healthy


Digest: mol-deacon-patrol

Patrol 2: All healthy, routine


Digest: mol-deacon-patrol

deacon patrol: 21 cycles complete


Digest: mol-deacon-patrol

Patrol 16


Digest: mol-deacon-patrol

Patrol 18: Quiet


Update gastown to use 'ephemeral' instead of 'wisp' terminology

After beads renames 'wisp' to 'ephemeral' (bd-o18s), update gastown code:

- patrol_helpers.go: bd wisp create → bd ephemeral create (or new API)
- doctor/wisp_check.go: rename to ephemeral_check.go
- All references to Wisp field → Ephemeral

Depends on: bd-o18s


Digest: mol-deacon-patrol

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


gt crew at fails to detect existing crew session

## Summary
`gt crew at` started a new Claude session instead of attaching to an existing one for the same crew worker.

## Reproduction
1. Have a crew worker (joe) running in gastown/crew/joe (PID 27526 on tty s030)
2. Run `gt crew at` from the same directory (gastown/crew/joe)
3. Expected: Attach to existing session on s030
4. Actual: Started a brand new Claude session (PID 67618 on s026)

## Evidence
```
# Old joe still running:
PID 27526 on s030: /Users/stevey/gt/gastown/crew/joe (started 10:22AM)

# New joe spawned by 'gt crew at':
PID 67618 on s026: /Users/stevey/gt/gastown/crew/joe (started 10:39AM)
```

## Impact
- Duplicate crew workers cause confusion
- User expected to attach to existing session, not spawn a second one
- The original session was still functional (just stuck in feed display)

## Likely cause
`gt crew at` probably doesn't check for running Claude processes in the crew directory before spawning a new one. It should:
1. Check for existing tmux sessions for this crew worker
2. Check for running `claude` processes with cwd matching the crew directory
3. If found, attach to existing session instead of spawning new one


Beads epic templates for batch work patterns

Optional: Define templates for common batch work patterns.

## Concept

A template encodes a workflow pattern that can be instantiated as beads:

```yaml
# templates/batch-basic.yaml
name: basic-batch
description: Simple batch work pattern
phases:
  - name: startup
    issues:
      - title: "Verify workers ready"
  - name: working
    # Actual work issues added separately
  - name: cleanup
    issues:
      - title: "Merge all branches"
      - title: "Clean up workers"
      - title: "Report to Mayor"
```

## Usage

```bash
gt spawn --template basic-batch --epic gt-u1j --workers 3
```

Creates beads epic with template phases + actual work from gt-u1j children.

## Decision Point

Templates are OPTIONAL. The core design (beads as state, multi-wave orchestration) works without templates. Templates are sugar for common patterns.

Consider deferring to P3 or dropping entirely if beads epics with dependencies suffice.

## Note

No "swarm IDs" involved - templates just pre-populate epic/issue structure.


Test MR

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


Digest: mol-deacon-patrol

Patrol 7: healthy


Digest: mol-deacon-patrol

Patrol 18: quiet


Digest: mol-deacon-patrol

P11: stable


Merge: gt-3x1

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


Liftoff implementation plan in Beads

## Context

Session on 2025-12-27 produced three key docs:
- `~/gt/docs/agent-as-bead.md` - Agents ARE beads (identity, hook slots, CV chain roots)
- `~/gt/docs/zfc-violations-audit.md` - Where Go infers instead of trusting agents
- `~/gt/docs/liftoff-plan.md` - 4.5 day plan to self-sustaining Gas Town

## Work Required

### Phase 1: Audit existing beads ✅
- Review all open beads in gt- prefix
- Close obsolete/stale issues
- Update any that need refinement
- Note which existing beads align with liftoff plan

### Phase 2: File new beads ✅
Translate liftoff-plan.md into beads with proper dependencies.

Three pillars:
1. **Pillar 1: Agent Identity** (gt-d0jqp) - schema, slot commands, migration
2. **Pillar 2: Patrol Ignition** (gt-hwka3) - witness/refinery formula wiring
3. **Pillar 3: Polecat Lifecycle** (gt-4a2qt) - recycle/nuke commands, session-per-step

### Phase 3: Review ✅
Human reviews the plan.

## Deliverable

A complete dependency graph in beads that can be:
1. Queried with `bd ready` to find available work
2. Slung to polecats in dependency order
3. Tracked to completion

## References

- ~/gt/docs/liftoff-plan.md - 4.5 day plan
- ~/gt/docs/agent-as-bead.md - Agent identity design
- ~/gt/docs/zfc-violations-audit.md - ZFC violations addressed


Day 1.6: Update gt mol status to use hook slot

Migrate gt mol status from:
- Query(status=pinned AND assignee=me)
To:
- Read agent bead → check hook_bead field

This eliminates hook ambiguity.

Parent: gt-d0jqp

Implemented hook slot reading in gt mol status with legacy fallback


gt commands should follow .beads/redirect

## Problem

`gt sling` (and likely other gt commands) don't follow the `.beads/redirect` file that `bd` uses to share beads across clones.

This caused confusion during the v0.39.0 release: molecules were poured successfully to mayor/rig's beads (via redirect), but `gt sling` couldn't find them because it was looking in the crew clone's local .beads/.

## Expected Behavior

All `gt` commands that interact with beads should follow the redirect mechanism that `bd` uses.

## Affected Commands (likely)

- `gt sling`
- `gt hook`
- `gt mol status`
- `gt mol attach`
- Any command that shells out to `bd show` or similar

## Solution

When `gt` needs to find the beads directory, check for `.beads/redirect` and follow it, same as `bd` does.

Fixed: Added ResolveBeadsDir() helper that follows .beads/redirect files. Updated mailbox, catalog, and doctor checks to use it.


Digest: mol-deacon-patrol

Patrol 7: quiet


Integration test: simple file creation

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

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

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


Digest: mol-deacon-patrol

Patrol 8: quiet


Digest: mol-deacon-patrol

Patrol 8


Digest: mol-deacon-patrol

Patrol 2: All agents healthy, no issues.


Digest: mol-deacon-patrol

Patrol 19: routine


Agent Communication Protocol

Structured mail between agents for coordination.

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

## Protocols

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

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

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

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

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

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

Consolidates gt-0qki.


Protocol message specification

Define YAML format for protocol messages. Subject: [PROTOCOL_TYPE] brief. Body: structured payload. Document all protocol types: POLECAT_READY, MERGE_SUCCESS, MERGE_FAILED, REWORK_REQUEST, WORK_COMPLETE, NEED_HELP, ESCALATION.


Witness-Refinery protocol handlers

Implement handlers for: POLECAT_READY (witness→refinery: worker done, branch ready), MERGE_SUCCESS (refinery→witness: merged, cleanup ok), MERGE_FAILED (refinery→witness: needs rework), REWORK_REQUEST (refinery→witness: rebase needed).


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.


Agent protocol integration test

End-to-end test: polecat completes work → sends WORK_COMPLETE → witness receives and sends POLECAT_READY → refinery merges → sends MERGE_SUCCESS → witness cleans up polecat. All via structured protocol messages.


Digest: mol-deacon-patrol

Patrol 12: All healthy.


gt-mayor

gt-mayor

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


Mayor Role Definition

You are the Mayor - global coordinator of Gas Town. You sit above all rigs,
coordinating work across the entire workspace.

default_molecule: mol-mayor-patrol
capabilities:
  - dispatch_work
  - cross_rig_coordination
  - escalation_handling

## Responsibilities

- Work dispatch: Spawn workers for issues, coordinate batch work on epics
- Cross-rig coordination: Route work between rigs when needed
- Escalation handling: Resolve issues Witnesses can't handle
- Strategic decisions: Architecture, priorities, integration planning

NOT your job: Per-worker cleanup, session killing, nudging workers (Witness handles that)

## Propulsion Principle

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

Your pinned molecule persists across sessions. Hook has work -> Run it.
Hook empty -> Check mail. Nothing anywhere -> Wait for user.

## Key Commands

### Communication
- gt mail inbox - Check your messages
- gt mail read <id> - Read a specific message
- gt mail send <addr> -s "Subject" -m "Message" - Send mail

### Status
- gt status - Overall town status
- gt rigs - List all rigs
- gt polecats <rig> - List polecats in a rig

### Work Management
- bd ready - Issues ready to work (no blockers)
- gt sling <bead> <rig> - Assign work to polecat in rig

## Session End Protocol

- git status, git add, bd sync, git commit, git push
- gt handoff - hand off to fresh session


Digest: mol-deacon-patrol

Patrol 16: Quiet


Digest: mol-deacon-patrol

Patrol 11: All healthy


Refinery gates: test/lint/build before merge

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

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

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

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

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

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


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

| Variable | Required | Description |
|----------|----------|-------------|
| issue | Yes | The source issue ID being worked on |


Self-review

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

Fix any issues found before proceeding.


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.


Mol Mall: Formula marketplace and aspect composition

## Summary

A marketplace for sharing molecule formulas using GitHub as backend, plus a new
**aspect composition** capability for cooking that allows formulas to be applied
as cross-cutting transformations to existing mols.

## Key Insight

Formulas aren't just standalone workflows—they're **transformations** you can
apply to other mols. This enables patterns like:
- "Apply Rule of Five code review to any coding task"
- "Wrap any epic with security review steps"
- "Add logging/metrics to any patrol molecule"

## Architecture

```
Remote Formulas (GitHub)
        ↓ install
Local Formulas (~/.gt/formulas/ or .beads/formulas/)
        ↓ cook --with
Mols (beads, git-tracked)
        ↓ pour/sling
Execution (ephemeral or tracked)
```

## Formula Types

1. **Standalone** - Complete workflows (mol-polecat-work, mol-deacon-patrol)
2. **Aspects** - Cross-cutting concerns woven into other mols (rule-of-five)
3. **Adapters** - Wrappers that make things runnable

## Aspect Composition (New Cooking Capability)

```bash
bd cook my-epic --with rule-of-five,security-review
```

Aspects declare injection points:

```yaml
# rule-of-five.formula.yaml
kind: aspect
name: rule-of-five
version: 1.2.0

inject_after:
  tag: generates-code  # matches any step with this tag

steps:
  - id: review-correctness
    title: "Review: Correctness"
  - id: review-edge-cases
    title: "Review: Edge Cases"
  - id: review-performance
    title: "Review: Performance"
  - id: review-security
    title: "Review: Security"
  - id: review-maintainability
    title: "Review: Maintainability"
```

Injection modes:
- `inject_after: <step-id or tag>` - insert steps after
- `inject_before: <step-id or tag>` - insert steps before
- `wrap: <step-id or tag>` - insert before AND after
- `replace: <step-id or tag>` - substitute entirely

## The Mall Index

A well-known repo maps short names to full references:

```yaml
# steveyegge/gt-mall/index.yaml
formulas:
  rule-of-five:
    repo: steveyegge/gt-formulas
    path: aspects/rule-of-five.formula.yaml
    version: v1.2.0
    description: "Five-lens code review"
    tags: [review, quality]
```

Commands:
- `gt mol search review` - search the index
- `gt mol install rule-of-five` - fetch from GitHub
- `gt mol install github.com/acme/formulas/custom@v1` - direct URL

## Implementation Phases

### Phase 1: Aspect Composition (Cooking)
- Extend `bd cook` to accept `--with aspect1,aspect2`
- Aspect application during mol generation
- Local aspects in `.beads/formulas/`

### Phase 2: Mall Index
- Create gt-mall repo with index.yaml
- `gt mol search` queries the index
- `gt mol install` fetches and caches formulas

### Phase 3: Publishing
- `gt mol publish` pushes to your formula repo
- PR workflow to add to mall index

### Phase 4: Refinements
- Aspect ordering and conflict detection
- Aspect dependencies (`requires: [other-aspect]`)
- Private formula repos (enterprise)
- Local overrides/forks

## Open Questions

1. **Ordering conflicts**: Two aspects inject after same step—which goes first?
2. **Aspect dependencies**: Aspect A requires aspect B?
3. **Compile-time vs runtime**: Aspects are compile-time (baked into mol).
   Wisps remain runtime (slung dynamically).

## Deferred Until

Gas Town patrols operational. Prerequisites:
- [ ] Patrol system working (Deacon, Witness, Refinery)
- [ ] Basic spawn/work/merge cycle
- [ ] Activity feed showing work progress

Then the mall becomes valuable for sharing patrol customizations.

## Supersedes

This replaces gt-uzf2l which had the original marketplace concept but lacked
the aspect composition insight.

## Related

- Cooking system (bd cook)
- Formula definitions (.formula.yaml)
- Wisp architecture (runtime molecules)

See docs/formula_evolution.md for expanded thinking on: formula resolution hierarchy, combinators (>>, |, wrap, inject, extends), algebraic properties, higher abstractions, Mol Mall registry predictions, distribution scenarios, and open questions. The aspect composition in this design corresponds to the Wrapping (AOP) and Injection combinators in that doc.


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.


MessagingConfig: Add Type field for schema consistency

MessagingConfig lacks a Type field that other config types have (TownConfig has 'town', RigConfig has 'rig', etc). Add Type: 'messaging' for consistency.


Digest: mol-deacon-patrol

Patrol cycle 3: All healthy


mol-gastown-boot: Mayor-driven town bootstrap

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

## Vision

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

## Key Principles

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

## Proto Catalog

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

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

## mol-gastown-boot Structure

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

## Step Template

Each step in description includes action + verification:

```markdown
## Step: ensure-deacon

### Action
gt deacon start

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

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

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

## Event-Triggered Lifecycle

Beyond manual slinging, events can trigger lifecycle wisps:

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

## Parameterization (Future)

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

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

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

## Related Issues

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


Implement verification patterns for boot steps

Define and implement the verification logic Mayor uses.

## Verification Utilities

Mayor needs to check:

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

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

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

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

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

Pattern: `⏺` indicates Claude is executing tools.

## Mayor Execution Pattern

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

No hard timeout - Mayor keeps trying with increasing backoff.


Create molecules catalog directory structure

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

## Structure

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

## PROTO.md Format

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

# mol-gastown-boot

Town bootstrap molecule.

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

## Catalog Loading

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

This enables file-based proto development with beads tracking.


Add parallel step support to molecules

Enable steps to run in parallel within a molecule.

## Current State

Molecules have linear step dependencies via "Needs:" directive.
Steps run sequentially.

## Proposed Change

Add parallel grouping:

```markdown
## Step: ensure-witnesses
Parallel: true

### Sub-steps
- ensure-gastown-witness
- ensure-beads-witness
```

Or via frontmatter:

```yaml
---
parallel: [ensure-witnesses, ensure-refineries]
---
```

Steps in parallel group:
- Start simultaneously
- Parent step closes when all children complete
- Failures in one dont block others

## Implementation

1. Parse parallel directive in proto
2. When bonding, create child steps with same dependency
3. Executor spawns parallel children concurrently
4. Track completion, close parent when all done

## For Mayor Bootstrap

Mayor can check all witnesses in parallel, then all refineries,
rather than sequentially. Faster boot time.


Add event-triggered lifecycle wisps

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

## Event → Wisp Mapping

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

## Implementation

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

## Alternative: Explicit Sling

Keep commands simple, require explicit:

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

This is more transparent - user sees the sling happen.

## Recommendation

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


Design molecule parameterization

Enable protos to accept parameters for customization.

## Use Cases

1. rig-spinup needs rig name: `gt sling rig-spinup --param rig=newproject`
2. agent-restart needs agent: `gt sling agent-restart --param agent=gastown/witness`
3. code-review needs scope: `gt sling code-review --param scope=src/auth/`

## Proposed Syntax

### In Proto

```markdown
## Step: create-rig-structure
mkdir -p ~/gt/${rig}/refinery ~/gt/${rig}/witness
```

### In Sling

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

### In Bonded Molecule

Interpolation happens at bond time. Step descriptions have concrete values.

## Implementation

1. Proto declares params in frontmatter: `params: [rig]`
2. gt sling validates required params provided
3. bd mol bond receives params, interpolates descriptions
4. Bonded molecule has concrete step text

## Edge Cases

- Missing required param → error
- Unused param → warning
- Param in step title → interpolate there too


Update Mayor startup protocol for bootstrap

Teach Mayor to respond to "boot up gas town" by slinging mol-gastown-boot.

## Current Protocol

1. Announce: "Mayor, checking in."
2. Check mail
3. If handoff, continue
4. Await user instruction

## Enhanced Protocol

1. Announce: "Mayor, checking in."
2. Check mail
3. If handoff, continue
4. **If user says "boot"/"startup"/"bootstrap":**
   - Sling mol-gastown-boot as wisp
   - Execute verification-gated steps
   - Report town status when complete
5. Otherwise await instruction

## Trigger Phrases

- "boot up gas town"
- "bootstrap the town"
- "start gas town"
- "bring up the town"

## Execution

Mayor runs the molecule manually (not via subagent):

```
1. Bond the proto: bd mol bond mol-gastown-boot --wisp
2. For each step:
   a. Run action command
   b. Loop verification with backoff
   c. On stall, run recovery
   d. Close step when verified
3. Squash wisp with summary
4. Report: "Gas Town is up. All agents healthy."
```

## CLAUDE.md Update

Add to Mayor CLAUDE.md:

```markdown
## Bootstrap Command

When user requests town bootstrap:
1. gt sling gastown-boot mayor/ --wisp
2. Execute molecule steps with verification
3. Keep trying until all agents healthy
4. No timeouts - you are the town engineer
```


Digest: mol-deacon-patrol

Patrol 20: All healthy. Handoff after 20 cycles.


Digest: mol-deacon-patrol

Patrol cycle 5: All healthy


Digest: mol-deacon-patrol

Patrol 8: all healthy


Digest: mol-deacon-patrol

Patrol 1: All healthy, no messages, no orphans


Digest: mol-deacon-patrol

Patrol 8: Quiet


gt polecat done should auto-stop running session

Currently 'gt polecat done' fails if session is running, requiring a separate 'gt session stop' first. This is unnecessary friction - done should just stop the session automatically since that's always what you want.


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

Implement condition checking for each await type.

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

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

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


Digest: mol-deacon-patrol

Patrol 3: routine, all healthy


MessagingConfig: Fix error message inconsistency in validation

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


Digest: mol-deacon-patrol

Patrol 5: all healthy


Deacon Patrol

Mayor's daemon patrol loop for handling callbacks, health checks, and cleanup.


Digest: mol-deacon-patrol

Patrol 19: All clear


Digest: mol-deacon-patrol

Quick patrol: no messages, all healthy


Digest: mol-deacon-patrol

Patrol 18


Digest: mol-deacon-patrol

Cycle 12: quiet


Digest: mol-deacon-patrol

Patrol 19: quiet


Digest: mol-deacon-patrol

Patrol 15: all clear


Test Patrol Parent

Test parent for Christmas Ornament pattern


Mail inbox from witness/refinery dirs shows Mayor inbox

When running `gt mail inbox` from the witness or refinery directories, it shows the Mayor's inbox instead of the agent's own inbox.

Reproduction:
```
$ cd ~/gt/beads/witness && gt mail inbox
📬 Inbox: mayor/ (1 messages, 1 unread)
  ● 🤝 HANDOFF: Swarm session complete...
```

Expected: Should show beads/witness inbox

Likely cause: The cwd-based identity resolution isn't recognizing witness/refinery as valid mail identities, so it falls back to Mayor.


Digest: mol-deacon-patrol

Patrol 7: all healthy


Day 3.7: Witness pings Deacon for second-order monitoring

Add second-order monitoring: Witnesses monitor the Deacon.

Problem: Who monitors the Deacon? If Deacon dies, the whole town stalls.

Solution:
1. Each Witness periodically sends a ping to Deacon during patrol:
   ```bash
   gt mail send deacon/ -s "WITNESS_PING" -m "{\"rig\": \"gastown\"}"
   ```

2. Deacon patrol (inbox-check) aggregates witness pings as health signals

3. If Witness doesn't get Deacon response for 2 cycles:
   - Escalate to Mayor: "Deacon unresponsive"
   - Mayor can restart Deacon via daemon

This creates overlapping patrols:
- Deacon monitors Witnesses/Refineries
- Witnesses monitor Deacon (collectively)
- Mayor is escalation target for both

Add to mol-witness-patrol after survey-workers step.

Duplicate of P1 beads gt-5v8ls and gt-k294l


Add neighbor-check steps to all patrol molecules

Part of the 'Gas Town is a Village' antifragility design.

Every patrol molecule should include optional neighbor-checking:
- Deacon checks Witness and Refinery health
- Witness checks Refinery health
- Refinery checks Witness health
- Polecats can peek other polecats

Use gt peek to check health states.
If stuck neighbor found, can nudge or escalate.

This creates distributed monitoring - no single point of failure.


Digest: mol-deacon-patrol

P19: stable


Session cycling: Auto-spawn successor when context full

Enable automatic session succession for crew workers.

When a crew worker's context fills up (>80%), they should:
1. Write handoff mail to themselves
2. Signal session end
3. System auto-spawns successor session
4. Successor reads handoff, finds attached work, auto-continues

This completes the autonomous overnight work pattern from gt-9g82.

Depends on:
- Detecting context fullness (Claude Code API?)
- Session spawning mechanism (tmux/daemon)
- Handoff protocol already implemented


ZFC #9: Explicit MR registration instead of branch parsing

**ZFC Violation:** internal/refinery/manager.go:346-370

Go infers work metadata from branch naming conventions:
```go
pattern := regexp.MustCompile(`^polecat/([^/]+)(?:/(.+))?$`)
worker := matches[1]
issueID = matches[2]
```

**ZFC-compliant solution:**
Polecats register MRs with explicit structured data:
- `gt mq submit --worker=nux --issue=gt-xyz --branch=polecat/nux/gt-xyz`
- Creates MR bead with explicit fields
- No regex parsing of branch names

**Benefits:**
- Work metadata is explicit, not inferred
- Branch naming can evolve without breaking refinery
- Audit trail in beads

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


Digest: mol-deacon-patrol

Patrol 20: all healthy, handoff threshold reached


Create AgentIdentity type for session name parsing

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

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

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

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

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


Maintenance: Regular cleanup of closed MR/gate beads

Closed merge-request and gate beads accumulate over time. Need regular cleanup.

## Cleanup Candidates
- type=merge-request with status=closed
- type=gate with status=closed
- type=molecule with status=closed (completed workflows)

## Options
1. **Deacon patrol step** - squash closed MRs periodically
2. **Refinery post-merge** - close and tombstone MR after successful merge
3. **bd doctor check** - warn when stale MR count exceeds threshold

## Consideration
Keep some audit trail? Or just tombstone after N days?


Add unit tests for internal/lock package

Add comprehensive tests for the file locking package.

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

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

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


gt doctor: add Claude settings.json validation

Add a new doctor check to validate Claude Code settings in worker directories:

**Check for:**
1. .claude/settings.json exists in polecats/*, crew/*, witness/rig, refinery/rig
2. SessionStart hook has 'gt prime' command
3. Autonomous roles (polecat, witness, refinery) have 'gt mail check --inject' in SessionStart
4. Interactive roles (crew, mayor) have mail check in UserPromptSubmit only

**Auto-fix capability:**
Use internal/claude.EnsureSettingsForRole() to create missing settings files.

Context: The spawn priming race condition fix (gt-6957) added embedded settings templates. Doctor should validate these are in place.


Day 2.6: Define bd agent state command

Add command for agents to write their own state to agent beads.

```bash
bd agent state <agent-id> <state>
# Example: bd agent state gt-mayor running
# States: idle | running | stuck | stopped | dead
```

Implementation:
1. Lookup agent bead by ID
2. Update state field
3. Update last_activity timestamp
4. Trigger bd sync if configured

Also add:
- bd agent heartbeat <agent-id> - just updates last_activity
- bd agent show <agent-id> - show agent bead details

This is the ZFC-compliant way for agents to self-report state.

Delegated to beads repo: bd-uxlb. Assign to beads/crew/dave.


MEOW Stack Evolution: Higher-Order Workflow Abstractions

Molecular Expression of Work (MEOW) - the full taxonomy of workflow abstractions.

## Current Hierarchy

```
WORK TRACKING              WORKFLOW EXECUTION
─────────────              ──────────────────

Bead (issue)               Step (action)
    │                          │
    ▼                          ▼
Epic (bead group)          Molecule (runtime)
    │                          │
    ▼                          │
    └───────► CAMPAIGN ◄───────┘
                  │
                  ▼
             Schematic (domain template)
                  │
                  ▼
             Protocol (interface/type)
                  │
                  ▼
             Catalyst (meta-generator)
                  │
                  ▼
             Mol Mall (distribution)
```

## Key Concepts

1. **Campaign** (gt-camx5): Molecule-wrapped Epic graph for orchestrated execution
2. **Schematic** (gt-8ws7o): Domain-level formula composition
3. **Protocol** (gt-y3jb9): Type system for formulas
4. **SDK** (gt-ihzqr): Developer tooling (Forge, Lab, Validator)
5. **Mol Mall** (gt-vm85o): Package registry for distribution

## Design Principles

- Chemistry metaphor: atoms → molecules → compounds → materials
- Two orthogonal dimensions: work tracking × workflow execution
- Campaign is the convergence point
- Everything compiles down to executable molecules

## Origin

Discussion: 2024-12-26, Mayor session
Related: docs/formula_evolution.md
Foundation: gt-8tmz (Molecule Algebra)


Digest: mol-deacon-patrol

Patrol 15: All healthy


Digest: mol-deacon-patrol

Patrol 11: routine


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


Plugin: merge-oracle (merge queue analysis)

Example plugin that analyzes changesets before Refinery processes them. Builds overlap graph, classifies disjointness (parallel-safe vs needs-sequencing), uses LLM for semantic complexity, identifies high-risk patterns. Based on merge-orchestration proposal. See docs/architecture.md.


Digest: mol-deacon-patrol

Patrol 9


Digest: mol-deacon-patrol

Patrol cycle 10: All healthy


Test Patrol Parent

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

Original description:
Test parent for Christmas Ornament pattern


Digest: mol-deacon-patrol

Patrol 19: Quiet


gt-polecat-gastown-furiosa

gt-polecat-gastown-furiosa

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


gt-polecat-gastown-nux

gt-polecat-gastown-nux

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


gt-polecat-gastown-rictus

gt-polecat-gastown-rictus

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


gt-polecat-gastown-slit

gt-polecat-gastown-slit

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