Go to file
2026-11-10 19:11:07 +01:00
.github/workflows Bump flutter 3.47.9, node 26.13.0, python dev pins, web deps to latest 2026-11-01 17:54:17 +01:00
contracts workout-poke: instant phone credit over LAN (signed POST :8773) and Firebase stream 2026-10-09 19:05:51 +02:00
docs docs: move android_ui TODO to testsAndMisc 2026-11-01 20:52:37 +01:00
screen_locker Reapply "Merge branch 'worktree-agent-aa94bbfa685da1112'" 2026-11-10 18:36:47 +01:00
scripts Shard ci-precommit into four gate steps 2026-11-10 17:45:58 +01:00
stronglift_replacement Bump astro 7.4.8 and gradle 9.9.1 2026-11-10 19:11:07 +01:00
web Bump astro 7.4.8 and gradle 9.9.1 2026-11-10 19:11:07 +01:00
.fvmrc Bump flutter 3.47.9, node 26.13.0, python dev pins, web deps to latest 2026-11-01 17:54:17 +01:00
.gitignore web: migrate status page from React to Astro 2026-11-01 20:12:46 +01:00
.mcp.json Add read-only MCP server exposing workout/lock status 2026-07-10 21:28:09 +02:00
.phone-deploy-flavor Workout screen never moves when a rest starts; a sandbox flavor to prove it on the phone 2026-09-20 18:55:08 +02:00
.pre-commit-config.yaml Shard ci-precommit into four gate steps 2026-11-10 17:45:58 +01:00
AGENTS.md late credits: a credit at/after tonight's shutdown sets it to min(23:00, its own time + reward); grace floor removed (also on sick days) 2026-10-17 18:14:04 +02:00
arm.sh shutdown bonus: apply the moment a gate writes its ledger (earner-bonus.path) 2026-10-06 20:42:13 +02:00
CLAUDE.md Adopt crdt-sync v0.9.0's bundled sync_log signature 2026-08-21 14:28:14 +02:00
early-bird-workout-check.timer The early-bird window is the wake-alarm carrot, not a clock 2026-09-20 08:39:44 +02:00
earner-bonus.service earner-bonus: add the new modules, unit and tests missed by 7851d59 2026-10-06 20:44:01 +02:00
install_autostart.sh chore: set up as standalone repo 2026-05-28 07:43:06 +02:00
install_systemd.sh Fix gate: file caps, silent catch, pin bump 2026-11-10 17:31:18 +01:00
install.sh sick days: one shared sickdays pool, 20:00 shutdown floor, one-tap lock button 2026-10-16 13:30:39 +02:00
LICENSE chore: add MIT LICENSE 2026-10-11 18:10:46 +02:00
pyproject.toml Pin gatelock v0.12.0 2026-11-07 08:31:14 +01:00
README.md Reapply "Merge branch 'worktree-agent-aa94bbfa685da1112'" 2026-11-10 18:36:47 +01:00
remove_autostart.sh chore: set up as standalone repo 2026-05-28 07:43:06 +02:00
remove_systemd.sh Wire runnerup-retire units into installer 2026-11-10 16:01:37 +01:00
requirements.txt Fix gate: file caps, silent catch, pin bump 2026-11-10 17:31:18 +01:00
run.sh Reconcile the venv on launch, so a pin bump cannot sit unnoticed 2026-08-23 06:56:26 +02:00
runnerup-retire.service Wire runnerup-retire units into installer 2026-11-10 16:01:37 +01:00
runnerup-retire.timer Wire runnerup-retire units into installer 2026-11-10 16:01:37 +01:00
runnerup-watch.service runnerup-watch: credit a RunnerUp run the moment its TCX upload finishes; lock the workout-log write 2026-10-07 20:36:38 +02:00
screen-locker-web.service Serve the lock decision, its history and its health at 127.0.0.1:8770 2026-08-29 22:50:19 +02:00
workout-locker.service Close the four gaps that let a decided lock never reach the screen 2026-08-30 15:24:45 +02:00
workout-locker.timer Close the four gaps that let a decided lock never reach the screen 2026-08-30 15:24:45 +02:00
workout-poke.service workout poke moves to tcp/8774: 8773 is book-guard's desktop wrapper, whose window the poke server had been answering since 2026-10-09 (phone app rebuilt, selftest default, contract doc) 2026-10-10 09:38:21 +02:00
workout-sync.service Re-point paths for the ~ reorganisation (home-tidy) 2026-09-11 18:46:28 +02:00
workout-sync.timer Let the PC see phone workouts again, and stop losing progression on reinstall 2026-08-10 14:39:02 +02:00

screen-locker

Tkinter/systemd screen locker with workout tracking, shared sick days, and wake-alarm integration. It enforces a workout cadence by locking the screen until a verified (RunnerUp, StrongLifts) or budgeted manual workout is logged.

Sick days

A sick day is a date in the fleet-wide sickdays pool, nothing else: no budget, countdown, justification or debt. The lock screen's I'm sick button (retry screen, no-phone screen and the Tue-Thu prompt) marks today with one tap and unlocks; sickdays add or the phone do the same. On a sick day the workout lock stands down, wake-alarm's workday penalty is void, and shutdown is 20:00 plus the gaming pay of everything credited that day, capped at 23:00 (earned_time.sick_shutdown_from). A day marked after its daily reset is raised to it by the next live pass (_sick_floor), and every later credit that day raises it again.

A credit landing at or after tonight's shutdown is never lost: tonight becomes min(23:00, the credit's own time + its reward) (_late_credit). install.sh migrates the old sick_history.json dates into the pool once (scripts/migrate_sick_history.py).

A sick day may keep lockers (sickdays add --keep workout). A kept workout lock behaves exactly as on a normal day (lock, quota, relaxed-day penalty on that date); only the 20:00 shutdown floor still applies, since it belongs to the day, not to a locker. The one-tap button keeps nothing. The missed-alarm penalty is void on its ring day only if alarm is off, and on its penalty day only if workout is off. All of it goes through _sick.stands_down_workout.

Shutdown time

Every day starts at 19:00 from 2026-10-01, 20:00 before it (base_minutes() in screen_locker/_shutdown_base.py) and bonuses push it later, capped at 23:00: the first counted workout +2h, each further one +1h, an accepted LeetCode submission that day +1h (flat, read from leetcode-guard's ledger, once per day), and a credited paper-book reading session of 20+ pages / 20+ minutes +1h (flat, read from book-guard's ledger, once per day, keyed on when the reading ended). The config is re-derived from those sources on each new day, so nothing has to be "remembered" across reboots.

A RunnerUp run is credited from its TCX export, found in the WebDAV drop directory RunnerUp uploads to on this machine (~/data/cloud/RunnerUp/, plus processed/ once endurain-import has taken it) or, when a phone is adb-visible, in /sdcard/Documents/RunnerUp/. The 15-minute workout-sync.timer scans both, so a run finished after 20:00 pushes the hour later without the phone being plugged in; the per-minute shutdown check then lifts the night lockdown on its own once the hour is past "now" (digital-wellbeing's check script).

Runs and walks are credited by ONE rule, whatever the TCX sport or the _Walking file-name suffix (RunnerUp's files and the workout app's alike): a day's activities are summed, and the day counts once their distance reaches 3.5 km (5% GPS tolerance) OR their moving time reaches the shared duration bar (30 min minus the hidden leeway). Both are summed over the track cut into windows of at least 10 s, each judged by its average speed: faster than 25 km/h (a vehicle or a GPS jump) counts neither time nor distance; moving time needs 0.5 m/s or more (1 m/s across a signal dropout longer than 30 s, so a pause earns nothing but a run through a dropout keeps its time). Files without trackpoints earn nothing (the lap totals are never used). The reward is the usual one workout credit per day (runnerup_verified:<date>); the log's activity field is display only. Code: screen_locker/_walk_tcx.py.

Retiring RunnerUp. A week after the PC first credits a run recorded by the workout app (TCX author kuhy workout_app), runnerup-retire.timer (daily, retries until the phone is attached) backs /sdcard/Documents/RunnerUp up to ~/data/cloud/RunnerUp/runnerup_phone_backup_<date>/ and uninstalls org.runnerup.free, once the app's .workout_app_import.json matches processed/; no app run, no uninstall. python3 -m screen_locker.runnerup_retire --status / --dry-run change nothing.

MCP server (Claude Code integration)

screen-locker exposes a read-only MCP server (screen_locker._mcp) so Claude Code and its subagents can query workout compliance and the lock-decision state through typed tools — without shelling out to the screen-locker-status CLI or opening the Tk window.

  • Read-only tools: get_status (full status snapshot), get_summary (one-line i3blocks summary + ok/warn/lock state word), explain_lock (why the screen is / isn't locked right now), get_flags (the individual boolean lock-decision predicates for today).
  • There are no write/action tools, by design. Nothing here logs a workout or mutates state — workouts are logged only from RunnerUp-verified TCX data, never from a caller's claim. No tool exposes the sync token or any HMAC key.

The mcp SDK is an optional dependency (pip install -e '.[mcp]'), kept out of the CLI/systemd system-python path. One-time setup of the dedicated venv that Claude Code spawns:

./scripts/setup_mcp.sh

Registration lives in the checked-in .mcp.json (project scope). Restart Claude Code in this repo and approve the project MCP-server prompt for it to load. Verify with claude mcp list.