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>
Use regexp.QuoteMeta to treat Query and FromFilter as literal strings
instead of raw regex patterns. This prevents ReDoS attacks from malicious
patterns and provides more intuitive literal string matching for users.
Fixes gt-kwa09
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reverts the session naming changes from PR #70. Multi-town support
on a single machine is not a real use case - rigs provide project
isolation, and true isolation should use containers/VMs.
Changes:
- MayorSessionName() and DeaconSessionName() no longer take townName parameter
- ParseSessionName() handles simple gt-mayor and gt-deacon formats
- Removed Town field from AgentIdentity and AgentSession structs
- Updated all callers and tests
Generated with 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>
Town-level agents (mayor/, deacon/) need trailing slash to match
addressToIdentity() normalization.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements announce channel delivery in router.go:
- Add isAnnounceAddress() and parseAnnounceName() helpers
- Add ErrUnknownAnnounce error variable
- Add expandAnnounce() to load AnnounceConfig from messaging.json
- Add sendToAnnounce() for bulletin board delivery (single copy, no claiming)
- Add pruneAnnounce() for retention-based message cleanup
- Integrate announce routing in Send()
Announce channels store ONE copy of each message (unlike lists which fan-out).
Messages persist until retention limit is reached, with oldest messages
pruned automatically when limit is exceeded.
Also includes address helpers (gt-pn2fq dependency).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add announce address detection to internal/mail/router.go following
the same pattern as isListAddress/parseListName and isQueueAddress/
parseQueueName.
Added:
- isAnnounceAddress(address string) bool - returns true for 'announce:' prefix
- parseAnnounceName(address string) string - extracts channel name
- ErrUnknownAnnounce error variable
(gt-pn2fq)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- gt mail mark <id> --read/--unread: Change message read status
- gt mail delete <id> --force: Add confirmation prompt (skip with --force)
- gt mail archive: Batch operations with --older-than, --all-read, --dry-run
- gt mail purge: Delete archived messages with --older-than, --dry-run, --force
- gt mail search <query>: Regex search with --from, --subject, --body, --json
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Pass CLAUDE_SESSION_ID environment variable to bd close for work attribution
tracking, enabling queries like "what work did this session do?" for entity
CV building (per decision 009-session-events-architecture.md).
Changes:
- beads.Close() and CloseWithReason() now pass --session to bd close
- Updated all direct exec.Command("bd", "close"...) calls:
- internal/mail/mailbox.go - closeInDir()
- internal/cmd/swarm.go - swarm land and cancel
- internal/cmd/hook.go - auto-replace completed beads
- internal/cmd/synthesis.go - convoy close
- internal/cmd/crew_lifecycle.go - workspace removal
- internal/cmd/polecat.go - polecat nuke
The bd CLI already supports --session (or CLAUDE_SESSION_ID env var) so
this change ensures consistent session tracking across all gt close paths.
Fixes: gt-nvz8b
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements queue message delivery in internal/mail/router.go:
- Validates queue exists via expandQueue()
- Creates single message (no fan-out unlike lists)
- Stores in town-level beads with queue metadata label
- Uses queue:name as assignee for inbox filtering
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add queue expansion to internal/mail/router.go following the expandList() pattern:
- Add ErrUnknownQueue error for unknown queue names
- Add expandQueue() method to look up QueueConfig from messaging.json
- Add TestExpandQueue and TestExpandQueueNoTownRoot tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add queue address detection following the same pattern as list address
detection. Includes tests in router_test.go.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements @group address resolution for mail routing:
- @rig/<rigname>: All agents in a rig
- @town: All town-level agents (mayor, deacon)
- @witnesses, @dogs, @refineries: Role-based groups
- @crew/<rig>, @polecats/<rig>: Role+rig scoped groups
- @overseer: Human operator (uses overseer.json)
Resolution uses `bd list --type=agent` queries with
description filtering. Fan-out at send time creates
individual messages for each resolved recipient.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement static mailing list expansion for the mail system:
- Add list:name address syntax (e.g., list:oncall)
- Load lists from ~/gt/config/messaging.json
- Fan-out delivery: each list member gets their own message copy
- Clear error handling for unknown list names
- Add tests for list detection, parsing, and expansion
- Update gt mail send help text with list:name documentation
- Show recipients in output when sending to a list
Example:
gt mail send list:oncall -s "Alert" -m "System down"
# Expands to: mayor/, gastown/witness
# Creates 2 message copies
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds first-class support for the human overseer in Gas Town mail:
- New OverseerConfig in internal/config/overseer.go with identity
detection (git config, gh cli, environment)
- Overseer detected/saved on town install (mayor/overseer.json)
- Simplified detectSender(): GT_ROLE set = agent, else = overseer
- New overseer address alongside mayor/ and deacon/
- Added --cc flag to mail send for CC recipients
- Inbox now includes CC'd messages via label query
- gt status shows overseer identity and unread mail count
- New gt whoami command shows current mail identity
Generated with Claude Code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added ResolveBeadsDir() helper that follows .beads/redirect files,
enabling crew workers and polecats to properly access shared beads.
Updated callers:
- mailbox.go: NewMailboxFromAddress follows redirect
- catalog.go: LoadCatalog follows redirect at all levels
- doctor checks: beads_check, patrol_check, wisp_check follow redirect
Also added comprehensive tests for the redirect resolution logic.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add CreatedBy field to Issue struct (matches beads GH#748)
- Add Actor field to CreateOptions, pass --actor to bd create
- Add ActorString() method to RoleInfo for identity formatting
- Update all beads.Create() callers to pass Actor
- Update direct bd create exec calls with --actor:
- mail/router.go: uses sender identity
- patrol_helpers.go: uses role name
- doctor/patrol_check.go: uses "gt-doctor"
- rig/manager.go: uses "gt-rig-init"
🤖 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>
Removed dual-routing architecture that used separate .beads-wisp/ directory.
Now uses single .beads/ with --wisp flag passed to bd create.
Changes:
- router.go: Remove resolveWispDir(), simplify shouldBeWisp()
- mailbox.go: Remove wispDir field and dual-source query logic
- types.go: Rename Ephemeral to Wisp, remove MessageSource
- mail.go: Rename --ephemeral to --wisp flag
- spawn.go: Use Wisp field for lifecycle messages
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add dual-inbox architecture where ephemeral messages go to
.beads-wisp/.beads/ instead of .beads/. Lifecycle messages
(POLECAT_STARTED, NUDGE, etc.) auto-detect as ephemeral.
Changes:
- Add Ephemeral and Source fields to mail.Message
- Add --ephemeral flag to gt mail send
- Router auto-detects lifecycle message patterns
- Mailbox merges messages from both persistent and wisp storage
- Inbox displays (ephemeral) indicator for wisp messages
- Delete/archive works for both message types
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Normalize crew/ and polecats/ to canonical form in addressToIdentity():
- gastown/crew/dave -> gastown/dave
- gastown/polecats/Toast -> gastown/Toast
Follows Postels Law: be liberal in what you accept.
Changes mail identity format from hyphenated (gastown-Toast) to
slash-based (gastown/polecats/Toast) to:
- Match directory structure (REST-like)
- Distinguish from hyphenated bead IDs (gt-xyz) and molecule names
Updated:
- addressToIdentity(): preserve slashes instead of replacing with dashes
- identityToAddress(): simplified, identity == address now
- detectSender(): include /polecats/ in polecat addresses
- Tests updated for new format
Closes gt-cxtu: shared beads architecture verified working.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Strip crew/ and polecats/ from addresses before identity conversion.
This ensures beads/crew/dave and beads/dave both resolve to beads-dave,
fixing the inbox mismatch bug (hq-0ol).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The mail router was incorrectly routing rig-level addresses
(e.g., gastown/crew/max) to {rig}/.beads instead of town beads.
This caused mail to be invisible when agents checked their inbox.
Two-level beads architecture:
- ALL mail uses town beads ({townRoot}/.beads)
- Rig-level beads are for project issues only
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The merge at 96c773f lost changes from 5791752 (beads-sync branch).
Re-implementing:
- router.go: Use bd create --type=message with labels
- mailbox.go: Use bd list/show/close instead of bd mail commands
- types.go: Add Pinned field to Message struct
Original work was in commits 5791752 and 4c060f4 on beads-sync.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Pinned field to BeadsMessage and sort pinned messages before
unpinned ones in listBeads(). Within each group, sort by newest first.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace `bd mail send/inbox/read/ack` commands with `bd create/list/show/close`.
This separates the orchestration layer (gt) from the data plane (beads).
Changes:
- router.go: Use `bd create --type=message` instead of `bd mail send`
- mailbox.go: Use `bd list --type=message` and `bd show` for inbox/read
- types.go: Parse metadata from labels (from:, thread:, reply-to:)
- mail.go: Fix findBeadsWorkDir to prefer rig-level beads, fix crew address format
Messages are now stored as beads issues with type=message. Metadata (sender,
thread, reply-to) is stored in labels for retrieval.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changed --priority flag from string values (low/normal/high/urgent) to
numeric values (0-4) to match bd mail send interface:
- 0 = urgent/critical
- 1 = high
- 2 = normal (default)
- 3 = low
- 4 = backlog
Also added --urgent flag as shortcut for --priority 0.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add deacon/ as a town-level mail identity alongside mayor/.
Both addressToIdentity() and identityToAddress() now handle
deacon/deacon/ consistently with trailing slash preservation.
Tested with bd mail send deacon/ and bd mail inbox --identity deacon/
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add delivery modes to mail messages:
- queue (default): message stored for periodic checking
- interrupt: inject system-reminder directly into session
New features:
- --interrupt flag on gt mail send for urgent/lifecycle messages
- --quiet flag on gt mail check for silent non-blocking checks
- gt mail wait command to block until mail arrives (with optional timeout)
Interrupt delivery injects a system-reminder via tmux send-keys,
useful for stuck detection, nudges, and urgent notifications.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix ~50 errcheck warnings across the codebase:
- Add explicit `_ =` for intentionally ignored error returns (cleanup,
best-effort operations, etc.)
- Use `defer func() { _ = ... }()` pattern for defer statements
- Handle tmux SetEnvironment, KillSession, SendKeysRaw returns
- Handle mail router.Send returns
- Handle os.RemoveAll, os.Rename in cleanup paths
- Handle rand.Read returns for ID generation
- Handle fmt.Fprint* returns when writing to io.Writer
- Fix for-select with single case to use for-range
- Handle cobra MarkFlagRequired returns
All tests pass. Code compiles without errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace tmux display-message (subtle status bar notification) with
send-keys to echo a visible banner to the terminal. This ensures
mail notifications are actually seen by agents.
Closes gt-vnp9
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The addressToIdentity function was stripping the trailing slash from
'mayor/' addresses, causing a mismatch with beads messages that have
assignee 'mayor/'. This fix ensures mayor addresses always use 'mayor/'
for consistent mail routing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds:
- gt mail send now triggers tmux notification for recipients
- Merge execution with config and retry logic
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add MessageType enum (task, scavenge, notification, reply)
- Expand Priority from 2 to 4 levels (low, normal, high, urgent)
- Add ThreadID and ReplyTo fields to Message struct
- Add --type and --reply-to flags to 'gt mail send'
- Add 'gt mail thread <id>' command to view conversation threads
- Update inbox/read display to show type and threading info
- Auto-generate thread IDs for new messages
- Reply messages inherit thread from original
Closes: gt-hgk
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add DisplayMessage/DisplayMessageDefault to tmux package for non-disruptive
status line notifications
- Change mail send to always notify recipients (not just high priority)
- Use display-message instead of send-keys to avoid disrupting agent input
- Support notifications for mayor, polecat, and refinery sessions
Closes gt-7lt
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The bd CLI renamed 'message' subcommand to 'mail'. Updated gt mail
to call the correct bd subcommand, and fixed body to use -m flag
instead of positional argument.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The beads command for messaging is 'bd message', not 'bd mail'.
Fixed 4 locations:
- mailbox.go: inbox, read, ack commands
- router.go: send command (also fixed arg order to match bd API)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Mail commands (send/inbox/read/delete) now wrap bd mail CLI
- Address translation: mayor/ → mayor, rig/polecat → rig-polecat
- Beads stores messages as type=message issues
- Legacy JSONL mode retained for crew workers (local mail)
- Refinery notifications use new mail interface
- Swarm landing notifications use new mail interface
Closes gt-u1j.6, gt-u1j.12
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>