mirror of
https://github.com/kuhyx/wake-alarm.git
synced 2026-11-10 18:07:05 +01:00
On 2026-09-19 the phone was powered off after the bathroom tag and nothing on the PC noticed: every finished session was published and nothing read it. Now wake-alarm distils the phone's morning into an HMAC-signed ~/.local/state/wake_alarm/morning_session.json (wake-alarm-session.timer, 07:00-11:00 every 2 min) that screen-locker and leetcode-guard read to defer their locks -- provisionally until min(start + 90 min, 09:30) while the session is live, until 11:00 once it completed inside that cutoff, and not at all for a session finished later (the phone switched on at noon completes the legs then; that must un-lock nothing). The phone publishes the session at start as well as at finish (current_session, cleared with the finish stamp), so the PC can tell "started, not finished" from "never started". A required 1-5 energy tap joins the weigh-in and ships as latest_energy with a PC reader mirroring _weight (the shared half is _field_reader). Also fixes the vmbox harness, red since the 2026-09-11 home-tidy: the guest tree lives at ~/wake-alarm, and the INSTALL-OK grep matched vm's echo of the command line. Takes Flutter 3.47.5. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tp54ewdXRLaimUuT8LgYyn
108 lines
3.4 KiB
Bash
Executable File
108 lines
3.4 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# ============================================================================
|
|
# wake_alarm vmbox harness — the cases that run INSIDE the guest.
|
|
#
|
|
# Kept as its own file rather than inlined into `vm run` for two reasons: the
|
|
# vmbox runner wraps its argument in `sh -c` and chokes on a multi-line
|
|
# command, and a real file gets shellcheck like every other script here.
|
|
#
|
|
# Each case prints "PASS <name>" or "FAIL <name>: <why>" and the script exits
|
|
# non-zero if any failed, so the host adjudicates on an exit code rather than
|
|
# by reading prose.
|
|
# ============================================================================
|
|
|
|
set -uo pipefail
|
|
|
|
readonly PY=/usr/bin/python3
|
|
FAILURES=0
|
|
|
|
pass() {
|
|
echo "PASS $1"
|
|
}
|
|
|
|
fail() {
|
|
echo "FAIL $1: $2"
|
|
FAILURES=$((FAILURES + 1))
|
|
}
|
|
|
|
# --- Case: the package installs and imports from SYSTEM python ---------------
|
|
# The units run /usr/bin/python directly, never a venv, so this is the check
|
|
# that actually matters (CLAUDE.md).
|
|
case_imports() {
|
|
local name="imports-from-system-python"
|
|
if "$PY" -c "import wake_alarm, gatelock" 2>/dev/null; then
|
|
pass "$name"
|
|
else
|
|
fail "$name" "wake_alarm or gatelock missing from /usr/bin/python3"
|
|
fi
|
|
}
|
|
|
|
# --- Case: the trigger timer is actually enabled -----------------------------
|
|
# install.sh writing the unit file is not the same as systemd running it, and
|
|
# a timer that is present but disabled fails silently every morning.
|
|
case_units() {
|
|
local timer state
|
|
for timer in wake-alarm-trigger.timer wake-alarm-session.timer; do
|
|
state="$(systemctl --user is-enabled "$timer" 2>&1)"
|
|
if [[ "$state" == "enabled" ]]; then
|
|
pass "${timer%.timer}-enabled"
|
|
else
|
|
fail "${timer%.timer}-enabled" "expected enabled, got '$state'"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# --- Case: the session refresh runs without a backend ------------------------
|
|
# The guest has no Firebase config, so --refresh must degrade to "no verified
|
|
# morning session" (exit 1) rather than crash: that is what an unconfigured
|
|
# machine's timer does every two minutes.
|
|
case_session_refresh() {
|
|
local name="session-refresh-degrades" output
|
|
# Exit 1 is the expected verdict here ("nothing verifiable"), so capture
|
|
# first: piped straight into grep, pipefail would fail the case on it.
|
|
output="$("$PY" -m wake_alarm._session --refresh 2>/dev/null || true)"
|
|
if grep -q "no verified" <<<"$output"; then
|
|
pass "$name"
|
|
else
|
|
fail "$name" "--refresh did not degrade cleanly"
|
|
fi
|
|
}
|
|
|
|
# --- Case: the trigger decides without crashing ------------------------------
|
|
case_trigger_dry_run() {
|
|
local name="trigger-dry-run"
|
|
if "$PY" -m wake_alarm._trigger --dry-run 2>/dev/null | grep -q "firing"; then
|
|
pass "$name"
|
|
else
|
|
fail "$name" "--dry-run produced no decision line"
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
case "${1:-all}" in
|
|
all)
|
|
case_imports
|
|
case_units
|
|
case_trigger_dry_run
|
|
case_session_refresh
|
|
;;
|
|
*)
|
|
echo "usage: $0 [all]" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
|
|
echo "---"
|
|
if [[ $FAILURES -gt 0 ]]; then
|
|
echo "$FAILURES case(s) failed"
|
|
exit 1
|
|
fi
|
|
# The host greps for this exact string. `vm run` reports the VM's verdict
|
|
# rather than this script's exit code, so an exit status alone would let
|
|
# the gate fail open.
|
|
echo "ALL-CASES-PASSED"
|
|
}
|
|
|
|
main "$@"
|