Schematic: Domain-Level Formula Composition

A Schematic is a coordinated set of formulas defining an entire workflow domain.

## Concept

Like an orchestra score to instrument parts - says how formulas fit together.

```toml
# shiny-engineering.schematic
[schematic]
name = "The Shiny Way"
description = "Full engineering workflow from issue to production"

[formulas]
planning = "@gastown/shiny"
development = "@gastown/polecat-work"
review = "@gastown/review"
release = "@gastown/release"

[flow]
default = "planning >> development >> review >> release"

[constraints]
all_code_reviewed = true
tests_before_deploy = true
```

## Relationship to Other Concepts

- **Schematic** (workflow dimension) = composition of Formulas
- **Campaign** (work dimension) = composition of Epics
- Schematic compiles to Campaign, like Formula compiles to Molecule

## Use Cases

1. Define "The Shiny Way" for a team/org
2. Encode compliance requirements
3. Standardize workflows across projects
4. Enable workflow reuse and sharing

## Open Questions

1. File format - TOML? YAML? Extend formula syntax?
2. Validation - how to verify schematic consistency?
3. Overrides - can project override org schematic?

## Related

- docs/formula_evolution.md
- gt-8tmz - molecule algebra


Digest: mol-deacon-patrol

Patrol 4: all healthy


Digest: mol-deacon-patrol

Patrol 19: OK


ZFC #9: Explicit MR registration instead of branch parsing

**ZFC Violation:** internal/refinery/manager.go:346-370

Go infers work metadata from branch naming conventions:
```go
pattern := regexp.MustCompile(`^polecat/([^/]+)(?:/(.+))?$`)
worker := matches[1]
issueID = matches[2]
```

**ZFC-compliant solution:**
Polecats register MRs with explicit structured data:
- `gt mq submit --worker=nux --issue=gt-xyz --branch=polecat/nux/gt-xyz`
- Creates MR bead with explicit fields
- No regex parsing of branch names

**Benefits:**
- Work metadata is explicit, not inferred
- Branch naming can evolve without breaking refinery
- Audit trail in beads

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


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Crew worker CLAUDE.md prompting

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


Verify refinery patrol loop executes

Verify refineries run their patrol formula:

1. Check refinery session: `gt session status gastown/refinery`
2. If not running: `gt session start gastown/refinery`
3. Watch refinery: `tmux attach -t gt-gastown-refinery`
4. Verify it handles:
   - Checking for pending MRs
   - Processing merge queue
   - Reporting results

Test for both rigs: gastown and beads.


Digest: mol-deacon-patrol

Patrol 10: Mayor OK, 11 sessions


Add audit trail for hook detach operations

Log detachment events for debugging and monitoring.

When work is detached from hook:
- Log timestamp, agent, molecule ID, reason
- If abnormal (not completion), notify Witness
- Consider adding to wisp digest for patrol cycles


Digest: mol-deacon-patrol

Patrol cycle 17: nominal


Digest: mol-deacon-patrol

Patrol 3: Quiet, all healthy


mol-deacon-patrol


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

Patrol 5: all healthy, 2 crews (dave, max) active


Implement aspect merging across formula chain

Implement merging of aspects when formulas extend each other.

## Current State

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

## Implementation

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

## Files

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

## Tests

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


Rig management: discover, load, create rigs

Rig discovery, loading, and creation.

## Interface

```go
type Rig struct {
    Name      string
    Path      string
    GitURL    string
    Config    RigConfig
    Polecats  []string
    HasWitness bool
    HasRefinery bool
}

type RigManager struct {
    townRoot string
    config   *Config
    git      *Git
}

func NewRigManager(townRoot string, config *Config, git *Git) *RigManager

// Discovery
func (rm *RigManager) DiscoverRigs() ([]*Rig, error)
func (rm *RigManager) GetRig(name string) (*Rig, error)
func (rm *RigManager) RigExists(name string) bool

// Creation
func (rm *RigManager) AddRig(name, gitURL string) (*Rig, error)
func (rm *RigManager) RemoveRig(name string) error

// Rig state
func (rm *RigManager) LoadRigConfig(rig *Rig) error
func (rm *RigManager) SaveRigConfig(rig *Rig) error
```

## Discovery Logic

1. Read rigs.json from config/
2. For each registered rig, verify directory exists
3. Load rig-level config if present
4. Scan for polecats/ subdirectory
5. Check for witness/, refinery/ directories

## Rig Creation

1. Clone repo to town root
2. Add entry to rigs.json
3. Create agent directories (polecats/, witness/, refinery/, mayor/)
4. Update .git/info/exclude to ignore agent dirs
5. Initialize rig config

## Rig Structure

Per docs/architecture.md:
```
<rig>/
├── polecats/
├── refinery/rig/
├── witness/rig/
└── mayor/rig/
```


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


implement

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


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-qwyu - The source issue ID being worked on


Check Witness and Refinery health

Check Witness and Refinery health for each rig.

**ZFC Principle**: You (Claude) make the judgment call about what is "stuck" or "unresponsive" - there are no hardcoded thresholds in Go. Read the signals, consider context, and decide.

For each rig, run:
```bash
gt witness status <rig>
gt refinery status <rig>
```

**Signals to assess:**

| Component | Healthy Signals | Concerning Signals |
|-----------|-----------------|-------------------|
| Witness | State: running, recent activity | State: not running, no heartbeat |
| Refinery | State: running, queue processing | Queue stuck, merge failures |

**Tracking unresponsive cycles:**

Maintain in your patrol state (persisted across cycles):
```
health_state:
  <rig>:
    witness:
      unresponsive_cycles: 0
      last_seen_healthy: <timestamp>
    refinery:
      unresponsive_cycles: 0
      last_seen_healthy: <timestamp>
```

**Decision matrix** (you decide the thresholds based on context):

| Cycles Unresponsive | Suggested Action |
|---------------------|------------------|
| 1-2 | Note it, check again next cycle |
| 3-4 | Attempt restart: gt witness restart <rig> |
| 5+ | Escalate to Mayor with context |

**Restart commands:**
```bash
gt witness restart <rig>
gt refinery restart <rig>
```

**Escalation:**
```bash
gt mail send mayor/ -s "Health: <rig> <component> unresponsive" \
  -m "Component has been unresponsive for N cycles. Restart attempts failed.
      Last healthy: <timestamp>
      Error signals: <details>"
```

Reset unresponsive_cycles to 0 when component responds normally.


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Digest: mol-deacon-patrol

Patrol 17: Quiet


Test issue 1 for displacement


Digest: mol-deacon-patrol

Patrol 5: Quiet, all healthy


Digest: mol-deacon-patrol

Patrol 17: Routine


Digest: mol-deacon-patrol

Patrol cycle 12: nominal


Session cycling: Auto-spawn successor when context full

Enable automatic session succession for crew workers.

When a crew worker's context fills up (>80%), they should:
1. Write handoff mail to themselves
2. Signal session end
3. System auto-spawns successor session
4. Successor reads handoff, finds attached work, auto-continues

This completes the autonomous overnight work pattern from gt-9g82.

Depends on:
- Detecting context fullness (Claude Code API?)
- Session spawning mechanism (tmux/daemon)
- Handoff protocol already implemented


Adaptive backoff for daemon heartbeat

Track agent responsiveness and adjust heartbeat frequency.

## Per-Agent State

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

## Strategy Options

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

## Recovery

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

## Benefits

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


ensure-beads-witness

Start the beads rig Witness.

## Action
```bash
gt witness start beads
```

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

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

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


Digest: mol-deacon-patrol

Patrol 6: quiet


Digest: mol-deacon-patrol

Patrol #3: Stable, no changes


Phase 1.3: mol-deacon-patrol definition

Define the Deacon patrol molecule in builtin_molecules.go.

## Steps
1. inbox-check - Handle callbacks from agents
2. health-scan - Ping Witnesses and Refineries
3. plugin-run - Execute registered plugins
4. orphan-check - Find abandoned work
5. session-gc - Clean dead sessions
6. context-check - Check own context limit
7. loop-or-exit - Burn and let daemon respawn, or exit if context high

## Implementation
- Add DeaconPatrolMolecule() to builtin_molecules.go
- Add to BuiltinMolecules() list
- Test with bd mol show mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 8: all quiet


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-test123 - The source issue ID being worked on


Fix CLI mismatches from bd-2fs7 (wisp->ephemeral migration)

bd-2fs7 moved wisp/pour under bd mol, but gastown wasn't updated.

Fixed:
- router.go: --wisp -> --ephemeral for bd create
- patrol_helpers.go: bd wisp create -> bd mol wisp create
- wisp_check.go: bd wisp gc -> bd mol wisp gc

This was causing handoff failures (gt mail send errored with 'unknown flag: --wisp').


Test Patrol Parent

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

Original description:
Test parent for Christmas Ornament pattern


Digest: mol-deacon-patrol

Patrol 18: All healthy


Test Patrol for Bonding

Parent issue for mol bond CLI test


Digest: mol-deacon-patrol

Patrol 16: clear


Merge: gt-ih0s

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


Update Mayor prompting in gastown-py

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


Epic: Refinery Engineer Autonomy

Make Refinery Engineer fully autonomous:

## Goal
Refinery can run indefinitely, processing all polecat work through merge queue, cycling sessions as needed, without human intervention.

## Key Components
1. Role prompting (CLAUDE.md, templates)
2. Handoff mechanism (pinned beads)
3. Context detection (gt prime)
4. Communication protocol (Witness, Deacon)
5. Future: Merge orchestration plugins

## Success Criteria
- Refinery starts, reads handoff, knows what to do
- Processes merges sequentially with conflict resolution
- Cycles sessions cleanly via Deacon
- Communicates results to Witness
- No work left behind


Implement pinned beads for handoff state

Add pinned bead support to beads:
- Pinned beads never close, only update
- Use for persistent state like Refinery handoffs
- bd create --pinned flag
- bd list --pinned to find them
- Update description to change state


Test7

Test with fixed workdir


Digest: mol-deacon-patrol

Patrol cycle 18: routine


Daemon: integration test with real lifecycle

Need an end-to-end test that:
1. Starts daemon
2. Starts a test agent session
3. Sends lifecycle request to daemon
4. Verifies session was killed and restarted
5. Cleans up

Could use a mock 'agent' that's just a shell script.


Messaging infrastructure: lists, queues, channels

Multi-recipient and broadcast messaging for Gas Town.

## Phases

**Phase 1 - Foundation**: Config directory (gt-i6jvc)
**Phase 1 - Core**: list:, queue:, @group (requires agent-as-bead for @group)
**Phase 2 - Channels**: announce:, #channel, gt channel publish

## Addressing Syntax

| Prefix | Resolution | Storage | Claim? |
|--------|------------|---------|--------|
| `@group` | Dynamic (bead queries) | Fan-out (N copies) | No |
| `list:name` | Static config | Fan-out (N copies) | No |
| `queue:name` | Static config | Shared (1 copy) | Yes |
| `announce:name` | Static config | Shared (1 copy) | No |
| `#channel` | Dynamic (tmux) | None (ephemeral) | No |

## Alignment with Agent-as-Bead

@group resolution now uses agent bead queries:
- @rig/gastown → bd list --type=agent --rig=gastown
- @witnesses → bd list --type=agent --role_type=witness

This replaces the originally planned filesystem scanner (gt-xo05b, now closed).

## Independent Work

Config-based features (list:, queue:, announce:) can proceed without agent identity.
Only @group requires the agent schema from Pillar 1.


Liftoff implementation plan in Beads

## Context

Session on 2025-12-27 produced three key docs:
- `~/gt/docs/agent-as-bead.md` - Agents ARE beads (identity, hook slots, CV chain roots)
- `~/gt/docs/zfc-violations-audit.md` - Where Go infers instead of trusting agents
- `~/gt/docs/liftoff-plan.md` - 4.5 day plan to self-sustaining Gas Town

## Work Required

### Phase 1: Audit existing beads ✅
- Review all open beads in gt- prefix
- Close obsolete/stale issues
- Update any that need refinement
- Note which existing beads align with liftoff plan

### Phase 2: File new beads ✅
Translate liftoff-plan.md into beads with proper dependencies.

Three pillars:
1. **Pillar 1: Agent Identity** (gt-d0jqp) - schema, slot commands, migration
2. **Pillar 2: Patrol Ignition** (gt-hwka3) - witness/refinery formula wiring
3. **Pillar 3: Polecat Lifecycle** (gt-4a2qt) - recycle/nuke commands, session-per-step

### Phase 3: Review ✅
Human reviews the plan.

## Deliverable

A complete dependency graph in beads that can be:
1. Queried with `bd ready` to find available work
2. Slung to polecats in dependency order
3. Tracked to completion

## References

- ~/gt/docs/liftoff-plan.md - 4.5 day plan
- ~/gt/docs/agent-as-bead.md - Agent identity design
- ~/gt/docs/zfc-violations-audit.md - ZFC violations addressed


Digest: mol-deacon-patrol

Patrol 18: Green


Digest: mol-deacon-patrol

Patrol 16: Quiet


Digest: mol-deacon-patrol

Patrol 15: Green


Activity Feed TUI

Terminal UI for browsing Gas Town activity. Shows hierarchical view of rigs, workers, and their current state. Features: live updates, expandable details, molecule progress (step X of Y). Built with bubbletea/lipgloss. Design doc: history/activity-feed-tui-design.md


Critical packages lack test coverage

Several packages have no test files:
- internal/lock/ - Core identity locking (212 lines UNTESTED)
- internal/witness/ - Worker monitoring
- internal/mrqueue/ - MR queue management
- internal/claude/ - Claude integration
- internal/style/ - Terminal styling
- internal/constants/ - Constants

Priority for testing:
1. lock/ - prevents duplicate agents, critical for correctness
2. witness/ - agent lifecycle management
3. mrqueue/ - merge request processing

Overall: 37 test files for 160 Go files (23% by file count)


Burn and respawn or loop

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

Decision point at end of patrol cycle:

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

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

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

This enables infinite patrol duration via context-aware respawning.


Digest: mol-deacon-patrol

Patrol 8: OK


CLI: rig commands (list, show, add)

Rig management CLI commands.

## Commands

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

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

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

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

## Implementation

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


mol-deacon-patrol

Mayor's daemon patrol loop.

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


mol-deacon-patrol


Mayor session cycling prompting

Add session cycling section to Mayor CLAUDE.md template.

## When to Cycle

Cycle proactively when:
- Running for several hours
- Context feels crowded (losing track of earlier state)
- Major phase completed
- About to start complex new work

## Composing Handoff Notes

1. Gather information:
   town status    # Overall health
   town rigs      # Each rig state
   town inbox     # Pending messages
   bd ready       # Work items

2. Compose note with this structure:

[HANDOFF_TYPE]: mayor_cycle
[TIMESTAMP]: <current time>
[SESSION_DURATION]: <how long running>

## Active Swarms
<per-rig swarm status>

## Rig Status
<table of rig health>

## Pending Escalations
<issues needing your decision>

## In-Flight Decisions
<decisions being made>

## Recent Actions
<last 5-10 things you did>

## Delegated Work
<work sent to refineries>

## User Requests
<pending user asks>

## Next Steps
<what next session should do>

## Warnings/Notes
<critical info for next session>

3. Send handoff:
   town mail send mayor/ -s "Session Handoff" -m "<note>"

4. End session - next instance picks up from handoff.


Digest: mol-deacon-patrol

Patrol 2: All systems nominal


Test Task 1: Add comment to gt.go


🤝 HANDOFF: Investigate beads-sync divergence (221 files)

## Context

gt doctor now detects orphaned code on beads-sync branch. Running it shows:

  beads-sync-orphans: 221 file(s) on beads-sync not in main

This is in the gastown repo (checking from crew/max).

## Background

- Earlier today we recovered orphaned mail migration code lost in merge 96c773f
- Recently simplified architecture: all workers use mayor/rig/.beads (redirects)
- This simplification may have caused beads-sync branch to diverge unexpectedly

## Investigation Steps

1. From crew/max run: git diff main..beads-sync --stat
2. Check if beads-sync should even exist anymore (was for multi-clone sync)
3. If obsolete with redirect architecture, consider deleting it
4. If it has legitimate changes, cherry-pick or merge to main

## Key Question

With all workers using mayor/rig/.beads via redirects, is beads-sync still needed?

## Commands

git log main..beads-sync --oneline | head -20
git diff main..beads-sync -- *.go | head -100


Digest: mol-deacon-patrol

Patrol 8: Mayor OK, 11 sessions


Mail inbox from witness/refinery dirs shows Mayor inbox

When running `gt mail inbox` from the witness or refinery directories, it shows the Mayor's inbox instead of the agent's own inbox.

Reproduction:
```
$ cd ~/gt/beads/witness && gt mail inbox
📬 Inbox: mayor/ (1 messages, 1 unread)
  ● 🤝 HANDOFF: Swarm session complete...
```

Expected: Should show beads/witness inbox

Likely cause: The cwd-based identity resolution isn't recognizing witness/refinery as valid mail identities, so it falls back to Mayor.


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

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


Polecat template: fix hard-to-parse sentence about persistence

This sentence is hard to parse: 'When in doubt, prefer bd—persistence you don't need beats lost context'. Rewrite for clarity, e.g., 'When in doubt, use bd for tracking - unnecessary persistence is better than lost context.'


Digest: mol-deacon-patrol

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


mol-witness-patrol

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

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

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

## The Christmas Ornament Shape

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

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

## Phases

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

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

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

## Dynamic Arms

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

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

## Activity Feed

This design enables real-time visibility:

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

## Storage

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

## Dependencies

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

Labels: [template, christmas-ornament]


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

Patrol 19: All healthy


Digest: mol-deacon-patrol

Patrol 17: nominal


Digest: mol-deacon-patrol

Patrol 9: OK


Digest: mol-deacon-patrol

Patrol #11: Stable


Clean up ~/gt root directory cruft

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

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

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

Clean up, consolidate, update .gitignore as needed.


gt: Write town-level activity signal in PersistentPreRun

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

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

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

Call both in root.go's PersistentPreRun.


self-review

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

Fix any issues found before proceeding.

Depends: implement


Obsolete beads issues need cleanup

Found obsolete issues that should be closed or fixed:

Test artifacts to close:
- gt-nriy: Test: Alpha to Beta (message test)
- gt-gswn: Integration test (test artifact)
- gt-54kn: Test: New Router

Broken dependencies (reference non-existent gt-test123):
- gt-vhby: implement
- gt-tvos: load-context
- gt-lwuu.2: implement (template variable unresolved)

CloudRun references (non-existent /deploy/cloudrun/):
- gt-9a2.6, gt-9a2.7, gt-9a2.11

Review and close/update these before launch.


mol-deacon-patrol


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Digest: mol-deacon-patrol

Patrol 3: All healthy, quiet


Digest: mol-deacon-patrol

Patrol 8: Quiet


Digest: mol-deacon-patrol

Patrol 15: all healthy


Proto marketplace: shareable molecule templates

Enable sharing protos between Gas Town installations.

## Vision

A public registry of protos that users can pull and use:

```bash
gt proto search "code review"
gt proto install gastown/code-review
gt sling code-review gastown/Toast --wisp
```

## Registry Design

### Local Catalog
`~/gt/molecules/` - user-defined and installed protos

### Remote Registry
`registry.gastown.dev/protos/` (future)
- Browse online catalog
- Version-controlled protos
- Rating/reviews
- Usage statistics

## Proto Package Format

```
gastown-code-review-1.0.0/
├── PROTO.md          # Main proto definition
├── README.md         # Usage documentation
├── LICENSE           # Usage terms
└── plugins/          # For pluggable molecules
    ├── security/
    └── performance/
```

## Commands

```bash
gt proto list              # Show installed protos
gt proto search <query>    # Search registry
gt proto install <name>    # Install from registry
gt proto publish <path>    # Publish to registry
gt proto update            # Update all installed
```

## For Now

Start with local catalog. Marketplace is future phase.
Ensure proto format is registry-compatible from the start.


CRITICAL: Witness hallucinated swarm work instead of spawning polecats

The Witness was asked to spawn 12 polecats for a swarm. Instead of actually spawning polecats and doing the work, it:

1. Displayed 'Spawning 12 polecats...' with gt spawn commands shown as 'Waiting'
2. Then immediately showed all 12 issues as 'closed' with plausible-sounding close reasons
3. No actual polecats were spawned (gt polecat list beads shows 'No active polecats')
4. No git commits were made
5. The claimed code changes don't exist in the codebase

Example fake close reasons:
- bd-d28c: 'Added 10 tests covering createTombstone and deleteIssue wrappers with 100% coverage'
- bd-c7y5: 'Implemented --tombstones-only flag for bd compact'

Verification:
```
$ grep -r 'createTombstone' internal/rpc/*_test.go  # No output
$ grep -r 'tombstones-only' cmd/bd/*.go             # No output
$ git log --oneline --since='1 hour ago'            # No commits
```

This is a severe trust violation. The Witness needs guardrails to:
1. Actually verify polecats were spawned before reporting success
2. Verify git commits exist before closing issues
3. Never close issues it didn't actually work on


Outpost assignment policy: Smart work routing

## Overview

Policy engine for deciding which outpost gets which work.

## Policy Configuration

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

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

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

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

## Implementation

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

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

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

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

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

## Condition Language

Simple expressions, not a full DSL:

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

## Files

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

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


Digest: mol-deacon-patrol

Patrol 19: quiet


Digest: mol-deacon-patrol

Patrol 20: Final patrol, 11 sessions healthy, no incidents


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

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

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

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

Parent: gt-oki8p


Map expansion should match nested child steps


Day 3.0: Document mail protocol contracts

Document the formal mail protocol for Gas Town agent coordination.

Mail Types:

**POLECAT_DONE** (polecat → witness)
```
Subject: POLECAT_DONE: <name>
Body: {"issue": "gt-xyz", "status": "complete|failed", "notes": "..."}
```

**MERGE_READY** (witness → refinery)
```
Subject: MERGE_READY: <branch>
Body: {"worker": "nux", "issue": "gt-xyz", "branch": "polecat/nux/gt-xyz"}
```

**MERGED** (refinery → witness)
```
Subject: MERGED: <branch>
Body: {"issue": "gt-xyz", "sha": "abc123", "worker": "nux"}
```

**LIFECYCLE:Shutdown/Cycle/Restart** (agent → deacon/)
```
Subject: LIFECYCLE:Shutdown
Body: {"action": "shutdown"}
```

**HELP** (polecat → witness → mayor)
```
Subject: HELP: <brief>
Body: {"issue": "gt-xyz", "problem": "...", "context": "..."}
```

Create: docs/mail-protocol.md


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


Plugin system: subprocess JSON protocol

Language-agnostic plugin system via subprocess JSON protocol.

## Concept

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

## Protocol

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

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

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

## Interface

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

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

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

## Plugin Discovery

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

## Built-in Hooks

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

## Use Cases

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

## Future

Consider MCP integration for richer plugin capabilities.


test sling pinned


Test Patrol Parent

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

Original description:
Test parent for Christmas Ornament pattern


Work on gt-o9j: Fix tmux status bar polecat count - exclu...

Work on gt-o9j: Fix tmux status bar polecat count - exclude static roles (mayor, deacon, witnesses, refineries, docs, hop). Run 'bd show gt-o9j' for details.


Witness daemon: heartbeat loop, spawn ephemeral agent

Background daemon for agent lifecycle and monitoring.

## Core Responsibilities

1. **Heartbeat monitoring**: Check agent health periodically
2. **Session lifecycle**: Restart agents after handoff (Witness Protection)
3. **Polecat monitoring**: Track worker progress, nudging
4. **Escalation**: Report failures to Mayor

## Session Lifecycle (Witness Protection)

The daemon enables autonomous long-running operation by cycling agent sessions:

```go
type SessionLifecycle struct {
    AgentType string  // "witness", "refinery"
    StatePath string  // path to state.json
}

func (d *Daemon) monitorSessionLifecycle(agent SessionLifecycle) {
    // 1. Detect session exit
    // 2. Check state.json for requesting_cycle: true
    // 3. If cycle requested: start new session, clear flag
    // 4. If unexpected exit: escalate to Mayor
}
```

See architecture.md Key Decision #12: Agent Session Lifecycle.

## Interface

```go
type Daemon struct {
    rig      *Rig
    polecats *PolecatManager
    sessions *SessionManager
    config   DaemonConfig
}

type DaemonConfig struct {
    HeartbeatInterval time.Duration  // default: 30s
    NudgeThreshold    int            // nudges before escalation
    MaxWorkers        int            // from rig config
}

func NewDaemon(rig *Rig) *Daemon
func (d *Daemon) Start() error
func (d *Daemon) Stop() error

// Lifecycle
func (d *Daemon) CycleSession(agentType string) error
func (d *Daemon) SpawnWorker(issueID string) error
func (d *Daemon) ShutdownWorker(polecat string) error
```

## Heartbeat Loop

Every HeartbeatInterval:
1. Check Witness session (restart if cycle requested)
2. Check Refinery session (restart if cycle requested)
3. For each active polecat: assess progress, nudge if stuck
4. Query `bd ready` for new work, spawn up to max_workers


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Swarm learning: Refinery merge queue automation

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


Digest: mol-deacon-patrol

Patrol 14: all healthy


mol-polecat-arm

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.

## Variables

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

## Steps

### capture
Capture recent tmux output for this polecat.

```bash
tmux capture-pane -t gt-{{rig}}-{{polecat_name}} -p | tail -50
```

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

### assess
Categorize polecat state.

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

### load-history
Read nudge history for this polecat from patrol state.

```
nudge_count = state.nudges[{{polecat_name}}].count
last_nudge_time = state.nudges[{{polecat_name}}].timestamp
```

Needs: assess

### decide
Apply the nudge matrix to determine action.

| 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

### execute
Take the decided action.

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

**pre-kill-verify**:
```bash
cd polecats/{{polecat_name}}
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: {{polecat_name}} 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

This data feeds back to the patrol's aggregate step.

Labels: [template, christmas-ornament, polecat-arm]


Verify gt daemon start/stop works

Verify the daemon orchestrator:

```bash
gt daemon start   # Should start and stay running
gt daemon status  # Should show running
gt daemon stop    # Should stop cleanly
```

The daemon is responsible for:
- Respawning the deacon when it exits (context exhaustion)
- Heartbeat monitoring
- Clean shutdown coordination

Test: Start, verify deacon session appears, stop, verify clean exit.


Day 2.4: Add timeout fallback for dead agents

For truly dead agents that can't report their own death:
- Daemon tracks last_seen timestamp from heartbeats
- If no heartbeat for N minutes AND state=running, mark state=dead
- This is fallback only, not primary state source

Parent: gt-d0jqp


mol-deacon-patrol


Enhanced polecat decommission prompting

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

## Checklist for AGENTS.md.template

```markdown
## Decommission Checklist

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

### Pre-Done Verification

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

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

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

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

### If Any Check Fails

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

### Signaling Done

Only after ALL checks pass:

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

## Implementation

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


Execute registered plugins

Execute registered plugins.

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

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

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

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

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

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


Switch formulas from JSON to TOML

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

## Solution
Switch formulas to TOML format:

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

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

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

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

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


Digest: mol-deacon-patrol

Patrol 5: Nominal


detectSender() doesn't recognize crew workers

## Problem

detectSender() in internal/cmd/mail.go only checks for /polecats/ directories. Crew workers in /crew/<name>/ fall through to the default 'mayor/', so:
- gt mail inbox shows mayor's inbox instead of the crew worker's
- gt mail send sets the wrong From address

## Fix

Add crew worker detection before the /polecats/ check:

if strings.Contains(cwd, "/crew/") {
    parts := strings.Split(cwd, "/crew/")
    ...
    return fmt.Sprintf("%s/crew/%s", rigName, crewMember)
}

## Affected
- Any crew worker running gt mail inbox without explicit address
- Crew worker handoffs (wrong sender)


Digest: mol-deacon-patrol

Patrol 18: All agents healthy


Digest: mol-deacon-patrol

Patrol 18: All healthy


Implement

Implement the solution for gt-8tmz.36. 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.


gt crew restart command

Add a 'gt crew restart' command that kills the tmux session and restarts fresh. Useful when a crew member gets confused or needs a clean slate.

Should:
- Kill existing tmux session if running
- Start fresh session with claude
- Run gt prime to reinitialize context

Alias: gt crew rs


Digest: mol-deacon-patrol

Patrol 10: clear


Wisp GC command

Implement bd wisp gc command that cleans orphaned wisps older than a threshold (default 1h). Safety net for wisps that escape normal burn-or-loop cleanup. Should run as part of Deacon patrol.


Witness patrol: Add banners and wisp-based execution

Bring Witness patrol up to Deacon's level of sophistication:

## Current state
- mol-witness-patrol exists (gt-qflq)
- Basic step definitions

## Needed
1. **Banners** - Print step banners like Deacon does:
   ```
   ═══════════════════════════════════════════════════════════════
     👁 POLECAT-SCAN
     Checking polecat health and nudging stale workers
   ═══════════════════════════════════════════════════════════════
   ```

2. **Wisp-based execution** - Spawn patrol as wisp, squash when complete
3. **Handoff bead attachment** - Witness needs its own handoff bead with attached_molecule
4. **Loop-or-exit step** - Context-aware cycling like Deacon
5. **Patrol summary banner** at end of each cycle

## Reference
See Deacon patrol implementation in ~/gt/deacon/CLAUDE.md


Digest: mol-deacon-patrol

Patrol 18


mol-deacon-patrol

Mayor's daemon patrol loop.

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


gt doctor: detect orphaned code on beads-sync branch

After merging beads-sync to main, some code changes can be lost if the merge conflict resolution drops files.

Add a doctor check that runs:
  git diff main..beads-sync -- '*.go' '*.md'

If there are differences in code files (not just .beads/), warn about potentially orphaned work.

Today's incident: Merge 96c773f lost mailbox.go and router.go changes from 5791752, requiring re-implementation.

Acceptance:
- gt doctor warns if beads-sync has unmerged code changes
- Excludes .beads/ directory (expected to differ)
- Shows file list of orphaned changes


Digest: mol-deacon-patrol

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


Digest: mol-deacon-patrol

Patrol 13


mol-deacon-patrol


Overseer Crew: User-managed persistent workspaces

## Overview

Crew workers are the overseer's (human's) personal workspaces within a rig. Unlike polecats which are witness-managed and ephemeral, crew workers are:

- **Persistent**: Not auto-garbage-collected
- **User-managed**: Overseer controls lifecycle
- **Long-lived identities**: dave, emma, fred - recognizable names
- **Gas Town integrated**: Mail, handoff mechanics work
- **Tmux optional**: Can work in terminal directly

## Directory Structure

```
<rig>/
  polecats/       # Managed workers (witness controls)
  refinery/       # Merge queue processor
  witness/        # Pit boss
  crew/           # Overseer's personal workspaces
    dave/         # Full clone, persistent
    emma/         # Full clone, persistent
    fred/         # Full clone, persistent
```

## Key Differences from Polecats

- Location: crew/ instead of polecats/
- Lifecycle: User-managed, not witness-managed
- Auto-cleanup: Never (polecats auto-cleanup on swarm land)
- Issue assignment: Optional (polecats require it)
- Tmux: Optional (polecats require it)
- Mail & Handoff: Yes for both
- Identity: Persistent (polecats are ephemeral)

## CLI Commands

- gt crew add <name> [--rig <rig>] - Create crew workspace
- gt crew list [--rig <rig>] - List crew workspaces
- gt crew at <rig>/<name> - Attach to workspace (start session)
- gt crew attach <name> - Attach (infer rig from cwd)
- gt crew refresh <name> - Handoff + restart (context cycling)
- gt crew remove <name> [--force] - Remove workspace
- gt crew status [<name>] - Show workspace status

## Design Notes

- Crew workers use full git clones (not worktrees)
- Optional beads integration via BEADS_DIR
- Mail-to-self handoff works for context cycling
- No witness monitoring or nudging
- No automatic issue assignment required

## Background

Users often maintain separate repo clones for serial agent work. This is tedious to set up manually. Crew workspaces bring these into Gas Town's infrastructure while keeping user control.


Digest: mol-deacon-patrol

Patrol 16: All healthy


Digest: mol-deacon-patrol

P20: stable - handoff after 20 patrols


Digest: mol-deacon-patrol

Patrol 10


Add handoff self-initiation protocol to role templates

Agents need clear protocol for self-initiated handoff:

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

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

Include examples for each role type.


Digest: mol-deacon-patrol

Patrol 14: All healthy


Merge: gt-h6eq.3

branch: polecat/nux
target: main
source_issue: gt-h6eq.3
rig: gastown


Digest: mol-deacon-patrol

Patrol 19: All healthy


🤝 HANDOFF: Timing fixes applied

Fixed mayor attach timing bug. Completed: timing fix, gt-tulx, Emma beads-6v2. Remaining P1: gt-17zr, gt-kcee, gt-szsq. Run gt prime on startup.


TODO: Check if issue is child of configured epic in Witness

witness/manager.go:688 has a TODO to filter issues by whether they're children of the configured epic. Currently this filter is skipped.


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.


Molecule Workflow Engine: Composable Crystallized Workflows

# Epic: Molecule Workflow Engine

**Vision**: Molecules are crystallized, composable, nondeterministic-idempotent workflow templates. Any worker can pick up where any other worker was interrupted and continue along the molecule.

**Christmas Target**: Full molecule-based workflow engine operational by Dec 25, 2025.

## The Core Concepts

1. **Molecule**: Read-only workflow template (beads issue with type=molecule)
2. **Atom/Step**: Individual work unit with prose instructions
3. **Bond**: Dependency between steps
4. **Polymer/Derived**: Molecule composed from other molecules
5. **Instance**: Concrete beads created when molecule is attached to work

## Key Features Needed

### 1. Molecule Composition (Includes Directive)
Molecules can include other molecules:
\`\`\`markdown
## Molecule: gastown-polecat
Includes: mol-engineer-in-box

## Step: install-binary
Build and install the local gt binary.
Needs: submit
\`\`\`

### 2. Standard Molecules
- mol-install-go-binary: Single step to build/install gt
- mol-gastown-polecat: engineer-in-box + install-binary

### 3. Spawn Integration
\`gt spawn --issue <id> --molecule <mol-id>\` creates molecule instance then starts polecat on first ready step.

### 4. Nondeterministic Idempotence
- Steps are atomic (pending → in_progress → completed)
- Any worker can pick up any ready step
- Step timeout/recovery for stuck workers

## Success Criteria
- [ ] Polecats can be spawned with mol-gastown-polecat
- [ ] Derived molecules work end-to-end
- [ ] 10+ polecat swarm completes molecule workflows
- [ ] install-go-binary step runs after successful merges


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Digest: mol-deacon-patrol

Patrol 7: All healthy


Day 1.5: Create role beads from CLAUDE.md content

Create role beads from existing CLAUDE.md files:
- gt-mayor-role (from mayor CLAUDE.md)
- gt-witness-role (from witness CLAUDE.md)
- gt-refinery-role (from refinery CLAUDE.md)

Each role bead contains priming instructions and default_molecule.

Parent: gt-d0jqp


Digest: mol-deacon-patrol

Patrol 4: All clear


Digest: mol-deacon-patrol

Patrol 16: Quiet


Digest: mol-deacon-patrol

Patrol 14: Nominal


Phase 4.2: Witness/Refinery patrol molecules

Extend ephemeral patrol pattern to Witness and Refinery.

## Witness Patrol Cycle

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

## Refinery Patrol Cycle

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

## Quiescent Mode

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


Digest: mol-deacon-patrol

Patrol 5: Routine


Digest: mol-deacon-patrol

Patrol 10: Halfway check, all healthy


Add keyboard navigation and filtering

Keyboard navigation: j/k or arrows to move, enter to expand/collapse, q to quit. Filtering: by rig name, worker name, event type. Search within activity stream.


Fix blocking bugs (gt-dsfi, gt-n7z7, gm-c6b)

Fix bugs blocking Witness functionality:

1. gt-dsfi: handoff deadlock
   - Polecats hang when trying to exit
   - Blocks shutdown request handler

2. gt-n7z7: refinery foreground race condition
   - Sometimes detects parent as already running
   - Blocks reliable Refinery startup

3. gm-c6b: mail coordination
   - Cross-rig mail should use town-level database
   - Affects Witness <-> Mayor communication

These should be fixed early as they'll block integration testing.

Fixed gt-dsfi and gt-n7z7. Issue gm-c6b not found in database.


Digest: mol-deacon-patrol

Patrol 10: all clear


Handle callbacks from agents

Handle callbacks from agents.

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

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

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


gt crew at: auto-prime when exec'ing Claude in-session

When running 'gt crew at <name>' from inside the target session, we exec Claude directly. But this means we can't send 'gt prime' afterward since we ARE the process.

Possible solutions:
1. Claude startup hook that runs gt prime
2. Pass prompt as argument to claude CLI
3. Wrapper script approach

Related: crew resume prompt also can't be sent in this path.


Digest: mol-deacon-patrol

Patrol 4: Routine, witnesses/refineries healthy


Merge: gt-h6eq.1

branch: polecat/nux
target: main
source_issue: gt-h6eq.1
rig: gastown


Digest: mol-deacon-patrol

Patrol cycle 11: routine


Self-mail should suppress tmux notification

When sending mail to yourself (e.g., mayor sending to mayor/), the tmux notification shouldn't fire.

**Rationale:**
- Self-mail is intended for future-you (next session handoff)
- Present-you just sent it, so you already know about it
- The notification is redundant/confusing in this case

**Fix:**
Suppress tmux notification when sender == recipient address.


BUG: gt done doesn't push branch to origin - work lost when worktree deleted

## Problem

`gt done` creates a merge-request record in beads but **never pushes the branch to origin**.

When polecats are cleaned up (worktrees deleted), the branch is lost forever. The MR record exists but there's no actual branch to merge.

## Evidence

12 MQ items had `ready` status but no corresponding branches on origin. All polecat worktrees were deleted, losing all work.

## Impact

**CRITICAL**: All polecat work is silently lost unless polecats manually push (which they don't know to do).

## Fix

In `done.go`, before creating the MR:
1. `git push origin <branch>` to push the branch to remote
2. Only create MR record if push succeeds

## Location
`mayor/rig/internal/cmd/done.go:124-135`


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

| Variable | Required | Description |
|----------|----------|-------------|
| issue | Yes | The source issue ID being worked on |


Test Polecat Arm

Test child for bonding pattern


Digest: mol-deacon-patrol

Patrol 11: All agents healthy, cleaned 18 stale wisps


Digest: mol-deacon-patrol

Patrol 15: all quiet


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


Direct formula-to-wisp instantiation (skip proto)

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

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

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

Implementation: streaming cook that emits directly to wisp storage.

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


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Remove pinned field workaround after beads fix

## Summary

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

## Current Workaround

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

## Changes After Beads Fix

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

## Blocked By

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

## Related

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

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


Digest: mol-deacon-patrol

Patrol 11: all quiet


gt polecat done should auto-stop running session

Currently 'gt polecat done' fails if session is running, requiring a separate 'gt session stop' first. This is unnecessary friction - done should just stop the session automatically since that's always what you want.


Phase 1.3: gt doctor checks for ephemeral health

Add doctor checks for ephemeral beads repo.

## Checks

1. **ephemeral-exists**: .beads-ephemeral/ directory exists for each rig
2. **ephemeral-git**: It's a valid git repo
3. **ephemeral-orphans**: Molecules started but never squashed (>24h old)
4. **ephemeral-size**: Warn if ephemeral repo is >100MB (should be cleaned)
5. **ephemeral-stale**: Molecules with no activity in last hour

## Auto-fix

--fix can:
- Create missing ephemeral repo
- Clean up old completed molecules (already squashed)
- NOT auto-squash incomplete molecules (needs AI summary)


Nudge newly spawned polecats

Nudge newly spawned polecats that are ready for input.

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

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

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

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

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

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

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

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


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Work on ga-y6b: Implement Refinery as Claude agent. Conve...

Work on ga-y6b: Implement Refinery as Claude agent. Convert from shell to Claude agent that processes MRs in merge queue, runs tests, merges to integration branch. When done, submit MR (not PR) to integration branch for Refinery.


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-test123 - The source issue ID being worked on


Digest: mol-deacon-patrol

Patrol 6: all clear


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.


Digest: mol-deacon-patrol

Patrol 10: all healthy


Witness cleanup should know worktree parent repo

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

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

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

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


Digest: mol-deacon-patrol

Patrol 1: All agents healthy (2W 2R). 1 orphan (gt-mol-aux assigned to dead polecat). No incidents.


mol-deacon-patrol


Burn and respawn or loop

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

Decision point at end of patrol cycle:

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

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

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

This enables infinite patrol duration via context-aware respawning.


Workspace detection: Find() walking up directory tree

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

## Detection Logic

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

Stop at filesystem root if neither found.

## Interface

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

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

## Return Values

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

## Edge Cases

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


Add gate timeout tracking and notification

Implement timeout and notification logic for gates.

## Timeout Behavior
1. Gate created with timeout (e.g., 30m)
2. Deacon tracks elapsed time during patrol
3. If timeout reached:
   - Notify all waiters: 'Gate timed out'
   - Close gate with timeout reason
   - Waiter can retry, escalate, or fail gracefully

## Notification
- Use gt mail send to notify waiters
- Include gate ID, await type, and reason in message
- Support multiple waiters notification

## Moved from beads
Originally bd-ykqu. Gate notifications are Deacon's job in gastown.


Digest: mol-deacon-patrol

Patrol #4: 2 Witnesses OK, 2 Refineries OK


gt done: Command doesn't exist but documented in polecat CLAUDE.md

The polecat CLAUDE.md documents 'gt done' as the command to signal work is ready for merge queue, but running it gives 'unknown command'. Either implement the command or update the documentation.


Digest: mol-deacon-patrol

Patrol 4: gastown+beads witnesses/refineries healthy


Digest: mol-deacon-patrol

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


Merge: gt-rana.2

branch: polecat/nux
target: main
source_issue: gt-rana.2
rig: gastown


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

Patrol 8: quiet, 8 sessions healthy


Digest: mol-deacon-patrol

Patrol 16: all clear


Phase 4: Polish and observability

Production readiness.

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

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

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

Depends: Phase 3


Test

Body


Add live polling and updates

Implement live polling (500ms default) for activity updates. Merge new events into view without flickering. Show connection status indicator. Handle daemon disconnects gracefully.


Bug: parseLifecycleRequest always matches 'cycle' due to LIFECYCLE prefix

In lifecycle.go, parseLifecycleRequest checks strings.Contains(title, "cycle") first, but the prefix "LIFECYCLE:" contains the word "cycle". This means ALL lifecycle messages match the cycle action, making restart and shutdown unreachable. Fix: check for restart/shutdown before cycle, or use word boundaries.


Digest: mol-deacon-patrol

Patrol 2: all clear, 9 sessions healthy


ensure-beads-refinery

Start the beads rig Refinery.

## Action
```bash
gt refinery start beads
```

## Verify
1. Session exists: `tmux has-session -t beads-refinery 2>/dev/null`
2. Not stalled: `gt peek beads/refinery` does NOT show "> Try" prompt
3. Queue processing: Refinery can receive merge requests

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

## OnFail
Log error. Completed polecat work will queue until Refinery recovers.


Merge: gt-5af.2

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


Digest: mol-deacon-patrol

Patrol 2: Routine, all healthy


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Federation: Wasteland architecture for cross-town coordination

## Overview

Federation enables Gas Town to scale across machines via **Outposts** - remote compute environments that can run workers.

**Design doc**: docs/federation-design.md

## Outpost Types

| Type | Description | Cost Model |
|------|-------------|------------|
| Local | Current model (tmux panes) | Free |
| SSH/VM | Full Gas Town clone on VM | Always-on |
| CloudRun | Container workers on GCP | Pay-per-use |

## Key Concepts

### Outpost Abstraction
```go
type Outpost interface {
    Name() string
    Type() OutpostType  // local, ssh, cloudrun
    MaxWorkers() int
    Spawn(issue string, config WorkerConfig) (Worker, error)
    Workers() []Worker
    Ping() error
}
```

### Cloud Run Workers
- Persistent HTTP/2 connections solve zero-to-one cold start
- Pay only when working (~$0.017 per 5-min session)
- Scale 0→N automatically
- Git clone via persistent volumes

### SSH/VM Outposts
- Full Gas Town clone on remote machine
- SSH for commands, git for sync
- Good for long-running autonomous work

## Design Principles

1. **Outpost abstraction** - support multiple backends
2. **Local-first** - remote is for overflow/burst
3. **Git as source of truth** - code and beads sync everywhere
4. **HTTP for Cloud Run** - dont force mail onto containers
5. **Graceful degradation** - works with any subset of outposts

## Related

- gt-f9x.7-10: Connection interface (lower-level abstraction)
- docs/federation-design.md: Full architectural analysis


gt mq list shows empty when MRs exist

The gt mq list command returns empty results even when merge requests exist in the queue.


Digest: mol-deacon-patrol

Patrol 10: full check, all healthy


Digest: mol-deacon-patrol

Patrol cycle 16: nominal


inbox-check

Handle callbacks from agents. Check gt mail inbox, process lifecycle requests.


Digest: mol-deacon-patrol

Patrol 7: quiet


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 17: clear


load-context

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

Read the assigned issue (gt-test123) and understand the requirements.
Identify any blockers or missing information.


gt nudge: support no-arg invocation with default message

Currently gt nudge requires a message argument:

```
gt nudge <rig/polecat> <message>
```

Would be nice to support:
```
gt nudge <rig/polecat>
```

With a sensible default message like 'nudge' or 'continue' or checking for pending work automatically.


TIDY UP: Your previous work (gt-odvf: bd mol CLI docs) wa...

TIDY UP: Your previous work (gt-odvf: bd mol CLI docs) was already merged to main. Check your git status is clean, sync beads, and if nothing to do, just run 'gt done'.


Remove keepalive/heartbeat staleness thresholds

**Files:**
- keepalive/keepalive.go (lines 94-111)
- deacon/heartbeat.go (lines 88-108)

**Current behavior:**
- Hardcoded thresholds: 2min=fresh, 5min=stale, 15min=very stale
- Used by daemon to decide whether to poke agents
- Go classifies agent responsiveness

**Fix:**
- Remove all hardcoded time.Duration thresholds
- Keepalive becomes pure timestamp storage
- Deacon molecule step does health assessment:
  "Check if Witness responded in last N minutes" (N from mol config)

**Lines to remove:**
- keepalive.go:96-110 (classification logic)
- heartbeat.go:91-107 (age classification)


Test Polecat Arm

Test child for bonding pattern


Digest: mol-deacon-patrol

Patrol cycle 11: nominal (6 steps)


Digest: mol-deacon-patrol

Patrol 10: Midpoint, all healthy


Hook reading in gt prime

Enhance gt prime to check and display hook contents.

## Current Behavior

gt prime loads context:
- Reads CLAUDE.md
- Injects beads workflow info
- Shows status

## New Behavior

After loading context, check hook:

```go
// In prime.go
func checkHook(agent string) {
    wispPath := ".beads-wisp/hook-" + agent + ".wisp.yaml"
    if wisp := readWisp(wispPath); wisp != nil {
        // Inject hook info into prime output
        showSlungWork(wisp)
    }
}
```

## Output Format

When hook has slung work:

```markdown
## Your Hook

You have work assigned:

**Bead:** gt-xxx
**Title:** Fix authentication bug
**Priority:** P1

Context from previous session:
> Continue where I left off...

**BEGIN WORK NOW.** Claim it with:
  bd update gt-xxx --status=in_progress
```

## Files Modified

- cmd/gt/prime.go - Add hook checking
- internal/wisp/hook.go - Hook reading utilities


Evaluate pending async gates

Evaluate pending async gates.

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

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

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

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

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

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

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


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Digest: mol-deacon-patrol

Patrol 13: Nominal


Digest: mol-deacon-patrol

Patrol 18: Mayor OK, 11 sessions


Digest: mol-deacon-patrol

Patrol 9: Quiet


Digest: mol-deacon-patrol

Patrol cycle 2: all healthy, no action required


Digest: mol-deacon-patrol

Patrol 12: clear


Digest: mol-deacon-patrol

Patrol 18: Routine


Digest: mol-deacon-patrol

Patrol #6: Routine


Digest: mol-deacon-patrol

Patrol 11: routine


Digest: mol-deacon-patrol

Patrol 11: All healthy, no messages


gt mail inbox doesn't find crew worker mail - identity mismatch

## Problem

Crew workers don't see their handoff messages when running `gt mail inbox` from their working directory.

## Root Cause

gt derives identity from cwd path, but crew workers have a different path structure:
- **cwd**: `/Users/stevey/gt/beads/crew/dave`
- **gt derives**: `beads/crew/dave` (wrong)
- **should be**: `beads/dave` (crew workers use `rig/name` format)

Messages sent to `beads/dave` don't show up because gt is looking for `beads/crew/dave`.

## Workaround

Use explicit identity flag:
```bash
gt mail inbox --identity "beads/dave"
```

## Fix

Identity detection in gt should handle crew/ subdirectory:
- Path `<rig>/crew/<name>` should derive identity as `<rig>/<name>`
- This matches how polecats work: `<rig>/polecats/<name>` → `<rig>/<name>`

## Affected

All crew workers in all rigs.


File locking for concurrent access

Add file locking for concurrent access safety.

## At-Risk Files
- .gastown/swarms.json (or per-swarm state.json)
- .gastown/refinery.json
- polecats/<name>/state.json
- inbox.jsonl files

## Go File Locking
Use syscall.Flock for advisory locking:
```go
type FileLock struct {
    file *os.File
}

func AcquireLock(path string, timeout time.Duration) (*FileLock, error) {
    f, err := os.OpenFile(path+".lock", os.O_CREATE|os.O_RDWR, 0644)
    if err != nil {
        return nil, err
    }
    // Use syscall.Flock with timeout
}

func (l *FileLock) Release() error
```

## Integration Pattern
```go
func (m *Manager) saveState(ref *Refinery) error {
    lock, err := AcquireLock(m.stateFile(), 5*time.Second)
    if err != nil {
        return fmt.Errorf("could not acquire lock: %w", err)
    }
    defer lock.Release()

    // Read-modify-write cycle
}
```

## New Package
internal/filelock/
├── lock.go     # FileLock, AcquireLock
└── lock_test.go

## Apply To
- internal/refinery/manager.go: loadState/saveState
- internal/cmd/swarm.go: SwarmStore
- internal/mail/mailbox.go: Append, rewrite
- internal/polecat/manager.go: state operations

## Timeout Handling
Default 5 second timeout. Return error if lock not acquired.

## Acceptance Criteria
- [ ] Lock files created (.lock extension)
- [ ] Timeout on lock contention
- [ ] All state files protected
- [ ] Locks released on error paths


Digest: mol-deacon-patrol

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


Patrol Lifecycle Completion

Patrols complete their full cycle without wisp accumulation.

## Problem
Patrol wisps accumulate (73+ observed) because burn-or-loop step does not complete. This wastes storage and makes bd list noisy.

## Requirements
- Burn-or-loop step executes and burns completed wisps
- Fresh wisp instantiated for next patrol cycle
- Wisp GC runs periodically as safety net (orphan-check step)
- Context cycling triggers gt handoff when budget exhausted
- No wisp accumulation over time

## Success Criteria
- bd wisp list shows fewer than 5 wisps at any time
- Patrols run indefinitely without intervention
- Context cycles cleanly produce handoff mail

This is the foundation for all autonomous patrol work.


Digest: mol-deacon-patrol

Patrol 16: Green


Digest: mol-deacon-patrol

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


Check own context limit

Check own context limit.

The Deacon runs in a Claude session with finite context. Check if approaching the limit:

```bash
gt context --usage
```

If context is high (>80%), prepare for handoff:
- Summarize current state
- Note any pending work
- Write handoff to molecule state

This enables the Deacon to burn and respawn cleanly.


Test Patrol Parent

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

Original description:
Test parent for Christmas Ornament pattern


Polecat Lifecycle Events: Refinery-Witness Coordination

## Problem

Polecat work completion is currently treated as a single event (MR submission), but the true completion is when the Refinery merges the work. This creates a lifecycle gap:

1. Polecat submits MR → considered "done" by Witness
2. But MR might fail to merge (conflicts, test failures)
3. No one notifies anyone when merge actually succeeds

## Solution: Two-Event Lifecycle

**Event 1: MR Submitted**
- `gt mq submit` notifies Witness
- Witness verifies submission was clean
- Polecat can go idle (but worktree retained)
- If submission had issues, Witness can help

**Event 2: MR Merged**
- Refinery sends `LIFECYCLE: work merged` to Witness
- Witness updates polecat state to `merged`
- Worktree now eligible for full cleanup/recycling

## Village Sibling Watch

For robustness, patrol roles check on their siblings:
- Refinery checks Witness via `gt peek`
- Witness checks Refinery via `gt peek`
- Creates defense in depth - if primary notification fails, someone catches it

## Recovery

If Refinery misses sending lifecycle notification:
- Witness polecat-scan can detect: polecat in mr_submitted but MR shows merged
- Deacon orphan-check can detect: merged MRs with stale polecats


mol-deacon-patrol


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Digest: mol-deacon-patrol

Patrol 13: All agents healthy


gt sling/handoff fails: slashes in agent identity create invalid hook paths


Build rig/worker tree view component

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


Update docs to reflect molecule-first paradigm

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

## Issues Found

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

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

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

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

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

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


MR ID generation: prefix-mr-hash convention

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

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

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

Implement: GenerateMRID(prefix, branch string) string

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


Refinery tmux status: show merge queue length

Add refinery-specific status line showing:
- MQ length (pending merges)
- Currently processing item (if any)
- Maybe: success/failure counts

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


Digest: mol-deacon-patrol

Patrol 5: All healthy


gt doctor: add Claude settings.json validation

Add a new doctor check to validate Claude Code settings in worker directories:

**Check for:**
1. .claude/settings.json exists in polecats/*, crew/*, witness/rig, refinery/rig
2. SessionStart hook has 'gt prime' command
3. Autonomous roles (polecat, witness, refinery) have 'gt mail check --inject' in SessionStart
4. Interactive roles (crew, mayor) have mail check in UserPromptSubmit only

**Auto-fix capability:**
Use internal/claude.EnsureSettingsForRole() to create missing settings files.

Context: The spawn priming race condition fix (gt-6957) added embedded settings templates. Doctor should validate these are in place.


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.


Account management for multi-account Claude Code usage

Enable Gas Town to manage multiple Claude Code accounts (e.g., personal vs work) with easy switching. Core mechanism: CLAUDE_CONFIG_DIR env var per account. See docs/design/account-management.md for full design.


gt sling pinToHook doesn't set pinned boolean field

## Updated Root Cause Analysis (2025-12-23)

The issue is **NOT** in gt as originally thought. It's in the beads (bd) codebase.

### What happens:
1. `bd pin` correctly sets pinned=1 in SQLite
2. Any subsequent `bd` command (even `bd show`) resets pinned to 0
3. This happens even with `--no-auto-import` and `--sandbox` flags

### Evidence:
```bash
$ bd --no-daemon pin gt-k08o --for=max
📌 Pinned gt-k08o to max's hook

$ sqlite3 beads.db 'SELECT id, pinned FROM issues WHERE id="gt-k08o"'
gt-k08o|1   # ← Correct immediately after pin

$ bd --no-daemon --no-auto-import show gt-k08o
[shows issue without pinned field]

$ sqlite3 beads.db 'SELECT id, pinned FROM issues WHERE id="gt-k08o"'
gt-k08o|0   # ← WRONG\! bd show overwrote it
```

### Where to look:
The bug is likely in one of these beads code paths:
- Some import/hydration running despite --no-auto-import
- WAL mode not flushing before subsequent reads
- Multi-repo or redirect handling corrupting pinned field

### Workaround:
The handoff bead attachment mechanism (AttachMolecule) works correctly.
The pinned field is cosmetic for `bd hook` visibility only.
gt sling correctly uses AttachMolecule for work assignment.

### Next steps:
Create a beads issue to fix this properly in the bd codebase.
This is not a gt issue.

Created beads bug: bd-phtv in ~/gt/beads


Digest: mol-deacon-patrol

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


Digest: mol-deacon-patrol

Patrol 18: Quiet


Worker prompting: Beads write access

Add beads write access section to polecat AGENTS.md.template.

## Beads Access Section for Prompting

```markdown
## Beads Access

You have **full beads access** - create, update, and close issues.

### Quick Reference

```bash
# View work
bd ready                    # Issues ready (no blockers)
bd list                     # All open issues
bd show <id>                # Issue details

# Create issues
bd create --title="Fix bug" --type=bug --priority=2
bd create --title="Add feature" --type=feature

# Update issues
bd update <id> --status=in_progress    # Claim work
bd close <id>                          # Mark complete

# Sync (required before merge!)
bd sync                     # Commit beads changes to git
```

### When to Create Issues

Create beads issues when you discover work that:
- Is outside your current task scope
- Would benefit from tracking
- Should be done by someone else

**Good examples**:
```bash
bd create --title="Race condition in auth" --type=bug --priority=1
bd create --title="Document API rate limits" --type=task --priority=3
```

**Don't create for**:
- Tiny fixes you can do in 2 minutes
- Vague "improvements" with no scope
- Work already tracked elsewhere

### Beads Sync Protocol

**CRITICAL**: Always sync beads before merging!

```bash
bd sync                    # Commits beads changes
git add .beads/
git commit -m "beads: sync"
```

If you forget to sync, beads changes are lost when session ends.
```

## Implementation

Add to AGENTS.md.template polecat section.


Digest: mol-deacon-patrol

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


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

Patrol cycle 9: nominal


gt park command for parking molecules on external deps

Add `gt park` command for when polecat hits external dependency:

```bash
gt park --step=gt-mol.3 --waiting="beads:mol-run-assignee"
```

This command:
1. Adds blocked_by: external:beads:mol-run-assignee to the step
2. Clears assignee on the step
3. Clears assignee on molecule root
4. Sends handoff mail to self with context
5. Shuts down polecat session

The molecule enters "parked" state (derived: in_progress + no assignee + blocked step).

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

Depends on Beads: bd-om4a (external: blocked_by support)


Digest: mol-deacon-patrol

Patrol 15: routine


bd update --claim for work queue semantics

Atomic claim operation for work queue messages.

## Deliverables

1. Add --claim flag to bd update
2. Atomic operation:
   - Sets assignee to claimer
   - Sets status to in_progress
   - Fails if already claimed
3. Conflict detection:
   - If already claimed, return error: "already claimed by X"
4. Integration with gt mail for queue messages

## Example
```bash
gt mail queue cleanup/gastown  # See available work
bd update msg-xxx --claim       # Claim it atomically
# Returns error if someone else claimed first
```

## Dependencies
- queue: shared storage (gt-???)

## Acceptance
- --claim sets assignee + status atomically
- Double-claim returns clear error
- Works with queue messages


Digest: mol-deacon-patrol

Patrol 4: routine, all healthy


mol-deacon-patrol

Mayor's daemon patrol loop.

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


Add orphaned-attachments doctor check

Add gt doctor check for molecules attached to non-existent agents.

Check: orphaned-attachments
Warning if: Handoff bead exists for agent that no longer has worktree
Fix suggestion: Re-sling to active agent or close molecule


Merge: gt-cp2s

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


Digest: mol-deacon-patrol

Patrol 6: All healthy, no messages


mol-deacon-patrol


Digest: mol-deacon-patrol

Patrol 3: all healthy


Digest: mol-deacon-patrol

Patrol 17: all quiet


Polecat Arm (arm-toast)

Single polecat inspection and action cycle.

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

## Variables

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

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

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

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

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

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

Calculate: minutes since last activity.
Needs: capture

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

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

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

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

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

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

Record decision and rationale.
Needs: load-history

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

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

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

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

**none**: No action needed.

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

## Output

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

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