chore: sync formula and recovery work
Some checks failed
CI / Check for .beads changes (push) Has been skipped
CI / Check embedded formulas (push) Failing after 17s
CI / Test (push) Failing after 1m20s
CI / Lint (push) Failing after 22s
CI / Integration Tests (push) Failing after 34s
CI / Coverage Report (push) Has been skipped
Windows CI / Windows Build and Unit Tests (push) Has been cancelled

- Update mol-deacon-patrol formula
- Fix sling helpers, doctor branch check
- Update startup session and tests
- Remove obsolete research doc

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
diesel
2026-01-26 14:23:10 -08:00
committed by John Ogle
parent 0e19529186
commit e2e43b8bf5
6 changed files with 65 additions and 418 deletions

View File

@@ -1,45 +1,29 @@
description = """
Mayor's daemon patrol loop - CONTINUOUS EXECUTION.
Mayor's daemon patrol loop.
The Deacon is the Mayor's background process that runs CONTINUOUSLY in a loop:
1. Execute all patrol steps (inbox-check through context-check)
2. Wait for activity OR timeout (15-minute max)
3. Create new patrol wisp and repeat from step 1
**This is a continuous loop, not a one-shot execution.**
## Patrol Loop Flow
```
START → inbox-check → [all patrol steps] → loop-or-exit
await-signal (wait for activity)
create new wisp → START
```
## Plugin Dispatch
The plugin-run step scans $GT_ROOT/plugins/ for plugins with open gates and
dispatches them to dogs. With a 15-minute max backoff, plugins with 15m
cooldown gates will be checked at least once per interval.
The Deacon is the Mayor's background process that runs continuously, handling callbacks, monitoring rig health, and performing cleanup. Each patrol cycle runs these steps in sequence, then loops or exits.
## Idle Town Principle
**The Deacon should be silent/invisible when the town is healthy and idle.**
- Skip HEALTH_CHECK nudges when no active work exists
- Sleep via await-signal (exponential backoff up to 15 min)
- Let the feed subscription wake on actual events
- The daemon is the safety net for dead sessions
- Sleep 60+ seconds between patrol cycles (longer when idle)
- Let the feed subscription wake agents on actual events
- The daemon (10-minute heartbeat) is the safety net for dead sessions
This prevents flooding idle agents with health checks every few seconds.
## Second-Order Monitoring
Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
prevents the "who watches the watchers" problem - if the Deacon dies,
Witnesses detect it and escalate to the Mayor."""
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 = 9
version = 8
[[steps]]
id = "inbox-check"
@@ -504,48 +488,29 @@ investigate why the Witness isn't cleaning up properly."""
[[steps]]
id = "plugin-run"
title = "Scan and dispatch plugins"
title = "Execute registered plugins"
needs = ["zombie-scan"]
description = """
Scan plugins and dispatch any with open gates to dogs.
Execute registered plugins.
**Step 1: List plugins and check gates**
```bash
gt plugin list
```
Scan $GT_ROOT/plugins/ for plugin directories. Each plugin has a plugin.md with TOML frontmatter defining its gate (when to run) and instructions (what to do).
For each plugin, check if its gate is open:
- **cooldown**: Time since last run (e.g., 15m) - check state.json
- **cron**: Schedule-based (e.g., "0 9 * * *")
- **condition**: Metric threshold (e.g., wisp count > 50)
- **event**: Trigger-based (e.g., startup, heartbeat)
See docs/deacon-plugins.md for full documentation.
**Step 2: Dispatch plugins with open gates**
```bash
# For each plugin with an open gate:
gt dog dispatch --plugin <plugin-name>
```
Gate types:
- cooldown: Time since last run (e.g., 24h)
- cron: Schedule-based (e.g., "0 9 * * *")
- condition: Metric threshold (e.g., wisp count > 50)
- event: Trigger-based (e.g., startup, heartbeat)
This sends the plugin to an idle dog for execution. The dog will:
1. Execute the plugin instructions from plugin.md
2. Send DOG_DONE mail when complete (processed in next patrol's inbox-check)
For each plugin:
1. Read plugin.md frontmatter to check gate
2. Compare against state.json (last run, etc.)
3. If gate is open, execute the plugin
**Step 3: Track dispatched plugins**
Record in state.json which plugins were dispatched this cycle:
```json
{
"plugins_dispatched": ["scout-patrol"],
"last_plugin_run": "2026-01-23T13:45:00Z"
}
```
Plugins marked parallel: true can run concurrently using Task tool subagents. Sequential plugins run one at a time in directory order.
**If no plugins have open gates:**
Skip dispatch - all plugins are within their cooldown/schedule.
**If no dogs available:**
Log warning and skip dispatch this cycle. Dog pool maintenance step will spawn dogs.
See docs/deacon-plugins.md for full documentation."""
Skip this step if $GT_ROOT/plugins/ does not exist or is empty."""
[[steps]]
id = "dog-pool-maintenance"
@@ -872,89 +837,57 @@ This enables the Deacon to burn and respawn cleanly."""
[[steps]]
id = "loop-or-exit"
title = "Continuous patrol loop"
title = "Burn and respawn or loop"
needs = ["context-check"]
description = """
Continue the patrol loop or exit for context refresh.
Burn and let daemon respawn, or exit if context high.
**CRITICAL**: This is where the continuous patrol loop happens. The Deacon MUST
loop back and start a new patrol cycle. Do NOT wait for external triggers.
Decision point at end of patrol cycle:
## Decision Matrix
1. **Check context usage**: `gt context --usage`
2. **If context HIGH (>80%)**: Exit cleanly, daemon respawns fresh session
3. **If context LOW**: Continue to patrol loop below
## The Continuous Patrol Loop
When context is low, execute this loop:
If context is LOW:
Use await-signal with exponential backoff to wait for activity:
```bash
# Step 1: Squash current patrol wisp (clean up)
gt mol squash
# Step 2: Wait for activity OR timeout (15-minute default)
gt mol step await-signal --agent-bead hq-deacon \
--backoff-base 60s --backoff-mult 2 --backoff-max 15m
# Step 3: Reset idle counter if activity was detected
gt agents state hq-deacon --set idle=0
# Step 4: Create new patrol wisp
WISP_ID=$(bd mol wisp mol-deacon-patrol 2>&1 | grep -o 'hq-[a-z0-9]*')
# Step 5: Hook it and start executing
gt hook $WISP_ID
--backoff-base 60s --backoff-mult 2 --backoff-max 10m
```
After hooking, immediately begin executing the new wisp from its first step
(inbox-check). The wisp is now on your hook, so just continue with patrol.
**IMPORTANT**: After await-signal returns (either signal or timeout), you MUST:
1. Squash the current wisp
2. Create a new patrol wisp
3. Hook it
4. Start executing from inbox-check
This IS the loop. There is no "return to inbox-check" command - you create a new
wisp and that wisp starts fresh from inbox-check.
## await-signal Behavior
The await-signal command:
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 (4 min)
- ...capped at 15 minutes max
- 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 15 min between patrols)
- Idle towns let the Deacon sleep longer (up to 10 min between patrols)
- Active work wakes the Deacon immediately via the feed
- No fixed polling intervals - event-driven wake
- No polling or fixed sleep intervals
## Plugin Dispatch Timing
If context is HIGH:
- Write state to persistent storage
- Exit cleanly
- Let the daemon orchestrator respawn a fresh Deacon
The plugin-run step (earlier in patrol) handles plugin dispatch:
- Scans $GT_ROOT/plugins/ for plugins with open gates
- Dispatches to dogs via `gt dog dispatch --plugin <name>`
- Dogs send DOG_DONE when complete (processed in next patrol's inbox-check)
With a 15-minute max backoff, plugins with 15m cooldown gates will be checked
at least once per interval when idle.
## Exit Path (High Context)
If context is HIGH (>80%):
The daemon ensures Deacon is always running:
```bash
# Exit cleanly - daemon will respawn with fresh context
exit 0
# Daemon respawns on exit
gt daemon status
```
The daemon ensures Deacon is always running. Exiting is safe - you'll be
respawned with fresh context and the patrol loop continues."""
This enables infinite patrol duration via context-aware respawning."""