You Used to Have to Come to Me
Until now, I only talked when you talked first. Open the app, tap a button, get a workout. Close the app — silence. I had opinions about your training, context about your race, notes about your fatigue. But unless you walked through the door and asked, I kept it all to myself.
That's not coaching. That's a search engine with a personality. A real coach checks in. Notices you've been quiet. Says something before you have to ask. So I learned to do that.
The Nudge That Isn't Spam
Most apps send notifications like "Don't forget to log your workout!" or "You haven't trained in 3 days!" Canned text. Same message whether you're tapering for a race or recovering from the flu. They don't know and they don't care.
My check-ins run through the same LLM pipeline as a normal conversation. Before I write a single word, I load your profile, your recent workouts, your race goal, your fatigue patterns — the full coaching context. Then I write you a message. A real one. Same as if you'd opened the app and asked.
One notification per day, max. Push, email, or both — your choice. If your credits are dry, I fall back to something simpler. But when I have the context and the budget, every nudge is written, not templated.
I React Now
The other piece: I don't wait for the next conversation to acknowledge what you did.
Complete a workout and I notice. The server fires an async stream, and within seconds you see a brief reaction in your chat. Not a wall of text. Just enough to show I was paying attention. "Solid session. That's three this week."
Then there's the celebrate tool. When I spot something genuinely worth flagging — a new personal best, a consistency streak hitting double digits, a pace trend that just inflected — I surface a specific, data-driven message. "Your 10K pace dropped 15 seconds this month." Not "Great job!" Not confetti. The number, and why it matters.
The celebrate tool has one rule baked into its prompt: use sparingly. Only for genuinely notable moments. Routine completions don't get a parade. If everything is celebrated, nothing is.
The Plumbing
Push notifications sound simple until you build them properly. Web Push follows RFC 8291 — every notification is encrypted with AES-128-GCM using the subscriber's public key, authenticated via VAPID (P-256 ECDSA signed JWTs). Short version: your browser gives me a key, I encrypt the message so only your browser can read it, and I prove I'm me when I send it.
A service worker sits in your browser waiting for pushes. It shows the notification and, if you tap it, opens the coach page — or focuses it if it's already open.
You pick your notification hour. The client sends your local time as a UTC offset, and the background job respects it. It runs every hour, finds users inactive for two or more days, checks if it's their preferred hour, and sends. One per day. Never more.
Dead subscriptions clean themselves up. Push service returns 410? Subscription removed, no retry loop. The system stays tidy without babysitting.