Polecats have exactly three operating conditions - there is no idle pool:
- Working: session active, doing assigned work
- Stalled: session stopped unexpectedly, never nudged back
- Zombie: gt done called but cleanup failed
Key clarifications:
- These are SESSION states; polecat identity persists across sessions
- "Stalled" and "zombie" are detected conditions, not stored states
- The status:idle label only applies to persistent agents, not polecats
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds per-agent-type health tracking to the Mayor's tmux statusline, showing
working/idle counts for Polecats, Witnesses, Refineries, and Deacon.
All agent types are always displayed, even when no agents of that type are
running (shows as '0/0 😺').
Format: active: 4/4 😺 6/10 👁️ 7/10 🏭 1/1 ⛪
Co-authored-by: gastown/crew/dennis <steve.yegge@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Role beads (hq-*-role) are templates that define role characteristics.
They are created during gt install but creation may fail silently.
Without role beads, agents fall back to defaults.
Changes:
- Add beads.AllRoleBeadDefs() as single source of truth for role bead definitions
- Update gt install to use shared definitions
- Add doctor check that detects missing role beads (warning, not error)
- Doctor --fix creates missing role beads
Fixes#371
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add two tests:
- TestAddWithOptions_HasAgentsMD: verifies AGENTS.md exists in worktree
after creation when it's in git
- TestAddWithOptions_AgentsMDFallback: verifies fallback copy works when
AGENTS.md is not in git but exists in mayor/rig
Fixes: gt-sq1.3
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When creating or repairing worktrees, if AGENTS.md doesn't exist after
checkout (e.g., stale fetch or local-only file), copy it from mayor/rig.
This ensures polecats always have the critical "land the plane" instructions.
Applied to both AddWithOptions and RepairWorktreeWithOptions for
consistency. Errors are non-fatal (warning only).
Fixes: gt-sq1.2
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(doctor): filter bd "Note:" messages from custom types check
bd outputs "Note: No git repository initialized..." to stdout when
running outside a git repo, which was contaminating the custom types
parsing and causing false warnings.
- Use Output() instead of CombinedOutput() to avoid stderr
- Filter out lines starting with "Note:" from stdout
Co-Authored-By: Claude <noreply@anthropic.com>
* test(doctor): add unit tests for custom types Note: filtering
Extract parseConfigOutput helper function and add tests verifying
that bd "Note:" informational messages are properly filtered from
config output. Tests fail without the fix and pass with it.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude <noreply@anthropic.com>
* fix(beads): prevent routes.jsonl corruption from bd auto-export
When issues.jsonl doesn't exist, bd's auto-export mechanism writes
issue data to routes.jsonl, corrupting the routing configuration.
Changes:
- install.go: Create issues.jsonl before routes.jsonl at town level
- manager.go: Create issues.jsonl in rig beads; don't create routes.jsonl
(rig-level routes.jsonl breaks bd's walk-up routing to town routes)
- Add integration tests for routes.jsonl corruption prevention
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(doctor): add check to detect and fix rig-level routes.jsonl
Add RigRoutesJSONLCheck to detect routes.jsonl files in rig .beads
directories. These files break bd's walk-up routing to town-level
routes.jsonl, causing cross-rig routing failures.
The fix unconditionally deletes rig-level routes.jsonl files since
bd will auto-export to issues.jsonl on next run.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test(rig): add verification that routes.jsonl does NOT exist in rig .beads
Add explicit test assertion and detailed comment explaining why rig-level
routes.jsonl files must not exist (breaks bd walk-up routing to town routes).
Also verify that issues.jsonl DOES exist (prevents bd auto-export corruption).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(doctor): ensure town root route exists in routes.jsonl
The RoutesCheck now detects and fixes missing town root routes (hq- -> .).
This can happen when routes.jsonl is corrupted or was created without the
town route during initialization.
Changes:
- Detect missing hq- route in Run()
- Add hq- route in Fix() when missing
- Handle case where routes.jsonl is corrupted (regenerate with town route)
- Add comprehensive unit tests for route detection and fixing
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test(beads): fix routing integration test for routes.jsonl corruption
The TestBeadsRoutingFromTownRoot test was failing because bd's auto-export
mechanism writes issue data to routes.jsonl when issues.jsonl doesn't exist.
This corrupts the routing configuration.
Fix: Create empty issues.jsonl after bd init to prevent corruption.
This mirrors what gt install does to prevent the same bug.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When using `gt sling <formula> --on <bead>`, the code was only passing
the `feature` variable (set to bead title). This broke formulas that
expect `issue` (set to bead ID), like mol-polecat-work.
Now passes both common variables:
- feature: bead title (for shiny-style formulas)
- issue: bead ID (for mol-polecat-work-style formulas)
This allows either formula type to work with --on without requiring
the user to manually specify variables.
Fixes#355
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Role beads created by gt install were missing the gt:role label required
by GetRoleConfig(), causing witness startup to fail with:
"bead hq-witness-role is not a role bead (missing gt:role label)"
This regression was introduced in 96970071 which migrated from type-based
to label-based bead classification. The install code used raw exec.Command
instead of the beads API, so it wasn't updated to add labels.
Changes:
- Use bd.CreateWithID() API which auto-converts Type:"role" to gt:role label
- Add RoleLabelCheck doctor migration to fix existing installations
- Add comprehensive unit tests with mocked dependencies
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Bare clones don't have refs/remotes/origin/* populated by default.
The configureRefspec fix (a91e6cd6) set up the fetch config but didn't
actually run a fetch, leaving origin/main unavailable.
This caused polecat worktree creation to fail with:
fatal: invalid reference: origin/main
Fixes:
1. Add git fetch after configureRefspec in bare clone setup
2. Add fetch before polecat worktree creation (ensures latest code)
The second fix matches RepairWorktreeWithOptions which already had a fetch.
Related: #286
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add severity-based routing for escalations with config-driven targets.
Changes:
- EscalationConfig type with severity routes and external channels
- beads/beads_escalation.go: Escalation bead operations (create/ack/close/list)
- Refactored gt escalate command with subcommands:
- list: Show open escalations
- ack: Acknowledge an escalation
- close: Resolve with reason
- stale: Find unacknowledged escalations past threshold
- show: Display escalation details
- Added TypeEscalationAcked and TypeEscalationClosed event types
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add --debug flag for troubleshooting crew attach issues. Shows:
- Current working directory
- Detected rig and crew name
- Computed session ID
- Whether inside tmux
- Which session we are attaching to
Also adds Attaching to session message before attach.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
If the agent bead doesn't exist when gt done tries to clear the hook,
return early instead of failing. This happens for polecats created
before identity beads existed.
gt done must be resilient and forgiving - the important thing is work
gets submitted to merge queue, not that cleanup succeeds.
Fixes: hq-i26n2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Boot sessions run in `deacon/dogs/boot/` but were incorrectly detected
as deacon role because the deacon check matched first. This caused Boot
to receive Deacon's context instead of Boot-specific context.
Changes:
- Add RoleBoot constant
- Add boot path detection before deacon check in detectRole()
- Add boot case in buildRoleAnnouncement()
- Add boot case in getAgentIdentity() (returns "boot")
- Add boot case in getAgentBeadID() (uses deacon's bead as subprocess)
The boot.md.tmpl template already exists and will now be used.
Fixes#318
Since the self-cleaning model (Jan 10), polecats push branches to origin
before `gt done`. The refinery was only deleting local branches after
merge, causing stale `polecat/*` branches to accumulate on the remote.
Now deletes both local and remote branches after successful merge.
Uses existing `git.DeleteRemoteBranch()` function. Remote deletion is
non-fatal if the branch doesn't exist.
Fixes#359
* test(util): add comprehensive tests for atomic write functions
Add tests for:
- File permissions
- Empty data handling
- Various JSON types (string, int, float, bool, null, array, nested)
- Unmarshallable types error handling
- Read-only directory permission errors
- Concurrent writes
- Original content preservation on failure
- Struct serialization/deserialization
- Large data (1MB)
* test(connection): add edge case tests for address parsing
Add comprehensive test coverage for ParseAddress edge cases:
- Empty/whitespace/slash-only inputs
- Leading/trailing slash handling
- Machine prefix edge cases (colons, empty machine)
- Multiple slashes in polecat name (SplitN behavior)
- Unicode and emoji support
- Very long addresses
- Special characters (hyphens, underscores, dots)
- Whitespace in components
Also adds tests for MustParseAddress panic behavior and RigPath method.
Closes: gt-xgjyp
* test(checkpoint): add comprehensive test coverage for checkpoint package
Tests all public functions: Read, Write, Remove, Capture, WithMolecule,
WithHookedBead, WithNotes, Age, IsStale, Summary, Path.
Edge cases covered: missing file, corrupted JSON, stale detection.
Closes: gt-09yn1
* test(lock): add comprehensive tests for lock package
Add lock_test.go with tests covering:
- LockInfo.IsStale() with valid/invalid PIDs
- Lock.Acquire/Release lifecycle
- Re-acquiring own lock (session refresh)
- Stale lock cleanup during Acquire
- Lock.Read() for missing/invalid/valid files
- Lock.Check() for unlocked/owned/stale scenarios
- Lock.Status() string formatting
- Lock.ForceRelease()
- processExists() helper
- FindAllLocks() directory scanning
- CleanStaleLocks() with mocked tmux
- getActiveTmuxSessions() parsing
- splitOnColon() and splitLines() helpers
- DetectCollisions() for stale/orphaned locks
Coverage: 84.4%
* test(keepalive): add example tests demonstrating usage patterns
Add ExampleTouchInWorkspace, ExampleRead, and ExampleState_Age to
serve as documentation for how to use the keepalive package.
* fix(test): correct boundary test timing race in checkpoint_test.go
The 'exactly threshold' test case was flaky due to timing: by the time
time.Since() runs after setting Timestamp, microseconds have passed,
making age > threshold. Changed expectation to true since at-threshold
is effectively stale.
---------
Co-authored-by: slit <gt@gastown.local>
* test(costs): add failing test for multi-location session event query
Add integration test that verifies querySessionEvents finds session.ended
events from both town-level and rig-level beads databases.
The test demonstrates the bug: events created by rig-level agents (polecats,
witness, etc.) are stored in the rig's .beads database, but querySessionEvents
only queries the town-level beads, missing rig-level events.
Test setup:
- Creates town with gt install
- Adds rig with gt rig add (separate beads DB)
- Creates session.ended event in town beads (simulating mayor)
- Creates session.ended event in rig beads (simulating polecat)
- Verifies querySessionEvents finds both events
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(costs): query all beads locations for session events
querySessionEvents previously only queried the town-level beads database,
missing session.ended events created by rig-level agents (polecats, witness,
refinery, crew) which are stored in each rig's own .beads database.
The fix:
- Load rigs from mayor/rigs.json
- Query each rig's beads location in addition to town-level beads
- Merge and deduplicate results by session ID + timestamp
This ensures `gt costs` finds all session cost events regardless of which
agent's beads database they were recorded in.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When a repo with tracked .beads/ is added as a rig, the beads.db file
doesn't exist because it's gitignored. Previously, bd init was only run
if prefix detection succeeded. If there were no issues in issues.jsonl,
detection failed and bd init was never run, causing "Error: no beads
database found" when running bd commands.
Changes:
- Always run bd init when tracked beads exist but db is missing
- Detect prefix from existing issues in issues.jsonl
- Only error on prefix mismatch if user explicitly passed --prefix
- If no issues exist, use the derived/provided prefix
Fixes#72
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update identity.md to reflect the implemented polecat identity model.
The previous text incorrectly stated "Polecats are ephemeral... no
persistent polecat CV" which contradicted the polecat-lifecycle.md
docs and the gt polecat identity implementation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
sessionWorkDir had cases for mayor, deacon, crew, witness, and refinery
but not polecats. When gt handoff was run from a polecat session like
gt-tanwa_info-slit, it failed with "unknown session type".
Fix uses session.ParseSessionName to parse the session name and extract
rig/name for polecat sessions, mapping to <townRoot>/<rig>/polecats/<name>.
Fixes: gm-lie6
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The --naked flag (skip tmux session creation) was a vestige of an earlier
design requiring manual session management. With the current polecat
architecture where polecats are witness-managed, ephemeral, and self-deleting
after task completion, manual session management is no longer needed.
The flag also created invalid states (e.g., slinging to crew --naked left
them unreachable since crew require tmux sessions for communication).
Closes gt-xhn5s
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Plugin System (gt-n08ix):
- Deacon-dispatched periodic automation
- Dog execution model (non-blocking)
- Wisps for state tracking (no state.json)
- Gate types: cooldown, cron, condition, event
- First plugin: rebuild-gt for stale binary detection
Escalation System (gt-i9r20):
- Unified gt escalate command with severity routing
- Config-driven: settings/escalation.json
- Escalation beads for tracking
- Stale escalation re-escalation
- Actions: bead, mail, email, sms
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add new `gt polecat identity` (alias: `id`) subcommand group with commands:
- add <rig> [name]: Create identity bead (auto-generates name if omitted)
- list <rig>: List polecat identity beads with session/worktree status
- show <rig> <name>: Show identity details and CV (work history)
- rename <rig> <old> <new>: Rename identity, preserving CV chain
- remove <rig> <name>: Remove identity with safety checks
Each command manipulates agent beads with role_type=polecat. Safety checks
prevent removal of identities with active sessions or work on hook.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add deprecation warning pointing users to 'gt polecat identity add':
- Cobra Deprecated field emits automatic warning on command use
- Custom warning in runPolecatAdd for prominent stderr output
- Updated help text with deprecation notice and new command example
The command still functions but will be removed in v1.0.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Improve tmux statusline: sort rigs by activity and add visual grouping
- Sort rigs by running state, then polecat count, then operational state
- Add visual grouping with | separators between state groups
- Show process state with icons (🟢 both running, 🟡 one running, 🅿️ parked, 🛑 docked, ⚫ idle)
- Display polecat counts for active rigs
- Improve icon spacing: 2 spaces after Park emoji, 1 space for others
* Fix golangci-lint warnings
- Check error return from os.Setenv
- Check error return from lock.Unlock
- Mark intentionally unused parameters with _
---------
Co-authored-by: joshuavial <git@codewithjv.com>
When polecats run 'gt done' without --cleanup-status, the witness may
prematurely nuke the worktree before the refinery can merge.
This fix auto-detects git state:
- uncommitted: has uncommitted changes
- stash: has stashed changes
- unpushed: branch not pushed or has unpushed commits
- clean: everything pushed
Uses BranchPushedToRemote() which properly handles polecat branches
that don't have upstream tracking (compares against origin/main).
On error, defaults to 'unpushed' to prevent accidental data loss.
Fixes: #342
Co-authored-by: mayor <mayor@gastown.local>
When running `gt install --wrappers` in an existing Gas Town HQ,
the command now installs wrappers directly without requiring --force
or recreating the entire HQ structure.
Previously, `gt install --wrappers` would fail with "directory is
already a Gas Town HQ" unless --force was used, which would then
unnecessarily reinitialize the entire workspace.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add documentation to make the formula package more discoverable and
demonstrate its value as a reusable workflow definition library.
The formula package provides TOML-based workflow definitions with:
- Type inference (convoy, workflow, expansion, aspect)
- Comprehensive validation
- Cycle detection in dependency graphs
- Topological sorting (Kahn's algorithm)
- Ready-step computation for parallel execution
New files:
- doc.go: Package-level godoc with examples and API overview
- README.md: User guide with installation, quick start, and API reference
- example_test.go: Runnable examples for godoc and testing
The package has 130% test coverage (1,200 LOC tests for 925 LOC code)
and only depends on github.com/BurntSushi/toml.
gt prime recovers context inside an existing session (after compaction,
clear, or new session). It's not an alternative to 'gt mayor attach'
which starts a new Mayor session.
Explains that autonomous roles (polecat, witness, refinery, deacon)
get automatic mail injection on startup since they operate without
human prompting. Non-autonomous roles (mayor, crew) skip this.
Closes: gt-pawy3
Explains the integer-to-string conversion behavior:
- Direct rune conversion for single digits (efficiency)
- Iterative digit extraction for larger numbers
- Avoids strconv import for simple formatting
Added "Issue IDs" section to Core Concepts explaining that Gas Town
uses Beads' auto-generated short IDs (e.g., gt-x7k2m) rather than
sequential numbers like GitHub issues.
Updated all example issue IDs throughout the README to use realistic
Beads-style IDs instead of confusing "issue-123" format.
Fixes: GitHub #309
When bd --no-daemon show <id> does not find an issue, it incorrectly exits
with code 0 (success) but writes the error to stderr and leaves stdout empty.
This causes JSON parse failures throughout gt when code tries to unmarshal
the empty stdout.
This PR handles the bug defensively in all affected code paths:
- beads.go run(): Detect empty stdout + non-empty stderr as error
- beads.go wrapError(): Add 'no issue found' to ErrNotFound patterns
- sling.go: Check len(out) == 0 in multiple functions
- convoy.go getIssueDetails(): Check stdout.Len() == 0
- prime_molecule.go: Check stdout.Len() == 0
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The verifyFormulaExists function now checks for non-empty output,
so the test stub must output something for formula show commands.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix beads.run() to always explicitly set BEADS_DIR based on the working
directory or explicit override
- This prevents inherited environment variables (e.g., from mayor session
with BEADS_DIR=/home/erik/gt/.beads) from causing prefix mismatch errors
when creating agent beads for rigs
- Update polecat manager to use NewWithBeadsDir for explicitness
- Add comprehensive test coverage for BEADS_DIR routing and validation
- Add SessionLister interface for deterministic orphan session testing
Root cause: When BEADS_DIR was set in the parent environment, all bd
commands used the town database (hq- prefix) instead of the rig database
(gt- prefix), causing "prefix mismatch: database uses 'hq' but you
specified 'gt'" errors during polecat spawn.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes CI lint failures by handling unchecked error returns and marking
unused parameters with blank identifiers.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Simplified notification delivery to always use NudgeSession, since all
sessions are Claude Code (or similar AI sessions), never plain terminals.
This removes unnecessary complexity and the IsClaudeRunning check.