Use ResolveRoleAgentConfig instead of LoadRuntimeConfig to properly
resolve role-specific agent settings (like ready_prompt_prefix) when
starting the refinery. This fixes timeout issues when role_agents.refinery
is configured with a non-default agent.
Also move AcceptBypassPermissionsWarning before WaitForRuntimeReady to
avoid a race condition where the bypass dialog can block prompt detection.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add Fix() method to SessionHookCheck to automatically update
settings.json files when 'gt prime' is used without '--hook'.
This enables 'gt doctor --fix' to repair existing installations
that use bare 'gt prime' in SessionStart/PreCompact hooks.
Changes:
- Changed SessionHookCheck to embed FixableCheck instead of BaseCheck
- Added filesToFix cache populated during Run()
- Implemented Fix() method that parses JSON and replaces 'gt prime'
with 'gt prime --hook' in command strings
- Uses json.Encoder with SetEscapeHTML(false) to preserve readable
ampersands in command strings
Closes: gt-1tj0c
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The FetchMergeQueue function was hardcoded to query PRs from
michaellady/roxas and michaellady/gastown, causing the dashboard
to show unrelated PRs regardless of which rigs are actually registered.
This fix:
- Adds townRoot to LiveConvoyFetcher to access workspace config
- Loads registered rigs from mayor/rigs.json dynamically
- Adds gitURLToRepoPath helper to convert git URLs (HTTPS/SSH) to
owner/repo format for the gh CLI
- Updates comments to reflect the new behavior
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Replace MergeNoFF with MergeSquash in the Refinery to preserve the original
conventional commit message (feat:/fix:) from polecat branches instead of
creating "Merge polecat/... into main" commits.
Changes:
- Add MergeSquash function to internal/git/git.go
- Add GetBranchCommitMessage helper to retrieve branch commit messages
- Update engineer.go doMerge to use squash merge with original message
Fixes#855
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix(install): use go install to match docs
Makefile install target now uses 'go install' instead of cp to
~/.local/bin, aligning with documented installation method and
GOPATH/GOBIN conventions.
Closes: hq-93c
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(beads): set BEADS_DIR for config
* test(config,rig): update startup and bd stubs
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The pre-push hook now detects when an `upstream` remote is configured
and allows feature branches for the fork contribution workflow.
Previously, the hook blocked all non-main branches, which prevented
pushing PR branches to forks. Now the blocking logic checks for an
upstream remote - if present, it skips the block and allows the push.
The check wraps the blocking logic (rather than early-out) so that
any future additions to the hook will still apply to contributor
workflows.
Fixes#848
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add explicit routing rules to ping-deacon step:
- WITNESS_PING always goes to deacon/, never to mayor/
- Mayor only receives ALERT messages (Step 3)
- Define what 'healthy' looks like (idle at prompt is normal)
This prevents the witness LLM from misinterpreting the formula
and sending raw WITNESS_PING messages to mayor instead of deacon.
Fixes: gt-uvz90
When the repo is in a broken state (wrong branch, detached HEAD, deleted
worktree), gt handoff would fail with "cannot detect town root" error.
This is exactly when handoff is most needed - to recover and hand off
to a fresh session.
Changes:
- detectTownRootFromCwd() now falls back to GT_TOWN_ROOT and GT_ROOT
environment variables when cwd-based detection fails
- buildRestartCommand() now propagates GT_ROOT to ensure subsequent
handoffs can also use the fallback
- Added tests for the fallback behavior
Fixes gt-x2q81.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add support for --comment flag as an alias for --reason in the
gt close command. This provides a more intuitive option name for
users who think of close messages as comments rather than reasons.
Handles both --comment value and --comment=value forms.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The rename operation was only copying AgentState and CleanupStatus,
missing HookBead (the primary fix), ActiveMR, and NotificationLevel.
This ensures all agent state is preserved when renaming an identity.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add hooks_registry.go: LoadRegistry(), HookRegistry/HookDefinition types
- Add hooks_install.go: gt hooks install command with --role and --all-rigs flags
- gt hooks list now reads from ~/gt/hooks/registry.toml
- Supports dry-run, deduplication, and creates .claude dirs as needed
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Allow `gt mail reply <id> "message"` in addition to `-m` flag.
This is a desire-path fix - agents naturally try positional syntax.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add support for agent presets to specify environment variables that
get exported when starting sessions. This enables agents to use
environment-based configuration.
Changes:
- Add Env field to RuntimeConfig struct in types.go
- Add Env field to AgentPresetInfo struct in agents.go
- Update RuntimeConfigFromPreset to copy Env from preset
- Update fillRuntimeDefaults to preserve Env field
- Merge agent Env vars in BuildStartupCommand functions
- Add comprehensive tests for Env preservation and copy semantics
This is a prerequisite for the OpenCode agent preset which uses
OPENCODE_PERMISSION='{"*":"allow"}' for auto-approve mode.
Co-authored-by: Avyukth <subhrajit.makur@hotmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add ability to access sessions from other accounts when using gt seance --talk.
After gt account switch, sessions from previous accounts are now accessible
via temporary symlinks.
Changes:
- Search all account config directories in accounts.json for session
- Create temporary symlink from source account to current account project dir
- Update sessions-index.json with session entry (using json.RawMessage to preserve fields)
- Cleanup removes symlink and index entry when seance exits
- Add startup cleanup for orphaned symlinks from interrupted sessions
Based on PR #797 by joshuavial, with added orphan cleanup to handle ungraceful exits.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add ShellQuote function to properly escape environment variable values
containing shell special characters ({, }, *, $, ", etc.).
Changes:
- Add ShellQuote() that wraps values in single quotes when needed
- Escape embedded single quotes using '\'' idiom
- Update ExportPrefix to use ShellQuote
- Update BuildStartupCommand and PrependEnv in loader.go
- Add comprehensive tests for shell quoting edge cases
Backwards compatible: paths, hyphens, dots, and slashes are NOT quoted,
preserving existing agent behavior (GT_ROOT, BD_ACTOR, etc.).
This is a prerequisite for the OpenCode agent preset which uses
OPENCODE_PERMISSION='{"*":"allow"}' for auto-approve mode.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The listFromDir function was making 3-6 serial bd subprocess calls
(one per identity variant × status). This caused gt mail inbox to take
~32 seconds in typical setups.
Change to run all queries in parallel using goroutines, reducing
inbox load time to ~5 seconds.
Implementation notes:
- Pre-allocate results slice indexed by query position (no mutex needed)
- Deduplication happens after wg.Wait() in single-threaded collection
- Existing error handling preserved (partial success allowed)
Fixes#705
- Update patrol_check tests to expect StatusOK instead of StatusWarning
for missing templates (embedded templates fill the gap)
- Add moveDir helper with cross-filesystem fallback for git clones
- Remove accidentally committed events.jsonl file
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes two bugs in multi-repo routing scenarios:
1. "invalid issue type: agent" error when creating agent beads
- Added EnsureCustomTypes() with two-level caching (in-memory + sentinel file)
- CreateAgentBead() now resolves routing target and ensures custom types
2. "could not set role slot: issue not found" warning when setting slots
- Added runSlotSet() and runSlotClear() helpers that run bd from correct directory
- Slot operations now use the resolved target directory
New files:
- internal/beads/beads_types.go - routing resolution and custom types logic
- internal/beads/beads_types_test.go - unit tests
Based on PR #811 by Perttulands, rebased onto current main.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add logs/, settings/, and .events.jsonl to gitignore.
These are runtime files created during gt operation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously, `gt done` would fail with "0 commits ahead; nothing to merge"
if work was pushed directly to main instead of via PR. This blocked
polecats from completing even when their work was done, causing them to
become zombies.
Now, if the branch has no commits ahead of main, `gt done` skips MR
creation but still completes successfully - notifying the witness,
cleaning up the worktree, and terminating the session.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add three new verification legs to the code-review convoy formula:
- wiring: detects dependencies added but not actually used
- commit-discipline: reviews commit quality and atomicity
- test-quality: verifies tests are meaningful, not just present
Also adds presets for common review modes:
- gate: light review (wiring, security, smells, test-quality)
- full: all 10 legs for comprehensive review
- security-focused: security-heavy for sensitive changes
- refactor: code quality focus
This is a minimal subset of PR #4's Worker → Reviewer pattern,
focusing on the most valuable additions without Go code changes.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Two commits modified internal/formula/formulas/ without updating the
source at .beads/formulas/:
- d6a4bc22: added patrol-digest step
- bd655f58: disabled costs-digest step
This caused go generate to overwrite the embedded file with stale
source content on every build.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When starting agents with environment variables, the previous approach
used 'export VAR=val && claude' which keeps bash as the pane command
with the agent as a child process. WaitForCommand polls pane_current_command
which returns 'bash', causing a 60-second timeout.
Changed to 'exec env VAR=val claude' which replaces the shell with the
agent process, making it detectable via pane_current_command.
Fixes startup timeout on macOS: 'still running excluded command'
The quick-add command (used by shell hook's "Add to Gas Town?" prompt)
previously only checked hardcoded paths ~/gt and ~/gastown, ignoring
GT_TOWN_ROOT and any other Gas Town installations.
This caused rigs to be added to the wrong town when users had multiple
Gas Town installations (e.g., ~/gt and ~/Documents/code/gt).
Fix the town discovery order:
1. GT_TOWN_ROOT env var (explicit user preference)
2. workspace.FindFromCwd() (supports multiple installations)
3. Fall back to ~/gt and ~/gastown
The polecat name allocator was assigning reserved infrastructure agent
names like 'witness' to polecats. Added ReservedInfraAgentNames map
containing witness, mayor, deacon, and refinery. Modified getNames()
to filter these from all themes and custom name lists.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Move convoy check to run after verifyCommitOnMain succeeds, before the
cleanup_status switch. This ensures convoys can close when tracked work
is merged, even if polecat cleanup is blocked (has_uncommitted, etc.).
Previously the convoy check only ran after successful nuke, meaning
blocked polecats would prevent convoy completion detection.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PR #759 introduced cleanupOrphanedClaude() using syscall.Kill directly,
which breaks Windows builds. This extracts the function to:
- start_orphan_unix.go: Full implementation with SIGTERM/SIGKILL
- start_orphan_windows.go: Stub (orphan signals not supported)
Follows existing pattern: process_unix.go / process_windows.go
Fix "Unable to attach mayor" timeout caused by claude being installed
as a shell alias rather than in PATH. Non-interactive shells spawned
by tmux cannot resolve aliases, causing the session to exit immediately.
Changes:
- Add resolveClaudePath() to find claude at ~/.claude/local/claude
- Apply path resolution in RuntimeConfigFromPreset() for claude preset
- Make hasClaudeChild() recursive (now hasClaudeDescendant()) to search
entire process subtree as defensive improvement
- Update fillRuntimeDefaults() to use DefaultRuntimeConfig() for
consistent path resolution
Fixes https://github.com/steveyegge/gastown/issues/703
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
## Problem
The deacon patrol was leaking claude processes. Every patrol cycle (1-3 minutes),
a new claude process was spawned under the hq-deacon tmux session, but old processes
were never terminated. This resulted in 12+ accumulated claude processes consuming
resources.
## Root Cause
In molecule_step.go:331, handleStepContinue() used tmux respawn-pane -k to restart
the pane between patrol steps. The -k flag sends SIGHUP to the shell but does not
kill all descendant processes (claude and its node children).
## Solution
Added KillPaneProcesses() function in tmux.go that explicitly kills all descendant
processes before respawning the pane. This function:
- Gets all descendant PIDs recursively
- Sends SIGTERM to all (deepest first)
- Waits 100ms for graceful shutdown
- Sends SIGKILL to survivors
Updated handleStepContinue() to call KillPaneProcesses() before RespawnPane().
Co-authored-by: Roland Tritsch <roland@ailtir.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
The daemon creates hq-deacon and hq-mayor sessions (headquarters sessions)
that were incorrectly flagged as orphaned by gt doctor.
Changes:
- Update orphan session check to recognize hq-* prefix in addition to gt-*
- Update orphan process check to detect 'tmux: server' process name on Linux
- Add test coverage for hq-* session validation
- Update documentation comments to reflect hq-* patterns
This fixes the false positive warnings where hq-deacon session and its
child processes were incorrectly reported as orphaned.
Co-authored-by: Roland Tritsch <roland@ailtir.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Newer versions of Claude Code report the tmux pane command as "claude"
instead of "node". This caused gt mayor attach (and similar commands) to
incorrectly detect that the runtime had exited and restart the session.
The fix adds "claude" to the expected pane commands alongside "node",
matching the behavior of IsClaudeRunning() which already handles both.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add support for checking a specific convoy by ID instead of all convoys:
- `gt convoy check <convoy-id>` - check specific convoy
- `gt convoy check` - check all (existing behavior)
- `gt convoy check --dry-run` - preview mode
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds --all/-a flag as a semantic complement to --unread. While the
default behavior already shows all messages, --all makes the intent
explicit when viewing the complete inbox.
The flags are mutually exclusive - using both --all and --unread
returns an error.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds `gt bead read <id>` as an alias for `gt bead show <id>` to provide
an alternative verb that may feel more natural for viewing bead details.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Per PRIMING.md principle "Redundant Monitoring Is Resilience", add convoy
completion checks to Witness and Refinery for redundant observation:
- New internal/convoy/observer.go with shared CheckConvoysForIssue function
- Witness: checks convoys after successful polecat nuke in HandleMerged
- Refinery: checks convoys after closing source issue in both success handlers
Multiple observers closing the same convoy is idempotent - each checks if
convoy is already closed before running `gt convoy check`.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When --owner flag is not provided on gt convoy create, the owner now
defaults to the creator's identity (via detectSender()) rather than
being left empty. This ensures completion notifications always go to
the right place - the agent who requested the convoy.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
TestQuerySessionEvents_FindsEventsFromAllLocations was failing because
events created via bd create were not being found. This is caused by
bd CLI 0.47.2 having a bug where database writes do not commit.
Skip the test until the upstream bd CLI bug is fixed, consistent with
how other affected tests were skipped in commit 7714295a.
The original stack overflow issue (gt-obx) was caused by subprocess
interactions with the parent workspace daemon and was already fixed
by the existing skip logic that triggers when GT_TOWN_ROOT or BD_ACTOR
is set.
Fixes: gt-obx
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When Claude sessions were terminated using KillSession(), bash subprocesses
spawned by Claude's Bash tool could survive because they ignore SIGHUP.
This caused zombie processes to accumulate over time.
Changed all critical session termination paths to use KillSessionWithProcesses()
which explicitly kills all descendant processes before terminating the session.
Fixes: gt-ew3tk
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove Witness and Refinery structs that recorded observable state
(State, PID, StartedAt, etc.) in violation of ZFC and "Discover,
Don't Track" principles.
Changes:
- Remove Witness struct and State type alias from witness/types.go
- Remove Refinery struct and State type alias from refinery/types.go
- Remove deprecated run(*Refinery) method from refinery/manager.go
- Update witness/types_test.go to remove tests for deleted types
The managers already derive running state from tmux sessions
(following the deacon pattern). The deleted types were vestigial
and unused.
Resolves: gt-r5pui
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The existing PPID=1 detection misses orphaned Claude processes that get
reparented to something other than init/launchd. The new --aggressive
flag cross-references Claude processes against active tmux sessions to
find ALL orphans not in any gt-* or hq-* session.
Testing shows this catches ~3x more orphans (117 vs 39 in one sample).
Usage:
gt orphans procs --aggressive # List ALL orphans
gt orphans procs kill --aggressive # Kill ALL orphans
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>