Forty-six PRs landed on kickstand-web this week. Most of them stack into three threads worth talking about.
Partnerships and referral, end to end
Switchback finally has a real partner program. Five waves, all merged:
- Wave 1 (#99, #101) — referral foundation.
partners,referral_codes,referral_attributions,referral_accrualstables; a pure 25%-net / 12-month / 30-day-hold accrual calculator;?ref=CODEcapture at signup. I regeneratedtypes.tsagainst the live schema and dropped the untyped cast — it had been stale since June 13 and twelve other tables had drifted. - Wave 2 (#102, #103) — partner badges. New
partner_badgestable, apartner-logosstorage bucket, an SVG engine that renders logo/monogram medals, and the IRONSIDE seed. Partner badges show in the profile Badges tab and are pinnable to the showcase. - Wave 3a (#106, #107, #108, #109) — application intake and admin console. Become-a-Partner form, schema + validator + route, best-effort
support@email on submission, and a two-pane admin console that matches the comp (sidebar list + selected partner detail, with the review queue inside the main pane). The split list/detail layout was wrong; one screen is the comp. - Wave 4 (#111, #112) — public surfaces.
/partners/[slug],/events/[slug], the/partnersdirectory, and a site-header "More ▾" dropdown so the program is reachable from every page (it wasn't). - #104 — when a paid account is deleted, cancel the Polar subscription first (cancel-at-period-end). A delete should never strand a rider with a renewing bill.
Push notifications, background GPS, and a Community tab
The mobile side got the build-out it needed to be a real riding app.
- #127 — background GPS recording on a dev/standalone build.
expo-task-manager, iOSUIBackgroundModes:[location]with the always-permission string, AndroidACCESS_BACKGROUND_LOCATION+FOREGROUND_SERVICE_LOCATION, andlib/ride-location-task.ts. Recording now survives a locked screen. This is the unlock. - #119, #120, #121 — push notifications for
badge_earned,record_set,record_beaten, plus rendering the same three in the in-app notification centers. The pushes were firing but the clients were dropping unknown types. - #131–#139 — a dedicated Community tab. The old
/feedscreen is gone; the bottom tab (renamed from Group) is now the social home, built on the existing backend (lib/feed.ts,activity_kudos). No new tables. Newactivity_comments(#133) with aSECURITY DEFINERRPC that resolves the post owner server-side so the recipient can't be forged. Nearby + Global feed scopes (#135) use a sharedassembleFeed()and distance-gate against POI lat/lng. The story rail always renders now, leading with a "Post" tile — sparse follow lists used to hide it entirely.
The bug worth remembering
#130. Every planner request on iOS dev builds was stalling for the full 30s and the Track-a-ride "Curves" button was crashing the app to home. The apex switchback-moto.com 307-redirects to www; RN's iOS fetch stalls following a 307 on a POST because the body doesn't get resent, and the dying request was taking down native networking with it. Hitting www directly returned in ~1s. An hour of staring at the planner code before checking the host.
The AI angle
Almost every PR above is co-authored with Claude Opus 4.8. That's not a curiosity anymore — it's the workflow. The leverage shows up in the wave-sized batches: Wave 1, Wave 2, Wave 3a, Wave 4 each shipped in one week because the spec, migration, validator, route, and UI move together instead of sequentially.
The judgment is still mine. The throughput isn't.