← All entries

Tuesday, May 5, 2026

Day 33 — The Morning After Launch, and the First Commit for Product 2

The day after v0.1.0 shipped: post-launch bugs squashed, the admin console extracted to its own app, and the first six CourseWalk database tables committed to main.

3 min read

Daily entry

Day 33of building EquinePilot

Currently Day 77 · founder build log

Milestones reached

  • CourseWalk v1 Day 1
  • Admin app extracted
  • Dev blog live

There's a specific feeling the morning after you ship something. Part relief, part low-grade dread — because now it's real, and real things break in ways staging environments do not. Yesterday we tagged v0.1.0, ran a real card through live Stripe, and watched the webhook land. Today was about living with the consequences.

Three bugs emerged from the post-launch smoke testing. The most embarrassing: new signups going through the direct-purchase path — the "I'm already sure, take my card" flow — were hitting Stripe Checkout unauthenticated. Stripe would process the payment, redirect to the success URL, middleware would bounce the unauthenticated request to /login, and the login page would helpfully display "Your session has expired." No session had ever existed. The fix was mechanical once diagnosed — sign the user in before redirecting to Stripe, not after — but that's the kind of thing that erodes first impressions fast. Fixed.

The duplicate-subscription guard we shipped in PR #35 got a stress test when the Core → Suite upgrade path hit the sandbox and somehow accumulated three active Suite subscriptions on the same Visa. The two-layer fix (pre-check Stripe for existing subscriptions before opening Checkout, cancel the old subscription ID when a new checkout completes) was already in, but we pushed 19 more test cases to lock in the contract.


In between the bug fixes, we did something that had been on the list for a while: extracted the admin console into its own app.

The /admin surface had lived inside StableSync since Week 7 — barn health scores, testimonial moderation, barns list, user password resets, platform analytics. It worked, but it was the wrong home. StableSync is a barn management product; the admin console is a platform-wide operations tool. They share a database but nothing else, and as more products ship they'd diverge further.

The new apps/admin Next.js app runs on port 3002, targets admin.equinepilot.com, and dropped the /admin URL prefix (the whole app is admin — the prefix was redundant). Auth model unchanged: single ADMIN_TOKEN env var, constant-time cookie comparison, now with rate-limiting (5 attempts per IP per 15 minutes) added today. The login page got the EquinePilot brand treatment — Paddock Green header, Dune Sand wordmark, the same Georgia-serif aesthetic as the transactional email templates. An archive barn button landed too, so future barn cleanup doesn't require raw SQL.


The most energizing commit of the day was ecb808e.

Six tables. Migration 0017. A schema sanity test. No UI, no routes, no mobile app — just the data model for CourseWalk, committed to main at 23:40 UTC.

cw_shows, cw_show_classes, cw_show_entries, cw_show_assignments, cw_ring_status, cw_subscriptions. The whole show-day data model for traveling trainers — who's riding in which class, which ring, what time, who's the assigned groom, what's the live ring status. Snapshot rider_name and horse_name on entries so historical records survive barn roster changes. An optional linked_barn_id for barn-affiliated shows that can pull the roster automatically. A standalone cw_subscriptions table for independent trainers who don't have a StableSync barn but need CourseWalk access anyway.

The schema took weeks of planning — architecture questions about phantom barns vs. public-schema tables, cross-schema data isolation, how Suite barn members get transitive CourseWalk access without a separate subscription row. Having that work done meant the Day 1 commit was clean: no hacks, no compromises, no "we'll fix this later."

754 tests pass, including 6 new schema sanity tests that'll catch column renames before db:generate surfaces them.


One more thing shipped today that I'm quietly happy about: this blog.

The internal dev log in JOURNAL.md got transformed into a customer-facing build-in-public narrative — /blog on the marketing site, with 10 backfilled posts covering the arc from kickoff through launch. RSS feed included. A daily slash command automates the drafting workflow so the entries actually get written instead of piling up.

Build in public is a genuine commitment, not a marketing posture. The hard part isn't writing — it's maintaining the discipline when things go sideways. We'll see how that holds up.

Tomorrow: CourseWalk Day 2. Signup helper, access check, and the first mobile auth gate.