Fixes from code review:
- Remove duplicate generateDogNameForDispatch, reuse generateDogName
- Fix race condition: assign work BEFORE sending mail
- Add rollback if mail send fails (clear work assignment)
- Fix misleading help text (was "hooks mail", actually sends mail)
- Add --json flag for scripted output
- Add --dry-run flag to preview without executing
The order change (assign work first, then send mail) ensures that if
AssignWork fails, no mail has been sent. If mail fails after work is
assigned, we rollback by clearing the work assignment.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove duplicate *Parallel variants, consolidate into single functions
- Cache discoverAllRigs() result at top level, pass to functions
- Use sync/atomic for startedAny flag instead of extra mutex
- Functions now take rigs slice and mutex as parameters
Net reduction: 83 lines
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Workers now get primed on the desire-paths philosophy:
- crew.md.tmpl: New "Desire Paths" section before Tips
- polecat.md.tmpl: Updated "Agent UX" section with desire-path label
When a command fails but the guess was reasonable, workers are
encouraged to file a bead with the desire-path label. This helps
improve agent ergonomics by surfacing intuitive command patterns.
References ~/gt/docs/AGENT-ERGONOMICS.md for full philosophy.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements gt-n08ix.2: formalized plugin dispatch to dogs.
The new `gt dog dispatch --plugin <name>` command:
- Finds plugin definition using the existing plugin scanner
- Creates a mail work unit with plugin instructions
- Assigns work to an idle dog (or creates one with --create)
- Returns immediately (non-blocking)
Usage:
gt dog dispatch --plugin rebuild-gt
gt dog dispatch --plugin rebuild-gt --rig gastown
gt dog dispatch --plugin rebuild-gt --dog alpha
gt dog dispatch --plugin rebuild-gt --create
This enables the Deacon to dispatch plugins to dogs during patrol
cycles without blocking on execution.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- gt plugin run: Manual plugin execution with gate check
- --force to bypass cooldown gate
- --dry-run to preview without executing
- Records successful runs as ephemeral beads
- gt plugin history: Show execution history from ephemeral beads
- --json for machine-readable output
- --limit to control number of results
- Fix recording.go to use valid bd list flags (--created-after instead of --since)
Closes: gt-n08ix.4
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Start Mayor, Deacon, rig agents, and crew all in parallel rather than
sequentially. This reduces worst-case startup from N*60s to ~60s since
all agents can start concurrently.
Closes gt-dgbwk
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Updated all component versions:
- gt CLI: 0.2.5 → 0.2.6
- npm package: 0.2.5 → 0.2.6
Highlights:
- Unified escalation system with severity levels and routing
- gt stale command for binary staleness checks
- Per-agent-type health tracking in statusline
- Refactored sling.go into 7 focused modules
- Many bug fixes for beads, sling, and session lifecycle
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Exposes CheckStaleBinary() via CLI for scripting. Supports --json for
machine-readable output and --quiet for exit-code-only mode (0=stale,
1=fresh, 2=error).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
MR beads were being created as regular beads, showing up in `bd ready`
when they should be ephemeral wisps that get cleaned up after merge.
Added Ephemeral field to CreateOptions and set it when creating MR beads.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When using gastown/max style paths, resolvePathToSession was treating
all non-role names as polecats, generating gt-gastown-max instead of
gt-gastown-crew-max.
Now checks if <townRoot>/<rig>/crew/<name> exists before defaulting
to polecat format. This fixes gt sling to crew members using the
shorthand rig/name syntax.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add fallback instructions to start/restart topics in FormatStartupNudge()
so agents have actionable instructions even if SessionStart hook fails.
Previously, "start" and "restart" beacons only contained metadata like:
[GAS TOWN] beads/crew/fang <- human • 2025-01-12 • start
If the SessionStart hook failed to inject context via `gt prime`, agents
would sit idle at "No recent activity" screen with no instructions.
Now these topics include:
Run `gt prime` now for full context, then check your hook and mail.
Also warn instead of silently discarding settings provisioning errors in
crew_at.go.
Fixes: gt-uoc64
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(config): don't export empty GT_ROOT/BEADS_DIR in AgentEnv
Fix polecats not having GT_ROOT environment variable set. The symptom was
polecat sessions showing GT_ROOT="" instead of the expected town root.
Root cause: AgentEnvSimple doesn't set TownRoot, but AgentEnv was always
setting env["GT_ROOT"] = cfg.TownRoot even when empty. This empty value
in export commands would override the tmux session environment.
Changes:
- Only set GT_ROOT and BEADS_DIR in env map if non-empty
- Refactor daemon.go to use AgentEnv with full AgentEnvConfig instead
of AgentEnvSimple + manual additions
- Update test to verify keys are absent rather than empty
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(lint): silence unparam for unused executeExternalActions args
The external action params (beadID, severity, description) are reserved
for future email/SMS/slack implementations but currently unused.
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: max <steve.yegge@gmail.com>
- Merge two session iteration loops into single pass
- Remove unused polecatCount variable
- Consolidate rig status and health tracking
- Net reduction of 17 lines
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change EscalationConfig to use Routes map with action strings
- Rename severity "normal" to "medium" per design doc
- Move config from config/ to settings/escalation.json
- Add --source flag for escalation source tracking
- Add Source field to EscalationFields
- Add executeExternalActions() for email/sms/slack with warnings
- Add default escalation config creation in gt install
- Add comprehensive unit tests for config loading
- Update help text with correct severity levels and paths
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix misleading language that could suggest polecats wait in an idle pool:
- refinery/engineer.go: "available polecat" → "fresh polecat (spawned on demand)"
- namepool.go: Clarify this pools NAMES not polecats; polecats are spawned
fresh and nuked when done, only name slots are reused
- dog-pool-architecture.md: "Pool allocation pattern" → "Name slot allocation
pattern (pool of names, not instances)"
There is no idle pool of polecats. They are spawned for work and nuked when done.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Polecats have exactly three operating conditions - there is no idle pool:
- Working: session active, doing assigned work
- Stalled: session stopped unexpectedly, never nudged back
- Zombie: gt done called but cleanup failed
Key clarifications:
- These are SESSION states; polecat identity persists across sessions
- "Stalled" and "zombie" are detected conditions, not stored states
- The status:idle label only applies to persistent agents, not polecats
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds per-agent-type health tracking to the Mayor's tmux statusline, showing
working/idle counts for Polecats, Witnesses, Refineries, and Deacon.
All agent types are always displayed, even when no agents of that type are
running (shows as '0/0 😺').
Format: active: 4/4 😺 6/10 👁️ 7/10 🏭 1/1 ⛪
Co-authored-by: gastown/crew/dennis <steve.yegge@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Role beads (hq-*-role) are templates that define role characteristics.
They are created during gt install but creation may fail silently.
Without role beads, agents fall back to defaults.
Changes:
- Add beads.AllRoleBeadDefs() as single source of truth for role bead definitions
- Update gt install to use shared definitions
- Add doctor check that detects missing role beads (warning, not error)
- Doctor --fix creates missing role beads
Fixes#371
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add two tests:
- TestAddWithOptions_HasAgentsMD: verifies AGENTS.md exists in worktree
after creation when it's in git
- TestAddWithOptions_AgentsMDFallback: verifies fallback copy works when
AGENTS.md is not in git but exists in mayor/rig
Fixes: gt-sq1.3
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When creating or repairing worktrees, if AGENTS.md doesn't exist after
checkout (e.g., stale fetch or local-only file), copy it from mayor/rig.
This ensures polecats always have the critical "land the plane" instructions.
Applied to both AddWithOptions and RepairWorktreeWithOptions for
consistency. Errors are non-fatal (warning only).
Fixes: gt-sq1.2
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(doctor): filter bd "Note:" messages from custom types check
bd outputs "Note: No git repository initialized..." to stdout when
running outside a git repo, which was contaminating the custom types
parsing and causing false warnings.
- Use Output() instead of CombinedOutput() to avoid stderr
- Filter out lines starting with "Note:" from stdout
Co-Authored-By: Claude <noreply@anthropic.com>
* test(doctor): add unit tests for custom types Note: filtering
Extract parseConfigOutput helper function and add tests verifying
that bd "Note:" informational messages are properly filtered from
config output. Tests fail without the fix and pass with it.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude <noreply@anthropic.com>
* fix(beads): prevent routes.jsonl corruption from bd auto-export
When issues.jsonl doesn't exist, bd's auto-export mechanism writes
issue data to routes.jsonl, corrupting the routing configuration.
Changes:
- install.go: Create issues.jsonl before routes.jsonl at town level
- manager.go: Create issues.jsonl in rig beads; don't create routes.jsonl
(rig-level routes.jsonl breaks bd's walk-up routing to town routes)
- Add integration tests for routes.jsonl corruption prevention
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(doctor): add check to detect and fix rig-level routes.jsonl
Add RigRoutesJSONLCheck to detect routes.jsonl files in rig .beads
directories. These files break bd's walk-up routing to town-level
routes.jsonl, causing cross-rig routing failures.
The fix unconditionally deletes rig-level routes.jsonl files since
bd will auto-export to issues.jsonl on next run.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test(rig): add verification that routes.jsonl does NOT exist in rig .beads
Add explicit test assertion and detailed comment explaining why rig-level
routes.jsonl files must not exist (breaks bd walk-up routing to town routes).
Also verify that issues.jsonl DOES exist (prevents bd auto-export corruption).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(doctor): ensure town root route exists in routes.jsonl
The RoutesCheck now detects and fixes missing town root routes (hq- -> .).
This can happen when routes.jsonl is corrupted or was created without the
town route during initialization.
Changes:
- Detect missing hq- route in Run()
- Add hq- route in Fix() when missing
- Handle case where routes.jsonl is corrupted (regenerate with town route)
- Add comprehensive unit tests for route detection and fixing
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test(beads): fix routing integration test for routes.jsonl corruption
The TestBeadsRoutingFromTownRoot test was failing because bd's auto-export
mechanism writes issue data to routes.jsonl when issues.jsonl doesn't exist.
This corrupts the routing configuration.
Fix: Create empty issues.jsonl after bd init to prevent corruption.
This mirrors what gt install does to prevent the same bug.
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>
When using `gt sling <formula> --on <bead>`, the code was only passing
the `feature` variable (set to bead title). This broke formulas that
expect `issue` (set to bead ID), like mol-polecat-work.
Now passes both common variables:
- feature: bead title (for shiny-style formulas)
- issue: bead ID (for mol-polecat-work-style formulas)
This allows either formula type to work with --on without requiring
the user to manually specify variables.
Fixes#355
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Role beads created by gt install were missing the gt:role label required
by GetRoleConfig(), causing witness startup to fail with:
"bead hq-witness-role is not a role bead (missing gt:role label)"
This regression was introduced in 96970071 which migrated from type-based
to label-based bead classification. The install code used raw exec.Command
instead of the beads API, so it wasn't updated to add labels.
Changes:
- Use bd.CreateWithID() API which auto-converts Type:"role" to gt:role label
- Add RoleLabelCheck doctor migration to fix existing installations
- Add comprehensive unit tests with mocked dependencies
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Bare clones don't have refs/remotes/origin/* populated by default.
The configureRefspec fix (a91e6cd6) set up the fetch config but didn't
actually run a fetch, leaving origin/main unavailable.
This caused polecat worktree creation to fail with:
fatal: invalid reference: origin/main
Fixes:
1. Add git fetch after configureRefspec in bare clone setup
2. Add fetch before polecat worktree creation (ensures latest code)
The second fix matches RepairWorktreeWithOptions which already had a fetch.
Related: #286
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add severity-based routing for escalations with config-driven targets.
Changes:
- EscalationConfig type with severity routes and external channels
- beads/beads_escalation.go: Escalation bead operations (create/ack/close/list)
- Refactored gt escalate command with subcommands:
- list: Show open escalations
- ack: Acknowledge an escalation
- close: Resolve with reason
- stale: Find unacknowledged escalations past threshold
- show: Display escalation details
- Added TypeEscalationAcked and TypeEscalationClosed event types
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add --debug flag for troubleshooting crew attach issues. Shows:
- Current working directory
- Detected rig and crew name
- Computed session ID
- Whether inside tmux
- Which session we are attaching to
Also adds Attaching to session message before attach.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
If the agent bead doesn't exist when gt done tries to clear the hook,
return early instead of failing. This happens for polecats created
before identity beads existed.
gt done must be resilient and forgiving - the important thing is work
gets submitted to merge queue, not that cleanup succeeds.
Fixes: hq-i26n2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Boot sessions run in `deacon/dogs/boot/` but were incorrectly detected
as deacon role because the deacon check matched first. This caused Boot
to receive Deacon's context instead of Boot-specific context.
Changes:
- Add RoleBoot constant
- Add boot path detection before deacon check in detectRole()
- Add boot case in buildRoleAnnouncement()
- Add boot case in getAgentIdentity() (returns "boot")
- Add boot case in getAgentBeadID() (uses deacon's bead as subprocess)
The boot.md.tmpl template already exists and will now be used.
Fixes#318
Since the self-cleaning model (Jan 10), polecats push branches to origin
before `gt done`. The refinery was only deleting local branches after
merge, causing stale `polecat/*` branches to accumulate on the remote.
Now deletes both local and remote branches after successful merge.
Uses existing `git.DeleteRemoteBranch()` function. Remote deletion is
non-fatal if the branch doesn't exist.
Fixes#359
* test(util): add comprehensive tests for atomic write functions
Add tests for:
- File permissions
- Empty data handling
- Various JSON types (string, int, float, bool, null, array, nested)
- Unmarshallable types error handling
- Read-only directory permission errors
- Concurrent writes
- Original content preservation on failure
- Struct serialization/deserialization
- Large data (1MB)
* test(connection): add edge case tests for address parsing
Add comprehensive test coverage for ParseAddress edge cases:
- Empty/whitespace/slash-only inputs
- Leading/trailing slash handling
- Machine prefix edge cases (colons, empty machine)
- Multiple slashes in polecat name (SplitN behavior)
- Unicode and emoji support
- Very long addresses
- Special characters (hyphens, underscores, dots)
- Whitespace in components
Also adds tests for MustParseAddress panic behavior and RigPath method.
Closes: gt-xgjyp
* test(checkpoint): add comprehensive test coverage for checkpoint package
Tests all public functions: Read, Write, Remove, Capture, WithMolecule,
WithHookedBead, WithNotes, Age, IsStale, Summary, Path.
Edge cases covered: missing file, corrupted JSON, stale detection.
Closes: gt-09yn1
* test(lock): add comprehensive tests for lock package
Add lock_test.go with tests covering:
- LockInfo.IsStale() with valid/invalid PIDs
- Lock.Acquire/Release lifecycle
- Re-acquiring own lock (session refresh)
- Stale lock cleanup during Acquire
- Lock.Read() for missing/invalid/valid files
- Lock.Check() for unlocked/owned/stale scenarios
- Lock.Status() string formatting
- Lock.ForceRelease()
- processExists() helper
- FindAllLocks() directory scanning
- CleanStaleLocks() with mocked tmux
- getActiveTmuxSessions() parsing
- splitOnColon() and splitLines() helpers
- DetectCollisions() for stale/orphaned locks
Coverage: 84.4%
* test(keepalive): add example tests demonstrating usage patterns
Add ExampleTouchInWorkspace, ExampleRead, and ExampleState_Age to
serve as documentation for how to use the keepalive package.
* fix(test): correct boundary test timing race in checkpoint_test.go
The 'exactly threshold' test case was flaky due to timing: by the time
time.Since() runs after setting Timestamp, microseconds have passed,
making age > threshold. Changed expectation to true since at-threshold
is effectively stale.
---------
Co-authored-by: slit <gt@gastown.local>
* test(costs): add failing test for multi-location session event query
Add integration test that verifies querySessionEvents finds session.ended
events from both town-level and rig-level beads databases.
The test demonstrates the bug: events created by rig-level agents (polecats,
witness, etc.) are stored in the rig's .beads database, but querySessionEvents
only queries the town-level beads, missing rig-level events.
Test setup:
- Creates town with gt install
- Adds rig with gt rig add (separate beads DB)
- Creates session.ended event in town beads (simulating mayor)
- Creates session.ended event in rig beads (simulating polecat)
- Verifies querySessionEvents finds both events
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(costs): query all beads locations for session events
querySessionEvents previously only queried the town-level beads database,
missing session.ended events created by rig-level agents (polecats, witness,
refinery, crew) which are stored in each rig's own .beads database.
The fix:
- Load rigs from mayor/rigs.json
- Query each rig's beads location in addition to town-level beads
- Merge and deduplicate results by session ID + timestamp
This ensures `gt costs` finds all session cost events regardless of which
agent's beads database they were recorded in.
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>
When a repo with tracked .beads/ is added as a rig, the beads.db file
doesn't exist because it's gitignored. Previously, bd init was only run
if prefix detection succeeded. If there were no issues in issues.jsonl,
detection failed and bd init was never run, causing "Error: no beads
database found" when running bd commands.
Changes:
- Always run bd init when tracked beads exist but db is missing
- Detect prefix from existing issues in issues.jsonl
- Only error on prefix mismatch if user explicitly passed --prefix
- If no issues exist, use the derived/provided prefix
Fixes#72
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
sessionWorkDir had cases for mayor, deacon, crew, witness, and refinery
but not polecats. When gt handoff was run from a polecat session like
gt-tanwa_info-slit, it failed with "unknown session type".
Fix uses session.ParseSessionName to parse the session name and extract
rig/name for polecat sessions, mapping to <townRoot>/<rig>/polecats/<name>.
Fixes: gm-lie6
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The --naked flag (skip tmux session creation) was a vestige of an earlier
design requiring manual session management. With the current polecat
architecture where polecats are witness-managed, ephemeral, and self-deleting
after task completion, manual session management is no longer needed.
The flag also created invalid states (e.g., slinging to crew --naked left
them unreachable since crew require tmux sessions for communication).
Closes gt-xhn5s
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add new `gt polecat identity` (alias: `id`) subcommand group with commands:
- add <rig> [name]: Create identity bead (auto-generates name if omitted)
- list <rig>: List polecat identity beads with session/worktree status
- show <rig> <name>: Show identity details and CV (work history)
- rename <rig> <old> <new>: Rename identity, preserving CV chain
- remove <rig> <name>: Remove identity with safety checks
Each command manipulates agent beads with role_type=polecat. Safety checks
prevent removal of identities with active sessions or work on hook.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add deprecation warning pointing users to 'gt polecat identity add':
- Cobra Deprecated field emits automatic warning on command use
- Custom warning in runPolecatAdd for prominent stderr output
- Updated help text with deprecation notice and new command example
The command still functions but will be removed in v1.0.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Improve tmux statusline: sort rigs by activity and add visual grouping
- Sort rigs by running state, then polecat count, then operational state
- Add visual grouping with | separators between state groups
- Show process state with icons (🟢 both running, 🟡 one running, 🅿️ parked, 🛑 docked, ⚫ idle)
- Display polecat counts for active rigs
- Improve icon spacing: 2 spaces after Park emoji, 1 space for others
* Fix golangci-lint warnings
- Check error return from os.Setenv
- Check error return from lock.Unlock
- Mark intentionally unused parameters with _
---------
Co-authored-by: joshuavial <git@codewithjv.com>
When polecats run 'gt done' without --cleanup-status, the witness may
prematurely nuke the worktree before the refinery can merge.
This fix auto-detects git state:
- uncommitted: has uncommitted changes
- stash: has stashed changes
- unpushed: branch not pushed or has unpushed commits
- clean: everything pushed
Uses BranchPushedToRemote() which properly handles polecat branches
that don't have upstream tracking (compares against origin/main).
On error, defaults to 'unpushed' to prevent accidental data loss.
Fixes: #342
Co-authored-by: mayor <mayor@gastown.local>
When running `gt install --wrappers` in an existing Gas Town HQ,
the command now installs wrappers directly without requiring --force
or recreating the entire HQ structure.
Previously, `gt install --wrappers` would fail with "directory is
already a Gas Town HQ" unless --force was used, which would then
unnecessarily reinitialize the entire workspace.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add documentation to make the formula package more discoverable and
demonstrate its value as a reusable workflow definition library.
The formula package provides TOML-based workflow definitions with:
- Type inference (convoy, workflow, expansion, aspect)
- Comprehensive validation
- Cycle detection in dependency graphs
- Topological sorting (Kahn's algorithm)
- Ready-step computation for parallel execution
New files:
- doc.go: Package-level godoc with examples and API overview
- README.md: User guide with installation, quick start, and API reference
- example_test.go: Runnable examples for godoc and testing
The package has 130% test coverage (1,200 LOC tests for 925 LOC code)
and only depends on github.com/BurntSushi/toml.