diet-guard/scripts
Krzysztof kuhy Rudnicki 24be05b9c3 feat: mirror the catering parser and slot spread in Dart, gated by one fixture
The phone is getting its own Kuchnia Wikinga importer so the feature works
with the PC switched off. That turns a risk `_kuchnia_spread.py` called
"theoretical" into a live one: two implementations must now agree exactly on
which dishes are dropped, which slot each lands on, and the JSON a banked
record encodes to. Disagreement means each device re-adds what the other
dropped, and since `add_manual_entry` restamps `t` unconditionally, the whole
curated bank republishes to every peer on every refresh. A slot mismatch is
worse -- a checkpoint one device offers and the other does not can never be
satisfied.

So the port lands with its gate rather than after it. One shared fixture,
`tests/fixtures/kuchnia_day.json`, is asserted by both `pytest` and
`flutter test`: same input, same expected dishes, drops, slots, bank keys and
records. Two suites written independently from the same prose is not a gate.

The fixture is five real dishes (a scrubbed 2026-08-22 capture) plus synthetic
meals that each pin one drop or fallback path -- a per-100 g mix-up, an absurd
portion, a stringly-typed number, a non-dict entry, a missing `nutrition`, a
missing `mealPriority`, and a twin-dish pair. Without those, "the same meals
are dropped" is a vacuous assertion against an all-happy-path capture. Both
halves were mutation-checked: flipping an expected slot, drop count or record
value fails each suite, and flipping `435.0` to `435` fails the byte-identity
test while the value comparison still passes, which is why both exist.

Three traps the port had to survive, each caught before it shipped:

- `_ENERGY_TOLERANCE` is **0.35**. The prose said "~1%", which describes how
  closely the captured day agreed, not the threshold; porting 0.01 would drop
  dishes the PC keeps. That paragraph now says so.
- `jsonEncode(435)` emits `435` where Python emits `435.0`, so every Dart
  macro is forced to `double` and a canonical-encoding test pins it.
- Dart's `List.sort` is unstable where Python's `sorted` is stable, so the
  Dart comparator carries the payload index as a final tiebreak.

The real dishes live in their own file rather than as a literal in the
generator: transcribing them by hand corrupted three of the five, and the
error was invisible until the generated fixture was diffed against the
capture.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QQpCiPaSbyRpwsLkH3dgsX
2026-08-23 08:00:54 +02:00
..
build_kuchnia_fixture.py feat: mirror the catering parser and slot spread in Dart, gated by one fixture 2026-08-23 08:00:54 +02:00
bump_patch_version.sh Bump the patch version on every commit 2026-08-09 23:04:17 +02:00
check_file_length.sh Enforce the shared 250-line file cap 2026-08-15 16:35:01 +02:00
check_no_crdt_sync_override.sh Add Google sign-in support for the Firebase sync account 2026-08-12 23:36:10 +02:00
check_patch_targets.py Fix check_patch_targets' own fail-open: put the repo root on sys.path 2026-08-15 21:27:24 +02:00
check_requirements_sync.py Add CI-mirror pre-push gate + requirements-sync check 2026-07-11 17:58:49 +02:00
ci_mirror.sh Stop two keyboard tests hanging, and make the CI mirror mirror CI 2026-08-04 21:50:15 +02:00
probe_kuchnia_auth.py feat: import Kuchnia Wikinga catering deliveries into the food bank 2026-08-22 23:04:22 +02:00
probe_kuchnia_details.py feat: import Kuchnia Wikinga catering deliveries into the food bank 2026-08-22 23:04:22 +02:00
probe_kuchnia_shape.py feat: import Kuchnia Wikinga catering deliveries into the food bank 2026-08-22 23:04:22 +02:00
probe_kuchnia.py feat: import Kuchnia Wikinga catering deliveries into the food bank 2026-08-22 23:04:22 +02:00
setup_mcp.sh Fix pre-existing CI failures (deps, lint, whitespace) 2026-07-10 23:55:56 +02:00
sync_freshness.py feat: gate a phone deploy on a peer already holding the device's state 2026-08-23 07:41:26 +02:00