← dean's list
data engineering · 2026 · ● live

KitchenSync Food Forecasting System

Live kitchen production system. ML pulls furthest ahead of a naive baseline exactly when weather diverges from the seasonal norm — and reports honestly the one condition where it doesn't.

view on github ↗
KitchenSync Food Forecasting System screenshot
open dashboard ↗

This is what kitchen staff cook from all shift — live Kitchen and Chicken production queues plus running waste and sales metrics, refreshed every 5 minutes.

day 34 of a/b test
2026-08-07 through 2026-09-09
service level
waste rate
cumulative averages
ml model
8.4%avg waste
96.9%service level
baseline
11.4%avg waste
95.7%service level
latest run · 2026-09-09
ml model
7.7% waste -0.7pp vs avg
97.2% service level +0.4pp vs avg
baseline
8.3% waste -3.1pp vs avg
96.1% service level +0.3pp vs avg
ML lifts service level +1.2pp and lowers waste 3.0pp — ML wins on both.
720 simulated store-days
+2.0ppextreme heat · service level
+2.0ppextreme cold · service level
2.9xbigger than the neutral-day gap
On precipitation days ML's service level runs marginally below baseline (-0.6pp) — but waste drops to roughly half (9.8% vs 18.3%), a trade-off a flat historical average can't make.
how it's built

Retail kitchens waste food when production outpaces demand and miss revenue when they run short. Forecasting the right quantity per item per store at a 15-minute grain, refreshed continuously, needs a real pipeline, not a spreadsheet — and the harder question is honest evaluation: does ML actually earn its complexity cost, and where does it not? Modeled after the Kitchen Production System (KPS) at Kwik Trip.

End-to-end simulation of a Kwik Trip-style Kitchen Production System running live on AWS EC2. The POS simulator generates events for 12 stores via Poisson arrivals, FIFO batch inventory, and slot-boundary production logic, buffering sales/waste/stockout in memory and flushing straight to Neon Postgres every 5 minutes — no ingest API in front of it. A nightly cron rebuilds the demand baseline and runs an A/B comparison entirely against Neon: LightGBM (fed real per-date, per-region weather as a "perfect forecast") against a naive hourly-average baseline that structurally can't use a weather axis, over 12 stores × 45 items × 672 weekly slots. A Streamlit dashboard surfaces split Kitchen and Chicken production queues with 5-minute auto-refresh, reading live from Neon. Results write to ab_results_v2.json, commit to GitHub, and trigger this portfolio site to rebuild. Retraining is manual and Neon-native end to end — no Snowflake step; a dbt project remains in the repo as a portfolio-only artifact, not run against live data.

  • Weather signal too weak to show up: the baseline's own historical average already bakes in an *average* weather effect, so a synthetic signal has to be deliberately stronger than reality to produce a visible A/B gap at all — narrowed the neutral band, raised precip probability, and halved unrelated day-to-day noise, growing the extreme-temperature service-level gap from +0.31pp to +2.0pp
  • Diagnosed a silent cost leak: an always-open ingest-API connection pool kept Neon's compute endpoint permanently awake, burning a week's free-tier compute-hours regardless of actual traffic — deleted the API entirely and rewrote the simulator to buffer events in memory and flush via short-lived batched connections every 5 minutes
  • Traced a 1.89x overproduction at a low-traffic store to a fleet-wide cold-start average masking per-store scale — fixed with a data-driven per-store traffic ratio and a three-tier warm/zero/cold fallback, flattening predicted-vs-actual to a 1.02–1.03x band across stores
  • Caught a conditional-mean trap: a demand profile averaging only sale-days instead of all days inflated low-traffic predictions 3–4x — a generalizable lesson now called out for any future profile-building script, not just a one-off fix
PythonPostgreSQL (Neon)LightGBMStreamlitasyncio / psycopg2dbt Core (portfolio-only)DockerAWS EC2systemduv
view archived report ↗

An earlier Power BI report built to explore the same data — kept as an archived artifact, not maintained or refreshed anymore. Stores ranked by waste % level, drill-through to individual store stats, and an item performance page sliced by category or store.