steam-backlog-enforcer/steam_backlog_enforcer
Krzysztof kuhy Rudnicki 63321f16cc
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (push) Waiting to run
Tests / test (push) Waiting to run
Pay an hour of gaming budget for a LeetCode solve
The budget was binary: 8h with a counted workout, 6h without. There was no
base/bonus structure anywhere -- resolve_budget_seconds returned one of two
absolute config values, and the "2h bonus" was emergent arithmetic (8 - 6) that
appeared in no line of code. A second earner could not be bolted onto that
shape without coupling the two.

It is now a sum: 5h floor + 2h workout + 1h LeetCode, so a day is worth 5, 6, 7
or 8 hours. Note the deliberate tightening -- a workout-only day drops 8h to
7h, and a day that earns nothing drops 6h to 5h.

The two earners are read independently and added. That is what "must not
interfere with the workout" means in code: an unreadable LeetCode ledger leaves
a workout day at 7h, not 5h, and there is a test asserting exactly that.

LeetCode is read from leetcode-guard's ledger directly -- it answers even when
leetcode-guard is not running, which is most of the day -- and falls back to its
loopback endpoint. Credits are signature-checked; _leetcode_ledger reproduces
gatelock's canonicalisation in eight lines because gatelock lives in the user's
site-packages and this daemon runs as root, and a pinned vector fails loudly if
that ever drifts.

Failing to get an answer grants nothing AND raises an incident: desktop
notification, warning log, and a line in leetcode_bonus_incidents.log
(gitignored, so it never becomes an uncommitted change), rate-limited to once
per reason per day. A silently missing hour looks exactly like an unearned one.

resolve_budget_seconds is *renamed*, not wrapped: two functions would recreate
the second resolution seam this module's docstring warns about. rules_for calls
it once and carries the breakdown on PlaytimeRules, so /api/budget reports why
today is what it is without resolving again.

Known limitation, accepted: leetcode-guard only writes credits from its lock
window's poll loop, so a voluntary extra solve after the day is settled never
reaches the ledger.

Verified live against real services -- all four cases (5/6/7/8h), the endpoint
fallback, the fail-closed path with its notification, and the rate limit.
1782 tests, 100% branch coverage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fp6zWH9zW1AkGdDyKjrbi8
2026-08-30 15:22:18 +02:00
..
docs Auto-detect completed manual picks before enforcing the pick cap 2026-08-29 14:53:04 +02:00
main Make serve re-runnable, and cover it to 100% 2026-08-29 18:52:11 +02:00
tests Pay an hour of gaming budget for a LeetCode solve 2026-08-30 15:22:18 +02:00
__init__.py feat: steam 100 percent extension 2026-03-02 20:29:32 +01:00
_actions.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_allowed_games.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_attribution_labels.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_bonus_incident.py Pay an hour of gaming budget for a LeetCode solve 2026-08-30 15:22:18 +02:00
_budget_games.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_budget_log_tail.py Show the gaming budget in the web UI 2026-08-29 07:17:42 +02:00
_budget_resolve.py Pay an hour of gaming budget for a LeetCode solve 2026-08-30 15:22:18 +02:00
_budget_view.py Pay an hour of gaming budget for a LeetCode solve 2026-08-30 15:22:18 +02:00
_cdp.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_cmd_done_finalize.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_cmd_done.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_cmd_playtime.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_config_setup.py Show the gaming budget in the web UI 2026-08-29 07:17:42 +02:00
_controller_input.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
_counted_procs.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_desktop_env.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
_echo.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
_enforce_loop.py Auto-detect completed manual picks before enforcing the pick cap 2026-08-29 14:53:04 +02:00
_enforce_steps.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
_engagement_probes.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_engagement_types.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
_game_names.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
_hltb_cached.py Split _actions, _whitelist, _cmd_done and hltb under the cap 2026-08-24 14:55:57 +02:00
_hltb_comp_extract.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_hltb_confidence.py Split _actions, _whitelist, _cmd_done and hltb under the cap 2026-08-24 14:55:57 +02:00
_hltb_detail.py Bring the HLTB modules under the 250-line cap 2026-08-24 15:23:13 +02:00
_hltb_matching.py Bring the HLTB modules under the 250-line cap 2026-08-24 15:23:13 +02:00
_hltb_page_parse.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_hltb_search_api.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_hltb_search.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_hltb_types.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_hosts_protection.py Split store_blocker along its one-way call boundaries 2026-08-24 15:06:15 +02:00
_leetcode_bonus.py Pay an hour of gaming budget for a LeetCode solve 2026-08-30 15:22:18 +02:00
_leetcode_ledger.py Pay an hour of gaming budget for a LeetCode solve 2026-08-30 15:22:18 +02:00
_manual_pick_lifecycle.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_mcp_actions.py Auto-detect completed manual picks before enforcing the pick cap 2026-08-29 14:53:04 +02:00
_mcp_gaming_time.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_mcp_query.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_mcp_server.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_mcp.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
_owned_apps_cache.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_pacman.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_pick_completion.py Auto-detect completed manual picks before enforcing the pick cap 2026-08-29 14:53:04 +02:00
_playtime_block.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
_playtime_budget.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_playtime_cutoff.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_playtime_engagement.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_playtime_history_parse.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_playtime_history.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_playtime_kill.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_playtime_log.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_playtime_mountinfo.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_playtime_notify.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_playtime_procs.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_playtime_run.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_playtime_session.py Show the gaming budget in the web UI 2026-08-29 07:17:42 +02:00
_playtime_state.py Pay an hour of gaming budget for a LeetCode solve 2026-08-30 15:22:18 +02:00
_playtime.py Pay an hour of gaming budget for a LeetCode solve 2026-08-30 15:22:18 +02:00
_polls_reporting.py Split steam_api and _scanning_confidence under the cap 2026-08-24 14:25:43 +02:00
_protected_apps.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
_scanning_assign.py Auto-detect completed manual picks before enforcing the pick cap 2026-08-29 14:53:04 +02:00
_scanning_candidates.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
_scanning_confidence.py Split steam_api and _scanning_confidence under the cap 2026-08-24 14:25:43 +02:00
_scanning_tampering.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_screen_hold.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
_serve_guard.py Stop the web UI serving numbers the enforcer is not applying 2026-08-29 23:39:40 +02:00
_serve_stale.py Stop the web UI serving numbers the enforcer is not applying 2026-08-29 23:39:40 +02:00
_serve_startup.py Make serve re-runnable, and cover it to 100% 2026-08-29 18:52:11 +02:00
_snapshot.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_stats_display.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_stats_gathering.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
_stats_types.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
_stats.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_steam_api_client.py Split steam_api and _scanning_confidence under the cap 2026-08-24 14:25:43 +02:00
_steam_client.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
_steam_errors.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
_steam_launch.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
_steam_models.py Split steam_api and _scanning_confidence under the cap 2026-08-24 14:25:43 +02:00
_steam_process.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
_steam_restart_guard.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
_steam_state.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
_store_iptables.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_total_block_domains.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
_total_block_hosts.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_total_block_iptables.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_total_block_launchers.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
_total_block_purge.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_total_block.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_web_build.py Make serve re-runnable, and cover it to 100% 2026-08-29 18:52:11 +02:00
_web_dataset.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
_web_games.py Bring _web_dataset.py under the 250-line cap — cap campaign complete 2026-08-24 16:08:12 +02:00
_web_models.py Bring _web_dataset.py under the 250-line cap — cap campaign complete 2026-08-24 16:08:12 +02:00
_web_pace.py Bring _web_dataset.py under the 250-line cap — cap campaign complete 2026-08-24 16:08:12 +02:00
_web_server.py Stop the web UI serving numbers the enforcer is not applying 2026-08-29 23:39:40 +02:00
_whitelist_locking.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_whitelist.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_workout_budget.py Pay an hour of gaming budget for a LeetCode solve 2026-08-30 15:22:18 +02:00
_x_focus.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
_x_probe.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
config.py Pay an hour of gaming budget for a LeetCode solve 2026-08-30 15:22:18 +02:00
enforcer.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
game_install.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
game_uninstall.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
hltb.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
library_hider.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
protondb.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
scanning.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
steam_api.py Split steam_api and _scanning_confidence under the cap 2026-08-24 14:25:43 +02:00
store_blocker.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00