diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index a9ecc9ad..9d6d067b 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -1665,8 +1665,8 @@ {"id":"gt-wrw2","title":"Test2","description":"Testing gt mail","status":"tombstone","priority":2,"issue_type":"message","created_at":"2025-12-20T21:39:05.875792-08:00","updated_at":"2025-12-25T14:12:42.26153-08:00","deleted_at":"2025-12-25T14:12:42.26153-08:00","deleted_by":"daemon","delete_reason":"delete","original_type":"message"} {"id":"gt-ws8ol","title":"Design: Deacon exponential backoff when town is idle","description":"## Problem\n\nWhen the user goes to sleep and all workers have stopped, the Deacon continues\npolling at full speed (every daemon heartbeat). This wastes resources and creates\nunnecessary log noise.\n\n## Requirements\n\n1. **Gradual slowdown**: Deacon patrol frequency should exponentially back off\n when there's no activity in the town.\n\n2. **Wake on activity**: Any gt or bd command (human or agent) should reset\n the backoff immediately.\n\n3. **Daemon coordination**: Deacon needs to \"program\" the daemon for next\n heartbeat duration.\n\n## Design Considerations\n\n### Option A: Daemon tracks last-activity timestamp\n- Daemon mechanically slows heartbeat based on time since last activity\n- Activity signal comes from gt/bd command hooks\n- Simple, no Deacon state needed\n- But: Daemon is \"dumb\" by design - adding intelligence here feels wrong\n\n### Option B: Deacon tracks patrol-count-since-activity\n- Deacon maintains counter (via label on patrol molecule or state.json)\n- Each idle patrol increments counter, calculates next sleep\n- Deacon tells daemon \"wake me in X minutes\"\n- More complex but keeps intelligence in AI agent\n\n### Option C: Discovery-based (preferred if feasible)\n- Can we detect \"no activity\" without explicit counters?\n- Ideas:\n - Compare current time to last git commit across all clones?\n - Check tmux activity timestamps?\n - Look at beads modification times?\n- Pros: No state to manage\n- Cons: May be expensive to compute, could miss activity\n\n### Wake mechanism\n- Hook on gt commands (gt.hooks.pre-command or similar)\n- Hook on bd commands (bd.hooks.pre-command)\n- Both should signal daemon (SIGUSR2?) or write to activity file\n- Daemon reads activity file on each heartbeat, resets backoff if fresh\n\n### Backoff schedule (strawman)\n| Idle patrols | Next heartbeat |\n|--------------|----------------|\n| 0-5 | 5 min (default) |\n| 6-10 | 10 min |\n| 11-20 | 30 min |\n| 21+ | 60 min (max) |\n\n## Open Questions\n\n1. Should tmux activity count as \"activity\"? (cursor movement, typing)\n2. How does Deacon communicate next-heartbeat to daemon?\n3. Where to store backoff state? (daemon state.json? deacon state.json? label?)\n4. Should wake be instant (SIGUSR) or next-heartbeat?\n\n## Implementation Sketch\n\nActivity file written by hooks at ~/gt/daemon/activity.json:\n- last_command: timestamp\n- command: the gt/bd command that ran\n- actor: who ran it","status":"pinned","priority":2,"issue_type":"epic","assignee":"gastown/crew/max","created_at":"2025-12-26T19:09:17.544704-08:00","updated_at":"2025-12-26T19:21:42.440497-08:00"} {"id":"gt-ws8ol.1","title":"gt: Write town-level activity signal in PersistentPreRun","description":"Extend gt's existing keepalive infrastructure to also write a town-level activity file.\n\nCurrently gt writes to \u003cworkspace\u003e/.runtime/keepalive.json (per-rig).\nAdd a second write to ~/gt/daemon/activity.json (town-level).\n\nThe keepalive package already has TouchInWorkspace(). Add a new function\nTouchTownActivity() that writes to the town daemon directory.\n\nCall both in root.go's PersistentPreRun.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-26T19:21:54.160207-08:00","updated_at":"2025-12-26T19:21:54.160207-08:00","dependencies":[{"issue_id":"gt-ws8ol.1","depends_on_id":"gt-ws8ol","type":"parent-child","created_at":"2025-12-26T19:21:54.160763-08:00","created_by":"daemon"}]} -{"id":"gt-ws8ol.2","title":"bd: Add town-level activity signal in PersistentPreRun","description":"Add activity signaling to beads so daemon can detect bd usage.\n\nIn cmd/bd/main.go PersistentPreRun, add a call to write activity to\nthe Gas Town daemon directory if running inside a Gas Town workspace.\n\nThe signal file is ~/gt/daemon/activity.json (or detected town root).\n\nFormat:\n{\n \"last_command\": \"bd create ...\",\n \"actor\": \"gastown/crew/max\",\n \"timestamp\": \"2025-12-26T19:30:00Z\"\n}\n\nShould be best-effort (silent failure) to avoid breaking bd outside Gas Town.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-26T19:22:04.033458-08:00","updated_at":"2025-12-26T19:22:04.033458-08:00","dependencies":[{"issue_id":"gt-ws8ol.2","depends_on_id":"gt-ws8ol","type":"parent-child","created_at":"2025-12-26T19:22:04.03401-08:00","created_by":"daemon"}]} -{"id":"gt-ws8ol.3","title":"Daemon: Implement exponential backoff based on activity file","description":"Modify daemon heartbeat logic to use exponential backoff when idle.\n\nRead ~/gt/daemon/activity.json on each heartbeat.\nCompute idle duration: time.Since(activity.Timestamp)\nAdjust next ticker interval based on idle duration:\n\n| Idle Duration | Next Heartbeat |\n|---------------|----------------|\n| 0-5 min | 5 min (base) |\n| 5-15 min | 10 min |\n| 15-45 min | 30 min |\n| 45+ min | 60 min (max) |\n\nImplementation:\n1. Add activity file reading to daemon heartbeat\n2. Replace fixed ticker with dynamic interval calculation\n3. Reset to base interval when activity is fresh\n\nNo counter tracking needed - pure discovery from single timestamp.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-26T19:22:14.170114-08:00","updated_at":"2025-12-26T19:22:14.170114-08:00","dependencies":[{"issue_id":"gt-ws8ol.3","depends_on_id":"gt-ws8ol","type":"parent-child","created_at":"2025-12-26T19:22:14.170719-08:00","created_by":"daemon"},{"issue_id":"gt-ws8ol.3","depends_on_id":"gt-ws8ol.1","type":"blocks","created_at":"2025-12-26T19:22:19.844324-08:00","created_by":"daemon"},{"issue_id":"gt-ws8ol.3","depends_on_id":"gt-ws8ol.2","type":"blocks","created_at":"2025-12-26T19:22:19.90297-08:00","created_by":"daemon"}]} +{"id":"gt-ws8ol.2","title":"bd: Add town-level activity signal in PersistentPreRun","description":"Add activity signaling to beads so daemon can detect bd usage.\n\nIn cmd/bd/main.go PersistentPreRun, add a call to write activity to\nthe Gas Town daemon directory if running inside a Gas Town workspace.\n\nThe signal file is ~/gt/daemon/activity.json (or detected town root).\n\nFormat:\n{\n \"last_command\": \"bd create ...\",\n \"actor\": \"gastown/crew/max\",\n \"timestamp\": \"2025-12-26T19:30:00Z\"\n}\n\nShould be best-effort (silent failure) to avoid breaking bd outside Gas Town.","status":"tombstone","priority":2,"issue_type":"task","created_at":"2025-12-26T19:22:04.033458-08:00","updated_at":"2025-12-26T19:25:27.854783-08:00","dependencies":[{"issue_id":"gt-ws8ol.2","depends_on_id":"gt-ws8ol","type":"parent-child","created_at":"2025-12-26T19:22:04.03401-08:00","created_by":"daemon"}],"deleted_at":"2025-12-26T19:25:27.854783-08:00","deleted_by":"daemon","delete_reason":"delete","original_type":"task"} +{"id":"gt-ws8ol.3","title":"Daemon: Implement exponential backoff based on activity file","description":"Modify daemon heartbeat logic to use exponential backoff when idle.\n\nRead ~/gt/daemon/activity.json on each heartbeat.\nCompute idle duration: time.Since(activity.Timestamp)\nAdjust next ticker interval based on idle duration:\n\n| Idle Duration | Next Heartbeat |\n|---------------|----------------|\n| 0-5 min | 5 min (base) |\n| 5-15 min | 10 min |\n| 15-45 min | 30 min |\n| 45+ min | 60 min (max) |\n\nImplementation:\n1. Add activity file reading to daemon heartbeat\n2. Replace fixed ticker with dynamic interval calculation\n3. Reset to base interval when activity is fresh\n\nNo counter tracking needed - pure discovery from single timestamp.\n\nCROSS-RIG DEPENDENCY: Also requires beads:bd-v8ku (assigned to beads/crew/dave)","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-26T19:22:14.170114-08:00","updated_at":"2025-12-26T19:25:45.317-08:00","dependencies":[{"issue_id":"gt-ws8ol.3","depends_on_id":"gt-ws8ol","type":"parent-child","created_at":"2025-12-26T19:22:14.170719-08:00","created_by":"daemon"},{"issue_id":"gt-ws8ol.3","depends_on_id":"gt-ws8ol.1","type":"blocks","created_at":"2025-12-26T19:22:19.844324-08:00","created_by":"daemon"},{"issue_id":"gt-ws8ol.3","depends_on_id":"gt-ws8ol.2","type":"blocks","created_at":"2025-12-26T19:22:19.90297-08:00","created_by":"daemon"}]} {"id":"gt-wsa2","title":"Digest: mol-deacon-patrol","description":"Patrol #6: Routine","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-24T22:22:39.448813-08:00","updated_at":"2025-12-24T22:22:39.448813-08:00","closed_at":"2025-12-24T22:22:39.448781-08:00","close_reason":"Squashed from 8 wisps"} {"id":"gt-wsjg","title":"Test Patrol for Bonding","description":"Parent issue for mol bond CLI test","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-24T21:17:02.116318-08:00","updated_at":"2025-12-25T01:33:00.40243-08:00","closed_at":"2025-12-25T01:33:00.40243-08:00","close_reason":"Test pollution cleanup"} {"id":"gt-wsjg.1","title":"Polecat Arm (arm-toast)","description":"Single polecat inspection and action cycle.\n\nThis molecule is bonded dynamically by mol-witness-patrol's survey-workers step.\nEach polecat being monitored gets one arm that runs in parallel with other arms.\n\n## Variables\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| polecat_name | Yes | Name of the polecat to inspect |\n| rig | Yes | Rig containing the polecat |\n\n## Step: capture\nCapture recent tmux output for toast.\n\n```bash\ntmux capture-pane -t gt-gastown-toast -p | tail -50\n```\n\nRecord:\n- Last activity timestamp (when was last tool call?)\n- Visible errors or stack traces\n- Completion indicators (\"Done\", \"Finished\", etc.)\n\n## Step: assess\nCategorize polecat state based on captured output.\n\nStates:\n- **working**: Recent tool calls, active processing\n- **idle**: At prompt, no recent activity\n- **error**: Showing errors or stack traces\n- **requesting_shutdown**: Sent LIFECYCLE/Shutdown mail\n- **done**: Showing completion indicators\n\nCalculate: minutes since last activity.\nNeeds: capture\n\n## Step: load-history\nRead nudge history for toast from patrol state.\n\n```\nnudge_count = state.nudges[toast].count\nlast_nudge_time = state.nudges[toast].timestamp\n```\n\nThis data was loaded by the parent patrol's load-state step and passed\nto the arm via the bonding context.\nNeeds: assess\n\n## Step: decide\nApply the nudge matrix to determine action for toast.\n\n| State | Idle Time | Nudge Count | Action |\n|-------|-----------|-------------|--------|\n| working | any | any | none |\n| idle | \u003c10min | any | none |\n| idle | 10-15min | 0 | nudge-1 (gentle) |\n| idle | 15-20min | 1 | nudge-2 (direct) |\n| idle | 20+min | 2 | nudge-3 (final) |\n| idle | any | 3 | escalate |\n| error | any | any | assess-severity |\n| requesting_shutdown | any | any | pre-kill-verify |\n| done | any | any | pre-kill-verify |\n\nNudge text:\n1. \"How's progress? Need any help?\"\n2. \"Please wrap up soon. What's blocking you?\"\n3. \"Final check. Will escalate in 5 min if no response.\"\n\nRecord decision and rationale.\nNeeds: load-history\n\n## Step: execute\nTake the decided action for toast.\n\n**nudge-N**:\n```bash\ntmux send-keys -t gt-gastown-toast \"{{nudge_text}}\" Enter\n```\n\n**pre-kill-verify**:\n```bash\ncd polecats/toast\ngit status # Must be clean\ngit log origin/main..HEAD # Check for unpushed\nbd show \u003cassigned-issue\u003e # Verify closed/deferred\n```\nIf clean: kill session, remove worktree, delete branch\nIf dirty: record failure, retry next cycle\n\n**escalate**:\n```bash\ngt mail send mayor/ -s \"Escalation: toast stuck\" -m \"...\"\n```\n\n**none**: No action needed.\n\nRecord: action taken, result, updated nudge count.\nNeeds: decide\n\n## Output\n\nThe arm completes with:\n- action_taken: none | nudge-1 | nudge-2 | nudge-3 | killed | escalated\n- result: success | failed | pending\n- updated_state: New nudge count and timestamp for toast\n\nThis data feeds back to the parent patrol's aggregate step.\n---\nbonded_from: mol-polecat-arm\nbonded_to: gt-wsjg\nbonded_ref: arm-toast\nbonded_at: 2025-12-23T10:00:00Z\n","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-24T21:17:02.197808-08:00","updated_at":"2025-12-24T21:17:02.363919-08:00","closed_at":"2025-12-24T21:17:02.363919-08:00","close_reason":"Closed","dependencies":[{"issue_id":"gt-wsjg.1","depends_on_id":"gt-wsjg","type":"parent-child","created_at":"2025-12-24T21:17:02.198268-08:00","created_by":"daemon"}]}