Scheduled task catch-up skips hourly and minute jobs after any run in the last 24 hours
Summary
runMissedJobs() uses a fixed 24-hour recent-run window for every schedule type. That is correct-ish for daily jobs, but it means hourly and minute-based jobs can miss due executions after the browser/extension has been asleep or restarted.
Evidence
In packages/browseros-agent/apps/app/entrypoints/background/scheduledJobRuns.ts, the missed-job loop computes:
const cutoff = now - TWENTY_FOUR_HOURS_MS
...
const hasRecentRun = runs.some(
(r) => r.jobId === job.id && new Date(r.startedAt).getTime() > cutoff,
)
if (hasRecentRun) continueOnly after that does it look at scheduleType / scheduleInterval for hourly and minute jobs. So a job scheduled every 5 minutes with a run from an hour ago is considered recently run and skipped, even though many intervals were missed.
createAlarmFromJob() also recreates interval alarms from "now" when missing:
if (job.scheduleType === 'minutes' && job.scheduleInterval) {
time = {
delayInMinutes: job.scheduleInterval,
periodInMinutes: job.scheduleInterval,
}
}That makes the startup catch-up path important: if the alarm was missing while the app was down, the next run is delayed another full interval and the missed due run is not recorded.
Repro sketch
- Create a scheduled task with
scheduleType: 'minutes'andscheduleInterval: 5. - Let it run once.
- Keep BrowserOS/extension inactive long enough to miss at least one interval, for example 30-60 minutes.
- Start BrowserOS again so
runMissedJobs()runs. - The task is skipped because it has a run in the last 24 hours, even though it is overdue by its configured interval.
Expected
Missed-job detection should use the job's actual cadence:
- daily: compare against today's scheduled time / last daily window
- hourly/minutes: compare
lastRunAtor latest run time againstscheduleInterval
Actual
Any run in the previous 24 hours suppresses catch-up for all schedule types.
Source: browseros-ai/BrowserOS