Oscar

Oscar was my dog — a toy poodle who got old the way they all do: slowly, and then all at once.

The last stretch was the hard part. His back legs started to go, and some mornings he just couldn't get himself off the bed. The dementia came too — there were moments he didn't seem to know quite where he was, in rooms he'd known his whole life. But every evening, when the food came out, he still lit up like a puppy. I held onto that one.

Here's what nobody warns you about: it isn't the vet visits that wear you down. It's the months in between. You're the one home with them, every single day, quietly trying to answer an impossible question — is he actually worse than last week, or am I imagining it? Is this just a bad day, or the new normal? And then you finally get to the vet, and the best you can manage is "he's been a bit slower lately" — when what you needed was to walk in with the whole picture.

I build data systems for a living. I spend my days taking messy, real-world information and turning it into something you can actually see. And it kept nagging at me that the one record I cared about most — Oscar's — was the one I was keeping worst. It lived in my head, in my camera roll, and in a fog of half-remembered worry.

So I built the thing I wish I'd had while he was still here.

What it does

Oscar is a calm daily companion for anyone looking after a senior or chronically-ill dog or cat. It's a twenty-second check-in you do once a day — how they're moving, how they seem in themselves. Those small notes add up into a line you can actually see: holding steady, or slipping. When something feels off, it looks back through everything you've logged and tells you this reminds me of a stretch back in spring — the kind of pattern you'd never hold in your own head. And when it's time for the vet, it hands you a clear, honest summary instead of a shrug.

It supports your vet. It never tries to be one.

How I built it

The whole thing hangs on a single decision. A pet's history isn't one shape — it's facts (the meds, the rehab plan), it's change over time (how they moved, day by day), and it's the fuzzy, human stuff: a rough morning that feels like one from weeks back. Most people would reach for three different databases to handle that. I kept all of it in one — AWS Aurora PostgreSQL with pgvector. Relational tables, time-series check-ins, and vector search across both text and images, all sitting on the same rows. Ask it "when did this happen before?" in plain words, and it finds the match. Show it a photo, and it pulls up the days he was moving like that. No second database, no sync, no glue holding it all together.

The AI runs on Amazon Bedrock — Claude writes the gentle narration, and Titan turns journal notes and photos into vectors. The front end is Next.js on Vercel; the photos and clips live in S3.

But the rule I refused to break was this: it never diagnoses. Every clinical number is worked out by plain, boring, testable code — the same way, every time. The language model never decides anything medical. It only ever puts the data into kind words and raises the questions worth taking to your vet, and every line it writes is checked against a guardrail before it reaches you. A health-adjacent AI kept safe by how it's built, not by hoping the prompt behaves.

What I learned

That the hardest part of building with AI is often deciding where the AI isn't allowed to go. Letting a model narrate a trend is lovely; letting it grade your dog's health would be reckless. Drawing that line cleanly — code owns the facts, the model owns the words — turned out to be the most important decision in the whole project.

And on the technical side: that one good database really can do the work of three, and that "vector search" stops being a buzzword the second it hands you back a memory you'd genuinely forgotten you had.

What fought back

Plenty did. Setting my environment variables from PowerShell quietly slipped an invisible character onto the front of my database URL — one byte I couldn't see — and I spent an evening watching it throw "Invalid URL" at a URL that was perfectly valid. Bedrock turned its nose up at every model name until I worked out it wanted a specific regional prefix. Aurora would hang for two full minutes and time out every time my home IP changed. And then the small, quiet one: the clips of Oscar had my family's voices in the background, so I had to strip the audio out of every one before it went anywhere near the internet.

For Oscar

I never got to use this for him. He was gone before I wrote the first line. But the photos and the clips in the demo are real — they're him. The slow walks, the afternoon naps, the evening he still got excited about dinner. I named it after him, and I built it so that the next person sitting at home, watching their old friend slow down, has something better to hold onto than memory and worry.

In loving memory of Oscar. 🐾

Built With

Share this project:

Updates