feat(cron): disable_on_success for goal-driven usercron#817
feat(cron): disable_on_success for goal-driven usercron#817chaodu-agent wants to merge 3 commits into
Conversation
Implements the Phase 1 'escape room' mode from ADR #816: - Add disable_on_success field to CronJobConfig (shell command) - Add disable_on_success_match (optional stdout match string) - Add disable_on_success_timeout_secs (default 60s) - Add disable_on_success_working_dir (optional cwd) - Evaluate command before firing: exit 0 + match = goal achieved - On success: post notification, write enabled=false to usercron, trigger reload - On failure/timeout: fire cron job normally (send message) - 9 unit tests covering all paths
- 🔴 F1: Replace blocking thread::sleep with async tokio::process::Command + tokio::time::timeout - 🟡 F2: Rewrite disable_job_in_usercron with line-based editing (preserves comments) - 🟡 F3: Only evaluate disable_on_success for usercron jobs (skip baseline) - 🟡 F4: Match jobs by id field instead of schedule+channel+message - Add id field to CronJobConfig (required for disable_on_success per ADR) - Add test for comment preservation - Convert evaluate tests to #[tokio::test] async
…ments - 🔴 Remove duplicate id: fields caused by sed (compile error) - 🟡 Add validation: disable_on_success without id is rejected at load time - 🟡 Fix id line parser to strip inline comments before matching
0b1fe69 to
91738c1
Compare
OpenAB PR ScreeningThis is auto-generated by the OpenAB project-screening flow for context collection and reviewer handoff.
Screening report## IntentPR #817 adds a way for scheduled usercron jobs to stop themselves once an external goal condition is met. The operator-visible problem: goal-driven cron loops currently keep firing even after the task is done. This creates noisy follow-up messages and requires manual disabling. The PR proposes a pre-run success check that can auto-disable the job and notify the channel when the goal is achieved. FeatFeature. It adds Who It ServesPrimary beneficiary: agent runtime operators and deployers running goal-driven scheduled tasks. Secondary beneficiaries: Discord users and maintainers, because completed loops should stop creating noise and require less manual cleanup. Rewritten PromptImplement Phase 1 of goal-driven usercron auto-disable behavior. Add optional usercron job fields:
Before dispatching a scheduled job, evaluate the configured command with a bounded timeout and optional working directory. Treat success as: exit code Cover config parsing, success/failure/match/timeout behavior, working directory handling, and TOML write-back. Avoid overlapping evaluations and avoid corrupting usercron state on write failure. Merge PitchThis is worth advancing because it turns usercron from a repeated reminder mechanism into a basic goal-driven agent loop. That is directly useful for “keep working until condition X is true” workflows. Risk profile is medium. The core concern is not the config shape; it is execution and persistence safety. Reviewers should focus on shell-command execution boundaries, timeout behavior, file write atomicity, concurrent scheduler ticks, and whether auto-editing user config is acceptable without stronger locking. Best-Practice ComparisonRelevant OpenClaw principles:
Relevant Hermes Agent principles:
Implementation OptionsOption 1: Conservative config-only gate Option 2: Balanced auto-disable with safe persistence Option 3: Ambitious durable goal-runner model Comparison Table
RecommendationAdvance the balanced option. The feature is directionally right, but merge discussion should center on hardening the state mutation path: atomic writes, locking, clear failure behavior, and observability. If those are not already solid in PR #817, split the work: merge config parsing and success evaluation first, then land TOML auto-disable once persistence safety is reviewed. |
Summary
Implements Phase 1 of the goal-driven agent loop ("escape room" mode) per ADR #816.
Adds
disable_on_successsupport to usercron[[jobs]]: before firing a cron job, run a shell command — if it exits 0 (and optional match string is found in stdout), the goal is achieved, the job auto-disables, and a success notification is posted.Changes
src/config.rsCronJobConfig:disable_on_success: Option<String>— shell command to evaluatedisable_on_success_match: Option<String>— required stdout substringdisable_on_success_timeout_secs: Option<u64>— command timeout (default 60s)disable_on_success_working_dir: Option<String>— working directorysrc/cron.rsevaluate_disable_on_success()— runs command with timeout, checks exit code + optional matchdisable_job_in_usercron()— writesenabled = falseback to usercron TOML filerun_schedulerloop: check runs beforefire_cronjob, posts ✅ notification on success, triggers hot-reloadTests (9 new)
Configuration Example
Related
cc @pahud