The Plan Looked Fine. Eighteen Meals Broke the Rule.
Eighteen of twenty-one meal slots came from outside the allowed recipe pool. The finished plan needed a check of its own.

In one production run, a background process in ThisWeekEats silently stopped applying a meal rule. Nothing crashed, and the plan kept shipping.
ThisWeekEats is my meal-planning product. A model can suggest meals, but the application still has rules it can check directly. A meal containing a forbidden protein isn't acceptable even if the rest of the plan reads well. Another model saying “I followed the rules” doesn't check anything.
The per-protein preference feature was already in place. Each household member can rate a protein LOVE, LIKE, OK, AVOID, or NEVER. The generation code checks a candidate protein against those preferences before the meal can land on a plan.
There is also a deliberate default. A protein someone has never rated resolves as neutral, not NEVER. Silence isn't read as a ban. Code sets that default. Interpreting an unrated preference isn't left to the model suggesting dinner.
Meal slots for one plan type were supposed to draw from a fixed twelve-recipe pool.
Eighteen of the twenty-one generated meal slots in that run came from outside the intended pool.
The filter worked in isolation. Nobody checked the finished plan. Eighteen meals got through. Nobody caught it in that run.
IMO, the completed plan needs its own check. By then the system has chosen actual meals, so the comparison can be literal: each protein against every household member's NEVER list, and each recipe against the pool for that plan type. Those are separate rules and should produce separate failures.
I'd deliberately give the check a bad plan too: one meal with a forbidden protein and another from outside the recipe pool. If the check accepts either, its green result on an ordinary plan tells me nothing about that rule.
These tests won't catch every bad meal plan. They'll catch these two failures before a household gets the plan.
After that run, I want the completed list of twenty-one slots checked against the twelve allowed recipes. All eighteen outside the pool should fail loudly and point to every meal that broke the rule.