Expiry system deep-dive: secondary use windows and ingredient-specific post-date behavior #83

Closed
opened 2026-04-14 14:02:02 -07:00 by pyr0ball · 1 comment
Owner

Overview

The current expiry system treats use-by dates as binary (fresh → expired), but several common pantry items have a meaningful secondary use window after their nominal use-by date. During this window the item is not spoiled — it is specifically suited for certain recipes and should be matched to those rather than flagged as expired.

Known examples

Ingredient Secondary state Best uses Notes
Bread Stale (not moldy) Croutons, stuffing, panzanella, bread pudding, French toast Stale bread is preferred in many of these
Rice (cooked) 1-2 days old Fried rice Day-old rice is functionally required — fresh rice is too moist
Tortillas Stale/dry Chilaquiles, migas, tortilla soup, casserole filling Same principle as bread
Bananas Overripe Banana bread, smoothies, pancakes Overripe unlocks recipes unavailable with fresh
Milk (slightly sour) Sour Pancakes, quick breads, baking Acid activates leavening — often called for explicitly
Hard cheese Dried rind Broth, soups, risotto Parmesan rinds especially

This list is not exhaustive — a proper review will likely surface more.

What needs to change

1. Expiration predictor (app/services/expiration_predictor.py)

  • Add a secondary_window_days concept alongside use_by_days per ingredient category
  • Items in their secondary window should not be treated as expired
  • LLM fallback should be aware of secondary windows when estimating shelf life

2. Recipe engine

  • Items in secondary window should be preferentially matched to recipes that call for their secondary state (stale bread, day-old rice, overripe banana, etc.)
  • FTS queries should expand "stale bread" / "day-old bread" synonyms for items in secondary window
  • Items in secondary window should not be excluded from pantry coverage checks

3. UX / messaging

  • Replace generic expiry warnings with context-aware suggestions: "Your bread is best for croutons or stuffing now" rather than a generic expiry alert
  • Aligns with the no-panic UX principle — calm and actionable, not alarming
  • Secondary-window items could get a distinct visual state (different from fresh and from expired)

Scope of deep-dive

  • Audit ingredient categories in the expiry predictor for secondary-window candidates
  • Review FTS corpus for recipes that specifically require a secondary-state ingredient
  • Design the data model for secondary_window_days and how it propagates to the recipe engine
  • Define UX for the secondary-window state in inventory and recipe suggestion views
  • Consider LLM fallback behavior: does it already infer secondary windows, or does it need explicit prompting?
  • No-panic UX principle: memory/feedback_kiwi_no_panic.md
  • Expiry LLM fallback: app/services/expiration_predictor.py
  • Assembly template system: stale-bread and day-old-rice templates may already exist in corpus
## Overview The current expiry system treats use-by dates as binary (fresh → expired), but several common pantry items have a meaningful **secondary use window** after their nominal use-by date. During this window the item is not spoiled — it is specifically suited for certain recipes and should be matched to those rather than flagged as expired. ## Known examples | Ingredient | Secondary state | Best uses | Notes | |---|---|---|---| | Bread | Stale (not moldy) | Croutons, stuffing, panzanella, bread pudding, French toast | Stale bread is *preferred* in many of these | | Rice (cooked) | 1-2 days old | Fried rice | Day-old rice is functionally required — fresh rice is too moist | | Tortillas | Stale/dry | Chilaquiles, migas, tortilla soup, casserole filling | Same principle as bread | | Bananas | Overripe | Banana bread, smoothies, pancakes | Overripe unlocks recipes unavailable with fresh | | Milk (slightly sour) | Sour | Pancakes, quick breads, baking | Acid activates leavening — often called for explicitly | | Hard cheese | Dried rind | Broth, soups, risotto | Parmesan rinds especially | This list is not exhaustive — a proper review will likely surface more. ## What needs to change ### 1. Expiration predictor (`app/services/expiration_predictor.py`) - Add a `secondary_window_days` concept alongside `use_by_days` per ingredient category - Items in their secondary window should not be treated as expired - LLM fallback should be aware of secondary windows when estimating shelf life ### 2. Recipe engine - Items in secondary window should be **preferentially matched** to recipes that call for their secondary state (stale bread, day-old rice, overripe banana, etc.) - FTS queries should expand "stale bread" / "day-old bread" synonyms for items in secondary window - Items in secondary window should not be excluded from pantry coverage checks ### 3. UX / messaging - Replace generic expiry warnings with context-aware suggestions: "Your bread is best for croutons or stuffing now" rather than a generic expiry alert - Aligns with the no-panic UX principle — calm and actionable, not alarming - Secondary-window items could get a distinct visual state (different from fresh and from expired) ## Scope of deep-dive - Audit ingredient categories in the expiry predictor for secondary-window candidates - Review FTS corpus for recipes that specifically require a secondary-state ingredient - Design the data model for `secondary_window_days` and how it propagates to the recipe engine - Define UX for the secondary-window state in inventory and recipe suggestion views - Consider LLM fallback behavior: does it already infer secondary windows, or does it need explicit prompting? ## Related - No-panic UX principle: `memory/feedback_kiwi_no_panic.md` - Expiry LLM fallback: `app/services/expiration_predictor.py` - Assembly template system: stale-bread and day-old-rice templates may already exist in corpus
pyr0ball added the
enhancement
needs-design
labels 2026-04-15 23:13:09 -07:00
Author
Owner

All work shipped in commits 8fd77bd, b2c546e, e7ba305 on main:

  • Secondary-use window hints wired into recipe engine + API + frontend
  • Recipe browser _all unfiltered option added
  • Full hierarchical subcategory navigation across all domains (Italian, Mexican, Asian, Indian, Mediterranean, American, European, Latin American, meal types, proteins, vegetables)
  • Subcategory pills load non-blocking; user can browse at any level of the hierarchy
All work shipped in commits 8fd77bd, b2c546e, e7ba305 on main: - Secondary-use window hints wired into recipe engine + API + frontend - Recipe browser `_all` unfiltered option added - Full hierarchical subcategory navigation across all domains (Italian, Mexican, Asian, Indian, Mediterranean, American, European, Latin American, meal types, proteins, vegetables) - Subcategory pills load non-blocking; user can browse at any level of the hierarchy
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Circuit-Forge/kiwi#83
No description provided.