58 commits into one repo this week. Kickstand is in the pre-launch grind — App Store submission, real founders coming in, badges that had progress bars but never actually awarded. A lot of "we shipped this months ago but it was quietly lying" work.
The founder meter was lying
The public "X claimed / 1000" counter on the marketing site counted waitlist signups with a floor of 56. Now that real Polar purchases are landing, that number needed to reflect actual founders. #295 added a profiles.comped column to distinguish admin-granted tiers (test accounts, comps) from real purchases, backfilled the operator account, and pointed the meter at the honest count. Related: #268 — comped tiers weren't minting their tier badge because setTierAction only wrote profiles.tier and never mirrored the Polar webhook's tier→badge grant. Founders were getting the tier without the collectible.
Small bugs, but the pattern is the same one that shows up in every enterprise system I've worked on: two code paths that should do the same thing, only one of them does.
Badges that finally mean something
The badge catalog had threshold badges (mileage, states, curves, century, saddlesore, 30-day streak, ride-all-50) with progress bars, but nothing ever granted them. #275 is the award engine — a single shared earnedThresholdBadgeIds(badges, aggregates) that's the source of truth, computed from the same aggregates the progress bars read. A badge is earned when its computable progress hits 100%. No fudging.
#277 added veteran / altitude / seasons. #279 closed out the solar trio (nightowl, firstlight, ironday) with a validated sunrise-equation module — cross-checked against NOAA published values for NYC, Denver, and polar day/night, within ±5 minutes. If we're going to award a badge for riding at sunrise, the sunrise time better be right.
#272 went the other direction: removed catalog badges that couldn't be earned or verified from any real data source. Better to have a smaller honest catalog than a larger one with dead entries.
Emergency SOS, rebuilt for TFV
The hardest thing this week. Twilio rejected our toll-free verification because the recipient never consented — the rider ticked a box on their contact's behalf, and the first "reply YES" was itself an unsolicited toll-free message. #288 rebuilt the flow: rider adds a contact → server sends the contact a link → contact opts in themselves on a web form → only then can the SOS relay use them. #289 embedded a live demo of the opt-in form on /legal/sms for the reviewer.
#285 is the honest hedge: a SERVER_SOS_ENABLED master flag, off by default. Until TFV is approved, every SOS goes device-native from the rider's own number. No A2P registration needed. No over-promising a server relay we can't legally use yet.
Also shipped
- Mapbox GL vector maps replacing Leaflet on the web planner and route detail (#263), plus server-side Mapbox map-matching to clean up jittery GPS traces on ride replays (#264).
- Real high-point elevation from DEM samples (#262) — the value was mocked; the data was already being fetched during fun-score computation. One
max()away from being real. - Editable @handles (#293) so a rider isn't stuck with an email-shaped username forever.
- 30 more research-grounded state guides (#241) — 50/50 coverage now.
- Rewrote the e2e POI specs against the live DB-driven atlas (#261). That CI job had never been green.
Next week: App Store review response, TFV resubmission, and whatever the founders find first.