research: analyze role template management strategy
Findings: - Two competing mechanisms: embedded templates vs local-fork edits - Local-fork created ~200 lines of divergent content in mayor/CLAUDE.md - TOML config overrides exist but only handle operational config Recommendation: Extend TOML override system to support [content] sections for template customization, unifying all override mechanisms.
This commit is contained in:
@@ -1,29 +1,45 @@
|
||||
description = """
|
||||
Mayor's daemon patrol loop.
|
||||
Mayor's daemon patrol loop - CONTINUOUS EXECUTION.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## 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 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.
|
||||
- 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
|
||||
|
||||
## 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.
|
||||
|
||||
The Deacon's agent bead last_activity timestamp is updated during each patrol
|
||||
cycle. Witnesses check this timestamp to verify health."""
|
||||
Witnesses detect it and escalate to the Mayor."""
|
||||
formula = "mol-deacon-patrol"
|
||||
version = 8
|
||||
version = 9
|
||||
|
||||
[[steps]]
|
||||
id = "inbox-check"
|
||||
@@ -488,29 +504,48 @@ investigate why the Witness isn't cleaning up properly."""
|
||||
|
||||
[[steps]]
|
||||
id = "plugin-run"
|
||||
title = "Execute registered plugins"
|
||||
title = "Scan and dispatch plugins"
|
||||
needs = ["zombie-scan"]
|
||||
description = """
|
||||
Execute registered plugins.
|
||||
Scan plugins and dispatch any with open gates to dogs.
|
||||
|
||||
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).
|
||||
**Step 1: List plugins and check gates**
|
||||
```bash
|
||||
gt plugin list
|
||||
```
|
||||
|
||||
See docs/deacon-plugins.md for full documentation.
|
||||
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)
|
||||
|
||||
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)
|
||||
**Step 2: Dispatch plugins with open gates**
|
||||
```bash
|
||||
# For each plugin with an open gate:
|
||||
gt dog dispatch --plugin <plugin-name>
|
||||
```
|
||||
|
||||
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
|
||||
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)
|
||||
|
||||
Plugins marked parallel: true can run concurrently using Task tool subagents. Sequential plugins run one at a time in directory order.
|
||||
**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"
|
||||
}
|
||||
```
|
||||
|
||||
Skip this step if $GT_ROOT/plugins/ does not exist or is empty."""
|
||||
**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."""
|
||||
|
||||
[[steps]]
|
||||
id = "dog-pool-maintenance"
|
||||
@@ -837,57 +872,89 @@ This enables the Deacon to burn and respawn cleanly."""
|
||||
|
||||
[[steps]]
|
||||
id = "loop-or-exit"
|
||||
title = "Burn and respawn or loop"
|
||||
title = "Continuous patrol loop"
|
||||
needs = ["context-check"]
|
||||
description = """
|
||||
Burn and let daemon respawn, or exit if context high.
|
||||
Continue the patrol loop or exit for context refresh.
|
||||
|
||||
Decision point at end of patrol cycle:
|
||||
**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.
|
||||
|
||||
If context is LOW:
|
||||
Use await-signal with exponential backoff to wait for activity:
|
||||
## 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:
|
||||
|
||||
```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 10m
|
||||
--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
|
||||
```
|
||||
|
||||
This command:
|
||||
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:
|
||||
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
|
||||
- Third timeout: 240s (4 min)
|
||||
- ...capped at 15 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)
|
||||
- Idle towns let the Deacon sleep longer (up to 15 min between patrols)
|
||||
- Active work wakes the Deacon immediately via the feed
|
||||
- No polling or fixed sleep intervals
|
||||
- No fixed polling intervals - event-driven wake
|
||||
|
||||
If context is HIGH:
|
||||
- Write state to persistent storage
|
||||
- Exit cleanly
|
||||
- Let the daemon orchestrator respawn a fresh Deacon
|
||||
## Plugin Dispatch Timing
|
||||
|
||||
The daemon ensures Deacon is always running:
|
||||
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%):
|
||||
```bash
|
||||
# Daemon respawns on exit
|
||||
gt daemon status
|
||||
# Exit cleanly - daemon will respawn with fresh context
|
||||
exit 0
|
||||
```
|
||||
|
||||
This enables infinite patrol duration via context-aware respawning."""
|
||||
The daemon ensures Deacon is always running. Exiting is safe - you'll be
|
||||
respawned with fresh context and the patrol loop continues."""
|
||||
|
||||
Reference in New Issue
Block a user