Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

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

## Variables

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

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

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

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

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

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

Calculate: minutes since last activity.
Needs: capture

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

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

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

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

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

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

Record decision and rationale.
Needs: load-history

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

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

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

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

**none**: No action needed.

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

## Output

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

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


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.


Digest: mol-deacon-patrol

Patrol 2: All agents healthy, no messages, no orphans


Digest: mol-deacon-patrol

Patrol 2: routine, all healthy


Merge conflicts: Buzzard/mq-status, Dementus/harness-docs


CLI: plugin commands (list, status)

Add gt plugins <rig> to list plugins and gt plugin status <name> to check plugin state. Simple directory scan of <rig>/plugins/.


Digest: mol-deacon-patrol

Patrol 2: Quiet cycle, all agents healthy


Digest: mol-deacon-patrol

Patrol 14: all clear


Digest: mol-deacon-patrol

Cycle 12: quiet


Swarm-in-Beads: Discovery-based swarm execution

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


Digest: mol-deacon-patrol

Patrol 10: routine


Mol Mall: Molecule marketplace distribution

Design the Mol Mall distribution mechanism for sharing molecules.

## Core Insight

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

## Distribution Model

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

## Registry Format

Simple JSON index served from HTTPS:

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

## CLI Commands

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

## Installation Locations

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

## Version Management

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

## Auth (Future)

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

## Related

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


Digest: mol-deacon-patrol

Patrol cycle 19: routine


Witness patrol formula should document 'dead' agent state

## Problem

The witness patrol formula survey-workers step documents these agent states:

  agent_state: running|idle|stuck|done

But the daemon lifecycle (daemon/lifecycle.go:647) can set agent_state to 'dead'
when an agent goes unresponsive. This state isn't mentioned in the formula.

## Found in commit

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

## Suggested fix

Update the formula survey-workers step to include 'dead' in the state table:

| agent_state | Meaning | Action |
|-------------|---------|--------|
| running | Actively working | Check progress (Step 3) |
| idle | No work assigned | Skip (no action needed) |
| stuck | Self-reported stuck | Handle stuck protocol |
| done | Work complete | Verify cleanup triggered |
| dead | Marked dead by daemon | Clean up or respawn |


gt mol status: Change 'Pinned' to 'Hooked' in output

The `gt mol status` output shows "📌 Pinned: <bead-id>" for hooked beads, which is confusing now that we have distinct statuses:

- **hooked**: Work assigned to you (triggers autonomous mode)
- **pinned**: Permanent reference beads (like agent beads, role beads)

The output currently says:
```
📌 Pinned: gt-3amkz: Overhaul autonomous-mode guidance...
No molecule attached
```

Should say:
```
🪝 Hooked: gt-3amkz: Overhaul autonomous-mode guidance...
No molecule attached
```

Additional improvements:
1. Change icon from 📌 to 🪝 for hooked beads
2. Add a note clarifying that hooked work triggers autonomous mode even without an attached molecule
3. Consider adding "AUTONOMOUS MODE" banner when hooked work is found (similar to gt prime)

Files to update:
- internal/cmd/molecule_status.go (outputMoleculeStatus function around line 576)


Digest: mol-deacon-patrol

Patrol 14


Digest: mol-deacon-patrol

Patrol 3: OK


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

Patrol 19: All healthy


Polecats must not create GitHub PRs

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

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

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

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


Digest: mol-deacon-patrol

Patrol cycle 19: nominal


Swarm status doesn't update after task completions

During swarm bd-784c, the swarm status remained at '9% (1/11 tasks merged)' even after 5 tasks were completed and closed.

**Expected:**
Swarm status should reflect actual completion count, either by:
1. Polling issue status from beads
2. Updating when polecats close issues
3. Updating when commits are pushed to integration branch

**Impact:**
Coordinator had to manually check bd list to know actual progress.


Digest: mol-deacon-patrol

Patrol cycle 5: all healthy


mol-deacon-patrol

Mayor's daemon patrol loop.

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


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


Digest: mol-deacon-patrol

Patrol OK: 0 mail, all agents healthy, 3 polecats working


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

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

## Variables

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

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

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

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

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

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

Calculate: minutes since last activity.
Needs: capture

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

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

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

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

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

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

Record decision and rationale.
Needs: load-history

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

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

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

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

**none**: No action needed.

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

## Output

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

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


gt handoff: Deadlock in waitForRetirement


Test polecat lifecycle end-to-end

Test the full polecat lifecycle:

1. **Spawn**: Sling a test issue to a polecat
```bash
bd create --title="Test issue for polecat" --type=task
gt sling gt-<id> gastown
```

2. **Execute**: Watch polecat work
```bash
tmux attach -t gt-gastown-<name>
```

3. **Complete**: Polecat should:
   - Close the issue: `bd close gt-<id>`
   - Commit work: `git commit && git push`
   - Send POLECAT_DONE mail to witness

4. **Cleanup**: Witness should:
   - Receive POLECAT_DONE
   - Verify git state clean
   - Kill session and remove worktree

Success: Issue closed, PR merged, polecat cleaned up.


gt sling: patrol spawning must use wisp storage, not main DB

**P0 Launch Blocker**

gt sling to patrol roles (witness/refinery/deacon) currently spawns patrols into the main beads database instead of wisp storage. This will pollute the permanent database with ephemeral patrol cycles.

**Root cause:**
spawnMoleculeFromProto() with IsWisp=true uses `--db .beads-wisp/beads.db`, but the patrol TEMPLATE (e.g., gt-qp2w for mol-witness-patrol) only exists in the main database. So bd mol run fails with 'no issue found'.

**The fix:**
bd mol run needs to support reading the template from the main DB while writing the spawned instance to wisp storage. This is a cross-database spawn operation.

Options:
1. Add `--template-db` flag to bd mol run (read template from here, write instance to --db)
2. Copy template to wisp DB before spawning
3. Make bd mol run automatically check main DB for templates when using wisp storage

**Workaround applied (MUST REVERT):**
Set IsWisp=false in slingToWitness/slingToRefinery/slingToDeacon patrol spawning.

**Files:**
- internal/cmd/sling.go: lines 680, 883, 1050 (IsWisp: false)
- Needs fix in bd mol run (beads repo)


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


Computed range expansion (for-each over expressions)

Support for-each expansion over computed ranges, not just static lists:

for-each:
  var: move_num
  range: "1..2^{disks}"  # Computed at cook time

This enables formulas like Towers of Hanoi where the step count is a function of input variables. The range expression is evaluated during cooking.

Related: towers-of-hanoi.formula.yaml demonstrates the need.
Depends on: gt-8tmz.8 (Runtime dynamic expansion)


Witness Patrol Molecule

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

Similar to DeaconPatrolMolecule but for per-rig witness duties.

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

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


Merge: gt-3x0z.4

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


Digest: mol-deacon-patrol

Patrol cycle 8: All healthy


Digest: mol-deacon-patrol

Patrol cycle 13: routine


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.


Phase 2.2: gt prime shows ephemeral molecule context

Update gt prime to detect and display ephemeral molecule state.

## Detection

1. Check for active ephemeral molecule assigned to current identity
2. Parse molecule progress (current step, total steps)
3. Show in prime output

## Output

```
🔧 Polecat furiosa, checking in.

📦 Molecule: mol-polecat-work (eph-abc123)
   Step 3/8: do-work
   Source: gt-rixa
   Started: 10 minutes ago

Run 'bd mol status' for full molecule state.
```


Pinned Beads: Ongoing concerns and anchors

Pinned beads represent persistent concerns that do not close traditionally. Stay out of bd ready. Examples: Monitor production, Weekly syncs. Implementation: pinned: true field on bead.


gt done: MR bead not created

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

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

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


Digest: mol-deacon-patrol

Patrol #16


Implement aspect merging across formula chain

Implement merging of aspects when formulas extend each other.

## Current State

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

## Implementation

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

## Files

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

## Tests

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


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

Patrol 15


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

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

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

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

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

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

This is blocking the entire MQ pipeline.


Digest: mol-deacon-patrol

Patrol 9: routine


Digest: mol-deacon-patrol

Patrol 2: Routine, all healthy


Merge: gt-unrd

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


Digest: mol-deacon-patrol

Patrol 14: Nominal


Interactive prompts for destructive operations

Add interactive confirmations for destructive operations.

## Operations Needing Confirmation
- `gt swarm cancel` - Cancels active swarm
- `gt swarm land --force` - Force landing
- `gt polecat decommission` - Removes polecat
- `gt rig remove` - Removes rig
- `gt stop --all` - Stops all sessions
- `gt mail purge` - Permanently deletes mail

## Implementation Options

### Option 1: promptui library
```go
import "github.com/manifoldco/promptui"

prompt := promptui.Prompt{
    Label:     "Delete polecat Toast",
    IsConfirm: true,
}
result, err := prompt.Run()
```

### Option 2: Simple stdin
```go
func confirm(prompt string) bool {
    fmt.Printf("%s [y/N]: ", prompt)
    var response string
    fmt.Scanln(&response)
    return strings.ToLower(response) == "y"
}
```

## Bypass Flags
All confirmations skippable with --force or --yes:
```go
if !force && !confirm("Really cancel swarm?") {
    return nil
}
```

## Acceptance Criteria
- [ ] Destructive ops prompt by default
- [ ] --force or --yes bypasses
- [ ] Clear prompt text explaining action
- [ ] Non-interactive mode (piped input) auto-fails


gt-gastown-polecat-furiosa

gt-gastown-polecat-furiosa

role_type: polecat
rig: gastown
agent_state: running
hook_bead: gt-v9in1
role_bead: gt-polecat-role
cleanup_status: clean


attached_args: Extract molecule_lifecycle boilerplate

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 context --usage: estimate remaining context via tmux line count

## Summary

Implement `gt context --usage` to estimate remaining context capacity.

## Approach

Use tmux scrollback buffer line count as proxy for token usage:
- `tmux capture-pane -p | wc -l` gives current line count
- Heuristics (tunable per model):
  - Opus 4.5: ~1800 lines = warning threshold
  - ~2000+ lines = critical, need handoff

## Output Format
```
Context Usage: 1642 lines (~82%)
Status: WARNING - consider handoff soon
```

## Notes
- Heuristics are model-specific, need config
- Line count is proxy, not exact token count
- Good enough for autonomous patrol decisions


Digest: mol-deacon-patrol

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


Implement/document gt sling mechanism

Investigate and implement the gt sling command for attaching molecules to crew workers. Check if gt sling exists, design the flow (mail-based vs direct pin vs wisp spawn), implement in gastown CLI, integrate with bd mol/wisp/pin commands.


Digest: mol-deacon-patrol

Patrol cycle 10: routine, healthy - midpoint


Max expansion depth limit (default 5)

Add maxExpansionDepth config (default 5) to prevent runaway nested expansions. This still allows massive work generation but with a safety bound. Note: distinguish compile-time cooking (produces proto with placeholders) vs runtime cooking (produces mol with real issue IDs). Compile-time is for modeling/estimation/contractor handoff.


Digest: mol-deacon-patrol

Patrol 14: all healthy


Digest: mol-deacon-patrol

Patrol 10: quiet


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

Current gt shutdown only kills tmux sessions but leaves:

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

This causes stale polecats to be reused on next startup.

## Expected Behavior

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

## Current Code

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

## Fix

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


mol-polecat-lease: Semaphore proto for tracking polecat lifecycle

Define a small proto for tracking a single polecat in the Witness patrol wisp:

```markdown
## Molecule: polecat-lease
Semaphore tracking a single polecat's lifecycle.
Vars: {{polecat}}, {{issue}}

## Step: boot
Spawned. Verify it starts working.
gt peek {{polecat}} - if idle, gt nudge.
Timeout: 60s before escalation.

## Step: working
Actively working. Monitor for stuck.
Wait for SHUTDOWN mail.
Needs: boot

## Step: done
Exit received. Ready for cleanup.
Kill session, prune worktree.
Needs: working
```

Used by Witness: bd mol bond mol-polecat-lease wisp-patrol --var polecat=X


Remove keepalive file infrastructure

The keepalive mechanism (~/gt/daemon/activity.json) is deprecated in favor of feed-based wake.

**Current state:**
- keepalive.TouchTownActivity() writes to activity.json on every gt/bd command
- Daemon reads activity.json to calculate exponential backoff

**New model (feed-based wake):**
- Patrol agents subscribe to `bd activity --follow`
- Any beads mutation wakes agents within ~500ms
- Daemon heartbeat is recovery-focused, not wake-focused
- Read-only commands don't need to wake the town

**TODO:**
1. Remove keepalive.TouchTownActivity() calls from gt commands
2. Remove keepalive.ReadTownActivity() from daemon
3. Delete internal/keepalive package
4. Update daemon to use longer base heartbeat (recovery-focused)

**Whitelist consideration:**
Some read-only commands might need to wake (TBD):
- gt mail inbox (checking for work?)
- None identified yet

See docs/patrol-system-design.md for full feed-based wake design.


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

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


Patrols: Cyclic molecules for autonomous maintenance

## Vision

Patrols are **cyclic molecules** - workflow loops that give Gas Town its autonomous nervous system. While regular molecules are DAGs that terminate, patrols loop forever, performing maintenance tasks at varying cadences.

This is the "steam engine" of Gas Town: converting episodic Claude sessions into continuous autonomous operation.

## Core Concepts

### 1. Cyclic Molecules

Regular molecule:     A -> B -> C -> done
Patrol molecule:      A -> B -> C --+
                      ^             |
                      +-------------+

Patrols have a loop_to field specifying where to restart.

### 2. Cooldown-Aware Steps (Atoms)

Steps encode their own cadence. When patrol reaches a step:
1. Check: now - last_run > cooldown?
2. If yes: execute, update last_run
3. If no: skip (immediately close)

The patrol runner is simple - steps self-skip. Complexity distributed into atoms.

### 3. The Beacon

The heartbeat that fires patrol triggers:
- Internal ticker in Deacon (goroutine)
- Or external cron firing gt deacon tick
- Or mail-based triggers

Without beacon, nothing proactive happens.

### 4. Session Reset as Patrol Step

Connects to auto-handoff (gt-bcwn). Session reset is a patrol step, not a separate mechanism.

### 5. Multi-Role Patrols

Each supervisor has its own patrol:

**Deacon patrol:** health-check (30s), session-gc (5m), beacon-tick (10s)
**Witness patrol:** orphan-scan (10m), stuck-check (2m), molecule-progress (1m)
**Refinery patrol:** queue-check (30s), pr-status (1m), merge-ready (30s)

### 6. Cadence Tiers

- Critical (10-30s): Health checks
- Active (1-5m): Progress, nudges
- Maintenance (10-30m): Orphans, GC
- Periodic (1h+): Reports

### 7. Best-Effort Scheduling

Not real-time - more like cron. No hard deadlines. Catch-up, dont pile-up.
Priority preemption (mail interrupts patrol). Graceful degradation under load.

## Open Questions

1. State persistence: Beads (self-describing) or file (faster)?
2. Interruption: How does urgent mail preempt patrol?
3. Error recovery: Backoff? Escalate? Circuit breaker?
4. Coordination: Can patrols send mail to trigger other patrols?

## Related

- gt-bcwn: Auto-handoff (session reset is a patrol step)
- Molecule system (patrols extend molecules with loops)
- Deacon lifecycle management

## Metaphor

Claude was fire. Claude Code was steam. Gas Town is the steam engine. Beads is the train tracks.

The steam engine converts episodic combustion into continuous rotary motion.
Gas Town converts episodic Claude sessions into continuous autonomous work.


Digest: mol-deacon-patrol

Patrol 17: routine


Digest: mol-deacon-patrol

Patrol 7: All healthy


Nudge newly spawned polecats

Nudge newly spawned polecats that are ready for input.

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

**ZFC-Compliant Observation** (AI observes AI):

```bash
# View pending spawns with captured terminal output
gt deacon pending
```

For each pending session, analyze the captured output:
- Look for Claude's prompt indicator "> " at the start of a line
- If prompt is visible, Claude is ready for input
- Make the judgment call yourself - you're the AI observer

For each ready polecat:
```bash
# 1. Trigger the polecat
gt nudge <session> "Begin."

# 2. Clear from pending list
gt deacon pending <session>
```

This triggers the UserPromptSubmit hook, which injects mail so the polecat sees its assignment.

**Bootstrap mode** (daemon-only, no AI available):
The daemon uses `gt deacon trigger-pending` with regex detection. This ZFC violation is acceptable during cold startup when no AI agent is running yet.


Digest: mol-deacon-patrol

Patrol 12: All clear


Outpost integration tests: Mock and E2E testing

## Overview

Integration tests for the outpost system.

## Mock Outpost

```go
type MockOutpost struct {
    name        string
    maxWorkers  int
    spawnDelay  time.Duration
    spawnError  error
    workers     []*MockWorker
}

func (o *MockOutpost) Spawn(issue string, cfg WorkerConfig) (Worker, error) {
    if o.spawnError != nil {
        return nil, o.spawnError
    }
    time.Sleep(o.spawnDelay)
    worker := &MockWorker{id: uuid.New().String(), issue: issue}
    o.workers = append(o.workers, worker)
    return worker, nil
}
```

## Unit Tests

```go
func TestOutpostManager_SelectOutpost(t *testing.T) {
    // Test policy-based selection
}

func TestOutpostManager_SpawnWithRetry(t *testing.T) {
    // Test retry logic with transient failures
}

func TestCloudRunOutpost_CostTracking(t *testing.T) {
    // Test cost calculation and caps
}
```

## Integration Tests (with mocked Cloud Run)

```go
func TestCloudRunOutpost_Integration(t *testing.T) {
    // Start mock HTTP server
    server := httptest.NewServer(mockWorkHandler())
    defer server.Close()

    outpost := NewCloudRunOutpost(OutpostConfig{
        ServiceURL: server.URL,
    })

    worker, err := outpost.Spawn("test-issue", WorkerConfig{})
    // Assert worker created, events streamed
}
```

## E2E Tests (requires real Cloud Run)

```go
// +build e2e

func TestCloudRunOutpost_E2E(t *testing.T) {
    if os.Getenv("CLOUDRUN_SERVICE_URL") == "" {
        t.Skip("CLOUDRUN_SERVICE_URL not set")
    }
    // Test against real Cloud Run service
}
```

## Files

- `internal/outpost/mock_test.go`
- `internal/outpost/manager_test.go`
- `internal/outpost/cloudrun_test.go`
- `internal/cloudrun/integration_test.go`

## Dependencies

Depends on: gt-9a2.8 (CloudRunOutpost), gt-9a2.15 (error handling)
Lower priority - can be done after implementations work.


mol-polecat-work

Full polecat lifecycle from assignment to decommission.

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

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


Digest: mol-deacon-patrol

Patrol 20: final cycle


MVP Cutover: GGT replaces PGT for batch work

When this is closed, stop using town and start using gt.

## Acceptance Criteria

1. gt spawn assigns issue to polecat and starts session
2. gt spawn --epic spawns workers for all epic children
3. gt session manages tmux lifecycle
4. gt send / gt inbox work for mail
5. Refinery processes merge queue with semantic merges
6. Integration branches created and landed correctly
7. gt stop --all halts all sessions
8. One successful test batch completed end-to-end

## What Must Work

- Spawn polecat with issue assignment
- Spawn workers for epic children
- Session start/stop/attach
- Mail send/inbox/read
- Refinery merge loop (semantic)
- Integration branch → main landing
- Witness cleanup protocol
- Emergency stop

## What Can Be Deferred

- Doctor checks (use PGT)
- TUI dashboard
- Plugin system
- Federation
- Ephemeral rigs
- Detailed landing reports

## Test Plan

1. Create epic with 2 tasks, spawn 2 workers
2. Verify polecats get assigned and sessions start
3. Simulate task completion
4. Verify Refinery merges to integration
5. Verify landing to main
6. Verify cleanup

## Validation

Run one real batch implementing GGT issues using GGT.

## Note

No "swarm IDs" - just spawn workers for epic, let merge queue coordinate.


Digest: mol-deacon-patrol

Test patrol cycle - verifying wisp flow


Digest: mol-deacon-patrol

Patrol 18: all quiet


Digest: mol-deacon-patrol

Cycle 15: quiet, handoff for fresh context


Digest: mol-deacon-patrol

Patrol 6: all clear


Digest: mol-deacon-patrol

Patrol 3: quiet, health OK


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.


Merge: gt-svi.1

type: merge-request
branch: polecat/Max
target: main
source_issue: gt-svi.1
rig: gastown


Digest: mol-deacon-patrol

Patrol 6: All healthy, no messages


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


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

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

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


Shutdown request handler

Handle polecat shutdown requests:

When polecat runs 'gt handoff --shutdown':
1. Polecat sends mail to <rig>/witness requesting shutdown
2. Witness receives mail
3. Witness verifies:
   - Git working tree is clean
   - Work is submitted (MR exists or in queue)
   - No uncommitted beads changes
4. If clean:
   - gt session stop <rig>/<polecat>
   - git worktree remove polecats/<name>
   - git branch -d polecat/<name>
5. If not clean:
   - Send nudge back to polecat
   - Track retry count

Depends on fixing gt-dsfi (handoff deadlock).


Digest: mol-deacon-patrol

Patrol 12: all quiet


Digest: mol-deacon-patrol

Patrol 4: Nominal


Support space-separated rig/polecat syntax in gt polecat commands

## Problem
`gt polecat remove beads obsidian` fails with:
```
Error: invalid address 'beads': invalid address format: expected 'rig/polecat', got 'beads'
```

Requires slash syntax: `gt polecat remove beads/obsidian`

## Proposal
Support both syntaxes for ergonomics:
```bash
gt polecat remove beads/obsidian    # address syntax (current)
gt polecat remove beads obsidian    # positional syntax (proposed)
```

## Implementation
In polecat subcommands that take rig/polecat addresses:
- If arg contains `/`, use as-is
- If two args without `/`, join with `/`
- Apply to: remove, status, sync, reset, done

```go
func normalizePolecatAddress(args []string) string {
    if len(args) == 1 && strings.Contains(args[0], "/") {
        return args[0]
    }
    if len(args) == 2 {
        return args[0] + "/" + args[1]
    }
    // error handling...
}
```

## Rationale
- Unambiguous context - polecat commands always take rig + name
- Matches positional arg patterns in docker, kubectl, etc.
- Reduces agent friction (natural first guess is space-separated)
- Slash syntax remains valid for consistency with mail addresses


load-context

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

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


Digest: mol-deacon-patrol

Patrol 18: All agents healthy


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


Add 'wit' alias for witness command

ref works as alias for refinery, but wit doesn't work for witness. Add Aliases: []string{"wit"} to witnessCmd.


Test activity feed enhancement


bd mol publish: Push formula to GitHub repo

Publish a local formula to a GitHub repository.

Usage: bd mol publish <formula-name> <github-repo>

Should:
- Create or update GitHub repo with formula
- Add README.md from formula metadata
- Tag with version
- Set appropriate topics for discoverability


Merge: furiosa-1767073359963

attached_args: Code review this merge request

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


bd sync: Handle mol-* prefix for protos gracefully

When bd sync imports protos (mol-*), it warns about prefix mismatch. The allowed_prefixes config doesn't seem to apply to imports.

Current behavior: bd sync fails import step with prefix mismatch error
Expected: mol-* should be recognized as valid proto prefix, or allowed_prefixes should be respected

Workaround: The export/push still succeeds, only import fails.

Related: bd-47qx


Digest: mol-deacon-patrol

Patrol 12: Mayor OK, 11 sessions


Witness Plugin System

Patrol extensions via molecule bonding.

## Core Insight

Plugins ARE molecules. No separate format needed.

## How It Works

```
mol-witness-patrol
    │
    ├── survey-workers
    │
    ├── plugin-run ←── bonds registered plugin molecules
    │       │
    │       ├── mol-mood-check (if registered)
    │       ├── mol-security-scan (if registered)
    │       └── mol-custom-X (user-installed)
    │
    └── aggregate (WaitsFor: all-children)
```

## Plugin Molecule Structure

A plugin is just a molecule proto with specific labels:

```json
{
  "id": "mol-mood-check",
  "title": "Polecat Mood Check",
  "description": "Assess polecat emotional state from output. Vars: {{polecat_name}}, {{captured_output}}",
  "labels": ["template", "plugin", "witness", "tier:haiku"],
  "issue_type": "task"
}
```

Labels encode metadata:
- `plugin` - marks as bondable plugin
- `witness` / `deacon` / `refinery` - which patrol can use it
- `tier:haiku` / `tier:sonnet` - model tier hint

## Registration

Plugins registered in rig config or molecules.jsonl:

```bash
# Install from Mol Mall
bd mol install mol-mood-check

# Or add to ~/.beads/molecules.jsonl manually
```

## Execution

The `plugin-run` step in patrol:

```bash
# For each registered plugin molecule matching this patrol:
bd mol bond mol-mood-check $PATROL_WISP \
  --ref mood-{{polecat_name}} \
  --var polecat_name=$POLECAT \
  --var captured_output="$OUTPUT"
```

## CLI

- `bd mol list --label plugin` - List available plugins
- `bd mol install <id>` - Install from Mol Mall
- `gt patrol plugins <role>` - Show plugins for a patrol role

## Benefits

- No YAML schema to maintain
- No separate plugin loading code
- Plugins are versioned, synced, auditable (they are beads)
- Distribution via Mol Mall (molecules.jsonl registry)
- Same {{var}} substitution as all molecules


Digest: mol-deacon-patrol

P19: stable


Digest: mol-deacon-patrol

Patrol 16: quiet


Refinery tmux status: show merge queue length

Add refinery-specific status line showing:
- MQ length (pending merges)
- Currently processing item (if any)
- Maybe: success/failure counts

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


Standardize wisp creation syntax across role templates

## Task

Role templates use inconsistent syntax for creating wisps.

## Current State

**Deacon template:**
```bash
bd mol wisp create mol-deacon-patrol
bd update <wisp-id> --status=pinned --assignee=deacon
```

**Witness template (BROKEN - see gt-xxx):**
```bash
bd mol wisp mol-witness-patrol --assignee={{ .RigName }}/witness
```

## Recommended Pattern

All patrol roles should use the same two-step pattern:
```bash
bd mol wisp create mol-<role>-patrol
bd update <wisp-id> --status=pinned --assignee=<identity>
```

Or we should add `--assignee` flag to `bd mol wisp` (file in beads repo if preferred).

## Templates to Update

- witness.md.tmpl (broken, needs immediate fix)
- Verify consistency in refinery.md.tmpl if it also creates wisps

## Related

This is a consistency task; the witness template bug is tracked separately as P0.


CLI: cleanup commands for stale state

Cleanup commands for recovering from stale state.

## Commands

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

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

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

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

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

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

## New File
internal/cmd/cleanup.go

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


Digest: mol-deacon-patrol

Patrol cycle 20: final before handoff


Digest: mol-deacon-patrol


gt status should show runtime state, not just structure

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

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

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

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

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


Agent-centric dashboard view (gt dashboard)

Charmbracelet TUI for gt feed with dashboard + stream layout.

## Layout (horizontal split)

**Top panel: Agent Tree (role/town view)**
- Shows EVERY agent organized by role
- Each agent shows their MOST RECENT activity inline
- At a glance: who is doing what RIGHT NOW
- Collapsible sections

**Bottom panel: Event Stream (chronological feed)**
- Full scrollable history of events
- Most recent at top
- More detail than the tree view
- This is what you scroll through

## Visual
```
+-------------------------------------------------------------+
| GT Feed                                    Filter: all      |
+-------------------------------------------------------------+
| gastown/                           <- AGENT TREE (top)      |
|   mayor      [10:30] Dispatched gt-xyz -> Toast             |
|   witness    [10:28] Nudged polecat-3 (idle 5m)             |
|   refinery   [10:25] Merged PR #123                         |
|   crew/                                                      |
|      joe        [10:22] Completed gt-95j13                  |
|      max        [10:18] Working on gt-s6r44                 |
|   polecats/                                                  |
|      Toast      [10:30] Received gt-xyz                     |
+-------------------------------------------------------------+
| [10:30] Toast: Received molecule gt-xyz                     |
| [10:28] witness: Nudged polecat-3 after 5m idle             |
| [10:25] refinery: Merged PR #123                            |
| ...                                        <- EVENT STREAM  |
+-------------------------------------------------------------+
| j/k: scroll  /: search  f: filter  q: quit  ?: help         |
+-------------------------------------------------------------+
```

## Why this layout?
- Top answers: what is everyone doing?
- Bottom answers: what just happened?
- Together: complete situational awareness

## Replaces
- gt feed (currently just wraps bd activity)
- gt dashboard task merges into this

## Tech
- bubbletea Model with two viewports
- Tab switches focus between tree/stream
- j/k scrolls focused panel


rebase-main

Rebase against main to incorporate any changes.
Resolve conflicts if needed.

git fetch origin main
git rebase origin/main

If there are conflicts, resolve them carefully and continue the rebase.

Depends: self-review, verify-tests


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

Patrol 19


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

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

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

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

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


Improve looksLikeFormulaName heuristic in mol_bond

The looksLikeFormulaName() function in mol_bond.go uses simple heuristics that could have edge cases:

Current checks:
- starts with 'mol-'
- contains '.formula'
- contains path separators

Potential improvements:
- Check if operand matches issue ID format (prefix-hash pattern)
- Consider formula search paths when determining if something could be a formula
- Add --formula flag to explicitly mark an operand as a formula name

Low priority since edge cases are rare in practice.


Day 2.1: Daemon reads agent bead state field

Daemon queries agent beads for state instead of inferring from PID/tmux.

Change getAgentState() to:
1. Look up agent bead by ID (gt-mayor, gt-witness, etc.)
2. Read state field (idle|running|stuck|stopped)
3. Trust the value

Parent: gt-d0jqp


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


Digest: mol-deacon-patrol

Patrol 14: All clear


Add molecules.jsonl as separate catalog file for template molecules


Digest: mol-deacon-patrol

Patrol 8: nominal


CloudRun worker container: Dockerfile and entrypoint

## Overview

Docker container image for Cloud Run workers.

## Dockerfile

```dockerfile
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o gt ./cmd/gt

FROM ubuntu:22.04

# Install Claude Code
RUN apt-get update && apt-get install -y     curl git nodejs npm     && npm install -g @anthropic-ai/claude-code     && apt-get clean

# Install gt
COPY --from=builder /app/gt /usr/local/bin/gt

# Worker entrypoint
COPY deploy/cloudrun/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

EXPOSE 8080
ENTRYPOINT ["/entrypoint.sh"]
```

## Entrypoint

```bash
#!/bin/bash
git config --global user.name "$GIT_USER"
git config --global user.email "$GIT_EMAIL"
exec gt worker serve --port 8080
```

## Files

- `deploy/cloudrun/Dockerfile`
- `deploy/cloudrun/entrypoint.sh`
- `deploy/cloudrun/README.md`

## Dependencies

Depends on: gt-9a2.11 (HTTP server - gt worker serve)


Molecule Phase Terminology Documentation

Document the three-phase molecule lifecycle with consistent terminology:

## The Phases (States of Matter)

| Phase | State | Nature |
|-------|-------|--------|
| **Proto** | Solid/Crystal | Frozen template, static definition in catalog |
| **Mol** | Liquid | Reified instance, malleable, tracked in git (durable) |
| **Wisp** | Gas | Evaporates after squash, ephemeral orchestration |

## Documentation Gaps Identified

### High Priority
1. Update vision.md Steam Engine metaphor to use proto/mol/wisp terminology
2. Add lifecycle diagram: Proto → bond → Mol or Wisp → squash → Digest
3. Document Go types for phases (ProtoMolecule, etc.)
4. Document .beads-ephemeral/ structure and purpose

### Medium Priority
5. Update CLAUDE.md files (polecat, witness, refinery) with molecule workflow
6. Document bd mol bond/squash/burn CLI API with examples
7. Add polecat guide: Executing molecules and generating summaries

### Low Priority
8. Add terminology glossary to architecture.md
9. Create troubleshooting playbook for stuck molecules
10. Add reference diagrams for state transitions

## Current State
- architecture.md has good molecule basics but uses inconsistent terminology
- vision.md has Steam Engine metaphor (proto=fuel, wisp=steam, digest=distillate)
- molecules.md has detailed reference but needs phase clarity
- builtin_molecules.go has 8 molecules but no phase documentation in code


Digest: mol-deacon-patrol

Patrol 6: All clear


Swarm learning: Witness needs automated context cycling

Furiosa hit 2% context during swarm work. Witness role needs automated detection of low context and should trigger session cycling before agents get stuck. Add to Witness responsibilities in prompts.md.


mol-theme-generate: Custom name pool generation via cognition

Currently polecat name themes are hardcoded in namepool.go. Simple theme selection is fine as static config, but custom name generation should be a molecule.

Examples requiring cognition:
- "Generate 50 polecat names in the style of [book/movie/game]"
- "Create names that match the project's domain (medical, financial, etc.)"
- "Extend the mad-max theme with 20 more obscure characters"

## Molecule: theme-generate
Generate custom polecat names for a rig.

## Step: analyze-request
Understand the naming request:
- Theme style or source material
- Number of names needed
- Any constraints (length, format)

## Step: generate-candidates
Generate candidate names matching the style.
Aim for 2x the requested count.

## Step: validate-names
Filter candidates:
- No duplicates with existing themes
- Appropriate length (3-15 chars)
- No problematic terms

## Step: create-theme
Write the custom theme to rig config or beads.
Register in name pool.
Needs: validate-names

## Notes
- Output is a new theme entry in BuiltinThemes equivalent
- Could be stored as a pinned bead for persistence


Digest: mol-deacon-patrol

Patrol 11: All healthy


Pick next branch. Rebase on current main.

```bash
git checkout -b temp origin/<polecat-branch>
git rebase origin/main
```

If rebase conflicts and unresolvable:
- git rebase --abort
- Notify polecat to fix and resubmit
- Skip to loop-check for next branch

instantiated_from: mol-refinery-patrol
step: process-branch


Add mutex to swarm.Manager for thread safety

attached_args: Add swarm.Manager mutex for thread safety

Fix potential race condition in swarm manager's map access.

## Files to modify
- internal/swarm/manager.go
- internal/swarm/manager_test.go (add concurrency test)

## Problem
The swarms map[string]*Swarm is accessed without synchronization:
```go
type Manager struct {
    swarms map[string]*Swarm  // No mutex protection
}
```

Multiple goroutines could call Create(), GetSwarm(), etc. simultaneously.

## Implementation
```go
type Manager struct {
    mu     sync.RWMutex
    swarms map[string]*Swarm
}

func (m *Manager) GetSwarm(id string) *Swarm {
    m.mu.RLock()
    defer m.mu.RUnlock()
    return m.swarms[id]
}
```

## Acceptance criteria
- [ ] sync.RWMutex added to Manager struct
- [ ] All map accesses protected by mutex
- [ ] RLock for reads, Lock for writes
- [ ] Concurrency test added
- [ ] go test -race ./internal/swarm/... passes


Witness handoff bead state persistence

Implement state persistence for Witness across wisp cycles.

## Problem
Wisps burn between cycles, but Witness needs to remember:
- Which workers have been nudged
- How many times (nudge_count)
- When was last nudge
- Last observed activity

## Solution
Witness handoff bead with worker_states field:

```json
{
  "id": "gt-witness-state",
  "type": "handoff",
  "assignee": "<rig>/witness",
  "pinned": true,
  "worker_states": {
    "furiosa": {
      "issue": "gt-123",
      "nudge_count": 2,
      "last_nudge": "2024-12-22T10:00:00Z"
    }
  },
  "last_patrol": "2024-12-22T10:05:00Z"
}
```

## Implementation
1. On patrol start: bd show <witness-handoff-id> to load state
2. During patrol: update in-memory state
3. On save-state step: bd update to persist
4. State survives wisp burn/squash

## Depends on
- gt-83k0 (mol-witness-patrol definition)


End-to-end swarm integration test

Full integration test for swarm lifecycle.

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

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

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


Federation: Wasteland architecture for cross-town coordination

## Overview

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

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

## Outpost Types

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

## Key Concepts

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

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

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

## Design Principles

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

## Related

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


Deprecate hook files, use pinned beads for propulsion

## Problem

Hook files were "deprecated" via comments but NOT eradicated. The code is all still there and actively used.

## Current State (NOT Done)

**Live hook file exists:**
- `/Users/stevey/gt/deacon/.beads/hook-deacon.json`

**Code still calling hook functions:**
- `resume.go:151` - WriteSlungWork
- `park.go:114` - ReadHook
- `spawn.go:407` - WriteSlungWork
- `molecule_step.go:293` - WriteSlungWork
- `molecule_step.go:368` - BurnHook

**Hook infrastructure still exists:**
- `internal/wisp/io.go` - All hook I/O functions
- `internal/wisp/types.go` - SlungWork, Wisp types, constants
- `.beads/.gitignore` - hook-*.json pattern

**14 docs still reference hooks:**
architecture.md, beads-data-plane.md, molecular-chemistry.md, molecules.md,
pinned-beads-design.md, polecat-lifecycle.md, polecat-wisp-architecture.md,
propulsion-principle.md, session-lifecycle.md, sling-design.md, vision.md,
wisp-architecture.md, witness-patrol-design.md, ~/gt/CLAUDE.md

## Goal: Total Eradication

1. Remove ALL hook file code from callers
2. Delete hook functions from internal/wisp/
3. Delete hook types and constants
4. Remove gitignore patterns
5. Delete any live hook files, convert to pinned beads
6. Update all documentation
7. Verify propulsion cycle works with pinned beads only


Digest: mol-deacon-patrol

Patrol 4: stable


gt mol command tree: status, catalog, burn, squash

Add gt mol subcommand as agent-side API for molecule operations.

Commands needed:
- gt mol status - What's on my hook? (pinned molecule, current step, progress)
- gt mol catalog - List available protos (delegate to bd mol catalog)
- gt mol burn - Burn current attachment
- gt mol squash - Squash current molecule to digest

This completes the agent-side API and makes the docs (sling-design.md, propulsion-principle.md) match reality.

Blocks: deacon.md.tmpl update (can't use gt mol status until it exists)


Digest: mol-deacon-patrol

Patrol 4: All healthy


CloudRun persistent connections: HTTP/2 keepalive

## Overview

Implement persistent HTTP/2 connections to keep Cloud Run containers warm.

## Problem

Cloud Run has cold start latency when scaling from 0. Persistent connections keep containers warm.

## Implementation

```go
type PersistentConnectionManager struct {
    serviceURL string
    conn       net.Conn
    client     *http2.ClientConn
    mu         sync.Mutex
    lastUsed   time.Time
    keepAlive  time.Duration
}

func (m *PersistentConnectionManager) GetConnection() (*http2.ClientConn, error) {
    m.mu.Lock()
    defer m.mu.Unlock()

    // Reuse existing connection if healthy
    if m.client != nil && m.client.CanTakeNewRequest() {
        m.lastUsed = time.Now()
        return m.client, nil
    }

    // Create new connection
    return m.dial()
}

func (m *PersistentConnectionManager) keepAliveLoop() {
    ticker := time.NewTicker(m.keepAlive / 2)
    for range ticker.C {
        m.mu.Lock()
        if time.Since(m.lastUsed) > m.keepAlive {
            // Connection idle too long, close it
            m.close()
        } else {
            // Send ping to keep alive
            m.ping()
        }
        m.mu.Unlock()
    }
}
```

## Integration with CloudRunOutpost

```go
type CloudRunOutpost struct {
    // ...existing fields...
    connMgr *PersistentConnectionManager
}

func (o *CloudRunOutpost) Spawn(...) {
    conn, err := o.connMgr.GetConnection()
    // Use connection for work dispatch
}
```

## Config

```yaml
outposts:
  - name: cloudrun-burst
    type: cloudrun
    # ...
    keep_alive: 5m  # Keep connection warm for 5 minutes
```

## Files

- `internal/cloudrun/connection.go`

## Dependencies

Depends on: gt-9a2.8 (basic CloudRunOutpost)


Remove shell respawn loops from witness and deacon

Currently witness and deacon use shell loops (`while true; do claude; done`) to auto-respawn when Claude exits. This bypasses the proper lifecycle architecture.

## Current Behavior
- witness.go: ensureWitnessSession() wraps Claude in a shell loop
- deacon.go: runDeaconStart() wraps Claude in a shell loop
- When Claude exits, shell automatically restarts it

## Desired Behavior
- Agents should request lifecycle changes via LIFECYCLE mail to deacon
- Daemon processes lifecycle requests and handles restarts
- No shell loops - daemon and deacon's health-scan handle respawns

## Why Change
1. Shell loops bypass state verification (requesting_cycle flag)
2. Shell loops bypass handoff mail protocol
3. Shell loops make lifecycle tracking harder
4. Daemon already has infrastructure for this

## Migration
1. Remove shell loop from witness (daemon/deacon handles restart)
2. Remove shell loop from deacon (daemon handles restart via ensureDeaconRunning)
3. Verify daemon's health check properly restarts dead agents
4. Update templates if needed

## Risk
Low - we have gt witness restart and gt deacon restart as fallbacks


Update rig init to create refinery as worktree

Modify rig initialization to create refinery as a git worktree instead of a separate clone.

## Changes
- internal/rig/init.go or similar - change refinery setup
- Create worktree at <rig>/refinery/ with branch 'refinery'
- Remove clone logic for refinery
- Update any path assumptions

## Considerations
- Refinery worktree should track main (or its own branch?)
- May need to handle existing rigs (migration path)


Refinery startup: Use Claude agent instead of foreground mode

WIP found in stash: Refactor refinery startup to spawn Claude as the refinery agent rather than using gt refinery start --foreground.

Changes needed:
- Use refineryDir (refinery/rig) as working directory
- Start Claude with --dangerously-skip-permissions
- Wait for shell ready, then Claude ready
- Send gt prime, then refinery startup prompt
- Remove foreground mode complexity

Related: gt-n7z7 (refinery --foreground race condition bug)


Compile-time vs runtime cooking

Distinguish two cooking modes: 1) Compile-time: produces proto with variable placeholders, for modeling/estimation/contractor handoff. 2) Runtime: produces mol with real issue IDs. Compile-time lets you pre-decompose work for untrusted executors or planning purposes.


Digest: mol-deacon-patrol

Patrol complete: town quiet, all healthy


Digest: mol-deacon-patrol

P20: stable - handoff after 20 patrols


gt mail inbox shows wrong identity when run from deacon directory

When running 'gt mail inbox' from ~/gt/deacon/, it shows 'Inbox: mayor/' instead of 'Inbox: deacon/'. The role detection works (deacon checks in correctly), but mail identity detection is broken.


gt handoff: Deadlock bug in runHandoff

Running 'gt handoff' from a polecat causes a deadlock:

Stack trace shows:
- goroutine 1 [select (no cases)] in runHandoff
- File: internal/cmd/handoff.go:125

The command successfully sends the shutdown request but then hangs with 'fatal error: all goroutines are asleep - deadlock!'


Merge: gt-h5n.5

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


Merge: gt-3x0z.3

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


Digest: mol-deacon-patrol

Patrol cycle 9: nominal


ZFC #4: Replace daemon identity parsing with agent self-registration

**ZFC Violation:** internal/daemon/lifecycle.go:209-309

The daemon has embedded switch statements that parse agent identity strings to determine:
- Session names
- Working directories
- Which agents need pre-sync
- Restart commands

**Current anti-pattern:**
```go
if strings.HasSuffix(identity, "-refinery") {
    needsPreSync = true
    workDir = filepath.Join(...)
}
```

**ZFC-compliant solution:**
Agent types self-register via role beads:
- Role bead contains: working_dir, session_name_pattern, needs_sync, restart_command
- Daemon reads role bead to get lifecycle config
- No identity string parsing in Go

**Depends on:** Day 1.5 (role beads exist)

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


Burn and respawn or loop

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

Decision point at end of patrol cycle:

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

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

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

This enables infinite patrol duration via context-aware respawning.


Polecats should auto-cleanup after MR submission

Currently polecats must manually run 'gt handoff --shutdown' after completing work. This is error-prone and leaves stale polecats around.

## Desired Flow

1. Polecat completes work
2. Polecat runs 'gt mq submit' (or similar)
3. MR is added to integration queue
4. **Polecat automatically cleans up** (no manual handoff needed)

## Implementation Options

### Option A: mq submit triggers cleanup
In 'gt mq submit':
1. Submit MR to queue
2. Automatically run cleanup (same as gt handoff --shutdown)
3. Polecat session terminates

### Option B: Refinery triggers cleanup
When Refinery picks up MR:
1. Refinery processes MR
2. Sends message to Witness: 'CLEANUP: <polecat>'
3. Witness cleans up polecat

### Option C: Molecule-driven
Define cleanup as final phase of polecat-work molecule:
1. code → test → submit-mr → cleanup

## Note
This reinforces the ephemeral model: polecats exist only for the duration of a single task.


Digest: mol-deacon-patrol

Patrol 18: Mayor OK, 11 sessions


Merge to main and push immediately.

Merge to main and push immediately.

```bash
git checkout main
git merge --ff-only temp
git push origin main
git branch -d temp
git push origin --delete <polecat-branch>
```

Main has moved. Any remaining branches need rebasing on new baseline.

instantiated_from: mol-refinery-patrol
step: merge-push


burn-or-loop

Squash wisp and decide: loop or cycle session.

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

Needs: generate-summary


Epic: Wisp Molecule Integration

## Vision

Integrate Beads wisp molecules into Gas Town. All orchestration work runs as wisp molecules - patrols, polecat workflows, batch operations. Only digests reach main beads.

## The Steam Engine Metaphor

```
Engine does work → generates steam
Steam wisps rise → execution trace
Steam condenses → digest (distillate)
Steam dissipates → cleaned up (burned)
```

## Architecture

```
Proto Molecules (templates)
    ↓ bd mol bond
Wisps (.beads-wisps/ or inline)
    ↓ bd mol squash + AI summary
Main Beads (digests only)
```

## Vocabulary

| Term | Meaning |
|------|---------|
| bond | Attach proto to work (creates wisps) |
| wisp | Temporary execution step (steam rising) |
| squash | Condense wisps into digest |
| burn | Destroy wisps without record |
| digest | Permanent condensed record (distillate) |

## Key Design Decisions

1. **Wisp location**: Per-rig or inline in main beads with wisp flag
2. **Summary generation**: Agent that did work generates summary (inversion of control)
3. **Squash timing**: Final step of molecule workflow, before signaling done
4. **Crash recovery**: Wisps persist, Witness detects stalls, new agent resumes
5. **Patrols**: Each cycle is fresh wisp molecule, squashed on completion

## Digest Contents

- Molecule type and instance ID
- Assignee, start/end times
- Source issue reference
- AI-generated summary
- Outcomes (issues closed, commits, branches)

## Integration Points

- gt rig init: Configure wisp storage
- gt spawn --molecule: Bond creates wisps
- gt prime: Show wisp molecule context
- Polecat CLAUDE.md: Summary + squash protocol
- gt doctor: Wisp health checks
- Deacon/Witness/Refinery: Patrol molecules

## Phases

Phase 1: Wisp setup infrastructure
Phase 2: Spawn integration
Phase 3: Completion flow (summary + squash)
Phase 4: Patrol integration
Phase 5: Documentation and polish


Digest: mol-deacon-patrol

Patrol 14: All healthy, read Mayor handoff (info)


Account management for multi-account Claude Code usage

Enable Gas Town to manage multiple Claude Code accounts (e.g., personal vs work) with easy switching. Core mechanism: CLAUDE_CONFIG_DIR env var per account. See docs/design/account-management.md for full design.


Digest: mol-deacon-patrol

Patrol 5: All healthy.


Polecat didn't commit before gt done

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol cycle 14: routine


Create mol-gastown-polecat derived molecule

Create the standard Gas Town polecat workflow molecule.

## Molecule Definition
\`\`\`markdown
## Molecule: gastown-polecat
Full workflow for Gas Town polecats including binary installation.

Includes: mol-engineer-in-box

## Step: install-binary
After merge is submitted, rebuild and install the local gt binary.
This ensures the latest code is available to all local agents.

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

Needs: submit
\`\`\`

## Why This Molecule
Every polecat that pushes to main should also rebuild the binary.
This ensures the installed gt is always current with main.

## Implementation
1. Add to builtin_molecules.go (after Includes support lands)
2. Update SeedBuiltinMolecules
3. Run gt molecule seed

## Acceptance
- [ ] mol-gastown-polecat exists
- [ ] Includes all engineer-in-box steps
- [ ] Adds install-binary step after submit
- [ ] Can be used with gt spawn --molecule


Digest: mol-deacon-patrol

Patrol 10: All healthy


Use Beads issue status as spawn source of truth

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

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

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

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

This simplifies state management and ensures consistency.


Digest: mol-deacon-patrol

Patrol 12: OK


Digest: mol-deacon-patrol

P13: stable


Merge: gt-83k0

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


Digest: mol-deacon-patrol

Patrol 17: All healthy


Merge: gt-caih

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


Update harness beads redirect for GGT

Change ~/ai/.beads/redirect from mayor/rigs/gastown/.beads to gastown/mayor/.beads for the GGT directory structure


Design org formula hierarchy pattern

Design how organizational formula hierarchies work.

## Pattern

    company-base.formula.yaml (org-wide policies)
      └── team-frontend.formula.yaml (extends company-base)
           └── project-dashboard.formula.yaml (extends team-frontend)

## Key Questions

1. How are org formulas discovered? (search path? explicit import?)
2. How do aspects merge across levels?
3. Can child override parent aspects?
4. How to handle aspect conflicts?

## Proposed Design

- Formulas use extends: just like today
- Aspect lists merge (child appends to parent)
- Child can exclude parent aspects via "exclude-aspects: [name]"
- Search path: local -> rig -> org (configurable)

## Deliverables

1. Document pattern in docs/formula-inheritance.md
2. Define conflict resolution rules
3. Identify implementation tasks


Add agent abstraction layer to support non-Claude agents

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

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

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


gt mq status: detailed MR view

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

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

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

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


Digest: mol-deacon-patrol

Patrol cycle 4: routine check, all nominal


Add orphaned-attachments doctor check

Add gt doctor check for molecules attached to non-existent agents.

Check: orphaned-attachments
Warning if: Handoff bead exists for agent that no longer has worktree
Fix suggestion: Re-sling to active agent or close molecule