Commit Graph

2987 Commits

Author SHA1 Message Date
gastown/crew/dennis
c7e1451ce6 fix(polecat): ignore .beads/ files when detecting uncommitted work
Add CleanExcludingBeads() method that returns true if the only uncommitted
changes are .beads/ database files. These files are synced across worktrees
and shouldn't block polecat cleanup.

Fixes #516

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 15:26:35 -08:00
aleiby
f89ac47ff9 fix(tmux): kill pane process explicitly to prevent setsid orphans (#567)
KillSessionWithProcesses was only killing descendant processes,
assuming the session kill would terminate the pane process itself.
However, if the pane process (claude) calls setsid(), it detaches
from the controlling terminal and survives the session kill.

This fix explicitly kills the pane PID after killing descendants,
before killing the tmux session. This catches processes that have
escaped the process tree via setsid().

Fixes #513

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 15:25:52 -08:00
aleiby
e344e77921 fix(tmux): serialize nudges to prevent interleaving (#571)
When multiple agents start simultaneously (e.g., `gt up`), each runs
`gt nudge deacon session-started` in their SessionStart hook. These
nudges arrive concurrently and can interleave in the tmux input buffer,
causing:

1. Text from one nudge mixing with another
2. Enter keys not properly submitting messages
3. Garbled input requiring manual intervention

This fix adds per-session mutex serialization to NudgeSession() and
NudgePane(). Concurrent nudges to the same session now queue and
execute one at a time.

## Root Cause

The NudgeSession pattern sends text, waits 500ms, sends Escape, waits
100ms, then sends Enter. When multiple nudges arrive within this ~800ms
window, their send-keys commands interleave, corrupting the input.

## Alternatives Considered

1. **Delay deacon nudges** - Add sleep before nudge in SessionStart
   - Simplest (one-line change)
   - But: doesn't prevent concurrent nudges from multiple agents

2. **Debounce session-started** - Deacon ignores rapid-fire nudges
   - Medium complexity
   - But: only helps session-started, not other nudge types

3. **File-based signaling** - Replace tmux nudges with file watches
   - Avoids tmux input issues entirely
   - But: significant architectural change

4. **File upstream bug** - Report to Claude Code team
   - SessionStart hooks fire async and can interleave
   - But: fix timeline unknown, need robustness now

## Tradeoffs

- Concurrent nudges to same session now queue (adds latency)
- Memory: one mutex per unique session name (bounded, acceptable)
- Does not fix Claude Code's underlying async hook behavior

## Testing

- Build passes
- All tmux package tests pass
- Manual testing: started deacon + multiple witnesses concurrently,
  nudges processed correctly without garbled input

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 15:25:49 -08:00
Erik LaBianca
a09c6b71d7 test(rig): add tests for agent bead creation during rig add (#578)
Add tests to verify that rig.Manager.AddRig correctly creates witness
and refinery agent beads via initAgentBeads. Also improve mock bd:

- Fix mock bd to handle --no-daemon --allow-stale global flags
- Return valid JSON for create commands with bead ID
- Log create commands for test verification
- Add TestRigAddCreatesAgentBeads integration test
- Add TestAgentBeadIDs unit test for bead ID generation

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 15:23:35 -08:00
Erik LaBianca
4fa6cfa0da fix(mq): skip closed MRs in list, next, and ready views (#563)
* fix(mq): skip closed MRs in list, next, and ready views (gt-qtb3w)

The gt mq list command with --status=open filter was incorrectly displaying
CLOSED merge requests as 'ready'. This occurred because bd list --status=open
was returning closed issues.

Added manual status filtering in three locations:
- mq_list.go: Filter closed MRs in all list views
- mq_next.go: Skip closed MRs when finding next ready MR
- engineer.go: Skip closed MRs in refinery's ready queue

Also fixed build error in mail_queue.go where QueueConfig struct (non-pointer)
was being compared to nil.

Workaround for upstream bd list status filter bug.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* style: fix gofmt issue in engineer.go comment block

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-16 15:23:28 -08:00
Steve Whittaker
c51047b654 docs: fix misleading help text for gt mail read (#565)
The help text claimed 'gt mail read' marks messages as read, but this
was intentionally removed in 71d313ed to preserve handoff messages.

Update the help text to accurately reflect the current behavior and
point users to 'gt mail mark-read' for explicit read marking.
2026-01-16 15:22:09 -08:00
gastown/crew/gus
d42a9bd6e0 fix(polecat): validate issue exists before starting session
Add validateIssue() to check that an issue exists and is not tombstoned
before creating the tmux session. This prevents CPU spin loops from
agents retrying work on invalid issues.

Fixes #569

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 15:18:54 -08:00
gastown/crew/george
08ef50047d fix(doctor): add zombie session check to detect dead Claude in tmux
When gt doctor runs, it now detects and kills zombie sessions - tmux
sessions that are valid Gas Town sessions (gt-*, hq-*) but have no
Claude/node process running inside. These occur when Claude exits or
crashes but the tmux session remains.

Previously, OrphanSessionCheck only validated session names but did not
check if Claude was actually running. This left empty sessions
accumulating over time.

Fixes #472

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:54:45 -08:00
gastown/crew/dennis
95cb58e36f fix(beads): ensure directory exists before writing routes.jsonl
WriteRoutes() would fail if the beads directory didn't exist yet.
Add os.MkdirAll before creating the routes file.

Fixes #552

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:49:44 -08:00
gastown/crew/dennis
d3606c8c46 fix(ready): filter formula scaffolds from gt ready output
Formula scaffolds (beads with IDs starting with "mol-") are templates
created when formulas are installed, not actual work items. They were
incorrectly appearing in gt ready output as actionable work.

Fixes #579

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:43:24 -08:00
gastown/crew/dennis
a88d2e1a9e fix(mail): filter unread messages in beads mode
ListUnread() was returning all messages in beads mode instead of
filtering by the Read field. Apply the same filtering logic used
in legacy mode to both code paths.

Fixes #595

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:43:24 -08:00
gastown/crew/george
29039ed69d fix(migrate_agents_test): test actually calls getMigrationStatusIcon
The test was duplicating the icon selection logic in a switch statement
instead of calling the actual function being tested. Extract the icon
logic into getMigrationStatusIcon() and have the test call it directly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 12:41:23 -08:00
JJ
b1a5241430 fix(beads): align agent bead prefixes and force multi-hyphen IDs (#482)
* fix(beads): align agent bead prefixes and force multi-hyphen IDs

* fix(checkpoint): treat threshold as stale at boundary
2026-01-16 12:33:51 -08:00
sigfawn
03213a7307 fix(migrate_agents_test): fix icon expectations to match actual output (#439)
* fix(beads): cache version check and add timeout to prevent cli lag

* fix(migrate_agents_test): fix icon expectations to match actual output

The printMigrationResult function uses icons with two leading spaces
("  ✓", "  ⊘", "  ✗") but the test expected icons without spaces.
This fixes the test expectations to match the actual output format.
2026-01-16 11:41:52 -08:00
Julian Knutsen
7e158cddd6 fix(sling): set attached_molecule field when bonding formula to bead (#451)
When using `gt sling <formula> --on <bead>`, the wisp was bonded to the
target bead but the attached_molecule field wasn't being set in the
bead's description. This caused `gt hook` to report "No molecule
attached" even though the formula was correctly bonded.

Now both sling.go (--on mode) and sling_formula.go (standalone formula)
call storeAttachedMoleculeInBead() to record the molecule attachment
after wisp creation. This ensures gt hook can properly display molecule
progress.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 11:41:05 -08:00
Julian Knutsen
e5aea04fa1 fix(done): get issue ID from agent hook and detect integration branches (#411) (#453)
Branch names like "polecat/furiosa-mkb0vq9f" don't contain the actual
issue ID, causing gt done to incorrectly parse "furiosa-mkb0vq9f" as the
issue. This broke integration branch auto-detection since the wrong issue
was used for parent epic lookup.

Changes:
- After parsing branch name, check the agent's hook_bead field which
  contains the actual issue ID (e.g., "gt-845.1")
- Fix parseBranchName to not extract fake issue IDs from modern polecat branches
- Fix detectIntegrationBranch to traverse full parent chain (molecule → bug → epic)
- Include issue ID in polecat branch names when HookBead is set

Added tests covering:
- Agent hook returns correct issue ID
- Modern polecat branch format parsing
- Integration branch detection through parent chain

Fixes #411

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 11:40:18 -08:00
Daniel Sauer
8332a719ab fix(errors): use errors.As for wrapped error handling (#462)
IsSilentExit used type assertion which fails on wrapped errors.
Changed to errors.As to properly unwrap and detect SilentExitError.

Added test to verify wrapped error detection works.
2026-01-16 11:05:59 -08:00
Jasper Croome
139f3aeba3 Fix stop hook failing in role subdirectories (#597)
The stop hook runs 'gt costs record' which executes 'bd create' to
record session costs. When run from a role subdirectory (e.g., mayor/)
that doesn't have its own .beads database, bd fails with:
  'database not initialized: issue_prefix config is missing'

Fix by using workspace.FindFromCwd() to locate the town root and
setting bdCmd.Dir to run bd from there, where the .beads database
exists.
2026-01-16 10:59:42 -08:00
Erik LaBianca
add3d56c8b fix(doctor): add sqlite3 availability check (#575)
- Add sqlite3 to README.md prerequisites section
- Add gt doctor check that warns if sqlite3 CLI is not found
- Documents that sqlite3 is required for convoy database queries

Fixes #534

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 10:59:12 -08:00
jonathan berger
5c13e5f95a Properly place 'Getting Started' section in README (#598)
It got jammed at the bottom, apparently by accident. Here's a better place for it.
2026-01-16 10:57:33 -08:00
gastown/crew/max
3ebb1118d3 fix(mail): use workspace.Find for consistent town root detection
detectTownRoot() was only checking for mayor/town.json, but some
workspaces only have the mayor/ directory without town.json.
This caused mail routing to fail silently - messages showed
success but werent persisted because townRoot was empty.

Now uses workspace.Find() which supports both primary marker
(mayor/town.json) and secondary marker (mayor/ directory).

Fixes: gt-6v7z89

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 19:24:03 -08:00
gastown/crew/max
618b0d9810 feat(cli): add 'gt show' command for inspecting beads
Desire path: agents naturally try 'gt show <id>' to inspect beads.
This wraps 'bd show' via syscall.Exec, passing all flags through.

- Works with any prefix (gt-, bd-, hq-, etc.)
- Routes to correct beads database automatically
- DisableFlagParsing passes all flags to bd show

Closes gt-82jxwx

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 19:19:13 -08:00
beads/crew/emma
39185f8d00 feat(cmd): add 'gt cat' command to display bead content
Implements the desire-path from bd-dcahx: agents naturally try
'gt cat <bead-id>' to view bead content, following Unix conventions.

The command validates bead ID prefixes (bd-*, hq-*, mol-*) and
delegates to 'bd show' for the actual display.

Supports --json flag for programmatic use.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 17:44:40 -08:00
beads/crew/emma
a4776b9bee refactor(polecat): remove unused 'cat' alias
The 'cat' alias for 'gt polecat' was never used by agents.
Removing it frees up 'cat' for a more intuitive use case:
displaying bead content (gt cat <bead-id>).

See: bd-dcahx

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 17:40:28 -08:00
gastown/crew/max
20effb0a51 fix(beads): add CreatedAt to group/channel creation, check channel status
- Add CreatedAt timestamp to CreateGroupBead() in beads_group.go
- Add CreatedAt timestamp to CreateChannelBead() in beads_channel.go
- Check channel status before sending in router.go sendToChannel()
  - Reject sends to closed channels with appropriate error message

Closes: gt-yibjdm, gt-bv2f97

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 07:33:36 -08:00
gastown/crew/max
4f02abb535 fix(mail): add channel routing to router.Send()
The router was missing support for beads-native channel addresses.
When mail_send.go resolved an address to RecipientChannel, it set
msg.To to "channel:<name>" but router.Send() had no handler for this
prefix, causing channel messages to fail silently.

Added:
- isChannelAddress() and parseChannelName() helper functions
- sendToChannel() method that creates messages with proper channel:
  labels for channel queries
- Channel validation before sending
- Retention enforcement after message creation

Also updated docs/beads-native-messaging.md with more comprehensive
documentation of the beads-native messaging system.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 07:23:34 -08:00
gastown/crew/max
cbbf566f06 fix(beads): use hq- prefix for group/channel beads (town-level entities)
Groups and channels are town-level entities that span rigs, so they
should use the hq- prefix rather than gt- (rig-level).

Changes:
- GroupBeadID: gt-group- → hq-group-
- ChannelBeadID: gt-channel- → hq-channel-
- Add --force flag to bypass prefix validation (town beads may have
  mixed prefixes from test runs)
- Update tests and documentation

Also adds docs/beads-native-messaging.md documenting:
- New bead types (gt:group, gt:queue, gt:channel)
- CLI commands (gt mail group, gt mail channel)
- Address resolution logic
- Usage examples

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 07:23:34 -08:00
gastown/crew/dennis
e30e46a87a feat(mail): add queue management commands
Add beads-native queue management commands to gt mail:
- gt mail queue create <name> --claimers <pattern>
- gt mail queue show <name>
- gt mail queue list
- gt mail queue delete <name>

Also enhanced QueueFields struct with CreatedBy and CreatedAt fields
to support queue metadata tracking.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:29:37 -08:00
gastown/crew/george
7bbc09230e fix(beads): use hq prefix for channel bead IDs
Change ChannelBeadID to use hq-channel-* prefix instead of gt-channel-*
to match the town-level beads database prefix, fixing the "prefix mismatch"
error when creating channels.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:25:46 -08:00
gastown/crew/jack
2ffc8e8712 feat(mail): implement beads-native gt mail claim command
Implement claiming for queue messages using beads-native approach:

- Add claim_pattern field to QueueFields for eligibility checking
- Add MatchClaimPattern function for pattern matching (wildcards supported)
- Add FindEligibleQueues to find all queues an agent can claim from
- Rewrite runMailClaim to use beads-native queue lookup
- Support optional queue argument (claim from any eligible if not specified)
- Use claimed-by/claimed-at labels instead of changing assignee
- Update runMailRelease to work with new claiming approach
- Add comprehensive tests for pattern matching and validation

Queue messages are now claimed via labels:
  - claimed-by: <agent-identity>
  - claimed-at: <RFC3339 timestamp>

Messages with queue:<name> label but no claimed-by are unclaimed.

Closes gt-xfqh1e.11

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:25:11 -08:00
gastown/crew/max
012d50b2b2 feat(beads): implement channel message retention
Add two-layer retention for beads-native channel messages:

1. On-write cleanup (EnforceChannelRetention):
   - Called after posting to channel
   - Deletes oldest messages when count > retainCount

2. Deacon patrol backup (PruneAllChannels):
   - Scans all channels periodically
   - Uses 10% buffer to avoid thrashing
   - Catches edge cases: crashed mid-write, manual insertions

Part of gt-xfqh1e.13 (channel retention task).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:23:34 -08:00
gastown/crew/max
bf8bddb004 feat(mail): add channel viewing and management commands
Add gt mail channel subcommands for beads-native channels:
- gt mail channel [name] - list channels or show messages
- gt mail channel list - list all channels
- gt mail channel show <name> - show channel messages
- gt mail channel create <name> [--retain-count=N] [--retain-hours=N]
- gt mail channel delete <name>

Channels are pub/sub streams for broadcast messaging with retention policies.
Messages are stored with channel:<name> label and retrieved via beads queries.

Part of gt-xfqh1e.12 (channel viewing task).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:22:00 -08:00
gastown/crew/max
42999d883d feat(mail): update mail send to use address resolver
Integrate the new address resolver into gt mail send:
- Resolves addresses to determine delivery mode (agent, queue, channel)
- Queue/channel: single message delivery
- Agent/group/pattern: fan-out to all resolved recipients
- Falls back to legacy routing if resolver fails
- Shows resolved recipients when fan-out occurs

Supports all new address types:
- Direct: gastown/crew/max
- Patterns: */witness, gastown/*
- Groups: @ops-team (beads-native groups)
- Queues: queue:work-requests
- Channels: channel:alerts

Part of gt-xfqh1e.10 (mail send update task).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:19:54 -08:00
gastown/crew/max
b3b980fd79 feat(mail): add group management commands
Add gt mail group subcommands:
- gt mail group list - list all groups
- gt mail group show <name> - show group details
- gt mail group create <name> [members...] - create new group
- gt mail group add <name> <member> - add member
- gt mail group remove <name> <member> - remove member
- gt mail group delete <name> - delete group

Includes validation for group names and member patterns.
Supports direct addresses, wildcards, @-patterns, and nested groups.

Part of gt-xfqh1e.7 (group commands task).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:18:20 -08:00
gastown/crew/max
839fa19e90 feat(mail): implement address resolution for beads-native messaging
Add Resolver type with comprehensive address resolution:
- Direct agent addresses (contains '/')
- Pattern matching (*/witness, gastown/*)
- @-prefixed patterns (@town, @crew, @rig/X)
- Beads-native groups (gt:group beads)
- Name lookup: group → queue → channel
- Conflict detection with explicit prefix requirement

Implements resolution order per gt-xfqh1e epic design:
1. Contains '/' → agent address or pattern
2. Starts with '@' → special pattern
3. Otherwise → lookup by name with conflict detection

Part of gt-xfqh1e.5 (address resolution task).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:15:51 -08:00
gastown/crew/george
7164e7a6d2 feat(beads): add channel bead type for pub/sub messaging
Add ChannelFields struct and CRUD operations for channel beads:
- ChannelFields with name, subscribers, status, retention settings
- CreateChannelBead, GetChannelBead, GetChannelByID methods
- SubscribeToChannel, UnsubscribeFromChannel for subscriber management
- UpdateChannelRetention, UpdateChannelStatus for configuration
- ListChannelBeads, LookupChannelByName, DeleteChannelBead
- Unit tests for parsing, formatting, and round-trip serialization

Part of gt-xfqh1e convoy: Beads-native messaging

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:15:28 -08:00
gastown/crew/jack
8eafcc8a16 feat(mail): extend message bead for queues/channels
Add queue/channel routing fields to message beads:
- queue: string (queue name, mutually exclusive with to/channel)
- channel: string (channel name, mutually exclusive with to/queue)
- claimed_by: string (who claimed queue message)
- claimed_at: timestamp (when claimed)

Messages can now be direct (To), queued (Queue), or broadcast (Channel).
Added constructors NewQueueMessage/NewChannelMessage, type helpers
IsQueueMessage/IsChannelMessage/IsDirectMessage/IsClaimed, and
Validate() for mutual exclusivity checks.

Also fixes build error in mail_queue.go (QueueConfig struct nil comparison).

Closes gt-xfqh1e.4

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:14:36 -08:00
gastown/crew/dennis
a244c3d498 feat(beads): add queue bead type
Add queue bead type for tracking work queues in Gas Town. This includes:
- QueueFields struct with status, concurrency, processing order, and counts
- Parse/Format functions for queue field serialization
- CRUD methods: CreateQueueBead, GetQueueBead, UpdateQueueFields, etc.
- Queue registered in BeadsCustomTypes for bd CLI support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:11:19 -08:00
gastown/crew/max
0bf68de517 feat(beads): add group bead type for beads-native messaging
Add type=group to beads schema for mail distribution groups.

Fields:
- name: unique group identifier
- members: addresses, patterns, or group names (can nest)
- created_by: provenance tracking
- created_at: timestamp

Groups support:
- Direct addresses (gastown/crew/max)
- Patterns (*/witness, @crew)
- Nested groups (members can reference other groups)

Part of gt-xfqh1e epic (beads-native messaging).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:09:48 -08:00
Steve Yegge
42d9890e5c fix(deacon): improve health check reliability and error handling (#499)
Co-authored-by: Dylan <sigfawn@gmail.com>
2026-01-13 22:34:03 -08:00
JeremyKalmus
92144757ac fix(prime): add gt done to Session Close Protocol in PRIME.md (#490)
Polecats were not calling `gt done` after completing work because
the compact PRIME.md context (used after compaction or when the
SessionStart hook is the only context) was missing this critical step.

The Session Close Protocol listed steps 1-6 (git status, add, bd sync,
commit, bd sync, push) but omitted step 7 (`gt done`), which:
- Submits work to the merge queue
- Exits the polecat session
- Allows the witness to spawn new polecats for remaining work

Without `gt done`, polecats would push code and announce "done" but
remain idle in their sessions, blocking the workflow cascade.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 22:14:00 -08:00
Julian Knutsen
e7ca4908dc refactor(config): remove BEADS_DIR from agent environment and add doctor check (#455)
* fix(sling_test): update test for cook dir change

The cook command no longer needs database context and runs from cwd,
not the target rig directory. Update test to match this behavior
change from bd2a5ab5.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(tests): skip tests requiring missing binaries, handle --allow-stale

- Add skipIfAgentBinaryMissing helper to skip tests when codex/gemini
  binaries aren't available in the test environment
- Update rig manager test stub to handle --allow-stale flag

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(config): remove BEADS_DIR from agent environment

Stop exporting BEADS_DIR in AgentEnv - agents should use beads redirect
mechanism instead of relying on environment variable. This prevents
prefix mismatches when agents operate across different beads databases.

Changes:
- Remove BeadsDir field from AgentEnvConfig
- Remove BEADS_DIR from env vars set on agent sessions
- Update doctor env_check to not expect BEADS_DIR
- Update all manager Start() calls to not pass BeadsDir

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(doctor): detect BEADS_DIR in tmux session environment

Add a doctor check that warns when BEADS_DIR is set in any Gas Town
tmux session. BEADS_DIR in the environment overrides prefix-based
routing and breaks multi-rig lookups - agents should use the beads
redirect mechanism instead.

The check:
- Iterates over all Gas Town tmux sessions (gt-* and hq-*)
- Checks if BEADS_DIR is set in the session environment
- Returns a warning with fix hint to restart sessions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 22:13:57 -08:00
sigfawn
3cf77b2e8b fix(daemon): improve error handling and security (#445)
* fix(beads): cache version check and add timeout to prevent cli lag

* fix(mail_queue): add nil check for queue config

Prevents potential nil pointer panic when queue config exists
in map but has nil value. Added || queueCfg == nil check to
the queue lookup condition in runMailClaim function.

Fixes potential panic that could occur if a queue entry exists
in config but with a nil value.

* fix(migrate_agents_test): fix icon expectations to match actual output

The printMigrationResult function uses icons with two leading spaces
("  ✓", "  ⊘", "  ✗") but the test expected icons without spaces.
This fixes the test expectations to match the actual output format.

* fix(hook): handle error from events.LogFeed

Previously the error from LogFeed was silently ignored with _.
Now we log the error to stderr at warning level but don't fail
the operation since the primary hook action succeeded.

* fix(tmux): security and error handling improvements

- Fix unchecked regexp error in IsClaudeRunning (CVE-like)
- Add input sanitization to SetPaneDiedHook to prevent shell injection
- Add session name validation to SetDynamicStatus
- Sanitize mail from/subject in SendNotificationBanner
- Return error on parse failure in GetEnvironment
- Track skipped lines in ListSessionIDs for debuggability

See: tmux.fix for full analysis

* fix(daemon): improve error handling and security

- Capture stderr in syncWorkspace for better debuggability
- Fail fast on git fetch failures to prevent stale code
- Add logging to previously silent bd list errors
- Change notification state file permissions to 0600
- Improve error messages with actual stderr content

This prevents agents from starting with stale code and provides
better visibility into daemon operations.
2026-01-13 22:13:54 -08:00
Julian Knutsen
a1195cb104 fix(crew): prevent restart when attaching to crew session with running agent (#491)
* fix(sling_test): update test for cook dir change

The cook command no longer needs database context and runs from cwd,
not the target rig directory. Update test to match this behavior
change from bd2a5ab5.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(tests): skip tests requiring missing binaries, handle --allow-stale

- Add skipIfAgentBinaryMissing helper to skip tests when codex/gemini
  binaries aren't available in the test environment
- Update rig manager test stub to handle --allow-stale flag

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(crew): prevent restart when attaching to session with running agent

When running `gt crew at <name>` while already inside the target tmux
session, the command would unconditionally start the agent, causing
Claude to restart even if it was already running.

Add IsAgentRunning check before starting the agent when already in
the target session, matching the behavior for the external attach case.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 22:13:47 -08:00
Julian Knutsen
80af0547ea chore: fix build break (#483)
* fix(sling_test): update test for cook dir change

The cook command no longer needs database context and runs from cwd,
not the target rig directory. Update test to match this behavior
change from bd2a5ab5.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(tests): skip tests requiring missing binaries, handle --allow-stale

- Add skipIfAgentBinaryMissing helper to skip tests when codex/gemini
  binaries aren't available in the test environment
- Update rig manager test stub to handle --allow-stale flag

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 22:13:35 -08:00
Keith Wyatt
08755f62cd perf(tmux): batch session queries in gt down (#477)
* perf(tmux): batch session queries in gt down to reduce N+1 subprocess calls

Add SessionSet type to tmux package for O(1) session existence checks.
Instead of calling HasSession() (which spawns a subprocess) for each
rig/session during shutdown, now calls ListSessions() once and uses
in-memory map lookups.

Changes:
- internal/tmux/tmux.go: Add SessionSet type with GetSessionSet() and Has()
- internal/cmd/down.go: Use SessionSet for dry-run checks and session stops
- internal/session/town.go: Add StopTownSessionWithCache() variant
- internal/tmux/tmux_test.go: Add test for SessionSet

With 5 rigs, this reduces subprocess calls from ~15 to 1 during shutdown
preview, saving 60-150ms of execution time.

Closes: gt-xh2bh

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* perf(tmux): optimize SessionSet to avoid intermediate slice allocation

- Build map directly from tmux output instead of calling ListSessions()
- Use strings.IndexByte for efficient newline parsing
- Pre-size map using newline count to avoid rehashing
- Simplify nil checks in Has() and Names()

* fix(sling): restore bd cook directory context for formula-on-bead mode

The bd cook command needs to run from the target rig's directory to
access the correct formula database. This was accidentally removed
in a previous commit, causing TestSlingFormulaOnBeadRoutesBDCommandsToTargetRig
to fail.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 22:07:05 -08:00
Johann Dirry
5d96243414 fix: Windows build support with platform-specific process/signal handling
Separate platform-dependent code into build-tagged files:
- process_unix.go / process_windows.go: isProcessRunning() implementation
- signals_unix.go / signals_windows.go: daemon signal handling (Windows lacks SIGUSR1)

Windows implementation uses windows.OpenProcess with PROCESS_QUERY_LIMITED_INFORMATION
and checks exit code against STILL_ACTIVE (259).

Original-PR: #447
Co-Authored-By: Johann Dirry <johann.dirry@microsea.at>
2026-01-13 20:59:15 -08:00
gastown/crew/jack
60da5de104 feat(identity): add gt commit wrapper and gt trail command
gt-f6mkz: Agent git identity
- Add `gt commit` wrapper that sets git author from agent identity
- Identity mapping: gastown/crew/jack → gastown.crew.jack@gastown.local
- Add `agent_email_domain` to TownSettings (default: gastown.local)
- Add `gt config agent-email-domain` command to manage domain

gt-j1m5v: gt trail command
- Add `gt trail` with aliases `gt recent` and `gt recap`
- Subcommands: commits, beads, hooks
- Flags: --since, --limit, --json, --all
- Filter commits by agent email domain

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 19:34:29 -08:00
gastown/refinery
0a6fa457f6 fix(shutdown): kill entire process tree to prevent orphaned Claude processes
Merge polecat/dementus-mkddymu6: Improves KillSessionWithProcesses to
recursively find and kill all descendant processes, not just direct
children. This prevents orphaned Claude processes when the process
tree is deeper than one level.

Adds getAllDescendants() helper and TestGetAllDescendants test.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 18:37:36 -08:00
dementus
1043f00d06 fix(shutdown): kill entire process tree to prevent orphaned Claude processes
The previous implementation used `pkill -P pid` which only kills direct
children. When Claude spawns subprocesses (like node workers), those
grandchild processes would become orphaned (PPID=1) when their parent
was killed, causing them to survive `gt shutdown -fa`.

The fix recursively finds all descendant processes and kills them in
deepest-first order, ensuring no process becomes orphaned during
shutdown.

Fixes: gt-wd3ce

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 18:21:25 -08:00