Commit Graph

1610 Commits

Author SHA1 Message Date
Steve Yegge
7c2f9687ec feat(wisp): add misclassified wisp detection and defense-in-depth filtering (#833)
Add CheckMisclassifiedWisps doctor check to detect issues that should be
marked as wisps but aren't. This catches merge-requests, patrol molecules,
and operational work that lacks the wisp:true flag.

Add defense-in-depth wisp filtering to gt ready command. While bd ready
should already filter wisps, this provides an additional layer to ensure
ephemeral operational work doesn't leak into the ready work display.

Changes:
- New doctor check: misclassified-wisps (fixable, CategoryCleanup)
- gt ready now filters wisps from issues.jsonl in addition to scaffolds
- Detects wisp patterns: merge-request type, patrol labels, mol-* IDs

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 20:20:49 -08:00
Daniel Sauer
e591f2ae25 fix(formulas): replace hardcoded ~/gt/ paths with $GT_ROOT (#758)
* fix(formulas): replace hardcoded ~/gt/ paths with $GT_ROOT

Formula files contained hardcoded ~/gt/ paths that break when running
Gas Town from a non-default location (e.g., ~/gt-private/). This causes:

- Dogs stuck in working state (can't write to wrong path)
- Cross-town contamination when ~/gt/ exists as separate town
- Boot triage, deacon patrol, and log archival failures

Replaces all ~/gt/ and $HOME/gt/ references with $GT_ROOT which is
set at runtime to the actual town root directory.

Fixes #757

* chore: regenerate embedded formulas

Run go generate to sync embedded formulas with .beads/formulas/ source.
2026-01-20 20:19:42 -08:00
gastown/crew/mel
0a6b0b892f fix(witness,rig): code review cleanup
- Remove unused workDir field from witness manager
- Use witMgr.IsRunning() consistently instead of direct tmux call

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 20:19:05 -08:00
benzene
8357a94cae chore: sync embedded formula after go generate 2026-01-20 20:03:07 -08:00
benzene
195ecf7578 fix(sling): allow auto-attach mol-polecat-work on open polecat beads 2026-01-20 20:02:44 -08:00
gastown/crew/mel
5218102f49 refactor(witness,refinery): ZFC-compliant state management
Remove state files from witness and refinery managers, following
the "Discover, Don't Track" principle. Tmux session existence is
now the source of truth for running state (like deacon).

Changes:
- Add IsRunning() that checks tmux HasSession
- Change Status() to return *tmux.SessionInfo
- Remove loadState/saveState/stateManager
- Simplify Start()/Stop() to not use state files
- Update CLI commands (witness/refinery/rig) for new API
- Update tests to be ZFC-compliant

This fixes state file divergence issues where witness/refinery
could show "running" when the actual tmux session was dead.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 20:00:56 -08:00
Julian Knutsen
126ec84bb3 fix(sling): check hooked status and send LIFECYCLE:Shutdown on --force (#828)
* fix(sling): check hooked status and send LIFECYCLE:Shutdown on --force

- Change sling validation to check both pinned and hooked status (was only
  checking pinned, likely a bug)
- Add --force handling that sends LIFECYCLE:Shutdown message to witness when
  forcibly reassigning work from an already-hooked bead
- Use existing LIFECYCLE:Shutdown protocol instead of new KILL_POLECAT -
  witness will auto-nuke if clean, or create cleanup wisp if dirty
- Use agent.Self() to identify the requester (falls back to "unknown"
  for CLI users without GT_ROLE env vars)

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

* fix: use env vars instead of undefined agent.Self()

The agent.Self() function does not exist in the agent package.
Replace with direct env var lookups for GT_POLECAT (when running
as a polecat) or USER as fallback.

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>
Co-authored-by: beads/crew/lizzy <steve.yegge@gmail.com>
2026-01-20 19:57:28 -08:00
gastown/crew/george
9a91a1b94f fix(done): restrict gt done to polecats only
Add BD_ACTOR check at start of runDone() to prevent non-polecat roles
(crew, deacon, witness, etc.) from calling gt done. Only polecats are
ephemeral workers that self-destruct after completing work.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 19:51:37 -08:00
Julian Knutsen
f82477d6a6 fix(tmux): prevent gt done from killing itself during session cleanup (#821)
When gt done runs inside a tmux session (e.g., after polecat task
completion), calling KillSessionWithProcesses would kill the gt done
process itself before it could complete cleanup operations like writing
handoff state.

Add KillSessionWithProcessesExcluding() function that accepts a list of
PIDs to exclude from the kill sequence. Update selfKillSession to pass
its own PID, ensuring gt done completes before the session is destroyed.

Also fix both Kill*WithProcesses functions to ignore "session not found"
errors from KillSession - when we kill all processes in a session, tmux
may automatically destroy it before we explicitly call KillSession.

Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 19:34:20 -08:00
aleiby
4dd11d4ffa fix(mq): use label instead of issue_type for merge-request filtering (#831)
The mq list --ready command was filtering by issue.Type == "merge-request",
but beads created by `gt done` have issue_type='task' (the default) with
a gt:merge-request label. This caused ready MRs to be filtered out.

Changed to use beads.HasLabel() which checks the label, completing the
migration from the deprecated issue_type field to labels.

Added TestMRFilteringByLabel to verify the fix handles the bug scenario.

Fixes #816

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 19:31:26 -08:00
Julian Knutsen
7564cd5997 fix(patrol): use gt formula list instead of bd mol catalog (#827)
The bd mol catalog command was renamed to bd formula list, and gt formula
list is preferred since it works from any directory without needing the
--no-daemon flag.

Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 19:31:17 -08:00
mayor
5a14053a6b docs(templates): add explicit bead filing guidance to role templates
Agents were misfiling beads in HQ when they should go to project-specific
rigs (beads, gastown). The templates explained routing mechanics but not
decision making. Added "Where to File Beads" sections with:

- Routing table based on what code the issue affects
- Simple heuristic: "Which repo would the fix be committed to?"
- Examples for bd CLI → beads, gt CLI → gastown, coordination → HQ

Affects: mayor, deacon, crew, polecat templates.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 17:48:51 -08:00
gastown/crew/dennis
0db2bda6e6 feat(deacon): add zombie-scan command for tmux-verified process cleanup
Unlike cleanup-orphans (which uses TTY="?" detection), zombie-scan uses
tmux verification: it checks if each Claude process is in an active
tmux session by comparing against actual pane PIDs.

A process is a zombie if:
- It's a Claude/codex process
- It's NOT the pane PID of any active tmux session
- It's NOT a child of any pane PID
- It's older than 60 seconds

Also refactors:
- getChildPIDs() with ps fallback when pgrep unavailable
- State file handling with file locking for concurrent access

Usage:
  gt deacon zombie-scan           # Find and kill zombies
  gt deacon zombie-scan --dry-run # Just list zombies

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 14:19:29 -08:00
joshuavial
48ace2cbf3 fix(handoff): preserve GT_AGENT across session restarts (#788)
Adds GT_AGENT env var to track agent override when using --agent flag.
Handoff reads and preserves GT_AGENT so non-default agents persist across restarts.

Co-authored-by: joshuavial <git@codewithjv.com>
2026-01-20 14:17:52 -08:00
Johann Dirry
3d5a66f850 Fixing unit tests on windows (#813)
* Add Windows stub for orphan cleanup

* Fix account switch tests on Windows

* Make query session events test portable

* Disable beads daemon in query session events test

* Add Windows bd stubs for sling tests

* Make expandOutputPath test OS-agnostic

* Make role_agents test Windows-friendly

* Make config path tests OS-agnostic

* Make HealthCheckStateFile test OS-agnostic

* Skip orphan process check on Windows

* Normalize sparse checkout detail paths

* Make dog path tests OS-agnostic

* Fix bare repo refspec config on Windows

* Add Windows process detection for locks

* Add Windows CI workflow

* Make mail path tests OS-agnostic

* Skip plugin file mode test on Windows

* Skip tmux-dependent polecat tests on Windows

* Normalize polecat paths and AGENTS.md content

* Make beads init failure test Windows-friendly

* Skip rig agent bead init test on Windows

* Make XDG path tests OS-agnostic

* Make exec tests portable on Windows

* Adjust atomic write tests for Windows

* Make wisp tests Windows-friendly

* Make workspace find tests OS-agnostic

* Fix Windows rig add integration test

* Make sling var logging Windows-friendly

* Fix sling attached molecule update ordering

---------

Co-authored-by: Johann Dirry <johann.dirry@microsea.at>
2026-01-20 14:17:35 -08:00
joshuavial
183a0d7d8d fix(crew): use directory name as source of truth in loadState (#785)
Fixes gt crew list showing wrong names when state.json contains stale data.
Always use directory name as source of truth in loadState() instead of
trusting potentially stale state.json.

Co-authored-by: joshuavial <git@codewithjv.com>
2026-01-20 14:16:24 -08:00
Steve Whittaker
477c28c9d1 Create initial commit before gh repo create --push
gh repo create --push fails on empty repos. Add ensureInitialCommit()
to stage and commit before pushing.
2026-01-20 14:15:31 -08:00
Daniel Sauer
f58a516b7b fix(test): remove stale TestInstallTownRoleSlots test (#819)
Role slots were removed in a6102830 (feat(roles): switch daemon to
config-based roles, remove role beads), but the test was not updated.

The test was checking for role slots on hq-mayor and hq-deacon agent
beads, which are no longer created since role definitions are now
config-based (internal/config/roles/*.toml).
2026-01-20 14:14:32 -08:00
dustin
fd61259336 feat: add initial prompt for autonomous patrol startup (deacon & witness) (#769)
Add initial prompt to deacon and witness startup commands to trigger
autonomous patrol. Without this, agents sit idle at the prompt after
SessionStart hooks run.

Implements GUPP (Gas Town Universal Propulsion Principle): agents start
patrol immediately when Claude launches.
2026-01-20 14:10:30 -08:00
Daniel Sauer
6a22b47ef6 fix(await-signal): update agent last_activity on signal received (#774)
When await-signal detects activity, call `bd agent heartbeat` to update
the agent's last_activity timestamp. This enables witnesses to accurately
detect agent liveness and prevents false "agent unresponsive" alerts.

Previously, await-signal only managed the idle:N label but never updated
last_activity, causing it to remain NULL for all agents.

Fixes #773
2026-01-20 14:10:26 -08:00
Johann Dirry
5c45b4438a Add Windows stub for orphan cleanup (#808)
Co-authored-by: Johann Dirry <johann.dirry@microsea.at>
2026-01-20 14:10:21 -08:00
aleiby
08cee416a4 fix(handoff): normalize identity in sendHandoffMail (#780)
The handoff mail bead was created with un-normalized assignee
(e.g., gastown/crew/holden) but mailbox queries use normalized identity
(gastown/holden), causing self-mail to be invisible to the inbox.

Export addressToIdentity as AddressToIdentity and call it in
sendHandoffMail() to normalize the agent identity before storing,
matching the normalization performed in Router.sendToSingle().

Fix handoff mail delivery (hq-snp8)
2026-01-20 14:09:54 -08:00
Julian Knutsen
2fe23b7be5 fix(done): terminate polecat session for all exit types (#800)
Previously, gt done only killed the polecat session when exitType was
COMPLETED. For DEFERRED, ESCALATED, and PHASE_COMPLETE, it would call
os.Exit(0) which only exited the gt process, leaving Claude running.

Now all exit types terminate the polecat session ("done means gone").
Only COMPLETED also nukes the worktree - other statuses preserve the
work in case it needs to be resumed.

Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 14:09:51 -08:00
Adam Zionts
6c5c671595 feat(doctor): add routing-mode check to detect .beads-planning routing bug (#810)
Adds a new doctor check that detects when beads routing.mode is set to
"auto" (or unset, which defaults to auto). In auto mode, beads uses
git remote URL to detect user role, and non-SSH URLs are interpreted
as "contributor" mode, which routes all writes to ~/.beads-planning
instead of the local .beads directory.

This causes mail and issues to silently go to the wrong location,
breaking inter-agent communication.

The check:
- Warns when routing.mode is not set or not "explicit"
- Is auto-fixable via `gt doctor --fix`
- References beads issue #1165 for context

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 14:09:39 -08:00
Shaun
371074cc67 Fix tmux error handling for "no current target" (#755)
When starting crew without mayor running, tmux has-session can return
"no current target" if no tmux server exists. This error was not
mapped to ErrNoServer, causing crew start to fail instead of
bootstrapping a new tmux server.

Add "no current target" to the ErrNoServer detection so crew (and
other agents) can start independently without requiring an existing
tmux session.

Reproduction:
- Ensure no tmux server running (tmux kill-server)
- Run: gt crew start <rig>/<name>
- Before fix: "Error: checking session: tmux has-session: no current target"
- After fix: Crew session starts successfully

Co-authored-by: Shaun <TechnicallyShaun@users.noreply.github.com>
2026-01-20 13:12:21 -08:00
Steve Whittaker
6966eb4c28 Escape backticks and dollar signs in quoteForShell (#777)
* Escape backticks and dollar signs in quoteForShell

* Sync embedded formulas with .beads/formulas
2026-01-20 13:11:00 -08:00
Daniel Sauer
55a3b9858a fix(config): correct Claude prompt prefix from > to ❯ (#765)
The WaitForRuntimeReady function was looking for "> " to detect
when Claude is ready, but Claude Code uses "❯" (U+276F) as its
prompt character. This caused refineries to timeout during startup.

Fixes #763

Executed-By: mayor
Role: mayor
2026-01-20 13:10:05 -08:00
gastown/crew/max
08bc632a03 fix(session): add instructions for attach topic in startup nudge
When a human attaches to mayor via gt mayor and the runtime has
exited, it restarts with Topic: attach. But FormatStartupNudge
did not include instructions for this topic, causing Claude to act
generically instead of checking hook/mail.

Add attach to the list of topics that get explicit instructions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 12:58:01 -08:00
gastown/crew/max
a610283078 feat(roles): switch daemon to config-based roles, remove role beads (Phase 2+3)
Phase 2: Daemon now uses config.LoadRoleDefinition() instead of role beads
- lifecycle.go: getRoleConfigForIdentity() reads from TOML configs
- Layered override resolution: builtin → town → rig

Phase 3: Remove role bead creation and references
- Remove RoleBead field from AgentFields struct
- gt install no longer creates role beads
- Remove 'role' from custom types list
- Delete migrate_agents.go (no longer needed)
- Deprecate beads_role.go (kept for reading existing beads)
- Rewrite role_beads_check.go to validate TOML configs

Existing role beads are orphaned but harmless.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 12:58:01 -08:00
gastown/crew/max
544cacf36d feat(roles): add config-based role definition system (Phase 1)
Replace role beads with embedded TOML config files for role definitions.
This is Phase 1 of gt-y1uvb - adds the config infrastructure without
yet switching the daemon to use it.

New files:
- internal/config/roles.go: RoleDefinition types, LoadRoleDefinition()
  with layered override resolution (builtin → town → rig)
- internal/config/roles/*.toml: 7 embedded role definitions
- internal/config/roles_test.go: unit tests

New command:
- gt role def <role>: displays effective role configuration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 12:57:46 -08:00
gastown/crew/tom
b8eb936219 fix(sling): prevent agent self-interruption during tests
The formula sling path was calling NudgePane directly without checking
GT_TEST_NO_NUDGE. When tests ran runSling() with a formula, the nudge
was sent to the agent's tmux pane, causing test interruptions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 17:40:03 -08:00
beads/crew/emma
dcf7b81011 refactor(hooks): rename to gt tap guard pr-workflow
Reorganizes Claude Code hook handlers under `gt tap` namespace:
- gt tap - parent command for all hook handlers
- gt tap guard - subcommand for blocking operations
- gt tap guard pr-workflow - blocks PR creation and feature branches

This structure allows future expansion:
- gt tap audit <x>   - logging/metrics (PostToolUse)
- gt tap inject <x>  - input modification (PreToolUse)
- gt tap check <x>   - validation (PostToolUse)

Replaces the flat gt block-pr-workflow command.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:21:54 -08:00
beads/crew/emma
37f465bde5 feat(hooks): add gt block-pr-workflow command for PreToolUse hook
Implements infrastructure-level enforcement of the "no PRs" policy.
When a Claude Code agent tries to run `gh pr create`, `git checkout -b`,
or `git switch -c`, the PreToolUse hook calls this command which:

- Detects if we're in a Gas Town agent context (crew, polecat, etc.)
- If so, exits with code 2 to BLOCK the tool execution
- Outputs helpful guidance on what to do instead (push to main)

This makes the rule ironclad - agents can't create PRs even if they
try, because the hook intercepts and blocks before execution.

Hook configuration (add to .claude/settings.json):
  "PreToolUse": [{
    "matcher": "Bash(gh pr create*)",
    "hooks": [{"command": "gt block-pr-workflow --reason pr-create"}]
  }]

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:09:15 -08:00
mayor
9cd2696abe chore: Bump version to 0.4.0
Some checks failed
Release / goreleaser (push) Failing after 5m20s
Release / publish-npm (push) Has been skipped
Release / update-homebrew (push) Has been skipped
Key fix: Orphan cleanup now skips Claude processes in valid Gas Town
tmux sessions (gt-*/hq-*), preventing false kills of witnesses,
refineries, and deacon during startup.

Updated all component versions:
- gt CLI: 0.3.1 → 0.4.0
- npm package: 0.3.0 → 0.4.0

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 12:46:49 -08:00
mayor
2b3f287f02 fix(orphan): prevent killing Claude processes in valid tmux sessions
The orphan cleanup was killing witness/refinery/deacon Claude processes
during startup because they temporarily show TTY "?" before fully
attaching to the tmux session.

Added getGasTownSessionPIDs() to discover all PIDs belonging to valid
gt-* and hq-* tmux sessions (including child processes). The orphan
cleanup now skips these PIDs, only killing truly orphaned processes
from dead sessions.

This fixes the race condition where:
1. Daemon starts a witness/refinery session
2. Claude starts but takes time to show a prompt
3. Startup detection times out
4. Orphan cleanup sees Claude with TTY "?" and kills it

Now processes in valid sessions are protected regardless of TTY state.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 12:46:49 -08:00
tom
021b087a12 fix(mail): improve channel subscribe/unsubscribe feedback
- Report "already subscribed" instead of false success on re-subscribe
- Report "not subscribed" instead of false success on redundant unsubscribe
- Add explicit channel existence check before subscribe/unsubscribe
- Return empty JSON array [] instead of null for no subscribers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 09:49:53 -08:00
george
3cb3a0bbf7 fix(dog): exclude non-dog entries from kennel listing
The boot watchdog lives in deacon/dogs/boot/ but uses .boot-status.json,
not .dog.json. The dog manager was returning a fake idle dog when
.dog.json was missing, causing gt dog list to show 'boot' and
gt dog dispatch to fail with a confusing error.

Now Get() returns ErrDogNotFound when .dog.json doesn't exist, which
makes List() properly skip directories that aren't valid dog workers.

Also skipped two more tests affected by the bd CLI 0.47.2 commit bug.

Fixes: bd-gfcmf

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 09:46:25 -08:00
george
7714295a43 fix(beads): skip tests affected by bd CLI 0.47.2 commit bug
Tests calling bd create were picking up BD_ACTOR from the environment,
routing to production databases instead of isolated test databases.
After extensive investigation, discovered the root cause is bd CLI
0.47.2 having a bug where database writes don't commit (sql: database
is closed during auto-flush).

Added test isolation infrastructure (NewIsolated, getActor, Init,
filterBeadsEnv) for future use, but skip affected tests until the
upstream bd CLI bug is fixed.

Fixes: gt-lnn1xn

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 09:42:19 -08:00
joe
616ff01e2c fix(channel): enforce RetentionHours in channel message retention
The RetentionHours field in ChannelFields was never enforced - only
RetentionCount was checked. Now both EnforceChannelRetention and
PruneAllChannels delete messages older than the configured hours.

Also fixes sling tests that were missing TMUX_PANE and GT_TEST_NO_NUDGE
guards, causing them to inject prompts into active tmux sessions during
test runs.

Fixes: gt-uvnfug

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 04:49:57 -08:00
gastown/crew/jack
3f724336f4 feat(patrol): add backoff test formula and fix await-signal
Add mol-backoff-test formula for integration testing exponential backoff
with short intervals (2s base, 10s max) to observe multiple cycles quickly.

Fix await-signal to use --since 1s when subscribing to activity feed.
Without this, historical events would immediately wake the signal,
preventing proper timeout and backoff behavior.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 04:45:35 -08:00
mayor
238ad8cd95 chore: release v0.3.1
### Fixed
- Orphan cleanup on macOS - TTY comparison now handles macOS '??' format
- Session kill orphan prevention - gt done and gt crew stop use KillSessionWithProcesses

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 03:54:06 -08:00
gus
50bcf96afb fix(beads): fix test failures with proper routing config
Tests in internal/beads were failing with "database not initialized:
issue_prefix config is missing" because bd's default routing was sending
test issues to ~/.beads-planning instead of the test's temporary database.

Fix:
- Add initTestBeads() helper that properly initializes a test beads database
  with routing.contributor set to "." to keep issues local
- Update all affected tests to use the helper
- Update TestAgentBeadTombstoneBug to skip gracefully if the bd tombstone
  bug appears to be fixed

Fixes: gt-sqme94

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 03:51:27 -08:00
mayor
2feefd1731 fix(orphan): prevent Claude Code session leaks on macOS
Three bugs were causing orphaned Claude processes to accumulate:

1. TTY comparison in orphan.go checked for "?" but macOS shows "??"
   - Orphan cleanup never found anything on macOS
   - Changed to check for both "?" and "??"

2. selfKillSession in done.go used basic tmux kill-session
   - Claude Code can survive SIGHUP
   - Now uses KillSessionWithProcesses for proper cleanup

3. Crew stop commands used basic KillSession
   - Same issue as #2
   - Updated runCrewRemove, runCrewStop, runCrewStopAll

Root cause of 383 accumulated sessions: every gt done and crew stop
left orphans, and the cleanup never worked on macOS.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 03:49:18 -08:00
max
4a856f6e0d test(patrol): add unit tests for patrol.go
Add tests for:
- extractPatrolRole() - various title format cases
- PatrolDigest struct - date format and field access
- PatrolCycleEntry struct - field access

Covers pure functions; bd-dependent functions would need mocking.

Fixes: gt-bm9nx5

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 03:47:18 -08:00
mel
e853ac3539 feat(channels): add subscriber fan-out delivery
When messages are sent to a channel, subscribers now receive a copy
in their inbox with [channel:name] prefix in the subject.

Closes: gt-3rldf6

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 03:44:01 -08:00
tom
f14dadc956 feat(mail): add channel subscribe/unsubscribe/subscribers CLI commands
Adds three new subcommands to `gt mail channel`:
- subscribe <name>: Subscribe current identity to a channel
- unsubscribe <name>: Unsubscribe current identity from a channel
- subscribers <name>: List all subscribers to a channel

These commands expose the existing beads.SubscribeToChannel and
beads.UnsubscribeFromChannel functions through the CLI.

Closes gt-77334r

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 03:42:02 -08:00
max
f19a0ab5d6 fix(patrol): add idempotency check for digest command
Checks if a 'Patrol Report YYYY-MM-DD' bead already exists before
attempting to create a new one. This prevents confusing output when
the patrol digest runs multiple times per day.

Fixes: gt-budqv9

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 03:41:18 -08:00
jack
38d3c0c4f1 fix(mail): resolve beads-native queues/channels by name
resolveByName() only checked config-based queues/channels, missing
beads-native ones (gt:queue, gt:channel). Added lookup for both.

Also added LookupQueueByName to beads package for parity with
LookupChannelByName.

Fixes: gt-l5qbi3

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 03:40:35 -08:00
max
d4ad4c0726 fix(broadcast): exclude sender from recipients
Prevents gt broadcast from nudging the sender's own session,
which would interrupt the command mid-execution with exit 137.

Fixes: gt-y5ss

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 03:19:08 -08:00
george
88a74c50f7 fix(polecat): prune stale worktree entries on early return in RemoveWithOptions
When repoBase() fails in RemoveWithOptions, the function previously
returned early after removing the directory but without calling
WorktreePrune(). This could leave stale worktree entries in
.git/worktrees/ if the polecat was created before the repo base
became unavailable.

Now we attempt to prune from both possible repo locations (bare repo
and mayor/rig) before the early return. This is a best-effort cleanup
that handles edge cases where the repo base is corrupted but worktree
entries still exist.

Resolves: gt-wisp-618ar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 02:57:31 -08:00