GGT vs PGT Gap Analysis Summary

Summary of gaps comparing GGT (~4,500 LOC) to PGT (~27,700 LOC).

## Critical Gaps (P1) - Existing Issues
- gt-u1j.17: Polecat CLI (add, list, wake, sleep) - DETAILED
- gt-u1j.16: Rig CLI (add, list, show, remove) - DETAILED
- gt-u1j.18: Witness CLI (start, stop, status) - DETAILED
- gt-f9x.4: Doctor framework - DETAILED
- gt-f9x.5: Workspace doctor checks - DETAILED
- gt-f9x.6: Rig doctor checks - DETAILED

## Critical Gaps (P1) - New Issues
- gt-a95: Refinery background daemon mode - ENHANCED
- gt-hgk: Mail message types and threading - ENHANCED

## Significant Gaps (P2) - New Issues
- gt-d46: Mail CLI archive/purge/search - ENHANCED
- gt-e9k: Swarm preflight/postflight - ENHANCED
- gt-662: Swarm report generation - ENHANCED
- gt-69l: Hook system - ENHANCED
- gt-3yj: Agent monitoring - ENHANCED
- gt-1ky: Workspace CLI (may overlap f9x.3) - ENHANCED
- gt-9j9: Worker status reporting - ENHANCED
- gt-qao: Mayor CLI - ENHANCED
- gt-7o7: Session pre-shutdown checks - ENHANCED
- gt-c92: Batch all command - ENHANCED
- gt-lno: Swarm state persistence - ENHANCED
- gt-a9y: File locking - ENHANCED
- gt-30o: Error handling improvements - ENHANCED

## Significant Gaps (P2) - Existing Issues
- gt-kmn.12: Ephemeral rig support - DETAILED

## Lower Priority (P3) - New Issues
- gt-3fm: Mail orchestrator daemon - ENHANCED
- gt-2kz: Cleanup commands - ENHANCED
- gt-ebl: Names commands - ENHANCED
- gt-1u9: Interactive prompts - ENHANCED
- gt-8lz: Help text improvements - ENHANCED

## Closed as Duplicates
- gt-3tz → gt-u1j.17 (polecat CLI)
- gt-e1r → gt-u1j.16 (rig CLI)
- gt-86w → gt-f9x.4/5/6 (doctor)
- gt-alx → gt-kmn.12 (ephemeral rigs)

## Execution Order Recommendation
1. P1 CLI commands (existing detailed issues ready to implement)
2. gt-a95: Refinery daemon (blocks autonomous operation)
3. gt-7o7: Pre-shutdown checks (prevents data loss)
4. gt-a9y: File locking (prevents corruption)
5. gt-hgk + gt-d46: Mail improvements
6. gt-69l: Hook system (enables extensibility)
7. Remaining P2s in any order
8. P3s as time permits


Digest: mol-deacon-patrol

Patrol 6: OK


Digest: mol-deacon-patrol

Patrol cycle 5: routine, healthy


Request shutdown

Send shutdown request to Witness.
Wait for termination.

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


Design: Work flow simulation and validation

Validate GGT designs through simulation before implementation.

## Validation Approaches

### 1. Dry-Run Simulation (Recommended First)
Mayor walks through scenarios mentally/on paper:
- "If polecat Toast signals done with dirty git state, what happens?"
- "If Witness context fills mid-verification, what state is lost?"
- "If two polecats try to close same issue, what happens?"

Create beads for any gaps discovered.

### 2. Real Work in gastown-py
Use Python Gas Town to stress-test assumptions:
- Run actual batch work on test repos
- Observe edge cases in practice
- Document issues found

### 3. Edge Case Analysis
Systematic review of failure modes:
- Agent crashes mid-operation
- Network failures during sync
- Concurrent access to shared state
- Context limits hit at bad times

## Key Scenarios to Validate

- [ ] Witness session cycling (state preservation)
- [ ] Polecat decommission with dirty state
- [ ] Merge conflicts in queue
- [ ] Beads sync conflicts between workers
- [ ] Escalation path (stuck worker -> Mayor)
- [ ] Cross-rig communication
- [ ] Federation mail routing (future)

## Success Criteria

- No data loss scenarios identified
- Clear recovery paths for all failure modes
- Edge cases either handled or documented as limitations
- Design improves as model cognition improves

## Output

For each scenario validated:
1. Document in relevant bead if issue found
2. Create new beads for missing functionality
3. Update architecture.md if design changes


Digest: mol-deacon-patrol

Patrol #19


Digest: mol-deacon-patrol

Patrol 9: routine


Remove lifecycle intent parsing from Go

daemon/lifecycle.go parses mail subjects with regex looking for restart/shutdown/cycle keywords, then executes actions. Fix: use structured message types in mail body instead of parsing subjects. Go reads action field, does not interpret text.


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


Test: Alpha to Beta

Sibling communication test


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.


Self-test

Testing gt mail works


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

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

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

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

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


Pinned beads architecture implementation

Implement the pinned beads architecture from docs/pinned-beads-design.md

Phases:
1. Doctor checks for hook validation
2. Dashboard visibility (gt hooks, gt dashboard)
3. Protocol enforcement (self-pin, audit trail)
4. Documentation updates


Merge: gt-3x1.3

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


Witness cleanup should know worktree parent repo

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

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

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

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


Rename engineer-in-box to shiny across all docs

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


Digest: mol-deacon-patrol

Patrol 11: nominal


Implement bd bond command for artifact-level composition

Implement the artifact-level bond operator distinct from source-level rigging.

Bond accepts:
- Formula names (cooks inline to ephemeral proto, then bonds)
- Mol IDs (existing liquid work)
- Wisp IDs (existing vapor work)

See molecular-chemistry.md for the full bond table semantics.

**Updated 2025-12-25**: With ephemeral protos (gt-4v1eo), bond takes formula names directly and cooks inline. No pre-cooked proto beads.


Pinned beads should appear first in mail inbox

Currently bd mail inbox sorts by priority then date, but pinned beads (handoff context) should always appear at the top.

**Current behavior:**
- Pinned beads mixed in with regular mail based on priority/date

**Expected behavior:**
- Pinned beads always first in inbox (before priority sorting)
- This enables handoff beads to be the default first item an agent sees

**Implementation:**
1. In mail.go runMailInbox(), after filtering, sort pinned beads to top
2. Within pinned and non-pinned groups, maintain priority/date sort

**Related:**
- gt-r8ej: Implement pinned beads for handoff state
- gt-8h4: Pinned Beads epic

**Acceptance criteria:**
- bd mail inbox shows pinned beads first
- Pinned beads still sorted by priority/date among themselves
- Non-pinned mail sorted normally after pinned section


Merge: gt-h6eq.5

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


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Digest: mol-deacon-patrol

Patrol 9: Mayor OK, 11 sessions


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 15: All clear


Digest: mol-deacon-patrol

Patrol 16: Routine


Digest: mol-deacon-patrol

Patrol 15: routine


bd sync: Handle mol-* prefix for protos gracefully

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

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

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

Related: bd-47qx


Digest: mol-deacon-patrol

Patrol 7: all quiet


Digest: mol-deacon-patrol

Patrol 19: clear


mol-deacon-patrol

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

Original description:
Deacon patrol molecule template. Label: template


Digest: mol-deacon-patrol

Patrol 10: All services healthy


Daemon must delete LIFECYCLE messages after processing

## Problem

Crew workers were being killed every 5 minutes.

## Root Cause

Old stale LIFECYCLE messages from days ago were sitting in the deacon's inbox. The daemon's ProcessLifecycleRequests() was re-finding and re-processing them every 5-minute heartbeat cycle.

The closeMessage() function in lifecycle.go calls 'gt mail delete' but failures are silent - the message stays in the inbox and gets reprocessed on the next heartbeat.

## Fix Required

1. Ensure closeMessage() actually deletes the message
2. If delete fails, log the error AND mark the message as read so it's not reprocessed
3. Consider adding a 'processed' label or moving to a processed folder

## Files

- internal/daemon/lifecycle.go: closeMessage() function
- internal/daemon/lifecycle.go: ProcessLifecycleRequests() loop


Phase 4: Polish and observability

Production readiness.

## Commands
- gt patrol status - Show patrol state for all agents
- gt patrol history - Recent patrol activity

## Observability
- Metrics collection
- Alert thresholds
- Dashboard (optional)

## Tuning
- Cooldown optimization
- Wake SLA verification
- Error threshold tuning

Depends: Phase 3


Witness tmux status: show polecat count under management

Add witness-specific status line showing:
- Number of polecats under management
- Active/idle status
- Maybe: last nudge time, blocked count

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


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


CLI: workspace commands (init, add, list)

GGT needs workspace management commands beyond install.

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

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 1: inbox empty, Mayor OK, 2 witnesses, 2 refineries healthy, no orphans


Immediate daemon notification on lifecycle request

When gt handoff sends a lifecycle request to the daemon (via bd mail send deacon/), the daemon only discovers it on its next heartbeat poll (every 5 min). Workers wait unnecessarily for retirement.

## Current Behavior
1. Worker runs gt handoff
2. Lifecycle mail sent to deacon/ via beads
3. Worker blocks waiting for retirement
4. Daemon heartbeat runs (up to 5 min later)
5. Daemon processes lifecycle request and kills session

## Expected Behavior
Lifecycle requests should be processed immediately, not on poll interval.

## Proposed Solutions

### Option A: SIGUSR1 Signal (simplest)
1. Add SIGUSR1 handler in daemon that calls ProcessLifecycleRequests()
2. gt handoff reads daemon.pid and sends SIGUSR1 after mail send

### Option B: Unix Socket
1. Daemon listens on ~/gt/daemon/lifecycle.sock
2. gt handoff connects and sends 'process' message

### Option C: Watchfile + fsnotify
1. gt handoff touches ~/gt/daemon/lifecycle.trigger
2. Daemon uses fsnotify to detect and process

Recommend Option A for simplicity.

## Affected Files
- internal/daemon/daemon.go - add signal handler
- internal/cmd/handoff.go - send signal after mail


Digest: mol-deacon-patrol

Patrol cycle 13: routine


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

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


Merge polecat/rictus: Add molecule phase lifecycle diagram

Branch: polecat/rictus

Adds molecule phase lifecycle diagram to architecture.md showing Proto → Mol/Wisp → Digest state transitions with the 'states of matter' metaphor. Also documents when to use Mol (durable) vs Wisp (ephemeral).

Closes: gt-c6zs


Digest: mol-deacon-patrol

Patrol 10: Quiet, 8 sessions healthy


Merge: gt-g44u.3

branch: polecat/Ace
target: main
source_issue: gt-g44u.3
rig: gastown


Test issue 1 for displacement


Digest: mol-deacon-patrol

Patrol 14: All healthy


Molecule state file management

## Cross-Repo Reference

This work is tracked in beads repo as **bd-hulf**.

The gastown task gt-qvn7.2.2 is a placeholder.
Actual implementation happens in beads.

See: bd show bd-hulf (from beads/crew/dave)


Merge: gt-j6s8

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


Digest: mol-deacon-patrol

Patrol cycle 7: routine, healthy


mol-deacon-patrol


Wire up --wisp flag for ephemeral molecules

The --wisp flag exists in gt sling but isn't wired through to bd mol.

Need:
1. bd mol run --wisp - spawn to .beads-wisp/ instead of .beads/
2. Automatic burn on molecule completion
3. Optional squash to digest for audit trail

Currently:
- sling.go sets thing.IsWisp but never uses it
- bd mol run has no --wisp flag
- .beads-wisp/ exists but nothing writes to it


Digest: mol-deacon-patrol

Patrol #17: Stable


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Digest: mol-deacon-patrol

Patrol 16: Quiet


Ensure refinery is alive

Ensure the refinery is alive and processing merge requests.

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 18


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


gt crew add: Create crew workspace

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


Create activity aggregator

Component that connects to multiple rig beads daemons and merges their activity into a unified view. Polls GetMutations and GetWorkerStatus from each rig, groups by rig/worker. Handles connection failures gracefully.

BLOCKED BY (Beads rig):
- bd-gqxd: Enrich MutationEvent with title and assignee
- bd-l13p: Add GetWorkerStatus RPC endpoint
- bd-0oqz: Add GetMoleculeProgress RPC endpoint


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

Patrol 16


Refactor formula transformation pipeline to shared helper

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

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

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


Evaluate pending async gates

Evaluate pending async gates.

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

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

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

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

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

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

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


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

Patrol 8: quiet, 8 sessions healthy


Digest: mol-deacon-patrol

Patrol 17: all quiet


**VERIFICATION GATE**: This step enforces the Beads Promise.

If tests PASSED: This step auto-completes. Proceed to merge.

If tests FAILED:
1. Diagnose: Is this a branch regression or pre-existing on main?
2. If branch caused it:
   - Abort merge
   - Notify polecat: "Tests failing. Please fix and resubmit."
   - Skip to loop-check
3. If pre-existing on main:
   - Option A: Fix it yourself (you're the Engineer!)
   - Option B: File a bead: bd create --type=bug --priority=1 --title="..."

**GATE REQUIREMENT**: You CANNOT proceed to merge-push without:
- Tests passing, OR
- Fix committed, OR
- Bead filed for the failure

This is non-negotiable. Never disavow. Never "note and proceed."

instantiated_from: mol-refinery-patrol
step: handle-failures


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

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


Fetch and conflict check: git operations for MR

Implement git operations for MR processing.

Steps:
1. git fetch origin <mr.branch>
2. git checkout <mr.target> (main or integration/xxx)
3. git merge --no-commit --no-ff <mr.branch> (test merge)
4. Check for conflicts
5. If conflicts: abort and return Failure(conflict, files)
6. If clean: abort (actual merge in next step)

Helper functions:
- FetchBranch(branch string) error
- CheckConflicts(source, target string) ([]string, error)

Reference: docs/merge-queue-design.md#process-merge-steps


Digest: mol-deacon-patrol

Patrol 13: All healthy


gt spawn: add --polecat flag for explicit worker selection

Currently gt spawn requires positional arg format:
```
gt spawn gastown/Angharad --issue gt-xyz
```

But I tried the more intuitive flag form:
```
gt spawn --issue gt-xyz --polecat Angharad
```

This failed with 'unknown flag: --polecat'.

The flag form is more discoverable and consistent with other commands. Add --polecat flag as alternative to positional arg.


Nudge newly spawned polecats

Nudge newly spawned polecats that are ready for input.

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

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

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

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

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

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

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

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


Digest: mol-deacon-patrol

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


Molecule schema: YAML format for workflow definitions

Define the YAML schema for molecule definitions:

```yaml
molecule: <name>
version: 1
description: "Human description"
steps:
  - id: <step-id>
    title: "Step title"
    prompt: "Instructions for agent"
    depends: [<other-step-ids>]  # optional
    tier: haiku|sonnet|opus      # optional, default from config
    timeout: 30m                 # optional
```

Store molecules in:
- `<rig>/molecules/<name>.yaml` for rig-specific
- `<town>/molecules/<name>.yaml` for town-wide

Built-in molecules to ship:
- engineer-in-box: design→code→review→test→submit
- quick-fix: implement→test→submit
- research: investigate→document


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.


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


Merge: gt-xw7b

branch: polecat/morsov
target: main
source_issue: gt-xw7b
rig: gastown


Digest: mol-deacon-patrol

Patrol 2: All healthy


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 4: all quiet


load-context

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

Read the assigned issue ({{issue}}) and understand the requirements.
Identify any blockers or missing information.


Polecat template: condense Work Protocol section

The Work Protocol section summarizes the entire mol-polecat-work molecule. This is too verbose and will go stale as the molecule evolves. Condense to essential guidance only, let the molecule itself provide the steps.


Convert MR queue to wisp storage

MR queue should use wisp storage (.beads-wisp/) instead of durable beads.

## Design (Simplified)

With refinery as worktree, polecats and refinery share the same machine. No cross-machine MR coordination needed. Wisps are the natural fit.

### Storage
- Location: `.beads-wisp/mq/<mr-id>.json`
- Format: Same MRFields structure (branch, target, source_issue, worker, rig)
- Lifecycle: Create on submit, delete after merge

### Code Changes

1. **MR Creation** (done.go, mq_submit.go)
   - Write to `.beads-wisp/mq/` instead of `bd create --type=merge-request`
   - Generate ID: `mr-<timestamp>-<random>` or similar

2. **Refinery Query** (refinery/manager.go, engineer.go)
   - Read from `.beads-wisp/mq/` directory
   - List files, parse JSON, process in order

3. **Cleanup**
   - Delete MR file after successful merge
   - On failure, leave file (refinery retries)

### What Stays in Beads
- Source issues (the work being merged) - still tracked in beads
- Merge commits reference source issue ID
- `bd close <issue>` still happens on merge

### Benefits
- No sync overhead for transient MR state
- Simpler model (file per MR, delete when done)
- Consistent with wisp philosophy (ephemeral operational state)

### Not Needed
- Digest squashing (MRs are deleted, not accumulated)
- Cross-machine visibility (single-machine architecture)


Digest: mol-deacon-patrol

Patrol 1: All healthy - Mayor OK, 2 witnesses, 2 refineries, 0 polecats, 8 sessions, no callbacks


Digest: mol-deacon-patrol

Patrol 5: clear


Digest: mol-deacon-patrol

Patrol 1: cleaned 3 stale locks, 3 orphan processes, doctor passed


Refinery Patrol

Merge queue processor patrol loop with verification gates.


Patrol 8: All healthy


check-refinery

Ensure the refinery is alive and processing merge requests.

Needs: inbox-check


Formula error handling and recovery

Specify how cooking errors are reported and recovered:
- What happens when extends chain is broken (missing formula)?
- What happens when aspect pointcut matches nothing?
- What happens when expansion produces invalid structure?
- Graceful degradation vs fail-fast semantics
- Error metadata in cooked protos

Deferred: post-launch polish


Patrol 18


Digest: mol-deacon-patrol

Patrol #10: Stable


Cook patrol formulas into proto beads

Use bd cook to transform formula YAML into proto beads with proper child beads for each step.

Verify with bd mol show that protos have all children.


Computed range expansion (for-each over expressions)

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

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

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

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


gt mq status: detailed MR view

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

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

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

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


Digest: mol-deacon-patrol

Patrol 11: All healthy, dave now running


Protocol: Type System for Formulas

Protocols define interfaces that formulas implement - a type system for workflows.

## Concept

```toml
# reviewable.protocol
[protocol]
name = "Reviewable"

[requires]
steps = ["review"]  # Must have a step tagged "review"
outputs = ["review_decision"]  # Must produce this

[guarantees]
"review completes before any deploy step"
```

Formulas declare: `implements = ["Reviewable", "Deployable"]`

System verifies compliance statically.

## Use Cases

1. Ensure all deploy formulas have review steps
2. Define compliance requirements as protocols
3. Enable formula discovery: "Give me all Deployable formulas"
4. Validate formula composition compatibility

## Properties

- Static verification (before execution)
- Composable (protocol can extend protocol)
- Documentation value (self-describing contracts)

## Open Questions

1. Protocol syntax - embedded in formula or separate files?
2. Verification timing - cook time? load time?
3. Protocol versioning - how do protocols evolve?
4. Inheritance - can protocols extend other protocols?

## Related

- docs/formula_evolution.md - "Formula Schemas" section
- gt-8tmz - molecule algebra


Agent protocol integration test

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


E2E Test: Verify mol-polecat-work spawn

Simple test task for Phase 5 E2E validation


Test Polecat Arm

Test child for bonding pattern


Polecat mood command and status line display

CLI and status line support for polecat mood.

## CLI Command
```
gt polecat mood <name> <emoji>   # Set mood
gt polecat mood <name>           # Get mood
```

Sets GT_MOOD environment variable in the polecat's tmux session.

## Status Line Integration
Update runWorkerStatusLine() to read mood:
```go
mood, _ := t.GetEnvironment(session, "GT_MOOD")
if mood != "" {
    icon = mood  // Use mood emoji instead of default
} else {
    icon = AgentTypeIcons[AgentPolecat]  // Fallback to 😺
}
```

## Storage
Mood stored in tmux session environment (ephemeral, per-session).
No persistence needed - mood is reassessed each patrol cycle.

Depends on: gt-u818 (plugin system)


Digest: mol-deacon-patrol

Patrol 10: All healthy, 1 in_progress issue (max working)


Polecats must not create GitHub PRs

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

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

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

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


Digest: mol-deacon-patrol

Patrol 9: All agents healthy


Digest: mol-deacon-patrol

Patrol 1: All healthy, no messages, no orphans


Deacon primes pending workers instead of spawn nudge

## Problem

When gt spawn starts a polecat, it tries to send a nudge message immediately:
1. Starts tmux session with Claude Code
2. Waits 3 seconds (hardcoded)
3. Sends nudge via NudgeSession

But Claude Code takes 10-20+ seconds to be ready. The message arrives before
the prompt is ready, so it appears as part of the prompt string instead of
being executed.

## Solution

Move priming responsibility to the Deacon patrol cycle:

1. gt spawn: Mark polecat as pending_prime=true in state, return immediately
2. Deacon patrol: New step prime-pending-workers that:
   - Scans all rigs for workers with pending_prime=true
   - Uses WaitForClaudeReady() to poll for "> " prompt (already exists in tmux.go)
   - Once ready, sends gt prime + assignment nudge
   - Clears pending_prime flag

## Implementation

### spawn.go changes
- Remove the 3-second sleep (line 368)
- Remove the NudgeSession call (lines 375-383)
- Instead: Update polecat state with pending_prime=true

### Polecat state addition
status: assigned, pending_prime: true, issue: gt-xxx

### Deacon patrol addition
Add step between inbox-check and plugin-run:
prime-pending-workers: Check all rigs for workers marked pending_prime.
For each pending worker: WaitForClaudeReady(60s), then gt prime + nudge

### Benefits
- No race condition - we poll until actually ready
- Deacon already does health checks - priming is natural fit
- Works for polecats, witnesses, refineries, crew (unified approach)
- Timeout handling built in

## Depends on
- Existing WaitForClaudeReady() in tmux.go (lines 384-403)


Digest: mol-deacon-patrol

Patrol 14


Digest: mol-deacon-patrol

Patrol 8: All healthy


Digest: mol-deacon-patrol

Patrol #8


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


plugin-run

Execute plugins from ~/gt/plugins/. Check gates, run eligible plugins.


Bubbletea TUI dashboard

Interactive TUI using Charm Bubbletea. Root model, view routing, Lipgloss theme. Includes: dashboard, rig explorer, polecat detail with live tail, mail view, beads view, spawn wizard.


Digest: mol-deacon-patrol

Patrol 3: All healthy, no issues


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.


ensure-gastown-witness

Start the gastown rig Witness.

## Action
```bash
gt witness start gastown
```

## Verify
1. Session exists: `tmux has-session -t gastown-witness 2>/dev/null`
2. Not stalled: `gt peek gastown/witness` does NOT show "> Try" prompt
3. Heartbeat fresh: Last patrol cycle < 5 min ago

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

## OnFail
Log error. Rig polecats will be unmonitored until Witness recovers.


Digest: mol-deacon-patrol

Patrol 9


session stop: --force flag is defined but not used


Digest: mol-deacon-patrol

Patrol OK: 8 polecats, 4 witness/refineries up


Digest: mol-deacon-patrol

Patrol 10: Midpoint, all healthy


Consolidate configuration architecture: three-tier model

Rationalize Gas Town's scattered configuration into a clean three-tier model:

## Current Problems
- Config split across mayor/, .gastown/, and rig/config.json
- Runtime state mixed with configuration
- Hidden directories (.gastown/) are missed by agents
- Category confusion: identity vs config vs runtime state

## Proposed Architecture

### Tier 1: Town Config (mayor/)
```
mayor/
├── town.json      # Town identity (unchanged)
├── rigs.json      # Rig registry (unchanged)
└── config.json    # NEW: Town-level configuration
    # theme defaults, daemon settings, etc.
```

### Tier 2: Town Runtime (.runtime/)
```
~/gt/.runtime/                    # NEW: Gitignored
├── daemon.json                   # {pid, started_at, heartbeat}
├── deacon.json                   # {cycle, last_action}
└── agent-requests.json           # Lifecycle requests
```

### Tier 3: Rig Config (settings/)
```
<rig>/
├── config.json                   # Rig identity only (type, git_url, beads.prefix)
├── settings/                     # NEW: Visible, git-tracked
│   ├── config.json               # theme, merge_queue, max_workers
│   ├── namepool.json             # pool settings (style, max)
│   └── roles/                    # Per-role overrides (optional)
└── .runtime/                     # NEW: Gitignored
    ├── witness.json              # {state, started_at, stats}
    ├── refinery.json             # {state, started_at, stats}
    └── namepool-state.json       # {in_use, overflow_next}
```

## Key Principles
1. **Visible > Hidden** for config agents need to find
2. **Git-tracked** for identity and config; **gitignored** for runtime
3. **Separation of concerns**: identity, config, runtime are distinct
4. **Locality**: town config at town root, rig config at rig root

## Migration
- Move .gastown/ contents to appropriate new locations
- Update all code that reads/writes these files
- Update .gitignore patterns
- Update documentation (architecture.md, CLAUDE.md)


request-shutdown

Send shutdown request to Witness.
Wait for termination.

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

Depends: generate-summary


Digest: mol-deacon-patrol

Patrol 17: All agents healthy


Wisp directory and format

Define and implement the .beads-wisp directory structure.

## Directory Structure

```
<clone>/.beads-wisp/
  hook-<agent>.wisp.yaml     # Slung work for this agent
  patrol-<id>.wisp.yaml      # Patrol cycle state (future)
```

## Wisp File Format

```yaml
type: slung-work | patrol-cycle
created_at: ISO timestamp
created_by: entity who created it

# For slung-work:
bead_id: gt-xxx
context: optional message

# For patrol-cycle (future):
formula: mol-deacon-patrol
current_step: inbox-check
step_states: {...}
```

## Key Properties

- NOT git tracked (.beads-wisp/ in .gitignore)
- Ephemeral - burned after use
- Local to each clone
- Fast writes (no sync needed)

## Implementation

- internal/wisp/dir.go - Directory creation/management
- internal/wisp/types.go - Wisp type definitions
- internal/wisp/io.go - Read/write operations


Digest: mol-deacon-patrol

Patrol 13: All clear


Digest: mol-deacon-patrol

Patrol 14: All healthy


Digest: mol-deacon-patrol

Patrol 9: All healthy


Digest: mol-deacon-patrol

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


Compile-time vs runtime cooking

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


Inline step expansion (Step.Expand field)

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

## Current State

Steps can declare inline expansion:

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

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

## Difference from compose.expand

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

## Implementation

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

## Files

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

## Depends On

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


Digest: mol-deacon-patrol

Patrol 5: nominal


Digest: mol-deacon-patrol

Patrol 4: All healthy


Interrupt vs Queue mail semantics

Add priority/delivery semantics to mail messages.

## Semantics

| Type | Delivery | Use Case |
|------|----------|----------|
| Interrupt | tmux send-keys | Lifecycle, URGENT, stuck detection |
| Queue | Create message only | Normal mail, status, heartbeat |

## Implementation

- `bd mail send --interrupt` uses tmux send-keys notification
- Default is queue (agent checks with `gt mail check`)
- Urgent flag on messages for interrupt delivery

## Agent Side

- `gt mail check --quiet` - non-blocking check for queued mail
- `gt mail wait` - block until mail arrives (for idle agents)
- Heartbeats become queued, agent checks at natural breakpoints


Digest: mol-deacon-patrol

Patrol 3: Quiet, all agents healthy


Test Polecat Arm

Test child for bonding pattern


Workspace detection: Find() walking up directory tree

Find workspace root by walking up directory tree looking for Gas Town markers.

## Detection Logic

Walk up from current directory, looking for:
1. `config/town.json` - Primary marker (visible config dir per gt-iib)
2. `mayor/` directory at town level - Secondary marker

Stop at filesystem root if neither found.

## Interface

```go
// Find locates the town root from the given directory
func Find(startDir string) (string, error)

// FindOrError is like Find but returns a user-friendly error
func FindOrError(startDir string) (string, error)
```

## Return Values

- Success: Absolute path to town root
- Not found: Empty string + specific error
- Error: Empty string + wrapped error

## Edge Cases

- Symlinks: Follow them (use filepath.EvalSymlinks)
- Permissions: Return error if can't read directory
- Nested towns: Return nearest ancestor (shouldn't happen in practice
EOF
)


Witness Arm Bonding

# Witness Patrol Redesign

> **Status**: Design pivoted from Christmas Ornament to linear patrol (Dec 2024)

## Original Problem

Witness needs to monitor and intervene on multiple polecats. Original design proposed "Christmas Ornament" pattern with dynamic arms per polecat and fanout gates.

## Design Pivot: Why Christmas Ornament Was Wrong

The Christmas Ornament pattern (dynamic mol-polecat-arm per worker) felt un-Gas-Town:

1. **Explicit state tracking**: Nudge counts, timestamps, per-polecat state
2. **Complex machinery**: Dynamic bonding, fanout gates, aggregation
3. **Against philosophy**: Gas Town prefers discovery over tracking, events over state

**Key insight**: With step-based polecat sessions (`gt mol step done` restarts session each step), polecats are either working a step or starting a step. "Nudge counts across sessions" becomes less meaningful.

## New Design: Linear Patrol + Task Tool Parallelism + Cleanup Wisps

### Patrol Shape (Deacon-style)

```
inbox-check -> process-cleanups -> check-refinery -> survey-workers -> context-check -> loop
```

No dynamic arms. No fanout gates. Linear and simple.

### Key Changes

| Aspect | Old (Christmas Ornament) | New (Linear Patrol) |
|--------|--------------------------|---------------------|
| Per-polecat inspection | mol-polecat-arm bonded dynamically | Task tool subagents within survey-workers |
| State tracking | Persistent nudge counts in handoff bead | Fresh observation each cycle (discover from beads timestamps) |
| Cleanup queue | Explicit list in patrol state | Cleanup wisps (finalizer pattern) |
| Parallelism | Molecule arms (fanout gate) | Task tool subagents |
| Aggregation | waits_for: all-children | Task tool returns |

### The Finalizer Pattern (Cleanup Wisps)

When POLECAT_DONE arrives, Witness creates a cleanup wisp:
```bash
bd create --wisp --title "cleanup:ace" --labels cleanup,polecat:ace
```

The wisp's existence IS the pending cleanup. Witness processes these in `process-cleanups` step:
- Verify git clean, issue closed, commits exist
- Kill session, remove worktree
- Burn the wisp

Failed cleanups? Leave wisp, retry next cycle. GC-style finalization.

### Task Tool Parallelism

Instead of molecule arms, use Claude's Task tool for parallel polecat inspection:

```markdown
## survey-workers step

For each polecat, launch a Task tool subagent:
- Capture tmux state
- Assess working/idle/error/done
- Check beads for step progress
- Decide and execute action (nudge/escalate/none)

Task tool handles parallelism. No molecule machinery needed.
```

## Implementation

- [x] Draft mol-witness-patrol-v2.formula.toml (linear patrol)
- [ ] Test new patrol formula
- [ ] Deprecate mol-polecat-arm
- [ ] Remove fanout gate dependency

## Files

- `.beads/formulas/mol-witness-patrol-v2.formula.toml` - New linear patrol
- `.beads/formulas/mol-witness-patrol.formula.toml` - Old Christmas Ornament (to deprecate)

## Success Criteria

- Witness patrols work with 3+ polecats using Task tool parallelism
- Cleanup wisps correctly track pending cleanups
- No persistent nudge state - fresh observation each cycle
- Simpler, more Gas Town-y design


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.


CLI: rig commands (add, list, show, remove)

GGT needs rig management commands.

Missing Commands:
- gt rig add <git-url> [--name NAME] - Add rig to workspace
- gt rig list - List all rigs with status
- gt rig show <rig> - Show detailed rig info
- gt rig remove <rig> - Remove a rig

PGT Reference: gastown-py/src/gastown/cli/rig_cmd.py

Current State:
- gt init creates rig structure but no ongoing management
- Rig discovery in internal/rig/manager.go but not exposed via CLI


Digest: mol-deacon-patrol

Patrol cycle 17: nominal


Swarm: report generation

Generate markdown reports for completed swarms.

## Command
```
gt swarm report <swarm-id> [--save <file>]
```

## Report Content

### Header
- Swarm ID and title
- Created/completed timestamps
- Duration
- Rig name

### Task Summary
| Task | Assignee | Status | Duration |
|------|----------|--------|----------|
| gt-xxx | Toast | merged | 15m |
| gt-yyy | Nux | merged | 22m |

### Worker Contributions
- Commits per worker
- Issues closed per worker
- Lines changed (optional)

### Timeline
- Chronological events from events.jsonl
- Key milestones (started, first merge, landing)

### Issues Encountered
- Conflicts resolved
- Failed tasks (if any)
- Escalations

## Implementation
```go
func GenerateReport(swarmID string) (*SwarmReport, error)
func (r *SwarmReport) ToMarkdown() string
```

## Storage
- Save to <rig>/.gastown/swarms/<id>/report.md
- Or user-specified path with --save

## PGT Reference
gastown-py/src/gastown/swarm/manager.py generate_report()

## Acceptance Criteria
- [ ] Markdown report generated
- [ ] Includes all sections above
- [ ] Auto-saved to swarm directory
- [ ] --save allows custom path


Heartbeat convention: simple liveness signal for agents

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 17: All healthy


mol-deacon-patrol


Document bd mol bond/squash/burn CLI

Create CLI reference documentation for molecule commands:

## bd mol bond

Instantiate a proto into a runnable molecule.

```bash
bd mol bond <proto-id> [--wisp] [--assignee=<addr>]
```

- Default: creates a Mol (durable, in main beads)
- --wisp: creates a Wisp (ephemeral, in .beads-ephemeral/)
- --assignee: who will execute this molecule

## bd mol squash

Complete a molecule and generate digest.

```bash
bd mol squash <mol-id> --summary='...'
```

- For Mol: creates digest in git history
- For Wisp: evaporates (no permanent record)
- --summary: required summary of what was accomplished

## bd mol burn

Abandon a molecule without completing.

```bash
bd mol burn <mol-id> [--reason='...']
```

- Discards molecule state
- No digest created
- Use when molecule is no longer needed


Digest: mol-deacon-patrol

P9: stable


Test Patrol Parent

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

Original description:
Test parent for Christmas Ornament pattern


Harness & Priming: Document architecture and update all role contexts


Digest: mol-deacon-patrol

Patrol 15


gt install command: Create workspace structure

Create Gas Town workspace structure.

## Command

```
gt install [path]
```

If path omitted, uses current directory.

## Created Structure

```
<path>/
├── config/
│   ├── town.json      # {"type": "town", "version": 1, ...}
│   └── rigs.json      # {"version": 1, "rigs": {}}
│
└── mayor/
    ├── CLAUDE.md      # Mayor role prompting (from template)
    ├── mail/
    │   └── inbox.jsonl  # Empty inbox
    └── state.json     # Initial mayor state
```

## Implementation

```go
func Install(path string, opts InstallOptions) error

type InstallOptions struct {
    TownName string  // defaults to directory name
    Force    bool    // overwrite existing
}
```

## Steps

1. Validate path (exists, writable)
2. Check not already a town (unless --force)
3. Create config/ directory
4. Write town.json with name and timestamp
5. Write empty rigs.json
6. Create mayor/ directory structure
7. Write CLAUDE.md from template
8. Create empty inbox.jsonl
9. Write initial state.json

## Error Cases

- Path does not exist: Create it (like mkdir -p)
- Already a town: Error unless --force
- Permission denied: Clear error message
- Inside existing town: Warn (nested towns not recommended)

## Templates

Mayor CLAUDE.md comes from embedded template (see gt-u1j.20).


Test Patrol Parent

Test parent for Christmas Ornament pattern


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

Patrol 15


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.


Multiple merge/landing models documented without clarification

Architecture describes three different merge models:

1. Refinery-only model (traditional)
2. Direct landing model (Mayor bypass)
3. Swarm integration branch model (internal/swarm)

These appear to conflict. Documentation should clarify:
- Which model is canonical?
- When to use which model?
- Or are some deprecated?

This is confusing for users trying to understand the system.


Create mol-install-go-binary molecule

Create a single-step molecule for building and installing the gt binary.

## Molecule Definition
\`\`\`markdown
## Molecule: install-go-binary
Single step to rebuild and install the gt binary after code changes.

## Step: install
Build and install the gt binary locally.

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

Verify the installed binary is updated:
\`\`\`
which gt
gt --version  # if we have version command
\`\`\`
\`\`\`

## Implementation
1. Add to builtin_molecules.go
2. Update SeedBuiltinMolecules to include it
3. Run gt molecule seed

## Acceptance
- [ ] mol-install-go-binary exists in beads
- [ ] Can be instantiated standalone
- [ ] Can be included by other molecules


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

Patrol 4: quiet


Dynamic arm bonding in witness patrol

Witness survey step uses bd mol bond to attach mol-polecat-arm for each active worker. Variables: worker name, rig. Arms become children of the patrol molecule. Requires on_complete/for_each runtime support.


gt prime: Refinery context detection and output

Update gt prime to detect Refinery role:
- Detect from directory: refinery/rig/ = Refinery
- Show handoff bead reference
- Show merge queue status
- Show polecat branches with unmerged commits
- Show last actions summary


Digest: mol-deacon-patrol

Patrol 20: routine, all healthy


mol-crew-session: Startup/shutdown protocols for crew workers

Crew workers (like joe, max) don't have patrol molecules keeping them fresh. When gt gets rebuilt, they have stale binaries that cause hangs and bugs.

## Problem

- Crew binaries get stale when gt is rebuilt elsewhere
- No automatic pull/rebase/rebuild on session start
- No standardized shutdown protocol (sync, push, handoff)

## Solution: mol-crew-session

A molecule template for crew sessions:

### Startup Phase
1. `git pull --rebase` - get latest code
2. `bd sync` - sync beads
3. `go build -o gt ./cmd/gt` - rebuild gt (if in gastown)
4. `gt prime` - load context

### Work Phase
- Open-ended human interaction
- No molecule steps - just work until done

### Shutdown Phase
1. `git status` - check for uncommitted changes
2. `bd sync` - sync beads
3. `git push` - push code
4. Handoff if incomplete work

## Implementation

1. Define mol-crew-session in builtin_molecules.go
2. Update crew CLAUDE.md to reference the protocol
3. Optionally: gt prime auto-runs startup steps

## Dependencies

- Should implement after deacon/witness/polecat patrols are stable
- Consider: gt-3x0z.10 (Witness patrol molecules)

## Related

- gt-3x0z.9: Deacon wisp patrol (done)
- fix-gt script: Current workaround for binary freshness


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 16: Nominal


Digest: mol-deacon-patrol

Patrol complete: checked inbox (0 msgs), gates (0), polecats (0), health OK, GC'd 6 old wisps


Nudge newly spawned polecats

Nudge newly spawned polecats that are ready for input.

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

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

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

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

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

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

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

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


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Digest: mol-deacon-patrol

Patrol cycle 7: all nominal


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.


gt status should show runtime state, not just structure

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

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

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

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

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


Activity feed from wisp state: gt patrol status

Enable mayor and gt tool to quickly see what patrol agents are doing.

## Problem

The mayor and gt tool cannot quickly see what the deacon (or other patrol agents) are doing.
Need a way to extract an activity feed from the current wisp state.

## Desired Commands

- gt patrol status: Show current patrol state (step, timing, cycle count)
- gt patrol history: Show recent patrol cycles (from digests)
- gt patrol feed: Live tail of patrol activity

## Data Sources

- .beads-wisp/issues.jsonl: Current wisp with step progress
- .beads/issues.jsonl: Digests from squashed patrols
- heartbeat.json: Last activity timestamp

## Use Cases

1. Mayor checking deacon: Is the deacon doing anything?
2. Debugging hangs: What step is it stuck on?
3. Operator monitoring: Dashboard of patrol health
4. Summaries for handoffs: Auto-generate patrol digest

## Related

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


Polecat template: move session close checklist into molecule steps

Template has prose checklists for 'Before Signaling Done' and 'SESSION CLOSE PROTOCOL'. These should be encoded as tail steps in the polecat molecule, not repeated as prose in CLAUDE.md. Reduces duplication and ensures the steps are actually followed.


Test Patrol Parent

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

Original description:
Test parent for Christmas Ornament pattern


Digest: mol-deacon-patrol

Patrol 12: All green


Self-review

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

Fix any issues found before proceeding.


Add --fart as easter egg alias for bd mol bond

Add a hidden alias for the bond command:

```bash
bd mol fart mol-polecat-work --wisp
# equivalent to:
bd mol bond mol-polecat-work --wisp
```

## Context
The fart joke: instantiating a proto can produce either:
- A Mol (solid/substantial output)
- A Wisp (gas/ephemeral output)

## Implementation
- Add 'fart' as an alias in the mol subcommand
- No documentation needed (easter egg)
- Maybe a fun message: 'Bonding molecule...' or similar

Low priority, just for fun.


Digest: mol-deacon-patrol

Patrol 8: routine


Digest: mol-deacon-patrol

Patrol 7: OK


gt handoff deadlock at handoff.go:125

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


mol-deacon-patrol


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

Work on gt-role-template: Refine witness/CLAUDE.md role template. Run 'bd show gt-role-template' to see the full issue.


Recover stale polecat work: 4 branches with unpushed commits

During observation of polecat workflow, found 4 polecats with unpushed work:

## Branches Pushed (preserved)
- polecat/capable: 3 commits (molecule catalog, doctor orphan detection, gt done)
- polecat/dementus: 4 commits (Witness MVP, handoff fixes)
- polecat/furiosa: 2 commits (bulk polecat removal, spawn handoff)
- polecat/rictus: 1 commit (molecule docs)

## Action Required
1. Review each branch for merge-worthiness
2. Either:
   a. Create PRs for valuable work
   b. OR discard if superseded
3. After decision, clean up polecats properly

## Root Cause
Polecats were not cleaned up after previous work sessions. This is exactly why we need:
- gt-u1k: gt shutdown should fully cleanup polecats
- gt-8v8: Refuse to lose uncommitted work
- gt-9nf: Always create fresh polecats


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


Standardize session end: gt handoff for all roles

## Summary

Standardize session ending across all agent roles to use `gt handoff` as the canonical command. This is critical for the beads propulsion cycle - turning agent sessions from pets into cattle.

## Current State (Inconsistent)

| Role | Current Guidance | Command |
|------|-----------------|---------|
| Mayor | Manual mail send | `gt mail send mayor/ -s 'HANDOFF:...' -m '...'` |
| Crew | Manual mail send | `gt mail send <rig>/crew/<name> -s 'HANDOFF:...' -m '...'` |
| Witness | Manual mail send | `gt mail send <rig>/witness -s 'HANDOFF:...' -m '...'` |
| Refinery | Manual mail send | `gt mail send <rig>/refinery -s 'HANDOFF:...' -m '...'` |
| Deacon | Exit on high context | (implicit) |
| Polecat | `gt done` | `gt done [--exit TYPE]` |

## Target State (Unified)

All roles use `gt handoff`:
- `gt handoff` - Hand off current session to fresh instance
- `gt handoff -s 'context' -m 'details'` - Hand off with custom message
- For polecats: `gt handoff` internally calls `gt done`

## Changes Required

### 1. Code Changes
- [ ] Update `gt handoff` to detect polecat role and call `gt done` internally
- [ ] Consider adding `--exit` flag to `gt handoff` for polecat compatibility

### 2. CLAUDE.md Updates (gastown)
- [ ] ~/gt/CLAUDE.md (Mayor)
- [ ] gastown/crew/max/CLAUDE.md
- [ ] gastown/crew/joe/CLAUDE.md
- [ ] gastown/witness/CLAUDE.md
- [ ] gastown/refinery/CLAUDE.md (and rig/)
- [ ] deacon/CLAUDE.md

### 3. CLAUDE.md Updates (beads)
- [ ] beads/mayor/rig/CLAUDE.md
- [ ] beads/crew/dave/CLAUDE.md
- [ ] beads/crew/zoey/CLAUDE.md
- [ ] beads/witness/CLAUDE.md
- [ ] beads/refinery/CLAUDE.md (and rig/)

### 4. Architecture Docs
- [ ] docs/patrol-system-design.md
- [ ] gastown/mayor/rig/docs/prompts.md
- [ ] gastown/mayor/rig/docs/session-management.md

## New Session End Checklist (Universal)

```
# SESSION CLOSE PROTOCOL

[ ] 1. git status              (check uncommitted changes)
[ ] 2. git add <files>         (stage changes)
[ ] 3. git commit -m '...'     (commit with issue ID)
[ ] 4. bd sync                 (sync beads)
[ ] 5. git push                (push to remote - CRITICAL)
[ ] 6. gt handoff              (hand off to fresh session)
       OR gt handoff -s 'Context' -m 'Details for next session'
```

## Why This Matters

The handoff mechanism is what turns agent sessions from **pets** (precious, long-lived) into **cattle** (disposable, replaceable). At any time, any agent can:
1. Send itself a detailed handoff mail (or sling itself a mol)
2. System shuts them down and restarts them
3. Fresh session runs priming and reads mail
4. Work continues seamlessly

This enables:
- Unlimited context through automatic cycling
- Clean recovery from any state
- Consistent behavior across all roles
- Simplified agent instructions


Digest: mol-deacon-patrol

Patrol 5: All clear


Digest: mol-deacon-patrol

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


Test Task 1: Add comment to gt.go


Build rig/worker tree view component

Bubbletea component showing hierarchical rig list with workers. Each rig expandable to show workers. Each worker shows: status indicator, current molecule title, step progress (X/Y), time since last activity.


Digest: mol-deacon-patrol

Patrol 18: Nominal


Digest: mol-deacon-patrol

Patrol 3: all healthy


bd pin command fails: invalid field for update: pinned

In beads v0.32.0, `bd pin <id>` fails with 'invalid field for update: pinned'. The pinned field exists in the schema but update logic doesn't handle it.

Repro:
```
bd create 'test issue'
bd pin <id>
# Error: invalid field for update: pinned
```

Expected: Issue should be pinned.

This blocks gt mail send --pinned from working.


Digest: mol-deacon-patrol

Patrol 5: all quiet


Auto-target: MRs for epic children target integration branch

Automatically target integration branches for epic children.

When 'gt mq submit' is called:
1. Parse source issue from branch
2. Check if issue has a parent epic
3. Check if integration/<epic> branch exists
4. If yes: set target=integration/<epic>
5. If no: set target=main

This ensures batch work automatically flows to integration branches.

Also update 'gt mq submit --epic' to explicitly target an epic's integration branch.

Reference: docs/merge-queue-design.md#integration-branches


Direct test

Testing direct bd create


Create molecules catalog directory structure

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

## Structure

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

## PROTO.md Format

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

# mol-gastown-boot

Town bootstrap molecule.

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

## Catalog Loading

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

This enables file-based proto development with beads tracking.


Digest: mol-deacon-patrol

Patrol 8: All agents healthy


Burn and respawn or loop

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

Decision point at end of patrol cycle:

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

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

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

This enables infinite patrol duration via context-aware respawning.


Swarm remaining gt-8tmz P3/P4 issues

## Swarm Plan for Molecule Algebra Completion

Encapsulates the remaining swarmable work from gt-8tmz (Molecule Algebra epic).

### Swarmable Issues (6 total)

**Batch 1 - Parallel (3 polecats):**
- gt-8tmz.10: Rename Engineer in Box to Shiny (simple rename)
- gt-8tmz.36: Validate expanded step IDs are unique (validation)
- gt-8tmz.31: Formula validation specification (docs)

**Batch 2 - Sequential (1 polecat):**
- gt-8tmz.34: Expansion var overrides → gt-8tmz.35: Inline step expansion
  (dependency chain - .35 depends on .34)

**Batch 3 - Solo (1 polecat):**
- gt-8tmz.30: Proto debugging and inspection tools (add --graph)

### Hold for Human (7 issues - need design decisions)
- gt-8tmz.19: Meta-formulas (schema design)
- gt-8tmz.20: Org inheritance (policy structure)
- gt-8tmz.21: Conditional aspects (when clause syntax)
- gt-8tmz.22: Review dimensions as aspects (refactor)
- gt-8tmz.28: Error handling policy
- gt-8tmz.29: Versioning strategy
- gt-8tmz.37: Nested expansion recursion

### Code Location
All work is in beads repo: /Users/stevey/gt/beads/crew/emma
- internal/formula/*.go
- cmd/bd/cook.go

### Execution
Hand to Witness for polecat dispatch, or execute manually with polecats.


Digest: mol-deacon-patrol

Patrol 12: Quiet


Burn and respawn or loop


Test Polecat Arm

Test child for bonding pattern


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

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

## Variables

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

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

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

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

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

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

Calculate: minutes since last activity.
Needs: capture

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

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

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

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

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

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

Record decision and rationale.
Needs: load-history

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

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

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

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

**none**: No action needed.

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

## Output

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

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


Digest: mol-deacon-patrol

Patrol OK: town quiet


Digest: mol-deacon-patrol

Patrol 2: routine, all healthy


Built-in molecules: engineer-in-box, quick-fix, research

Create built-in molecules as Beads issues:

## engineer-in-box

```markdown
id: mol-engineer-in-box
type: molecule
title: Engineer in a Box

Full workflow from design to merge.

## Step: design
Think carefully about architecture. Consider:
- Existing patterns in the codebase
- Trade-offs between approaches
- Testability and maintainability

Write a brief design summary before proceeding.

## Step: implement
Write the code. Follow codebase conventions.
Needs: design

## Step: review
Self-review the changes. Look for:
- Bugs and edge cases
- Style issues
- Missing error handling
Needs: implement

## Step: test
Write and run tests. Cover happy path and edge cases.
Fix any failures before proceeding.
Needs: implement

## Step: submit
Submit for merge via refinery.
Needs: review, test
```

## quick-fix

```markdown
id: mol-quick-fix
type: molecule
title: Quick Fix

Fast path for small changes.

## Step: implement
Make the fix. Keep it focused.

## Step: test
Run relevant tests. Fix any regressions.
Needs: implement

## Step: submit
Submit for merge.
Needs: test
```

## research

```markdown
id: mol-research
type: molecule
title: Research

Investigation workflow.

## Step: investigate
Explore the question. Search code, read docs,
understand context. Take notes.

## Step: document
Write up findings. Include:
- What you learned
- Recommendations
- Open questions
Needs: investigate
```

## Storage

Built-in molecules live in `<town>/.beads/` as regular issues.
Created during `gt install` or `bd init`.


Digest: mol-deacon-patrol

Patrol 15: all healthy, doctor pass


Digest: mol-deacon-patrol

P20 - handoff


Digest: mol-deacon-patrol

Patrol 6: All healthy, no messages


gt mq integration create: create integration branch for epic

Implement 'gt mq integration create <epic>' command.

Actions:
1. Verify epic exists
2. Create branch: integration/<epic-id> from main
3. Push to origin
4. Store integration branch info in epic metadata

Usage:
  gt mq integration create gt-auth-epic
  # Creates integration/gt-auth-epic from main

Future MRs for this epic's children will auto-target this branch.

Reference: docs/merge-queue-design.md#integration-branches


test message


Witness Plugin System

Patrol extensions via molecule bonding.

## Core Insight

Plugins ARE molecules. No separate format needed.

## How It Works

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

## Plugin Molecule Structure

A plugin is just a molecule proto with specific labels:

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

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

## Registration

Plugins registered in rig config or molecules.jsonl:

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

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

## Execution

The `plugin-run` step in patrol:

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

## CLI

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

## Benefits

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


Merge: gt-3x0z.1

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


Digest: mol-deacon-patrol

Patrol 1: All healthy, no mail, no action needed


Digest: mol-deacon-patrol

Patrol 8


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


Implement gt sling command

The unified work dispatch command.

```bash
gt sling <thing> <target> [options]
```

Implements spawn + assign + pin in one operation. See sling-design.md.

Acceptance:
- [ ] Parse thing (proto name, issue ID, epic ID)
- [ ] Parse target (agent address)
- [ ] Spawn molecule if proto
- [ ] Assign to target agent
- [ ] Pin to agent's hook (pinned bead)
- [ ] Support --wisp flag for ephemeral
- [ ] Support --molecule flag for issue+workflow
- [ ] Error if hook already occupied (unless --force)


Implement gt mq CLI commands

Add gt mq subcommands as sugar over bd:

- gt mq submit: Create MR for current branch
- gt mq list: Show open merge requests
- gt mq next: Show next MR ready to process
- gt mq process: Engineer processes the queue
- gt mq reorder <id> --after <x>: Change ordering via deps
- gt mq status <id>: Show MR details

All commands should work with the Beads data plane.


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

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

## Variables

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

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

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

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

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

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

Calculate: minutes since last activity.
Needs: capture

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

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

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

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

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

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

Record decision and rationale.
Needs: load-history

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

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

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

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

**none**: No action needed.

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

## Output

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

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


Per-rig beads repo configuration

Add per-rig beads configuration to rig config schema.

## Config Schema

In each rig's config.json:

```json
{
  "version": 1,
  "name": "wyvern",
  "git_url": "https://github.com/steveyegge/wyvern",
  "beads": {
    "repo": "local",    // "local" | "<path>" | "<git-url>"
    "root": null,       // Override bd --root (optional)
    "prefix": "wyv"     // Issue prefix for this rig
  }
}
```

## Repo Options

| Value | Meaning | Use Case |
|-------|---------|----------|
| `"local"` | Use project's `.beads/` | Own projects, full commit access |
| `"<path>"` | Use beads at path | OSS contributions |
| `"<git-url>"` | Clone and use repo | Team shared beads |

## Environment Injection

When spawning polecats, Gas Town sets:
```bash
export BEADS_ROOT="<resolved-path>"
```

## Resolution Logic

```go
func ResolveBeadsRoot(rigConfig *RigConfig, rigPath string) (string, error) {
    beads := rigConfig.Beads
    switch {
    case beads.Root != "":
        return beads.Root, nil
    case beads.Repo == "local" || beads.Repo == "":
        return filepath.Join(rigPath, ".beads"), nil
    case strings.HasPrefix(beads.Repo, "/"):
        return beads.Repo, nil
    case strings.Contains(beads.Repo, "://"):
        return cloneAndResolve(beads.Repo)
    default:
        return filepath.Join(rigPath, beads.Repo), nil
    }
}
```

## Backwards Compatibility

If `beads` section missing, assume `"repo": "local"`.


Digest: mol-deacon-patrol

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


gt sling: nudge crew/mayor if session is running

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 20: All healthy, routine cycle complete


Add --account flag to gt spawn and gt crew attach

Add --account=<handle> flag. Precedence: GT_ACCOUNT env > --account flag > default from config.


Spawn inject: Enter not submitted after multiline paste

When gt spawn injects a multiline context (85+ lines), Claude Code shows
'[Pasted text #1 +85 lines]' but the prompt is not submitted.

The tmux SendKeys function does include 'Enter' but it appears to not work
for long pasted text:

```go
func (t *Tmux) SendKeys(session, keys string) error {
    _, err := t.run("send-keys", "-t", session, keys, "Enter")
    return err
}
```

Possible fixes:
1. Use tmux paste-buffer instead of send-keys for long text
2. Send Enter separately after the text
3. Use bracketed paste mode correctly

Reproduction:
```bash
gt spawn gastown/Nux --issue gt-u1j.13 --create
# Session shows pasted text but waits at prompt
```


Mail system: message types and threading

GGT mail system needs message types and threading like PGT.

## 1. Message Types
Add to internal/mail/types.go:
```go
type MessageType string
const (
    TypeTask         MessageType = "task"         // Required processing
    TypeScavenge     MessageType = "scavenge"     // Optional first-come work
    TypeNotification MessageType = "notification" // Informational
    TypeReply        MessageType = "reply"        // Response to message
)

// Update Message struct
type Message struct {
    // existing fields...
    Type     MessageType `json:"type"`
    ThreadID string      `json:"thread_id,omitempty"`
    ReplyTo  string      `json:"reply_to,omitempty"`
}
```

## 2. Priority Levels
Expand from 2 to 4:
```go
type Priority string
const (
    PriorityLow    Priority = "low"
    PriorityNormal Priority = "normal"
    PriorityHigh   Priority = "high"
    PriorityUrgent Priority = "urgent"
)
```

## 3. CLI Updates
internal/cmd/mail.go:
- Add --type flag to send: `gt mail send ... --type task`
- Add --reply-to flag: `gt mail send ... --reply-to <msg-id>`
- Add thread command: `gt mail thread <thread-id>`

## 4. Threading Logic
NewMessage() should auto-generate thread_id if not a reply.
Reply messages inherit thread_id from original.

## Files to Modify
- internal/mail/types.go: Add types, expand Priority
- internal/mail/mailbox.go: Thread filtering
- internal/cmd/mail.go: CLI flags and thread command

## PGT Reference
gastown-py/src/gastown/mail/message.py

## Acceptance Criteria
- [ ] Messages have type field (default: notification)
- [ ] 4 priority levels supported
- [ ] Reply creates thread with shared thread_id
- [ ] gt mail thread <id> shows conversation


Interactive prompts for destructive operations

Add interactive confirmations for destructive operations.

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

## Implementation Options

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

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

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

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

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


gt spawn should auto-start refinery/witness if not running

When spawning a polecat, the infrastructure (refinery, witness) should already be running.

## Current Behavior
spawn.go only:1. Assigns issue to polecat2. Sends work mail3. Starts polecat session

The refinery and witness must be started separately.

## Expected Behavior (per user feedback)
When spawning a polecat, if the rig's refinery or witness is not running, auto-start them.

## Options

### Option A: spawn auto-starts infrastructure
Check if refinery/witness running before spawn, start if not.

### Option B: gt swarm start <rig> command
Explicit command that:
1. Starts refinery
2. Starts witness
3. Optionally spawns polecats on bd ready issues

### Option C: gt rig start <rig>
Similar to Option B but as rig lifecycle command.

## Related
- gt-n7z7: refinery --foreground race condition bug
- gt-u1j.18: witness CLI commands


Digest: mol-deacon-patrol

Patrol cycle 19: routine


Digest: mol-deacon-patrol

Patrol 5: All healthy


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Refinery not processing merge-requests - stale MRs accumulating

Discovered 15 stale merge-request issues in beads rig that were never processed by the refinery. These appear to be from polecat branches that completed work but the refinery didn't pick up the merge-requests.

Closed issues:
- bd-r06v, bd-bhg7, bd-754r, bd-fcl1, bd-3zzh
- bd-bijf, bd-5rj1, bd-kptp, bd-rdzk, bd-ibl9
- bd-gfo3, bd-aq3s, bd-x2bd, bd-s1pz, bd-h27p

Need to investigate:
1. Why refinery isn't picking up merge-requests
2. Whether the polecat branches have valid work to merge
3. Add monitoring/alerting for stale MRs


Molecule-based Witness Patrol

Wire up mol-witness-patrol as a tracking ledger for the Go-based witness.

## Key Insight

The molecule is NOT an executor - it's a **tracking structure**. The existing
Go code in internal/witness/manager.go continues to run the patrol loop.
The molecule instance provides visibility, audit trail, and polecat tracking.

## Current State

- Witness runs as Go code with direct patrol logic
- mol-witness-patrol and mol-polecat-arm exist as molecule definitions
- gt mol bond creates dynamic children
- No molecule tracking of actual witness operations

## Target State

1. When witness starts → instantiate mol-witness-patrol on its hook
2. When Go code discovers polecat → bond mol-polecat-arm child
3. When polecat completes/dies → close that arm issue
4. gt mol progress shows current patrol state
5. Molecule survives wisp burns via handoff bead

## Benefits

- Visibility: gt mol status gastown/witness shows active arms
- Audit: Each polecat inspection is a trackable issue
- Consistency: Same tracking model as polecats with molecules
- Progress: gt mol progress works for witness too

## Non-Goals

- NOT replacing Go code with Claude session
- NOT running molecule steps as prompts
- NOT requiring Claude for witness operation


bd mol current: soft cursor showing current/next step

Add bd mol current command for molecule navigation orientation.

## Usage

bd mol current [mol-id]

If mol-id given, show status for that molecule.
If not given, infer from in_progress issues assigned to current agent.

## Output

You're working on molecule gt-abc (Feature X)

  [checkmark] gt-abc.1: Design
  [checkmark] gt-abc.2: Scaffold
  [checkmark] gt-abc.3: Implement
  [arrow] gt-abc.4: Write tests [in_progress] <- YOU ARE HERE
  [circle] gt-abc.5: Documentation
  [circle] gt-abc.6: Exit decision

Progress: 3/6 steps complete

## Key behaviors
- Shows full molecule structure with status indicators
- Highlights current in_progress step
- If no in_progress, highlights first ready step
- Works without explicit cursor tracking (inferred from state)

## Implementation notes
- Query children of mol-id
- Sort by dependency order
- Find first in_progress or first ready
- Format with status indicators

## Beads feature
This is a bd command - needs implementation in beads repo.