Sangheetha Parthasarathy

Consumer AI

Dinner, decided before anyone has to think about it

Every family knows the question. What are we eating? It arrives every single day, and it is never the cooking that wears you down. It is the deciding, the checking, and the explaining.

Here is one evening in a kitchen that answers it.

The pile

All week, recipes get saved, mostly on Instagram, the way they always do. They used to stay there, unopened. Now each one is read, in whatever language it was written in, and comes out as one plain recipe the whole house can use. Then it is sorted. Is the full recipe there, or only a video? How much effort is it? Does something need soaking the night before? Does it fit the family's diet? A recipe that breaks a rule is marked, never thrown away.

One saved recipe parsed into ingredients and steps
Recipe cards with diet marks, prep ahead, pantry match, Choose and We cooked this

The pick

It is someone's turn to choose. They open the list on a laptop or a phone, whichever is to hand, and pick. Or they tap "I'm feeling lucky" and get something that fits.

Recipes home: I'm feeling lucky, Fits Demeter, filters, taste tags and recipe cards with diet marks

The message

Within a minute, the kitchen WhatsApp group has the recipe in Hindi, the language the cook reads, with the video link. The cook reads it in an app they already use every day. Nothing to install. Nothing to log into.

The real message the cook received on 9 Nov, in Hindi, with the video link
The same recipe cards with Hindi names

The plate

Dinner gets made, and someone takes a photo, because people always do. The photo goes back onto the recipe, and an AI model reads it to name the dish. Next time it is your turn, you can see what was made last week and what nobody has cooked in a month.

The dish from that message, cooked on 9 Nov, in the pan

The cupboard In progress

All the while, the pantry keeps count: plenty, low, out. You could photograph the shelves and the fridge and ask software to make sense of them, but that is slow and unreliable. This family orders almost everything online, so the pantry goes straight to the source and reads the orders, with the odd gap filled once by hand. The next piece, in progress now, closes the circle. When something runs low, the order goes in on Blinkit or Zepto by itself, the delivery code and the gate entry are approved without anyone reaching for a phone, and the groceries land back in the pantry. Tomorrow's pick starts from what is really in the kitchen.

Pantry read from grocery orders, with use-soon days

Then the question comes round again, and this time it is already half answered.

What sits underneath

An untidy household process. The boring middle handed to software. The result delivered to people who will never open an app. Most operations work looks like this, whatever the business.

So far

89 recipes sorted out of a pile of saved posts. 87 grocery orders read by themselves since mid-October, almost every day. A choice reaches the kitchen in under a minute. It runs in one household. Recipes are still gathered by hand for now. Ordering and gate approvals are in progress. The screens are from the real app and the real kitchen group, with every name removed.


Demeter: under the hood

A note for engineering teams. Taken from the git history and the code, not from memory. Roles only: "the family", "the cook".

The shape

PieceWhat it is
AppReact screens inside a larger family app, served on its own subdomain
APINode and Express, hosted on Railway
DataPostgres on Supabase. Row-level security on, service role only. Photos in a private bucket, served by signed links
WhatsAppAn openclaw gateway on a computer at home, holding one WhatsApp session
SenderA small script the gateway's scheduler runs every minute
Recipe intakeAn hourly sync from a separate brain-dump app, read through its own read-only export

The WhatsApp harness

This is the piece that makes the rest usable. The cook never opens the app. Everything they need arrives in a WhatsApp group they already read.

Why WhatsApp and not an app. The person doing the work already lives in WhatsApp. An app means an install, a login, and a habit to build. A message in an existing group needs none of them.

Why a queue. The API runs in the cloud and cannot reach the WhatsApp session at home. So the server never sends. Choosing a meal writes a row with the message and the destination on it, status pending. The sender at home picks it up within a minute.

The address belongs to the record. The group id is copied onto the choice when it is made and read back exactly as written by the sender. It is never looked up again at send time. A destination recomputed at send time is the kind of bug where everything reports success and the message goes somewhere else.

Never send twice. The sender claims a row (pending to sending) with a conditional update, so two runs cannot both take it. It writes sent only after the send succeeds. A row stuck in sending for ten minutes becomes failed, with a note to check the group first. It is never retried blind. Three failed attempts and it stops. Failures post to a Slack alert.

The screen tells the truth. The panel says "sent" only once the row reads sent. If nothing has happened after two and a half minutes, it says so and offers Share and Copy, so the family can still get the message to the cook by hand.

One harness, several jobs. The same gateway also sends the family's daily schedule note and transport reminders to staff. Each job is its own scheduled task, so one failing does not stop the others.

The translation layer

Three languages meet in this kitchen: the creator's, the family's and the cook's. Captions arrive in whatever language the creator posted in, sometimes romanised, sometimes in their own script. The parse reads them all into one structured English recipe for the family. Then each recipe is written once into Devanagari Hindi for the cook, and pantry items carry a Hindi name too, so a low-stock list can be copied straight into the kitchen group. One structured record in the middle means each side reads its own language and nobody translates by hand.

The message

Why Hindi. The cook reads Hindi. Each recipe is translated once, ingredients and method, and the translation is stored on the row, so the same dish always reads the same way and translation is not paid for twice.

Why the video link as well. At first, recipes with a video sent only the dish name and the link, on the idea that the video was enough. That was changed on 3 Nov: the message now always carries the Hindi ingredients and steps when the recipe has them, plus the link.

Recipes

Capture. Saved posts land in a brain-dump app that sorts them by category. Anything tagged recipes that has text is pulled hourly through a read-only export with its own token. The recipe app holds no key to the other app's database.

Parsing. A language model turns each caption into a structured recipe through a fixed tool schema: full recipe or link only, ingredients, method, effort, prep the night before, tags, region. When a caption has no recipe, the author's comment, website or video description is read, and kept only when it is the same dish. Steps the model adds that are not in the source are dropped.

Diet rules are marks, never deletions. Every recipe stays visible, marked against the family's rules. Only "I'm feeling lucky" filters, and it picks only from recipes that fit. A rule that deletes recipes hides the reason, and the recipe can never come back when the rule changes.

Imported rows are never overwritten. A failed parse retries. A good one is left alone.

Whose turn

Per-person codes instead of logins. The app runs on shared laptops and phones, and everyone in the family needs to open it as themselves without a parent's login. Each person opens it with a short code of their own. The code is hashed, there is no default, wrong guesses lock out for twice as long each time (serialised so parallel guesses cannot race it), and device tokens last 90 days and are stored hashed. Changing a code signs out only that person.

The turn is the app's job. It knows whose day it is. Choosing on someone else's day asks "are you sure?" first.

The pantry

Order history, not photos of shelves. Reading shelf and fridge photos is slow and error-prone. The family buys almost everything online, so the pantry reads the orders instead, and the few things bought elsewhere were added once by hand. A photo route exists in the code and has not been used. Since 11 Oct: 87 orders read (Blinkit and Amazon), on most days, with no one touching them. The reader is a scheduled browser task in a signed-in Chrome at home, so it runs only when that machine is on.

Orders are read where they already are. A browser task reads past orders from the shop's own page and posts them from a signed-in Demeter tab. There is no import token, because a token used inside a shop's page can be read by that shop's scripts.

Only orders delivered home count. One order delivered to a relative's address was filed into the pantry. Orders are now matched against the home delivery address first, and anything else is recorded as read and files nothing.

Idempotent. An order is keyed on shop and order number, so reading it twice changes nothing.

Can-make. Each recipe keeps up to six defining ingredients, which the pantry is matched against.

Photos

Upload originals. The photo's own location is how home cooking is told from eating out. Shrinking an image in the browser strips that data, so every dish would silently become "unknown". Resizing, if ever added, happens on the server after the location is read.

Google Photos was ruled out, with reasons. Google removed library read access in March 2025, and its API has never returned a photo's location. The response is a success with the field simply missing, so it looks like a bug rather than a policy. Recorded so nobody re-derives it.

A guess looks like a guess. If the photo has no location, an AI model guesses home or restaurant from the picture. The guess is shown as a guess and never sets the answer. A person's toggle locks the row against any later re-analysis. With no home set, everything stays "unknown", because a boundary with no centre would call the whole world home.

Tested

At the time of each commit: an offline test gate with no credentials or network (39 tests by the per-person codes change), and end-to-end runs against the live database that snapshot and restore it (31 of 31 for the same change). The WhatsApp send is verified on the gateway's own log: a message id returned for the kitchen group at the minute the row reads sent.

What didn't work

  • The automatic recipe harvest has never run. It depends on a browser task that was designed and documented and never set going. Recipes are still harvested by hand. This is the weakest link.
  • Google Photos, above.
  • The platform's official caption API was judged far too heavy for one household and dropped.
  • An import token inside shop pages, rejected in review, above.
  • Link-only messages for video recipes, replaced, above.

What I'd change

  • The sender lives on one computer at home. If it is off, choices wait and the screen says so. A second sender, or a hosted WhatsApp session, would remove that single point.
  • sent means WhatsApp accepted the message, not that the cook read it. A read receipt or a one-tap reply would close that gap.
  • Move the recipe harvest off a browser task the family has to keep running.

In progress

Auto-ordering from the pantry on Blinkit or Zepto, and approving the delivery code and gate entry, so the circle closes: cooked, running low, ordered, delivered, back in the pantry.

Known limits

  • Runs in one household.
  • The photo is read by an AI model. No claims are made here about where photo data is processed.
  • The screens are the real app and a real message to the kitchen group, cropped so no names, group name or codes show.

Want to talk about this?

Book a call

Or write to me.

Thank you. Your message reached me, and I will reply by email myself.

That did not go through. Please try again.