Sync: EGG LIMIT hard rule + weekly cuisine rotation + scripts
- SKILL.md: add egg-limit hard rule (max 4/meal, one egg-meal/day), cuisine-rotation section - scripts/pick_cuisine.py: seeded weekly world-cuisine picker (stable per week, no recent repeats) - scripts/verify_eggs.py: regression guard for the egg cap - README.md: full project documentation - .gitignore: exclude cuisine-rotation.json + python caches (live data stays local)
This commit is contained in:
68
scripts/verify_eggs.py
Normal file
68
scripts/verify_eggs.py
Normal file
@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Regression guard for the EGG LIMIT hard rule.
|
||||
|
||||
Run after any change to meal-history.json, inventory.json, or the daily job,
|
||||
and before sending a regenerated day's email:
|
||||
|
||||
python3 scripts/verify_eggs.py
|
||||
|
||||
Checks the EGG LIMIT contract against the persisted rules in inventory.json:
|
||||
- No single meal may exceed diet.egg_rules.max_per_meal (default 4).
|
||||
- No day may have more than diet.egg_rules.max_meals_per_day_with_eggs
|
||||
(default 1) meals containing eggs — i.e. the other meal must be egg-free.
|
||||
|
||||
Exits non-zero on violation so it can gate a send or a CI-style check.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
SKILL_DIR = os.path.dirname(HERE)
|
||||
|
||||
|
||||
def load(name):
|
||||
with open(os.path.join(SKILL_DIR, name)) as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def main():
|
||||
inv = load("inventory.json")
|
||||
hist = load("meal-history.json")
|
||||
|
||||
rules = inv.get("diet", {}).get("egg_rules", {})
|
||||
max_per_meal = rules.get("max_per_meal", 4)
|
||||
max_egg_meals = rules.get("max_meals_per_day_with_eggs", 1)
|
||||
|
||||
violations = []
|
||||
by_date = {}
|
||||
for e in hist.get("entries", []):
|
||||
eggs = next((i["qty"] for i in e["items"] if i["name"].lower() == "eggs"), 0)
|
||||
by_date.setdefault(e["date"], []).append((e["meal"], e.get("name", ""), eggs))
|
||||
|
||||
for date, meals in by_date.items():
|
||||
for meal, name, eggs in meals:
|
||||
if eggs > max_per_meal:
|
||||
violations.append(
|
||||
f"{date} {meal} ('{name}'): {eggs} eggs > max {max_per_meal}/meal")
|
||||
egg_meal_count = sum(1 for _, _, eggs in meals if eggs > 0)
|
||||
if egg_meal_count > max_egg_meals:
|
||||
violations.append(
|
||||
f"{date}: {egg_meal_count} egg-containing meals > max {max_egg_meals}/day "
|
||||
f"(other meal must be egg-free)")
|
||||
|
||||
# Also confirm the inventory metadata carries the cap.
|
||||
if not rules:
|
||||
violations.append("inventory.json missing diet.egg_rules — egg cap not enforced at the data level")
|
||||
|
||||
if violations:
|
||||
print("FAIL: egg-limit violations found:")
|
||||
for v in violations:
|
||||
print(f" - {v}")
|
||||
sys.exit(1)
|
||||
print(f"PASS: egg limit OK (max {max_per_meal}/meal, max {max_egg_meals} egg-meal/day) "
|
||||
f"across {len(by_date)} day(s).")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user