Files
VolcanicAgenticCore/docs/superpowers/specs/2026-10-14-skill-jobs-design.md
EugeneTes 6e3241fed9 feat(scheduler): scheduled jobs inside skills (.volcanic/skills/<skill>/jobs/<job>.md)
Skill directories may now declare prompt or watcher jobs as files (cron or one-off at).
The file is the source of truth: the scheduler re-reads every chat's jobs/ every minute,
around agent runs and before /jobs and /skills, and schedules, re-plans or removes them.
Ids are <skill>/<job>; run state lives in .volcanic/scheduler/skill-jobs-state.json, never
in jobs.json. Watchers run from the skill directory with VOLCANIC_WORKSPACE and
VOLCANIC_SKILL_DIR. Broken files are shown with the reason in /jobs, /skills, schedule list
and the new <skill_jobs> prompt section; cancel answers with the file to edit. Every part of
a skill directory is optional; file reading and frontmatter parsing move to Abstractions.
2026-10-14 12:54:40 +00:00

3.6 KiB

Skill jobs — design

Goal

Let a skill declare scheduled jobs as files: <workspace>/.volcanic/skills/<skill>/jobs/<job>.md, prompt or watcher, cron or one-off at. Every part of a skill directory (SKILL.md, INSTRUCTIONS.md, scripts, jobs/) is optional, in any combination. The owner's spec (2026-10-14) was accepted with the eight amendments below.

Decisions

  • One scheduler, separate source. Skill jobs run in the process-wide JobScheduler: one timer, the same script runner, script environment (GOOGLE_ACCESS_TOKEN), redaction, failure alerts without repeats, and the skip of an occurrence while the previous run is in flight. They are never written to jobs.json: the file is the source of truth. SyncSkillJobsAsync(target) re-reads the target's files and adds, re-plans (definition changed) or removes (file gone, enabled: false, broken) entries; an unchanged definition keeps its next run. Entries carry ScheduledJob.Skill ([JsonIgnore]), and their table key includes the target because <skill>/<job> is only unique per chat.
  • Sync points: on start (before overdue one-offs fire), every Scheduler:SkillJobSyncInterval (1 min) for every deliverable known target, in SchedulerExtension on before_agent_start and agent_end (a file the agent just wrote is in /jobs at once), and before /jobs, /skills and schedule list.
  • State per workspace in .volcanic/scheduler/skill-jobs-state.json: last run and result, the at text that fired (doneAt), and the at text last seen with when it was first seen. State of deleted files is dropped.
  • Amendments (accepted):
    1. The id comes from paths only: <skill dir>/<file name without .md>. name is a label.
    2. The header carries the file: [Scheduled job "<skill>/<job>" (skill job, .volcanic/skills/<skill>/jobs/<job>.md)]; the failure alert too.
    3. An at already past (more than 60 s) when the bot first saw that at never fires and is shown as not scheduled; an at seen before it was due and missed while the bot was down fires late on start.
    4. Sync every minute plus before and after each run (above).
    5. File reading and the simplified frontmatter parser live in Abstractions (SkillFiles, Frontmatter), shared by the core's SkillCatalog and the scheduler. Job files additionally cut YAML comments (# after whitespace).
    6. The format and the chat's skill jobs go into the system prompt from SchedulerExtension (<skill_jobs>), so sessions without the scheduler never advertise them.
    7. Prompt text up to 16 KB.
    8. enabled: false unschedules the job; it stays listed as disabled.
  • No frequency guard: cron has 5 fields (no seconds), so once a minute is the floor, and overlapping runs are skipped already.
  • Limit: Scheduler:MaxSkillJobsPerTarget (50), separate from MaxJobsPerTarget; beyond it, files are listed as over the limit. At most 200 job files are read per workspace.
  • Watchers run from the skill directory with VOLCANIC_WORKSPACE and VOLCANIC_SKILL_DIR added after the environment filter; those paths are not redacted (only the host's extra values are).
  • Control is by files only: /cancel and schedule cancel on a skill job id answer with the file to edit.
  • Errors aren't silent: missing kind, neither or both of cron/at, a watcher without command, an invalid cron, at, timeoutSeconds or enabled → not scheduled, shown with the reason in /jobs, /skills, schedule list and the prompt section.

User docs

docs/telegram-bot.md#skill-jobs, and the skill directory rules under #skills.