Inspiration
Every federal food recall notice carries a column headed Facilities that Received Affected Products. On the Idaho Foodbank recall of July 2017, that column reads:
Mobile pantries. Community meal sites. School Pantries. Senior centers.
The recalled food went to the places people go when they have run out of options. The notice's instruction to whoever received it is to throw it away immediately, which assumes somebody tells them.
Best By is the somebody. It watches the live openFDA feed against a pantry's shelves, produces the pull list, and calls the households that already took some home.
The reason nobody does this by hand is in the data. A recall names its product by lot code, typed into one free text field:
{
"recall_number": "F-1170-2024",
"product_description": "H&NATURAL 2 PACK! BRAZIL SEED 60 PIECES, 5 GRAMS PER BOX",
"code_info": "No Lot code on label. Expiration date listed as 05/24 and 03/24.",
"classification": "Class I"
}
| Across 614 open FDA food enforcement reports | Share |
|---|---|
| Publish a parseable lot code | 45.7% |
| Publish a parseable UPC | 12% |
| Name nothing checkable at all | 27.0% |
Source: api.fda.gov/food/enforcement.json, pulled 2026-09-14, n=614 open reports.
A pantry cannot match a shelf against that. Neither could we, so we stopped trying.
What it does
The inversion: instead of recovering a lot code weeks later from a record that never carried one, the code is captured at the loading dock. A volunteer photographs the case label once, while the food is in their hands, and a vision model reads the code off the picture. Weeks later, when the notice arrives, matching is exact rather than hopeful.
Then every hit splits into two different jobs, because they are done by different people at different urgencies:
| Job | Who does it | What it looks like |
|---|---|---|
| Still on the shelf | a volunteer, walking the aisle | a printed pull sheet grouped by storage bay |
| Already went home | the coordinator, on the phone | a notice to named households, held until approved |
The second job is the one that protects people, and it is the one no spreadsheet does.
The console is built for the floor rather than for a dashboard. The pull sheet groups by bay, sets the lot code large enough to compare against a stamped can, and carries a sign-off block for who pulled it and how many units came back. It refuses to double count: two recalls naming the same intake lot are one trip to the bay, so what looks like 345 units across 9 lots is really 261 units across 3.
Every notice records its delivery mode and its intended recipient on the case, so a coordinator can always tell which households were reached and which still need a phone call. The roster in this build is representative, on a domain the project controls.
How we built it
Notifying a household is irreversible. Frightening someone about food they never received is the failure that teaches a pantry to ignore the tool forever, so the path to it is gated twice.

NotifyVeto, a StrandsBeforeToolCallEventhook, cancelsnotify_householdsunless the engine returned a confirmed lot match with a distribution record behind it.approval_gate, a StrandsHumanInTheLoopintervention configuredallowed_tools=["*", "!notify_households"], means a coordinator approves before any household is contacted.- The vision read is structured output, and it reports an unreadable photo as unreadable rather than guessing a code.
Lambda runs the unattended pass, EventBridge Scheduler runs it daily, DynamoDB holds the cases, S3 holds the disposal record a pantry can hand an inspector, and SES sends from a DKIM-verified domain. Strands is model agnostic, so the model provider is one config line.
Challenges we ran into
A rerun tried to re-notify ten households. The scheduled pass rebuilds every case from the feed, and a case that had already notified ten households came back as awaiting approval while still holding its ten real SES message ids. Approving it again would have sent all ten a second notice. Terminal states are now terminal, and the test that proves it fails the moment the guard is removed.
The vision model fabricated a barcode. On a 240px image it produced three different confident wrong readings. Small images are upscaled before the model sees them, and the unreadable case now asks for a better photo. An invented lot code is a false claim against a real company.
Naive sums double counted the work. Several recalls name the same intake lot, and a volunteer walks to the bay once. Summing units across cases said 345 units and 9 lots; deduplicating by lot id gave the truth, 261 units and 3 lots. A pull sheet that overstates the work is a pull sheet nobody finishes.
Accomplishments that we're proud of
The delivery states are honest by construction. A notice that reached a household renders as reached; anything else renders in its own colour with both addresses named, and the success state is withheld while any household on a closed case is still unreached. The product can always tell you what it did rather than what it intended.
124 tests pass from a clean clone with no credentials and no network.
What we learned
The useful artifact is not a dashboard. It is a sheet of paper a volunteer can carry down an aisle and tick.
What's next for Best By
SES production access. Intake photography from a phone rather than a laptop. And the pantry network above a single pantry, because a recall almost never stops at one.
Built With
- amazon-dynamodb
- amazon-eventbridge
- amazon-ses
- amazon-web-services
- anthropic-claude
- aws-lambda
- next.js
- openfda
- python
- strands-agents
- typescript
- vercel
Log in or sign up for Devpost to join the conversation.