Skip to main content

schedule

This skill should be used when creating, listing, or deleting scheduled agent tasks via GitHub Actions cron workflows. It generates workflow YAML files that invoke Soleur skills on a recurring schedule using claude-code-action.

Source facts

Repository
jikig-ai/soleur
Last source activity
September 29, 2026 at 22:02
Detected SKILL.md language
English
Stars
16
Forks
5

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
schedule
description
This skill should be used when creating, listing, or deleting scheduled agent tasks via GitHub Actions cron workflows. It generates workflow YAML files that invoke Soleur skills on a recurring schedule using claude-code-action.
<!-- soleur-cloud-mode:start --> **Cloud Mode (Devin):** before pipeline work run `bash "${CLAUDE_PLUGIN_ROOT}/scripts/cloud-detect.sh"`. If `CLAUDE_PLUGIN_ROOT` is unset (cloud exec shells do not export it), resolve the root by IDENTITY, never by script basename: for `d` in `"$HOME/.local/share/devin/cli/plugins/cache"` and `/opt/.devin/plugins`, skip unless `[ -d "$d" ]`, then `MANIFEST="$(find "$d" -path '*/.claude-plugin/plugin.json' -exec grep -l '"name"[[:space:]]*:[[:space:]]*"soleur"' {} + 2>/dev/null | head -1)"`, `ROOT="${MANIFEST%/.claude-plugin/plugin.json}"` — one resolution, two consumers: `$ROOT/scripts/cloud-detect.sh` and `$ROOT/scripts/precommit-guard.sh`. (Shape check, not authentication: a planted `{"name":"soleur"}` dir passes — ADR-179 A11.) `local` or `not-local:no-devin-env` proceeds normally; any other `not-local:<reason>` applies `<plugin-root>/devin/INSTRUCTIONS.md` §Cloud Mode: emit the `--banner`, fan out sequentially with `Reviewed-Coverage: sequential-fallback` disclosure (never claim an independent review ran), `message_user` ack before any secrets read or production mutation, and run `precommit-guard.sh` before any `git commit` — hooks do not fire in cloud. <!-- soleur-cloud-mode:end --> <!-- grok-harness-invoke:start --> **Grok Build (`plugins/soleur/lib/harness.ts` `invokeSkill()`):** Read this SKILL.md in this process and run it to completion. A one-segment `soleur:<name>` in this document names a SKILL — on Grok Build, Read `plugins/soleur/skills/<name>/SKILL.md` in this process; it is not a nested tool_use. A multi-segment id such as `soleur:<domain>:<name>` names an AGENT: spawn it, never Read it, and on Grok Build spawn_subagent takes the id with its colons replaced by hyphens (`agentIdToGrokSubagentType`). **Claude Code:** Skill tool for a skill (`soleur:<name>`), Task tool with `subagent_type` for an agent. Forbidden is executing a subset, not the Read. <!-- grok-harness-invoke:end --> # Schedule Manager Generate GitHub Actions workflow files that run Soleur skills on a schedule. Two modes: - **Recurring** (`--cron`): Standard cron-triggered workflow that fires on every matching tick. - **One-time** (`--once --at <YYYY-MM-DD>`): Single-fire workflow that self-disables after running. Useful for "remind me to do X in 2 weeks" style tasks. Each schedule becomes a standalone `.github/workflows/scheduled-<name>.yml`. ## When to use this skill vs harness `schedule` Two skills exist with the name `schedule`. They serve different jobs: | Use this (`soleur:schedule`) when | Use harness `schedule` when | |---|---| | Push commits, open PRs, modify the user's repo | Analyze, summarize, report — no repo writes | | Use repo secrets (Doppler, Vercel, Cloudflare) | No secrets needed | | Invoke a Soleur skill (`soleur:<skill>`) | Generic Claude API task | | Run Terraform / migrations / deploys | Read-only research, posting somewhere | Examples for `soleur:schedule`: - "Open a cleanup PR removing feature flag X in 2 weeks" → `--once` - "Run a weekly Terraform drift check" → recurring Examples for `harness schedule`: - "Summarize recent issues every Monday and post to Slack" - "Check if a vendor's API changed and email the diff" If the agent doesn't need access to your repo, prefer harness `schedule`. **Both `schedule` skills generate GitHub-Actions cron only.** For a **one-time, autonomous, server-side action** that needs fire-time prd secrets or an installation-token repo write (no operator, no GH-Actions runner), use the Inngest path instead: a self-armed **oneshot** (ADR-046, bespoke logic) or the generic **reminder primitive** (`POST /api/internal/schedule-reminder`, no deploy for a comment / registered check — incl. the parametric `sentry-issue-rate` check). See [`inngest-oneshot-and-reminder-patterns.md`](../../../../knowledge-base/engineering/operations/runbooks/inngest-oneshot-and-reminder-patterns.md). **`create`'s Step 0 routing gate (below) enforces this automatically** — Inngest is the structural default for secret/server-side work; GHA is the pure-GH-ops exception. ## Commands ### `create` Generate a new scheduled workflow. **Step 0: Execution-substrate routing gate (run FIRST — before generating any GHA YAML).** This skill generates GitHub-Actions cron. But a GHA runner has **no Doppler/prd access**, so a scheduled task that needs fire-time prd secrets (Sentry/Supabase/ Doppler tokens) will *fire but fail to authenticate* — and the `new-scheduled-cron-prefer-inngest` PreToolUse hook blocks the YAML write anyway. Route by what the task actually needs BEFORE generating anything; Inngest is the **structural default** for secret/server-side work, GHA the exception. Classify `$ARGUMENTS` (the task description): 1. **Needs fire-time prd secrets OR autonomous server-side logic.** Grep the task for `doppler`, `SUPABASE_SERVICE_ROLE_KEY`, `SENTRY_*` tokens, an installation- token repo write, or "server-side"/"runtime" execution. **Route to Inngest — STOP, do not generate GHA:** - **Verification-shaped** ("did Sentry issue X drop below N/day?", "is metric Y under threshold?", "comment/close an issue after a check") → the reminder primitive's **`named-check`** (zero-deploy to arm; `POST /api/internal/schedule-reminder`). If a suitable check exists in `CHECK_REGISTRY` (e.g. **`sentry-issue-rate`**, the worked example — events/day of a tagged Sentry issue over a window, with optional `close_on_pass`), arming is a single zero-deploy POST. If not, add a small reviewed check to the registry (one deploy), then arm. Note: a check that sets `close_on_pass` performs a **scoped issue mutation** — it closes the action's own `report_to_issue` (never an arbitrary issue) — so treat arming a closing check as a privileged action, not a read-only probe. - **Bespoke server-side logic** (multi-step, custom data shape) → a self-armed **oneshot** (ADR-046; one `.ts` + boot-arm + deploy). - Full decision matrix + the exact arm HTTP shape: [`inngest-oneshot-and-reminder-patterns.md`](../../../../knowledge-base/engineering/operations/runbooks/inngest-oneshot-and-reminder-patterns.md). 2. **Periodic verification needing a secret already wired to the follow-through sweeper** (`scheduled-followthrough-sweeper.yml` passes an allowlist; e.g. the job exposes the repo secret `SENTRY_ACTIONS_RO_TOKEN`, the org-level read-only `actions-read-prd` Sentry integration, under that same name) → a vetted follow-through script (under the repo's `followthroughs` dir) + a follow-through directive. No new workflow, no Inngest deploy. 3. **Pure-GH ops** (no prd secrets — a skill reading public state, a Dependabot- like job). Only this class falls through to Step 0a and GHA-cron generation. Announce the routing decision. If routing to Inngest/sweeper, do NOT proceed to Step 0a — point the operator at the substrate and stop. The `new-scheduled-cron-prefer-inngest` hook is now the BACKSTOP (it should rarely fire because this gate routes upstream); its `gate-override` is reserved for a genuinely pure-GH op and the override comment must assert "no prd secrets". **Step 0a: Mode detection (recurring vs one-time)** Inspect `$ARGUMENTS`: - If both `--cron` and `--once` are present → error: `Cannot specify both --once and --cron`. Stop. - If neither `--cron` nor `--once` is present → error: `Specify either --once <YYYY-MM-DD> or --cron <expression>`. Stop. (No silent default — the operator must declare intent.) - If `--once` is present → **one-time mode**. Validate per Step 0c below, then skip to Step 2 → Step 3b. - If `--cron` is present → **recurring mode**. Continue with Step 0b below. **Step 0b: Check arguments (recurring mode)** If `$ARGUMENTS` contains `--name`, `--skill`, `--cron`, and `--model` flags, extract values directly and skip to Step 2. Validate each value using the same rules below. If any required flag is missing, proceed to Step 1 for the missing parameters only. Optional flags: `--timeout` (minutes, default 30) and `--max-turns` (default 30). **Step 0c: Check arguments (one-time mode)** For `--once`, all five flags are MANDATORY. There is no AskUserQuestion fallback (Step 1 is recurring-only). Operator-supplied values are required so the workflow file is self-documenting at `delete`/cleanup time. Each value MUST match its regex EXACTLY before substitution into the YAML — these regexes are the load-bearing defense against shell/YAML injection at fire time. If ANY regex below fails, emit `ERROR: --<flag> value '<value>' does not match required pattern <regex>` to stderr and stop. - `--once` (mode flag, no value) - `--at <YYYY-MM-DD>` — strict ISO date pattern `^\d{4}-\d{2}-\d{2}$`. Default time = 09:00 UTC. Natural-language dates (`"2 weeks from now"`, `tomorrow`) and ISO datetimes (`2026-05-17T03:00`) are intentionally rejected. Validate with: ```bash python3 -c " import re, sys from datetime import datetime, timezone, timedelta if not re.fullmatch(r'\d{4}-\d{2}-\d{2}', sys.argv[1]): sys.exit(f\"ERROR: --at value '{sys.argv[1]}' does not match required pattern ^\\\d{{4}}-\\\d{{2}}-\\\d{{2}}\$\") d = datetime.fromisoformat(sys.argv[1]).replace(tzinfo=timezone.utc) now = datetime.now(timezone.utc) if d.date() < now.date(): sys.exit('ERROR: --at date is in the past') if (d - now) > timedelta(days=50): sys.exit('ERROR: --at date is more than 50 days out (GHA auto-disables workflows after 60d inactivity; 10d margin)') " '<AT_VALUE>' ``` - `--issue <N>` — GitHub issue number, pattern `^[1-9][0-9]{0,8}$` (positive integer, no leading zeros, ≤9 digits). - `--comment <id>` — GitHub comment ID, pattern `^[1-9][0-9]{0,18}$` (positive int64, no leading zeros). Look it up via `gh api repos/<OWNER>/<REPO>/issues/<N>/comments --jq '.[] | "\(.id) \(.user.login) \(.created_at): \(.body | .[0:80])"'` if the operator needs to find it. - `--name <kebab-case>` — schedule name, pattern `^[a-z][a-z0-9-]{0,49}$` (lowercase, leading letter, ≤50 chars). Reject if `.github/workflows/scheduled-<name>.yml` already exists with the exact error: ```text ERROR: .github/workflows/scheduled-<name>.yml already exists. Pick a different --name (no --force flag in v1). ``` **Comment integrity capture (D5 — comment-author-pin defense):** The fire-time agent fetches the task spec from the referenced comment body. If the comment is editable between authoring and fire, an attacker with comment-edit access can rewrite the task — single-user incident vector. Pin the comment at create time: ```bash COMMENT_META=$(gh api "repos/${REPO_OWNER_AND_NAME}/issues/comments/${COMMENT_ID}" \ --jq '"\(.user.login)\t\(.created_at)\t\(.updated_at)"' 2>/dev/null) || { echo "ERROR: --comment <id> not found or not accessible" >&2; exit 1; } EXPECTED_AUTHOR=$(echo "$COMMENT_META" | cut -f1) COMMENT_CREATED_AT=$(echo "$COMMENT_META" | cut -f2) COMMENT_UPDATED_AT=$(echo "$COMMENT_META" | cut -f3) if [[ "$COMMENT_CREATED_AT" != "$COMMENT_UPDATED_AT" ]]; then echo "ERROR: comment $COMMENT_ID has been edited (created_at != updated_at)." >&2 echo "Re-post the task spec as a fresh comment and pass the new --comment <id>." >&2 exit 1 fi ``` Both `$EXPECTED_AUTHOR` and `$COMMENT_CREATED_AT` are embedded into the workflow's `env:` block (Step 3b) and re-checked by the fire-time pre-flight (D5) — see Step 3b below. **Default-branch warning:** If the current branch is not the default branch (`gh repo view --json defaultBranchRef --jq .defaultBranchRef.name`), print a **WARNING** (not an error): ```text WARNING: GHA cron triggers fire only from workflows on the default branch. Merge this workflow before <FIRE_DATE> or it will not fire. ``` If any one-time validation fails, stop. Do not write the file. **Step 1: Collect inputs (recurring mode only)** One-time mode does not pass through Step 1 — all flags are mandatory at the command line so the resulting workflow is self-documenting. For recurring mode, use the **AskUserQuestion tool** to gather missing inputs one at a time: 1. **Schedule name** — A short kebab-case name (e.g., `weekly-security-audit`). Validate: lowercase letters, numbers, and hyphens only. Check that `.github/workflows/scheduled-<name>.yml` does not already exist. 2. **Skill to run** — Which Soleur skill to invoke. Show available skills: ```bash ls plugins/soleur/skills/*/SKILL.md | sed 's|plugins/soleur/skills/||;s|/SKILL.md||' | sort ``` Present the list and let the user choose. Prefix with `soleur:` in the generated prompt. 3. **Cron expression** — A 5-field POSIX cron expression (minute hour day-of-month month day-of-week). - Reject named values (`MON`, `JAN`) — GitHub Actions does not support them - Reject schedules more frequent than every 5 minutes - Note: GitHub Actions cron has ~15-minute variance in trigger timing 4. **Model** — Which Claude model to use. Default: `claude-sonnet-5-5` (good balance of cost and capability). Accept any valid Anthropic model identifier. 5. **Timeout (minutes)** — Job-level timeout to prevent runaway billing. Default: 30. Validate: positive integer, minimum 5 minutes. 6. **Max turns** — Maximum number of agent turns before stopping. Default: 30. Validate: positive integer, minimum 5 turns. Budget formula: plugin overhead (~10 turns) + task tool calls + error buffer (~5). Multi-platform data collection or PR-based persist workflows typically need 40-50 turns. **Step 2: Resolve action SHAs**
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub