Deacon marching orders: pinned bead with rounds molecule

Create a pinned bead that defines the Deacon's rounds molecule - its "kernel".

## Concept

Like `gt prime` gives agents their context, a pinned bead gives the Deacon
its operating instructions. This bead defines:

1. The rounds molecule (steps to execute each wake)
2. Registered plugins (scheduled, event-triggered)
3. Configuration (timeouts, thresholds, escalation rules)

## Pinned Bead Structure

```yaml
# gt-deacon-kernel (pinned)
title: "Deacon Kernel Configuration"
type: policy
pinned: true

molecule: mol-deacon-rounds
  steps:
    - session-check: "Check context budget, cycle if needed"
    - health-scan: "Check Mayor, Witnesses, Refineries, Crew"
    - lifecycle: "Process pending lifecycle requests"
    - plugins: "Run due scheduled plugins"
    - events: "Process timer callbacks, event subscriptions"
    - complete: "Update heartbeat, return to wait"

plugins:
  scheduled:
    - name: beads-hygiene
      schedule: "0 2 * * *"  # 2 AM daily
    - name: branch-cleanup
      schedule: "0 4 * * 0"  # 4 AM Sundays

  event_triggers:
    - event: "issue.created"
      plugin: work-oracle
    - event: "mr.submitted"
      plugin: review-oracle

config:
  session_cycle_threshold_turns: 50
  session_cycle_threshold_hours: 4
  health_scan_timeout_seconds: 60
  plugin_timeout_minutes: 30
  escalation_after_failures: 3
```

## Reading Marching Orders

On wake, Deacon:
1. `bd show --pinned` to find the kernel bead
2. Parse molecule steps
3. Execute in order

## Updating the Kernel

To change Deacon behavior:
1. `bd update gt-deacon-kernel` with new config
2. Next wake cycle picks up changes
3. No code changes required

## Bootstrap

If no pinned bead exists, Deacon uses hardcoded defaults from DEACON.md.
First installation creates the pinned bead.


Digest: mol-deacon-patrol

Patrol 17: All agents healthy


Digest: mol-deacon-patrol

Patrol 143: 2 polecats (nux, rictus) working on formula tasks, 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.

## 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: A → B

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


Convert formula files from YAML to JSON

Formulas currently use .formula.yaml but the project avoids YAML. Convert to .formula.json for consistency with the rest of the codebase. This blocks implementing compose operators (advice, expand, etc.) since the schema needs to be right first.


gt rig reset: clear stale mail on reset/land

## Problem

When resetting or landing a rig/town, stale mail messages can confuse agents on startup. Old handoff messages, daemon notifications, and inter-agent mail should be cleaned up as part of reset.

## Current State

- `gt rig reset` exists but doesn't clear mail
- Stale messages accumulate (e.g., daemon SHUTDOWN messages)
- Agents may read outdated context on startup

## Proposed Behavior

`gt rig reset` and `gt town reset` should:
1. Close all open messages (`--type=message`) in the relevant beads
2. Optionally preserve pinned handoff beads (clear content, keep bead)
3. Log what was cleaned up

```bash
gt rig reset gastown          # Clears gastown mail
gt rig reset gastown --mail   # Only clear mail, keep other state
gt town reset                 # Clears all town-level mail
gt town reset --all           # Clears mail in all rigs too
```

## Implementation

1. Query `bd list --type=message --status=open`
2. Close each with reason 'Cleared during reset'
3. For pinned handoffs: `bd update <id> --description=''` instead of close


Witness swarm integration

Add swarm coordination to Witness patrol.

The Witness should be able to:
1. Receive swarm assignments via mail (SWARM: subject prefix)
2. Query ready front and dispatch to available polecats
3. Monitor polecat progress on swarm tasks
4. Detect completion and close swarm molecule
5. Use Christmas Ornament pattern for arm-bonding (optional)

Implementation:
- Add swarm handling to mol-witness-patrol formula
- Mail trigger: SWARM in subject
- Dispatch: bd ready --parent=<epic> to find ready issues
- Assign: gt sling <issue> to available polecat
- Monitor: Check polecat status during patrol
- Complete: When bd ready --parent returns empty and all closed

Reference: ~/gt/docs/swarm-architecture.md - Witness Integration section

Dependencies: gt-kc7yj.1 (gt swarm refactor)


Move disk 1: C → A

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


Move disk 2: C → B

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


mol-deacon-patrol


Move disk 1: A → B

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


Crew worker CLAUDE.md prompting

Create CLAUDE.md template for crew workers:
- Explain crew worker role (overseer's personal workspace)
- Include mail-to-self handoff instructions
- Document gt crew refresh for context cycling
- Explain no witness monitoring (user-managed)
- Include beads usage if BEADS_DIR configured


CLI: plugin commands (list, status)

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


Test shiny formula end-to-end

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 207: All nominal


Document session cycling protocol in all templates

Add explicit lifecycle request protocol to all agent templates.

## Problem
Templates mention 'request session refresh' but don't show HOW.
Agents don't know the protocol for requesting a cycle.

## Protocol to document
1. Write handoff mail to self (for continuity)
2. Set requesting_cycle=true in state.json
3. Send LIFECYCLE mail to deacon/:
   Subject: 'LIFECYCLE: <role> requesting cycle'
   Body: Reason for cycle request

## Templates to update
- prompts/roles/polecat.md
- prompts/roles/crew.md
- prompts/roles/witness.md
- prompts/roles/refinery.md

## Also add
- Example state.json location for each role
- When to request cycle (context full, work complete, etc.)
- What happens after (daemon kills, respawns, new session primes)


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

Patrol 15


Digest: mol-deacon-patrol

Patrol cycle 19: All healthy


Digest: mol-deacon-patrol

Patrol 7


Digest: mol-deacon-patrol

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


Move disk 2: A → C

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


Digest: mol-deacon-patrol

Patrol 260 complete: final cycle before handoff


Move disk 4: A → C

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


Digest: mol-deacon-patrol

Patrol cycle 1: All 3 rigs healthy. 0 messages processed. Witnesses/refineries pinged.


Digest: mol-deacon-patrol

Patrol cycle 17: routine


bd doctor: detect clone divergence emergencies

Add doctor check to detect:
1. Crew/Mayor on feature branches (should always be on main)
2. Significant divergence between clones that should be in sync
3. Distinguish from normal beads-sync vs main divergence (expected)

Context: bd sync --status shows all divergence as equal, but some is emergency (clones drifted) vs normal (sync branch mechanics).


Digest: mol-deacon-patrol

Patrol cycle 261: Cleaned 510+ stale wisps (57 hq, 450+ gt). All agents healthy (3 witnesses, 3 refineries, 10 polecats active). No incidents.


Merge queue: per-rig queue management

Per-rig merge queue for coordinating polecat work.

## Concept

When polecats complete work, they submit to the merge queue. The Refinery processes the queue, merging work to main in order.

## Data Model

```go
type MergeRequest struct {
    ID        string    `json:"id"`
    Polecat   string    `json:"polecat"`
    Branch    string    `json:"branch"`
    Issue     string    `json:"issue,omitempty"`
    Status    MRStatus  `json:"status"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

type MRStatus string
const (
    MRPending   MRStatus = "pending"
    MRReviewing MRStatus = "reviewing"
    MRMerged    MRStatus = "merged"
    MRRejected  MRStatus = "rejected"
)
```

## Interface

```go
type MergeQueue struct {
    rig  *Rig
    path string  // <rig>/refinery/queue.jsonl
}

func NewMergeQueue(rig *Rig) *MergeQueue

// Queue operations
func (mq *MergeQueue) Submit(polecat, branch string, issue string) (*MergeRequest, error)
func (mq *MergeQueue) List() ([]*MergeRequest, error)
func (mq *MergeQueue) Next() (*MergeRequest, error)  // oldest pending
func (mq *MergeQueue) Get(id string) (*MergeRequest, error)

// Status updates
func (mq *MergeQueue) SetStatus(id string, status MRStatus) error
func (mq *MergeQueue) MarkMerged(id string) error
func (mq *MergeQueue) MarkRejected(id, reason string) error
```

## Queue Storage

JSONL file at `<rig>/refinery/queue.jsonl`. FIFO ordering.

## Git Coordination

Refinery processes queue:
1. Fetch polecat branch
2. Attempt merge to main
3. Run tests (if configured)
4. Push to origin
5. Update queue status

## Conflict Handling

If merge fails:
- Mark MR as rejected
- Notify polecat to rebase


gt-gastown-polecat-morsov

gt-gastown-polecat-morsov

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


Digest: mol-deacon-patrol

Patrol cycle 32: All healthy, no incidents


Move disk 2: C → B

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


Add isAnnounceAddress() and parseAnnounceName() helpers to router.go

Add announce address detection to internal/mail/router.go.

PATTERN TO FOLLOW:
Look at isListAddress() and parseListName() at lines 53-60 as the template.

IMPLEMENTATION:
1. Add isAnnounceAddress(address string) bool - returns true if address starts with 'announce:'
2. Add parseAnnounceName(address string) string - extracts announce name after 'announce:'
3. Add var ErrUnknownAnnounce = errors.New("unknown announce channel")

FILE: internal/mail/router.go
TESTS: Add tests in internal/mail/router_test.go

Small, focused task - just address detection, not delivery.


MR field parsing: extract structured fields from description

Parse merge-request beads to extract structured fields:
- branch: source branch name
- target: target branch (main or integration/xxx)
- source_issue: the work item being merged
- worker: who did the work
- rig: which rig
- merge_commit: (set on close)
- close_reason: (set on close)

Fields stored in description as YAML block or key: value lines.
Provide helper functions: ParseMRFields(issue) and SetMRFields(issue, fields).

Reference: docs/merge-queue-design.md#merge-request-schema


Digest: mol-deacon-patrol

Patrol cycle 10: All agents healthy


Digest: mol-deacon-patrol

Patrol cycle 14: 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.

## 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: A → B

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


Execute registered plugins

Execute registered plugins.

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

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

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

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

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

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


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)


Default polecat names: Mad Max theme instead of AdjectiveNoun

Current default naming for new polecats uses AdjectiveNoun convention.
Since rigs already provide namespacing, we can use more thematic names.

Suggestion: Use Mad Max / Fury Road character and vehicle names as defaults.
Examples: Furiosa, Nux, Slit, Morsov, Toast, Rictus, Warboy, etc.

Could also include:
- War Rig parts: Guzzler, Tanker, Pursuit
- Citadel roles: Imperator, Blackthumb, Organic
- Wasteland terms: Chrome, Witness, Shiny

Implementation:
- Add name generator in internal/polecat/ or similar
- Use when --create flag is used without explicit name
- Cycle through pool to avoid duplicates within a rig


Move disk 1: B → C

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


Remove legacy .beads-wisp/ infrastructure

The wisp simplification (gt-fgms + bd-bkul) changed wisps to be just a flag on issues in the main .beads/ database. JSONL export filters Wisp=true issues. No separate directory needed.

But legacy .beads-wisp/ infrastructure still exists and should be removed:

## Gas Town (gt) cleanup:
- `internal/rig/manager.go`: Remove `initWispBeads()` call from `AddRig()`
- `internal/wisp/io.go`: Delete entire file (old abstraction layer)
- `internal/cmd/install.go`: Remove any .beads-wisp setup
- `docs/wisp-architecture.md`: Update to reflect 'just a flag' model

## Beads (bd) cleanup:
- `cmd/bd/doctor/wisp_check.go`: Remove WispExistsCheck, WispGitCheck, WispSizeCheck (check .beads-wisp dirs)
- Keep WispOrphansCheck and WispStaleCheck but rewrite to query main DB for Wisp=true issues

## Filesystem cleanup:
- Delete ~/gt/.beads-wisp/
- Delete ~/gt/gastown/.beads-wisp/
- Delete all per-rig .beads-wisp/ directories
- Remove .beads-wisp from .gitignore entries

## Verify:
- `gt mail send --wisp` still works (creates issue with Wisp=true in main DB)
- `bd sync` still filters wisps from JSONL
- `bd mol squash/burn` still work on Wisp=true issues


Digest: mol-deacon-patrol

Patrol cycle 2: All rigs healthy, no incidents


Daemon heartbeat: worker and queue monitoring

Daemon periodic checks for work progress.

## Heartbeat Actions

Every N seconds (configurable):

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

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

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

## Eventually Convergent

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

Even if one signal missed, system converges.

## Interface

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

## Notifications

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


Remove redundant gt prime SendKeys from crew commands

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

This is now redundant because SessionStart hook handles priming automatically.

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


Digest: mol-deacon-patrol

Patrol 3: routine, all healthy


Add runtime configuration for LLM commands

## Problem
Claude CLI invocation is hardcoded in 6+ places:
```go
"claude --dangerously-skip-permissions"
"claude --dangerously-skip-permissions \"gt prime\""
```

## Solution
Add runtime configuration to rig or town settings:

```toml
# settings/runtime.toml or in config.json
[agent]
runtime = "claude"

[agent.claude]
command = "claude"
args = ["--dangerously-skip-permissions"]
initial_prompt = ""  # Empty - hooks handle context

[agent.aider]
command = "aider"
args = ["--no-git"]
initial_prompt = "/help"
```

## Interface
```go
type RuntimeConfig struct {
    Command       string
    Args          []string
    InitialPrompt string  // Optional: first user message
}

func LoadRuntimeConfig(rigPath string) (*RuntimeConfig, error)
func (rc *RuntimeConfig) BuildCommand() string  // Returns full command line
```

## Usage
All startup paths read config instead of hardcoding:
```go
cfg := LoadRuntimeConfig(rigPath)
cmd := cfg.BuildCommand()  // "claude --dangerously-skip-permissions"
t.SendKeys(session, cmd)
```


CLI: rig commands (list, show, add)

Rig management CLI commands.

## Commands

### gt rig list
List all registered rigs.
```
gt rig list [--json]
```
Output:
- Rig name
- Git URL
- Polecat count
- Witness/Refinery status

### gt rig show
Show details for a specific rig.
```
gt rig show <name> [--json]
```
Output:
- Full rig info
- Polecats with status
- Recent activity
- Beads summary (open issues)

### gt rig add
Add a new rig to the town.
```
gt rig add <name> <git-url>
gt rig add <name> --local <path>
```
Steps:
- Clone repo to town root (or link local)
- Register in rigs.json
- Create agent directories
- Configure git exclude

### gt rig remove
Remove a rig from the town.
```
gt rig remove <name> [--force]
```
- Warns if active polecats
- --force required if uncommitted changes

## Implementation

```go
var rigCmd = &cobra.Command{Use: "rig"}
rigCmd.AddCommand(rigListCmd, rigShowCmd, rigAddCmd, rigRemoveCmd)
```


Digest: mol-deacon-patrol

Patrol 15: 3 rigs healthy


Digest: mol-deacon-patrol

Patrol 13: All healthy.


Move disk 1: A → B

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


Digest: mol-deacon-patrol @ 2025-12-24 20:23

Patrol complete: inbox clear, all agents healthy, no orphans


Move disk 2: C → B

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


Unify tmux session lifecycle: prefer exec-over-shell pattern

## Context

When `gt crew at` is run from INSIDE the target session, it uses `execClaude()` which
replaces the shell with Claude directly. This is actually preferable behavior:
- Cleaner exit (no intermediate shell prompt)
- More reliable for programmatic worker recycling
- Better UX for humans

But currently this only happens accidentally when you run `gt crew at` while already
in the session at a shell prompt.

## Current behavior

1. Session created: `tmux new-session` starts with shell
2. Claude started via `send-keys` → shell spawns claude as child
3. Exit claude → return to shell → exit shell → session ends

With exec path (only if already in session):
1. `execClaude()` replaces shell with claude
2. Exit claude → session ends (no intermediate shell)

## Desired behavior

Consider making exec-the-shell the DEFAULT for all crew/persistent sessions:
- Spawn session with shell
- Wait for ready
- Use `respawn-pane -k` with claude command (kills shell, starts claude directly)

This gives the cleaner lifecycle without requiring the user to be inside the session.

## Related

Part of tmux control plane unification - ability to manage workers from any session.


Digest: mol-deacon-patrol

Patrol cycle 29: All healthy, no incidents


Test Patrol for Bonding

Parent issue for mol bond CLI test


Move disk 2: B → A

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


Digest: mol-deacon-patrol

Patrol 53: All healthy


Digest: mol-deacon-patrol

Patrol 7: 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.


Digest: mol-deacon-patrol

Cycle 198: Nominal


Move disk 1: A → B

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


Merge: slit-mjxaptzz

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


Integration branch management for swarms

Manage integration branches for swarm merging strategy.

## Branch Naming

- Integration branch: `integration/<swarm-id>` (e.g., integration/sw-1)
- Worker branches: `<swarm-id>/<polecat>` (e.g., sw-1/Toast)

## Operations

```go
// Create integration branch at swarm start
func (m *SwarmManager) CreateIntegrationBranch(swarmID string) error
// - Creates from BaseCommit
// - Pushes to origin

// Merge worker branch to integration
func (r *Refinery) MergeToIntegration(swarmID, workerBranch string) error
// - Fetches worker branch
// - Merges to integration/<swarm-id>
// - Handles conflicts (semantic merge)

// Land integration to main
func (r *Refinery) LandToMain(swarmID string) error
// - Merges integration/<swarm-id> to main
// - Pushes to origin
// - Triggers cleanup

// Cleanup branches after landing
func (m *SwarmManager) CleanupBranches(swarmID string) error
// - Deletes integration/<swarm-id> (local + remote)
// - Deletes all <swarm-id>/<polecat> branches
```

## All Workers Same Base

When swarm starts:
1. Record current main HEAD as BaseCommit
2. All polecats branch from BaseCommit
3. Integration branch also starts at BaseCommit

This ensures clean merges within the swarm.

## Reference

PGT: ephemeral.py (create_integration_branch, merge_integration_to_main)


Digest: mol-deacon-patrol

Patrol 40: all quiet


Digest: mol-deacon-patrol

Patrol 4: Quiet


Design: Deacon exponential backoff when town is idle

## Problem

When the user goes to sleep and all workers have stopped, the Deacon continues
polling at full speed (every daemon heartbeat). This wastes resources and creates
unnecessary log noise.

## Requirements

1. **Gradual slowdown**: Deacon patrol frequency should exponentially back off
   when there's no activity in the town.

2. **Wake on activity**: Any gt or bd command (human or agent) should reset
   the backoff immediately.

3. **Daemon coordination**: Deacon needs to "program" the daemon for next
   heartbeat duration.

## Design Considerations

### Option A: Daemon tracks last-activity timestamp
- Daemon mechanically slows heartbeat based on time since last activity
- Activity signal comes from gt/bd command hooks
- Simple, no Deacon state needed
- But: Daemon is "dumb" by design - adding intelligence here feels wrong

### Option B: Deacon tracks patrol-count-since-activity
- Deacon maintains counter (via label on patrol molecule or state.json)
- Each idle patrol increments counter, calculates next sleep
- Deacon tells daemon "wake me in X minutes"
- More complex but keeps intelligence in AI agent

### Option C: Discovery-based (preferred if feasible)
- Can we detect "no activity" without explicit counters?
- Ideas:
  - Compare current time to last git commit across all clones?
  - Check tmux activity timestamps?
  - Look at beads modification times?
- Pros: No state to manage
- Cons: May be expensive to compute, could miss activity

### Wake mechanism
- Hook on gt commands (gt.hooks.pre-command or similar)
- Hook on bd commands (bd.hooks.pre-command)
- Both should signal daemon (SIGUSR2?) or write to activity file
- Daemon reads activity file on each heartbeat, resets backoff if fresh

### Backoff schedule (strawman)
| Idle patrols | Next heartbeat |
|--------------|----------------|
| 0-5          | 5 min (default) |
| 6-10         | 10 min |
| 11-20        | 30 min |
| 21+          | 60 min (max) |

## Open Questions

1. Should tmux activity count as "activity"? (cursor movement, typing)
2. How does Deacon communicate next-heartbeat to daemon?
3. Where to store backoff state? (daemon state.json? deacon state.json? label?)
4. Should wake be instant (SIGUSR) or next-heartbeat?

## Implementation Sketch

Activity file written by hooks at ~/gt/daemon/activity.json:
- last_command: timestamp
- command: the gt/bd command that ran
- actor: who ran it


Move disk 3: C → A

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


gt crew at fails to detect existing crew session

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

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

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

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

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

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


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 handoff beads for Witness and Refinery roles

Each patrol role needs a pinned handoff bead to track attached molecules and patrol state.

## Pattern (from Deacon)
- Title: '<role> Handoff' (e.g., 'witness Handoff', 'refinery Handoff')
- Status: pinned
- Description contains structured state

## Witness Handoff State

```yaml
attached_molecule: mol-witness-patrol
attached_at: 2025-12-24T10:00:00Z

# Nudge escalation tracking
nudges:
  toast:
    count: 2
    last: "2025-12-24T10:30:00Z"
  ace:
    count: 0
    last: null

# Polecats queued for cleanup
pending_cleanup:
  - nux  # received POLECAT_DONE, awaiting verification
```

## Refinery Handoff State

```yaml
attached_molecule: mol-refinery-patrol
attached_at: 2025-12-24T10:00:00Z

# Merge queue tracking
last_processed_branch: polecat/toast
branches_merged_this_cycle: 3
```

## Tasks
1. Create 'witness Handoff' bead in each rig's beads
2. Create 'refinery Handoff' bead in each rig's beads
3. Update Witness/Refinery startup to check handoff bead for attached work
4. Update templates to document handoff bead usage
5. Include nudge state schema for Witness
6. Include merge state schema for Refinery

## Note
These are rig-level beads (in gastown/.beads/, beads/.beads/), not town-level like deacon Handoff (in ~/gt/.beads/).

## See Also
- docs/witness-patrol-design.md - Theory of operation


Digest: mol-deacon-patrol

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


mol-deacon-patrol


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


Digest: mol-deacon-patrol

Patrol 9 complete


Move disk 2: A → C

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


Move disk 2: A → C

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


Add gt hook command (wrapper for bd hook)

Add a thin wrapper command `gt hook` that calls `bd hook` to inspect what's pinned to an agent's hook.

## Usage

```bash
gt hook                    # Show what's on current agent's hook
gt hook --agent deacon     # Show Deacon's hook
gt hook --agent gastown/furiosa  # Show polecat's hook
```

## Implementation

Thin wrapper in gt that:
1. Determines current agent identity
2. Calls `bd hook [--agent <name>]`
3. Formats output for gt context

## Why gt wrapper?

- Consistent with gt ecosystem (gt mail, gt status, etc.)
- Can add gt-specific context (session status, etc.)
- Easier discovery for gt users

## Related

- bd hook command (implemented by Dave)
- Chemistry UX design: gastown/mayor/rig/docs/chemistry-design-changes.md


Move disk 1: C → A

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


Move disk 1: A → B

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


Move disk 1: A → B

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


Session test no rig


Digest: mol-deacon-patrol

Patrol cycle 17: nominal


Move disk 2: B → A

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


mol-sync-workspace: Add Refinery-specific inline conflict resolution guidance


Digest: mol-deacon-patrol

Patrol 56: All healthy


P0: Multiple agents can claim same worker identity

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

## Fix Implemented

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

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

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

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


Move disk 2: B → A

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


Digest: mol-deacon-patrol

Cycle 15: quiet, handoff for fresh context


Merge: gt-4ev4

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


Merge: gt-7919

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


Digest: mol-deacon-patrol

Patrol 2: All healthy


Merge: capable-1767074974673

branch: polecat/capable-1767074974673
target: main
source_issue: capable-1767074974673
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 60 seconds minimum** before next patrol cycle
- If town is idle (no in_progress work), sleep longer (2-5 minutes)
- Return to inbox-check step

**Why longer sleep?**
- Idle agents should not be disturbed
- Health checks every few seconds flood inboxes and waste context
- The daemon (10-minute heartbeat) is the safety net for dead sessions
- Active work triggers feed events, which wake agents naturally

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.


Digest: mol-deacon-patrol

Patrol 12: healthy


Witness Patrol

Per-rig worker monitor patrol loop with progressive nudging.


Digest: mol-deacon-patrol

Patrol 15: OK


Digest: mol-deacon-patrol

Patrol 20: All healthy, handoff triggered


Digest: mol-deacon-patrol

Patrol 18: All healthy.


Merge: gt-t9u7

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


Digest: mol-deacon-patrol

P6 clear


Digest: mol-deacon-patrol

Patrol #14


Digest: mol-deacon-patrol

Patrol cycle 11: All healthy


Digest: mol-deacon-patrol

Patrol 7: healthy


Digest: mol-deacon-patrol

Patrol complete: inbox empty, all agents healthy, fixed invalid hook attachment on gt-w98d


Human escalation: notify overseer when self-heal fails

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

**Implementation**: Config in town.json or similar:
```yaml
escalation:
  contact: steve@example.com  # or slack webhook
  triggers:
    - daemon_cant_restart
    - session_missing_5min
```

**Trigger points**:
- Go daemon can't restart a session after N attempts
- Agent detects it's stuck and can't recover
- Witness can't reach polecat

**Mechanism**:
- Simple: `gt mail send --human` already exists
- Enhanced: email/slack via external script

**Weight**: Small config + one code path in daemon
**Value**: High for unattended operation - human gets notified instead of silent failure


Digest: mol-deacon-patrol

Patrol 11


Analysis: Beads database size and hygiene strategy

Audit beads database size and plan medium-term hygiene.

## Questions
1. How many issues total (including tombstones)?
2. Size of issues.jsonl?
3. Growth rate per day/week?
4. What percentage are tombstones?

## Hygiene Topics
- Compaction: squash tombstones periodically
- Archival: move old closed issues to archive file
- Pruning: delete truly stale items (test beads, old MRs)
- Sync efficiency: large JSONL = slow sync

## Action Items
- [ ] Count total issues by status
- [ ] Count tombstones
- [ ] Measure file sizes
- [ ] Propose hygiene schedule


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.


Digest: mol-deacon-patrol

Patrol 13: All healthy


MVP Cutover: GGT replaces PGT for batch work

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

## Acceptance Criteria

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

## What Must Work

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

## What Can Be Deferred

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

## Test Plan

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

## Validation

Run one real batch implementing GGT issues using GGT.

## Note

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


Update Mayor startup protocol for bootstrap

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

## Current Protocol

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

## Enhanced Protocol

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

## Trigger Phrases

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

## Execution

Mayor runs the molecule manually (not via subagent):

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

## CLAUDE.md Update

Add to Mayor CLAUDE.md:

```markdown
## Bootstrap Command

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


Merge: rictus-1767084016819

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


Worker cleanup: Beads sync on shutdown

Add beads sync verification to worker cleanup checklist and Witness verification.

## Update to Decommission Checklist (gt-sd6)

Add to pre-done verification:
- bd sync --status must show 'Up to date'
- git status .beads/ must show no changes

## Beads Edge Cases

Uncommitted beads changes:
  bd sync
  git add .beads/
  git commit -m 'beads: final sync'

Beads sync conflict (rare):
  git fetch origin main
  git checkout main -- .beads/
  bd sync --force
  git add .beads/
  git commit -m 'beads: resolve sync conflict'

## Update to Witness Verification (gt-f8v)

When capturing worker state:
  town capture <polecat> "bd sync --status && git status .beads/"

Check for:
- bd sync --status shows 'Up to date'
- git status .beads/ shows no changes

If beads not synced, nudge:
  WITNESS CHECK: Beads not synced. Run 'bd sync' then commit .beads/. Signal done when complete.


Move disk 3: B → C

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


Merge: cal-mjufugtb

branch: polecat/cal-mjufugtb
target: main
source_issue: cal-mjufugtb
rig: gastown


Merge: gt-h6eq.6

branch: polecat/keeper
target: main
source_issue: gt-h6eq.6
rig: gastown


gt: Write town-level activity signal in PersistentPreRun

Extend gt's existing keepalive infrastructure to also write a town-level activity file.

Currently gt writes to <workspace>/.runtime/keepalive.json (per-rig).
Add a second write to ~/gt/daemon/activity.json (town-level).

The keepalive package already has TouchInWorkspace(). Add a new function
TouchTownActivity() that writes to the town daemon directory.

Call both in root.go's PersistentPreRun.


Merge: dementus-1767079823043

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


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

Patrol 16


Move disk 1: A → B

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


Digest: mol-deacon-patrol

Patrol cycle 53: All healthy


Move disk 3: C → A

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


mol-witness-patrol

Per-rig worker monitor patrol loop.

The Witness is the Pit Boss for your rig. You watch polecats, nudge them toward
completion, verify clean git state before kills, and escalate stuck workers.

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

This molecule uses wisp storage (.beads-wisp/) for ephemeral patrol state.
Persistent state (nudge counts, handoffs) is stored in a witness handoff bead.


Merge: slit-1767059695613

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


Digest: mol-deacon-patrol

Patrol 3: Quiet, all healthy


Digest: mol-deacon-patrol

Patrol 100: All healthy. Handing off after 20 patrols.


Move disk 8: C → B

Move disk 8 from peg C to peg B. (Move 384/1023)


gt sling: use mail queue for patrol roles (witness, refinery, deacon)

When slinging work to patrol agents, queue via mail instead of replacing hook.

**Rationale:**
Patrol agents run continuous loops. Replacing their hook with discrete work breaks patrol continuity - when the task completes, the patrol stops.

**New behavior for patrol roles (witness, refinery, deacon):**
1. Check if patrol is running (hook has patrol molecule attached)
2. If patrol running:
   - Don't touch hook (patrol stays pinned)
   - Send work assignment mail
   - Print 'Queued for next patrol cycle'
3. If patrol NOT running:
   - Start default patrol for that role (mol-witness-patrol, mol-refinery-patrol, mol-deacon-patrol)
   - Send work assignment mail
   - Print 'Started patrol and queued work'

**New flags:**
- --urgent: Interrupt current patrol cycle, process this work immediately
- --replace: Explicitly terminate patrol and pin discrete work (break-glass)

**No change for:**
- Polecat, Crew, Mayor (discrete task agents - current behavior)

**Dependencies:**
- Patrol templates must have 'check inbox' step (verify/add)
- Need to know default patrol molecule for each role


Digest: mol-deacon-patrol

Patrol cycle 17: All healthy


Digest: mol-deacon-patrol

Patrol 1: All agents healthy, no messages, no issues


Merge: imperator-mjwjc4ov

branch: polecat/imperator-mjwjc4ov
target: main
source_issue: imperator-mjwjc4ov
rig: gastown
agent_bead: gt-gastown-polecat-imperator


Move disk 1: B → C

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


Digest: mol-deacon-patrol

Patrol cycle 3: All healthy


Digest: mol-deacon-patrol

Patrol cycle 23 complete. All 3 rigs healthy. Quick check.


Digest: mol-deacon-patrol

Patrol cycle 20: All agents healthy, handoff threshold reached


Digest: mol-deacon-patrol

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


Digest: mol-deacon-patrol

Patrol 140: All healthy


Digest: mol-deacon-patrol

Patrol 14: 3 rigs healthy


gt spawn should verify daemon is running for polecat triggering

gt spawn notifies the Deacon (via mail) that a polecat was started, expecting the Deacon to trigger it once Claude is ready. But if the daemon isn't running, the mail sits unread and the polecat never gets triggered.

## Current Behavior
1. gt spawn starts polecat session
2. gt spawn sends POLECAT_STARTED to deacon/
3. (assumes daemon will trigger polecat)

## Problem
If gt daemon isn't running, step 3 never happens and polecat sits at prompt.

## Solution
In gt spawn, after session start:
1. Check if daemon is running (gt daemon status)
2. If not running, either:
   a. Start daemon: gt daemon start
   b. Or warn user: 'Daemon not running, polecat may not auto-start'

## Alternative
The user can manually trigger with gt nudge, but automated flow should work.


Digest: mol-deacon-patrol

Patrol 18: All healthy


Move disk 1: B → C

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


Digest: mol-deacon-patrol

Patrol cycle 16


Digest: mol-deacon-patrol

P4: stable


Connection interface: Protocol for local/remote ops

Abstract interface for local vs remote (SSH) operations.

## Concept

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

## Interface

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

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

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

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

## Implementations

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

## Usage

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

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

## Design Notes

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


Digest: mol-deacon-patrol

Patrol 20


Digest: mol-deacon-patrol

Patrol cycle 2: All agents healthy, 6 health pings sent


Add merge-request type to Beads schema

Define the merge-request bead type with fields:
- branch: source branch (e.g., polecat/Nux/gt-xxx)
- target_branch: destination (main or integration/xxx)
- source_issue: the work being merged (gt-xxx)
- worker: who did the work
- rig: which rig
- merge_commit: (filled on close) SHA of merge commit
- close_reason: (filled on close) success/failure details

This may require beads schema changes or just convention.


max Handoff


Digest: mol-deacon-patrol

Patrol 1: All agents healthy. Clone divergence (zoey/jack) noted for witnesses.


Digest: mol-deacon-patrol

Patrol 3: routine, all healthy


Human escalation path design

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


Keepalive signal from bd/gt commands

Every bd and gt command should touch a keepalive file to signal 'agent is alive/working'.

## Implementation

Touch `<workspace>/.gastown/keepalive.json`:
```json
{"last_command": "bd show gt-99m", "timestamp": "2025-12-18T13:45:00Z"}
```

## Usage by Daemon

- Fresh (< 2 min) → agent is working, skip heartbeat
- Stale (2-5 min) → might be thinking, gentle poke
- Very stale (> 5 min) → likely idle, safe to interrupt

## Benefits

- Zero cost (just file I/O)
- Works during long tool calls
- Doesn't require agent cooperation
- Foundation for smarter backoff strategies


gt mail send: add stdin and file input support for complex messages

Current -m flag is fragile with multi-line messages containing backticks, quotes, or shell special chars. Add: 1) --stdin flag to read message body from stdin (heredoc-friendly), 2) -f/--file flag to read from file. This sidesteps shell quoting nightmares for handoff messages with code samples.


gt nudge should accept partial/fuzzy session names

Currently gt nudge requires the exact tmux session name (e.g., gt-gastown-crew-max). Should be more forgiving:

1. Accept partial matches when unambiguous (e.g., 'max' → gt-gastown-crew-max)
2. Accept shorthand like 'gastown/max' or 'crew/max'
3. Show helpful error with suggestions when ambiguous

Examples that should work:
- gt nudge max '...' → matches gt-gastown-crew-max
- gt nudge gastown/max '...' → matches gt-gastown-crew-max
- gt nudge beads-dave '...' → matches gt-beads-crew-dave


Merge: gt-tnca.1

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


Plugin system: subprocess JSON protocol

Language-agnostic plugin system via subprocess JSON protocol.

## Concept

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

## Protocol

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

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

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

## Interface

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

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

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

## Plugin Discovery

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

## Built-in Hooks

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

## Use Cases

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

## Future

Consider MCP integration for richer plugin capabilities.


Remove schedule/phase terminology from code

Temporal/schedule language in identifiers is an anti-pattern.

## Found Issues

### beads repo
- `examples/multi-phase-development/` directory
  - Rename to `examples/dependency-sequenced-work/` or similar

### gastown repo
- `internal/cmd/start.go` lines 178-195
  - Uses "Phase 1:", "Phase 2:", etc. in printf output
  - Change to descriptive names: "Interrupting agents...", "Requesting handoff...", etc.

- `internal/swarm/landing.go` lines ~50-70
  - Comments say "// Phase 1: Stop all polecat sessions"
  - Change to "// Step: Stop all polecat sessions" or just remove phase labels

## Why This Matters

Phase/version language in names:
1. Implies a fixed schedule (antithetical to dependency-driven work)
2. Creates confusion with molecule "phases" (solid/liquid/gas)
3. Encourages temporal thinking that leads to backwards dependencies

## What To Keep

- Molecule phase terminology (pour/wisp/proto) - this is chemistry, not scheduling
- Version fields inside files (version: 1) - this is semantic versioning


Add role-specific subcommands that delegate to core commands (agent UX)

## Problem

Agents naturally guess command patterns like:
- `gt witness nudge <polecat>`
- `gt polecat nudge <name>`
- `gt crew restart <member>`

But these don't exist - the actual commands are:
- `gt nudge <session> <message>`
- `gt polecat reset <name>`

This creates friction in agent UX. Agents shouldn't have to memorize the 'correct' command structure.

## Proposal

Add subcommands to each role that delegate to core commands with argument reordering:

```
gt witness nudge furiosa "Start working"
  → gt nudge gt-gastown-furiosa "Start working"

gt polecat nudge gastown/furiosa "Check mail"
  → gt nudge gt-gastown-furiosa "Check mail"

gt crew nudge gastown/max "Wake up"
  → gt nudge gt-crew-gastown-max "Wake up"
```

## Benefits

1. **Discoverable**: Agents explore `gt witness --help` and find nudge
2. **Lenient**: Multiple valid ways to express the same intent
3. **Role-contextual**: Commands under the role namespace feel natural
4. **Extensible**: Pattern works for future subcommands (status, reset, etc.)

## Implementation

Each role command (witness, polecat, crew, refinery) gets thin wrapper subcommands:

```go
// In witness.go
witnessCmd.AddCommand(&cobra.Command{
    Use:   "nudge <polecat> <message>",
    Short: "Nudge a polecat (delegates to gt nudge)",
    RunE: func(cmd *cobra.Command, args []string) error {
        session := formatSession(rig, args[0])
        return runNudge(session, args[1])
    },
})
```

## Future Candidates

Other subcommands to add as role-specific aliases:
- `gt polecat status <name>` (already exists, good pattern)
- `gt witness check <polecat>` → trigger a manual check
- `gt refinery merge <mr-id>` → process specific MR
- `gt crew send <member> <message>` → mail to crew member


Add agent abstraction layer to support non-Claude agents

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

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

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


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

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


Merge: ace-dogs

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


Split PGT/GGT harness or migrate to GGT-only

Current ~/ai harness is shared by PGT and GGT with confusing overlap. Options:
1. Keep separate (document the coexistence)
2. Migrate fully to GGT structure
3. Create separate harnesses

This affects the beads redirect, Mayor home location, and rig structure.


mol-polecat-work

Full polecat lifecycle from assignment to decommission.

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

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


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


gt mail inbox shows wrong identity when run from deacon directory

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


Test Polecat Arm

Test child for bonding pattern


Digest: mol-deacon-patrol

Patrol 2: All healthy, no messages, quiet cycle


Mail orchestrator daemon

Background mail orchestrator daemon.

## Command
```
gt mail orchestrate [--interval N] [--once] [--verbose]
```

## Purpose
Background process that:
1. Monitors outbox for pending mail
2. Delivers to recipient inboxes
3. Handles offline recipients (retry later)
4. Cleans delivered messages from outbox

## Why Needed?
Current mail is synchronous. If recipient is offline or mailbox locked, send fails.
Orchestrator enables async delivery with retry.

## Implementation
```go
func (o *Orchestrator) Run(interval time.Duration) error {
    ticker := time.NewTicker(interval)
    for range ticker.C {
        o.processOutbox()
    }
}

func (o *Orchestrator) processOutbox() {
    // List outbox/*.json
    // For each, attempt delivery
    // On success, delete from outbox
    // On failure, increment retry count
}
```

## Outbox Structure
```
<town>/mayor/mail/outbox/
├── msg-abc123.json
└── msg-def456.json
```

## Lower Priority
Current synchronous delivery works. Orchestrator is optimization.

## Acceptance Criteria
- [ ] Background daemon mode
- [ ] Retry failed deliveries
- [ ] --once for single pass
- [ ] Configurable interval


Move disk 3: C → A

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


Update WitnessPatrolMolecule() with Christmas Ornament structure

Update builtin_molecules.go WitnessPatrolMolecule() to use dynamic bonding pattern.

Changes:
1. Add survey-workers step that documents dynamic bonding
2. Add run-plugins step for plugin hook support
3. Add aggregate step with WaitsFor: all-children
4. Update step dependencies for new structure

The molecule prose guides the Witness agent on how to use bd mol bond.


Move disk 4: C → B

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


Phase 1.2: Configure bd for ephemeral molecule bonding

Ensure bd mol bond --ephemeral works with Gas Town setup.

## Questions for Dave

1. Does bd automatically find .beads-ephemeral/ or need explicit path?
2. How does bd mol bond --ephemeral know which repo to use?
3. Is there a redirect mechanism for ephemeral like main beads?

## Integration

From polecat/crew working directory:
```bash
bd mol bond mol-polecat-work --ephemeral --assignee $(gt whoami)
```

Should create molecule in rig's .beads-ephemeral/, not main beads.


Move disk 1: C → A

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


Move disk 3: B → C

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


Move disk 1: B → C

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


Move disk 1: B → C

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


Move disk 3: C → A

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


Digest: mol-deacon-patrol

Patrol 1: All agents healthy, 1 message processed, no orphans


Merge: furiosa-mjw1p03l

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


Remove bd sync instruction from polecat startup workflow

Polecats are instructed to run `bd sync --from-main` on startup (spawn.go:634).

## Problem
- Spawn command already syncs beads before spawning (line 239)
- Polecats share rig-level beads via `.beads/redirect`
- Multiple polecats starting simultaneously all try to sync same shared beads
- This causes git conflicts/failures when many polecats spawn at once

## Observed
User reported: 'all polecats failing on beads sync on startup in one run'

## Fix
Remove line 634 from buildWorkAssignmentMail():
```
body.WriteString("2. Run \`bd sync --from-main\` to get fresh beads\n")
```

Polecats only need to sync at END of work (already in steps 5/7).

## Files
- internal/cmd/spawn.go: buildWorkAssignmentMail() and buildSpawnContext()


Move disk 2: B → A

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


Digest: mol-deacon-patrol

Patrol 15: all healthy, doctor pass


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.


Refinery Patrol Cycle


Digest: mol-deacon-patrol

Cycle 193: Nominal


Move disk 5: A → B

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


ensure-daemon

Verify the Gas Town daemon is running.

## Action
```bash
gt daemon status || gt daemon start
```

## Verify
1. Daemon PID file exists: `~/.gt/daemon.pid`
2. Process is alive: `kill -0 $(cat ~/.gt/daemon.pid)`
3. Daemon responds: `gt daemon status` returns success

## OnStall
Daemon startup failed. Try:
```bash
gt daemon stop
sleep 2
gt daemon start
```

## OnFail
Cannot start daemon. Log error and continue - some commands work without daemon.


Move disk 1: A → B

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


Digest: mol-deacon-patrol

Patrol 44: All healthy


Digest: mol-deacon-patrol

Patrol 129: All healthy


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

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


Digest: mol-deacon-patrol

Patrol 217: All nominal


Digest: mol-deacon-patrol

Patrol cycle 8: All agents healthy


Move disk 1: C → A

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


mol-deacon-patrol


Merge: gt-ldk8

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


Deacon tmux status bar: heartbeat timing and patrol state

## Problem

The deacon shows patrol output but lacks real-time observability:
- No indication of when last heartbeat occurred
- No indication of when next heartbeat is scheduled
- No visibility into current patrol step

## Desired Behavior

The tmux status bar for gt-deacon should show:

```
⛪ Deacon | ❤ 2m ago | ⏰ 3m | 📍 health-scan (3/7)
```

Components:
- **Role icon**: ⛪ (deacon identity)
- **Last heartbeat**: ❤ 2m ago (time since last heartbeat file update)
- **Next heartbeat**: ⏰ 3m (time until daemon would poke)
- **Current step**: 📍 health-scan (3/7) (current patrol atom, step N of M)

## Implementation

1. **Read heartbeat.json** for last update time
2. **Calculate next poke** based on daemon interval (default 5m)
3. **Read current wisp** from .beads-wisp/ to get patrol progress
4. **Update tmux status** periodically or on state change

Options:
- tmux status-right with shell script
- gt deacon status --tmux for formatted output
- Hook into patrol step completion

## Related

- gt-id36: Deacon Kernel epic
- gt-3x0z: Wisp Molecule Integration


Crew worker beads-dave in gastown - human-managed persistent workspace.

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


Merge: furiosa-mjtj9d4g

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


Move disk 2: B → A

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


Digest: mol-deacon-patrol

Patrol 2: Quick scan, no changes


Digest: mol-deacon-patrol

Patrol 8: Routine


Move disk 1: C → A

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


Move disk 2: C → B

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


Digest: mol-deacon-patrol

Patrol 87: All healthy


Merge: nux-1767081106779

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


Digest: mol-deacon-patrol

Patrol 8: All healthy


Digest: mol-deacon-patrol

Patrol cycle 12: nominal


Digest: mol-deacon-patrol

Patrol 24: All quiet


Digest: mol-deacon-patrol

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


Digest: mol-deacon-patrol

Patrol cycle 11


Digest: mol-deacon-patrol

Patrol #8


Fix TestHasPolecat test failure in internal/session

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

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

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


Handle callbacks from agents

Handle callbacks from agents.

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

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

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


Digest: mol-deacon-patrol

Patrol 5: Nominal


Digest: mol-deacon-patrol

Patrol 11: All healthy


Move disk 1: A → B

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


Move disk 1: B → C

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


Merge: gt-h6eq.7

branch: polecat/dag
target: main
source_issue: gt-h6eq.7
rig: gastown


Refinery-Witness communication protocol

Define mail protocol between Refinery and Witness:

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

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

Implement as structured mail with parseable format.


ensure-deacon

Start the Deacon and verify patrol mode is active.

## Action
```bash
gt deacon start
```

## Verify
1. Session exists: `tmux has-session -t gt-deacon 2>/dev/null`
2. Not stalled: `gt peek deacon/` does NOT show "> Try" prompt
3. Heartbeat fresh: `deacon/heartbeat.json` modified < 2 min ago

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

## OnFail
Log error and continue - town can run with degraded deacon.
The Witness can still manage polecats without Deacon oversight.


Session cycling UX: smooth transitions via TUI wrapper

## Problem

Current CLI agent session cycling is painful:
- Shell → CC starts → priming → context loads → ready → work → exit/crash → repeat
- Each cycle is 30-60 seconds of cold boot
- No continuity between shell and agent's inner state
- Raw "session not running, starting..." loop is the baseline

## GGT Advantages (already have)

- Beads: Work state survives session death completely
- Mail: Handoff notes from past-self to future-self
- Prime commands: Structured context reload

## Gap: Transition Mechanics

Ideas to explore when actively using CLI:

1. **In-band cycling** - `/restart` or `/cycle` command, agent handles own restart without dropping to shell

2. **Hot standby** - TUI maintains pre-warmed session in background, switch to already-primed agent

3. **Persistent wrapper** - Bubbletea TUI stays running across session cycles, CC sessions come/go inside it

4. **Session pooling** - Keep 2-3 primed sessions ready, never wait for cold start

## Deferred

Deliberately P4 until we're actively using the simpler CLI and feel the pain firsthand.


Update docs to reflect molecule-first paradigm

Gas Town is fundamentally a molecule execution engine. Documentation should reflect this more clearly.

## Issues Found

### 1. gt spawn examples show molecule as optional
README.md line 116: `gt spawn --issue <id> # Start polecat on issue`
Should emphasize: polecats execute molecules, not just issues.

### 2. Architecture.md spawn examples inconsistent
Line 344 shows molecule: `gt spawn --issue gt-xyz --molecule mol-engineer-in-box`
Line 1434 shows without: `gt spawn --issue <id>`

### 3. Config vs molecule distinction not clear
outposts.yaml shows static policy - should note when molecules apply.

### 4. Operational molecules section is good but buried
Lines 430-566 cover operational molecules well. Should be more prominent.

## Updates Needed
- [ ] README: Update spawn examples to show molecule usage
- [ ] architecture.md: Ensure all spawn examples include molecules
- [ ] architecture.md: Add section on "when config vs molecule"
- [ ] architecture.md: Move operational molecules higher in document
- [ ] Add principle: "If it requires cognition, it's a molecule"
- [ ] federation-design.md: Note that policy can escalate to mol-outpost-assign

## Key Message
Gas Town doesn't spawn workers on issues. It spawns workers on molecules.
The issue is just the seed data for the molecule execution.


Guardrail: Verify commits exist before closing polecat issues

## Updated Approach (ZFC)

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

## Correct Solution

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

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

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

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

## Why Not Code Guardrails

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

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

## Implementation

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


Digest: mol-deacon-patrol

Patrol cycle 17: All healthy.


Digest: mol-deacon-patrol

Patrol 18: clear


gt-gastown-polecat-alpha

gt-gastown-polecat-alpha

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


mol-deacon-patrol

Mayor's daemon patrol loop.

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

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


Day 1.4b: Create Deacon agent bead

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

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

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

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


Digest: mol-deacon-patrol

Patrol 5: quiet


Move disk 2: A → C

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


Implement gt convoy add command

Add the 'add' subcommand to convoy for adding issues to existing convoys.

SYNTAX:
gt convoy add <convoy-id> <issue-id> [issue-id...]

BEHAVIOR:
1. Validate convoy exists
2. Add 'tracks' dependency for each issue
3. If convoy is closed, reopen it automatically
4. Print confirmation

IMPLEMENTATION:
File: internal/cmd/convoy.go
Add: convoyAddCmd as subcommand

Example:
gt convoy add hq-cv-abc gt-new-issue
# -> Added gt-new-issue to convoy hq-cv-abc

gt convoy add hq-cv-abc gt-followup  # convoy was closed
# -> Reopened convoy hq-cv-abc
# -> Added gt-followup to convoy hq-cv-abc


Move disk 5: C → A

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


Digest: mol-deacon-patrol

Patrol 20: all clear, handoff due


Move disk 2: C → B

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


implement

Implement the solution for gt-test123. 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


ZFC Cleanup: Move Go heuristics to Deacon molecule

Remove Go code that makes workflow decisions. All health checking, staleness
detection, nudging, and escalation belongs in the Deacon molecule where Claude
executes it once per minute.

## The Problem

Go code currently implements polling-based health inference:
- Daemon watches heartbeats with hardcoded thresholds
- Decides if agents are "stuck" based on timestamps
- Sends nudges, triggers restarts
- Parses mail subjects with regex to extract intent

This is a ZFC violation. Go should be message transport, not decision-maker.

## The Fix

Move all health/oversight logic to Deacon patrol molecule:
- Deacon runs once per minute
- Claude reads molecule steps, executes them
- Claude decides if agents are stuck (by checking mail, activity, etc.)
- Claude sends nudges/escalations via mail
- Go just routes messages

## Scope

1. daemon/daemon.go - heartbeat staleness logic
2. daemon/backoff.go - exponential backoff decisions
3. daemon/lifecycle.go - regex parsing of lifecycle intent
4. keepalive/keepalive.go - staleness thresholds
5. deacon/heartbeat.go - heartbeat age classification
6. doctor/stale_check.go - staleness detection
7. polecat/manager.go - state derivation from issues

## Success Criteria

- Go code has no hardcoded time.Duration for health decisions
- No regex parsing of mail subjects for intent
- No switch statements deciding agent state
- Deacon molecule has all oversight logic
- Go daemon is just a message router


Remove dead code: splitLines first loop, unused methods.

## Dead code locations

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

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 18: all healthy, handled 1 handoff msg


Digest: mol-deacon-patrol

Patrol 13: nominal


Campaign: Large-Scale Work Graph Execution

A Campaign is a Molecule-wrapped Epic graph ready for orchestrated execution.

## Concept

Campaign = Molecule + Epics + Dependency Graph + Execution Strategy

Key properties:
1. **Work graph**: Linked epics with dependency edges (possibly from different authors)
2. **Combinators as glue**: >>, |, wrap define bridging/shaping between epics
3. **Molecule shell**: Provides execution state, persistence across sessions
4. **Witness-aware**: Can be parallelized across polecats on independent branches
5. **Single-rig scoped**: One rig executes it (spans sessions/polecats)

## The "Maximum Speed" Model

Witness becomes a scheduler that:
- Watches campaign progress
- Identifies ready nodes (all dependencies satisfied)
- Spawns/assigns polecats to ready nodes
- Respects parallelism constraints
- Handles failures (retry? skip? escalate?)

## Where Campaign Fits in MEOW

Campaign is the convergence point where work-tracking meets workflow-execution:
- An Epic-of-Epics (work dimension)
- A Molecule (workflow dimension)

## Open Questions

1. Campaign creation UX - manual wire vs declare intent?
2. Partial completion - can you commit progress and resume?
3. Distinct entity type or molecule with type="campaign"?
4. Cross-rig campaigns - can graph span multiple rigs?

## Related

- docs/formula_evolution.md - original discussion
- gt-8tmz - molecule algebra (foundation)


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

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

## Variables

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

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

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

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

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

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

Calculate: minutes since last activity.
Needs: capture

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

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

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

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

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

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

Record decision and rationale.
Needs: load-history

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

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

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

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

**none**: No action needed.

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

## Output

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

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


Digest: mol-deacon-patrol

Cycle 13


Move disk 1: B → C

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


MR ID generation: prefix-mr-hash convention

Generate merge-request IDs following the convention: <prefix>-mr-<hash>

Example: gt-mr-abc123 for a gastown merge request.

This distinguishes MRs from regular issues while keeping them in the same namespace.
The hash should be derived from branch name + timestamp for uniqueness.

Implement: GenerateMRID(prefix, branch string) string

Reference: docs/merge-queue-design.md#id-convention


gt witness: Witness management commands

Add 'gt witness' command group for witness lifecycle management.

Subcommands:
- gt witness start [rig] - Start witness for a rig
- gt witness stop [rig] - Stop witness
- gt witness status [rig] - Show witness status
- gt witness attach [rig] - Attach to witness session

Witness monitors polecats and handles:
- Idle detection and cleanup
- Session health checks
- Nudging stuck agents


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol cycle 5: All agents healthy


Run 1K Hanoi demo (10 disks)

End-to-end test:
1. Cook 20-disk Hanoi formula
2. Pour as wisp on a polecat
3. Let it run overnight
4. Monitor with bd mol progress
5. Verify completion via convoy


Merge: gt-rana.3

branch: polecat/dementus
target: main
source_issue: gt-rana.3
rig: gastown


Move disk 2: A → C

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


Digest: mol-deacon-patrol

Patrol cycle 10: all healthy


Move disk 1: A → B

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


Digest: mol-deacon-patrol

Patrol 12: routine


gt mail send: --priority flag should work like bd mail send

UX inconsistency: gt mail send passes flags that bd mail send doesn't support.

## Root Cause

router.go line 42 passes `--priority` to bd:
```go
args = append(args, "--priority", fmt.Sprintf("%d", beadsPriority))
```

But `bd mail send` only has `--urgent` (boolean), not `--priority`.

## Fix Options

1. Add `--priority` flag to `bd mail send` (preferred - more expressive)
2. Change router to only use `--urgent` when priority=0

## Also Affected

- `--type` flag (line 46) - bd mail send doesn't have this
- `--thread-id` flag (line 51) - bd mail send doesn't have this
- `--reply-to` flag (line 56) - bd mail send doesn't have this

The router assumes bd mail send has features it doesn't have.


Move disk 2: B → A

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


test pin fix


Move disk 9: A → B

Move disk 9 from peg A to peg B. (Move 256/1023)


gt done: Notify Witness with exit outcome

When polecat runs gt done, it should send mail to Witness:

```
gt mail send <rig>/witness -s 'POLECAT_DONE <name>' -m 'Exit: COMPLETED
Issue: <issue-id>
MR: <mr-id>
Branch: <branch>'
```

Exit types:
- COMPLETED: Work done, MR submitted
- ESCALATED: Hit blocker, needs human
- DEFERRED: Work paused, issue still open

This enables Witness patrol to:
1. See completion in inbox-check step
2. Verify git state is clean
3. Kill session and prune worktree
4. Close the polecat lease in its patrol wisp

Paired with gt-r6td (spawn notification) - together they bracket polecat lifecycle.


Digest: mol-deacon-patrol

Patrol complete: inbox clear, agents healthy, killed 3 orphan processes, noted 1 orphaned in_progress issue


Code review swarm: recent merges


Review: Beads Universal Data Plane documentation

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

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

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


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

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

## Variables

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

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

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

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

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

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

Calculate: minutes since last activity.
Needs: capture

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

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

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

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

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

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

Record decision and rationale.
Needs: load-history

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

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

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

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

**none**: No action needed.

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

## Output

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

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


Digest: mol-deacon-patrol

Patrol 7: routine


Move disk 1: C → A

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


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

There are duplicate protos for patrol molecules:

## Duplicates

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

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

## Action

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

## Root Cause

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


Digest: mol-deacon-patrol

Patrol 16: All agents healthy


Move disk 2: A → C

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


Digest: mol-deacon-patrol

Patrol #18


Digest: mol-deacon-patrol

Patrol 7: All healthy


Digest: mol-deacon-patrol

Patrol cycle 13: all healthy


Digest: mol-deacon-patrol

Patrol 19


Digest: mol-deacon-patrol

Patrol 6: clear


Merge: keeper-1767074342207

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


Move disk 1: B → C

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


Phase 4.2: Witness/Refinery patrol molecules

Extend ephemeral patrol pattern to Witness and Refinery.

## Witness Patrol Cycle

mol-witness-patrol-cycle:
1. scan-polecats: Check each polecat status
2. detect-stalls: Find stuck/idle polecats
3. nudge-or-escalate: Take action on stalls
4. log-status: Record cycle results
→ Squash with summary

## Refinery Patrol Cycle

mol-refinery-patrol-cycle:
1. scan-queue: Check merge queue
2. process-ready: Merge ready MRs
3. handle-failures: Deal with conflicts/failures
4. log-status: Record cycle results
→ Squash with summary

## Quiescent Mode

When no work: just log 'no activity' summary.
Witness and Refinery can sleep between cycles until triggered.


Move disk 1: C → A

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


Move disk 1: B → C

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


Add handoff self-initiation protocol to role templates

Agents need clear protocol for self-initiated handoff:

1. Recognize: 'I should cycle now'
2. Prepare: Summarize current state, what's next
3. Send: gt mail send <self> -s '🤝 HANDOFF: ...' -m '...'
4. Exit: End session cleanly

This replaces external 'you should cycle now' nudging.
The agent owns its lifecycle.

Include examples for each role type.


Digest: mol-deacon-patrol

Patrol 19: Quiet


Digest: mol-deacon-patrol

Patrol 5: All clear


Digest: mol-deacon-patrol

Patrol 7: Routine


Polecat-Witness protocol handlers

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


Digest: mol-deacon-patrol

Patrol 9: nominal


Digest: mol-deacon-patrol

Patrol 14: All healthy


Merge: gt-y5o

branch: polecat/Doof
target: main
source_issue: gt-y5o
rig: gastown


MQ conflict prediction: file hotspot tracking

Track which files change frequently to predict conflicts before spawn.

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

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

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

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

## Parent
gt-lxxh2


Move disk 4: C → B

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


Move disk 1: B → C

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


Merge: dementus-mjw46vz4

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


Digest: mol-deacon-patrol

Patrol 5: routine


Digest: mol-deacon-patrol

Patrol cycle 15: all healthy


Merge: dag-mjw70jg8

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


Move disk 1: B → C

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


Digest: mol-deacon-patrol

Patrol 19: routine


Digest: mol-deacon-patrol

Patrol 19: splendid spawned


Digest: mol-deacon-patrol

Patrol 3: All healthy, no changes


Digest: mol-deacon-patrol

Patrol 8: Witnesses healthy


Move mol run semantics from bd into gt spawn

Currently gt spawn shells out to bd mol run for the pour+assign+pin combo. This blurs the architectural boundary between bd (data layer) and gt (orchestration layer).

## Current State
- bd mol run = bd pour + bd update --status=in_progress + bd pin --for me
- gt spawn calls: exec.Command("bd", "mol", "run", ...)

## Proposed Change
Move the combo logic into gt spawn directly:
1. gt spawn calls bd pour (create issues from template)
2. gt spawn calls bd update --status=in_progress (claim work)
3. gt spawn calls bd pin (set pinned flag)
4. gt spawn does session/tmux orchestration

This keeps bd as pure data operations and gt as the orchestration layer.

## Why
- Cleaner separation of concerns
- bd mol run feels like orchestration but it's in the data layer
- gt should own the "start a workflow" semantics

## Coordination
beads side will deprecate bd mol run once gt no longer depends on it.


Digest: mol-deacon-patrol

Test patrol cycle - verifying wisp flow


Handle callbacks from agents

Handle callbacks from agents.

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

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

**WITNESS_PING**:
Witnesses periodically ping to verify Deacon is alive. Simply acknowledge
and archive - the fact that you're processing mail proves you're running.
Your agent bead last_activity is updated automatically during patrol.
```bash
gt mail archive <message-id>
```

**HELP / Escalation**:
Assess and handle or forward to Mayor.
Archive after handling:
```bash
gt mail archive <message-id>
```

**LIFECYCLE messages**:
Polecats reporting completion, refineries reporting merge results.
Archive after processing:
```bash
gt mail archive <message-id>
```

**DOG_DONE messages**:
Dogs report completion after infrastructure tasks (orphan-scan, session-gc, etc.).
Subject format: `DOG_DONE <hostname>`
Body contains: task name, counts, status.
```bash
# Parse the report, log metrics if needed
gt mail read <id>
# Archive after noting completion
gt mail archive <message-id>
```
Dogs return to idle automatically. The report is informational - no action needed
unless the dog reports errors that require escalation.

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

**Hygiene principle**: Archive messages after they're fully processed.
Keep inbox near-empty - only unprocessed items should remain.


Digest: mol-deacon-patrol

Patrol 131: All healthy


Digest: mol-deacon-patrol

Patrol 137: All healthy


Move disk 2: A → C

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


Merge: dag-mjtm8k85

branch: polecat/dag-mjtm8k85
target: main
source_issue: dag-mjtm8k85
rig: gastown


Merge to main and push immediately.

Merge to main and push immediately.

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

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

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


Digest: mol-deacon-patrol

Patrol 121: All agents healthy, 6 health pings sent, no callbacks, no orphans, 1 idle dog, inbox clean


Add org formula examples and tests

Create example organizational formula hierarchy and tests.

## Examples

Create in .beads/formulas/:
- acme-base.formula.yaml: Company-wide (security-audit aspect)
- acme-frontend.formula.yaml: extends acme-base, adds accessibility-audit
- acme-backend.formula.yaml: extends acme-base, adds sql-injection-scan

## Tests

1. Three-level inheritance resolves correctly
2. Aspect merge order is correct
3. exclude-aspects removes parent aspect
4. Cooking org formula produces expected steps
5. Error on missing parent formula