Deacon is a town-level role, so its beads should be at ctx.TownRoot
(~/gt/.beads/) not ctx.WorkDir (~/gt/deacon/). This fixes the issue
where outputDeaconPatrolContext couldn't find patrol molecules because
it was looking in the wrong location.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
verifyBeadExists was setting BEADS_DIR to town root, which overrides
bd's native prefix-based routing via routes.jsonl. This broke resolution
of rig-level beads (e.g., gt-* beads routed via gt- -> gastown/mayor/rig).
Fix:
- Remove BEADS_DIR override in verifyBeadExists
- Set cmd.Dir to town root so bd can find routes.jsonl
- Apply same fix to getBeadInfo for consistency
Now gt sling gt-xxx correctly finds beads using the same routing as
bd show gt-xxx.
(gt-l5qwb)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add version check that enforces beads >= 0.44.0 at CLI startup,
required for custom type support (bd-i54l). Commands like version,
help, and completion bypass the check.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Status line showing operational state (OPERATIONAL/PARKED/DOCKED)
with source indication (local/global - synced/default).
The state is looked up using the property layer system:
1. Wisp layer (local/ephemeral): .beads-wisp/config/<rig>.json
2. Rig bead labels (global/synced): status:parked or status:docked
3. Default: OPERATIONAL
Example output:
gastown
Status: PARKED (local)
Path: /Users/stevey/gt/gastown
...
Closes: gt-5l7h4
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement gt rig dock <rig> and gt rig undock <rig> commands for
global/persistent rig control:
- dock: stops witness/refinery, sets status:docked label on rig bead
- undock: removes docked label, allows daemon to restart agents
This is Level 2 (global/persistent) control:
- Uses rig identity bead labels (synced via git)
- Affects all clones of the rig
- Persists until explicitly undocked
Also includes cherry-picked rig identity bead infrastructure:
- RigFields struct for rig metadata
- CreateRigBead and RigBeadID helpers
- Auto-create rig bead for legacy rigs on first dock
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Additional cleanup from the agent_state refactoring:
- Remove dead code: checkStaleAgents(), markAgentDead() in lifecycle.go
- Remove dead code: reportAgentState(), getAgentFields() in prime.go
- Update getAgentBeadState() comment to clarify non-observable states only
- Update mol-witness-patrol.formula.toml to use tmux discovery
- Update mol-polecat-lease.formula.toml to use POLECAT_DONE mail
- Update docs/watchdog-chain.md to reflect new architecture
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement gt rig park <rig> and gt rig unpark <rig> commands:
- park: stops witness/refinery, sets status=parked in wisp layer
- unpark: clears parked status, allows daemon to restart agents
This is Level 1 (local/ephemeral) control - affects only this town
and disappears on wisp cleanup. Exports IsRigParked() for daemon use.
(gt-vxv0u)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements config viewing and manipulation commands for rig configuration
across property layers.
Commands:
- gt rig config show <rig> # Show effective config
- gt rig config show <rig> --layers # Show source of each value
- gt rig config set <rig> <key> <value> # Set in wisp layer
- gt rig config set <rig> <key> <value> --global # Set in bead layer
- gt rig config set <rig> <key> --block # Block inheritance
- gt rig config unset <rig> <key> # Remove from wisp
Includes cherry-picked dependencies:
- Property layer lookup (cb927a73, gt-emh1c)
- Rig identity bead schema for bead layer
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The agent_state field was recording observable state like "running",
"dead", "idle" which violated the "Discover, Don't Track" principle.
This caused stale state bugs where agents were marked "dead" in beads
but actually running in tmux.
Changes:
- Remove daemon's checkStaleAgents() which marked agents "dead"
- Simplify ensureXxxRunning() to use tmux.IsClaudeRunning() directly
- Remove reportAgentState() calls from gt prime and gt handoff
- Add SetHookBead/ClearHookBead helpers that don't update agent_state
- Use ClearHookBead in gt done and gt unsling
- Simplify gt status to derive state from tmux, not bead
Non-observable states (stuck, awaiting-gate, muted, paused) are still
set because they represent intentional agent decisions that can't be
discovered from tmux state.
Fixes: gt-zecmc
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add --verbose/-v flag to show detailed multi-line output (old behavior)
- Compact mode shows: name + status indicator (●/○) + hook + mail count
- MQ info displayed inline with refinery
- Fix Makefile install target to use ~/.local/bin
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add watch mode to gt status
- Add --watch/-w flag for continuous status refresh
- Add --interval/-n flag to set refresh interval (default 2s)
- Clears screen and shows timestamp on each refresh
- Graceful Ctrl+C handling to stop watch mode
- Works with existing --fast and --json flags
* fix(status): validate watch interval to prevent panic on zero/negative values
* fix(status): harden watch mode with signal cleanup, TTY detection, and tests
- Add defer signal.Stop() to prevent signal handler leak
- Reject --json + --watch combination (produces invalid output)
- Add TTY detection for ANSI escapes (safe when piped)
- Use style.Dim for header when in TTY mode
- Fix duplicate '(default 2)' in flag help
- Add tests for interval validation and flag conflicts
Resolved conflict in internal/witness/manager.go:
- Kept session import (used by PR code)
- Kept PR's more accurate comment for PID check
- Removed duplicate sessionName method introduced by merge
The previous commit (a3bccc8) violated ZFC by implementing molecule step logic
in Go handlers. Per PRIMING.md: Agent decides. Go transports.
This commit:
1. Reverts the gt witness process command (Go code should not make decisions)
2. Updates mol-witness-patrol formula with explicit CLI commands
3. Fixes --wisp to --ephemeral (bd create flag correction)
4. Removes --wisp from bd list calls (invalid flag)
The Witness Claude agent now has explicit instructions:
- Parse POLECAT_DONE message for polecat name
- Check cleanup_status via bd show
- Run gt polecat nuke or bd create --ephemeral based on status
- Archive mail after handling
ZFC: Agent decides. Go transports.
Fixed two issues in `gt crew stop <name>`:
1. --dry-run flag now works for individual crew stops (previously only
worked with --all)
2. HasSession errors are now properly handled instead of being ignored,
which could cause "No session found" messages even when sessions exist
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The Witness handlers (HandlePolecatDone, HandleMerged, etc.) existed in Go
code but were never called - there was no CLI command to invoke them.
This caused polecats to remain in 'done' state after MR merge because
POLECAT_DONE messages were never processed.
Changes:
- Add `gt witness process <rig>` command to process Witness mail
- Fix --wisp flag to --ephemeral in cleanup wisp creation
- Command processes POLECAT_DONE, MERGED, HELP, SWARM_START messages
- Auto-nukes clean polecats, creates cleanup wisps for dirty ones
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add `gt deacon stale-hooks` command to find and unhook stale beads.
Problem: Beads can get stuck in 'hooked' status when agents die or
abandon work without properly unhooking.
Solution:
- New command scans for hooked beads older than threshold (default 1h)
- Checks if assignee agent is still alive (tmux session exists)
- Unhooks beads with dead agents (sets status back to 'open')
- Supports --dry-run to preview without making changes
Also adds "stale-hook-check" step to Deacon patrol formula.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
gt sling failed when hooking rig-level beads from town root because
bd update doesn't support cross-database routing like bd show does.
The fix adds a ResolveHookDir helper that:
1. Extracts the prefix from bead ID (e.g., "ap-xxx" → "ap-")
2. Looks up the rig path from routes.jsonl
3. Falls back to townRoot if prefix not found
Also removes the BEADS_DIR environment override which was preventing
routing from working correctly.
Fixes#148
buildRestartCommand() now propagates Claude-related env vars when
respawning sessions via tmux. Fresh shells don't inherit parent env,
so CLAUDE_CODE_USE_BEDROCK, ANTHROPIC_API_KEY, AWS_*, etc. were lost.
This caused any tmux respawn to result in a non-functional claude.
Adds claudeEnvVars list and includes them in the export command when
building the restart command.
* fix: create mayor/daemon.json during gt start and gt doctor --fix (#5)
- Add DaemonPatrolConfig type with heartbeat and patrol settings
- Add Load/Save/Ensure functions for daemon patrol config
- Create daemon.json in gt start (non-fatal if fails)
- Make PatrolHooksWiredCheck fixable with Fix() method
- Add comprehensive tests for both config and doctor checks
This fixes the issue where gt doctor expects mayor/daemon.json to exist
but it was never created by gt start or any other command.
* refactor: use constants.DirMayor instead of hardcoded string
* feat: Beads redirect architecture for tracked and local beads
This change implements proper redirect handling so that all rig agents
(Witness, Refinery, Crew, Polecats) can work with both:
- Tracked beads: .beads/ checked into git at mayor/rig/.beads
- Local beads: .beads/ created at rig root during gt rig add
Key changes:
1. SetupRedirect now handles tracked beads by skipping redirect chains.
The bd CLI doesn't support chains (A→B→C), so worktrees redirect
directly to the final destination (mayor/rig/.beads for tracked).
2. ResolveBeadsDir is now used consistently in polecat and refinery
managers instead of hardcoded mayor/rig paths.
3. Rig-level agents (witness, refinery) now use rig beads with rig
prefix instead of town beads. This follows the architecture where
town beads are only for Mayor/Deacon.
4. prime.go simplified to always use ../../.beads for crew redirects,
letting rig-level redirect handle tracked vs local routing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(doctor): Add beads-redirect check for tracked beads
When a repo has .beads/ tracked in git (at mayor/rig/.beads), the rig root
needs a redirect file pointing to that location. This check:
- Detects missing rig-level redirect for tracked beads
- Verifies redirect points to correct location (mayor/rig/.beads)
- Auto-fixes with 'gt doctor --fix'
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: Handle fileLock.Unlock error in daemon
Wrap fileLock.Unlock() return value to satisfy errcheck linter.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When `gt sling` targets an existing polecat session, it now waits for
Claude to be ready before sending the nudge message. This fixes issue #115
where the "Work slung" message would arrive before Claude had fully started.
Changes:
- Add getSessionFromPane() to extract session name from pane target
- Add ensureClaudeReady() to wait for Claude startup using the same
pragmatic approach as session.Start() (poll for node, accept bypass
dialog, then 8-second delay)
- Call ensureClaudeReady() before injectStartPrompt() in runSling()
The fix uses IsClaudeRunning() for a fast path when Claude is already
running, avoiding unnecessary delays for sessions that have been
running for a while.
Fixes#115🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Handle empty repos in ConfigureSparseCheckout (skip read-tree when no HEAD)
- Fix errcheck: wrap fileLock.Unlock() error in defer
- Fix unparam: remove unused *rig.Rig return from getWitnessManager
- Fix unparam: mark unused agentType parameter with blank identifier
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
detectCurrentTmuxSession() was only accepting gt-* prefix sessions,
rejecting town-level sessions like hq-mayor. Now accepts both gt-*
and hq-* prefixes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Witness and Refinery startup was duplicated across cmd/witness.go, cmd/up.go,
cmd/rig.go, and daemon.go. Worse, not all code paths sent the propulsion nudge
(GUPP - Gas Town Universal Propulsion Principle). Now unified in Manager.Start()
which handles everything including nudges.
Changes:
- witness/manager.go: Full rewrite with session creation, env vars, theming,
WaitForClaudeReady, startup nudge, and propulsion nudge (GUPP)
- refinery/manager.go: Add propulsion nudge sequence after Claude startup
- cmd/witness.go: Simplify to just call mgr.Start(), remove ensureWitnessSession
- cmd/rig.go: Use witness.Manager.Start() instead of inline session creation
- cmd/start.go: Use witness.Manager.Start()
- cmd/up.go: Use witness.Manager.Start(), remove ensureWitness(),
add EnsureSettingsForRole in ensureSession()
- daemon.go: Use witness.Manager.Start() and refinery.Manager.Start() for
unified startup with proper nudges
This ensures all agent startup paths (gt witness start, gt rig boot, gt up,
daemon restarts) consistently apply GUPP propulsion nudges.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Settings creation was scattered across multiple places (createPatrolHooks,
ensurePatrolHooks, inline code). Now unified via claude.EnsureSettingsForRole().
Changes:
- Add "deacon" to autonomous roles in claude/settings.go
- Remove ensurePatrolHooks() from cmd/deacon.go, use EnsureSettingsForRole
- Remove createPatrolHooks() from rig/manager.go (no longer needed at rig add)
- Add EnsureSettingsForRole call in crew_lifecycle.go
- Add doctor check for stale/missing Claude settings files
- Wire up claude-settings check in cmd/doctor.go
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add a new 'prefix-mismatch' check to gt doctor that detects when the
prefix configured in rigs.json differs from what routes.jsonl actually
uses for a rig's beads.
This can happen when:
- deriveBeadsPrefix() generates a different prefix than what's in the DB
- Someone manually edited rigs.json with the wrong prefix
- Beads were initialized before auto-derive existed with a different prefix
The check is fixable: running 'gt doctor --fix' will update rigs.json
to match the actual prefixes from routes.jsonl.
Includes comprehensive tests for:
- No routes (nothing to check)
- No rigs.json (nothing to check)
- Matching prefixes (OK)
- Mismatched prefixes (Warning)
- Fix functionality
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Per docs/architecture.md, Witness and Refinery are rig-level agents that
should use the rig's configured prefix (e.g., pi- for pixelforge) instead
of hardcoded "gt-".
This extends PR #183's creation fix to also fix all lookup paths:
- internal/rig/manager.go: Create agent beads in rig beads with rig prefix
- internal/daemon/daemon.go: Use rig prefix when looking up agent state
- internal/daemon/lifecycle.go: Use rig prefix for identity-to-bead mapping
- internal/cmd/sling.go: Pass townRoot for prefix lookup
- internal/cmd/unsling.go: Pass townRoot for prefix lookup
- internal/cmd/molecule_status.go: Use rig prefix for agent bead lookups
- internal/cmd/molecule_attach.go: Use rig prefix for agent bead lookups
- internal/config/loader.go: Add GetRigPrefix helper
Without this fix, the daemon would:
- Create pi-gastown-witness but look for gt-gastown-witness
- Report agents as missing/dead when they are running
- Fail to manage agent lifecycle correctly
Based on work by Johann Taberlet in PR #183.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Johann Taberlet <johann.taberlet@gmail.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
In startConfiguredCrew(), only HasSession() was checked, missing the case
where a tmux session exists but Claude has exited. Now checks IsClaudeRunning()
and restarts Claude with BuildCrewStartupCommand if dead, matching the behavior
in runStartCrew(). (gt-ms8s4)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add --purge flag to gt crew remove that:
- Deletes the agent bead (not just closes it)
- Unassigns any beads assigned to the crew member
- Properly handles git worktrees (not just regular clones)
- Add gt doctor crew-worktrees check to detect stale cross-rig worktrees
- Worktrees in crew/ with hyphenated names are now properly cleaned up
using git worktree remove instead of rm -rf
The --purge flag is for accidental/test crew that should leave no trace
in the capability ledger. Normal crew removal closes the agent bead to
preserve CV history per HOP architecture.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Session-ended event beads were accumulating without being processed.
Modified costs.go to auto-close these events immediately after creation
since they are informational audit events. The event data is preserved
in the closed bead and remains queryable.
Also bulk-closed 83 existing stale session-ended events.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
gt crew start beads and gt crew stop beads now default to --all
behavior when no crew names are specified, matching user expectations.
- crew start: accepts 0-1 args (rig only) and starts all crew
- crew stop: detects if single arg is a rig name vs crew name
- Updated help text with new examples
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Global agents like mayor and deacon are town-level agents that should use
the "hq-" prefix, not "gt-". Changed to use AgentBeadIDWithPrefix with
TownBeadsPrefix for consistency with the beads architecture.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>