Commit Graph

45 Commits

Author SHA1 Message Date
45627f7075 Bump pytest-randomly to 5.1.0
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Waiting to run
Tests / test (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWYndZhdUrRGFEjBazQ
2026-11-09 10:24:25 +01:00
1af80a6525 Pin gatelock v0.12.0
Also bump coverage, pydantic, ruff for the freshness gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWZd3smuxDGmEgXkYP8
2026-11-07 08:40:21 +01:00
e3af1f571b Gate: stand down inside declared holds
No lock and no owed slot while holds.covers("diet") is true; slots
inside a hold are excused; an open lock lifts within 30 s of a hold
starting. Fail closed (normal rules) if holds is missing or raises.
Pin holds-v0.1.0; bump coverage 7.17.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWVSoczW2NHQd887FUm
2026-11-03 20:27:11 +01:00
b82ff7c040 Pin focus-project 0.3.0, sickdays 0.4.0
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Waiting to run
Tests / test (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Conflicting git-tag pins in the shared user site let the last install downgrade focus_project/sickdays (no harvest, no focus locker).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 10:03:02 +01:00
e75e2bc89a chore: bump deps to newest stable
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWXTUM9dpnCS2wtFkx4
2026-11-01 20:12:24 +01:00
84c78a0b1d deps: gradle 9.8.2, fl_chart 1.3.0, web 1.1.2, coverage 7.17.0, ruff 0.17.2 (newest stable)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017k9j5dbjenpfZj7dh2faPN
2026-10-23 18:49:15 +02:00
eec7856664 Pin gatelock-v0.11.0 (Claim.grab_held + held_grabs)
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Has been cancelled
Tests / test (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
repo-contract / repo contract (push) Has been cancelled
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dsh4Znwh2KnC7q6vbGpX1N
2026-10-19 11:47:24 +02:00
8eb72647d5 deps: gatelock 0.9.0 -> 0.10.0 (pin only; diet-guard does not opt in to night_lockdown)
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Has been cancelled
Tests / test (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
repo-contract / repo contract (push) Has been cancelled
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 18:47:36 +02:00
ee81788ed6 deps: sickdays 0.3.0
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Waiting to run
Tests / test (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAhbNszs5JKdtnD4uBi3xp
2026-10-17 13:52:40 +02:00
8b82faf8f9 Sick days: diet stands down only when the day does not keep it
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Waiting to run
Tests / test (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
due_slots asks sickdays.stands_down(LOCKER_DIET, day) instead of
is_sick_day, so a sick day that keeps diet is a normal day for the gate.
Bump sickdays pins to v0.2.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAhbNszs5JKdtnD4uBi3xp
2026-10-16 15:12:59 +02:00
6587258da9 Bump deps to newest stable (freshness gate)
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Waiting to run
Tests / test (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
flutter_local_notifications 22.3.2, idb_shim 2.10.1+1, pre-commit 4.7.0,
pydantic 2.14.1, pylint 4.1.3, ruff 0.17.1, gradle 9.8.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xfj1sxKH9VBPqpcAni8Jry
2026-10-16 14:12:57 +02:00
419ada929a gate: stand down on a sick day (sickdays 0.1.0)
due_slots() returns nothing on a sick day, like a free day: no lock, no debt;
logging still works. Unreadable pool reads as not sick (fail closed). Pins
sickdays-v0.1.0 and freedays-v0.4.0 (pyproject, requirements, pylint hook env).
Phone gap documented in docs/DOCS-sick-days.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xfj1sxKH9VBPqpcAni8Jry
2026-10-16 08:36:27 +02:00
f2606fe0ca deps: flutter 3.47.7, crdt-sync 0.9.2, freedays 0.3.1, pydantic 2.14.0, ruff 0.17.0
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Has been cancelled
Tests / test (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
repo-contract / repo contract (push) Has been cancelled
ruff 0.17 stabilised CPY001 (ignored: no per-file copyright headers here),
ISC004 and ICN001 (autofixed).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015npsYVB2PdtWkG7MPAi4tn
2026-10-10 20:27:52 +02:00
357bdb7e94 deps: mcp 2.3.0, pylint 4.1.2, url_launcher 6.3.3, github_device_auth 0.3.2
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Has been cancelled
Tests / test (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
repo-contract / repo contract (push) Has been cancelled
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-04 18:08:19 +02:00
908fbd2d1e gatelock 0.8.2 -> 0.9.0; mypy 2.4.0, pylint 4.1.1, ruff 0.16.10
gatelock 0.9.0 adds gatelock.morning_session (the shared reader of
wake-alarm's morning carrot, now used by screen-locker, leetcode-guard and
book-guard); nothing here changes behaviour. The tag moved the freshness
gate, so the pin moves with it, together with today's lint-tool releases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FBbkN4ZB4aUPJPzR2prRan
2026-10-02 14:19:54 +02:00
e761926df4 Body tab: weight/body-fat logs, BMI, ideal weight, goal-based kcal target, workouts + Health Connect steps, week planner
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Has been cancelled
Tests / test (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 09:03:03 +02:00
49b3ea4d57 Give every xdist worker its own Xvfb (pytest-xvfb)
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Waiting to run
Tests / test (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
The first -n auto Tests run segfaulted in Tk's focus_force(): keyboard
focus is global to an X display, and four workers were sharing the one
xvfb-run display, stealing each other's focus. Stressed locally (layout +
keyboard tests x40, -n 8): 5 of 2760 failed on a shared display (focus-ring
assertions), 0 of 4600 with pytest-xvfb's per-process display -- and faster.
The geometry xvfb-run used moves into pyproject; the workflow keeps the
Xvfb install guard and drops xvfb-run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188kZdx3bb56QM8JFUnbHuM
2026-09-26 18:24:41 +02:00
11c8be838d Take pylint 4.0.9 and ruff 0.16.9
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Waiting to run
Tests / test (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Both released upstream since the last bump; the dependency-freshness gate
(pre-commit hook and the --strict CI workflow) reports them behind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188kZdx3bb56QM8JFUnbHuM
2026-09-26 18:00:29 +02:00
2e06cda31b Take gatelock 0.8.2, coverage 7.16.1 and ruff 0.16.8
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 09:42:31 +02:00
3f6a9b0ebf Take gatelock 0.8.1: colour reads through LockConfig.palette
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Tests / test (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
LockConfig's colours moved into a nested LockPalette record; every
_COLORS.<colour> read follows. Nothing about the gate's behaviour changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHCbkJWXHjtTUaqvnHRAqA
2026-09-12 19:59:44 +02:00
b3aeb4f4b7 Re-point paths for the ~ reorganisation (home-tidy)
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (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
release-apk / release (APK) (push) Waiting to run
Also bumps Flutter 3.47.3, flutter_secure_storage 11.1.0, idb_shim 2.9.9,
mcp 2.2.0 and ruff 0.16.7 so the freshness gate lets this through.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwtowrowDXx6ccmWVPkCaF
2026-09-11 19:53:03 +02:00
058f806db7 Pin freedays v0.2.0
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Tests / test (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
v0.2.0 drops freedays's gatelock dependency (importing gatelock pulled in
tkinter, so the headless pool needed a GUI toolkit) and adds the sync
timer that makes a day marked on the phone actually reach this machine.

Mechanical, but not optional: the freshness gate compares the pinned tag
against the newest release, so leaving v0.1.0 here would turn this repo
red on its next commit.

Suite re-run against v0.2.0 before pinning it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 15:58:42 +02:00
b77145010c Stop asking for meals on a shared free day
Third app onto freedays (~/utils/freedays), the shared pool of
pre-declared days when every gate app stands down. due_slots() returns
nothing on a free day, so no slot is ever due, the lock never opens and
nothing is said about the missing meals.

Read from one local file, never the network, so this cannot hang the
systemd-fired gate -- and an unreadable pool reads as "not free", which
means the normal rules apply rather than the gate quietly switching
itself off. Tested explicitly: fail closed, not open.

The conftest redirect is load-bearing. The pool lives under
~/.local/share, outside every diet_guard path the suite already isolates,
so without it a real free day on this machine would fail every "the gate
is due" test here for a reason that looks nothing like the cause.

gatelock moves v0.7.2 -> v0.7.3, and the pylint hook's own pins (which
had drifted further, to gatelock v0.7.1 and crdt-sync v0.6.0) come with
it. Not cosmetic: pip refuses two different direct URLs for one
distribution, so any stale pin alongside freedays -- which requires
v0.7.3 -- is an unresolvable environment rather than a warning.

requirements.txt also picks up pylint 4.0.8, pytest-randomly 5.0.0 and
ruff 0.16.6, which the freshness gate flagged once this file was touched.
1043 tests pass at 100% coverage across the pytest-randomly major bump.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 15:22:38 +02:00
862a114909 Take coverage 7.16.0
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Tests / test (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
The freshness gate was already red on this before today's Python work touched
the repo -- it only surfaced now because the CI mirror runs the gate over the
whole tree, while the commit hook had no reason to look at requirements.txt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SfnJmxxsZ534AKk16rC2Gr
2026-08-29 11:36:42 +02:00
3c314ecf9a Bump pydantic to 2.13.5
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (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
release-apk / release (APK) (push) Waiting to run
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 20:16:14 +02:00
81eeb12c1e Latest stable dependencies, and the gate that keeps them there
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Python side: gatelock v0.7.1 -> v0.7.2, crdt-sync v0.9.0 -> v0.9.1 (both
re-cut so consumers stop resolving pre-upgrade constraints) and every `>=`
floor turned into an exact pin, five majors among them (mypy 1 -> 2.3.1,
pylint 3 -> 4.0.7, pytest 8 -> 9.1.1, pytest-cov 4 -> 7.1.0, ruff 0.8 ->
0.16.5).

app/: twenty pins, of which permission_handler 12.0.3 -> 13.0.1 and
workmanager 0.9.0+3 -> 0.10.9 are real major moves the lockfile had been
holding back, plus the flutter_secure_storage 11 chain (compileSdk 37 /
AGP 9.3.2 / Gradle 9.7.1). CI's Flutter pin was 3.44.8 against an .fvmrc of
3.47.2; both say 3.47.2 now, and the gate reads that pin.

`scripts/ci_mirror.sh` green end to end: the Python suite in a clean venv and
706 Flutter tests, pre-commit --all-files clean, gate exit 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRykabMwPgDzFHcARoqpaK
2026-08-28 15:23:13 +02:00
9752142c28 Re-pin crdt-sync to v0.9.0
Some checks failed
file-length / file length <= 250 lines (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Tests / test (3.10) (push) Has been cancelled
Tests / test (3.11) (push) Has been cancelled
Tests / test (3.12) (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
diet-guard does not call sync_log directly, so v0.9.0's bundled signature is
a no-op here; re-pinned to keep every consumer on one tag. 830 tests green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCucBhUkHBBHHbsY7bSdGV
2026-08-21 14:30:13 +02:00
044d7fd9fd Re-pin crdt-sync to v0.8.1
Some checks are pending
file-length / file length <= 250 lines (push) Waiting to run
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
v0.8.0's tag was already pushed when the timeout-forwarding tests landed, so
the covering tag is v0.8.1. No library behaviour change over v0.8.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCucBhUkHBBHHbsY7bSdGV
2026-08-21 14:04:54 +02:00
a7696c4143 Re-pin crdt-sync to v0.8.0 for connection pooling
Some checks are pending
file-length / file length <= 250 lines (push) Waiting to run
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
v0.8.0 pools HTTP connections in the shared library, which is worth ~95ms per
GitHub request and ~52ms per Realtime Database request on this machine.

This jumps two tags, not one: the pin was still v0.6.0, so it also picks up
v0.7.0's "let a mirror read survive the primary being down". Full suite
re-run after the bump rather than assuming only the pooling change landed --
830 passing at 100% branch coverage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCucBhUkHBBHHbsY7bSdGV
2026-08-21 13:55:52 +02:00
0173f4c56c Queue behind stronger lockers; bump gatelock to v0.7.1
Diet_guard was one of the two lockers missing gatelock's
wait_for_turn fix -- confirmed live on 2026-08-21, it held the global
grab for ~3 minutes against a newly-arriving, higher-ranked
screen_locker, which just retried and logged instead of making
progress. Now queues behind any stronger claim before arming.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018M5gKyYzxWG6QkCaj1wWfk
2026-08-21 13:06:19 +02:00
b621ccaed0 Use gatelock's shared button and WidgetGroup
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Phase 2 consumer migration. diet-guard was the donor for make_button, so it
is one of the two proof consumers the extraction is validated against.

- _gatelock_buttons.py is deleted; its make_button now lives in gatelock,
  where the variant API (text colour picked *from* the fill) and the
  <Return>/<KP_Enter> bindings serve all four gates instead of one. Call
  sites pass _COLORS explicitly, since a shared widget cannot read this
  app's module-level palette. Pixel-identical: BODY/LABEL were -16/-14 and
  LockConfig.font("body"/"label") yields the same negative (pixel) sizes.
- WidgetGroup now subclasses gatelock's, keeping the list[_W] constructor and
  re-narrowing `first` to the widget type. The specialized groups (EntryGroup
  over a shared StringVar, TextGroup, ListboxGroup, MacroGroup) are unchanged
  behaviour and stay -- gatelock has no equivalent for them.
- bind() keeps *replacing* semantics, inverted from gatelock's additive
  default and now documented + tested. _wire_events runs once per
  build_surface and again on rebuild, so an additive bind would stack a
  second <Return> handler on every surviving widget and log the meal twice.
- conftest swaps gatelock.widgets into the fake-tk module list in the same
  edit that removes _gatelock_buttons, since that list fails the whole suite
  if it drifts in either direction.

All three gatelock pins move together to v0.5.0 -- pyproject, requirements.txt
and the pylint hook's additional_dependencies. The config comment already
warned that a lagging hook pin lints against a gatelock without the API the
code calls; that is exactly what it did before this bump.

The "<Return> activates a button" test moves to gatelock with the button, so
it runs against real Tk rather than a fake widget that can only report the
binding it was handed.

Verified: 692 tests, 100% branch coverage against the *installed* gatelock
0.5.0 (not a PYTHONPATH shim); the gate builds under xvfb and <Return> and
<KP_Enter> both fire a real button's command, with the accent focus ring
(#B8862E, 2px) instead of Tk's invisible 1px black.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U4Rj3s7K2qzrLNgJqqPhno
2026-08-15 14:23:11 +02:00
e23f7ac3b4 Push under a persisted per-install uuid, not a fixed role id
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
diet_guard pushed as "pc" (Python), "phone" (Android) and "desktop" (web).
A role constant collides the moment a second machine takes the same role,
and after a reinstall the new install inherits the previous one's CRDT
identity -- the same reasoning home_inventory already records for choosing a
uuid. Both halves now mint a uuid once and persist it: the PC in
~/.local/share/diet_guard/.device_id, the app under SharedPreferences
'crdt.nodeId' (the key todo and home_inventory already use).

The old constant survives as the legacy id and is passed to sync_log /
syncLog, so devices/<legacy>/ is skipped as this device's own. Without that
every tick would re-download and re-merge this device's own pre-migration
log as though a peer had written it -- idempotent under LWW, but hundreds of
KB, 96 times a day. It drops to None once that path is reclaimed; a test
covers both states, including a negative control proving the skip is the
legacy id's doing.

All four documents move together (food_log, food_bank, food_bank_manual,
budget), and the hist:<date> fields riding on the budget record are
untouched -- merge_record unions field names, so they relay through
unchanged.

The resolver lives in its own module (_device.py / sync_device_id.dart)
because _sync_merge needs it for HLC stamping too and importing _sync there
would be a cycle. Dart resolves it asynchronously at startup, before
LogStorageService.init(); currentSyncDeviceId falls back to the compile-time
role constant until then, so a too-early Hlc stamp gets a valid id rather
than crashing.

conftest redirects the new id file in the same change that introduces it --
without that a test mints a uuid into the real state dir and this machine's
live sync identity is decided by whichever test ran first. Verified by
checking ~/.local/share/diet_guard/ before and after: untouched.

Verified live, not just against mocks: the sync timer ran mid-change and
migrated this machine to f8f46da6. All 358 food_log records and all 100
food_bank records are present under the new path (0 missing vs devices/pc/),
and the new log's HLCs carry only its own id -- confirming it did not
re-merge the old path as a peer.

Python: 687 tests, 100% branch coverage. App: 518 tests, analyzer clean.
Pins bumped to crdt-sync-v0.6.0 / crdt_sync_dart-v0.7.0 (pyproject,
requirements.txt, .pre-commit-config.yaml, pubspec), and the new library is
installed into /usr/bin/python's user site-packages so the production timer
does not ImportError on its next tick.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZH89WEqAjuJT8WZdqc7AP
2026-08-09 17:18:42 +02:00
4d803203ab Make Firebase the visible sync path, and GitHub the mirror it now is
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Four things about the sync settings read wrong on the phone, all of them
left over from when GitHub was the only backend:

- "Connect GitHub" sat on the primary path as a peer of "Connect Firebase",
  implying a choice. It is not one: GitHub is the cutover mirror. Its whole
  configuration -- device-flow OAuth, owner, repo, client id, PAT fallback --
  now lives under "Advanced (GitHub mirror)".
- "Test connection" builds a GitHubClient and calls canAccessRepo(). It has
  never said anything about Firebase, so it is now "Test GitHub connection",
  and its result messages name GitHub too.
- Once connected, the account still rendered as an editable email box beside
  an empty password box, which reads as "you still have to enter this" -- a
  connected device looked unconfigured. It now shows the account as text with
  a Disconnect button, and the form appears only when disconnected.

Also pins crdt-sync v0.5.1 (from v0.3.0), the shared-config release the
Firebase cutover needs; verified importable under /usr/bin/python3, which is
what diet-guard-gate.service actually runs.

Adds coverage for both Firebase states, which had none: the disconnected form
and the connected read-only display.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017FH32BgGQbQLDstBhR26Xa
2026-08-05 08:55:12 +02:00
fdce5a1031 Move the crdt-sync pin to v0.3.0
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
Your in-progress bump, committed as its own change so it is not buried in
anything of mine. All three declaration sites move together -- the manifest,
the lockfile-equivalent and pylint's hook env -- because a pin that lags the
others lints the code against a library it does not actually run with.

Verified in a clean venv that v0.3.0 still provides every symbol this repo
imports: GitHubSyncClient, GitHubSyncError, Log, merge_logs, Hlc and Record.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BsXnTHyuuCUZsqetTaajcF
2026-08-03 20:08:53 +02:00
58e6731705 Port the MCP server to mcp 2.x, and say what each tool is allowed to do
mcp 2.0.0 removed mcp.server.fastmcp; MCPServer is the same object under a
new name, so @mcp.tool() and mcp.run() are untouched and every tool keeps its
name, signature and returned shape. Verified against a clean 2.0.0 venv: all
four tools list, and the suite passes at 100% coverage, 609 tests.

Two things the port buys, neither of which existed in 1.x:

* Tool annotations, which matter more here than in the sibling repos because
  this server has a tool that writes. The three readers are marked read-only,
  idempotent and closed-world; log_meal is marked not-read-only but
  explicitly NOT destructive -- it appends and never rewrites or deletes --
  not idempotent, since a second confirmed call logs a second meal, and
  open-world, because it reaches Open Food Facts when macros are not given.
  A client can now tell the difference without reading the docstring.
* Structured output: 2.x derives an output schema from the return types and
  sends structured content alongside the text. No code change -- it falls out
  of the type hints that were already there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BsXnTHyuuCUZsqetTaajcF
2026-08-03 20:02:00 +02:00
df5916f616 Pin mcp below 2.0, which dropped the module _mcp.py imports
CI has been red since 2026-07-30 for a reason unrelated to any commit here:
mcp 2.0.0 removed mcp.server.fastmcp, which _mcp.py imports at module scope,
and the manifests asked for `mcp>=1.9` with no upper bound. Verified
directly -- 2.0.0 raises ModuleNotFoundError on that import, 1.29.0 provides
it. It failed pylint here rather than only the test job, because the repo
root now reaches pylint's import resolution.

Pinned `<2` in all three places the dependency is declared until the server
is ported to the 2.x API.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BsXnTHyuuCUZsqetTaajcF
2026-08-03 17:45:50 +02:00
4a5e350baf Fit the meal gate on 1366x768 with a full day logged, and stop the auto-scroll
gatelock 0.4.0 (pinned here) stops the viewport following the app's own
focus_set(): on 2026-08-03 that moved the screen locker's lock screen to
mid-content and back with nobody touching the keyboard, and this gate uses
the same viewport. Scrolling now happens only on the user's own keypress,
wheel or click.

That makes fitting the screen the gate's own responsibility rather than
something a scrollbar covers for, and with a day of meals logged this one did
not: 770px of content in a 739px pane. Trimmed the vertical rhythm (the
title is one step down the scale, and the gaps around the description,
suggestions, macros and footer are one step tighter) for 718px, with room for
the dashboard to keep growing.

Its layout test now requires the gate to *fit* rather than merely be
reachable by scrolling, empty and populated. Dropped the 1024x600 tier: no
machine runs it, this gate cannot reach it without dropping content, and a
target nobody uses is a constraint that only ever costs.

The probe presses Tab before moving focus, because a bare focus_set() no
longer scrolls -- it would have been testing a path no user can take. The
Tk fakes learned pack_forget(), which the viewport calls to take the
scrollbar down whenever the content fits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BsXnTHyuuCUZsqetTaajcF
2026-08-03 16:00:13 +02:00
1702685afd Make the meal gate usable without a pointer on a 1366x768 screen
The Log Meal tab did not fit this laptop, and had not for some time. Measured
under a real 1366x768 X server: it required 866px inside a 739px content pane
with every field EMPTY, and 938px with a day of meals logged.

Because the content frame was place()-centred it was sheared at BOTH edges at
once -- the "Diet Gate" title and the slot header (which meal you are being
asked to log) off the top, the calorie headline and the whole dashboard off the
bottom -- with no scrollbar anywhere in the package and nothing to indicate
content was missing. This is a hard lock: overrideredirect, a global grab, VT
switching disabled. Unreachable content is unrecoverable.

Two independent causes, both needed fixing:

- Every font size was handed to Tk as *points*. Tk encodes the unit in the
  SIGN of the size, and _gatelock_typography carried a comment asserting the
  opposite ("Tk has no separate px/pt distinction"). At ~100 DPI that made
  every string ~30% oversized. Negative sizes now; the docstring records why.
- There was no scroll container. build_layout now uses gatelock's
  ScrollableSurface, which top-anchors, scrolls by keyboard, and scrolls to
  follow focus.

Font sizes alone were not enough: they take the layout to 702px, still over the
571px pane at 1024x600. Both fixes are load-bearing.

The grams/items selector was mouse-only. tk.OptionMenu's Menubutton defaults to
takefocus=0, so ::tk::FocusOK rejects it and it never entered the tab ring --
verified by walking the real ring, which ran Entry -> Text -> Listbox -> Button
-> TNotebook and skipped it entirely. Switching units rescales the whole entry,
so a pointerless user could not correct "250 grams" to "3 items" inside a lock
they cannot leave without logging a meal. It is now two Radiobuttons, which are
in the ring by default and take both Space and Return -- and which post nothing,
unlike a menu, whose grab-stealing is why screen-locker bans OptionMenu on lock
surfaces outright.

Also: the focus ring was invisible (Tk's default is 1px BLACK on #211D1B) and
_build_desc actively inverted it by setting highlightbackground, the UNfocused
ring, so the box outlined while idle and went black the moment it took focus.
Enter now activates buttons (X11 Tk binds only <space>). Text widgets no longer
trap Tab. notebook.enable_traversal() is called, so Ctrl+Tab and Ctrl+PageDown
work. wraplength drops 900 -> 640, the line-length cap, which also stops a
label forcing a 900px-wide layout onto a 1024px screen.

The Flutter app had two controls with no keyboard route at all. Its desktop
target is the web build in a Chrome --app window, so a touch gesture is not a
fallback there, it is the only interaction:

- Deleting a history entry was bound solely to onLongPress. Long-press has no
  keyboard equivalent whatsoever. It now has a focusable IconButton; the
  long-press stays as a redundant pointer shortcut.
- Calendar day cells were a bare GestureDetector, so navigating to
  date-filtered history was pointer-only and invisible to a screen reader.
  Now Semantics(button: true) over an InkWell.

Plus: the filter sheet built a TextEditingController inside build() with the
caret forced to end-of-text, so every keystroke discarded it and silently undid
mid-string Home/End/arrow editing. And nothing capped width, so on the 1366px
desktop window prose ran ~180 characters per line (twice the readable limit)
and a four-digit kcal field spanned ~1334px. AppWidth adds those caps.

Tests: the render check is a failing test, not a script -- ruff runs with
--unsafe-fixes, whose T201 fix DELETES print statements, which would silently
turn a diagnostic into a no-op. test_gate_layout_fits.py was verified red on
the pre-fix code ("content 866 in viewport 768 -> NO SCROLL VIEWPORT ->
UNREACHABLE CONTENT") and green after. The Flutter suite gains its first
keyboard-reachability assertions; it previously had ~109 tester.tap calls and
zero checks that anything was focusable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:21:27 +02:00
c6663da06a Pin gatelock 0.2.1 for the recovery-loop fail-open fix
0.2.0's recovery loop scheduled the next tick after running it, so any
exception out of a tick permanently ended all re-assertion of coverage, grab
and VT for the rest of the lock -- silently, since Tk logs and swallows
exceptions from `after` callbacks. 0.2.1 moves the reschedule into a
`finally`.

Also in 0.2.1: `holds_grab` no longer reads a grab held by one of our own
transient children (a posted menu) as "the grab was lost" and takes it back.
That one was a real, user-visible bug on screen-locker's gate windows; the
same class of widget on a diet_guard gate would have behaved the same way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 15:21:19 +02:00
84bd550331 Migrate diet_guard to gatelock v0.2.0 per-output locking
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
The gate is laid out once per live output instead of once on the primary,
and claims the screen through an Arbiter at RANK_DIET_GUARD so the wake alarm
and the workout locker can both outrank it.

Three kinds of state needed three different answers. The display fields
already fanned out for free -- GateVars holds root-mastered StringVars bound
to labels. The typed-in fields did not: amount, per, meal name and the four
macro cells were bare tk.Entry widgets, so N copies would have been N
independent forms. They are variable-backed now, which is what makes logging
work from whichever screen the user is actually looking at. The description
box and the suggestion list cannot be shared at all -- tk.Text and tk.Listbox
have no variable -- so those genuinely diverge and reads answer from the copy
the user touched.

The History tab is per-surface too: its grid, budget field and edit button
are driven across every monitor, with the budget likewise variable-backed so
the value cannot differ between screens mid-edit.

Laying the gate out on the primary monitor only was considered and rejected:
not having to hunt for the screen the form landed on is the entire point of
per-output locking.

Tests: the suite hung rather than failed on first run -- diet_guard patches
its own `tk`, but LockWindow builds Toplevels through gatelock's, and real
tkinter over a MagicMock root recurses without bound. `_hermetic_gatelock`
closes that and redirects the runtime dir so no test can stand a production
locker down. Every other test runs single-output, where fan-out is
unobservable, so test_gatelock_fanout runs on two; each assertion was checked
by cropping the fan-out loops to the first surface and confirming it goes red.

_gatelock_ui and _gatelock_calendar were each split in two to stay under the
500-line limit, and GateVars' typed-in fields moved into a nested
GateEntryVars rather than raising pylint's attribute cap.

470 tests, 100% branch coverage, pre-commit clean, no lint suppressions.
2026-07-25 16:56:13 +02:00
2a772d624f app: warm-tint the shared palette, fix Snack chip + on-fill contrast
- theme.dart: warm unified-design-system palette, on-fill for
  onPrimary/onError, and inputDecorationTheme/dividerTheme (previously
  unset -> every TextField/Divider fell back to Material defaults).
- slot_selector_row.dart: the Snack ChoiceChip had no explicit
  backgroundColor/side/labelStyle like the hour chips, so it fell through
  to Flutter's own default -- a literal pure-white border.
- Regenerated launcher icon assets (warm charcoal + warm gold accent) via
  python_pkg/app_icons.
- Bump gatelock pin to the released v0.1.1 carrying this same palette.

Verified: flutter analyze clean, diet_guard pytest 443/443 @ 100% coverage,
phone-installed APK pixel-checked (Snack chip border, on-fill contrast).
2026-07-21 21:16:26 +02:00
0976c84254 Add CI-mirror pre-push gate + requirements-sync check
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
Mirrors the CI-green safeguards rolled out repo-wide (see screen-locker):
- scripts/check_requirements_sync.py: pre-commit hook that fails if a
  package import isn't declared in requirements.txt. On rollout it caught
  a real latent gap — diet_guard/_mcp.py imports pydantic but it was only
  present transitively (via mcp); now declared explicitly.
- scripts/ci_mirror.sh: pre-push gate = requirements.txt-only venv
  (hash-gated) running `pre-commit run --all-files` + `pytest`, replacing
  the plain pytest-coverage pre-push hook.

Only the tooling files + the pydantic declaration are committed; unrelated
in-flight work in the tree is left untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018PMRRVVkeRyrSaaqueMZKG
2026-07-11 17:58:49 +02:00
e6812f7b4f Fix pre-existing CI failures (deps, lint, whitespace)
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
CI on main was already red before the crdt_sync migration. Make the
pre-commit --all-files run and the test job green:

- requirements.txt: add crdt-sync (now a runtime dep) and mcp so CI's
  `pip install -r requirements.txt` can import diet_guard._sync and
  collect test_mcp.py (both were ModuleNotFoundError in CI).
- .pre-commit-config.yaml: add mcp + pydantic to the pylint hook deps so
  it can resolve diet_guard._mcp's imports (was E0401).
- pyproject.toml: raise pylint min-similarity-lines to 8 — the shared
  nutrition-field keys in _foodbank/_mcp mirror one domain schema, not
  real duplicate code.
- scripts/setup_mcp.sh: split declare/assign for SCRIPT_DIR/REPO_DIR
  (shellcheck SC2155).
- codespell: fix "packge"/"unparseable" typos; trailing-whitespace
  auto-fixes across app/.agents/skills docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RRCdR4D8SaVrN2xraMQxM
2026-07-10 23:55:56 +02:00
a121da211d Point sync at the consolidated utils/syncs monorepos
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
gatelock and diet-guard-sync were folded into shared monorepos:
gatelock -> github.com/kuhyx/utils (subdirectory), diet-guard-sync ->
github.com/kuhyx/syncs (subdirectory). Update the gatelock pip URL,
SYNC_REPO_NAME, the devices-dir path prefix (PC and phone), and add a
persisted-value migration so an already-configured phone picks up
'syncs' on next load.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-10 14:12:16 +02:00
843f5e0221 Extract diet_guard from testsAndMisc as a standalone repo
Rewrites python_pkg.diet_guard imports to diet_guard, vendors the
shared as_float coercion helper, drops the monorepo PYTHONPATH from
install.sh and the systemd unit (package is now pip-installed), and
scaffolds standalone lint/test config matching testsAndMisc's real
enforced bar (pylint --fail-under=10 with tests excluded and the
use-implicit-booleaness/consider-using-with disables, mypy's actual
disabled-error-code set, ruff ALL, bandit, 100% branch coverage).
2026-06-22 12:18:39 +02:00