From 480903731609dd0ee7b181f76d913c54f6c9fc36 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Sun, 21 Dec 2025 17:41:50 -0800 Subject: [PATCH] docs: Add deacon plugin system design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New docs/deacon-plugins.md with full plugin architecture - Directory-based discovery at ~/gt/plugins/ - Gate types: cooldown, cron, condition, event - Parallel execution support via Task tool subagents - Updated mol-deacon-patrol plugin-run step to reference new docs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- docs/deacon-plugins.md | 289 ++++++++++++++++++++++++++++ internal/beads/builtin_molecules.go | 26 ++- 2 files changed, 308 insertions(+), 7 deletions(-) create mode 100644 docs/deacon-plugins.md diff --git a/docs/deacon-plugins.md b/docs/deacon-plugins.md new file mode 100644 index 00000000..10e94cc8 --- /dev/null +++ b/docs/deacon-plugins.md @@ -0,0 +1,289 @@ +# Deacon Plugins + +Town-level plugins that run during the Deacon's patrol loop. + +## Overview + +The Deacon's patrol includes a `plugin-run` step. Rather than hardcoding what +runs, we use **directory-based discovery** at `~/gt/plugins/`. Each plugin +directory contains enough information for the Deacon to decide whether to run +it and what to do. + +Plugins are not pre-validated by tooling. The Deacon reads each plugin's +metadata and instructions, decides if the gate is open, and executes +accordingly. + +## Directory Structure + +``` +~/gt/plugins/ +├── beads-cleanup/ +│ ├── plugin.md # Gate info + instructions (combined) +│ └── state.json # Auto-managed runtime state +├── health-report/ +│ ├── plugin.md +│ └── state.json +└── wisp-pressure/ + ├── plugin.md + └── state.json +``` + +Each plugin is a directory. If the directory exists, the plugin exists. + +## Plugin Metadata (plugin.md) + +The `plugin.md` file contains YAML frontmatter for gate configuration, +followed by markdown instructions for the Deacon. + +```markdown +--- +gate: cooldown +interval: 24h +parallel: true +--- + +# Beads Cleanup + +Clean up old wisps daily. + +## Actions + +1. Run `bd cleanup --wisp --force` +2. Run `bd doctor` to verify health +3. Log summary of cleaned items +``` + +### Frontmatter Fields + +| Field | Required | Description | +|-------|----------|-------------| +| `gate` | Yes | Gate type: `cooldown`, `cron`, `condition`, `event` | +| `interval` | For cooldown | Duration: `1h`, `24h`, `5m`, etc. | +| `schedule` | For cron | Cron expression: `0 9 * * *` | +| `check` | For condition | Command that outputs a number | +| `operator` | For condition | Comparison: `gt`, `lt`, `eq`, `ge`, `le` | +| `threshold` | For condition | Numeric threshold | +| `trigger` | For event | Event name: `startup`, `heartbeat` | +| `cooldown` | Optional | Min time between runs (for condition gates) | +| `parallel` | Optional | If true, can run concurrently with other plugins | + +## Gate Types + +### Cooldown + +Run at most once per interval since last successful run. + +```yaml +--- +gate: cooldown +interval: 24h +--- +``` + +### Cron + +Run on a schedule (standard cron syntax). + +```yaml +--- +gate: cron +schedule: "0 9 * * *" # 9am daily +--- +``` + +### Condition + +Run when a command's output crosses a threshold. + +```yaml +--- +gate: condition +check: "bd count --type=wisp" +operator: gt +threshold: 50 +cooldown: 30m # Don't spam even if condition stays true +--- +``` + +The `check` command must output a single number. The Deacon compares it +against `threshold` using `operator`. + +### Event + +Run in response to specific events. + +```yaml +--- +gate: event +trigger: startup # Run once when daemon starts +--- +``` + +Triggers: `startup`, `heartbeat` (every patrol), `mail` (when inbox has items). + +## State Management + +The Deacon maintains `state.json` in each plugin directory: + +```json +{ + "lastRun": "2025-12-21T10:00:00Z", + "lastResult": "success", + "runCount": 42, + "nextEligible": "2025-12-22T10:00:00Z" +} +``` + +This is auto-managed. Plugins don't need to touch it. + +## Parallel Execution + +Plugins marked `parallel: true` can run concurrently. During `plugin-run`, +the Deacon: + +1. Scans `~/gt/plugins/` for plugins with open gates +2. Groups them: parallel vs sequential +3. Launches parallel plugins using Task tool subagents +4. Runs sequential plugins one at a time +5. Waits for all to complete before continuing patrol + +Sequential plugins (default) run in directory order. Use sequential for +plugins that modify shared state or have ordering dependencies. + +## Example Plugins + +### beads-cleanup (daily maintenance) + +```markdown +--- +gate: cooldown +interval: 24h +parallel: true +--- + +# Beads Cleanup + +Daily cleanup of ephemeral beads. + +## Actions + +1. Run `bd cleanup --wisp --force` to remove old wisps +2. Run `bd doctor` to check for issues +3. Report count of cleaned items +``` + +### wisp-pressure (condition-triggered) + +```markdown +--- +gate: condition +check: "bd count --type=wisp" +operator: gt +threshold: 100 +cooldown: 1h +parallel: true +--- + +# Wisp Pressure Relief + +Triggered when wisp count gets too high. + +## Actions + +1. Run `bd cleanup --wisp --age=4h` (remove wisps > 4h old) +2. Check `bd count --type=wisp` +3. If still > 50, run `bd cleanup --wisp --age=1h` +4. Report final count +``` + +### health-report (scheduled) + +```markdown +--- +gate: cron +schedule: "0 9 * * *" +parallel: false +--- + +# Morning Health Report + +Generate daily health summary for the overseer. + +## Actions + +1. Run `gt status` to get overall health +2. Run `bd stats` to get metrics +3. Check for stale agents (no heartbeat > 1h) +4. Send summary: `gt mail send --human -s "Morning Report" -m "..."` +``` + +### startup-check (event-triggered) + +```markdown +--- +gate: event +trigger: startup +parallel: false +--- + +# Startup Verification + +Run once when the daemon starts. + +## Actions + +1. Verify `gt doctor` passes +2. Check all rigs have healthy Witnesses +3. Report any issues to Mayor inbox +``` + +## Patrol Integration + +The `mol-deacon-patrol` step 3 (plugin-run) works as follows: + +```markdown +## Step: plugin-run + +Execute registered plugins whose gates are open. + +1. Scan `~/gt/plugins/` for plugin directories +2. For each plugin, read `plugin.md` frontmatter +3. Check if gate is open (based on type and state.json) +4. Collect eligible plugins into parallel and sequential groups + +5. For parallel plugins: + - Use Task tool to spawn subagents for each + - Each subagent reads the plugin's instructions and executes + - Wait for all to complete + +6. For sequential plugins: + - Execute each in order + - Read instructions, run actions, update state + +7. Update state.json for each completed plugin +8. Log summary: "Ran N plugins (M parallel, K sequential)" +``` + +## Limitations + +- **No polecat spawning**: Plugins cannot spawn polecats. If a plugin tries + to use `gt spawn`, behavior is undefined. This may change in the future. + +- **No cross-plugin dependencies**: Plugins don't declare dependencies on + each other. If ordering matters, mark both as `parallel: false`. + +- **No plugin-specific state API**: Plugins can write to their own directory + if needed, but there's no structured API for plugin state beyond what the + Deacon auto-manages. + +## CLI Commands (Future) + +```bash +gt plugins list # Show all plugins, gates, status +gt plugins check # Show plugins with open gates +gt plugins show # Detail view of one plugin +gt plugins run # Force-run (ignore gate) +``` + +These commands are not yet implemented. The Deacon reads the directory +structure directly during patrol. diff --git a/internal/beads/builtin_molecules.go b/internal/beads/builtin_molecules.go index 5d0de15d..f0f981fb 100644 --- a/internal/beads/builtin_molecules.go +++ b/internal/beads/builtin_molecules.go @@ -565,14 +565,26 @@ Needs: inbox-check ## Step: plugin-run Execute registered plugins. -Run any plugins registered with the Deacon: -- Custom health checks -- Integration hooks (Slack, GitHub, etc.) -- Metrics collection -- External system sync +Scan ~/gt/plugins/ for plugin directories. Each plugin has a plugin.md with +YAML frontmatter defining its gate (when to run) and instructions (what to do). -Plugins are defined in the Mayor's config and run on each patrol cycle. -Skip this step if no plugins are registered. +See docs/deacon-plugins.md for full documentation. + +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) + +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 + +Plugins marked parallel: true can run concurrently using Task tool subagents. +Sequential plugins run one at a time in directory order. + +Skip this step if ~/gt/plugins/ does not exist or is empty. Needs: health-scan ## Step: orphan-check