CeremonyPlugin Reference
CeremonyPlugin replaces hardcoded cron tasks with configurable, observable, and hot-reloadable YAML-defined ceremonies. A ceremony is a recurring scheduled ritual — for example, a daily board health check, PR triage, or weekly sprint review.
What is a Ceremony?
Section titled “What is a Ceremony?”A ceremony is a named, cron-scheduled task that:
- Fires on a cron schedule (e.g. every morning at 9am, every Friday at 5pm)
- Invokes an agent skill against one or more project targets
- Publishes lifecycle events on the EventBus
- Persists execution outcomes to
knowledge.db - Optionally notifies a Discord channel on completion
Ceremonies are defined in YAML files placed in workspace/ceremonies/. They are loaded at startup and hot-reloaded every 5 seconds when files change, with no restart required.
YAML Schema
Section titled “YAML Schema”Each ceremony is defined in its own .yaml file inside workspace/ceremonies/.
id: board.pr-audit # required — unique ceremony identifiername: PR Audit # required — human-readable nameschedule: "0 9 * * 1-5" # required — cron expression (UTC)skill: audit-prs # required — agent skill to invoketargets: # required — project paths or ['all'] - projects/my-projectnotifyChannel: eng-standup # optional — Discord channel slug for notificationsenabled: true # optional — defaults to true; set false to pauseField Reference
Section titled “Field Reference”| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Unique identifier, e.g. board.pr-audit. Used in bus topics. |
name | string | yes | Human-readable name shown in logs and notifications. |
schedule | string | yes | Standard 5-field cron expression (UTC). |
skill | string | yes | Agent skill invoked when the ceremony fires. |
targets | string[] | yes | Non-empty list of project paths, or ['all'] to target all projects. |
notifyChannel | string | no | Discord channel slug for outcome notifications. |
enabled | boolean | no | Whether this ceremony is active. Defaults to true. |
Cron Expression Examples
Section titled “Cron Expression Examples”| Expression | Meaning |
|---|---|
0 9 * * 1-5 | 9:00 AM UTC, Monday–Friday |
0 17 * * 5 | 5:00 PM UTC on Fridays |
0 */3 * * * | Every 3 hours |
0 8 * * 1 | 8:00 AM UTC every Monday |
File Layout
Section titled “File Layout”workspace/ ceremonies/ # global ceremonies (all projects) board-pr-audit.yaml weekly-sprint-review.yaml
.proto/ projects/ {project-slug}/ ceremonies/ # project-scoped overrides board-pr-audit.yaml # overrides global ceremony with same idProject-level ceremonies with the same id override global ones. All other global ceremonies are inherited unchanged.
How CeremonyPlugin Integrates with the Scheduler and Bus
Section titled “How CeremonyPlugin Integrates with the Scheduler and Bus”At startup, CeremonyPlugin.install(bus):
- Loads all ceremony YAML files via
CeremonyYamlLoader - Schedules a cron timer for each enabled ceremony
- Starts a 5-second hot-reload polling loop
When a ceremony fires:
- Publishes
ceremony.{id}.executeon the EventBus - Publishes
agent.skill.requestto dispatch the skill to an agent - Waits up to 120 seconds for
agent.skill.response.{runId} - Publishes
ceremony.{id}.completedwith the outcome - Persists the outcome to
knowledge.db(capped at 500 entries per ceremony) - Sends a Discord notification if
notifyChannelis set
Bus Topics
Section titled “Bus Topics”| Topic | Direction | Description |
|---|---|---|
ceremony.{id}.execute | published | Fired when a ceremony’s cron triggers |
ceremony.{id}.completed | published | Fired after a ceremony run finishes |
agent.skill.request | published | Dispatches the skill to an agent for execution |
agent.skill.response.{runId} | subscribed | Result from the agent skill execution |
ceremony.# | subscribed | Wildcard — used internally to intercept completed events |
world.state.snapshot | published | Ceremony state update via CeremonyStateExtension |
Execute Payload (ceremony.{id}.execute)
Section titled “Execute Payload (ceremony.{id}.execute)”{ type: "ceremony.execute", context: { runId: string, // UUID for this run ceremonyId: string, projectPaths: string[], // resolved targets startedAt: number, // Unix ms }, skill: string, ceremonyName: string,}Completed Payload (ceremony.{id}.completed)
Section titled “Completed Payload (ceremony.{id}.completed)”{ type: "ceremony.completed", outcome: { runId: string, ceremonyId: string, skill: string, status: "success" | "failure" | "timeout", duration: number, // ms targets: string[], startedAt: number, completedAt: number, result?: string, // optional summary from skill error?: string, // set on failure or timeout }}Built-in vs Custom Ceremonies
Section titled “Built-in vs Custom Ceremonies”Built-in ceremonies ship as default YAML files in src/plugins/ceremonies/defaults/. On first run, CeremonyPlugin copies any missing defaults into workspace/ceremonies/. You can edit these files to customize them — they will not be overwritten on subsequent runs.
Custom ceremonies are any YAML files you add to workspace/ceremonies/ or .proto/projects/{slug}/ceremonies/. There is no registration step — just drop the file and it will be picked up within 5 seconds.
Outcome Persistence
Section titled “Outcome Persistence”Ceremony outcomes are stored in knowledge.db using CeremonyOutcomesRepository. Each ceremony retains up to 500 historical outcomes; older entries are pruned automatically. Status values are success, failure, and timeout (after 120 seconds with no skill response).