Check own context limit

Check own context limit.

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

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

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

This enables the Deacon to burn and respawn cleanly.


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 95/1023)


Digest: mol-deacon-patrol

Patrol cycle 15: All 3 rigs healthy. No incidents.


Digest: mol-deacon-patrol

Cycle 292: All healthy, no incidents


Crew workers: design documentation and remaining work

## Summary

Crew workers are user-managed persistent workspaces within a rig, distinct from polecats.

## Current Implementation

### Commands (all working)
- `gt crew add <name>` - Create workspace (git clone)
- `gt crew list` - List workspaces with status
- `gt crew at <name>` - Attach to tmux session
- `gt crew remove <name>` - Remove workspace
- `gt crew pristine [<name>]` - git pull + bd sync
- `gt crew refresh <name>` - Context cycle with handoff mail
- `gt crew status [<name>]` - Detailed status
- `gt crew rename <name>` - Rename workspace

### Crew vs Polecat

| Aspect | Crew | Polecat |
|--------|------|---------|
| Lifecycle | User-managed | Witness-managed |
| Scheduling | Manual | `gt spawn` |
| Merge flow | Direct push OK | Integration branch → refinery |
| Garbage collection | Never | Auto on completion |
| Identity | Long-lived (emma, dave) | Ephemeral (Nux, Toast) |

## Known Bugs

- **gt-70b3**: detectSender() doesn't recognize crew workers
- **gt-vdp0**: Crew CLAUDE.md shows Refinery template

## Potential Enhancements

1. Rebase helper in `gt crew pristine` for conflict resolution
2. Cross-rig crew support (crew worker in multiple rigs?)
3. Better mail identity auto-detection for crew


Move disk 4: A → C

Move disk 4 from peg A to peg C. (Move 56/1023)


Request shutdown

Send shutdown request to Witness.
Wait for termination.

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


Digest: mol-deacon-patrol

Patrol 16: Nominal


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.


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 427/1023)


Move disk 3: B → C

Move disk 3 from peg B to peg C. (Move 756/1023)


Move disk 4: B → A

Move disk 4 from peg B to peg A. (Move 184/1023)


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 611/1023)


Digest: mol-deacon-patrol

Patrol #8


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.


Digest: mol-deacon-patrol

Patrol: TRACER BULLET SUCCESS - gt-oiv0 merged to main, furiosa completed and exited cleanly, 0 polecats now, all agents up


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 81/1023)


Update all documentation to remove hook references

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

Update all to reflect pinned beads as the sole propulsion mechanism.


Digest: mol-deacon-patrol

Patrol 4: All clear


Check convoy completion

Check convoy completion status.

Convoys are coordination beads that track multiple issues across rigs. When all tracked issues close, the convoy auto-closes.

**Step 1: Find open convoys**
```bash
bd list --type=convoy --status=open
```

**Step 2: For each open convoy, check tracked issues**
```bash
bd show <convoy-id>
# Look for 'tracks' or 'dependencies' field listing tracked issues
```

**Step 3: If all tracked issues are closed, close the convoy**
```bash
# Check each tracked issue
for issue in tracked_issues:
    bd show <issue-id>
    # If status is open/in_progress, convoy stays open
    # If all are closed (completed, wontfix, etc.), convoy is complete

# Close convoy when all tracked issues are done
bd close <convoy-id> --reason "All tracked issues completed"
```

**Note**: Convoys support cross-prefix tracking (e.g., hq-* convoy can track gt-*, bd-* issues). Use full IDs when checking.


Digest: mol-deacon-patrol

P7: escalation for mayor (wrong-rig work)


gt rig reset --stale: Clear orphaned in_progress items

Reset in_progress issues when their assigned agent no longer exists.

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

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

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

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

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


gt spawn: pasted work assignment needs manual Enter to start

## Problem

After `gt spawn` pastes the work assignment into Claude, the session waits for Enter.

## Current Behavior

1. `gt spawn gastown/Rictus --issue gt-xxx`
2. Session starts, work is pasted
3. Claude shows 'Pasted text #1 +53 lines' but doesn't start
4. Must manually send Enter or attach and press Enter

## Expected

The spawn should send Enter after pasting to kick off the work.

## Related

Same debounce issue as tmux notifications (gt-vnp9).


Merge: valkyrie-1767147773208

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


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


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 327/1023)


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.

## Idle Town Principle

**The Deacon should be silent/invisible when the town is healthy and idle.**

- Skip HEALTH_CHECK nudges when no active work exists
- Sleep 60+ seconds between patrol cycles (longer when idle)
- Let the feed subscription wake agents on actual events
- The daemon (10-minute heartbeat) is the safety net for dead sessions

This prevents flooding idle agents with health checks every few seconds.

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


Move disk 2: B → A

Move disk 2 from peg B to peg A. (Move 586/1023)


Document the Propulsion Principle

Write canonical documentation for the Universal Gas Town Propulsion Principle.

Location: gastown/mayor/rig/docs/propulsion-principle.md

Content:
- The One Rule (hook has work → work happens)
- Why it works (stateless agents, molecule-driven)
- The sling lifecycle diagram
- Agent startup protocol
- Examples and anti-patterns

This is foundational theory-of-operation documentation.


Move disk 2: A → C

Move disk 2 from peg A to peg C. (Move 794/1023)


Digest: mol-deacon-patrol

Patrol 3: Quiet, all healthy


Digest: mol-deacon-patrol

Patrol 43: All healthy, 6 agents pinged


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 179/1023)


Move disk 4: A → C

Move disk 4 from peg A to peg C. (Move 680/1023)


Digest: mol-deacon-patrol

Cycle 11


Move disk 2: B → A

Move disk 2 from peg B to peg A. (Move 82/1023)


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 855/1023)


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.


Digest: mol-deacon-patrol

Patrol 17 complete


Digest: mol-deacon-patrol

P17: stable


Test


Guardrail: Verify commits exist before closing polecat issues

## Updated Approach (ZFC)

The original proposal was for mechanical guardrails in bd close. This contradicts the ZFC principle: all decisions go to models, not code.

## Correct Solution

The verification should happen in **mol-polecat-arm** execute step, not bd close:

In the pre-kill-verify action:
```bash
# Current steps
git status                    # Must be clean
git log origin/main..HEAD     # Check for unpushed
bd show <assigned-issue>      # Verify closed/deferred

# ADD: Verify productive work
git log --oneline --grep='<issue-id>' | head -1
# If no commits AND issue being closed as 'done' → flag for review
```

The agent (Witness) makes the decision. The mol gives it the verification step.

## Why Not Code Guardrails

Polecats have legitimate reasons to close issues without commits:
- Already done (someone else fixed it)
- Deferred (out of scope)
- Escalated (needs human decision)
- Duplicate (merged with another issue)

A code guardrail would block all these. The mol step lets the agent verify AND make the judgment call.

## Implementation

Update mol-polecat-arm execute step to include commit verification for 'done' closures.


Molecule format bridge: convert embedded markdown to child issues

## Problem

Two molecule formats exist:
1. **Old (gastown builtin)**: Steps embedded as markdown in Description field, parsed by ParseMoleculeSteps()
2. **New (bd mol)**: Steps as child issues in proper beads DAG

The daemon's InstantiateMolecule() uses the old format. bd mol spawn uses the new format.

## Options

### Option A: Require child issues (new format)
- Update daemon to use bd mol spawn instead of InstantiateMolecule
- Deprecate embedded markdown format
- Pro: One format, simpler
- Con: Breaking change for existing molecules

### Option B: Build a bridge
- In InstantiateMolecule, detect format:
  - If Description has ## Step: markers → parse to child issues
  - If issue has children with template label → use directly
- Convert old format to new format on instantiation
- Pro: Backward compatible
- Con: More complexity

## Recommendation

Option B - build the bridge. On instantiation, convert markdown steps to child issues. This unifies execution while preserving authoring flexibility.


Merge: toast-mjxb88ev

branch: polecat/toast-mjxb88ev
target: main
source_issue: toast-mjxb88ev
rig: gastown
agent_bead: gt-gastown-polecat-toast


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


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 893/1023)


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 287/1023)


mol-deacon-patrol


Ephemeral Polecat Merge Workflow: Rebase-as-Work Architecture

## Vision

Polecats are truly ephemeral: done at MR submission, recyclable immediately. Conflict resolution becomes tracked work in the ledger.

## Current State

- Polecats submit MRs and wait around
- Refinery sends 'rebase needed' mail back to polecats
- Polecats are blocked on MQ throughput
- No priority scheduling in MQ

## Target State

### Polecat Lifecycle
```
Polecat spawns → Does feature work → Submits MR → DONE (recyclable)
```

### Refinery Workflow
```
MR enters queue
    ↓
Priority scoring (convoy age, epic priority, retry count)
    ↓
Attempt mechanical rebase
    ├─→ Success: Run tests, merge
    └─→ Conflicts: Create conflict-resolution task, skip MR
```

### Conflict Resolution
- New bead: 'Resolve conflicts for MR gt-xxx'
- Dispatched to any available polecat
- Fresh polecat: clone, read MR/issue, resolve, force-push
- MR re-enters queue with priority boost

## Priority Objective Function

```go
score := baseScore
score += convoyAgeFactor * hoursOld(convoy)
score += epicPriorityFactor * (5 - epicPriority)  // P0=5pts, P4=1pt
score += retryPenalty * conflictRetryCount        // Prevent thrashing
```

Factors tunable. Refinery processes highest-score first.

## Key Principles

- **ZFC**: Refinery automates automatable, agents judge judgeable
- **Nondeterministic Idempotence**: Different polecat, same outcome
- **HOP-aligned**: Conflict resolution is tracked work with skill attribution
- **Convoy pressure**: Older convoys get priority, preventing starvation

## Success Criteria

- [ ] Polecats recyclable immediately after MR submit
- [ ] Mechanical rebases handled by refinery (no dispatch)
- [ ] Conflicts create dispatchable tasks
- [ ] MQ priority based on convoy/epic/retry
- [ ] Documentation updated
- [ ] Formulas updated (refinery, polecat)


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)


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)


Move disk 4: C → B

Move disk 4 from peg C to peg B. (Move 840/1023)


Digest: mol-deacon-patrol

Patrol 150 complete


Merge: slit-mjw18vx8

branch: polecat/slit-mjw18vx8
target: main
source_issue: slit-mjw18vx8
rig: gastown
agent_bead: gt-gastown-polecat-slit


Merge: nux-1767073376184

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


Digest: mol-deacon-patrol

Patrol 145: All healthy


Merge: gt-svi.1

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


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 245/1023)


Digest: mol-deacon-patrol

Patrol 3: Quick cycle, all quiet


Move disk 3: B → C

Move disk 3 from peg B to peg C. (Move 588/1023)


gt sling: '.' target should resolve to current agent identity, not literal dot

## Problem

When slinging from a crew directory with "." as target, the assignee becomes
literal "." instead of resolving to the current agent.

```bash
cd ~/gt/beads/crew/dave
gt sling bd-tksk .
# Result: assignee="." instead of "beads/crew/dave"
```

## Expected

"." should resolve to the current agent identity based on cwd, like git does
with "." meaning current directory.

## Impact

Work is not properly assigned, agent cannot find hooked work.

## Fix

In sling.go, resolve "." target to current agent identity before updating.


orphan-check

Find abandoned work. Check for in_progress issues with no active agent.


Move disk 2: A → C

Move disk 2 from peg A to peg C. (Move 302/1023)


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


Create rig .runtime/ for rig runtime state

Move ephemeral rig state to .runtime/:
- Create <rig>/.runtime/ directory
- Move .gastown/witness.json → .runtime/witness.json
- Move .gastown/refinery.json → .runtime/refinery.json
- Move .gastown/namepool.json (state portion) → .runtime/namepool-state.json
- Update .gitignore to ignore .runtime/
- Update all code references


Move disk 3: A → B

Move disk 3 from peg A to peg B. (Move 508/1023)


Digest: mol-deacon-patrol

Patrol 15: All healthy


Digest: mol-deacon-patrol

Patrol 13: Quick cycle, all quiet


gt-gastown-polecat-bullet

gt-gastown-polecat-bullet

role_type: polecat
rig: gastown
agent_state: spawning
hook_bead: gt-q73h3
role_bead: gt-polecat-role
cleanup_status: null
active_mr: gt-5bbc0
notification_level: null


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 969/1023)


Digest: mol-deacon-patrol

Patrol cycle 1: All 3 rigs healthy, 6 agents pinged, no incidents, inbox clean


Digest: mol-deacon-patrol

Patrol cycle 2: Witnessed escalation re: 14 polecats with unpushed work. All agents healthy. No pending work.


Digest: mol-deacon-patrol

Patrol 9: healthy


Session ended: gt-gastown-slit


CLI: gt swarm commands (create, status, list, land)

CLI commands for swarm management.

## Commands

### gt swarm create
Create a new swarm in a rig.

```
gt swarm create <rig> --title "Fix all P1 bugs" \
  --task gt-abc --task gt-def \
  --worker Toast --worker Nux
```

Options:
- --title: Swarm title (required)
- --task: Beads issue IDs to include (repeatable)
- --worker: Polecat names (repeatable, or --workers=3 to auto-assign)
- --start: Start swarm immediately after creation

### gt swarm status <swarm-id>
Show swarm status and progress.

Output:
- Swarm metadata (title, created, workers)
- Task status (pending/in_progress/completed/merged)
- Integration branch status
- Overall progress percentage

### gt swarm list [rig]
List swarms, optionally filtered by rig or status.

```
gt swarm list --status=active
gt swarm list gastown --status=landed
```

### gt swarm land <swarm-id>
Trigger manual landing (normally automatic).

### gt swarm cancel <swarm-id>
Cancel a swarm in progress.

## Output Format

Support --json for machine-readable output.

## Dependencies

Needs: gt-kmn.1 (types), gt-kmn.2 (manager)


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

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

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


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 4: all quiet


Move disk 3: B → C

Move disk 3 from peg B to peg C. (Move 660/1023)


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 217/1023)


Move disk 2: C → B

Move disk 2 from peg C to peg B. (Move 450/1023)


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 14: Quiet


Detect cleanup needs

**DETECT ONLY** - Check if cleanup is needed and dispatch to dog.

**Step 1: Preview cleanup needs**
```bash
gt doctor -v
# Check output for issues that need cleaning
```

**Step 2: If cleanup needed, dispatch to dog**
```bash
# Sling session-gc formula to an idle dog
gt sling mol-session-gc deacon/dogs --var mode=conservative
```

**Important:** Do NOT run `gt doctor --fix` inline. Dogs handle cleanup.
The Deacon stays lightweight - detection only.

**Step 3: If nothing to clean**
Skip dispatch - system is healthy.

**Cleanup types (for reference):**
- orphan-sessions: Dead tmux sessions
- orphan-processes: Orphaned Claude processes
- wisp-gc: Old wisps past retention

**Exit criteria:** Session GC dispatched to dog (if needed).


Merge: slit-1767080431456

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


Merge: dementus-mjw46vz4

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


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 535/1023)


Move disk 3: A → C

Move the largest disk from peg A to peg C.


Digest: mol-deacon-patrol

Patrol 13: All agents healthy


Extend auto-continue to polecats (not just crew)

gt prime currently only outputs AUTO-CONTINUE MODE for crew workers.
Polecats with attached work should also auto-continue.

## Current behavior
outputCrewAttachmentStatus() in prime.go:
- Only runs for RoleCrew
- Outputs '→ AUTO-CONTINUE MODE' when attached work detected

## Desired behavior
- Rename to outputAttachmentStatus() or similar
- Run for RoleCrew AND RolePolecat
- Same directive: if attachment exists, work immediately

## The Propulsion Principle
'If you find something on your hook, YOU RUN IT.'

This applies to ALL workers, not just crew.

## Implementation
1. Extend role check in outputCrewAttachmentStatus()
2. Adjust assignee detection for polecats vs crew
3. Test with both worker types


Digest: mol-deacon-patrol

Patrol 7


Digest: mol-deacon-patrol

Patrol 19 complete


Digest: mol-deacon-patrol

Patrol 6: All healthy


Daemon/Boot/Deacon Watchdog Chain: Fix session lifecycle management

## Summary

The watchdog chain (Daemon → Boot → Deacon → Witness/Refinery) has multiple bugs preventing auto-recovery of dead agents:

1. Boot spawns in wrong session
2. Daemon can't kill zombie sessions
3. Binary/process age mismatch goes undetected
4. Status display doesn't reconcile bead vs tmux state
5. Start commands don't have 'ensure' semantics

## Impact

When witness/refinery die, they stay dead until manual intervention. The entire purpose of the Deacon (health monitoring) is defeated.

## Root Cause

Session lifecycle management lacks 'ensure running' semantics. The code assumes:
- If session doesn't exist → create it
- If session exists → it's healthy

Reality:
- Sessions can be zombies (tmux alive, Claude dead)
- Bead state and tmux state can diverge
- Daemon process can run old code

## Child Issues

- gt-sgzsb: Boot session confusion (P1)
- gt-j1i0r: Zombie session blocking (P1)
- gt-d48f2: Binary age detection (P2)
- gt-doih4: Status bead/tmux mismatch (P2)
- gt-ekc5u: Start 'ensure' semantics (P2)
- gt-1847v: Boot/Deacon documentation (P2)

## Success Criteria

1. Dead witness/refinery auto-restart within 5 minutes
2. `gt status` accurately reflects session health
3. Daemon warns if running old code
4. Boot/Deacon lifecycle is clear and documented


mol-deacon-patrol


Move disk 3: A → B

Move disk 3 from peg A to peg B. (Move 172/1023)


Refinery background daemon mode

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

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

## Requirements

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

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

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

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

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

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

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


Merge: gt-svdsy

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


Digest: mol-deacon-patrol

Patrol 4: quiet, health OK


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.


Move disk 3: B → C

Move disk 3 from peg B to peg C. (Move 396/1023)


Phase 2: Quiescent agents (Witness, Refinery)

Enable Witness and Refinery to sleep until triggered.

## Scope
- Wake triggers: gt spawn, MR submit, mail, Deacon ping
- Wake SLA: <1 minute
- mol-witness-patrol and mol-refinery-patrol definitions
- Quiescent entry/exit protocol

## Implementation
- Kill tmux session on quiescent entry
- Preserve sandbox (worktree, state files)
- Restart session on wake trigger
- Agent checks pinned, spawns default patrol if naked

Depends: Phase 1 complete


Merge: gt-pyqv

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


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

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

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


Automatic spawn for ready work

Auto-spawn polecats for ready work:

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

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

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


Digest: mol-deacon-patrol

Cycle 4: All healthy


Merge: warboy-mjwjbptd

branch: polecat/warboy-mjwjbptd
target: main
source_issue: warboy-mjwjbptd
rig: gastown
agent_bead: gt-gastown-polecat-warboy


Digest: mol-deacon-patrol

Patrol 8: Routine


Digest: mol-deacon-patrol

Patrol 107: All healthy


attached_args: Refactor runStart into focused helper functions

Break up the 126-line runStart function in start.go.

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

## Current structure (lines 129-255)
runStart does:
1. Crew path detection
2. Workspace verification
3. Mayor session start
4. Deacon session start
5. Optional rig agent startup
6. Configured crew auto-start

## Refactoring plan
Extract these helpers:
```go
func startMayorSession(townRoot string, verbose bool) error
func startDeaconSession(townRoot string) error
func startRigAgents(townRoot string, rigs []string) error
func startConfiguredCrew(townRoot string, config *TownConfig) error
```

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


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 169/1023)


mol-deacon-patrol


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

Patrol 16


Update polecat template: Use gt done --exit

## Task

Update `templates/polecat-CLAUDE.md` to tell polecats to use `gt done --exit` instead of `gt done` + waiting.

## Changes

1. Update completion checklist:
```
OLD:
[ ] 6. Run gt done:       gt done
[ ] 7. WAIT:              Witness will kill your session

NEW:
[ ] 6. Exit session:      gt done --exit
```

2. Remove "WAIT" step - no more waiting

3. Update explanation of gt done to mention --exit flag

## Dependencies

- gt-lynar (gt done --exit feature)


Add gt rig rename command

Allow renaming a rig after creation. Similar to gt crew rename, this should:

- Update the directory name
- Update mayor/rigs.json entry
- Update the rig's config.json
- Handle running agents gracefully (require shutdown first or --force)

Use case: User creates a rig, later wants to change its name for better organization.


Silent error handling with _ = err patterns

Multiple locations intentionally ignore errors with comments like 'Ignore errors'. While some are valid (best-effort operations), others should be audited:

- witness/manager.go:542 - Ignores cmd.Run() error
- refinery/manager.go:411, 438, 509 - Ignores loadState and git pull errors
- swarm/manager.go:47 - Ignores getGitHead error
- polecat/manager.go:69, 284 - Ignores pool.Load and DeleteBranch errors
- swarm/integration.go:101, 137 - Ignores git pull and push errors

Each should be evaluated: log it, handle it, or confirm ignoring is intentional.


Digest: mol-deacon-patrol

Patrol cycle 79: All healthy


Digest: mol-deacon-patrol

Patrol 19: all clear


Digest: mol-deacon-patrol

Patrol 2: 1 mail archived, all agents healthy, 1 orphan fixed


Digest: mol-deacon-patrol

Patrol 18: All healthy


Digest: mol-deacon-patrol

Patrol 12: all healthy


Day 1.4b: Create Deacon agent bead

Create gt-deacon agent bead for the Deacon (daemon beacon).

The Deacon is a separate Claude Code agent from Mayor:
- Receives the ONLY mechanical daemon heartbeats in the system
- Runs town plugins, cleanup, and monitoring
- Has own top-level directory: ~/gt/deacon/
- No rig preassignment (town-level, like Mayor)
- role_type: deacon

Agent bead structure:
```yaml
id: gt-deacon
type: agent
entity_platform: gastown
entity_org: steveyegge
entity_id: deacon
hook_bead: null  # or current patrol wisp
role_bead: gt-deacon-role
state: idle
role_type: deacon
```

Must be created alongside mayor, witness, refinery agent beads.


Move disk 3: A → B

Move disk 3 from peg A to peg B. (Move 340/1023)


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 917/1023)


Witness patrol wisp with polecat leases

Witness should maintain a rolling patrol wisp that tracks active polecats:

```
wisp-witness-patrol
├── lease: furiosa (boot → working → done)
├── lease: nux (working)
└── lease: slit (done, closed)
```

When POLECAT_STARTED arrives:
- bd mol bond mol-polecat-lease wisp-patrol --var polecat=X

Patrol loop iterates leases:
- gt peek $polecat
- If idle: gt nudge
- If shutdown received: close lease

When all leases closed:
- bd mol squash wisp-xxx --summary='N polecats processed'

Requires mol-polecat-lease proto definition.


Digest: mol-deacon-patrol

Patrol cycle 1: All 6 agents healthy, inbox clean, no incidents


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

Patrol 13: All healthy


Digest: mol-deacon-patrol

Patrol #15: Stable


Digest: mol-deacon-patrol

Patrol cycle 12: All healthy


Move disk 2: C → B

Move disk 2 from peg C to peg B. (Move 1014/1023)


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 395/1023)


Digest: mol-deacon-patrol

Patrol 8: routine, 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)


Task B: First feature


Digest: mol-deacon-patrol

Patrol 120: All healthy - session complete


Move disk 3: B → C

Move disk 3 from peg B to peg C. (Move 228/1023)


Digest: mol-deacon-patrol

Patrol 94: All healthy


Digest: mol-deacon-patrol

Patrol 18: routine


Move disk 2: B → A

Move disk 2 from peg B to peg A. (Move 238/1023)


Merge: gt-si8rq.4

branch: polecat/rictus-mjxaq6fm
target: main
source_issue: gt-si8rq.4
rig: gastown
agent_bead: gt-gastown-polecat-rictus


Mol Mall: Formula library and sharing

## Mol Mall

Library/marketplace for formula molecules.

### Features
- Browse available formulas
- Formula metadata (author, version, inputs, outputs)
- Custom formula creation
- Formula versioning
- Cross-town formula sharing (future)

### Commands
```bash
gt formula list              # List available formulas
gt formula show <name>       # Formula details
gt formula create <name>     # Create new formula
gt formula edit <name>       # Modify formula
gt formula export <name>     # Export for sharing
gt formula import <file>     # Import shared formula
```

### Storage
- Formulas as beads in town-level .beads/
- type: formula
- Structured schema for legs, synthesis, inputs

### Future: Public Mol Mall
- Central registry of community formulas
- gt formula search 'code review'
- gt formula install community/security-audit


Digest: mol-deacon-patrol

Patrol cycle 4: All 6 agents healthy


gt-witness-gastown

gt-witness-gastown

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


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.


Day 2.2: Remove PID/tmux state inference

Remove ZFC violations where Go infers state from signals:
- No more PID file checks
- No more tmux session parsing
- No more branch pattern analysis

Daemon becomes pure transport layer.

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

Parent: gt-d0jqp

Removed PID/tmux state inference from refinery Status(), witness Status(), daemon ensureDeaconRunning(), and daemon pokeDeacon(). Daemon now trusts agent bead state per ZFC. Removed 78 lines of inference code.


mol-deacon-patrol


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 12: All agents healthy


Move disk 3: A → B

Move disk 3 from peg A to peg B. (Move 4/1023)


Digest: mol-deacon-patrol

Patrol 219: All nominal


Merge: rictus-mjxc967h

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


Digest: mol-deacon-patrol

Patrol cycle 8


Digest: mol-deacon-patrol

Patrol 5: all clear


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 803/1023)


Move disk 2: C → B

Move disk 2 from peg C to peg B. (Move 474/1023)


Inline step expansion (Step.Expand field)

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

## Current State

Steps can declare inline expansion:

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

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

## Difference from compose.expand

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

## Implementation

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

## Files

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

## Depends On

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


Merge: furiosa-mjxkrt01

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


Polecat-Witness protocol handlers

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


Digest: mol-deacon-patrol

Patrol 11: routine, all healthy


Digest: mol-deacon-patrol

Patrol 86: All healthy


Digest: mol-deacon-patrol

Patrol 17: All clear


Merge: gt-rana.2

branch: polecat/nux
target: main
source_issue: gt-rana.2
rig: gastown


Digest: mol-deacon-patrol

Patrol #6: All quiet, 1 dog idle.


Move disk 2: C → B

Move disk 2 from peg C to peg B. (Move 534/1023)


Polecat template: clarify work status commands (bd list vs gt mol status)

Template shows 'bd list --status=in_progress' for checking work, but polecats should probably use 'gt mol status' instead (or in addition). Clarify the right approach for polecats to check their current work assignment.


Digest: mol-deacon-patrol

Patrol cycle 5: all healthy


Digest: mol-deacon-patrol

Patrol 82: 3 rigs healthy, 6 agents pinged, no incidents


Deacon event/timer callbacks: reactive triggers

Implement event-triggered and timer-based callbacks in the Deacon.

## Timer Callbacks

Agents can schedule future wakes by mailing the Deacon:
```
Subject: TIMER: gastown/witness wake at 2025-12-20T16:00:00Z
Body: Please nudge me - I'm waiting for external dependency
```

Deacon processing:
1. Parse timer mail
2. Check if time has passed
3. If yes: mail the agent "WAKE: Timer fired"
4. Close the timer mail

## Event Subscriptions

Plugins/agents can subscribe to events:
```yaml
# In kernel bead or plugin config
event_triggers:
  - event: "issue.created"
    action: mail
    target: "plugins/work-oracle"

  - event: "mr.submitted"
    action: run
    plugin: "review-oracle"

  - event: "agent.stuck"
    action: escalate
    target: human
```

## Event Sources

Events come through mail to deacon/:
```
Subject: EVENT: issue.created gt-abc123
Body: New issue created in gastown rig
```

## Event Types

Initial event types:
- `issue.created` - New bead created
- `issue.closed` - Bead closed
- `mr.submitted` - Merge request submitted
- `mr.merged` - Merge request merged
- `agent.stuck` - Agent appears stuck (from health scan)
- `agent.failed` - Agent remediation failed

## Event Processing

During events step:
1. Read event mail from inbox
2. Match against subscriptions
3. Execute actions:
   - `mail`: Send mail to target
   - `run`: Execute plugin inline
   - `spawn`: Spawn subagent for plugin
   - `escalate`: Mail human

## Relation to Mail

Mail IS the event bus. No separate event system.
Events are just specially-formatted mail to deacon/.


Move disk 2: C → B

Move disk 2 from peg C to peg B. (Move 42/1023)


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 547/1023)


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.


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 495/1023)


Digest: mol-deacon-patrol

Patrol #9


Digest: mol-deacon-patrol

Patrol 6: quiet


Digest: mol-deacon-patrol

Patrol 49: All healthy


Patrol agents subscribe to bd activity --follow

Each patrol agent (Deacon, Witness, Refinery) should run bd activity --follow as a background process. Any output line is a wake signal. This is the primary wake mechanism.

Implementation:
1. Add feed subscription to await-signal step logic
2. Agent starts bd activity --follow on entering await-signal
3. Any line of output breaks the wait immediately
4. Timeout still applies as safety net

See patrol-system-design.md for full specification.


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 505/1023)


Move disk 3: C → A

Move disk 3 from peg C to peg A. (Move 668/1023)


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 809/1023)


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


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 683/1023)


Digest: mol-deacon-patrol

Patrol 5: routine


Digest: mol-deacon-patrol

Patrol 1: All agents healthy (2W 2R). 1 orphan (gt-mol-aux assigned to dead polecat). No incidents.


Digest: mol-deacon-patrol

Patrol 80: Routine, completing 20-cycle handoff


Digest: mol-deacon-patrol

Patrol cycle 8: All 3 rigs healthy. No incidents.


Check Witness and Refinery health

Check Witness and Refinery health for each rig.

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

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

# Health ping (clears backoff as side effect)
gt nudge <rig>/witness 'HEALTH_CHECK from deacon'
gt nudge <rig>/refinery 'HEALTH_CHECK from deacon'
```

**Health Ping Benefit**: The nudge commands serve dual purposes:
1. **Liveness verification** - Agent responds to prove it's alive
2. **Backoff reset** - Any nudge resets agent's backoff to base interval

This ensures patrol agents remain responsive even during quiet periods when the
feed has no mutations. Deacon patrols every ~1-2 minutes, so maximum backoff
is bounded by the ping interval.

**Signals to assess:**

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

**Tracking unresponsive cycles:**

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

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

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

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

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

Reset unresponsive_cycles to 0 when component responds normally.


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 819/1023)


attached_args: Standardize warning output to use style.Warning

Unify warning message formatting across codebase.

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

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

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

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


Merge: capable-mjxm03qy

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


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

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

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

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

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


Digest: mol-deacon-patrol

Patrol cycle 55: All healthy


Move disk 2: B → A

Move disk 2 from peg B to peg A. (Move 946/1023)


generate-summary

Generate a summary for molecule squash.
File any remaining work as issues.

Document any important context for the squash digest.

Depends: submit-merge


Digest: mol-deacon-patrol

Patrol 16: nominal


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 165/1023)


Digest: mol-deacon-patrol

Test patrol cycle - verifying wisp flow


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 cycle 20: all healthy, handing off


Digest: mol-deacon-patrol

Patrol cycle 34: All healthy, no incidents


Define meta-formula YAML schema

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

## Proposed Schema

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

## Key Decisions

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

## Deliverables

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


Digest: mol-deacon-patrol

Patrol 145 complete: All healthy


Digest: mol-deacon-patrol

Patrol 5: quiet


Digest: mol-deacon-patrol

Patrol 7: 3 rigs healthy


Digest: mol-deacon-patrol

Patrol cycle 12: All healthy.


Digest: mol-deacon-patrol

Patrol cycle 14: nominal


Beads schema: Add overseer field for team scenarios

For future team support: overseer field identifies human owner when multiple humans share a workspace. For now, email in git commits serves this purpose. Low priority until teams needed.


Digest: mol-deacon-patrol

Patrol #2: All systems nominal, 3 convoys active, no incidents


Polecat sessions terminate unexpectedly during work

During swarm bd-784c, polecat sessions (Toast, Nux) terminated mid-task without completing their work.

**Observed:**
- Polecats were actively working (edits in progress, tests running)
- Sessions suddenly showed 'not running' in gt polecat status
- tmux sessions existed but were empty/reset
- Work was partially complete (some commits made, issue still open)

**Impact:**
- Lost work progress
- Required manual intervention to restart
- Unclear if work was saved/committed

**Suggestion:**
- Add session health monitoring
- Auto-restart on unexpected termination
- Log session exit reasons


Digest: mol-deacon-patrol

Patrol 18: All agents healthy


mol-deacon-patrol

Mayor's daemon patrol loop.

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

## Second-Order Monitoring

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

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


Digest: mol-deacon-patrol

Patrol 17: all quiet


Add Gas Town theory of operation to all role primings

All roles (Mayor, Witness, Refinery, Polecat) should get basic GT architecture context: harness, rigs, agents, mail, beads workflow


Proto marketplace: shareable molecule templates

Enable sharing protos between Gas Town installations.

## Vision

A public registry of protos that users can pull and use:

```bash
gt proto search "code review"
gt proto install gastown/code-review
gt sling code-review gastown/Toast --wisp
```

## Registry Design

### Local Catalog
`~/gt/molecules/` - user-defined and installed protos

### Remote Registry
`registry.gastown.dev/protos/` (future)
- Browse online catalog
- Version-controlled protos
- Rating/reviews
- Usage statistics

## Proto Package Format

```
gastown-code-review-1.0.0/
├── PROTO.md          # Main proto definition
├── README.md         # Usage documentation
├── LICENSE           # Usage terms
└── plugins/          # For pluggable molecules
    ├── security/
    └── performance/
```

## Commands

```bash
gt proto list              # Show installed protos
gt proto search <query>    # Search registry
gt proto install <name>    # Install from registry
gt proto publish <path>    # Publish to registry
gt proto update            # Update all installed
```

## For Now

Start with local catalog. Marketplace is future phase.
Ensure proto format is registry-compatible from the start.


Merge: slit-1767075898886

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


Beads witness sends HEALTH_OK to mayor instead of deacon

The beads/witness is responding to deacon health checks by sending to 'mayor/deacon' or 'mayor/' instead of 'deacon/'.

Current behavior:
gt mail send mayor/deacon -s "HEALTH_OK" ...

Expected behavior:
gt mail send deacon/ -s "HEALTH_OK" ...

The gastown witness was fixed and now correctly sends to deacon/. The beads witness needs the same fix.


Digest: mol-deacon-patrol

Patrol 151: All healthy


Rule of Five expansion template

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

First example of a macro-style expansion proto.


mol-deacon-patrol


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


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


Remove daemon heartbeat staleness logic

**Files:** daemon/daemon.go (lines 272-325), daemon/backoff.go

**Current behavior:**
- Hardcoded 2min/5min thresholds classify agent health
- Exponential backoff decides nudge intervals
- Go decides "is this agent stuck?" based on timestamps

**Fix:**
- Remove staleness classification from Go
- Remove backoff algorithm
- Daemon becomes pure message router
- Deacon molecule step checks agent health instead

**Lines to remove/refactor:**
- daemon.go:272-274 (staleness thresholds)
- daemon.go:282-295 (poke decision logic)
- daemon.go:317-325 (conditional nudging)
- backoff.go:108-120 (backoff calculations)


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 283/1023)


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 753/1023)


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 597/1023)


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 107/1023)


Digest: mol-deacon-patrol

Patrol 3: Quiet, all agents healthy


gt molecule current: Show what agent should be working on

Query what an agent identity is supposed to be working on via breadcrumb trail.

## Command
```bash
gt molecule current <identity>
gt molecule current gastown/furiosa
gt molecule current deacon
```

## Logic
1. Find handoff bead for identity (pinned bead titled "<role> Handoff")
2. Parse attachment field → molecule ID
3. If no attachment → "naked" (no active molecule)
4. If attached → load molecule, find current step:
   - bd ready --parent=<mol-id> → next unblocked step
   - Or first in_progress step

## Output
```
Identity: gastown/furiosa
Handoff:  gt-8v2 (Furiosa Handoff)
Molecule: gt-mol-abc (mol-polecat-work)
Progress: 3/8 steps complete
Current:  gt-mol-abc.4 - verify-tests
```

Or if naked:
```
Identity: gastown/angharad
Handoff:  gt-9x1 (Angharad Handoff)
Molecule: (none attached)
Status:   naked - awaiting work assignment
```

## Use Cases
- Mayor checking what polecats are doing
- Witness verifying polecat progress
- Debug: "why isnt this polecat working?"
- Deacon patrol: track all agent states


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

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

## Variables

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

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

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

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

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

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

Calculate: minutes since last activity.
Needs: capture

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

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

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

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

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

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

Record decision and rationale.
Needs: load-history

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

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

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

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

**none**: No action needed.

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

## Output

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

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


Digest: mol-deacon-patrol

Patrol 93: All healthy


Digest: mol-deacon-patrol

Patrol 4: all clear


Agent parking and resuming

Implement gt park and gt resume commands. Agent parks work on a gate, state persists across sessions. When gate condition met, Deacon sends wake mail. Agent resumes from parked state. Critical for async workflows.


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

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

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

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

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


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

## Summary

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

## Current Behavior

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

## Desired Behavior

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

## Implementation Notes

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

## Edge Cases

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


Convoy list UI: numbered shortcuts + expandable tree view

Improve gt convoy list UI:

1. Add numbered shortcuts so users can type 'gt convoy status 1' instead of full ID
2. Consider charmbracelet TUI (bubbletea/gum/lipgloss) for expandable tree view
3. Design decision: simple numbered list vs interactive TUI vs hybrid

Current output shows convoy IDs that are hard to type:
  🚚 hq-cv-7x3ig: Witness nuke fix ●
  🚚 hq-cv-eyxnc: Hanoi Demo Launch Prep ●


Fix template syntax in code-review.formula.toml (Handlebars vs Go)

code-review.formula.toml uses Handlebars syntax ({{#if}}, {{#each}}) but Go text/template uses {{if}}, {{range}}. Either switch to Go template syntax or implement a Handlebars-compatible template engine.


Find abandoned work

Find abandoned work.

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

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

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


Move disk 6: B → A

Move disk 6 from peg B to peg A. (Move 352/1023)


Doctor check: Zombie session cleanup

Detect and clean up zombie tmux sessions via gt doctor.

## Problem

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

## Checks

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

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

## Output

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

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

## Auto-Fix (--fix flag)

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

## Safety

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


Move disk 2: C → B

Move disk 2 from peg C to peg B. (Move 306/1023)


gt sling to town roles should use Town beads

## Problem

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

## Solution

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

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

## Tested

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


Fix golangci-lint errcheck warnings (~160 issues)

Running golangci-lint shows ~160 errcheck warnings for unchecked error returns.

Common patterns:
- t.SetEnvironment() return values
- os.WriteFile(), os.RemoveAll()
- MarkFlagRequired() on cobra commands
- Various manager methods

Run: golangci-lint run ./...

Could batch fix with:
1. Add explicit _ = for intentionally ignored errors
2. Handle errors properly where they matter
3. Consider adding //nolint:errcheck for cobra flag setup


Move disk 2: C → B

Move disk 2 from peg C to peg B. (Move 678/1023)


Digest: mol-deacon-patrol

Patrol 96: All healthy


Move disk 3: C → A

Move disk 3 from peg C to peg A. (Move 140/1023)


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.


Test shiny formula end-to-end

Test the shiny (engineer-in-box) formula:

1. **Create issue with formula**:
```bash
gt sling shiny --var feature="Add hello world endpoint" gastown
```

2. **Watch polecat execute shiny steps**:
   - design: Architecture thinking
   - implement: Write the code
   - review: Self-review
   - test: Run tests
   - submit: Create PR

3. **Verify output**:
   - Design notes in issue/commit
   - Implementation matches design
   - Tests pass
   - PR created and ready for merge

Success: A reviewed, tested PR from a single command.


Digest: mol-deacon-patrol

Patrol 18: All clear


Merge: slit-mjtj9dc8

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


Digest: mol-deacon-patrol

Patrol 10: All services healthy


Digest: mol-deacon-patrol

Patrol 16: Quiet


Digest: mol-deacon-patrol

Patrol cycle 16: All healthy


mol-deacon-patrol


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 503/1023)


CloudRunOutpost: Basic implementation

## Overview

Basic CloudRunOutpost implementation. Persistent connections and cost tracking are separate tasks.

## Implementation

```go
type CloudRunOutpost struct {
    name        string
    project     string
    region      string
    service     string
    maxWorkers  int
    client      *WorkClient
    workers     map[string]*CloudRunWorker
    mu          sync.RWMutex
}

func NewCloudRunOutpost(cfg OutpostConfig) (*CloudRunOutpost, error) {
    serviceURL := fmt.Sprintf(
        "https://%s-%s.a.run.app",
        cfg.Service, cfg.Region,
    )
    return &CloudRunOutpost{
        name:       cfg.Name,
        project:    cfg.Project,
        region:     cfg.Region,
        service:    cfg.Service,
        maxWorkers: cfg.MaxWorkers,
        client:     NewWorkClient(serviceURL),
        workers:    make(map[string]*CloudRunWorker),
    }, nil
}
```

## Spawn

```go
func (o *CloudRunOutpost) Spawn(issue string, cfg WorkerConfig) (Worker, error) {
    req := WorkRequest{
        IssueID: issue,
        Rig:     RigConfig{URL: cfg.RigURL, Branch: cfg.GitBranch},
        Beads:   BeadsConfig{URL: cfg.BeadsURL, Branch: "beads-sync"},
        Branch:  "polecat/" + issue,
    }

    events, err := o.client.DispatchWork(context.Background(), req)
    if err != nil {
        return nil, err
    }

    worker := &CloudRunWorker{
        id:      uuid.New().String(),
        outpost: o.name,
        issue:   issue,
        events:  events,
        status:  WorkerStatusWorking,
    }

    o.mu.Lock()
    o.workers[worker.id] = worker
    o.mu.Unlock()

    go worker.monitor()
    return worker, nil
}
```

## CloudRunWorker

```go
type CloudRunWorker struct {
    id      string
    outpost string
    issue   string
    status  WorkerStatus
    events  <-chan WorkEvent
    logs    []string
}

func (w *CloudRunWorker) Attach() error {
    return errors.New("Cloud Run workers do not support attach")
}

func (w *CloudRunWorker) Logs() (io.Reader, error) {
    return strings.NewReader(strings.Join(w.logs, "\n")), nil
}
```

## Files

- `internal/outpost/cloudrun.go`

## Dependencies

Depends on: gt-9a2.1 (interfaces), gt-9a2.12 (HTTP client)
Blocks: gt-9a2.13 (persistent connections), gt-9a2.14 (cost tracking)


Digest: mol-deacon-patrol

Patrol 19: routine


Digest: mol-deacon-patrol

Patrol cycle 15: All healthy


BUG: gt sling updates bead status but doesn't create .hook file

gt sling updates the bead to status=hooked but doesn't actually create the .hook file in the polecat directory.

## Symptom
- gt sling reports success
- Bead shows status=hooked, assignee set
- Polecat's .hook file doesn't exist
- gt hook shows empty

## Root Cause
gt sling updates bead state via bd commands but never writes to polecats/<name>/.hook

## Fix
After updating bead status, write the bead ID to the .hook file:
```go
hookPath := filepath.Join(polecatDir, ".hook")
os.WriteFile(hookPath, []byte(beadID), 0644)
```

## Related
This is blocking gt-zqt4d dispatch to splendid.


Deacon Patrol

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


Move disk 5: B → C

Move disk 5 from peg B to peg C. (Move 528/1023)


Merge: furiosa-mjxlt6tk

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


Digest: mol-deacon-patrol

Cycle 6: quiet


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 365/1023)


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 919/1023)


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

Patrol 19


Move disk 3: A → B

Move disk 3 from peg A to peg B. (Move 388/1023)


Messaging infrastructure: lists, queues, channels

Multi-recipient and broadcast messaging for Gas Town.

## Phases

**Phase 1 - Foundation**: Config directory (gt-i6jvc)
**Phase 1 - Core**: list:, queue:, @group (requires agent-as-bead for @group)
**Phase 2 - Channels**: announce:, #channel, gt channel publish

## Addressing Syntax

| Prefix | Resolution | Storage | Claim? |
|--------|------------|---------|--------|
| `@group` | Dynamic (bead queries) | Fan-out (N copies) | No |
| `list:name` | Static config | Fan-out (N copies) | No |
| `queue:name` | Static config | Shared (1 copy) | Yes |
| `announce:name` | Static config | Shared (1 copy) | No |
| `#channel` | Dynamic (tmux) | None (ephemeral) | No |

## Alignment with Agent-as-Bead

@group resolution now uses agent bead queries:
- @rig/gastown → bd list --type=agent --rig=gastown
- @witnesses → bd list --type=agent --role_type=witness

This replaces the originally planned filesystem scanner (gt-xo05b, now closed).

## Independent Work

Config-based features (list:, queue:, announce:) can proceed without agent identity.
Only @group requires the agent schema from Pillar 1.


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 391/1023)


Merge: gt-5af.1

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


Digest: mol-deacon-patrol

Patrol 141 complete: All agents healthy, no incidents, no orphans


Plugin system: subprocess JSON protocol

Language-agnostic plugin system via subprocess JSON protocol.

## Concept

Allow extending Gas Town with external scripts/programs. Plugins communicate via stdin/stdout JSON.

## Protocol

Request (gt → plugin):
```json
{"method": "check", "params": {"rig": "wyvern", "polecat": "Toast"}}
```

Response (plugin → gt):
```json
{"result": {...}, "error": null}
```

Error response:
```json
{"result": null, "error": {"code": 1, "message": "..."}}
```

## Interface

```go
type Plugin struct {
    Name    string
    Path    string  // executable path
    Methods []string
}

type PluginManager struct {
    plugins map[string]*Plugin
}

func NewPluginManager() *PluginManager
func (pm *PluginManager) Register(plugin *Plugin) error
func (pm *PluginManager) Call(plugin, method string, params any) (any, error)
func (pm *PluginManager) Discover(dir string) error  // auto-discover plugins
```

## Plugin Discovery

Look in:
1. `<town>/plugins/`
2. `~/.config/gastown/plugins/`
3. System path with `gt-plugin-*` prefix

## Built-in Hooks

Plugins can register for events:
- `polecat.started` - Polecat session started
- `polecat.done` - Polecat signals done
- `merge.requested` - MR submitted
- `merge.completed` - MR merged

## Use Cases

- Custom health checks
- Integration with external systems (Slack, etc.)
- Custom merge policies
- Metrics collection

## Future

Consider MCP integration for richer plugin capabilities.


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.


Resource Beads: Leases, locks, and quotas

Resource beads represent reserved resources. Types: vm, lock, slot, quota. Fields: holder, expires, renewable. Daemon monitors for expiry and manages contention.


Rename engineer-in-box to shiny across all docs

Systematic rename of mol-engineer-in-box to shiny throughout docs: molecular-chemistry.md, molecules.md, architecture.md, etc. The shiny name is now canonical per the Breaking Bad × Mad Max naming.


Merge: slit-mjtnyszt

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


Session ended: gt-gastown-crew-gus


Detect abandoned work

**DETECT ONLY** - Check for orphaned state and dispatch to dog if found.

**Step 1: Quick orphan scan**
```bash
# Check for in_progress issues with dead assignees
bd list --status=in_progress --json | head -20
```

For each in_progress issue, check if assignee session exists:
```bash
tmux has-session -t <session> 2>/dev/null && echo "alive" || echo "orphan"
```

**Step 2: If orphans detected, dispatch to dog**
```bash
# Sling orphan-scan formula to an idle dog
gt sling mol-orphan-scan deacon/dogs --var scope=town
```

**Important:** Do NOT fix orphans inline. Dogs handle recovery.
The Deacon's job is detection and dispatch, not execution.

**Step 3: If no orphans detected**
Skip dispatch - nothing to do.

**Exit criteria:** Orphan scan dispatched to dog (if needed).


Digest: mol-deacon-patrol

Patrol #2: Routine - all 6 agents healthy


mol-deacon-patrol


Move disk 2: B → A

Move disk 2 from peg B to peg A. (Move 838/1023)


Move disk 3: B → C

Move disk 3 from peg B to peg C. (Move 420/1023)


Digest: mol-deacon-patrol

Patrol 4: Routine


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.


Ensure refinery is alive

Ensure the refinery is alive and processing merge requests.

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

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

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

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

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


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 277/1023)


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


gt mr: Add alias for merge request operations

Agent guessed `gt mr register <branch>` to submit a branch to the merge queue. The actual command is `gt mq submit`.

**Suggestion**: Add `gt mr` as an alias or subcommand for merge request workflows:
- `gt mr submit` → `gt mq submit`
- `gt mr status` → `gt mq status`
- `gt mr list` → `gt mq list`

**Why**: 'MR' (merge request) is the conceptual noun; 'mq' (merge queue) is the implementation detail. Agents and humans think in terms of 'I have a merge request' not 'I need to interact with the queue'.

**Alternative**: Just document `gt mq` more prominently, but aliases cost little and reduce friction.

Filed by: gastown/polecats/nux (agent UX feedback)


Digest: mol-deacon-patrol

Patrol 3: All healthy, MQ 1 pending


Move disk 2: B → A

Move disk 2 from peg B to peg A. (Move 826/1023)


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


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


Architecture: beads config and direct landing docs

Added to architecture.md:
- Beads multi-agent configuration table (daemon, worktree, sync-branch)
- ASCII directory layout for non-mermaid rendering
- Direct landing workflow (bypass merge queue)
- Design decisions 9 and 10 for direct landing and daemon awareness
- CLI commands for gt land


Move disk 7: A → B

Move disk 7 from peg A to peg B. (Move 832/1023)


Digest: mol-refinery-patrol

Patrol: Closed 21 merge requests that were already merged to main. All tests passed. Queue now empty.


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.


Merge: capable-mjw47ef9

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


Dynamically discover rigs in checkStaleAgents

In internal/daemon/lifecycle.go:734-735, the rig list is hardcoded:

```go
// Add rig-specific agents (witness, refinery) for known rigs
// For now, we check gastown - could be expanded to discover rigs dynamically
rigs := []string{"gastown", "beads"}
```

This should use gt rigs or similar to dynamically discover configured rigs instead of hardcoding.

Affects: checkStaleAgents() which checks for dead agents that stopped updating their bead state.


Hardcoded 'gastown' rig in Deacon patrol context

In prime.go, the Deacon patrol context hardcodes 'gastown' as the rig name.

**Location**: prime.go L807

```go
rigBeadsDir := filepath.Join(ctx.TownRoot, "gastown", "mayor", "rig")
```

This breaks if:
- The rig is renamed
- The town has multiple rigs
- The Deacon is deployed to a different rig

**Fix options**:
1. Detect rig from context (ctx.Rig)
2. Use town-level beads for Deacon patrol
3. Pass rig name as parameter

The Deacon is a town-level role, so option 2 may be most appropriate.


Move disk 5: A → B

Move disk 5 from peg A to peg B. (Move 784/1023)


check-refinery

Ensure the refinery is alive and processing merge requests.

Needs: inbox-check


Evaluate pending async gates

Evaluate pending async gates.

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

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

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

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

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

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

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


Implement gt mol status command

Show what's on an agent's hook.

```bash
gt mol status [target]
```

Output:
- What's slung (molecule name, associated issue)
- Current phase and progress
- Whether it's a wisp
- Next action hint

If no target, shows current agent's status.

Acceptance:
- [ ] Read pinned bead attachment
- [ ] Display molecule/issue info
- [ ] Show phase progress
- [ ] Indicate wisp vs durable


Digest: mol-deacon-patrol

Patrol #11: Stable


Digest: mol-deacon-patrol

Patrol 3: All healthy, no incidents


Digest: mol-deacon-patrol

Patrol 260 complete: final cycle before handoff


Digest: mol-deacon-patrol

Patrol 54: All healthy


Merge: capable-1767074974673

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


Move disk 1: C → A

Move disk 1 from peg C to peg A. (Move 707/1023)


gt mq list: show queue with status/priority/age

Implement 'gt mq list' command to display the merge queue.

Output format:
ID          STATUS       PRIORITY  BRANCH                    WORKER  AGE
gt-mr-001   ready        P0        polecat/Nux/gt-xyz        Nux     5m
gt-mr-002   in_progress  P1        polecat/Toast/gt-abc      Toast   12m
gt-mr-003   blocked      P1        polecat/Capable/gt-def    Capable 8m
            (waiting on gt-mr-001)

Options:
- --ready: show only ready-to-merge (no blockers, not in progress)
- --status STATUS: filter by status
- --worker WORKER: filter by worker
- --epic EPIC: show MRs targeting integration/EPIC

Under the hood: bd list --type=merge-request with filters.

Reference: docs/merge-queue-design.md#gt-mq-list


Digest: mol-deacon-patrol

Patrol 5: 9 sessions healthy


Day 3.6: Witness pings Deacon for second-order monitoring

Implement WITNESS_PING protocol for second-order monitoring.

Witnesses collectively monitor Deacon health:
1. Each Witness sends WITNESS_PING to Deacon during patrol
2. Deacon responds (or doesn't if dead)
3. If no response after threshold, Witness escalates to Mayor

This prevents the 'who watches the watchers' problem - if Deacon dies, Witnesses detect it.

Implementation:
- Add WITNESS_PING step to mol-witness-patrol
- Deacon acknowledges pings (updates last_activity)
- Witness checks Deacon agent bead last_activity
- Escalate if stale beyond threshold


Test Polecat Arm

Test child for bonding pattern


Digest: mol-deacon-patrol

Patrol 8: clear


Move disk 2: B → A

Move disk 2 from peg B to peg A. (Move 862/1023)


Digest: mol-deacon-patrol

Patrol #20: Final before handoff


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


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.


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 1011/1023)


Merge: rictus-mjuic92c

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


Session pre-shutdown checks

Session stop should verify clean state before killing, like PGT.

## Pre-Shutdown Checks

### 1. Git Working Tree Clean
```go
func checkGitClean(clonePath string) error {
    // git status --porcelain
    // Fail if any output
}
```

### 2. All Commits Pushed
```go
func checkCommitsPushed(clonePath string) error {
    // git log origin/HEAD..HEAD
    // Fail if any unpushed commits
}
```

### 3. Assigned Issues Handled
```go
func checkIssuesHandled(polecat *Polecat) error {
    // If polecat.Issue != "", check if closed or reassigned
}
```

### 4. Beads Synced
```go
func checkBeadsSynced(clonePath string) error {
    // bd sync --status in clone directory
}
```

## Behavior on Failure
1. First attempt: Nudge worker to fix
2. Retry up to 3 times with delay
3. After retries: Escalate to Witness/Mayor

## Integration
Modify internal/session/manager.go Stop():
```go
func (m *Manager) Stop(polecat string, force bool) error {
    if !force {
        if err := m.runPreShutdownChecks(polecat); err != nil {
            return fmt.Errorf("pre-shutdown checks failed: %w", err)
        }
    }
    // existing stop logic
}
```

## Flags
- --force: Skip checks
- --grace-period N: Time to wait for fixes

## Dependencies
- Ties into gt-69l (hook system) - can be hook-based
- Ties into gt-f8v (Witness pre-kill verification)

## Acceptance Criteria
- [ ] Stop fails if uncommitted changes (without --force)
- [ ] Stop fails if unpushed commits
- [ ] Clear error messages with fix instructions
- [ ] --force bypasses all checks


Digest: mol-deacon-patrol

Patrol #14


Epic: Async Gates for Agent Coordination

## Summary

Agents need an async primitive for waiting on external events (CI completion,
API responses, human approval). Currently they either poll wastefully or cant
resume after handoff.

## Design: Deacon-Managed Gates

### Core Concepts

**Gate** = wisp issue that blocks until external condition is met
- Type: `gate`
- Phase: wisp (never synced, ephemeral)
- Assignee: `deacon/` (Deacon monitors it)
- Fields: `await_type`, `await_id`, `timeout`, `waiters[]`

**Await Types:**
- `gh:run:<id>` - GitHub Actions run completion
- `gh:pr:<id>` - PR merged/closed
- `timer:<duration>` - Simple delay (e.g., "5m", "1h")
- `human:<prompt>` - Human approval required
- `mail:<pattern>` - Wait for mail matching pattern

### Commands

```bash
bd gate create --await <type>:<id> --timeout <duration> --notify <addr>
bd gate show <id>
bd gate list
bd gate close <id> --reason "completed"
bd gate wait <id> --notify <addr>
```

## Children (suggested breakdown)

1. Add `gate` issue type to beads
2. Add gate fields: await_type, await_id, timeout, waiters
3. Implement `bd gate create/show/list/close/wait` commands
4. Add gate checking to Deacon patrol loop
5. Implement await type handlers (gh:run, gh:pr, timer, human, mail)
6. Add gate timeout tracking and notification
7. Integration test: agent waits for CI via gate

## Open Questions

- Should gates live in wisp storage or main storage with wisp flag?
- Do we need a gate catalog (like molecule catalog)?
- Should `waits-for` dep type work with gates?


Digest: mol-deacon-patrol

Patrol 10: All agents healthy


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 793/1023)


Move disk 2: B → A

Move disk 2 from peg B to peg A. (Move 274/1023)


Add 'gt account list' command

Show registered accounts from accounts.yaml. Mark default with asterisk. Show handle, email, description.


Digest: mol-deacon-patrol

Patrol cycle 1: All 3 rigs healthy (beads, gastown, wyvern), no orphans, no cleanup needed, inbox clean


Fix convoy refresh scheduling causing duplicates

model.go:191-197 schedules both fetchConvoys() and convoyRefreshTick() on every update, potentially causing duplicate refreshes. The tick should only be scheduled once initially.