From 88eda8dc5132344dd418c176b141a06d893e9169 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Sat, 18 Jul 2026 06:51:26 +0200 Subject: [PATCH] fix(night-lockdown): schedule a dynamic wake-floor trigger for any shutdown time The alarm floor (lockdown-entry + 8h) from the previous commit still relied on the fixed 05:00/05:15/05:30/06:00/07:00 unlock triggers to ever re-check it. Shutdown time is configurable (/etc/shutdown-schedule.conf, changed by the screen_locker sick-day feature) and can vary night to night, so a late enough lockdown (e.g. 23:30) pushes the floor past every remaining trigger (07:30 > 07:00) and the alarm would silently never fire that morning. Enter script now schedules a transient systemd-run --on-calendar one-shot timer at exactly lockdown-entry + MIN_WAKE_AFTER_LOCKDOWN_HOURS, computed from the real lockdown timestamp rather than assumed shutdown hours. It re-runs the idempotent, self-dedup'd unlock script, guaranteeing the alarm gets a correctly-timed chance regardless of what time lockdown started. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01S4YWZhDcEUMUi8cyw9AzTR --- ...ockdown-dynamic-wake-floor-2026-07-18.json | 42 +++++++++++++++++++ .../digital_wellbeing/setup_night_lockdown.sh | 23 +++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 docs/superpowers/evidence/night-lockdown-dynamic-wake-floor-2026-07-18.json diff --git a/docs/superpowers/evidence/night-lockdown-dynamic-wake-floor-2026-07-18.json b/docs/superpowers/evidence/night-lockdown-dynamic-wake-floor-2026-07-18.json new file mode 100644 index 00000000..0581841d --- /dev/null +++ b/docs/superpowers/evidence/night-lockdown-dynamic-wake-floor-2026-07-18.json @@ -0,0 +1,42 @@ +{ + "intent": "Make the morning wake-alarm floor (lockdown-entry + MIN_WAKE_AFTER_LOCKDOWN_HOURS) correct for ANY shutdown time, not just the shutdown hours the static 05:00/05:15/05:30/06:00/07:00 unlock triggers happen to cover.", + "scope": [ + "linux_configuration/scripts/periodic_background/digital_wellbeing/setup_night_lockdown.sh (enter-script generator section only)", + "Non-goal: did not remove or resize the existing staggered unlock triggers, which still handle console/GUI restore and remain a redundant fallback for the alarm in the common case" + ], + "changes": [ + "Identified a gap in the earlier same-day fix: shutdown time is dynamically configurable (/etc/shutdown-schedule.conf, changed by the screen_locker sick-day feature) and can, in principle, be any hour. If a lockdown started late enough that entry+8h fell after the last static trigger (07:00), the alarm would silently never fire that morning -- confirmed via isolated arithmetic test (23:30 lockdown -> 07:30 floor, past every static trigger).", + "Enter script now schedules a transient one-shot systemd-run --on-calendar timer, computed at lockdown-entry time, that fires at exactly lockdown_epoch + MIN_WAKE_AFTER_LOCKDOWN_HOURS and re-runs the (idempotent, self-dedup'd) unlock script -- guaranteeing the alarm gets a correctly-timed chance to fire regardless of what time lockdown actually started.", + "Added MIN_WAKE_AFTER_LOCKDOWN_HOURS to the enter-script's own config default-fallback block (previously only the unlock script had it)." + ], + "verification": [ + { + "command": "bash -n setup_night_lockdown.sh; shellcheck -s bash setup_night_lockdown.sh (plus extracted enter-script heredoc body)", + "result": "pass", + "evidence": "Only pre-existing SC1091 info notice; no new warnings." + }, + { + "command": "sudo systemd-run --unit=night-lockdown-wake-floor-test --collect --on-calendar=\"<+20s>\" logger ...; wait; journalctl -t ...", + "result": "pass", + "evidence": "Transient one-shot timer fired at the exact scheduled second and self-removed via --collect (systemctl status afterward: 'could not be found')." + }, + { + "command": "sudo setup_night_lockdown.sh setup; sudo DRY_RUN=1 night-lockdown-enter.sh", + "result": "pass", + "evidence": "Redeployed cleanly; DRY_RUN log now includes 'would schedule a one-shot wake-floor trigger ~8h from now'; real STATE_FILE untouched (still UNLOCKED)." + }, + { + "command": "Isolated arithmetic check with a scratch STATE_DIR, simulating a 23:30 lockdown", + "result": "pass", + "evidence": "floor computed as 2026-07-19 07:30:00 -- correctly past the last static 07:00 trigger, demonstrating why the dynamic one-shot is necessary and that the math is correct." + } + ], + "risks": [ + "Real end-to-end behavior (does the scheduled one-shot actually re-run the unlock script and start the alarm at the exact floor moment during a genuine overnight cycle) is not yet confirmed live -- deferred to the next real lockdown cycle.", + "systemd-run --unit name is static (night-lockdown-wake-floor); a stale leftover transient unit with the same name from a crashed prior run could in theory cause this scheduling call to fail. It runs through the existing run() wrapper, which logs a WARN and continues rather than aborting lockdown, and the static triggers remain a fallback for shutdown hours they do cover." + ], + "rollback": [ + "git revert this commit and re-run 'sudo setup_night_lockdown.sh setup' to redeploy the prior script version.", + "Manually remove any lingering night-lockdown-wake-floor.timer/.service with 'systemctl stop/reset-failed' if one is ever left active after a revert." + ] +} diff --git a/linux_configuration/scripts/periodic_background/digital_wellbeing/setup_night_lockdown.sh b/linux_configuration/scripts/periodic_background/digital_wellbeing/setup_night_lockdown.sh index d44392dc..8f140b4c 100755 --- a/linux_configuration/scripts/periodic_background/digital_wellbeing/setup_night_lockdown.sh +++ b/linux_configuration/scripts/periodic_background/digital_wellbeing/setup_night_lockdown.sh @@ -186,6 +186,7 @@ ALSA_CARDS="${ALSA_CARDS:-}" RGB_ENABLE="${RGB_ENABLE:-1}" MONITORED_USER_UNITS="${MONITORED_USER_UNITS:-}" MONITORED_PROCS="${MONITORED_PROCS:-}" +MIN_WAKE_AFTER_LOCKDOWN_HOURS="${MIN_WAKE_AFTER_LOCKDOWN_HOURS:-8}" CONSOLE_TTY="${CONSOLE_TTY:-/dev/tty1}" CONSOLE_BLANK_MODE="${CONSOLE_BLANK_MODE:-1}" DRY_RUN="${DRY_RUN:-}" @@ -303,7 +304,27 @@ done # (05:00 timer, dead-man, or manual) then correctly restores. if [[ "$DRY_RUN" != "1" ]]; then echo LOCKED >"$STATE_FILE" - date +%s >"$STATE_DIR/locked_at_epoch" + lockdown_epoch="$(date +%s)" + echo "$lockdown_epoch" >"$STATE_DIR/locked_at_epoch" + + # 5a. Schedule a one-shot wake-floor trigger for exactly + # lockdown_epoch + MIN_WAKE_AFTER_LOCKDOWN_HOURS. The staggered + # 05:00/05:15/05:30/06:00/07:00 unlock triggers are a fixed list sized + # for the usual shutdown hours (~21:00-23:00), but the shutdown hour + # itself is configurable (/etc/shutdown-schedule.conf) and changes + # dynamically (see the sick-day feature). If lockdown ever starts late + # enough that the floor lands after every remaining fixed trigger, the + # alarm would otherwise silently never fire that morning. This + # transient timer fires at the exact floor moment for THIS lockdown + # regardless of what time it actually started, re-running the + # (idempotent, self-dedup'd) unlock script, which is where the alarm + # is actually attempted. + floor_epoch=$((lockdown_epoch + MIN_WAKE_AFTER_LOCKDOWN_HOURS * 3600)) + floor_cal="$(date -d "@$floor_epoch" '+%Y-%m-%d %H:%M:%S')" + run systemd-run --unit=night-lockdown-wake-floor --collect \ + --on-calendar="$floor_cal" /usr/local/bin/night-lockdown-unlock.sh +else + logg "DRY_RUN: would schedule a one-shot wake-floor trigger ~${MIN_WAKE_AFTER_LOCKDOWN_HOURS}h from now" fi # 6a. Silence the console BEFORE stopping the GUI, so the teardown itself