Replaceable notifications via Claude Code queue

Leverage Claude Code's ability to replace queued text for notifications that supersede previous ones.

## Problem

If daemon sends 10 heartbeats while agent is busy, agent returns to see 10 stacked messages. Wasteful and noisy.

## Solution

Use Claude Code's queue replacement for:
- Heartbeat messages (only latest matters)
- Status updates that supersede previous
- Progress notifications

## Implementation

Notifications get a 'slot' identifier. New notification in same slot replaces old one:
- Slot: 'heartbeat' → only one heartbeat queued at a time
- Slot: 'status-<rig>' → latest status per rig
- No slot → stacks normally (for unique messages)

## Research Needed

- How does Claude Code expose queue replacement?
- tmux send-keys behavior with pending input
- Alternative: clear + resend pattern


Test2

Testing gt mail


Layered context onboarding pattern

Pattern from handoff discussion:

## Pattern: Layered Context Onboarding

Town CLAUDE.md (user/org) -> Rig CLAUDE.md (project) -> Role priming

## Ultra-compressed HOP for workers (no reveal)

- Permanent record: All work tracked. Outcomes matter.
- Quality gates: Molecule steps exist for a reason.
- Attribution: Completions build your track record.
- Handoff clean: Leave state any worker can continue.

## Recommendation

Create Town @AGENTS.md for shared worker context that all workers see.
This provides common behavioral guidance without revealing full HOP context.


gt handoff: Deadlock bug in runHandoff

Running 'gt handoff' from a polecat causes a deadlock:

Stack trace shows:
- goroutine 1 [select (no cases)] in runHandoff
- File: internal/cmd/handoff.go:125

The command successfully sends the shutdown request but then hangs with 'fatal error: all goroutines are asleep - deadlock!'


Work assignment mail not received by polecat

When gt spawn sends a work assignment:

1. gt spawn says '✓ Work assignment sent'
2. Polecat runs 'gt mail inbox'
3. Shows '0 messages, 0 unread'

The work assignment mail never arrived at the polecat's inbox.

## Observed in session
- Spawned polecat dementus on gt-th7
- Polecat checked inbox: empty
- Polecat couldn't find issue (separate sync bug)

## Possible causes
- Mail routing issue for polecat addresses
- gt spawn not actually sending mail
- Mail sent to wrong address format

This was likely caused by stale beads in old polecats. With gt-9nf (fresh polecats), polecats now use shared rig beads via redirect file, eliminating sync issues. Mail routing uses town beads correctly.

Fixed by gt-9nf: fresh polecats use shared beads, mail routes correctly to town beads


Molecule converge: iterate until AI says done

Work isn't 'done' until AI is confident it's done. Fixes the 85% problem.

**From VC**: internal/iterative/converge.go - 3-7 iterations with AI convergence detection. ~600 lines.
Target: 20%+ more issues discovered. Observed: ~25% average improvement.

**Gas Town implementation**: Molecule converge config:
```yaml
converge:
  strategy: ai
  min_iterations: 3
  max_iterations: 7
  confidence: 0.85
```

Polecat iterates, AI checks convergence. Stop when AI is confident or max reached.

**Value**: Raw agents get to ~85% and stop. Iteration catches the remaining 15%.

**VC metrics**: Convergence rate >70%, mean 4-5 iterations for complex work.


Digest: mol-deacon-patrol

Old test patrol cycle - cleanup


Work on ga-lue: Implement Witness as Claude agent. Conver...

Work on ga-lue: Implement Witness as Claude agent. Convert from shell script to Claude agent that monitors polecats, nudges idle ones, handles escalations. When done, submit MR (not PR) to integration branch for Refinery.


Merge: gt-g44u.1

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


Define constants for magic strings

Several magic strings are hardcoded throughout the codebase:
- "mayor" appears in 20+ places as a path component
- "main" branch name appears in 20+ places
- "beads-sync" branch name in multiple files
- "rigs.json" filename

These should be constants in a central location for maintainability.

Created internal/constants/constants.go with centralized definitions for:
- Directory names (mayor, polecats, crew, etc.)
- File names (rigs.json, town.json, state.json)
- Git branches (main, beads-sync)
- Session names (gt-mayor, gt-deacon)
- Agent roles

Updated key files (rig_helpers.go, daemon.go, status.go) to use the new constants package. Full migration of all usages (~100 files) can be done incrementally.


P0: Multiple agents can claim same worker identity

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

## Fix Implemented

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

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

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

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

Implemented lock mechanism with prevention (gt prime), detection (gt agents check), and correction (gt doctor/agents fix)


Add gt crew rename command


Add molecule to update local go binary on push to main

When pushing to main branch, automatically rebuild and install the gt binary on the local machine so changes are immediately available.


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

Add a hidden alias for the bond command:

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

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

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

Low priority, just for fun.


Implement bd mol bond --ephemeral flag

Add --ephemeral flag to bd mol bond command to support ephemeral molecule bonding.

## Context
Phase 1.2 of Wisp Molecule Integration (gt-3x0z.2) discovered that this flag doesn't exist.

## Requirements

1. Add --ephemeral flag to mol bond command
2. When --ephemeral is set:
   - Mark spawned issues with wisp=true
   - Optionally write to separate .beads-ephemeral/ storage (Phase 2)

## Architecture Reference
See gastown architecture.md lines 487-491 for the full ephemeral storage design.

## Related
- gt-3x0z.2: Configure bd for ephemeral molecule bonding (closed - blocked on this)


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


Merge: gt-3x1.5

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


Daemon: verify requesting_cycle before kill

Per gt-gby spec, daemon should verify agent state shows requesting_cycle=true before killing session. Currently kills on any lifecycle request without verification.

Requires state.json in agent workspace with requesting_cycle field.


Molecule format bridge: convert embedded markdown to child issues

## Problem

Two molecule formats exist:
1. **Old (gastown builtin)**: Steps embedded as markdown in Description field, parsed by ParseMoleculeSteps()
2. **New (bd mol)**: Steps as child issues in proper beads DAG

The daemon's InstantiateMolecule() uses the old format. bd mol spawn uses the new format.

## Options

### Option A: Require child issues (new format)
- Update daemon to use bd mol spawn instead of InstantiateMolecule
- Deprecate embedded markdown format
- Pro: One format, simpler
- Con: Breaking change for existing molecules

### Option B: Build a bridge
- In InstantiateMolecule, detect format:
  - If Description has ## Step: markers → parse to child issues
  - If issue has children with template label → use directly
- Convert old format to new format on instantiation
- Pro: Backward compatible
- Con: More complexity

## Recommendation

Option B - build the bridge. On instantiation, convert markdown steps to child issues. This unifies execution while preserving authoring flexibility.


Merge: gt-3x1

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


Document merge queue architecture

Update docs/architecture.md with:

- Merge Queue section explaining Beads-native approach
- Engineer role (renamed from Refinery)
- Session restart protocol
- gt mq command reference
- Federation considerations for queue

Also update any references to .gastown/ → config/.


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


Town root .beads has schema mismatch with bd

The .beads directory at town root (/Users/stevey/gt/.beads) has an incompatible schema:

```
Error: failed to open database: failed to initialize schema: sqlite3: SQL logic error: no such column: thread_id
```

Meanwhile, gastown/.beads (symlinked to mayor/rig/.beads) works fine.

## Impact

- gt mail inbox fails at town root
- gt handoff sends mail to broken db
- Daemon can't check its inbox

## Options

1. Delete town root .beads/beads.db and let it recreate
2. Symlink town root .beads to gastown/.beads
3. Run schema migration on existing db

## Root Cause

Likely a beads version upgrade that added thread_id column, but the town root db was created before that.


Merge polecat/rictus: Add molecule phase lifecycle diagram

Branch: polecat/rictus

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

Closes: gt-c6zs

ORPHANED: Branch never pushed, worktree deleted


Overhaul gt mol to match bd mol chemistry interface

## The Sling: Unified Work Dispatch

This issue tracks the overhaul of `gt molecule` to align with chemistry metaphor and introduce the **Universal Gas Town Propulsion Principle**.

### The Propulsion Principle

> **If you find something on your hook, YOU RUN IT.**

This is the one rule that drives all Gas Town agents.

### The Sling Operation

`gt sling <thing> <target> [options]` - unified command for spawn + assign + pin.

See: `gastown/mayor/rig/docs/sling-design.md`

### Implementation Tasks

| Issue | Title | Priority |
|-------|-------|----------|
| gt-4ev4 | Implement gt sling command | P1 |
| gt-uym5 | Implement gt mol status command | P1 |
| gt-i4kq | Update templates for Propulsion Principle | P1 |
| gt-7hor | Document the Propulsion Principle | P2 |

### Command Changes

| Old | New |
|-----|-----|
| `gt molecule instantiate` | `gt sling` |
| `gt molecule attach` | `gt sling --force` |
| `gt molecule detach` | `gt mol burn` |
| `gt molecule progress` | `gt mol status` |
| `gt molecule list` | `gt mol catalog` |
| `gt spawn --molecule` | `gt sling` |

### Acceptance Criteria

- [ ] `gt sling` works for protos, issues, and epics
- [ ] `gt mol status` shows hook state
- [ ] Templates updated for propulsion principle
- [ ] Old commands deprecated with warnings
- [ ] Documentation complete


Plugin: Plan-to-Epic converter

## Purpose

Help users create beads epics from various planning inputs.

## Inputs
- Markdown task lists
- GitHub issues
- Linear/Jira exports
- Free-form descriptions
- Existing beads epics

## Output
- Beads epic with properly structured children
- Dependencies set for wave ordering
- Priorities assigned
- Ready for `gt spawn --epic <id>`

## Implementation Options

### Option A: CLI Tool
```bash
gt plan import --from github --repo owner/repo --label batch-candidate
gt plan import --from markdown tasks.md
gt plan structure <epic-id>  # analyze and add dependencies
```

### Option B: Plugin Agent
A plugin at `<rig>/plugins/plan-oracle/` that:
- Receives planning requests via mail
- Analyzes scope and requirements
- Creates structured beads epic
- Sets dependencies based on analysis

### Option C: Interactive Mode
```bash
gt plan create
# Walks through questions, creates epic interactively
```

## Axiom

As stated: 'The Planning phase should end in the creation of a workable Beads plan.'

This plugin bridges the gap between human planning and machine-executable work.

## Priority

P2 - Nice to have for MVP. Manual epic creation works for now.

## Note

No "swarm IDs" - output is just a beads epic with children. Workers process it independently.


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

Implemented status line functions for witness and refinery


Merge: gt-rana.2

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


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.

Implemented epic child filtering via isChildOfEpic() which checks if issue blocks the configured epic


VC Pattern Integration: Bring validated ideas to Gas Town

Analysis of ~/src/vc identified 6 validated patterns from the 2nd orchestrator attempt that map cleanly to Gas Town primitives. VC achieved 254 issues closed, 90.9% gate pass rate, and 24 successful missions.

Key insight: VC built ~4300 lines of Go for features that become ~65 lines of YAML + CLI flags in Gas Town's architecture.

Child tasks track each pattern to integrate.


mol-outpost-assign: Intelligent work routing to outposts

The federation design shows outposts.yaml with a static policy section. Simple preference ordering is fine as config, but intelligent work routing should be a molecule.

Current static approach:
```yaml
policy:
  default_preference: [local, gce-burst, cloudrun-burst]
```

Cases requiring cognition:
- "This is a long-running research task, route to VM not CloudRun"
- "This touches sensitive code, keep local"
- "These 5 issues are related, batch them to same outpost"
- "CloudRun cost is high today, prefer local even if slower"

## Molecule: outpost-assign
Intelligent work-to-outpost routing.

## Step: classify-work
Analyze the issue/work item:
- Expected duration (quick fix vs multi-hour)
- Resource requirements
- Sensitivity/security tier
- Related work (same epic?)

## Step: check-capacity
Query outpost status:
- Current load on each
- Cost accrued today
- Health status

## Step: select-outpost
Choose optimal outpost based on:
- Work classification
- Capacity/cost
- Policy constraints
Needs: classify-work, check-capacity

## Step: emit-assignment
Record decision in beads for audit.
Needs: select-outpost

## Notes
- This molecule is invoked by Mayor/Witness when spawning
- Simple cases can short-circuit to static policy
- Full analysis only for ambiguous cases


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


gt rig info: Show detailed rig information

Add 'gt rig info <rig>' command to show detailed rig status.

Should show:
- Rig path and git URL
- Active polecats with status
- Refinery status
- Witness status
- Recent activity
- Beads summary (open issues count)


Swarm learning: Beads database locality gap

## PGT Bug (GGT Design Already Correct)

Mayor created issues in `~/ai/mayor/rigs/beads/.beads/` but polecats use `~/ai/beads/.beads/`. Different databases = polecats can't see Mayor's beads.

**GGT Fix**: architecture.md already specifies:
- All agents use BEADS_DIR pointing to rig-level `.beads/`
- Lines 116-143: Beads Configuration for Multi-Agent
- Lines 573-586: Rig-Level Beads via BEADS_DIR

This is a PGT implementation gap, not a design issue. GGT spawn must set BEADS_DIR correctly.


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)


bd pin command fails: invalid field for update: pinned

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

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

Expected: Issue should be pinned.

This blocks gt mail send --pinned from working.

Fixed by adding pinned to allowedUpdateFields


Immediate daemon notification on lifecycle request

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

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

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

## Proposed Solutions

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

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

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

Recommend Option A for simplicity.

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


Per-rig beads repo configuration

Add per-rig beads configuration to rig config schema.

## Config Schema

In each rig's config.json:

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

## Repo Options

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

## Environment Injection

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

## Resolution Logic

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

## Backwards Compatibility

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


request-shutdown

Send shutdown request to Witness.
Wait for termination.

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

Depends: generate-summary