Sous
techstack.textsous.com — how the system is built

An engineering tour, told one text at a time

What happens when you text your chef

Sous is a meal-planning service you talk to over text message. There is no app to install and no feed to check — it plans your family's week, writes your grocery list, and checks in on Wednesday, all in one SMS thread. This page walks through what's underneath: the machinery between the moment you hit send and the moment Sous texts back.

SOUS · TEXT MESSAGE
can we swap thursday? school thing ran late, need something fast
Done, Chef — Thursday is now sheet-pan gnocchi, about 25 minutes hands-on. The salmon moved to Friday and your grocery list is already updated.
Everything on this page happens between those two bubbles.
1codebase — SMS, web app, and schedulers ship together
3Claude model tiers, matched to the stakes of each job
27data models behind every plan, rating, and receipt
2,500+automated tests, plus a zero-credential build on every push
14allergens tracked per ingredient, to the EU standard
0passwords — anywhere in the system

Part 01 · The wire

Your text runs a gauntlet before Sous even reads it

When you text Sous, Twilio — the carrier-grade SMS provider — delivers your message to a single webhook in the Sous application, a Next.js app running on Vercel. In the first few milliseconds, the request is rate-limited, cryptographically verified as genuinely coming from Twilio, and schema-validated. Carrier compliance keywords like STOP and HELP are handled before any AI ever sees a word.

Sous identifies you by your phone number, stored in strict E.164 form so lookup is an exact match. Then comes idempotency: carriers retry webhook deliveries, so duplicates are caught twice over — once in application code and once at the database — and quietly ignored. Your text is logged to the conversation thread before anything else happens to it.

Then the part most SMS products get wrong: Twilio only waits about fifteen seconds for a webhook to respond, but a real conversational turn — reasoning about your week, editing a plan, refreshing a grocery list — can take longer. So Sous acknowledges the carrier in milliseconds and runs the actual turn in the background, on durable infrastructure.

Durable by design

Every household gets its own serialized queue lane on Inngest, a durable-execution platform: turns run one at a time in order, and a crashed step retries automatically. The queue itself never sees your data — events carry only opaque IDs, and message content is re-read from Sous's own Postgres inside the step (anything that must travel rides encrypted). And if the queue is ever unavailable, Sous degrades gracefully rather than going silent. The rule is written into the source: a queue outage may never silence Sous.

AI judgment deterministic code Your phone one text Twilio carrier network Webhook gauntlet rate limit · verify signature dedupe · pre-log SMS POST ack in milliseconds queued as opaque IDs — no text, no numbers Your household's lane serialized · durable · retries Conversation engine one brain for SMS + web Claude three model tiers runs turn every call metered through the truthfulness gate Reply, paced & ordered GSM-7 safe · segment-aware spacing Your phone parts arrive in order Delivery ledger carrier receipt per message SMS status callbacks: queued → sent → delivered
The life of one text. The carrier is acknowledged in milliseconds; the real work runs durably in a per-household lane, and every outbound message is tracked to a carrier-confirmed receipt.

Replies are engineered for the medium

SMS has physics. A single em-dash or curly quote silently flips a message from the standard encoding (160 characters per segment) to a wide one (70 per segment), more than doubling carrier cost and splitting texts unpredictably. So every outbound message passes through one normalization choke point: markdown stripped, typographic punctuation transliterated, emoji removed except where they're the point (the calendar itinerary). Multi-part replies are paced from real carrier throughput — the delay before each part is computed from the segment count of the part before it — so long messages arrive in order instead of scrambled. And every send is written to a delivery ledger that Twilio's status callbacks later confirm, message by message: Sous knows the difference between sent and delivered.

Part 02 · The brain

One conversation engine answers everything

Every message — from the phone or the web — flows into a single function: the conversation engine. It walks a routing ladder of roughly ten structured intents before anything freeform: are you answering the weekly calendar review? Confirming a proposed plan? Making a mid-week correction? Asking for the grocery list, tonight's menu, a recipe? Each of these is a dedicated, mostly deterministic path. Only when nothing structured matches does the message fall through to open conversation.

As you talk, a fast extraction pass quietly reads your messages for durable facts — a new allergy, a kid's age, "pizza Fridays are sacred" — and files them into your household's structured profile, with careful rules about what's permanent versus what's just about this week. Sous learns from ordinary conversation, and everything it learns is visible and editable on your Preferences page. It also keeps short notes on how your household likes to communicate, distilled from past conversations, so replies match your tone.

Three models, matched to the stakes

Claude Opus 4.8 Weekly plan & grocery generation The quality-critical job. Runs in the background, streamed, grounded in your confirmed week and the recipe corpus.
Claude Sonnet 5 The conversational voice The conversational replies you read. Voice is churn-critical — this tier is chosen for how it writes, not just what it knows.
Claude Sonnet 4.6 Structured extraction & classification Turning free text into typed data, where a mistake corrupts records. New households get this tier automatically boosted to Opus for their first weeks — early mistakes compound.

Replies are grounded, never improvised: the model is handed the actual stored plan, the confirmed week outline, your preferences, and the correct local date and time in your timezone. When you ask "what's Thursday again?", the answer is read from the database — the model is never asked to remember.

The truthfulness gate

The engine keeps a per-turn ledger of every database write it actually made — a preference saved, a slot edited, a grocery list recalculated. Before any reply is sent, a deterministic gate checks the model's prose against that ledger. If the text claims work the ledger can't back — "Done, I've updated your list!" when nothing changed — the claim is replaced with honest copy before it ever reaches your phone. A prompt is a plea; this is a gate.

Models classify. Code composes.

A consistent design rule runs through the engine: AI makes judgment calls, deterministic code produces the artifacts. The grocery list you receive is never improvised in the moment — a tiny classifier decides whether you asked for it, and the text renders from stored rows. Models help build those rows (estimating purchase quantities, proposing merges), but deterministic code verifies every merge, proves nothing was lost or invented, and does all the unit math. Recipe texts are answered from the stored plan so they can't drift. Calendar-conflict messages are assembled from templates, because a model asked to editorialize about someone's calendar will eventually say something alarming about a medical appointment.

Plan edits are routed by a pure, exhaustively tested module built around an explicit cost asymmetry: a surgical edit regenerates one slot and leaves every other day byte-identical, while a full rework re-runs the planner. Ambiguous requests deliberately route surgical — a wrong surgical edit costs one recovery message; a wrong rework burns a full plan generation. And every Sous-initiated rewrite snapshots the previous plan first, so "go back to what we had" is a deterministic one-step restore, never an AI reconstruction.

Part 03 · Two surfaces

The web app is the same conversation with a screen

Sous has a full web dashboard — but it isn't a second product. The SMS webhook and the web chat call literally the same engine function, and every message from both surfaces lands in one thread per person, tagged only with the channel it arrived on. Start a thought on your phone in the checkout line, finish it at your desk; the web chat shows your texts inline, marked "via text."

Text message from your phone Web chat on the dashboard Conversation engine the same function, verbatim One thread both channels, in order writes after a web session goes quiet, a recap is texted to your phone
One conversation, two surfaces. The claim is verifiable in code: both entry points invoke the same engine and write to one shared thread.

The loop closes in both directions. If you plan in the browser, Sous waits until the web conversation has been quiet for a while, then texts a short recap to your phone — watermarked so nothing repeats, and guarded so a recap can never assert a change that didn't actually happen.

The dashboard itself is a live window onto the same data your texts are built from: Your Week with every meal on an editable card (drag a meal to another day; the plan updates for everyone), a toggle that overlays your real calendar onto the menu, per-person ratings, an aisle-organized grocery list, a distraction-free recipe companion scaled to your household's exact serving count, and a Preferences page that shows everything Sous has learned in conversation — human-editable, because the AI's memory should be correctable.

Sign-in is passwordless: Google, Microsoft, Apple, or an emailed magic link. Onboarding verifies your phone with a one-time code, captures your timezone silently from the browser, and connects your calendar read-only if you choose — then hands you off to the conversation, where everything else happens.

Part 04 · The rhythm

Sous texts first — on your clock, in your timezone

The week has a rhythm, and Sous initiates every beat of it. "Sunday morning" means 9 a.m. where you live, not 9 a.m. on a server — a rule enforced all the way down: every wall-clock helper in the codebase requires the household's timezone as its first argument, so a Pacific household getting a 3 a.m. text is a compile error, not a runtime surprise.

Your planning moment in your timezone Calendars read Google · Outlook · ICS Week outline texted you confirm or correct Plan generated Claude Opus + recipe corpus Proposal first batch-cook day question Menu & groceries released on your yes Instacart one tap, shoppable list Wed. check-in + conflict scan
One week with Sous. The flame-colored beat is the only one where a model invents; every other beat is deterministic choreography around your answers.

At your planning moment, Sous reads your merged calendars — Google Calendar via read-only OAuth, Outlook, or any subscribable ICS link, with real recurring-event expansion — and classifies each day of the coming week: normal, busy, away, dinner out, solo-parent night. A week-long "Shore Week" banner sets the scene without cancelling dinner; a single-day wedding does. Sous texts you that read of the week as a friendly itinerary and asks you to confirm, because calendars are never 100% accurate. If you go quiet, it nudges once — the clock resets whenever you reply, because a person mid-correction hasn't vanished — and after a day it proceeds on its own, so a plan always arrives. Quiet hours are enforced in your local timezone: Sous does not text at night.

Delivery is proposal-first. Before you see a single dish, Sous names the batch-cook anchor — the meal whose components feed two other dinners that week — and asks which day suits cooking it. The full menu releases on your yes; the grocery list follows on your thumbs-up. Refinement happens before you're attached to a menu, and timed backstops make sure the question is never a blocker. From the list, one tap builds a shoppable Instacart cart from whatever you haven't checked off.

Wednesday evening, in your local window, Sous asks about a specific dinner it knows you cooked — in two-adult households, each person gets asked about a different night, and whoever was down to cook gets an effort question, capped so it never feels like an interrogation. Just before that check-in, Sous silently diffs your live calendar against the snapshot the week was planned around. If something new landed on a cooking night, it raises the conflict only after you've answered — or the next morning if you never do.

Restraint is a feature

The conflict detector's rules are mostly about not texting: at most one conflict message per day, each conflicting day mentioned once ever, nothing early in the morning, silence if the plan was already reworked today. Conflicts are never stored — every pass re-detects against live data, so a conflict you quietly resolved simply stops being mentioned.

Under it all, three independent scheduler engines, on separate infrastructure, fire on their own clocks against the same pass. They can all run at once without double-texting anyone, because every state transition in the weekly loop is an idempotent compare-and-swap: extra runs are free, and an outage of any one engine is covered by the other two. A pure, property-tested "next-wake computer" calculates the earliest instant each household could possibly be owed work, so at scale Sous wakes exactly who's due.

Part 05 · The library

Recipes are data, with provenance

Everything a user experiences rests on one Postgres database — 27 models under a fully versioned migration history, hosted on Supabase and accessed through Prisma. The design principle throughout: store facts with provenance, and never let the AI be the only safeguard between a stated allergy and a plate.

The meals themselves draw on an owned recipe corpus, ingested from professionally tested recipe cards across multiple publishers. Every recipe carries its source, its ingredients and steps as structured rows, batchable components with food-safety metadata — safe internal temperatures, fridge life, reheat guidance — and a lifecycle: recipes enter through an automated quality gate, get arranged into real dinner plans, and are promoted or retired purely by what happens at the table: ratings, swaps, and check-in sentiment.

Households are first-class too: two adult logins per household, partner invites whose link tokens are stored only as hashes — and the link alone is never enough, because the joiner must also verify the invited phone number. Each member controls only their own notification switches; muting your partner's texts is structurally impossible, not merely hidden in the UI.

Part 06 · Trust

Built so the AI never has the last word

An AI that plans your family's dinners holds two kinds of trust: what it cooks and what it says. Sous puts an independent, deterministic gauntlet behind each.

WHAT SOUS COOKS WHAT SOUS SAYS Drafted week corpus recipes + generated ideas Retrieval hard-filter EU-14 allergens · dietary flags, pre-model Generation repair pass flagged plans re-run under hard constraints Plate-side assertion pure code · audits every meal, every plan Your week Model's drafted reply what Claude wants to say Commit-ledger gate claims checked against actual DB writes Your phone unbacked claims replaced with honest copy
Two gauntlets. Every plan is checked by three independent layers — the third is pure code, no network, no model, designed to fail closed, and being staged into enforcement through a shadow-mode rollout. Words pass the truthfulness gate before sending.

Equal engineering went into not over-alarming. The allergen parser grades severity from how people actually talk — "my son is allergic to peanuts" is treated as a hard constraint the assertion checks every plan against; "we're trying to cut back on dairy" doesn't strip a week of dinners — and its false-positive fixes are documented in the code: coconut milk is not dairy, peanut-free granola is not peanuts, butter lettuce is not butter. A safety feature that cries wolf gets switched off; this one is engineered not to.

Reliability is keyed on Sous's own data

Privacy is structural


Appendix · The roster

The stack, by name

One deliberately boring foundation, so the interesting engineering can live in the product.

Twilio SMS in & out · receipts Anthropic Claude three model tiers Langfuse AI observability Your phone SMS surface Your browser dashboard surface Resend sign-in email The Sous app Next.js — one deployable, on Vercel webhooks · conversation engine tRPC API · scheduler passes Calendars Google · Outlook · ICS Instacart shoppable cart link Stripe subscriptions Inngest durable queues & cron Supabase Postgres 27 models · every fact SMS webhooks & receipts paced sends · verify grounded prompts, every call metered redacted traces dashboard · web chat magic-link email reads events · read-only grocery items → cart link subscriptions · webhooks events (opaque IDs), durable runs call back one source of truth
The whole roster on one map. The app is the only hub — no service talks to another directly, every edge is labeled with what actually flows, and the only data that leaves for AI is a grounded, metered prompt.
Next.js 14 + TypeScriptOne app: marketing, onboarding, dashboard, webhooks, and schedulers ship together.
VercelHosting and continuous deployment — the whole product builds and ships together, gated by CI.
Supabase Postgres + PrismaOne database, 27 models, a fully versioned migration chain.
tRPCEnd-to-end typed API between the dashboard and the same data the texts are built from.
Anthropic ClaudeThree model tiers — Opus for planning, Sonnet for voice and extraction.
TwilioSMS in and out, phone verification, and per-message delivery receipts.
InngestDurable execution: serialized per-household lanes, retries, and the recurring tick.
Google · Microsoft · ICSCalendar hub: read-only OAuth, Graph API, and a dependency-free ICS parser with recurrence expansion.
Instacart Developer APIUnchecked grocery items become a shoppable cart in one call.
StripeSubscriptions, with an idempotent webhook ledger.
LangfuseTracing for every AI call — payloads redacted by default.
ResendMagic-link sign-in email. No passwords, anywhere.