The hook system had two disconnected mechanisms:
1. beads pinned field (bd pin) - what mol status checked
2. wisp hook files (gt hook) - what prime/startup checked
Now gt mol status checks BOTH, so hooked work via gt hook/sling/handoff
shows up properly in mol status output.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add gt hook <bead>: durability primitive, attaches work to hook
- Update gt handoff: accept optional bead arg (detects bead vs role)
- Deprecate gt sling: shows warning, points to new commands
- Update doctor fix hint to reference new commands
🤖 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>
Code review fixes:
1. CRITICAL: Move polecat check to start of runSling
- Previously wrote wisp THEN failed, leaving orphan
- Now fails fast before any file operations
2. CRITICAL: Sanitize slashes in agent IDs for filenames
- Agent IDs like 'gastown/crew/joe' were creating subdirs
- Now converts '/' to '--' for safe filenames
- Added sanitizeAgentID/unsanitizeAgentID helpers
3. MODERATE: Use git root instead of WorkDir in prime.go
- Hooks are written to clone root, not cwd
- Added getGitRoot() helper for consistency
4. MODERATE: Fix silent error swallowing
- Now logs non-ErrNoHook errors when reading hooks
- Warns if bead doesn't exist before burning hook
- Preserves hook if bead is missing for debugging
Phase 1 of tracer bullet: Slinging Handoff
- Add internal/wisp package for ephemeral work attachment
- Add gt sling command to attach work and restart
- Update gt prime to check/burn slung work on hook
- Add .beads-wisp/ to gitignore
The resume prompt wasn't getting its Enter key reliably. Changed from
SendKeysDelayedDebounced (3s delay, 300ms debounce) to NudgeSession
(5s delay, 500ms debounce) which is the battle-tested method for
messaging Claude sessions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When running `gt crew at` from inside tmux:
- No longer auto-links the target session as a tab
- Just prints "Started X. Use C-b s to switch."
- User stays in their current pane
When running from outside tmux:
- Default: attach to the session (existing behavior)
- With -d/--detached: start session without attaching
This gives users more control over tmux session navigation and
reduces confusion for tmux newcomers.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove Go code that makes workflow decisions. All health checking,
staleness detection, nudging, and escalation belongs in the Deacon
molecule where Claude executes it.
Removed:
- internal/daemon/backoff.go (190 lines) - exponential backoff decisions
- internal/doctor/stale_check.go (284 lines) - staleness detection
- IsFresh/IsStale/IsVeryStale from keepalive.go
- pokeMayor, pokeWitnesses, pokeWitness from daemon.go
- Heartbeat staleness classification from pokeDeacon
Changed:
- Lifecycle parsing now uses structured body (JSON or simple text)
instead of keyword matching on subject line
- Daemon now only ensures Deacon is running and sends simple heartbeats
- No backoff, no staleness classification, no decision-making
Total: ~800 lines removed from Go code
The Deacon molecule will handle all health checking, nudging, and
escalation. Go is now just a message router.
See gt-gaxo epic for full rationale.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Architecture changes:
- Refinery created as worktree of mayor clone (shares .git)
- Polecat branches stay local (never pushed to origin)
- MRs stored as wisps in .beads-wisp/mq/ (ephemeral)
- Only main gets pushed to origin after merge
New mrqueue package for wisp-based MR storage.
Updated spawn, done, mq_submit, refinery, molecule templates.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Revert IsWisp: false → true for patrol spawning. bd mol run now
auto-discovers the main database for templates when --db contains
.beads-wisp, so patrol molecules can spawn correctly into ephemeral storage.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When slinging work to patrol agents (witness, refinery, deacon), queue via
mail instead of replacing the hook. This preserves patrol continuity.
New behavior:
- Default: Check if patrol is running, start default patrol if not, send
work via mail. Patrol processes queued work during its cycle.
- --urgent: Marks mail as urgent (🚨 URGENT prefix)
- --replace: Legacy behavior, explicitly terminates patrol (break-glass)
New helper functions:
- isPatrolRole(kind): Returns true for witness/refinery/deacon
- getDefaultPatrolMolecule(role): Returns patrol template name
- resolvePatrolMoleculeID(path, title): Looks up beads issue ID by title
- isPatrolRunning(path, addr): Checks if patrol molecule is attached
Note: Patrol spawning currently uses main DB (not wisp storage) because
templates must be looked up from the main database. Wisp support for
patrol instances is a future enhancement.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When slinging with --force to an agent with occupied hook, the displaced
molecule is now returned to the ready pool rather than silently orphaned.
Changes:
- Modified checkHookCollision to take force param and return displaced ID
- Added releaseDisplacedWork helper to unpin and release displaced molecules
- Updated all 5 sling handlers (polecat, crew, witness, refinery, mayor)
Behavior:
- Without --force: still errors "hook already occupied by X"
- With --force: prints warning, releases old work, proceeds with new sling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The witness session was starting in the rig root (e.g., /gt/gastown)
instead of the witness directory (e.g., /gt/gastown/witness/rig).
This caused gt prime to detect the Mayor role instead of Witness role.
Now ensureWitnessSession uses the same pattern as ensureRefinerySession:
1. Try <rig>/witness/rig/ first (for rigs with worktree setup)
2. Fall back to <rig>/witness/ (for simpler setups)
3. Last resort: use rig root (shouldn't happen normally)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
resolveRoleToSession now accepts paths like <rig>/crew/<name>,
<rig>/witness, and <rig>/refinery in addition to role shortcuts.
For example: 'gt handoff gastown/crew/max' now works.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update gt handoff to detect polecats (via GT_POLECAT env var) and
call gt done instead of respawning - polecats have Witness-managed lifecycle
- Update CLAUDE.md Session End Checklist to use gt handoff
- This is the canonical way to end any agent session - it handles mail,
respawn, and context restoration via SessionStart hook
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
buildRestartCommand now detects the town root and includes a cd to the
appropriate directory before launching claude. This ensures each role
starts in its correct working directory:
- gt-mayor → town root (~/gt)
- gt-deacon → ~/gt/deacon
- crew sessions → ~/gt/<rig>/crew/<name>
- witness sessions → ~/gt/<rig>/witness
- refinery sessions → ~/gt/<rig>/refinery
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New command that sends a message to all active workers (polecats + crew).
Supports filtering by rig with --rig flag, and can include infrastructure
agents (mayor, deacon, witness, refinery) with --all flag.
Usage:
gt broadcast "Check your mail"
gt broadcast --rig gastown "New work available"
gt broadcast --all "System maintenance in 5 minutes"
gt broadcast --dry-run "Test"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add doctor check to detect handoff beads for agents that no longer exist.
This happens when a polecat worktree is deleted but its handoff bead remains.
- Check: orphaned-attachments
- Warning if: Handoff bead exists for agent that no longer has worktree
- Supports polecats (rig/name), crew (rig/crew/name), mayor, witness, refinery
- Suggests re-sling to active agent or close the molecule
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add HookInfo struct and hook discovery to show which agents have work
attached to their hooks. Updates both JSON and text output:
- New HookInfo type with agent, role, has_work, molecule, and title fields
- RigStatus now includes Hooks slice
- StatusSum includes ActiveHooks count
- discoverRigHooks() scans polecats, crew, witness, and refinery
- getAgentHook() retrieves hook status from handoff beads
- Text output shows active hooks per rig with agent and molecule info
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Allows agents to self-pin work from mail messages. The command:
1. Reads a mail message by ID
2. Extracts molecule ID from the body (attached_molecule:, molecule_id:, etc.)
3. Attaches the molecule to the agent's pinned bead (hook)
4. Marks the mail as read
Includes unit tests for the molecule ID extraction logic.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add a command to list all hooks in the workspace. Scans for .claude/settings.json
files and displays hooks by type (SessionStart, PreCompact, UserPromptSubmit, etc).
Features:
- Scans town root, rigs, polecats, crew, witness, and refinery directories
- Shows hook type, location, status, and agent that owns it
- Supports --verbose flag to show hook commands
- Supports --json flag for machine-readable output
(gt-h6eq.5)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add doctor check to ensure each agent has at most one handoff bead.
Detects when multiple pinned beads exist with the same "{role} Handoff"
title, which can cause confusion about which handoff is authoritative.
- Check: hook-singleton
- Error if: Multiple pinned beads with same '{role} Handoff' title
- Fix: Automatically closes duplicates (keeps oldest) when run with --fix
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements a new doctor check that detects attached molecules that
haven't been updated in too long, which may indicate stuck work.
The check:
- Finds all pinned beads with attachments across rigs
- Checks the attached molecule's UpdatedAt timestamp
- Reports molecules in_progress with no activity for >1 hour
- Provides actionable fix hints for stuck polecats
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add doctor check to verify that attached molecules exist and are not closed.
Detects when a hook's attached_molecule field points to a non-existent or
closed issue, which can leave agents with stale work assignments.
- Check: hook-attachment-valid
- Error if: Hook's attached_molecule field points to non-existent or closed issue
- Fix: Automatically detaches invalid molecules when run with --fix
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds Pin() and Unpin() methods to the beads wrapper that call bd pin/unpin.
Updates pinToHook() to call b.Pin() after AttachMolecule() to set the
pinned boolean field and assignee on the work issue. This should enable
bd hook to show pinned work.
NOTE: There's a known issue where bd pin via subprocess doesn't actually
set the pinned field even though it reports success. The handoff bead
attachment remains the primary mechanism until this is resolved.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add DefaultCrewName constant ('max') to config package
- Add default_crew_name field to MayorConfig for town-level override
- Update gt rig add to resolve crew name: --crew flag > town config > default
- Update help text to reflect new default
Priority: --crew flag > MayorConfig.DefaultCrewName > config.DefaultCrewName ('max')
Adds support for slinging work to four new target types:
- crew (e.g., beads/crew/dave): Human-managed persistent workers
- No worktree recreation
- No auto-session start
- Sends work assignment mail
- witness (e.g., gastown/witness): Per-rig lifecycle manager
- Suggests --wisp for ephemeral work
- Pins to witness hook
- refinery (e.g., gastown/refinery): Per-rig merge queue
- Accepts protos, issues, and epics
- Pins to refinery hook
- mayor (e.g., mayor/): Town-level coordinator
- Uses town-level beads
- Human-managed like crew
- Sends work assignment mail
Updated help text with new target formats and examples.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The help text documented this subcommand but it wasn't implemented.
Adds status command showing currently resolved account with source
(GT_ACCOUNT env var or default).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds support for managing multiple Claude Code accounts in Gas Town:
- accounts.json config parsing in mayor/ directory
- gt account list/add/default commands
- GT_ACCOUNT env var support with priority resolution
- --account flag on gt spawn and gt crew at commands
- CLAUDE_CONFIG_DIR injection into tmux sessions
Priority order: GT_ACCOUNT env var > --account flag > default from config
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Shell loops bypassed the proper lifecycle architecture. Now:
- Witness: Launches Claude directly, daemon/deacon health-scan handles restart
- Deacon: Launches Claude directly, daemon detects exit and restarts
- Daemon: ensureDeaconRunning() now checks if Claude is running (pane cmd)
and restarts it if session exists but Claude has exited
- gt deacon restart: Now does stop+start instead of Ctrl-C
This enforces proper lifecycle flow through LIFECYCLE mail and daemon
heartbeat rather than bypassing it with shell loops.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Agents naturally expect `gt handoff -s "Subject" -m "Message"` to work
like `gt mail send`. Now it does:
- Added --subject/-s and --message/-m flags to gt handoff
- Added --self flag to gt mail send for sending to self
- Handoff auto-sends mail to self before respawning pane
This makes agent-initiated handoff more ergonomic - they can include
context in a single command instead of two separate steps.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add auto-spawn logic to outputDeaconPatrolContext()
- Deacon now auto-spawns mol-deacon-patrol if no active patrol
- All three patrol roles now have consistent auto-bond behavior:
Deacon, Witness, Refinery
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add outputWitnessPatrolContext() function in prime.go
- Witness now auto-spawns mol-witness-patrol if no active patrol
- Fixed catalog ID parsing (strip trailing colon) for both witness and refinery
- Created mol-witness-patrol template with 10 steps (gt-qflq)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The previous implementation used session.Manager which only works for polecats.
Refinery and Witness have their own managers that handle their tmux sessions.
- Use refinery.Manager for refinery auto-start
- Use witness.Manager for witness auto-start
- Check state before starting to avoid duplicate starts
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements redundant systems to ensure infrastructure stays operational:
- gt spawn: Auto-starts refinery and witness if not running
- mol-witness-patrol: Added check-refinery step to verify refinery is alive
This ensures polecats don't wait forever for merges.
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>