Add an on-demand cadence to app custom scheduled jobs - #6391
Merged
Conversation
Custom scheduled tasks could only recur on a clock, so "run only when I ask" had to be faked with a weekly job left disabled — which also made it un-runnable. `on-demand` is now a first-class cadence: the job stays enabled and manually triggerable, but no timer is ever armed for it. `resolveIntervalMs` now returns an explicit null no-interval sentinel instead of ending in `default: return DAY`, which silently rescheduled an unrecognized or on-demand cadence as a daily job. All three due/next-fire implementations handle the sentinel — `autonomousJobs/scheduler.js` (getDueJobs and computeNextJobRun) and `cosJobScheduler.js` (computeNextJobFireTime, the one that actually arms the timer). The cadence vocabulary moves to the new pure `lib/autonomousJobIntervals.js` so `createCosJobSchema` can validate against it. `interval` was a bare `z.string()`, so a typo'd cadence reached disk and silently became daily; it is now the cadence enum, and `updateCosJobSchema` inherits it.
atomantic
force-pushed
the
claim/issue-6375
branch
from
September 6, 2026 15:36
5c0f072 to
e435107
Compare
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
Adds
on-demandas a first-class cadence for CoS autonomous jobs (app custom scheduled tasks). The job stays enabled and manually triggerable viaPOST /api/cos/jobs/:id/trigger, but no timer is ever armed for it — previously the only way to get "run when I ask" was a weekly job left disabled, which also made it un-runnable.resolveIntervalMsreturns an explicitnullno-interval sentinel instead of ending indefault: return DAY. That fall-through silently rescheduled both the on-demand cadence and any typo'd cadence as a daily job.autonomousJobs/scheduler.js(getDueJobs,computeNextJobRun) andcosJobScheduler.js(computeNextJobFireTime— the one that actually arms the timer, which now cancels rather than scheduling). Arithmetic onnullwould otherwise read as "due on every sweep" /Invalid Date.createCosJobSchema.intervalis now the cadence enum rather than a barez.string(), closing the silent-daily typo hole at the boundary;updateCosJobSchemainherits it via.partial().intervalMsstays optional and is never back-filled for on-demand.server/lib/autonomousJobIntervals.jsowns the vocabulary (INTERVAL_OPTIONS,JOB_INTERVAL_VALUES,resolveIntervalMs,isOnDemandJob) so the Zod boundary can validate against the same list without a lib → services import.services/autonomousJobs/constants.jsre-exports it, so existing deep imports are unchanged. It is deliberately import-free (time units declared locally) — afileUtilsimport there is a hard failure in the many route suites that mockfileUtilswith an exhaustive factory.ScheduleFieldsoffers the cadence and hides the time-of-day input when it is selected; the card rendersNext: On demandand never shows the Due badge for such a job.scripts-state.jsonmigration now maps anon-demandscript to the real cadence (keeping its ownenabledflag) instead of a disabled daily job, and derives its valid-interval set from the shared vocabulary.Existing recurring cadences,
intervalMsvalues, next-due rendering, andjob:spawnedbookkeeping are unchanged. No on-disk format change, so no migration; autonomous jobs are not federated, so no schema-version bump.Import budget
server/lib/importScoping.test.js's tree-wide ceiling was raised 89,500 → 89,900 (actual 89,442 → 89,680), documented inline. The new module is a zero-import leaf reached by 194 suites throughcosValidation.js— the floor cost of sharing one constant list with the Zod boundary, with nothing to narrow or defer. The alternative is re-declaring the cadence list at the boundary, which is the drift this issue exists to close.Test plan
cd server && npm test— 2,011 files pass (one pre-existing timeout flake inroutes/settings.secretsStrip.test.jsunder full-suite load; green in isolation).cd client && npm test— 869 files pass (two pre-existing flakes under load; green in isolation).npm run lintclean.server/lib/autonomousJobIntervals.test.js— the sentinel is neitherDAYnorNaN, no fall-through for an unknown cadence,isOnDemandJobprecedence (cron-mode wins), and a server↔client lockstep guard overJOB_INTERVAL_OPTIONS(verified to fail when a label is drifted on one side only).server/services/autonomousJobs.test.js— an on-demand job is never due when never run and after a week elapsed;computeNextJobRunreturnsnull; create/update store the sentinel and drop a pinned time-of-day; every cadence pluscustomstill round-trips.server/services/cosJobScheduler.test.js—registerSingleJobSchedulearms no timer and cancels a stale one, including for aweekdaysOnlyon-demand job that would otherwise fall into the synthesized daily cron; a recurring job still arms.server/lib/cosValidation.test.js— every legal cadence accepted, unknown rejected on create and update, on-demand accepted withoutintervalMs.client/src/components/cos/JobCard.test.jsx— selecting the cadence hides the time input,Next: On demandrenders with noNaN/Invalid Date, and the Due badge stays off.Closes #6375