Files
Catubba b2d15a54d1 fix(scheduler): only report a missed run when Joulenap was really down
The startup check treated "a schedule slot came round and no run happened" as
proof of downtime. It is equally true when the schedule was changed since, when
a route was disabled and re-enabled, and when the kill-switch was off -- so
setting a route to run earlier in the day produced "A scheduled route did not
run because Joulenap was offline when it was due" about a process that had been
running the whole time, and re-sent it on every restart until that route next
ran. Reproduced on real hardware: a schedule changed at 10:21 alerted about a
02:00 slot that never existed, on a container with an uptime spanning both.

Nothing recorded that the app was alive, so nothing could tell the difference.
It does now: a file under the data dir is touched every minute, and its mtime is
the whole payload -- no table and no migration. A slot is reported only if it
fell after the app was last known to be running; unknown liveness (first boot,
an unwritable data dir) reports nothing, because inventing downtime is the
failure being fixed.

The stamp is read before the heartbeat starts overwriting it, and touched again
on clean shutdown so a graceful restart does not leave a minute looking like an
outage. The heartbeat is armed outside rearm(): it records that the app is up,
which is just as true with the kill-switch off, and one that stopped there would
make the next restart invent downtime. The notification still shows the real
last run -- only the decision to send it moved.
2026-08-07 12:17:27 +02:00
..
2026-08-05 16:20:41 +02:00