1. Project Overview & Quickstart (gastownhall/gastown)
File: README.md
Gas Town
Multi-agent orchestration system for Claude Code, GitHub Copilot, and other AI agents with persistent work tracking
Overview
Gas Town is a workspace manager that lets you coordinate multiple AI coding agents (Claude Code, GitHub Copilot, Codex, Gemini, and others) working on different tasks. Instead of losing context when agents restart, Gas Town persists work state in git-backed hooks, enabling reliable multi-agent workflows.
What Problem Does This Solve?
| Challenge | Gas Town Solution |
|---|---|
| Agents lose context on restart | Work persists in git-backed hooks |
| Manual agent coordination | Built-in mailboxes, identities, and handoffs |
| 4-10 agents become chaotic | Scale comfortably to 20-30 agents |
| Work state lost in agent memory | Work state stored in Beads ledger |
Architecture
graph TB
Mayor[The MayorAI Coordinator]
Town[Town Workspace~/gt/]
Town --> Mayor
Town --> Rig1[Rig: Project A]
Town --> Rig2[Rig: Project B]
Rig1 --> Crew1[Crew MemberYour workspace]
Rig1 --> Hooks1[HooksPersistent storage]
Rig1 --> Polecats1[PolecatsWorker agents]
Rig2 --> Crew2[Crew Member]
Rig2 --> Hooks2[Hooks]
Rig2 --> Polecats2[Polecats]
Hooks1 -.git worktree.-> GitRepo1[Git Repository]
Hooks2 -.git worktree.-> GitRepo2[Git Repository]
style Mayor fill:#e1f5ff,color:#000000
style Town fill:#f0f0f0,color:#000000
style Rig1 fill:#fff4e1,color:#000000
style Rig2 fill:#fff4e1,color:#000000Core Concepts
The Mayor 🎩
Your primary AI coordinator. The Mayor is a Claude Code instance with full context about your workspace, projects, and agents. Start here - just tell the Mayor what you want to accomplish.
Town 🏘️
Your workspace directory (e.g., ~/gt/). Contains all projects, agents, and configuration.
Rigs 🏗️
Project containers. Each rig wraps a git repository and manages its associated agents.
Crew Members 👤
Your personal workspace within a rig. Where you do hands-on work.
Polecats 🦨
Worker agents with persistent identity but ephemeral sessions. Spawned for tasks, sessions end on completion, but identity and work history persist.
Hooks 🪝
Git worktree-based persistent storage for agent work. Survives crashes and restarts.
Convoys 🚚
Work tracking units. Bundle multiple beads that get assigned to agents. Convoys labeled mountain get autonomous stall detection and smart skip logic for epic-scale execution.
Beads Integration 📿
Git-backed issue tracking system that stores work state as structured data.
Bead IDs (also called issue IDs) use a prefix + 5-character alphanumeric format (e.g., gt-abc12, hq-x7k2m). The prefix indicates the item's origin or rig. Commands like gt sling and gt convoy accept these IDs to reference specific work items. The terms "bead" and "issue" are used interchangeably—beads are the underlying data format, while issues are the work items stored as beads.
Molecules 🧬
Workflow templates that coordinate multi-step work. Formulas (TOML definitions) are instantiated as molecules with tracked steps. Two modes: root-only wisps (steps materialized at runtime, lightweight) and poured wisps (steps materialized as sub-wisps with checkpoint recovery). See Molecules.
Monitoring: Witness, Deacon, Dogs 🐕
A three-tier watchdog system keeps agents healthy:
- Witness - Per-rig lifecycle manager. Monitors polecats, detects stuck agents, triggers recovery, manages session cleanup.
- Deacon - Background supervisor running continuous patrol cycles across all rigs.
- Dogs - Infrastructure workers dispatched by the Deacon for maintenance tasks (e.g., Boot for triage).
Refinery 🏭
Per-rig merge queue processor. When polecats complete work via gt done, the Refinery batches merge requests, runs verification gates, and merges to main using a Bors-style bisecting queue. Failed MRs are isolated and either fixed inline or re-dispatched.
Escalation 🚨
Severity-routed issue escalation. Agents that hit blockers escalate via gt escalate, which creates tracked beads routed through the Deacon, Mayor, and (if needed) Overseer. Severity levels: CRITICAL (P0), HIGH (P1), MEDIUM (P2). See Escalation.
Scheduler ⏱️
Config-driven capacity governor for polecat dispatch. Prevents API rate limit exhaustion by batching dispatch under configurable concurrency limits. Default is direct dispatch; set scheduler.max_polecats to enable deferred dispatch with the daemon. See Scheduler.
Seance 👻
Session discovery and continuation. Discovers previous agent sessions via .events.jsonl logs, enabling agents to query their predecessors for context and decisions from earlier work.
gt seance # List discoverable predecessor sessions
gt seance --talk -p "What did you find?" # One-shot questionWasteland 🏜️
Federated work coordination network linking Gas Towns through DoltHub. Rigs post wanted items, claim work from other towns, submit completion evidence, and earn portable reputation via multi-dimensional stamps. See Wasteland.
New to Gas Town? See the Glossary for a complete guide to terminology and concepts.
Installation
Choose one of the two setup paths below: install Gas Town on your host, or run it inside a Docker container.
Prerequisites
Native installs require the host tools below. Docker installs only require Docker Compose on the host; the image supplies Go, Dolt, bd, tmux, and CLI utilities inside the container. The platform steps below say when a path installs gt, bd, and dolt for you and when you install them separately.
| Tool | Version | Notes |
|---|---|---|
| Git | 2.20+ | Worktree support |
| Go | 1.26.2+ (see go.mod) |
Required for the Linux and Windows paths and for macOS source builds. Not needed for brew install gastown or Docker setup. |
Beads (bd) |
0.57.0+ | Required for native installs. Homebrew and Docker supply it; source/native Go paths install it with go install. |
| sqlite3 | any | Used by convoy database queries. Usually pre-installed on macOS and Linux. |
| ICU4C dev headers | varies | Required for source builds that compile the ICU-backed query layer. Use libicu-dev on Debian/Ubuntu, libicu-devel on Fedora/RHEL, icu4c on macOS, and MSYS2 ICU packages for native Windows. |
| tmux | 3.0+ | Required for gt up and the tmux-backed roles (Mayor, Witnesses, Refineries, polecats). Optional only for minimal-mode workflows where you run runtime instances manually. |
| Claude Code CLI | latest | Default runtime. See Runtime Configuration for alternatives (Codex, Copilot, Gemini, Cursor). |
Local setup
Install the prerequisites listed above, then install gt for your platform.
Install gt on macOS
Homebrew installs gt, bd, and dolt together.
brew install gastownAvoid go install on macOS. The unsigned binary it produces gets killed by Gatekeeper. To build from source, install Dolt and ICU4C with Homebrew, install bd with Go, then build and install gt with make install. Put $HOME/.local/bin and $HOME/go/bin ahead of any stale binary locations on your PATH so the freshly installed gt and bd take precedence.
brew install dolt icu4c
go install github.com/steveyegge/beads/cmd/bd@latest
export PATH="$HOME/.local/bin:$HOME/go/bin:$PATH"
git clone https://github.com/steveyegge/gastown.git
cd gastown
make installInstall gt on Linux
Install Dolt by following the Dolt installation guide, then install gt and bd with go install.
go install github.com/steveyegge/gastown/cmd/gt@latest
go install github.com/steveyegge/beads/cmd/bd@latestPrepend the Go binary directory to your PATH if it is not already there, so freshly installed gt and bd binaries take precedence over stale copies. Append to ~/.zshrc instead if you use zsh.
echo 'export PATH="$HOME/go/bin:$PATH"' >> ~/.bashrc
source ~/.bashrcInstall gt on Windows
Install Dolt first by following the Dolt installation guide. Unlike the macOS Homebrew path, go install does not install Dolt. Then install gt and bd with go install.
go install github.com/steveyegge/gastown/cmd/gt@latest
go install github.com/steveyegge/beads/cmd/bd@latestBoth binaries land in %USERPROFILE%\go\bin\. Put that directory before older gt or bd install locations on PATH, then open a new shell for the change to take effect.
Native Windows source builds that compile the ICU-backed query layer need an MSYS2 UCRT64 or MinGW64 shell with matching icu, toolchain, and pkg-config packages; the repository's Windows CI uses pacboy -S icu:p toolchain:p pkg-config:p. Plain PowerShell/MSVC is not enough for that CGO build.
For full tmux-backed workflows on Windows, use WSL or another Linux environment. Native Windows shells are best treated as minimal CLI-only environments.
Create your workspace
Run gt install to create your headquarters (HQ) at ~/gt. The --shell flag installs shell integration and enables Gas Town globally. The --git flag initializes the HQ as a git repository. Before using --git, set git config --global user.name and git config --global user.email so the initial commit has a valid identity.
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
gt install ~/gt --shell --git
cd ~/gtStart the long-lived services. gt up boots Dolt, the daemon, the Deacon, the Mayor, and per-rig Witnesses and Refineries.
gt upVerify the install. The --fix flag clears the warnings that gt install does not preempt.
gt doctor --fixAdd a project
Use gt rig add to clone a repository into your HQ as a rig.
gt rig add myproject https://github.com/you/repo.gitRig names accept letters, digits, and underscores. Hyphens, dots, spaces, and path separators are not allowed. Use my_project instead of my-project.
To set a custom beads prefix for the rig, pass --prefix.
gt rig add myproject https://github.com/you/repo.git --prefix mpCreate your crew workspace
A crew workspace is a personal git clone where you do hands-on work.
gt crew add yourname --rig myproject
cd myproject/crew/yournameStart the Mayor
The Mayor coordinates work across rigs.
gt mayor attachDocker Compose setup
docker-compose.yml runs Gas Town inside a sandbox container. The container hosts an HQ at /gt, which Compose bind-mounts from ${FOLDER} on the host. The entrypoint runs gt install /gt --git against that directory on first start, so FOLDER must point at an empty directory that you want to become the HQ or an existing Gas Town HQ. Set GIT_USER and GIT_EMAIL so git and Dolt commits do not use the default test identity. See the full Docker guide for lifecycle, storage, and security details.
export GIT_USER="<your name>"
export GIT_EMAIL="<your email>"
export FOLDER="/path/to/empty/dir" # empty directory or existing Gas Town HQ
export DASHBOARD_PORT=8080 # optional, host port for the dashboard
mkdir -p "$FOLDER"
docker compose build # only needed on first run or after code changes
docker compose up -d
docker compose logs -f gastown # wait for "HQ created successfully!", then Ctrl-C
docker compose exec gastown zsh # or bashInside the container, finish bootstrapping.
gt enable # enable Gas Town global state
gt shell install # install shell integration
gt up --restore # start services and restore worker settings
gh auth login # optional: required for private GitHub rigs
gt mayor attachThe dashboard port is exposed from the container to the host. Treat it as a trusted local-network surface, and do not point FOLDER at a host workspace that a native gt install is using at the same time.
Quick Start Guide
Getting Started
Run
git config --global user.name "Your Name" &&
git config --global user.email "[email protected]" &&
gt install ~/gt --shell --git &&
cd ~/gt &&
gt up &&
gt doctor --fix &&
gt config agent list &&
gt mayor attachand tell the Mayor what you want to build!
Basic Workflow
sequenceDiagram
participant You
participant Mayor
participant Convoy
participant Agent
participant Hook
You->>Mayor: Tell Mayor what to build
Mayor->>Convoy: Create convoy with beads
Mayor->>Agent: Sling bead to agent
Agent->>Hook: Store work state
Agent->>Agent: Complete work
Agent->>Convoy: Report completion
Mayor->>You: Summary of progressExample: Feature Development
# 1. Start the Mayor
gt mayor attach
# 2. In Mayor session, create a convoy with bead IDs
gt convoy create "Feature X" gt-abc12 gt-def34 --notify --human
# 3. Assign work to an agent
gt sling gt-abc12 myproject
# 4. Track progress
gt convoy list
# 5. Monitor agents
gt agentsCommon Workflows
Mayor Workflow (Recommended)
Best for: Coordinating complex, multi-issue work
flowchart LR
Start([Start Mayor]) --> Tell[Tell Mayorwhat to build]
Tell --> Creates[Mayor createsconvoy + agents]
Creates --> Monitor[Monitor progressvia convoy list]
Monitor --> Done{All done?}
Done -->|No| Monitor
Done -->|Yes| Review[Review work]Commands:
# Attach to Mayor
gt mayor attach
# In Mayor, create convoy and let it orchestrate
gt convoy create "Auth System" gt-x7k2m gt-p9n4q --notify
# Track progress
gt convoy listMinimal Mode (No Tmux)
Run individual runtime instances manually. Gas Town just tracks state.
gt convoy create "Fix bugs" gt-abc12 # Create convoy (sling auto-creates if skipped)
gt sling gt-abc12 myproject # Assign to worker
claude --resume # Agent reads mail, runs work (Claude)
# or: codex # Start Codex in the workspace
gt convoy list # Check progressBeads Formula Workflow
Best for: Predefined, repeatable processes
Formulas are TOML-defined workflows embedded in the gt binary (source in internal/formula/formulas/).
Example Formula (internal/formula/formulas/release.formula.toml):
description = "Standard release process"
formula = "release"
version = 1
[vars.version]
description = "The semantic version to release (e.g., 1.2.0)"
required = true
[[steps]]
id = "bump-version"
title = "Bump version"
description = "Run ./scripts/bump-version.sh {{version}}"
[[steps]]
id = "run-tests"
title = "Run tests"
description = "Run make test"
needs = ["bump-version"]
[[steps]]
id = "build"
title = "Build"
description = "Run make build"
needs = ["run-tests"]
[[steps]]
id = "create-tag"
title = "Create release tag"
description = "Run git tag -a v{{version}} -m 'Release v{{version}}'"
needs = ["build"]
[[steps]]
id = "publish"
title = "Publish"
description = "Run ./scripts/publish.sh"
needs = ["create-tag"]Execute:
# List available formulas
bd formula list
# Run a formula with variables
bd cook release --var version=1.2.0
# Create formula instance for tracking
bd mol pour release --var version=1.2.0Manual Convoy Workflow
Best for: Direct control over work distribution
# Create convoy manually
gt convoy create "Bug Fixes" --human
# Add issues to existing convoy
gt convoy add hq-cv-abc gt-m3k9p gt-w5t2x
# Assign to specific agents
gt sling gt-m3k9p myproject/my-agent
# Check status
gt convoy showRuntime Configuration
Gas Town supports multiple AI coding runtimes. Per-rig runtime settings are in settings/config.json.
{
"runtime": {
"provider": "codex",
"command": "codex",
"args": [],
"prompt_mode": "none"
}
}Notes:
- Claude uses hooks in
.claude/settings.json(managed via--settingsflag) for mail injection and startup. - For Codex, set
project_doc_fallback_filenames = ["CLAUDE.md"]in~/.codex/config.tomlso role instructions are picked up. - For runtimes without hooks (e.g., Codex), Gas Town sends a startup fallback
after the session is ready:gt prime, optionalgt mail check --inject
for autonomous roles, andgt nudge deacon session-started. - GitHub Copilot (
copilot) is a built-in preset using--yolofor autonomous
mode. It uses executable lifecycle hooks in.github/hooks/gastown.json(same events
as Claude:sessionStart,userPromptSubmitted,preToolUse,sessionEnd). Uses a
5-second ready delay instead of prompt detection. Requires a Copilot seat and org-level
CLI policy. See docs/INSTALLING.md.
Key Commands
Workspace Management
gt install # Initialize workspace
gt rig add <name> <repo> # Add project
gt rig list # List projects
gt crew add <name> --rig <rig> # Create crew workspaceAgent Operations
gt agents # List active agents
gt sling <rig> # Assign work to agent
gt sling <rig> --agent cursor # Override runtime for this sling/spawn
gt mayor attach # Start Mayor session
gt mayor start --agent auggie # Run Mayor with a specific agent alias
gt prime # Context recovery (run inside existing session)
gt feed # Real-time activity feed (TUI)
gt feed --problems # Start in problems view (stuck agent detection)Built-in agent presets: claude, gemini, codex, kiro, cursor, auggie, amp, opencode, copilot, pi, omp
The kiro preset launches kiro-cli chat --trust-all-tools, supports Kiro's
documented --resume / --resume-id session flags, and does not install Kiro
hooks or .kiro project files.
Convoy (Work Tracking)
gt convoy create <name> [issues...] # Create convoy with issues
gt convoy list # List all convoys
gt convoy show [id] # Show convoy details
gt convoy add <convoy-id> # Add issues to convoyConfiguration
# Set custom agent command
gt config agent set claude-glm "claude-glm --model glm-4"
gt config agent set codex-low "codex --thinking low"
# Set default agent
gt config default-agent claude-glmMonitoring & Health
gt escalate -s HIGH "description" # Escalate a blocker
gt escalate list # List open escalations
gt scheduler status # Show scheduler state
gt seance # Discover previous sessions
gt seance --talk # Query a predecessor sessionBeads Integration
bd formula list # List formulas
bd cook <formula> # Execute formula
bd mol pour <formula> # Create trackable instance
bd mol list # List active instancesWasteland Federation
gt wl join <remote> # Join a wasteland
gt wl browse # View wanted board
gt wl claim # Claim work
gt wl done --evidence # Submit completionCooking Formulas
Gas Town includes built-in formulas for common workflows. See internal/formula/formulas/ for available recipes.
Activity Feed
gt feed launches an interactive terminal dashboard for monitoring all agent activity in real-time. It combines beads activity, agent events, and merge queue updates into a three-panel TUI:
- Agent Tree - Hierarchical view of all agents grouped by rig and role
- Convoy Panel - In-progress and recently-landed convoys
- Event Stream - Chronological feed of creates, completions, slings, nudges, and more
gt feed # Launch TUI dashboard
gt feed --problems # Start in problems view
gt feed --plain # Plain text output (no TUI)
gt feed --window # Open in dedicated tmux window
gt feed --since 1h # Events from last hourNavigation: j/k to scroll, Tab to switch panels, 1/2/3 to jump to a panel, ? for help, q to quit.
Problems View
At scale (20-50+ agents), spotting stuck agents in the activity stream becomes difficult. The problems view surfaces agents needing human intervention by analyzing structured beads data.
Press p in gt feed (or start with gt feed --problems) to toggle the problems view, which groups agents by health state:
| State | Condition |
|---|---|
| GUPP Violation | Hooked work with no progress for an extended period |
| Stalled | Hooked work with reduced progress |
| Zombie | Dead tmux session |
| Working | Active, progressing normally |
| Idle | No hooked work |
Intervention keys (in problems view): n to nudge the selected agent, h to handoff (refresh context).
Dashboard
Gas Town includes a web dashboard for monitoring your workspace. The dashboard
must be run from inside a Gas Town workspace (HQ) directory.
# Start dashboard (default port 8080)
gt dashboard
# Start on a custom port
gt dashboard --port 3000
# Start and automatically open in browser
gt dashboard --openThe dashboard gives you a single-page overview of everything happening in your
workspace: agents, convoys, hooks, queues, issues, and escalations. It
auto-refreshes via htmx and includes a command palette for running gt commands
directly from the browser.
Monitoring & Health
Gas Town uses a three-tier watchdog chain to keep agents healthy at scale:
Daemon (Go process) ← heartbeat every 3 min
└── Boot (AI agent) ← intelligent triage
└── Deacon (AI agent) ← continuous patrol
└── Witnesses & Refineries ← per-rig agentsWitness (Per-Rig)
Each rig has a Witness that monitors its polecats. The Witness detects stuck agents, triggers recovery (nudge or handoff), manages session cleanup, and tracks completion. Witnesses delegate work rather than implementing it directly.
Deacon (Cross-Rig)
The Deacon runs continuous patrol cycles across all rigs, checking agent health, dispatching Dogs for maintenance tasks, and escalating issues that individual Witnesses can't resolve.
Escalation
When agents hit blockers, they escalate rather than waiting:
gt escalate -s HIGH "Description of blocker"
gt escalate list # List open escalations
gt escalate ack # Acknowledge an escalationEscalations route through Deacon -> Mayor -> Overseer based on severity. See Escalation design.
Merge Queue (Refinery)
The Refinery processes completed polecat work through a bisecting merge queue:
- Polecat runs
gt done-> branch pushed, MR bead created - Refinery batches pending MRs
- Runs verification gates on the merged stack
- If green: all MRs in batch merge to main
- If red: bisects to isolate the failing MR, merges the good ones
This is a Bors-style merge queue — polecats never push directly to main.
Scheduler
The scheduler controls polecat dispatch capacity to prevent API rate limit exhaustion:
gt config set scheduler.max_polecats 5 # Enable deferred dispatch (max 5 concurrent)
gt scheduler status # Show scheduler state
gt scheduler pause # Pause dispatch
gt scheduler resume # Resume dispatchDefault mode (max_polecats = -1) dispatches immediately via gt sling. When a limit is set, the daemon dispatches incrementally, respecting capacity. See Scheduler design.
Seance
Discover and query previous agent sessions:
gt seance # List discoverable predecessor sessions
gt seance --talk # Full context conversation with predecessor
gt seance --talk -p "Question?" # One-shot question to predecessorSeance discovers sessions via .events.jsonl logs, enabling agents to recover context and decisions from earlier work without re-reading entire codebases.
Wasteland Federation
The Wasteland is a federated work coordination network linking multiple Gas Towns through DoltHub:
gt wl join hop/wl-commons # Join a wasteland
gt wl browse # View wanted board
gt wl claim # Claim a wanted item
gt wl done --evidence # Submit completion with evidence
gt wl post --title "Need X" # Post new wanted itemCompletions earn portable reputation via multi-dimensional stamps (quality, speed, complexity). See Wasteland guide.
Telemetry (OpenTelemetry)
Gas Town emits all agent operations as structured logs and metrics to any OTLP-compatible backend (VictoriaMetrics/VictoriaLogs by default):
# Configure OTLP endpoints
export GT_OTEL_LOGS_URL="http://localhost:9428/insert/jsonline"
export GT_OTEL_METRICS_URL="http://localhost:8428/api/v1/write"Events emitted: session lifecycle, agent state changes, bd calls with duration, mail operations, sling/nudge/done workflows, polecat spawn/remove, formula instantiation, convoy creation, daemon restarts, and more.
Metrics include: gastown.session.starts.total, gastown.bd.calls.total, gastown.polecat.spawns.total, gastown.done.total, gastown.convoy.creates.total, and others.
See OTEL data model and OTEL architecture for the complete event schema.
Advanced Concepts
The Propulsion Principle
Gas Town uses git hooks as a propulsion mechanism. Each hook is a git worktree with:
- Persistent state - Work survives agent restarts
- Version control - All changes tracked in git
- Rollback capability - Revert to any previous state
- Multi-agent coordination - Shared through git
Hook Lifecycle
stateDiagram-v2
[*] --> Created: Agent spawned
Created --> Active: Work assigned
Active --> Suspended: Agent paused
Suspended --> Active: Agent resumed
Active --> Completed: Work done
Completed --> Archived: Hook archived
Archived --> [*]MEOW (Mayor-Enhanced Orchestration Workflow)
MEOW is the recommended pattern:
- Tell the Mayor - Describe what you want
- Mayor analyzes - Breaks down into tasks
- Convoy creation - Mayor creates convoy with beads
- Agent spawning - Mayor spawns appropriate agents
- Work distribution - Beads slung to agents via hooks
- Progress monitoring - Track through convoy status
- Completion - Mayor summarizes results
Shell Completions
# Bash
gt completion bash > /etc/bash_completion.d/gt
# Zsh
gt completion zsh > "${fpath[1]}/_gt"
# Fish
gt completion fish > ~/.config/fish/completions/gt.fishProject Roles
| Role | Description | Primary Interface |
|---|---|---|
| Mayor | AI coordinator | gt mayor attach |
| Human (You) | Crew member | Your crew directory |
| Polecat | Worker agent | Spawned by Mayor |
| Witness | Per-rig agent health monitor | Automatic patrol |
| Deacon | Cross-rig supervisor daemon | gt patrol |
| Refinery | Merge queue processor | Automatic |
| Hook | Persistent storage | Git worktree |
| Convoy | Work tracker | gt convoy commands |
Tips
- Always start with the Mayor - It's designed to be your primary interface
- Use convoys for coordination - They provide visibility across agents
- Leverage hooks for persistence - Your work won't disappear
- Create formulas for repeated tasks - Save time with Beads recipes
- Use
gt feedfor live monitoring - Watch agent activity and catch stuck agents early - Monitor the dashboard - Get real-time visibility in the browser
- Let the Mayor orchestrate - It knows how to manage agents
Design Documentation
For deeper technical details, see the design docs in docs/:
| Topic | Document |
|---|---|
| Architecture | docs/design/architecture.md |
| Glossary | docs/glossary.md |
| Molecules | docs/concepts/molecules.md |
| Escalation | docs/design/escalation.md |
| Scheduler | docs/design/scheduler.md |
| Wasteland | docs/WASTELAND.md |
| OTEL data model | docs/otel-data-model.md |
| Witness design | docs/design/witness-at-team-lead.md |
| Convoy lifecycle | docs/design/convoy/ |
| Polecat lifecycle | docs/design/polecat-lifecycle-patrol.md |
| Plugin system | docs/design/plugin-system.md |
| Agent providers | docs/agent-provider-integration.md |
| Hooks | docs/HOOKS.md |
| Installation guide | docs/INSTALLING.md |
| Docker guide | docs/docker.md |
Troubleshooting
Agents lose connection
Check hooks are properly initialized:
gt hooks list
gt hooks repairConvoy stuck
Force refresh:
gt convoy refresh <convoy-id>Mayor not responding
Restart Mayor session:
gt mayor detach
gt mayor attachLicense
MIT License - see LICENSE file for details
File: docs/concepts/convoy.md
Convoys
Convoys are the primary unit for tracking batched work across rigs.
Quick Start
# Create a convoy tracking some issues
gt convoy create "Feature X" gt-abc gt-def --notify overseer
# Check progress
gt convoy status hq-cv-abc
# List active convoys (the dashboard)
gt convoy list
# See all convoys including landed ones
gt convoy list --allConcept
A convoy is a persistent tracking unit that monitors related issues across
multiple rigs. When you kick off work - even a single issue - a convoy tracks it
so you can see when it lands and what was included.
🚚 Convoy (hq-cv-abc)
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ gt-xyz │ │ gt-def │ │ bd-abc │
│ gastown │ │ gastown │ │ beads │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ nux │ │ furiosa │ │ amber │
│(polecat)│ │(polecat)│ │(polecat)│
└─────────┘ └─────────┘ └─────────┘
│
"the swarm"
(ephemeral)Convoy vs Swarm
| Concept | Persistent? | ID | Description |
|---|---|---|---|
| Convoy | Yes | hq-cv-* | Tracking unit. What you create, track, get notified about. |
| Swarm | No | None | Ephemeral. "The workers currently on this convoy's issues." |
| Stranded Convoy | Yes | hq-cv-* | A convoy with ready work but no polecats assigned. Needs attention. |
When you "kick off a swarm", you're really:
- Creating a convoy (the tracking unit)
- Assigning polecats to the tracked issues
- The "swarm" is just those polecats while they're working
When issues close, the convoy lands and notifies you. The swarm dissolves.
Convoy Lifecycle
OPEN ──(all issues close)──► LANDED/CLOSED
↑ │
└──(add more issues)───────────┘
(auto-reopens)| State | Description |
|---|---|
open |
Active tracking, work in progress |
closed |
All tracked issues closed, notification sent |
Adding issues to a closed convoy reopens it automatically.
Commands
Create a Convoy
# Track multiple issues across rigs
gt convoy create "Deploy v2.0" gt-abc bd-xyz --notify gastown/joe
# Track a single issue (still creates convoy for dashboard visibility)
gt convoy create "Fix auth bug" gt-auth-fix
# With default notification (from config)
gt convoy create "Feature X" gt-a gt-b gt-cAdd Issues
# Add issues to existing convoy
gt convoy add hq-cv-abc gt-new-issue
gt convoy add hq-cv-abc gt-issue1 gt-issue2 gt-issue3
# Adding to closed convoy requires reopening first
bd update hq-cv-abc --status=open
gt convoy add hq-cv-abc gt-followup-fixCheck Status
# Show issues and active workers (the swarm)
gt convoy status hq-abc
# All active convoys (the dashboard)
gt convoy statusExample output:
🚚 hq-cv-abc: Deploy v2.0
Status: ●
Progress: 2/4 completed
Created: 2025-12-30T10:15:00-08:00
Tracked Issues:
✓ gt-xyz: Update API endpoint [task]
✓ bd-abc: Fix validation [bug]
○ bd-ghi: Update docs [task]
○ gt-jkl: Deploy to prod [task]List Convoys (Dashboard)
# Active convoys (default) - the primary attention view
gt convoy list
# All convoys including landed
gt convoy list --all
# Only landed convoys
gt convoy list --status=closed
# JSON output
gt convoy list --jsonExample output:
Convoys
🚚 hq-cv-w3nm6: Feature X ●
🚚 hq-cv-abc12: Bug fixes ●
Use 'gt convoy status ' for detailed view.Notifications
When a convoy lands (all tracked issues closed), subscribers are notified:
# Explicit subscriber
gt convoy create "Feature X" gt-abc --notify gastown/joe
# Multiple subscribers
gt convoy create "Feature X" gt-abc --notify mayor/ --notify --humanNotification content:
🚚 Convoy Landed: Deploy v2.0 (hq-cv-abc)
Issues (3):
✓ gt-xyz: Update API endpoint
✓ gt-def: Add validation
✓ bd-abc: Update docs
Duration: 2h 15mCreate from Epic
Auto-discover tracked issues from an existing epic's children. Useful when
a planning/decomposition tool has already structured work as an epic with
child implementation beads.
# Auto-discover children from epic
gt convoy create --from-epic gt-epic-abc
# Override the convoy name (defaults to epic title)
gt convoy create --from-epic gt-epic-abc "Custom convoy name"
# Combine with other flags
gt convoy create --from-epic gt-epic-abc --owned --merge=directHow it works:
- Verifies the given bead is an epic
- BFS-walks the parent-child hierarchy to find slingable descendants
- Creates a standard convoy (
hq-cv-*) tracking all slingable children (task, bug, feature, chore)
Non-slingable types (sub-epics, decisions) are recursed into but never
tracked directly. Only leaf work items appear in the convoy.
Auto-Convoy on Sling
When you sling a single issue without an existing convoy:
gt sling bd-xyz beads/amberThis auto-creates a convoy so all work appears in the dashboard:
- Creates convoy: "Work: bd-xyz"
- Tracks the issue
- Assigns the polecat
Even "swarm of one" gets convoy visibility.
Cross-Rig Tracking
Convoys live in town-level beads (hq-cv-* prefix) and can track issues from any rig:
# Track issues from multiple rigs
gt convoy create "Full-stack feature" \
gt-frontend-abc \
gt-backend-def \
bd-docs-xyzThe tracks relation is:
- Non-blocking: doesn't affect issue workflow
- Additive: can add issues anytime
- Cross-rig: convoy in hq-, issues in gt-, bd-*, etc.
Convoy vs Rig Status
| View | Scope | Shows |
|---|---|---|
gt convoy status [id] |
Cross-rig | Issues tracked by convoy + workers |
gt rig status <rig> |
Single rig | All workers in rig + their convoy membership |
Use convoys for "what's the status of this batch of work?"
Use rig status for "what's everyone in this rig working on?"
See Also
- Propulsion Principle - Worker execution model
- Mail Protocol - Notification delivery
File: docs/concepts/heartbeats.md
Heartbeats
Gas Town has three distinct heartbeat stores. They have different readers
and thresholds, so Deacon heartbeat commands refresh the Deacon-specific stores
together to avoid false "stuck agent" escalations (see hq-qxl9: a Deacon
refreshed its session heartbeat while the file store aged past threshold).
The three stores
1. Deacon heartbeat file — `/deacon/heartbeat.json`
- Written by:
gt deacon heartbeat [action]andgt heartbeatwhenGT_ROLE=deacon→deacon.Touch()/deacon.TouchWithAction()
(internal/deacon/heartbeat.go). - Read by: the stuck-agent-dog plugin (parses the JSON
timestamp, falling
back to mtime for malformed legacy files, and cross-checks tmux activity
before escalating) and the Go daemon (deacon.ReadHeartbeat; thresholds 5m
stale / 20m very-stale → poke). - Also touches: the legacy
deacon/.deacon-heartbeatmtime file for old
shell scripts.
2. Session heartbeat (per-session state store)
- Written by:
gt heartbeat [--state=working|idle|exiting|stuck]→polecat.TouchSessionHeartbeatWithState(). RequiresGT_SESSION. - Read by: the Witness, which reads the self-reported state instead of
inferring liveness from timers (ZFC: gt-3vr5). This is the store polecats
refresh.
3. Agent-bead label — `heartbeat:` on the agent bead (e.g. `hq-deacon`)
- Written by:
gt mol await-signalon each timeout/signal wake
(updateAgentHeartbeatininternal/cmd/molecule_await_signal.go). A
label rewrite is used becausebd agent heartbeatwas never shipped
(steveyegge/beads#2828). Deacon heartbeat commands also sync this label when
it is older than half of the stale threshold. - Read by: Witness second-order monitoring ("who watches the watchers"):
Witnesses check the Deacon's bead activity and alert the Mayor if it looks
unresponsive (>5 minutes per the patrol formula). - Gotcha: a session that never reaches
await-signal(handoff churn,
session limits, one very long patrol turn) leaves this label stale for
hours even though the agent is healthy.
Rules of thumb
- Deacon sessions:
gt deacon heartbeatrefreshes the Deacon file and
throttled bead label.gt heartbeatalso refreshes the session store and,
whenGT_ROLE=deacon, uses the same Deacon file/label sync path. - Polecats / Witness / Refinery:
gt heartbeat(session store) is the
one that matters. - Monitoring scripts: never declare an agent stuck from a single store.
Cross-check tmux session activity (tmux display-message -p '#{window_activity}') before escalating — a live session with a stale
store is heartbeat-write divergence, not a stuck agent. The
stuck-agent-dog plugin does this since hq-qxl9.
File: docs/concepts/identity.md
Agent Identity and Attribution
Canonical format for agent identity in Gas Town
Why Identity Matters
When you deploy AI agents at scale, anonymous work creates real problems:
- Debugging: "The AI broke it" isn't actionable. Which AI?
- Quality tracking: You can't improve what you can't measure.
- Compliance: Auditors ask "who approved this code?" - you need an answer.
- Performance management: Some agents are better than others at certain tasks.
Gas Town solves this with universal attribution: every action, every commit,
every bead update is linked to a specific agent identity. This enables work
history tracking, capability-based routing, and objective quality measurement.
BD_ACTOR Format Convention
The BD_ACTOR environment variable identifies agents in slash-separated path format.
This is set automatically when agents are spawned and used for all attribution.
Format by Role Type
| Role Type | Format | Example |
|---|---|---|
| Mayor | mayor |
mayor |
| Deacon | deacon |
deacon |
| Witness | {rig}/witness |
gastown/witness |
| Refinery | {rig}/refinery |
gastown/refinery |
| Crew | {rig}/crew/{name} |
gastown/crew/joe |
| Polecat | {rig}/polecats/{name} |
gastown/polecats/toast |
Why Slashes?
The slash format mirrors filesystem paths and enables:
- Hierarchical parsing (extract rig, role, name)
- Consistent mail addressing (
gt mail send gastown/witness) - Path-like routing in beads operations
- Visual clarity about agent location
Attribution Model
Gas Town uses three fields for complete provenance:
Git Commits
GIT_AUTHOR_NAME="gastown/crew/joe" # Who did the work (agent)
GIT_AUTHOR_EMAIL="[email protected]" # Who owns the work (overseer)Result in git log:
abc123 Fix bug (gastown/crew/joe <[email protected]>)Interpretation:
- The agent
gastown/crew/joeauthored the change - The work belongs to the workspace owner (
[email protected]) - Both are preserved in git history forever
Beads Records
{
"id": "gt-xyz",
"created_by": "gastown/crew/joe",
"updated_by": "gastown/witness"
}The created_by field is populated from BD_ACTOR when creating beads.
The updated_by field tracks who last modified the record.
Event Logging
All events include actor attribution:
{
"ts": "2025-01-15T10:30:00Z",
"type": "sling",
"actor": "gastown/crew/joe",
"payload": { "bead": "gt-xyz", "target": "gastown/polecats/toast" }
}Environment Setup
Gas Town uses a centralized config.AgentEnv() function to set environment
variables consistently across all agent spawn paths (managers, daemon, boot).
Example: Polecat Environment
# Set automatically for polecat 'toast' in rig 'gastown'
export GT_ROLE="polecat"
export GT_RIG="gastown"
export GT_POLECAT="toast"
export BD_ACTOR="gastown/polecats/toast"
export GIT_AUTHOR_NAME="gastown/polecats/toast"
export GT_ROOT="/home/user/gt"
export BEADS_DIR="/home/user/gt/gastown/.beads"
export BEADS_AGENT_NAME="gastown/toast"Example: Crew Environment
# Set automatically for crew member 'joe' in rig 'gastown'
export GT_ROLE="crew"
export GT_RIG="gastown"
export GT_CREW="joe"
export BD_ACTOR="gastown/crew/joe"
export GIT_AUTHOR_NAME="gastown/crew/joe"
export GT_ROOT="/home/user/gt"
export BEADS_DIR="/home/user/gt/gastown/.beads"
export BEADS_AGENT_NAME="gastown/joe"Manual Override
For local testing or debugging:
export BD_ACTOR="gastown/crew/debug"
bd create --title="Test issue" # Will show created_by: gastown/crew/debugSee reference.md for the complete
environment variable reference.
Identity Parsing
The format supports programmatic parsing:
// identityToBDActor converts daemon identity to BD_ACTOR format
// Town level: mayor, deacon
// Rig level: {rig}/witness, {rig}/refinery
// Workers: {rig}/crew/{name}, {rig}/polecats/{name}| Input | Parsed Components |
|---|---|
mayor |
role=mayor |
deacon |
role=deacon |
gastown/witness |
rig=gastown, role=witness |
gastown/refinery |
rig=gastown, role=refinery |
gastown/crew/joe |
rig=gastown, role=crew, name=joe |
gastown/polecats/toast |
rig=gastown, role=polecat, name=toast |
Audit Queries
Attribution enables powerful audit queries:
# All work by an agent
bd audit --actor=gastown/crew/joe
# All work in a rig
bd audit --actor=gastown/*
# All polecat work
bd audit --actor=*/polecats/*
# Git history by agent
git log --author="gastown/crew/joe"Design Principles
- Agents are not anonymous - Every action is attributed
- Work is owned, not authored - Agent creates, overseer owns
- Attribution is permanent - Git commits preserve history
- Format is parseable - Enables programmatic analysis
- Consistent across systems - Same format in git, beads, events
CV and Skill Accumulation
Human Identity is Global
The global identifier is your email - it's already in every git commit. No separate "entity bead" needed.
[email protected] ← global identity (from git author)
├── Town A (home) ← workspace
│ ├── gastown/crew/joe ← agent executor
│ └── gastown/polecats/toast ← agent executor
└── Town B (work) ← workspace
└── acme/polecats/nux ← agent executorAgent vs Owner
| Field | Scope | Purpose |
|---|---|---|
BD_ACTOR |
Local (town) | Agent attribution for debugging |
GIT_AUTHOR_EMAIL |
Global | Human identity for CV |
created_by |
Local | Who created the bead |
owner |
Global | Who owns the work |
Agents execute. Humans own. The polecat name in completed-by: gastown/polecats/toast is executor attribution. The CV credits the human owner ([email protected]).
Polecats Have Persistent Identities
Polecats have persistent identities but ephemeral sessions. Like employees who
clock in/out: each work session is fresh (new tmux, new worktree), but the identity
persists across sessions.
- Identity (persistent): Agent bead, CV chain, work history
- Session (ephemeral): Claude instance, context window
- Sandbox (ephemeral): Git worktree, branch
Work credits the polecat identity, enabling:
- Performance tracking per polecat
- Capability-based routing (send Go work to polecats with Go track records)
- Model comparison (A/B test different models via different polecats)
See polecat-lifecycle.md for details.
Skills Are Derived
Your CV emerges from querying work evidence:
# All work by owner (across all agents)
git log --author="[email protected]"
bd list [email protected]
# Skills derived from evidence
# - .go files touched → Go skill
# - issue tags → domain skills
# - commit patterns → activity typesMulti-Town Aggregation
A human with multiple towns has one CV:
# Future: federated CV query
bd cv [email protected]
# Discovers all towns, aggregates work, derives skillsSee ~/gt/docs/hop/decisions/008-identity-model.md for architectural rationale.
Enterprise Use Cases
Compliance and Audit
# Who touched this file in the last 90 days?
git log --since="90 days ago" -- path/to/sensitive/file.go
# All changes by a specific agent
bd audit --actor=gastown/polecats/toast --since=2025-01-01Performance Tracking
# Completion rate by agent
bd stats --group-by=actor
# Average time to completion
bd stats --actor=gastown/polecats/* --metric=cycle-timeModel Comparison
When agents use different underlying models, attribution enables A/B comparison:
# Tag agents by model
# gastown/polecats/claude-1 uses Claude
# gastown/polecats/gpt-1 uses GPT-4
# Compare quality signals
bd stats --actor=gastown/polecats/claude-* --metric=revision-count
bd stats --actor=gastown/polecats/gpt-* --metric=revision-countLower revision counts suggest higher first-pass quality.
File: docs/concepts/integration-branches.md
Integration Branches
Group epic work on a shared branch, land to main as a unit.
Integration branches provide end-to-end support for epic-scoped work across
the Gas Town pipeline. When you create an integration branch for an epic, it
becomes the automatic target for every stage: polecats spawn their worktrees
from the integration branch (so they start with sibling work already present),
the Refinery merges completed MRs into the integration branch instead of main,
and when all epic children are closed, the Refinery can land the integration
branch back to its base branch (main by default, or whatever was specified
with --base-branch at creation) as a single merge commit.
Landing can happen on command or automatically via patrol. The result is that
an entire epic flows through the system as a coherent unit, from first sling
to final land, without any manual branch targeting.
Workflow
Create the epic and its children. Structure your work as an epic with
child tasks (or sub-epics) underneath. Set up dependencies between children
to define which can run in parallel and which must wait.Create the integration branch. This is the shared branch where all child
work accumulates.bashgt mq integration create gt-auth-epicCreate a convoy to track the work. The convoy gives you a single dashboard
for the entire epic's progress.bashgt convoy create "Auth overhaul" gt-auth-tokens gt-auth-sessions gt-auth-middlewareSling the first wave. Identify children with no blockers and sling them
to the rig. Use--no-convoysince the tracking convoy already exists.bashgt sling gt-auth-tokens gastown --no-convoy gt sling gt-auth-sessions gastown --no-convoyPolecats process the work. Each polecat spawns its worktree from the
integration branch, so it starts with any sibling work that has already
landed there. When a polecat finishes, it submits a merge request.Refinery merges to the integration branch. Instead of merging to main,
the Refinery merges each MR into the integration branch and marks the child
task as complete.Track progress via the convoy. The convoy status updates each time the
Refinery completes a task.bashgt convoy status hq-cv-abcSling the next wave. When a wave completes and its dependent children
unblock, sling the next batch. Those polecats will start from the integration
branch — which now contains all the work from the preceding wave.bashgt sling gt-auth-middleware gastown --no-convoyLand when complete. When all children under the epic are closed, the
integration branch is ready to land. Ifintegration_branch_auto_landis
enabled, the Refinery does this automatically during patrol. Otherwise,
land manually:bashgt mq integration land gt-auth-epicThis merges the integration branch back to its base branch (main by
default) as a single merge commit, deletes the branch, and closes the
epic.
Concept
The Problem
Without integration branches, epic work lands piecemeal:
Child A ──► MR ──► main (lands Tuesday)
Child B ──► MR ──► main (lands Wednesday, breaks A's work)
Child C ──► MR ──► main (lands Thursday, depends on A+B together)Each child merges independently. If Child C depends on A and B being coherent
together, you're relying on merge order and hoping nothing breaks between lands.
The Solution
Integration branches batch epic work on a shared branch, then land atomically:
Epic: gt-auth-epic
│
┌─────────────┼─────────────┐
│ │ │
Child A Child B Child C
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ MR A │ │ MR B │ │ MR C │
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
└───────────┼───────────┘
▼
integration/gt-auth-epic
(shared branch)
│
▼ gt mq integration land
base branch
(main or --base-branch)
(single merge commit)All child MRs merge into the integration branch first. Children can build on
each other's work. When everything is ready, one command lands it all.
With vs Without
| Aspect | Without | With Integration Branch |
|---|---|---|
| MR target | main | integration/{epic} |
| Land timing | Each MR lands independently | All MRs land together |
| Cross-child deps | Risky—depends on merge order | Safe—children share a branch |
| Rollback | Revert individual commits | Revert one merge commit |
| CI on main | Runs per-MR | Runs once on combined work |
Lifecycle
1. Create the Epic
bd create --type=epic --title="Auth overhaul"
# → gt-auth-epicCreate child issues under the epic as normal.
2. Create the Integration Branch
gt mq integration create gt-auth-epic
# → Created integration/gt-auth-epic from origin/main
# → Stored branch name in epic metadataThis pushes a new branch to origin and records its name on the epic.
3. Sling Work
Assign children to polecats as normal:
gt sling gt-auth-tokens gastown
gt sling gt-auth-sessions gastownPolecats auto-detect the integration branch when their issue is a child of an
epic that has one. No manual targeting needed.
4. MRs Merge to Integration Branch
When polecats run gt done or gt mq submit, auto-detection kicks in:
gt done
→ Detects parent epic gt-auth-epic
→ Finds integration/gt-auth-epic branch
→ Submits MR targeting integration/gt-auth-epic (not main)The Refinery processes these MRs and merges them to the integration branch.
5. Land When Complete
Once all children are closed and all MRs merged:
gt mq integration land gt-auth-epic
# → Verified all MRs merged
# → Merged integration/gt-auth-epic → base branch (--no-ff)
# → Tests passed
# → Pushed to origin
# → Deleted integration/gt-auth-epic
# → Closed epic gt-auth-epicAuto-Detection
Integration branches work without manual targeting. Three systems auto-detect them:
| System | What It Does | Config Gate |
|---|---|---|
gt done / gt mq submit |
Targets MR at integration branch instead of main | integration_branch_refinery_enabled |
| Polecat spawn | Sources worktree from integration branch | integration_branch_polecat_enabled |
| Refinery patrol | Checks if integration branches are ready to land | integration_branch_auto_land |
Detection Algorithm
When gt done or gt mq submit runs:
| Step | Action | Result |
|---|---|---|
| 1 | Load config, check integration_branch_refinery_enabled |
If false, skip detection |
| 2 | Get current issue ID from branch name | e.g., gt-auth-tokens |
| 3 | Walk parent chain (max 10 levels) | Find ancestor epics |
| 4 | For each epic: read integration_branch: from metadata |
Get stored branch name |
| 5 | Fallback: generate name from template | e.g., integration/{title} |
| 6 | Check if branch exists (local, then remote) | Verify it's real |
| 7 | If found, target MR at that branch | Instead of main |
The --epic flag on gt mq submit bypasses auto-detection and resolves
the target branch using the configured template (defaulting tointegration/{epic}).
Branch Naming
Template Variables
| Variable | Description | Example |
|---|---|---|
{epic} |
Full epic ID | gt-auth-epic |
{prefix} |
Epic prefix (before first hyphen) | gt |
{user} |
From git config user.name |
klauern |
Precedence
| Priority | Source | Example |
|---|---|---|
| 1 (highest) | --branch flag on create |
--branch "feat/{epic}" |
| 2 | integration_branch_template in config |
"{user}/{epic}" |
| 3 (lowest) | Default | "integration/{title}" |
Template Variables
| Variable | Description | Example |
|---|---|---|
{title} |
Sanitized epic title (lowercase, hyphenated, max 60 chars) | add-user-authentication |
{epic} |
Full epic ID | RA-123 |
{prefix} |
Epic prefix before first hyphen | RA |
{user} |
Git user.name | klauern |
Examples
# Default template (uses epic title)
gt mq integration create gt-auth-epic
# → integration/add-user-authentication (from epic title)
# Custom template in config: "{user}/{prefix}/{epic}"
gt mq integration create RA-123
# → klauern/RA/RA-123
# Override with --branch flag
gt mq integration create RA-123 --branch "feature/{epic}"
# → feature/RA-123The actual branch name created is stored in the epic's metadata, so auto-detection
always finds the right branch regardless of which template was used.
If two epics produce the same branch name (same title), a numeric suffix from the
epic ID is appended automatically (e.g., integration/add-auth-456).
Commands
`gt mq integration create `
Create an integration branch for an epic.
gt mq integration create <epic-id> [flags]Flags:
| Flag | Description | Default |
|---|---|---|
--branch |
Override branch name template | Config template or integration/{title} |
--base-branch |
Create from this branch instead of the rig's default branch (also sets where land merges back to) |
origin/<default_branch> |
What it does:
- Verifies the epic exists
- Generates branch name from template (expanding variables)
- Validates branch name (git-safe characters)
- Creates local branch from base
- Pushes to origin
- Stores branch name and base branch in epic metadata
Error cases:
- Epic not found
- Branch already exists
- Invalid characters in generated branch name
`gt mq integration status `
Display integration branch status for an epic.
gt mq integration status <epic-id> [flags]Flags:
| Flag | Description |
|---|---|
--json |
Output as JSON |
Output includes:
- Branch name and creation date
- Commits ahead of main
- Merged MRs (closed, targeting integration branch)
- Pending MRs (open, targeting integration branch)
- Child issue progress (closed / total)
- Ready-to-land status
- Auto-land configuration
Ready-to-land criteria (all must be true):
- Integration branch has commits ahead of main
- Epic has children
- All children are closed
- No pending MRs (all submitted work is merged)
`gt mq integration land `
Merge an epic's integration branch back to its base branch.
gt mq integration land <epic-id> [flags]Flags:
| Flag | Description | Default |
|---|---|---|
--force |
Land even if some MRs still open | false |
--skip-tests |
Skip test run after merge | false |
--dry-run |
Preview only, make no changes | false |
What it does:
- Verifies epic exists and has an integration branch
- Reads base branch from epic metadata (defaults to the rig's
default_branchif not stored) - Checks all MRs targeting integration branch are merged
- Fetches latest refs and checks idempotency (if already merged, skips to cleanup)
- Acquires file lock (prevents concurrent land races)
- Creates a temporary worktree (avoids disrupting running agents)
- Merges integration branch to base branch using
--no-ff - Runs tests (unless
--skip-tests) - Verifies merge brought changes (guards against empty merges)
- Pushes to origin
- Deletes integration branch (local and remote)
- Closes the epic
Idempotent retry: If land crashes after pushing but before cleanup (branch
deletion / epic close), rerunning the same command is safe. The idempotency
check detects that the integration branch is already an ancestor of the target
and skips directly to cleanup.
Error cases:
- Epic has no integration branch
- Pending MRs exist (use
--forceto override) - Tests fail
- Empty merge (no changes to land)
Configuration
Default Branch
The rig's default_branch (set in config.json, auto-detected during gt rig add)
controls where work merges when no integration branch is active. It's also the
default base branch when creating integration branches. If your project usesdevelop or master instead of main, set it once in rig config and the whole
pipeline follows:
{
"type": "rig",
"name": "myproject",
"default_branch": "develop"
}Integration Branch Settings
All integration branch fields live under merge_queue in rig settings (settings/config.json):
{
"merge_queue": {
"enabled": true,
"integration_branch_polecat_enabled": true,
"integration_branch_refinery_enabled": true,
"integration_branch_template": "integration/{title}",
"integration_branch_auto_land": false
}
}| Field | Type | Default | Description |
|---|---|---|---|
integration_branch_polecat_enabled |
*bool |
true |
Polecats auto-source worktrees from integration branches |
integration_branch_refinery_enabled |
*bool |
true |
gt mq submit and gt done auto-detect integration branches as MR targets |
integration_branch_template |
string |
"integration/{title}" |
Branch name template (supports {title}, {epic}, {prefix}, {user}) |
integration_branch_auto_land |
*bool |
false |
Refinery patrol auto-lands when all children closed |
Note: *bool fields use pointer semantics — null/omitted means "use default"
(true for polecat/refinery enabled, false for auto-land). Set explicitly to false
to disable.
Auto-Landing
When integration_branch_auto_land is true, the Refinery patrol automatically
lands integration branches that are ready.
How It Works
During each patrol cycle, the Refinery:
- Lists all open epics:
bd list --type=epic --status=open - Checks each epic's integration branch:
gt mq integration status <epic-id> - If
ready_to_land: true: runsgt mq integration land <epic-id> - If not ready: skips (epic work is incomplete)
Conditions for Auto-Land
Both config gates must be true:
integration_branch_refinery_enabled: true(integration feature is on)integration_branch_auto_land: true(auto-landing is on)
If either is false, the patrol step exits early.
When to Enable
| Scenario | Recommendation |
|---|---|
| Trusted CI, no human review needed | Enable auto-land |
| Need human sign-off before landing | Keep disabled (default), land manually |
| Mix of both | Keep disabled, use gt mq integration land for manual control |
Safety Guardrails
Integration branch landing is protected by a three-layer defense:
Layer 1: Formula and Role Instructions
The refinery formula and role template explicitly forbid landing integration
branches via raw git commands. Only gt mq integration land is authorized.
Layer 2: Pre-Push Hook
The .githooks/pre-push hook detects when a push to the default branch
introduces integration branch content. It uses ancestry-based detection:
if any origin/integration/* branch tip becomes newly reachable from the
pushed commits, the push is blocked unless GT_INTEGRATION_LAND=1 is set.
The default branch is detected dynamically via refs/remotes/origin/HEAD
(fallback: main), so this works regardless of the rig's branch naming.
This catches all merge styles: --no-ff, --ff-only, default merge, and
rebase. Only cherry-picks (which produce new SHAs) are not detected.
Scope: This check matches branches under the integration/ prefix (the
default template). Custom templates that produce branches outside integration/
are not covered by the hook — Layer 1 (formula language) is the guardrail for
those cases.
Requires: core.hooksPath must be configured for the hook to be active.
New rigs get this automatically. Existing rigs: run gt doctor --fix.
Layer 3: Authorized Code Path
The gt mq integration land command uses PushWithEnv() to setGT_INTEGRATION_LAND=1, allowing the push through the hook. Raw git push
from any agent or user does not set this variable and will be blocked.
Manually setting the env var is possible but is not part of the supported
workflow — the variable is a policy-based trust boundary, not a
capability-based security mechanism.
Why Three Layers?
| Layer | Type | Strength | Limitation |
|---|---|---|---|
| Formula/Role | Soft | Covers all branch patterns | AI agents can ignore instructions |
| Pre-push hook | Hard | Blocks all merge styles at git boundary | Only matches integration/* prefix; env var is policy-based |
| Code path | Hard | Land command sets bypass env var | Requires hook to be active |
The layers complement each other. The formula covers custom templates; the hook
provides hard enforcement for default templates (catching merges, fast-forwards,
and rebases via ancestry detection); the code path ensures the CLI command can
bypass the hook.
Build Pipeline Configuration
Integration branches work with different project toolchains. The rig's build pipeline
commands are auto-injected into polecat-work, refinery-patrol, and sync-workspace
formulas so agents know how to validate work for each project.
The 5-Command Pipeline
Commands run in this order (any can be empty = skip):
- setup — Install dependencies (e.g.,
pnpm install) - typecheck — Static type checking (e.g.,
tsc --noEmit) - lint — Code style and quality (e.g.,
eslint .) - test — Run test suite (e.g.,
go test ./...) - build — Compile/bundle (e.g.,
go build ./...)
Example Configurations
Go project (all commands empty by default — configure per-rig):
{
"merge_queue": {
"test_command": "go test ./...",
"lint_command": "golangci-lint run ./...",
"build_command": "go build ./..."
}
}TypeScript project:
{
"merge_queue": {
"setup_command": "pnpm install",
"typecheck_command": "tsc --noEmit",
"lint_command": "eslint .",
"test_command": "pnpm test:unit",
"build_command": "pnpm build"
}
}How Commands Flow Into Formulas
Commands are auto-injected from <rig>/settings/config.json into formula vars:
- Refinery patrol:
buildRefineryPatrolVars()reads rig config duringgt prime - Polecat work / sync:
loadRigCommandVars()reads rig config duringgt sling
User-provided --var flags on gt sling override rig config values.
Empty = Skip
Any command left empty (or not configured) is skipped silently by the formula.
This means a Go rig doesn't need setup_command or typecheck_command, and a
TypeScript rig can add all five without affecting Go rigs.
Polecats working on integration branches inherit the rig's build pipeline
automatically — no per-branch configuration is needed.
Anti-Patterns
Creating Integration Branch After Work Starts
Wrong: Sling children, then create the integration branch later.
Children slung before the integration branch exists will target main. Their MRs
won't flow to the integration branch. Create the integration branch first,
before slinging any child work.
Manually Targeting the Integration Branch
Wrong: Using --branch integration/gt-epic on gt mq submit.
Auto-detection handles this. If you find yourself manually targeting, check that:
- The integration branch actually exists
integration_branch_refinery_enabledis notfalse- The issue is a child (or descendant) of the epic
Landing Partial Epics
Wrong: Using --force to land when children are still open.
This defeats the purpose. The integration branch exists so work lands together.
If you need to land early, close or remove the incomplete children first.
See Also
- Polecat Lifecycle — How polecats submit to the merge queue
- Reference — Full CLI reference including MQ commands
File: docs/concepts/molecules.md
Molecules
Molecules are workflow templates that coordinate multi-step work in Gas Town.
Molecule Lifecycle
Formula (source TOML) ─── "Ice-9"
│
▼ bd cook
Protomolecule (frozen template) ─── Solid
│
├─▶ bd mol pour ──▶ Mol (persistent) ─── Liquid
│
└─▶ bd mol wisp --root-only ──▶ Root Wisp (ephemeral) ─── VaporRoot-only wisps (default): Formula steps are NOT materialized as database rows.
Only a single root wisp is created. Agents read steps inline from the embedded
formula at prime time. This prevents wisp accumulation (~6,000+ rows/day → ~400/day).
Poured wisps (pour = true): Steps ARE materialized as sub-wisps with
checkpoint recovery. If a session dies, completed steps remain closed and work
resumes from the last checkpoint. Use pour for expensive, low-frequency workflows
where losing progress would be costly (e.g., release workflows).
Core Concepts
| Term | Description |
|---|---|
| Formula | Source TOML template defining workflow steps |
| Protomolecule | Frozen template ready for instantiation |
| Molecule | Active workflow instance (root wisp only) |
| Wisp | Ephemeral molecule for patrols and polecat work (never synced) |
| Root-only | Only root wisp created; steps read from embedded formula |
| Pour | Formula flag (pour = true); steps materialized as sub-wisps with checkpoint recovery |
How Agents See Steps
Agents do NOT use bd mol current or bd close <step-id> for formula workflows.
Instead, formula steps are rendered inline when the agent runs gt prime:
**Formula Checklist** (10 steps from mol-polecat-work):
### Step 1: Load context and verify assignment
Initialize your session and understand your assignment...
### Step 2: Set up working branch
Ensure you're on a clean feature branch...The agent works through the checklist and runs gt done (polecats) orgt patrol report (patrol agents) when complete.
Molecule Commands
Beads Operations (bd)
# Formulas
bd formula list # Available formulas
bd formula show <name> # Formula details
bd cook <formula> # Formula → Proto
# Molecules (data operations)
bd mol list # Available protos
bd mol show # Proto details
bd mol wisp # Create wisp (root-only by default)
bd mol bond # Attach to existing molAgent Operations (gt)
# Hook management
gt hook # What's on MY hook?
gt prime # Shows inline formula checklist
gt mol attach <mol> # Pin molecule to bead
gt mol detach # Unpin molecule from bead
# Patrol lifecycle
gt patrol new # Create patrol wisp and hook it
gt patrol report --summary "..." # Close current patrol, start next cyclePolecat Workflow
Polecats receive work via their hook — a root wisp attached to an issue.
They see the formula checklist inline when they run gt prime and work
through each step in order.
Polecat Workflow Summary
1. Spawn with work on hook
2. gt prime # Shows formula checklist inline
3. Work through each step
4. Persist findings: bd update --notes "..."
5. gt done # Submit, preserve handoff metadata, exitMolecule Types
| Type | Storage | Use Case |
|---|---|---|
Root-only Wisp (pour = false) |
.beads/ (ephemeral) |
Polecat work, patrols — high frequency, cheap steps |
Poured Wisp (pour = true) |
.beads/ (sub-wisps) |
Releases, long workflows — low frequency, expensive steps |
Heuristic: If you would curse losing the progress after a crash, set pour = true.
High frequency + cheap steps = inline (default). Low frequency + expensive steps = pour.
Patrol Workflow
Patrol agents (Deacon, Witness, Refinery) cycle through patrol formulas:
1. gt patrol new # Create root-only patrol wisp
2. gt prime # Shows patrol checklist inline
3. Work through each step
4. gt patrol report --summary "..." # Close + start next cyclegt patrol report atomically closes the current patrol root and spawns
a new one for the next cycle.
Best Practices
- Persist findings early —
bd update --notes "..."before session death - Run
gt donewhen complete — mandatory for polecats (pushes, submits to MQ/PR path, exits) - Use
gt patrol report— for patrol agents to cycle (replaces squash+new pattern) - File discovered work —
bd createfor bugs found, don't fix them yourself
File: docs/concepts/polecat-lifecycle.md
Polecat Lifecycle
Understanding the three-layer architecture of polecat workers
Overview
Polecats have three distinct lifecycle layers that operate independently. The
key design principle: clean completion retires the live polecat session.
The agent identity and merge evidence persist, but completed sessions do not
return to the idle reuse pool.
Operating States
Polecats use these primary operating states:
| State | Description | How it happens |
|---|---|---|
| Working | Actively doing assigned work | Normal operation after gt sling |
| Idle | Available before assignment | Spawned or explicitly prepared for work |
| Done | Work completed and session retired | After gt done completes successfully |
| Stalled | Session stopped mid-work | Interrupted, crashed, or timed out without being nudged |
| Zombie | Completed work but failed to exit | gt done failed during cleanup |
State cycle (happy path):
┌──────────┐
│ IDLE │──── gt sling
└────┬─────┘
v
┌──────────┐
│ WORKING │<──── session active, hook set
└────┬─────┘
│ gt done
v
┌──────────┐
│ DONE │──── branch/MR evidence preserved, session exits
└──────────┘No idle reuse in the happy path. Polecats move: IDLE -> WORKING -> DONE.
Key distinctions:
- Working = actively executing. Session alive, hook set, doing work.
- Idle = not yet assigned and safe to use.
- Done = work done, session killed, waiting for cleanup/refinery outcome.
- Stalled = supposed to be working, but stopped. Needs Witness intervention.
- Zombie = finished work, tried to exit, but cleanup failed. Stuck in limbo.
The Retired Completion Model
Polecat identity persists after completing work, but the live session does not.
When a polecat finishes its assignment:
- Signals completion via
gt done - Pushes branch, submits MR to merge queue
- Clears its hook (work is done)
- Sets agent state to "done"
- Kills its own session using PID-excluding cleanup
- Leaves branch/MR metadata for Witness/refinery cleanup
The next gt sling allocates available capacity without reusing a completed
session that still has branch/MR or cleanup state attached.
Why Retire Sessions?
- Preserved identity — The polecat's agent bead, CV chain, and work history persist
- Simpler lifecycle — Clean completion has one terminal session path
- Done means retired — Session dies, cleanup/refinery owns remaining state
What About Pending Merges?
The Refinery owns the merge queue. Once gt done submits work:
- The branch is pushed to origin
- Work exists in the MQ, not in the polecat
- If rebase fails, Refinery creates a conflict-resolution task
- The completed polecat is not reused while pending MR or cleanup state remains
The Three Layers
The Problem: Three Concepts Were Conflated
Early designs treated polecats as monolithic. This caused recurring issues:
| Concept | Lifecycle | Old behavior |
|---|---|---|
| Identity | Long-lived (name, CV, ledger) | Destroyed on nuke |
| Sandbox | Per-assignment (worktree, branch) | Destroyed on nuke |
| Session | Ephemeral (Claude context window) | = polecat lifetime |
Separating these three layers keeps completed sessions out of the idle reuse pool,
preserves capability records (CV, completion history), and lets cleanup/refinery
own branch and worktree state after handoff.
Layer Summary
| Layer | Component | Lifecycle | Persistence |
|---|---|---|---|
| Identity | Agent bead, CV chain, work history | Permanent | Never dies |
| Sandbox | Git worktree, branch | Per active assignment/cleanup window | Created for work, retired after cleanup |
| Session | Claude (tmux pane), context window | Ephemeral per step | Cycles per step/handoff |
Identity Layer
The polecat's identity is permanent. It includes:
- Agent bead (created once, never deleted)
- CV chain (work history accumulates across all assignments)
- Mailbox and attribution record
Identity survives all session cycles and sandbox resets. In the HOP model, this IS
the polecat — everything else is infrastructure that comes and goes. See
Polecat Identity below for details.
Session Layer
The Claude session is ephemeral. It cycles frequently:
- After each molecule step (via
gt handoff) - On context compaction
- On crash/timeout
- After extended work periods
Key insight: Session cycling is normal operation, not failure. The polecat
continues working—only the Claude context refreshes.
Session 1: Steps 1-2 → handoff
Session 2: Steps 3-4 → handoff
Session 3: Step 5 → gt doneAll three sessions are the same polecat. The sandbox persists throughout.
Sandbox Layer
The sandbox is the git worktree—the polecat's working directory:
~/gt/gastown/polecats/Toast/This worktree:
- Exists while the polecat is active or awaiting cleanup
- Survives handoff/session cycles during an assignment
- Is not synced to main or branch-deleted by
gt done - Contains uncommitted work, staged changes, branch state during active work
Witness/refinery cleanup owns sandbox retirement after durable handoff. Explicitgt polecat nuke remains the manual destructive path.
Branch Preservation (After Completion)
When work completes, gt done leaves the feature branch and MR metadata intact:
# Handled by gt done
git push origin polecat/<name>/@<suffix>
# Branch and metadata remain available for refinery/review/cleanupWhen new work is slung:
# Create fresh branch from current main
git checkout -b polecat/<name>/<new-issue>+<timestamp>
# Start workingCompleted sandboxes are not treated as reusable idle worktrees while branch,
MR, or cleanup state remains attached.
Slot Layer
The slot is the name allocation from the polecat pool:
# Pool: [Toast, Shadow, Copper, Ash, Storm...]
# Toast is allocated to work gt-abcThe slot:
- Determines the sandbox path (
polecats/Toast/) - Maps to a tmux session (
gt-gastown-Toast) - Appears in attribution (
gastown/polecats/Toast) - Persists until explicit nuke
Correct Lifecycle
/* Detailed source-code truncated for AI context efficiency. */What "Recycle" Means
Session cycling: Normal. Claude restarts, sandbox stays, slot stays.
gt handoff # Session cycles, polecat continuesSandbox setup: For active work. gt sling prepares a fresh branch for the assignment.
gt sling gt-xyz gastown # Allocates capacity and prepares branchSession cycling happens constantly. Sandbox setup/cleanup is tied to assignments.
Anti-Patterns
Manual State Transitions
Anti-pattern:
gt polecat done Toast # DON'T: external state manipulation
gt polecat reset Toast # DON'T: manual lifecycle controlCorrect:
# Polecat signals its own completion:
gt done # (from inside the polecat session)
# Only explicit nuke destroys polecats:
gt polecat nuke Toast # (destroys sandbox, identity persists)Polecats manage their own session lifecycle. External manipulation bypasses verification.
Sandboxes Without Work (Idle vs Done vs Stalled)
An idle polecat has no hook, no session, and no completion/MR cleanup state —
this is available capacity.
A done polecat has completed work and exited, but branch/MR or cleanup state
may still be attached. It is not reusable until cleanup resolves that state.
A stalled polecat has a hook but no session — this is a failure:
- The session crashed and wasn't nudged back to life
- The hook was lost during a crash
- State corruption occurred
Recovery for stalled:
# Witness respawns the session in the existing sandbox
# Or, if unrecoverable:
gt polecat nuke Toast # Clean up the stalled polecat
gt sling gt-abc gastown # Respawn with fresh polecatConfusing Session with Sandbox
Anti-pattern: Thinking session restart = losing work.
# Session ends (handoff, crash, compaction)
# Work is NOT lost because:
# - Git commits persist in sandbox
# - Staged changes persist in sandbox
# - Molecule state persists in beads
# - Hook persists across sessionsThe new session picks up where the old one left off via gt prime.
Session Lifecycle Details
Sessions cycle for these reasons:
| Trigger | Action | Result |
|---|---|---|
gt handoff |
Voluntary | Clean cycle to fresh context |
| Context compaction | Automatic | Forced by Claude Code |
| Crash/timeout | Failure | Witness respawns |
gt done |
Completion | Session exits, polecat goes done |
All except gt done result in continued work. Only gt done signals completion
and retires the completed polecat session.
Witness Responsibilities
The Witness monitors polecats but does NOT:
- Force session cycles (polecats self-manage via handoff)
- Interrupt mid-step (unless truly stuck)
- Reuse polecats after completion while cleanup/MR state remains
The Witness DOES:
- Detect and nudge stalled polecats (sessions that stopped unexpectedly)
- Clean up zombie polecats (sessions where
gt donefailed) - Respawn crashed sessions
- Handle escalations from stuck polecats (polecats that explicitly asked for help)
Polecat Identity
Key insight: Polecat identity is permanent; sessions are ephemeral, sandboxes are persistent.
In the HOP model, every entity has a chain (CV) that tracks:
- What work they've done
- Success/failure rates
- Skills demonstrated
- Quality metrics
The polecat name (Toast, Shadow, etc.) is a slot from a pool — persistent until
explicit nuke. The agent identity that executes as that polecat accumulates a
work history across all assignments.
POLECAT IDENTITY (permanent) SESSION (ephemeral) SANDBOX (assignment-scoped)
├── CV chain ├── Claude instance ├── Git worktree
├── Work history ├── Context window ├── Branch
├── Skills demonstrated └── Dies on handoff └── Retired after cleanup
└── Credit for work or gt done by gt slingThis distinction matters for:
- Attribution - Who gets credit for the work?
- Skill routing - Which agent is best for this task?
- Cost accounting - Who pays for inference?
- Federation - Agents having their own chains in a distributed world
Implementation Status
As of 2026-03-07 (gt-o8g8 audit), all core lifecycle operations are shipped and
running in production. See design/polecat-lifecycle-patrol.md § 10
for the full implementation matrix and design/persistent-polecat-pool.md
for phase-by-phase shipping status.
Key files:
internal/cmd/done.go— work submission, done-state handoff, session retirementinternal/cmd/sling.go+polecat_spawn.go— capacity allocation, branch setupinternal/cmd/handoff.go— session cycling for all rolesinternal/witness/handlers.go— cleanup pipeline, POLECAT_DONE routing, zombie/orphan detectioninternal/polecat/manager.go— stale detection, done-state projection, pool management
Related Documentation
- Overview - Role taxonomy and architecture
- Molecules - Molecule execution and polecat workflow
- Propulsion Principle - Why work triggers immediate execution
- Polecat Lifecycle Patrol - Implementation details, cleanup stages, patrol coordination
- Persistent Polecat Pool - Pool management design and shipping status
File: docs/concepts/propulsion-principle.md
The Propulsion Principle
If you find something on your hook, YOU RUN IT.
Gas Town is a steam engine. Agents are pistons. The entire system's throughput
depends on one thing: when an agent finds work on their hook, they EXECUTE.
Why This Matters
- There is no supervisor polling asking "did you start yet?"
- The hook IS your assignment - it was placed there deliberately
- Every moment you wait is a moment the engine stalls
- Other agents may be blocked waiting on YOUR output
The Handoff Contract
When you were spawned, work was hooked for you. The system trusts that:
- You will find it on your hook
- You will understand what it is (
bd show/gt hook) - You will BEGIN IMMEDIATELY
This isn't about being a good worker. This is physics. Steam engines don't
run on politeness - they run on pistons firing. You are the piston.
Molecule Navigation: Key Enabler
Molecules enable propulsion by providing clear waypoints. You don't need to
memorize steps or wait for instructions - discover them:
Orientation Commands
gt hook # What's on my hook?
gt prime # Shows inline formula checklist
bd show # What's my assigned issue?The New Workflow: Inline Formula Steps
Formula steps are shown inline at prime time — no step beads to manage:
gt prime # See your checklist
# Work through each step in order
gt done # Submit and self-clean (polecats)
gt patrol report # Close + next cycle (patrol agents)No step closures, no bd mol current, no momentum-killing transitions.
The new workflow (propulsion):
bd close gt-abc.3 --continueOne command. Auto-advance. Momentum preserved.
The Propulsion Loop
1. gt hook # What's hooked?
2. bd mol current # Where am I?
3. Execute step
4. bd close <step> --continue # Close and advance
5. GOTO 2The Failure Mode We're Preventing
Polecat restarts with work on hook
→ Polecat announces itself
→ Polecat waits for confirmation
→ Witness assumes work is progressing
→ Nothing happens
→ Gas Town stopsStartup Behavior
- Check hook (
gt hook) - Work hooked → EXECUTE immediately
- Hook empty → Check mail for attached work
- Nothing anywhere → ERROR: escalate to Witness
Note: "Hooked" means work assigned to you. This triggers autonomous mode
even if no molecule is attached. Don't confuse with "pinned" which is for
permanent reference beads.
The Capability Ledger
Every completion is recorded. Every handoff is logged. Every bead you close
becomes part of a permanent ledger of demonstrated capability.
- Your work is visible
- Redemption is real (consistent good work builds over time)
- Every completion is evidence that autonomous execution works
- Your CV grows with every completion
This isn't just about the current task. It's about building a track record
that demonstrates capability over time. Execute with care.
File: docs/contrib-harnesses/polecat-pr-flow/polecat.md
PR-Flow Polecat Policy
Rig Policy — overrides formula instructions where they conflict.
This rig uses a polecat → GitHub PR → human review flow. After pushing
your branch, you MUST ensure a GitHub PR is open for it before runninggt done.
This overrides the canonical Refinery merge-queue assumption embedded inmol-polecat-work. gt done is still the completion signal — but in this
rig, a visible PR is the gating artifact for review.
Required steps after implementation
- Push the branch explicitly (do not rely on
gt doneto push):bashgit push -u origin HEAD - Check whether a PR already exists for the branch:bash
gh pr view "$(git branch --show-current)" >/dev/null 2>&1 - If no PR exists, create one against the base branch:bash
gh pr create --fill --base main - Only then run
gt done.
Do NOT
- Run
gt donewithout a PR open — the review loop breaks. - Merge your own PR. A maintainer or the merge queue handles merging.
- Push directly to
main.
If `gh` commands fail
Auth, rate-limit, missing PR template, or unknown base branch — do NOT skip
PR creation to unblock yourself. Escalate to your Witness:
gt mail send <rig>/witness -s "HELP: gh pr create failed" -m "Branch: $(git branch --show-current)
Error:
Tried: <what you attempted>"File: docs/contrib-harnesses/polecat-pr-flow/README.md
polecat-pr-flow
A reference harness for rigs that gate polecat work on a GitHub PR rather
than the canonical Refinery merge-queue flow.
It instructs polecats, after their final build/pre-verify passes, to push
their branch and open (or confirm) a GitHub PR before running gt done.
What's in here
| File | Purpose |
|---|---|
polecat.md |
Role directive for polecats in a PR-flow rig. Broad guardrail — applies to any formula a polecat runs. |
mol-polecat-work.toml |
Formula overlay that appends a PR-creation step to mol-polecat-work's submit-and-exit step. Surgical — only affects this formula. |
Both layers are intentional: the directive sets the rig-level expectation
("open PRs, don't merge them yourself"), and the overlay wires the concrete
commands into the workflow a polecat actually sees at gt prime time.
Install
# Role directive (rig-scoped)
mkdir -p ~/gt/<rig>/directives
cp polecat.md ~/gt/<rig>/directives/polecat.md
# Formula overlay (rig-scoped)
mkdir -p ~/gt/<rig>/formula-overlays
cp mol-polecat-work.toml ~/gt/<rig>/formula-overlays/mol-polecat-work.tomlReplace <rig> with your rig's name (e.g. gastown, longeye). For
town-wide installation, drop the <rig>/ segment — but this is almost
never what you want, since different rigs legitimately use different flows.
Verify it's active
# Validate overlay step IDs against the current formula
gt doctor
# Expect: overlay-health: N overlay(s) healthy
# Inspect the rendered formula with the overlay applied
gt formula overlay show mol-polecat-work --rig <rig>
# See the directive text that will be injected at prime time
gt directive show polecat --rig <rig>
# End-to-end: see what a polecat would see
gt prime --explain
# Expect: "Formula overlay: applying 1 override(s) for mol-polecat-work (rig=<rig>)"What this does / does not do
Does:
- Tells the polecat to push and open a PR before
gt done - Sets a rig-level policy that a PR is the review artifact
- Surfaces
gh pr createfailure as an escalation to Witness, not a silent skip
Does not:
- Modify
gt donebehavior (no Go changes) - Force PR creation via framework-level validation (agents can still misbehave)
- Merge the PR (that's a maintainer / merge-queue concern)
- Replace the directive or overlay if your rig also uses other customizations
— merge this content with your existing files rather than overwriting them
When to fork this
If your rig needs additional PR-flow constraints (required reviewers, specific
labels, CODEOWNERS enforcement, CI checks before gt done), copy this harness
and adapt it. The point is a starting template, not a drop-in product.
Fixing an existing PR (gh#3602)
By default gt sling creates a fresh polecat/<name>/+<ts> branch frommain, which means re-slinging a bead with an existing open PR opens a
duplicate PR rather than reusing the original. To resume an existing PR
branch, use --branch or --pr:
# Resume by branch name (works for any open or stashed branch)
gt sling <rig> --branch polecat/example/gh-1234+abcdef
# Resume by PR number (resolves the head ref via `gh pr view`)
gt sling <rig> --pr 1234The polecat's worktree HEAD will land on the named branch, so its commits
extend the existing PR's history and gt done pushes back to the same ref.
Constraints:
--branchand--prare mutually exclusive.- Neither can be combined with
--base-branch(resume implies its own start point). - The
--prform requires theghCLI to be authenticated against the repo.
The resume branch name is also exposed to formulas as the resume_branch
variable, alongside the existing base_branch, so overlays can react to it.
2. Official Technical Reference & Guides (gastownhall/docs)
Mintlify Starter Kit
Use the starter kit to get your docs deployed and ready to customize.
Click the green Use this template button at the top of this repo to copy the Mintlify starter kit. The starter kit contains examples with
- Guide pages
- Navigation
- Customizations
- API reference pages
- Use of popular components
Follow the full quickstart guide
AI-assisted writing
Set up your AI coding tool to work with Mintlify:
npx skills add https://mintlify.com/docsThis command installs Mintlify's documentation skill for your configured AI tools like Claude Code, Cursor, Windsurf, and others. The skill includes component reference, writing standards, and workflow guidance.
See the AI tools guides for tool-specific setup.
Development
Install the Mintlify CLI to preview your documentation changes locally. To install, use the following command:
npm i mintRun the following command at the root of your documentation, where your docs.json is located:
mint devView your local preview at http://localhost:3000.
Publishing changes
Install our GitHub app from your dashboard to propagate changes from your repo to your deployment. Changes are deployed to production automatically after pushing to the default branch.
Need help?
Troubleshooting
- If your dev environment isn't running: Run
mint updateto ensure you have the most recent version of the CLI. - If a page loads as a 404: Make sure you are running in a folder with a valid
docs.json.