Implement Witness handoff bead state persistence (gt-caih)

Add persistent state storage for Witness across wisp burns:
- Add WorkerState and WitnessHandoffState types
- Implement loadHandoffState/saveHandoffState for bead persistence
- Update getNudgeCount/recordNudge to use persistent state
- Add activity tracking integration into healthCheck

🤖 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-23 00:11:56 -08:00
parent cdc5664948
commit f74687486c
3 changed files with 293 additions and 16 deletions

View File

@@ -17,7 +17,7 @@
{"id":"gt-16rv","title":"implement","description":"Implement the solution for gt-test. 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-21T22:04:43.420903-08:00","updated_at":"2025-12-21T22:05:10.501473-08:00","closed_at":"2025-12-21T22:05:10.501473-08:00","close_reason":"test cleanup","dependencies":[{"issue_id":"gt-16rv","depends_on_id":"gt-jvr3","type":"parent-child","created_at":"2025-12-21T22:04:43.422515-08:00","created_by":"stevey"},{"issue_id":"gt-16rv","depends_on_id":"gt-g844","type":"blocks","created_at":"2025-12-21T22:04:43.423201-08:00","created_by":"stevey"}],"wisp":true}
{"id":"gt-17r","title":"Doctor check: Zombie session cleanup","description":"Detect and clean up zombie tmux sessions via gt doctor.\n\n## Problem\n\nZombie sessions occur when:\n- Agent crashes without cleanup\n- gt kill fails mid-operation\n- System restart leaves orphan sessions\n- Session naming collision\n\n## Checks\n\n### ZombieSessionCheck\n- List all tmux sessions matching gt-* pattern\n- Cross-reference with known polecats\n- Flag sessions with no corresponding polecat state\n- Flag sessions for removed polecats\n- Check session age vs polecat creation time\n\n### Detection Criteria\n- Session exists but polecat directory doesn't\n- Session name doesn't match any registered polecat\n- Polecat state=idle but session running\n- Multiple sessions for same polecat\n\n## Output\n\n```\n[WARN] Zombie tmux sessions detected:\n - gt-wyvern-OldPolecat (polecat removed)\n - gt-beads-Unknown (no matching polecat)\n - gt-wyvern-Toast (duplicate session)\n\n Run 'gt doctor --fix' to clean up\n```\n\n## Auto-Fix (--fix flag)\n\n- Kill orphan tmux sessions\n- Update polecat state to match reality\n- Log all cleanup actions\n\n## Safety\n\n- Never kill sessions where polecat state=working\n- Prompt before killing if --fix used without --force\n- Create audit log of killed sessions","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-15T23:18:01.446702-08:00","updated_at":"2025-12-15T23:18:39.517644-08:00","dependencies":[{"issue_id":"gt-17r","depends_on_id":"gt-f9x.4","type":"blocks","created_at":"2025-12-15T23:19:05.66301-08:00","created_by":"daemon"},{"issue_id":"gt-17r","depends_on_id":"gt-7ik","type":"blocks","created_at":"2025-12-17T15:44:41.945064-08:00","created_by":"daemon"}]}
{"id":"gt-17zr","title":"gt refinery start: doesn't actually start a session","description":"## Problem\n\n`gt refinery start gastown` reports success but doesn't start a tmux session.\n\n## Evidence\n\n```\n$ gt refinery start gastown\nStarting refinery for gastown...\n✓ Refinery started for gastown\n\n$ tmux list-sessions | grep refinery\n(nothing)\n\n$ gt refinery status gastown\nState: ○ stopped\n```\n\n## Expected\n\nShould start a tmux session (e.g., gt-gastown-refinery) with Claude processing the merge queue.\n\n## Related\n\n- gt-kcee: Witness commands also need implementation\n- The refinery 'start' may just be updating state.json without spawning a session","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-18T21:58:57.188389-08:00","updated_at":"2025-12-19T01:33:49.858934-08:00","closed_at":"2025-12-19T01:33:49.858934-08:00"}
{"id":"gt-19gw","title":"Digest: mol-deacon-patrol","description":"Patrol: mayor handoff (Batch 1 complete, notifications working), 5 polecats now active (Batch 2)","status":"closed","priority":4,"issue_type":"task","created_at":"2025-12-22T23:48:49.79781-08:00","updated_at":"2025-12-22T23:48:49.79781-08:00","closed_at":"2025-12-22T23:48:49.797768-08:00","close_reason":"Squashed from 5 wisps"}
{"id":"gt-19gw","title":"Digest: mol-deacon-patrol","description":"Patrol: mayor handoff (Batch 1 complete, notifications working), 5 polecats now active (Batch 2)","status":"closed","priority":4,"issue_type":"task","created_at":"2025-12-22T23:48:49.79781-08:00","updated_at":"2025-12-22T23:48:49.79781-08:00","closed_at":"2025-12-22T23:48:49.797768-08:00","close_reason":"Squashed from 5 wisps","dependencies":[{"issue_id":"gt-19gw","depends_on_id":"gt-uaoy","type":"parent-child","created_at":"2025-12-22T23:48:49.798395-08:00","created_by":"stevey"}]}
{"id":"gt-1ar5","title":"Merge: gt-qsvq","description":"branch: polecat/capable\ntarget: main\nsource_issue: gt-qsvq\nrig: gastown","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-20T07:55:31.049583-08:00","updated_at":"2025-12-20T23:17:25.793049-08:00","closed_at":"2025-12-20T23:17:25.793049-08:00","close_reason":"Branches nuked, MRs obsolete"}
{"id":"gt-1cuq","title":"Merge: gt-svi.1","description":"type: merge-request\nbranch: polecat/Max\ntarget: main\nsource_issue: gt-svi.1\nrig: gastown","status":"closed","priority":0,"issue_type":"task","created_at":"2025-12-18T20:15:31.738938-08:00","updated_at":"2025-12-18T20:15:49.759778-08:00","closed_at":"2025-12-18T20:15:49.759778-08:00"}
{"id":"gt-1ero","title":"Test message","description":"Test body","status":"open","priority":2,"issue_type":"message","assignee":"gastown-alpha","created_at":"2025-12-20T21:53:03.66658-08:00","updated_at":"2025-12-20T21:53:03.66658-08:00"}
@@ -124,7 +124,7 @@
{"id":"gt-5v29","title":"Add 'wit' alias for witness command","description":"ref works as alias for refinery, but wit doesn't work for witness. Add Aliases: []string{\"wit\"} to witnessCmd.","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-20T23:11:53.453692-08:00","updated_at":"2025-12-20T23:11:53.453692-08:00"}
{"id":"gt-5wb7","title":"Update vision.md with proto/mol/wisp terminology","description":"Update the Steam Engine Metaphor section to use consistent phase terminology:\n\nCurrent (vision.md):\n- Proto molecules = fuel (templates)\n- Wisps = steam (transient execution traces) \n- Digests = distillate (permanent records)\n\nNew terminology:\n- **Proto** = crystal/solid - frozen template\n- **Mol** = liquid - reified durable instance (tracked in git)\n- **Wisp** = gas - ephemeral instance (evaporates after squash)\n- **Digest** = distillate - compressed summary after squash\n\nKey clarification needed:\n- Proto → bond → creates either Mol (durable) or Wisp (ephemeral)\n- The choice of Mol vs Wisp depends on --wisp flag\n- Default is Mol (durable, recorded in main beads)\n- Wisp lives in .beads-ephemeral/, squashes away","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-21T16:32:45.68234-08:00","updated_at":"2025-12-21T17:20:42.826322-08:00","dependencies":[{"issue_id":"gt-5wb7","depends_on_id":"gt-62hm","type":"blocks","created_at":"2025-12-21T16:33:17.307488-08:00","created_by":"daemon"}]}
{"id":"gt-5wtw","title":"Shutdown request handler","description":"Handle polecat shutdown requests:\n\nWhen polecat runs 'gt handoff --shutdown':\n1. Polecat sends mail to \u003crig\u003e/witness requesting shutdown\n2. Witness receives mail\n3. Witness verifies:\n - Git working tree is clean\n - Work is submitted (MR exists or in queue)\n - No uncommitted beads changes\n4. If clean:\n - gt session stop \u003crig\u003e/\u003cpolecat\u003e\n - git worktree remove polecats/\u003cname\u003e\n - git branch -d polecat/\u003cname\u003e\n5. If not clean:\n - Send nudge back to polecat\n - Track retry count\n\nDepends on fixing gt-dsfi (handoff deadlock).","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T03:14:22.968711-08:00","updated_at":"2025-12-20T09:28:15.512143-08:00","closed_at":"2025-12-20T09:28:15.512143-08:00","dependencies":[{"issue_id":"gt-5wtw","depends_on_id":"gt-53w6","type":"parent-child","created_at":"2025-12-20T03:14:37.300578-08:00","created_by":"daemon"},{"issue_id":"gt-5wtw","depends_on_id":"gt-mxyj","type":"blocks","created_at":"2025-12-20T03:14:38.893061-08:00","created_by":"daemon"}]}
{"id":"gt-5xph","title":"Document session cycling protocol in all templates","description":"Add explicit lifecycle request protocol to all agent templates.\n\n## Problem\nTemplates mention 'request session refresh' but don't show HOW.\nAgents don't know the protocol for requesting a cycle.\n\n## Protocol to document\n1. Write handoff mail to self (for continuity)\n2. Set requesting_cycle=true in state.json\n3. Send LIFECYCLE mail to deacon/:\n Subject: 'LIFECYCLE: \u003crole\u003e requesting cycle'\n Body: Reason for cycle request\n\n## Templates to update\n- prompts/roles/polecat.md\n- prompts/roles/crew.md \n- prompts/roles/witness.md\n- prompts/roles/refinery.md\n\n## Also add\n- Example state.json location for each role\n- When to request cycle (context full, work complete, etc.)\n- What happens after (daemon kills, respawns, new session primes)","status":"in_progress","priority":1,"issue_type":"task","assignee":"gastown/slit","created_at":"2025-12-22T16:43:54.10282-08:00","updated_at":"2025-12-23T00:09:06.898993-08:00"}
{"id":"gt-5xph","title":"Document session cycling protocol in all templates","description":"Add explicit lifecycle request protocol to all agent templates.\n\n## Problem\nTemplates mention 'request session refresh' but don't show HOW.\nAgents don't know the protocol for requesting a cycle.\n\n## Protocol to document\n1. Write handoff mail to self (for continuity)\n2. Set requesting_cycle=true in state.json\n3. Send LIFECYCLE mail to deacon/:\n Subject: 'LIFECYCLE: \u003crole\u003e requesting cycle'\n Body: Reason for cycle request\n\n## Templates to update\n- prompts/roles/polecat.md\n- prompts/roles/crew.md \n- prompts/roles/witness.md\n- prompts/roles/refinery.md\n\n## Also add\n- Example state.json location for each role\n- When to request cycle (context full, work complete, etc.)\n- What happens after (daemon kills, respawns, new session primes)","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-22T16:43:54.10282-08:00","updated_at":"2025-12-22T16:43:54.10282-08:00"}
{"id":"gt-5y5p","title":"Preflight molecule: verify baseline health before work","description":"Before assigning work, verify baseline (main branch) is healthy.\n\n**From VC**: Self-healing state machine (HEALTHY → SELF_HEALING → ESCALATED). ~200 lines.\n\n**Gas Town implementation**: Preflight molecule or refinery feature:\n```yaml\npreflight:\n gates: [test, lint, build]\n on_failure: create-fix-issue\n```\n\nRun at session start. If baseline broken, file a P0 fix issue and work on that first.\n\n**Value**: Self-healing baseline. Agents don't start from broken state.\n\n**VC lesson**: Prevents cascading failures. Agent shouldn't start work on broken code.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-20T20:30:13.807641-08:00","updated_at":"2025-12-20T20:30:13.807641-08:00","dependencies":[{"issue_id":"gt-5y5p","depends_on_id":"gt-zhpa","type":"parent-child","created_at":"2025-12-20T20:30:27.469804-08:00","created_by":"daemon"}]}
{"id":"gt-61o","title":"Review and audit all GGT beads","description":"Thorough review of all filed beads in gastown GGT repo. Check for: consistency, completeness, correct dependencies, accurate descriptions, proper prioritization. Ensure beads are self-contained and dont rely on external docs.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-15T20:24:07.152386-08:00","updated_at":"2025-12-15T21:23:58.255447-08:00","closed_at":"2025-12-15T21:23:58.255447-08:00"}
{"id":"gt-62hm","title":"Molecule Phase Terminology Documentation","description":"Document the three-phase molecule lifecycle with consistent terminology:\n\n## The Phases (States of Matter)\n\n| Phase | State | Nature |\n|-------|-------|--------|\n| **Proto** | Solid/Crystal | Frozen template, static definition in catalog |\n| **Mol** | Liquid | Reified instance, malleable, tracked in git (durable) |\n| **Wisp** | Gas | Evaporates after squash, ephemeral orchestration |\n\n## Documentation Gaps Identified\n\n### High Priority\n1. Update vision.md Steam Engine metaphor to use proto/mol/wisp terminology\n2. Add lifecycle diagram: Proto → bond → Mol or Wisp → squash → Digest\n3. Document Go types for phases (ProtoMolecule, etc.)\n4. Document .beads-ephemeral/ structure and purpose\n\n### Medium Priority\n5. Update CLAUDE.md files (polecat, witness, refinery) with molecule workflow\n6. Document bd mol bond/squash/burn CLI API with examples\n7. Add polecat guide: Executing molecules and generating summaries\n\n### Low Priority\n8. Add terminology glossary to architecture.md\n9. Create troubleshooting playbook for stuck molecules\n10. Add reference diagrams for state transitions\n\n## Current State\n- architecture.md has good molecule basics but uses inconsistent terminology\n- vision.md has Steam Engine metaphor (proto=fuel, wisp=steam, digest=distillate)\n- molecules.md has detailed reference but needs phase clarity\n- builtin_molecules.go has 8 molecules but no phase documentation in code","status":"open","priority":1,"issue_type":"epic","created_at":"2025-12-21T16:32:27.537487-08:00","updated_at":"2025-12-21T16:32:27.537487-08:00"}
@@ -153,7 +153,7 @@
{"id":"gt-7920","title":"Create mol-refinery-patrol with test failure gates","description":"## Problem\n\nThe Refinery role lacks:\n1. A formal patrol molecule (Deacon has mol-deacon-patrol)\n2. Engineer identity/philosophy guidance\n3. Structural enforcement of failure handling (not just documentation)\n\nWhen tests fail during merge processing, the current guidance doesn't prevent 'disavowal' - noting a problem exists and proceeding without tracking it.\n\n## The Structural Solution\n\nCreate `mol-refinery-patrol` with **verification gates** that make disavowal structurally impossible:\n\n```markdown\n## Molecule: mol-refinery-patrol\n\n## Step: inbox-check\nCheck mail for MR submissions, escalations, messages.\nProcess any urgent items first.\n\n## Step: queue-scan\nFetch remote, identify polecat branches waiting.\nIf queue empty, skip to context-check.\nTrack branch list for this cycle.\nNeeds: inbox-check\n\n## Step: process-branch\nPick next branch. Rebase on current main.\nIf rebase conflicts: notify polecat, skip to next branch.\nNeeds: queue-scan\n\n## Step: run-tests\nRun go test ./...\nTrack results (pass/fail count).\nNeeds: process-branch\n\n## Step: handle-failures\n**GATE**: If tests passed, this step auto-completes.\nIf tests failed:\n- Diagnose: branch regression or pre-existing?\n- Branch regression → abort, notify polecat\n- Pre-existing → EITHER fix OR \\`bd create --type=bug\\`\n**VERIFY**: Fix committed OR bead filed before proceeding.\nNeeds: run-tests\n\n## Step: merge-push\nMerge to main (ff-only preferred).\nPush immediately.\nDelete polecat branch.\nNeeds: handle-failures\n\n## Step: loop-check\nMore branches? Return to process-branch.\nOtherwise continue to summary.\nNeeds: merge-push\n\n## Step: generate-summary\nSummarize cycle: branches processed, tests results, issues filed.\nNeeds: loop-check\n\n## Step: context-check\nCheck context usage.\nIf high: write handoff, prepare for burn/respawn.\nNeeds: generate-summary\n\n## Step: burn-or-loop\nIf context LOW and queue non-empty: loop (return to queue-scan)\nIf context HIGH or queue empty: burn and exit\nNeeds: context-check\n```\n\n## Implementation Tasks\n\n### 1. Add RefineryPatrolMolecule() to builtin_molecules.go\n- Copy pattern from DeaconPatrolMolecule\n- Include all steps with proper `Needs:` dependencies\n- Emphasize handle-failures gate in description\n\n### 2. Update Refinery CLAUDE.md\nAdd sections:\n- **The Engineer Mindset** (identity/philosophy)\n- **The Scotty Test** (Would Scotty walk past this?)\n- **Patrol Molecule** reference (mol-refinery-patrol)\n- **Test Failure Protocol** (explicit decision tree)\n\n### 3. Create prompts/roles/refinery.md\n- Follow pattern of deacon.md\n- Include patrol execution loop diagram\n- Reference the molecule\n\n### 4. Consider mol-witness-patrol\n- Witness has detailed heartbeat protocol but no formal molecule\n- Similar pattern could apply\n\n## Why This Matters\n\nGUPP: 'If you have work on your hook, you have to run it.'\nThe patrol molecule puts failure handling ON THE HOOK as a mandatory step.\nYou can't skip handle-failures to get to merge-push.\nThe structure enforces good behavior.\n\n## Related\n- gt-7919: Fix failing beads tests (discovered during this session)\n- mol-deacon-patrol (existing pattern to follow)\n- docs/molecular-chemistry.md (chemistry concepts)\n- docs/wisp-architecture.md (wisp vs mol decision)","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-22T13:01:48.588945-08:00","updated_at":"2025-12-22T13:20:37.565029-08:00","closed_at":"2025-12-22T13:13:36.329669-08:00"}
{"id":"gt-7921","title":"Add await-work and plugin-run steps to mol-refinery-patrol","description":"## Context\n\nThe mol-refinery-patrol needs two additional steps:\n\n### 1. await-work (Decision Point)\n\nNot just 'wait for signal' but a crossroads:\n- Check for pending signals (mail, nudge, human)\n- Check plugin gates (any plugins need to run?)\n- Check maintenance schedule (daily/weekly due?)\n- Evaluate context and decide next action\n\nPosition: Between burn-or-loop and inbox-check (the loop point)\n\n### 2. plugin-run (Like Deacon)\n\nRefinery needs plugin support for:\n- **Monitors**: Flag PRs touching sensitive code\n- **Gates**: Block merges under certain conditions\n- **Schedulers**: Reorder queue based on priority/urgency\n- **Maintenance**: Weekly cleanup, audits, stats\n- **Audits**: Log merge statistics, track patterns\n\nPosition: After queue-scan, before process-branch\n\n## Implementation\n\nUpdate RefineryPatrolMolecule() in builtin_molecules.go to include:\n- await-work step with decision tree\n- plugin-run step with gate types\n\n## Related\n- gt-7920 (original mol-refinery-patrol)\n- docs/deacon-plugins.md (existing plugin model)","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-22T13:24:11.267237-08:00","updated_at":"2025-12-22T13:24:11.267237-08:00"}
{"id":"gt-7922","title":"Dynamic wisp modification: insert, reorder, bond mid-execution","description":"## Vision\n\nWisps should be living documents that adapt mid-execution, not static scripts.\n\n## Operations Needed\n\n### bd mol insert\nInsert a step into a running wisp:\n```bash\nbd mol insert \u003cwisp-id\u003e --after \u003cstep\u003e --step 'security-gate: Review auth changes'\n```\n\n### bd mol reorder \nChange step order in a running wisp:\n```bash\nbd mol reorder \u003cwisp-id\u003e --move process-branch --before current\n```\n\n### bd mol bond (to wisp)\nAttach an entire molecule to a running wisp:\n```bash\nbd mol bond mol-weekly-maintenance \u003cwisp-id\u003e --after await-work\n```\n\n### bd mol skip\nSkip a step (with reason):\n```bash\nbd mol skip \u003cwisp-id\u003e \u003cstep\u003e --reason 'Queue empty, skipping summary'\n```\n\n## Use Cases\n\n1. **Hotfix priority**: Reorder queue to process urgent MR first\n2. **Security gate**: Insert review step when PR touches sensitive files\n3. **Scheduled maintenance**: Bond maintenance mol on schedule\n4. **Context adaptation**: Skip steps when they don't apply\n\n## Why This Matters\n\nEngine room problems emerge dynamically. The wisp's flexibility lets agents model situations as they evolve, not execute rigid scripts.\n\nGUPP still applies: whatever's on your hook, you run. But the hook contents can adapt.\n\n## Implementation\n\nThis requires Beads changes to support wisp mutation:\n- Track step ordering in wisp storage\n- Support step insertion/deletion/reordering\n- Handle bond-to-wisp (spawn steps into existing wisp)\n- Preserve audit trail of modifications","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-22T13:24:26.533247-08:00","updated_at":"2025-12-22T13:24:26.533247-08:00"}
{"id":"gt-7923","title":"gt rig add / gt doctor: patrol awareness and wiring","description":"## Problem\n\nWhen a rig is installed or audited, we need to ensure all built-in patrols and role hooks are properly wired up.\n\n## gt rig add Changes\n\nWhen adding a rig, automatically:\n\n1. **Create patrol molecules** for each role:\n - mol-deacon-patrol (town-level)\n - mol-witness-patrol (per-rig)\n - mol-refinery-patrol (per-rig)\n\n2. **Set up hooks** that trigger patrols:\n - Deacon: daemon timer / heartbeat\n - Witness: daemon timer / polecat lifecycle events\n - Refinery: MR submission events / daemon timer\n\n3. **Configure daemon** to manage these patrols:\n - Register patrol molecules in daemon config\n - Set up respawn behavior for each role\n\n4. **Create plugin directories**:\n - ~/gt/plugins/ (town-level)\n - \u003crig\u003e/plugins/ (rig-level, if needed)\n\n## gt doctor Changes\n\nAdd patrol health checks:\n\n### patrol-molecules-exist\n- Verify mol-deacon-patrol, mol-witness-patrol, mol-refinery-patrol exist\n- Check they parse correctly (valid steps, dependencies)\n\n### patrol-hooks-wired\n- Verify hooks trigger patrol execution\n- Check daemon is configured to manage patrols\n\n### patrol-not-stuck\n- Detect wisps that have been in-progress too long\n- Flag orphaned patrol molecules (no active session)\n\n### patrol-plugins-accessible\n- Verify plugin directories exist and are readable\n- Check plugin frontmatter parses correctly\n\n### patrol-roles-have-prompts\n- Verify prompts/roles/*.md exist for each role\n- Check they reference the correct patrol molecule\n\n## Auto-fix\n\ngt doctor --fix can:\n- Create missing patrol molecules\n- Wire up missing hooks\n- Create plugin directories\n- NOT restart stuck patrols (needs human decision)\n\n## Related\n- gt-7920 (mol-refinery-patrol)\n- gt-7921 (await-work and plugin-run)\n- docs/wisp-architecture.md","status":"in_progress","priority":1,"issue_type":"feature","assignee":"gastown/rictus","created_at":"2025-12-22T13:24:43.158379-08:00","updated_at":"2025-12-23T00:10:58.83252-08:00"}
{"id":"gt-7923","title":"gt rig add / gt doctor: patrol awareness and wiring","description":"## Problem\n\nWhen a rig is installed or audited, we need to ensure all built-in patrols and role hooks are properly wired up.\n\n## gt rig add Changes\n\nWhen adding a rig, automatically:\n\n1. **Create patrol molecules** for each role:\n - mol-deacon-patrol (town-level)\n - mol-witness-patrol (per-rig)\n - mol-refinery-patrol (per-rig)\n\n2. **Set up hooks** that trigger patrols:\n - Deacon: daemon timer / heartbeat\n - Witness: daemon timer / polecat lifecycle events\n - Refinery: MR submission events / daemon timer\n\n3. **Configure daemon** to manage these patrols:\n - Register patrol molecules in daemon config\n - Set up respawn behavior for each role\n\n4. **Create plugin directories**:\n - ~/gt/plugins/ (town-level)\n - \u003crig\u003e/plugins/ (rig-level, if needed)\n\n## gt doctor Changes\n\nAdd patrol health checks:\n\n### patrol-molecules-exist\n- Verify mol-deacon-patrol, mol-witness-patrol, mol-refinery-patrol exist\n- Check they parse correctly (valid steps, dependencies)\n\n### patrol-hooks-wired\n- Verify hooks trigger patrol execution\n- Check daemon is configured to manage patrols\n\n### patrol-not-stuck\n- Detect wisps that have been in-progress too long\n- Flag orphaned patrol molecules (no active session)\n\n### patrol-plugins-accessible\n- Verify plugin directories exist and are readable\n- Check plugin frontmatter parses correctly\n\n### patrol-roles-have-prompts\n- Verify prompts/roles/*.md exist for each role\n- Check they reference the correct patrol molecule\n\n## Auto-fix\n\ngt doctor --fix can:\n- Create missing patrol molecules\n- Wire up missing hooks\n- Create plugin directories\n- NOT restart stuck patrols (needs human decision)\n\n## Related\n- gt-7920 (mol-refinery-patrol)\n- gt-7921 (await-work and plugin-run)\n- docs/wisp-architecture.md","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-22T13:24:43.158379-08:00","updated_at":"2025-12-22T13:24:43.158379-08:00"}
{"id":"gt-7hor","title":"Document the Propulsion Principle","description":"Write canonical documentation for the Universal Gas Town Propulsion Principle.\n\nLocation: gastown/mayor/rig/docs/propulsion-principle.md\n\nContent:\n- The One Rule (hook has work → work happens)\n- Why it works (stateless agents, molecule-driven)\n- The sling lifecycle diagram\n- Agent startup protocol\n- Examples and anti-patterns\n\nThis is foundational theory-of-operation documentation.","status":"closed","priority":2,"issue_type":"task","assignee":"gastown/slit","created_at":"2025-12-22T03:17:47.790012-08:00","updated_at":"2025-12-22T12:31:43.230007-08:00","closed_at":"2025-12-22T12:31:43.230007-08:00","close_reason":"Documentation complete. Created docs/propulsion-principle.md covering the One Rule, sling lifecycle, agent startup protocol, and examples/anti-patterns."}
{"id":"gt-7hz3","title":"Merge: gt-92l","description":"branch: polecat/Ace\ntarget: main\nsource_issue: gt-92l\nrig: gastown","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-19T16:31:37.716367-08:00","updated_at":"2025-12-19T18:26:14.102101-08:00","closed_at":"2025-12-19T17:48:09.627376-08:00"}
{"id":"gt-7iek","title":"context-check","description":"Assess own context usage. If high, prepare for handoff.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-21T17:51:45.43771-08:00","updated_at":"2025-12-21T17:51:45.43771-08:00","dependencies":[{"issue_id":"gt-7iek","depends_on_id":"gt-hbnz","type":"parent-child","created_at":"2025-12-21T17:51:45.442974-08:00","created_by":"stevey"}],"wisp":true}
@@ -269,9 +269,8 @@
{"id":"gt-ca4v.7","title":"More branches to process?","description":"More branches to process?\n\nIf yes: Return to process-branch with next branch.\nIf no: Continue to generate-summary.\n\nTrack: branches processed, branches skipped (with reasons).\n\ninstantiated_from: mol-refinery-patrol\nstep: loop-check","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-22T17:13:47.746798-08:00","updated_at":"2025-12-22T17:13:47.746798-08:00","dependencies":[{"issue_id":"gt-ca4v.7","depends_on_id":"gt-ca4v","type":"parent-child","created_at":"2025-12-22T17:13:47.747125-08:00","created_by":"daemon"},{"issue_id":"gt-ca4v.7","depends_on_id":"gt-ca4v.6","type":"blocks","created_at":"2025-12-22T17:13:48.464174-08:00","created_by":"daemon"}]}
{"id":"gt-ca4v.8","title":"Summarize this patrol cycle.","description":"Summarize this patrol cycle.\n\nInclude:\n- Branches processed (count, names)\n- Test results (pass/fail)\n- Issues filed (if any)\n- Branches skipped (with reasons)\n- Any escalations sent\n\nThis becomes the digest when the patrol is squashed.\n\ninstantiated_from: mol-refinery-patrol\nstep: generate-summary","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-22T17:13:47.827331-08:00","updated_at":"2025-12-22T17:13:47.827331-08:00","dependencies":[{"issue_id":"gt-ca4v.8","depends_on_id":"gt-ca4v","type":"parent-child","created_at":"2025-12-22T17:13:47.827682-08:00","created_by":"daemon"},{"issue_id":"gt-ca4v.8","depends_on_id":"gt-ca4v.7","type":"blocks","created_at":"2025-12-22T17:13:48.545582-08:00","created_by":"daemon"}]}
{"id":"gt-ca4v.9","title":"Check own context usage.","description":"Check own context usage.\n\nIf context is HIGH (\u003e80%):\n- Write handoff summary\n- Prepare for burn/respawn\n\nIf context is LOW:\n- Can continue processing\n\ninstantiated_from: mol-refinery-patrol\nstep: context-check","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-22T17:13:47.909098-08:00","updated_at":"2025-12-22T17:13:47.909098-08:00","dependencies":[{"issue_id":"gt-ca4v.9","depends_on_id":"gt-ca4v","type":"parent-child","created_at":"2025-12-22T17:13:47.909418-08:00","created_by":"daemon"},{"issue_id":"gt-ca4v.9","depends_on_id":"gt-ca4v.8","type":"blocks","created_at":"2025-12-22T17:13:48.621628-08:00","created_by":"daemon"}]}
{"id":"gt-caih","title":"Witness handoff bead state persistence","description":"Implement state persistence for Witness across wisp cycles.\n\n## Problem\nWisps burn between cycles, but Witness needs to remember:\n- Which workers have been nudged\n- How many times (nudge_count)\n- When was last nudge\n- Last observed activity\n\n## Solution\nWitness handoff bead with worker_states field:\n\n```json\n{\n \"id\": \"gt-witness-state\",\n \"type\": \"handoff\",\n \"assignee\": \"\u003crig\u003e/witness\",\n \"pinned\": true,\n \"worker_states\": {\n \"furiosa\": {\n \"issue\": \"gt-123\",\n \"nudge_count\": 2,\n \"last_nudge\": \"2024-12-22T10:00:00Z\"\n }\n },\n \"last_patrol\": \"2024-12-22T10:05:00Z\"\n}\n```\n\n## Implementation\n1. On patrol start: bd show \u003cwitness-handoff-id\u003e to load state\n2. During patrol: update in-memory state\n3. On save-state step: bd update to persist\n4. State survives wisp burn/squash\n\n## Depends on\n- gt-83k0 (mol-witness-patrol definition)","status":"closed","priority":1,"issue_type":"task","assignee":"gastown/furiosa","created_at":"2025-12-22T16:42:57.427131-08:00","updated_at":"2025-12-23T00:11:46.404639-08:00","closed_at":"2025-12-23T00:11:46.404639-08:00","close_reason":"Implemented handoff bead state persistence for Witness with WorkerState tracking","dependencies":[{"issue_id":"gt-caih","depends_on_id":"gt-83k0","type":"blocks","created_at":"2025-12-22T16:43:59.609821-08:00","created_by":"daemon"}]}
{"id":"gt-caih","title":"Witness handoff bead state persistence","description":"Implement state persistence for Witness across wisp cycles.\n\n## Problem\nWisps burn between cycles, but Witness needs to remember:\n- Which workers have been nudged\n- How many times (nudge_count)\n- When was last nudge\n- Last observed activity\n\n## Solution\nWitness handoff bead with worker_states field:\n\n```json\n{\n \"id\": \"gt-witness-state\",\n \"type\": \"handoff\",\n \"assignee\": \"\u003crig\u003e/witness\",\n \"pinned\": true,\n \"worker_states\": {\n \"furiosa\": {\n \"issue\": \"gt-123\",\n \"nudge_count\": 2,\n \"last_nudge\": \"2024-12-22T10:00:00Z\"\n }\n },\n \"last_patrol\": \"2024-12-22T10:05:00Z\"\n}\n```\n\n## Implementation\n1. On patrol start: bd show \u003cwitness-handoff-id\u003e to load state\n2. During patrol: update in-memory state\n3. On save-state step: bd update to persist\n4. State survives wisp burn/squash\n\n## Depends on\n- gt-83k0 (mol-witness-patrol definition)","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-22T16:42:57.427131-08:00","updated_at":"2025-12-22T16:42:57.427131-08:00","dependencies":[{"issue_id":"gt-caih","depends_on_id":"gt-83k0","type":"blocks","created_at":"2025-12-22T16:43:59.609821-08:00","created_by":"daemon"}]}
{"id":"gt-caz","title":"Timed Beads: Scheduled recurring work","description":"## Summary\n\nTimed beads wake up periodically and get injected into the ready queue by the daemon.\n\n## Schema Extension\n\n```yaml\nid: gt-weekly-sync\ntype: task # or sentinel\nschedule: \"0 9 * * 1\" # cron: Monday 9am\n# OR\ninterval: 24h # every 24 hours\ntier: haiku # cheap model for routine checks\nnext_run: 2025-12-20T09:00:00Z\n```\n\n## Daemon Integration\n\nDaemon heartbeat loop:\n1. Check timed beads where `next_run \u003c= now`\n2. For each due bead:\n - Inject into ready queue (set status to open if needed)\n - Update `next_run` based on schedule/interval\n3. Witnesses pick up work via `bd ready`\n\n## Use Cases\n\n- Weekly team sync reminders\n- Daily health checks\n- Periodic cleanup tasks\n- Scheduled reports\n\n## Interaction with Pinned Beads\n\nA pinned bead can be timed - it wakes up periodically but never closes.\nThis is how you model \"background services\" in Gas Town.","status":"open","priority":2,"issue_type":"epic","created_at":"2025-12-18T18:07:39.665294-08:00","updated_at":"2025-12-18T18:07:39.665294-08:00"}
{"id":"gt-cd76","title":"mol-deacon-patrol","description":"[RESURRECTED] This issue was deleted but recreated as a tombstone to preserve hierarchical structure.\n\nOriginal description:\nDeacon patrol molecule template. Label: template","status":"open","priority":4,"issue_type":"epic","created_at":"2025-12-22T22:37:41.835838-08:00","updated_at":"2025-12-22T22:37:41.835838-08:00","wisp":true}
{"id":"gt-cik","title":"Overseer Crew: User-managed persistent workspaces","description":"## Overview\n\nCrew workers are the overseer's (human's) personal workspaces within a rig. Unlike polecats which are witness-managed and ephemeral, crew workers are:\n\n- **Persistent**: Not auto-garbage-collected\n- **User-managed**: Overseer controls lifecycle\n- **Long-lived identities**: dave, emma, fred - recognizable names\n- **Gas Town integrated**: Mail, handoff mechanics work\n- **Tmux optional**: Can work in terminal directly\n\n## Directory Structure\n\n```\n\u003crig\u003e/\n polecats/ # Managed workers (witness controls)\n refinery/ # Merge queue processor\n witness/ # Pit boss\n crew/ # Overseer's personal workspaces\n dave/ # Full clone, persistent\n emma/ # Full clone, persistent\n fred/ # Full clone, persistent\n```\n\n## Key Differences from Polecats\n\n- Location: crew/ instead of polecats/\n- Lifecycle: User-managed, not witness-managed\n- Auto-cleanup: Never (polecats auto-cleanup on swarm land)\n- Issue assignment: Optional (polecats require it)\n- Tmux: Optional (polecats require it)\n- Mail \u0026 Handoff: Yes for both\n- Identity: Persistent (polecats are ephemeral)\n\n## CLI Commands\n\n- gt crew add \u003cname\u003e [--rig \u003crig\u003e] - Create crew workspace\n- gt crew list [--rig \u003crig\u003e] - List crew workspaces\n- gt crew at \u003crig\u003e/\u003cname\u003e - Attach to workspace (start session)\n- gt crew attach \u003cname\u003e - Attach (infer rig from cwd)\n- gt crew refresh \u003cname\u003e - Handoff + restart (context cycling)\n- gt crew remove \u003cname\u003e [--force] - Remove workspace\n- gt crew status [\u003cname\u003e] - Show workspace status\n\n## Design Notes\n\n- Crew workers use full git clones (not worktrees)\n- Optional beads integration via BEADS_DIR\n- Mail-to-self handoff works for context cycling\n- No witness monitoring or nudging\n- No automatic issue assignment required\n\n## Background\n\nUsers often maintain separate repo clones for serial agent work. This is tedious to set up manually. Crew workspaces bring these into Gas Town's infrastructure while keeping user control.","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-12-16T16:47:37.529887-08:00","updated_at":"2025-12-16T20:59:46.13518-08:00","closed_at":"2025-12-16T20:59:46.13518-08:00"}
{"id":"gt-cik.1","title":"Crew directory structure and config","description":"Add crew/ directory support to rig structure. Include:\n- crew/ as peer to polecats/, refinery/, witness/\n- Crew worker subdirectories with full git clones\n- Optional BEADS_DIR configuration for beads integration\n- Crew state tracking (separate from polecat state)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-16T16:48:00.285499-08:00","updated_at":"2025-12-16T20:47:23.003869-08:00","closed_at":"2025-12-16T20:31:23.558027-08:00","dependencies":[{"issue_id":"gt-cik.1","depends_on_id":"gt-cik","type":"parent-child","created_at":"2025-12-16T16:48:00.28789-08:00","created_by":"daemon"}]}
{"id":"gt-cik.2","title":"gt crew add: Create crew workspace","description":"Implement 'gt crew add \u003cname\u003e [--rig \u003crig\u003e]' command:\n- Clone repo into \u003crig\u003e/crew/\u003cname\u003e/\n- Create feature branch (optional, or stay on main)\n- Register for mail delivery\n- Initialize CLAUDE.md with crew worker prompting\n- Do NOT register with witness (user-managed)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-16T16:48:02.208618-08:00","updated_at":"2025-12-16T20:47:23.004728-08:00","closed_at":"2025-12-16T20:46:19.873949-08:00","dependencies":[{"issue_id":"gt-cik.2","depends_on_id":"gt-cik","type":"parent-child","created_at":"2025-12-16T16:48:02.210603-08:00","created_by":"daemon"}]}
@@ -415,7 +414,7 @@
{"id":"gt-iua8","title":"Merge: gt-frs","description":"branch: polecat/Slit\ntarget: main\nsource_issue: gt-frs\nrig: gastown","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-19T16:30:05.529099-08:00","updated_at":"2025-12-19T18:26:14.104887-08:00","closed_at":"2025-12-19T17:48:44.654109-08:00"}
{"id":"gt-j4nu","title":"Merge: gt-g44u.3","description":"branch: polecat/Ace\ntarget: main\nsource_issue: gt-g44u.3\nrig: gastown","status":"closed","priority":0,"issue_type":"merge-request","created_at":"2025-12-19T16:14:52.767156-08:00","updated_at":"2025-12-19T17:35:36.663796-08:00","closed_at":"2025-12-19T17:35:36.663796-08:00"}
{"id":"gt-j5tk","title":"Work assignment messages should auto-close on completion","description":"When a polecat completes work on an issue, the work assignment message (msg-type:task) stays open. Found 7 stale work assignments in gastown after swarm completed.\n\nProposal: When bd close is called on an issue, auto-close any work assignment messages that reference that issue in their body.\n\nAlternative: Work assignment messages could use a different lifecycle - perhaps they should be acked (closed) when the polecat starts working, not when they finish.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-20T03:12:28.403974-08:00","updated_at":"2025-12-20T03:12:28.403974-08:00"}
{"id":"gt-j6s8","title":"Refinery startup: bond mol-refinery-patrol on start","description":"Wire up Refinery to automatically bond its patrol molecule on startup.\n\n## Current state\n- mol-refinery-patrol exists in builtin_molecules.go\n- prompts/roles/refinery.md describes the protocol\n- Refinery doesn't auto-bond on startup\n\n## Desired behavior\nOn Refinery session start:\n1. gt prime detects RoleRefinery\n2. Check for existing in-progress patrol: bd list --status=in_progress --assignee=refinery\n3. If found: resume from current step\n4. If not found: bd mol bond mol-refinery-patrol --wisp\n5. Output patrol context to agent\n\n## Implementation options\nA) Add to gt prime (outputRefineryPatrolContext)\nB) Add startup hook in refinery CLAUDE.md\nC) Both (prime detects, template reinforces)\n\n## Testing\n- Start refinery session\n- Verify patrol bonds automatically\n- Kill mid-patrol, restart, verify resumes\n\n## Depends on\n- gt-3x0z.10 (existing issue for Refinery patrol)","status":"closed","priority":1,"issue_type":"task","assignee":"gastown/dementus","created_at":"2025-12-22T16:43:34.739741-08:00","updated_at":"2025-12-23T00:10:46.503806-08:00","closed_at":"2025-12-23T00:10:46.503806-08:00","close_reason":"Implemented outputRefineryPatrolContext in gt prime. Auto-spawns mol-refinery-patrol wisp on Refinery startup when no active patrol exists."}
{"id":"gt-j6s8","title":"Refinery startup: bond mol-refinery-patrol on start","description":"Wire up Refinery to automatically bond its patrol molecule on startup.\n\n## Current state\n- mol-refinery-patrol exists in builtin_molecules.go\n- prompts/roles/refinery.md describes the protocol\n- Refinery doesn't auto-bond on startup\n\n## Desired behavior\nOn Refinery session start:\n1. gt prime detects RoleRefinery\n2. Check for existing in-progress patrol: bd list --status=in_progress --assignee=refinery\n3. If found: resume from current step\n4. If not found: bd mol bond mol-refinery-patrol --wisp\n5. Output patrol context to agent\n\n## Implementation options\nA) Add to gt prime (outputRefineryPatrolContext)\nB) Add startup hook in refinery CLAUDE.md\nC) Both (prime detects, template reinforces)\n\n## Testing\n- Start refinery session\n- Verify patrol bonds automatically\n- Kill mid-patrol, restart, verify resumes\n\n## Depends on\n- gt-3x0z.10 (existing issue for Refinery patrol)","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-22T16:43:34.739741-08:00","updated_at":"2025-12-22T16:43:34.739741-08:00"}
{"id":"gt-j87","title":"Design: Work flow simulation and validation","description":"Validate GGT designs through simulation before implementation.\n\n## Validation Approaches\n\n### 1. Dry-Run Simulation (Recommended First)\nMayor walks through scenarios mentally/on paper:\n- \"If polecat Toast signals done with dirty git state, what happens?\"\n- \"If Witness context fills mid-verification, what state is lost?\"\n- \"If two polecats try to close same issue, what happens?\"\n\nCreate beads for any gaps discovered.\n\n### 2. Real Work in gastown-py\nUse Python Gas Town to stress-test assumptions:\n- Run actual batch work on test repos\n- Observe edge cases in practice\n- Document issues found\n\n### 3. Edge Case Analysis\nSystematic review of failure modes:\n- Agent crashes mid-operation\n- Network failures during sync\n- Concurrent access to shared state\n- Context limits hit at bad times\n\n## Key Scenarios to Validate\n\n- [ ] Witness session cycling (state preservation)\n- [ ] Polecat decommission with dirty state\n- [ ] Merge conflicts in queue\n- [ ] Beads sync conflicts between workers\n- [ ] Escalation path (stuck worker -\u003e Mayor)\n- [ ] Cross-rig communication\n- [ ] Federation mail routing (future)\n\n## Success Criteria\n\n- No data loss scenarios identified\n- Clear recovery paths for all failure modes\n- Edge cases either handled or documented as limitations\n- Design improves as model cognition improves\n\n## Output\n\nFor each scenario validated:\n1. Document in relevant bead if issue found\n2. Create new beads for missing functionality\n3. Update architecture.md if design changes","status":"open","priority":1,"issue_type":"epic","created_at":"2025-12-15T20:24:11.251841-08:00","updated_at":"2025-12-16T17:25:49.858717-08:00"}
{"id":"gt-jgdx","title":"Digest: mol-deacon-patrol","description":"Test patrol cycle - first run, no actual work done","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-22T02:07:03.388821-08:00","updated_at":"2025-12-22T02:07:03.388821-08:00","closed_at":"2025-12-22T02:07:03.388793-08:00","close_reason":"Squashed from 5 wisps"}
{"id":"gt-jpt","title":"Town-level beads: Real DB for coordination mail","description":"Implement Option A from mail redesign: Town gets real beads DB for coordination.\n\n## Background\n\nMail is now Beads. But currently:\n- Town .beads/redirect points to rig beads\n- mayor/mail/ has legacy JSONL files\n- Cross-rig coordination has no clear home\n\n## Design\n\nTown beads = coordination, cross-rig mail, mayor inbox, handoffs\nRig beads = project issues, work items\n\nMatches HOP hierarchy: platform \u003e project \u003e worker\n\n## Structure\n\n~/gt/\n .beads/ # REAL beads DB (prefix: gm-)\n mayor/\n town.json\n state.json # NO mail/ directory\n gastown/\n .beads/ # Rig beads (prefix: ga-)\n\n## Tasks\n\n1. Delete ~/gt/.beads/redirect\n2. Run bd init --prefix gm at ~/gt/ (town beads)\n3. Delete ~/gt/mayor/mail/ directory\n4. Update gt mail to use beads not JSONL\n5. Add mail fields (thread_id, reply_to, msg_type)\n6. Update gt prime for two-tier model\n7. Update docs/architecture.md\n\n## Addressing\n\n- mayor/ -\u003e town beads\n- rig/agent -\u003e rig beads\n- Cross-rig -\u003e town beads","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-12-17T19:09:55.855955-08:00","updated_at":"2025-12-19T01:57:17.032558-08:00","closed_at":"2025-12-19T01:57:17.032558-08:00"}
@@ -461,7 +460,7 @@
{"id":"gt-l6r9","title":"Test molecule","description":"Test","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T14:29:43.985973-08:00","updated_at":"2025-12-19T14:30:00.084138-08:00","closed_at":"2025-12-19T14:30:00.084138-08:00"}
{"id":"gt-l7cd","title":"Work on gt-role-template: Refine witness/CLAUDE.md role t...","description":"Work on gt-role-template: Refine witness/CLAUDE.md role template. See issue for details. Run 'bd show gt-role-template' to see the full issue.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-20T03:47:29.414394-08:00","updated_at":"2025-12-20T03:54:13.699528-08:00","closed_at":"2025-12-20T03:54:13.699528-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-ldk8","title":"Witness should verify polecat submitted before stopping session","description":"## Problem\n\nWitness stopped dementus's session before it could call `gt done`, losing the MR submission. I had to manually push and submit the branch.\n\n## Root Cause\n\nWitness cleanup is triggered by nudge/manual check rather than by receiving POLECAT_DONE message. When Witness cleans up based on issue status (closed), it doesn't wait for the polecat to complete its shutdown sequence.\n\n## Expected Behavior\n\nWitness should only stop a polecat session after:\n1. Receiving POLECAT_DONE message from that polecat, OR\n2. Timeout waiting for POLECAT_DONE after issue is closed\n\n## Current Behavior\n\nWitness stops sessions immediately when asked to check for completions, even if polecat hasn't called `gt done` yet.\n\n## Fix\n\nIn mol-witness-patrol inbox-check step:\n- Only clean up polecats that have sent POLECAT_DONE\n- For polecats with closed issues but no DONE message, nudge them to complete\n- Add timeout before force-killing","status":"in_progress","priority":1,"issue_type":"bug","assignee":"gastown/nux","created_at":"2025-12-22T23:54:12.969528-08:00","updated_at":"2025-12-23T00:10:56.589919-08:00"}
{"id":"gt-ldk8","title":"Witness should verify polecat submitted before stopping session","description":"## Problem\n\nWitness stopped dementus's session before it could call `gt done`, losing the MR submission. I had to manually push and submit the branch.\n\n## Root Cause\n\nWitness cleanup is triggered by nudge/manual check rather than by receiving POLECAT_DONE message. When Witness cleans up based on issue status (closed), it doesn't wait for the polecat to complete its shutdown sequence.\n\n## Expected Behavior\n\nWitness should only stop a polecat session after:\n1. Receiving POLECAT_DONE message from that polecat, OR\n2. Timeout waiting for POLECAT_DONE after issue is closed\n\n## Current Behavior\n\nWitness stops sessions immediately when asked to check for completions, even if polecat hasn't called `gt done` yet.\n\n## Fix\n\nIn mol-witness-patrol inbox-check step:\n- Only clean up polecats that have sent POLECAT_DONE\n- For polecats with closed issues but no DONE message, nudge them to complete\n- Add timeout before force-killing","status":"open","priority":1,"issue_type":"bug","created_at":"2025-12-22T23:54:12.969528-08:00","updated_at":"2025-12-22T23:54:12.969528-08:00"}
{"id":"gt-ldm4","title":"verify-tests","description":"Run existing tests. Add new tests for new functionality.\nEnsure adequate coverage.\n\ngo test ./...\n\nFix any test failures before proceeding.\n\nDepends: implement","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-21T21:48:26.322056-08:00","updated_at":"2025-12-21T21:48:26.322056-08:00","dependencies":[{"issue_id":"gt-ldm4","depends_on_id":"gt-i4lo","type":"parent-child","created_at":"2025-12-21T21:48:26.326258-08:00","created_by":"stevey"},{"issue_id":"gt-ldm4","depends_on_id":"gt-vhby","type":"blocks","created_at":"2025-12-21T21:48:26.326815-08:00","created_by":"stevey"}],"wisp":true}
{"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-leeb","title":"load-context","description":"Run gt prime and bd prime. Verify issue assignment.\nCheck inbox for any relevant messages.\n\nRead the assigned issue (gt-qwyu) and understand the requirements.\nIdentify any blockers or missing information.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-21T21:58:52.59974-08:00","updated_at":"2025-12-21T21:59:10.94207-08:00","closed_at":"2025-12-21T21:59:10.94207-08:00","close_reason":"test cleanup","dependencies":[{"issue_id":"gt-leeb","depends_on_id":"gt-q6hl","type":"parent-child","created_at":"2025-12-21T21:58:52.600633-08:00","created_by":"stevey"}],"wisp":true}
@@ -476,7 +475,7 @@
{"id":"gt-lwuu.8","title":"request-shutdown","description":"Send shutdown request to Witness.\nWait for termination.\n\nThe polecat is now ready to be cleaned up.\nDo not exit directly - wait for Witness to kill the session.\n\nDepends: generate-summary","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-21T21:47:53.776509-08:00","updated_at":"2025-12-21T21:47:53.776509-08:00","labels":["template"],"dependencies":[{"issue_id":"gt-lwuu.8","depends_on_id":"gt-lwuu","type":"parent-child","created_at":"2025-12-21T21:47:53.777872-08:00","created_by":"daemon"}]}
{"id":"gt-lx3n","title":"Witness startup: bond mol-witness-patrol on start","description":"Wire up Witness to automatically bond its patrol molecule on startup.\n\n## Desired behavior\nOn Witness session start:\n1. gt prime detects RoleWitness\n2. Check for existing in-progress patrol\n3. If found: resume from current step\n4. If not found: bd mol bond mol-witness-patrol --wisp\n5. Output patrol context to agent\n\n## Depends on\n- gt-83k0 (mol-witness-patrol definition)\n- gt-caih (handoff bead state persistence)","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-22T16:43:42.840567-08:00","updated_at":"2025-12-22T16:43:42.840567-08:00","dependencies":[{"issue_id":"gt-lx3n","depends_on_id":"gt-83k0","type":"blocks","created_at":"2025-12-22T16:43:59.685455-08:00","created_by":"daemon"},{"issue_id":"gt-lx3n","depends_on_id":"gt-caih","type":"blocks","created_at":"2025-12-22T16:43:59.760763-08:00","created_by":"daemon"}]}
{"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"}
{"id":"gt-lz13","title":"Update templates with molecule navigation workflow","description":"Update all agent templates to use new molecule navigation commands.\n\n## Commands to integrate\n- bd mol current: orientation after startup/handoff (bd-sal9)\n- bd close --continue: seamless step transitions (bd-ieyy)\n\n## Templates to update\n\n### prompts/roles/polecat.md\n- Add bd mol current to 'Finding Your Work' section\n- Replace manual 3-command dance with bd close --continue\n- Update 'Working Through Steps' section\n\n### prompts/roles/crew.md \n- Add molecule navigation to workflow section\n- Show bd mol current for session startup\n\n### prompts/roles/refinery.md\n- Update patrol step transitions to use --continue\n\n### prompts/roles/witness.md\n- Update patrol step transitions to use --continue\n\n### prompts/roles/deacon.md\n- Update patrol step transitions to use --continue\n\n## Key message\nThe Propulsion Principle: close a step, immediately get handed the next.\nNo friction, no forgetting, no 3-command dance.\n\n## Blocked by (Beads features)\n- bd-sal9: bd mol current\n- bd-ieyy: bd close --continue","status":"in_progress","priority":1,"issue_type":"task","assignee":"gastown/scrotus","created_at":"2025-12-22T17:01:12.119194-08:00","updated_at":"2025-12-23T00:11:54.64288-08:00","dependencies":[{"issue_id":"gt-lz13","depends_on_id":"gt-qswb","type":"blocks","created_at":"2025-12-22T17:01:31.707885-08:00","created_by":"daemon"},{"issue_id":"gt-lz13","depends_on_id":"gt-fly0","type":"blocks","created_at":"2025-12-22T17:01:31.78232-08:00","created_by":"daemon"}]}
{"id":"gt-lz13","title":"Update templates with molecule navigation workflow","description":"Update all agent templates to use new molecule navigation commands.\n\n## Commands to integrate\n- bd mol current: orientation after startup/handoff (bd-sal9)\n- bd close --continue: seamless step transitions (bd-ieyy)\n\n## Templates to update\n\n### prompts/roles/polecat.md\n- Add bd mol current to 'Finding Your Work' section\n- Replace manual 3-command dance with bd close --continue\n- Update 'Working Through Steps' section\n\n### prompts/roles/crew.md \n- Add molecule navigation to workflow section\n- Show bd mol current for session startup\n\n### prompts/roles/refinery.md\n- Update patrol step transitions to use --continue\n\n### prompts/roles/witness.md\n- Update patrol step transitions to use --continue\n\n### prompts/roles/deacon.md\n- Update patrol step transitions to use --continue\n\n## Key message\nThe Propulsion Principle: close a step, immediately get handed the next.\nNo friction, no forgetting, no 3-command dance.\n\n## Blocked by (Beads features)\n- bd-sal9: bd mol current\n- bd-ieyy: bd close --continue","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-22T17:01:12.119194-08:00","updated_at":"2025-12-22T17:04:28.920287-08:00","dependencies":[{"issue_id":"gt-lz13","depends_on_id":"gt-qswb","type":"blocks","created_at":"2025-12-22T17:01:31.707885-08:00","created_by":"daemon"},{"issue_id":"gt-lz13","depends_on_id":"gt-fly0","type":"blocks","created_at":"2025-12-22T17:01:31.78232-08:00","created_by":"daemon"}]}
{"id":"gt-m3hh","title":"Merge: gt-7hor","description":"branch: polecat/slit\ntarget: main\nsource_issue: gt-7hor\nrig: gastown","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-22T12:32:43.108463-08:00","updated_at":"2025-12-22T16:01:13.501585-08:00","closed_at":"2025-12-22T16:01:13.501585-08:00","close_reason":"Merged to main"}
{"id":"gt-mc4n","title":"load-context","description":"Run gt prime and bd prime. Verify issue assignment.\nCheck inbox for any relevant messages.\n\nRead the assigned issue (gt-test123) and understand the requirements.\nIdentify any blockers or missing information.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-21T21:56:18.534569-08:00","updated_at":"2025-12-21T21:56:27.508015-08:00","closed_at":"2025-12-21T21:56:27.508015-08:00","close_reason":"test cleanup","dependencies":[{"issue_id":"gt-mc4n","depends_on_id":"gt-zjqs","type":"parent-child","created_at":"2025-12-21T21:56:18.535487-08:00","created_by":"stevey"}],"wisp":true}
{"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","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"}
@@ -499,7 +498,7 @@
{"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"}
{"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-ne1t","title":"Design molecule step hooks","description":"Hooks that fire between molecule steps. When a bead in a molecule closes, trigger hook that can spawn agent attention to prompts/requests. This enables reactive orchestration - the molecule drives, hooks respond. Gas Town feature built on Beads data plane.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-21T17:53:14.568075-08:00","updated_at":"2025-12-21T17:53:14.568075-08:00"}
{"id":"gt-neim","title":"Digest: mol-deacon-patrol","description":"Patrol: dave handoff (vision docs, 7 design gaps filed), furiosa tracer bullet on gt-oiv0, all agents up","status":"closed","priority":4,"issue_type":"task","created_at":"2025-12-22T22:38:37.785872-08:00","updated_at":"2025-12-22T22:38:37.785872-08:00","closed_at":"2025-12-22T22:38:37.785832-08:00","close_reason":"Squashed from 5 wisps","dependencies":[{"issue_id":"gt-neim","depends_on_id":"gt-cd76","type":"parent-child","created_at":"2025-12-22T22:38:37.786401-08:00","created_by":"stevey"}]}
{"id":"gt-neim","title":"Digest: mol-deacon-patrol","description":"Patrol: dave handoff (vision docs, 7 design gaps filed), furiosa tracer bullet on gt-oiv0, all agents up","status":"closed","priority":4,"issue_type":"task","created_at":"2025-12-22T22:38:37.785872-08:00","updated_at":"2025-12-22T22:38:37.785872-08:00","closed_at":"2025-12-22T22:38:37.785832-08:00","close_reason":"Squashed from 5 wisps"}
{"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","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"}
{"id":"gt-ngpz","title":"mol-christmas-launch: 3-day execution plan","description":"\n\n---\n\n## Wisp Integration Wave Plan (added 2025-12-21)\n\nDependency-ordered execution for gt-3x0z (Wisp Molecules) + gt-rana (Patrol System):\n\n### Wave 1 (parallel - no blockers)\n- gt-3x0z.1: gt rig init creates .beads-ephemeral/\n- gt-3x0z.2: Configure bd for ephemeral molecule bonding\n- gt-3x0z.3: gt doctor checks for ephemeral health\n- gt-rana.1: Attachment field on pinned beads\n- gt-rana.3: mol-deacon-patrol definition\n\n### Wave 2 (after Wave 1)\n- gt-3x0z.4: gt spawn --molecule bonds ephemeral (GATE 1)\n- gt-rana.2: Daemon attachment detection\n\n### Wave 3-5 (sequential)\n- gt-3x0z.5, gt-3x0z.6 → gt-3x0z.7 → gt-3x0z.8 (GATE 2: squash)\n\n### Wave 6+ (patrol integration)\n- gt-rana.4: Basic patrol runner (needs gt-rana.3 + gt-3x0z.8)\n- gt-3x0z.9: mol-deacon-patrol uses wisp (needs gt-rana.3 + gt-3x0z.8)\n- Then: gt-rana.5-7, gt-3x0z.10-12\n\n### Key Gates\n1. gt-3x0z.4 - spawn/bond unlocks Phase 2\n2. gt-3x0z.8 - squash unlocks patrol integration\n3. gt-rana.4 - patrol runner unlocks Phase 2+ patrol\n","status":"open","priority":0,"issue_type":"epic","created_at":"2025-12-20T21:06:44.718065-08:00","updated_at":"2025-12-21T15:26:55.860072-08:00"}
{"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","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"}]}
@@ -597,7 +596,7 @@
{"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-tmm","title":"Polecat beads not synced with rig beads","description":"When gt spawn assigns an issue to a polecat, the polecat cannot find the issue because:\n\n1. Rig beads at /gt/gastown/.beads contains gt-th7\n2. Polecat beads at /gt/gastown/polecats/dementus/.beads does NOT have gt-th7\n3. bd sync in polecat dir pulls from beads-sync branch which doesn't have the new issue\n4. Rig bd sync uses 'main' branch, causing a branch mismatch\n\nThe polecat gets a work assignment for an issue it literally cannot see.\n\n## Root Cause\n\n- Rig beads and polecat beads are separate git-tracked copies\n- No mechanism to propagate newly created issues from rig to polecats before assignment\n- Sync branch configuration mismatch between rig and polecats\n\n## Fix Options\n\n1. gt spawn should sync rig beads before assigning work\n2. gt spawn should sync polecat beads after assignment\n3. Use shared beads (symlink or same DB) instead of copies\n4. Push new issues immediately on create","notes":"Note: This sync mismatch is resolved by gt-9nf (fresh polecats). Rather than fixing sync between stale clones, we'll always create fresh worktrees. This issue documents the root cause for posterity.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-20T15:17:10.158624-08:00","updated_at":"2025-12-21T10:12:57.282455-08:00","closed_at":"2025-12-21T10:12:57.282455-08:00","close_reason":"Fixed by gt-9nf: fresh polecats now use shared rig beads via redirect"}
{"id":"gt-tnca","title":"Design mol-ready-work patrol for crew workers","description":"## Summary\n\nDesign and implement a protomolecule (mol-ready-work) that enables crew workers to autonomously work through backlogs when the overseer is away. This replaces the normal \"await instructions\" patrol step with productive backlog processing.\n\n## Context\n\nCrew workers like dave/joe are persistent, user-managed agents. Currently their patrol loop has an \"await instructions\" step that idles. We want an alternative: sling mol-ready-work at them and they crank through backlogs until:\n- The overseer interrupts with new instructions\n- Context fills up (request handoff)\n- Backlogs are empty\n\n## Backlogs (Priority Order)\n\n1. **Open PRs** - Review/merge pending pull requests\n2. **Untriaged GH issues** - New issues needing triage\n3. **Open beads work** - bd ready items (unblocked issues)\n4. **Triaged GH issues** - Bugs/features to implement\n\n## Key Features\n\n### ROI-Based Selection\nInstead of strict priority, agent applies ROI heuristic:\n- Size estimate (fits in remaining context?)\n- Achievability (has all needed info?)\n- Impact (priority + type weight)\n- Pick highest-value achievable item\n\n### Context Management\n- Check token usage after each work item\n- Request handoff before running out\n- Leave clear handoff notes (mail to self)\n- Next session picks up the patrol\n\n### Patrol Loop Structure\n```\norient → scan-backlogs → select-work → execute-work → check-context → (loop or handoff)\n```\n\n## Design Questions\n\n1. How does gt sling work? Does it exist?\n2. How does this interact with the normal crew patrol?\n3. Should the molecule go in Gas Town catalog or per-rig?\n4. How does bonding work for discovered work during execution?\n5. Should this be a wisp (ephemeral) or mol (persistent)?\n\n## Acceptance Criteria\n\n- [ ] mol-ready-work protomolecule defined in catalog\n- [ ] gt sling command implemented (or existing mechanism documented)\n- [ ] Context-checking step works with agent token awareness\n- [ ] Handoff mechanism integrates with gt mail\n- [ ] Documentation for crew workers on using the patrol","status":"open","priority":2,"issue_type":"epic","created_at":"2025-12-22T23:45:40.63509-08:00","updated_at":"2025-12-22T23:45:40.63509-08:00"}
{"id":"gt-tnca.1","title":"Draft mol-ready-work protomolecule definition","description":"Draft the actual protomolecule definition in markdown format.\n\n## Molecule Structure\n\n```markdown\n## Molecule: ready-work\nAutonomous backlog processing patrol for crew workers.\n\nPhase: vapor (wisp) - ephemeral patrol cycles\nSquash: after each work item or context threshold\n\n## Step: orient\nLoad context and check for interrupts:\n- Read mail for overseer instructions\n- Check for predecessor handoff\n- Load current context state\n\n## Step: scan-backlogs\nSurvey all backlogs in priority order:\n1. gh pr list --state open\n2. gh issue list --state open --label untriaged (or no label)\n3. bd ready\n4. gh issue list --state open --label triaged\n\nCapture counts and candidates.\n\nNeeds: orient\n\n## Step: select-work\nApply ROI heuristic to select best work item:\n- Estimate size (tokens needed)\n- Check remaining context capacity\n- Weight by impact (priority, type)\n- Select highest ROI achievable item\n- If empty: exit patrol\n\nNeeds: scan-backlogs\n\n## Step: execute-work\nWork the selected item:\n- For PRs: review, request changes, or merge\n- For untriaged: triage and label\n- For beads: implement and close\n- For triaged GH: implement fix\n\nCommit, push, close/update as appropriate.\n\nNeeds: select-work\n\n## Step: check-context\nAssess context state:\n- Estimate remaining capacity\n- If \u003c 20%: goto handoff\n- If ok: loop to scan-backlogs\n\nNeeds: execute-work\n\n## Step: handoff\nPrepare for session transition:\n- Summarize work completed this cycle\n- Note any in-progress items\n- Send handoff mail to self\n- Squash wisp to digest\n- Exit for fresh session\n\nNeeds: check-context\n```\n\n## Variables\n\n- `backlog_priority`: Override backlog scan order\n- `context_threshold`: Percentage at which to handoff (default: 20)\n- `max_items`: Maximum items to process per session\n\n## Notes\n\n- This is a vapor-phase molecule (wisp)\n- Each work item should squash to a digest\n- The patrol itself squashes at handoff","status":"in_progress","priority":2,"issue_type":"task","assignee":"gastown/immortan","created_at":"2025-12-22T23:46:00.012418-08:00","updated_at":"2025-12-23T00:11:33.351627-08:00","dependencies":[{"issue_id":"gt-tnca.1","depends_on_id":"gt-tnca","type":"parent-child","created_at":"2025-12-22T23:46:00.012887-08:00","created_by":"daemon"}]}
{"id":"gt-tnca.1","title":"Draft mol-ready-work protomolecule definition","description":"Draft the actual protomolecule definition in markdown format.\n\n## Molecule Structure\n\n```markdown\n## Molecule: ready-work\nAutonomous backlog processing patrol for crew workers.\n\nPhase: vapor (wisp) - ephemeral patrol cycles\nSquash: after each work item or context threshold\n\n## Step: orient\nLoad context and check for interrupts:\n- Read mail for overseer instructions\n- Check for predecessor handoff\n- Load current context state\n\n## Step: scan-backlogs\nSurvey all backlogs in priority order:\n1. gh pr list --state open\n2. gh issue list --state open --label untriaged (or no label)\n3. bd ready\n4. gh issue list --state open --label triaged\n\nCapture counts and candidates.\n\nNeeds: orient\n\n## Step: select-work\nApply ROI heuristic to select best work item:\n- Estimate size (tokens needed)\n- Check remaining context capacity\n- Weight by impact (priority, type)\n- Select highest ROI achievable item\n- If empty: exit patrol\n\nNeeds: scan-backlogs\n\n## Step: execute-work\nWork the selected item:\n- For PRs: review, request changes, or merge\n- For untriaged: triage and label\n- For beads: implement and close\n- For triaged GH: implement fix\n\nCommit, push, close/update as appropriate.\n\nNeeds: select-work\n\n## Step: check-context\nAssess context state:\n- Estimate remaining capacity\n- If \u003c 20%: goto handoff\n- If ok: loop to scan-backlogs\n\nNeeds: execute-work\n\n## Step: handoff\nPrepare for session transition:\n- Summarize work completed this cycle\n- Note any in-progress items\n- Send handoff mail to self\n- Squash wisp to digest\n- Exit for fresh session\n\nNeeds: check-context\n```\n\n## Variables\n\n- `backlog_priority`: Override backlog scan order\n- `context_threshold`: Percentage at which to handoff (default: 20)\n- `max_items`: Maximum items to process per session\n\n## Notes\n\n- This is a vapor-phase molecule (wisp)\n- Each work item should squash to a digest\n- The patrol itself squashes at handoff","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-22T23:46:00.012418-08:00","updated_at":"2025-12-22T23:46:00.012418-08:00","dependencies":[{"issue_id":"gt-tnca.1","depends_on_id":"gt-tnca","type":"parent-child","created_at":"2025-12-22T23:46:00.012887-08:00","created_by":"daemon"}]}
{"id":"gt-tnca.2","title":"Implement context-checking for agent sessions","description":"Implement the context-checking step that allows agents to assess their remaining context capacity.\n\n## Challenge\n\nAgents don't have direct access to token counts. Need heuristics:\n\n### Approach 1: Message count heuristic\n- Count messages in conversation\n- Estimate tokens per message\n- Compare to model's context window\n\n### Approach 2: External tool\n- Claude Code could expose a /context command\n- Returns estimated usage percentage\n- Agent queries before each work item\n\n### Approach 3: Conservative fixed limits\n- After N work items, always handoff\n- Simple but may waste context or handoff too early\n\n### Approach 4: Hook-based injection\n- SessionStart hook injects context estimate\n- Updated periodically via tool call\n\n## Recommendation\n\nStart with Approach 3 (fixed limits) as MVP:\n- Default: 3-5 work items per session\n- Agent can override via variable\n- Upgrade to smarter heuristics later\n\n## Integration\n\n- Add check-context step to mol-ready-work\n- Implement handoff trigger logic\n- Test with various context states","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-22T23:46:28.414584-08:00","updated_at":"2025-12-22T23:46:28.414584-08:00","dependencies":[{"issue_id":"gt-tnca.2","depends_on_id":"gt-tnca","type":"parent-child","created_at":"2025-12-22T23:46:28.414964-08:00","created_by":"daemon"}]}
{"id":"gt-tnca.3","title":"Implement/document gt sling mechanism","description":"Investigate and implement the gt sling command for attaching molecules to crew workers. Check if gt sling exists, design the flow (mail-based vs direct pin vs wisp spawn), implement in gastown CLI, integrate with bd mol/wisp/pin commands.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-22T23:46:38.592538-08:00","updated_at":"2025-12-22T23:46:38.592538-08:00","dependencies":[{"issue_id":"gt-tnca.3","depends_on_id":"gt-tnca","type":"parent-child","created_at":"2025-12-22T23:46:38.592961-08:00","created_by":"daemon"}]}
{"id":"gt-tnca.4","title":"Document crew worker patrol integration","description":"Document how mol-ready-work integrates with crew worker workflow. Cover: starting the patrol, patrol vs normal operation, backlog configuration, handoff mechanics, discovered work handling. Update CLAUDE.md with patrol section.","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-22T23:46:38.867975-08:00","updated_at":"2025-12-22T23:46:38.867975-08:00","dependencies":[{"issue_id":"gt-tnca.4","depends_on_id":"gt-tnca","type":"parent-child","created_at":"2025-12-22T23:46:38.868332-08:00","created_by":"daemon"}]}
@@ -635,15 +634,16 @@
{"id":"gt-u818","title":"Witness Plugin System","description":"Plugin infrastructure for witness patrol customization.\n\n## Plugin Location\n`\u003crig\u003e/plugins/witness/\u003cplugin-name\u003e/`\n\n## Plugin Structure\n```\nplugin.yaml # Config: name, description, trigger, tier\nprompt.md # The prompt to run\n```\n\n## plugin.yaml Schema\n```yaml\nname: plugin-name\ndescription: What it does\ntrigger: patrol # When to run (patrol, on-nudge, on-shutdown)\ntier: haiku # Model tier (haiku, sonnet, opus)\ninput: polecat-list # What context to gather (polecat-list, session-capture, etc)\n```\n\n## Execution\n1. Witness reads plugins from directory\n2. For each plugin matching current trigger:\n - Gather specified input context\n - Run prompt.md against specified tier\n - Parse output for gt commands\n - Execute commands\n\n## CLI\n- `gt plugin list \u003crig\u003e` - List installed plugins\n- `gt plugin run \u003crig\u003e \u003cplugin\u003e` - Manual run","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-21T16:16:53.886931-08:00","updated_at":"2025-12-21T16:16:53.886931-08:00"}
{"id":"gt-u82","title":"Design: Mayor session cycling and handoff","description":"Design for Mayor session cycling and structured handoff.\n\n## Overview\n\nMayor coordinates across all rigs and runs for extended periods. Needs session cycling pattern with structured handoff notes.\n\n## Key Elements\n\n1. Session cycling recognition (when to cycle)\n2. Handoff note format (structured state capture)\n3. Handoff delivery (mail to self)\n4. Fresh session startup (reading and resuming)\n\n## Subtasks (implementation)\n\n- gt-g2d: Mayor session cycling prompting\n- gt-sye: Mayor startup protocol prompting\n- gt-vci: Mayor handoff mail template\n- gt-1le: town handoff command (optional, P2)\n\n**Design complete.** Each subtask has full specification in its description.","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-12-15T20:03:16.125725-08:00","updated_at":"2025-12-15T20:49:26.203276-08:00","closed_at":"2025-12-15T20:16:10.772149-08:00"}
{"id":"gt-ua5f","title":"Digest: mol-deacon-patrol","description":"Patrol: read mayor handoff (gt-mzal boot design), all agents up, furiosa working gt-oiv0, note: gt-4eim orphaned (angharad gone)","status":"closed","priority":4,"issue_type":"task","created_at":"2025-12-22T21:29:07.064593-08:00","updated_at":"2025-12-22T21:29:07.064593-08:00","closed_at":"2025-12-22T21:29:07.064557-08:00","close_reason":"Squashed from 5 wisps"}
{"id":"gt-uaoy","title":"mol-deacon-patrol","description":"[RESURRECTED] This issue was deleted but recreated as a tombstone to preserve hierarchical structure.\n\nOriginal description:\nDeacon patrol molecule template. Label: template","status":"open","priority":4,"issue_type":"epic","created_at":"2025-12-22T23:47:41.477047-08:00","updated_at":"2025-12-22T23:47:41.477047-08:00","wisp":true}
{"id":"gt-uhc3","title":"gt mq list shows empty when MRs exist","description":"The gt mq list command returns empty results even when merge requests exist in the queue.","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-21T17:51:18.712633-08:00","updated_at":"2025-12-21T17:51:18.712633-08:00"}
{"id":"gt-um6q","title":"Update docs with molecule navigation workflow","description":"Update architecture and workflow docs with new molecule navigation.\n\n## Docs to update\n\n### docs/molecules.md\n- Add 'Navigating Molecules' section\n- Document bd mol current usage\n- Document bd close --continue workflow\n- Show the propulsion pattern\n\n### docs/propulsion-principle.md\n- Add molecule navigation as key enabler\n- Show before/after workflow comparison\n\n### docs/polecat-wisp-architecture.md\n- Update step execution section\n- Show bd close --continue in examples\n\n## New section content\n\n### Molecule Navigation\n\nFinding your place:\n bd mol current # Where am I?\n bd mol current gt-abc # Status of specific molecule\n\nSeamless transitions:\n bd close gt-abc.3 --continue # Close and advance\n bd close gt-abc.3 --no-auto # Close but don't auto-claim next\n\nThe old way (3 commands):\n bd close gt-abc.3\n bd ready --parent=gt-abc\n bd update gt-abc.4 --status=in_progress\n\nThe new way (1 command):\n bd close gt-abc.3 --continue\n\n## Blocked by (Beads features)\n- bd-sal9: bd mol current\n- bd-ieyy: bd close --continue","status":"in_progress","priority":1,"issue_type":"task","assignee":"gastown/valkyrie","created_at":"2025-12-22T17:01:24.849951-08:00","updated_at":"2025-12-23T00:12:16.1059-08:00","dependencies":[{"issue_id":"gt-um6q","depends_on_id":"gt-qswb","type":"blocks","created_at":"2025-12-22T17:01:31.856627-08:00","created_by":"daemon"},{"issue_id":"gt-um6q","depends_on_id":"gt-fly0","type":"blocks","created_at":"2025-12-22T17:01:31.930072-08:00","created_by":"daemon"}]}
{"id":"gt-um6q","title":"Update docs with molecule navigation workflow","description":"Update architecture and workflow docs with new molecule navigation.\n\n## Docs to update\n\n### docs/molecules.md\n- Add 'Navigating Molecules' section\n- Document bd mol current usage\n- Document bd close --continue workflow\n- Show the propulsion pattern\n\n### docs/propulsion-principle.md\n- Add molecule navigation as key enabler\n- Show before/after workflow comparison\n\n### docs/polecat-wisp-architecture.md\n- Update step execution section\n- Show bd close --continue in examples\n\n## New section content\n\n### Molecule Navigation\n\nFinding your place:\n bd mol current # Where am I?\n bd mol current gt-abc # Status of specific molecule\n\nSeamless transitions:\n bd close gt-abc.3 --continue # Close and advance\n bd close gt-abc.3 --no-auto # Close but don't auto-claim next\n\nThe old way (3 commands):\n bd close gt-abc.3\n bd ready --parent=gt-abc\n bd update gt-abc.4 --status=in_progress\n\nThe new way (1 command):\n bd close gt-abc.3 --continue\n\n## Blocked by (Beads features)\n- bd-sal9: bd mol current\n- bd-ieyy: bd close --continue","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-22T17:01:24.849951-08:00","updated_at":"2025-12-22T17:04:43.037795-08:00","dependencies":[{"issue_id":"gt-um6q","depends_on_id":"gt-qswb","type":"blocks","created_at":"2025-12-22T17:01:31.856627-08:00","created_by":"daemon"},{"issue_id":"gt-um6q","depends_on_id":"gt-fly0","type":"blocks","created_at":"2025-12-22T17:01:31.930072-08:00","created_by":"daemon"}]}
{"id":"gt-unev","title":"Clean up ~/gt root directory cruft","description":"The town root has accumulated various directories and files that need review:\n\n**To evaluate:**\n- daemon/ - old daemon code? move or delete?\n- deacon/ - WIP deacon work? consolidate with gastown?\n- mayor/ - old mayor structure? \n- AGENTS.md - is this still used?\n- gt binary at root - should be gitignored\n\n**Already correct:**\n- .beads/, .claude/, .gastown/, .runtime/ - runtime dirs\n- beads/, gastown/ - rig directories \n- docs/hop/ - strategic docs\n- CLAUDE.md - mayor context\n\nClean up, consolidate, update .gitignore as needed.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-22T22:10:09.722561-08:00","updated_at":"2025-12-22T22:10:09.722561-08:00"}
{"id":"gt-unrd","title":"Fix gt prime to give crew workers crew context","description":"gt prime currently gives Mayor context to all agents. Crew workers should get crew-specific context. Also extract shared theory of operation from mayor priming into shared context for all roles.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-19T15:37:49.671015-08:00","updated_at":"2025-12-19T17:22:52.551704-08:00","closed_at":"2025-12-19T15:41:38.806903-08:00"}
{"id":"gt-uohw","title":"gt doctor: detect tmux session anomalies (linked panes, etc.)","description":"Add a tmux health check to gt doctor that detects:\n\n1. **Linked panes between sessions** - The bug where gt-deacon and gt-mayor shared pane @283, causing heartbeat crosstalk\n2. **Session naming anomalies** - Sessions that don't match expected patterns\n3. **Orphaned panes** - Panes in gt-* sessions with no running process\n\nDetection approach:\n- List all gt-* sessions\n- For each session, get window/pane IDs\n- Check for duplicate pane IDs across different sessions\n- If found, report which sessions are linked and suggest fix\n\nAuto-fix: Could offer to kill and recreate the offending session.\n\nReference: gt-rt6g documented a case where this caused the daemon's Deacon heartbeats to appear in the Mayor's input, eating user prompts.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-22T17:13:04.147197-08:00","updated_at":"2025-12-22T17:16:15.645263-08:00","closed_at":"2025-12-22T17:16:15.645263-08:00","close_reason":"Closed"}
{"id":"gt-upom","title":"Witness patrol: cleanup idle orphan polecats","description":"Add patrol step to find and cleanup polecats that are idle with no assigned issue. These orphans occur when polecats crash before sending DONE or Witness misses the message. Patrol should verify git is clean before removing worktree. Part of gt-rana.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-21T23:09:41.756753-08:00","updated_at":"2025-12-21T23:09:41.756753-08:00"}
{"id":"gt-us8","title":"Daemon: configurable heartbeat interval","description":"Heartbeat interval is hardcoded to 60s. Should be configurable via:\n- town.json config\n- Command line flag\n- Environment variable\n\nDefault 60s is reasonable but some deployments may want faster/slower.","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-18T13:38:14.282216-08:00","updated_at":"2025-12-18T13:38:14.282216-08:00","dependencies":[{"issue_id":"gt-us8","depends_on_id":"gt-99m","type":"blocks","created_at":"2025-12-18T13:38:26.704111-08:00","created_by":"daemon"}]}
{"id":"gt-usy0","title":"Merge: gt-3x0z.3","description":"branch: polecat/rictus\ntarget: main\nsource_issue: gt-3x0z.3\nrig: gastown","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-21T16:03:43.535266-08:00","updated_at":"2025-12-21T17:20:27.505696-08:00","closed_at":"2025-12-21T17:20:27.505696-08:00","close_reason":"ORPHANED: Branch never pushed, worktree deleted"}
{"id":"gt-utwc","title":"Self-mail should suppress tmux notification","description":"When sending mail to yourself (e.g., mayor sending to mayor/), the tmux notification shouldn't fire.\n\n**Rationale:**\n- Self-mail is intended for future-you (next session handoff)\n- Present-you just sent it, so you already know about it\n- The notification is redundant/confusing in this case\n\n**Fix:**\nSuppress tmux notification when sender == recipient address.","status":"closed","priority":3,"issue_type":"bug","created_at":"2025-12-22T17:55:39.573705-08:00","updated_at":"2025-12-22T23:58:02.827026-08:00","closed_at":"2025-12-22T23:58:02.827026-08:00","close_reason":"Skip tmux notification when sender == recipient"}
{"id":"gt-utwc","title":"Self-mail should suppress tmux notification","description":"When sending mail to yourself (e.g., mayor sending to mayor/), the tmux notification shouldn't fire.\n\n**Rationale:**\n- Self-mail is intended for future-you (next session handoff)\n- Present-you just sent it, so you already know about it\n- The notification is redundant/confusing in this case\n\n**Fix:**\nSuppress tmux notification when sender == recipient address.","status":"open","priority":3,"issue_type":"bug","created_at":"2025-12-22T17:55:39.573705-08:00","updated_at":"2025-12-22T17:55:39.573705-08:00"}
{"id":"gt-uym5","title":"Implement gt mol status command","description":"Show what's on an agent's hook.\n\n```bash\ngt mol status [target]\n```\n\nOutput:\n- What's slung (molecule name, associated issue)\n- Current phase and progress\n- Whether it's a wisp\n- Next action hint\n\nIf no target, shows current agent's status.\n\nAcceptance:\n- [ ] Read pinned bead attachment\n- [ ] Display molecule/issue info\n- [ ] Show phase progress\n- [ ] Indicate wisp vs durable","status":"closed","priority":1,"issue_type":"task","assignee":"gastown/nux","created_at":"2025-12-22T03:17:34.679963-08:00","updated_at":"2025-12-22T12:34:19.942265-08:00","closed_at":"2025-12-22T12:34:19.942265-08:00","close_reason":"Implemented gt mol status command with mol alias, auto-detection, progress display, wisp detection, and next action hints"}
{"id":"gt-v5hv","title":"Work on ga-y6b: Implement Refinery as Claude agent. Conve...","description":"Work on ga-y6b: Implement Refinery as Claude agent. Convert from shell to Claude agent that processes MRs in merge queue, runs tests, merges to integration branch. When done, submit MR (not PR) to integration branch for Refinery.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T22:58:17.576892-08:00","updated_at":"2025-12-19T23:23:22.778407-08:00","closed_at":"2025-12-19T23:23:22.778407-08:00"}
{"id":"gt-v5k","title":"Design: Failure modes and recovery","description":"Document failure modes and recovery strategies for Gas Town operations.\n\n## Critical Failure Modes\n\n### 1. Agent Crash Mid-Operation\n\n**Scenario**: Polecat crashes while committing, Witness crashes while verifying\n\n**Detection**:\n- Session suddenly gone (tmux check fails)\n- State shows 'working' but no session\n- Heartbeat stops (for Witness)\n\n**Recovery**:\n- Doctor detects via ZombieSessionCheck\n- Capture any recoverable state\n- Reset agent state to 'idle'\n- For Witness: auto-restart via supervisor or manual gt witness start\n\n### 2. Git State Corruption\n\n**Scenario**: Merge conflict, failed rebase, detached HEAD\n\n**Detection**:\n- Git commands fail\n- Dirty state that won't commit\n- Branch diverged from origin\n\n**Recovery**:\n- gt doctor reports git health issues\n- Manual intervention recommended\n- Severe cases: remove clone, re-clone\n\n### 3. Beads Sync Conflict\n\n**Scenario**: Two polecats modify same issue\n\n**Detection**:\n- bd sync fails with conflict\n- Beads tombstone mechanism handles most cases\n\n**Recovery**:\n- Beads has last-write-wins semantics\n- bd sync --force in extreme cases\n- Issues may need manual dedup\n\n### 4. Tmux Failure\n\n**Scenario**: Tmux server crashes, socket issues\n\n**Detection**:\n- All sessions inaccessible\n- \"no server running\" errors\n\n**Recovery**:\n- Kill any orphan processes\n- tmux kill-server \u0026\u0026 tmux start-server\n- All agent states reset to idle\n- Re-spawn active work\n\n### 5. Claude API Issues\n\n**Scenario**: Rate limits, outages, context limits\n\n**Detection**:\n- Sessions hang or produce errors\n- Repeated failure patterns\n\n**Recovery**:\n- Exponential backoff (handled by Claude Code)\n- For context limits: session cycling (mail-to-self)\n- For outages: wait and retry\n\n### 6. Disk Full\n\n**Scenario**: Clones, logs, or beads fill disk\n\n**Detection**:\n- Write operations fail\n- git/bd commands error\n\n**Recovery**:\n- Clean up logs: rm ~/.gastown/logs/*\n- Remove old polecat clones\n- gt doctor --fix can clean some cruft\n\n### 7. Network Failure\n\n**Scenario**: Can't reach GitHub, API servers\n\n**Detection**:\n- git fetch/push fails\n- Claude sessions hang\n\n**Recovery**:\n- Work continues locally\n- Queue pushes for later\n- Sync when connectivity restored\n\n## Recovery Principles\n\n1. **Fail safe**: Prefer stopping over corrupting\n2. **State is recoverable**: Git and beads have recovery mechanisms\n3. **Doctor heals**: gt doctor --fix handles common issues\n4. **Emergency stop**: gt stop --all as last resort\n5. **Human escalation**: Some failures need Overseer intervention\n\n## Implementation\n\n- Document each failure mode in architecture.md\n- Ensure doctor checks cover detection\n- Add recovery hints to error messages\n- Log all failures for debugging","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-15T23:19:07.198289-08:00","updated_at":"2025-12-15T23:19:28.171942-08:00"}

View File

@@ -27,8 +27,9 @@ var (
// Manager handles witness lifecycle and monitoring operations.
type Manager struct {
rig *rig.Rig
workDir string
rig *rig.Rig
workDir string
handoffState *WitnessHandoffState // Cached handoff state for persistence across burns
}
// NewManager creates a new witness manager for a rig.
@@ -80,6 +81,166 @@ func (m *Manager) saveState(w *Witness) error {
return os.WriteFile(m.stateFile(), data, 0644)
}
// handoffBeadID returns the well-known ID for this rig's witness handoff bead.
func (m *Manager) handoffBeadID() string {
return fmt.Sprintf("gt-%s-%s", m.rig.Name, HandoffBeadID)
}
// loadHandoffState loads worker states from the handoff bead.
// If the bead doesn't exist, returns an empty state and creates the bead.
func (m *Manager) loadHandoffState() (*WitnessHandoffState, error) {
beadID := m.handoffBeadID()
// Try to read the bead
cmd := exec.Command("bd", "show", beadID, "--json")
cmd.Dir = m.workDir
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
// Bead doesn't exist - create it
if strings.Contains(stderr.String(), "not found") || strings.Contains(stderr.String(), "No issue") {
if err := m.ensureHandoffBead(); err != nil {
return nil, fmt.Errorf("creating handoff bead: %w", err)
}
return &WitnessHandoffState{
WorkerStates: make(map[string]WorkerState),
}, nil
}
return nil, fmt.Errorf("reading handoff bead: %s", stderr.String())
}
// Parse the bead JSON
var issues []struct {
Description string `json:"description"`
}
if err := json.Unmarshal(stdout.Bytes(), &issues); err != nil {
return nil, fmt.Errorf("parsing handoff bead: %w", err)
}
if len(issues) == 0 {
return &WitnessHandoffState{
WorkerStates: make(map[string]WorkerState),
}, nil
}
// The description contains our JSON state
desc := issues[0].Description
// Extract JSON from description (skip any markdown header)
state := &WitnessHandoffState{
WorkerStates: make(map[string]WorkerState),
}
// Try to find JSON in the description
if idx := strings.Index(desc, "{"); idx >= 0 {
jsonPart := desc[idx:]
// Find the matching closing brace
if endIdx := findMatchingBrace(jsonPart); endIdx > 0 {
jsonPart = jsonPart[:endIdx+1]
if err := json.Unmarshal([]byte(jsonPart), state); err != nil {
// If parsing fails, just return empty state
return &WitnessHandoffState{
WorkerStates: make(map[string]WorkerState),
}, nil
}
}
}
return state, nil
}
// findMatchingBrace finds the index of the matching closing brace.
func findMatchingBrace(s string) int {
depth := 0
inString := false
escaped := false
for i, c := range s {
if escaped {
escaped = false
continue
}
if c == '\\' && inString {
escaped = true
continue
}
if c == '"' {
inString = !inString
continue
}
if inString {
continue
}
if c == '{' {
depth++
} else if c == '}' {
depth--
if depth == 0 {
return i
}
}
}
return -1
}
// saveHandoffState persists worker states to the handoff bead.
func (m *Manager) saveHandoffState(state *WitnessHandoffState) error {
beadID := m.handoffBeadID()
// Serialize state to JSON
stateJSON, err := json.MarshalIndent(state, "", " ")
if err != nil {
return fmt.Errorf("serializing state: %w", err)
}
// Update the bead's description with the JSON state
desc := fmt.Sprintf("Witness handoff state for %s.\n\n```json\n%s\n```", m.rig.Name, string(stateJSON))
cmd := exec.Command("bd", "update", beadID, "--description", desc)
cmd.Dir = m.workDir
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("updating handoff bead: %s", strings.TrimSpace(string(out)))
}
return nil
}
// ensureHandoffBead creates the handoff bead if it doesn't exist.
func (m *Manager) ensureHandoffBead() error {
beadID := m.handoffBeadID()
title := fmt.Sprintf("Witness handoff state (%s)", m.rig.Name)
desc := fmt.Sprintf("Witness handoff state for %s.\n\n```json\n{\"worker_states\": {}, \"last_patrol\": null}\n```", m.rig.Name)
// Create pinned handoff bead with specific ID
cmd := exec.Command("bd", "create",
"--id", beadID,
"--title", title,
"--type", "task",
"--priority", "4", // Low priority - just state storage
"--description", desc,
)
cmd.Dir = m.workDir
if out, err := cmd.CombinedOutput(); err != nil {
// If it already exists, that's fine
if strings.Contains(string(out), "already exists") {
return nil
}
return fmt.Errorf("creating handoff bead: %s", strings.TrimSpace(string(out)))
}
// Pin the bead so it survives cleanup
cmd = exec.Command("bd", "update", beadID, "--pinned")
cmd.Dir = m.workDir
_ = cmd.Run() // Best effort - pinning might not be supported
return nil
}
// Status returns the current witness status.
func (m *Manager) Status() (*Witness, error) {
w, err := m.loadState()
@@ -165,6 +326,17 @@ func (m *Manager) run(w *Witness) error {
fmt.Println("Witness running...")
fmt.Println("Press Ctrl+C to stop")
// Load handoff state from persistent bead (survives wisp burns)
handoffState, err := m.loadHandoffState()
if err != nil {
fmt.Printf("Warning: could not load handoff state: %v\n", err)
handoffState = &WitnessHandoffState{
WorkerStates: make(map[string]WorkerState),
}
}
m.handoffState = handoffState
fmt.Printf("Loaded handoff state with %d worker(s)\n", len(m.handoffState.WorkerStates))
// Initial check immediately
m.checkAndProcess(w)
@@ -195,6 +367,13 @@ func (m *Manager) checkAndProcess(w *Witness) {
fmt.Printf("Auto-spawn error: %v\n", err)
}
}
// Update last patrol time and persist handoff state
if m.handoffState != nil {
now := time.Now()
m.handoffState.LastPatrol = &now
// Note: individual nudge/activity updates already persist, so this is just for LastPatrol
}
}
// healthCheck performs a health check on all monitored polecats.
@@ -225,6 +404,9 @@ func (m *Manager) healthCheck(w *Witness) error {
status := m.checkPolecatHealth(p.Name, p.ClonePath)
if status == PolecatStuck {
m.handleStuckPolecat(w, p.Name)
} else if status == PolecatHealthy {
// Worker is active - update activity tracking and clear nudge count
m.updateWorkerActivity(p.Name, "")
}
}
}
@@ -331,9 +513,16 @@ func (m *Manager) handleStuckPolecat(w *Witness, polecatName string) {
}
// getNudgeCount returns how many times a polecat has been nudged.
// Uses handoff state for persistence across wisp burns.
func (m *Manager) getNudgeCount(w *Witness, polecatName string) int {
// Count occurrences in SpawnedIssues that start with "nudge:" prefix
// We reuse SpawnedIssues to track nudges with a "nudge:<name>" pattern
// First check handoff state (persistent across burns)
if m.handoffState != nil {
if ws, ok := m.handoffState.WorkerStates[polecatName]; ok {
return ws.NudgeCount
}
}
// Fallback to legacy SpawnedIssues for backwards compatibility
count := 0
nudgeKey := "nudge:" + polecatName
for _, entry := range w.SpawnedIssues {
@@ -345,11 +534,70 @@ func (m *Manager) getNudgeCount(w *Witness, polecatName string) int {
}
// recordNudge records that a nudge was sent to a polecat.
// Updates both handoff state (persistent) and legacy SpawnedIssues.
func (m *Manager) recordNudge(w *Witness, polecatName string) {
now := time.Now()
// Update handoff state (persistent across burns)
if m.handoffState != nil {
if m.handoffState.WorkerStates == nil {
m.handoffState.WorkerStates = make(map[string]WorkerState)
}
ws := m.handoffState.WorkerStates[polecatName]
ws.NudgeCount++
ws.LastNudge = &now
m.handoffState.WorkerStates[polecatName] = ws
// Persist to handoff bead
if err := m.saveHandoffState(m.handoffState); err != nil {
fmt.Printf("Warning: failed to persist handoff state: %v\n", err)
}
}
// Also update legacy SpawnedIssues for backwards compatibility
nudgeKey := "nudge:" + polecatName
w.SpawnedIssues = append(w.SpawnedIssues, nudgeKey)
}
// clearNudgeCount clears the nudge count for a polecat (e.g., when they become active again).
func (m *Manager) clearNudgeCount(polecatName string) {
if m.handoffState != nil && m.handoffState.WorkerStates != nil {
if ws, ok := m.handoffState.WorkerStates[polecatName]; ok {
ws.NudgeCount = 0
ws.LastNudge = nil
now := time.Now()
ws.LastActive = &now
m.handoffState.WorkerStates[polecatName] = ws
// Persist to handoff bead
if err := m.saveHandoffState(m.handoffState); err != nil {
fmt.Printf("Warning: failed to persist handoff state: %v\n", err)
}
}
}
}
// updateWorkerActivity updates the last active time for a worker.
func (m *Manager) updateWorkerActivity(polecatName, issueID string) {
if m.handoffState != nil {
if m.handoffState.WorkerStates == nil {
m.handoffState.WorkerStates = make(map[string]WorkerState)
}
ws := m.handoffState.WorkerStates[polecatName]
now := time.Now()
ws.LastActive = &now
if issueID != "" {
ws.Issue = issueID
}
// Reset nudge count if worker is active
if ws.NudgeCount > 0 {
ws.NudgeCount = 0
ws.LastNudge = nil
}
m.handoffState.WorkerStates[polecatName] = ws
}
}
// escalateToMayor sends an escalation message to the Mayor.
func (m *Manager) escalateToMayor(polecatName string) error {
subject := fmt.Sprintf("ESCALATION: Polecat %s stuck", polecatName)

View File

@@ -84,3 +84,32 @@ type WitnessStats struct {
// TodayNudges is the number of nudges today.
TodayNudges int `json:"today_nudges"`
}
// WorkerState tracks the state of a single worker (polecat) across wisp burns.
type WorkerState struct {
// Issue is the current issue the worker is assigned to.
Issue string `json:"issue,omitempty"`
// NudgeCount is how many times this worker has been nudged.
NudgeCount int `json:"nudge_count"`
// LastNudge is when the worker was last nudged.
LastNudge *time.Time `json:"last_nudge,omitempty"`
// LastActive is when the worker was last seen active.
LastActive *time.Time `json:"last_active,omitempty"`
}
// WitnessHandoffState tracks all worker states across wisp burns.
// This is persisted in a pinned handoff bead that survives wisp burns.
type WitnessHandoffState struct {
// WorkerStates maps polecat names to their state.
WorkerStates map[string]WorkerState `json:"worker_states"`
// LastPatrol is when the last patrol cycle completed.
LastPatrol *time.Time `json:"last_patrol,omitempty"`
}
// HandoffBeadID is the well-known ID suffix for the witness handoff bead.
// The full ID is constructed as "<rig>-witness-state" (e.g., "gastown-witness-state").
const HandoffBeadID = "witness-state"