Two bugs in /routines/suggest where the LLM could override hard constraints:
1. Products with min_interval_hours (e.g. retinol at 72h) were passed to
the LLM even if used too recently. The LLM reasoned away the constraint
in at least one observed case. Fix: added _filter_products_by_interval()
which removes ineligible products before the prompt is built, so they
don't appear in AVAILABLE PRODUCTS at all.
2. Minoxidil was included in the available products list regardless of the
include_minoxidil_beard flag. Only the objectives context was gated,
leaving the product visible to the LLM which would include it based on
recent usage history. Fix: added include_minoxidil param to
_get_available_products() and threaded it through suggest_routine and
suggest_batch.
Also refactored _build_products_context() to accept a pre-supplied
products list instead of calling _get_available_products() internally,
ensuring the tool handler and context text always use the same filtered set.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>