Plugin System Design
Design document for the Gas Town plugin system. Written 2026-01-11, crew/george session.
Problem Statement
Section titled “Problem Statement”Gas Town needs extensible, project-specific automation that runs during Deacon patrol cycles. The immediate use case is rebuilding stale binaries (gt, bd, wv), but the pattern generalizes to any periodic maintenance task.
Current state:
- Plugin infrastructure exists conceptually (patrol step mentions it)
~/gt/plugins/directory exists with README- No actual plugins in production use
- No formalized execution model
Design Principles Applied
Section titled “Design Principles Applied”Discover, Don’t Track
Section titled “Discover, Don’t Track”Reality is truth. State is derived.
Plugin state (last run, run count, results) lives on the ledger as wisps, not in shadow state files. Gate evaluation queries the ledger directly.
ZFC: Zero Framework Cognition
Section titled “ZFC: Zero Framework Cognition”Agent decides. Go transports.
The Deacon (agent) evaluates gates and decides whether to dispatch. Go code provides transport (gt dog dispatch) but doesn’t make decisions.
MEOW Stack Integration
Section titled “MEOW Stack Integration”| Layer | Plugin Analog |
|---|---|
| Molecule | plugin.md - work template with TOML frontmatter |
| Ephemeral | Plugin-run wisps - high-volume, digestible |
| Observable | Plugin runs appear in bd activity feed |
| Workflow | Gate → Dispatch → Execute → Record → Digest |
Architecture
Section titled “Architecture”Plugin Locations
Section titled “Plugin Locations”~/gt/├── plugins/ # Town-level plugins (universal)│ └── README.md├── gastown/│ └── plugins/ # Rig-level plugins│ └── rebuild-gt/│ └── plugin.md├── beads/│ └── plugins/│ └── rebuild-bd/│ └── plugin.md└── wyvern/ └── plugins/ └── rebuild-wv/ └── plugin.mdTown-level (~/gt/plugins/): Universal plugins that apply everywhere.
Rig-level (<rig>/plugins/): Project-specific plugins.
The Deacon scans both locations during patrol.
Execution Model: Dog Dispatch
Section titled “Execution Model: Dog Dispatch”Key insight: Plugin execution should not block Deacon patrol.
Dogs are reusable workers designed for infrastructure tasks. Plugin execution is dispatched to dogs:
Deacon Patrol Dog Worker───────────────── ─────────────────1. Scan plugins2. Evaluate gates3. For open gates: └─ gt dog dispatch plugin ──→ 4. Execute plugin (non-blocking) 5. Create result wisp 6. Send DOG_DONE4. Continue patrol ...5. Process DOG_DONE ←── (next cycle)Benefits:
- Deacon stays responsive
- Multiple plugins can run concurrently (different dogs)
- Plugin failures don’t stall patrol
- Consistent with Dogs’ purpose (infrastructure work)
State Tracking: Wisps on the Ledger
Section titled “State Tracking: Wisps on the Ledger”Each plugin run creates a wisp:
bd wisp create \ --label type:plugin-run \ --label plugin:rebuild-gt \ --label rig:gastown \ --label result:success \ --body "Rebuilt gt: abc123 → def456 (5 commits)"Gate evaluation queries wisps instead of state files:
# Cooldown check: any runs in last hour?bd list --type=wisp --label=plugin:rebuild-gt --since=1h --limit=1Derived state (no state.json needed):
| Query | Command |
|---|---|
| Last run time | bd list --label=plugin:X --limit=1 --json |
| Run count | bd list --label=plugin:X --json | jq length |
| Last result | Parse result: label from latest wisp |
| Failure rate | Count result:failure vs total |
Digest Pattern
Section titled “Digest Pattern”Like cost digests, plugin wisps accumulate and get squashed daily:
gt plugin digest --yesterdayCreates: Plugin Digest 2026-01-10 bead with summary
Deletes: Individual plugin-run wisps from that day
This keeps the ledger clean while preserving audit history.
Plugin Format Specification
Section titled “Plugin Format Specification”File Structure
Section titled “File Structure”rebuild-gt/└── plugin.md # Definition with TOML frontmatterplugin.md Format
Section titled “plugin.md Format”+++name = "rebuild-gt"description = "Rebuild stale gt binary from source"version = 1
[gate]type = "cooldown"duration = "1h"
[tracking]labels = ["plugin:rebuild-gt", "rig:gastown", "category:maintenance"]digest = true
[execution]timeout = "5m"notify_on_failure = true+++
# Rebuild gt Binary
Instructions for the dog worker to execute...TOML Frontmatter Schema
Section titled “TOML Frontmatter Schema”# Requiredname = "string" # Unique plugin identifierdescription = "string" # Human-readable descriptionversion = 1 # Schema version (for future evolution)
[gate]type = "cooldown|cron|condition|event|manual"# Type-specific fields:duration = "1h" # For cooldownschedule = "0 9 * * *" # For croncheck = "gt stale -q" # For condition (exit 0 = run)on = "startup" # For event
[tracking]labels = ["label:value", ...] # Labels for execution wispsdigest = true|false # Include in daily digest
[execution]timeout = "5m" # Max execution timenotify_on_failure = true # Escalate on failureseverity = "low" # Escalation severity if failedGate Types
Section titled “Gate Types”| Type | Config | Behavior |
|---|---|---|
cooldown | duration = "1h" | Query wisps, run if none in window |
cron | schedule = "0 9 * * *" | Run on cron schedule |
condition | check = "cmd" | Run check command, run if exit 0 |
event | on = "startup" | Run on Deacon startup |
manual | (no gate section) | Never auto-run, dispatch explicitly |
Instructions Section
Section titled “Instructions Section”The markdown body after the frontmatter contains agent-executable instructions. The dog worker reads and executes these steps.
Standard sections:
- Detection: Check if action is needed
- Action: The actual work
- Record Result: Create the execution wisp
- Notification: On success/failure
Escalation System
Section titled “Escalation System”Problem
Section titled “Problem”Current escalation is ad-hoc “mail Mayor”. Issues:
- Mayor gets backlogged easily
- No severity differentiation
- No alternative channels (email, SMS, etc.)
- No tracking of stale escalations
Solution: Unified Escalation API
Section titled “Solution: Unified Escalation API”New command:
gt escalate \ --severity=<low|medium|high|critical> \ --subject="Plugin FAILED: rebuild-gt" \ --body="Build failed: make returned exit code 2" \ --source="plugin:rebuild-gt"Escalation Routing
Section titled “Escalation Routing”The command reads town config (~/gt/config.json or similar) for routing rules:
{ "escalation": { "routes": { "low": ["bead"], "medium": ["bead", "mail:mayor"], "high": ["bead", "mail:mayor", "email:human"], "critical": ["bead", "mail:mayor", "email:human", "sms:human"] }, "contacts": { "human_sms": "+1234567890" }, "stale_threshold": "4h" }}Escalation Actions
Section titled “Escalation Actions”| Action | Behavior |
|---|---|
bead | Create escalation bead with severity label |
mail:mayor | Send mail to mayor/ |
email:human | Send email via configured service |
sms:human | Send SMS via configured service |
Escalation Beads
Section titled “Escalation Beads”Every escalation creates a bead:
type: escalationstatus: openlabels: - severity:high - source:plugin:rebuild-gt - acknowledged:falseStale Escalation Patrol
Section titled “Stale Escalation Patrol”A patrol step (or plugin!) checks for unacknowledged escalations:
bd list --type=escalation --label=acknowledged:false --older-than=4hStale escalations get re-escalated at higher severity.
Acknowledging Escalations
Section titled “Acknowledging Escalations”gt escalate ack <bead-id># Sets label acknowledged:trueNew Commands Required
Section titled “New Commands Required”gt stale
Section titled “gt stale”Expose binary staleness check:
gt stale # Human-readable outputgt stale --json # Machine-readablegt stale --quiet # Exit code only (0=stale, 1=fresh)gt dog dispatch
Section titled “gt dog dispatch”Formalized plugin dispatch to dogs:
gt dog dispatch --plugin <name> [--rig <rig>]This:
- Finds the plugin definition
- Slinga a standardized work unit to an idle dog
- Returns immediately (non-blocking)
gt escalate
Section titled “gt escalate”Unified escalation API:
gt escalate \ --severity=<level> \ --subject="..." \ --body="..." \ [--source="..."]
gt escalate ack <bead-id>gt escalate list [--severity=...] [--stale]gt plugin
Section titled “gt plugin”Plugin management:
gt plugin list # List all pluginsgt plugin show <name> # Show plugin detailsgt plugin run <name> [--force] # Manual triggergt plugin digest [--yesterday] # Squash wisps to digestgt plugin history <name> # Show execution historyImplementation Plan
Section titled “Implementation Plan”Phase 1: Foundation
Section titled “Phase 1: Foundation”gt stalecommand - Expose CheckStaleBinary() via CLI- Plugin format spec - Finalize TOML schema
- Plugin scanning - Deacon scans town + rig plugin dirs
Phase 2: Execution
Section titled “Phase 2: Execution”gt dog dispatch --plugin- Formalized dog dispatch- Plugin execution in dogs - Dog reads plugin.md, executes
- Wisp creation - Record results on ledger
Phase 3: Gates & State
Section titled “Phase 3: Gates & State”- Gate evaluation - Cooldown via wisp query
- Other gate types - Cron, condition, event
- Plugin digest - Daily squash of plugin wisps
Phase 4: Escalation
Section titled “Phase 4: Escalation”gt escalatecommand - Unified escalation API- Escalation routing - Config-driven multi-channel
- Stale escalation patrol - Check unacknowledged
Phase 5: First Plugin
Section titled “Phase 5: First Plugin”rebuild-gtplugin - The actual gastown plugin- Documentation - So Beads/Wyvern can create theirs
Example: rebuild-gt Plugin
Section titled “Example: rebuild-gt Plugin”+++name = "rebuild-gt"description = "Rebuild stale gt binary from gastown source"version = 1
[gate]type = "cooldown"duration = "1h"
[tracking]labels = ["plugin:rebuild-gt", "rig:gastown", "category:maintenance"]digest = true
[execution]timeout = "5m"notify_on_failure = trueseverity = "medium"+++
# Rebuild gt Binary
Checks if the gt binary is stale (built from older commit than HEAD) and rebuilds.
## Gate Check
The Deacon evaluates this before dispatch. If gate closed, skip.
## Detection
Check binary staleness:
```bashgt stale --jsonIf "stale": false, record success wisp and exit early.
Action
Section titled “Action”Rebuild from source:
cd ~/gt/gastown/crew/george && make build && make installRecord Result
Section titled “Record Result”On success:
bd wisp create \ --label type:plugin-run \ --label plugin:rebuild-gt \ --label rig:gastown \ --label result:success \ --body "Rebuilt gt: $OLD → $NEW ($N commits)"On failure:
bd wisp create \ --label type:plugin-run \ --label plugin:rebuild-gt \ --label rig:gastown \ --label result:failure \ --body "Build failed: $ERROR"
gt escalate --severity=medium \ --subject="Plugin FAILED: rebuild-gt" \ --body="$ERROR" \ --source="plugin:rebuild-gt"---
## Open Questions
1. **Plugin discovery in multiple clones**: If gastown has crew/george, crew/max, crew/joe - which clone's plugins/ dir is canonical? Probably: scan all, dedupe by name, prefer rig-root if exists.
2. **Dog assignment**: Should specific plugins prefer specific dogs? Or any idle dog?
3. **Plugin dependencies**: Can plugins depend on other plugins? Probably not in v1.
4. **Plugin disable/enable**: How to temporarily disable a plugin without deleting it? Label on a plugin bead? `enabled = false` in frontmatter?
---
## References
- PRIMING.md - Core design principles- mol-deacon-patrol.formula.toml - Patrol step plugin-run- ~/gt/plugins/README.md - Current plugin stub