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
## Problem
gt shutdown failed to stop orphaned daemon processes because the
detection mechanism ignored errors and had no fallback.
## Root Cause
stopDaemonIfRunning() ignored errors from daemon.IsRunning(), causing:
1. Stale PID files to hide running daemons
2. Corrupted PID files to return silent false
3. No fallback detection for orphaned processes
4. Early return when no sessions running prevented daemon check
## Solution
1. Enhanced IsRunning() to return detailed errors
2. Added process name verification (prevents PID reuse false positives)
3. Added fallback orphan detection using pgrep
4. Fixed stopDaemonIfRunning() to handle errors and use fallback
5. Added daemon check even when no sessions are running
## Testing
Verified shutdown now:
- Detects and reports stale/corrupted PID files
- Finds orphaned daemon processes
- Kills all daemon processes reliably
- Reports detailed status during shutdown
- Works even when no other sessions are running
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
## Problems Fixed
1. **False reporting**: `gt shutdown` reported "0 sessions stopped" even when
all 5 sessions were successfully terminated
2. **Orphaned processes**: No way to clean up Claude processes left behind by
crashed/interrupted sessions
## Root Causes
1. **Counter bug**: `killSessionsInOrder()` only incremented the counter when
`KillSessionWithProcesses()` returned no error. However, this function can
return an error even after successfully killing all processes (e.g., when
the session auto-closes after its processes die, the final `kill-session`
command fails with "session not found").
2. **No orphan cleanup**: While `internal/util/orphan.go` provides orphan
detection infrastructure, it wasn't integrated into the shutdown workflow.
## Solutions
1. **Fix counter logic**: Modified `killSessionsInOrder()` to verify session
termination by checking if the session still exists after the kill attempt,
rather than relying solely on the error return value. This correctly counts
sessions that were terminated even if the kill command returned an error.
2. **Add `--cleanup-orphans` flag**: Integrated orphan cleanup with a simple
synchronous approach:
- Finds Claude/codex processes without a controlling terminal (TTY)
- Filters out processes younger than 60 seconds (avoids race conditions)
- Excludes processes belonging to active Gas Town tmux sessions
- Sends SIGTERM to all orphans
- Waits for configurable grace period (default 60s)
- Sends SIGKILL to any that survived SIGTERM
3. **Add `--cleanup-orphans-grace-secs` flag**: Allows users to configure the
grace period between SIGTERM and SIGKILL (default 60 seconds).
## Design Choice: Synchronous vs. Persistent State
The orphan cleanup uses a **synchronous wait approach** rather than the
persistent state machine approach in `util.CleanupOrphanedClaudeProcesses()`:
**Synchronous approach (this PR):**
- Send SIGTERM → Wait N seconds → Send SIGKILL (all in one invocation)
- Simpler to understand and debug
- User sees immediate results
- No persistent state file to manage
**Persistent state approach (util.CleanupOrphanedClaudeProcesses):**
- First run: SIGTERM → save state
- Second run (60s later): Check state → SIGKILL
- Requires multiple invocations
- Persists state in `/tmp/gastown-orphan-state`
The synchronous approach is more appropriate for `gt shutdown` where users
expect immediate cleanup, while the persistent approach is better suited for
periodic cleanup daemons.
## Testing
Before fix:
```
Sessions to stop: gt-boot, gt-pgqueue-refinery, gt-pgqueue-witness, hq-deacon, hq-mayor
✓ Gas Town shutdown complete (0 sessions stopped) ← Bug
```
After fix:
```
Sessions to stop: gt-boot, gt-pgqueue-refinery, gt-pgqueue-witness, hq-deacon, hq-mayor
✓ hq-deacon stopped
✓ gt-boot stopped
✓ gt-pgqueue-refinery stopped
✓ gt-pgqueue-witness stopped
✓ hq-mayor stopped
Cleaning up orphaned Claude processes...
→ PID 267916: sent SIGTERM (waiting 60s before SIGKILL)
⏳ Waiting 60 seconds for processes to terminate gracefully...
✓ 1 process(es) terminated gracefully from SIGTERM
✓ All processes cleaned up successfully
✓ Gas Town shutdown complete (5 sessions stopped) ← Fixed
```
All sessions verified terminated via `tmux ls`.
Co-authored-by: Roland Tritsch <roland@ailtir.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
ReconcilePool now detects and kills orphan tmux sessions (sessions without
corresponding polecat directories). This prevents allocation from being
blocked by broken state from crashed polecats.
Changes:
- Add tmux to Manager to check for orphan sessions during reconciliation
- Add ReconcilePoolWith for testable session/directory reconciliation logic
- Always clear hook_bead slot when reopening agent beads (fixes stale hooks)
- Prune stale git worktree entries during reconciliation
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix(config): implement role_agents support in BuildStartupCommand
The role_agents field in TownSettings and RigSettings existed but was
not being used by the startup command builders. All services fell back
to the default agent instead of using role-specific agent assignments.
Changes:
- BuildStartupCommand now extracts GT_ROLE from envVars and uses
ResolveRoleAgentConfig() for role-based agent selection
- BuildStartupCommandWithAgentOverride follows the same pattern when
no explicit override is provided
- refinery/manager.go uses ResolveRoleAgentConfig with constants
- cmd/start.go uses ResolveRoleAgentConfig with constants
- Updated comments from hardcoded agent name to generic "agent"
- Added ValidateAgentConfig() to check agent exists and binary is in PATH
- Added lookupAgentConfigIfExists() helper for validation
- ResolveRoleAgentConfig now warns to stderr and falls back to default
if configured agent is invalid or binary is missing
Resolution priority (now working):
1. Explicit --agent override
2. Rig's role_agents[role] (validated)
3. Town's role_agents[role] (validated)
4. Rig's agent setting
5. Town's default_agent
6. Hardcoded default fallback
Adds tests for:
- TestBuildStartupCommand_UsesRoleAgentsFromTownSettings
- TestBuildStartupCommand_RigRoleAgentsOverridesTownRoleAgents
- TestBuildAgentStartupCommand_UsesRoleAgents
- TestValidateAgentConfig
- TestResolveRoleAgentConfig_FallsBackOnInvalidAgent
Fixes: role_agents configuration not being applied to services
* fix(config): add GT_ROOT to BuildStartupCommandWithAgentOverride
- Fixes missing GT_ROOT and GT_SESSION_ID_ENV exports in
BuildStartupCommandWithAgentOverride, matching BuildStartupCommand behavior
- Adds test for override priority over role_agents
- Adds test verifying GT_ROOT is included in command
This addresses the Greptile review comment about agents started with
an override not having access to town-level resources.
Co-authored-by: Steve Yegge <steve.yegge@gmail.com>
* feat(refinery,boot): add --agent flag for model selection (hq-7d5m)
Add --agent flag to gt refinery start/attach/restart and gt boot spawn
commands for consistent model selection across all agent launch points.
Implementation follows the existing pattern from gt deacon start:
- Add StringVar flag for agent alias
- Pass override to Manager/Boot via SetAgentOverride()
- Use BuildAgentStartupCommandWithAgentOverride when override is set
Files affected:
- cmd/gt/refinery.go: add flags to start/attach/restart commands
- internal/refinery/manager.go: add SetAgentOverride and use in Start()
- cmd/gt/boot.go: add flag to spawn command
- internal/boot/boot.go: add SetAgentOverride and use in spawnTmux()
Closes#438
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor(refinery,boot): use parameter-passing pattern for --agent flag
Address PR review feedback:
1. ADD TESTS: Add tests for --agent flag existence following witness_test.go pattern
- internal/cmd/refinery_test.go: tests for start/attach/restart
- internal/cmd/boot_test.go: test for spawn
2. ALIGN PATTERN: Change from setter pattern to parameter-passing pattern
- Manager.Start(foreground, agentOverride) instead of SetAgentOverride + Start
- Boot.Spawn(agentOverride) instead of SetAgentOverride + Spawn
- Matches witness.go style: Start(foreground bool, agentOverride string, ...)
Updated all callers to pass empty string for default agent:
- internal/daemon/daemon.go
- internal/cmd/rig.go
- internal/cmd/start.go
- internal/cmd/up.go
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: furiosa <will@saults.io>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Before calling tmux kill-session, explicitly kill the pane's process tree
using pkill. This ensures claude processes don't survive session termination
due to SIGHUP being caught/ignored.
Implementation:
- Add KillSessionWithProcesses() to tmux.go
- Update killSessionsInOrder() in start.go to use new method
- Update stopSession() in down.go to use new method
Fixes: gt-5r7zr
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use errors.Is() for all ErrAlreadyRunning comparisons (consistency)
- Remove redundant HasSession check before Start() (was a race anyway)
- Remove unused tmux parameters from startRigAgents and startWitnessForRig
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove duplicate *Parallel variants, consolidate into single functions
- Cache discoverAllRigs() result at top level, pass to functions
- Use sync/atomic for startedAny flag instead of extra mutex
- Functions now take rigs slice and mutex as parameters
Net reduction: 83 lines
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Start Mayor, Deacon, rig agents, and crew all in parallel rather than
sequentially. This reduces worst-case startup from N*60s to ~60s since
all agents can start concurrently.
Closes gt-dgbwk
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Issue #336: Consolidate down/shutdown/stop commands
Changes:
- Add `gt down --polecats` flag to stop all polecat sessions
- Deprecate `gt stop` command (prints warning, directs to `gt down --polecats`)
- Update help text to clarify down vs shutdown distinction:
- down = pause (reversible, keeps worktrees)
- shutdown = done (permanent cleanup)
- Integrate --polecats with new --dry-run mode from recent PR
Note: The issue proposed renaming --nuke to --tmux, but PR #330 just
landed with --nuke having better safety (GT_NUKE_ACKNOWLEDGED env var),
so keeping --nuke as-is. The new --polecats flag absorbs gt stop
functionality as proposed.
Closes#336
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extends the --agent flag with a more general --env flag that allows
setting arbitrary environment variables when starting a witness.
Precedence (highest to lowest):
1. CLI --env overrides
2. Role bead env_vars
3. config.AgentEnv() defaults
Examples:
gt witness start greenplace --env ANTHROPIC_MODEL=claude-3-haiku
gt witness restart greenplace --env DEBUG=1 --env VERBOSE=true
Co-authored-by: joshuavial <git@codewithjv.com>
Agents were confused when receiving "gt prime" as their first prompt,
interpreting it as a command to investigate rather than understanding
they were starting a Gas Town session.
Changed crew_at.go, start.go, and handoff.go to use FormatStartupNudge()
which produces a proper beacon like:
[GAS TOWN] george/crew/george <- human • 2026-01-09T10:30 • start
The SessionStart hook (gt prime --hook) still injects context - the
prompt just needs to be something agents recognize as a greeting.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
gt shutdown was not stopping the daemon, which caused it to restart
agents (witnesses, refineries) after shutdown completed. The daemon
heartbeats every 3 minutes and calls ensureWitnessesRunning() and
ensureRefineriesRunning(), which would notice the sessions were dead
and restart them.
This adds daemon stop logic to both runGracefulShutdown (as Phase 6)
and runImmediateShutdown (after polecat cleanup), matching the behavior
that gt down already has.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The manager refactors (ea8bef2, 72544cc0) conflicted with the agent
override feature, causing regressions:
Deacon (ea8bef2):
- Lost agentOverride parameter
- Re-added respawn loop (removed in 5f2e16f)
- Lost GUPP (startup + propulsion nudges)
Crew (72544cc0):
- Lost agentOverride wiring to StartOptions
- --agent flag had no effect on crew refresh/restart
This fix restores agent override support and GUPP while keeping
improvements from the manager refactors (zombie detection, etc).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replaces inline ensureRefinerySession function with refinery.NewManager(r).Start(false) in gt start --all. Gains zombie detection, proper state tracking, and WaitForShellReady fix.
CI failures (lint in beads.go, integration tests) are pre-existing issues unrelated to this PR's changes.
Co-Authored-By: julianknutsen <julianknutsen@users.noreply.github.com>
- Create mayor.Manager for mayor lifecycle (Start/Stop/IsRunning/Status)
- Create deacon.Manager for deacon lifecycle with respawn loop
- Move session.Manager to polecat.SessionManager (clearer naming)
- Add zombie session detection for mayor/deacon (kills tmux if Claude dead)
- Remove duplicate session startup code from up.go, start.go, mayor.go
- Rename sessMgr -> polecatMgr for consistency
- Make witness/refinery SessionName() public for status display
All agent types now follow the same Manager pattern:
mgr := agent.NewManager(...)
mgr.Start(...)
mgr.Stop()
mgr.IsRunning()
mgr.Status()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Refactors all agent startup paths (witness, refinery, crew, polecat) to use
a consistent Manager interface with Start(), Stop(), IsRunning(), and
SessionName() methods.
Includes:
- Witness manager with GUPP propulsion nudge for startup
- Refinery manager for engineer sessions
- Crew manager for worker agents
- Session/polecat manager updates
- claude_settings_check doctor check for settings validation
- Settings management consolidated from rig/manager.go
- Settings location moved outside source repos to prevent conflicts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Resolved conflict in internal/witness/manager.go:
- Kept session import (used by PR code)
- Kept PR's more accurate comment for PID check
- Removed duplicate sessionName method introduced by merge
* fix: create mayor/daemon.json during gt start and gt doctor --fix (#5)
- Add DaemonPatrolConfig type with heartbeat and patrol settings
- Add Load/Save/Ensure functions for daemon patrol config
- Create daemon.json in gt start (non-fatal if fails)
- Make PatrolHooksWiredCheck fixable with Fix() method
- Add comprehensive tests for both config and doctor checks
This fixes the issue where gt doctor expects mayor/daemon.json to exist
but it was never created by gt start or any other command.
* refactor: use constants.DirMayor instead of hardcoded string
Witness and Refinery startup was duplicated across cmd/witness.go, cmd/up.go,
cmd/rig.go, and daemon.go. Worse, not all code paths sent the propulsion nudge
(GUPP - Gas Town Universal Propulsion Principle). Now unified in Manager.Start()
which handles everything including nudges.
Changes:
- witness/manager.go: Full rewrite with session creation, env vars, theming,
WaitForClaudeReady, startup nudge, and propulsion nudge (GUPP)
- refinery/manager.go: Add propulsion nudge sequence after Claude startup
- cmd/witness.go: Simplify to just call mgr.Start(), remove ensureWitnessSession
- cmd/rig.go: Use witness.Manager.Start() instead of inline session creation
- cmd/start.go: Use witness.Manager.Start()
- cmd/up.go: Use witness.Manager.Start(), remove ensureWitness(),
add EnsureSettingsForRole in ensureSession()
- daemon.go: Use witness.Manager.Start() and refinery.Manager.Start() for
unified startup with proper nudges
This ensures all agent startup paths (gt witness start, gt rig boot, gt up,
daemon restarts) consistently apply GUPP propulsion nudges.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
In startConfiguredCrew(), only HasSession() was checked, missing the case
where a tmux session exists but Claude has exited. Now checks IsClaudeRunning()
and restarts Claude with BuildCrewStartupCommand if dead, matching the behavior
in runStartCrew(). (gt-ms8s4)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Town-level services (Mayor, Deacon) now use hq- prefix instead of gt-:
- hq-mayor (was gt-mayor)
- hq-deacon (was gt-deacon)
This distinguishes town-level sessions from rig-level sessions which
continue to use gt- prefix (gt-gastown-witness, gt-gastown-crew-max, etc).
Changes:
- session.MayorSessionName() returns "hq-mayor"
- session.DeaconSessionName() returns "hq-deacon"
- ParseSessionName() handles both hq- and gt- prefixes
- categorizeSession() handles both prefixes
- categorizeSessions() accepts both prefixes
- Updated all tests and documentation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The issue: gt crew start --all was not priming crew sessions like
gt crew at does.
Root cause: gt crew at passes "gt prime" as the initial prompt to
BuildCrewStartupCommand(), while gt crew start (via startCrewMember
and runStartCrew) passed an empty string and tried to send gt prime
afterwards via NudgeSession. This created a race condition where the
SessionStart hook would fire and Claude would start responding before
the nudge arrived.
Fix: Pass "gt prime" directly in the startup command for all three
cases: startCrewMember, runStartCrew new session, and runStartCrew
session restart. This makes the behavior consistent with gt crew at.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add --branch flag to `gt rig add` to specify a custom default branch
instead of auto-detecting from remote. This supports repositories that
use non-standard default branches like `develop` or `release`.
Changes:
- Add --branch flag to `gt rig add` command
- Store default_branch in rig config.json
- Propagate default branch to refinery, witness, daemon, and all commands
- Rename ensureMainBranch to ensureDefaultBranch for clarity
- Add Rig.DefaultBranch() method for consistent access
- Update crew/manager.go and swarm/manager.go to use rig config
Based on PR #49 by @kustrun - rebased and extended with additional fixes.
Co-authored-by: kustrun <kustrun@users.noreply.github.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Session names `gt-mayor` and `gt-deacon` were hardcoded, causing tmux
session name collisions when running multiple towns simultaneously.
Changed to `gt-{town}-mayor` and `gt-{town}-deacon` format (e.g.,
`gt-ai-mayor`) to allow concurrent multi-town operation.
Key changes:
- session.MayorSessionName() and DeaconSessionName() now take townName param
- Added workspace.GetTownName() helper to load town name from config
- Updated all callers in cmd/, daemon/, doctor/, mail/, rig/, templates/
- Updated tests with new session name format
- Bead IDs remain unchanged (already scoped by .beads/ directory)
Fixes#60🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PropulsionNudgeForRole now accepts a workDir parameter and reads
session ID from .runtime/session_id to append [session:xxx] to the
nudge message. This enables Claude Code's /resume picker to discover
Gas Town sessions.
(gt-u49zh)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- startCrewMember: now uses BuildCrewStartupCommand (was GetRuntimeCommand)
- refinery/manager.go: now uses BuildAgentStartupCommand (was GetRuntimeCommand)
Both now properly inject BD_ACTOR and GT_ROLE so seance can identify
sessions correctly. This completes the seance metadata fix started in
the previous commit.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
gt crew start was using GetRuntimeCommand which doesn't set BD_ACTOR,
GT_RIG, GT_CREW, etc. This caused seance to misidentify crew sessions
(showing as "mayor" instead of their actual identity).
Now uses BuildCrewStartupCommand like gt crew restart does, ensuring
proper env var injection for session identification. (gt-jwxgb)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
GUPP (Gas Town Universal Propulsion Principle) is the propulsion nudge sent
after beacon to trigger autonomous work execution. Previously only polecats
received this nudge.
Now all roles get role-specific propulsion nudges on startup:
- Polecat/Crew: "Run `gt hook` to check your hook and begin work."
- Witness: "Run `gt prime` to check patrol status and begin work."
- Refinery: "Run `gt prime` to check MQ status and begin patrol."
- Deacon: "Run `gt prime` to check patrol status and begin heartbeat cycle."
- Mayor: "Run `gt prime` to check mail and begin coordination."
Changes:
- internal/session/names.go: Add PropulsionNudgeForRole() function
- internal/cmd/witness.go: Add GUPP nudge to ensureWitnessSession
- internal/cmd/start.go: Add GUPP nudge to ensureRefinerySession (also
converted from respawn loop to direct Claude launch like other roles)
- internal/cmd/deacon.go: Add GUPP nudge to startDeaconSession
- internal/cmd/mayor.go: Add GUPP nudge to startMayorSession
Fixes: gt-zzpmt
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
SendKeys has an Enter-concatenation bug that causes the nudge to fail.
NudgeSession is the canonical way to message Claude and works reliably.
Fixes cold starts of crew workers (gt crew start).
(gt-7swyi)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Using "greenplace" (The Green Place from Mad Max: Fury Road) as the
canonical example project/rig name in documentation and help text.
This provides a clearer distinction from the actual gastown repo name.
Changes:
- docs/*.md: Updated all example paths and commands
- internal/cmd/*.go: Updated help text examples
- internal/templates/: Updated example references
- Tests: Updated to use greenplace in example session names
Note: Import paths (github.com/steveyegge/gastown) and actual code
paths referencing the gastown repo structure are unchanged.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Inject an identity beacon as the first message when Gas Town starts Claude
sessions. This beacon becomes the session title in Claude Code '/resume
picker, enabling workers to find their predecessor sessions for debugging.
Beacon format: [GAS TOWN] <address> • <mol-id or "ready"> • <timestamp>
Examples:
- [GAS TOWN] gastown/crew/max • gt-abc12 • 2025-12-30T14:32
- [GAS TOWN] gastown/polecats/Toast • ready • 2025-12-30T09:15
- [GAS TOWN] deacon • patrol • 2025-12-30T08:00
Workers can now press / in /resume picker and search for their address
(e.g., "gastown/crew/max") to see all predecessor sessions.
Note: Respawn-loop agents (deacon/refinery via up.go) skip beacon injection
since Claude restarts multiple times - would need post-restart injection.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add PrintWarning helper in internal/style/style.go
- Update 35 warning message outputs across 16 files to use consistent format
- All warnings now display as "⚠ Warning: <message>" in yellow/bold
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extended the unified cycle system to include rig infrastructure sessions:
- Witness ↔ Refinery (per rig) now cycle with C-b n/p
Also moved SetCycleBindings into ConfigureGasTownSession so ALL Gas Town
sessions automatically get the unified cycle bindings. Removed redundant
individual calls from crew, mayor, and deacon startup code.
Cycle groups are now:
- Town: Mayor ↔ Deacon
- Crew (per rig): All crew members in same rig
- Infra (per rig): Witness ↔ Refinery
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reduced runStart from 126 lines to 48 lines by extracting:
- startCoreAgents: Mayor and Deacon session startup (28 lines)
- startRigAgents: Witness/Refinery startup for --all flag (37 lines)
- startConfiguredCrew: Auto-start configured crew members (29 lines)
No behavior change - all existing tests pass.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
gt start now reads rig settings (settings/config.json) and auto-starts
configured crew members. The crew.startup field supports:
- Single name: "dave"
- Multiple names: "max and joe" or "max, joe"
- All crew: "all"
- None: "none" or empty
Configured:
- beads: dave
- gastown: max and joe
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
All 156 instances of _ = error suppression in non-test code now have
explanatory comments documenting why the error is intentionally ignored.
Categories of intentional suppressions:
- non-fatal: session works without these - tmux environment setup
- non-fatal: theming failure does not affect operation - visual styling
- best-effort cleanup - defer cleanup on failure paths
- best-effort notification - mail/notifications that should not block
- best-effort interrupt - graceful shutdown attempts
- crypto/rand.Read only fails on broken system - random ID generation
- output errors non-actionable - fmt.Fprint to io.Writer
This addresses the silent failure and debugging concerns raised in the
issue by making the intentionality explicit in the code.
Generated with Claude Code https://claude.com/claude-code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When gt spawns agents (polecats, crew, patrol roles), it now sets the
BD_ACTOR env var so that bd commands (like `bd hook`) know the agent
identity without coupling to gt.
Updated spawn points:
- gt up (mayor, deacon, witness via ensureSession/ensureWitness)
- gt deacon start
- gt witness start
- gt start refinery
- gt mayor start
- Daemon deacon restart
- Daemon lifecycle restart
- Handoff respawn
- Refinery manager start
BD_ACTOR uses slash format (e.g., gastown/witness, gastown/crew/max)
while GT_ROLE may use dash format internally.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>