Skip to content

fix(heartbeat): emit valid + even cron expressions on Linux/WSL#2

Open
oratis wants to merge 1 commit into
mainfrom
fix/cron-approx-edge-cases-1
Open

fix(heartbeat): emit valid + even cron expressions on Linux/WSL#2
oratis wants to merge 1 commit into
mainfrom
fix/cron-approx-edge-cases-1

Conversation

@oratis
Copy link
Copy Markdown
Owner

@oratis oratis commented May 20, 2026

Closes #1.

What

secondsToCronApprox in src/heartbeat/install.ts is the Linux/WSL fallback for lisa heartbeat install --every <N> (macOS uses launchd StartInterval and never hits this path). The previous implementation produced cron lines that were either rejected by cron (*/90 * * * *, 0 */25 * * *) or run on an uneven schedule (0 */5 * * * fires at 00 05 10 15 20 then jumps back to 00 — only 4h between the last run and the next).

Change

Snap to safe field divisors:

Input Before After
--every 5h 0 */5 * * * (invalid wrap-around) 0 */4 * * *
--every 7h 0 */7 * * * (invalid wrap-around) 0 */6 * * *
--every 90m */90 * * * * (rejected by cron) 0 * * * *
--every 25h 0 */25 * * * (rejected by cron) 0 0 * * * (daily)
--every 8d (would loop forever in old loop) 0 0 * * 0 (weekly)

When a snap occurs, the installer's printed instructions now include a one-line note like:

Note: requested 5 hours doesn't map cleanly to a single cron expression
(cron can't span midnight evenly with hour-divisors other than 1,2,3,4,6,8,12).
Snapped to the nearest safe divisor: 4 hours.

Verification

Ran a behavior table over 15 input values — all outputs are now syntactically valid cron and produce even intervals across day boundaries. Output table is in the issue and the commit message.

Scope

  • One file: src/heartbeat/install.ts
  • Pure internal change to one function (secondsToCronApprox) and its one caller (Linux branch of installHeartbeat)
  • No public API change, no new deps
  • macOS launchd path untouched
  • npm run typecheck clean, npm run build clean

🤖 Generated with Claude Code

`secondsToCronApprox` produced cron lines that cron either rejects outright
(minute >59 / hour >23) or silently runs unevenly (`*/5` on hours fires at
00,05,10,15,20 then resets, leaving a 4h gap). macOS users are unaffected —
launchd uses `StartInterval` (seconds) and never hits this path.

Snap to safe field divisors and surface the rounding in the installer's
printed instructions so users see what their `--every` actually became:

  --every 5h  →  0 */4 * * *   (was: 0 */5 * * *,  invalid wrap-around)
  --every 7h  →  0 */6 * * *   (was: 0 */7 * * *,  invalid wrap-around)
  --every 90m →  0 * * * *     (was: */90 * * * *, rejected by cron)
  --every 25h →  0 0 * * *     (was: 0 */25 * * *, rejected by cron)
  --every 8d  →  0 0 * * 0     (weekly)

Pure internal change to `src/heartbeat/install.ts`. No public API change,
no new deps.

Closes #1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

heartbeat: secondsToCronApprox emits invalid or uneven cron lines on Linux/WSL

1 participant