Issue: gt-lpop Status: Design Author: Mayor
Three concepts are conflated in the polecat lifecycle:
| Concept | Lifecycle | Current behavior |
|---|---|---|
| Identity | Long-lived (name, CV, ledger) | Destroyed on nuke |
| Sandbox | Per-assignment (worktree, branch) | Destroyed on nuke |
| Session | Ephemeral (Claude context window) | = polecat lifetime |
Consequences:
- Work is lost when polecats are nuked before pushing
- 219 stale remote branches from destroyed worktrees
- Slow dispatch (~5s worktree creation per assignment)
- Lost capability record (CV, completion history)
- Idle polecats were treated as waste and nuked
IDENTITY (persistent)
Name: "furiosa"
Agent bead: gt-gastown-polecat-furiosa
CV: work history, languages, completion rate
Lifecycle: created once, never destroyed (unless explicitly retired)
SANDBOX (per-assignment, reusable)
Worktree: polecats/furiosa/gastown/
Branch: polecat/furiosa/<issue>@<timestamp>
Lifecycle: synced to main between assignments, not destroyed
SESSION (ephemeral)
Tmux: gt-gastown-furiosa
Claude context: cycles on compaction/handoff
Lifecycle: independent of identity and sandbox
┌──────────┐
┌───►│ IDLE │◄──── sync sandbox to main
│ └────┬─────┘ clear hook
│ │ gt sling
│ ▼
│ ┌──────────┐
│ │ WORKING │◄──── session active, hook set
│ └────┬─────┘
│ │ work complete
│ ▼
│ ┌──────────┐
└────┤ DONE │──── push branch, submit MR
└──────────┘
No nuke in the happy path. Polecats cycle: IDLE → WORKING → DONE → IDLE.
Pool size: Fixed per rig. Configured in rig.config.json:
{
"polecat_pool_size": 4,
"polecat_names": ["furiosa", "nux", "toast", "slit"]
}Initialization: gt rig add or gt polecat pool init <rig> creates N polecats
with identities and worktrees. They start in IDLE state.
Dispatch: gt sling <bead> <rig> finds an IDLE polecat (already does this via
FindIdlePolecat()), attaches work, starts session. No worktree creation needed.
Completion: When a polecat finishes work:
- Push branch to origin
- Submit MR (if code changes)
- Clear hook_bead
- Sync worktree:
git checkout main && git pull - Set state to IDLE
- Session stays alive or cycles — doesn't matter, identity persists
When work completes and MR is merged (or no code changes):
# In the polecat's worktree
git checkout main
git pull origin main
git branch -D polecat/furiosa/<old-issue>@<timestamp>
# Worktree is now clean, on main, ready for next assignmentWhen new work is slung:
# Create fresh branch from current main
git checkout -b polecat/furiosa/<new-issue>@<timestamp>
# Start workingNo worktree add/remove. Just branch operations on an existing worktree.
No changes to refinery. Refinery still:
- Sees MR from polecat branch
- Reviews and merges to main
- Deletes remote polecat branch (NEW: add this step)
The polecat doesn't care — it already moved to main locally during DONE → IDLE.
Witness patrol behavior (shipped):
- Sees idle polecat → healthy state, skip
- Stuck detection: Polecat in WORKING state for too long → escalate (don't nuke)
- Dead session detection: Session died but state=WORKING → restart session (not nuke polecat)
gt polecat nuke is reserved for exceptional cases:
- Polecat worktree is irrecoverably broken
- Need to reclaim disk space
- Decommissioning a rig
It should be rare and manual, not part of normal workflow.
With persistent polecats, branches have clear owners:
- Active branches: polecat is WORKING on them
- Merged branches: refinery deletes after merge
- Abandoned branches: polecat syncs to main on DONE → IDLE, old branch deleted locally
The 219 stale branches came from nuked polecats that never cleaned up. With persistent polecats, branch lifecycle is managed by the polecat itself.
For the existing 219 stale branches:
# Delete all remote polecat branches that don't belong to active polecats
git branch -r | grep 'origin/polecat/' | grep -v 'furiosa/gt-ziiu' | grep -v 'nux/gt-uj16' \
| sed 's/origin\///' | xargs -I{} git push origin --delete {}- Witness no longer nukes idle polecats
gt polecat donetransitions to IDLE instead of triggering nuke- Refinery deletes remote branch after merge
gt polecat pool init <rig>creates N persistent polecats- Pool size configured in rig.config.json
- Worktrees created once, reused across assignments
Status: Polecats are allocated on-demand by gt sling via FindIdlePolecat()
and AllocateAndAdd(). Pre-allocation is unnecessary because idle polecats are
reused automatically. Pool size enforcement is a future optimization, not a blocker.
- DONE → IDLE transition syncs worktree to main (
done.go) - IDLE → WORKING creates fresh branch (no worktree add) via
ReuseIdlePolecat() gt slingprefers idle polecats viaFindIdlePolecat()- Branch-only reuse eliminates ~5s worktree creation overhead
- Session cycling doesn't affect polecat state
- Dead sessions restarted by witness (restart-first policy, no auto-nuke)
- Handoff preserves polecat identity across session boundaries
gt handoffworks for all roles (Mayor, Crew, Witness, Refinery, Polecats)
- Polecat branch cleanup after merge: SHIPPED (landed to main; PRs #2436/#2437 closed)
- Refinery notifies mayor after merge: not yet shipped
- Pool reconciliation (
ReconcilePool): not yet implemented
| Component | Status | Key Files |
|---|---|---|
gt done (push, MR, idle, sandbox sync) |
SHIPPED | internal/cmd/done.go |
gt sling (idle reuse, branch-only repair) |
SHIPPED | internal/cmd/sling.go, polecat_spawn.go |
gt handoff (session cycle, all roles) |
SHIPPED | internal/cmd/handoff.go |
| Witness patrol (zombie, stale, orphan detection) | SHIPPED | internal/witness/handlers.go, internal/polecat/manager.go |
| Cleanup pipeline (POLECAT_DONE → MERGE_READY → MERGED) | SHIPPED | internal/witness/handlers.go, internal/refinery/engineer.go |
| Idle polecat heresy fix (skip healthy idle) | SHIPPED | internal/witness/handlers.go |
| Restart-first policy (no auto-nuke) | SHIPPED | internal/polecat/manager.go |
| Polecat branch always deleted after merge | SHIPPED | internal/refinery/engineer.go |
| Refinery notifies mayor after merge | NOT SHIPPED | — |
| Pool size enforcement | DEFERRED | — |
ReconcilePool() |
DEFERRED | — |
gt polecat pool init command |
DEFERRED | — |