Multiple gt commands call git rev-parse --show-toplevel, adding ~50ms
each invocation. Results rarely change within a session, and multiple
agents calling git concurrently contend on .git/index.lock.
Add cached RepoRoot() and RepoRootFrom() functions to the git package
and update all callers to use them. This ensures a single git subprocess
call per process for the common case of checking the current directory's
repo root.
Files updated:
- internal/git/git.go: Add RepoRoot() and RepoRootFrom()
- internal/cmd/prime.go: Use cached git.RepoRoot()
- internal/cmd/molecule_status.go: Use cached git.RepoRoot()
- internal/cmd/sling_helpers.go: Use cached git.RepoRoot()
- internal/cmd/rig_quick_add.go: Use git.RepoRootFrom() for path arg
- internal/version/stale.go: Use cached git.RepoRoot()
Closes: bd-2zd.5
Replace bd subprocess calls in gt commands with daemon RPC when available.
Each subprocess call has ~40ms overhead for Go binary startup, so using
the daemon's Unix socket protocol significantly reduces latency.
Changes:
- Add RPC client to beads package (beads_rpc.go)
- Modify List/Show/Update/Close methods to try RPC first, fall back to subprocess
- Replace runBdPrime() with direct content output (avoids bd subprocess)
- Replace checkPendingEscalations() to use beads.List() with RPC
- Replace hook.go bd subprocess calls with beads package methods
The RPC client:
- Connects to daemon via Unix socket at .beads/bd.sock
- Uses JSON-based request/response protocol (same as bd daemon)
- Falls back gracefully to subprocess if daemon unavailable
- Lazy-initializes connection on first use
Performance improvement targets (from bd-2zd.2):
- gt prime < 100ms (was 5.8s with subprocess chain)
- gt hook < 100ms (was ~323ms)
Closes: bd-2zd.2
The previous approach using KillPaneProcessesExcluding/KillPaneProcesses
killed the pane's main process (Claude/node) before calling RespawnPane.
This caused the pane to close (since tmux's remain-on-exit is off by default),
which then made RespawnPane fail because the target pane no longer exists.
The respawn-pane -k flag handles killing atomically - it kills the old process
and starts the new one in a single operation without closing the pane in between.
If orphan processes remain (e.g., Claude ignoring SIGHUP), they will be cleaned
up when the new session starts or by periodic cleanup processes.
This fixes both self-handoff and remote handoff paths.
Fixes: hq-bv7ef
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The ready command was using constants.RigMayorPath(r.Path) which returns
<rig>/mayor/rig, but this fails for rigs where the source repo doesn't
have tracked beads. In those cases, rig-level beads are stored at
<rig>/.beads directly.
Using r.Path (rig root) allows ResolveBeadsDir to properly handle both:
- Tracked beads: follows <rig>/.beads/redirect to mayor/rig/.beads
- Local beads: uses <rig>/.beads directly
Fixes "no beads database found" errors for google_cookie_retrieval and
home_assistant_blueprints rigs.
Closes: hq-c90jd
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The `gt plugin run` command was recording a "success" run even though it
only prints plugin instructions for an agent/user to execute - it doesn't
actually run the plugin.
This poisoned the cooldown gate: CountRunsSince counted these false
successes, preventing actual executions from running because the gate
appeared to have recent successful runs.
Remove the recording from `gt plugin run`. The actual plugin execution
(by whatever follows the printed instructions) should record the result.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Under high concurrency (17+ agents), the bd version check spawns
multiple git subprocesses per invocation, causing timeouts when
85-120+ git processes compete for resources.
This fix:
- Caches successful version checks to ~/.cache/gastown/beads-version.json
- Uses cached results for 24 hours to avoid subprocess spawning
- On timeout, uses stale cache if available or gracefully degrades
- Prints warning when using cached/degraded path
Fixes: https://github.com/steveyegge/gastown/issues/503
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add assignee display to both list and single-goal views. In list view,
assignee appears on the second line when present. In single-goal view,
it appears as a dedicated field after priority. JSON output also includes
the assignee field.
Closes: gt-libj
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The getCurrentWork function was returning ANY in_progress bead from the
workspace rather than only beads assigned to the current agent. This caused
crew workers to see wisps assigned to polecats in their status bar.
Changes:
- Add identity parameter to getCurrentWork function
- Add identity guard (return empty if identity is empty)
- Filter by Assignee in the beads query
This complements the earlier getHookedWork fix and ensures both hooked
AND in_progress beads are filtered by the agent's identity.
Fixes gt-zxnr (additional fix).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Root cause: tmux statusline showed wrong hook for all java crewmembers
because GT_CREW env var wasn't set in tmux session environment.
Changes:
- statusline.go: Add early return in getHookedWork() when identity is empty
to prevent returning ALL hooked beads regardless of assignee
- crew_at.go: Call SetEnvironment in the restart path so sessions created
before GT_CREW was being set get it on restart
Fixes gt-zxnr.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
KillPaneProcesses was killing ALL processes in the pane, including the
gt handoff process itself. This created a race condition where the
process could be killed before RespawnPane executes, causing the pane
to close prematurely and requiring manual reattach.
Added KillPaneProcessesExcluding() function that excludes specified PIDs
from being killed. The handoff command now passes its own PID to avoid
the race condition.
Fixes: gt-85qd
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When deacon patrol molecules completed, their child step wisps were not being
closed automatically. This caused orphan wisp accumulation - 143+ orphaned
wisps were found in one cleanup session.
The fix ensures that when a molecule completes (via gt done or gt mol step done),
all descendant step issues are recursively closed before the molecule itself.
Changes:
- done.go: Added closeDescendants() call in updateAgentStateOnDone before
closing the attached molecule
- molecule_step.go: Added closeDescendants() call in handleMoleculeComplete
for all roles (not just polecats)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When polecats are nuked, Claude child processes could survive and become
orphans, leading to memory exhaustion (observed: 142 orphaned processes
consuming ~56GB RAM).
This commit:
1. Increases the SIGTERM→SIGKILL grace period from 100ms to 2s to give
processes time to clean up gracefully
2. Adds orphan cleanup to `gt polecat nuke` that runs after session
termination to catch any processes that escaped
3. Adds a new `gt cleanup` command for manual orphan removal
The orphan detection uses aggressive tmux session verification to find
ALL Claude processes not in any active session, not just those with
PPID=1.
Fixes: gh-736
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add CleanupOrphanedSessions() function that runs at `gt start` time to
detect and kill zombie tmux sessions (sessions where tmux is alive but
the Claude process has died).
This prevents:
- Session name conflicts when restarting agents
- Resource accumulation from orphaned sessions
- Process accumulation that can overwhelm the system
The function scans for sessions with `gt-*` and `hq-*` prefixes, checks
if Claude is running using IsClaudeRunning(), and kills zombie sessions
using KillSessionWithProcesses() for proper cleanup.
Fixes#700
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Call beads.EnsureCustomTypes before attempting to create a convoy.
This fixes invalid issue type: convoy errors that occur when town
beads do not have custom types configured (e.g., incomplete install
or manually initialized beads).
The EnsureCustomTypes function uses caching (in-memory + sentinel file)
so this adds negligible overhead to convoy create.
Fixes: gt-1b8eg9
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The `gt hooks` command was not discovering settings at:
- <rig>/crew/.claude/settings.json (crew-level, inherited by all members)
- <rig>/polecats/.claude/settings.json (polecats-level)
This caused confusion when debugging hooks since Claude Code inherits
from parent directories, so hooks were executing but not shown by
`gt hooks`.
Also fixed: skip .claude directories when iterating crew members.
Fixes: gh-735
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Docking on non-main branches silently fails because rig identity beads
live on main. The dock appeared to work but was lost on checkout to main.
Now dock/undock check current branch and error with helpful message:
"cannot dock: must be on main branch (currently on X)"
Fixes hq-kc7
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: update test assertions and set BEADS_DIR in EnsureCustomTypes
- Update TestBuildAgentStartupCommand to check for 'exec env' instead
of 'export' (matches current BuildStartupCommand implementation)
- Add 'config' command handling to fake bd script in manager_test.go
- Set BEADS_DIR env var when running bd config in EnsureCustomTypes
to ensure bd operates on the correct database during agent bead creation
- Apply gofmt formatting
These fixes address pre-existing test failures on main.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: inject mock in TestRoleLabelCheck_NoBeadsDir for Windows CI
The test was failing on Windows CI because bd is not installed,
causing exec.LookPath("bd") to fail and return "beads not installed"
before checking for the .beads directory.
Inject an empty mock beadShower to skip the LookPath check, allowing
the test to properly verify the "No beads database" path.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: regenerate formulas and fix unused parameter lint error
- Regenerate mol-witness-patrol.formula.toml to sync with source
- Mark unused hookName parameter with _ in installHookTo
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(tests): make Windows CI tests pass
- Skip symlink tests on Windows (require elevated privileges)
- Fix GT_ROOT assertion to handle Windows path escaping
- Use platform-appropriate paths in TestNewManager_PathConstruction
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix tests for quoted env and OS paths
* fix(test): add Windows batch scripts to molecule lifecycle tests
The molecule_lifecycle_test.go tests were failing on Windows CI because
they used Unix shell scripts (#!/bin/sh) for mock bd commands, which
don't work on Windows.
This commit adds Windows batch file equivalents for all three tests:
- TestSlingFormulaOnBeadHooksBaseBead
- TestSlingFormulaOnBeadSetsAttachedMoleculeInBaseBead
- TestDoneClosesAttachedMolecule
Uses the same pattern as writeBDStub() from sling_test.go for
cross-platform test mocks.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(test): add Windows batch scripts to more tests
Adds Windows batch script equivalents to tests that use mock bd commands:
molecule_lifecycle_test.go:
- TestSlingFormulaOnBeadHooksBaseBead
- TestSlingFormulaOnBeadSetsAttachedMoleculeInBaseBead
- TestDoneClosesAttachedMolecule
sling_288_test.go:
- TestInstantiateFormulaOnBead
- TestInstantiateFormulaOnBeadSkipCook
- TestCookFormula
- TestFormulaOnBeadPassesVariables
These tests were failing on Windows CI because they used Unix shell
scripts (#!/bin/sh) which don't work on Windows.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(test): skip TestSlingFormulaOnBeadSetsAttachedMoleculeInBaseBead on Windows
The test's Windows batch script JSON output causes
storeAttachedMoleculeInBead to fail silently when parsing the bd show
response. This is a pre-existing limitation - the test was failing on
Windows before the batch scripts were added (shell scripts don't work
on Windows at all).
Skip this test on Windows until the underlying JSON parsing issue is
resolved.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: re-trigger CI after GitHub Internal Server Error
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
gt goals was only querying the default beads location (town-level
with hq- prefix), missing epics from rig-level beads (j-, sc-, etc.).
Now iterates over all rig directories with .beads/ subdirectories
and aggregates epics, deduplicating by ID.
Wisp molecules (gt-wisp-* IDs, mol-* titles) are transient operational
beads for witness/refinery/polecat patrol, not strategic goals that
need human attention. These are now filtered by default.
Add --include-wisp flag to show them when debugging.
Fixes gt-ysmj
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add `bd tree <id>` to Key Commands in bd prime template (beads.go)
- Add `bd tree <issue>` to prime_output.go for mayor/polecat/crew roles
- Helps agents understand bead ancestry, siblings, and dependencies
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add three new flags for filtering convoys by epic relationship:
- --orphans: show only convoys without a parent epic
- --epic <id>: show only convoys under a specific epic
- --by-epic: group convoys by parent epic
These support the Goals Layer feature (Phase 3) for hierarchical
focus management.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements gt goals command to show epics sorted by staleness × priority.
Features:
- List all open epics with staleness indicators (🟢/🟡/🔴)
- Sort by attention score (priority × staleness hours)
- Show specific goal details with description and linked convoys
- JSON output support
- Priority and status filtering
Staleness thresholds:
- 🟢 active: moved in last hour
- 🟡 stale: no movement for 1+ hours
- 🔴 stuck: no movement for 4+ hours
Closes: gt-vix
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create goals.go with basic command structure for viewing strategic
goals (epics) with staleness indicators. Includes --json, --status,
and --priority flags. Implementation stubs return not-yet-implemented
errors.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add explicit guidance on the Mayor → Crew → Polecats delegation model:
- Crew are coordinators for epics/goals needing decomposition
- Polecats are executors for well-defined tasks
- Include decision framework table for work type routing
Closes: gt-9jd
Implements the Overseer Experience epic (gt-k0kn):
- gt focus: Shows stalest high-priority goals, sorted by priority × staleness
- gt attention: Shows blocked items, PRs awaiting review, stuck workers
- gt status: Now includes GOALS and ATTENTION summary sections
- gt convoy list: Added --orphans, --epic, --by-epic flags
These commands reduce Mayor bottleneck by giving the overseer direct
visibility into system state without needing to ask Mayor.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add --convoy flag to gt sling that allows adding an issue to an existing
convoy instead of creating a new one. When specified:
- Validates the convoy exists and is open
- Adds tracking relation between convoy and issue
- Skips auto-convoy creation
Changes:
- Add slingConvoy variable and --convoy flag registration
- Add addToExistingConvoy() helper function in sling_convoy.go
- Modify auto-convoy logic to check slingConvoy first
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add flag variable declarations and Cobra flag registrations for:
- --epic: link auto-created convoy to parent epic
- --convoy: add to existing convoy instead of creating new
Closes: gt-n3o
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change molecule step completion instructions to use `gt mol step done`
instead of `bd close`. This ensures polecats get fresh context between
each step, which is critical for multi-step review workflows like
shiny-enterprise where each refinement pass should have unbiased attention.
The `gt mol step done` command already:
1. Closes the step
2. Finds the next ready step
3. Respawns the pane for fresh context
But polecats were being instructed to use `bd close` directly, which
skipped the respawn and let them run through entire workflows in a
single session with accumulated context.
Updated:
- prime_molecule.go: step completion instructions
- mol-polecat-work.formula.toml
- mol-polecat-code-review.formula.toml
- mol-polecat-review-pr.formula.toml
Fixes: hq-0kx7ra
Three fixes to make dog dispatch work end-to-end:
1. Add BuildDogStartupCommand in loader.go
- Similar to BuildPolecatStartupCommand/BuildCrewStartupCommand
- Passes AgentName to AgentEnv so BD_ACTOR is exported in startup command
2. Use BuildDogStartupCommand in dog.go
- Removes ineffective SetEnvironment calls (env vars set after shell starts
don't propagate to already-running processes)
3. Add "dog" case in mail_identity.go detectSenderFromRole
- Dogs now use BD_ACTOR for mail identity
- Without this, dogs fell through to "overseer" and couldn't find their mail
Tested: dog alpha now correctly sees inbox as deacon/dogs/alpha
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Recovered from reflog - these commits were lost during a rebase/force-push.
Dogs are directories with state files but no sessions. When `gt dog dispatch`
assigned work and sent mail, nothing executed because no session existed.
Changes:
1. Spawn tmux session after dispatch (gt-<town>-deacon-<dogname>)
2. Set BD_ACTOR=deacon/dogs/<name> so dogs can find their mail
3. Add dog case to AgentEnv for proper identity
Session spawn is non-blocking - if it fails, mail was sent and human can
manually start the session.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Dogs can now reset their own state to idle after completing work:
gt dog done # Auto-detect from BD_ACTOR
gt dog done alpha # Explicit name
This solves the issue where dog sessions would complete work but remain in
"working" state because nothing processed the DOG_DONE mail. Now dogs can
explicitly mark themselves idle before handing off.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
KillPaneProcesses was being called on new sessions before respawn,
which killed the fresh shell and destroyed the pane. This caused
"can't find pane" errors on session creation.
Now KillPaneProcesses is only called when restarting in an existing
session where Claude/Node processes might be running and ignoring
SIGHUP. For new sessions, we just use respawn-pane directly.
Also added retry limit and error checking for the stale session
recovery path.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add 'bd' alias for 'gt bead' command
- Add 'work' alias for 'gt hook' command
- Show deacon icon in mayor status line when running
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When a session exists but its pane is gone (e.g., after account switch
or town reboot), 'gt crew at' now detects the "can't find pane" error
and automatically recreates the session instead of failing.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Allow reading messages by their inbox position (e.g., 'gt mail read 3')
in addition to message ID. The inbox display now shows 1-based index
numbers for easy reference.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds gt mail hook <mail-id> command that attaches a mail message to
the agents hook. This provides a more intuitive command path when
working with mail-based workflows.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Users naturally try --body for the message body content (same semantic
field as --message but more precise - distinguishes body from subject).
Added as an alias following the same pattern as --address/--identity.
Closes: gt-bn9mt
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Allow `gt mail delete` to accept multiple message IDs at once,
matching the existing behavior of archive, mark-read, and mark-unread.
Also adds --body as an alias for --message in mail reply.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
## Problem
Claude processes were accumulating as orphans, with 100+ processes piling up
daily. Every `gt handoff` (used dozens of times/hour by crew) left orphaned
processes because `tmux respawn-pane -k` only sends SIGHUP, which Node/Claude
ignores.
## Root Cause
Previous fixes (1043f00d, f89ac47f, 2feefd17, 1b036aad) were laser-focused on
specific symptoms (shutdown, setsid, done.go, molecule_step.go) but never did
a comprehensive audit of ALL RespawnPane call sites. handoff.go was never
fixed despite being the main source of orphans.
## Solution
Added KillPaneProcesses() call before every RespawnPane() in:
- handoff.go (self handoff and remote handoff)
- mayor.go (mayor restart)
- crew_at.go (new session and restart)
KillPaneProcesses explicitly kills all descendant processes with SIGTERM/SIGKILL
before respawning, preventing orphans regardless of SIGHUP handling.
molecule_step.go already had this fix from commit 1b036aad.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
fix(sling): auto-apply mol-polecat-work (#288) and fix wisp orphan lifecycle bug (#842)
Fixes the formula-on-bead pattern to hook the base bead instead of the wisp:
- Auto-apply mol-polecat-work when slinging bare beads to polecats
- Hook BASE bead with attached_molecule pointing to wisp
- gt done now closes attached molecule before closing hooked bead
- Convoys complete properly when work finishes
Fixes#288, #842, #858
resolveSelfTarget returns "mayor/" with trailing slash per addressToIdentity
normalization, but agentIDToBeadID only checked for "mayor" without slash.
This caused `gt hook --clear` to fail with:
Error: could not convert agent ID mayor/ to bead ID
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Problem:
- Gas Town sets GT_TOWN_ROOT environment variable
- Beads searches for formulas using GT_ROOT environment variable
- This naming inconsistency prevents beads from finding town-level formulas
- Result: `bd mol seed --patrol` fails in rigs, causing false doctor warnings
Solution:
Export both GT_TOWN_ROOT and GT_ROOT from `gt rig detect` command:
- Modified stdout output to export both variables (lines 66, 70)
- Updated cache storage format (lines 134, 136, 138)
- Updated unset statement for both variables (line 110)
- Updated command documentation (lines 33, 37)
Both variables point to the same town root path. This maintains backward
compatibility with Gas Town (GT_TOWN_ROOT) while enabling beads formula
search (GT_ROOT).
Testing:
- `gt rig detect .` now outputs both GT_TOWN_ROOT and GT_ROOT
- `bd mol seed --patrol` works correctly when GT_ROOT is set
- Formula search paths work as expected: town/.beads/formulas/ accessible
Related:
- Complements bd mol seed --patrol implementation (beads PR #1149)
- Complements patrol formula doctor check fix (gastown PR #715)
Co-authored-by: Roland Tritsch <roland@ailtir.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
When slinging work to an agent, updateAgentHookBead() was running
bd slot set from townRoot. But agent beads with rig-level prefixes
(e.g., go-) live in rig databases, not the town database. This caused
"issue not found" errors when trying to update the hook_bead slot.
Fix: Use beads.ResolveHookDir() to resolve the correct working directory
based on the agent bead's prefix before calling SetHookBead().
Co-authored-by: furiosa <spencer@atmosphere-aviation.com>
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>