From ad8189b010cfafa0041d6540e185e21c04f0030c Mon Sep 17 00:00:00 2001 From: gastown/crew/jack Date: Fri, 9 Jan 2026 18:07:07 -0800 Subject: [PATCH] feat(deacon): use await-signal with exponential backoff in patrol loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Deacon patrol formula now uses `gt mol step await-signal` with exponential backoff instead of vague "sleep 60s" instructions. How it works: - Subscribes to `bd activity --follow` (beads activity feed) - Returns IMMEDIATELY when any gt/bd command triggers activity - On timeout, waits exponentially longer: 60s → 120s → 240s → max 10m - Tracks idle:N label on hq-deacon bead across invocations This connects the designed-but-unintegrated backoff mechanism to the actual patrol loop. Idle towns let Deacon sleep; active work wakes it. Co-Authored-By: Claude Opus 4.5 --- .../formulas/mol-deacon-patrol.formula.toml | 42 +++++++++++++++---- .../formulas/mol-deacon-patrol.formula.toml | 42 +++++++++++++++---- 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/.beads/formulas/mol-deacon-patrol.formula.toml b/.beads/formulas/mol-deacon-patrol.formula.toml index 854cc926..aca940d5 100644 --- a/.beads/formulas/mol-deacon-patrol.formula.toml +++ b/.beads/formulas/mol-deacon-patrol.formula.toml @@ -23,7 +23,7 @@ Witnesses detect it and escalate to the Mayor. The Deacon's agent bead last_activity timestamp is updated during each patrol cycle. Witnesses check this timestamp to verify health.""" formula = "mol-deacon-patrol" -version = 6 +version = 8 [[steps]] id = "inbox-check" @@ -707,15 +707,39 @@ Burn and let daemon respawn, or exit if context high. Decision point at end of patrol cycle: If context is LOW: -- **Sleep 60 seconds minimum** before next patrol cycle -- If town is idle (no in_progress work), sleep longer (2-5 minutes) -- Return to inbox-check step +Use await-signal with exponential backoff to wait for activity: -**Why longer sleep?** -- Idle agents should not be disturbed -- Health checks every few seconds flood inboxes and waste context -- The daemon (10-minute heartbeat) is the safety net for dead sessions -- Active work triggers feed events, which wake agents naturally +```bash +gt mol step await-signal --agent-bead hq-deacon \ + --backoff-base 60s --backoff-mult 2 --backoff-max 10m +``` + +This command: +1. Subscribes to `bd activity --follow` (beads activity feed) +2. Returns IMMEDIATELY when any beads activity occurs +3. If no activity, times out with exponential backoff: + - First timeout: 60s + - Second timeout: 120s + - Third timeout: 240s + - ...capped at 10 minutes max +4. Tracks `idle:N` label on hq-deacon bead for backoff state + +**On signal received** (activity detected): +Reset the idle counter and start next patrol cycle: +```bash +gt agent state hq-deacon --set idle=0 +``` +Then return to inbox-check step. + +**On timeout** (no activity): +The idle counter was auto-incremented. Continue to next patrol cycle +(the longer backoff will apply next time). Return to inbox-check step. + +**Why this approach?** +- Any `gt` or `bd` command triggers beads activity, waking the Deacon +- Idle towns let the Deacon sleep longer (up to 10 min between patrols) +- Active work wakes the Deacon immediately via the feed +- No polling or fixed sleep intervals If context is HIGH: - Write state to persistent storage diff --git a/internal/formula/formulas/mol-deacon-patrol.formula.toml b/internal/formula/formulas/mol-deacon-patrol.formula.toml index 454f5b35..b02a87ae 100644 --- a/internal/formula/formulas/mol-deacon-patrol.formula.toml +++ b/internal/formula/formulas/mol-deacon-patrol.formula.toml @@ -23,7 +23,7 @@ Witnesses detect it and escalate to the Mayor. The Deacon's agent bead last_activity timestamp is updated during each patrol cycle. Witnesses check this timestamp to verify health.""" formula = "mol-deacon-patrol" -version = 7 +version = 8 [[steps]] id = "inbox-check" @@ -749,15 +749,39 @@ Burn and let daemon respawn, or exit if context high. Decision point at end of patrol cycle: If context is LOW: -- **Sleep 60 seconds minimum** before next patrol cycle -- If town is idle (no in_progress work), sleep longer (2-5 minutes) -- Return to inbox-check step +Use await-signal with exponential backoff to wait for activity: -**Why longer sleep?** -- Idle agents should not be disturbed -- Health checks every few seconds flood inboxes and waste context -- The daemon (10-minute heartbeat) is the safety net for dead sessions -- Active work triggers feed events, which wake agents naturally +```bash +gt mol step await-signal --agent-bead hq-deacon \ + --backoff-base 60s --backoff-mult 2 --backoff-max 10m +``` + +This command: +1. Subscribes to `bd activity --follow` (beads activity feed) +2. Returns IMMEDIATELY when any beads activity occurs +3. If no activity, times out with exponential backoff: + - First timeout: 60s + - Second timeout: 120s + - Third timeout: 240s + - ...capped at 10 minutes max +4. Tracks `idle:N` label on hq-deacon bead for backoff state + +**On signal received** (activity detected): +Reset the idle counter and start next patrol cycle: +```bash +gt agent state hq-deacon --set idle=0 +``` +Then return to inbox-check step. + +**On timeout** (no activity): +The idle counter was auto-incremented. Continue to next patrol cycle +(the longer backoff will apply next time). Return to inbox-check step. + +**Why this approach?** +- Any `gt` or `bd` command triggers beads activity, waking the Deacon +- Idle towns let the Deacon sleep longer (up to 10 min between patrols) +- Active work wakes the Deacon immediately via the feed +- No polling or fixed sleep intervals If context is HIGH: - Write state to persistent storage