[Unit] Description=Re-check workout after early bird grace period expires (08:30 normal, 09:05 extended) # NO After=graphical-session.target here. This timer is WantedBy=timers.target, # and timers.target is ordered before basic.target before # graphical-session.target -- so that After= closes a dependency cycle. # systemd resolves such a cycle by DELETING this timer's job, which is exactly # what happened on 2026-08-04 ("Job early-bird-workout-check.timer/start deleted # to break ordering cycle"). The timer went disabled and the locker silently # stopped enforcing for days. The service it triggers keeps its own # After=graphical-session.target, which is where that ordering belongs. [Timer] # The early-bird window is the wake-alarm morning-session carrot since # 2026-09-20 (screen_locker/_early_bird.py): it ends at the signed # exempt_until -- 11:00 after a completed morning, min(start+90 min, 09:30) # after an aborted one, alarm+15 min when no session started. These runs land # just after the two boundaries the 5-minute tick could miss by up to five # minutes; every run is idempotent ("already logged", "carrot still in force"). OnCalendar=*-*-* 09:30:30 OnCalendar=*-*-* 11:00:30 Unit=workout-locker.service # Persistent=true: a machine asleep or off at a boundary would otherwise skip # the re-check entirely, leaving the early-bird marker banked and the day # unenforced. Both runs are idempotent (see above), so a catch-up run is safe. Persistent=true AccuracySec=1s RandomizedDelaySec=0 [Install] WantedBy=timers.target