ashley cogell

Spread

An allergen-aware event hosting app. Built, functional, beta-ready. Deliberately unshipped.

The problem

Hosting a dinner party for people with food restrictions looks like a data problem wearing a hospitality costume.

The host is holding a set of constraints: one guest is celiac, another has a tree nut allergy that is severe rather than inconvenient, a third is avoiding dairy this month. Then they are holding a set of dishes, each of which decomposes into ingredients, each of which may or may not contain the thing that matters. The join between those two sets gets done from memory, at 6pm, with the oven on.

People solve this badly today. They solve it with a group text, a spreadsheet, or the assumption that the guest will speak up. The failure mode of that last one is that guests with serious allergies quietly stop accepting invitations, which is the real social cost and nobody measures it.

Spread was my attempt to do the join properly. Building it is how I learned the join is the problem.

What it does

A host creates an event and invites guests. Guests enter their own restrictions rather than having the host relay them secondhand. The host builds a menu from recipes, each of which resolves down to a structured ingredient list. The app surfaces conflicts: which dish collides with which guest, on which ingredient.

Recipes enter the system three ways: a seeded corpus, a URL import, or a photo. Imports get parsed into the same structured ingredient shape as everything else, so a recipe scraped from a food blog and a recipe from the seed corpus are the same kind of object downstream.

Bar stations work the same way. Rather than build a parallel drinks model, a bar is the existing recipe and ingredient structure with a kind marker, plus a fork-on-add pattern so a host can adjust a base recipe for their event without mutating the shared original. One data model, two surfaces. That saved an entire second implementation of allergen resolution, which matters because allergen resolution is the part you cannot afford to have two of.

How it is built

  • Client: Expo and Tamagui, targeting iOS with a web build for the beta
  • Backend: Supabase for auth, Postgres, and storage
  • Import pipeline: Anthropic Batch API for parsing imported recipes into structured ingredients, batched because import is asynchronous by nature and the cost profile fits a job nobody is waiting on in real time
  • Seed data: TheMealDB as the initial recipe corpus, with URL and photo imports scoped user-private by default
  • Web deployment: Cloudflare Pages

The engineering I found most interesting was ingredient normalization. “Butter,” “unsalted butter,” and “2 tbsp butter, melted” need to collapse to one node in an allergen graph, and the graph has to be conservative in the right direction. It is a modeling problem with a clinical flavor. It is also, in retrospect, where the whole thing quietly went wrong.

Where it stands

The app works. Events, invitations, guest-entered restrictions, recipe import, ingredient resolution, conflict surfacing, bar stations. The web build is deployed. The infrastructure exists to run a beta with roughly twenty to fifty hosts.

It is not in the App Store, and I do not currently intend to put it there.

Why I have not shipped it

Two reasons. Neither is technical.

1. Allergies are not categorical. The interface is.

The core operation is a join: guest restrictions on one side, dish ingredients on the other, conflicts in the middle. Doing it well means getting both sides into clean, comparable, structured shape.

That is the problem. Not that the data is dirty. That the cleaning is itself the distortion.

An allergy is a dose-dependent, individual, context-sensitive response, and it resists every simplification the schema requires:

  • It is not one allergen. “Tree nut allergy” is a label covering people who react to cashew and pistachio and eat almonds without incident. Cross-reactivity clusters exist but they are patient-specific. The field says “tree nuts.” The person means something narrower, and they know exactly what.
  • It is not binary, it is a threshold. Eliciting doses vary between individuals by orders of magnitude. A column holds “contains” or “does not contain.” A body holds “how much, in what matrix, on what day.”
  • The threshold moves. Exercise, alcohol, illness, poor sleep, and NSAIDs are recognized cofactors that lower reaction thresholds. Food-dependent exercise-induced anaphylaxis is a named clinical entity. Same person, same dish, different Tuesday, different outcome.
  • Preparation changes the allergen. In oral allergy syndrome the cross-reactive proteins in raw fruit are denatured by cooking. Raw apple is a problem, apple pie is not. The ingredient string is identical.
  • The label hides the mechanism. Celiac disease, wheat allergy, and non-celiac gluten sensitivity all get typed into one field as “gluten.” Different mechanisms, different tolerances, different stakes.
  • Past severity does not predict future severity. Someone whose reactions have been mild has no guarantee about the next one. That is standard allergy guidance, and it is why the whole thing cannot be honestly risk-scored either.

So the app asks a guest to compress all of that into a field. The compression is lossy, and it is lossy in the direction that matters. Then the app joins the compressed thing against an ingredient list and renders a verdict carrying the visual confidence of a checkmark, read at a glance, by a host who is cooking.

Better data does not fix this. That is the part I had to sit with for a while. I could license a verified ingredient corpus with structured allergen fields and cross-contact information, and it would improve exactly one side of the join. The other side is a person’s own understanding of their own body, which is always more detailed than the form, and which no schema I write is going to hold.

A disclaimer does not fix it either. A disclaimer is a legal artifact. The green checkmark is a product artifact, and the product artifact is the one people act on.

The gap between what the interface asserts and what the underlying reality supports would be an acceptable design tension in most products. Here the consequence of landing in that gap is an ambulance.

2. The guest data is health data held by the wrong party

The quieter problem, and structurally harder.

A guest’s allergy profile is health information about that guest. In Spread it is collected inside an event created by a host, so the data subject and the person who benefits from the data are different people, and neither of them is me.

  • I sit outside HIPAA, which is worse rather than better. No defined framework tells me what my obligations are, while state health privacy statutes and consumer protection law still apply and vary by state.
  • Consent is structurally muddy. A guest thinks they are telling a host about one dinner. What they have actually done is create a persistent health record in a stranger’s database.
  • Retention, deletion, and breach notification need answers before launch, not after. A breach here does not leak preferences. It leaks a list of named people and their medical vulnerabilities.

That requires an entity, a consent flow, a retention policy, a deletion path that actually propagates, insurance, and counsel. Not costs you can defer until traction justifies them. Costs you carry from the first user.

What the shippable version looks like

If the join cannot be trusted, the product cannot be the join.

There is a version of Spread that is honest. It stops asserting and starts relaying. The guest describes their restriction in their own words, at whatever level of detail they choose, and the host sees it verbatim, attached to the dishes it might touch, with the full ingredient list and the provenance of every ingredient visible. Nothing is marked safe. Nothing is cleared. The app moves information between two people and leaves the judgment where it belongs, with the two humans who can ask each other a follow-up question before anyone eats anything.

That product is defensible. It is also much less interesting, because conflict detection was the reason to build it. What remains without it is a group text with better formatting and a real data model underneath.

I have not concluded that version is not worth shipping. I have concluded the version I built is not.

What I took from it

I build things for people navigating complex medical situations. That is the throughline in my clinical research work and it is why this existed in the first place. The instinct that made it worth building is the same one that stopped it. If someone is going to trust the output, the model behind the output has to be able to carry the weight of the thing it represents.

Spread is in my portfolio as finished engineering and an unfinished product, and I think that pairing is the honest one. The hard part of building in health-adjacent space was never the Expo build or the ingredient graph. It was recognizing that I had modeled a continuous, individual, shifting clinical reality as a boolean, and that the boolean was the product.