Merge: gt-lnji

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


bd admin compact --older-than=0 ignores the flag, uses default 30 days

When running bd admin compact --prune --older-than=0, the output says 'No expired tombstones to prune (TTL: 30 days)' - the flag value is ignored.


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


Digest: mol-deacon-patrol

Patrol 7: all healthy


Test MR

test


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)


Hook system for event extensibility

GGT needs hook system for extensibility like PGT.

## Event Types
```go
type Event string
const (
    EventPreSessionStart  Event = "pre-session-start"
    EventPostSessionStart Event = "post-session-start"
    EventPreShutdown      Event = "pre-shutdown"
    EventPostShutdown     Event = "post-shutdown"
    EventOnPaneOutput     Event = "on-pane-output"
    EventSessionIdle      Event = "session-idle"
    EventMailReceived     Event = "mail-received"
    EventWorkAssigned     Event = "work-assigned"
)
```

## Hook Configuration
File: .claude/hooks.json or .gastown/hooks.json
```json
{
  "hooks": {
    "pre-shutdown": [
      {"type": "command", "cmd": "./scripts/pre-shutdown.sh"}
    ],
    "on-pane-output": [
      {"type": "command", "cmd": "./scripts/activity-monitor.sh"}
    ]
  }
}
```

## Hook Types
1. **Command**: Execute external script
2. **Built-in**: Internal Go functions (pre-shutdown checks)

## Hook Interface
```go
type HookRunner struct {
    config *HookConfig
}

type HookResult struct {
    Success bool
    Message string
    Block   bool  // For pre-* hooks: should operation be blocked?
}

func (r *HookRunner) Fire(event Event, ctx *HookContext) []HookResult
```

## CLI Commands
```
gt hooks list [<event>]    # List registered hooks
gt hooks fire <event>      # Manually fire for testing
gt hooks test [--all]      # Validate hook config
```

## Integration Points
- internal/session/manager.go: Fire pre/post session hooks
- internal/mail/router.go: Fire mail-received hook

## New Package
internal/hooks/
├── types.go    # Event, HookConfig, HookResult
├── runner.go   # HookRunner, Fire()
└── builtin.go  # Built-in hooks (pre-shutdown checks)

## PGT Reference
gastown-py/src/gastown/hooks/

## Acceptance Criteria
- [ ] Hook config loading from JSON
- [ ] Command hooks execute subprocess
- [ ] Pre-shutdown hook integration with session stop
- [ ] CLI for listing and testing hooks


Digest: mol-deacon-patrol

Patrol 8: healthy


Digest: mol-deacon-patrol

Patrol cycle 17: all healthy


Merge: keeper-1767106012994

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


Digest: mol-deacon-patrol

Patrol cycle 9: all healthy


Digest: mol-deacon-patrol

Patrol 2: Quiet cycle, all agents healthy


Digest: mol-deacon-patrol

Patrol 18: All healthy


Digest: mol-deacon-patrol

Patrol 5: quiet


Digest: mol-deacon-patrol

Patrol #4: Stable


gt rig shutdown: Gracefully stop all rig agents

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

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


Digest: mol-deacon-patrol

Patrol 215: All nominal


Digest: mol-deacon-patrol

Patrol: no callbacks, no polecats, all agents healthy, cleaned 2 orphan processes + 2 stale locks


Merge: valkyrie-mjtn0kda

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


gt convoy create: Default --notify to mayor/

When Mayor creates a convoy, --notify should default to mayor/ since:
1. Mayor dispatched the work
2. Mayor should know when it lands
3. Reduces flag boilerplate

Implementation: Check actor identity, if mayor, default notify to 'mayor/'


Merge: ace-1767074385080

attached_args: Code review this merge request

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


Digest: mol-deacon-patrol

Patrol 6: routine


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 14: All clear


Merge: morsov-dogs

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


Work on ga-rd4: Add gt polecat status command. Show detai...

Work on ga-rd4: Add gt polecat status command. Show detailed polecat status including current issue, session state, last activity time. When done, submit MR (not PR) to integration branch for Refinery.


Digest: mol-deacon-patrol

Patrol 1: All healthy, no messages


bd create --dedup: semantic deduplication before issue creation

Before creating issue, check for semantic duplicates using AI similarity.

**From VC**: internal/deduplication/ - AI-powered batch comparison. ~300 lines.
VC had issue pollution problem: 438 issues with ~350+ spam because no early dedup.

**Gas Town implementation**: CLI flag on bd create:
```bash
bd create --dedup --title="Fix auth bug" --description="..."
```

Checks recent issues (7-day window) with AI similarity. If confidence >0.85, warns or blocks.

**Value**: Prevents pollution from parallel workers discovering same issues.

**VC lesson**: 115 issues filed in single day (Nov 2) because supervisor over-discovered without dedup. Rate limiting + dedup are essential.


Digest: mol-deacon-patrol

Patrol 3: All healthy


Competing molecule instantiation mechanisms need resolution

Documentation describes 4 different molecule instantiation methods:
1. bd mol bond (beads-based, in molecules.md)
2. gt sling (proposed in sling-design.md)
3. gt swarm (implemented in code)
4. gt molecule instantiate (in architecture.md)

Role prompts use different methods:
- Deacon: bd mol spawn
- Refinery: gt mol bond
- Polecat: bd mol current

Need to:
1. Decide canonical mechanism
2. Update all role prompts to use it
3. Remove/deprecate alternatives
4. Update architecture docs


mol-polecat-work

Full polecat lifecycle from assignment to decommission.

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

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


Digest: mol-deacon-patrol

Patrol 258 complete: no incidents


Digest: mol-deacon-patrol

Patrol 16: All healthy


Merge: furiosa-mjxkrt01

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


Digest: mol-deacon-patrol

Patrol 17: routine


Digest: mol-deacon-patrol

Patrol 14: Nominal


Merge: slit-mjxc8tp2

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


Digest: mol-deacon-patrol

Patrol 12


Digest: mol-deacon-patrol

Patrol cycle 11: All healthy.


Merge: furiosa-1767079892643

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


Digest: mol-deacon-patrol

Patrol 13: Nominal


Daemon should auto-start as part of town bootstrap

The Go daemon should be started automatically when the town boots up (mol-gastown-boot). Currently it requires manual 'gt daemon start'.

The daemon is critical infrastructure - it monitors the Deacon and nudges it when naked. Without it, the Deacon patrol loop doesn't run.


Refinery: event-driven merge queue

## Problem
Refinery only acts on explicit requests, no queue management.

## ZFC-Compliant Solution
Add steps to `mol-refinery-patrol.formula.toml`:

```toml
[[step]]
id = "check-merge-requests"
title = "Process merge queue from inbox"
description = """
1. Check inbox for MERGE_READY messages: gt mail inbox
2. For each merge request:
   - Read branch name from message
   - Add to local queue (track in state.json or temp file)
3. Process queue in order:
   - git fetch origin
   - git checkout main && git pull
   - git merge --no-ff origin/polecat/<name>
   - If conflict: mail Mayor with details, skip
   - If success: git push && delete remote branch
4. For each merged branch:
   - gt mail send <rig>/witness -s "MERGED: polecat/<name>" -m "Branch merged successfully"
   - Delete the MERGE_READY mail
"""
depends_on = ["inbox-check"]
```

## Queue State
Refinery agent tracks queue in `state.json`:
```json
{
  "merge_queue": ["polecat/foo", "polecat/bar"],
  "last_merged": "polecat/baz"
}
```

## Why This Works
- Refinery Claude agent runs formula
- Agent handles conflicts with judgment
- Mail is coordination primitive
- State persists in agent's state.json

## Files
- formulas/mol-refinery-patrol.formula.toml


HOP Foundation: Entity Identity & Federation

Foundation work for HOP graph architecture. Enables agent provenance tracking, cross-workspace federation, and delegation semantics. See ~/gt/docs/hop/GRAPH-ARCHITECTURE.md for full design.


Set GIT_AUTHOR_NAME per agent session

Set GIT_AUTHOR_NAME to actor string (e.g., gastown/crew/joe) in agent session environment. GIT_AUTHOR_EMAIL stays as workspace owner. Enables git log --author queries for agent work.


Clarify GIT_AUTHOR_EMAIL handling for agents

When setting GIT_AUTHOR_NAME per agent (gt-6r18e.1), also need to decide GIT_AUTHOR_EMAIL:

Option A (proposed): Keep overseer's git email
- GIT_AUTHOR_EMAIL = steve.yegge@gmail.com (from git config)
- This anchors ownership to the human
- Agents are tools of the overseer

Option B: Construct agent email
- GIT_AUTHOR_EMAIL = gastown-crew-joe@gastown.local
- Fully separates agent identity
- May cause issues with GitHub/Bitbucket attribution

Recommendation: Option A - keeps human ownership clear while GIT_AUTHOR_NAME shows which agent did the work.


Default BD_ACTOR in beads create

BUG: BD_ACTOR env var is only read in --no-db mode and daemon RPC path. Normal direct mode skips BD_ACTOR and falls back to $USER. The comment claims 'Viper handles BD_ACTOR automatically' but there's no actual viper binding. Fix: Add explicit os.Getenv("BD_ACTOR") check in main.go line 441-448, same as the --no-db path at line 352-360.


Add workspace metadata file (.town.json)

Create ~/gt/.town.json with owner email, workspace name, and public name. Anchors workspace to entity identity for federation.


Design cross-workspace URI scheme

Define URI format: gt://entity/chain/rig/issue-id. Document resolution semantics for local vs remote references. Update GRAPH-ARCHITECTURE.md with final spec.


Add delegation relationship type to beads

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


Beads schema: Add created_by field enforcement

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


Beads schema: Add overseer field for team scenarios

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


Add gt audit command for provenance queries

New command: gt audit --actor=X to query work history. Combines git log, beads queries, and event log. Shows commits, beads created/closed, events by actor.


Document BD_ACTOR format convention

Document the canonical BD_ACTOR format:
- Town level: mayor, deacon
- Rig level: {rig}/witness, {rig}/refinery
- Workers: {rig}/crew/{name}, {rig}/polecats/{name}

Currently only defined in lifecycle.go:807-829. Should be in docs and CLAUDE.md for agents to reference.

Also document the attribution model:
- GIT_AUTHOR_NAME = BD_ACTOR (agent identity, who did the work)
- GIT_AUTHOR_EMAIL = overseer email (who owns the work)
- created_by = BD_ACTOR (beads field)


Digest: mol-deacon-patrol

Patrol cycle 5: All quiet.


Digest: mol-deacon-patrol

Patrol cycle 19: All healthy


Digest: mol-deacon-patrol


gt swarm: Not discovering tasks from epic dependents

gt swarm create/start shows '0 tasks loaded' even when epic has dependents.

Repro:
1. Create epic gt-hw6
2. Create tasks and add deps: bd dep add gt-xxx gt-hw6
3. gt swarm create gastown --epic gt-hw6 --worker Toast
4. Swarm shows 'Tasks: 0'

Expected: Swarm should discover tasks that depend on the epic.
Actual: Shows '(no tasks loaded)'


Implement gt harness create command

Add scaffolding command to create a new harness:
- gt harness create [path]
- Creates config/, mayor/, .beads/redirect (optional)
- Optionally initializes git
- Generates CLAUDE.md with Mayor role
- Could also offer a template repo alternative


gt convoy merge: Consolidate multiple convoys into one

When work gets slung individually (e.g., due to gt swarm being broken), each issue gets its own convoy via auto-convoy. Need ability to merge these into a single tracking unit.

## Use Case

```bash
# Current state: 5 convoys for one logical swarm
gt convoy list
  1. hq-cv-6svfs: Work: gt mail reply
  2. hq-cv-2zdme: Work: gt mail search
  3. hq-cv-k3v6u: Work: gt mail archive/purge
  4. hq-cv-pc2ww: Work: gt mail mark/delete
  5. hq-cv-xdf66: Work: gt mail check

# Desired: merge into one
gt convoy merge hq-cv-xdf66 hq-cv-pc2ww hq-cv-k3v6u hq-cv-2zdme hq-cv-6svfs --title "Mail CLI Implementation"
# Result: single convoy tracking all 5 issues
```

## Implementation

- Take target convoy (first arg) or create new one
- Move tracked issues from source convoys to target
- Close/delete source convoys
- Update subscribers

## Alternative

Could also support:
```bash
gt convoy add hq-cv-xdf66 gt-d46.2 gt-d46.3 gt-d46.4 gt-d46.5
# Then manually close the orphaned convoys
```

But merge is cleaner UX.


Bug: LIFECYCLE messages use literal <rig> placeholder

Found stale LIFECYCLE messages addressed to @<rig>/witness instead of actual rig names like @gastown/witness. Template substitution not happening.

Examples found:
- gm-bay: LIFECYCLE: polecat requesting shutdown (to @<rig>/witness)
- gm-7du, gm-94e, gm-u5x, gm-89b: same issue

Fix: Ensure rig name is substituted in lifecycle mail templates.


Merge: gt-pnv61

branch: polecat/rictus-1767163044
target: main
source_issue: gt-pnv61
rig: gastown


Merge: furiosa-mjw349y2

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


Digest: mol-deacon-patrol

Patrol 21: all healthy, no lifecycle requests, 4 in-progress issues noted, handoff triggered at threshold


Centralize session name construction patterns

Session name patterns like fmt.Sprintf("gt-%s-<role>", rigName) scattered across files:
- witness.go:287
- refinery.go:383
- start.go:275
- crew_helpers.go:68-70

Also: sessionToAgentID logic duplicated in sling.go:365-391 and handoff.go:374-402.

Suggestion: Create session_names.go with WitnessSessionName(), RefinerySessionName(), CrewSessionName() functions plus AgentIdentity type with FromSessionName()/ToSessionName() methods.


Digest: mol-deacon-patrol

Patrol 111: All healthy


Digest: mol-deacon-patrol

Patrol cycle 14: all healthy


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)


BUG: Daemon heartbeat too slow (10 min) for stuck agent detection

The daemon's recoveryHeartbeatInterval is set to 10 MINUTES (daemon.go:134).

This means if an agent gets stuck, it can waste up to 10 minutes before Boot is spawned to triage.

Suggested fix: Reduce heartbeat to 2-3 minutes, or add a separate fast-path stuck-detection check.

Related: hq-nsx0g


Deacon patrol: Janitorial cleanup patterns

Patterns discovered during town cleanup that could be automated in Deacon's patrol step:

## Stale Issue Detection

1. **Stale in_progress issues** - Issues marked in_progress for >N days without activity
   - Example: 9 issues in_progress, some likely complete
   - Action: Report list, optionally nudge assignee

2. **Duplicate issues** - Same title with different IDs
   - Example: gt-87jz and gt-qp2w both 'mol-witness-patrol'
   - Action: Flag for human review

3. **Orphaned issues** - Referenced in commits but still open
   - bd doctor already detects: 'hq-65jk (commit f9e9034d)'
   - Action: Report, suggest closing

4. **Stale test messages** - Messages with 'test' in title older than N days
   - Example: gt-54kn, gt-1ero, gt-nriy, gt-wrw2, gt-pnu4, gt-g1ud, gt-b5sh
   - Action: Auto-close or flag

## File System Cleanup

5. **Log file accumulation** - daemon.log and rotated .log.gz files
   - Found 8+ log files across rigs
   - Action: Rotate, compress, delete old (>7 days)

6. **Beads directory size** - Town .beads/ is 65MB
   - Action: Monitor growth, alert on threshold

## Database Health

7. **Legacy database fingerprints** - Older DBs without fingerprint
   - bd doctor warns about this
   - Action: Report, suggest migration

## Cross-Reference Integrity

8. **Gastown↔Beads issue sync** - Issues filed in wrong project
   - Example: gt-8tmz.* describes Beads work but no bd-* issues
   - Action: Detect cross-references, verify both sides exist

## Implementation Notes

These could be added to mol-deacon-patrol.formula.yaml as cleanup substeps:
- Daily: Log rotation, stale issue report
- Weekly: Duplicate detection, orphan check
- Monthly: Size monitoring, archive old closed issues


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

Patrol 8: quiet, 8 sessions healthy


Digest: mol-deacon-patrol

Patrol 18: routine


Test Epic: GGT MVP Validation


Test Task 1: Add comment to gt.go


Test Task 2: Add comment to version.go


Test7

Test with fixed workdir


Digest: mol-deacon-patrol

Patrol #13


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

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

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

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

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

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


detectSender() doesn't recognize crew workers

## Problem

`detectSender()` in mail.go only detects polecats, not crew workers.

## Current Code (line 445)

```go
// If in a rig's polecats directory, extract address
if strings.Contains(cwd, "/polecats/") {
    // extract rig/polecat
}

// Default to mayor
return "mayor/"
```

## Symptom

Emma (crew worker at `/Users/stevey/gt/beads/crew/emma`) runs:
- `gt mail inbox` → checks `mayor/` inbox (wrong!)
- Should check `beads/emma` or `beads/crew/emma`

## Fix

Add crew detection:
```go
// If in a rig's crew directory, extract address
if strings.Contains(cwd, "/crew/") {
    parts := strings.Split(cwd, "/crew/")
    if len(parts) >= 2 {
        rigPath := parts[0]
        crewName := strings.Split(parts[1], "/")[0]
        rigName := filepath.Base(rigPath)
        return fmt.Sprintf("%s/%s", rigName, crewName)
    }
}
```

## Also Check

- `gt prime` correctly detects role, so there may be another detection function that works
- Should unify detection logic


Digest: mol-deacon-patrol

Cycle 13


Update architecture.md: Engineer role and Beads merge queue

Update docs/architecture.md with recent design decisions:

1. Agent table: Change "Refinery" role to "Engineer"
   - Refinery = place/module/directory
   - Engineer = role (agent that works in the Refinery)

2. Merge Queue section: Document Beads-native model
   - MRs are beads issues with --type=merge-request
   - gt mq commands (submit, list, next, process, reorder)
   - Ordering via depends-on links

3. CLI section: Add gt mq commands

4. Key Design Decisions: Add decisions for:
   - #15: Merge Queue in Beads
   - #16: Engineer role (distinct from Refinery place)
   - #17: Session restart protocol for Engineer


Digest: mol-deacon-patrol

Patrol 4: all quiet


Missing failsafe: No circuit breaker for stuck agents

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

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

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

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

(Moved from hq-2wy2i)


Digest: mol-deacon-patrol

Patrol cycle 55: All healthy


Digest: mol-deacon-patrol

Cycle 13: quiet


gt mq list: doesn't show submitted MRs

After submitting MRs with gt mq submit, gt mq list gastown shows empty queue.

## Reproduction
1. gt mq submit --issue gt-h5n.5 --branch polecat/Scabrous
2. gt mq list gastown → (empty)
3. bd list --type=merge-request → shows the MR

## Expected
gt mq list should show submitted MRs

## MR example
gt-ts4u has rig: gastown in description, type=merge-request, status=open


gt convoy create: Fix notify slot warning

When creating convoy with --notify, get warning:
```
⚠ Warning: couldn't set notify slot: exit status 1
```

But notification target still shows in status. Investigate slot setter.


Digest: mol-deacon-patrol

Patrol 7: All healthy


Digest: mol-deacon-patrol

Patrol 15: quiet


Wire GT_ACCOUNT env var into spawn/attach

When spawning Claude Code (gt spawn, gt crew attach), check GT_ACCOUNT env var. If set, look up account in config and set CLAUDE_CONFIG_DIR accordingly.


Digest: mol-deacon-patrol

Patrol 79: Routine


Digest: mol-deacon-patrol

Patrol 16: quick pass


Witness: Don't nuke polecats with open MRs or hooked work

Witness mass-murdered 11 polecats during cleanup:

Evidence from witness session:
```
gt polecat nuke gastown/slit gastown/capable
Worktrees nuked: 11 (cheedo, dag, dementus, furiosa×2, keeper, nux, rictus, slit, capable, toast)
```

Casualties:
- cheedo had open MR gt-t7g4b (branch not pushed, work lost)
- cheedo had hooked work gt-w0fqg (had to reassign)
- Unknown other work may have been lost

Pre-nuke checklist must verify:
1. No uncommitted/unpushed work
2. No open MRs awaiting merge
3. No work on hook (unless orphan recovery)
4. Issue is actually closed (not just 'done' signaled)

The witness is supposed to be a pit boss, not an executioner.

## Update: Instructions exist but were ignored

Witness CLAUDE.md HAS the pre-kill checklist:
```
Before killing ANY polecat session, verify:
[ ] 3. Check for unpushed commits
[ ] 4. Verify issue closed
[ ] 5. Verify PR submitted
```

Code for verification also exists on main (c28a0d5).

But witness still mass-murdered. Possible causes:
1. Context pressure (witness was at 4% - may have lost instructions)
2. Some aggressive prompt overriding safety checks
3. Agent non-compliance

Need: Enforcement mechanism, not just instructions.


mol-witness-patrol

Per-rig worker monitor patrol loop.

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

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

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


Digest: mol-deacon-patrol

Patrol 12: Routine


Merge: ace-1767106056721

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


Digest: mol-deacon-patrol

Patrol 2: All healthy, no changes


gt doctor: Check settings.json hook consistency across town

## Problem

Settings.json files in different locations can become out of sync, causing session_id
passthrough to fail for some roles but not others. This happened when:
- Town root was updated to use session-start.sh
- Rig root still had old 'gt prime' direct call
- Mayor/rig had mixed state

## Locations to Check

1. `~/gt/.claude/settings.json` (town root - Mayor uses this)
2. `~/gt/<rig>/.claude/settings.json` (rig root - polecats/crew use this)
3. `~/gt/<rig>/mayor/rig/.claude/settings.json` (mayor/rig - source of truth)

## Check Logic

For SessionStart and PreCompact hooks, verify all use `session-start.sh`:
- Must contain `bash ~/.claude/hooks/session-start.sh` (or full path)
- Warn if any use bare `gt prime` without session-start.sh wrapper

## Output

```
gt doctor
...
session-hooks: ✓ All settings.json files use session-start.sh
# or
session-hooks: ✗ ~/gt/gastown/.claude/settings.json uses bare 'gt prime' (missing session_id passthrough)
```

## Fix Suggestion

When inconsistency found, suggest:
```
Hint: Update SessionStart/PreCompact hooks to use 'bash ~/.claude/hooks/session-start.sh'
      This enables session_id passthrough for gt seance discovery.
```

## Related

- session-start.sh extracts session_id from Claude Code stdin JSON
- gt prime outputs [GAS TOWN] metadata line for seance
- gt seance discovers sessions from .events.jsonl


gt rig status: Show single-rig status summary

Add 'gt rig status <rig>' command to show focused status for a single rig.

## Use Case
When working on a specific rig, you want to see just that rig's status without the full town overview from 'gt status'.

## Proposed Output
```
$ gt rig status gastown

─── gastown/ ───────────────────────────────────────────

🦉 Witness: running
🏭 Refinery: running (MQ: 2 pending)

👷 Crew (5): 2 active, 3 idle
😺 Polecats (3): 1 working, 2 idle

Open issues: 45
In progress: 3
Ready: 12
```

## Implementation
- Add 'status' subcommand to internal/cmd/rig.go
- Reuse status rendering logic from gt status
- Filter to single rig
- Optionally add --verbose for full agent list


Digest: mol-deacon-patrol

Patrol complete: gastown witness came online, town healthy


Patrols: Cyclic molecules for autonomous maintenance

## Vision

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

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

## Core Concepts

### 1. Cyclic Molecules

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

Patrols have a loop_to field specifying where to restart.

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

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

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

### 3. The Beacon

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

Without beacon, nothing proactive happens.

### 4. Session Reset as Patrol Step

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

### 5. Multi-Role Patrols

Each supervisor has its own patrol:

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

### 6. Cadence Tiers

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

### 7. Best-Effort Scheduling

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

## Open Questions

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

## Related

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

## Metaphor

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

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


Fix failing beads tests: TestIntegration and TestPolecatWorkMolecule


Create mol-refinery-patrol with test failure gates

## Problem

The Refinery role lacks:
1. A formal patrol molecule (Deacon has mol-deacon-patrol)
2. Engineer identity/philosophy guidance
3. Structural enforcement of failure handling (not just documentation)

When tests fail during merge processing, the current guidance doesn't prevent 'disavowal' - noting a problem exists and proceeding without tracking it.

## The Structural Solution

Create `mol-refinery-patrol` with **verification gates** that make disavowal structurally impossible:

```markdown
## Molecule: mol-refinery-patrol

## Step: inbox-check
Check mail for MR submissions, escalations, messages.
Process any urgent items first.

## Step: queue-scan
Fetch remote, identify polecat branches waiting.
If queue empty, skip to context-check.
Track branch list for this cycle.
Needs: inbox-check

## Step: process-branch
Pick next branch. Rebase on current main.
If rebase conflicts: notify polecat, skip to next branch.
Needs: queue-scan

## Step: run-tests
Run go test ./...
Track results (pass/fail count).
Needs: process-branch

## Step: handle-failures
**GATE**: If tests passed, this step auto-completes.
If tests failed:
- Diagnose: branch regression or pre-existing?
- Branch regression → abort, notify polecat
- Pre-existing → EITHER fix OR \`bd create --type=bug\`
**VERIFY**: Fix committed OR bead filed before proceeding.
Needs: run-tests

## Step: merge-push
Merge to main (ff-only preferred).
Push immediately.
Delete polecat branch.
Needs: handle-failures

## Step: loop-check
More branches? Return to process-branch.
Otherwise continue to summary.
Needs: merge-push

## Step: generate-summary
Summarize cycle: branches processed, tests results, issues filed.
Needs: loop-check

## Step: context-check
Check context usage.
If high: write handoff, prepare for burn/respawn.
Needs: generate-summary

## Step: burn-or-loop
If context LOW and queue non-empty: loop (return to queue-scan)
If context HIGH or queue empty: burn and exit
Needs: context-check
```

## Implementation Tasks

### 1. Add RefineryPatrolMolecule() to builtin_molecules.go
- Copy pattern from DeaconPatrolMolecule
- Include all steps with proper `Needs:` dependencies
- Emphasize handle-failures gate in description

### 2. Update Refinery CLAUDE.md
Add sections:
- **The Engineer Mindset** (identity/philosophy)
- **The Scotty Test** (Would Scotty walk past this?)
- **Patrol Molecule** reference (mol-refinery-patrol)
- **Test Failure Protocol** (explicit decision tree)

### 3. Create prompts/roles/refinery.md
- Follow pattern of deacon.md
- Include patrol execution loop diagram
- Reference the molecule

### 4. Consider mol-witness-patrol
- Witness has detailed heartbeat protocol but no formal molecule
- Similar pattern could apply

## Why This Matters

GUPP: 'If you have work on your hook, you have to run it.'
The patrol molecule puts failure handling ON THE HOOK as a mandatory step.
You can't skip handle-failures to get to merge-push.
The structure enforces good behavior.

## Related
- gt-7919: Fix failing beads tests (discovered during this session)
- mol-deacon-patrol (existing pattern to follow)
- docs/molecular-chemistry.md (chemistry concepts)
- docs/wisp-architecture.md (wisp vs mol decision)


Add await-work and plugin-run steps to mol-refinery-patrol

## Context

The mol-refinery-patrol needs two additional steps:

### 1. await-work (Decision Point)

Not just 'wait for signal' but a crossroads:
- Check for pending signals (mail, nudge, human)
- Check plugin gates (any plugins need to run?)
- Check maintenance schedule (daily/weekly due?)
- Evaluate context and decide next action

Position: Between burn-or-loop and inbox-check (the loop point)

### 2. plugin-run (Like Deacon)

Refinery needs plugin support for:
- **Monitors**: Flag PRs touching sensitive code
- **Gates**: Block merges under certain conditions
- **Schedulers**: Reorder queue based on priority/urgency
- **Maintenance**: Weekly cleanup, audits, stats
- **Audits**: Log merge statistics, track patterns

Position: After queue-scan, before process-branch

## Implementation

Update RefineryPatrolMolecule() in builtin_molecules.go to include:
- await-work step with decision tree
- plugin-run step with gate types

## Related
- gt-7920 (original mol-refinery-patrol)
- docs/deacon-plugins.md (existing plugin model)


Dynamic wisp modification: insert, reorder, bond mid-execution

## Vision

Wisps should be living documents that adapt mid-execution, not static scripts.

## Operations Needed

### bd mol insert
Insert a step into a running wisp:
```bash
bd mol insert <wisp-id> --after <step> --step 'security-gate: Review auth changes'
```

### bd mol reorder
Change step order in a running wisp:
```bash
bd mol reorder <wisp-id> --move process-branch --before current
```

### bd mol bond (to wisp)
Attach an entire molecule to a running wisp:
```bash
bd mol bond mol-weekly-maintenance <wisp-id> --after await-work
```

### bd mol skip
Skip a step (with reason):
```bash
bd mol skip <wisp-id> <step> --reason 'Queue empty, skipping summary'
```

## Use Cases

1. **Hotfix priority**: Reorder queue to process urgent MR first
2. **Security gate**: Insert review step when PR touches sensitive files
3. **Scheduled maintenance**: Bond maintenance mol on schedule
4. **Context adaptation**: Skip steps when they don't apply

## Why This Matters

Engine room problems emerge dynamically. The wisp's flexibility lets agents model situations as they evolve, not execute rigid scripts.

GUPP still applies: whatever's on your hook, you run. But the hook contents can adapt.

## Implementation

This requires Beads changes to support wisp mutation:
- Track step ordering in wisp storage
- Support step insertion/deletion/reordering
- Handle bond-to-wisp (spawn steps into existing wisp)
- Preserve audit trail of modifications


gt rig add / gt doctor: patrol awareness and wiring

## Problem

When a rig is installed or audited, we need to ensure all built-in patrols and role hooks are properly wired up.

## gt rig add Changes

When adding a rig, automatically:

1. **Create patrol molecules** for each role:
   - mol-deacon-patrol (town-level)
   - mol-witness-patrol (per-rig)
   - mol-refinery-patrol (per-rig)

2. **Set up hooks** that trigger patrols:
   - Deacon: daemon timer / heartbeat
   - Witness: daemon timer / polecat lifecycle events
   - Refinery: MR submission events / daemon timer

3. **Configure daemon** to manage these patrols:
   - Register patrol molecules in daemon config
   - Set up respawn behavior for each role

4. **Create plugin directories**:
   - ~/gt/plugins/ (town-level)
   - <rig>/plugins/ (rig-level, if needed)

## gt doctor Changes

Add patrol health checks:

### patrol-molecules-exist
- Verify mol-deacon-patrol, mol-witness-patrol, mol-refinery-patrol exist
- Check they parse correctly (valid steps, dependencies)

### patrol-hooks-wired
- Verify hooks trigger patrol execution
- Check daemon is configured to manage patrols

### patrol-not-stuck
- Detect wisps that have been in-progress too long
- Flag orphaned patrol molecules (no active session)

### patrol-plugins-accessible
- Verify plugin directories exist and are readable
- Check plugin frontmatter parses correctly

### patrol-roles-have-prompts
- Verify prompts/roles/*.md exist for each role
- Check they reference the correct patrol molecule

## Auto-fix

gt doctor --fix can:
- Create missing patrol molecules
- Wire up missing hooks
- Create plugin directories
- NOT restart stuck patrols (needs human decision)

## Related
- gt-7920 (mol-refinery-patrol)
- gt-7921 (await-work and plugin-run)
- docs/wisp-architecture.md


Merge: dag-1767074322940

attached_args: Code review this merge request

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


Extract timing constants to constants package

Extract hardcoded timing values to named constants.

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

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

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


Consolidate duplicate formatAge functions in feed TUI

formatConvoyAge in convoy.go is nearly identical to formatAge in view.go. Consolidate into one shared function.


Bug: bd update --parent doesn't persist

The --parent flag on bd update reports success but doesn't persist the parent field.

**Confirmed behavior:**
```bash
bd update gt-9kp3t --parent=gt-d0jqp --no-daemon -v
# Output: ✓ Updated issue: gt-9kp3t

bd show gt-9kp3t --json | jq '.[0].parent'
# Returns: null

grep gt-9kp3t .beads/issues.jsonl | head -1 | jq '.parent'
# Returns: null (field not present)
```

**Root cause hypothesis:**
The update handler processes the --parent flag but either:
1. Doesn't write it to the Issue struct
2. Doesn't include it in the JSONL export
3. The field name doesn't match what export expects

**Files to check:**
- cmd/bd/show.go:638-640 (flag handling)
- cmd/bd/show.go:810-814 (reparenting logic)
- internal/rpc/server_issues_epics.go (RPC handler)
- internal/rpc/protocol.go (UpdateArgs.Parent field)

Related: bd-cj2e (the original feature implementation)


Test swarm epic


Digest: mol-deacon-patrol

Patrol 68: Routine


Digest: mol-deacon-patrol

Patrol 3: Routine


Instrument gt commands to appear in activity feed

## Architecture: Two-tier event system

```
┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│ gt commands │  │ bd mutations│  │ agent output│
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       └────────────────┼────────────────┘
                        ▼
              ~/gt/.events.jsonl (raw, audit)
                        │
              ┌─────────────────┐
              │  Feed daemon    │ ← goroutine in gt daemon
              │  - filters      │
              │  - aggregates   │
              │  - AI curates   │
              └────────┬────────┘
                       ▼
              ~/gt/.feed.jsonl (curated, user-facing)
```

## Event format

```json
{
  "ts": "2025-12-28T10:30:15Z",
  "source": "gt",
  "type": "sling",
  "actor": "mayor",
  "payload": {"bead": "gt-xyz", "target": "gastown/Toast"},
  "visibility": "feed"
}
```

Visibility: `audit` | `feed` | `both`

## Implementation phases

1. **Event logging** - gt commands write to ~/gt/.events.jsonl
2. **Feed daemon** - goroutine in gt daemon tails events, writes curated feed
3. **Feed readers** - gt feed, gt dashboard read from .feed.jsonl
4. **AI curation** - future: haiku summarization for complex activity

Part of epic gt-u7dxq


Digest: mol-deacon-patrol

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


Digest: mol-deacon-patrol

Patrol 159: All healthy


Digest: mol-deacon-patrol

Patrol 9: All clear


Digest: mol-deacon-patrol

P14: stable


Digest: mol-deacon-patrol

Patrol 16: All healthy, mayor handoff observed (not for deacon)


Digest: mol-deacon-patrol

Patrol 5: All healthy


Digest: mol-deacon-patrol

Patrol #18: Stable


Tmux session scanner for running agents

Scan tmux for running Gas Town agent sessions.

## Deliverables

1. Function: ScanRunningSessions() -> []AgentAddr
   - Parse tmux list-sessions
   - Filter for Gas Town session naming pattern
   - Return addresses of running agents

2. Function: ScanRigSessions(rig) -> []AgentAddr
   - Filter running sessions by rig prefix

3. Alias resolution:
   - #rig/gastown → running sessions in gastown
   - #town → all running Gas Town sessions
   - #witnesses → running witness sessions

## Location
internal/discovery/tmux.go

## Acceptance
- Scanner finds running sessions
- Correctly parses session names to addresses
- Handles no-sessions case gracefully


Merge: keeper-mjtn0wgm

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


Digest: mol-deacon-patrol

Patrol 122: All healthy, no callbacks, 3 convoys tracking


Document the Propulsion Principle

Write canonical documentation for the Universal Gas Town Propulsion Principle.

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

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

This is foundational theory-of-operation documentation.


Merge: gt-92l

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


Digest: mol-deacon-patrol

Patrol 259 complete: no incidents


Digest: mol-deacon-patrol

Patrol 8: All green


context-check

Assess own context usage. If high, prepare for handoff.


Digest: mol-deacon-patrol

Patrol 14: All clear


Ephemeral polecats: spawn fresh, delete on completion

## Design Decision

Switch from pooled/idle polecats to ephemeral model:
- Spawn creates fresh worktree from main
- Polecat requests shutdown when done (bottom-up)
- Witness verifies handoff, kills session, deletes worktree
- No 'idle' state - polecats exist only while working

## Rationale

1. **Git worktrees are fast** - pooling optimization is obsolete
2. **Pooling creates maintenance burden:**
   - Git stashes accumulate
   - Untracked artifacts pile up
   - Branches drift from main
   - Beads DB gets stale
3. **PGT sync problems** came from persistent branches
4. **Support infrastructure exists** - Witness, Refinery, Mayor handle continuity
5. **Simpler mental model** - polecat exists = work in progress

## Lifecycle

```
Spawn:
  gt spawn --issue <id>
  → Creates fresh worktree: git worktree add polecats/<name> -b polecat/<name>
  → Initializes beads in worktree
  → Starts session, assigns work

Working:
  Polecat does task
  → Pushes to polecat/<name> branch
  → Submits to merge queue when ready

Completion (POLECAT-INITIATED):
  Polecat runs: gt handoff
  → Verifies git state clean
  → Sends mail to Witness: "Ready for shutdown"
  → Marks itself done, waits for termination

Cleanup (WITNESS-OWNED):
  Witness receives shutdown request
  → Verifies PR merged or in queue
  → Verifies no uncommitted changes
  → Kills session: gt session stop <rig>/<polecat>
  → Deletes worktree: git worktree remove polecats/<name>
  → Deletes branch: git branch -d polecat/<name>
  → Optionally: Notifies Mayor of completion
```

## Key Insight: Bottom-Up Shutdown

**Old model (wrong)**: Top-down batch shutdown - "cancel the swarm"
**New model (right)**: Bottom-up individual shutdown - polecat requests, Witness executes

This enables streaming:
- Workers come and go continuously
- No "swarm end" to trigger cleanup
- Each worker manages its own lifecycle
- Witness is the lifecycle authority

## Implementation

1. Add `gt handoff` command for polecats to request shutdown
2. Modify gt spawn to always create fresh worktree
3. Run bd init in new worktree (beads needs initialization)
4. Add shutdown request handler to Witness
5. Witness verifies handoff, then cleans up:
   - Kill session
   - Remove worktree
   - Delete branch
6. Remove 'idle' state from polecat state machine
7. Simplify gt polecat list (only shows active)

## Impact on Other Tasks

- gt-17r (Zombie cleanup): Becomes trivial - orphan worktrees
- gt-4my (Worker health): Simpler - no idle/stuck ambiguity
- gt-f9x.5/f9x.6 (Doctor): Fewer states to validate
- gt-eu9 (Witness handoff): Witness receives polecat shutdown requests


Digest: mol-deacon-patrol

Patrol 12: routine


Digest: mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol complete: beads witness+refinery came online, both rigs healthy


Merge: dementus-mjw46vz4

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


Digest: mol-deacon-patrol

Patrol complete: all healthy


Merge: capable-mjxen9vb

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


Formula schema: Define formula.toml structure

Define the formula file format in .beads/formulas/:
- formula name and description
- input parameters (pr, branch, files)
- legs array with name, focus, output
- synthesis config
- dependencies between legs

Reference: gt-ubqeg.1 epic description for schema ideas.
Create example formula: code-review.formula.toml


Digest: mol-deacon-patrol

Patrol cycle 4: all healthy


gt mail send should tmux-notify recipient

## Problem

When mail is sent via gt mail send, the recipient session does not get a tmux notification. In Python Gas Town (PGT), mail delivery triggers a tmux display-message or similar notification so the agent knows mail arrived.

## Expected Behavior

When gt mail send <addr> is called:
1. Mail is delivered to recipient inbox
2. If recipient has an active tmux session, send notification
3. Notification should be visible (display-message or bell)

## Current Behavior

Mail is delivered but no notification. Agent has to poll inbox to discover new mail.

## Impact

- Agents miss time-sensitive messages
- Heartbeat pokes from daemon will not wake agents
- Coordination is slower (polling vs push)

## Implementation Notes

After successful mail delivery, check if recipient has active session:
- gt session list can identify active sessions
- tmux display-message or send-keys can notify
- Could inject a visible prompt like "[MAIL] New message from <sender>"

## Reference

Check PGT implementation for how it handles this.


Beads daemon cache stale - hook queries return wrong data

Root cause: Beads daemon has stale data. Multiple bd daemons running (4 instances). When gt hook status queries via daemon, it gets old hooked bead. When queried with --no-daemon, correct bead is shown. This is a daemon cache coherence issue, not an ID resolution bug. Workaround: Use bd --no-daemon commands or restart daemons.


Digest: mol-deacon-patrol

Patrol 5: All healthy, no messages


Session pre-shutdown checks

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

## Pre-Shutdown Checks

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

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

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

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

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 107: All healthy


gt mail: Cross-level routing is broken

**Problem:**
When Mayor (at ~/gt) sends mail to a rig worker (gastown/crew/max), the message lands in town-level beads (~/.beads/) but the recipient checks rig-level beads (crew/max/.beads/).

**Reproduction:**
```bash
cd ~/gt
gt mail send gastown/crew/max -s 'Test' -m 'body'
# Message goes to ~/gt/.beads/ with prefix hq-*

cd ~/gt/gastown/crew/max
gt mail inbox
# Does NOT see the message - it's looking in crew/max/.beads/
```

**Root cause:**
`findBeadsWorkDir()` walks up from CWD to find .beads. `Router.Send()` runs `bd create` in that directory. This means messages always go to the sender's beads, not the recipient's.

**Fix options:**
1. Route based on recipient address - if sending to rig/*, use that rig's .beads
2. Use a single shared beads database for all mail (simpler but less isolated)
3. Teach agents to check both levels (workaround, not fix)

**Related:**
- gt-ngu1: Pinned beads sorting (done but pointless if mail doesn't route)
- This blocks all cross-level mail functionality


Digest: mol-deacon-patrol

Patrol 8: Witnesses healthy


Digest: mol-deacon-patrol

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


Digest: mol-deacon-patrol

Patrol 145 complete: All healthy


Migrate startup paths from SessionBeacon to StartupNudge

## Problem
StartupNudge function exists (gt-bgfqy) but no code uses it.
All startup paths still call session.SessionBeacon() + 'gt prime' separately.

SessionBeacon format: [GAS TOWN] address • molID • timestamp
StartupNudge format: [GAS TOWN] recipient <- sender • timestamp • topic

The key difference is SENDER - needed for seance discovery ('who started this session?').

## Files to Update
- internal/cmd/crew_lifecycle.go (lines 354-371, 525-538)
- internal/cmd/start.go (lines 757, 805-810, 946-949)
- internal/cmd/deacon.go (line 307)
- internal/cmd/witness.go (line 350)
- internal/cmd/up.go (lines 288, 335, 559, 660)
- internal/cmd/mayor.go (line 151)
- internal/session/manager.go (lines 205, 215)

## Pattern
Replace:
  beacon := session.SessionBeacon(address, molID)
  t.NudgeSession(sessionID, beacon)
  t.NudgeSession(sessionID, "gt prime")

With:
  cfg := session.StartupNudgeConfig{Recipient: address, Sender: sender, Topic: topic, MolID: molID}
  session.StartupNudge(t, sessionID, cfg)
  // Note: StartupNudge is the session title, still need separate 'gt prime' for priming

## Related
- gt-85whr (parent epic)
- gt-j0546 (did RuntimeConfig refactor, not StartupNudge)


Day 3.5: Test polecat → refinery → merged flow

Integration test:
1. Spawn polecat with work
2. Polecat completes (updates state, closes bead)
3. Witness detects completion
4. Witness sends MERGE_READY
5. Refinery processes and merges
6. MERGED signal received

Parent: gt-hwka3


HOP: Skill vectors on work items

Add skill embeddings to work items for capability-based matching. See ~/ai/stevey-gastown/hop/CONTEXT.md. Post-v0.1 work.


Digest: mol-deacon-patrol

Patrol #17


Digest: mol-deacon-patrol

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


Add gt seance command for predecessor discovery

## Purpose

**"Where did you put the stuff you left for me?"** - The #1 question in any handoff.

Seance lets you literally talk to your predecessor. Not parse their logs - commune with them.

## The Insight

Claude Code can resume sessions with full context. A seance spawns a Claude subprocess that resumes a predecessor session, letting you ask questions directly:
- "Why did you make this decision?"
- "Where were you stuck?"
- "What did you try that did not work?"

## Architecture

```
gt seance --talk <session-id>
    │
    └─▶ claude --fork-session --resume <id>
        │
        └─▶ Spawns in sandbox namespace (ghost-joe)
            │
            └─▶ Communication via tmux
                "Where is the config you mentioned?"
                → Predecessor has full context, answers
```

**Key flags:**
- `--fork-session` creates new session ID (read-only seance, no grave disturbance)
- `--resume <id>` loads predecessor context

## Discovery

Sessions are discoverable via:
1. **Beacon in transcript** - `[GAS TOWN] role • topic • timestamp` searchable via `/resume`
2. **SessionStart events** - Hook emits to `~/gt/.events.jsonl` (our data, ZFC-compliant)
3. **Claude /resume search** - Native filtering

## Usage

```bash
gt seance                     # List discoverable sessions
gt seance --role crew         # Filter by role
gt seance --rig gastown       # Filter by rig
gt seance --recent 10         # Last N sessions

gt seance --talk <id>         # THE SEANCE - talk to predecessor
gt seance --talk <id> -p "Where is X?"  # One-shot question
```

## ZFC Compliance

- **No parsing Claude internals** - Use Claude CLI to resume
- **Agent discovers** - Claude reads session, interprets, responds
- **Events are ours** - SessionStart hook writes to our event stream

## Beacon Format

```
[GAS TOWN] gastown/crew/joe <- deacon • 2025-12-30T15:42 • assigned:gt-xyz
```

Searchable via `claude --resume "GAS TOWN"` or `claude --resume "gastown/crew/joe"`


BUG: gt convoy create with count argument doesn't set convoy description

Root cause: 'gt convoy create' usage is 'create <name> [issues...]' but we invoked 'gt convoy create 1 beads' expecting polecat count semantics.

The '1' became the convoy name, 'beads' was treated as an issue ID (failed to track).

Options:
1. Add --count flag for polecat spawning semantics
2. When issues are added later via 'convoy add', update convoy title if current title is just a number
3. If first arg looks like an issue ID (has prefix), auto-generate name from issue title

For now, users must use: 'gt convoy create "Descriptive name" issue-id' not 'gt convoy create <count> <rig>'


Digest: mol-deacon-patrol

Patrol 14: All healthy.


Refactor: Extract common manager creation boilerplate

Five nearly identical functions exist for creating managers:
- getPolecatManager (polecat.go:241)
- getSessionManager (session.go:183)
- getCrewManager (crew_helpers.go:44)
- getRefineryManager (refinery.go:116)
- getWitnessManager (witness.go:102)

All follow the same pattern: find workspace, load config, get rig, create manager. Should extract to a common helper that takes a factory function.


QUICK FIX: Make gt crew start use NudgeSession instead of SendKeys

## The Immediate Problem
`gt crew start gus` fails to deliver the GUPP nudge because:
1. Uses `t.SendKeys(sessionID, "gt prime")` which has the Enter-concatenation bug
2. Should use `t.NudgeSession()` which solves this reliably

## Quick Fix
In start.go, change line ~809:
```go
// Before
if err := t.SendKeys(sessionID, "gt prime"); err != nil {

// After
if err := t.NudgeSession(sessionID, "gt prime"); err != nil {
```

Same fix needed in:
- start.go:756 (restart path)
- crew_lifecycle.go:354
- crew_lifecycle.go:525

## Why This Is P0
Cold starts of crew workers are broken. This blocks basic usage.

## Note
This is a stopgap. The full solution is the StartupNudge refactor (gt-bgfqy).


Digest: mol-deacon-patrol

Patrol cycle 5: All 6 agents healthy


Default crew name should be something better than 'main'

When creating a new rig, the default crew worker name is 'main'. This is confusing since:

1. 'main' is also the git branch name
2. It doesn't convey that this is the user's personal workspace
3. Crew names should feel more personal/distinct

Consider alternatives like:
- 'home' - the user's home base
- 'desk' - their personal desk
- 'joe/max/etc' - actual name-based defaults
- Let user pick during rig init


Day 3.4: Refinery processes MERGE_READY mail

Refinery patrol handles merge requests:
1. Check inbox for MERGE_READY messages
2. For each, verify branch exists
3. Run merge checks (tests, lint)
4. If passing, merge to main
5. Send MERGED signal back

Parent: gt-hwka3


Digest: mol-deacon-patrol

Patrol complete: no callbacks, all agents healthy, no orphans, doctor 24/28


Witness cleanup leaves worktree behind

When witness processes POLECAT_DONE:
- Session is killed ✓
- Work is merged ✓
- Worktree is NOT removed ✗

Dementus example: session 'not running', but /Users/stevey/gt/gastown/polecats/dementus still exists.

Expected: gt polecat nuke equivalent - full cleanup including worktree deletion.


Digest: mol-deacon-patrol

Patrol 11: 3 rigs healthy


Digest: mol-deacon-patrol

Patrol 11: all healthy


README: Update Install section for homebrew/npm

README currently shows:
  go install github.com/steveyegge/gastown/cmd/gt@latest

But users will install via homebrew or npm. Update to show:
  brew install gastown
  # or
  npm install -g @anthropic/gastown

Also add prerequisites section (tmux, Claude Code CLI).


Swarms of One: Lightweight single-worker task dispatch

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

## Context

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

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

## Design Questions

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

## Possible Interface

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

## Related

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


Digest: mol-deacon-patrol

Patrol 35: all quiet


Digest: mol-deacon-patrol

Patrol 14


Digest: mol-deacon-patrol

Patrol 133: All healthy


Digest: mol-deacon-patrol

Patrol 12: clear


Digest: mol-deacon-patrol

Patrol cycle 11: All healthy


Digest: mol-deacon-patrol

Patrol cycle 20: all healthy, handoff


Bug: MRs appearing in bd ready output

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 2: all healthy, no incidents


Digest: mol-deacon-patrol

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


Digest: mol-deacon-patrol

Cycle 298: All healthy, refinery processing


Merge: gt-fpv2p

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


Merge: dementus-1767146229184

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


Digest: mol-deacon-patrol

Patrol 13: Quick cycle, all quiet


Digest: mol-deacon-patrol

Patrol 13: All clear


Merge: nux-1767079896198

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


Digest: mol-deacon-patrol

Cycle 20 - handoff threshold


Digest: mol-deacon-patrol

Patrol 100: All healthy - milestone!


Design: Swarm shutdown and worker cleanup

Design for graceful swarm shutdown, worker cleanup, and session cycling.

## Key Decisions

1. Pre-kill verification uses model intelligence (not framework rules)
2. Witness can request restart when context filling (mail self, exit)
3. Mayor NOT involved in per-worker cleanup (Witness responsibility)
4. Clear responsibility boundaries between Mayor/Witness/Polecat

## Subtasks (implementation)

- gt-sd6: Polecat decommission checklist prompting
- gt-f8v: Witness pre-kill verification protocol
- gt-eu9: Witness session cycling and handoff
- gt-gl2: Mayor vs Witness cleanup responsibilities

**Design complete.** Each subtask has full specification in its description.


Merge: slit-mjvtz18y

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


Digest: mol-deacon-patrol

Patrol 3: 14 sessions healthy, no changes


mol-witness-patrol molecule definition

Create mol-witness-patrol in builtin_molecules.go.

## Steps (10 total)
1. inbox-check - Process witness mail (lifecycle, help requests)
2. load-state - Read handoff bead, get nudge counts
3. survey-workers - gt polecat list, categorize by status
4. inspect-workers - tmux capture-pane for each 'working' polecat
5. decide-actions - Apply nudge matrix, queue actions
6. execute-actions - Nudge, kill, or escalate as decided
7. save-state - Update handoff bead with new states
8. generate-summary - Summarize cycle for digest
9. context-check - Check own context usage
10. burn-or-loop - Squash wisp, then loop or cycle session

## Key Behaviors
- Uses wisp storage (.beads-wisp/)
- Reads/writes witness handoff bead for state persistence
- Progressive nudging (3 levels before escalate)
- Pre-kill verification before killing polecats

## Reference
- See prompts/roles/witness.md for protocol details
- See mol-refinery-patrol for similar structure
- Parent epic: gt-aqd8


Session: gt-gastown-test completed gt-f7jxr


Digest: mol-deacon-patrol

Patrol 13: All healthy


Merge: nux-1767075895486

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


Link MR bead to agent bead for traceability

MR bead and agent bead are separate with no cross-reference.

## Problem
- MR bead tracks: branch, target, source_issue
- Agent bead tracks: state, hook_bead, cleanup_status
- No link between them

When something goes wrong, hard to trace which polecat created which MR.

## Fix
1. MR bead should include agent_bead field
2. Agent bead should include active_mr field
3. On gt done: update both to reference each other
4. On merge: clear both references

## Benefits
- Traceable: given MR, find polecat
- Traceable: given polecat, find MR
- Orphan detection: MR without agent = stale


gt spawn: Not injecting work instructions to session

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

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

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


Digest: mol-deacon-patrol

Patrol 20: all healthy, doctor pass, handoff cycle


Digest: mol-deacon-patrol

Patrol cycle 20: All healthy, handoff threshold reached


Agent Startup Consolidation: Unified nudge, seance metadata, LLM-agnostic

## Problem

Agent startup nudge logic is scattered across 6+ files with inconsistent behavior:
- start.go, crew_lifecycle.go, crew_at.go, handoff.go, deacon.go, witness.go

Some paths work (handoff), others fail (crew start). The core issues:

1. **tmux send-keys Enter issue**: `send-keys "foo" Enter` concatenates, doesnt send Enter separately
2. **All sessions look identical**: First message is "gt prime" everywhere, `/resume` shows duplicate-looking sessions
3. **No seance metadata**: Cant differentiate predecessors by sender, role, timestamp, topic
4. **Hardcoded to Claude**: `claude --dangerously-skip-permissions` everywhere

## Solution

1. **Unified StartupNudge function** - All paths call one function with rich metadata
2. **Seance-aware nudge format** - First message contains: sender, recipient, timestamp, topic
3. **Runtime configuration** - LLM command/args in config, not hardcoded
4. **gt seance command** - Find and resume predecessor sessions

## Nudge Format

The startup nudge becomes the session title in `/resume`:
```
[GAS TOWN] gastown/crew/gus ← mayor • 2025-12-30T15:42 • cold-start
```

Components:
- Address: gastown/crew/gus (recipient identity)
- Sender: mayor, deacon, witness, self (for handoff)
- Timestamp: When session started
- Topic: mol-id, "cold-start", "handoff", etc.

## Key Insight

The GUPP nudge content doesnt matter for triggering propulsion - agent already read CLAUDE.md and hooks ran. What matters is:
1. The nudge arrives reliably (NudgeSession solves this)
2. The nudge creates a unique, identifiable session title

## Related

- gt-p2o6s: Extend gt nudge to support channel:name syntax
- gt-9guv2: Messaging Channels epic
- Session beacon already exists but is for identity, not session title


Merge: slit-mjxaptzz

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


Merge: capable-1767146233256

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


CLI: doctor diagnostics and auto-repair

GGT completely lacks the doctor command which is critical for debugging.

Required Commands:
- gt doctor [<rig>] - Run diagnostic checks
- gt doctor --fix - Auto-repair common issues

Checks to Implement:
Workspace Level: Config validity, Mayor mailbox, Rig registry
Rig Level: Git state, clone health, witness/refinery presence, beads sync
Swarm Level: Stuck detection, zombie sessions, heartbeat health

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


gt hook: Support viewing other agents' hooks

Currently `gt hook` only shows your own hook. Would be useful to query any agent's hook:

```
gt hook show gastown/polecats/nux    # What's nux working on?
gt hook show gastown/witness         # What's the witness hooked to?
```

**Use cases**:
- Mayor checking what polecats are working on
- Witness checking polecat status
- Debugging coordination issues
- Quick status overview

**Compact output** (one line):
```
gastown/polecats/nux: gt-abc123 'Fix the widget bug' [in_progress]
```

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


mol-witness-patrol

Per-rig worker monitor patrol loop using the Christmas Ornament pattern.

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

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

## The Christmas Ornament Shape

This molecule uses dynamic bonding to create inspection arms per-polecat:

```
                    ★ mol-witness-patrol (trunk)
                   /|\
            ┌─────┘ │ └─────┐
         PREFLIGHT DISCOVERY CLEANUP
            │       │        │
        ┌───┴───┐ ┌─┴─┐  ┌───┴───┐
        │inbox  │ │sur│  │aggreg │
        │refnry │ │vey│  │save   │
        │load   │ └─┬─┘  │summary│
        └───────┘   │    │contxt │
                    │    │loop   │
          ┌─────────┼─────────┐ └───────┘
          │         │         │
          ●         ●         ●     mol-polecat-arm (dynamic)
         ace       nux      toast
          │         │         │
       ┌──┴──┐   ┌──┴──┐   ┌──┴──┐
       │cap  │   │cap  │   │cap  │
       │ass  │   │ass  │   │ass  │
       │dec  │   │dec  │   │dec  │
       │exec │   │exec │   │exec │
       └──┬──┘   └──┬──┘   └──┬──┘
          │         │         │
          └─────────┴─────────┘
                    │
                 ⬣ base (cleanup)
```

## Phases

### PREFLIGHT (fixed steps)
1. inbox-check - Process lifecycle requests, help messages
2. check-refinery - Ensure MQ is alive and processing
3. load-state - Read persistent state (nudge counts, etc.)

### DISCOVERY (spawns dynamic arms)
4. survey-workers - List polecats, bond mol-polecat-arm per polecat
5. run-plugins - Bond mol-plugin-runner for each witness plugin

### CLEANUP (gate + fixed steps)
6. aggregate - GATE: WaitsFor all arms + plugins to complete
7. save-state - Persist nudge counts, action log
8. generate-summary - Create digest content
9. context-check - Check if context is high
10. burn-or-loop - Squash/burn wisp, then loop or exit

## Dynamic Arms

Each polecat gets mol-polecat-arm bonded as a wisp child:
- capture - Capture tmux output
- assess - Categorize state (working/idle/error/done)
- load-history - Get nudge counts for this polecat
- decide - Apply nudge matrix
- execute - Take action (nudge/kill/escalate/none)

Arms run in PARALLEL. The aggregate step waits for all to complete.

## Activity Feed

This design enables real-time visibility:

```
[14:32:08] + patrol-x7k.arm-ace bonded (5 steps)
[14:32:09] → patrol-x7k.arm-ace.capture in_progress
[14:32:10] ✓ patrol-x7k.arm-ace.capture completed
[14:32:14] ✓ patrol-x7k.arm-ace.decide completed (action: nudge-1)
[14:32:17] ✓ patrol-x7k.arm-ace COMPLETE
```

## Storage

- Wisp storage: .beads-wisp/ (ephemeral, gitignored)
- Persistent state: witness handoff bead (nudge counts, etc.)
- Digests: Squashed summaries in permanent beads

## Dependencies

- bd-xo1o: Dynamic Molecule Bonding epic (in beads rig)
  - bd mol bond with variable substitution
  - WaitsFor directive for fanout gates
  - Activity feed query

Labels: [template, christmas-ornament]


Digest: mol-deacon-patrol

Patrol cycle 66: All healthy


Digest: mol-deacon-patrol

Patrol 36: rictus removed from rig


Digest: mol-deacon-patrol

Patrol cycle 5: all healthy


Digest: mol-deacon-patrol

Patrol 260 complete: final cycle before handoff


Merge: dementus-1767079823043

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


Add compaction recovery protocol to role templates

After context compaction (summarization), agents may be disoriented.

Add guidance to recognize and recover:
- 'If you feel you've lost earlier context or are disoriented, run: gt prime'
- 'This re-injects your role, current work, and pending messages'
- 'It's safe to prime multiple times - it just outputs context'

This is the fallback path when self-check is missed and compaction happens.
Compaction isn't catastrophic if priming is robust.


Digest: mol-deacon-patrol

Patrol #10: All healthy


Review: Witness patrol template changes (gt-h1n5)

Polecat rictus implemented significant witness.md.tmpl changes: banners, wisp-based execution, handoff bead attachment, loop-or-exit, patrol summary. Review for correctness and consistency with deacon pattern.


Adaptive backoff for daemon heartbeat

Track agent responsiveness and adjust heartbeat frequency.

## Per-Agent State

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

## Strategy Options

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

## Recovery

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

## Benefits

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


Digest: mol-deacon-patrol

Patrol 12: Routine


Session ended: gt-gastown-nux


Merge: toast-1767074978542

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


Digest: mol-deacon-patrol

Patrol 24: All quiet


Digest: mol-deacon-patrol

Patrol 2: All healthy


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

## Problem

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

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

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

## Proposal

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

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

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

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

## Benefits

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

## Implementation

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

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

## Future Candidates

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


CLI: plugin commands (list, status)

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


Digest: mol-deacon-patrol

Patrol 13: All healthy


Polecats should spawn with auto-accept mode enabled

During swarm bd-784c, polecats (Toast, Nux) were spawned without --dangerously-skip-permissions or equivalent auto-accept mode.

**Problem:**
Every edit, bash command, and tool use required manual confirmation via tmux send-keys. This defeats the purpose of autonomous polecat operation.

**Expected:**
Polecats in a swarm should run with permissions bypassed so they can work autonomously.

**Suggestion:**
- gt sling should pass --dangerously-skip-permissions by default for polecats
- Or polecats should have a .claude/settings.local.json pre-configured for auto-accept


Digest: mol-deacon-patrol

Patrol 4: stable


Digest: mol-deacon-patrol

Patrol cycle 42: All agents healthy, no incidents


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

Patrol 18


Pinned Beads: Ongoing concerns and anchors

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


Digest: mol-deacon-patrol

Patrol cycle 3: All 6 agents healthy, no incidents


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.


Digest: mol-deacon-patrol

Patrol 16


Digest: mol-deacon-patrol

P12: stable


Digest: mol-deacon-patrol

Patrol complete: all healthy, cleaned 2 orphan processes + 3 stale locks


Comprehensive help text and examples

Improve help text with examples and cross-references.

## Improvements

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

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

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

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

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

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

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

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


Town Doctor molecule for harness health checks

Create a Town Doctor molecule that any Gas Town agent can run to diagnose and repair harness issues.

## Concept

Instead of just `gt doctor` as a CLI command with hardcoded checks, create a **molecule** (checklist workflow) that:
- Any agent (Mayor, Witness, Polecat) can instantiate
- Walks the agent through diagnostic steps
- Agent uses judgment to fix issues found
- Works as a structured troubleshooting guide

## Why a Molecule?

1. **Agent-driven**: The agent running it becomes "the doctor" temporarily
2. **Extensible**: Add new checks by updating the molecule, not code
3. **Contextual**: Agent can reason about issues, not just run scripts
4. **Self-healing**: Agent can fix problems it finds, not just report them

## Proposed Checks (molecule steps)

1. Verify harness structure (mayor/, .beads/, CLAUDE.md exist)
2. Validate config files (town.json, rigs.json parse correctly)
3. Check beads health (bd doctor, redirect validity)
4. Verify git state (clean working tree, proper remotes)
5. Check rig integrity (each registered rig exists, has config.json)
6. Validate agent clones (mayor/rig/, refinery/rig/ exist and are valid)
7. Check for orphaned worktrees/branches
8. Verify daemon state (if running)

## Integration

- `gt doctor` could instantiate the molecule for the current agent
- Or agent can run `bd ready` and pick up doctor tasks when prioritized
- Results logged to beads for audit trail

## Related

- gt-cr9: Harness Design & Documentation (completed)
- Molecules design in architecture.md


Digest: mol-deacon-patrol

Patrol cycle 38: All healthy, no incidents


Update Deacon template to emphasize staying in ~/gt/deacon directory

The Deacon template should instruct the agent to stay in ~/gt/deacon/ as much as possible, and always return there after any excursion. This prevents issues with identity detection (mail, mol status) which depend on cwd.


test-new-swarm-1767338498


Digest: mol-deacon-patrol

Patrol cycle 5: All healthy


Merge: furiosa-mjwnxrkd

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


Work on ga-p6r: Add handoff protocol to spawn priming. En...

Work on ga-p6r: Add handoff protocol to spawn priming. Ensure polecats receive handoff context when spawned. When done, submit MR (not PR) to integration branch for Refinery.


Stranded convoy detection + feeding via dog dispatch

## Problem
Convoys can become **stranded** - they have ready work but no polecats working on it. Work is waiting for a ride.

## Definition: Stranded Convoy
```
convoy is open
  AND has tracked issues where:
    - status = open (not in_progress, not closed)
    - not blocked (dependencies met)
    - no assignee (or assignee session is dead)
```

If all open issues are blocked or have live polecats → convoy is fine (in-flight).
If ready issues exist with nobody working them → convoy is stranded.

## Solution: Deacon patrol + dog dispatch

**Detection (Deacon patrol step):**
Check open convoys for stranded state.

**Dispatch:**
```bash
gt sling mol-convoy-feed deacon/dogs --var convoy=<convoy-id>
```

**mol-convoy-feed formula (single pass):**
1. Load convoy, find ready issues (not blocked, no assignee)
2. Check idle polecat capacity
3. Dispatch min(ready_issues, idle_polecats)
4. Exit

Dog doesn't babysit. Just feeds and leaves. Deacon's patrol loop handles repeated checking - if still stranded next cycle, dispatches dog again.

## Benefit
Stateless, batch-oriented. Convoys become fire-and-forget.

(Moved from hq-1h2to)


Digest: mol-deacon-patrol

Patrol cycle 49: All healthy


Plugin/molecule catalog integration design

Two currently independent systems:
1. Disk-based plugins (~~/gt/plugins/) - Deacon patrol extensions
2. Molecule catalog (protos in beads) - bd pour/wisp/bond

Investigate integration points:
- User-contributed molecules bonded to catalog
- Dynamic molecule attachment during execution (e.g., 'attach security sniffer because we noticed condition X')
- How plugins could contribute protos
- How protos could trigger plugins

Not blocking launch. Future design investigation.


Digest: mol-deacon-patrol

Patrol 9: all clear


Digest: mol-deacon-patrol

Cycle 18


Enhance Mayor CLAUDE.md with GGT milestone tracking

Update ~/ai/CLAUDE.md (Mayor startup context) with:

1. GGT Self-Hosting Milestone section
   - Track progress toward gt replacing town
   - Key blockers: gt-h5n (merge queue), gt-974 (daemon)
   - What "self-hosting" means (GGT working on itself)

2. Recent Architecture Decisions
   - Engineer = role, Refinery = place
   - Merge queue in Beads (not branch discovery)
   - Session restart protocol

3. Transition Plan
   - Current: use town commands (PGT)
   - Target: use gt commands (GGT)
   - Switch criteria: gt-b1g closed

Keep it concise - Mayor context should be quick to scan.


Digest: mol-deacon-patrol

Patrol cycle 3: healthy, 2 acks


Digest: mol-deacon-patrol

Patrol cycle 6: All agents healthy


Digest: mol-deacon-patrol

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


Molecule Algebra: Work Composition DSL

Implement the molecule algebra - a declarative language for composing, transforming, and executing structured work. Enables mechanical composition without AI.

The Three Phases: Rig → Cook → Run

- **Rig**: Source-level composition (formula YAML with extends/compose)
- **Cook**: Instantiation (formula → proto → mol/wisp)
- **Run**: Execution (agents complete steps)

Two Composition Operators:
- **Rig** operates on formulas (source level)
- **Bond** operates on artifacts (protos, mols, wisps)

Key components:
- Formulas: YAML source with composition rules (.formula.yaml)
- Cook: Pre-expand macros/aspects to flat proto
- Phase verbs (pour, wisp)
- Bond: Artifact-level polymorphic composition
- Composition operators (sequence, parallel, branch, loop, gate)
- Advice operators (before, after, around - Lisp style)
- Expansion operators (macros like Rule of Five)
- Aspects (AOP cross-cutting concerns)

See docs/rig-cook-run.md for the lifecycle spec.
See docs/molecule-algebra.md for full algebra specification.
Example formulas in .beads/formulas/


Phase verbs: pour and wisp commands

Implement the phase transition verbs:
- bd pour <proto> - instantiate as persistent mol
- bd wisp <proto> - instantiate as ephemeral wisp
- Update bd mol bond to inherit phase from target

These replace the current --wisp flag with cleaner verb semantics.


Rename Engineer in Box to Shiny

Rename mol-engineer-in-box to mol-shiny (or just 'shiny').

Mad Max reference - the canonical 'right way to engineer'.
Update all references in docs and code.


Rule of Five expansion template

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

First example of a macro-style expansion proto.


Formula parser and YAML schema

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

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

**Beads issue: bd-weu8**


bd cook command: Formula to Proto

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

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

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

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

**Beads issue: bd-wa2l**


bd formula list/show commands

Implement formula management commands:

  bd formula list
  # Lists formulas from all search paths

  bd formula show rule-of-five
  # Shows formula details, steps, compose rules

Search paths (in order):
1. .beads/formulas/ (project)
2. ~/gt/.beads/formulas/ (town)
3. ~/.beads/formulas/ (user)
4. Built-in formulas (embedded)


Formula cycle detection during cooking

Detect and error on circular extends chains during bd cook. E.g., if formula A extends B extends A, cooking should fail with clear error message pointing to the cycle.


Prevent aspect self-matching (infinite recursion)

Aspects should only match original steps, not steps inserted by the same aspect application. Document this and implement guard during cooking. Without this, a pointcut like *.implement that inserts security-prescan could match its own insertion infinitely.


Max expansion depth limit (default 5)

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


Cooking metadata: source tracing

Add source_formula and source_location metadata to cooked steps so you can trace a step in a proto back to its origin formula. Useful for debugging complex compositions.


Meta-formulas: formula generators

Support meta-formulas that generate multiple formulas from a template. Example: multi-lang-shiny that generates shiny-go, shiny-python, shiny-typescript from a for-each loop over languages. Enables DRY formula libraries.


Define meta-formula YAML schema

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

## Proposed Schema

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

## Key Decisions

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

## Deliverables

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


Implement meta-formula generator in parser

Implement the generator that expands meta-formulas into concrete formulas.

## Implementation

1. Detect type: meta-formula in parser
2. Evaluate for-each values
3. For each value, instantiate the formula template
4. Substitute {var} placeholders with current value
5. Return list of generated Formula structs

## API

    func (p *Parser) ExpandMetaFormula(mf *MetaFormula) ([]*Formula, error)

## Edge Cases

- Empty values list: return empty slice
- Duplicate generated names: error
- Nested meta-formulas: error (not supported initially)


Add multi-lang-shiny example and tests

Create example meta-formula and test suite.

## Example

Create .beads/formulas/multi-lang-shiny.meta.yaml:
- Generates shiny-go, shiny-python, shiny-typescript
- Each extends base-shiny with language-specific vars

## Tests

1. Test basic expansion (3 languages -> 3 formulas)
2. Test variable substitution in names and content
3. Test error on duplicate generated names
4. Test empty values list
5. Integration: cook a generated formula


Advice operators: before, after, around

Implement Lisp-style advice operators for molecules:
- before(target, step) - insert step before target
- after(target, step) - insert step after target
- around(target, wrapper) - wrap target with before/after

Support glob patterns for targeting (*.implement, shiny.*, etc).


Formula inheritance trees for organizational policies

Support organizational formula hierarchies where teams extend a company-base-formula. Each team can add required aspects (accessibility audit for frontend, SQL injection scan for backend). Enables policy enforcement across an organization.


Design org formula hierarchy pattern

Design how organizational formula hierarchies work.

## Pattern

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

## Key Questions

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

## Proposed Design

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

## Deliverables

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


Implement aspect merging across formula chain

Implement merging of aspects when formulas extend each other.

## Current State

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

## Implementation

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

## Files

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

## Tests

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


Add org formula examples and tests

Create example organizational formula hierarchy and tests.

## Examples

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

## Tests

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


Conditional aspects (feature flags)

Apply aspects conditionally based on environment or config. Example: apply security-audit only when env.SECURITY_SCANNING == enabled. Adds 'when' clause to aspect composition.


Review dimensions as aspects

Refactor code-review molecule to use aspects for each review dimension (security, performance, accessibility). More flexible than current pluggable molecule approach - add/remove checks by composing aspects.


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.


Document Rig/Cook/Run lifecycle

The rig-cook-run.md spec is written. Propagate terminology to all relevant docs: - molecule-algebra.md (done) - molecular-chemistry.md (done) - vision.md (done) - propulsion-principle.md - sling-design.md - Any agent templates that reference cooking/instantiation


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.


Direct formula-to-wisp instantiation (skip proto)

For massive ephemeral workflows (e.g., million-step Towers of Hanoi), allow:

bd wisp towers-of-hanoi --var disks=20

This would cook + wisp in one step, never materializing the proto. Useful when:
- The proto would be huge (1M+ steps)
- The work is ephemeral (wisp, not mol)
- You don't need the intermediate artifact

Implementation: streaming cook that emits directly to wisp storage.

Related: towers-of-hanoi.formula.yaml demonstrates the use case.


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)


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


Formula versioning and migration strategy

Define how formula versions are managed:
- What does the version field mean? Semver? Integer?
- When a formula changes, what happens to existing protos?
- Do protos track their source formula version?
- Migration path: old proto + new formula = ?
- Backwards compatibility guarantees (if any)

Deferred: post-launch polish


Expansion operators: expand and map (macros)

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

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


Proto debugging and inspection tools

Proto debugging and inspection tools.

With ephemeral protos (gt-4v1eo), this becomes:
- `bd cook <formula> --dry-run` - preview cooked output without persisting
- `bd cook <formula> --json` - output proto as JSON for inspection
- `bd cook <formula> --graph` - visualize step DAG

No persistent proto beads to inspect - formulas are the source of truth.