#!/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()