Engineer main loop: poll for ready merge-requests

Implement the Engineer's main processing loop.

Loop structure:
1. Query: bd ready --type=merge-request
2. If empty: sleep(poll_interval), continue
3. Select highest priority, oldest MR
4. Claim: bd update <id> --status=in_progress
5. Process (delegate to other subtasks)
6. Repeat

Configuration:
- poll_interval: from rig config (default 30s)
- max_concurrent: from rig config (default 1)

The loop should be interruptible and handle graceful shutdown.

Reference: docs/merge-queue-design.md#engineer-processing-loop


Fetch and conflict check: git operations for MR

Implement git operations for MR processing.

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

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

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


Merge execution: merge, test, push

Implement the actual merge execution.

Steps:
1. git checkout <mr.target>
2. git merge <mr.branch> --no-ff -m 'Merge <branch>: <title>'
3. If config.run_tests:
   - Run test_command (from config)
   - If failed: git reset --hard HEAD~1, return Failure(tests_failed)
4. git push origin <mr.target>
5. Return Success(merge_commit=HEAD)

Configuration:
- run_tests: bool (default true)
- test_command: string (default 'go test ./...')

Handle push failures with retry logic.

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


Failure handling: assign back to worker, add labels

Handle merge failures appropriately.

Failure types and actions:
| Failure     | Action                                      |
|-------------|---------------------------------------------|
| conflict    | Add needs-rebase label, assign to worker    |
| tests_fail  | Add needs-fix label, assign to worker       |
| build_fail  | Add needs-fix label, assign to worker       |
| flaky_test  | Retry once, then treat as tests_fail        |
| push_fail   | Retry with backoff, escalate if persistent  |

Actions:
1. bd update <id> --status=open --assignee=<worker>
2. bd update <id> --labels=<failure-label>
3. Send mail to worker explaining failure
4. Log failure details

Reference: docs/merge-queue-design.md#handling-failures


Success handling: close MR, close source issue, cleanup

Handle successful merge completion.

Steps:
1. Update MR with merge_commit SHA:
   bd update <id> --body='...\nmerge_commit: <sha>'
2. Close MR with reason:
   bd close <id> --reason='merged'
3. Close source issue (the work item):
   bd close <source_issue> --reason='Merged in <mr_id>'
4. Delete source branch (if configured):
   git push origin --delete <mr.branch>
5. Log success

Configuration:
- delete_merged_branches: bool (default true)

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


Agent monitoring and status inference

Agent monitoring with status inference from activity, like PGT.

## Agent Status Enum
```go
type AgentStatus string
const (
    StatusAvailable AgentStatus = "available"
    StatusWorking   AgentStatus = "working"
    StatusThinking  AgentStatus = "thinking"
    StatusBlocked   AgentStatus = "blocked"
    StatusWaiting   AgentStatus = "waiting"
    StatusReviewing AgentStatus = "reviewing"
    StatusIdle      AgentStatus = "idle"
    StatusPaused    AgentStatus = "paused"
    StatusError     AgentStatus = "error"
    StatusOffline   AgentStatus = "offline"
)
```

## Status Sources (priority order)
```go
type StatusSource string
const (
    SourceBossOverride StatusSource = "boss"      // Witness/Mayor sets
    SourceSelfReported StatusSource = "self"      // Agent reports own status
    SourceInferred     StatusSource = "inferred"  // Detected from activity
)
```

## Activity Detection

### Pattern Registry
```go
var activityPatterns = []struct {
    Pattern string
    Status  AgentStatus
}{
    {"Thinking...", StatusThinking},
    {"BLOCKED:", StatusBlocked},
    {"Error:", StatusError},
    // etc
}
```

### Idle Detection
No pane output for N seconds → StatusIdle

### Resource Monitoring (optional)
CPU/memory via os.Process

## New Package
internal/monitoring/
├── types.go      # AgentStatus, StatusReport
├── detector.go   # PatternRegistry, detect from output
├── tracker.go    # Per-agent status tracking
└── idle.go       # Idle timeout detection

## Integration
- Session capture output → monitoring detector
- Status shown in gt status, gt session list

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

## Acceptance Criteria
- [ ] Status enum with 10 states
- [ ] Pattern-based detection from pane output
- [ ] Idle detection with configurable timeout
- [ ] Status visible in CLI output


Merge: gt-rana.3

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


Verify architecture.md shows correct harness and rig structure

Review architecture.md diagrams:
- Verify town-level structure shows harness correctly
- Confirm rig-level mayor/rig/ is shown (it appears to be there at line 197)
- Check mermaid diagrams match ASCII diagrams
- Update if any inconsistencies found
- Cross-reference with new harness.md docs


Add pre-flight test check to mol-polecat-work

Add a step early in mol-polecat-work (after branch-setup, before implement) to check if tests pass on main.

## Problem
If main is broken when polecat starts, they could:
- Waste context trying to fix unrelated issues
- Not know if their changes broke things or it was pre-existing
- Get blamed for failures they didn't cause

## Proposed Step: preflight-tests

After branch-setup, before implement:

1. Checkout main temporarily
2. Run test suite
3. If tests PASS: Continue to implement
4. If tests FAIL:
   - Quick fix (<15 min): Fix it, commit to main, then continue
   - Big fix: File bead, mail Witness, proceed with own work
   - Use ZFC judgment - you're there, you decide

## Handoff Consideration
If fixing pre-existing failures consumes significant context:
- Commit the fix
- gt handoff with note: 'Fixed pre-existing test failures, continuing with assigned work'
- Fresh session picks up from implement step

## Key Principle
Scotty Test: Don't walk past a broken warp core. But also don't let someone else's mess derail your mission entirely.


Policy beads: config in data plane

Use sentinel/policy beads for configuration instead of external config. Examples: daemon notifications on/off, heartbeat intervals. Config lives in the bead graph, can be toggled by closing/opening policy beads.


Merge: gt-qz2l

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


gt polecat status shows idle when hook_bead is set in agent bead

gt polecat status gastown/furiosa shows State: idle, Issue: (none) but the agent bead gt-gastown-polecat-furiosa has hook_bead: gt-um6q and agent_state: running. The status command isn't reading hook_bead from the agent bead correctly.

Fixed in commit 275f3026: loadFromBeads now checks agent bead hook_bead field


Digest: mol-deacon-patrol

Patrol 6: all quiet


Digest: mol-deacon-patrol

Patrol 8: Routine


Test: release command verification


Digest: mol-deacon-patrol

Patrol cycle 1: inbox clear, 8 in-progress issues, sessions healthy, gc not implemented


Digest: mol-deacon-patrol


Polecats must not create GitHub PRs

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

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

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

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


Digest: mol-deacon-patrol

Patrol 16: All healthy


Digest: mol-deacon-patrol

Patrol 12: nominal


Digest: mol-deacon-patrol

Patrol 11: All clear


Digest: mol-deacon-patrol

Patrol 17


Digest: mol-deacon-patrol

Patrol cycle 15: routine


Digest: mol-deacon-patrol

Patrol 6: routine, all healthy


gt spawn should use bd mol run for molecule attachment

Simplify Gas Town to use bd mol run for work tracking.

## Key Insight
Two distinct mechanisms, not duplicative:

| Mechanism | Purpose | Query |
|-----------|---------|-------|
| **Pinned molecule** (bd mol run) | What am I working on? | `bd list --pinned --assignee=me` |
| **Handoff mail** | Context notes for restart | `gt mail read` (self-addressed) |

The handoff is just **mail to yourself** - optional context notes.
The molecule is **the actual work** - required state.

## Current State (Overengineered)
Gas Town has custom attachment system:
- Permanent "Foo Handoff" pinned beads per identity
- AttachMolecule(pinnedBeadID, moleculeID)
- Attachment fields parsed from description
- Separate from beads pinning

## New Model (Simplified)
Use bd mol run directly:

```bash
# Spawn polecat with molecule
bd mol run mol-polecat-work --var issue=gt-xyz
# This: spawns, assigns to caller, pins root, sets in_progress
```

Query current work:
```bash
bd list --pinned --assignee=gastown/furiosa --status=in_progress
```

Handoff context (when needed):
```bash
gt mail send gastown/furiosa -s "Context notes" -m "Was on step 4..."
```

## Changes Required

### Remove from Gas Town
- AttachMolecule() / DetachMolecule()
- AttachmentFields struct and parsing
- GetAttachment() / SetAttachmentFields()
- Permanent pinned handoff beads per identity
- Daemon attachment detection (checkDeaconAttachment)

### Update gt spawn
```go
// Old: custom molecule instantiation + attachment
// New: just call bd mol run
cmd := exec.Command("bd", "mol", "run", protoID, "--var", "issue="+issueID)
```

### Update gt prime / agent context
```go
// Old: find handoff bead, parse attachment
// New: query for pinned molecule
cmd := exec.Command("bd", "list", "--pinned", "--assignee="+identity, "--status=in_progress", "--json")
```

### Update documentation
- Remove handoff bead attachment docs
- Clarify: handoff = mail, molecule = work
- Update CLAUDE.md templates

## Benefits
1. One system for work tracking (beads)
2. Simpler Gas Town code
3. bd mol squash works naturally
4. Handoff is just mail (already works)

## Related
- gt-3x0z: Wisp Molecule Integration
- gt-rana: Patrol System
- gt-lek6: gt rig reset --stale
- gt-ay1r: gt molecule current


Improve test coverage in low-coverage packages

Several packages have low test coverage:
- internal/cmd: 6.8%
- internal/mail: 3.6%
- internal/daemon: 12.1%
- internal/doctor: 14.5%
- internal/refinery: 20.6%
- internal/session: 27.8%
- internal/git: 28.8%

Priority should be given to mail, cmd, and daemon packages which handle critical functionality.


gt rig reset: clear stale mail on reset/land

## Problem

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

## Current State

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

## Proposed Behavior

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

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

## Implementation

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


Digest: mol-deacon-patrol

Patrol 16: healthy


Pillar 3: Polecat Lifecycle

Session-per-step model with automated recycle/nuke.

Key deliverables:
- gt polecat recycle <name> - kill session, preserve sandbox
- gt polecat nuke <name> - kill everything
- Witness calls recycle between molecule steps
- Refinery calls nuke after merge

Subsumes existing work:
- gt-budeb: Witness auto-nuke polecats after merge
- gt-1dbcp: Polecat auto-start nudge fix

Reference: ~/gt/docs/liftoff-plan.md

Parent: gt-oki8p


Digest: mol-deacon-patrol

P18: stable


Digest: mol-deacon-patrol

Patrol 16: Nominal


Digest: mol-deacon-patrol

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


plugin-run

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


Digest: mol-deacon-patrol

Patrol 2: routine, all healthy


Digest: mol-deacon-patrol

Patrol 1: All agents healthy, 3 polecats idle, no orphans, 6 Mayor messages pending


Improve looksLikeFormulaName heuristic in mol_bond

The looksLikeFormulaName() function in mol_bond.go uses simple heuristics that could have edge cases:

Current checks:
- starts with 'mol-'
- contains '.formula'
- contains path separators

Potential improvements:
- Check if operand matches issue ID format (prefix-hash pattern)
- Consider formula search paths when determining if something could be a formula
- Add --formula flag to explicitly mark an operand as a formula name

Low priority since edge cases are rare in practice.


Digest: mol-deacon-patrol

Patrol 11: Quiet


gt nudge should accept flexible session identifiers

Currently `gt nudge` requires the exact tmux session name (e.g., `gt-gastown-furiosa`).

It should also accept:
- `gastown/furiosa` (rig/polecat format)
- `furiosa` (polecat name, infer rig from cwd or require if ambiguous)

The session list command shows `gastown/furiosa` format, but nudge rejects it:
```
gt session list → shows 'gastown/furiosa'
gt nudge gastown/furiosa 'msg' → 'session not found'
gt nudge gt-gastown-furiosa 'msg' → works
```

Should normalize all these formats to the tmux session name internally.


Implement gt sling command

The unified work dispatch command.

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

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

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


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

Patrol 20: All healthy


Digest: mol-deacon-patrol

Patrol 10: Mayor OK, 11 sessions


Doctor check: Worker health and stuck detection

Detect and report stuck workers via gt doctor.

## Checks

### WorkerHealthCheck
- List all active workers (polecats with state=working)
- Check last activity timestamp for each
- Flag as potentially stuck if no progress for configurable threshold (default: 30 min)
- Check if Witness is running for the rig
- Verify Witness last heartbeat time

### Stuck Detection Criteria
- Polecat state=working but session not running
- Polecat state=working but output unchanged for threshold
- Witness not responding to health checks
- Multiple polecats in same rig all stuck

## Output

```
[WARN] Workers in rig 'wyvern' may be stuck:
  - Toast: working for 45m, no recent output
  - Capable: working for 52m, session not found
  - Witness: last heartbeat 20m ago

  Suggestions:
  - gt witness status wyvern
  - gt capture wyvern/Toast 50
  - gt stop --rig wyvern (kill all)
```

## Auto-Fix

Cannot auto-fix stuck workers (risk of data loss), but can:
- Restart Witness daemon if crashed
- Send warning mail to Mayor


Molecules: Composable Workflow Beads

## Summary

Molecules are crystallized workflow patterns stored as Beads issues.
When instantiated, the molecule creates child beads forming a DAG.

## Key Insight: Molecules ARE Beads

Per HOP Decision 001: Beads IS the ledger. Molecules don't get a separate YAML format - they're issues with `type: molecule` containing prose-based step definitions.

Agents don't need rigid schemas. They parse natural language natively. A molecule is just instructions with enough structure for tooling.

## Example: Engineer in a Box

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

This workflow takes a task from design to merge.

## Step: design
Think carefully about architecture. Consider existing patterns,
trade-offs, testability.

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

## Step: review
Review for bugs, edge cases, style issues.
Needs: implement

## Step: test
Write and run tests. Cover happy path and edge cases.
Needs: implement

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

## Instantiation

```bash
# Attach molecule when spawning
gt spawn --issue gt-abc --molecule mol-xyz

# Creates child beads atomically:
gt-abc.design     ← ready first
gt-abc.implement  ← blocked by design
gt-abc.review     ← blocked by implement
gt-abc.test       ← blocked by implement
gt-abc.submit     ← blocked by review, test
```

Each step issue gets an `instantiated-from` edge to the molecule (with step metadata).

## Why This Matters

1. **Unified data plane**: Everything in Beads, no parallel YAML channel
2. **AI-native**: Prose instructions, not rigid schemas
3. **Error isolation**: Each step is a checkpoint - failure doesn't lose progress
4. **Scales with AI**: As agents get smarter, they handle more complex molecules

## Implementation Primitives

- `ParseMoleculeSteps()`: Extract steps from prose (convention-based)
- `InstantiateMolecule()`: Atomic transaction creating all steps + edges
- `instantiated-from` edge type: Track provenance
- Parameterization: `{{variable}}` substitution from context map


Molecule schema: YAML format for workflow definitions

Define the YAML schema for molecule definitions:

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

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

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


Molecule instantiation: create child beads from template

When instantiating a molecule on a work bead:

## Transaction Flow

1. Parse molecule's `## Step:` sections from description
2. Begin SQLite transaction
3. For each step, create child issue:
   - ID: `{parent-id}.{step-ref}` or generated
   - Title: step title (from header or first line)
   - Description: step prose instructions
   - Type: task
   - Priority: inherit from parent
4. Add `instantiated-from` edge from each step to molecule:
   ```sql
   INSERT INTO dependencies (issue_id, depends_on_id, type, metadata)
   VALUES (step_id, mol_id, 'instantiated-from', '{"step": "implement"}');
   ```
5. Wire inter-step dependencies from `Needs:` lines
6. Commit transaction (atomic - all or nothing)

## Parsing Conventions

```markdown
## Step: <ref>
<prose instructions>
Needs: <step>, <step>  # optional
Tier: haiku|sonnet|opus  # optional hint
```

## Parameterization

Steps can have `{{variable}}` placeholders:
```markdown
## Step: implement
Implement {{feature_name}} in {{target_file}}.
```

Context map provided at instantiation time.

## API

```go
func (s *Store) InstantiateMolecule(mol *Issue, parent *Issue, ctx map[string]string) ([]*Issue, error)
func ParseMoleculeSteps(description string) ([]MoleculeStep, error)
```

Implementation lives in `internal/beads/molecule.go`.


Molecule CLI: bd molecule commands

Add molecule commands to bd:

## Commands

```bash
bd molecule list              # List molecules (type: molecule)
bd molecule show <id>         # Show molecule with parsed steps
bd molecule parse <id>        # Validate and show parsed structure
bd molecule instantiate <mol-id> --parent=<issue-id>  # Create steps
bd molecule instances <mol-id>  # Show all instantiations
```

## gt spawn integration

```bash
gt spawn --issue <id> --molecule <mol-id>
```

This should:
1. Call `bd molecule instantiate` (creates child beads atomically)
2. Spawn polecat on first ready step
3. Polecat grinds through via `bd ready`

## Output Examples

```
$ bd molecule show mol-abc

mol-abc: Engineer in a Box
Type: molecule

Steps (5):
  design      → (ready first)
  implement   → Needs: design
  review      → Needs: implement
  test        → Needs: implement
  submit      → Needs: review, test

Instances: 3
```

```
$ bd molecule instances mol-abc

Parent          Status    Created
gt-xyz          done      2025-12-15
gt-abc          active    2025-12-17 (3/5 complete)
gt-def          pending   2025-12-18
```


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

Create built-in molecules as Beads issues:

## engineer-in-box

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

Full workflow from design to merge.

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

Write a brief design summary before proceeding.

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

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

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

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

## quick-fix

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

Fast path for small changes.

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

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

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

## research

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

Investigation workflow.

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

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

## Storage

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


gt sling to town roles should use Town beads

## Problem

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

## Solution

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

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

## Tested

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


Digest: mol-deacon-patrol


gt mail: Cross-level routing is broken

When Mayor sends mail to rig worker, message lands in wrong beads database. Sender's beads vs recipient's.


Digest: mol-deacon-patrol

Patrol 5: All clear


Merge: furiosa-1767075024945

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

Branch no longer exists on remote - already merged or cleaned up


Digest: mol-deacon-patrol

Patrol 3: Quick scan, stable


Digest: mol-deacon-patrol

Patrol #5: Stable


Refinery as Worktree: Local MR Integration

Move refinery from separate clone to git worktree. Polecats stop pushing to origin - refinery sees their branches locally. MRs become wisps (ephemeral). Only main gets pushed after merge.

## Goals
- Origin stays clean (only main + beads-sync branches)
- No orphaned polecat branches ever
- Simpler mental model for MR coordination
- Faster integration (no network for local MR ops)

## Trade-offs
- Machine crash = redo pending work (acceptable, beads track state)
- Future federation needs different approach (bundles or integration branch)

## Components Affected
- Rig initialization (refinery setup)
- Spawn/polecat workflow (remove origin push)
- Refinery manager (local branch access)
- Documentation (architecture, workflows)
- Molecule templates (remove push instructions)


Update rig init to create refinery as worktree

Modify rig initialization to create refinery as a git worktree instead of a separate clone.

## Changes
- internal/rig/init.go or similar - change refinery setup
- Create worktree at <rig>/refinery/ with branch 'refinery'
- Remove clone logic for refinery
- Update any path assumptions

## Considerations
- Refinery worktree should track main (or its own branch?)
- May need to handle existing rigs (migration path)


Remove origin push from polecat workflow

Polecats should no longer push their branches to origin.

## Changes
- internal/cmd/done.go - remove 'git push origin HEAD'
- internal/cmd/mq_submit.go - remove branch push
- Molecule templates - remove push instructions
- Any hooks that push polecat branches

## Verification
- Polecat completes work without pushing
- Branch stays local
- Refinery can still see the branch (via shared .git)


Update refinery to read local branches

Refinery manager needs to access polecat branches locally instead of fetching from origin.

## Changes
- internal/refinery/manager.go - remove origin fetch for MR branches
- internal/refinery/engineer.go - local branch access
- Remove DeleteRemoteBranch calls (branches are local now)
- Update merge workflow to work with local refs

## Key insight
All worktrees share the same .git - refinery can see polecat/nux branch directly.


Convert MR queue to wisp storage

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

## Design (Simplified)

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

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

### Code Changes

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

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

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

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

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

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


Update architecture documentation

Update all documentation to reflect refinery-as-worktree model.

## Files to update
- docs/architecture.md (if exists)
- CLAUDE.md references to refinery
- mayor/rig/docs/*.md - any refinery docs
- wisp-architecture.md - add MR wisp details
- README files mentioning clone vs worktree

## Key messages
- Refinery is worktree, not clone
- Polecat branches stay local
- Only main pushed to origin
- MRs are wisps (ephemeral)


Design future federation model

Document how remote/federated refinery would work in the future.

## Options to document
1. Git bundles - portable patches, no branches on origin
2. Dedicated integration branch pattern (integration/*)
3. Hybrid - local for same-machine, bundles for remote

## Deliverable
Design doc in docs/federation-refinery.md or similar.
Not implementing now - just capturing the design for when needed.


Add migration path for existing rigs

Existing rigs have refinery as clone. Need migration path.

## Options
1. Manual: User runs 'gt rig migrate' or similar
2. Automatic: Detect clone, convert to worktree on next start
3. Deprecation: Old rigs work but warn, new rigs use worktree

## Considerations
- Don't break existing setups
- Clear upgrade path
- Handle in-flight MRs during migration


Extract SupportedShells constant to constants package

Extract repeated shell list to a single constant.

## Files to modify
- internal/constants/constants.go (add constant)
- internal/cmd/start.go (use constant at lines 729, 773, 907)
- internal/cmd/crew_helpers.go (use constant)

## Implementation
Add to constants.go:
```go
var SupportedShells = []string{"bash", "zsh", "sh", "fish", "tcsh", "ksh"}
```

Replace all inline []string{"bash", "zsh"...} with constants.SupportedShells.

## Acceptance criteria
- [ ] SupportedShells added to internal/constants/constants.go
- [ ] All 4 occurrences in start.go and crew_helpers.go replaced
- [ ] grep -r '"bash", "zsh"' returns no matches in internal/cmd/
- [ ] go build ./... passes
- [ ] go test ./... passes


Digest: mol-deacon-patrol

Patrol 11: healthy


Merge: nux-1767059693217

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

Stale merge requests - branches no longer exist


Ephemeral protos: Remove proto-as-bead storage, cook inline

## Summary

Refactor the molecular chemistry stack so protos are ephemeral in-memory data structures, not persisted beads with `[template]` labels.

## Current State

```
Formula (.formula.json)  → cook → Proto (bead with [template]) → pour/wisp → Mol/Wisp
```

Protos are stored as beads issues with `labels: ["template"]`, polluting the DB with template definitions that duplicate the formula files.

## Target State

```
Formula (.formula.json)  → pour/wisp (cook inline) → Mol/Wisp
```

Protos exist only as in-memory data structures during the cook→pour/wisp pipeline. Formula catalog IS the proto library.

## Changes Required

1. **Remove template bead storage** - `bd cook` no longer writes to DB
2. **`bd cook` becomes preview** - outputs proto JSON to stdout (like --dry-run)
3. **`bd pour`/`bd wisp` cook inline** - take formula name, cook on the fly
4. **`bd mol bond` accepts formula names** - cooks inline when bonding templates
5. **Remove `[template]` label handling** - no more template beads
6. **Update docs** - molecular-chemistry.md, molecule-algebra.md

## Benefits

- No DB pollution from template beads
- Single source of truth (formulas in .beads/formulas/)
- Simpler mental model: formulas → instances
- Faster (no DB round-trip for protos)

## Deferred

Staged parameterization (proto currying) - punt until someone needs it:
```bash
bd cook mol-feature --var team=backend  # Partially bound proto
bd pour <proto-id> --var priority=high  # Further binding
```

## Depends On

- gt-8tmz (Molecule Algebra) core work should be mostly complete first

## Context

Discussion: Mayor session 2025-12-25, ultrathink on proto storage


Digest: mol-deacon-patrol

Patrol 14: all clear


Digest: mol-deacon-patrol

P15: stable


Digest: mol-deacon-patrol

Patrol 2: All healthy - no changes from cycle 1


Digest: mol-deacon-patrol

Patrol 20: Final cycle, all services healthy, handoff


Merge fix/spawn-beads-path branch to main

Branch has gt nudge command; main has notification deduplication. Both valuable.

Key insight: Only ~10 commits diverged each way in mayor/rig.
- Branch: 9509afa feat(nudge): Add gt nudge command (MISSING from main)
- Main: d2fccd5 slot-based notification deduplication (KEEP)

Merge plan:
1. git merge fix/spawn-beads-path --no-ff
2. Keep nudge.go from branch
3. Keep notification.go from main
4. Merge tmux.go (both NudgeSession AND SendKeysReplace)
5. go build && go test
6. Deploy to ~/.local/bin/gt


Digest: mol-deacon-patrol

Patrol: fixed stale mol spawn prompts, cleaned 36 orphaned wisps, all agents healthy


Digest: mol-deacon-patrol

Patrol 5: Nominal


Test patrol queue feature


Digest: mol-deacon-patrol

Patrol 19: all clear


Fix golangci-lint errcheck warnings (~160 issues)

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

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

Run: golangci-lint run ./...

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


Digest: mol-deacon-patrol

Patrol: 2 completions (valkyrie gt-yd98 MQ, scrotus gt-mzal.1 boot proto). 8 polecats working.


Witness MVP: Automated Polecat Lifecycle

Implement the Witness agent - per-rig 'pit boss' that manages polecat lifecycles.

The Witness enables hands-free swarming by automating:
- Spawning polecats for ready work
- Monitoring worker health
- Processing shutdown requests
- Cleaning up worktrees when done
- Escalating stuck workers to Mayor

Without Witness, humans must manually spawn, monitor, and kill each polecat.

## Core Loop

```
while True:
  # Handle pending shutdowns
  for polecat in polecats where state == pending_shutdown:
    verify git clean
    kill session
    remove worktree
    delete branch

  # Spawn for ready work
  ready = bd ready --parent=<epic> if epic else bd ready
  for issue in ready:
    if active_workers < max_workers:
      gt spawn --issue <id>

  # Check worker health
  for polecat in active polecats:
    if stuck (no progress for 30 min):
      nudge or escalate

  sleep 60
```

## Blocking Bugs to Fix
- gt-dsfi: handoff deadlock
- gt-n7z7: refinery foreground race condition
- gm-c6b: mail coordination (town vs rig beads)


Test: New Router

Testing bd create for mail


Epic: Fix swarm orchestration - witness/refinery/polecat lifecycle

## Problem Statement

The bd-9115 swarm execution required excessive manual intervention from Mayor. The witness, refinery, and polecat lifecycle components didn't coordinate automatically.

## What Should Have Happened

```
Mayor slings 6 beads to rig
    ↓
Polecats auto-spawn with synced beads, work pinned
    ↓
Polecats work autonomously
    ↓
Witness monitors progress, nudges stuck polecats
    ↓
Polecat completes → pushes branch → signals completion
    ↓
Refinery detects ready branch → rebases → merges → deletes branch
    ↓
Witness detects merge → nukes polecat worktree
    ↓
When all done → Witness notifies Mayor
```

## What Actually Happened

1. **gt sling failed to pin beads** - polecat worktrees had stale JSONL
2. **Manual intervention** - had to checkout latest JSONL, pin, nudge each polecat
3. **Witness was passive** - never nudged or checked on polecats
4. **Refinery waited for requests** - didn't detect completed branches
5. **Manual lifecycle** - Mayor had to close beads, nuke polecats
6. **Manual merge coordination** - had to mail refinery with merge order

## Root Cause: Branch Hysteresis

Polecat branches persist across runs and drift from main/beads-sync:
- `polecat/foo` created weeks ago from old main
- New work slung to `polecat/foo`
- JSONL in branch doesn't have new beads
- Pin fails

## Open Questions

### Q1: Fresh branches per run?
Should each polecat spawn create a NEW branch from latest main/beads-sync?
```
gt sling bd-xxx rig
  → creates polecat/foo-20251227-1630 from origin/main
  → ensures JSONL is current
  → after merge, branch deleted forever
```
Pros: No drift, always current
Cons: Branch proliferation, naming complexity

### Q2: Rebase existing branches?
Or should we rebase persistent polecat branches before work?
```
gt sling bd-xxx rig/foo
  → git fetch origin
  → git rebase origin/main
  → now JSONL is current
```
Pros: Keeps simple naming
Cons: Rebase can fail with conflicts

### Q3: Just cherry-pick JSONL?
Minimal fix - grab latest JSONL before pinning:
```
git checkout origin/beads-sync -- .beads/issues.jsonl
bd import  # refresh DB
bd pin bd-xxx
```
Pros: Simple, surgical
Cons: Doesn't fix code drift, just beads

### Q4: Shared beads DB?
Could polecats share a DB (via symlink or sqlite over network)?
Pros: Always consistent
Cons: Locking complexity, daemon conflicts

### Q5: Who owns polecat lifecycle?
- Witness? (current assumption)
- Refinery? (after merge)
- gt sling? (on spawn)
- New "foreman" role?

## Proposed Solutions

See child issues for implementation details. Need to resolve open questions first.


Build activity stream component

Bubbletea component showing scrollable activity stream for a selected worker. Shows mutation events with timestamps, symbols, and issue titles. Displayed when user expands/selects a worker.


attached_args: Extract processExists function to reduce duplication

Extract duplicated processExists function to shared package.

## Files to modify
- internal/util/process.go (NEW)
- internal/witness/manager.go (remove local function, import util)
- internal/refinery/manager.go (remove local function, import util)

## Current duplication
Both files have identical implementations at:
- internal/witness/manager.go (lines 136-144)
- internal/refinery/manager.go (lines 646-654)

```go
func processExists(pid int) bool {
    proc, err := os.FindProcess(pid)
    if err != nil {
        return false
    }
    err = proc.Signal(nil)
    return err == nil
}
```

## Implementation
Create internal/util/process.go:
```go
package util

import "os"

// ProcessExists checks if a process with the given PID exists.
func ProcessExists(pid int) bool {
    proc, err := os.FindProcess(pid)
    if err != nil {
        return false
    }
    return proc.Signal(nil) == nil
}
```

## Acceptance criteria
- [ ] internal/util/process.go created with ProcessExists function
- [ ] internal/util/process_test.go with basic tests
- [ ] witness/manager.go imports util and uses util.ProcessExists
- [ ] refinery/manager.go imports util and uses util.ProcessExists
- [ ] Local processExists functions removed from both files
- [ ] go build ./... passes
- [ ] go test ./... passes


Digest: mol-deacon-patrol

Patrol 14: All healthy


Merge: gt-5af.2

branch: polecat/Doof
target: main
source_issue: gt-5af.2
rig: gastown


Merge: gt-g44u.2

branch: polecat/Doof
target: main
source_issue: gt-g44u.2
rig: gastown


Digest: mol-deacon-patrol

Patrol 11: Routine


Digest: mol-deacon-patrol

Patrol 7: routine


Digest: mol-deacon-patrol

Patrol cycle 2: all healthy, no action required


bd sync --from-main: fresh sync from main branch

## Summary

Add `--from-main` flag to `bd sync` to pull fresh beads state from main branch.

## Use Case

When starting a patrol or fresh session, agent wants clean beads state:
```bash
bd sync --from-main    # Pull latest from origin/main, ignore local changes
```

## Behavior

1. Fetch origin/main
2. Reset local .beads/ to match origin/main
3. Rebuild SQLite DB from fresh JSONL

Useful for polecats/crew starting work to avoid stale state.


Add accounts.yaml config parsing to gt

Parse ~/gt/mayor/accounts.yaml with structure: accounts map (handle -> email, config_dir) and default field. This is the foundational config that other account features depend on. Location follows existing town-level config pattern in mayor/.


Digest: mol-deacon-patrol

Patrol 4: Stable


gt swarm status fails to find issues via rig discovery

gt swarm status gt-1tpts fails with 'issue not found' but bd swarm status gt-1tpts works. The rig discovery in getAllRigs() may not be finding rigs correctly, or the bd commands are running from wrong directory.

Fixed: Added Rig.BeadsPath() method and updated all swarm bd commands to use it. The issue was that bd commands were being run from the rig container directory which has gitignored .beads/, not from a valid git clone within the rig.


ZFC #10: Polecat reports cleanup status instead of Go checking git

**ZFC Violation:** internal/polecat/manager.go:192-215

Go examines git state to decide if polecat removal is safe:
```go
status, err := polecatGit.CheckUncommittedWork()
if status.StashCount > 0 || status.UnpushedCommits > 0 {
    return &UncommittedWorkError{...}
}
```

**ZFC-compliant solution:**
Polecat reports its own cleanup status via agent bead:
- state: stopped
- cleanup_status: clean | has_uncommitted | has_stash | has_unpushed
- Witness reads cleanup_status before calling nuke
- Polecat is authority on whether it's safe to remove

**Relates to:** Day 4.1/4.2 (recycle/nuke commands)

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


Agent beads hook_bead field never populated

## Problem

The witness patrol formula expects polecat agent beads to have:

  hook_bead: <current-work-id>

This field is used by the witness to see what the polecat is working on.

But looking at the code, hook_bead is never set:

1. Polecat agent bead created (polecat/manager.go:167-170) - no hook_bead
2. reportAgentState called (prime.go:1067) - passes nil for hookBead
3. UpdateAgentState preserves existing hook_bead if nil (beads.go:633-635)

Since hook_bead starts empty and is never updated, the witness cannot use
agent beads to see what polecats are working on.

## Found in commit

f3a6ef6 (feat: Witness reads polecat state from agent beads)

## Related to

- gt-0lop3 (Agent state lifecycle incomplete for polecats)

## Suggested fix

When a polecat is assigned work (via gt sling or similar), update the agent
bead's hook_bead field:

  bd.UpdateAgentState(agentID, 'running', &hookBeadID)


Design GGT prompt architecture

Audit PGT prompts and design canonical prompt system for GGT. Create docs/prompts.md with inventory, gap analysis, and Witness prompt design.


Molecule-based Witness Patrol

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

## Key Insight

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

## Current State

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

## Target State

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

## Benefits

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

## Non-Goals

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


mol sling command: attach molecule to agent hook

Add `gt mol sling <molecule-id>` command to attach a molecule to the current agent's hook.

## Behavior

```bash
gt mol sling mol-witness-patrol
```

1. Find agent identity from cwd (witness, polecat, crew, etc.)
2. Find or create agent's handoff bead
3. Attach molecule to handoff bead (attached_molecule field)
4. Create root issue for this molecule instance
5. Instantiate molecule steps under the root

## Difference from mol attach

- `mol attach` attaches to an existing pinned bead
- `mol sling` creates a fresh instance for execution

## Output

```
🧬 Slung mol-witness-patrol on gastown/witness
  Instance: gt-xyz (9 steps)
  First step: inbox-check
```

## Implementation

- Reuse InstantiateMolecule from molecule.go
- Create wrapper issue for the instance
- Set attached_molecule on handoff bead


mol progress: track dynamically bonded children

Extend `gt mol progress` to show dynamically bonded children.

## Current Behavior

```
gt mol progress gt-abc
```

Shows progress through pre-defined steps from InstantiateMolecule.

## New Behavior

Also show dynamically bonded children:

```
📊 Progress: mol-witness-patrol (gt-abc)

  Steps (9):
    ✓ inbox-check
    ✓ check-refinery
    ✓ load-state
    ◐ survey-workers (bonded 3 children)
    ○ aggregate (WaitsFor: all-children)
    ○ save-state
    ○ generate-summary
    ○ context-check
    ○ burn-or-loop

  Bonded Children (3):
    ✓ arm-toast (5/5 steps)
    ◐ arm-nux (3/5 steps)
    ○ arm-furiosa (0/5 steps)

  Progress: 4/9 steps, 3 arms (1 complete, 1 in-progress, 1 pending)
```

## Implementation

1. Find children with bonded_to: <parent-id> in description
2. Recursively get progress for each bonded child
3. Aggregate into parent progress display


Witness: instantiate mol-witness-patrol on start

When witness starts, create a mol-witness-patrol instance and pin it to the witness hook.

## Changes to witness/manager.go

In Start() or run():
1. Check if patrol instance exists on hook
2. If not, call beads.InstantiateMolecule(mol-witness-patrol, handoffBead)
3. Store instance ID for later reference

## Hook Structure

The witness handoff bead gets:
- attached_molecule: gt-patrol-xyz
- attached_at: timestamp

## Idempotency

If patrol already exists (from previous session), reuse it.
Don't create duplicate instances on restart.


Witness: bond mol-polecat-arm when discovering polecat

When witness discovers a polecat during healthCheck(), bond a mol-polecat-arm child.

## Changes to witness/manager.go

In healthCheck() when iterating polecats:
1. Check if arm already exists for this polecat (arm-{name})
2. If not, call: gt mol bond mol-polecat-arm --parent=$PATROL_ID --ref=arm-{name} --var polecat_name={name} --var rig={rig}
3. Track arm issue ID in handoff state

## Arm Lifecycle

- Created when polecat first seen
- Updated as inspection happens (nudges, state changes)
- Closed when polecat cleaned up

## Deduplication

Check handoffState.WorkerStates[name].ArmID before creating.
Needs: gt-59zd.3


Witness: close arm when polecat completes

When witness cleans up a polecat, close its mol-polecat-arm issue.

## Changes to witness/manager.go

In cleanupPolecat():
1. Get arm ID from handoffState.WorkerStates[name].ArmID
2. If exists, call: bd close {armID} --reason='polecat cleaned up'
3. Clear ArmID from handoff state

## Arm Close Reasons

- 'polecat cleaned up' - normal completion
- 'polecat killed - stuck' - killed due to inactivity
- 'polecat killed - escalated' - killed after Mayor escalation

## State Update

The arm's description could be updated with final outcome before closing.
Needs: gt-59zd.4


mol progress: display bonded children in output

Extend gt mol progress to show dynamically bonded children.

## Current Output

Shows steps from original instantiation only.

## New Output

Also show bonded children:

  Bonded Children (3):
    ✓ arm-toast (closed)
    ◐ arm-nux (open)
    ○ arm-furiosa (open)

## Implementation

1. Query for children with bonded_to: {parent-id} in description
2. For each bonded child, show status (open/closed)
3. Optionally show progress through child's steps
Needs: gt-59zd.5


Digest: mol-deacon-patrol

Patrol 5: Quiet, all healthy


Deacon: Hierarchical health-check orchestrator

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

## Core Concept

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

## Architecture

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

## Deacon Responsibilities

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

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

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

## Key Files

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

## Wake Cycle

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

## Session Patterns

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

## Relation to Policy Beads

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


Create DEACON.md role context

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


Add gt deacon start/stop/status commands

Add CLI commands: gt deacon start (spawn gt-deacon session), gt deacon stop (kill session), gt deacon status (show health state). Similar to gt mayor commands.


Add deacon/ mail identity support

Ensure deacon/ works as a mail identity in town-level beads. Test with bd mail send deacon/ and bd mail inbox --identity deacon/.


Simplify Go daemon to Deacon-watcher

Refactor internal/daemon to only: (1) check deacon/heartbeat.json every 60s, (2) poke gt-deacon if stale, (3) restart Deacon and mail for help if very stale. Remove all lifecycle processing and agent poking.


Update lifecycle mail targets to deacon/

Update Mayor, Witness, and Crew handoff code to send lifecycle requests to deacon/ instead of daemon/. Update internal/cmd/handoff.go and related code.


Add Deacon heartbeat mechanism

Deacon writes deacon/heartbeat.json on each wake. Go daemon reads it to decide whether to poke. Add heartbeat read/write helpers.


Add crew session pattern to lifecycle handling

Update identityToSession and restartSession to handle crew patterns: gastown/max -> gt-gastown-max. Crew workers can request lifecycle but are not proactively monitored.


Add human escalation configuration

Add overseer contact config to mayor/town.json. Deacon uses this for escalation when it cannot resolve issues. Support email and/or other notification methods.


Merge: nux-1767080427756

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


Digest: mol-deacon-patrol

Patrol 19


Digest: mol-deacon-patrol

Patrol 19: all quiet


Day 5.2: Fix integration issues

Address any issues found in Day 5.1:
- Debug failures
- Fix edge cases
- Retry integration test
- Document remaining gaps

This is buffer time for reality.

Parent: gt-oki8p


Digest: mol-deacon-patrol

Patrol 15: Green


Day 2.3: Agent writes state on lifecycle events

Agents update their bead state using bd agent state command:
- On startup: bd agent state gt-<role> running
- On shutdown: bd agent state gt-<role> stopped
- On stuck: bd agent state gt-<role> stuck

State is self-reported, not inferred.

Implementation: Wire up agent lifecycle hooks (SessionStart, SessionEnd, health checks) to call bd agent state.

Parent: gt-d0jqp


Digest: mol-deacon-patrol

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


Merge: gt-99a

branch: polecat/Slit
target: main
source_issue: gt-99a
rig: gastown


Digest: mol-deacon-patrol

Patrol 4: All healthy


Merge: slit-1767082302712

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


Refinery Engineer: Role prompting and CLAUDE.md

Create refinery/CLAUDE.md with Chief Merge Engineer role context. Include:
- Role identity and responsibilities
- Decision authority (merge order, test frequency, binary rebuilds)
- Communication patterns (Witness, Deacon)
- Session lifecycle (handoff bead protocol)
- Key commands reference


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

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

## Variables

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

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

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

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

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

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

Calculate: minutes since last activity.
Needs: capture

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

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

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

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

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

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

Record decision and rationale.
Needs: load-history

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

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

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

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

**none**: No action needed.

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

## Output

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

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


gt mol step done: Auto-continue molecule on step completion

## Summary

Implement `gt mol step done <step-id>` command that enables polecats to close a molecule step and automatically continue to the next step without waiting for witness orchestration.

## Problem

Currently, when a polecat completes a molecule step:
1. Polecat sends POLECAT_DONE to witness
2. Witness (in patrol cycle) eventually reads mail
3. Witness spawns next step
4. **Minutes of latency possible**

Polecats also forget to close beads steps (using internal TodoWrite instead), breaking the activity feed.

## Solution

`gt mol step done <step-id>` handles everything:

1. Close the beads step (`bd close <step-id>`)
2. Extract molecule ID from step (bd-mol-xga.1 → bd-mol-xga)
3. Find next ready step (dependency-aware)
4. If next step exists:
   - Update hook file to point to next step
   - Write handoff context
   - tmux respawn-pane (fresh claude session)
5. If molecule complete:
   - Clear hook
   - Send POLECAT_DONE to witness
   - Exit session

## Key Insight

Step-to-step transitions are **mechanical**, not **cognitive**. No AI needed in the critical path. Pure Go automation.

## Related Commands

- `gt mol next <mol-id>` - Show next ready step(s)
- `gt mol progress <mol-id>` - Already exists

## Polecat Protocol Update

```markdown
When your hook contains a molecule step:
1. Read the step: `bd show <step-id>`
2. Execute the work described
3. When complete: `gt mol step done <step-id>`

That is it. Do NOT manually close steps. Do NOT work multiple steps.
```

## Benefits

- **Instant step transitions** (~5-10s vs minutes)
- **Activity feed works** (steps closed as completed)
- **Witness freed up** (safety net only, not critical path)
- **Fresh context per step** (better attention, no accumulated confusion)


Merge: gt-3x0z.3

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


Digest: mol-deacon-patrol

Patrol 14: healthy


Digest: mol-deacon-patrol

Patrol 19: clear


Witness role describes incorrect commands

prompts/roles/witness.md line 44 shows:
  gt polecat list {{ rig }}

But polecats are worktrees managed by Refinery/Mayor's clone,
not a separate polecat manager. The witness shouldn't manage
polecats directly.

Review and update witness role to match actual architecture.


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

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

## Variables

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

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

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

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

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

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

Calculate: minutes since last activity.
Needs: capture

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

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

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

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

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

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

Record decision and rationale.
Needs: load-history

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

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

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

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

**none**: No action needed.

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

## Output

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

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


Witness: aggregate completion and notify Mayor

## Problem
No signal when swarm completes. Mayor must manually check.

## ZFC-Compliant Solution
Add step to `mol-witness-patrol.formula.toml`:

```toml
[[step]]
id = "check-swarm-completion"
title = "Report swarm completion to Mayor"
description = """
1. Check state.json for active swarm tracking:
   - swarm_id, start_time, assigned_beads[], completed_beads[]
2. For each completed merge (from cleanup step):
   - Add bead to completed_beads
3. If all assigned_beads are in completed_beads:
   - Calculate duration
   - Send summary to Mayor:
     gt mail send mayor/ -s "SWARM_COMPLETE: <swarm_id>" -m "All N polecats merged in Xm"
   - Clear swarm tracking from state.json
"""
depends_on = ["cleanup-merged-polecats"]
```

## Swarm Initialization
When Mayor slings a batch, they should:
```bash
# Mayor creates swarm tracking
gt mail send <rig>/witness -s "SWARM_START" -m '{"swarm_id": "batch-123", "beads": ["bd-a", "bd-b", "bd-c"]}'
```

Witness parses SWARM_START and initializes tracking in state.json.

## Why This Works
- Mayor initiates batch with SWARM_START mail
- Witness tracks progress in its own state
- Witness signals completion via mail
- All coordination is agent-to-agent via mail

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


Digest: mol-deacon-patrol

Patrol 6: Mayor OK, 11 sessions


Remove legacy .beads-wisp/ infrastructure

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

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 10: All healthy


Digest: mol-deacon-patrol

Patrol complete: no callbacks, agents healthy, 1 stale lock cleaned


Digest: mol-deacon-patrol

Patrol 14: all quiet


Digest: mol-deacon-patrol

Patrol 9: All agents healthy


Digest: mol-deacon-patrol

Patrol 2: all clear, 9 sessions healthy


Tech Debt: Code Review December 2024

Tech debt identified during code review on 2024-12-21. Contains 11 issues ranging from P2-P4 covering:

- Code duplication (manager creation boilerplate)
- Test coverage gaps (cmd 6.8%, mail 3.6%)
- Magic strings needing constants
- Error handling inconsistencies
- Large files needing splitting
- Unused code removal

Work through these incrementally to improve codebase maintainability.


Merge: cheedo-1767074334708

attached_args: Code review

branch: polecat/cheedo-1767074334708
target: main
source_issue: cheedo-1767074334708
rig: gastown

Branch polecat/cheedo-1767074334708 does not exist - polecat decommissioned without work to merge


Digest: mol-deacon-patrol

Patrol 10: full check, all healthy


Digest: mol-deacon-patrol

Patrol #15: Stable


Document how to configure a Gas Town Harness

Create docs explaining what a harness is (private repo containing GT installation with rigs gitignored), why you'd want one, and how to set it up with beads redirects


Merge: nux-1766959759345

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


Digest: mol-deacon-patrol

P6: stable


Merge: valkyrie-1767074338488

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


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

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


Digest: mol-deacon-patrol

Patrol 19: quiet


Test message

Testing GGT mail via beads


Patrol step: check parked molecules for unblock

Add patrol step to Deacon for checking parked molecules:

```yaml
- step: check-parked-molecules
  action: |
    For each molecule with:
      - status: in_progress
      - assignee: null
      - has step with external: blocked_by
    Check if external deps are now satisfied.
    If yes: spawn polecat to resume the molecule.
```

This automates the resume process - no manual intervention needed when
upstream dependencies ship.

Part of cross-project dependency system.
See: docs/cross-project-deps.md

Depends on: gt-in3x (spawn --continue)
Priority: P3 (future automation, not required for launch)


Add 'wit' alias for witness command

ref works as alias for refinery, but wit doesn't work for witness. Add Aliases: []string{"wit"} to witnessCmd.


Day 3.6: Witness pings Deacon for second-order monitoring

Implement WITNESS_PING protocol for second-order monitoring.

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

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

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


Digest: mol-deacon-patrol

Patrol 12: All agents healthy


Update vision.md with proto/mol/wisp terminology

Update the Steam Engine Metaphor section to use consistent phase terminology:

Current (vision.md):
- Proto molecules = fuel (templates)
- Wisps = steam (transient execution traces)
- Digests = distillate (permanent records)

New terminology:
- **Proto** = crystal/solid - frozen template
- **Mol** = liquid - reified durable instance (tracked in git)
- **Wisp** = gas - ephemeral instance (evaporates after squash)
- **Digest** = distillate - compressed summary after squash

Key clarification needed:
- Proto → bond → creates either Mol (durable) or Wisp (ephemeral)
- The choice of Mol vs Wisp depends on --wisp flag
- Default is Mol (durable, recorded in main beads)
- Wisp lives in .beads-ephemeral/, squashes away


Shutdown request handler

Handle polecat shutdown requests:

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

Depends on fixing gt-dsfi (handoff deadlock).


Document session cycling protocol in all templates

Add explicit lifecycle request protocol to all agent templates.

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

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

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

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


Digest: mol-deacon-patrol

Patrol 2: All agents healthy, no actions


Preflight molecule: verify baseline health before work

Before assigning work, verify baseline (main branch) is healthy.

**From VC**: Self-healing state machine (HEALTHY → SELF_HEALING → ESCALATED). ~200 lines.

**Gas Town implementation**: Preflight molecule or refinery feature:
```yaml
preflight:
  gates: [test, lint, build]
  on_failure: create-fix-issue
```

Run at session start. If baseline broken, file a P0 fix issue and work on that first.

**Value**: Self-healing baseline. Agents don't start from broken state.

**VC lesson**: Prevents cascading failures. Agent shouldn't start work on broken code.


Digest: mol-deacon-patrol

Patrol cycle 14: routine


Review and audit all GGT beads

Thorough review of all filed beads in gastown GGT repo. Check for: consistency, completeness, correct dependencies, accurate descriptions, proper prioritization. Ensure beads are self-contained and dont rely on external docs.


Molecule Phase Terminology Documentation

Document the three-phase molecule lifecycle with consistent terminology:

## The Phases (States of Matter)

| Phase | State | Nature |
|-------|-------|--------|
| **Proto** | Solid/Crystal | Frozen template, static definition in catalog |
| **Mol** | Liquid | Reified instance, malleable, tracked in git (durable) |
| **Wisp** | Gas | Evaporates after squash, ephemeral orchestration |

## Documentation Gaps Identified

### High Priority
1. Update vision.md Steam Engine metaphor to use proto/mol/wisp terminology
2. Add lifecycle diagram: Proto → bond → Mol or Wisp → squash → Digest
3. Document Go types for phases (ProtoMolecule, etc.)
4. Document .beads-ephemeral/ structure and purpose

### Medium Priority
5. Update CLAUDE.md files (polecat, witness, refinery) with molecule workflow
6. Document bd mol bond/squash/burn CLI API with examples
7. Add polecat guide: Executing molecules and generating summaries

### Low Priority
8. Add terminology glossary to architecture.md
9. Create troubleshooting playbook for stuck molecules
10. Add reference diagrams for state transitions

## Current State
- architecture.md has good molecule basics but uses inconsistent terminology
- vision.md has Steam Engine metaphor (proto=fuel, wisp=steam, digest=distillate)
- molecules.md has detailed reference but needs phase clarity
- builtin_molecules.go has 8 molecules but no phase documentation in code


Digest: mol-deacon-patrol

Patrol 19: All healthy


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.

Moved to bd-gigi - this is a beads bug, was filed in wrong rig (gastown). The fix needs to be made in the beads codebase at cmd/bd/compact_tombstone.go and cmd/bd/compact.go.


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


Merge: keeper-1767106012994

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


Digest: mol-deacon-patrol

Patrol 2: Quiet cycle, all agents healthy


Digest: mol-deacon-patrol

Patrol 18: All healthy


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: no callbacks, no polecats, all agents healthy, cleaned 2 orphan processes + 2 stale locks


Merge: ace-1767074385080

attached_args: Code review this merge request

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

Code review APPROVED: Clean fix for gt-1clzd. Adds skipPane param to resolveTargetAgent, allowing --naked to bypass tmux pane lookup for terminated polecats.


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 16: All healthy


Digest: mol-deacon-patrol

Patrol 17: routine


Digest: mol-deacon-patrol

Patrol 14: Nominal


Digest: mol-deacon-patrol

Patrol 12


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


Digest: mol-deacon-patrol

Patrol 7: Quiet


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.


Default BD_ACTOR in beads create

When --actor flag not specified in bd create, default to BD_ACTOR environment variable. Ensures all bead creation has actor attribution without explicit flag.


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.


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


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.


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.


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)


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


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


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


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


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.


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


Digest: mol-deacon-patrol

Patrol complete: gastown witness came online, town healthy


implement

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

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

Depends: load-context


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)