mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 20:07:16 +01:00
A real 1h57m workout on 2026-08-24 synced to Firebase and the locker still locked the screen. The trigger was auth: screen_locker was the only app with no cached firebase_auth.json, so it fell back to a stale shared password and got HTTP 400 every run. The defect was what it did next — an unreadable primary source was reported as "no workout" rather than "could not check", which is the same conflation that took 2026-06-12. Recover the session by borrowing a live refresh token from a sibling app, track degraded sources as state instead of a log line, and say so on the lock screen: name the backend, the error, and how stale the data is, so a sync fault reads as a sync fault. Also fix two bugs found while finishing the recovery: restore_and_bonus.py banked `existing + bonus` with no idempotence guard while finish_2026_08_24.sh advertised "safe to re-run" — a second run silently granted the compensation hours again. Guard on a durable `shutdown_bonus_granted_for` list; the bonus map alone cannot answer the question, since a week's total mixes earned streak hours with corrections. _extra_benefits._save_state rebuilt state from four hardcoded keys, so the next weekly rollover would have dropped that guard and re-armed the double-grant. Merge onto loaded state instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EJmPLyT8NU4DzDJPV1KEX5
90 lines
2.9 KiB
Python
Executable File
90 lines
2.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Fail unless a counted workout is on disk for a given date.
|
|
|
|
A gate, not a report. The 2026-08-24 recovery revokes a sick day and re-arms
|
|
enforcement on the strength of "the workout was ingested" -- so that claim has
|
|
to be checked by code before the irreversible steps run, not asserted by
|
|
whoever is watching the output scroll past.
|
|
|
|
Exits non-zero when the date has no counted workout, which stops the calling
|
|
script before it revokes anything.
|
|
|
|
Reads ``log.json`` directly rather than importing ``screen_locker``, matching
|
|
``add_scheduled_skip.py``: these scripts must keep working even when the
|
|
package itself is mid-edit.
|
|
|
|
Usage:
|
|
python3 scripts/verify_workout_credited.py --date 2026-08-24
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
LOG_FILE = Path(__file__).resolve().parent.parent / "screen_locker" / "log.json"
|
|
|
|
# Mirrors ``_weekly_check.COUNTED_WORKOUT_TYPES``. Duplicated deliberately: a
|
|
# gate that imports the code it is checking cannot fail independently of it.
|
|
COUNTED_WORKOUT_TYPES = frozenset(
|
|
{
|
|
"phone_verified",
|
|
"runnerup_verified",
|
|
"pc_workout_verified",
|
|
"manual_workout",
|
|
}
|
|
)
|
|
|
|
|
|
def _report(message: str) -> None:
|
|
"""Write one line to stdout (matches the other scripts in this directory)."""
|
|
sys.stdout.write(f"{message}\n")
|
|
|
|
|
|
def main() -> int:
|
|
"""Report whether ``--date`` holds a counted workout; non-zero if not."""
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--date", required=True, help="YYYY-MM-DD to check")
|
|
parser.add_argument("--log-file", default=str(LOG_FILE))
|
|
args = parser.parse_args()
|
|
|
|
log_path = Path(args.log_file)
|
|
if not log_path.is_file():
|
|
_report(f"FAIL: no workout log at {log_path}")
|
|
return 1
|
|
|
|
try:
|
|
log = json.loads(log_path.read_text())
|
|
except (OSError, json.JSONDecodeError) as exc:
|
|
_report(f"FAIL: could not read {log_path}: {exc}")
|
|
return 1
|
|
|
|
entries = log.get(args.date) or []
|
|
counted = [
|
|
entry
|
|
for entry in entries
|
|
if (entry.get("workout_data") or {}).get("type") in COUNTED_WORKOUT_TYPES
|
|
]
|
|
|
|
if not counted:
|
|
_report(f"FAIL: no counted workout logged for {args.date}.")
|
|
_report(f" {len(entries)} entr(ies) exist for that date, none counted.")
|
|
_report(" Nothing to revoke a sick day against — stopping here so")
|
|
_report(" enforcement is not re-armed against a log that cannot see")
|
|
_report(" the workout you actually did.")
|
|
return 1
|
|
|
|
for entry in counted:
|
|
data = entry.get("workout_data") or {}
|
|
_report(
|
|
f"OK: {args.date} has {data.get('type')} "
|
|
f"(id={entry.get('workout_id')}, source={data.get('source', 'n/a')})"
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|