A one-off `pip install "diet_guard @ git+..."` from a scratch directory was used during the original cutover; that's a non-editable snapshot that a later git push silently never reaches. Document the correct deployment convention (install.sh from a durable ~/diet-guard clone) so this doesn't drift again.
4.5 KiB
CLAUDE.md — diet_guard
What this does
A log-to-unlock gate: every ~30 minutes, diet-guard-gate.timer runs
diet-guard-gate.service, which checks whether a meal slot (08:00, 12:00,
16:00, 20:00) has elapsed without a logged meal. If so, it opens a fullscreen
Tk window that blocks the desktop until the user logs what they ate (with
autocomplete from a local food-name "bank", optionally seeded from Open Food
Facts via requests). It also tracks a daily calorie/macro budget, sealed at
init time and tamper-resistant via chattr +i.
See docs/design.md for the original feature spec (meal-slot timing logic,
the Tue/Wed/Thu "filled most of the day" catch-up rule, multi-item meals).
Scheduling
diet-guard-gate.timer — wall-clock OnCalendar=*-*-* *:00/30:00,
Persistent=true. Deliberately wall-clock rather than boot-relative: an
earlier boot-relative timer interacted badly with fullscreen games grabbing
keyboard/mouse input around the same point in their session, so this is
pinned to the clock instead of "N minutes after boot/login."
diet-guard-gate.service is Type=oneshot, fires every tick, and exits 0
immediately if no lock is due — cheap enough to run that often. It needs
DISPLAY/XAUTHORITY because it opens a Tk window; see the inline comments
in the unit file for why XAUTHORITY is pinned explicitly (a Persistent=true
catch-up run at session start can beat the display manager writing
~/.Xauthority) and why a real fix lives in Python (wait_for_display())
rather than in the unit file.
Production dependency installation — read this before adding any dependency
diet-guard-gate.service runs /usr/bin/python directly — not a venv.
Any new non-stdlib dependency (this package itself, gatelock, requests,
anything added later) must be installed into system Python's user
site-packages, the same place python-kasa already lives:
/usr/bin/python3 -m pip install --user --break-system-packages -e .
install.sh already does this. If you add a dependency and only install it
into a dev venv, the production service will silently fail with
ModuleNotFoundError on its next tick — this exact gap caused a 3-day
diet_guard production outage (2026-06-19 to 2026-06-22) when gatelock was
added but only pip install-ed into .venv. Always verify against
/usr/bin/python3 -c "import <new_dep>", not just the dev venv, before
considering a dependency change done.
Always run install.sh (or pip install -e) from a durable clone, not a
scratch directory. install.sh does pip install -e "$REPO_DIR" —
editable, so a later git pull in that same clone updates the running
production code with no reinstall needed. The clone must live somewhere
permanent (this repo's convention: ~/diet-guard, mirroring ~/screen-locker
for the screen-locker package) — if you pip install -e from /tmp/... or
run pip install "diet_guard @ git+https://..." as a one-off, you get a
non-editable snapshot frozen at that commit, and the next git push here
silently does not reach the running service.
Operational gotchas
- The budget file is sealed immutable.
~/.local/share/diet_guard/.budgetgetschattr +iafterinit(seeinstall.shstep 5). This is the actual tamper-resistance mechanism — the budget can't be casually edited to "make room" once locked. To intentionally change it:sudo chattr -ithe file, re-runpython -m diet_guard init, then re-lock. - Biometrics are used once and discarded.
initasks for biometrics to compute the daily budget, then the only persisted output is the computed budget number — never the biometrics themselves. - State lives entirely under
~/.local/share/diet_guard/— no cross-repo file coupling (unlike wake_alarm, which reads~/screen-locker/screen_locker/workout_log.json). Safe to reason about in isolation.
Commands
- Run tests:
python -m pytest diet_guard/tests/ --cov=diet_guard --cov-branch --cov-fail-under=100 - Lint:
pre-commit run --all-files - Test the lock manually (safe, closeable):
python -m diet_guard gate --demo - Install for production:
bash install.sh
Do NOT
- Don't relax the meal-slot/macro logic without re-reading
docs/design.md— the Tue/Wed/Thu catch-up rule and multi-item meal summing are deliberate, not accidental complexity. - Don't add a dependency without doing the production install-path check above.
- Don't remove the
chattr +istep frominstall.sh— it's the actual enforcement mechanism, not a formality.