fix: Polecat lifecycle cleanup - stale worktrees and git tracking
Fixes gt-v07fl: Polecat lifecycle cleanup for stale worktrees and git tracking conflicts. Changes: 1. Add .claude/ to .gitignore (prevents untracked file accumulation) 2. Add beads runtime state patterns to .gitignore (prevents future tracking) 3. Remove .beads/ runtime state from git tracking (mq/, issues.jsonl, etc.) - Formulas and config remain tracked (needed for go install) - Created follow-up gt-mpyuq for formulas refactor 4. Add DetectStalePolecats() to polecat manager for identifying cleanup candidates 5. Add CountCommitsBehind() to git package for staleness detection 6. Add `gt polecat stale <rig>` command for stale polecat detection/cleanup - Shows polecats without active sessions - Identifies polecats far behind main (configurable threshold) - Optional --cleanup flag to auto-nuke stale polecats The existing `gt polecat gc` command handles branch cleanup. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
5261
.beads/issues.jsonl
5261
.beads/issues.jsonl
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
gt-gastown-polecat-warboy
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"database": "beads.db",
|
|
||||||
"jsonl_export": "issues.jsonl"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-09eim",
|
|
||||||
"branch": "polecat/toast-1767088545235",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "toast-1767088545235",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: toast-1767088545235",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T02:01:08.537717-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-0a0vr",
|
|
||||||
"branch": "polecat/furiosa-1767087671424",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "furiosa-1767087671424",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: furiosa-1767087671424",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T01:53:07.730594-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-0h89l",
|
|
||||||
"branch": "polecat/furiosa-1767084006859",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "furiosa-1767084006859",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: furiosa-1767084006859",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:47:11.803227-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-215tk",
|
|
||||||
"branch": "polecat/warboy-1767106060799",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "warboy-1767106060799",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: warboy-1767106060799",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T10:40:55.503776-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-2c4o0",
|
|
||||||
"branch": "polecat/dementus-1767087772272",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "dementus-1767087772272",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: dementus-1767087772272",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T02:06:35.286507-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-2hirc",
|
|
||||||
"branch": "polecat/capable-1767084028536",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "capable-1767084028536",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: capable-1767084028536",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T01:03:19.471054-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-2puev",
|
|
||||||
"branch": "polecat/dementus-1767081113622",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "dementus-1767081113622",
|
|
||||||
"worker": "dementus-1767081113622",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: dementus-1767081113622",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:05:15.468509-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-2tspu",
|
|
||||||
"branch": "polecat/furiosa-dogs",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "furiosa-dogs",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: furiosa-dogs",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T10:42:17.458391-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-3gepq",
|
|
||||||
"branch": "polecat/toast-1767081120579",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "toast-1767081120579",
|
|
||||||
"worker": "toast-1767081120579",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: toast-1767081120579",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:05:15.468721-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-4a9y4",
|
|
||||||
"branch": "polecat/slit-1767138831931",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "slit-1767138831931",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: slit-1767138831931",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T16:15:39.347085-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-4nobz",
|
|
||||||
"branch": "polecat/capable-1767140263101",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "capable-1767140263101",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: capable-1767140263101",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T16:26:48.128098-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-4q7wh",
|
|
||||||
"branch": "polecat/nux-1767141948667",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "nux-1767141948667",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: nux-1767141948667",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T16:51:43.00565-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-5ggcs",
|
|
||||||
"branch": "polecat/slit-1767082302712",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "slit-1767082302712",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: slit-1767082302712",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:18:54.19263-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-643ie",
|
|
||||||
"branch": "polecat/slit-1767141951901",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "slit-1767141951901",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: slit-1767141951901",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T16:56:13.685311-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-6l7h1",
|
|
||||||
"branch": "polecat/morsov-dogs",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "morsov-dogs",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: morsov-dogs",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T10:41:30.109352-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-804je",
|
|
||||||
"branch": "polecat/dementus-1767146229184",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "dementus-1767146229184",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: dementus-1767146229184",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T18:01:50.012819-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-860md",
|
|
||||||
"branch": "polecat/capable-1767146233256",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "capable-1767146233256",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: capable-1767146233256",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T18:03:37.998767-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-9g6md",
|
|
||||||
"branch": "polecat/nux-1767082300311",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "nux-1767082300311",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: nux-1767082300311",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:18:32.959791-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-9hfky",
|
|
||||||
"branch": "polecat/toast-1767140378007",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "toast-1767140378007",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: toast-1767140378007",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T16:28:18.459411-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-aa1jz",
|
|
||||||
"branch": "polecat/keeper-dogs",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "keeper-dogs",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: keeper-dogs",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T10:36:28.247719-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-apft7",
|
|
||||||
"branch": "polecat/capable-1767084028536",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "capable-1767084028536",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: capable-1767084028536",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T01:04:07.334023-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-bnfus",
|
|
||||||
"branch": "polecat/rictus-1767138835254",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "rictus-1767138835254",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: rictus-1767138835254",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T16:27:17.228997-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-bx4ki",
|
|
||||||
"branch": "polecat/keeper-dogs",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "keeper-dogs",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: keeper-dogs",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T10:53:39.674941-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-c7qtp",
|
|
||||||
"branch": "polecat/rictus-1767084016819",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "rictus-1767084016819",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: rictus-1767084016819",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:49:18.337909-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-cfpd8",
|
|
||||||
"branch": "polecat/capable-mq-events",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "capable-mq",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: capable-mq",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T01:14:13.648371-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-cpxxv",
|
|
||||||
"branch": "polecat/organic-1767106082951",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "organic-1767106082951",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: organic-1767106082951",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T10:42:25.228746-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-djv74",
|
|
||||||
"branch": "polecat/nux-1767081106779",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "nux-1767081106779",
|
|
||||||
"worker": "nux-1767081106779",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: nux-1767081106779",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:05:15.468625-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-dufx1",
|
|
||||||
"branch": "polecat/capable-1767140263101",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "capable-1767140263101",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: capable-1767140263101",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T16:24:39.547495-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-e0p84",
|
|
||||||
"branch": "polecat/toast-1767081120579",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "toast-1767081120579",
|
|
||||||
"worker": "toast-1767081120579",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: toast-1767081120579",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:05:15.468573-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-gdbcb",
|
|
||||||
"branch": "polecat/rictus-1767141956287",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "rictus-1767141956287",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: rictus-1767141956287",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T16:47:36.875216-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-gnuat",
|
|
||||||
"branch": "polecat/dementus-1767081113622",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "dementus-1767081113622",
|
|
||||||
"worker": "dementus-1767081113622",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: dementus-1767081113622",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:05:15.468374-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-gres0",
|
|
||||||
"branch": "polecat/nux-1767084010093",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "nux-1767084010093",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: nux-1767084010093",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:48:40.079116-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-hrhts",
|
|
||||||
"branch": "polecat/cheedo-1767146245543",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "cheedo-1767146245543",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: cheedo-1767146245543",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T18:00:27.283919-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-i6xqu",
|
|
||||||
"branch": "polecat/toast-1767146237529",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "toast-1767146237529",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: toast-1767146237529",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T18:03:32.883944-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-i7tmd",
|
|
||||||
"branch": "polecat/rictus-1767084016819",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "rictus-1767084016819",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: rictus-1767084016819",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:58:46.110174-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-i9y2a",
|
|
||||||
"branch": "polecat/toast-1767146237529",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "toast-1767146237529",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: toast-1767146237529",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T18:04:15.705404-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-iai8v",
|
|
||||||
"branch": "polecat/nux-1767082300311",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "nux-1767082300311",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: nux-1767082300311",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:16:15.874394-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-jl4ze",
|
|
||||||
"branch": "polecat/dementus-1767084022436",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "dementus-1767084022436",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: dementus-1767084022436",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:49:44.391479-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-jsoiw",
|
|
||||||
"branch": "polecat/dag-1767146241770",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "dag-1767146241770",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: dag-1767146241770",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T18:03:10.025552-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-klu0r",
|
|
||||||
"branch": "polecat/nux-1767083432904",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "nux-1767083432904",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: nux-1767083432904",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:35:43.911656-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-l2b6v",
|
|
||||||
"branch": "polecat/slit-1767084013378",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "slit-1767084013378",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: slit-1767084013378",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:49:46.335483-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-nduix",
|
|
||||||
"branch": "polecat/nux-1767138828269",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "nux-1767138828269",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: nux-1767138828269",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T16:17:54.718789-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-npu0m",
|
|
||||||
"branch": "polecat/imperator-1767106079026",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "imperator-1767106079026",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: imperator-1767106079026",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T10:40:11.954481-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-nq5l9",
|
|
||||||
"branch": "polecat/nux-1767087680976",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "nux-1767087680976",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: nux-1767087680976",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T13:43:41.691922-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-nu47q",
|
|
||||||
"branch": "polecat/rictus-1767087768853",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "rictus-1767087768853",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: rictus-1767087768853",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T01:54:12.913353-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-pulkh",
|
|
||||||
"branch": "polecat/ace-dogs",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "ace-dogs",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: ace-dogs",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T10:36:01.970507-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-qduud",
|
|
||||||
"branch": "polecat/furiosa-1767084006859",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "furiosa-1767084006859",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: furiosa-1767084006859",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:48:06.518381-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-r099o",
|
|
||||||
"branch": "polecat/imperator-1767106079026",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "imperator-1767106079026",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: imperator-1767106079026",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T10:46:40.452899-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-sp1tv",
|
|
||||||
"branch": "polecat/rictus-1767081110235",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "rictus-1767081110235",
|
|
||||||
"worker": "rictus-1767081110235",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: rictus-1767081110235",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:05:15.468677-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-svmj8",
|
|
||||||
"branch": "polecat/cheedo-1767088553821",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "cheedo-1767088553821",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: cheedo-1767088553821",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T10:37:17.028645-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-t072g",
|
|
||||||
"branch": "polecat/nux-1767084010093",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "nux-1767084010093",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: nux-1767084010093",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:50:06.177433-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-tjy9r",
|
|
||||||
"branch": "polecat/capable-1767074974673",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "capable-1767074974673",
|
|
||||||
"worker": "capable-1767074974673",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: capable-1767074974673",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:05:15.468769-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-tpq7i",
|
|
||||||
"branch": "polecat/keeper-1767074342207",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "keeper-1767074342207",
|
|
||||||
"worker": "keeper-1767074342207",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: keeper-1767074342207",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:05:15.468817-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-u65t8",
|
|
||||||
"branch": "polecat/valkyrie-1767106008400",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "valkyrie-1767106008400",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: valkyrie-1767106008400",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T10:43:03.505961-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-ug23r",
|
|
||||||
"branch": "polecat/cheedo-1767088553821",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "cheedo-1767088553821",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: cheedo-1767088553821",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T02:00:38.571996-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-w4v1o",
|
|
||||||
"branch": "polecat/furiosa-dogs",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "furiosa-dogs",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: furiosa-dogs",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T11:01:55.023855-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-x1xf4",
|
|
||||||
"branch": "polecat/slit-1767087730371",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "slit-1767087730371",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: slit-1767087730371",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T01:52:04.349503-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-xv6b6",
|
|
||||||
"branch": "polecat/dementus-1767140140908",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "dementus-1767140140908",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: dementus-1767140140908",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T16:23:04.504091-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-yh051",
|
|
||||||
"branch": "polecat/rictus-1767084016819",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "rictus-1767084016819",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: rictus-1767084016819",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T00:48:16.329248-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-yjrb7",
|
|
||||||
"branch": "polecat/furiosa-dogs",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "furiosa-dogs",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: furiosa-dogs",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T10:52:57.896189-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-yqxcq",
|
|
||||||
"branch": "polecat/furiosa-1767141944421",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "furiosa-1767141944421",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: furiosa-1767141944421",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T16:49:14.139123-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-zet9d",
|
|
||||||
"branch": "polecat/nux-1767087680976",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "nux-1767087680976",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: nux-1767087680976",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T01:50:53.298145-08:00"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "gt-zvfnu",
|
|
||||||
"branch": "polecat/furiosa-1767138824776",
|
|
||||||
"target": "main",
|
|
||||||
"source_issue": "furiosa-1767138824776",
|
|
||||||
"worker": "",
|
|
||||||
"rig": "gastown",
|
|
||||||
"title": "Merge: furiosa-1767138824776",
|
|
||||||
"priority": 2,
|
|
||||||
"created_at": "2025-12-30T16:09:55.272069-08:00"
|
|
||||||
}
|
|
||||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -17,6 +17,9 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
# Claude Code local state
|
||||||
|
.claude/
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
coverage.out
|
coverage.out
|
||||||
*.test
|
*.test
|
||||||
@@ -29,7 +32,17 @@ gt
|
|||||||
# Runtime state
|
# Runtime state
|
||||||
state.json
|
state.json
|
||||||
.runtime/
|
.runtime/
|
||||||
|
|
||||||
|
# Beads runtime state (not tracked)
|
||||||
|
# Formulas ARE tracked for `go install @latest` - see bottom of file
|
||||||
.beads/redirect
|
.beads/redirect
|
||||||
|
.beads/issues.jsonl
|
||||||
|
.beads/interactions.jsonl
|
||||||
|
.beads/metadata.json
|
||||||
|
.beads/mq/
|
||||||
|
.beads/last-touched
|
||||||
|
.beads/daemon-*.log.gz
|
||||||
|
.beads-wisp/
|
||||||
|
|
||||||
# Clone-specific CLAUDE.md (regenerated locally per clone)
|
# Clone-specific CLAUDE.md (regenerated locally per clone)
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
|
|||||||
@@ -229,6 +229,37 @@ Examples:
|
|||||||
RunE: runPolecatCheckRecovery,
|
RunE: runPolecatCheckRecovery,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
polecatStaleJSON bool
|
||||||
|
polecatStaleThreshold int
|
||||||
|
polecatStaleCleanup bool
|
||||||
|
)
|
||||||
|
|
||||||
|
var polecatStaleCmd = &cobra.Command{
|
||||||
|
Use: "stale <rig>",
|
||||||
|
Short: "Detect stale polecats that may need cleanup",
|
||||||
|
Long: `Detect stale polecats in a rig that are candidates for cleanup.
|
||||||
|
|
||||||
|
A polecat is considered stale if:
|
||||||
|
- No active tmux session
|
||||||
|
- Way behind main (>threshold commits) OR no agent bead
|
||||||
|
- Has no uncommitted work that could be lost
|
||||||
|
|
||||||
|
The default threshold is 20 commits behind main.
|
||||||
|
|
||||||
|
Use --cleanup to automatically nuke stale polecats that are safe to remove.
|
||||||
|
Use --dry-run with --cleanup to see what would be cleaned.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
gt polecat stale greenplace
|
||||||
|
gt polecat stale greenplace --threshold 50
|
||||||
|
gt polecat stale greenplace --json
|
||||||
|
gt polecat stale greenplace --cleanup
|
||||||
|
gt polecat stale greenplace --cleanup --dry-run`,
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
RunE: runPolecatStale,
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// List flags
|
// List flags
|
||||||
polecatListCmd.Flags().BoolVar(&polecatListJSON, "json", false, "Output as JSON")
|
polecatListCmd.Flags().BoolVar(&polecatListJSON, "json", false, "Output as JSON")
|
||||||
@@ -259,6 +290,11 @@ func init() {
|
|||||||
// Check-recovery flags
|
// Check-recovery flags
|
||||||
polecatCheckRecoveryCmd.Flags().BoolVar(&polecatCheckRecoveryJSON, "json", false, "Output as JSON")
|
polecatCheckRecoveryCmd.Flags().BoolVar(&polecatCheckRecoveryJSON, "json", false, "Output as JSON")
|
||||||
|
|
||||||
|
// Stale flags
|
||||||
|
polecatStaleCmd.Flags().BoolVar(&polecatStaleJSON, "json", false, "Output as JSON")
|
||||||
|
polecatStaleCmd.Flags().IntVar(&polecatStaleThreshold, "threshold", 20, "Commits behind main to consider stale")
|
||||||
|
polecatStaleCmd.Flags().BoolVar(&polecatStaleCleanup, "cleanup", false, "Automatically nuke stale polecats")
|
||||||
|
|
||||||
// Add subcommands
|
// Add subcommands
|
||||||
polecatCmd.AddCommand(polecatListCmd)
|
polecatCmd.AddCommand(polecatListCmd)
|
||||||
polecatCmd.AddCommand(polecatAddCmd)
|
polecatCmd.AddCommand(polecatAddCmd)
|
||||||
@@ -269,6 +305,7 @@ func init() {
|
|||||||
polecatCmd.AddCommand(polecatCheckRecoveryCmd)
|
polecatCmd.AddCommand(polecatCheckRecoveryCmd)
|
||||||
polecatCmd.AddCommand(polecatGCCmd)
|
polecatCmd.AddCommand(polecatGCCmd)
|
||||||
polecatCmd.AddCommand(polecatNukeCmd)
|
polecatCmd.AddCommand(polecatNukeCmd)
|
||||||
|
polecatCmd.AddCommand(polecatStaleCmd)
|
||||||
|
|
||||||
rootCmd.AddCommand(polecatCmd)
|
rootCmd.AddCommand(polecatCmd)
|
||||||
}
|
}
|
||||||
@@ -1461,3 +1498,116 @@ func runPolecatNuke(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runPolecatStale(cmd *cobra.Command, args []string) error {
|
||||||
|
rigName := args[0]
|
||||||
|
mgr, r, err := getPolecatManager(rigName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Detecting stale polecats in %s (threshold: %d commits behind main)...\n\n", r.Name, polecatStaleThreshold)
|
||||||
|
|
||||||
|
staleInfos, err := mgr.DetectStalePolecats(polecatStaleThreshold)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("detecting stale polecats: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(staleInfos) == 0 {
|
||||||
|
fmt.Println("No polecats found.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON output
|
||||||
|
if polecatStaleJSON {
|
||||||
|
return json.NewEncoder(os.Stdout).Encode(staleInfos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Summary counts
|
||||||
|
var staleCount, safeCount int
|
||||||
|
for _, info := range staleInfos {
|
||||||
|
if info.IsStale {
|
||||||
|
staleCount++
|
||||||
|
} else {
|
||||||
|
safeCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display results
|
||||||
|
for _, info := range staleInfos {
|
||||||
|
statusIcon := style.Success.Render("●")
|
||||||
|
statusText := "active"
|
||||||
|
if info.IsStale {
|
||||||
|
statusIcon = style.Warning.Render("○")
|
||||||
|
statusText = "stale"
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s %s (%s)\n", statusIcon, style.Bold.Render(info.Name), statusText)
|
||||||
|
|
||||||
|
// Session status
|
||||||
|
if info.HasActiveSession {
|
||||||
|
fmt.Printf(" Session: %s\n", style.Success.Render("running"))
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" Session: %s\n", style.Dim.Render("stopped"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commits behind
|
||||||
|
if info.CommitsBehind > 0 {
|
||||||
|
behindStyle := style.Dim
|
||||||
|
if info.CommitsBehind >= polecatStaleThreshold {
|
||||||
|
behindStyle = style.Warning
|
||||||
|
}
|
||||||
|
fmt.Printf(" Behind main: %s\n", behindStyle.Render(fmt.Sprintf("%d commits", info.CommitsBehind)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agent state
|
||||||
|
if info.AgentState != "" {
|
||||||
|
fmt.Printf(" Agent state: %s\n", info.AgentState)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" Agent state: %s\n", style.Dim.Render("no bead"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uncommitted work
|
||||||
|
if info.HasUncommittedWork {
|
||||||
|
fmt.Printf(" Uncommitted: %s\n", style.Error.Render("yes"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reason
|
||||||
|
fmt.Printf(" Reason: %s\n", info.Reason)
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Summary
|
||||||
|
fmt.Printf("Summary: %d stale, %d active\n", staleCount, safeCount)
|
||||||
|
|
||||||
|
// Cleanup if requested
|
||||||
|
if polecatStaleCleanup && staleCount > 0 {
|
||||||
|
fmt.Println()
|
||||||
|
if polecatNukeDryRun {
|
||||||
|
fmt.Printf("Would clean up %d stale polecat(s):\n", staleCount)
|
||||||
|
for _, info := range staleInfos {
|
||||||
|
if info.IsStale {
|
||||||
|
fmt.Printf(" - %s: %s\n", info.Name, info.Reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Cleaning up %d stale polecat(s)...\n", staleCount)
|
||||||
|
nuked := 0
|
||||||
|
for _, info := range staleInfos {
|
||||||
|
if !info.IsStale {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf(" Nuking %s...", info.Name)
|
||||||
|
if err := mgr.RemoveWithOptions(info.Name, true, false); err != nil {
|
||||||
|
fmt.Printf(" %s (%v)\n", style.Error.Render("failed"), err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" %s\n", style.Success.Render("done"))
|
||||||
|
nuked++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("\n%s Nuked %d stale polecat(s).\n", style.SuccessPrefix, nuked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -699,6 +699,24 @@ func (g *Git) CommitsAhead(base, branch string) (int, error) {
|
|||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CountCommitsBehind returns the number of commits that HEAD is behind the given ref.
|
||||||
|
// For example, CountCommitsBehind("origin/main") returns how many commits
|
||||||
|
// are on origin/main that are not on the current HEAD.
|
||||||
|
func (g *Git) CountCommitsBehind(ref string) (int, error) {
|
||||||
|
out, err := g.run("rev-list", "--count", "HEAD.."+ref)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int
|
||||||
|
_, err = fmt.Sscanf(out, "%d", &count)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("parsing commit count: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
// StashCount returns the number of stashes in the repository.
|
// StashCount returns the number of stashes in the repository.
|
||||||
func (g *Git) StashCount() (int, error) {
|
func (g *Git) StashCount() (int, error) {
|
||||||
out, err := g.run("stash", "list")
|
out, err := g.run("stash", "list")
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@@ -831,3 +832,131 @@ func (m *Manager) CleanupStaleBranches() (int, error) {
|
|||||||
|
|
||||||
return deleted, nil
|
return deleted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StalenessInfo contains details about a polecat's staleness.
|
||||||
|
type StalenessInfo struct {
|
||||||
|
Name string
|
||||||
|
CommitsBehind int // How many commits behind origin/main
|
||||||
|
HasActiveSession bool // Whether tmux session is running
|
||||||
|
HasUncommittedWork bool // Whether there's uncommitted or unpushed work
|
||||||
|
AgentState string // From agent bead (empty if no bead)
|
||||||
|
IsStale bool // Overall assessment: safe to clean up
|
||||||
|
Reason string // Why it's considered stale (or not)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetectStalePolecats identifies polecats that are candidates for cleanup.
|
||||||
|
// A polecat is considered stale if:
|
||||||
|
// - No active tmux session AND
|
||||||
|
// - Either: way behind main (>threshold commits) OR no agent bead/activity
|
||||||
|
// - Has no uncommitted work that could be lost
|
||||||
|
//
|
||||||
|
// threshold: minimum commits behind main to consider "way behind" (e.g., 20)
|
||||||
|
func (m *Manager) DetectStalePolecats(threshold int) ([]*StalenessInfo, error) {
|
||||||
|
polecats, err := m.List()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("listing polecats: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(polecats) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get default branch from rig config
|
||||||
|
defaultBranch := "main"
|
||||||
|
if rigCfg, err := rig.LoadRigConfig(m.rig.Path); err == nil && rigCfg.DefaultBranch != "" {
|
||||||
|
defaultBranch = rigCfg.DefaultBranch
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []*StalenessInfo
|
||||||
|
for _, p := range polecats {
|
||||||
|
info := &StalenessInfo{
|
||||||
|
Name: p.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for active tmux session
|
||||||
|
// Session name follows pattern: gt-<rig>-<polecat>
|
||||||
|
sessionName := fmt.Sprintf("gt-%s-%s", m.rig.Name, p.Name)
|
||||||
|
info.HasActiveSession = checkTmuxSession(sessionName)
|
||||||
|
|
||||||
|
// Check how far behind main
|
||||||
|
polecatGit := git.NewGit(p.ClonePath)
|
||||||
|
info.CommitsBehind = countCommitsBehind(polecatGit, defaultBranch)
|
||||||
|
|
||||||
|
// Check for uncommitted work
|
||||||
|
status, err := polecatGit.CheckUncommittedWork()
|
||||||
|
if err == nil && !status.Clean() {
|
||||||
|
info.HasUncommittedWork = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check agent bead state
|
||||||
|
agentID := m.agentBeadID(p.Name)
|
||||||
|
_, fields, err := m.beads.GetAgentBead(agentID)
|
||||||
|
if err == nil && fields != nil {
|
||||||
|
info.AgentState = fields.AgentState
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine staleness
|
||||||
|
info.IsStale, info.Reason = assessStaleness(info, threshold)
|
||||||
|
results = append(results, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkTmuxSession checks if a tmux session exists.
|
||||||
|
func checkTmuxSession(sessionName string) bool {
|
||||||
|
// Use has-session command which returns 0 if session exists
|
||||||
|
cmd := exec.Command("tmux", "has-session", "-t", sessionName) //nolint:gosec // G204: sessionName is constructed internally
|
||||||
|
return cmd.Run() == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// countCommitsBehind counts how many commits a worktree is behind origin/<defaultBranch>.
|
||||||
|
func countCommitsBehind(g *git.Git, defaultBranch string) int {
|
||||||
|
// Use rev-list to count commits: origin/main..HEAD shows commits ahead,
|
||||||
|
// HEAD..origin/main shows commits behind
|
||||||
|
remoteBranch := "origin/" + defaultBranch
|
||||||
|
count, err := g.CountCommitsBehind(remoteBranch)
|
||||||
|
if err != nil {
|
||||||
|
return 0 // Can't determine, assume not behind
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// assessStaleness determines if a polecat should be cleaned up.
|
||||||
|
func assessStaleness(info *StalenessInfo, threshold int) (bool, string) {
|
||||||
|
// Never clean up if there's uncommitted work
|
||||||
|
if info.HasUncommittedWork {
|
||||||
|
return false, "has uncommitted work"
|
||||||
|
}
|
||||||
|
|
||||||
|
// If session is active, not stale
|
||||||
|
if info.HasActiveSession {
|
||||||
|
return false, "session active"
|
||||||
|
}
|
||||||
|
|
||||||
|
// No active session - check other indicators
|
||||||
|
|
||||||
|
// If agent reports "running" state but no session, that's suspicious
|
||||||
|
// but give benefit of doubt (session may have just died)
|
||||||
|
if info.AgentState == "running" {
|
||||||
|
return false, "agent reports running (session may be restarting)"
|
||||||
|
}
|
||||||
|
|
||||||
|
// If agent reports "done" or "idle", it's a cleanup candidate
|
||||||
|
if info.AgentState == "done" || info.AgentState == "idle" {
|
||||||
|
return true, fmt.Sprintf("agent_state=%s, no active session", info.AgentState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Way behind main is a strong staleness signal
|
||||||
|
if info.CommitsBehind >= threshold {
|
||||||
|
return true, fmt.Sprintf("%d commits behind main, no active session", info.CommitsBehind)
|
||||||
|
}
|
||||||
|
|
||||||
|
// No agent bead and no session - likely abandoned
|
||||||
|
if info.AgentState == "" {
|
||||||
|
return true, "no agent bead, no active session"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default: not enough evidence to consider stale
|
||||||
|
return false, "insufficient staleness indicators"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user