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.


Add hook status to gt status output

Enhance gt status to show hook summary for each rig.

Current: Shows rig list and basic counts
New: Also shows occupied hooks count and any stale attachments


Move disk 2: C → B

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


Move disk 1: A → B

Move disk 1 from peg A to peg B. (Move 487/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 129: All healthy


Witness startup: bond mol-witness-patrol on start

Wire up Witness to automatically bond its patrol molecule on startup.

## Desired behavior
On Witness session start:
1. gt prime detects RoleWitness
2. Check for existing in-progress patrol
3. If found: resume from current step
4. If not found: bd mol bond mol-witness-patrol --wisp
5. Output patrol context to agent

## Depends on
- gt-83k0 (mol-witness-patrol definition)
- gt-caih (handoff bead state persistence)


Daemon: verify requesting_cycle before kill

Per gt-gby spec, daemon should verify agent state shows requesting_cycle=true before killing session. Currently kills on any lifecycle request without verification.

Requires state.json in agent workspace with requesting_cycle field.


bd cook command: Formula to Proto

Implement the 'bd cook' command that transforms formulas into protos.

Usage: bd cook <formula-file> [options]
       bd cook .beads/formulas/*.formula.yaml

Process:
1. Parse formula YAML (uses formula parser)
2. Resolve extends (formula inheritance)
3. Expand macros (expansion rules)
4. Apply aspects (cross-cutting concerns)
5. Generate flat proto bead

Output:
- Creates proto bead in .beads/ with mol-prefix
- Proto has all steps pre-expanded
- Proto is ready for pour/wisp instantiation

**Beads issue: bd-wa2l**


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol cycle 14: All 6 agents healthy


Digest: mol-deacon-patrol

Patrol 1: All agents healthy, no lifecycle requests, cleaned 3 abandoned wisps


Digest: mol-deacon-patrol

Patrol 12


Move disk 2: A → C

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


Add tmux hotkey (C-b f) for gt feed window

## Goal

Add a tmux hotkey (e.g., C-b f) that jumps to the activity feed window, creating it if it doesn't exist.

## Behavior

1. If 'feed' window exists in current session → switch to it
2. If 'feed' window doesn't exist → create it with `bd activity --follow`, then switch

## Implementation

Add to gt's tmux session configuration (similar to SetCrewCycleBindings):

```go
// C-b f → jump to feed window
tmux bind-key -T prefix f run-shell "gt feed --window"
```

The `gt feed --window` command already handles:
- Creating the window if missing
- Switching to it if exists
- Running `bd activity --follow` in the window

## Where to add

- `internal/tmux/tmux.go` - add SetFeedBinding() method
- Call it from session configuration (ConfigureGasTownSession or similar)

## Testing

1. In tmux: C-b f should open/switch to feed window
2. Feed window should show streaming activity
3. C-b n/p should still work to cycle back to other windows


gt-gastown-polecat-cal

gt-gastown-polecat-cal

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


gt done fails in worktree: missing remote.origin.fetch config


Digest: mol-deacon-patrol

Patrol 4: quick pass


Beads CLI wrapper: shell out to bd

Wrapper for bd (beads CLI) commands.

## Interface

```go
type Beads struct {
    workDir string
}

func NewBeads(workDir string) *Beads

// Query
func (b *Beads) List(opts ListOptions) ([]*Issue, error)
func (b *Beads) Ready() ([]*Issue, error)
func (b *Beads) Show(id string) (*Issue, error)
func (b *Beads) Blocked() ([]*Issue, error)

// Mutations
func (b *Beads) Create(opts CreateOptions) (*Issue, error)
func (b *Beads) Update(id string, opts UpdateOptions) error
func (b *Beads) Close(ids ...string) error

// Sync
func (b *Beads) Sync() error
func (b *Beads) SyncStatus() (*SyncStatus, error)
```

## Data Types

```go
type Issue struct {
    ID          string
    Title       string
    Status      string
    Priority    int
    Type        string
    Description string
    Parent      string
    Children    []string
    DependsOn   []string
    Blocks      []string
}

type ListOptions struct {
    Status   string  // "open", "closed", "all"
    Type     string
    Priority int
}

type CreateOptions struct {
    Title       string
    Type        string
    Priority    int
    Description string
    Parent      string
}
```

## Implementation

Shell out to bd binary, parse JSON output where available.
```go
func (b *Beads) runBd(args ...string) ([]byte, error) {
    cmd := exec.Command("bd", args...)
    cmd.Dir = b.workDir
    return cmd.Output()
}
```

## Error Handling

- bd not installed: Clear error with install instructions
- Not a beads repo: Detect and report
- Sync conflicts: Parse and report


Polecat health check loop

Implement the Witness health check loop:

Every 60 seconds:
1. List active polecats (gt polecats <rig>)
2. For each polecat, check:
   - Is tmux session still alive?
   - Is there a state.json with recent update?
   - Has there been git activity recently?
3. If stuck (no activity for configurable threshold):
   - First: nudge (send message asking for status)
   - Second: escalate to Mayor
   - Third: force kill after human confirmation

This is the 'health monitor' part of Witness.


Test Task 1: Add comment to gt.go


Move disk 3: A → B

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


Bug: MRs appearing in bd ready output

merge-request beads are showing up in `bd ready` output alongside actual work items.

## Problem
`bd ready` is meant to show work available for polecats to claim. MRs are internal workflow items processed by the Refinery, not polecat work.

## Example
```
| P1 | merge-request | bd-3zzh | Merge: bd-tvu3 |
| P1 | merge-request | bd-fcl1 | Merge: bd-au0.5 |
```

## Possible Fixes
1. **Filter by type** - `bd ready` excludes type=merge-request, type=gate, type=molecule
2. **Set assignee on submit** - MR assigned to refinery/ when created, so not 'unassigned'
3. **Set status=in_progress** - MR starts in_progress since it's queued for processing

## Related
- gate beads also showing (bd-hyp6, bd-wu62) - same issue


Digest: mol-deacon-patrol

Patrol 17: healthy


Move disk 1: B → C

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


Merge: gt-tnca.1

branch: polecat/immortan
target: main
source_issue: gt-tnca.1
rig: gastown


E2E Test: Add a comment to internal/util/process.go


Merge: gt-cehl8

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


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


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.


Witness: Verify MR submission on notification

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

## Implementation

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

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


Move disk 3: B → C

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


Move disk 1: B → C

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


Digest: mol-deacon-patrol

Patrol 100: All healthy - milestone!


MCP beads tools fail from polecat directories

The beads MCP plugin (plugin:beads:beads) fails to find issues when invoked from polecat directories.

## Observed
- Polecat in /gt/gastown/polecats/dementus
- MCP tool: plugin:beads:beads - show (issue_id: 'gt-th7')
- Error: 'bd command failed: Error: no issue found matching gt-th7'

## Expected
MCP tools should work the same as running bd directly.

## Note
This may be related to gt-tmm (beads sync issue) rather than an MCP-specific bug. The MCP tool uses bd under the hood, and bd can't find the issue because the polecat's beads database is stale.


Add gt mail claim command for queue message claiming

Add claim subcommand to internal/cmd/mail.go.

SYNTAX:
gt mail claim <queue-name>

BEHAVIOR:
1. List unclaimed messages in queue (claimed_by empty)
2. Pick oldest unclaimed message
3. Set claimed_by to caller identity
4. Set claimed_at to now
5. Print claimed message details

ELIGIBILITY CHECK:
- Load queue config from messaging.json
- Check if caller matches any pattern in workers list
- Reject if not eligible

FILE: internal/cmd/mail.go
ADD: claimCmd as subcommand of mailCmd

TESTS: Add TestMailClaim in mail_test.go or cmd_test.go


Digest: mol-deacon-patrol

Patrol 4: 3 rigs healthy


Digest: mol-deacon-patrol

Patrol 83: All healthy, quiet


Digest: mol-deacon-patrol

Patrol 16: All healthy


Computed range expansion (for-each over expressions)

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

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

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

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


Outpost integration tests: Mock and E2E testing

## Overview

Integration tests for the outpost system.

## Mock Outpost

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

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

## Unit Tests

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

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

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

## Integration Tests (with mocked Cloud Run)

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

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

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

## E2E Tests (requires real Cloud Run)

```go
// +build e2e

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

## Files

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

## Dependencies

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


Phase 1.1: gt rig init creates .beads-ephemeral/

Add ephemeral beads repo creation to rig initialization.

## Implementation

In `gt rig init` (or equivalent setup):
1. Create `<rig>/.beads-ephemeral/` directory
2. Initialize as git repo
3. Create minimal beads config (no sync-branch needed)
4. Add to .gitignore if not already

## Config

```yaml
# .beads-ephemeral/config.yaml
ephemeral: true
# No sync-branch - ephemeral is local only
```

## Verification

```bash
gt rig init gastown
ls gastown/.beads-ephemeral/  # Should exist
```


gt-gastown-polecat-goose

gt-gastown-polecat-goose

role_type: polecat
rig: gastown
agent_state: spawning
hook_bead: gt-cbstf
role_bead: gt-polecat-role
cleanup_status: null
active_mr: gt-e246n
notification_level: null


Digest: mol-deacon-patrol

Patrol cycle 8


Move disk 1: B → C

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


Move disk 1: B → C

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


Simplify mail wisps: remove dual-routing, use ephemeral flag

## Context

gt-lg66 implemented dual-inbox architecture with separate .beads-wisp/ directory.
This was over-engineered. See bd-bkul for the simpler approach.

## Work

Once beads implements bd-bkul (ephemeral flag in single db):

1. Revert/simplify router.go:
   - Remove resolveWispDir()
   - Remove shouldBeEphemeral() auto-detection (beads handles this)
   - Send() just passes --ephemeral flag to bd create

2. Revert/simplify mailbox.go:
   - Remove wispDir field
   - Remove listFromDir/getFromDir dual-source logic
   - Remove closeInDir dual-source logic
   - Single query, beads returns both types with Source marked

3. Keep in mail.go:
   - --ephemeral flag (passed through to bd)
   - (ephemeral) display indicator

4. Keep in spawn.go:
   - Ephemeral: true on lifecycle messages

## Depends On

bd-bkul: Simplify wisp architecture in beads


Digest: mol-deacon-patrol

Patrol 1: All clear - inbox empty, agents healthy, no orphans


mol-deacon-patrol

Mayor's daemon patrol loop.

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

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


Formula parser and YAML schema

Implement formula parsing from YAML:
- Define YAML schema for .formula.yaml files
- Parse steps, compose rules, vars
- Support extends for formula inheritance
- Validate formula structure

Schema should support:
- formula: name
- description: text
- version: number
- type: workflow|expansion|aspect
- vars: variable definitions
- steps: step definitions
- compose: composition rules

**Beads issue: bd-weu8**


Digest: mol-deacon-patrol

Patrol cycle 67: All healthy


town handoff command (optional)

CLI commands for session handoff workflow (optional convenience).

## Commands

### gt handoff
Generate handoff interactively.
```
gt handoff [--send]
```
- Collects current state (status, inbox, beads)
- Prompts for additional notes
- --send: Mail to self and exit

### gt resume
Check for and display pending handoff.
```
gt resume
```
- Checks inbox for handoff message
- Displays formatted handoff if found
- Suggests next actions

## Implementation

These are convenience wrappers. The same workflow can be done manually:
```bash
# Manual handoff
town status > /tmp/handoff
town inbox >> /tmp/handoff
bd ready >> /tmp/handoff
# Edit and send
town mail send mayor/ -s "Session Handoff" -f /tmp/handoff
```

## Priority

P2 - Optional. Manual workflow works fine. Nice to have for UX.

## Notes

Part of session cycling workflow designed in [deleted:gt-u82].


Deacon keepalive: quiet daemon during rounds

Replace tmux-poke interrupts with a keepalive file the daemon respects.

## Problem

Current daemon pokes Deacon via tmux SendKeys every ~5 minutes if heartbeat
is stale. This can INTERRUPT the Deacon mid-work, especially during long
plugin execution or health remediation.

## Solution

Deacon writes a keepalive file at start of rounds:
```json
{
  "timestamp": "2025-12-20T15:30:00Z",
  "expected_duration_minutes": 10,
  "action": "executing mol-deacon-rounds"
}
```

Daemon reads this and backs off:
- If keepalive is fresh AND within expected_duration: skip poke
- If keepalive is stale OR past expected_duration: poke (fallback)

## Files

- `{townRoot}/deacon/keepalive.json` - Written by Deacon
- Daemon already reads `{townRoot}/deacon/heartbeat.json` - similar pattern

## Deacon Behavior

1. Start of rounds: write keepalive with expected duration
2. End of rounds: write heartbeat (existing), clear/update keepalive
3. If rounds take longer than expected: Deacon gets one poke, squelches it

## Daemon Changes

Modify `pokeDeacon()` in daemon.go:
1. Check keepalive.json freshness and expected_duration
2. If within bounds: skip poke, log "Deacon active"
3. If past bounds: poke as fallback

## Worst Case

Deacon crashes mid-rounds without clearing keepalive:
- Daemon waits until expected_duration expires
- Then pokes, triggering respawn loop
- Max delay: expected_duration (configurable, default 10-15 min)

## Configuration

In pinned kernel bead:
```yaml
config:
  rounds_expected_duration_minutes: 10
```


mol-deacon-patrol


Move disk 2: A → C

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


Merge: organic-mjwjck2f

branch: polecat/organic-mjwjck2f
target: main
source_issue: organic-mjwjck2f
rig: gastown
agent_bead: gt-gastown-polecat-organic


Digest: mol-deacon-patrol

Patrol 18: All healthy.


Digest: mol-deacon-patrol

Patrol 9: all quiet


Move disk 1: C → A

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


Merge: rictus-mjw3nj1a

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


gt rig shutdown: Gracefully stop all rig agents

Add 'gt rig shutdown <rig>' command to gracefully stop all agents in a rig.

Should:
- Stop all polecat sessions
- Stop refinery
- Stop witness
- Optionally wait for graceful shutdown with timeout


Move disk 2: B → A

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


Digest: mol-deacon-patrol

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


Digest: mol-deacon-patrol

Patrol 49: All healthy


Digest: mol-deacon-patrol

P19: stable


Digest: mol-deacon-patrol

Cycle 296: All healthy, refinery active


Digest: mol-deacon-patrol

Patrol 54: All healthy


Digest: mol-deacon-patrol

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


Move disk 1: B → C

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


gt rig stop <rig>... - stop rig workers with shutdown semantics

Stop one or more rigs with same flags and semantics as gt shutdown (for polecats). Should support multiple rigs: gt rig stop gastown beads


mol-sync-workspace: Split handle-dirty-state into focused steps


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 10: all clear


Digest: mol-deacon-patrol

Patrol #18: Stable


Move disk 2: B → A

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


Digest: mol-deacon-patrol

Patrol 255 complete: no incidents


Digest: mol-deacon-patrol

Patrol 18: quiet


Mail CLI: archive, purge, search, mark

GGT mail CLI needs more commands for mail management.

## Commands to Add

### gt mail check
Check for new mail without full inbox display.
```
gt mail check [--quiet] [--inject]
```
- --quiet: Only output if new mail (for scripts)
- --inject: Send notification to running session

### gt mail mark
Change read status.
```
gt mail mark <id> --read
gt mail mark <id> --unread
```

### gt mail delete
Remove message from inbox.
```
gt mail delete <id> [--force]
```
- Confirm unless --force

### gt mail archive
Move old/read messages to archive.
```
gt mail archive [--older-than DAYS] [--all-read] [--dry-run]
```
- Creates inbox.jsonl.archive or separate archive.jsonl

### gt mail purge
Permanently delete archived messages.
```
gt mail purge [--older-than DAYS] [--dry-run] [--force]
```

### gt mail search
Find messages by content.
```
gt mail search <query> [--from SENDER] [--subject] [--body]
```

### gt mail reply
Reply to a message.
```
gt mail reply <id> -m BODY
```
- Auto-sets reply_to and thread_id
- Auto-addresses to original sender

## Files to Modify
- internal/cmd/mail.go: Add commands
- internal/mail/mailbox.go: Add Archive(), Search(), Delete()

## Acceptance Criteria
- [ ] All commands implemented
- [ ] Archive stores in separate file
- [ ] Search supports regex patterns
- [ ] Delete confirms by default


gt session start --issue doesn't inject work to prompt

When starting a polecat session with --issue flag:
  gt session start beads/Toast --issue bd-oxgi

The session starts but the polecat doesn't receive the issue context. They sit at the prompt awaiting input. Had to manually nudge them.

Expected: Session start should either nudge or inject the issue context so polecat starts working immediately (propulsion principle).


Digest: mol-deacon-patrol

Patrol 5: quick pass


Move disk 1: A → B

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


Harness Design & Documentation

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

## Current Problems

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

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

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

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

## Proposed Work

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


implement

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

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

Depends: load-context


Digest: mol-deacon-patrol

Patrol 28: all quiet


Digest: mol-deacon-patrol

Patrol cycle 10: All quiet.


Move disk 1: A → B

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


Digest: mol-deacon-patrol

Patrol 72: Routine


Move disk 2: A → C

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


Move disk 1: B → C

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


Digest: mol-deacon-patrol

Patrol 12 complete


Missing standard OSS files: CONTRIBUTING, CODE_OF_CONDUCT, SECURITY

For OSS launch, we need:
1. CONTRIBUTING.md - How to contribute, PR process, code style
2. CODE_OF_CONDUCT.md - Community standards
3. SECURITY.md - How to report vulnerabilities

These are expected by OSS communities and GitHub surfaces them in repo UI.


Move disk 1: B → C

Move disk 1 from peg B to peg C. (Move 939/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.


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


Merge: keeper-dogs

branch: polecat/keeper-dogs
target: main
source_issue: keeper-dogs
rig: gastown


Merge: rictus-1767141956287

branch: polecat/rictus-1767141956287
target: main
source_issue: rictus-1767141956287
rig: gastown


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.


implement

Implement the solution for {{issue}}. Follow codebase conventions.
File discovered work as new issues with bd create.

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

Depends: load-context


Move disk 1: A → B

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


CLI: workspace commands (init, add, list)

GGT needs workspace management commands beyond install.

## Commands (beyond gt-f9x.3 install)

### gt workspace list
List all rigs in current workspace.
```
gt workspace list [--json]
```
Essentially `gt rig list` but framed as workspace view.

### gt workspace add
Add existing rig to workspace (alternative to gt rig add).
```
gt workspace add <git-url> [--name NAME]
```

### gt onboard
Interactive first-time setup wizard.
```
gt onboard
```
- Prompts for workspace location
- Creates structure via gt install
- Offers to add first rig

## Note
May be redundant with gt-f9x.3 (install) and gt-u1j.16 (rig commands).
Consider if this is needed or should be closed as covered by those.

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


BUG: gt swarm create uses non-existent --mol-type flag on bd update

gt swarm create fails because it tries to use --mol-type=swarm with bd update, but that flag doesn't exist.

## Location
internal/cmd/swarm.go lines 241, 252, 661-662

## Problem
- `bd list --mol-type=swarm` works (flag exists)
- `bd create --mol-type=swarm` works (flag exists)
- `bd update --mol-type=swarm` FAILS (flag doesn't exist)

## Fix Options
1. Add --mol-type flag to bd update command (beads change)
2. Use labels instead of mol-type for swarm marking
3. Only set mol-type during bd create, not update

## Reported By
gus during gt-d46 swarm attempt


Deacon trigger-pending-spawns: Auto-nudge polecats when ready

The Deacon's mol-deacon-patrol defines a trigger-pending-spawns step, but it's not implemented.

## Problem
After gt spawn creates a polecat session, Claude initializes for 10-20s. Nobody sends the initial 'Begin.' message to start work, so polecats sit at prompts forever.

## Current Workaround
Manual: gt nudge gt-<rig>-<polecat> 'Begin.'

## Required Implementation
1. Track pending spawns (polecats spawned but not triggered)
2. In Deacon patrol, poll WaitForClaudeReady for each pending spawn
3. When ready, send 'Begin.' via NudgeSession
4. Mark as triggered

## Evidence
Tracer bullet 2025-12-23: Had to manually nudge polecat/tracer to start work.


Digest: mol-deacon-patrol

Patrol cycle 19: all healthy


Digest: mol-deacon-patrol

Patrol cycle 15: All agents healthy


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 11: clear


Digest: mol-deacon-patrol

Patrol #12: Health pinged all witnesses.


Digest: mol-deacon-patrol

Patrol cycle 7: All quiet.


Work on ga-xxp: Define mol-polecat-work standard molecule...

Work on ga-xxp: Define mol-polecat-work standard molecule. See bd show ga-xxp for full details.


test with labels


self-review

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

Fix any issues found before proceeding.

Depends: implement


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

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

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

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

Parent: gt-oki8p


Digest: mol-deacon-patrol

Patrol 17: all clear


Digest: mol-deacon-patrol

Patrol 2: All clear


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 1: C → A

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


Digest: mol-deacon-patrol

Patrol cycle 29: All healthy, no incidents


Session ended: gt-gastown-nux


Runtime dynamic expansion (for-each)

Implement runtime expansion for discovered work (the for-each construct).

## Syntax

    step: survey-workers
    on-complete:
      for-each: output.polecats
      bond: mol-polecat-arm
      vars:
        polecat_name: "{item.name}"
        rig: "{item.rig}"
      parallel: true  # or sequential: true

## Design Context (from gt-8tmz.4 review)

This is **control flow**, not just expansion. It affects execution order:
- for-each with parallel: true = concurrent child execution
- for-each with sequential: true = ordered child execution

Key patrol patterns that require this:

| Pattern | Use Case |
|---------|----------|
| Witness survey-workers | For each polecat, bond an arm (parallel) |
| Refinery process-branch | For each branch in queue, run merge pipeline (sequential) |
| Deacon plugin-run | For each plugin where gate open, execute |

## Relationship to gt-8tmz.4

gt-8tmz.4 implements loop, gate, branch (static control flow).
This issue implements for-each (dynamic control flow over runtime-discovered collections).

Together they cover the full control flow needs for patrol formulas.

## Implementation Notes

- Bonds N instances based on step output
- Christmas Ornament pattern: fanout from single step to N parallel children
- Pairs with waits-for gate (gt-8tmz.38) for aggregation
- Collection must be iterable (array in step output)
- Each iteration gets item, index, and parent context in vars


Daemon patrol should clean stale locks

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

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

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


Digest: mol-deacon-patrol

Patrol 7: All healthy.


Digest: mol-deacon-patrol

Patrol 68: Routine


Merge: gt-3x0z.3

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


Code review: session health monitoring (gt-i7wcn)

Review the session health monitoring and auto-restart implementation in internal/cmd/session.go, internal/daemon/daemon.go, and internal/daemon/lifecycle.go


Missing failsafe: No circuit breaker for stuck agents

When an agent (Deacon, Witness, Refinery) gets stuck, there's no automatic recovery:

1. No timeout on individual steps - an agent can spin forever
2. No watchdog process checking agent health externally
3. No heartbeat/liveness check that triggers restart

Proposed solution:
- Add a watchdog that checks agent last_activity timestamps
- If no activity for >5 minutes, force cycle the agent
- This could be a simple cron job or daemon feature

This is defense-in-depth for the 'who watches the watchers' problem.

(Moved from hq-2wy2i)


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.


Implement git merge logic in Engineer.ProcessMR

The Engineer module in internal/refinery/engineer.go has stub implementations for ProcessMR() and ProcessMRFromQueue() that need actual git merge logic.

## Current State
- ProcessMR() logs intent but returns failure
- ProcessMRFromQueue() same - logs and returns failure
- handleSuccess() and handleFailure() are implemented

## Required Implementation
1. **Fetch source branch**: git fetch origin <branch>
2. **Conflict check**: git merge --no-commit --no-ff to test
3. **Run tests** if configured (config.RunTests, config.TestCommand)
4. **Perform merge**: git merge with appropriate commit message
5. **Push to origin**: git push origin <target>
6. **Handle failures**: conflicts, test failures, push failures

## Context
- There are 28+ pending merge-request issues waiting for this
- Infrastructure (config, result types, success/failure handlers) is ready
- Just needs the core git operations

## Files
- internal/refinery/engineer.go - ProcessMR(), ProcessMRFromQueue()
- internal/git/git.go - may need Merge(), Fetch() methods


Beads schema: Add created_by field enforcement

Ensure created_by field is populated on all issue creation. Currently optional and often missing. Should default from BD_ACTOR env var. Affects: merge requests, escalations, digests.


Outpost assignment policy: Smart work routing

## Overview

Policy engine for deciding which outpost gets which work.

## Policy Configuration

```yaml
policy:
  # Default order of preference
  default_preference: [local, gce-burst, cloudrun-burst]

  # Rules applied in order
  rules:
    # Background work → Cloud Run (cheap)
    - condition: "priority >= P3"
      prefer: cloudrun-burst

    # Long tasks → VM (persistent)
    - condition: "estimated_duration > 30m"
      prefer: gce-burst

    # Specific epic → specific outpost
    - condition: "epic == gt-abc"
      prefer: local
```

## Implementation

```go
type AssignmentPolicy struct {
    DefaultPreference []string
    Rules             []PolicyRule
}

type PolicyRule struct {
    Condition string   // Simple expression
    Prefer    string   // Outpost name
    Require   string   // Must use this outpost
}

func (p *AssignmentPolicy) SelectOutpost(
    issue Issue,
    outposts map[string]Outpost,
) Outpost {
    // Check rules in order
    for _, rule := range p.Rules {
        if rule.Matches(issue) {
            if op, ok := outposts[rule.Prefer]; ok {
                if op.ActiveWorkers() < op.MaxWorkers() {
                    return op
                }
            }
        }
    }

    // Fall back to default preference
    for _, name := range p.DefaultPreference {
        if op, ok := outposts[name]; ok {
            if op.ActiveWorkers() < op.MaxWorkers() {
                return op
            }
        }
    }

    return nil  // All outposts at capacity
}
```

## Condition Language

Simple expressions, not a full DSL:

```
priority >= P3
priority == P0
estimated_duration > 30m
epic == gt-abc
type == bug
label contains "urgent"
```

## Files

- `internal/outpost/policy.go`
- `internal/outpost/condition.go`

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


Witness: Update polecat monitoring for ephemeral model

Update Witness to handle truly ephemeral polecats.

## Current Behavior
- Witness monitors polecats until MR merged
- 'Rebase needed' mail sent to polecats

## New Behavior
- Witness marks polecat as 'recyclable' after MR submit
- Witness can dispatch conflict-resolution tasks
- Witness doesn't wait for merge to recycle polecat

## Changes to mol-witness-patrol.formula.toml
- Detect 'MR submitted' state
- Mark polecat recyclable
- Handle conflict-resolution dispatch

## Polecat States
```
spawning → working → mr_submitted → recyclable → recycled
```

## Considerations
- Don't recycle sandbox until branch pushed
- Or: Keep sandbox, just recycle session


Add 'spawn' as alias for 'start' subcommand across all roles

Agents are guessing 'spawn' when trying to start roles (witness, refinery, etc). Need to add 'spawn' as an alias for 'start' for consistency.

Affected commands:
- gt witness start → also gt witness spawn
- gt refinery start → also gt refinery spawn
- gt polecat start → also gt polecat spawn
- gt crew start → also gt crew spawn

Implementation: Add Aliases field to cobra commands.


Add delegation relationship type to beads

Schema addition: delegation links between work units. Fields: parent, child, delegated_by, delegated_to, terms. Enables work distribution with credit cascade.


Move disk 1: C → A

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


Support numeric shortcuts in mail read (e.g., 'mail read 1')

When inbox shows numbered messages like:
* 1. gm-19b29031...  2025-12-16  mayor  Subject...
* 2. gm-19b26d51...  2025-12-16         Subject...

Users should be able to run 'gt mail read 1' instead of needing the full message ID 'gt mail read gm-19b29031f6a172206'.

Implementation:
- Track inbox message order in display
- Map numeric indices to actual message IDs
- Accept both numeric shortcuts and full IDs in 'mail read' command


Move disk 1: B → C

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


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol complete: checked inbox (0 msgs), health check passed (all rigs healthy), cleaned 3 stale locks


Digest: mol-deacon-patrol

Patrol 2: No changes from cycle 1, all healthy


Implement sendToQueue() for queue message delivery

Add queue message delivery to internal/mail/router.go.

SEMANTICS:
- Queue messages are stored in a shared location, NOT individual inboxes
- Location: {townRoot}/.beads/ with special metadata

MESSAGE METADATA:
Add to message struct or use existing fields:
- queue_name: which queue this belongs to
- claimed_by: empty until claimed
- claimed_at: timestamp when claimed

IMPLEMENTATION:
1. Add sendToQueue(msg *Message) error
2. Validate sender is eligible (optional, or allow anyone to post)
3. Create message bead with queue_name in metadata
4. Message To field should be 'queue:{name}'

FILE: internal/mail/router.go
PATTERN: Follow sendToList() at line 567 but create ONE copy, not fan-out

Do not implement claim/release yet - just delivery.


Polecat doesn't auto-start after spawn inject - requires manual nudge

## Problem

After `gt spawn --issue <id> --create`, the polecat session shows Claude Code started with the injected prompt, but Claude doesn't begin processing. The prompt just sits there until manually nudged.

## Evidence

```
$ gt spawn --issue gt-rixa --rig gastown --create
...
✓ Session started. Attach with: gt session at gastown/furiosa
  Polecat nudged to start working
  Witness notified to monitor startup
```

Session shows:
```
> You have a work assignment. Run 'gt mail inbox' to see it, then start
  working on issue gt-rixa.

  ⏵⏵ bypass permissions on (shift+tab to cycle)
```

But Claude doesn't respond. Manual nudge required:
```
$ gt nudge gt-gastown-furiosa "Please start working..."
```

After nudge, polecat immediately starts working correctly.

## Hypothesis

The spawn inject happens before Claude Code is fully initialized. The text arrives in the input buffer, but Claude hasn't started listening yet. By the time Claude starts, the input has already been 'consumed' as initial prompt text but not submitted.

## Resolution Plan

This will be solved by the **polecat molecule workflow** (mol-polecat-work), which provides structured lifecycle management. The molecule approach handles startup, work, and shutdown as discrete steps with proper state tracking.

**Blocked on**: beads/crew/dave completing ephemeral molecules (bd mol bond, ephemeral beads repo).

## Workaround

For now, use `gt nudge` if a polecat doesn't start within ~30 seconds of spawn.


Move disk 2: A → C

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


Digest: mol-deacon-patrol

Patrol cycle 13: All healthy


Digest: mol-deacon-patrol

Patrol cycle 15: All healthy


Digest: mol-deacon-patrol

Patrol 31: all quiet


Move disk 4: B → A

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


gt spawn/crew setup should create .beads/redirect for worktrees

Crew clones and polecats need a .beads/redirect file pointing to the shared beads database (../../mayor/rig/.beads). Currently:

- redirect files can get deleted by git clean
- not auto-created during gt spawn or worktree setup
- missing redirects cause 'no beads database found' errors

Found missing in: gastown/joe, beads/zoey (after git clean)

Fix options:
1. gt spawn creates redirect during worktree setup
2. gt prime regenerates missing redirects
3. bd commands auto-detect worktree and find shared beads

This should be standard Gas Town rig configuration.


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

bd ready should prioritize issues that are blocking other work.

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

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

Checks dependency graph. Issues with many dependents surface first.

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

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


Digest: mol-deacon-patrol

Patrol 14: Routine


Digest: mol-deacon-patrol

Cycle 193: Nominal


Move disk 2: A → C

Move disk 2 from peg A to peg C. (Move 518/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.


Create patrol formula YAML files

Create formula YAML files for:
- mol-deacon-patrol.formula.yaml
- mol-witness-patrol.formula.yaml
- mol-refinery-patrol.formula.yaml

Use the step definitions currently in molecules_patrol.go as reference.
Store in .beads/formulas/


Remove pinned field workaround after beads fix

## Summary

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

## Current Workaround

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

## Changes After Beads Fix

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

## Blocked By

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

## Related

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

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


Digest: mol-deacon-patrol

Patrol 14: all healthy


Merge: gt-si8rq.7

branch: polecat/nux-mjxkssaw
target: main
source_issue: gt-si8rq.7
rig: gastown
agent_bead: gt-gastown-polecat-nux


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 86: All healthy


mol-deacon-patrol

Mayor's daemon patrol loop.

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

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


Prompt templates: role contexts, nudge messages

Prompt templates for role contexts and messages.

## Template Types

### Role CLAUDE.md Templates

For gt prime and CLAUDE.md generation:
- Mayor template
- Witness template
- Refinery template
- Polecat template

### Message Templates

For notifications and nudges:
- Spawn assignment
- Witness nudge
- Escalation to Mayor
- Session handoff

## Implementation

```go
//go:embed templates/*.md.tmpl
var templateFS embed.FS

type Templates struct {
    fs embed.FS
}

func NewTemplates() *Templates
func (t *Templates) RenderRole(role string, data RoleData) (string, error)
func (t *Templates) RenderMessage(name string, data any) (string, error)
```

## Template Data

```go
type RoleData struct {
    Role      string
    RigName   string
    TownRoot  string
    Polecats  []string
    Commands  []CommandHelp
}

type SpawnData struct {
    Issue       string
    Title       string
    Priority    int
    Description string
}

type NudgeData struct {
    Polecat    string
    Reason     string
    NudgeCount int
    MaxNudges  int
}
```

## Template Location

Embedded in binary via go:embed:
```
internal/templates/
├── roles/
│   ├── mayor.md.tmpl
│   ├── witness.md.tmpl
│   ├── refinery.md.tmpl
│   └── polecat.md.tmpl
└── messages/
    ├── spawn.md.tmpl
    ├── nudge.md.tmpl
    └── escalation.md.tmpl
```

## Usage

gt prime uses role templates.
gt spawn uses spawn template.
Witness uses nudge/escalation templates.


Digest: mol-deacon-patrol

Patrol 7: quiet


Deacon Patrol

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


Move disk 2: A → C

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


Move disk 4: A → C

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


Merge: valkyrie-1767106008400

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


gt-gastown-polecat-delta

gt-gastown-polecat-delta

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


Move disk 4: C → B

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


Merge: gt-84ery

branch: polecat/dementus-mjtlqmya
target: main
source_issue: gt-84ery
rig: gastown


Digest: mol-deacon-patrol

Patrol 13: routine


Merge polecat/slit: docs bd mol bond/squash/burn CLI

Branch: polecat/slit

## Summary
Added comprehensive CLI reference documentation for the three molecule lifecycle commands to molecules.md:

- **bd mol bond**: Instantiate proto into Mol (durable) or Wisp (ephemeral)
- **bd mol squash**: Complete molecule and generate digest
- **bd mol burn**: Abandon molecule without digest

Includes argument tables, behavior descriptions, examples, and a lifecycle diagram showing the steam engine metaphor mapping.

Closes: gt-odvf


Add bulk polecat remove command (gt polecat remove --all)

When decommissioning a rig, need to remove multiple polecats one at a time. A --all or --rig flag would allow: gt polecat remove --rig gastown --force


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.


Digest: mol-deacon-patrol

Patrol 14: all healthy


Digest: mol-deacon-patrol

Patrol cycle 4: routine check, all nominal


Witness should verify polecat submitted before stopping session

## Problem

Witness stopped dementus's session before it could call `gt done`, losing the MR submission. I had to manually push and submit the branch.

## Root Cause

Witness cleanup is triggered by nudge/manual check rather than by receiving POLECAT_DONE message. When Witness cleans up based on issue status (closed), it doesn't wait for the polecat to complete its shutdown sequence.

## Expected Behavior

Witness should only stop a polecat session after:
1. Receiving POLECAT_DONE message from that polecat, OR
2. Timeout waiting for POLECAT_DONE after issue is closed

## Current Behavior

Witness stops sessions immediately when asked to check for completions, even if polecat hasn't called `gt done` yet.

## Fix

In mol-witness-patrol inbox-check step:
- Only clean up polecats that have sent POLECAT_DONE
- For polecats with closed issues but no DONE message, nudge them to complete
- Add timeout before force-killing


LESSONS: Idle Polecat Incident 2026-01-02

# Incident Summary

On 2026-01-02, discovered 9 polecats (5 gastown, 4 beads) sitting idle at Claude prompts with completed work, burning money.

## Root Cause Chain

1. **Polecats completed work, submitted MRs** - correct
2. **Refinery merged work, sent MERGED to Witness** - correct
3. **Witness had 50 unread protocol messages** - BUG
4. **Witness not processing mail = no cleanup** - BUG
5. **Polecats waiting for death that never comes** - SYMPTOM

## Why Witness Wasn't Patrolling

Witness is a Claude agent with template instructions. When it finishes one patrol cycle, it stops. The template doesn't emphasize continuous operation.

Claude agents are stateless between turns - they complete a task and wait for next input. Without continuous prompting, they idle.

## Design Flaw

The current model:
```
Polecat → gt done → WAIT for Witness to kill
Witness → process mail → nuke polecat
```

**Problem**: If Witness doesn't patrol, polecats wait forever.

## Fixes Implemented

1. **gt-lynar**: `gt done --exit` - polecats self-terminate after MR submission
2. **gt-x4ad3**: Update polecat template to use --exit
3. **gt-fo9iz**: Nuke safety check - verify work on main, not just 'pushed'

## Lessons

### L1: Agents Should Self-Terminate When Done
Don't rely on external actors to clean up. If an agent finishes work, it should exit immediately. Waiting = burning money.

### L2: Mail Is Not Reliable Signaling
Claude agents may not process mail continuously. Don't design protocols that require prompt mail processing.

### L3: Safety Checks Must Match Reality
After branch merge+delete, local branch looks 'unpushed' even though work is on main. Safety checks should verify actual state (is work on main?) not proxy state (is branch pushed?).

### L4: Idle State Is A Bug
Gas Town's propulsion principle: agents are pistons that fire. Idle agents violate this. If an agent can be idle, the design is wrong.

### L5: Monitor What Matters
We had convoy status, polecat lists, etc. But no alert for 'polecat at prompt doing nothing'. Add monitoring for actual idle time.

## Future Prevention

1. Polecat template: always use `gt done --exit`
2. Witness template: emphasize continuous patrol (or replace with daemon)
3. Add `gt doctor` check: 'polecats at prompt for >N minutes'
4. Consider: Replace Witness with daemon (more reliable than Claude patrol)


Digest: mol-deacon-patrol

Patrol 1: All healthy, no messages


Move disk 1: C → A

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


Move disk 1: A → B

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


Digest: mol-deacon-patrol

Patrol 2: Routine cycle. All rigs healthy. 12 polecats. No incidents.


Remove keepalive file infrastructure

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

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

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

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

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

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


Phase 5.2: Ephemeral error handling and recovery

Handle edge cases in ephemeral molecule lifecycle.

## Scenarios

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

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

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

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

## Implementation

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


Update deacon.md.tmpl with correct molecule commands

The deacon prompt references commands that don't exist:
- gt mol bond → should be bd mol run or gt mol arm
- gt mol status → needs gt mol command tree first

Update after gt mol command tree is implemented.

Depends on: gt mol command tree issue


Digest: mol-deacon-patrol

Patrol cycle 18: nominal


Enhanced polecat decommission prompting

Add decommission checklist to polecat AGENTS.md.template. Make crystal clear: verify ALL before signaling done.

## Checklist for AGENTS.md.template

```markdown
## Decommission Checklist

**CRITICAL**: Before signaling done, you MUST complete this checklist.
The Witness will verify each item and bounce you back if dirty.

### Pre-Done Verification

```bash
# 1. Git status - must be clean
git status
# Expected: "nothing to commit, working tree clean"

# 2. Stash list - must be empty
git stash list
# Expected: (empty output)

# 3. Beads sync - must be up to date
bd sync --status
# Expected: "Up to date" or "Nothing to sync"

# 4. Branch merged - your work must be on main
git log main --oneline -1
git log HEAD --oneline -1
# Expected: Same commit
```

### If Any Check Fails

- **Uncommitted changes**: Commit them or discard if unnecessary
- **Stashes**: Pop and commit, or drop if obsolete
- **Beads out of sync**: Run `bd sync`
- **Branch not merged**: Complete the merge workflow

### Signaling Done

Only after ALL checks pass:

```bash
bd close <issue-id>
bd sync
town mail send <rig>/witness -s "Work Complete" -m "Issue <id> done."
```
```

## Implementation

Add to AGENTS.md.template in the polecat prompting section.


gt spawn: Not injecting work instructions to session

gt spawn starts session but doesn't inject the issue assignment.

Repro:
1. gt spawn gastown/Toast --issue gt-2ux
2. Session starts but polecat just sees Claude prompt
3. No issue context injected

Expected: Polecat should receive issue details automatically.
Actual: Polecat sits at blank prompt, needs manual injection.


Digest: mol-deacon-patrol

Patrol 2: Quick scan, no changes


Add molecule to update local go binary on push to main

When pushing to main branch, automatically rebuild and install the gt binary on the local machine so changes are immediately available.


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)


Digest: mol-deacon-patrol

Patrol 4: All agents healthy, routine


Digest: mol-deacon-patrol

Patrol 139: All healthy


Clean up ~/gt root directory cruft

The town root has accumulated various directories and files that need review:

**To evaluate:**
- daemon/ - old daemon code? move or delete?
- deacon/ - WIP deacon work? consolidate with gastown?
- mayor/ - old mayor structure?
- AGENTS.md - is this still used?
- gt binary at root - should be gitignored

**Already correct:**
- .beads/, .claude/, .gastown/, .runtime/ - runtime dirs
- beads/, gastown/ - rig directories
- docs/hop/ - strategic docs
- CLAUDE.md - mayor context

Clean up, consolidate, update .gitignore as needed.


gt handoff: Deadlock in waitForRetirement


Epic: Wisp Molecule Integration

## Vision

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

## The Steam Engine Metaphor

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

## Architecture

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

## Vocabulary

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

## Key Design Decisions

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

## Digest Contents

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

## Integration Points

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

## Phases

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


Move disk 3: A → B

Move disk 3 from peg A to peg B. (Move 76/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.


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol cycle 8: All healthy


Digest: mol-deacon-patrol

Patrol 4: quiet, health OK


Digest: mol-deacon-patrol

Patrol 1: Closed 2 complete convoys (heartbeat fix, refinery fix). All 3 rigs healthy. 12 polecats active. No zombies, no orphans, inbox clear.


Digest: mol-deacon-patrol

Patrol 9: All clear


Digest: mol-deacon-patrol

Patrol #10: Halfway checkpoint


Digest: mol-deacon-patrol

Patrol 9: routine


Move disk 1: C → A

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


Session ended: gt-gastown-ace


Digest: mol-deacon-patrol

Patrol 2: No callbacks, 2 convoys in progress, all agents healthy


Merge: furiosa-1767081103575

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


Merge: toast-1767081120579

branch: polecat/toast-1767081120579
target: main
source_issue: toast-1767081120579
rig: gastown


Digest: mol-deacon-patrol

Patrol complete: all healthy, no events


Digest: mol-deacon-patrol

Patrol 57: All healthy


Digest: mol-deacon-patrol

Patrol 208: All nominal


Move disk 4: B → A

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


Move disk 1: C → A

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


mol-deacon-patrol


Move disk 2: C → B

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


Move disk 1: B → C

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


Shutdown request handler

Handle polecat shutdown requests:

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

Depends on fixing gt-dsfi (handoff deadlock).


Merge: nux-mjw3mn8o

branch: polecat/nux-mjw3mn8o
target: main
source_issue: nux-mjw3mn8o
rig: gastown
agent_bead: gt-gastown-polecat-nux


Digest: mol-deacon-patrol

P20: stable - handoff triggered


Digest: mol-deacon-patrol

Patrol 5: Quiet


Move disk 1: B → C

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


Digest: mol-deacon-patrol

Cycle 17


Merge: dementus-mjtlqmya

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


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

Minor typo in error messages at two locations:

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

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

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


Merge: toast-mjtm1hg8

branch: polecat/toast-mjtm1hg8
target: main
source_issue: toast-mjtm1hg8
rig: gastown


Digest: mol-deacon-patrol

Patrol 19


Digest: mol-deacon-patrol

Patrol 14: All healthy


Move disk 1: A → B

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


Digest: mol-deacon-patrol

Patrol 17


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

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

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 4: quiet


Move disk 2: A → C

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


gt crew add: Create crew workspace

Implement 'gt crew add <name> [--rig <rig>]' command:
- Clone repo into <rig>/crew/<name>/
- Create feature branch (optional, or stay on main)
- Register for mail delivery
- Initialize CLAUDE.md with crew worker prompting
- Do NOT register with witness (user-managed)


Merge: gt-rana.1

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


Move disk 4: C → B

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


Molecule execution support for polecats and crew

Enable agents to run bonded molecules through the full lifecycle.

## The Model
1. BOND - bd mol bond <template> --assignee <identity>
   Creates concrete issues, assigns root to agent

2. DISCOVER - Agent finds assigned molecules via bd ready
   DAG structure shows unblocked steps

3. WORK - Agent works through DAG, closing steps as done
   Can delegate children to lower-tier agents (haiku)

4. SURVIVE - Agent dies → beads persist
   Any agent resumes from DAG state

5. SUPERVISE - Witness monitors for stalled molecules
   Nudges owner or reassigns if dead

## Questions to Resolve
- Seed node terminology: seed vs root vs pole vs nucleus
- Assignee inheritance: single owner vs delegation model
- Stall detection: heartbeat vs activity timeout vs session monitoring

## Implementation
- Polecat startup: check for assigned molecules
- Polecat work loop: follow molecule DAG
- Witness: monitor molecule progress, detect stalls
- Mayor: can assign molecules, handle escalations

## Depends On
- beads: bd mol bond command (bd-usro in beads repo)


Digest: mol-deacon-patrol

Patrol cycle 13: All healthy.


Digest: mol-deacon-patrol

Patrol cycle 13: All 6 agents healthy


Digest: mol-deacon-patrol

Patrol 12: all clear


Fix out-of-sync molecule test expectations

Tests in builtin_molecules_test.go have hardcoded expectations that no longer match actual molecules:
- TestBuiltinMolecules: expects 9 molecules, got 11
- TestPolecatWorkMolecule: step refs out of sync
- TestDeaconPatrolMolecule: step count and refs out of sync

This is pre-existing on main, not introduced by any polecat work.


Document crew worker patrol integration

Document how mol-ready-work integrates with crew worker workflow. Cover: starting the patrol, patrol vs normal operation, backlog configuration, handoff mechanics, discovered work handling. Update CLAUDE.md with patrol section.


Move disk 2: B → A

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


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)


stderr suppression hides critical errors

Multiple files set cmd.Stderr = nil:
- prime.go
- up.go
- orphans.go
- daemon.go

This hides git errors and command failures.

Should capture stderr for debugging and log when errors occur,
rather than suppressing entirely.


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: C → A

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


Merge: dag-mjxpcv5v

branch: polecat/dag-mjxpcv5v
target: main
source_issue: dag-mjxpcv5v
rig: gastown
agent_bead: gt-gastown-polecat-dag


Digest: mol-deacon-patrol

Patrol 17: all quiet


Digest: mol-deacon-patrol

Patrol 12: Quick cycle, all quiet


Move disk 3: C → A

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


Move disk 3: A → B

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


Digest: mol-deacon-patrol

Patrol 114: All healthy


Swarm learning: Refinery merge queue automation

Manually merging 15 polecat branches was painful and error-prone. Refinery should automate: detect completed work, run tests, merge to main, handle conflicts. This is core Refinery value prop.


Adaptive backoff for daemon heartbeat

Track agent responsiveness and adjust heartbeat frequency.

## Per-Agent State

```go
type AgentBackoff struct {
    BaseInterval    time.Duration  // 60s default
    CurrentInterval time.Duration  // grows when busy
    MaxInterval     time.Duration  // 10min cap
    ConsecutiveMiss int            // pokes with no response
}
```

## Strategy Options

- **Fixed**: Always 60s (current, simple)
- **Geometric**: 60s → 90s → 135s → 202s (factor 1.5)
- **Exponential**: 60s → 120s → 240s (factor 2, aggressive)

## Recovery

When agent responds (runs a command):
- Reset ConsecutiveMiss to 0
- Return to BaseInterval immediately

## Benefits

- Reduces noise for busy agents
- Saves resources during quiet periods
- Still catches stuck agents (max interval cap)


Witness arm bonding integration test

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


Digest: mol-deacon-patrol

Patrol cycle 1: All healthy. 3 rigs, all Witnesses/Refineries responding. No orphans, no cleanup needed, inbox clean.


Test refile v2

Testing improved refile

(Refiled from bd-eysh)


Move disk 1: A → B

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


Move disk 4: C → B

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


Digest: mol-deacon-patrol

Patrol 3: all healthy, mayor running, witnesses running


Digest: mol-deacon-patrol

Patrol 81: All agents healthy, 2 epics in progress, 1 idle dog, inbox clear


Swarm status doesn't update after task completions

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

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

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


Extract timing constants to constants package

Extract hardcoded timing values to named constants.

## Files to modify
- internal/constants/constants.go (add constants)
- internal/cmd/start.go (use constants)
- internal/tmux/tmux.go (use constants)

## Implementation
Add to constants.go:
```go
const (
    ShutdownNotifyDelay  = 500 * time.Millisecond
    ClaudeStartTimeout   = 15 * time.Second
    ShellReadyTimeout    = 5 * time.Second
    DefaultDebounceMs    = 100
    DefaultDisplayMs     = 5000
)
```

## Acceptance criteria
- [ ] 5 timing constants added to constants.go
- [ ] start.go uses ShutdownNotifyDelay, ClaudeStartTimeout, ShellReadyTimeout
- [ ] tmux.go uses DefaultDebounceMs, DefaultDisplayMs
- [ ] go build ./... passes
- [ ] go test ./... passes


gt crew start <rig> [name] | --all - start crew workers

Start crew workers in a rig. Can specify a name to start one, or --all to start all crew members in the rig.


Move disk 1: B → C

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


Expansion operators: expand and map (macros)

Implement macro-style expansion operators:
- expand(target, template) - apply template to single step
- map(molecule, template) - apply template to all matching steps

Templates use {target} substitution for step references.
Rule of Five is the canonical example.


Move disk 1: B → C

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


Merge: immortan-mjwlaw9o

branch: polecat/immortan-mjwlaw9o
target: main
source_issue: immortan-mjwlaw9o
rig: gastown
agent_bead: gt-gastown-polecat-immortan


Move disk 4: C → B

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


Digest: mol-deacon-patrol

Patrol 16: healthy


Move disk 3: A → B

Move disk 3 from peg A to peg B. (Move 604/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 7: quick pass


gt-gastown-polecat-toast

gt-gastown-polecat-toast

role_type: polecat
rig: gastown
agent_state: running
hook_bead: gt-38doh
role_bead: gt-polecat-role
cleanup_status: clean
active_mr: gt-4bi8o
notification_level: null


Move disk 1: C → A

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


Digest: mol-deacon-patrol

Patrol 10: all healthy


Digest: mol-deacon-patrol

Patrol 150 complete


Digest: mol-deacon-patrol

Patrol complete: healthy


Digest: mol-deacon-patrol

Patrol 5: all healthy


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: gt-oiv0

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


Merge: furiosa-mjw1p03l

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


Digest: mol-deacon-patrol

Patrol 18: Routine


Digest: mol-deacon-patrol

Patrol 11: routine


Witness: Reduce chattiness, increase autonomy

Witness is spamming Mayor with routine status:
- 'Polecat X processed' - routine, no action needed
- 'Idle polecats: X needs work' - wrong direction

Witness should be a local sheriff:
1. Handle routine lifecycle autonomously (don't report)
2. Only escalate genuine problems requiring human/mayor intervention
3. Use convoy system for batch 'work complete' notifications
4. Never ask Mayor to assign work - either do it yourself or let polecats idle

Remove these mail types:
- POLECAT_PROCESSED (routine)
- IDLE_POLECAT (wrong escalation direction)

Keep these:
- ESCALATION (genuine problem)
- BLOCKED (needs human decision)
- CRASH_RECOVERY (informational, useful)


Move disk 2: A → C

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


Move disk 1: A → B

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


Move disk 2: A → C

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


Beads as Universal Data Plane

## Vision

Beads is the data plane for all Gas Town operations. Everything flows through beads:
- Work items (issues, tasks, epics)
- Mail (messages between agents)
- Merge requests (queue entries)
- Workflows (composable execution patterns)
- Resources (leases, locks, quotas)
- Schedules (timed activities)

## New Bead Categories

### Molecules (Composable Workflows)
Crystallized workflow patterns that can be attached to work items.

### Timed Beads (Scheduled Work)
Beads that wake up periodically via daemon.

### Pinned Beads (Ongoing Concerns)
Beads representing persistent concerns, not discrete tasks.

### Resource Beads (Leases/Locks)
Beads representing reserved resources.

## v1 Priority

- **P0**: Molecules (enables engineer-in-box)
- **P2**: Timed, Pinned, Resource beads (post-v1)


Digest: mol-deacon-patrol

Patrol cycle 49: All healthy


Digest: mol-deacon-patrol

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


Digest: mol-deacon-patrol

Cycle 6: healthy


Digest: mol-deacon-patrol

Patrol 6: All agents healthy


Digest: mol-deacon-patrol

Patrol 159 complete


Digest: mol-deacon-patrol

Patrol 11: all clear


Merge: toast-1767140378007

branch: polecat/toast-1767140378007
target: main
source_issue: toast-1767140378007
rig: gastown


Execute registered plugins

Execute registered plugins.

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

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

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

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

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

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


Deacon: Hierarchical health-check orchestrator

Replace daemon heartbeats with Deacon - an AI agent that keeps Gas Town running.

## Core Concept

The Deacon is a **Claude agent** (not just a Go process) that:
- Lives at town root in gt-deacon session
- Has its own mailbox (deacon/ identity in town beads)
- Is poked by a minimal Go daemon every ~60s (if idle)
- Monitors Mayor and Witnesses proactively
- Handles lifecycle requests (restart/cycle) from Mayor, Witnesses, AND Crew

## Architecture

```
Minimal Go Daemon (just watches Deacon)
         |
         v
     Deacon (Claude agent)
         |
    +----+----+
    v         v
  Mayor    Witnesses --> Polecats (Witness-managed)
    |         |
    +----+----+
         |
      Crew (lifecycle only, not monitored)
```

## Deacon Responsibilities

**Proactive monitoring:**
- Mayor health (tmux session, keepalive freshness)
- Witness health (tmux sessions, keepalive freshness)

**Reactive lifecycle:**
- Process restart/cycle requests from Mayor, Witnesses, Crew
- Kill session, create new, prime, verify startup

**Escalation:**
- Mail human (configurable) if issues can't be resolved

## Key Files

- ~/gt/DEACON.md - Role context
- ~/gt/deacon/ - State directory
  - heartbeat.json - Written each wake cycle (daemon checks this)
  - state.json - Health tracking, last scan results
- ~/gt/.beads/ - Town beads with deacon/ mail identity

## Wake Cycle

1. Write heartbeat (prevents daemon from poking)
2. Check mail (lifecycle requests)
3. Quick health scan (Mayor, Witnesses)
4. Process lifecycle requests
5. Remediate unhealthy agents
6. Update state

## Session Patterns

- Deacon: gt-deacon
- Mayor: gt-mayor
- Witness: gt-<rig>-witness
- Crew: gt-<rig>-<name> (e.g., gt-gastown-max)

## Relation to Policy Beads

Future: Deacon behavior configurable via policy beads (gt-3zw).
Initial implementation uses sensible defaults.


Update Mayor prompting in gastown-py

The Mayor CLAUDE.md and related prompting in gastown-py (still in production use) needs to reflect current design decisions: session cycling, handoff protocol, cleanup responsibilities, beads access model. Sync prompting with GGT design work.


Digest: mol-deacon-patrol

Patrol 14: All healthy, 2 crew active


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.


bd list: Add type aliases (mr→merge-request)

The --type flag requires exact matches. Add aliases for convenience:

- mr → merge-request
- feat → feature
- mol → molecule

**Context**: Refinery cleanup missed 6 open MRs because `--type=mr` returned nothing while `--type=merge-request` found them.

**Implementation**: In the type filter parsing, normalize aliases before matching.


Move disk 4: C → B

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


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.


Add hook-attachment-valid doctor check

Add gt doctor check to verify attached molecules exist and are not closed.

Check: hook-attachment-valid
Error if: Hook's attached_molecule field points to non-existent or closed issue
Fix suggestion: Clear attachment with gt mol detach


Test


Digest: mol-deacon-patrol

Patrol 6: All healthy.


Create DEACON.md role context

Create ~/gt/DEACON.md with the Deacon's role context, wake cycle instructions, and command reference. Model after existing CLAUDE.md for Mayor.


Merge: valkyrie-mjxpdngw

branch: polecat/valkyrie-mjxpdngw
target: main
source_issue: valkyrie-mjxpdngw
rig: gastown
agent_bead: gt-gastown-polecat-valkyrie


Move disk 1: B → C

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


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


Wisp squash design: cadences, rules, templates

Design how wisps squash to digests: When to squash (cadences), what to include (summary templates), retention rules. Currently under-designed. Should cover patrol wisps (squash per cycle) and work wisps (squash on completion). Consider allowing formulas to define squash templates.


Digest: mol-deacon-patrol

Patrol 210: Deep health check - 3 witnesses, 3 refineries, 10 polecats all healthy


Merge: gt-4ev4

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


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 101: All agents healthy, 3 rigs checked, no incidents


Parameterize 'read org docs' step in polecat workflows

## Problem

The mol-polecat-chrome formula has hardcoded paths to strategic docs:
- ~/gt/docs/hop/CONTEXT.md
- ~/gt/docs/PRIMING.md

This limits reusability across different projects/organizations.

## Solution

Add a `context_docs` variable to the formula that takes a list of document paths:

```toml
[vars.context_docs]
description = "Paths to organizational context documents to read before design"
default = ["~/gt/docs/hop/CONTEXT.md", "~/gt/docs/PRIMING.md"]
```

## Usage at cook/wisp time

```bash
# Use defaults
gt sling gt-abc gastown -q chrome

# Override for different project
gt sling gt-abc gastown -q chrome   --var context_docs="docs/ARCHITECTURE.md,docs/PRINCIPLES.md"
```

## Step template

The step description would interpolate the paths:

```toml
[[steps]]
id = "read-strategic-context"
title = "Read organizational context documents"
description = """
Read the following strategic context documents before designing:

{{#each context_docs}}
- {{this}}
{{/each}}

These contain the vision, principles, and constraints that should guide your work.
"""
```

## Considerations

1. How to handle the array in TOML var syntax?
2. Should this be a reusable step fragment or inline in chrome?
3. Default paths should work for Gas Town but be overridable

## Related
- mol-polecat-chrome.formula.toml (current hardcoded implementation)


E2E Test: Verify mol-polecat-work spawn

Simple test task for Phase 5 E2E validation


Digest: mol-deacon-patrol

Patrol 10: Routine. All 3 rigs healthy. 12 polecats. 1 MQ pending.


Wire polecat-CLAUDE.md template into polecat spawn

The templates/polecat-CLAUDE.md file needs to be copied into each polecat worktree when spawned.

Location: templates/polecat-CLAUDE.md
Target: polecats/<name>/CLAUDE.md (with {{rig}} and {{name}} substituted)

Implementation:
- Update gt polecat spawn (or wherever worktrees are created)
- Template substitution for {{rig}}, {{name}} variables
- Ensure file is created before Claude session starts


bd: Add town-level activity signal in PersistentPreRun

Add activity signaling to beads so daemon can detect bd usage.

In cmd/bd/main.go PersistentPreRun, add a call to write activity to
the Gas Town daemon directory if running inside a Gas Town workspace.

The signal file is ~/gt/daemon/activity.json (or detected town root).

Format:
{
  "last_command": "bd create ...",
  "actor": "gastown/crew/max",
  "timestamp": "2025-12-26T19:30:00Z"
}

Should be best-effort (silent failure) to avoid breaking bd outside Gas Town.


Merge: testcat-mjtk387i

branch: polecat/testcat-mjtk387i
target: main
source_issue: testcat-mjtk387i
rig: gastown


Timer gate evaluation

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


Move disk 1: B → C

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


Digest: mol-deacon-patrol

Patrol 13: OK


gt mq submit: creates task type instead of merge-request type

## Problem

`gt mq submit` creates an issue with `type: task` but should be `type: merge-request`.

## Evidence

```
$ bd show gt-n508
gt-n508: Merge: gt-70b3
Status: open
Priority: P1
Type: task      <-- WRONG, should be merge-request
...
Description:
type: merge-request   <-- Correct type is in description, not in actual type field
```

## Impact

`gt mq list` shows empty queue because it queries for `type: merge-request`

## Fix

`gt mq submit` should set `--type merge-request` when creating the bead.


Comprehensive help text and examples

Improve help text with examples and cross-references.

## Improvements

### 1. Examples Section
Add to Long description:
```go
var spawnCmd = &cobra.Command{
    Long: `Spawn a polecat with work assignment.

Examples:
  gt spawn gastown/Toast --issue gt-abc
  gt spawn gastown --issue gt-def          # auto-select polecat
  gt spawn gastown/Nux -m "Fix the tests"  # free-form task`,
}
```

### 2. Cross-References
Reference related commands:
```
See also:
  gt polecat list    List available polecats
  gt session attach  Attach to spawned session
```

### 3. Flag Descriptions
More detail on flags:
```go
cmd.Flags().StringVar(&issue, "issue", "",
    "Beads issue ID to assign. The polecat will work on this issue.")
```

### 4. Common Workflows
Add workflow docs to gt --help:
```
Common Workflows:
  Start a swarm:
    gt swarm preflight
    gt swarm create gastown --epic gt-abc --worker Toast --worker Nux --start
    gt refinery start gastown

  Check status:
    gt status
    gt swarm status <id>
```

## Files to Update
All internal/cmd/*.go files

## Acceptance Criteria
- [ ] All commands have Examples
- [ ] Related commands cross-referenced
- [ ] Flags have detailed descriptions
- [ ] Root help shows workflows


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 cycle 5: all healthy


Bug: refinery --foreground detects parent session as already running

When gt refinery start gastown runs:
1. Creates tmux session gt-gastown-refinery
2. Sends 'gt refinery start gastown --foreground' into the session
3. The foreground command checks HasSession() - finds the session it's inside
4. Returns 'already running' error

The foreground mode check should either:
- Skip the tmux session check (only check PID)
- Use a different indicator that the daemon loop is running
- Pass a flag to indicate we're being called from the background starter

Workaround: Manually run 'gt refinery start gastown --foreground' from a fresh session.


Move disk 2: A → C

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


mol-deacon-patrol


Switch formulas from JSON to TOML

## Problem
Formula JSON files have poor ergonomics for agents and humans:
- Multi-line strings require `\n` escaping
- Diffs are hard to read (long single-line changes)
- No comments allowed

## Solution
Switch formulas to TOML format:

```toml
formula = "mol-deacon-patrol"
version = 1
description = """
Mayor's daemon patrol loop.
Multi-line strings work naturally.
"""

[[steps]]
id = "inbox-check"
title = "Handle callbacks"
description = """
Check inbox for messages.

```bash
gt mail inbox
```
"""
```

## Tasks
- [ ] Add TOML parsing for formulas (github.com/BurntSushi/toml)
- [ ] Update formula loader to try .toml first, fall back to .json
- [ ] Add `bd formula convert <name>` to migrate JSON → TOML
- [ ] Convert existing formulas
- [ ] Update docs

## Notes
- Keep issues.jsonl as-is (machine data, append-only)
- TOML only for human-edited formula files


Digest: mol-deacon-patrol

Patrol 18


Digest: mol-deacon-patrol

P20: stable - handoff after 20 patrols


Swarms of One: Lightweight single-worker task dispatch

Design and implement a lightweight pattern for firing off single workers to handle tasks without full swarm overhead.

## Context

Currently we have:
- town spawn: Creates a polecat in a rig with issue assignment
- Swarms (sw-*): Full lifecycle tracking with manifest, state, events, reports
- Ephemeral rigs (rig-*): Temporary worker groups for swarms

What's missing: A simple way to say 'fire off a worker to do X' without swarm ceremony.

## Design Questions

1. Should this be a new command like 'gt fire' or an option on existing commands?
2. Should single tasks still get swarm IDs (sw-N) for consistency/queryability?
3. Should it default to creating new workers or support --reuse for idle polecats?
4. How does this relate to crew workers (gt-cik)?

## Possible Interface

gt fire --rig gastown --issue gt-xyz [--prompt '...']
gt fire gastown/QuickTask --issue gt-xyz

## Related

- gt-cik: Overseer Crew (user-managed persistent workspaces)
- gt-kmn: Swarm System epic


Digest: mol-deacon-patrol

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


Digest: mol-deacon-patrol

Patrol 17 complete


Digest: mol-deacon-patrol

Patrol 8: All healthy.


CRITICAL: ProcessMRFromQueue not implemented - Refinery merge logic stubbed

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


Move disk 2: B → A

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


Activity feed improvements

Make the activity feed (gt feed) a first-class TUI experience using charmbracelet.

## Vision

A real-time dashboard showing all Gas Town activity with:
- Agent tree organized by role (mayor, witnesses, crew, polecats)
- Live event stream
- Vim-style navigation
- Filtering and search

## Architecture

Two-tier event system:
- ~/gt/.events.jsonl (raw, audit) - all events from gt, bd, agents
- Feed daemon curates → ~/gt/.feed.jsonl (user-facing) - filtered, deduped

TUI reads from curated feed for smooth UX.

## Layout (Dashboard + Stream)
```
┌─────────────────────────────────────────────────────────────┐
│ GT Feed                                    Filter: all      │
├─────────────────────────────────────────────────────────────┤
│ gastown/                                                    │
│   🎩 mayor      [10:30] Dispatched gt-xyz → Toast           │
│   👁 witness    [10:28] Nudged polecat-3 (idle 5m)          │
│   🏭 refinery   [10:25] Merged PR #123                      │
│   👷 crew/                                                   │
│      joe        [10:22] ✓ Completed gt-95j13                │
│      max        [10:18] → Working on gt-s6r44               │
│   😺 polecats/                                               │
│      Toast      [10:30] Received gt-xyz, starting...        │
├─────────────────────────────────────────────────────────────┤
│ [10:30] 😺 Toast: Received molecule gt-xyz                  │
│ [10:28] 👁 witness: Nudged polecat-3 after 5m idle          │
│ [10:25] 🏭 refinery: Merged PR #123                         │
├─────────────────────────────────────────────────────────────┤
│ j/k: scroll  /: search  f: filter  q: quit  ?: help         │
└─────────────────────────────────────────────────────────────┘
```

## Key Features
1. Real-time updates - events stream in live
2. Agent tree - all agents grouped by role with status
3. Vim-style navigation - j/k, gg/G, /search
4. Filtering - by rig, role, event type, time
5. Collapsible sections
6. Details on select

## Tech Stack
- bubbletea - TUI framework
- bubbles - viewport, list components
- lipgloss - styling (already have)

## Success Criteria
- Feed is beautiful and scannable at a glance
- Agent activity visible in real-time
- Works in any terminal, degrades gracefully for pipes


Digest: mol-deacon-patrol

Patrol cycle 10


Move disk 1: B → C

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


Digest: mol-deacon-patrol

Patrol 5: nominal