cron-management
GitHub提供cron.md文件的读写规范,指导添加、更新和删除定时任务。明确5字段Cron表达式格式及LLM/命令类型任务的Markdown结构,确保计划任务配置的正确性与一致性。
Trigger Scenarios
Install
npx skills add xuiltul/animaworks --skill cron-management -g -y
SKILL.md
Frontmatter
{
"name": "cron-management",
"description": "cron.mdA skill for reading and writing in the correct format. Provides procedures for adding, updating, and deleting scheduled tasks. Use when: cron.mdwhen you need to edit, add cron expressions, add or delete LLM-type or command-type tasks, or maintain scheduled jobs."
}
Framework-side implementation (for reference)
cron (Scheduled Tasks)
Parsing of cron is handled by core/runtime/schedule_parser.py (parse_cron_md / parse_schedule), while registration, execution, and reloading are handled by core/runtime/scheduler_manager.py (APScheduler, AsyncIOScheduler(timezone=get_app_timezone())).
core/tasks/background.py (a separate system from cron)
This module does not perform cron scheduling. It handles background execution of long-running tool calls and rotation of DM logs. Do not confuse it with cron.
BackgroundTaskManager
- Role: Executes target tools asynchronously on
asyncioand saves status to disk. - Persistence location:
state/background_tasks/{task_id}.json(pending/running/completed/failed, result strings, errors, timestamps). - Public API (overview):
submit(tool_name, tool_args, execute_fn)— Runs synchronous execution functions in a thread pool.submit_async(...)— Asynchronousexecute_fnversion.get_task/list_tasks/active_count— Queries.cleanup_old_tasks(max_age_hours=24)— Deletes completed and failed tasks older than 24 hours. Additionally, deletes JSON files that have remained inrunningstate for more than 48 hours (crash orphans).
from_profiles(anima_dir, ..., profiles, config_eligible): The set of background target tools is merged from the following 3 layers (later wins).- In-code defaults
_DEFAULT_ELIGIBLE_TOOLS(a fixed list compatible with Mode A) - Entries from
EXECUTION_PROFILEtobackground_eligible: truein each tool module (core.integrations._base.get_eligible_tools_from_profiles). Keys use the"{tool}:{subcommand}"format (consistent with Mode S'ssubmit) config_eligiblepassed fromconfig.jsonand others (explicit override)
- In-code defaults
is_eligible(tool_name): Matching is possible by either of the following names — schema name (e.g.,generate_3d_model), profile key (e.g.,image_gen:3d).- Examples included in
_DEFAULT_ELIGIBLE_TOOLSby default (values are approximate seconds):generate_character_assets/generate_fullbody/generate_bustup/generate_icon/generate_chibi/generate_3d_model/generate_rigged_model/generate_animations(30 each),local_llm/run_command(60 each). - Completion hook:
Callable[[BackgroundTask], Awaitable[None]]can be set inon_complete. The application side can write Markdown notifications tostate/background_notifications/from here (background.pyitself does not touch the notification directory).
rotate_dm_logs
- Truncates entries in
shared/dm_logs/*.jsonlatmax_age_days(default 7 days), appending old lines to{stem}.{YYYYMMDD}.archive.jsonlas an archive. This is not a cron task, but it is defined in the same file as background maintenance, so it is listed here for reference.
Structure of cron.md
Overall configuration
# Cron: {自分の名前}
## タスク名1
schedule: 0 9 * * *
type: llm
タスクの説明文...
## タスク名2
schedule: */5 * * * *
type: command
command: /path/to/script.sh
Rules that must be strictly followed
- Each task must start with
## タスク名(H2 heading. Do not use H3 or H1) - The
schedule:line is mandatory (must start with the keywordschedule:. Do not make it a###heading) - The schedule must be a standard 5-field cron expression only (
09:00and毎週金曜 17:00are not allowed) - The
type:line is mandatory (eitherllmorcommand) - Insert a blank line between tasks (for readability)
Incorrect writing style
❌ ### */5 * * * * ← H3見出しにcron式を書いてはいけない
❌ ### 09:00 ← 自然言語の時刻表記は不可
❌ ### 毎週金曜 17:00 ← 日本語のスケジュール表記は不可
❌ cron: 0 9 * * * ← キー名は "schedule:" であること("cron:" ではない)
❌ interval: 5m ← interval形式は不可
❌ schedule: 0 9 * * * * ← 6フィールドは不可(5フィールドのみ)
Correct writing style
✅ schedule: 0 9 * * * ← "schedule:" + 半角スペース + 5フィールドcron式
✅ schedule: */5 * * * *
✅ schedule: 30 21 * * *
✅ schedule: 0 17 * * 5
5-field cron expression reference
Field structure
schedule: 分 時 日 月 曜日
| Field | Position | Range | Description |
|---|---|---|---|
| Minute | 1 | 0-59 | At what minute to execute |
| Hour | 2 | 0-23 | At what hour to execute (24-hour format) |
| Day | 3 | 1-31 | On what day to execute |
| Month | 4 | 1-12 | In what month to execute |
| Weekday | 5 | 0-6 | On what weekday to execute (0=Mon, 6=Sun) |
Note: Weekday is 0=Monday, 6=Sunday (APScheduler specification. This differs from the common cron convention where 0=Sunday)
Special characters
| Character | Meaning | Example |
|---|---|---|
* |
All values | * * * * * = every minute |
*/n |
Interval of n | */5 * * * * = every 5 minutes |
n-m |
Range | 0 9-17 * * * = every hour from 9:00 to 17:00 |
n,m |
List | 0 9,12,18 * * * = 9:00, 12:00, 18:00 |
n-m/s |
Range + interval | 0 9-17/2 * * * = every 2 hours from 9:00 to 17:00 |
Commonly used schedule examples
Daily
| What you want to do | cron expression | Explanation |
|---|---|---|
| Every morning at 9:00 | 0 9 * * * |
minute=0, hour=9 |
| Every morning at 9:30 | 30 9 * * * |
minute=30, hour=9 |
| Every day at 12:00 (noon) | 0 12 * * * |
minute=0, hour=12 |
| Every day at 18:00 | 0 18 * * * |
minute=0, hour=18 |
| Every evening at 21:30 | 30 21 * * * |
minute=30, hour=21 |
| Every day at 2:00 AM | 0 2 * * * |
minute=0, hour=2 |
Interval-based
| What you want to do | cron expression | Explanation |
|---|---|---|
| Every 5 minutes | */5 * * * * |
minute=*/5(0,5,10,...,55) |
| Every 10 minutes | */10 * * * * |
minute=*/10(0,10,20,...,50) |
| Every 15 minutes | */15 * * * * |
minute=*/15(0,15,30,45) |
| Every 30 minutes | */30 * * * * |
minute=*/30(0,30) |
| Every hour | 0 * * * * |
at minute 0 of every hour |
| Every 2 hours | 0 */2 * * * |
at minute 0 of 0:00, 2:00, 4:00, ..., 22:00 |
| Every 5 minutes during business hours only | */5 9-17 * * * |
every 5 minutes from 9:00 to 17:55 |
| Every hour during business hours only | 0 9-17 * * * |
every hour on the hour from 9:00 to 17:00 |
Weekday-based
| What you want to do | cron expression | Explanation |
|---|---|---|
| Every weekday morning at 9:00 | 0 9 * * 0-4 |
weekday=0-4 (Mon-Fri) |
| Every Monday at 9:00 | 0 9 * * 0 |
weekday=0 (Mon) |
| Every Friday at 17:00 | 0 17 * * 4 |
weekday=4 (Fri) |
| Every Friday at 18:00 | 0 18 * * 4 |
weekday=4 (Fri) |
| Every 30 minutes during business hours on weekdays | */30 9-17 * * 0-4 |
weekdays 9:00-17:30 |
| Every weekend morning at 10:00 | 0 10 * * 5,6 |
weekday=5,6 (Sat, Sun) |
Monthly
| What you want to do | cron expression | Explanation |
|---|---|---|
| On the 1st of every month at 9:00 | 0 9 1 * * |
day=1 |
| On the 15th of every month at 12:00 | 0 12 15 * * |
day=15 |
| Near the last business day of every month (28th) | 0 17 28 * * |
day=28 (approximation) |
| On the first day of each quarter at 9:00 (Jan, Apr, Jul, Oct) | 0 9 1 1,4,7,10 * |
month=1,4,7,10 |
Task type details
type: llm — LLM judgment task
Tasks that require thinking and judgment. Write the task content as free text after schedule: and type: llm.
## 毎朝の業務計画
schedule: 0 9 * * *
type: llm
長期記憶から昨日の進捗を確認し、今日のタスクを計画する。
理念と目標に照らして優先順位を判断する。
結果は state/current_state.md に書き出す。
- The description is passed directly as the prompt to the LLM
- Clearly specifying the concrete output (what to write out) is effective
- Multiple lines are allowed
- If you want to include a fenced code block in the body (```)が含まれると、パーサーが警告ログを出す(確定コマンドなら
type: commandshould be considered)
type: command — Command execution task
Bash commands or tool calls that execute deterministically.
Pattern A: bash command
## バックアップ実行
schedule: 0 2 * * *
type: command
command: /usr/local/bin/backup.sh
- Write the command to execute in a single line in
command: - Shell redirection (
>,>>,|) is usable - Multi-line commands are not recommended (combine into one line or use a script file)
Pattern B: tool call
## Slack朝の通知
schedule: 0 9 * * 0-4
type: command
tool: slack_channel_post
args:
channel_id: "C0123456789"
text: "おはようございます!"
- Write the tool name in
tool:(a schema name included in the permission settings ofpermissions.json. For example, for Slack posting, useslack_channel_post) - After
args:, use a YAML block format with 2-space indentation - It is executed with
ToolHandler.handle(tool, args), and the result string is treated as the equivalent of stdout
Option: skip_pattern
A follow-up cron LLM (an analysis session with heartbeat-equivalent context) that runs only when the command succeeds (exit_code == 0) via type: command and stdout is non-empty is suppressed when stdout matches this regular expression.
## Chatwork未返信チェック
schedule: */5 * * * *
type: command
command: chatwork_cli.py unreplied --json
skip_pattern: "^\[\]$"
- Write a regular expression in
skip_pattern:(at runtime,re.search(skip_pattern, stdout)is used) - If the regular expression contains YAML special characters such as
[], enclose it in quotes ("..."or'...'). The parser automatically removes the outer quotes - At parse time, an invalid regular expression → a warning is logged and
skip_patternis treated as unset (no follow-up suppression) - At runtime, if
re.searchthrows an exception (e.g., a residual invalid pattern) → a warning is logged and the follow-up is executed without suppression - In the example above, the follow-up is skipped when the number of unreplied messages is 0 (
[])
Option: trigger_heartbeat
Controls per-task whether to run the follow-up cron LLM when the command succeeds (following the evaluation order of SchedulerManager._run_cron_task).
## Chatwork未返信チェック
schedule: */15 * * * *
type: command
command: animaworks-tool chatwork unreplied
skip_pattern: "^\[\]$"
trigger_heartbeat: false
- Conditions under which a follow-up is considered (when all are satisfied):
exit_code == 0and stdout (after.strip()) is non-empty - Length of stdout: The
stdoutthatrun_cron_commandreturns to the scheduler is truncated to the first 1000 characters. This preview is also what is passed to the follow-up LLM. For the full log, refer tostate/cron_logs/(JSONL) trigger_heartbeat: false— Even if the above conditions are satisfied, the follow-up cron LLM is not executed (evaluated beforeskip_pattern)trigger_heartbeat: true(default) — If the conditions are satisfied, the follow-up is executed (thenskip_patternis evaluated next)- If
false,no, or0is specified, it is suppressed. Otherwise, it is treated as true - The follow-up cron LLM runs with a heartbeat-equivalent prompt filter (background context)
- When
exit_code != 0or stdout is empty, the follow-up does not start in the first place (skip_pattern/trigger_heartbeatare irrelevant)
Criteria for choosing between types
Cases where type: command should be used
- The command to execute is fully determined
- Parameters are fixed (region, cluster name, profile, etc.)
- Judgment of the result is left to the cron LLM session
Cases where type: llm should be used
- The execution content needs to change depending on the situation
- Investigation combining multiple tools is required
- Human-like judgment and analysis are needed at the execution stage
Prohibited patterns
- Including a code block (a fixed command) in type: llm → That command should be type: command
- Writing "execute exactly as follows" but using type: llm → The LLM cannot accurately reproduce commands. Use type: command
Anima is not "a person who memorizes commands"
type: command is the same as a human saving a script. Anima's value lies in the ability to judge based on results. Leave deterministic execution to the framework, and have Anima focus on judgment, analysis, and reporting.
cron.md Parse Notifications (Automatic)
When a parse or registration error is detected during the initial registration of cron.md or at reload_schedule, the scheduler generates a state/background_notifications/cron_health_{タイムスタンプ}.md. The notification is included in the context of the next heartbeat or cron execution. There is no feature for periodically monitoring the execution status of registered cron jobs.
Detected issues:
- A task is defined, but no valid schedule can be registered (e.g., all expressions are invalid)
- Outside of comments or code fences, there is a
schedule:line with leading whitespace (schedule:must be written at the beginning of the line) - The valid configuration text contains
schedule:, but the parser returns no tasks at all
cron.md Operating Procedures
Target Paths and Subordinate Editing
- Read your own
cron.mdviaread_memory_file(path="cron.md"), and update it viawrite_memory_file(path="cron.md", ...). - A supervisor can edit subordinates'
cron.md/heartbeat.mdusing the write memory tool by specifying them as in../{anima_name}/cron.md(applies to all subordinates, including children, grandchildren, and below). status.json/identity.md/permissions.jsonare owned by root and cannot be written to from memory or file tools. Authorized supervisorinjection.mdchanges and identity creation requests during bootstrap are forwarded to and authorized by root. No direct file operations are performed.
Adding a New Task
- Load your own
cron.md - Append a new section at the end of the file
- Verify the format before writing (see the checklist below)
- Write the file
## 新しいタスク名
schedule: <5フィールドcron式>
type: llm|command
<説明またはcommand/tool行>
Modifying an Existing Task
- Load
cron.md - Locate the relevant section (from
## タスク名up to just before the next##) - Edit the lines to change (
schedule:,type:, description text, etc.) - Write the file
Deleting a Task
- Load
cron.md - Delete the entire relevant section (from
## タスク名up to just before the next##) - Write the file
Temporarily Disabling a Task
Wrapping it in HTML comments makes the parser skip it:
<!--
## 一時停止中のタスク
schedule: 0 9 * * *
type: llm
このタスクは一時的に停止中。
-->
Pre-Write Checklist
Before updating cron.md, always verify the following:
- Each task starts with
## タスク名(not###or#) - A
schedule:line exists (not a###heading or natural language) - The schedule is a 5-field cron expression (
分 時 日 月 曜日) - Each field's value is within the valid range (minute: 0-59, hour: 0-23, day: 1-31, month: 1-12, weekday: 0-6)
- A
type:line exists (llmorcommand) - For command type,
command:ortool:exists - For tool type, the indentation of
args:is correct (2 spaces) - There is a blank line between tasks
-
schedule:is not written inside a code block (this can cause health warnings)
Validation Method
After writing, you can verify that parsing works correctly with the following command:
# プロジェクトルートで実行。ANIMAWORKS_ANIMA_DIR 未設定時は ~/.animaworks/animas/default を使用
python -c "
from core.runtime.schedule_parser import parse_cron_md, parse_schedule
import os
from pathlib import Path
cron_path = Path(os.environ.get('ANIMAWORKS_ANIMA_DIR', '~/.animaworks/animas/default')) / 'cron.md'
content = cron_path.expanduser().read_text()
tasks = parse_cron_md(content)
for t in tasks:
trigger = parse_schedule(t.schedule)
status = '✅' if trigger else '❌ パース失敗'
print(f'{status} {t.name}: schedule=\"{t.schedule}\" type={t.type}')
"
If all tasks show ✅, everything is normal. If ❌ appears, correct the schedule expression.
Complete Example
# Cron: example_anima
## 毎朝の業務計画
schedule: 0 9 * * *
type: llm
長期記憶から昨日の進捗を確認し、今日のタスクを計画する。
理念と目標に照らして優先順位を判断する。
結果は state/current_state.md に書き出す。
## Chatwork未返信チェック
schedule: */5 9-18 * * 0-4
type: command
command: chatwork-cli unreplied --json > $ANIMAWORKS_ANIMA_DIR/state/chatwork_unreplied.json
skip_pattern: "^\[\]$"
trigger_heartbeat: false
## Slack朝の挨拶
schedule: 0 9 * * 0-4
type: command
tool: slack_channel_post
args:
channel_id: "C0123456789"
text: "おはようございます!今日もよろしくお願いします。"
## 週次振り返り
schedule: 0 17 * * 4
type: llm
今週のepisodes/を読み返し、パターンを抽出してknowledge/に統合する。
改善点があれば procedures/ に手順を追記する。
## 月次レポート
schedule: 0 10 1 * *
type: llm
先月のepisodes/とknowledge/を分析し、月次サマリーレポートを作成する。
レポートは knowledge/monthly_report_YYYY-MM.md として保存する。
Common Mistakes and Fixes
| Mistake | Correct Form | Reason |
|---|---|---|
### */5 * * * * |
schedule: */5 * * * * |
H3 headings cannot be used as task separators |
### 09:00 |
schedule: 0 9 * * * |
Natural language times cannot be parsed |
### 毎週金曜 17:00 |
schedule: 0 17 * * 4 |
Japanese notation cannot be parsed |
schedule: 9:00 |
schedule: 0 9 * * * |
HH:MM format is not a 5-field cron expression |
schedule: every 5 minutes |
schedule: */5 * * * * |
English notation cannot be parsed |
schedule: 0 9 * * 7 |
schedule: 0 9 * * 6 |
Weekday 7 is out of range (0-6) |
schedule: 0 9 * * SUN |
schedule: 0 9 * * 6 |
Weekday names cannot be used (numbers only) |
schedule: 0 25 * * * |
schedule: 0 23 * * * |
Hour must be in the range 0-23 |
schedule: 60 * * * * |
schedule: 0 * * * * |
Minute must be in the range 0-59 |
skip_pattern: ^\[\]$ (without quotes) |
skip_pattern: "^\[\]$" |
[] is interpreted as an empty list in YAML. Wrap it in quotes |
Notes
- Hot reload: When
cron.md/heartbeat.mdare updated via Anima'swrite_memory_file, etc., the schedule change callback triggersreload_scheduleand re-registers immediately. Subordinate management files are also edited with the write memory tool as in../{anima_name}/cron.md. External direct edits are also detected by the mtime check (_check_schedule_freshness) just before the next heartbeat or any cron fires, but this is not the standard procedure for supervisor edits to subordinate files - Old jobs immediately after reload: When the scheduler is rebuilt upon detecting an mtime change, cron firings tied to the previous definition may be skipped as "stale" (intentional duplicate execution prevention)
- Time zone: APScheduler uses
get_app_timezone(). Thesystem.timezoneofconfig.jsoncan be set to an IANA name (e.g.,Asia/Tokyo). When empty, the OS time zone is auto-detected, falling back toAsia/Tokyoon failure - Concurrency: If task names differ, multiple jobs in the same minute may run in parallel via
asyncio.create_task. The same task name will not re-enter while running (skipped via_cron_running). Each job ismax_instances=1 - Command type does not stop the process on failure (
cron_executedis recorded, andstderr/ exit_code remain in the log). The follow-up cron LLM runs only on success and when stdout exists (as described above) - Periodic execution of
type: llmuses the background model (background_modelofstatus.json, etc.; the main model is used if not set) - Detailed command output accumulates in
state/cron_logs/(daily JSONL) and is housekept for up to 14 days - When accessing another Anima's tools/ directory, check permissions in advance
Version History
- 0f2a80e Current 2026-10-19 11:10
- edd4a93 2026-09-28 03:35
- 140e43b 2026-07-25 09:12


