Agent beads hook_bead field never populated

## Problem

The witness patrol formula expects polecat agent beads to have:

  hook_bead: <current-work-id>

This field is used by the witness to see what the polecat is working on.

But looking at the code, hook_bead is never set:

1. Polecat agent bead created (polecat/manager.go:167-170) - no hook_bead
2. reportAgentState called (prime.go:1067) - passes nil for hookBead
3. UpdateAgentState preserves existing hook_bead if nil (beads.go:633-635)

Since hook_bead starts empty and is never updated, the witness cannot use
agent beads to see what polecats are working on.

## Found in commit

f3a6ef6 (feat: Witness reads polecat state from agent beads)

## Related to

- gt-0lop3 (Agent state lifecycle incomplete for polecats)

## Suggested fix

When a polecat is assigned work (via gt sling or similar), update the agent
bead's hook_bead field:

  bd.UpdateAgentState(agentID, 'running', &hookBeadID)


Digest: mol-deacon-patrol

Patrol cycle 6: Routine.


Merge: nux-1767080427756

branch: polecat/nux-1767080427756
target: main
source_issue: nux-1767080427756
rig: gastown


Merge: bullet-mjwlbeu5

branch: polecat/bullet-mjwlbeu5
target: main
source_issue: bullet-mjwlbeu5
rig: gastown
agent_bead: gt-gastown-polecat-bullet

Merged to main at c33c4a92 - fixed duplicate tests


Digest: mol-deacon-patrol

Patrol 19


Formula parser: Load and validate formula.toml files

Create internal/formula/parser.go:
- Parse formula.toml using BurntSushi/toml
- Validate required fields (name, legs)
- Resolve leg dependencies
- Return Formula struct

Add internal/formula/types.go for Formula, Leg, Input structs.

Already on main at a395b4e (formula parser)


Day 5.2: Fix integration issues

Address any issues found in Day 5.1:
- Debug failures
- Fix edge cases
- Retry integration test
- Document remaining gaps

This is buffer time for reality.

Parent: gt-oki8p


gt hook: Shows wrong agent identity when run from different rig's crew directory

## Problem

Running `gt hook` from another rig's crew directory shows the wrong agent.

```bash
cd ~/gt/beads/crew/dave
gt hook
# Shows: gastown/crew/joe (wrong - should be beads/crew/dave)
```

## Expected

`gt hook` should detect agent identity from cwd, showing the hook for the
agent whose directory you're in.

## Related

- gt-yud21: gt peek cross-rig crew path support
- gt-hldpv: gt sling "." resolution

## Root cause

Likely the agent identity detection in hook.go doesn't handle cross-rig
crew paths correctly.

Fixed: gt hook now uses cwd-based detection to show the correct agent identity when viewing from another rig's crew/polecat directory


Day 2.3: Agent writes state on lifecycle events

Agents update their bead state using bd agent state command:
- On startup: bd agent state gt-<role> running
- On shutdown: bd agent state gt-<role> stopped
- On stuck: bd agent state gt-<role> stuck

State is self-reported, not inferred.

Implementation: Wire up agent lifecycle hooks (SessionStart, SessionEnd, health checks) to call bd agent state.

Parent: gt-d0jqp


Digest: mol-deacon-patrol

Patrol cycle 5: All 6 agents healthy, gastown queue cleared


Session ended: gt-gastown-rictus

Bulk cleanup of stale session-ended events (gt-8tc1v)


Epic: Heresy Correction - Local-Only Polecat Branches

Shift polecat branch lifecycle back to local-only. Remote push was a 'heresy' that crept in.

**Original intent:** Polecat branches stay local, Refinery merges locally via shared .repo.git, only main gets pushed to origin.

**Current (wrong):** Polecats must push branches to origin, Refinery fetches from origin.

**Key insight:** All worktrees share .repo.git - Refinery can access polecat branches directly.

Phases:
1. Remove push enforcement (backward compatible)
2. Update Refinery to use local branches
3. Update docs/templates
4. Handle edge cases (conflict resolution, cleanup timing)

Heresy correction complete. All 4 phases implemented:
- Phase 1: Removed push enforcement from gt done
- Phase 2: Refinery now merges local branches
- Phase 3: Templates/docs updated
- Phase 4: Cleanup deferred until MR merged
PR #145 closed as superseded.


Merge: toast-mjtm1hg8

branch: polecat/toast-mjtm1hg8
target: main
source_issue: toast-mjtm1hg8
rig: gastown


Digest: mol-deacon-patrol

Patrol cycle 12: all healthy


Merge: immortan-mk02nagx

branch: polecat/immortan-mk02nagx
target: main
source_issue: immortan-mk02nagx
rig: gastown
agent_bead: gt-gastown-polecat-immortan
retry_count: 0
last_conflict_sha: null
conflict_task_id: null


Merge: slit-1767082302712

branch: polecat/slit-1767082302712
target: main
source_issue: slit-1767082302712
rig: gastown


Digest: mol-deacon-patrol

Patrol 14: Quiet


Audit and fix remaining gt-* references for town-level agents

After hq-* migration (gt-4r1ph), there are still references to gt-mayor/gt-deacon in code.

PR #73 addresses some of these but is not merged yet.

Remaining audit needed:
1. Merge PR #73
2. Check all files for gt-mayor/gt-deacon references
3. Update code to use hq-* for town-level agents
4. Update tmux session naming if needed
5. Run gt install to create any missing beads (hq-dog-role)

Related: gt-4r1ph (parent epic)

Updated all role bead references to use hq-* prefix via beads.RoleBeadIDTown(). Templates and formulas now use 'bd show hq-deacon' for bead queries. Tmux session names remain as gt-* (runtime identifiers).


Session ended: gt-gastown-blackfinger


Merge: slit-mjw3n5iw

branch: polecat/slit-mjw3n5iw
target: main
source_issue: slit-mjw3n5iw
rig: gastown
agent_bead: gt-gastown-polecat-slit


Witness: aggregate completion and notify Mayor

## Problem
No signal when swarm completes. Mayor must manually check.

## ZFC-Compliant Solution
Add step to `mol-witness-patrol.formula.toml`:

```toml
[[step]]
id = "check-swarm-completion"
title = "Report swarm completion to Mayor"
description = """
1. Check state.json for active swarm tracking:
   - swarm_id, start_time, assigned_beads[], completed_beads[]
2. For each completed merge (from cleanup step):
   - Add bead to completed_beads
3. If all assigned_beads are in completed_beads:
   - Calculate duration
   - Send summary to Mayor:
     gt mail send mayor/ -s "SWARM_COMPLETE: <swarm_id>" -m "All N polecats merged in Xm"
   - Clear swarm tracking from state.json
"""
depends_on = ["cleanup-merged-polecats"]
```

## Swarm Initialization
When Mayor slings a batch, they should:
```bash
# Mayor creates swarm tracking
gt mail send <rig>/witness -s "SWARM_START" -m '{"swarm_id": "batch-123", "beads": ["bd-a", "bd-b", "bd-c"]}'
```

Witness parses SWARM_START and initializes tracking in state.json.

## Why This Works
- Mayor initiates batch with SWARM_START mail
- Witness tracks progress in its own state
- Witness signals completion via mail
- All coordination is agent-to-agent via mail

## Files
- formulas/mol-witness-patrol.formula.toml


Session ended: gt-gastown-crew-max


Digest: mol-deacon-patrol

Patrol #3: All systems nominal


gt rig status shows operational state

Update 'gt rig status' to show park/dock state.

Add to output:
- Operational state (operational/parked/docked)
- Source of state (wisp/bead/default)
- Any blocked configs

Example:
  gt rig status gastown
  gastown
    Status: PARKED (local)
    Path: /Users/stevey/gt/gastown
    ...

When docked:
  gastown
    Status: DOCKED (global - synced)
    ...


Digest: mol-deacon-patrol

Patrol 10: All healthy


Digest: mol-deacon-patrol

Patrol complete: no callbacks, agents healthy, 1 stale lock cleaned


Digest: mol-deacon-patrol

Patrol 55: All healthy


Merge: cheedo-1767074334708

attached_args: Code review

branch: polecat/cheedo-1767074334708
target: main
source_issue: cheedo-1767074334708
rig: gastown

Branch polecat/cheedo-1767074334708 does not exist - polecat decommissioned without work to merge


Digest: mol-deacon-patrol

Patrol 10: full check, all healthy


Digest: mol-deacon-patrol

Patrol cycle 5: All healthy


Witness: Block nuke if polecat has unpushed work

Witness nukes polecats on MERGED signal without checking if work is actually safe to delete.

## Problem
If witness receives MERGED for polecat X, it nukes X. But:
- MERGED might be for a stale MR (already merged via different path)
- Polecat might have NEW unpushed work since the MR was created
- Race condition between merge and nuke

## Fix
Before nuke, verify:
1. Get polecat's cleanup_status from agent bead
2. If cleanup_status != 'clean', escalate to Mayor instead of nuke
3. Optionally: verify the specific commit SHA is reachable from main

## Location
internal/witness/handlers.go HandleMerged() or the witness patrol formula

Implemented cleanup_status check in HandleMerged to prevent work loss


Session ended: gt-gastown/crew/joe


Merge: nux-1766959759345

branch: polecat/nux-1766959759345
target: main
source_issue: nux-1766959759345
rig: gastown


Merge: rictus-mjtlq9xg

branch: polecat/rictus-mjtlq9xg
target: main
source_issue: rictus-mjtlq9xg
rig: gastown


Digest: mol-deacon-patrol

Patrol 3: All healthy, no incidents


Merge: ace-mjz94g3q

branch: polecat/ace-mjz94g3q
target: main
source_issue: ace-mjz94g3q
rig: gastown
agent_bead: gt-gastown-polecat-ace
retry_count: 0
last_conflict_sha: null
conflict_task_id: null


Merge: valkyrie-1767074338488

branch: polecat/valkyrie-1767074338488
target: main
source_issue: valkyrie-1767074338488
rig: gastown

Branch no longer exists on remote - already merged or cleaned up


Digest: mol-deacon-patrol

Patrol 129: All healthy


Merge: dag-mjxm10sv

branch: polecat/dag-mjxm10sv
target: main
source_issue: dag-mjxm10sv
rig: gastown
agent_bead: gt-gastown-polecat-dag


Phase 5: Close PR #50, update documentation

## Goal

Close PR #50 with explanation and update documentation.

## PR #50 Resolution

Close with comment explaining:
- The prefix mismatch is real but the fix direction is wrong
- Changing town beads to gt prefix would break hq-* issues
- The correct fix is in gt-4r1ph (this epic)
- Thank contributor for identifying the issue

## Documentation Updates

1. docs/architecture.md
   - Update agent bead location table
   - Clarify two-level agent storage

2. CLAUDE.md files
   - Update beads architecture section
   - Clarify hq-* vs rig-prefix agents

3. internal/rig/manager.go
   - Update comments to reflect correct architecture
   - Remove misleading PR #32 comments about town beads

## Testing

- Verify docs match implementation
- Run gt doctor to validate architecture

Completed: PR #50 closed, docs/architecture.md created, manager.go comments updated. Awaiting Phases 1-4 to verify docs match implementation.

Completed Phase 5: PR #50 closed, docs/architecture.md created with agent bead tables, manager.go comments updated with migration notes. CLAUDE.md templates verified already correct. Work submitted to merge queue (gt-trfaz). Final verification of docs matching implementation will occur when Phases 1-4 land.


convoy --notify flag doesn't actually notify

The gt convoy create --notify flag is captured but only stored in description. No code sends notification on convoy completion.

Implemented: notify address stored in slot, added gt convoy notify command


Digest: mol-deacon-patrol

Patrol 19: quiet


Session ended: gt-gastown-crew-max


Digest: mol-deacon-patrol

Patrol 152 complete


Digest: mol-deacon-patrol

Patrol 254 complete: no incidents


Session ended: gt-gastown-crew-george


Add 'wit' alias for witness command

ref works as alias for refinery, but wit doesn't work for witness. Add Aliases: []string{"wit"} to witnessCmd.


Day 3.6: Witness pings Deacon for second-order monitoring

Implement WITNESS_PING protocol for second-order monitoring.

Witnesses collectively monitor Deacon health:
1. Each Witness sends WITNESS_PING to Deacon during patrol
2. Deacon responds (or doesn't if dead)
3. If no response after threshold, Witness escalates to Mayor

This prevents the 'who watches the watchers' problem - if Deacon dies, Witnesses detect it.

Implementation:
- Add WITNESS_PING step to mol-witness-patrol
- Deacon acknowledges pings (updates last_activity)
- Witness checks Deacon agent bead last_activity
- Escalate if stale beyond threshold


Digest: mol-deacon-patrol

Cycle 20


Merge: slit-mjxem84i

branch: polecat/slit-mjxem84i
target: main
source_issue: slit-mjxem84i
rig: gastown
agent_bead: gt-gastown-polecat-slit


Fix flaky beads integration test (sync state issue)

Fixed by syncing database before integration test


Session ended: gt-gastown-refinery


Preflight molecule: verify baseline health before work

Before assigning work, verify baseline (main branch) is healthy.

**From VC**: Self-healing state machine (HEALTHY → SELF_HEALING → ESCALATED). ~200 lines.

**Gas Town implementation**: Preflight molecule or refinery feature:
```yaml
preflight:
  gates: [test, lint, build]
  on_failure: create-fix-issue
```

Run at session start. If baseline broken, file a P0 fix issue and work on that first.

**Value**: Self-healing baseline. Agents don't start from broken state.

**VC lesson**: Prevents cascading failures. Agent shouldn't start work on broken code.


Merge: gt-svdsy

branch: polecat/capable-mjtltnm5
target: main
source_issue: gt-svdsy
rig: gastown


Digest: mol-deacon-patrol

Patrol 144: All healthy, 2 polecats working


Digest: mol-deacon-patrol

Patrol 50: All healthy


Digest: mol-deacon-patrol

Patrol 82: 3 rigs healthy, 6 agents pinged, no incidents


Digest: mol-deacon-patrol

Cycle 10


Merge: slit-1767141951901

branch: polecat/slit-1767141951901
target: main
source_issue: slit-1767141951901
rig: gastown


Digest: mol-deacon-patrol

Patrol 19: All healthy


bd admin compact --older-than=0 ignores the flag, uses default 30 days

When running bd admin compact --prune --older-than=0, the output says 'No expired tombstones to prune (TTL: 30 days)' - the flag value is ignored.

Moved to bd-gigi - this is a beads bug, was filed in wrong rig (gastown). The fix needs to be made in the beads codebase at cmd/bd/compact_tombstone.go and cmd/bd/compact.go.


Session ended: gt-gastown-nux


Daemon respects rig operational state

Update daemon to check rig config before auto-starting.

In lifecycle manager:
- Before starting witness/refinery, check rig.GetConfig("status")
- If parked or docked, skip auto-start
- Log the skip with reason

Also check auto_restart config:
- If nil (blocked) or false, skip auto-start

This makes park/dock actually prevent restarts.


Merge: wraith-mk0vuiu8

branch: polecat/wraith-mk0vuiu8
target: main
source_issue: wraith-mk0vuiu8
rig: gastown
agent_bead: gt-gastown-polecat-gus
retry_count: 0
last_conflict_sha: null
conflict_task_id: null


Hook system for event extensibility

GGT needs hook system for extensibility like PGT.

## Event Types
```go
type Event string
const (
    EventPreSessionStart  Event = "pre-session-start"
    EventPostSessionStart Event = "post-session-start"
    EventPreShutdown      Event = "pre-shutdown"
    EventPostShutdown     Event = "post-shutdown"
    EventOnPaneOutput     Event = "on-pane-output"
    EventSessionIdle      Event = "session-idle"
    EventMailReceived     Event = "mail-received"
    EventWorkAssigned     Event = "work-assigned"
)
```

## Hook Configuration
File: .claude/hooks.json or .gastown/hooks.json
```json
{
  "hooks": {
    "pre-shutdown": [
      {"type": "command", "cmd": "./scripts/pre-shutdown.sh"}
    ],
    "on-pane-output": [
      {"type": "command", "cmd": "./scripts/activity-monitor.sh"}
    ]
  }
}
```

## Hook Types
1. **Command**: Execute external script
2. **Built-in**: Internal Go functions (pre-shutdown checks)

## Hook Interface
```go
type HookRunner struct {
    config *HookConfig
}

type HookResult struct {
    Success bool
    Message string
    Block   bool  // For pre-* hooks: should operation be blocked?
}

func (r *HookRunner) Fire(event Event, ctx *HookContext) []HookResult
```

## CLI Commands
```
gt hooks list [<event>]    # List registered hooks
gt hooks fire <event>      # Manually fire for testing
gt hooks test [--all]      # Validate hook config
```

## Integration Points
- internal/session/manager.go: Fire pre/post session hooks
- internal/mail/router.go: Fire mail-received hook

## New Package
internal/hooks/
├── types.go    # Event, HookConfig, HookResult
├── runner.go   # HookRunner, Fire()
└── builtin.go  # Built-in hooks (pre-shutdown checks)

## PGT Reference
gastown-py/src/gastown/hooks/

## Acceptance Criteria
- [ ] Hook config loading from JSON
- [ ] Command hooks execute subprocess
- [ ] Pre-shutdown hook integration with session stop
- [ ] CLI for listing and testing hooks


Session ended: gt-gastown-crew-gus


Digest: mol-deacon-patrol

Patrol 8: healthy


Digest: mol-deacon-patrol

Patrol cycle 17: all healthy


Merge: keeper-1767106012994

branch: polecat/keeper-1767106012994
target: main
source_issue: keeper-1767106012994
rig: gastown


Digest: mol-deacon-patrol

Patrol 18: All healthy


Digest: mol-deacon-patrol

Patrol 5: quiet


Session ended: gt-gastown/crew/george


Session ended: gt-gastown-scrotus


Error Handling: Suppressed errors with no logging in session/manager.go

The session/manager.go Start function has many suppressed errors (using `_`):

- Line 142-144: SetEnvironment calls for GT_RIG, GT_POLECAT
- Line 148: SetEnvironment for CLAUDE_CONFIG_DIR
- Line 157-159: SetEnvironment for BEADS_DIR, BEADS_NO_DAEMON, BEADS_AGENT_NAME
- Line 172: ConfigureGasTownSession
- Line 176: SetPaneDiedHook
- Line 193: WaitForCommand (comment says non-fatal but no logging)
- Line 200: AcceptBypassPermissionsWarning
- Line 212-217: StartupNudge
- Line 225: NudgeSession

While some are intentionally "best-effort", there's no logging or tracking of these failures. Consider:
1. Adding debug logging for suppressed errors
2. Aggregating errors and returning them as warnings
3. Using a structured approach like multierror

Files:
- internal/session/manager.go:142-225

Severity: medium

Added debugSession helper function with GT_DEBUG_SESSION env var to log suppressed errors


Session ended: gt-gastown-crew-george


Digest: mol-deacon-patrol

Patrol 5: healthy


Digest: mol-deacon-patrol

Patrol 215: All nominal


Merge: valkyrie-mjtn0kda

branch: polecat/valkyrie-mjtn0kda
target: main
source_issue: valkyrie-mjtn0kda
rig: gastown


gt convoy create: Default --notify to mayor/

When Mayor creates a convoy, --notify should default to mayor/ since:
1. Mayor dispatched the work
2. Mayor should know when it lands
3. Reduces flag boilerplate

Implementation: Check actor identity, if mayor, default notify to 'mayor/'

Implemented: convoy create now defaults --notify to mayor/ when mayor is the actor


Merge: ace-1767074385080

attached_args: Code review this merge request

branch: polecat/ace-1767074385080
target: main
source_issue: ace-1767074385080
rig: gastown

Code review APPROVED: Clean fix for gt-1clzd. Adds skipPane param to resolveTargetAgent, allowing --naked to bypass tmux pane lookup for terminated polecats.


Merge: blackfinger-mk0vu0da

branch: polecat/blackfinger-mk0vu0da
target: main
source_issue: blackfinger-mk0vu0da
rig: gastown
agent_bead: gt-gastown-polecat-gus
retry_count: 0
last_conflict_sha: null
conflict_task_id: null


Digest: mol-deacon-patrol

Patrol 14: All clear


Merge: morsov-dogs

branch: polecat/morsov-dogs
target: main
source_issue: morsov-dogs
rig: gastown


bd create --dedup: semantic deduplication before issue creation

Before creating issue, check for semantic duplicates using AI similarity.

**From VC**: internal/deduplication/ - AI-powered batch comparison. ~300 lines.
VC had issue pollution problem: 438 issues with ~350+ spam because no early dedup.

**Gas Town implementation**: CLI flag on bd create:
```bash
bd create --dedup --title="Fix auth bug" --description="..."
```

Checks recent issues (7-day window) with AI similarity. If confidence >0.85, warns or blocks.

**Value**: Prevents pollution from parallel workers discovering same issues.

**VC lesson**: 115 issues filed in single day (Nov 2) because supervisor over-discovered without dedup. Rate limiting + dedup are essential.


Digest: mol-deacon-patrol

Patrol 3: All healthy


Session ended: gt-gastown-crew-jack


Digest: mol-deacon-patrol

Patrol 258 complete: no incidents


Digest: mol-deacon-patrol

Patrol 16: All healthy


Merge: furiosa-mjxkrt01

branch: polecat/furiosa-mjxkrt01
target: main
source_issue: furiosa-mjxkrt01
rig: gastown
agent_bead: gt-gastown-polecat-furiosa


Merge: nux-mjyruwvu

branch: polecat/nux-mjyruwvu
target: main
source_issue: nux-mjyruwvu
rig: gastown
agent_bead: gt-gastown-polecat-nux
retry_count: 0
last_conflict_sha: null
conflict_task_id: null


Merge: slit-mjxc8tp2

branch: polecat/slit-mjxc8tp2
target: main
source_issue: slit-mjxc8tp2
rig: gastown
agent_bead: gt-gastown-polecat-slit

Branches merged, cleaning up stale MR beads


Digest: mol-deacon-patrol

Patrol 12


Digest: mol-deacon-patrol

Patrol cycle 11: All healthy.


Session ended: gt-gastown-crew-joe


Merge: furiosa-1767079892643

branch: polecat/furiosa-1767079892643
target: main
source_issue: furiosa-1767079892643
rig: gastown


Daemon should auto-start as part of town bootstrap

The Go daemon should be started automatically when the town boots up (mol-gastown-boot). Currently it requires manual 'gt daemon start'.

The daemon is critical infrastructure - it monitors the Deacon and nudges it when naked. Without it, the Deacon patrol loop doesn't run.


Digest: mol-deacon-patrol

Patrol 7: Quiet


HOP Foundation: Entity Identity & Federation

Foundation work for HOP graph architecture. Enables agent provenance tracking, cross-workspace federation, and delegation semantics. See ~/gt/docs/hop/GRAPH-ARCHITECTURE.md for full design.

HOP Foundation complete - 8/9 tasks done. Only P3 overseer field (gt-6r18e.7) remains for future team scenarios. Unblocks POP Foundation (gt-jzmsj).


Set GIT_AUTHOR_NAME per agent session

Set GIT_AUTHOR_NAME to actor string (e.g., gastown/crew/joe) in agent session environment. GIT_AUTHOR_EMAIL stays as workspace owner. Enables git log --author queries for agent work.

Implemented: Added GIT_AUTHOR_NAME export to all agent session spawn locations (13 files updated). Enables git log --author queries for agent work.


Clarify GIT_AUTHOR_EMAIL handling for agents

When setting GIT_AUTHOR_NAME per agent (gt-6r18e.1), also need to decide GIT_AUTHOR_EMAIL:

Option A (proposed): Keep overseer's git email
- GIT_AUTHOR_EMAIL = steve.yegge@gmail.com (from git config)
- This anchors ownership to the human
- Agents are tools of the overseer

Option B: Construct agent email
- GIT_AUTHOR_EMAIL = gastown-crew-joe@gastown.local
- Fully separates agent identity
- May cause issues with GitHub/Bitbucket attribution

Recommendation: Option A - keeps human ownership clear while GIT_AUTHOR_NAME shows which agent did the work.

Verified: Option A implemented and documented. GIT_AUTHOR_EMAIL not exported, so git uses workspace owner's email from git config. Documentation in docs/federation.md lines 94-98 is accurate.


Default BD_ACTOR in beads create

BUG: BD_ACTOR env var is only read in --no-db mode and daemon RPC path. Normal direct mode skips BD_ACTOR and falls back to $USER. The comment claims 'Viper handles BD_ACTOR automatically' but there's no actual viper binding. Fix: Add explicit os.Getenv("BD_ACTOR") check in main.go line 441-448, same as the --no-db path at line 352-360.

Fixed - BD_ACTOR now read in all code paths (normal, JSONL-auto, no-db)


Add workspace metadata file (.town.json)

Create ~/gt/.town.json with owner email, workspace name, and public name. Anchors workspace to entity identity for federation.

Added Owner and PublicName fields to TownConfig, bumped schema to v2, updated gt install command


Design cross-workspace URI scheme

Define URI format: gt://entity/chain/rig/issue-id. Document resolution semantics for local vs remote references. Update GRAPH-ARCHITECTURE.md with final spec.

Designed cross-workspace URI scheme: hop:// for federation, beads:// for cross-repo, local short forms. Updated GRAPH-ARCHITECTURE.md with full spec including resolution semantics.


Add delegation relationship type to beads

Schema addition: delegation links between work units. Fields: parent, child, delegated_by, delegated_to, terms. Enables work distribution with credit cascade.

Added Delegation and DelegationTerms structs with add/remove/get/list functions. Stores delegation as slot on child issue with blocking dependency on parent.


Beads schema: Add created_by field enforcement

Ensure created_by field is populated on all issue creation. Currently optional and often missing. Should default from BD_ACTOR env var. Affects: merge requests, escalations, digests.

Implemented BD_ACTOR defaulting in Create() and CreateAgentBead() functions


Beads schema: Add overseer field for team scenarios

For future team support: overseer field identifies human owner when multiple humans share a workspace. For now, email in git commits serves this purpose. Low priority until teams needed.


Add gt audit command for provenance queries

New command: gt audit --actor=X to query work history. Combines git log, beads queries, and event log. Shows commits, beads created/closed, events by actor.

Implemented gt audit command with --actor, --since, --json, -n flags. Queries git log, beads, town log, and events feed.


Document BD_ACTOR format convention

Document the canonical BD_ACTOR format:
- Town level: mayor, deacon
- Rig level: {rig}/witness, {rig}/refinery
- Workers: {rig}/crew/{name}, {rig}/polecats/{name}

Currently only defined in lifecycle.go:807-829. Should be in docs and CLAUDE.md for agents to reference.

Also document the attribution model:
- GIT_AUTHOR_NAME = BD_ACTOR (agent identity, who did the work)
- GIT_AUTHOR_EMAIL = overseer email (who owns the work)
- created_by = BD_ACTOR (beads field)

Documented BD_ACTOR format convention in docs/identity.md with attribution model


Digest: mol-deacon-patrol

Patrol cycle 5: All quiet.


Digest: mol-deacon-patrol

Patrol cycle 19: All healthy


gt convoy merge: Consolidate multiple convoys into one

When work gets slung individually (e.g., due to gt swarm being broken), each issue gets its own convoy via auto-convoy. Need ability to merge these into a single tracking unit.

## Use Case

```bash
# Current state: 5 convoys for one logical swarm
gt convoy list
  1. hq-cv-6svfs: Work: gt mail reply
  2. hq-cv-2zdme: Work: gt mail search
  3. hq-cv-k3v6u: Work: gt mail archive/purge
  4. hq-cv-pc2ww: Work: gt mail mark/delete
  5. hq-cv-xdf66: Work: gt mail check

# Desired: merge into one
gt convoy merge hq-cv-xdf66 hq-cv-pc2ww hq-cv-k3v6u hq-cv-2zdme hq-cv-6svfs --title "Mail CLI Implementation"
# Result: single convoy tracking all 5 issues
```

## Implementation

- Take target convoy (first arg) or create new one
- Move tracked issues from source convoys to target
- Close/delete source convoys
- Update subscribers

## Alternative

Could also support:
```bash
gt convoy add hq-cv-xdf66 gt-d46.2 gt-d46.3 gt-d46.4 gt-d46.5
# Then manually close the orphaned convoys
```

But merge is cleaner UX.


Session ended: gt-gastown-crew-joe


Merge: gt-pnv61

branch: polecat/rictus-1767163044
target: main
source_issue: gt-pnv61
rig: gastown


Consolidate /handoff command to town-level only

Currently crew workers have duplicate /handoff skills because:
1. Town-level ~/gt/.claude/commands/handoff.md exists (Mayor-specific)
2. Each crew member has their own .claude/commands/handoff.md (generic)
3. Claude Code walks up directory tree and finds both

Fix:
- Update town-level handoff.md to use generic wording (works for any role)
- Remove crew-level copies in gastown and beads (they inherit town-level)
- Result: Single source of truth, no duplicate skills

Updated town-level handoff.md to generic wording, removed 9 crew-level copies (4 gastown, 5 beads). Now all agents inherit from ~/gt/.claude/commands/handoff.md


Session ended: gt-gastown-warboy


Merge: furiosa-mjw349y2

branch: polecat/furiosa-mjw349y2
target: main
source_issue: furiosa-mjw349y2
rig: gastown
agent_bead: gt-gastown-polecat-furiosa


Session ended: gt-gastown-crew-jack


Centralize session name construction patterns

Session name patterns like fmt.Sprintf("gt-%s-<role>", rigName) scattered across files:
- witness.go:287
- refinery.go:383
- start.go:275
- crew_helpers.go:68-70

Also: sessionToAgentID logic duplicated in sling.go:365-391 and handoff.go:374-402.

Suggestion: Create session_names.go with WitnessSessionName(), RefinerySessionName(), CrewSessionName() functions plus AgentIdentity type with FromSessionName()/ToSessionName() methods.


Digest: mol-deacon-patrol

Patrol 111: All healthy


Digest: mol-deacon-patrol

Patrol cycle 14: all healthy


BUG: Daemon heartbeat too slow (10 min) for stuck agent detection

The daemon's recoveryHeartbeatInterval is set to 10 MINUTES (daemon.go:134).

This means if an agent gets stuck, it can waste up to 10 minutes before Boot is spawned to triage.

Suggested fix: Reduce heartbeat to 2-3 minutes, or add a separate fast-path stuck-detection check.

Related: hq-nsx0g


Deacon patrol: Janitorial cleanup patterns

Patterns discovered during town cleanup that could be automated in Deacon's patrol step:

## Stale Issue Detection

1. **Stale in_progress issues** - Issues marked in_progress for >N days without activity
   - Example: 9 issues in_progress, some likely complete
   - Action: Report list, optionally nudge assignee

2. **Duplicate issues** - Same title with different IDs
   - Example: gt-87jz and gt-qp2w both 'mol-witness-patrol'
   - Action: Flag for human review

3. **Orphaned issues** - Referenced in commits but still open
   - bd doctor already detects: 'hq-65jk (commit f9e9034d)'
   - Action: Report, suggest closing

4. **Stale test messages** - Messages with 'test' in title older than N days
   - Example: gt-54kn, gt-1ero, gt-nriy, gt-wrw2, gt-pnu4, gt-g1ud, gt-b5sh
   - Action: Auto-close or flag

## File System Cleanup

5. **Log file accumulation** - daemon.log and rotated .log.gz files
   - Found 8+ log files across rigs
   - Action: Rotate, compress, delete old (>7 days)

6. **Beads directory size** - Town .beads/ is 65MB
   - Action: Monitor growth, alert on threshold

## Database Health

7. **Legacy database fingerprints** - Older DBs without fingerprint
   - bd doctor warns about this
   - Action: Report, suggest migration

## Cross-Reference Integrity

8. **Gastown↔Beads issue sync** - Issues filed in wrong project
   - Example: gt-8tmz.* describes Beads work but no bd-* issues
   - Action: Detect cross-references, verify both sides exist

## Implementation Notes

These could be added to mol-deacon-patrol.formula.yaml as cleanup substeps:
- Daily: Log rotation, stale issue report
- Weekly: Duplicate detection, orphan check
- Monthly: Size monitoring, archive old closed issues


BUG: gt sling updates bead status but doesn't create .hook file

gt sling updates the bead to status=hooked but doesn't actually create the .hook file in the polecat directory.

## Symptom
- gt sling reports success
- Bead shows status=hooked, assignee set
- Polecat's .hook file doesn't exist
- gt hook shows empty

## Root Cause
gt sling updates bead state via bd commands but never writes to polecats/<name>/.hook

## Fix
After updating bead status, write the bead ID to the .hook file:
```go
hookPath := filepath.Join(polecatDir, ".hook")
os.WriteFile(hookPath, []byte(beadID), 0644)
```

## Related
This is blocking gt-zqt4d dispatch to splendid.

Fixed identity format mismatch in buildAgentIdentity - now uses 3-part format (rig/polecats/name) matching session.AgentIdentity.Address()


Digest: mol-deacon-patrol

Cycle 13


Merge: slit-mjtj9dc8

branch: polecat/slit-mjtj9dc8
target: main
source_issue: slit-mjtj9dc8
rig: gastown


Missing failsafe: No circuit breaker for stuck agents

When an agent (Deacon, Witness, Refinery) gets stuck, there's no automatic recovery:

1. No timeout on individual steps - an agent can spin forever
2. No watchdog process checking agent health externally
3. No heartbeat/liveness check that triggers restart

Proposed solution:
- Add a watchdog that checks agent last_activity timestamps
- If no activity for >5 minutes, force cycle the agent
- This could be a simple cron job or daemon feature

This is defense-in-depth for the 'who watches the watchers' problem.

(Moved from hq-2wy2i)

Added circuit breaker for stuck agents: (1) Refineries now monitored/restarted by daemon like Witnesses (2) Agents marked 'dead' by checkStaleAgents() are now force-killed and restarted even if Claude process is alive


Digest: mol-deacon-patrol

Patrol cycle 55: All healthy


Digest: mol-deacon-patrol

Cycle 13: quiet


gt convoy create: Fix notify slot warning

When creating convoy with --notify, get warning:
```
⚠ Warning: couldn't set notify slot: exit status 1
```

But notification target still shows in status. Investigate slot setter.

Fixed: removed invalid slot set for convoy notify


Digest: mol-deacon-patrol

Patrol 7: All healthy


Digest: mol-deacon-patrol

Patrol 15: quiet


Digest: mol-deacon-patrol

Patrol 79: Routine


Digest: mol-deacon-patrol

Patrol 16: quick pass


Witness: Don't nuke polecats with open MRs or hooked work

Witness mass-murdered 11 polecats during cleanup:

Evidence from witness session:
```
gt polecat nuke gastown/slit gastown/capable
Worktrees nuked: 11 (cheedo, dag, dementus, furiosa×2, keeper, nux, rictus, slit, capable, toast)
```

Casualties:
- cheedo had open MR gt-t7g4b (branch not pushed, work lost)
- cheedo had hooked work gt-w0fqg (had to reassign)
- Unknown other work may have been lost

Pre-nuke checklist must verify:
1. No uncommitted/unpushed work
2. No open MRs awaiting merge
3. No work on hook (unless orphan recovery)
4. Issue is actually closed (not just 'done' signaled)

The witness is supposed to be a pit boss, not an executioner.

## Update: Instructions exist but were ignored

Witness CLAUDE.md HAS the pre-kill checklist:
```
Before killing ANY polecat session, verify:
[ ] 3. Check for unpushed commits
[ ] 4. Verify issue closed
[ ] 5. Verify PR submitted
```

Code for verification also exists on main (c28a0d5).

But witness still mass-murdered. Possible causes:
1. Context pressure (witness was at 4% - may have lost instructions)
2. Some aggressive prompt overriding safety checks
3. Agent non-compliance

Need: Enforcement mechanism, not just instructions.

Consolidated into gt-xqh3y - safety checks are part of the nuke-after-completion implementation


Session ended: gt-gastown-crew-jack

Bulk cleanup of stale session-ended events (gt-8tc1v)


Merge: ace-1767106056721

branch: polecat/ace-1767106056721
target: main
source_issue: ace-1767106056721
rig: gastown


Merge: mediocre-mk0vwdaf

branch: polecat/mediocre-mk0vwdaf
target: main
source_issue: mediocre-mk0vwdaf
rig: gastown
agent_bead: gt-gastown-polecat-gus
retry_count: 0
last_conflict_sha: null
conflict_task_id: null


Session ended: gt-gastown-crew-george


Session ended: gt-gastown-crew-jack


gt doctor: Check settings.json hook consistency across town

## Problem

Settings.json files in different locations can become out of sync, causing session_id
passthrough to fail for some roles but not others. This happened when:
- Town root was updated to use session-start.sh
- Rig root still had old 'gt prime' direct call
- Mayor/rig had mixed state

## Locations to Check

1. `~/gt/.claude/settings.json` (town root - Mayor uses this)
2. `~/gt/<rig>/.claude/settings.json` (rig root - polecats/crew use this)
3. `~/gt/<rig>/mayor/rig/.claude/settings.json` (mayor/rig - source of truth)

## Check Logic

For SessionStart and PreCompact hooks, verify all use `session-start.sh`:
- Must contain `bash ~/.claude/hooks/session-start.sh` (or full path)
- Warn if any use bare `gt prime` without session-start.sh wrapper

## Output

```
gt doctor
...
session-hooks: ✓ All settings.json files use session-start.sh
# or
session-hooks: ✗ ~/gt/gastown/.claude/settings.json uses bare 'gt prime' (missing session_id passthrough)
```

## Fix Suggestion

When inconsistency found, suggest:
```
Hint: Update SessionStart/PreCompact hooks to use 'bash ~/.claude/hooks/session-start.sh'
      This enables session_id passthrough for gt seance discovery.
```

## Related

- session-start.sh extracts session_id from Claude Code stdin JSON
- gt prime outputs [GAS TOWN] metadata line for seance
- gt seance discovers sessions from .events.jsonl

Added session-hooks doctor check to verify settings.json files use session-start.sh


Refactor: Type CleanupStatus instead of raw strings

witness/handlers.go uses raw strings for cleanup status:
- "clean"
- "has_uncommitted"
- "has_stash"
- "has_unpushed"

Create a CleanupStatus type:
```go
type CleanupStatus string

const (
    CleanupClean        CleanupStatus = "clean"
    CleanupUncommitted  CleanupStatus = "has_uncommitted"
    CleanupStash        CleanupStatus = "has_stash"
    CleanupUnpushed     CleanupStatus = "has_unpushed"
)

func (s CleanupStatus) IsSafe() bool
func (s CleanupStatus) RequiresRecovery() bool
```

Files:
- internal/witness/handlers.go:469-517
- internal/polecat/manager.go:121-150

Implemented CleanupStatus type with constants and helper methods


gt rig status: Show single-rig status summary

Add 'gt rig status <rig>' command to show focused status for a single rig.

## Use Case
When working on a specific rig, you want to see just that rig's status without the full town overview from 'gt status'.

## Proposed Output
```
$ gt rig status gastown

─── gastown/ ───────────────────────────────────────────

🦉 Witness: running
🏭 Refinery: running (MQ: 2 pending)

👷 Crew (5): 2 active, 3 idle
😺 Polecats (3): 1 working, 2 idle

Open issues: 45
In progress: 3
Ready: 12
```

## Implementation
- Add 'status' subcommand to internal/cmd/rig.go
- Reuse status rendering logic from gt status
- Filter to single rig
- Optionally add --verbose for full agent list

Already implemented - binary was outdated


Session ended: gt-gastown-crew-joe


Digest: mol-deacon-patrol

Patrol complete: gastown witness came online, town healthy


Add await-work and plugin-run steps to mol-refinery-patrol

## Context

The mol-refinery-patrol needs two additional steps:

### 1. await-work (Decision Point)

Not just 'wait for signal' but a crossroads:
- Check for pending signals (mail, nudge, human)
- Check plugin gates (any plugins need to run?)
- Check maintenance schedule (daily/weekly due?)
- Evaluate context and decide next action

Position: Between burn-or-loop and inbox-check (the loop point)

### 2. plugin-run (Like Deacon)

Refinery needs plugin support for:
- **Monitors**: Flag PRs touching sensitive code
- **Gates**: Block merges under certain conditions
- **Schedulers**: Reorder queue based on priority/urgency
- **Maintenance**: Weekly cleanup, audits, stats
- **Audits**: Log merge statistics, track patterns

Position: After queue-scan, before process-branch

## Implementation

Update RefineryPatrolMolecule() in builtin_molecules.go to include:
- await-work step with decision tree
- plugin-run step with gate types

## Related
- gt-7920 (original mol-refinery-patrol)
- docs/deacon-plugins.md (existing plugin model)


Merge: dag-1767074322940

attached_args: Code review this merge request

branch: polecat/dag-1767074322940
target: main
source_issue: dag-1767074322940
rig: gastown


Extract timing constants to constants package

Extract hardcoded timing values to named constants.

## Files to modify
- internal/constants/constants.go (add constants)
- internal/cmd/start.go (use constants)
- internal/tmux/tmux.go (use constants)

## Implementation
Add to constants.go:
```go
const (
    ShutdownNotifyDelay  = 500 * time.Millisecond
    ClaudeStartTimeout   = 15 * time.Second
    ShellReadyTimeout    = 5 * time.Second
    DefaultDebounceMs    = 100
    DefaultDisplayMs     = 5000
)
```

## Acceptance criteria
- [ ] 5 timing constants added to constants.go
- [ ] start.go uses ShutdownNotifyDelay, ClaudeStartTimeout, ShellReadyTimeout
- [ ] tmux.go uses DefaultDebounceMs, DefaultDisplayMs
- [ ] go build ./... passes
- [ ] go test ./... passes


Consolidate duplicate formatAge functions in feed TUI

formatConvoyAge in convoy.go is nearly identical to formatAge in view.go. Consolidate into one shared function.

Consolidated formatConvoyAge into formatAge - both now use 'just now' for < 1 minute


Bug: bd update --parent doesn't persist

The --parent flag on bd update reports success but doesn't persist the parent field.

**Confirmed behavior:**
```bash
bd update gt-9kp3t --parent=gt-d0jqp --no-daemon -v
# Output: ✓ Updated issue: gt-9kp3t

bd show gt-9kp3t --json | jq '.[0].parent'
# Returns: null

grep gt-9kp3t .beads/issues.jsonl | head -1 | jq '.parent'
# Returns: null (field not present)
```

**Root cause hypothesis:**
The update handler processes the --parent flag but either:
1. Doesn't write it to the Issue struct
2. Doesn't include it in the JSONL export
3. The field name doesn't match what export expects

**Files to check:**
- cmd/bd/show.go:638-640 (flag handling)
- cmd/bd/show.go:810-814 (reparenting logic)
- internal/rpc/server_issues_epics.go (RPC handler)
- internal/rpc/protocol.go (UpdateArgs.Parent field)

Related: bd-cj2e (the original feature implementation)


Test swarm epic


Digest: mol-deacon-patrol

Patrol 68: Routine


Instrument gt commands to appear in activity feed

## Architecture: Two-tier event system

```
┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│ gt commands │  │ bd mutations│  │ agent output│
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       └────────────────┼────────────────┘
                        ▼
              ~/gt/.events.jsonl (raw, audit)
                        │
              ┌─────────────────┐
              │  Feed daemon    │ ← goroutine in gt daemon
              │  - filters      │
              │  - aggregates   │
              │  - AI curates   │
              └────────┬────────┘
                       ▼
              ~/gt/.feed.jsonl (curated, user-facing)
```

## Event format

```json
{
  "ts": "2025-12-28T10:30:15Z",
  "source": "gt",
  "type": "sling",
  "actor": "mayor",
  "payload": {"bead": "gt-xyz", "target": "gastown/Toast"},
  "visibility": "feed"
}
```

Visibility: `audit` | `feed` | `both`

## Implementation phases

1. **Event logging** - gt commands write to ~/gt/.events.jsonl
2. **Feed daemon** - goroutine in gt daemon tails events, writes curated feed
3. **Feed readers** - gt feed, gt dashboard read from .feed.jsonl
4. **AI curation** - future: haiku summarization for complex activity

Part of epic gt-u7dxq

Instrumented gt commands (nudge, unsling, up, down, stop, polecat_spawn) to emit events to ~/gt/.events.jsonl for the activity feed


Digest: mol-deacon-patrol

Patrol 159: All healthy


Digest: mol-deacon-patrol

Patrol 9: All clear


Digest: mol-deacon-patrol

P14: stable


Tmux session scanner for running agents

Scan tmux for running Gas Town agent sessions.

## Deliverables

1. Function: ScanRunningSessions() -> []AgentAddr
   - Parse tmux list-sessions
   - Filter for Gas Town session naming pattern
   - Return addresses of running agents

2. Function: ScanRigSessions(rig) -> []AgentAddr
   - Filter running sessions by rig prefix

3. Alias resolution:
   - #rig/gastown → running sessions in gastown
   - #town → all running Gas Town sessions
   - #witnesses → running witness sessions

## Location
internal/discovery/tmux.go

## Acceptance
- Scanner finds running sessions
- Correctly parses session names to addresses
- Handles no-sessions case gracefully


Merge: keeper-mjtn0wgm

branch: polecat/keeper-mjtn0wgm
target: main
source_issue: keeper-mjtn0wgm
rig: gastown


Digest: mol-deacon-patrol

Patrol 122: All healthy, no callbacks, 3 convoys tracking


Merge: bullet-mk02nnn6

branch: polecat/bullet-mk02nnn6
target: main
source_issue: bullet-mk02nnn6
rig: gastown
agent_bead: gt-gastown-polecat-bullet
retry_count: 0
last_conflict_sha: null
conflict_task_id: null


Merge: gt-svdsy

branch: polecat/capable-mjtltnm5
target: main
source_issue: gt-svdsy
rig: gastown


Digest: mol-deacon-patrol

Patrol 259 complete: no incidents


Merge: vuvalini-mk0bkcrq

branch: polecat/vuvalini-mk0bkcrq
target: main
source_issue: vuvalini-mk0bkcrq
rig: gastown
agent_bead: gt-gastown-polecat-vuvalini
retry_count: 0
last_conflict_sha: null
conflict_task_id: null


Digest: mol-deacon-patrol

Patrol complete: beads witness+refinery came online, both rigs healthy


Merge: dementus-mjw46vz4

branch: polecat/dementus-mjw46vz4
target: main
source_issue: dementus-mjw46vz4
rig: gastown
agent_bead: gt-gastown-polecat-dementus


Digest: mol-deacon-patrol

Patrol complete: all healthy


Merge: capable-mjxen9vb

branch: polecat/capable-mjxen9vb
target: main
source_issue: capable-mjxen9vb
rig: gastown
agent_bead: gt-gastown-polecat-capable


Formula schema: Define formula.toml structure

Define the formula file format in .beads/formulas/:
- formula name and description
- input parameters (pr, branch, files)
- legs array with name, focus, output
- synthesis config
- dependencies between legs

Reference: gt-ubqeg.1 epic description for schema ideas.
Create example formula: code-review.formula.toml

Schema defined in internal/formula/types.go (a395b4e)


Digest: mol-deacon-patrol

Patrol cycle 4: all healthy


Session ended: gt-gastown-crew-gus


Beads daemon cache stale - hook queries return wrong data

Root cause: Beads daemon has stale data. Multiple bd daemons running (4 instances). When gt hook status queries via daemon, it gets old hooked bead. When queried with --no-daemon, correct bead is shown. This is a daemon cache coherence issue, not an ID resolution bug. Workaround: Use bd --no-daemon commands or restart daemons.

Fixed: Hook discovery now reads from database column (hook_bead) instead of parsing stale description field


Merge: gt-svdsy


Session pre-shutdown checks

Session stop should verify clean state before killing, like PGT.

## Pre-Shutdown Checks

### 1. Git Working Tree Clean
```go
func checkGitClean(clonePath string) error {
    // git status --porcelain
    // Fail if any output
}
```

### 2. All Commits Pushed
```go
func checkCommitsPushed(clonePath string) error {
    // git log origin/HEAD..HEAD
    // Fail if any unpushed commits
}
```

### 3. Assigned Issues Handled
```go
func checkIssuesHandled(polecat *Polecat) error {
    // If polecat.Issue != "", check if closed or reassigned
}
```

### 4. Beads Synced
```go
func checkBeadsSynced(clonePath string) error {
    // bd sync --status in clone directory
}
```

## Behavior on Failure
1. First attempt: Nudge worker to fix
2. Retry up to 3 times with delay
3. After retries: Escalate to Witness/Mayor

## Integration
Modify internal/session/manager.go Stop():
```go
func (m *Manager) Stop(polecat string, force bool) error {
    if !force {
        if err := m.runPreShutdownChecks(polecat); err != nil {
            return fmt.Errorf("pre-shutdown checks failed: %w", err)
        }
    }
    // existing stop logic
}
```

## Flags
- --force: Skip checks
- --grace-period N: Time to wait for fixes

## Dependencies
- Ties into gt-69l (hook system) - can be hook-based
- Ties into gt-f8v (Witness pre-kill verification)

## Acceptance Criteria
- [ ] Stop fails if uncommitted changes (without --force)
- [ ] Stop fails if unpushed commits
- [ ] Clear error messages with fix instructions
- [ ] --force bypasses all checks


Digest: mol-deacon-patrol

Patrol 107: All healthy


Digest: mol-deacon-patrol

Patrol 141 complete: All agents healthy, no incidents, no orphans


Digest: mol-deacon-patrol

Patrol 145 complete: All healthy


Migrate startup paths from SessionBeacon to StartupNudge

## Problem
StartupNudge function exists (gt-bgfqy) but no code uses it.
All startup paths still call session.SessionBeacon() + 'gt prime' separately.

SessionBeacon format: [GAS TOWN] address • molID • timestamp
StartupNudge format: [GAS TOWN] recipient <- sender • timestamp • topic

The key difference is SENDER - needed for seance discovery ('who started this session?').

## Files to Update
- internal/cmd/crew_lifecycle.go (lines 354-371, 525-538)
- internal/cmd/start.go (lines 757, 805-810, 946-949)
- internal/cmd/deacon.go (line 307)
- internal/cmd/witness.go (line 350)
- internal/cmd/up.go (lines 288, 335, 559, 660)
- internal/cmd/mayor.go (line 151)
- internal/session/manager.go (lines 205, 215)

## Pattern
Replace:
  beacon := session.SessionBeacon(address, molID)
  t.NudgeSession(sessionID, beacon)
  t.NudgeSession(sessionID, "gt prime")

With:
  cfg := session.StartupNudgeConfig{Recipient: address, Sender: sender, Topic: topic, MolID: molID}
  session.StartupNudge(t, sessionID, cfg)
  // Note: StartupNudge is the session title, still need separate 'gt prime' for priming

## Related
- gt-85whr (parent epic)
- gt-j0546 (did RuntimeConfig refactor, not StartupNudge)

Migrated all 12 startup paths from SessionBeacon to StartupNudge. All startup paths now include sender information for better seance discovery. Files updated: crew_lifecycle.go (3), start.go (2), deacon.go (1), witness.go (1), up.go (4), mayor.go (1), session/manager.go (1). Build passes, all tests pass.


Day 3.5: Test polecat → refinery → merged flow

Integration test:
1. Spawn polecat with work
2. Polecat completes (updates state, closes bead)
3. Witness detects completion
4. Witness sends MERGE_READY
5. Refinery processes and merges
6. MERGED signal received

Parent: gt-hwka3


HOP: Skill vectors on work items

Add skill embeddings to work items for capability-based matching. See ~/ai/stevey-gastown/hop/CONTEXT.md. Post-v0.1 work.


Digest: mol-deacon-patrol

Patrol #17


Session ended: gt-gastown-crew-jack


Add gt seance command for predecessor discovery

## Purpose

**"Where did you put the stuff you left for me?"** - The #1 question in any handoff.

Seance lets you literally talk to your predecessor. Not parse their logs - commune with them.

## The Insight

Claude Code can resume sessions with full context. A seance spawns a Claude subprocess that resumes a predecessor session, letting you ask questions directly:
- "Why did you make this decision?"
- "Where were you stuck?"
- "What did you try that did not work?"

## Architecture

```
gt seance --talk <session-id>
    │
    └─▶ claude --fork-session --resume <id>
        │
        └─▶ Spawns in sandbox namespace (ghost-joe)
            │
            └─▶ Communication via tmux
                "Where is the config you mentioned?"
                → Predecessor has full context, answers
```

**Key flags:**
- `--fork-session` creates new session ID (read-only seance, no grave disturbance)
- `--resume <id>` loads predecessor context

## Discovery

Sessions are discoverable via:
1. **Beacon in transcript** - `[GAS TOWN] role • topic • timestamp` searchable via `/resume`
2. **SessionStart events** - Hook emits to `~/gt/.events.jsonl` (our data, ZFC-compliant)
3. **Claude /resume search** - Native filtering

## Usage

```bash
gt seance                     # List discoverable sessions
gt seance --role crew         # Filter by role
gt seance --rig gastown       # Filter by rig
gt seance --recent 10         # Last N sessions

gt seance --talk <id>         # THE SEANCE - talk to predecessor
gt seance --talk <id> -p "Where is X?"  # One-shot question
```

## ZFC Compliance

- **No parsing Claude internals** - Use Claude CLI to resume
- **Agent discovers** - Claude reads session, interprets, responds
- **Events are ours** - SessionStart hook writes to our event stream

## Beacon Format

```
[GAS TOWN] gastown/crew/joe <- deacon • 2025-12-30T15:42 • assigned:gt-xyz
```

Searchable via `claude --resume "GAS TOWN"` or `claude --resume "gastown/crew/joe"`

Implemented ZFC-compliant seance with --talk flag for literal predecessor conversations


BUG: gt convoy create with count argument doesn't set convoy description

Root cause: 'gt convoy create' usage is 'create <name> [issues...]' but we invoked 'gt convoy create 1 beads' expecting polecat count semantics.

The '1' became the convoy name, 'beads' was treated as an issue ID (failed to track).

Options:
1. Add --count flag for polecat spawning semantics
2. When issues are added later via 'convoy add', update convoy title if current title is just a number
3. If first arg looks like an issue ID (has prefix), auto-generate name from issue title

For now, users must use: 'gt convoy create "Descriptive name" issue-id' not 'gt convoy create <count> <rig>'

Fixed: convoy create now auto-detects issue IDs and generates name from issue title


Digest: mol-deacon-patrol

Patrol 14: All healthy.


QUICK FIX: Make gt crew start use NudgeSession instead of SendKeys

## The Immediate Problem
`gt crew start gus` fails to deliver the GUPP nudge because:
1. Uses `t.SendKeys(sessionID, "gt prime")` which has the Enter-concatenation bug
2. Should use `t.NudgeSession()` which solves this reliably

## Quick Fix
In start.go, change line ~809:
```go
// Before
if err := t.SendKeys(sessionID, "gt prime"); err != nil {

// After
if err := t.NudgeSession(sessionID, "gt prime"); err != nil {
```

Same fix needed in:
- start.go:756 (restart path)
- crew_lifecycle.go:354
- crew_lifecycle.go:525

## Why This Is P0
Cold starts of crew workers are broken. This blocks basic usage.

## Note
This is a stopgap. The full solution is the StartupNudge refactor (gt-bgfqy).

Fixed: Replaced SendKeys with NudgeSession for gt prime in start.go and crew_lifecycle.go


Session ended: gt-gastown-wasteland


Digest: mol-deacon-patrol

Patrol cycle 5: All 6 agents healthy


Default crew name should be something better than 'main'

When creating a new rig, the default crew worker name is 'main'. This is confusing since:

1. 'main' is also the git branch name
2. It doesn't convey that this is the user's personal workspace
3. Crew names should feel more personal/distinct

Consider alternatives like:
- 'home' - the user's home base
- 'desk' - their personal desk
- 'joe/max/etc' - actual name-based defaults
- Let user pick during rig init


Day 3.4: Refinery processes MERGE_READY mail

Refinery patrol handles merge requests:
1. Check inbox for MERGE_READY messages
2. For each, verify branch exists
3. Run merge checks (tests, lint)
4. If passing, merge to main
5. Send MERGED signal back

Parent: gt-hwka3


Digest: mol-deacon-patrol

Patrol complete: no callbacks, all agents healthy, no orphans, doctor 24/28


Witness cleanup leaves worktree behind

When witness processes POLECAT_DONE:
- Session is killed ✓
- Work is merged ✓
- Worktree is NOT removed ✗

Dementus example: session 'not running', but /Users/stevey/gt/gastown/polecats/dementus still exists.

Expected: gt polecat nuke equivalent - full cleanup including worktree deletion.

Fixed: HandleMerged now calls witness.AutoNukeIfClean to cleanup worktrees after merge


Digest: mol-deacon-patrol

Patrol 11: 3 rigs healthy


Witness patrol wisp not respawning after completion

The gastown witness session is alive but idle. Its patrol wisp completed and nothing spawned a new one. 50 POLECAT_DONE messages have piled up unprocessed.

Root cause: Patrol wisp pattern assumes continuous loops, but when a wisp ends (completed all steps, context limit, etc), nothing triggers respawn. The witness just sits with empty hook.

Evidence:
- Session exists (Jan 2nd session ID)
- gt hook shows nothing hooked
- gt status shows 'running [bead: dead]' - session alive, wisp dead
- 50 unread mail messages piling up

Proposed fix:
1. Witness startup hook should auto-spawn patrol wisp if hook is empty
2. Or: patrol wisp final step should spawn successor wisp before closing
3. Or: external health check (Deacon?) spawns new wisp when witness hook is empty

Fixed by adding explicit wisp respawn instructions to patrol work loops and formula. Agents now know to run 'bd mol wisp mol-<role>-patrol' to continue or 'gt handoff' to exit.


Digest: mol-deacon-patrol

Patrol 11: all healthy


Digest: mol-deacon-patrol

Patrol 35: all quiet


Digest: mol-deacon-patrol

Patrol 14


Refactor slash command provisioning to town-level only

Current architecture provisions .claude/commands/ per-workspace (crew/polecat), causing duplicate skills when Claude walks up the directory tree.

New architecture:
1. gt install: Create ~/gt/.claude/commands/ with handoff.md (and future commands)
2. gt crew add / gt polecat spawn: Remove ProvisionCommands() calls - inherit from town
3. bd doctor: Check town-level .claude/commands/ exists instead of per-workspace

Files to modify:
- internal/cmd/install.go - add town-level commands provisioning
- internal/crew/manager.go - remove ProvisionCommands call
- internal/polecat/manager.go - remove ProvisionCommands calls (2 places)
- internal/doctor/commands_check.go - check town-level instead of per-workspace
- internal/templates/templates.go - may need ProvisionTownCommands() variant

Refactored slash command provisioning to town-level only:
- gt install now creates ~/gt/.claude/commands/ with all commands
- Removed ProvisionCommands calls from crew/manager.go and polecat/manager.go
- Updated bd doctor to check town-level commands instead of per-workspace
All agents now inherit commands via Claude directory traversal


Digest: mol-deacon-patrol

Patrol 133: All healthy


Digest: mol-deacon-patrol

Patrol cycle 11: All healthy


Digest: mol-deacon-patrol

Patrol cycle 20: all healthy, handoff


Session ended: gt-gastown-crew-george


Digest: mol-deacon-patrol

Patrol 121: All agents healthy, 6 health pings sent, no callbacks, no orphans, 1 idle dog, inbox clean


Digest: mol-deacon-patrol

Cycle 298: All healthy, refinery processing


Merge: gt-fpv2p

branch: polecat/capable-mjw47ef9
target: main
source_issue: gt-fpv2p
rig: gastown
agent_bead: gt-gastown-polecat-capable


Merge: dementus-1767146229184

branch: polecat/dementus-1767146229184
target: main
source_issue: dementus-1767146229184
rig: gastown


Digest: mol-deacon-patrol

Patrol 13: All clear


Merge: nux-1767079896198

branch: polecat/nux-1767079896198
target: main
source_issue: nux-1767079896198
rig: gastown


Digest: mol-deacon-patrol

Cycle 20 - handoff threshold


Digest: mol-deacon-patrol

Patrol 100: All healthy - milestone!


Merge: slit-mjvtz18y

branch: polecat/slit-mjvtz18y
target: main
source_issue: slit-mjvtz18y
rig: gastown
agent_bead: gt-gastown-polecat-slit


Session: gt-gastown-test completed gt-f7jxr


Digest: mol-deacon-patrol

Patrol 13: All healthy


Merge: nux-1767075895486

branch: polecat/nux-1767075895486
target: main
source_issue: nux-1767075895486
rig: gastown


Link MR bead to agent bead for traceability

MR bead and agent bead are separate with no cross-reference.

## Problem
- MR bead tracks: branch, target, source_issue
- Agent bead tracks: state, hook_bead, cleanup_status
- No link between them

When something goes wrong, hard to trace which polecat created which MR.

## Fix
1. MR bead should include agent_bead field
2. Agent bead should include active_mr field
3. On gt done: update both to reference each other
4. On merge: clear both references

## Benefits
- Traceable: given MR, find polecat
- Traceable: given polecat, find MR
- Orphan detection: MR without agent = stale

Implemented bidirectional cross-references between MR beads (agent_bead field) and agent beads (active_mr field). gt done now sets both, refinery clears on merge.


Session ended: gt-gastown-rictus


Agent Startup Consolidation: Unified nudge, seance metadata, LLM-agnostic

## Problem

Agent startup nudge logic is scattered across 6+ files with inconsistent behavior:
- start.go, crew_lifecycle.go, crew_at.go, handoff.go, deacon.go, witness.go

Some paths work (handoff), others fail (crew start). The core issues:

1. **tmux send-keys Enter issue**: `send-keys "foo" Enter` concatenates, doesnt send Enter separately
2. **All sessions look identical**: First message is "gt prime" everywhere, `/resume` shows duplicate-looking sessions
3. **No seance metadata**: Cant differentiate predecessors by sender, role, timestamp, topic
4. **Hardcoded to Claude**: `claude --dangerously-skip-permissions` everywhere

## Solution

1. **Unified StartupNudge function** - All paths call one function with rich metadata
2. **Seance-aware nudge format** - First message contains: sender, recipient, timestamp, topic
3. **Runtime configuration** - LLM command/args in config, not hardcoded
4. **gt seance command** - Find and resume predecessor sessions

## Nudge Format

The startup nudge becomes the session title in `/resume`:
```
[GAS TOWN] gastown/crew/gus ← mayor • 2025-12-30T15:42 • cold-start
```

Components:
- Address: gastown/crew/gus (recipient identity)
- Sender: mayor, deacon, witness, self (for handoff)
- Timestamp: When session started
- Topic: mol-id, "cold-start", "handoff", etc.

## Key Insight

The GUPP nudge content doesnt matter for triggering propulsion - agent already read CLAUDE.md and hooks ran. What matters is:
1. The nudge arrives reliably (NudgeSession solves this)
2. The nudge creates a unique, identifiable session title

## Related

- gt-p2o6s: Extend gt nudge to support channel:name syntax
- gt-9guv2: Messaging Channels epic
- Session beacon already exists but is for identity, not session title

All success criteria met: (1) All agent startups use unified StartupNudge (gt-7pp3l), (2) Runtime commands from config (gt-j0546), (3) Sessions identifiable in /resume by role, sender, time, topic, (4) gt seance works for predecessor discovery (gt-7qvd7). Related Phase 1 tasks (gt-bgfqy, gt-dc2fs) also complete.


Merge: slit-mjxaptzz

branch: polecat/slit-mjxaptzz
target: main
source_issue: slit-mjxaptzz
rig: gastown
agent_bead: gt-gastown-polecat-slit


Merge: capable-1767146233256

branch: polecat/capable-1767146233256
target: main
source_issue: capable-1767146233256
rig: gastown


gt hook: Support viewing other agents' hooks

Currently `gt hook` only shows your own hook. Would be useful to query any agent's hook:

```
gt hook show gastown/polecats/nux    # What's nux working on?
gt hook show gastown/witness         # What's the witness hooked to?
```

**Use cases**:
- Mayor checking what polecats are working on
- Witness checking polecat status
- Debugging coordination issues
- Quick status overview

**Compact output** (one line):
```
gastown/polecats/nux: gt-abc123 'Fix the widget bug' [in_progress]
```

Filed by: gastown/polecats/nux (agent UX feedback)

Implemented gt hook show <agent> with compact one-line output format


Digest: mol-deacon-patrol

Patrol cycle 66: All healthy


Digest: mol-deacon-patrol

Patrol 36: rictus removed from rig


Digest: mol-deacon-patrol

Patrol 260 complete: final cycle before handoff


Merge: dementus-1767079823043

branch: polecat/dementus-1767079823043
target: main
source_issue: dementus-1767079823043
rig: gastown


Session ended: gt-gastown-crew-joe


Add compaction recovery protocol to role templates

After context compaction (summarization), agents may be disoriented.

Add guidance to recognize and recover:
- 'If you feel you've lost earlier context or are disoriented, run: gt prime'
- 'This re-injects your role, current work, and pending messages'
- 'It's safe to prime multiple times - it just outputs context'

This is the fallback path when self-check is missed and compaction happens.
Compaction isn't catastrophic if priming is robust.


Digest: mol-deacon-patrol

Patrol #10: All healthy


Review: Witness patrol template changes (gt-h1n5)

Polecat rictus implemented significant witness.md.tmpl changes: banners, wisp-based execution, handoff bead attachment, loop-or-exit, patrol summary. Review for correctness and consistency with deacon pattern.


Digest: mol-deacon-patrol

Patrol 12: Routine


Session ended: gt-gastown-nux


Merge: toast-1767074978542

branch: polecat/toast-1767074978542
target: main
source_issue: toast-1767074978542
rig: gastown

Branch no longer exists on remote - already merged or cleaned up


Digest: mol-deacon-patrol

Patrol 24: All quiet


CLI: plugin commands (list, status)

Add gt plugins <rig> to list plugins and gt plugin status <name> to check plugin state. Simple directory scan of <rig>/plugins/.


Polecats should spawn with auto-accept mode enabled

During swarm bd-784c, polecats (Toast, Nux) were spawned without --dangerously-skip-permissions or equivalent auto-accept mode.

**Problem:**
Every edit, bash command, and tool use required manual confirmation via tmux send-keys. This defeats the purpose of autonomous polecat operation.

**Expected:**
Polecats in a swarm should run with permissions bypassed so they can work autonomously.

**Suggestion:**
- gt sling should pass --dangerously-skip-permissions by default for polecats
- Or polecats should have a .claude/settings.local.json pre-configured for auto-accept

Main fix (--dangerously-skip-permissions for polecat sessions) was already in place since ec29ca07 (Dec 17). Fixed naked mode help text to also show the flag. All spawn paths verified: session.Manager.Start(), swarm spawning, and manual naked mode now all use --dangerously-skip-permissions.


Digest: mol-deacon-patrol

Patrol cycle 42: All agents healthy, no incidents


DRY: Duplicate bd command execution pattern in mail package

Multiple functions in mailbox.go and router.go repeat the same pattern for executing bd commands:

```go
cmd := exec.Command("bd", args...)
cmd.Dir = workDir
cmd.Env = append(cmd.Environ(), "BEADS_DIR="+beadsDir)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
    errMsg := strings.TrimSpace(stderr.String())
    // error handling...
}
```

This appears in:
- mailbox.go:171-211 (queryMessages)
- mailbox.go:283-314 (getFromDir)
- mailbox.go:343-368 (closeInDir)
- mailbox.go:399-419 (markUnreadBeads)
- router.go:448-486 (queryAgents)
- router.go:625-649 (sendToSingle)
- And more...

Extract a helper like:

```go
type bdResult struct {
    stdout []byte
    stderr string
}

func (m *Mailbox) runBdCommand(args []string, beadsDir string) (*bdResult, error)
```

Severity: medium

Extracted runBdCommand helper to bd.go, refactored mailbox.go and router.go to use it


Merge: max-mk02mxj6

branch: polecat/max-mk02mxj6
target: main
source_issue: max-mk02mxj6
rig: gastown
agent_bead: gt-gastown-polecat-max
retry_count: 0
last_conflict_sha: null
conflict_task_id: null


Digest: mol-deacon-patrol

Patrol cycle 3: All 6 agents healthy, no incidents


Digest: mol-deacon-patrol

P12: stable


Session ended: gt-gastown-crew-george

Bulk cleanup of stale session-ended events (gt-8tc1v)


Comprehensive help text and examples

Improve help text with examples and cross-references.

## Improvements

### 1. Examples Section
Add to Long description:
```go
var spawnCmd = &cobra.Command{
    Long: `Spawn a polecat with work assignment.

Examples:
  gt spawn gastown/Toast --issue gt-abc
  gt spawn gastown --issue gt-def          # auto-select polecat
  gt spawn gastown/Nux -m "Fix the tests"  # free-form task`,
}
```

### 2. Cross-References
Reference related commands:
```
See also:
  gt polecat list    List available polecats
  gt session attach  Attach to spawned session
```

### 3. Flag Descriptions
More detail on flags:
```go
cmd.Flags().StringVar(&issue, "issue", "",
    "Beads issue ID to assign. The polecat will work on this issue.")
```

### 4. Common Workflows
Add workflow docs to gt --help:
```
Common Workflows:
  Start a swarm:
    gt swarm preflight
    gt swarm create gastown --epic gt-abc --worker Toast --worker Nux --start
    gt refinery start gastown

  Check status:
    gt status
    gt swarm status <id>
```

## Files to Update
All internal/cmd/*.go files

## Acceptance Criteria
- [ ] All commands have Examples
- [ ] Related commands cross-referenced
- [ ] Flags have detailed descriptions
- [ ] Root help shows workflows


Digest: mol-deacon-patrol

Patrol cycle 38: All healthy, no incidents


Digest: mol-deacon-patrol

Patrol 6: Routine


Update Deacon template to emphasize staying in ~/gt/deacon directory

The Deacon template should instruct the agent to stay in ~/gt/deacon/ as much as possible, and always return there after any excursion. This prevents issues with identity detection (mail, mol status) which depend on cwd.


test-new-swarm-1767338498


Merge: furiosa-mjwnxrkd

branch: polecat/furiosa-mjwnxrkd
target: main
source_issue: furiosa-mjwnxrkd
rig: gastown
agent_bead: gt-gastown-polecat-furiosa


Stranded convoy detection + feeding via dog dispatch

## Problem
Convoys can become **stranded** - they have ready work but no polecats working on it. Work is waiting for a ride.

## Definition: Stranded Convoy
```
convoy is open
  AND has tracked issues where:
    - status = open (not in_progress, not closed)
    - not blocked (dependencies met)
    - no assignee (or assignee session is dead)
```

If all open issues are blocked or have live polecats → convoy is fine (in-flight).
If ready issues exist with nobody working them → convoy is stranded.

## Solution: Deacon patrol + dog dispatch

**Detection (Deacon patrol step):**
Check open convoys for stranded state.

**Dispatch:**
```bash
gt sling mol-convoy-feed deacon/dogs --var convoy=<convoy-id>
```

**mol-convoy-feed formula (single pass):**
1. Load convoy, find ready issues (not blocked, no assignee)
2. Check idle polecat capacity
3. Dispatch min(ready_issues, idle_polecats)
4. Exit

Dog doesn't babysit. Just feeds and leaves. Deacon's patrol loop handles repeated checking - if still stranded next cycle, dispatches dog again.

## Benefit
Stateless, batch-oriented. Convoys become fire-and-forget.

(Moved from hq-1h2to)

Implemented stranded convoy detection with gt convoy stranded command, mol-convoy-feed formula, and deacon patrol step


Digest: mol-deacon-patrol

Patrol cycle 49: All healthy


Plugin/molecule catalog integration design

Two currently independent systems:
1. Disk-based plugins (~~/gt/plugins/) - Deacon patrol extensions
2. Molecule catalog (protos in beads) - bd pour/wisp/bond

Investigate integration points:
- User-contributed molecules bonded to catalog
- Dynamic molecule attachment during execution (e.g., 'attach security sniffer because we noticed condition X')
- How plugins could contribute protos
- How protos could trigger plugins

Not blocking launch. Future design investigation.


Digest: mol-deacon-patrol

Cycle 18


Enhance Mayor CLAUDE.md with GGT milestone tracking

Update ~/ai/CLAUDE.md (Mayor startup context) with:

1. GGT Self-Hosting Milestone section
   - Track progress toward gt replacing town
   - Key blockers: gt-h5n (merge queue), gt-974 (daemon)
   - What "self-hosting" means (GGT working on itself)

2. Recent Architecture Decisions
   - Engineer = role, Refinery = place
   - Merge queue in Beads (not branch discovery)
   - Session restart protocol

3. Transition Plan
   - Current: use town commands (PGT)
   - Target: use gt commands (GGT)
   - Switch criteria: gt-b1g closed

Keep it concise - Mayor context should be quick to scan.


Session ended: gt-gastown-crew-gus


Digest: mol-deacon-patrol

Patrol cycle 3: healthy, 2 acks


Digest: mol-deacon-patrol

Patrol cycle 6: All agents healthy


Digest: mol-deacon-patrol

Patrol cycle 10: All 3 rigs healthy. No incidents.


Stale Session ended events accumulating in beads

Large number of 'Session ended' event beads accumulating without being processed/closed.

Evidence: bd list shows 20+ open 'Session ended: gt-gastown-*' events.

These should either:
1. Be processed by witness patrol and closed
2. Not be created as beads (use mail instead)
3. Have automatic expiry/cleanup

Related to gt-7vdqv (witness patrol not running).

Questions:
- Are these events useful for audit trail?
- Should they auto-close after acknowledgment?
- Should session events be mail instead of beads?

Resolved. The blocker (gt-7vdqv: witness patrol not respawning) was fixed. Witness patrol now processes session events properly - verified 10+ closed events in history. Closed the one remaining stale event (gt-lhaec). Design assessment: session events should remain as beads (audit trail with cost data, session info). No design change needed.


Define meta-formula YAML schema

Define the YAML schema for meta-formulas that generate multiple formulas.

## Proposed Schema

    type: meta-formula
    name: multi-lang-shiny
    generates:
      for-each:
        var: lang
        values: [go, python, typescript]
      formula:
        name: "shiny-{lang}"
        extends: base-shiny
        vars:
          language: "{lang}"

## Key Decisions

- Use "generates" key to distinguish from regular formulas
- for-each at generation level (not runtime)
- Output is N formula files or in-memory formulas

## Deliverables

1. Add MetaFormula type to types.go
2. Add ForEachGenerate struct
3. Document schema in comments


Implement meta-formula generator in parser

Implement the generator that expands meta-formulas into concrete formulas.

## Implementation

1. Detect type: meta-formula in parser
2. Evaluate for-each values
3. For each value, instantiate the formula template
4. Substitute {var} placeholders with current value
5. Return list of generated Formula structs

## API

    func (p *Parser) ExpandMetaFormula(mf *MetaFormula) ([]*Formula, error)

## Edge Cases

- Empty values list: return empty slice
- Duplicate generated names: error
- Nested meta-formulas: error (not supported initially)


Add multi-lang-shiny example and tests

Create example meta-formula and test suite.

## Example

Create .beads/formulas/multi-lang-shiny.meta.yaml:
- Generates shiny-go, shiny-python, shiny-typescript
- Each extends base-shiny with language-specific vars

## Tests

1. Test basic expansion (3 languages -> 3 formulas)
2. Test variable substitution in names and content
3. Test error on duplicate generated names
4. Test empty values list
5. Integration: cook a generated formula


Design org formula hierarchy pattern

Design how organizational formula hierarchies work.

## Pattern

    company-base.formula.yaml (org-wide policies)
      └── team-frontend.formula.yaml (extends company-base)
           └── project-dashboard.formula.yaml (extends team-frontend)

## Key Questions

1. How are org formulas discovered? (search path? explicit import?)
2. How do aspects merge across levels?
3. Can child override parent aspects?
4. How to handle aspect conflicts?

## Proposed Design

- Formulas use extends: just like today
- Aspect lists merge (child appends to parent)
- Child can exclude parent aspects via "exclude-aspects: [name]"
- Search path: local -> rig -> org (configurable)

## Deliverables

1. Document pattern in docs/formula-inheritance.md
2. Define conflict resolution rules
3. Identify implementation tasks


Implement aspect merging across formula chain

Implement merging of aspects when formulas extend each other.

## Current State

extends: already works for steps and vars.
Aspects need similar merge behavior.

## Implementation

1. In Resolve(), collect aspects from entire extends chain
2. Merge lists: parent aspects first, then child
3. Support exclude-aspects field to remove inherited aspects
4. Dedupe by aspect name (child wins on conflict)

## Files

- internal/formula/parser.go: Resolve()
- internal/formula/types.go: add ExcludeAspects field

## Tests

1. Child inherits parent aspects
2. Child adds additional aspects
3. Child excludes parent aspect
4. Three-level chain merges correctly


Add org formula examples and tests

Create example organizational formula hierarchy and tests.

## Examples

Create in .beads/formulas/:
- acme-base.formula.yaml: Company-wide (security-audit aspect)
- acme-frontend.formula.yaml: extends acme-base, adds accessibility-audit
- acme-backend.formula.yaml: extends acme-base, adds sql-injection-scan

## Tests

1. Three-level inheritance resolves correctly
2. Aspect merge order is correct
3. exclude-aspects removes parent aspect
4. Cooking org formula produces expected steps
5. Error on missing parent formula


Formula error handling and recovery

Specify how cooking errors are reported and recovered:
- What happens when extends chain is broken (missing formula)?
- What happens when aspect pointcut matches nothing?
- What happens when expansion produces invalid structure?
- Graceful degradation vs fail-fast semantics
- Error metadata in cooked protos

Deferred: post-launch polish


Formula versioning and migration strategy

Define how formula versions are managed:
- What does the version field mean? Semver? Integer?
- When a formula changes, what happens to existing protos?
- Do protos track their source formula version?
- Migration path: old proto + new formula = ?
- Backwards compatibility guarantees (if any)

Deferred: post-launch polish


Proto debugging and inspection tools

Proto debugging and inspection tools.

With ephemeral protos (gt-4v1eo), this becomes:
- `bd cook <formula> --dry-run` - preview cooked output without persisting
- `bd cook <formula> --json` - output proto as JSON for inspection
- `bd cook <formula> --graph` - visualize step DAG

No persistent proto beads to inspect - formulas are the source of truth.


Formula validation specification

Complete validation rules for formulas:
- Required vs optional fields
- Valid step ID patterns
- Valid pointcut glob syntax
- Variable reference validation
- Compose rule ordering constraints
- Type-specific validation (workflow vs expansion vs aspect)

Deferred: post-launch polish


Support nested expansion formulas


Session ended: gt-gastown-max


Session ended: gt-gastown-nux


Polecat prompting: gt mq submit on completion

Update Polecat CLAUDE.md prompting to:

1. On task completion, run: gt mq submit --issue <id>
2. This creates a merge-request bead in the queue
3. Engineer will process it

The Polecat self-reports completion to the merge queue.


Schematic: Domain-Level Formula Composition

A Schematic is a coordinated set of formulas defining an entire workflow domain.

## Concept

Like an orchestra score to instrument parts - says how formulas fit together.

```toml
# shiny-engineering.schematic
[schematic]
name = "The Shiny Way"
description = "Full engineering workflow from issue to production"

[formulas]
planning = "@gastown/shiny"
development = "@gastown/polecat-work"
review = "@gastown/review"
release = "@gastown/release"

[flow]
default = "planning >> development >> review >> release"

[constraints]
all_code_reviewed = true
tests_before_deploy = true
```

## Relationship to Other Concepts

- **Schematic** (workflow dimension) = composition of Formulas
- **Campaign** (work dimension) = composition of Epics
- Schematic compiles to Campaign, like Formula compiles to Molecule

## Use Cases

1. Define "The Shiny Way" for a team/org
2. Encode compliance requirements
3. Standardize workflows across projects
4. Enable workflow reuse and sharing

## Open Questions

1. File format - TOML? YAML? Extend formula syntax?
2. Validation - how to verify schematic consistency?
3. Overrides - can project override org schematic?

## Related

- docs/formula_evolution.md
- gt-8tmz - molecule algebra


Session ended: gt-gastown-crew-jack


Digest: mol-deacon-patrol

Patrol 14: 3 rigs healthy


Digest: mol-deacon-patrol

Patrol 112: All healthy


Digest: mol-deacon-patrol

Patrol 41: final patrol before handoff


Merge: capable-mjtltnm5

branch: polecat/capable-mjtltnm5
target: main
source_issue: capable-mjtltnm5
rig: gastown


Witness Patrol

Per-rig worker monitor patrol loop with progressive nudging.


Digest: mol-deacon-patrol

Patrol cycle 20


Session ended: gt-gastown-crew-max


Merge: rictus-mjyxjgwv

branch: polecat/rictus-mjyxjgwv
target: main
source_issue: rictus-mjyxjgwv
rig: gastown
agent_bead: gt-gastown-polecat-rictus
retry_count: 0
last_conflict_sha: null
conflict_task_id: null

Already merged to main at 4ca430d6 (PR #82)


Digest: mol-deacon-patrol

Patrol 13: Quiet


Digest: mol-deacon-patrol

Patrol 1: All 6 agents healthy, no incidents, inbox clean


Digest: mol-deacon-patrol

Patrol 4: MQ cleared (0), all healthy


Add tmux hotkey (C-b f) for gt feed window

## Goal

Add a tmux hotkey (e.g., C-b f) that jumps to the activity feed window, creating it if it doesn't exist.

## Behavior

1. If 'feed' window exists in current session → switch to it
2. If 'feed' window doesn't exist → create it with `bd activity --follow`, then switch

## Implementation

Add to gt's tmux session configuration (similar to SetCrewCycleBindings):

```go
// C-b f → jump to feed window
tmux bind-key -T prefix f run-shell "gt feed --window"
```

The `gt feed --window` command already handles:
- Creating the window if missing
- Switching to it if exists
- Running `bd activity --follow` in the window

## Where to add

- `internal/tmux/tmux.go` - add SetFeedBinding() method
- Call it from session configuration (ConfigureGasTownSession or similar)

## Testing

1. In tmux: C-b f should open/switch to feed window
2. Feed window should show streaming activity
3. C-b n/p should still work to cycle back to other windows


Digest: mol-deacon-patrol

Patrol cycle 1: All healthy. 3 rigs, all Witnesses/Refineries responding. No orphans, no cleanup needed, inbox clean.


Digest: mol-deacon-patrol

Patrol 12: All healthy


Add crew lifecycle support to Deacon

Currently crew sessions (like gastown/crew/max, beads/crew/dave) are marked as 'human-managed' and excluded from automated lifecycle.

## Current State
- `getManager(RoleCrew)` returns `"human"` (handoff.go:273)
- `gt handoff` for crew just prints a message, no lifecycle request sent
- Crew cannot request automated refresh from Deacon

## Desired State
Crew members should be able to request lifecycle actions from Deacon:
- `gt handoff --cycle` sends request to Deacon, which kills/restarts the crew session
- Useful when: context full, running molecules that need fresh session, automation

## Implementation
1. Change `getManager(RoleCrew)` to return `"deacon/"` (or new crew manager)
2. Teach Deacon to recognize crew session naming pattern
3. Add crew to Deacon's respawn loop handling
4. Test with `gt nudge` for reliable message delivery

## Use Case
Running a molecule (e.g., version-bump) and discovering mid-workflow that a fresh session is needed. Agent should be able to request refresh automatically rather than requiring human intervention.

## Related
- gt nudge: reliable message delivery to Claude sessions
- bd mol bond: molecule instantiation (coming in beads)


Digest: mol-deacon-patrol

Patrol 4: quick pass


Digest: mol-deacon-patrol

Patrol 72: Routine


Digest: mol-deacon-patrol

Patrol 2: All healthy, 12 polecats, nudged gastown (active work)


Merge: nux-1767073376184

branch: polecat/nux-1767073376184
target: main
source_issue: nux-1767073376184
rig: gastown


Polecat identity confusion leads to direct main commits

When polecats spawn without proper identity (due to gt-si6am and gt-y41ep):
1. They think they're mayor
2. They may commit from wrong directory (mayor/rig instead of polecats/<name>)
3. Work goes directly to main, bypassing merge queue

Observed: slit pushed 'gt polecat recycle' directly to main instead of its branch

Root cause:
- gt-si6am: Wrong CLAUDE.md template (says 'Mayor Rig Context')
- gt-y41ep: Env vars not exported (falls back to mayor detection)

Impact: CRITICAL - merge queue entirely bypassed, no refinery review


Merge: gt-4u49x

branch: polecat/furiosa-mjxn8bl8
target: main
source_issue: gt-4u49x
rig: gastown
agent_bead: gt-gastown-polecat-furiosa


Digest: mol-deacon-patrol

Patrol 154 complete


Refinery Engineer placeholder code references tombstoned gt-3x1.2

The Refinery Engineer module has placeholder code that references gt-3x1.2, which is now tombstoned (gt-3x1 epic was superseded).

## Affected code:

1. **internal/refinery/engineer.go:203-225** - ProcessMR() says "This is a placeholder that will be fully implemented in gt-3x1.2" and returns failure with "ProcessMR not fully implemented (see gt-3x1.2)"

2. **internal/refinery/engineer.go:279-291** - handleFailure() says "This is a placeholder that will be fully implemented in gt-3x1.4"

3. **internal/refinery/engineer.go:307-312** - ProcessMRFromQueue() returns "ProcessMRFromQueue not fully implemented (see gt-3x1.2)"

## Context:
gt-3x1 was about updating Refinery to use Beads merge queue. The approach may have changed. Need to either:
- Implement the actual merge logic (new issue)
- Remove the placeholder code if not needed
- Update references to point to current strategy

## Related:
- gt-3x1 (tombstone) - original epic
- Current MQ implementation may have superseded this

Removed stale gt-3x1.x references. Filed gt-pnv61 for implementing actual merge logic.


Merge: nux-1767082300311

branch: polecat/nux-1767082300311
target: main
source_issue: nux-1767082300311
rig: gastown


gt rig dock/undock commands

Implement Level 2 (global/persistent) rig control.

Commands:
- gt rig dock <rig>    # Set status:docked label on rig bead
- gt rig undock <rig>  # Remove label

Behavior:
- Stops witness/refinery if running
- Sets label on rig identity bead
- Syncs via git (all clones see it)
- Permanent until explicitly undocked

Output:
  gt rig dock gastown
  ✓ Rig gastown docked (global)
    Label added: status:docked
    Witness stopped
    Refinery stopped
    Run 'bd sync' to propagate to other clones


Messaging Channels: Unified list/queue/announce/nudge system

Implement the full messaging channel system for Gas Town.

TWO PARALLEL SYSTEMS:
1. Durable (polling): mail - list, queue, announce
2. Real-time (ephemeral): nudge - channels

ARCHITECTURE:
- Config: ~/gt/config/messaging.json (town-level)
- Messages: beads (.beads/issues.jsonl with type=message)

CHANNEL TYPES:
- list:name - Fan-out, each recipient gets copy (DONE)
- queue:name - Claim-based, exactly-once processing (gt-02431)
- announce:name - Bulletin board, read-only (gt-cqvgt)
- nudge channel:name - Real-time fan-out (gt-3txyh)

PRIORITY ORDER:
1. Queues - solves real polecat cleanup race condition
2. Nudge channels - gt broadcast mostly works but channels add precision
3. Announces - nice-to-have bulletin boards


Session ended: gt-gastown-crew-joe


Merge: toast-1767140378007

branch: polecat/toast-1767140378007
target: main
source_issue: toast-1767140378007
rig: gastown


Session ended: gt-gastown-crew-gus


Messaging infrastructure: lists, queues, channels

Multi-recipient and broadcast messaging for Gas Town.

## Phases

**Phase 1 - Foundation**: Config directory (gt-i6jvc)
**Phase 1 - Core**: list:, queue:, @group (requires agent-as-bead for @group)
**Phase 2 - Channels**: announce:, #channel, gt channel publish

## Addressing Syntax

| Prefix | Resolution | Storage | Claim? |
|--------|------------|---------|--------|
| `@group` | Dynamic (bead queries) | Fan-out (N copies) | No |
| `list:name` | Static config | Fan-out (N copies) | No |
| `queue:name` | Static config | Shared (1 copy) | Yes |
| `announce:name` | Static config | Shared (1 copy) | No |
| `#channel` | Dynamic (tmux) | None (ephemeral) | No |

## Alignment with Agent-as-Bead

@group resolution now uses agent bead queries:
- @rig/gastown → bd list --type=agent --rig=gastown
- @witnesses → bd list --type=agent --role_type=witness

This replaces the originally planned filesystem scanner (gt-xo05b, now closed).

## Independent Work

Config-based features (list:, queue:, announce:) can proceed without agent identity.
Only @group requires the agent schema from Pillar 1.


Session ended: gt-gastown-crew-jack


Digest: mol-deacon-patrol

Patrol 4: All healthy


Swarm: E2E Swarm Test Epic

Swarm molecule orchestrating epic gt-1tpts.

Epic: gt-1tpts
Coordinator:

E2E Test: All tasks complete, swarm landed


Merge: valkyrie-mjw71b7u

branch: polecat/valkyrie-mjw71b7u
target: main
source_issue: valkyrie-mjw71b7u
rig: gastown
agent_bead: gt-gastown-polecat-valkyrie


CLI: worker status reporting commands

Worker status reporting CLI for polecats to report progress.

## Commands

### gt worker started
```
gt worker started <issue-id> [-m MESSAGE]
```
Reports work started on issue.

### gt worker progress
```
gt worker progress <issue-id> <0-100> [-m MESSAGE]
```
Reports percentage complete.

### gt worker blocked
```
gt worker blocked <issue-id> <reason> [-m MESSAGE]
```
Reports blocked status with reason.

### gt worker completed
```
gt worker completed <issue-id> [-m MESSAGE]
```
Reports task completion.

### gt worker failed
```
gt worker failed <issue-id> <reason> [-m MESSAGE]
```
Reports task failure.

## Implementation
Each command sends mail to refinery with structured content:
```go
type WorkerStatusReport struct {
    IssueID    string
    Status     string  // started|progress|blocked|completed|failed
    Progress   int     // 0-100 for progress
    Reason     string  // for blocked/failed
    Message    string  // optional detail
    ReportedAt time.Time
}
```

## Message Format
Subject: "[STATUS] <issue-id>: <status>"
Body: JSON-encoded WorkerStatusReport

## Default Recipient
```go
// Determine from context
func getDefaultRecipient() string {
    rig := os.Getenv("GT_RIG")
    if rig != "" {
        return rig + "/refinery"
    }
    return "refinery/"
}
```

## New File
internal/cmd/worker.go

## PGT Reference
gastown-py/src/gastown/cli/worker_cmd.py

## Acceptance Criteria
- [ ] All 5 commands implemented
- [ ] Status sent as mail to refinery
- [ ] Structured JSON body for parsing
- [ ] Works from polecat session context


Digest: mol-deacon-patrol

Patrol 2: all healthy, no incidents


Digest: mol-deacon-patrol

Patrol 4: 3 polecats working, all healthy


Digest: mol-deacon-patrol

Patrol 2: all clear, systems healthy


Day 1.4b: Create Deacon agent bead

Create gt-deacon agent bead for the Deacon (daemon beacon).

The Deacon is a separate Claude Code agent from Mayor:
- Receives the ONLY mechanical daemon heartbeats in the system
- Runs town plugins, cleanup, and monitoring
- Has own top-level directory: ~/gt/deacon/
- No rig preassignment (town-level, like Mayor)
- role_type: deacon

Agent bead structure:
```yaml
id: gt-deacon
type: agent
entity_platform: gastown
entity_org: steveyegge
entity_id: deacon
hook_bead: null  # or current patrol wisp
role_bead: gt-deacon-role
state: idle
role_type: deacon
```

Must be created alongside mayor, witness, refinery agent beads.


Refinery startup: Use Claude agent instead of foreground mode

WIP found in stash: Refactor refinery startup to spawn Claude as the refinery agent rather than using gt refinery start --foreground.

Changes needed:
- Use refineryDir (refinery/rig) as working directory
- Start Claude with --dangerously-skip-permissions
- Wait for shell ready, then Claude ready
- Send gt prime, then refinery startup prompt
- Remove foreground mode complexity

Related: gt-n7z7 (refinery --foreground race condition bug)


Refinery not processing merge-requests - stale MRs accumulating

Discovered 15 stale merge-request issues in beads rig that were never processed by the refinery. These appear to be from polecat branches that completed work but the refinery didn't pick up the merge-requests.

Closed issues:
- bd-r06v, bd-bhg7, bd-754r, bd-fcl1, bd-3zzh
- bd-bijf, bd-5rj1, bd-kptp, bd-rdzk, bd-ibl9
- bd-gfo3, bd-aq3s, bd-x2bd, bd-s1pz, bd-h27p

Need to investigate:
1. Why refinery isn't picking up merge-requests
2. Whether the polecat branches have valid work to merge
3. Add monitoring/alerting for stale MRs

Fixed: gt done and mq submit now write to mrqueue. Added gt mq migrate for stale MR recovery.


Digest: mol-deacon-patrol

Patrol 19: All healthy.


gt mail inbox shows wrong identity when run from deacon directory

When running 'gt mail inbox' from ~/gt/deacon/, it shows 'Inbox: mayor/' instead of 'Inbox: deacon/'. The role detection works (deacon checks in correctly), but mail identity detection is broken.


Refactor: Extract mail notification builder pattern

refinery/manager.go has three nearly identical notification functions:
- notifyWorkerConflict() (lines 610-628)
- notifyWorkerMerged() (lines 631-644)
- notifyWorkerRejected() (lines 787-804)

Extract a fluent builder:
```go
type NotificationBuilder struct { ... }

func (m *Manager) NotifyWorker(worker string) *NotificationBuilder

// Usage:
m.NotifyWorker(mr.Worker).
    Subject("Merge conflict - rebase required").
    Priority(mail.PriorityHigh).
    Body(fmt.Sprintf(...)).
    Send()
```


Digest: mol-deacon-patrol

Patrol 16: 3 rigs healthy


Digest: mol-deacon-patrol

Patrol 148 complete


Session ended: gt-gastown-crew-joe


Merge: gt-a95

branch: polecat/Ace
target: main
source_issue: gt-a95
rig: gastown


Digest: mol-deacon-patrol

Patrol cycle 16: all healthy


Digest: mol-deacon-patrol

Patrol cycle 22 complete. All 3 rigs healthy. No orphans, gates, convoys, or plugins. Quick cycle.


Review PR #93: deploy SessionStart hooks in gt install


Digest: mol-deacon-patrol

Patrol cycle 12: All 6 agents healthy


Digest: mol-deacon-patrol

Patrol 153 complete


Critical packages lack test coverage

Several packages have no test files:
- internal/lock/ - Core identity locking (212 lines UNTESTED)
- internal/witness/ - Worker monitoring
- internal/mrqueue/ - MR queue management
- internal/claude/ - Claude integration
- internal/style/ - Terminal styling
- internal/constants/ - Constants

Priority for testing:
1. lock/ - prevents duplicate agents, critical for correctness
2. witness/ - agent lifecycle management
3. mrqueue/ - merge request processing

Overall: 37 test files for 160 Go files (23% by file count)


gt sling reports hook occupied but bd hook shows empty

gt sling thinks a hook is occupied when bd hook shows it empty. Example: bd hook --agent beads/crew/dave shows (empty), but gt sling bd-hobo beads/crew/dave says 'hook already occupied by bd-ul59'. They should use the same source of truth (beads database pinned field).

Fixed by using proper bd commands (bd agent state, bd slot set/clear) instead of embedding in description text. Now gastown and beads use the same SQLite columns for hook_bead and agent_state.


Session ended: gt-gastown-crew-george


Merge: dementus-1767073385126

branch: polecat/dementus-1767073385126
target: main
source_issue: dementus-1767073385126
rig: gastown


Session ended: gt-gastown-crew-max


gt spawn should verify daemon is running for polecat triggering

gt spawn notifies the Deacon (via mail) that a polecat was started, expecting the Deacon to trigger it once Claude is ready. But if the daemon isn't running, the mail sits unread and the polecat never gets triggered.

## Current Behavior
1. gt spawn starts polecat session
2. gt spawn sends POLECAT_STARTED to deacon/
3. (assumes daemon will trigger polecat)

## Problem
If gt daemon isn't running, step 3 never happens and polecat sits at prompt.

## Solution
In gt spawn, after session start:
1. Check if daemon is running (gt daemon status)
2. If not running, either:
   a. Start daemon: gt daemon start
   b. Or warn user: 'Daemon not running, polecat may not auto-start'

## Alternative
The user can manually trigger with gt nudge, but automated flow should work.


Review PR #140: docs(mayor) Add Polecat Operations section


mol-sync-workspace: Fix 'commits ahead' in report for crew on main

Updated mol-sync-workspace generate-report step to use 'Unpushed commits' instead of 'Commits ahead of main' for crew workers on main branch.


Session ended: gt-gastown-mediocre


Digest: mol-deacon-patrol

Patrol 15: All clear


Merge: gt-lynar

branch: polecat/furiosa-mjxeatgb
target: main
source_issue: gt-lynar
rig: gastown
agent_bead: gt-gastown-polecat-furiosa


Digest: mol-deacon-patrol

Patrol cycle 13: All agents healthy


Pre-Launch Code Review Swarm

# Pre-Launch Code Review Swarm

Gas Town launched 3 days ago. We've been firefighting bugs but haven't done a thorough
code review. With increased scrutiny coming, we need a comprehensive audit.

## Scope

~100k lines of Go across 302 files and 42 internal packages.

## Review Areas

### Heresies (Core Principle Violations)
- **Git as blockchain** - Are we treating git properly as our ledger?
- **Federation not global consensus** - Are we avoiding centralization?
- **Human-readable** - Is everything auditable via Markdown?
- **Propulsion principle** - Do agents execute immediately when work arrives?
- **Beads as truth** - Is state tracked in beads, not ephemeral places?

### Code Quality
- **Code smells** - Long functions, god objects, primitive obsession, etc.
- **Duplicated code** - DRY violations, copy-paste patterns
- **Missing abstractions** - Fiddly replicated logic that should be consolidated
- **Dead code** - Unused functions, unreachable branches

### Go Idioms
- **Error handling** - Consistent patterns, proper wrapping
- **Interface design** - Small interfaces, dependency injection
- **Package organization** - Clear boundaries, minimal coupling
- **Naming conventions** - Idiomatic Go naming

### Performance
- **Inefficient patterns** - N+1 queries, unnecessary allocations
- **Concurrency** - Proper goroutine/channel usage
- **Resource leaks** - Unclosed handles, goroutine leaks

### Security
- **Input validation** - Especially for shell commands
- **Path traversal** - File operations with user input
- **Secret handling** - No hardcoded credentials

## Deliverables

Each review task should produce:
1. List of issues found with file:line references
2. Severity rating (critical/high/medium/low)
3. Suggested fixes where appropriate
4. New beads for anything requiring follow-up work

## Success Criteria

- All major packages reviewed
- Critical issues identified and filed
- Code quality baseline established

All 11 review tasks completed by polecat swarm


Review: Agent Lifecycle Packages

# Review Agent Lifecycle Packages

## Packages
- internal/polecat/
- internal/witness/
- internal/refinery/
- internal/deacon/

## Focus Areas

1. **Code Smells**
   - Long functions (>50 lines)
   - God objects with too many responsibilities
   - Primitive obsession (raw strings instead of types)

2. **Duplicated Code**
   - Similar patterns across lifecycle managers
   - Copy-paste between polecat/witness/refinery

3. **Missing Abstractions**
   - Common patterns that should be extracted
   - Repeated error handling logic

4. **Propulsion Principle**
   - Do agents start immediately when work arrives?
   - Are there unnecessary delays or confirmations?

## Deliverables
- List of issues with file:line references
- Suggested refactoring opportunities
- New beads for follow-up work

Review complete. Created 7 follow-up beads for identified refactoring opportunities.


Review: Go Idioms and Elegance

Audit Go best practices and idiomatic code

## Scope
Codebase-wide patterns

## Focus Areas

1. **Error Handling**
   - Consistent error wrapping with context
   - No swallowed errors
   - Sentinel errors where appropriate

2. **Interface Design**
   - Accept interfaces, return structs
   - Small, focused interfaces
   - No interface pollution

3. **Naming**
   - Exported names start with capital
   - Receiver names consistent
   - Package names singular and lowercase

4. **Concurrency**
   - Context propagation
   - Proper sync.WaitGroup usage
   - No goroutine leaks

5. **Resource Management**
   - defer for cleanup
   - Proper Close() calls
   - No leaked file handles

## Deliverables
- Idiom violations list
- Pattern recommendations
- Style guide additions

Completed Go idioms review. Report at docs/code-review-go-idioms.md