fix: migrate gt mail to use bd v0.32.0 issue commands

bd v0.32.0 removed mail commands. Updated gt mail to use:
- bd list --type message (inbox)
- bd show (read)
- bd close (delete/ack)
- bd create --type message (send)

Sender/thread/reply-to now stored in labels and extracted on read.
Added --pinned flag (blocked by bd pin bug gt-zr0a).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-20 20:11:09 -08:00
parent b3ef048c28
commit 57917521e6
5 changed files with 96 additions and 43 deletions

View File

@@ -92,6 +92,7 @@
{"id":"gt-6z2","title":"Test Epic: GGT MVP Validation","status":"closed","issue_type":"epic","created_at":"2025-12-16T21:57:37.355269-08:00","updated_at":"2025-12-16T22:06:41.124727-08:00","closed_at":"2025-12-16T22:06:41.124727-08:00"} {"id":"gt-6z2","title":"Test Epic: GGT MVP Validation","status":"closed","issue_type":"epic","created_at":"2025-12-16T21:57:37.355269-08:00","updated_at":"2025-12-16T22:06:41.124727-08:00","closed_at":"2025-12-16T22:06:41.124727-08:00"}
{"id":"gt-6z2.1","title":"Test Task 1: Add comment to gt.go","status":"closed","issue_type":"task","created_at":"2025-12-16T21:57:43.554166-08:00","updated_at":"2025-12-16T22:06:41.126547-08:00","closed_at":"2025-12-16T22:06:41.126547-08:00","dependencies":[{"issue_id":"gt-6z2.1","depends_on_id":"gt-6z2","type":"parent-child","created_at":"2025-12-16T21:57:43.55748-08:00","created_by":"daemon"}]} {"id":"gt-6z2.1","title":"Test Task 1: Add comment to gt.go","status":"closed","issue_type":"task","created_at":"2025-12-16T21:57:43.554166-08:00","updated_at":"2025-12-16T22:06:41.126547-08:00","closed_at":"2025-12-16T22:06:41.126547-08:00","dependencies":[{"issue_id":"gt-6z2.1","depends_on_id":"gt-6z2","type":"parent-child","created_at":"2025-12-16T21:57:43.55748-08:00","created_by":"daemon"}]}
{"id":"gt-6z2.2","title":"Test Task 2: Add comment to version.go","status":"closed","issue_type":"task","created_at":"2025-12-16T21:57:43.693217-08:00","updated_at":"2025-12-16T22:06:41.127126-08:00","closed_at":"2025-12-16T22:06:41.127126-08:00","dependencies":[{"issue_id":"gt-6z2.2","depends_on_id":"gt-6z2","type":"parent-child","created_at":"2025-12-16T21:57:43.69361-08:00","created_by":"daemon"}]} {"id":"gt-6z2.2","title":"Test Task 2: Add comment to version.go","status":"closed","issue_type":"task","created_at":"2025-12-16T21:57:43.693217-08:00","updated_at":"2025-12-16T22:06:41.127126-08:00","closed_at":"2025-12-16T22:06:41.127126-08:00","dependencies":[{"issue_id":"gt-6z2.2","depends_on_id":"gt-6z2","type":"parent-child","created_at":"2025-12-16T21:57:43.69361-08:00","created_by":"daemon"}]}
{"id":"gt-6z9m","title":"Test7","description":"Test with fixed workdir","status":"closed","priority":2,"issue_type":"message","created_at":"2025-12-20T17:50:15.993225-08:00","updated_at":"2025-12-20T17:51:17.265938-08:00","closed_at":"2025-12-20T17:51:17.265938-08:00"}
{"id":"gt-70b3","title":"detectSender() doesn't recognize crew workers","description":"## Problem\n\n`detectSender()` in mail.go only detects polecats, not crew workers.\n\n## Current Code (line 445)\n\n```go\n// If in a rig's polecats directory, extract address\nif strings.Contains(cwd, \"/polecats/\") {\n // extract rig/polecat\n}\n\n// Default to mayor\nreturn \"mayor/\"\n```\n\n## Symptom\n\nEmma (crew worker at `/Users/stevey/gt/beads/crew/emma`) runs:\n- `gt mail inbox` → checks `mayor/` inbox (wrong!)\n- Should check `beads/emma` or `beads/crew/emma`\n\n## Fix\n\nAdd crew detection:\n```go\n// If in a rig's crew directory, extract address \nif strings.Contains(cwd, \"/crew/\") {\n parts := strings.Split(cwd, \"/crew/\")\n if len(parts) \u003e= 2 {\n rigPath := parts[0]\n crewName := strings.Split(parts[1], \"/\")[0]\n rigName := filepath.Base(rigPath)\n return fmt.Sprintf(\"%s/%s\", rigName, crewName)\n }\n}\n```\n\n## Also Check\n\n- `gt prime` correctly detects role, so there may be another detection function that works\n- Should unify detection logic","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-18T21:40:26.520559-08:00","updated_at":"2025-12-18T21:55:54.679969-08:00","closed_at":"2025-12-18T21:55:54.679969-08:00","dependencies":[{"issue_id":"gt-70b3","depends_on_id":"gt-l4gm","type":"blocks","created_at":"2025-12-18T21:50:04.812663-08:00","created_by":"daemon"}]} {"id":"gt-70b3","title":"detectSender() doesn't recognize crew workers","description":"## Problem\n\n`detectSender()` in mail.go only detects polecats, not crew workers.\n\n## Current Code (line 445)\n\n```go\n// If in a rig's polecats directory, extract address\nif strings.Contains(cwd, \"/polecats/\") {\n // extract rig/polecat\n}\n\n// Default to mayor\nreturn \"mayor/\"\n```\n\n## Symptom\n\nEmma (crew worker at `/Users/stevey/gt/beads/crew/emma`) runs:\n- `gt mail inbox` → checks `mayor/` inbox (wrong!)\n- Should check `beads/emma` or `beads/crew/emma`\n\n## Fix\n\nAdd crew detection:\n```go\n// If in a rig's crew directory, extract address \nif strings.Contains(cwd, \"/crew/\") {\n parts := strings.Split(cwd, \"/crew/\")\n if len(parts) \u003e= 2 {\n rigPath := parts[0]\n crewName := strings.Split(parts[1], \"/\")[0]\n rigName := filepath.Base(rigPath)\n return fmt.Sprintf(\"%s/%s\", rigName, crewName)\n }\n}\n```\n\n## Also Check\n\n- `gt prime` correctly detects role, so there may be another detection function that works\n- Should unify detection logic","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-18T21:40:26.520559-08:00","updated_at":"2025-12-18T21:55:54.679969-08:00","closed_at":"2025-12-18T21:55:54.679969-08:00","dependencies":[{"issue_id":"gt-70b3","depends_on_id":"gt-l4gm","type":"blocks","created_at":"2025-12-18T21:50:04.812663-08:00","created_by":"daemon"}]}
{"id":"gt-71i","title":"Update architecture.md: Engineer role and Beads merge queue","description":"Update docs/architecture.md with recent design decisions:\n\n1. Agent table: Change \"Refinery\" role to \"Engineer\"\n - Refinery = place/module/directory\n - Engineer = role (agent that works in the Refinery)\n\n2. Merge Queue section: Document Beads-native model\n - MRs are beads issues with --type=merge-request\n - gt mq commands (submit, list, next, process, reorder)\n - Ordering via depends-on links\n\n3. CLI section: Add gt mq commands\n\n4. Key Design Decisions: Add decisions for:\n - #15: Merge Queue in Beads\n - #16: Engineer role (distinct from Refinery place)\n - #17: Session restart protocol for Engineer","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-16T23:12:03.616159-08:00","updated_at":"2025-12-16T23:12:03.616159-08:00","dependencies":[{"issue_id":"gt-71i","depends_on_id":"gt-h5n","type":"blocks","created_at":"2025-12-16T23:12:14.92163-08:00","created_by":"daemon"}]} {"id":"gt-71i","title":"Update architecture.md: Engineer role and Beads merge queue","description":"Update docs/architecture.md with recent design decisions:\n\n1. Agent table: Change \"Refinery\" role to \"Engineer\"\n - Refinery = place/module/directory\n - Engineer = role (agent that works in the Refinery)\n\n2. Merge Queue section: Document Beads-native model\n - MRs are beads issues with --type=merge-request\n - gt mq commands (submit, list, next, process, reorder)\n - Ordering via depends-on links\n\n3. CLI section: Add gt mq commands\n\n4. Key Design Decisions: Add decisions for:\n - #15: Merge Queue in Beads\n - #16: Engineer role (distinct from Refinery place)\n - #17: Session restart protocol for Engineer","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-16T23:12:03.616159-08:00","updated_at":"2025-12-16T23:12:03.616159-08:00","dependencies":[{"issue_id":"gt-71i","depends_on_id":"gt-h5n","type":"blocks","created_at":"2025-12-16T23:12:14.92163-08:00","created_by":"daemon"}]}
{"id":"gt-72so","title":"gt mq list: doesn't show submitted MRs","description":"After submitting MRs with gt mq submit, gt mq list gastown shows empty queue.\n\n## Reproduction\n1. gt mq submit --issue gt-h5n.5 --branch polecat/Scabrous\n2. gt mq list gastown → (empty)\n3. bd list --type=merge-request → shows the MR\n\n## Expected\ngt mq list should show submitted MRs\n\n## MR example\ngt-ts4u has rig: gastown in description, type=merge-request, status=open","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-19T14:54:26.731813-08:00","updated_at":"2025-12-19T17:22:52.552662-08:00","closed_at":"2025-12-19T16:19:53.82455-08:00"} {"id":"gt-72so","title":"gt mq list: doesn't show submitted MRs","description":"After submitting MRs with gt mq submit, gt mq list gastown shows empty queue.\n\n## Reproduction\n1. gt mq submit --issue gt-h5n.5 --branch polecat/Scabrous\n2. gt mq list gastown → (empty)\n3. bd list --type=merge-request → shows the MR\n\n## Expected\ngt mq list should show submitted MRs\n\n## MR example\ngt-ts4u has rig: gastown in description, type=merge-request, status=open","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-19T14:54:26.731813-08:00","updated_at":"2025-12-19T17:22:52.552662-08:00","closed_at":"2025-12-19T16:19:53.82455-08:00"}
@@ -162,6 +163,7 @@
{"id":"gt-bj6f","title":"gt prime: Refinery context detection and output","description":"Update gt prime to detect Refinery role:\n- Detect from directory: refinery/rig/ = Refinery\n- Show handoff bead reference\n- Show merge queue status\n- Show polecat branches with unmerged commits\n- Show last actions summary","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T18:09:09.31791-08:00","updated_at":"2025-12-19T18:09:09.31791-08:00","dependencies":[{"issue_id":"gt-bj6f","depends_on_id":"gt-ktal","type":"blocks","created_at":"2025-12-19T18:09:39.462238-08:00","created_by":"daemon"}]} {"id":"gt-bj6f","title":"gt prime: Refinery context detection and output","description":"Update gt prime to detect Refinery role:\n- Detect from directory: refinery/rig/ = Refinery\n- Show handoff bead reference\n- Show merge queue status\n- Show polecat branches with unmerged commits\n- Show last actions summary","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T18:09:09.31791-08:00","updated_at":"2025-12-19T18:09:09.31791-08:00","dependencies":[{"issue_id":"gt-bj6f","depends_on_id":"gt-ktal","type":"blocks","created_at":"2025-12-19T18:09:39.462238-08:00","created_by":"daemon"}]}
{"id":"gt-bjft","title":"gt spawn should auto-start refinery/witness if not running","description":"When spawning a polecat, the infrastructure (refinery, witness) should already be running.\n\n## Current Behavior\nspawn.go only:1. Assigns issue to polecat2. Sends work mail3. Starts polecat session\n\nThe refinery and witness must be started separately.\n\n## Expected Behavior (per user feedback)\nWhen spawning a polecat, if the rig's refinery or witness is not running, auto-start them.\n\n## Options\n\n### Option A: spawn auto-starts infrastructure\nCheck if refinery/witness running before spawn, start if not.\n\n### Option B: gt swarm start \u003crig\u003e command\nExplicit command that:\n1. Starts refinery\n2. Starts witness\n3. Optionally spawns polecats on bd ready issues\n\n### Option C: gt rig start \u003crig\u003e\nSimilar to Option B but as rig lifecycle command.\n\n## Related\n- gt-n7z7: refinery --foreground race condition bug\n- gt-u1j.18: witness CLI commands","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-20T00:58:10.146332-08:00","updated_at":"2025-12-20T00:58:10.146332-08:00"} {"id":"gt-bjft","title":"gt spawn should auto-start refinery/witness if not running","description":"When spawning a polecat, the infrastructure (refinery, witness) should already be running.\n\n## Current Behavior\nspawn.go only:1. Assigns issue to polecat2. Sends work mail3. Starts polecat session\n\nThe refinery and witness must be started separately.\n\n## Expected Behavior (per user feedback)\nWhen spawning a polecat, if the rig's refinery or witness is not running, auto-start them.\n\n## Options\n\n### Option A: spawn auto-starts infrastructure\nCheck if refinery/witness running before spawn, start if not.\n\n### Option B: gt swarm start \u003crig\u003e command\nExplicit command that:\n1. Starts refinery\n2. Starts witness\n3. Optionally spawns polecats on bd ready issues\n\n### Option C: gt rig start \u003crig\u003e\nSimilar to Option B but as rig lifecycle command.\n\n## Related\n- gt-n7z7: refinery --foreground race condition bug\n- gt-u1j.18: witness CLI commands","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-20T00:58:10.146332-08:00","updated_at":"2025-12-20T00:58:10.146332-08:00"}
{"id":"gt-bmjw","title":"gt polecat add: should handle existing branch gracefully","description":"## Problem\n\n`gt polecat add gastown Nux` fails if the branch `polecat/Nux` already exists.\n\n## Current Behavior\n\n```\nfatal: a branch named 'polecat/Nux' already exists\n```\n\n## Expected Behavior\n\nShould either:\n1. Reuse the existing branch\n2. Or prompt to delete/recreate\n3. Or auto-suffix: polecat/Nux-2\n\n## Context\n\nBranch may exist from previous polecat that was removed but branch wasn't cleaned up.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-18T21:52:09.361672-08:00","updated_at":"2025-12-20T03:08:48.858604-08:00","closed_at":"2025-12-20T03:08:48.858604-08:00","close_reason":"Fixed in commit 8b659de"} {"id":"gt-bmjw","title":"gt polecat add: should handle existing branch gracefully","description":"## Problem\n\n`gt polecat add gastown Nux` fails if the branch `polecat/Nux` already exists.\n\n## Current Behavior\n\n```\nfatal: a branch named 'polecat/Nux' already exists\n```\n\n## Expected Behavior\n\nShould either:\n1. Reuse the existing branch\n2. Or prompt to delete/recreate\n3. Or auto-suffix: polecat/Nux-2\n\n## Context\n\nBranch may exist from previous polecat that was removed but branch wasn't cleaned up.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-18T21:52:09.361672-08:00","updated_at":"2025-12-20T03:08:48.858604-08:00","closed_at":"2025-12-20T03:08:48.858604-08:00","close_reason":"Fixed in commit 8b659de"}
{"id":"gt-bnik","title":"gt nudge should accept partial/fuzzy session names","description":"Currently gt nudge requires the exact tmux session name (e.g., gt-gastown-crew-max). Should be more forgiving:\n\n1. Accept partial matches when unambiguous (e.g., 'max' → gt-gastown-crew-max)\n2. Accept shorthand like 'gastown/max' or 'crew/max'\n3. Show helpful error with suggestions when ambiguous\n\nExamples that should work:\n- gt nudge max '...' → matches gt-gastown-crew-max\n- gt nudge gastown/max '...' → matches gt-gastown-crew-max\n- gt nudge beads-dave '...' → matches gt-beads-crew-dave","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-20T17:53:44.834337-08:00","updated_at":"2025-12-20T17:53:44.834337-08:00"}
{"id":"gt-bqbw","title":"detectSender() doesn't recognize crew workers","description":"## Problem\n\ndetectSender() in internal/cmd/mail.go only checks for /polecats/ directories. Crew workers in /crew/\u003cname\u003e/ fall through to the default 'mayor/', so:\n- gt mail inbox shows mayor's inbox instead of the crew worker's\n- gt mail send sets the wrong From address\n\n## Fix\n\nAdd crew worker detection before the /polecats/ check:\n\nif strings.Contains(cwd, \"/crew/\") {\n parts := strings.Split(cwd, \"/crew/\")\n ...\n return fmt.Sprintf(\"%s/crew/%s\", rigName, crewMember)\n}\n\n## Affected\n- Any crew worker running gt mail inbox without explicit address\n- Crew worker handoffs (wrong sender)","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-18T20:09:42.556373-08:00","updated_at":"2025-12-19T01:33:49.861756-08:00","closed_at":"2025-12-19T01:33:49.861756-08:00"} {"id":"gt-bqbw","title":"detectSender() doesn't recognize crew workers","description":"## Problem\n\ndetectSender() in internal/cmd/mail.go only checks for /polecats/ directories. Crew workers in /crew/\u003cname\u003e/ fall through to the default 'mayor/', so:\n- gt mail inbox shows mayor's inbox instead of the crew worker's\n- gt mail send sets the wrong From address\n\n## Fix\n\nAdd crew worker detection before the /polecats/ check:\n\nif strings.Contains(cwd, \"/crew/\") {\n parts := strings.Split(cwd, \"/crew/\")\n ...\n return fmt.Sprintf(\"%s/crew/%s\", rigName, crewMember)\n}\n\n## Affected\n- Any crew worker running gt mail inbox without explicit address\n- Crew worker handoffs (wrong sender)","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-18T20:09:42.556373-08:00","updated_at":"2025-12-19T01:33:49.861756-08:00","closed_at":"2025-12-19T01:33:49.861756-08:00"}
{"id":"gt-bxi8","title":"bd mail send: pinned column missing from schema","description":"gt mail send fails with: sqlite3: SQL logic error: table issues has no column named pinned\n\nLikely a schema migration issue - bd schema has 'pinned' field but SQLite table doesn't.\n\n## Reproduction\ngt mail send gastown/Immortan -s 'test' -m 'test'\n\n## Error\ntable issues has no column named pinned\n\n## Fix\nEither:\n1. Add migration to add pinned column\n2. Remove pinned from insert if not present\n3. Regenerate DB from JSONL","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-19T14:53:09.262403-08:00","updated_at":"2025-12-19T17:22:52.553097-08:00","closed_at":"2025-12-19T16:17:03.637686-08:00"} {"id":"gt-bxi8","title":"bd mail send: pinned column missing from schema","description":"gt mail send fails with: sqlite3: SQL logic error: table issues has no column named pinned\n\nLikely a schema migration issue - bd schema has 'pinned' field but SQLite table doesn't.\n\n## Reproduction\ngt mail send gastown/Immortan -s 'test' -m 'test'\n\n## Error\ntable issues has no column named pinned\n\n## Fix\nEither:\n1. Add migration to add pinned column\n2. Remove pinned from insert if not present\n3. Regenerate DB from JSONL","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-19T14:53:09.262403-08:00","updated_at":"2025-12-19T17:22:52.553097-08:00","closed_at":"2025-12-19T16:17:03.637686-08:00"}
{"id":"gt-bzd","title":"beads: Stop searching upward when .beads found","description":"## Problem\n\nWhen running bd commands in a nested directory structure with multiple .beads directories, bd shows a confusing warning:\n\n```\n╔══════════════════════════════════════════════════════════════════════════╗\n║ WARNING: 2 beads databases detected in directory hierarchy ║\n╠══════════════════════════════════════════════════════════════════════════╣\n║ Multiple databases can cause confusion and database pollution. ║\n║ ║\n║ /Users/stevey/gt/gastown/.beads (261 issues) ║\n║ /Users/stevey/gt/.beads (21 issues) ║\n║ ║\n║ WARNING: Not using the closest database! Check your BEADS_DB setting. ║\n║ ║\n║ RECOMMENDED: Consolidate or remove unused databases to avoid confusion. ║\n╚══════════════════════════════════════════════════════════════════════════╝\n```\n\n## Why This Is Wrong\n\nIn Gas Town, nested .beads directories are **intentional and necessary**:\n- Town level: /Users/stevey/gt/.beads (mail, town-level issues)\n- Rig level: /Users/stevey/gt/gastown/.beads (gastown project issues)\n- Worker level: polecats have their own beads in worktrees\n\nThese are **unrelated** beads instances for different scopes. They should never be consolidated.\n\n## Expected Behavior\n\nWhen bd finds a .beads directory, it should:\n1. Use that directory (closest ancestor wins)\n2. **Stop searching upward** - do not look for parent .beads directories\n3. **No warning** about multiple databases\n\n## Current Behavior\n\nbd searches all the way up to root, finds all .beads directories, and warns about \"multiple databases\" even though they are separate, intentional instances.\n\n## Fix\n\nIn the database discovery code, stop the upward search as soon as a .beads directory is found. The first .beads found is the one to use, and parent directories are out of scope.\n\n## Note\n\nThis is a beads issue, filed here for tracking. Should be implemented in the beads codebase.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-18T19:09:44.295743-08:00","updated_at":"2025-12-19T00:44:28.741548-08:00","closed_at":"2025-12-19T00:44:28.741548-08:00"} {"id":"gt-bzd","title":"beads: Stop searching upward when .beads found","description":"## Problem\n\nWhen running bd commands in a nested directory structure with multiple .beads directories, bd shows a confusing warning:\n\n```\n╔══════════════════════════════════════════════════════════════════════════╗\n║ WARNING: 2 beads databases detected in directory hierarchy ║\n╠══════════════════════════════════════════════════════════════════════════╣\n║ Multiple databases can cause confusion and database pollution. ║\n║ ║\n║ /Users/stevey/gt/gastown/.beads (261 issues) ║\n║ /Users/stevey/gt/.beads (21 issues) ║\n║ ║\n║ WARNING: Not using the closest database! Check your BEADS_DB setting. ║\n║ ║\n║ RECOMMENDED: Consolidate or remove unused databases to avoid confusion. ║\n╚══════════════════════════════════════════════════════════════════════════╝\n```\n\n## Why This Is Wrong\n\nIn Gas Town, nested .beads directories are **intentional and necessary**:\n- Town level: /Users/stevey/gt/.beads (mail, town-level issues)\n- Rig level: /Users/stevey/gt/gastown/.beads (gastown project issues)\n- Worker level: polecats have their own beads in worktrees\n\nThese are **unrelated** beads instances for different scopes. They should never be consolidated.\n\n## Expected Behavior\n\nWhen bd finds a .beads directory, it should:\n1. Use that directory (closest ancestor wins)\n2. **Stop searching upward** - do not look for parent .beads directories\n3. **No warning** about multiple databases\n\n## Current Behavior\n\nbd searches all the way up to root, finds all .beads directories, and warns about \"multiple databases\" even though they are separate, intentional instances.\n\n## Fix\n\nIn the database discovery code, stop the upward search as soon as a .beads directory is found. The first .beads found is the one to use, and parent directories are out of scope.\n\n## Note\n\nThis is a beads issue, filed here for tracking. Should be implemented in the beads codebase.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-18T19:09:44.295743-08:00","updated_at":"2025-12-19T00:44:28.741548-08:00","closed_at":"2025-12-19T00:44:28.741548-08:00"}
@@ -224,6 +226,7 @@
{"id":"gt-foct","title":"Merge: gt-5af.6","description":"branch: polecat/Slit\ntarget: main\nsource_issue: gt-5af.6\nrig: gastown","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-19T17:27:12.608473-08:00","updated_at":"2025-12-19T18:26:14.104443-08:00","closed_at":"2025-12-19T17:48:44.619449-08:00"} {"id":"gt-foct","title":"Merge: gt-5af.6","description":"branch: polecat/Slit\ntarget: main\nsource_issue: gt-5af.6\nrig: gastown","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-19T17:27:12.608473-08:00","updated_at":"2025-12-19T18:26:14.104443-08:00","closed_at":"2025-12-19T17:48:44.619449-08:00"}
{"id":"gt-frs","title":"Polecat name pooling: Bounded reusable names","description":"Polecats reuse names from a bounded pool (50) with overflow to sequence numbers.\n\n## Naming Scheme\n- Pool: polecat-01 through polecat-50 (prefer low numbers)\n- Overflow: \u003crigname\u003e-\u003csequenceNumber\u003e (e.g., beads-51, gastown-52)\n\n## Design\n- Witness tracks which pool names are in use\n- On spawn: pick first available from pool\n- If pool exhausted: use rigname-N format\n- On completion: pool name returns, sequence numbers don't\n\n## Why?\n- User experience: tmux sessions survive polecat restarts\n- Users stay attached, see new polecat start (like mayor respawn loop)\n- Bounded resource usage for common case\n- Scales beyond 50 when needed\n\n## Implementation\n- Witness maintains name allocation in beads or local state\n- Tmux session runs respawn loop (like mayor)\n- Name released on graceful exit or when witness detects dead session","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-18T18:32:28.43866-08:00","updated_at":"2025-12-19T17:22:52.551244-08:00","closed_at":"2025-12-19T16:29:30.648439-08:00"} {"id":"gt-frs","title":"Polecat name pooling: Bounded reusable names","description":"Polecats reuse names from a bounded pool (50) with overflow to sequence numbers.\n\n## Naming Scheme\n- Pool: polecat-01 through polecat-50 (prefer low numbers)\n- Overflow: \u003crigname\u003e-\u003csequenceNumber\u003e (e.g., beads-51, gastown-52)\n\n## Design\n- Witness tracks which pool names are in use\n- On spawn: pick first available from pool\n- If pool exhausted: use rigname-N format\n- On completion: pool name returns, sequence numbers don't\n\n## Why?\n- User experience: tmux sessions survive polecat restarts\n- Users stay attached, see new polecat start (like mayor respawn loop)\n- Bounded resource usage for common case\n- Scales beyond 50 when needed\n\n## Implementation\n- Witness maintains name allocation in beads or local state\n- Tmux session runs respawn loop (like mayor)\n- Name released on graceful exit or when witness detects dead session","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-18T18:32:28.43866-08:00","updated_at":"2025-12-19T17:22:52.551244-08:00","closed_at":"2025-12-19T16:29:30.648439-08:00"}
{"id":"gt-fryp","title":"Merge: gt-ih0s","description":"branch: polecat/capable\ntarget: main\nsource_issue: gt-ih0s\nrig: gastown","status":"open","priority":1,"issue_type":"merge-request","created_at":"2025-12-20T03:53:33.935017-08:00","updated_at":"2025-12-20T03:53:33.935017-08:00"} {"id":"gt-fryp","title":"Merge: gt-ih0s","description":"branch: polecat/capable\ntarget: main\nsource_issue: gt-ih0s\nrig: gastown","status":"open","priority":1,"issue_type":"merge-request","created_at":"2025-12-20T03:53:33.935017-08:00","updated_at":"2025-12-20T03:53:33.935017-08:00"}
{"id":"gt-g1ud","title":"Direct test","description":"Testing direct bd create","status":"open","priority":2,"issue_type":"message","created_at":"2025-12-20T17:45:55.058067-08:00","updated_at":"2025-12-20T17:45:55.058067-08:00"}
{"id":"gt-g2d","title":"Mayor session cycling prompting","description":"Add session cycling section to Mayor CLAUDE.md template.\n\n## When to Cycle\n\nCycle proactively when:\n- Running for several hours\n- Context feels crowded (losing track of earlier state)\n- Major phase completed\n- About to start complex new work\n\n## Composing Handoff Notes\n\n1. Gather information:\n town status # Overall health\n town rigs # Each rig state\n town inbox # Pending messages\n bd ready # Work items\n\n2. Compose note with this structure:\n\n[HANDOFF_TYPE]: mayor_cycle\n[TIMESTAMP]: \u003ccurrent time\u003e\n[SESSION_DURATION]: \u003chow long running\u003e\n\n## Active Swarms\n\u003cper-rig swarm status\u003e\n\n## Rig Status\n\u003ctable of rig health\u003e\n\n## Pending Escalations\n\u003cissues needing your decision\u003e\n\n## In-Flight Decisions\n\u003cdecisions being made\u003e\n\n## Recent Actions\n\u003clast 5-10 things you did\u003e\n\n## Delegated Work\n\u003cwork sent to refineries\u003e\n\n## User Requests\n\u003cpending user asks\u003e\n\n## Next Steps\n\u003cwhat next session should do\u003e\n\n## Warnings/Notes\n\u003ccritical info for next session\u003e\n\n3. Send handoff:\n town mail send mayor/ -s \"Session Handoff\" -m \"\u003cnote\u003e\"\n\n4. End session - next instance picks up from handoff.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-15T20:15:26.188561-08:00","updated_at":"2025-12-15T20:48:39.861022-08:00","dependencies":[{"issue_id":"gt-g2d","depends_on_id":"gt-u82","type":"blocks","created_at":"2025-12-15T20:15:39.361163-08:00","created_by":"daemon"}]} {"id":"gt-g2d","title":"Mayor session cycling prompting","description":"Add session cycling section to Mayor CLAUDE.md template.\n\n## When to Cycle\n\nCycle proactively when:\n- Running for several hours\n- Context feels crowded (losing track of earlier state)\n- Major phase completed\n- About to start complex new work\n\n## Composing Handoff Notes\n\n1. Gather information:\n town status # Overall health\n town rigs # Each rig state\n town inbox # Pending messages\n bd ready # Work items\n\n2. Compose note with this structure:\n\n[HANDOFF_TYPE]: mayor_cycle\n[TIMESTAMP]: \u003ccurrent time\u003e\n[SESSION_DURATION]: \u003chow long running\u003e\n\n## Active Swarms\n\u003cper-rig swarm status\u003e\n\n## Rig Status\n\u003ctable of rig health\u003e\n\n## Pending Escalations\n\u003cissues needing your decision\u003e\n\n## In-Flight Decisions\n\u003cdecisions being made\u003e\n\n## Recent Actions\n\u003clast 5-10 things you did\u003e\n\n## Delegated Work\n\u003cwork sent to refineries\u003e\n\n## User Requests\n\u003cpending user asks\u003e\n\n## Next Steps\n\u003cwhat next session should do\u003e\n\n## Warnings/Notes\n\u003ccritical info for next session\u003e\n\n3. Send handoff:\n town mail send mayor/ -s \"Session Handoff\" -m \"\u003cnote\u003e\"\n\n4. End session - next instance picks up from handoff.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-15T20:15:26.188561-08:00","updated_at":"2025-12-15T20:48:39.861022-08:00","dependencies":[{"issue_id":"gt-g2d","depends_on_id":"gt-u82","type":"blocks","created_at":"2025-12-15T20:15:39.361163-08:00","created_by":"daemon"}]}
{"id":"gt-g44u","title":"Molecule Workflow Engine: Composable Crystallized Workflows","description":"# Epic: Molecule Workflow Engine\n\n**Vision**: Molecules are crystallized, composable, nondeterministic-idempotent workflow templates. Any worker can pick up where any other worker was interrupted and continue along the molecule.\n\n**Christmas Target**: Full molecule-based workflow engine operational by Dec 25, 2025.\n\n## The Core Concepts\n\n1. **Molecule**: Read-only workflow template (beads issue with type=molecule)\n2. **Atom/Step**: Individual work unit with prose instructions\n3. **Bond**: Dependency between steps\n4. **Polymer/Derived**: Molecule composed from other molecules\n5. **Instance**: Concrete beads created when molecule is attached to work\n\n## Key Features Needed\n\n### 1. Molecule Composition (Includes Directive)\nMolecules can include other molecules:\n\\`\\`\\`markdown\n## Molecule: gastown-polecat\nIncludes: mol-engineer-in-box\n\n## Step: install-binary\nBuild and install the local gt binary.\nNeeds: submit\n\\`\\`\\`\n\n### 2. Standard Molecules\n- mol-install-go-binary: Single step to build/install gt\n- mol-gastown-polecat: engineer-in-box + install-binary\n\n### 3. Spawn Integration\n\\`gt spawn --issue \u003cid\u003e --molecule \u003cmol-id\u003e\\` creates molecule instance then starts polecat on first ready step.\n\n### 4. Nondeterministic Idempotence\n- Steps are atomic (pending → in_progress → completed)\n- Any worker can pick up any ready step\n- Step timeout/recovery for stuck workers\n\n## Success Criteria\n- [ ] Polecats can be spawned with mol-gastown-polecat\n- [ ] Derived molecules work end-to-end\n- [ ] 10+ polecat swarm completes molecule workflows\n- [ ] install-go-binary step runs after successful merges","status":"closed","issue_type":"epic","created_at":"2025-12-19T15:49:32.005023-08:00","updated_at":"2025-12-19T16:23:08.857768-08:00","closed_at":"2025-12-19T16:23:08.857768-08:00"} {"id":"gt-g44u","title":"Molecule Workflow Engine: Composable Crystallized Workflows","description":"# Epic: Molecule Workflow Engine\n\n**Vision**: Molecules are crystallized, composable, nondeterministic-idempotent workflow templates. Any worker can pick up where any other worker was interrupted and continue along the molecule.\n\n**Christmas Target**: Full molecule-based workflow engine operational by Dec 25, 2025.\n\n## The Core Concepts\n\n1. **Molecule**: Read-only workflow template (beads issue with type=molecule)\n2. **Atom/Step**: Individual work unit with prose instructions\n3. **Bond**: Dependency between steps\n4. **Polymer/Derived**: Molecule composed from other molecules\n5. **Instance**: Concrete beads created when molecule is attached to work\n\n## Key Features Needed\n\n### 1. Molecule Composition (Includes Directive)\nMolecules can include other molecules:\n\\`\\`\\`markdown\n## Molecule: gastown-polecat\nIncludes: mol-engineer-in-box\n\n## Step: install-binary\nBuild and install the local gt binary.\nNeeds: submit\n\\`\\`\\`\n\n### 2. Standard Molecules\n- mol-install-go-binary: Single step to build/install gt\n- mol-gastown-polecat: engineer-in-box + install-binary\n\n### 3. Spawn Integration\n\\`gt spawn --issue \u003cid\u003e --molecule \u003cmol-id\u003e\\` creates molecule instance then starts polecat on first ready step.\n\n### 4. Nondeterministic Idempotence\n- Steps are atomic (pending → in_progress → completed)\n- Any worker can pick up any ready step\n- Step timeout/recovery for stuck workers\n\n## Success Criteria\n- [ ] Polecats can be spawned with mol-gastown-polecat\n- [ ] Derived molecules work end-to-end\n- [ ] 10+ polecat swarm completes molecule workflows\n- [ ] install-go-binary step runs after successful merges","status":"closed","issue_type":"epic","created_at":"2025-12-19T15:49:32.005023-08:00","updated_at":"2025-12-19T16:23:08.857768-08:00","closed_at":"2025-12-19T16:23:08.857768-08:00"}
{"id":"gt-g44u.1","title":"Molecule composition: Includes directive","description":"Add support for molecule composition via the Includes directive.\n\n## Format\n\\`\\`\\`markdown\n## Molecule: derived-name\nIncludes: mol-base-molecule\n\n## Step: additional-step\nAdditional instructions here.\nNeeds: step-from-base\n\\`\\`\\`\n\n## Implementation\n1. Add \\`Includes:\\` parsing to ParseMoleculeSteps()\n2. Resolve included molecule by ID\n3. Merge steps from included molecule\n4. Allow new steps to depend on included steps\n5. Support multiple includes (polymers)\n\n## Files to modify\n- internal/beads/molecule.go\n- internal/beads/molecule_test.go\n\n## Acceptance\n- [ ] Parse Includes directive\n- [ ] Resolve and merge included steps\n- [ ] Dependencies across molecules work\n- [ ] Multiple includes supported\n- [ ] Tests cover composition scenarios","status":"closed","issue_type":"task","created_at":"2025-12-19T15:50:08.981634-08:00","updated_at":"2025-12-19T16:03:55.055353-08:00","closed_at":"2025-12-19T16:03:55.055353-08:00","dependencies":[{"issue_id":"gt-g44u.1","depends_on_id":"gt-g44u","type":"parent-child","created_at":"2025-12-19T15:50:08.983662-08:00","created_by":"daemon"}]} {"id":"gt-g44u.1","title":"Molecule composition: Includes directive","description":"Add support for molecule composition via the Includes directive.\n\n## Format\n\\`\\`\\`markdown\n## Molecule: derived-name\nIncludes: mol-base-molecule\n\n## Step: additional-step\nAdditional instructions here.\nNeeds: step-from-base\n\\`\\`\\`\n\n## Implementation\n1. Add \\`Includes:\\` parsing to ParseMoleculeSteps()\n2. Resolve included molecule by ID\n3. Merge steps from included molecule\n4. Allow new steps to depend on included steps\n5. Support multiple includes (polymers)\n\n## Files to modify\n- internal/beads/molecule.go\n- internal/beads/molecule_test.go\n\n## Acceptance\n- [ ] Parse Includes directive\n- [ ] Resolve and merge included steps\n- [ ] Dependencies across molecules work\n- [ ] Multiple includes supported\n- [ ] Tests cover composition scenarios","status":"closed","issue_type":"task","created_at":"2025-12-19T15:50:08.981634-08:00","updated_at":"2025-12-19T16:03:55.055353-08:00","closed_at":"2025-12-19T16:03:55.055353-08:00","dependencies":[{"issue_id":"gt-g44u.1","depends_on_id":"gt-g44u","type":"parent-child","created_at":"2025-12-19T15:50:08.983662-08:00","created_by":"daemon"}]}
@@ -290,6 +293,7 @@
{"id":"gt-l9g","title":"Beads epic templates for batch work patterns","description":"Optional: Define templates for common batch work patterns.\n\n## Concept\n\nA template encodes a workflow pattern that can be instantiated as beads:\n\n```yaml\n# templates/batch-basic.yaml\nname: basic-batch\ndescription: Simple batch work pattern\nphases:\n - name: startup\n issues:\n - title: \"Verify workers ready\"\n - name: working\n # Actual work issues added separately\n - name: cleanup\n issues:\n - title: \"Merge all branches\"\n - title: \"Clean up workers\"\n - title: \"Report to Mayor\"\n```\n\n## Usage\n\n```bash\ngt spawn --template basic-batch --epic gt-u1j --workers 3\n```\n\nCreates beads epic with template phases + actual work from gt-u1j children.\n\n## Decision Point\n\nTemplates are OPTIONAL. The core design (beads as state, multi-wave orchestration) works without templates. Templates are sugar for common patterns.\n\nConsider deferring to P3 or dropping entirely if beads epics with dependencies suffice.\n\n## Note\n\nNo \"swarm IDs\" involved - templates just pre-populate epic/issue structure.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-16T01:51:24.399235-08:00","updated_at":"2025-12-16T17:26:08.868396-08:00"} {"id":"gt-l9g","title":"Beads epic templates for batch work patterns","description":"Optional: Define templates for common batch work patterns.\n\n## Concept\n\nA template encodes a workflow pattern that can be instantiated as beads:\n\n```yaml\n# templates/batch-basic.yaml\nname: basic-batch\ndescription: Simple batch work pattern\nphases:\n - name: startup\n issues:\n - title: \"Verify workers ready\"\n - name: working\n # Actual work issues added separately\n - name: cleanup\n issues:\n - title: \"Merge all branches\"\n - title: \"Clean up workers\"\n - title: \"Report to Mayor\"\n```\n\n## Usage\n\n```bash\ngt spawn --template basic-batch --epic gt-u1j --workers 3\n```\n\nCreates beads epic with template phases + actual work from gt-u1j children.\n\n## Decision Point\n\nTemplates are OPTIONAL. The core design (beads as state, multi-wave orchestration) works without templates. Templates are sugar for common patterns.\n\nConsider deferring to P3 or dropping entirely if beads epics with dependencies suffice.\n\n## Note\n\nNo \"swarm IDs\" involved - templates just pre-populate epic/issue structure.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-16T01:51:24.399235-08:00","updated_at":"2025-12-16T17:26:08.868396-08:00"}
{"id":"gt-le1a","title":"Merge: gt-3x1","description":"branch: polecat/Slit\ntarget: main\nsource_issue: gt-3x1\nrig: gastown","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-19T14:53:47.674479-08:00","updated_at":"2025-12-19T18:30:24.050697-08:00","closed_at":"2025-12-19T18:30:24.0507-08:00"} {"id":"gt-le1a","title":"Merge: gt-3x1","description":"branch: polecat/Slit\ntarget: main\nsource_issue: gt-3x1\nrig: gastown","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-19T14:53:47.674479-08:00","updated_at":"2025-12-19T18:30:24.050697-08:00","closed_at":"2025-12-19T18:30:24.0507-08:00"}
{"id":"gt-lno","title":"Swarm state persistence: manifest + state + events","description":"Upgrade swarm persistence to match PGT pattern.\n\n## Current State\nSingle .gastown/swarms.json with all swarms.\n\n## Target State (per-swarm)\n```\n.gastown/swarms/\n└── \u003cswarm-id\u003e/\n ├── manifest.json # Immutable config\n ├── state.json # Mutable progress\n ├── events.jsonl # Audit log\n └── report.md # Generated report\n```\n\n## File Schemas\n\n### manifest.json (immutable after creation)\n```json\n{\n \"id\": \"sw-1\",\n \"title\": \"Epic description\",\n \"epic_id\": \"gt-abc\",\n \"rig\": \"gastown\",\n \"base_commit\": \"abc123\",\n \"integration_branch\": \"swarm/sw-1\",\n \"target_branch\": \"main\",\n \"workers\": [\"Toast\", \"Nux\"],\n \"tasks\": [{\"id\": \"gt-xyz\", \"title\": \"...\"}],\n \"created_at\": \"2024-01-01T00:00:00Z\"\n}\n```\n\n### state.json (mutable)\n```json\n{\n \"state\": \"active\",\n \"task_states\": {\n \"gt-xyz\": {\"state\": \"merged\", \"assignee\": \"Toast\"}\n },\n \"updated_at\": \"2024-01-01T01:00:00Z\",\n \"error\": null\n}\n```\n\n### events.jsonl (append-only audit)\n```jsonl\n{\"event\": \"created\", \"ts\": \"...\", \"data\": {...}}\n{\"event\": \"task_assigned\", \"ts\": \"...\", \"data\": {...}}\n{\"event\": \"task_merged\", \"ts\": \"...\", \"data\": {...}}\n```\n\n## Benefits\n- Audit trail via events.jsonl\n- Manifest immutability prevents corruption\n- Cleaner separation of concerns\n- Per-swarm isolation\n\n## Migration\nKeep backward compat with swarms.json during transition.\n\n## Files to Modify\n- internal/swarm/manager.go: Refactor persistence\n- internal/cmd/swarm.go: SwarmStore → directory-based\n\n## Acceptance Criteria\n- [ ] Per-swarm directory structure\n- [ ] Events logged to JSONL\n- [ ] Manifest immutable after creation\n- [ ] List command aggregates from directories","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-16T14:48:14.151538-08:00","updated_at":"2025-12-16T17:23:19.716405-08:00","closed_at":"2025-12-16T17:23:19.716405-08:00"} {"id":"gt-lno","title":"Swarm state persistence: manifest + state + events","description":"Upgrade swarm persistence to match PGT pattern.\n\n## Current State\nSingle .gastown/swarms.json with all swarms.\n\n## Target State (per-swarm)\n```\n.gastown/swarms/\n└── \u003cswarm-id\u003e/\n ├── manifest.json # Immutable config\n ├── state.json # Mutable progress\n ├── events.jsonl # Audit log\n └── report.md # Generated report\n```\n\n## File Schemas\n\n### manifest.json (immutable after creation)\n```json\n{\n \"id\": \"sw-1\",\n \"title\": \"Epic description\",\n \"epic_id\": \"gt-abc\",\n \"rig\": \"gastown\",\n \"base_commit\": \"abc123\",\n \"integration_branch\": \"swarm/sw-1\",\n \"target_branch\": \"main\",\n \"workers\": [\"Toast\", \"Nux\"],\n \"tasks\": [{\"id\": \"gt-xyz\", \"title\": \"...\"}],\n \"created_at\": \"2024-01-01T00:00:00Z\"\n}\n```\n\n### state.json (mutable)\n```json\n{\n \"state\": \"active\",\n \"task_states\": {\n \"gt-xyz\": {\"state\": \"merged\", \"assignee\": \"Toast\"}\n },\n \"updated_at\": \"2024-01-01T01:00:00Z\",\n \"error\": null\n}\n```\n\n### events.jsonl (append-only audit)\n```jsonl\n{\"event\": \"created\", \"ts\": \"...\", \"data\": {...}}\n{\"event\": \"task_assigned\", \"ts\": \"...\", \"data\": {...}}\n{\"event\": \"task_merged\", \"ts\": \"...\", \"data\": {...}}\n```\n\n## Benefits\n- Audit trail via events.jsonl\n- Manifest immutability prevents corruption\n- Cleaner separation of concerns\n- Per-swarm isolation\n\n## Migration\nKeep backward compat with swarms.json during transition.\n\n## Files to Modify\n- internal/swarm/manager.go: Refactor persistence\n- internal/cmd/swarm.go: SwarmStore → directory-based\n\n## Acceptance Criteria\n- [ ] Per-swarm directory structure\n- [ ] Events logged to JSONL\n- [ ] Manifest immutable after creation\n- [ ] List command aggregates from directories","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-16T14:48:14.151538-08:00","updated_at":"2025-12-16T17:23:19.716405-08:00","closed_at":"2025-12-16T17:23:19.716405-08:00"}
{"id":"gt-lpki","title":"test message","status":"closed","priority":2,"issue_type":"message","created_at":"2025-12-20T17:41:51.652131-08:00","updated_at":"2025-12-20T17:41:59.8038-08:00","closed_at":"2025-12-20T17:41:59.8038-08:00"}
{"id":"gt-lxsw","title":"gt done: Command doesn't exist but documented in polecat CLAUDE.md","notes":"The polecat CLAUDE.md documents 'gt done' as the command to signal work is ready for merge queue, but running it gives 'unknown command'. Either implement the command or update the documentation.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-20T07:59:44.548479-08:00","updated_at":"2025-12-20T13:20:50.919481-08:00","closed_at":"2025-12-20T13:20:50.919481-08:00","close_reason":"Duplicate of gt-qna4 which is fixed and pending merge (gt-u0c1). The gt done command is implemented on branch polecat/capable."} {"id":"gt-lxsw","title":"gt done: Command doesn't exist but documented in polecat CLAUDE.md","notes":"The polecat CLAUDE.md documents 'gt done' as the command to signal work is ready for merge queue, but running it gives 'unknown command'. Either implement the command or update the documentation.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-20T07:59:44.548479-08:00","updated_at":"2025-12-20T13:20:50.919481-08:00","closed_at":"2025-12-20T13:20:50.919481-08:00","close_reason":"Duplicate of gt-qna4 which is fixed and pending merge (gt-u0c1). The gt done command is implemented on branch polecat/capable."}
{"id":"gt-mcjd","title":"Work on gt-o9j: Fix tmux status bar polecat count - exclu...","description":"Work on gt-o9j: Fix tmux status bar polecat count - exclude static roles (mayor, deacon, witnesses, refineries, docs, hop). Run 'bd show gt-o9j' for details.","status":"closed","priority":2,"issue_type":"task","assignee":"gastown/furiosa","created_at":"2025-12-20T07:52:51.623541-08:00","updated_at":"2025-12-20T07:56:41.861992-08:00","closed_at":"2025-12-20T07:56:41.861992-08:00","close_reason":"Bug fixed"} {"id":"gt-mcjd","title":"Work on gt-o9j: Fix tmux status bar polecat count - exclu...","description":"Work on gt-o9j: Fix tmux status bar polecat count - exclude static roles (mayor, deacon, witnesses, refineries, docs, hop). Run 'bd show gt-o9j' for details.","status":"closed","priority":2,"issue_type":"task","assignee":"gastown/furiosa","created_at":"2025-12-20T07:52:51.623541-08:00","updated_at":"2025-12-20T07:56:41.861992-08:00","closed_at":"2025-12-20T07:56:41.861992-08:00","close_reason":"Bug fixed"}
{"id":"gt-mxyj","title":"Witness session startup (gt witness start)","description":"Implement gt witness start \u003crig\u003e\n\nShould:\n1. Verify rig exists and has witness/ directory\n2. Check for existing witness session (don't double-start)\n3. Create tmux session: gt-\u003crig\u003e-witness\n4. Start Claude Code with --dangerously-skip-permissions\n5. Send gt prime to load context\n6. Send startup prompt\n\nSimilar pattern to refinery startup in internal/refinery/manager.go","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T03:14:20.38027-08:00","updated_at":"2025-12-20T09:25:31.369825-08:00","closed_at":"2025-12-20T09:25:31.369825-08:00","close_reason":"Implemented gt witness start with background tmux session, theming, and auto-priming","dependencies":[{"issue_id":"gt-mxyj","depends_on_id":"gt-53w6","type":"parent-child","created_at":"2025-12-20T03:14:37.170879-08:00","created_by":"daemon"},{"issue_id":"gt-mxyj","depends_on_id":"gt-ni6a","type":"blocks","created_at":"2025-12-20T03:14:38.764636-08:00","created_by":"daemon"}]} {"id":"gt-mxyj","title":"Witness session startup (gt witness start)","description":"Implement gt witness start \u003crig\u003e\n\nShould:\n1. Verify rig exists and has witness/ directory\n2. Check for existing witness session (don't double-start)\n3. Create tmux session: gt-\u003crig\u003e-witness\n4. Start Claude Code with --dangerously-skip-permissions\n5. Send gt prime to load context\n6. Send startup prompt\n\nSimilar pattern to refinery startup in internal/refinery/manager.go","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T03:14:20.38027-08:00","updated_at":"2025-12-20T09:25:31.369825-08:00","closed_at":"2025-12-20T09:25:31.369825-08:00","close_reason":"Implemented gt witness start with background tmux session, theming, and auto-priming","dependencies":[{"issue_id":"gt-mxyj","depends_on_id":"gt-53w6","type":"parent-child","created_at":"2025-12-20T03:14:37.170879-08:00","created_by":"daemon"},{"issue_id":"gt-mxyj","depends_on_id":"gt-ni6a","type":"blocks","created_at":"2025-12-20T03:14:38.764636-08:00","created_by":"daemon"}]}
@@ -297,8 +301,9 @@
{"id":"gt-n7z7","title":"Bug: refinery --foreground detects parent session as already running","description":"When gt refinery start gastown runs:\n1. Creates tmux session gt-gastown-refinery\n2. Sends 'gt refinery start gastown --foreground' into the session\n3. The foreground command checks HasSession() - finds the session it's inside\n4. Returns 'already running' error\n\nThe foreground mode check should either:\n- Skip the tmux session check (only check PID)\n- Use a different indicator that the daemon loop is running\n- Pass a flag to indicate we're being called from the background starter\n\nWorkaround: Manually run 'gt refinery start gastown --foreground' from a fresh session.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-20T00:56:20.326369-08:00","updated_at":"2025-12-20T03:52:11.20518-08:00","closed_at":"2025-12-20T03:52:11.20518-08:00","close_reason":"Fixed: Added timeout warnings to handoff wait, fixed refinery foreground race condition"} {"id":"gt-n7z7","title":"Bug: refinery --foreground detects parent session as already running","description":"When gt refinery start gastown runs:\n1. Creates tmux session gt-gastown-refinery\n2. Sends 'gt refinery start gastown --foreground' into the session\n3. The foreground command checks HasSession() - finds the session it's inside\n4. Returns 'already running' error\n\nThe foreground mode check should either:\n- Skip the tmux session check (only check PID)\n- Use a different indicator that the daemon loop is running\n- Pass a flag to indicate we're being called from the background starter\n\nWorkaround: Manually run 'gt refinery start gastown --foreground' from a fresh session.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-20T00:56:20.326369-08:00","updated_at":"2025-12-20T03:52:11.20518-08:00","closed_at":"2025-12-20T03:52:11.20518-08:00","close_reason":"Fixed: Added timeout warnings to handoff wait, fixed refinery foreground race condition"}
{"id":"gt-nam3","title":"Update docs to reflect molecule-first paradigm","description":"Gas Town is fundamentally a molecule execution engine. Documentation should reflect this more clearly.\n\n## Issues Found\n\n### 1. gt spawn examples show molecule as optional\nREADME.md line 116: `gt spawn --issue \u003cid\u003e # Start polecat on issue`\nShould emphasize: polecats execute molecules, not just issues.\n\n### 2. Architecture.md spawn examples inconsistent\nLine 344 shows molecule: `gt spawn --issue gt-xyz --molecule mol-engineer-in-box`\nLine 1434 shows without: `gt spawn --issue \u003cid\u003e`\n\n### 3. Config vs molecule distinction not clear\noutposts.yaml shows static policy - should note when molecules apply.\n\n### 4. Operational molecules section is good but buried\nLines 430-566 cover operational molecules well. Should be more prominent.\n\n## Updates Needed\n- [ ] README: Update spawn examples to show molecule usage\n- [ ] architecture.md: Ensure all spawn examples include molecules\n- [ ] architecture.md: Add section on \"when config vs molecule\"\n- [ ] architecture.md: Move operational molecules higher in document\n- [ ] Add principle: \"If it requires cognition, it's a molecule\"\n- [ ] federation-design.md: Note that policy can escalate to mol-outpost-assign\n\n## Key Message\nGas Town doesn't spawn workers on issues. It spawns workers on molecules.\nThe issue is just the seed data for the molecule execution.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T03:26:31.842406-08:00","updated_at":"2025-12-20T09:26:21.813833-08:00","closed_at":"2025-12-20T09:26:21.813833-08:00","close_reason":"Updated docs with molecule-first paradigm - all spawn examples include molecules, added Config vs Molecule section with cognition principle, updated federation-design.md with policy escalation note"} {"id":"gt-nam3","title":"Update docs to reflect molecule-first paradigm","description":"Gas Town is fundamentally a molecule execution engine. Documentation should reflect this more clearly.\n\n## Issues Found\n\n### 1. gt spawn examples show molecule as optional\nREADME.md line 116: `gt spawn --issue \u003cid\u003e # Start polecat on issue`\nShould emphasize: polecats execute molecules, not just issues.\n\n### 2. Architecture.md spawn examples inconsistent\nLine 344 shows molecule: `gt spawn --issue gt-xyz --molecule mol-engineer-in-box`\nLine 1434 shows without: `gt spawn --issue \u003cid\u003e`\n\n### 3. Config vs molecule distinction not clear\noutposts.yaml shows static policy - should note when molecules apply.\n\n### 4. Operational molecules section is good but buried\nLines 430-566 cover operational molecules well. Should be more prominent.\n\n## Updates Needed\n- [ ] README: Update spawn examples to show molecule usage\n- [ ] architecture.md: Ensure all spawn examples include molecules\n- [ ] architecture.md: Add section on \"when config vs molecule\"\n- [ ] architecture.md: Move operational molecules higher in document\n- [ ] Add principle: \"If it requires cognition, it's a molecule\"\n- [ ] federation-design.md: Note that policy can escalate to mol-outpost-assign\n\n## Key Message\nGas Town doesn't spawn workers on issues. It spawns workers on molecules.\nThe issue is just the seed data for the molecule execution.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T03:26:31.842406-08:00","updated_at":"2025-12-20T09:26:21.813833-08:00","closed_at":"2025-12-20T09:26:21.813833-08:00","close_reason":"Updated docs with molecule-first paradigm - all spawn examples include molecules, added Config vs Molecule section with cognition principle, updated federation-design.md with policy escalation note"}
{"id":"gt-ngkd","title":"Work on gt-ogr: Fix rig count in tmux status bar. The cou...","description":"Work on gt-ogr: Fix rig count in tmux status bar. The count is showing wrong. Run 'bd show gt-ogr' for details.","status":"closed","priority":2,"issue_type":"task","assignee":"gastown/slit","created_at":"2025-12-20T07:53:01.093695-08:00","updated_at":"2025-12-20T07:59:51.445668-08:00","closed_at":"2025-12-20T07:59:51.445668-08:00","close_reason":"Fixed rig count logic by adding extractRigFromSession() to handle both witness naming patterns (gt-\u003crig\u003e-witness and gt-witness-\u003crig\u003e), and isPolecatSession() to exclude witness/refinery/crew from polecat count"} {"id":"gt-ngkd","title":"Work on gt-ogr: Fix rig count in tmux status bar. The cou...","description":"Work on gt-ogr: Fix rig count in tmux status bar. The count is showing wrong. Run 'bd show gt-ogr' for details.","status":"closed","priority":2,"issue_type":"task","assignee":"gastown/slit","created_at":"2025-12-20T07:53:01.093695-08:00","updated_at":"2025-12-20T07:59:51.445668-08:00","closed_at":"2025-12-20T07:59:51.445668-08:00","close_reason":"Fixed rig count logic by adding extractRigFromSession() to handle both witness naming patterns (gt-\u003crig\u003e-witness and gt-witness-\u003crig\u003e), and isPolecatSession() to exclude witness/refinery/crew from polecat count"}
{"id":"gt-ngu1","title":"Pinned beads should appear first in mail inbox","description":"Currently bd mail inbox sorts by priority then date, but pinned beads (handoff context) should always appear at the top.\n\n**Current behavior:**\n- Pinned beads mixed in with regular mail based on priority/date\n\n**Expected behavior:**\n- Pinned beads always first in inbox (before priority sorting)\n- This enables handoff beads to be the default first item an agent sees\n\n**Implementation:**\n1. In mail.go runMailInbox(), after filtering, sort pinned beads to top\n2. Within pinned and non-pinned groups, maintain priority/date sort\n\n**Related:**\n- gt-r8ej: Implement pinned beads for handoff state\n- gt-8h4: Pinned Beads epic\n\n**Acceptance criteria:**\n- bd mail inbox shows pinned beads first\n- Pinned beads still sorted by priority/date among themselves\n- Non-pinned mail sorted normally after pinned section","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-20T17:45:24.133521-08:00","updated_at":"2025-12-20T17:45:24.133521-08:00","dependencies":[{"issue_id":"gt-ngu1","depends_on_id":"gt-8h4","type":"parent-child","created_at":"2025-12-20T17:45:31.978176-08:00","created_by":"daemon"}]} {"id":"gt-ngu1","title":"Pinned beads should appear first in mail inbox","description":"Currently bd mail inbox sorts by priority then date, but pinned beads (handoff context) should always appear at the top.\n\n**Current behavior:**\n- Pinned beads mixed in with regular mail based on priority/date\n\n**Expected behavior:**\n- Pinned beads always first in inbox (before priority sorting)\n- This enables handoff beads to be the default first item an agent sees\n\n**Implementation:**\n1. In mail.go runMailInbox(), after filtering, sort pinned beads to top\n2. Within pinned and non-pinned groups, maintain priority/date sort\n\n**Related:**\n- gt-r8ej: Implement pinned beads for handoff state\n- gt-8h4: Pinned Beads epic\n\n**Acceptance criteria:**\n- bd mail inbox shows pinned beads first\n- Pinned beads still sorted by priority/date among themselves\n- Non-pinned mail sorted normally after pinned section","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T17:45:24.133521-08:00","updated_at":"2025-12-20T18:03:04.871622-08:00","closed_at":"2025-12-20T18:03:04.871622-08:00","close_reason":"Implemented in GGT (gt mail inbox) with commit b3ef048. Added Pinned field to mail types and sorting in listBeads() to show pinned messages first.","dependencies":[{"issue_id":"gt-ngu1","depends_on_id":"gt-8h4","type":"parent-child","created_at":"2025-12-20T17:45:31.978176-08:00","created_by":"daemon"}]}
{"id":"gt-ni6a","title":"Witness role template + CLAUDE.md","description":"Create the Witness agent's role context:\n\n1. internal/templates/roles/witness.md.tmpl\n - Witness responsibilities (polecat lifecycle)\n - Core loop description\n - Commands available (gt polecats, bd ready, etc)\n - Escalation protocol\n\n2. Generate CLAUDE.md for witness/ directories\n - Context about the rig\n - Mail address: \u003crig\u003e/witness\n - Startup protocol (gt prime, check inbox)\n\nThe Witness is the per-rig 'pit boss' - NOT a global coordinator. It manages polecats for ONE rig only.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T03:14:18.617496-08:00","updated_at":"2025-12-20T03:54:13.697998-08:00","closed_at":"2025-12-20T03:54:13.697998-08:00","close_reason":"Fixed gt mail ack → gt mail delete in witness template; template renders correctly","dependencies":[{"issue_id":"gt-ni6a","depends_on_id":"gt-53w6","type":"parent-child","created_at":"2025-12-20T03:14:37.105264-08:00","created_by":"daemon"}]} {"id":"gt-ni6a","title":"Witness role template + CLAUDE.md","description":"Create the Witness agent's role context:\n\n1. internal/templates/roles/witness.md.tmpl\n - Witness responsibilities (polecat lifecycle)\n - Core loop description\n - Commands available (gt polecats, bd ready, etc)\n - Escalation protocol\n\n2. Generate CLAUDE.md for witness/ directories\n - Context about the rig\n - Mail address: \u003crig\u003e/witness\n - Startup protocol (gt prime, check inbox)\n\nThe Witness is the per-rig 'pit boss' - NOT a global coordinator. It manages polecats for ONE rig only.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T03:14:18.617496-08:00","updated_at":"2025-12-20T03:54:13.697998-08:00","closed_at":"2025-12-20T03:54:13.697998-08:00","close_reason":"Fixed gt mail ack → gt mail delete in witness template; template renders correctly","dependencies":[{"issue_id":"gt-ni6a","depends_on_id":"gt-53w6","type":"parent-child","created_at":"2025-12-20T03:14:37.105264-08:00","created_by":"daemon"}]}
{"id":"gt-njem","title":"Remove gt mail dependency on bd mail commands","description":"Replaced bd mail send/inbox/read/ack with bd create/list/show/close. Messages are now stored as beads issues with type=message.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-20T17:52:43.47722-08:00","updated_at":"2025-12-20T17:52:48.155447-08:00","closed_at":"2025-12-20T17:52:48.155447-08:00"}
{"id":"gt-njr","title":"Engineer session restart protocol","description":"Implement session restart flow for when the Engineer needs to split work:\n\n1. Engineer creates subtask(s) in Beads assigned to self\n2. Engineer sends handoff mail to self (🤝 HANDOFF)\n3. Engineer sends restart request to Witness\n4. Witness verifies:\n - Handoff mail exists in Engineer outbox/sent\n - Subtasks filed in Beads\n5. Witness restarts the Refinery session (new Engineer context)\n\nThis enables \"occasionally consistent, eventually convergent\" work patterns.\nThe Refinery continues; the Engineer gets fresh context.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-16T23:02:48.22994-08:00","updated_at":"2025-12-16T23:07:42.05363-08:00","dependencies":[{"issue_id":"gt-njr","depends_on_id":"gt-h5n","type":"blocks","created_at":"2025-12-16T23:02:56.148564-08:00","created_by":"daemon"}]} {"id":"gt-njr","title":"Engineer session restart protocol","description":"Implement session restart flow for when the Engineer needs to split work:\n\n1. Engineer creates subtask(s) in Beads assigned to self\n2. Engineer sends handoff mail to self (🤝 HANDOFF)\n3. Engineer sends restart request to Witness\n4. Witness verifies:\n - Handoff mail exists in Engineer outbox/sent\n - Subtasks filed in Beads\n5. Witness restarts the Refinery session (new Engineer context)\n\nThis enables \"occasionally consistent, eventually convergent\" work patterns.\nThe Refinery continues; the Engineer gets fresh context.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-16T23:02:48.22994-08:00","updated_at":"2025-12-16T23:07:42.05363-08:00","dependencies":[{"issue_id":"gt-njr","depends_on_id":"gt-h5n","type":"blocks","created_at":"2025-12-16T23:02:56.148564-08:00","created_by":"daemon"}]}
{"id":"gt-oc2","title":"Daemon: proper rig discovery","description":"Currently discovers rigs by scanning tmux session names for gt-*-witness pattern. Should instead:\n- Read rigs from mayor/rigs.json\n- Or scan town directory for .gastown subdirs\n- Handle rigs that exist but don't have running witnesses","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-18T13:38:15.825299-08:00","updated_at":"2025-12-19T17:22:52.554474-08:00","closed_at":"2025-12-19T16:28:39.497935-08:00","dependencies":[{"issue_id":"gt-oc2","depends_on_id":"gt-99m","type":"blocks","created_at":"2025-12-18T13:38:26.826697-08:00","created_by":"daemon"}]} {"id":"gt-oc2","title":"Daemon: proper rig discovery","description":"Currently discovers rigs by scanning tmux session names for gt-*-witness pattern. Should instead:\n- Read rigs from mayor/rigs.json\n- Or scan town directory for .gastown subdirs\n- Handle rigs that exist but don't have running witnesses","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-18T13:38:15.825299-08:00","updated_at":"2025-12-19T17:22:52.554474-08:00","closed_at":"2025-12-19T16:28:39.497935-08:00","dependencies":[{"issue_id":"gt-oc2","depends_on_id":"gt-99m","type":"blocks","created_at":"2025-12-18T13:38:26.826697-08:00","created_by":"daemon"}]}
{"id":"gt-on46","title":"Work on gt-fix-bugs: Fix blocking infrastructure bugs. Se...","description":"Work on gt-fix-bugs: Fix blocking infrastructure bugs. See issue for details. Run 'bd show gt-fix-bugs' to see the full issue.","status":"closed","priority":2,"issue_type":"task","assignee":"gastown/capable","created_at":"2025-12-20T03:47:14.322631-08:00","updated_at":"2025-12-20T03:52:16.049326-08:00","closed_at":"2025-12-20T03:52:16.049326-08:00","close_reason":"Completed: Fixed gt-dsfi and gt-n7z7 bugs"} {"id":"gt-on46","title":"Work on gt-fix-bugs: Fix blocking infrastructure bugs. Se...","description":"Work on gt-fix-bugs: Fix blocking infrastructure bugs. See issue for details. Run 'bd show gt-fix-bugs' to see the full issue.","status":"closed","priority":2,"issue_type":"task","assignee":"gastown/capable","created_at":"2025-12-20T03:47:14.322631-08:00","updated_at":"2025-12-20T03:52:16.049326-08:00","closed_at":"2025-12-20T03:52:16.049326-08:00","close_reason":"Completed: Fixed gt-dsfi and gt-n7z7 bugs"}
@@ -321,6 +326,7 @@
{"id":"gt-qrze","title":"Work on gt-role-template: Refine witness/CLAUDE.md role t...","description":"Work on gt-role-template: Refine witness/CLAUDE.md role template. Add clearer heartbeat protocol, mail checking procedure, nudge decision criteria, escalation thresholds. Run 'bd show gt-role-template' for details.","status":"closed","priority":2,"issue_type":"task","assignee":"gastown/dementus","created_at":"2025-12-20T07:53:27.305468-08:00","updated_at":"2025-12-20T07:58:39.269234-08:00","closed_at":"2025-12-20T07:58:39.269234-08:00","close_reason":"Refined witness/CLAUDE.md with clearer heartbeat protocol, mail checking procedure, nudge decision criteria, and escalation thresholds"} {"id":"gt-qrze","title":"Work on gt-role-template: Refine witness/CLAUDE.md role t...","description":"Work on gt-role-template: Refine witness/CLAUDE.md role template. Add clearer heartbeat protocol, mail checking procedure, nudge decision criteria, escalation thresholds. Run 'bd show gt-role-template' for details.","status":"closed","priority":2,"issue_type":"task","assignee":"gastown/dementus","created_at":"2025-12-20T07:53:27.305468-08:00","updated_at":"2025-12-20T07:58:39.269234-08:00","closed_at":"2025-12-20T07:58:39.269234-08:00","close_reason":"Refined witness/CLAUDE.md with clearer heartbeat protocol, mail checking procedure, nudge decision criteria, and escalation thresholds"}
{"id":"gt-qscw","title":"Merge: gt-72so","description":"branch: polecat/Ace\ntarget: main\nsource_issue: gt-72so\nrig: gastown","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-19T16:20:02.203335-08:00","updated_at":"2025-12-19T18:26:14.105337-08:00","closed_at":"2025-12-19T17:48:09.571107-08:00"} {"id":"gt-qscw","title":"Merge: gt-72so","description":"branch: polecat/Ace\ntarget: main\nsource_issue: gt-72so\nrig: gastown","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-19T16:20:02.203335-08:00","updated_at":"2025-12-19T18:26:14.105337-08:00","closed_at":"2025-12-19T17:48:09.571107-08:00"}
{"id":"gt-qsvq","title":"gt doctor: Detect and clean up orphaned sessions/processes","description":"## Problem\nOrphaned tmux sessions and Claude processes accumulate over time, consuming memory.\n\n## Discovered Orphans\n- gt-deacon: Idle Claude session, not part of any rig\n\n## Proposed Solution\n\n### gt doctor --check (default)\nDetect issues without fixing:\n- Orphan sessions (not matching rig/polecat/crew/witness/refinery/mayor pattern)\n- Claude processes without parent tmux session\n- Tmux sessions without Claude (stuck at bash prompt)\n- Polecats marked 'working' but session idle\n\n### gt doctor --fix\nClean up detected issues:\n- Kill orphan sessions\n- Kill orphan Claude processes\n- Optionally reset stuck polecat state\n\n### gt gc (alternative name)\nShort alias for cleanup operations.\n\n## Acceptance Criteria\n- [ ] Detects orphan sessions\n- [ ] Detects orphan processes\n- [ ] Safe cleanup (doesn't kill active work)\n- [ ] Reports what was cleaned","status":"closed","priority":1,"issue_type":"feature","assignee":"gastown/capable","created_at":"2025-12-20T01:06:39.046959-08:00","updated_at":"2025-12-20T07:54:51.210265-08:00","closed_at":"2025-12-20T07:54:51.210265-08:00","close_reason":"Implemented orphan session and process detection in gt doctor"} {"id":"gt-qsvq","title":"gt doctor: Detect and clean up orphaned sessions/processes","description":"## Problem\nOrphaned tmux sessions and Claude processes accumulate over time, consuming memory.\n\n## Discovered Orphans\n- gt-deacon: Idle Claude session, not part of any rig\n\n## Proposed Solution\n\n### gt doctor --check (default)\nDetect issues without fixing:\n- Orphan sessions (not matching rig/polecat/crew/witness/refinery/mayor pattern)\n- Claude processes without parent tmux session\n- Tmux sessions without Claude (stuck at bash prompt)\n- Polecats marked 'working' but session idle\n\n### gt doctor --fix\nClean up detected issues:\n- Kill orphan sessions\n- Kill orphan Claude processes\n- Optionally reset stuck polecat state\n\n### gt gc (alternative name)\nShort alias for cleanup operations.\n\n## Acceptance Criteria\n- [ ] Detects orphan sessions\n- [ ] Detects orphan processes\n- [ ] Safe cleanup (doesn't kill active work)\n- [ ] Reports what was cleaned","status":"closed","priority":1,"issue_type":"feature","assignee":"gastown/capable","created_at":"2025-12-20T01:06:39.046959-08:00","updated_at":"2025-12-20T07:54:51.210265-08:00","closed_at":"2025-12-20T07:54:51.210265-08:00","close_reason":"Implemented orphan session and process detection in gt doctor"}
{"id":"gt-qxei","title":"Test4","description":"test4 body","status":"closed","priority":2,"issue_type":"message","created_at":"2025-12-20T17:47:12.137051-08:00","updated_at":"2025-12-20T17:51:08.785516-08:00","closed_at":"2025-12-20T17:51:08.785516-08:00"}
{"id":"gt-r01","title":"EXTERNAL: Beads Messaging \u0026 Knowledge Graph (bd-kwro)","description":"Tracking issue for external dependency on Beads v0.30.2 messaging features.\n\nBeads epic: bd-kwro in ~/src/beads (steveyegge/beads repo)\n\nThis blocks GGT work that depends on:\n- bd mail send/inbox/read/ack commands\n- message issue type\n- replies_to threading\n- Hooks system for notifications\n- Identity configuration\n\nGGT mail commands will be thin wrappers around bd mail once available.\n\nWhen bd-kwro ships in Beads v0.30.2, close this and unblock dependent work.","status":"closed","issue_type":"task","created_at":"2025-12-16T13:12:02.676883-08:00","updated_at":"2025-12-16T21:35:05.795776-08:00","closed_at":"2025-12-16T21:35:05.795776-08:00"} {"id":"gt-r01","title":"EXTERNAL: Beads Messaging \u0026 Knowledge Graph (bd-kwro)","description":"Tracking issue for external dependency on Beads v0.30.2 messaging features.\n\nBeads epic: bd-kwro in ~/src/beads (steveyegge/beads repo)\n\nThis blocks GGT work that depends on:\n- bd mail send/inbox/read/ack commands\n- message issue type\n- replies_to threading\n- Hooks system for notifications\n- Identity configuration\n\nGGT mail commands will be thin wrappers around bd mail once available.\n\nWhen bd-kwro ships in Beads v0.30.2, close this and unblock dependent work.","status":"closed","issue_type":"task","created_at":"2025-12-16T13:12:02.676883-08:00","updated_at":"2025-12-16T21:35:05.795776-08:00","closed_at":"2025-12-16T21:35:05.795776-08:00"}
{"id":"gt-r8ej","title":"Implement pinned beads for handoff state","description":"Add pinned bead support to beads:\n- Pinned beads never close, only update\n- Use for persistent state like Refinery handoffs\n- bd create --pinned flag\n- bd list --pinned to find them\n- Update description to change state","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T18:09:08.019293-08:00","updated_at":"2025-12-19T18:09:08.019293-08:00","dependencies":[{"issue_id":"gt-r8ej","depends_on_id":"gt-ktal","type":"blocks","created_at":"2025-12-19T18:09:39.340915-08:00","created_by":"daemon"}]} {"id":"gt-r8ej","title":"Implement pinned beads for handoff state","description":"Add pinned bead support to beads:\n- Pinned beads never close, only update\n- Use for persistent state like Refinery handoffs\n- bd create --pinned flag\n- bd list --pinned to find them\n- Update description to change state","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T18:09:08.019293-08:00","updated_at":"2025-12-19T18:09:08.019293-08:00","dependencies":[{"issue_id":"gt-r8ej","depends_on_id":"gt-ktal","type":"blocks","created_at":"2025-12-19T18:09:39.340915-08:00","created_by":"daemon"}]}
{"id":"gt-rixa","title":"Bug: parseLifecycleRequest always matches 'cycle' due to LIFECYCLE prefix","description":"In lifecycle.go, parseLifecycleRequest checks strings.Contains(title, \"cycle\") first, but the prefix \"LIFECYCLE:\" contains the word \"cycle\". This means ALL lifecycle messages match the cycle action, making restart and shutdown unreachable. Fix: check for restart/shutdown before cycle, or use word boundaries.","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-19T16:17:16.083512-08:00","updated_at":"2025-12-19T16:17:24.480543-08:00"} {"id":"gt-rixa","title":"Bug: parseLifecycleRequest always matches 'cycle' due to LIFECYCLE prefix","description":"In lifecycle.go, parseLifecycleRequest checks strings.Contains(title, \"cycle\") first, but the prefix \"LIFECYCLE:\" contains the word \"cycle\". This means ALL lifecycle messages match the cycle action, making restart and shutdown unreachable. Fix: check for restart/shutdown before cycle, or use word boundaries.","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-19T16:17:16.083512-08:00","updated_at":"2025-12-19T16:17:24.480543-08:00"}
@@ -344,6 +350,7 @@
{"id":"gt-sye","title":"Mayor startup protocol prompting","description":"Add startup protocol to Mayor CLAUDE.md template.\n\n## On Session Start\n\n1. Check for handoff:\n town inbox | grep \"Session Handoff\"\n\n2. If handoff found:\n - Read it: town read \u003cmsg-id\u003e\n - Process pending escalations (highest priority)\n - Check status of noted swarms\n - Verify rig health matches notes\n - Continue with documented next steps\n\n3. If no handoff:\n town status # Overall health\n town rigs # Each rig\n bd ready # Work items\n town inbox # Any messages\n Build your own picture of current state.\n\n4. After processing handoff:\n - Archive or delete the handoff message\n - You now own the current state\n\n## Handoff Best Practices\n\n- Be specific: 'Toast has merge conflict in auth/middleware.go' not 'Toast is stuck'\n- Include context: Why decisions are pending, what you were thinking\n- Prioritize next steps: What is most urgent\n- Note time-sensitive items: Anything that might have changed since handoff","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-15T20:15:27.915484-08:00","updated_at":"2025-12-15T20:48:57.555724-08:00","dependencies":[{"issue_id":"gt-sye","depends_on_id":"gt-u82","type":"blocks","created_at":"2025-12-15T20:15:39.459108-08:00","created_by":"daemon"}]} {"id":"gt-sye","title":"Mayor startup protocol prompting","description":"Add startup protocol to Mayor CLAUDE.md template.\n\n## On Session Start\n\n1. Check for handoff:\n town inbox | grep \"Session Handoff\"\n\n2. If handoff found:\n - Read it: town read \u003cmsg-id\u003e\n - Process pending escalations (highest priority)\n - Check status of noted swarms\n - Verify rig health matches notes\n - Continue with documented next steps\n\n3. If no handoff:\n town status # Overall health\n town rigs # Each rig\n bd ready # Work items\n town inbox # Any messages\n Build your own picture of current state.\n\n4. After processing handoff:\n - Archive or delete the handoff message\n - You now own the current state\n\n## Handoff Best Practices\n\n- Be specific: 'Toast has merge conflict in auth/middleware.go' not 'Toast is stuck'\n- Include context: Why decisions are pending, what you were thinking\n- Prioritize next steps: What is most urgent\n- Note time-sensitive items: Anything that might have changed since handoff","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-15T20:15:27.915484-08:00","updated_at":"2025-12-15T20:48:57.555724-08:00","dependencies":[{"issue_id":"gt-sye","depends_on_id":"gt-u82","type":"blocks","created_at":"2025-12-15T20:15:39.459108-08:00","created_by":"daemon"}]}
{"id":"gt-szsq","title":"gt spawn --create should auto-add polecat if missing","description":"## Problem\n\n`gt spawn gastown --issue gt-xxx --create` fails if no polecats exist.\n\n## Current Behavior\n\n```\nError: auto-select polecat: no available polecats in rig 'gastown'\n```\n\n## Expected Behavior\n\n`--create` should:\n1. Create a new polecat if none exist\n2. Or create the specified polecat if `gt spawn gastown/NewName --create`\n\n## Workaround\n\nMust manually run `gt polecat add gastown Name` first.\n\n## Principle\n\nAgent UX should be forgiving - if I say spawn with --create, make it work.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-18T21:52:07.75062-08:00","updated_at":"2025-12-19T01:33:49.859926-08:00","closed_at":"2025-12-19T01:33:49.859926-08:00"} {"id":"gt-szsq","title":"gt spawn --create should auto-add polecat if missing","description":"## Problem\n\n`gt spawn gastown --issue gt-xxx --create` fails if no polecats exist.\n\n## Current Behavior\n\n```\nError: auto-select polecat: no available polecats in rig 'gastown'\n```\n\n## Expected Behavior\n\n`--create` should:\n1. Create a new polecat if none exist\n2. Or create the specified polecat if `gt spawn gastown/NewName --create`\n\n## Workaround\n\nMust manually run `gt polecat add gastown Name` first.\n\n## Principle\n\nAgent UX should be forgiving - if I say spawn with --create, make it work.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-18T21:52:07.75062-08:00","updated_at":"2025-12-19T01:33:49.859926-08:00","closed_at":"2025-12-19T01:33:49.859926-08:00"}
{"id":"gt-t2vj","title":"Merge: gt-8v8","description":"branch: polecat/nux\ntarget: main\nsource_issue: gt-8v8\nrig: gastown","status":"open","priority":1,"issue_type":"merge-request","created_at":"2025-12-20T16:31:37.562534-08:00","updated_at":"2025-12-20T16:31:37.562534-08:00"} {"id":"gt-t2vj","title":"Merge: gt-8v8","description":"branch: polecat/nux\ntarget: main\nsource_issue: gt-8v8\nrig: gastown","status":"open","priority":1,"issue_type":"merge-request","created_at":"2025-12-20T16:31:37.562534-08:00","updated_at":"2025-12-20T16:31:37.562534-08:00"}
{"id":"gt-t5pc","title":"test with labels","status":"closed","priority":2,"issue_type":"message","created_at":"2025-12-20T17:42:30.258069-08:00","updated_at":"2025-12-20T17:43:00.502696-08:00","closed_at":"2025-12-20T17:43:00.502696-08:00"}
{"id":"gt-tca","title":"Polecats should auto-cleanup after MR submission","description":"Currently polecats must manually run 'gt handoff --shutdown' after completing work. This is error-prone and leaves stale polecats around.\n\n## Desired Flow\n\n1. Polecat completes work\n2. Polecat runs 'gt mq submit' (or similar)\n3. MR is added to integration queue\n4. **Polecat automatically cleans up** (no manual handoff needed)\n\n## Implementation Options\n\n### Option A: mq submit triggers cleanup\nIn 'gt mq submit':\n1. Submit MR to queue\n2. Automatically run cleanup (same as gt handoff --shutdown)\n3. Polecat session terminates\n\n### Option B: Refinery triggers cleanup\nWhen Refinery picks up MR:\n1. Refinery processes MR\n2. Sends message to Witness: 'CLEANUP: \u003cpolecat\u003e'\n3. Witness cleans up polecat\n\n### Option C: Molecule-driven\nDefine cleanup as final phase of polecat-work molecule:\n1. code → test → submit-mr → cleanup\n\n## Note\nThis reinforces the ephemeral model: polecats exist only for the duration of a single task.","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-20T15:22:54.485456-08:00","updated_at":"2025-12-20T15:22:54.485456-08:00","dependencies":[{"issue_id":"gt-tca","depends_on_id":"gt-9nf","type":"related","created_at":"2025-12-20T15:40:08.998908-08:00","created_by":"daemon"}]} {"id":"gt-tca","title":"Polecats should auto-cleanup after MR submission","description":"Currently polecats must manually run 'gt handoff --shutdown' after completing work. This is error-prone and leaves stale polecats around.\n\n## Desired Flow\n\n1. Polecat completes work\n2. Polecat runs 'gt mq submit' (or similar)\n3. MR is added to integration queue\n4. **Polecat automatically cleans up** (no manual handoff needed)\n\n## Implementation Options\n\n### Option A: mq submit triggers cleanup\nIn 'gt mq submit':\n1. Submit MR to queue\n2. Automatically run cleanup (same as gt handoff --shutdown)\n3. Polecat session terminates\n\n### Option B: Refinery triggers cleanup\nWhen Refinery picks up MR:\n1. Refinery processes MR\n2. Sends message to Witness: 'CLEANUP: \u003cpolecat\u003e'\n3. Witness cleans up polecat\n\n### Option C: Molecule-driven\nDefine cleanup as final phase of polecat-work molecule:\n1. code → test → submit-mr → cleanup\n\n## Note\nThis reinforces the ephemeral model: polecats exist only for the duration of a single task.","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-20T15:22:54.485456-08:00","updated_at":"2025-12-20T15:22:54.485456-08:00","dependencies":[{"issue_id":"gt-tca","depends_on_id":"gt-9nf","type":"related","created_at":"2025-12-20T15:40:08.998908-08:00","created_by":"daemon"}]}
{"id":"gt-th7","title":"Add agent abstraction layer to support non-Claude agents","description":"Currently Gas Town hardcodes 'claude --dangerously-skip-permissions' throughout the codebase for spawning agents. We should add an abstraction layer to support other AI agents (e.g., Gemini CLI, OpenAI agents, local models).\n\nLocations that spawn Claude:\n- internal/cmd/mayor.go:131\n- internal/cmd/deacon.go:150 \n- internal/cmd/witness.go:280\n- internal/cmd/crew.go (multiple locations)\n- internal/cmd/up.go:190, 229\n- internal/session/manager.go:146\n- internal/refinery/manager.go:207\n\nSuggested approach:\n1. Create an agent package with an interface\n2. Add configuration for agent type in town/rig config\n3. Replace hardcoded claude commands with agent.Spawn() calls\n4. Support agents: claude, gemini, openai, local (ollama, etc.)","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-20T15:11:15.931048-08:00","updated_at":"2025-12-20T15:26:54.236995-08:00"} {"id":"gt-th7","title":"Add agent abstraction layer to support non-Claude agents","description":"Currently Gas Town hardcodes 'claude --dangerously-skip-permissions' throughout the codebase for spawning agents. We should add an abstraction layer to support other AI agents (e.g., Gemini CLI, OpenAI agents, local models).\n\nLocations that spawn Claude:\n- internal/cmd/mayor.go:131\n- internal/cmd/deacon.go:150 \n- internal/cmd/witness.go:280\n- internal/cmd/crew.go (multiple locations)\n- internal/cmd/up.go:190, 229\n- internal/session/manager.go:146\n- internal/refinery/manager.go:207\n\nSuggested approach:\n1. Create an agent package with an interface\n2. Add configuration for agent type in town/rig config\n3. Replace hardcoded claude commands with agent.Spawn() calls\n4. Support agents: claude, gemini, openai, local (ollama, etc.)","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-20T15:11:15.931048-08:00","updated_at":"2025-12-20T15:26:54.236995-08:00"}
{"id":"gt-tl54","title":"MR: gt-test (main)","description":"branch: main\ntarget: main\nsource_issue: gt-test","status":"closed","priority":3,"issue_type":"merge-request","created_at":"2025-12-18T20:16:41.125975-08:00","updated_at":"2025-12-18T20:21:54.162843-08:00","closed_at":"2025-12-18T20:21:54.162843-08:00"} {"id":"gt-tl54","title":"MR: gt-test (main)","description":"branch: main\ntarget: main\nsource_issue: gt-test","status":"closed","priority":3,"issue_type":"merge-request","created_at":"2025-12-18T20:16:41.125975-08:00","updated_at":"2025-12-18T20:21:54.162843-08:00","closed_at":"2025-12-18T20:21:54.162843-08:00"}

View File

@@ -21,6 +21,7 @@ var (
mailBody string mailBody string
mailPriority int mailPriority int
mailUrgent bool mailUrgent bool
mailPinned bool
mailType string mailType string
mailReplyTo string mailReplyTo string
mailNotify bool mailNotify bool
@@ -180,6 +181,7 @@ func init() {
mailSendCmd.Flags().StringVar(&mailType, "type", "notification", "Message type (task, scavenge, notification, reply)") mailSendCmd.Flags().StringVar(&mailType, "type", "notification", "Message type (task, scavenge, notification, reply)")
mailSendCmd.Flags().StringVar(&mailReplyTo, "reply-to", "", "Message ID this is replying to") mailSendCmd.Flags().StringVar(&mailReplyTo, "reply-to", "", "Message ID this is replying to")
mailSendCmd.Flags().BoolVarP(&mailNotify, "notify", "n", false, "Send tmux notification to recipient") mailSendCmd.Flags().BoolVarP(&mailNotify, "notify", "n", false, "Send tmux notification to recipient")
mailSendCmd.Flags().BoolVar(&mailPinned, "pinned", false, "Pin message (for handoff context that persists)")
_ = mailSendCmd.MarkFlagRequired("subject") _ = mailSendCmd.MarkFlagRequired("subject")
// Inbox flags // Inbox flags
@@ -250,6 +252,9 @@ func runMailSend(cmd *cobra.Command, args []string) error {
// Set message type // Set message type
msg.Type = mail.ParseMessageType(mailType) msg.Type = mail.ParseMessageType(mailType)
// Set pinned flag
msg.Pinned = mailPinned
// Handle reply-to: auto-set type to reply and look up thread // Handle reply-to: auto-set type to reply and look up thread
if mailReplyTo != "" { if mailReplyTo != "" {
msg.ReplyTo = mailReplyTo msg.ReplyTo = mailReplyTo

View File

@@ -72,10 +72,13 @@ func (m *Mailbox) List() ([]*Message, error) {
} }
func (m *Mailbox) listBeads() ([]*Message, error) { func (m *Mailbox) listBeads() ([]*Message, error) {
// bd mail inbox --json // bd list --type message --assignee <identity> --status open --json
cmd := exec.Command("bd", "mail", "inbox", "--json") cmd := exec.Command("bd", "list",
"--type", "message",
"--assignee", m.identity,
"--status", "open",
"--json")
cmd.Dir = m.workDir cmd.Dir = m.workDir
cmd.Env = append(cmd.Environ(), "BD_IDENTITY="+m.identity)
var stdout, stderr bytes.Buffer var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout cmd.Stdout = &stdout
@@ -205,7 +208,8 @@ func (m *Mailbox) Get(id string) (*Message, error) {
} }
func (m *Mailbox) getBeads(id string) (*Message, error) { func (m *Mailbox) getBeads(id string) (*Message, error) {
cmd := exec.Command("bd", "mail", "read", id, "--json") // bd show <id> --json
cmd := exec.Command("bd", "show", id, "--json")
cmd.Dir = m.workDir cmd.Dir = m.workDir
var stdout, stderr bytes.Buffer var stdout, stderr bytes.Buffer
@@ -223,12 +227,16 @@ func (m *Mailbox) getBeads(id string) (*Message, error) {
return nil, err return nil, err
} }
var bm BeadsMessage // bd show returns an array with one element
if err := json.Unmarshal(stdout.Bytes(), &bm); err != nil { var beadsMsgs []BeadsMessage
if err := json.Unmarshal(stdout.Bytes(), &beadsMsgs); err != nil {
return nil, err return nil, err
} }
if len(beadsMsgs) == 0 {
return nil, ErrMessageNotFound
}
return bm.ToMessage(), nil return beadsMsgs[0].ToMessage(), nil
} }
func (m *Mailbox) getLegacy(id string) (*Message, error) { func (m *Mailbox) getLegacy(id string) (*Message, error) {
@@ -253,7 +261,8 @@ func (m *Mailbox) MarkRead(id string) error {
} }
func (m *Mailbox) markReadBeads(id string) error { func (m *Mailbox) markReadBeads(id string) error {
cmd := exec.Command("bd", "mail", "ack", id) // bd close <id> - close the message issue to mark as read/acknowledged
cmd := exec.Command("bd", "close", id, "-r", "acknowledged")
cmd.Dir = m.workDir cmd.Dir = m.workDir
var stderr bytes.Buffer var stderr bytes.Buffer
@@ -421,10 +430,13 @@ func (m *Mailbox) ListByThread(threadID string) ([]*Message, error) {
} }
func (m *Mailbox) listByThreadBeads(threadID string) ([]*Message, error) { func (m *Mailbox) listByThreadBeads(threadID string) ([]*Message, error) {
// bd message thread <thread-id> --json // bd list --type message --label thread:<thread-id> --json
cmd := exec.Command("bd", "message", "thread", threadID, "--json") // Threads are stored as labels on message issues
cmd := exec.Command("bd", "list",
"--type", "message",
"--label", "thread:"+threadID,
"--json")
cmd.Dir = m.workDir cmd.Dir = m.workDir
cmd.Env = append(cmd.Environ(), "BD_IDENTITY="+m.identity)
var stdout, stderr bytes.Buffer var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout cmd.Stdout = &stdout

View File

@@ -25,42 +25,49 @@ func NewRouter(workDir string) *Router {
} }
} }
// Send delivers a message via beads message. // Send delivers a message via beads create.
func (r *Router) Send(msg *Message) error { func (r *Router) Send(msg *Message) error {
// Convert addresses to beads identities // Convert addresses to beads identities
toIdentity := addressToIdentity(msg.To) toIdentity := addressToIdentity(msg.To)
fromIdentity := addressToIdentity(msg.From) fromIdentity := addressToIdentity(msg.From)
// Build command: bd mail send <recipient> -s <subject> -m <body> // Build labels for sender and thread
args := []string{"mail", "send", toIdentity, labels := []string{"from:" + fromIdentity}
"-s", msg.Subject, if msg.ThreadID != "" {
"-m", msg.Body, labels = append(labels, "thread:"+msg.ThreadID)
}
if msg.ReplyTo != "" {
labels = append(labels, "reply-to:"+msg.ReplyTo)
}
if msg.Type != "" && msg.Type != TypeNotification {
labels = append(labels, "msg-type:"+string(msg.Type))
}
// Build command: bd create --type message --assignee <to> --title <subject> -d <body>
args := []string{"create",
"--type", "message",
"--assignee", toIdentity,
"--title", msg.Subject,
"-d", msg.Body,
} }
// Add priority flag // Add priority flag
beadsPriority := PriorityToBeads(msg.Priority) beadsPriority := PriorityToBeads(msg.Priority)
args = append(args, "--priority", fmt.Sprintf("%d", beadsPriority)) args = append(args, "--priority", fmt.Sprintf("%d", beadsPriority))
// Add message type if set // Add labels
if msg.Type != "" && msg.Type != TypeNotification { if len(labels) > 0 {
args = append(args, "--type", string(msg.Type)) args = append(args, "--labels", strings.Join(labels, ","))
} }
// Add thread ID if set // Add --silent to get just the issue ID
if msg.ThreadID != "" { args = append(args, "--silent")
args = append(args, "--thread-id", msg.ThreadID)
}
// Add reply-to if set
if msg.ReplyTo != "" {
args = append(args, "--reply-to", msg.ReplyTo)
}
cmd := exec.Command("bd", args...) cmd := exec.Command("bd", args...)
cmd.Env = append(cmd.Environ(), "BEADS_AGENT_NAME="+fromIdentity)
cmd.Dir = r.workDir cmd.Dir = r.workDir
var stderr bytes.Buffer var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr cmd.Stderr = &stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
@@ -71,6 +78,16 @@ func (r *Router) Send(msg *Message) error {
return fmt.Errorf("sending message: %w", err) return fmt.Errorf("sending message: %w", err)
} }
// Get the created issue ID
issueID := strings.TrimSpace(stdout.String())
// Pin the message if requested
if msg.Pinned && issueID != "" {
pinCmd := exec.Command("bd", "pin", issueID)
pinCmd.Dir = r.workDir
_ = pinCmd.Run() // Best effort - don't fail if pin fails
}
// Notify recipient if they have an active session // Notify recipient if they have an active session
_ = r.notifyRecipient(msg) _ = r.notifyRecipient(msg)

View File

@@ -4,6 +4,7 @@ package mail
import ( import (
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"strings"
"time" "time"
) )
@@ -145,20 +146,18 @@ func generateThreadID() string {
return "thread-" + hex.EncodeToString(b) return "thread-" + hex.EncodeToString(b)
} }
// BeadsMessage represents a message as returned by bd mail commands. // BeadsMessage represents a message as returned by bd list/show commands.
// Sender, thread, reply-to, and message type are stored in labels.
type BeadsMessage struct { type BeadsMessage struct {
ID string `json:"id"` ID string `json:"id"`
Title string `json:"title"` // Subject Title string `json:"title"` // Subject
Description string `json:"description"` // Body Description string `json:"description"` // Body
Sender string `json:"sender"` // From identity
Assignee string `json:"assignee"` // To identity Assignee string `json:"assignee"` // To identity
Priority int `json:"priority"` // 0=urgent, 1=high, 2=normal, 3=low Priority int `json:"priority"` // 0=urgent, 1=high, 2=normal, 3=low
Status string `json:"status"` // open=unread, closed=read Status string `json:"status"` // open=unread, closed=read
Pinned bool `json:"pinned"` // Persistent context marker Pinned bool `json:"pinned"` // Persistent context marker
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
Type string `json:"type,omitempty"` // Message type Labels []string `json:"labels"` // Contains from:, thread:, reply-to:, msg-type:
ThreadID string `json:"thread_id,omitempty"` // Thread identifier
ReplyTo string `json:"reply_to,omitempty"` // Original message ID
} }
// ToMessage converts a BeadsMessage to a GGT Message. // ToMessage converts a BeadsMessage to a GGT Message.
@@ -176,16 +175,29 @@ func (bm *BeadsMessage) ToMessage() *Message {
priority = PriorityNormal priority = PriorityNormal
} }
// Convert message type, default to notification // Extract sender, thread, reply-to, and type from labels
var sender, threadID, replyTo string
msgType := TypeNotification msgType := TypeNotification
switch MessageType(bm.Type) { for _, label := range bm.Labels {
case TypeTask, TypeScavenge, TypeReply: switch {
msgType = MessageType(bm.Type) case strings.HasPrefix(label, "from:"):
sender = strings.TrimPrefix(label, "from:")
case strings.HasPrefix(label, "thread:"):
threadID = strings.TrimPrefix(label, "thread:")
case strings.HasPrefix(label, "reply-to:"):
replyTo = strings.TrimPrefix(label, "reply-to:")
case strings.HasPrefix(label, "msg-type:"):
t := strings.TrimPrefix(label, "msg-type:")
switch MessageType(t) {
case TypeTask, TypeScavenge, TypeReply:
msgType = MessageType(t)
}
}
} }
return &Message{ return &Message{
ID: bm.ID, ID: bm.ID,
From: identityToAddress(bm.Sender), From: identityToAddress(sender),
To: identityToAddress(bm.Assignee), To: identityToAddress(bm.Assignee),
Subject: bm.Title, Subject: bm.Title,
Body: bm.Description, Body: bm.Description,
@@ -194,8 +206,8 @@ func (bm *BeadsMessage) ToMessage() *Message {
Pinned: bm.Pinned, Pinned: bm.Pinned,
Priority: priority, Priority: priority,
Type: msgType, Type: msgType,
ThreadID: bm.ThreadID, ThreadID: threadID,
ReplyTo: bm.ReplyTo, ReplyTo: replyTo,
} }
} }