Fix cron expression parser, token quota semantics, and dashboard wiring#39
Open
Chocksy wants to merge 3 commits intoRightNow-AI:mainfrom
Open
Fix cron expression parser, token quota semantics, and dashboard wiring#39Chocksy wants to merge 3 commits intoRightNow-AI:mainfrom
Chocksy wants to merge 3 commits intoRightNow-AI:mainfrom
Conversation
The scheduler's check_quota() would reject all agent turns when max_llm_tokens_per_hour was set to 0, unlike the metering engine which correctly skips the cost check when max_cost_per_hour_usd is 0.0. Add a guard to skip the token check when the limit is 0, making the behavior consistent with metering.rs.
compute_next_run for Cron schedules was a placeholder that returned now + 60 seconds regardless of the cron expression. This caused all cron jobs to fire every minute, burning through token quotas rapidly (5 jobs × 60/hour = 300 LLM calls/hour). Added the `cron` crate (0.15) and implemented proper 5-field to 7-field conversion for standard cron expressions. Jobs now fire at their correct scheduled times (e.g., "0 9 * * 1-5" fires at 9 AM UTC weekdays only).
The scheduler page was calling /api/schedules (empty KV store) instead of /api/cron/jobs (the real CronScheduler engine). Jobs created by agents via cron_create were invisible in the dashboard. Changed scheduler.js to use /api/cron/jobs for all CRUD operations. Normalized the nested response format (schedule.expr, action.message) to flat fields the UI expects. Replaced "Runs" column with "Next Run" which shows when each job will fire next. Added friendly day-of-week labels to describeCron() (e.g., "Weekdays at 5:00 AM").
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three bugs that remain unfixed in v0.1.4 (confirmed by independent code review against both tags):
1. Cron expression parser is a placeholder — fires every 60 seconds
compute_next_run()incron.rsreturnsUtc::now() + Duration::seconds(60)for ALLCronSchedule::Cronvariants. The cron expression (e.g.0 9 * * 1-5) is completely ignored.Impact: 5 cron jobs × every 60 seconds = 300 LLM calls/hour. In our deployment, this burned $12.48 in ~4 hours before we caught it. Each call accumulates context (up to 61K prompt tokens per call) making it progressively more expensive.
Fix: Added the
croncrate (v0.15) and implemented real parsing with 5-field → 7-field conversion:2. Token quota treats 0 as "deny all" instead of unlimited
check_quota()inscheduler.rsdoes:When
max_llm_tokens_per_hour = 0, any usage > 0 triggers quota exceeded. This is inconsistent with the cost quota fields which explicitly document0.0 = unlimited(seeResourceQuotainagent.rs).Fix: Added a
> 0guard matching the cost quota convention:3. Dashboard scheduler page calls
/api/schedules(KV store) instead of/api/cron/jobs(real engine)The scheduler page in
scheduler.jscalls/api/schedules, which reads/writes to the KV memory store — a passive data store that has no connection to theCronSchedulerengine. Jobs created through the dashboard UI are never registered with the actual scheduler and never fire.The real cron jobs live in
/api/cron/jobs, backed by theDashMap-basedCronSchedulerthat the kernel tick loop polls viadue_jobs().Fix: Rewired all CRUD operations to use
/api/cron/jobswith proper field normalization between the nested API response format and the flat UI model. Also updated the "Next Run" column to show relative future times.Testing
0 9 * * 6fires at Saturday 9:00 AM UTC, not every 60 seconds)Files Changed
crates/openfang-kernel/src/cron.rscrates/openfang-kernel/Cargo.tomlcron = "0.15"dependencycrates/openfang-kernel/src/scheduler.rs> 0guardcrates/openfang-api/static/js/pages/scheduler.js/api/cron/jobscrates/openfang-api/static/index_body.htmlBased on v0.1.4 — these are the 3 fixes from PR #17 that weren't included in the v0.1.3 rebuild.