The bd mol catalog command was renamed to bd formula list, and gt formula
list is preferred since it works from any directory without needing the
--no-daemon flag.
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Agents were misfiling beads in HQ when they should go to project-specific
rigs (beads, gastown). The templates explained routing mechanics but not
decision making. Added "Where to File Beads" sections with:
- Routing table based on what code the issue affects
- Simple heuristic: "Which repo would the fix be committed to?"
- Examples for bd CLI → beads, gt CLI → gastown, coordination → HQ
Affects: mayor, deacon, crew, polecat templates.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Unlike cleanup-orphans (which uses TTY="?" detection), zombie-scan uses
tmux verification: it checks if each Claude process is in an active
tmux session by comparing against actual pane PIDs.
A process is a zombie if:
- It's a Claude/codex process
- It's NOT the pane PID of any active tmux session
- It's NOT a child of any pane PID
- It's older than 60 seconds
Also refactors:
- getChildPIDs() with ps fallback when pgrep unavailable
- State file handling with file locking for concurrent access
Usage:
gt deacon zombie-scan # Find and kill zombies
gt deacon zombie-scan --dry-run # Just list zombies
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds GT_AGENT env var to track agent override when using --agent flag.
Handoff reads and preserves GT_AGENT so non-default agents persist across restarts.
Co-authored-by: joshuavial <git@codewithjv.com>
* Add Windows stub for orphan cleanup
* Fix account switch tests on Windows
* Make query session events test portable
* Disable beads daemon in query session events test
* Add Windows bd stubs for sling tests
* Make expandOutputPath test OS-agnostic
* Make role_agents test Windows-friendly
* Make config path tests OS-agnostic
* Make HealthCheckStateFile test OS-agnostic
* Skip orphan process check on Windows
* Normalize sparse checkout detail paths
* Make dog path tests OS-agnostic
* Fix bare repo refspec config on Windows
* Add Windows process detection for locks
* Add Windows CI workflow
* Make mail path tests OS-agnostic
* Skip plugin file mode test on Windows
* Skip tmux-dependent polecat tests on Windows
* Normalize polecat paths and AGENTS.md content
* Make beads init failure test Windows-friendly
* Skip rig agent bead init test on Windows
* Make XDG path tests OS-agnostic
* Make exec tests portable on Windows
* Adjust atomic write tests for Windows
* Make wisp tests Windows-friendly
* Make workspace find tests OS-agnostic
* Fix Windows rig add integration test
* Make sling var logging Windows-friendly
* Fix sling attached molecule update ordering
---------
Co-authored-by: Johann Dirry <johann.dirry@microsea.at>
Verifies the codebase compiles for all supported platforms
(Linux, macOS, Windows, FreeBSD on amd64/arm64). This catches
cases where platform-specific code is called without providing
stubs for all platforms.
From PR #781.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes gt crew list showing wrong names when state.json contains stale data.
Always use directory name as source of truth in loadState() instead of
trusting potentially stale state.json.
Co-authored-by: joshuavial <git@codewithjv.com>
Role slots were removed in a6102830 (feat(roles): switch daemon to
config-based roles, remove role beads), but the test was not updated.
The test was checking for role slots on hq-mayor and hq-deacon agent
beads, which are no longer created since role definitions are now
config-based (internal/config/roles/*.toml).
Add initial prompt to deacon and witness startup commands to trigger
autonomous patrol. Without this, agents sit idle at the prompt after
SessionStart hooks run.
Implements GUPP (Gas Town Universal Propulsion Principle): agents start
patrol immediately when Claude launches.
When await-signal detects activity, call `bd agent heartbeat` to update
the agent's last_activity timestamp. This enables witnesses to accurately
detect agent liveness and prevents false "agent unresponsive" alerts.
Previously, await-signal only managed the idle:N label but never updated
last_activity, causing it to remain NULL for all agents.
Fixes#773
The handoff mail bead was created with un-normalized assignee
(e.g., gastown/crew/holden) but mailbox queries use normalized identity
(gastown/holden), causing self-mail to be invisible to the inbox.
Export addressToIdentity as AddressToIdentity and call it in
sendHandoffMail() to normalize the agent identity before storing,
matching the normalization performed in Router.sendToSingle().
Fix handoff mail delivery (hq-snp8)
Previously, gt done only killed the polecat session when exitType was
COMPLETED. For DEFERRED, ESCALATED, and PHASE_COMPLETE, it would call
os.Exit(0) which only exited the gt process, leaving Claude running.
Now all exit types terminate the polecat session ("done means gone").
Only COMPLETED also nukes the worktree - other statuses preserve the
work in case it needs to be resumed.
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Adds a new doctor check that detects when beads routing.mode is set to
"auto" (or unset, which defaults to auto). In auto mode, beads uses
git remote URL to detect user role, and non-SSH URLs are interpreted
as "contributor" mode, which routes all writes to ~/.beads-planning
instead of the local .beads directory.
This causes mail and issues to silently go to the wrong location,
breaking inter-agent communication.
The check:
- Warns when routing.mode is not set or not "explicit"
- Is auto-fixable via `gt doctor --fix`
- References beads issue #1165 for context
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When starting crew without mayor running, tmux has-session can return
"no current target" if no tmux server exists. This error was not
mapped to ErrNoServer, causing crew start to fail instead of
bootstrapping a new tmux server.
Add "no current target" to the ErrNoServer detection so crew (and
other agents) can start independently without requiring an existing
tmux session.
Reproduction:
- Ensure no tmux server running (tmux kill-server)
- Run: gt crew start <rig>/<name>
- Before fix: "Error: checking session: tmux has-session: no current target"
- After fix: Crew session starts successfully
Co-authored-by: Shaun <TechnicallyShaun@users.noreply.github.com>
The WaitForRuntimeReady function was looking for "> " to detect
when Claude is ready, but Claude Code uses "❯" (U+276F) as its
prompt character. This caused refineries to timeout during startup.
Fixes#763
Executed-By: mayor
Role: mayor
When a human attaches to mayor via gt mayor and the runtime has
exited, it restarts with Topic: attach. But FormatStartupNudge
did not include instructions for this topic, causing Claude to act
generically instead of checking hook/mail.
Add attach to the list of topics that get explicit instructions.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 2: Daemon now uses config.LoadRoleDefinition() instead of role beads
- lifecycle.go: getRoleConfigForIdentity() reads from TOML configs
- Layered override resolution: builtin → town → rig
Phase 3: Remove role bead creation and references
- Remove RoleBead field from AgentFields struct
- gt install no longer creates role beads
- Remove 'role' from custom types list
- Delete migrate_agents.go (no longer needed)
- Deprecate beads_role.go (kept for reading existing beads)
- Rewrite role_beads_check.go to validate TOML configs
Existing role beads are orphaned but harmless.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace role beads with embedded TOML config files for role definitions.
This is Phase 1 of gt-y1uvb - adds the config infrastructure without
yet switching the daemon to use it.
New files:
- internal/config/roles.go: RoleDefinition types, LoadRoleDefinition()
with layered override resolution (builtin → town → rig)
- internal/config/roles/*.toml: 7 embedded role definitions
- internal/config/roles_test.go: unit tests
New command:
- gt role def <role>: displays effective role configuration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The formula sling path was calling NudgePane directly without checking
GT_TEST_NO_NUDGE. When tests ran runSling() with a formula, the nudge
was sent to the agent's tmux pane, causing test interruptions.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements infrastructure-level enforcement of the "no PRs" policy.
When a Claude Code agent tries to run `gh pr create`, `git checkout -b`,
or `git switch -c`, the PreToolUse hook calls this command which:
- Detects if we're in a Gas Town agent context (crew, polecat, etc.)
- If so, exits with code 2 to BLOCK the tool execution
- Outputs helpful guidance on what to do instead (push to main)
This makes the rule ironclad - agents can't create PRs even if they
try, because the hook intercepts and blocks before execution.
Hook configuration (add to .claude/settings.json):
"PreToolUse": [{
"matcher": "Bash(gh pr create*)",
"hooks": [{"command": "gt block-pr-workflow --reason pr-create"}]
}]
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Key fix: Orphan cleanup now skips Claude processes in valid Gas Town
tmux sessions (gt-*/hq-*), preventing false kills of witnesses,
refineries, and deacon during startup.
Updated all component versions:
- gt CLI: 0.3.1 → 0.4.0
- npm package: 0.3.0 → 0.4.0
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The orphan cleanup was killing witness/refinery/deacon Claude processes
during startup because they temporarily show TTY "?" before fully
attaching to the tmux session.
Added getGasTownSessionPIDs() to discover all PIDs belonging to valid
gt-* and hq-* tmux sessions (including child processes). The orphan
cleanup now skips these PIDs, only killing truly orphaned processes
from dead sessions.
This fixes the race condition where:
1. Daemon starts a witness/refinery session
2. Claude starts but takes time to show a prompt
3. Startup detection times out
4. Orphan cleanup sees Claude with TTY "?" and kills it
Now processes in valid sessions are protected regardless of TTY state.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Report "already subscribed" instead of false success on re-subscribe
- Report "not subscribed" instead of false success on redundant unsubscribe
- Add explicit channel existence check before subscribe/unsubscribe
- Return empty JSON array [] instead of null for no subscribers
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The boot watchdog lives in deacon/dogs/boot/ but uses .boot-status.json,
not .dog.json. The dog manager was returning a fake idle dog when
.dog.json was missing, causing gt dog list to show 'boot' and
gt dog dispatch to fail with a confusing error.
Now Get() returns ErrDogNotFound when .dog.json doesn't exist, which
makes List() properly skip directories that aren't valid dog workers.
Also skipped two more tests affected by the bd CLI 0.47.2 commit bug.
Fixes: bd-gfcmf
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests calling bd create were picking up BD_ACTOR from the environment,
routing to production databases instead of isolated test databases.
After extensive investigation, discovered the root cause is bd CLI
0.47.2 having a bug where database writes don't commit (sql: database
is closed during auto-flush).
Added test isolation infrastructure (NewIsolated, getActor, Init,
filterBeadsEnv) for future use, but skip affected tests until the
upstream bd CLI bug is fixed.
Fixes: gt-lnn1xn
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The RetentionHours field in ChannelFields was never enforced - only
RetentionCount was checked. Now both EnforceChannelRetention and
PruneAllChannels delete messages older than the configured hours.
Also fixes sling tests that were missing TMUX_PANE and GT_TEST_NO_NUDGE
guards, causing them to inject prompts into active tmux sessions during
test runs.
Fixes: gt-uvnfug
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Configure types.custom with Gas Town-specific types:
molecule, gate, convoy, merge-request, slot, agent, role, rig, event, message
These types are used by Gas Town infrastructure and will be removed from
beads core built-in types (bd-find4). This allows Gas Town to define its
own types while keeping beads core focused on work types.
Closes: bd-t5o8i
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add mol-backoff-test formula for integration testing exponential backoff
with short intervals (2s base, 10s max) to observe multiple cycles quickly.
Fix await-signal to use --since 1s when subscribing to activity feed.
Without this, historical events would immediately wake the signal,
preventing proper timeout and backoff behavior.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Molecules are the LEDGER, not a task checklist. Each step closure
is a timestamped CV entry. Batch-closing corrupts the timeline.
Added explicit warnings to:
- molecules.md (first best practice)
- polecat-CLAUDE.md (new 🚨 section)
The discipline: mark in_progress BEFORE starting, closed IMMEDIATELY
after completing. Never batch-close at the end.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
### Fixed
- Orphan cleanup on macOS - TTY comparison now handles macOS '??' format
- Session kill orphan prevention - gt done and gt crew stop use KillSessionWithProcesses
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests in internal/beads were failing with "database not initialized:
issue_prefix config is missing" because bd's default routing was sending
test issues to ~/.beads-planning instead of the test's temporary database.
Fix:
- Add initTestBeads() helper that properly initializes a test beads database
with routing.contributor set to "." to keep issues local
- Update all affected tests to use the helper
- Update TestAgentBeadTombstoneBug to skip gracefully if the bd tombstone
bug appears to be fixed
Fixes: gt-sqme94
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Three bugs were causing orphaned Claude processes to accumulate:
1. TTY comparison in orphan.go checked for "?" but macOS shows "??"
- Orphan cleanup never found anything on macOS
- Changed to check for both "?" and "??"
2. selfKillSession in done.go used basic tmux kill-session
- Claude Code can survive SIGHUP
- Now uses KillSessionWithProcesses for proper cleanup
3. Crew stop commands used basic KillSession
- Same issue as #2
- Updated runCrewRemove, runCrewStop, runCrewStopAll
Root cause of 383 accumulated sessions: every gt done and crew stop
left orphans, and the cleanup never worked on macOS.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add tests for:
- extractPatrolRole() - various title format cases
- PatrolDigest struct - date format and field access
- PatrolCycleEntry struct - field access
Covers pure functions; bd-dependent functions would need mocking.
Fixes: gt-bm9nx5
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When messages are sent to a channel, subscribers now receive a copy
in their inbox with [channel:name] prefix in the subject.
Closes: gt-3rldf6
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds three new subcommands to `gt mail channel`:
- subscribe <name>: Subscribe current identity to a channel
- unsubscribe <name>: Unsubscribe current identity from a channel
- subscribers <name>: List all subscribers to a channel
These commands expose the existing beads.SubscribeToChannel and
beads.UnsubscribeFromChannel functions through the CLI.
Closes gt-77334r
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Checks if a 'Patrol Report YYYY-MM-DD' bead already exists before
attempting to create a new one. This prevents confusing output when
the patrol digest runs multiple times per day.
Fixes: gt-budqv9
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
resolveByName() only checked config-based queues/channels, missing
beads-native ones (gt:queue, gt:channel). Added lookup for both.
Also added LookupQueueByName to beads package for parity with
LookupChannelByName.
Fixes: gt-l5qbi3
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Prevents gt broadcast from nudging the sender's own session,
which would interrupt the command mid-execution with exit 137.
Fixes: gt-y5ss
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When repoBase() fails in RemoveWithOptions, the function previously
returned early after removing the directory but without calling
WorktreePrune(). This could leave stale worktree entries in
.git/worktrees/ if the polecat was created before the repo base
became unavailable.
Now we attempt to prune from both possible repo locations (bare repo
and mayor/rig) before the early return. This is a best-effort cleanup
that handles edge cases where the repo base is corrupted but worktree
entries still exist.
Resolves: gt-wisp-618ar
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Polish help text across all agent commands to clarify roles:
- crew: persistent workspaces vs ephemeral polecats
- deacon: town-level watchdog receiving heartbeats
- dog: cross-rig infrastructure workers (cats vs dogs)
- mayor: Chief of Staff for cross-rig coordination
- nudge: universal synchronous messaging API
- polecat: ephemeral one-task workers, self-cleaning
- refinery: merge queue serializer per rig
- witness: per-rig polecat health monitor
Add comprehensive gt nudge documentation to crew template explaining
when to use nudge vs mail, common patterns, and target shortcuts.
Add orphan-process-cleanup step to deacon patrol formula to clean up
claude subagent processes that fail to exit (TTY = "?").
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Cost tracking infrastructure works but has no data source:
- Claude Code displays costs in TUI status bar, not scrollback
- tmux capture-pane can't see TUI chrome
- All sessions show $0.00
Changes:
- Mark gt costs command as [DISABLED] with deprecation warnings
- Mark costs-digest patrol step as [DISABLED] with skip instructions
- Document requirement for Claude Code to expose CLAUDE_SESSION_COST
Infrastructure preserved for re-enabling when Claude Code adds support.
Ref: GH#24, gt-7awfjq
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Per-cycle patrol digests were polluting JSONL with O(cycles/day) beads.
Apply the same pattern used for cost digests:
- Make per-cycle squash digests ephemeral (not exported to JSONL)
- Add 'gt patrol digest' command to aggregate into daily summary
- Add patrol-digest step to deacon patrol formula
Daily cadence reduces noise while preserving observability.
Closes: gt-nbmceh
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>