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:
kerosene
2026-01-26 13:05:05 -08:00
committed by John Ogle
parent 149da3d2e2
commit 9d87f01823
2 changed files with 397 additions and 57 deletions

View File

@@ -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."""