refactor: split gt sling into gt hook + gt handoff <bead> (gt-z4bw)

- Add gt hook <bead>: durability primitive, attaches work to hook
- Update gt handoff: accept optional bead arg (detects bead vs role)
- Deprecate gt sling: shows warning, points to new commands
- Update doctor fix hint to reference new commands

🤖 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-24 20:41:51 -08:00
parent c4dda5c055
commit 32cac078e5
5 changed files with 271 additions and 14 deletions

View File

@@ -756,6 +756,8 @@
{"id":"gt-n8s1.1","title":"Polecat Arm (arm-toast)","description":"Single polecat inspection and action cycle.\n\nThis molecule is bonded dynamically by mol-witness-patrol's survey-workers step.\nEach polecat being monitored gets one arm that runs in parallel with other arms.\n\n## Variables\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| polecat_name | Yes | Name of the polecat to inspect |\n| rig | Yes | Rig containing the polecat |\n\n## Step: capture\nCapture recent tmux output for toast.\n\n```bash\ntmux capture-pane -t gt-gastown-toast -p | tail -50\n```\n\nRecord:\n- Last activity timestamp (when was last tool call?)\n- Visible errors or stack traces\n- Completion indicators (\"Done\", \"Finished\", etc.)\n\n## Step: assess\nCategorize polecat state based on captured output.\n\nStates:\n- **working**: Recent tool calls, active processing\n- **idle**: At prompt, no recent activity\n- **error**: Showing errors or stack traces\n- **requesting_shutdown**: Sent LIFECYCLE/Shutdown mail\n- **done**: Showing completion indicators\n\nCalculate: minutes since last activity.\nNeeds: capture\n\n## Step: load-history\nRead nudge history for toast from patrol state.\n\n```\nnudge_count = state.nudges[toast].count\nlast_nudge_time = state.nudges[toast].timestamp\n```\n\nThis data was loaded by the parent patrol's load-state step and passed\nto the arm via the bonding context.\nNeeds: assess\n\n## Step: decide\nApply the nudge matrix to determine action for toast.\n\n| State | Idle Time | Nudge Count | Action |\n|-------|-----------|-------------|--------|\n| working | any | any | none |\n| idle | \u003c10min | any | none |\n| idle | 10-15min | 0 | nudge-1 (gentle) |\n| idle | 15-20min | 1 | nudge-2 (direct) |\n| idle | 20+min | 2 | nudge-3 (final) |\n| idle | any | 3 | escalate |\n| error | any | any | assess-severity |\n| requesting_shutdown | any | any | pre-kill-verify |\n| done | any | any | pre-kill-verify |\n\nNudge text:\n1. \"How's progress? Need any help?\"\n2. \"Please wrap up soon. What's blocking you?\"\n3. \"Final check. Will escalate in 5 min if no response.\"\n\nRecord decision and rationale.\nNeeds: load-history\n\n## Step: execute\nTake the decided action for toast.\n\n**nudge-N**:\n```bash\ntmux send-keys -t gt-gastown-toast \"{{nudge_text}}\" Enter\n```\n\n**pre-kill-verify**:\n```bash\ncd polecats/toast\ngit status # Must be clean\ngit log origin/main..HEAD # Check for unpushed\nbd show \u003cassigned-issue\u003e # Verify closed/deferred\n```\nIf clean: kill session, remove worktree, delete branch\nIf dirty: record failure, retry next cycle\n\n**escalate**:\n```bash\ngt mail send mayor/ -s \"Escalation: toast stuck\" -m \"...\"\n```\n\n**none**: No action needed.\n\nRecord: action taken, result, updated nudge count.\nNeeds: decide\n\n## Output\n\nThe arm completes with:\n- action_taken: none | nudge-1 | nudge-2 | nudge-3 | killed | escalated\n- result: success | failed | pending\n- updated_state: New nudge count and timestamp for toast\n\nThis data feeds back to the parent patrol's aggregate step.\n---\nbonded_from: mol-polecat-arm\nbonded_to: gt-n8s1\nbonded_ref: arm-toast\nbonded_at: 2025-12-23T10:00:00Z\n","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T22:06:13.827065-08:00","updated_at":"2025-12-23T22:06:14.00142-08:00","closed_at":"2025-12-23T22:06:14.00142-08:00","close_reason":"Closed","dependencies":[{"issue_id":"gt-n8s1.1","depends_on_id":"gt-n8s1","type":"parent-child","created_at":"2025-12-23T22:06:13.827585-08:00","created_by":"daemon"}]}
{"id":"gt-n8u5","title":"bd list --parent: filter by parent issue","description":"Add --parent flag to bd list to filter issues by parent.\n\nExample:\n```bash\nbd list --parent=gt-h5n --status=open\n```\n\nWould show all open children of gt-h5n.\n\nUseful for:\n- Checking epic progress\n- Finding swarmable work within an epic\n- Molecule step listing","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-23T01:50:56.224031-08:00","updated_at":"2025-12-23T01:51:24.337084-08:00","closed_at":"2025-12-23T01:51:24.337084-08:00","close_reason":"Moving to beads rig"}
{"id":"gt-n9o2","title":"save-state","description":"Update handoff bead with new state.\n\nPersist nudge counts and pending actions.\n\nNeeds: execute-actions","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T01:41:54.507176-08:00","updated_at":"2025-12-23T04:39:39.677727-08:00","closed_at":"2025-12-23T04:39:39.677727-08:00","close_reason":"Parent gt-751s superseded by Christmas Ornament pattern","dependencies":[{"issue_id":"gt-n9o2","depends_on_id":"gt-751s","type":"parent-child","created_at":"2025-12-23T01:41:54.612922-08:00","created_by":"stevey"}]}
{"id":"gt-na7y","title":"Test Patrol for Bonding","description":"Parent issue for mol bond CLI test","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-24T20:40:58.973379-08:00","updated_at":"2025-12-24T20:40:59.344906-08:00","closed_at":"2025-12-24T20:40:59.344906-08:00","close_reason":"Closed"}
{"id":"gt-na7y.1","title":"Polecat Arm (arm-toast)","description":"Single polecat inspection and action cycle.\n\nThis molecule is bonded dynamically by mol-witness-patrol's survey-workers step.\nEach polecat being monitored gets one arm that runs in parallel with other arms.\n\n## Variables\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| polecat_name | Yes | Name of the polecat to inspect |\n| rig | Yes | Rig containing the polecat |\n\n## Step: capture\nCapture recent tmux output for toast.\n\n```bash\ntmux capture-pane -t gt-gastown-toast -p | tail -50\n```\n\nRecord:\n- Last activity timestamp (when was last tool call?)\n- Visible errors or stack traces\n- Completion indicators (\"Done\", \"Finished\", etc.)\n\n## Step: assess\nCategorize polecat state based on captured output.\n\nStates:\n- **working**: Recent tool calls, active processing\n- **idle**: At prompt, no recent activity\n- **error**: Showing errors or stack traces\n- **requesting_shutdown**: Sent LIFECYCLE/Shutdown mail\n- **done**: Showing completion indicators\n\nCalculate: minutes since last activity.\nNeeds: capture\n\n## Step: load-history\nRead nudge history for toast from patrol state.\n\n```\nnudge_count = state.nudges[toast].count\nlast_nudge_time = state.nudges[toast].timestamp\n```\n\nThis data was loaded by the parent patrol's load-state step and passed\nto the arm via the bonding context.\nNeeds: assess\n\n## Step: decide\nApply the nudge matrix to determine action for toast.\n\n| State | Idle Time | Nudge Count | Action |\n|-------|-----------|-------------|--------|\n| working | any | any | none |\n| idle | \u003c10min | any | none |\n| idle | 10-15min | 0 | nudge-1 (gentle) |\n| idle | 15-20min | 1 | nudge-2 (direct) |\n| idle | 20+min | 2 | nudge-3 (final) |\n| idle | any | 3 | escalate |\n| error | any | any | assess-severity |\n| requesting_shutdown | any | any | pre-kill-verify |\n| done | any | any | pre-kill-verify |\n\nNudge text:\n1. \"How's progress? Need any help?\"\n2. \"Please wrap up soon. What's blocking you?\"\n3. \"Final check. Will escalate in 5 min if no response.\"\n\nRecord decision and rationale.\nNeeds: load-history\n\n## Step: execute\nTake the decided action for toast.\n\n**nudge-N**:\n```bash\ntmux send-keys -t gt-gastown-toast \"{{nudge_text}}\" Enter\n```\n\n**pre-kill-verify**:\n```bash\ncd polecats/toast\ngit status # Must be clean\ngit log origin/main..HEAD # Check for unpushed\nbd show \u003cassigned-issue\u003e # Verify closed/deferred\n```\nIf clean: kill session, remove worktree, delete branch\nIf dirty: record failure, retry next cycle\n\n**escalate**:\n```bash\ngt mail send mayor/ -s \"Escalation: toast stuck\" -m \"...\"\n```\n\n**none**: No action needed.\n\nRecord: action taken, result, updated nudge count.\nNeeds: decide\n\n## Output\n\nThe arm completes with:\n- action_taken: none | nudge-1 | nudge-2 | nudge-3 | killed | escalated\n- result: success | failed | pending\n- updated_state: New nudge count and timestamp for toast\n\nThis data feeds back to the parent patrol's aggregate step.\n---\nbonded_from: mol-polecat-arm\nbonded_to: gt-na7y\nbonded_ref: arm-toast\nbonded_at: 2025-12-23T10:00:00Z\n","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-24T20:40:59.065447-08:00","updated_at":"2025-12-24T20:40:59.246697-08:00","closed_at":"2025-12-24T20:40:59.246697-08:00","close_reason":"Closed","dependencies":[{"issue_id":"gt-na7y.1","depends_on_id":"gt-na7y","type":"parent-child","created_at":"2025-12-24T20:40:59.065921-08:00","created_by":"daemon"}]}
{"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"}
{"id":"gt-nc9y","title":"gt polecat done should auto-stop running session","description":"Currently 'gt polecat done' fails if session is running, requiring a separate 'gt session stop' first. This is unnecessary friction - done should just stop the session automatically since that's always what you want.","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-23T04:12:13.30724-08:00","updated_at":"2025-12-23T04:12:13.30724-08:00"}
{"id":"gt-nd18","title":"Merge: gt-caih","description":"branch: polecat/furiosa\ntarget: main\nsource_issue: gt-caih\nrig: gastown","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-23T01:15:24.2771-08:00","updated_at":"2025-12-23T01:16:15.720728-08:00","closed_at":"2025-12-23T01:16:15.720728-08:00","close_reason":"Merged to main"}
@@ -818,6 +820,7 @@
{"id":"gt-pnu4","title":"Test","description":"Body","status":"open","priority":2,"issue_type":"message","assignee":"gastown/alpha","created_at":"2025-12-20T21:38:39.019559-08:00","updated_at":"2025-12-20T21:38:39.019559-08:00","sender":"Steve Yegge","wisp":true}
{"id":"gt-poxd","title":"Create handoff beads for Witness and Refinery roles","description":"Each patrol role needs a pinned handoff bead to track attached molecules and patrol state.\n\n## Pattern (from Deacon)\n- Title: '\u003crole\u003e Handoff' (e.g., 'witness Handoff', 'refinery Handoff')\n- Status: pinned\n- Description contains structured state\n\n## Witness Handoff State\n\n```yaml\nattached_molecule: mol-witness-patrol\nattached_at: 2025-12-24T10:00:00Z\n\n# Nudge escalation tracking\nnudges:\n toast:\n count: 2\n last: \"2025-12-24T10:30:00Z\"\n ace:\n count: 0\n last: null\n\n# Polecats queued for cleanup\npending_cleanup:\n - nux # received POLECAT_DONE, awaiting verification\n```\n\n## Refinery Handoff State\n\n```yaml\nattached_molecule: mol-refinery-patrol\nattached_at: 2025-12-24T10:00:00Z\n\n# Merge queue tracking\nlast_processed_branch: polecat/toast\nbranches_merged_this_cycle: 3\n```\n\n## Tasks\n1. Create 'witness Handoff' bead in each rig's beads\n2. Create 'refinery Handoff' bead in each rig's beads\n3. Update Witness/Refinery startup to check handoff bead for attached work\n4. Update templates to document handoff bead usage\n5. Include nudge state schema for Witness\n6. Include merge state schema for Refinery\n\n## Note\nThese are rig-level beads (in gastown/.beads/, beads/.beads/), not town-level like deacon Handoff (in ~/gt/.beads/).\n\n## See Also\n- docs/witness-patrol-design.md - Theory of operation","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-23T13:19:41.055563-08:00","updated_at":"2025-12-24T00:18:03.026989-08:00","dependencies":[{"issue_id":"gt-poxd","depends_on_id":"gt-y481","type":"parent-child","created_at":"2025-12-23T13:20:15.89851-08:00","created_by":"daemon"}]}
{"id":"gt-pv93","title":"Post-work discovery: AI analysis finds follow-on issues","description":"AI analyzes completed work to discover: bugs, punted work, follow-on tasks.\n\n**From VC**: Supervisor.AnalyzeResult() with iterative refinement. ~300 lines.\n\n**Gas Town implementation**: Post-work hook in molecule:\n```yaml\npost_work:\n discover:\n - bugs\n - punted_items\n - follow_on_work\n file_as: beads\n```\n\nPolecat output gets analyzed by AI, discovered work becomes beads issues.\n\n**Value**: Nothing gets forgotten. VC found ~25% more issues with refinement.\n\n**Key**: Use semantic deduplication (gt-xxx) to avoid pollution.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-20T20:30:14.723338-08:00","updated_at":"2025-12-20T20:30:14.723338-08:00","dependencies":[{"issue_id":"gt-pv93","depends_on_id":"gt-zhpa","type":"parent-child","created_at":"2025-12-20T20:30:27.534886-08:00","created_by":"daemon"},{"issue_id":"gt-pv93","depends_on_id":"gt-6m3e","type":"related","created_at":"2025-12-20T20:30:35.115095-08:00","created_by":"daemon"}]}
{"id":"gt-pvzj","title":"Digest: mol-deacon-patrol @ 2025-12-24 20:23","description":"Patrol complete: inbox clear, all agents healthy, no orphans","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-24T20:23:09.751138-08:00","updated_at":"2025-12-24T20:23:09.751138-08:00","closed_at":"2025-12-24T20:23:09.751066-08:00","close_reason":"Squashed from wisp gt-sb2 (9 issues)"}
{"id":"gt-pwep","title":"implement","description":"Implement the solution for gt-test123. Follow codebase conventions.\nFile discovered work as new issues with bd create.\n\nMake regular commits with clear messages.\nKeep changes focused on the assigned issue.\n\nDepends: load-context","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-21T21:56:18.534804-08:00","updated_at":"2025-12-21T21:56:27.509304-08:00","closed_at":"2025-12-21T21:56:27.509304-08:00","close_reason":"test cleanup","dependencies":[{"issue_id":"gt-pwep","depends_on_id":"gt-zjqs","type":"parent-child","created_at":"2025-12-21T21:56:18.536244-08:00","created_by":"stevey"},{"issue_id":"gt-pwep","depends_on_id":"gt-mc4n","type":"blocks","created_at":"2025-12-21T21:56:18.536793-08:00","created_by":"stevey"}],"wisp":true}
{"id":"gt-pyqv","title":"Work on ga-ct2: Add MR workflow to polecat completion. Wh...","description":"Work on ga-ct2: Add MR workflow to polecat completion. When polecat completes work, auto-create MR to integration branch. When done, submit MR (not PR) to integration branch for Refinery.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-19T22:58:35.473928-08:00","updated_at":"2025-12-21T17:20:42.831549-08:00"}
{"id":"gt-q3ac","title":"Digest: mol-deacon-patrol @ 2025-12-24 19:24","description":"Patrol 9: quiet","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-24T19:24:50.821721-08:00","updated_at":"2025-12-24T19:24:50.821721-08:00","closed_at":"2025-12-24T19:24:50.821645-08:00","close_reason":"Squashed from wisp gt-dri (9 issues)"}
@@ -871,6 +874,8 @@
{"id":"gt-qwyu","title":"Test issue for spawn molecule","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-21T21:58:43.699993-08:00","updated_at":"2025-12-21T21:59:10.936251-08:00","closed_at":"2025-12-21T21:59:10.936251-08:00","close_reason":"test cleanup"}
{"id":"gt-qx3k","title":"Merge: gt-jzot","description":"branch: polecat/nux\ntarget: main\nsource_issue: gt-jzot\nrig: gastown","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-22T22:55:45.3321-08:00","updated_at":"2025-12-22T22:57:00.353151-08:00","closed_at":"2025-12-22T22:57:00.353151-08:00","close_reason":"Merged to main"}
{"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-qy6u","title":"Test Patrol Parent","description":"Test parent for Christmas Ornament pattern","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-24T20:40:58.593162-08:00","updated_at":"2025-12-24T20:40:58.872307-08:00","closed_at":"2025-12-24T20:40:58.872307-08:00","close_reason":"Closed"}
{"id":"gt-qy6u.1","title":"Test Polecat Arm","description":"Test child for bonding pattern","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-24T20:40:58.680513-08:00","updated_at":"2025-12-24T20:40:58.780877-08:00","closed_at":"2025-12-24T20:40:58.780877-08:00","close_reason":"Closed","dependencies":[{"issue_id":"gt-qy6u.1","depends_on_id":"gt-qy6u","type":"parent-child","created_at":"2025-12-24T20:40:58.680943-08:00","created_by":"daemon"}]}
{"id":"gt-qz2l","title":"Refinery patrol: Add banners and wisp-based execution","description":"Bring Refinery patrol up to Deacon's level of sophistication:\n\n## Current state\n- mol-refinery-patrol exists (needs verification)\n- Basic merge queue processing\n\n## Needed\n1. **Banners** - Print step banners like Deacon does:\n ```\n ═══════════════════════════════════════════════════════════════\n ⚗️ QUEUE-CHECK\n Processing merge queue entries\n ═══════════════════════════════════════════════════════════════\n ```\n\n2. **Wisp-based execution** - Spawn patrol as wisp, squash when complete\n3. **Handoff bead attachment** - Refinery needs its own handoff bead with attached_molecule\n4. **Loop-or-exit step** - Context-aware cycling like Deacon\n5. **Patrol summary banner** at end of each cycle\n\n## Reference\nSee Deacon patrol implementation in ~/gt/deacon/CLAUDE.md","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-23T13:19:40.777589-08:00","updated_at":"2025-12-24T00:18:03.103523-08:00","closed_at":"2025-12-23T19:39:03.524185-08:00","close_reason":"Implemented banners, wisp-based execution, and propulsion protocol for Refinery patrol","dependencies":[{"issue_id":"gt-qz2l","depends_on_id":"gt-y481","type":"parent-child","created_at":"2025-12-23T13:20:15.787696-08:00","created_by":"daemon"}]}
{"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","priority":0,"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-r6td","title":"gt spawn: Notify Deacon and Witness on polecat start","description":"When gt spawn creates a polecat, it should mail both Deacon and Witness:\n\n```\ngt mail send \u003crig\u003e/witness -s 'POLECAT_STARTED furiosa' -m 'Issue: gt-xxx'\ngt mail send deacon/ -s 'POLECAT_STARTED gastown/furiosa' -m 'Issue: gt-xxx'\n```\n\nThis enables:\n- Witness to bond a lease to its patrol wisp\n- Deacon to verify worker started (redundancy)\n- Both to nudge if worker is idle at prompt\n\nPart of the village self-monitoring architecture.","status":"closed","priority":1,"issue_type":"feature","assignee":"gastown/furiosa","created_at":"2025-12-22T22:01:11.790203-08:00","updated_at":"2025-12-22T22:53:01.103369-08:00","closed_at":"2025-12-22T22:53:01.103369-08:00","close_reason":"Closed"}
@@ -1026,6 +1031,7 @@
{"id":"gt-wexr","title":"Polecat role references deprecated 'swarm' terminology","description":"prompts/roles/polecat.md line 12 says:\n'Part of a swarm: Other polecats may be working on related issues in parallel'\n\nBut architecture.md explicitly states:\n'There are no swarm IDs - just epics with children'\n\nThe swarm concept has been replaced by streams/dependency model.\nUpdate polecat.md to remove swarm references.","status":"open","priority":1,"issue_type":"bug","created_at":"2025-12-24T12:50:32.097647-08:00","updated_at":"2025-12-24T12:50:32.097647-08:00","dependencies":[{"issue_id":"gt-wexr","depends_on_id":"gt-jo9n","type":"blocks","created_at":"2025-12-24T12:52:05.023976-08:00","created_by":"daemon"}]}
{"id":"gt-wmhj","title":"tmux link-window auto-selects new window, causing agent confusion","description":"## Summary\n\nWhen running `gt crew at \u003cother\u003e` from inside a tmux session, the linked window auto-selects, causing the user to unknowingly switch agents.\n\n## Root Cause\n\n`internal/tmux/tmux.go:535-538` - `LinkWindow` doesn't use `-d` flag:\n```go\n_, err := t.run(\"link-window\", \"-s\", source) // Missing -d!\n```\n\nBy default, `tmux link-window` selects the newly linked window.\n\n## Reproduction\n\n1. Be in Max's tmux session talking to Max\n2. Ask Max to run `gt crew at joe`\n3. Max creates Joe's session and links it\n4. User is now in Joe's window without realizing it\n5. Max appears to have 'disappeared'\n\n## Fix\n\nAdd `-d` flag to prevent auto-selection:\n```go\n_, err := t.run(\"link-window\", \"-s\", source, \"-d\")\n```\n\n## Related\n\n- gt-09i4: Unify tmux session lifecycle (broader epic Max filed)\n","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-24T14:33:40.40319-08:00","updated_at":"2025-12-24T14:34:01.227518-08:00","closed_at":"2025-12-24T14:34:01.227518-08:00","close_reason":"Fixed by adding -d flag to link-window"}
{"id":"gt-wpg","title":"Replaceable notifications via Claude Code queue","description":"Leverage Claude Code's ability to replace queued text for notifications that supersede previous ones.\n\n## Problem\n\nIf daemon sends 10 heartbeats while agent is busy, agent returns to see 10 stacked messages. Wasteful and noisy.\n\n## Solution\n\nUse Claude Code's queue replacement for:\n- Heartbeat messages (only latest matters)\n- Status updates that supersede previous\n- Progress notifications\n\n## Implementation\n\nNotifications get a 'slot' identifier. New notification in same slot replaces old one:\n- Slot: 'heartbeat' → only one heartbeat queued at a time\n- Slot: 'status-\u003crig\u003e' → latest status per rig\n- No slot → stacks normally (for unique messages)\n\n## Research Needed\n\n- How does Claude Code expose queue replacement?\n- tmux send-keys behavior with pending input\n- Alternative: clear + resend pattern","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-18T14:19:29.821949-08:00","updated_at":"2025-12-20T13:19:00.398942-08:00","closed_at":"2025-12-20T13:19:00.398942-08:00","dependencies":[{"issue_id":"gt-wpg","depends_on_id":"gt-99m","type":"blocks","created_at":"2025-12-18T14:19:46.656972-08:00","created_by":"daemon"}]}
{"id":"gt-wqck","title":"bd doctor: detect clone divergence emergencies","description":"Add doctor check to detect:\n1. Crew/Mayor on feature branches (should always be on main)\n2. Significant divergence between clones that should be in sync\n3. Distinguish from normal beads-sync vs main divergence (expected)\n\nContext: bd sync --status shows all divergence as equal, but some is emergency (clones drifted) vs normal (sync branch mechanics).","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-24T20:26:32.71018-08:00","updated_at":"2025-12-24T20:26:32.71018-08:00"}
{"id":"gt-wrw2","title":"Test2","description":"Testing gt mail","status":"open","priority":2,"issue_type":"message","assignee":"gastown-alpha","created_at":"2025-12-20T21:39:05.875792-08:00","updated_at":"2025-12-20T21:39:05.875792-08:00","labels":["thread:thread-1fd9f932cef0"],"sender":"Steve Yegge","wisp":true}
{"id":"gt-wusk","title":"Layered context onboarding pattern","description":"Pattern from handoff discussion:\n\n## Pattern: Layered Context Onboarding\n\nTown CLAUDE.md (user/org) -\u003e Rig CLAUDE.md (project) -\u003e Role priming\n\n## Ultra-compressed HOP for workers (no reveal)\n\n- Permanent record: All work tracked. Outcomes matter.\n- Quality gates: Molecule steps exist for a reason.\n- Attribution: Completions build your track record.\n- Handoff clean: Leave state any worker can continue.\n\n## Recommendation\n\nCreate Town @AGENTS.md for shared worker context that all workers see.\nThis provides common behavioral guidance without revealing full HOP context.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-20T00:55:11.984103-08:00","updated_at":"2025-12-20T00:55:11.984103-08:00"}
{"id":"gt-wvyi","title":"sling pin test 2","status":"closed","priority":2,"issue_type":"task","assignee":"max","created_at":"2025-12-23T11:51:01.899435-08:00","updated_at":"2025-12-23T12:03:22.256591-08:00","closed_at":"2025-12-23T12:03:22.256591-08:00","close_reason":"test issue for gt-o3is debugging"}
@@ -1066,7 +1072,7 @@
{"id":"gt-z06w","title":"Digest: mol-deacon-patrol @ 2025-12-24 19:29","description":"Patrol 20: final patrol before handoff","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-24T19:29:37.516654-08:00","updated_at":"2025-12-24T19:29:37.516654-08:00","closed_at":"2025-12-24T19:29:37.516584-08:00","close_reason":"Squashed from wisp gt-lok (9 issues)"}
{"id":"gt-z3qf","title":"Overhaul gt mol to match bd mol chemistry interface","description":"## The Sling: Unified Work Dispatch\n\nThis issue tracks the overhaul of `gt molecule` to align with chemistry metaphor and introduce the **Universal Gas Town Propulsion Principle**.\n\n### The Propulsion Principle\n\n\u003e **If you find something on your hook, YOU RUN IT.**\n\nThis is the one rule that drives all Gas Town agents.\n\n### The Sling Operation\n\n`gt sling \u003cthing\u003e \u003ctarget\u003e [options]` - unified command for spawn + assign + pin.\n\nSee: `gastown/mayor/rig/docs/sling-design.md`\n\n### Implementation Tasks\n\n| Issue | Title | Priority |\n|-------|-------|----------|\n| gt-4ev4 | Implement gt sling command | P1 |\n| gt-uym5 | Implement gt mol status command | P1 |\n| gt-i4kq | Update templates for Propulsion Principle | P1 |\n| gt-7hor | Document the Propulsion Principle | P2 |\n\n### Command Changes\n\n| Old | New |\n|-----|-----|\n| `gt molecule instantiate` | `gt sling` |\n| `gt molecule attach` | `gt sling --force` |\n| `gt molecule detach` | `gt mol burn` |\n| `gt molecule progress` | `gt mol status` |\n| `gt molecule list` | `gt mol catalog` |\n| `gt spawn --molecule` | `gt sling` |\n\n### Acceptance Criteria\n\n- [ ] `gt sling` works for protos, issues, and epics\n- [ ] `gt mol status` shows hook state\n- [ ] Templates updated for propulsion principle\n- [ ] Old commands deprecated with warnings\n- [ ] Documentation complete","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-22T03:02:38.049324-08:00","updated_at":"2025-12-22T14:37:37.562677-08:00","closed_at":"2025-12-22T14:37:37.562677-08:00","close_reason":"Core sling work complete (gt-4ev4, gt-uym5, gt-7hor closed). gt-i4kq (template updates) remains open but is independent polish work.","dependencies":[{"issue_id":"gt-z3qf","depends_on_id":"gt-4ev4","type":"blocks","created_at":"2025-12-22T12:10:42.394653-08:00","created_by":"daemon"},{"issue_id":"gt-z3qf","depends_on_id":"gt-uym5","type":"blocks","created_at":"2025-12-22T12:10:42.46834-08:00","created_by":"daemon"},{"issue_id":"gt-z3qf","depends_on_id":"gt-i4kq","type":"blocks","created_at":"2025-12-22T12:10:42.541384-08:00","created_by":"daemon"},{"issue_id":"gt-z3qf","depends_on_id":"gt-7hor","type":"blocks","created_at":"2025-12-22T12:10:42.613099-08:00","created_by":"daemon"}]}
{"id":"gt-z3rf","title":"Remove schedule/phase terminology from code","description":"Temporal/schedule language in identifiers is an anti-pattern.\n\n## Found Issues\n\n### beads repo\n- `examples/multi-phase-development/` directory\n - Rename to `examples/dependency-sequenced-work/` or similar\n\n### gastown repo \n- `internal/cmd/start.go` lines 178-195\n - Uses \"Phase 1:\", \"Phase 2:\", etc. in printf output\n - Change to descriptive names: \"Interrupting agents...\", \"Requesting handoff...\", etc.\n\n- `internal/swarm/landing.go` lines ~50-70\n - Comments say \"// Phase 1: Stop all polecat sessions\"\n - Change to \"// Step: Stop all polecat sessions\" or just remove phase labels\n\n## Why This Matters\n\nPhase/version language in names:\n1. Implies a fixed schedule (antithetical to dependency-driven work)\n2. Creates confusion with molecule \"phases\" (solid/liquid/gas)\n3. Encourages temporal thinking that leads to backwards dependencies\n\n## What To Keep\n\n- Molecule phase terminology (pour/wisp/proto) - this is chemistry, not scheduling\n- Version fields inside files (version: 1) - this is semantic versioning","status":"open","priority":2,"issue_type":"chore","created_at":"2025-12-24T15:57:43.019728-08:00","updated_at":"2025-12-24T15:57:43.019728-08:00"}
{"id":"gt-z4bw","title":"Refactor sling to hook plus handoff","description":"Replace gt sling with gt hook (durability) and gt handoff bead (hook+restart)","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-24T16:42:00.384256-08:00","updated_at":"2025-12-24T16:42:00.384256-08:00"}
{"id":"gt-z4bw","title":"Refactor sling to hook plus handoff","description":"Replace gt sling with gt hook (durability) and gt handoff bead (hook+restart)","status":"closed","priority":0,"issue_type":"task","created_at":"2025-12-24T16:42:00.384256-08:00","updated_at":"2025-12-24T20:41:36.266572-08:00","closed_at":"2025-12-24T20:41:36.266572-08:00","close_reason":"Implemented gt hook and updated gt handoff to accept bead arg. Deprecated gt sling."}
{"id":"gt-z4g","title":"Plugin: Plan-to-Epic converter","description":"## Purpose\n\nHelp users create beads epics from various planning inputs.\n\n## Inputs\n- Markdown task lists\n- GitHub issues\n- Linear/Jira exports\n- Free-form descriptions\n- Existing beads epics\n\n## Output\n- Beads epic with properly structured children\n- Dependencies set for wave ordering\n- Priorities assigned\n- Ready for `gt spawn --epic \u003cid\u003e`\n\n## Implementation Options\n\n### Option A: CLI Tool\n```bash\ngt plan import --from github --repo owner/repo --label batch-candidate\ngt plan import --from markdown tasks.md\ngt plan structure \u003cepic-id\u003e # analyze and add dependencies\n```\n\n### Option B: Plugin Agent\nA plugin at `\u003crig\u003e/plugins/plan-oracle/` that:\n- Receives planning requests via mail\n- Analyzes scope and requirements\n- Creates structured beads epic\n- Sets dependencies based on analysis\n\n### Option C: Interactive Mode\n```bash\ngt plan create\n# Walks through questions, creates epic interactively\n```\n\n## Axiom\n\nAs stated: 'The Planning phase should end in the creation of a workable Beads plan.'\n\nThis plugin bridges the gap between human planning and machine-executable work.\n\n## Priority\n\nP2 - Nice to have for MVP. Manual epic creation works for now.\n\n## Note\n\nNo \"swarm IDs\" - output is just a beads epic with children. Workers process it independently.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-16T02:10:20.663549-08:00","updated_at":"2025-12-16T17:26:41.087304-08:00"}
{"id":"gt-z6a5","title":"Digest: mol-deacon-patrol @ 2025-12-24 19:43","description":"Patrol 3: All healthy","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-24T19:43:12.75587-08:00","updated_at":"2025-12-24T19:43:12.75587-08:00","closed_at":"2025-12-24T19:43:12.755801-08:00","close_reason":"Squashed from wisp gt-8ow (9 issues)"}
{"id":"gt-z94m","title":"load-state","description":"Read handoff bead and get nudge counts.\n\nNeeds: check-refinery","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T01:41:54.505607-08:00","updated_at":"2025-12-23T04:39:39.698069-08:00","closed_at":"2025-12-23T04:39:39.698069-08:00","close_reason":"Parent gt-751s superseded by Christmas Ornament pattern","dependencies":[{"issue_id":"gt-z94m","depends_on_id":"gt-751s","type":"parent-child","created_at":"2025-12-23T01:41:54.542384-08:00","created_by":"stevey"}],"wisp":true}

View File

@@ -10,10 +10,11 @@ import (
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/style"
"github.com/steveyegge/gastown/internal/tmux"
"github.com/steveyegge/gastown/internal/wisp"
)
var handoffCmd = &cobra.Command{
Use: "handoff [role]",
Use: "handoff [bead-or-role]",
Short: "Hand off to a fresh session, work continues from hook",
Long: `End watch. Hand off to a fresh agent session.
@@ -23,10 +24,13 @@ This is the canonical way to end any agent session. It handles all roles:
- Polecats: Calls 'gt done --exit DEFERRED' (Witness handles lifecycle)
When run without arguments, hands off the current session.
When given a bead ID (gt-xxx, hq-xxx), hooks that work first, then restarts.
When given a role name, hands off that role's session (and switches to it).
Examples:
gt handoff # Hand off current session
gt handoff gt-abc # Hook bead, then restart
gt handoff gt-abc -s "Fix it" # Hook with context, then restart
gt handoff -s "Context" -m "Notes" # Hand off with custom message
gt handoff crew # Hand off crew session
gt handoff mayor # Hand off mayor session
@@ -83,13 +87,27 @@ func runHandoff(cmd *cobra.Command, args []string) error {
return fmt.Errorf("getting session name: %w", err)
}
// Determine target session
// Determine target session and check for bead hook
targetSession := currentSession
if len(args) > 0 {
// User specified a role to hand off
targetSession, err = resolveRoleToSession(args[0])
if err != nil {
return fmt.Errorf("resolving role: %w", err)
arg := args[0]
// Check if arg is a bead ID (gt-xxx, hq-xxx, bd-xxx, etc.)
if looksLikeBeadID(arg) {
// Hook the bead first
if err := hookBeadForHandoff(arg); err != nil {
return fmt.Errorf("hooking bead: %w", err)
}
// Update subject if not set
if handoffSubject == "" {
handoffSubject = fmt.Sprintf("🪝 HOOKED: %s", arg)
}
} else {
// User specified a role to hand off
targetSession, err = resolveRoleToSession(arg)
if err != nil {
return fmt.Errorf("resolving role: %w", err)
}
}
}
@@ -395,3 +413,57 @@ func sendHandoffMail(subject, message string) error {
cmd.Stderr = os.Stderr
return cmd.Run()
}
// looksLikeBeadID checks if a string looks like a bead ID.
// Bead IDs have format: prefix-xxxx where prefix is 2+ letters and xxxx is alphanumeric.
func looksLikeBeadID(s string) bool {
// Common bead prefixes
prefixes := []string{"gt-", "hq-", "bd-", "beads-"}
for _, p := range prefixes {
if strings.HasPrefix(s, p) {
return true
}
}
return false
}
// hookBeadForHandoff attaches a bead to the current agent's hook.
func hookBeadForHandoff(beadID string) error {
// Verify the bead exists first
verifyCmd := exec.Command("bd", "show", beadID, "--json")
if err := verifyCmd.Run(); err != nil {
return fmt.Errorf("bead '%s' not found", beadID)
}
// Determine agent identity
agentID, err := detectAgentIdentity()
if err != nil {
return fmt.Errorf("detecting agent identity: %w", err)
}
// Get clone root for wisp storage
cloneRoot, err := detectCloneRoot()
if err != nil {
return fmt.Errorf("detecting clone root: %w", err)
}
// Create the slung work wisp
sw := wisp.NewSlungWork(beadID, agentID)
sw.Subject = handoffSubject
sw.Context = handoffMessage
fmt.Printf("%s Hooking %s...\n", style.Bold.Render("🪝"), beadID)
if handoffDryRun {
fmt.Printf("Would create wisp: %s\n", wisp.HookPath(cloneRoot, agentID))
return nil
}
// Write the wisp to the hook
if err := wisp.WriteSlungWork(cloneRoot, agentID, sw); err != nil {
return fmt.Errorf("writing wisp: %w", err)
}
fmt.Printf("%s Work attached to hook\n", style.Bold.Render("✓"))
return nil
}

176
internal/cmd/hook.go Normal file
View File

@@ -0,0 +1,176 @@
package cmd
import (
"fmt"
"os"
"os/exec"
"strings"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/style"
"github.com/steveyegge/gastown/internal/wisp"
)
var hookCmd = &cobra.Command{
Use: "hook <bead-id>",
Short: "Attach work to your hook (durable across restarts)",
Long: `Attach a bead (issue) to your hook for durable work tracking.
The hook is a lightweight, ephemeral attachment point. When you restart
(via gt handoff), your SessionStart hook finds the slung work and you
continue from where you left off.
This is the "durability primitive" - work on your hook survives session
restarts. For the full restart-and-resume flow, use: gt handoff <bead>
Examples:
gt hook gt-abc # Attach issue gt-abc to your hook
gt hook gt-abc -s "Fix the bug" # With subject for handoff mail
gt hook gt-abc -m "Check tests" # With context message
Related commands:
gt mol status # See what's on your hook
gt handoff <bead> # Hook + restart in one step
gt handoff # Restart (uses existing hook if present)`,
Args: cobra.ExactArgs(1),
RunE: runHook,
}
var (
hookSubject string
hookMessage string
hookDryRun bool
)
func init() {
hookCmd.Flags().StringVarP(&hookSubject, "subject", "s", "", "Subject for handoff mail (optional)")
hookCmd.Flags().StringVarP(&hookMessage, "message", "m", "", "Message for handoff mail (optional)")
hookCmd.Flags().BoolVarP(&hookDryRun, "dry-run", "n", false, "Show what would be done")
rootCmd.AddCommand(hookCmd)
}
func runHook(cmd *cobra.Command, args []string) error {
beadID := args[0]
// Polecats cannot hook - they use gt done for lifecycle
if polecatName := os.Getenv("GT_POLECAT"); polecatName != "" {
return fmt.Errorf("polecats cannot hook work (use gt done for handoff)")
}
// Verify the bead exists
if err := verifyBeadExists(beadID); err != nil {
return err
}
// Determine agent identity
agentID, err := detectAgentIdentity()
if err != nil {
return fmt.Errorf("detecting agent identity: %w", err)
}
// Get cwd for wisp storage (use clone root, not town root)
cloneRoot, err := detectCloneRoot()
if err != nil {
return fmt.Errorf("detecting clone root: %w", err)
}
// Create the slung work wisp
sw := wisp.NewSlungWork(beadID, agentID)
sw.Subject = hookSubject
sw.Context = hookMessage
fmt.Printf("%s Hooking %s...\n", style.Bold.Render("🪝"), beadID)
if hookDryRun {
fmt.Printf("Would create wisp: %s\n", wisp.HookPath(cloneRoot, agentID))
fmt.Printf(" bead_id: %s\n", beadID)
fmt.Printf(" agent: %s\n", agentID)
if hookSubject != "" {
fmt.Printf(" subject: %s\n", hookSubject)
}
if hookMessage != "" {
fmt.Printf(" context: %s\n", hookMessage)
}
return nil
}
// Write the wisp to the hook
if err := wisp.WriteSlungWork(cloneRoot, agentID, sw); err != nil {
return fmt.Errorf("writing wisp: %w", err)
}
fmt.Printf("%s Work attached to hook\n", style.Bold.Render("✓"))
fmt.Printf(" Use 'gt handoff' to restart with this work\n")
fmt.Printf(" Use 'gt mol status' to see hook status\n")
return nil
}
// verifyBeadExists checks that the bead exists using bd show.
// Defined in sling.go but duplicated here for clarity. Will be consolidated
// when sling.go is removed.
func verifyBeadExistsForHook(beadID string) error {
cmd := exec.Command("bd", "show", beadID, "--json")
if err := cmd.Run(); err != nil {
return fmt.Errorf("bead '%s' not found (bd show failed)", beadID)
}
return nil
}
// detectAgentIdentityForHook figures out who we are (crew/joe, witness, etc).
// Duplicated from sling.go - will be consolidated when sling.go is removed.
func detectAgentIdentityForHook() (string, error) {
// Check environment first
if crew := os.Getenv("GT_CREW"); crew != "" {
if rig := os.Getenv("GT_RIG"); rig != "" {
return fmt.Sprintf("%s/crew/%s", rig, crew), nil
}
}
// Check if we're a polecat
if polecat := os.Getenv("GT_POLECAT"); polecat != "" {
if rig := os.Getenv("GT_RIG"); rig != "" {
return fmt.Sprintf("%s/polecats/%s", rig, polecat), nil
}
}
// Try to detect from cwd
detected, err := detectCrewFromCwd()
if err == nil {
return fmt.Sprintf("%s/crew/%s", detected.rigName, detected.crewName), nil
}
// Check for other role markers in session name
if session := os.Getenv("TMUX"); session != "" {
sessionName, err := getCurrentTmuxSession()
if err == nil {
if sessionName == "gt-mayor" {
return "mayor", nil
}
if sessionName == "gt-deacon" {
return "deacon", nil
}
if strings.HasSuffix(sessionName, "-witness") {
rig := strings.TrimSuffix(strings.TrimPrefix(sessionName, "gt-"), "-witness")
return fmt.Sprintf("%s/witness", rig), nil
}
if strings.HasSuffix(sessionName, "-refinery") {
rig := strings.TrimSuffix(strings.TrimPrefix(sessionName, "gt-"), "-refinery")
return fmt.Sprintf("%s/refinery", rig), nil
}
}
}
return "", fmt.Errorf("cannot determine agent identity - set GT_RIG/GT_CREW or run from clone directory")
}
// detectCloneRootForHook finds the root of the current git clone.
// Duplicated from sling.go - will be consolidated when sling.go is removed.
func detectCloneRootForHook() (string, error) {
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
out, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("not in a git repository")
}
return strings.TrimSpace(string(out)), nil
}

View File

@@ -13,17 +13,20 @@ import (
)
var slingCmd = &cobra.Command{
Use: "sling <bead-id>",
Short: "Attach work to hook and restart agent",
Long: `Sling work onto the agent's hook and restart with that context.
Use: "sling <bead-id>",
Short: "[DEPRECATED] Use 'gt hook' or 'gt handoff <bead>' instead",
Deprecated: "Use 'gt hook <bead>' to attach work, or 'gt handoff <bead>' to attach and restart.",
Long: `DEPRECATED: This command is deprecated. Use instead:
gt hook <bead> # Just attach work to hook (no restart)
gt handoff <bead> # Attach work AND restart (what sling did)
Sling work onto the agent's hook and restart with that context.
This is the "restart-and-resume" mechanism - attach a bead (issue) to your hook,
then restart with a fresh context. The new session wakes up, finds the slung work
on its hook, and begins working on it immediately.
The wisp is ephemeral (stored in .beads-wisp/, not git-tracked). It's burned
after the agent picks it up.
Examples:
gt sling gt-abc # Attach issue and restart
gt sling gt-abc -s "Fix the bug" # With handoff subject

View File

@@ -388,7 +388,7 @@ func (c *OrphanedAttachmentsCheck) Run(ctx *CheckContext) *CheckResult {
Status: StatusWarning,
Message: fmt.Sprintf("Found %d orphaned handoff bead(s)", len(c.orphans)),
Details: details,
FixHint: "Re-sling molecule to active agent with 'gt sling', or close with 'bd close <id>'",
FixHint: "Re-hook molecule with 'gt hook <id>' then 'gt handoff', or close with 'bd close <id>'",
}
}