Adds ability to override embedded role templates at town or rig level:
- Town: <townRoot>/templates/roles/<role>.md.tmpl
- Rig: <rigPath>/templates/roles/<role>.md.tmpl
Rig-level overrides take precedence over town-level.
This enables customizing polecat (or other role) behavior per-rig without
modifying gastown source, following the same 3-tier override pattern as
role configs.
New APIs:
- NewWithOverrides(townRoot, rigPath string) - loads templates with overrides
- HasRoleOverride(role string) bool - check if role has override
- RoleOverrideCount() int - count of loaded overrides
Implements sc-6ghhn
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
Fixes intermittent 'timeout waiting for runtime prompt' errors that occur
when Claude takes longer than 60s to start under load or on slower machines.
Resolves: hq-j2wl
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 1 of agent security model: Set distinct email addresses for each
agent type to improve audit trail clarity.
Email format:
- Town-level: {role}@gastown.local (mayor, deacon, boot)
- Rig-level: {rig}-{role}@gastown.local (witness, refinery)
- Named agents: {rig}-{role}-{name}@gastown.local (polecat, crew)
This makes git log filtering by agent type trivial and provides a
foundation for per-agent key separation in future phases.
Refs: hq-biot
Mayor now checks `gt escalate list` between hook and mail checks at startup.
This ensures pending escalations from other agents are handled promptly.
Other roles (witness, refinery, polecat, crew, deacon) are unaffected -
they create escalations but don't handle them at startup.
Tags are used for releases and shouldn't be blocked by the branch
restriction that prevents feature branch pushes.
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>
The runConvoyCheck function was running `gt convoy check` without
the convoy ID, which checked all open convoys. Now it passes the
specific convoy ID to check only the relevant convoy, as specified
in the requirements.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When an issue closes, the daemon ConvoyWatcher now passes the specific
convoy ID to gt convoy check instead of running check on all open convoys.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Claude Code has no descendants, so only killing descendants left orphans.
Now kills the pane PID itself with SIGTERM+SIGKILL after descendants.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove redundant routing rules and health check documentation
that was duplicating information available elsewhere.
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>
Changes patrol-molecules-exist check to verify that patrol formulas
are accessible via `bd formula list` instead of looking for placeholder
molecule beads created by `bd create --type=molecule`.
## Problem
The check was looking for molecule-type beads with titles "Deacon Patrol",
"Witness Patrol", and "Refinery Patrol". These placeholder beads serve no
functional purpose because:
1. Patrols actually use `bd mol wisp mol-deacon-patrol` which cooks
formulas inline (on-the-fly)
2. The formulas already exist at the town level in .beads/formulas/
3. The placeholder beads are never referenced by any patrol code
## Solution
- Check for formula names (mol-deacon-patrol, mol-witness-patrol,
mol-refinery-patrol) instead of bead titles
- Use `bd formula list` instead of `bd list --type=molecule`
- Remove Fix() method that created unnecessary placeholder beads
- Update messages to reflect that formulas should exist in search paths
## Impact
- Check now verifies what patrols actually need (formulas)
- Eliminates creation of unnecessary placeholder beads
- More accurate health check for patrol system
Co-authored-by: Roland Tritsch <roland@ailtir.com>
Co-authored-by: Claude Sonnet 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>
Both settings-autonomous.json and settings-interactive.json templates
were using bare 'gt prime' in SessionStart and PreCompact hooks, which
caused gt doctor to report warnings about missing --hook flag.
The --hook flag is required for proper session ID passthrough from
Claude Code. When called as a hook, 'gt prime --hook' reads session
metadata (session_id, transcript_path, source) from stdin JSON that
Claude Code provides.
Without --hook, session tracking breaks and gt doctor correctly warns:
"SessionStart uses bare 'gt prime' - add --hook flag or use session-start.sh"
This fix updates both template files to use 'gt prime --hook' in:
- SessionStart hooks (lines 12)
- PreCompact hooks (lines 23)
New installations will now generate settings.json files with the
correct format that passes gt doctor validation.
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 checkDeaconHeartbeat detects a stuck Deacon and kills it, the code
relied on ensureDeaconRunning being called on the next heartbeat. However,
on the next heartbeat, checkDeaconHeartbeat exits early when it finds no
session (assuming ensureDeaconRunning already ran), creating a deadlock
where the Deacon is never restarted.
This fix calls ensureDeaconRunning immediately after the kill attempt,
regardless of success or failure, ensuring the Deacon is restarted
promptly.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: mayor
Role: mayor
* fix(costs): add event to BeadsCustomTypes constant
The gt costs record command creates beads with --type=event, but "event"
was missing from the BeadsCustomTypes constant. This caused stop hooks
to fail with "invalid issue type: event" errors.
Also fixes gt doctor --fix to properly register the event type when
running on existing installations.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(types): add message, molecule, gate, merge-request to BeadsCustomTypes
Extends PR #731 to include all custom types used by Gas Town:
- message: Mail system (gt mail send, mailbox, router)
- molecule: Work decomposition (patrol checks, gt swarm)
- gate: Async coordination (bd gate wait, park/resume)
- merge-request: Refinery MR processing (gt done, refinery)
Root cause: gt mail send was failing with "invalid issue type: message"
because message was never added to BeadsCustomTypes.
Also documents the origin/usage of each custom type in the constant.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: mayor
Role: mayor
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
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>