Keeps CI green by closing the local↔CI gap that caused this session's
red runs: the installed pre-commit hook only checks staged files and the
dev env already has every dep, while CI runs `pre-commit run --all-files`
and installs a fresh env from requirements.txt.
- scripts/check_requirements_sync.py: static hook (pre-commit stage) that
AST-scans the package's third-party imports and fails if any isn't
declared in requirements.txt — the exact ModuleNotFoundError class that
reddened three repos. Fast, runs at commit time.
- scripts/ci_mirror.sh: pre-push gate that reproduces both CI workflows —
a requirements.txt-only venv (hash-gated, rebuilt only when
requirements.txt changes) running `pre-commit run --all-files` +
`pytest`. Replaces the old pytest-coverage pre-push hook (subsumed by
the clean-venv pytest). A red result blocks the push; `git push
--no-verify` overrides.
- .ci-mirror-venv/ gitignored; pre-push hook installed.
Verified: mirror runs green (747 passed, 100% cov); dep-sync check
catches a removed crdt-sync; hash-gate reuses the venv on repeat runs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018PMRRVVkeRyrSaaqueMZKG
Expose the screen-locker's compliance and lock-decision state as read-only
MCP tools (screen_locker._mcp) so Claude Code and its subagents can query
workout status without shelling out to the CLI or opening the Tk window.
- _mcp.py: FastMCP stdio server. Read-only tools get_status, get_summary,
explain_lock, get_flags. Import leaf over stdout-free status functions
(gather_status, format_summary_line, explain_lock_decision, predicates);
logs to stderr. NO write/action tools by design — 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.
- .mcp.json + scripts/setup_mcp.sh: registration via a dedicated
~/.venvs/screen-locker-mcp interpreter; mcp is an optional-dep.
- 100% coverage of the new module; verified end-to-end over stdio.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQGQj8Ne6TriBExv91nG8v
- Add sqflite_common_ffi + very_good_analysis; tighten analysis_options
- Add BackupService for JSON export/import of exercise state
- Add full test coverage: models, screens, services, widgets
- Add scripts/check_flutter_coverage.sh to enforce 100% line coverage
- Add docstrings to ExerciseState fields and storage service
- Minor fixes across screens, widgets, and sync/HTTP services
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VuiPt6GPWkxpLbJFrnfy8U
- Refactor RunnerUp verification: extract RunnerUpDbMixin (_runnerup_db.py),
split _scan_and_fill_week_runnerup into a helper _try_fill_runnerup_for_date
to keep cyclomatic complexity ≤10
- Generalise TCX lookup to any date in the ISO week (was today-only); all gap
days Mon→today auto-filled on every startup and 08:30 timer firing
- Add _adjust_shutdown_time_by(): +1h per extra workout beyond the 4-workout
minimum, capped at midnight (hour=24)
- Add _shutdown_base.py: daily reset of shutdown config to a stored base so
the bonus doesn't silently accumulate across days
- Add _extra_benefits.py: streak tracking, skip credits (earn (n-4) credits
for 5+ workout weeks), early-bird extension to 09:00 for eligible weeks
- Add --status mode (_status.py): non-locking CLI view showing per-day
breakdown (✓/✗), RunnerUp auto-scan, bonus status, shutdown time, streak,
skip credits, and early-bird status
- Hook carrot into _check_non_verify_exits: bonus applied whenever auto-fill
pushes weekly count above the minimum
- Pass all pre-commit hooks (ruff, mypy, pylint, bandit, shellcheck,
codespell, max-file-length); 508 tests at 100% branch coverage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017auyHmf2ZwQcDAwXaSo7KX
Add scripts/check_file_length.py and a max-file-length pre-commit hook that
fails any Python/shell file exceeding 400 lines. Extract UIWidgetsMixin and
UIFlowsRelaxedMixin from screen_lock.py and _ui_flows.py respectively, and
split 6 oversized test files into part2/part3/part4 siblings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>