Building for what happens during the ride
Surfaces 1 and 2 live with Racer Sportif. Surface 3 begins — live fleet tracking, Support Beacon, captain controls. The ride itself.
Read on LinkedIn| Phase | Pillar set | Period | Status |
|---|---|---|---|
| 01Foundation and Vision | Admin PortalPillars I – IV | Mar 14 – Apr 11 | Shipped |
| 02Pivot | Rider Desktop PortalPillars I – IV | Apr 11 – Apr 28 | Shipped |
| 03Launch Readiness | VoC / MT / IAPillars I – IV | Apr 28 – May 12 | Shipped |
| Minor iterative releases shipping continuously | |||
| 04Rail 3 — Tactical Ride | Mobile / Live RidePillars I – IV | May 12 – ongoing | Initialized |
Real decisions, real commits, in the order they happened. The Bedrock shows what got committed; this log shows why — and when the build's reality pulled the Pillars somewhere new.
Surfaces 1 and 2 live with Racer Sportif. Surface 3 begins — live fleet tracking, Support Beacon, captain controls. The ride itself.
Read on LinkedInBackground GPS is unavailable to Service Workers on Android and blocked by WebKit on iOS. Capacitor considered and rejected — the parts of Rail 3 that are technically hard are exactly where Capacitor relies on native plugins anyway. Proof of concept is Sprint 0 + 1 of the production build, not a throwaway. Validation runs on real Android devices during a real Racer Sportif ride.
Roadmap as code: roadmap.md in the repo drives roadmap.html on the site, marked-up by shipping status. Multi-Club Phase 1 and Club Growth tagged complete; Android PoC promoted to next major.
Edge case from a minor enhancement. Ride creation hung when the route preview canvas was tainted by a cross-origin image. Fixed in one commit; QR converted to an on-demand action for all user tiers as part of the same patch.
Commit 29d6e47QR was right but the block placement was reading wrong. Replaced with a clear "Share Ride" button between stats and roster. Same capability, sharper affordance.
Commit 81f187eA friend in Ireland configured Vechelon for his club — not adopted yet, but seeding rides on real Irish routes for presentation. Speaking with another Toronto club about a varied opinion on what real club pain looks like next to Strava's gravity.
The participant_insert_policy tightened so admins cross-inserting participants must clear an explicit gate. Multi-tenant RLS is one of those things that's invisible when it works and catastrophic when it doesn't.
Admins can now add a crew member (captain or support) to a ride from RideBuilder. The notify-crew-added Edge Function sends them an email confirming role and ride details. Half feature, half operational hygiene.
Paginated upcoming-rides feed for admins, with the dashboard's Upcoming Scheduled stat card wired to it. The admin surface now has a dense view of what's coming up across the club beyond the summary.
Commit fbcc491Start/meetup labels in the ride side sheet now link to Google Maps with a visible brand-coloured affordance. Riders get a one-tap path to navigation from the ride view, and the route is accessible directly.
Commit 5749b17Tenant banner_url renders in the RiderHome hero. Browser favicon is the Vechelon half-chainring SVG (admin) and the tenant logo (rider). Platform admins now see the admin layout on every club portal they visit — one consistent surface across all tenants.
The dedicated cross-tenant admin surface — provision new clubs, view all rides, manage platform-wide settings. The piece that makes Vechelon a platform, not just a product.
Commit 1917996Platform Admin gets a real checklist for spinning up a new club: name, subdomain, branding upload, admin invite, magic link, validation. Provisioning is one of those things that has to be boringly reliable before live trials can fan out.
Commit e3064eaMagic links carry tenant context end-to-end. ensure_account_exists respects which club the rider is being invited into, so a single email coming from Racer Sportif and an invite from Bikes & Beers don't collide or drift. The plumbing that makes multi-tenancy quietly correct.
The PRD isn't there to be read — it's there to keep AI's speed pointed at your vision. Stitch pulled Strava patterns into Vechelon; the committed Pillars caught the drift immediately.
Read on LinkedInRiders can now submit feedback in-product without leaving the flow. The Edge Function gates submission and writes to the structured VoC store. VoC moves from drafted Pillar to running code.
Commit 1840953Riders can share a ride socially. The shared URL carries an HMAC-hashed rider identity gated by magic link, preserving identity without leaking it. Social distribution without giving up the membership boundary.
Commit 6fdc870QR code centre logo reads from each tenant's logo_url instead of a hardcoded Vechelon mark. Browser tab favicon set dynamically per tenant. RSVP button turns green and locks on success. The small touches that make a multi-tenant product stop feeling templated.
Subdomain-slug routing replaces the LIMIT-1 tenant lookup (W124). Vercel routes the right SPA to the right subdomain (W125). Bikes & Beers seeded as a real test tenant (W128). invite-member Edge Function uses host-aware portalBase URLs (W139). The plumbing that makes racersportif.vechelon.ca route to Racer Sportif, every time.
Search-path on Postgres helpers locked (W141); tenant-scope on ride_summaries view enforced. analytics_events INSERT policies tightened with an event-type allowlist + tenant scoping (W142). Guest QR + RSVP moved to Edge Functions with tighter anonymous RLS (W143). Three weeks of "we'll harden it before live trials" landing in a single afternoon.
Voice of Customer, Multi-Tenancy, and Innovation Accounting — the three capabilities critical for the next phase of live trials by real clubs. Pillars I–IV authored for this layer the same week. The transition from "MVP live with one club" to "platform ready for many."
Eight inter-related ships in one day. analytics_events table created (W120). Client-side IA event instrumentation (W131). Server-side IA — broadcast_copy + ride_closed trigger (W132). Platform Admin RLS read-all bypass with privacy-safe view (W126). Cross-club email validation in invite-member (W127). platform_admin + last_voc_submission columns added to accounts (W119). The IA pillar's specs becoming a measurement instrument, not a demo.
Six decisions landed: D33 changes signOut scope from local to global so the server-side session is actually revoked. D36 wires MobileMenu to real auth state. D37/D38 enforce roster email-strict uniqueness and admin email visibility. D39 compares account_id instead of row id for RSVP state. Each one was a small bug; together they're the difference between "looks logged out" and "is logged out."
A lot of hours fixing login flows — PKCE auth flow handling the ?code= exchange cleanly, eliminating the guest-tier flash, syncing drifted emails, enforcing accounts.email uniqueness. Five commits on the D32 decision in one day. Created a Stride milestone to install Playwright — an open-source UAT layer that will run on production builds. Confidence ladder before live trials.
Interested cyclists from my own club, testing the demo, want their own space to create fictitious rides — "testing me, Saturday hill ride." The schema needs to support a single account across multiple clubs. Pillar V Amendment A-01 (formally dated Apr 11) became the architectural shift that unlocked multi-club use. The first time UAT feedback rewrote the data model.
Vechelon's nesting under productdelivered.ca has outgrown the arrangement. The decision: move to vechelon.ca with per-club subdomains — racersportif.vechelon.ca, bikesandbeers.vechelon.ca. Domain already secured. Not yet executed.
A markety roadmap rendered on the site, sourced from roadmap.md in the repo, so the public roadmap is always one commit out of date instead of one quarter. Decided in principle here; shipped on May 12.
Claude produced an as-is + recommended-state document for the Product Trio — what we're calling an Execution Brief. Fundamentally different from a Pillar V Amendment: an Amendment is a Pillar-altering change; an Execution Brief is recognition that LLD and implementation decisions need to be reconciled with how a capability should now be built. Useful enough to enshrine in The Hands.
The MVE for the club admin demoed to Racer Sportif. The thinnest slice — ride creation integrated with the WhatsApp workflow he already runs — landed as designed. Interest from members to start using it for real. The first proof that the methodology produces something a real human wants to use.
Three rails moving in parallel. An MVE cut to one club admin's existing WhatsApp workflow. First live demo Friday.
Read on LinkedInW77 on the Stride board. The Hands skill enhanced so the agent flags UAT tasks explicitly and moves them to a needs review state in the Review column. A human-in-the-loop signal that's structural, not procedural — the same protective move that Stride itself makes for human-decision blockers.
A heated Product vs Developers debate at AI breakfast about Lean Startup and what an MVP actually is. The honest read: I could cut a thinner slice. Worked with Claude on the Minimum Viable Experience — solve one real problem for one real person (the club admin), integrated into the WhatsApp workflow he already runs. Reprioritisation and focus, not a change in direction.
A dense ship day. Edge-function admin invite with auto-affiliation. Marketing-grade invite email template with feature grid and plain-text fallback. Smart /ride/:rideId landing page (Scenario I) that disambiguates the rider's state before routing. QR codes overlay the Vechelon chainring with high error correction. Member avatars, dynamic tab title from tenant, nav cleanup, maps fix, broadcast URL.
The Rider Portal goes from spec to running. Authentication suite for riders, the rider home, the profile page, the pending HUD that catches new members before they're affiliated, and role-based access gates throughout. Admin invite-member flow added the same day — closing the loop on growth from inside the platform.
Commit 8470881A document-level audit of the gap between current MVP state and demo-ready, framed as a Three Amigos (PM / Tech Lead / QA) analysis. Captured what the BDD scenarios were saying we still needed to wire. The artifact that turned the next sprint's priorities from hunch into spec.
Commit 622c947One of the hard constraints from the Charter — guest phone numbers scrubbed at the 4-hour expiry — becomes code. The constraint that helped define the product, now enforced by the platform.
Commit 41fbbfaWalking the BDD scenarios from Pillar III to compile a list of plumbing gaps. Even comprehensive documentation can't make every connection explicit — nor should that be the goal. Formalising a new Hands process: use the Quality Gate as a hunt for the inference gaps, surface what isn't yet wired, enshrine the use cases the build uncovers.
Claude unavailable until tomorrow noon. Asked Gemini to work on gaps not already assigned to Claude, and to flag any scope creep against the four Pillars. It returned "everything was aligned." Prioritisation, LLD, implementation decisions taken cleanly inside The Hands' space. The clearest evidence yet that the documentation is doing the steering — not the model.
The day of FORCE DEPLOY v1.2.5 through v1.3.0. Vercel build pipeline, output directory, base path, asset loading, tenant routing — each one a small thing, all of them required before anyone external could reliably load the app. Sometimes the unglamorous commits are the most consequential.
Commit f93d4e2The Pillar V Amendment A-01 becomes code. The account_tenants junction replaces the single tenant_id on accounts, with tiered Row-Level Security policies layered on top. The schema that lets one rider belong to many clubs. Foundational for everything multi-tenant downstream.
Decision RP-D-23. Manual-enrollment clubs need an option to show upcoming rides to riders who are pending — so they can see what they're joining before they're approved. show_calendar_to_pending boolean added to the tenants table. Surfaces from the Charter that "guest entry should be frictionless" but only when the club chooses it.
Decision RP-D-15. Replace the single tenant_id on the accounts table with a many-to-many account_tenants junction (status, role, joined_at). The Pillar V mechanism doing what it was designed to do: surface a structural change that the original Pillars didn't anticipate, formalise the deviation, and route it back into the Bedrock with full traceability.
Root static site (parking + marketing) and the /admin React SPA configured to deploy from one repo as two different surfaces. The piece that lets the Vechelon admin run alongside the public marketing surface from day one.
QR-based zero-friction guest onboarding wired up to capture name and phone at scan time. Captain can tap their own QR for self-affiliation. The first end-to-end version of the differentiator: ride entry without an account.
Commit c20902bStride structurally blocks AI tasks behind unresolved human decisions. The Pillar docs surfaced Sprint-0 unknowns without being asked.
Read on LinkedInStatus: "In the mind — not committed." The strategic recommendation from The Hands that informed the Pivot. PWA assumed for everything is wrong; the rider's deepest experience belongs on desktop first (calendar, profile, ride detail), with mobile reserved for the live-ride layer. Voice of Customer added as a first-class layer to harvest UAT feedback at the platform level.
Career announcement. The Epiphany — a six-week gap from "help a gym owner spec it" to "I could have built it myself." Launch of productdelivered.ca. The thesis takes a public form.
Read on LinkedInFirst commit: parking page goes live. Days that followed: iteration on the half-chainring mark, wordmark layout, glow gradient, ring crop, rotation origin, club-colour picker. The visual language of Vechelon takes shape before a single feature commit. Identity locks before product.
First commit f3a862fFirst brainstorm sessions defined Vechelon as the club command centre — the gap that Strava (performance) and WhatsApp (chat) don't touch. Named the app, sketched the logo, picked the hero features. Firmly mapping away from telemetry, leaderboards, and the social patterns of big-brand cycling. The first observations on what enshrined product documentation should look like — the seeds of the Pillars.
AI is collapsing the line between defining and building. Are PMs gaining a bigger toolkit, or trading the strategic seat for it?
Read on LinkedInProduct owns Why/What. Project owns How/When. Reduce a PM to Date Enforcer and judgement is the casualty. The argument that found its proof, two months later, in Vechelon.
Read on LinkedIn
Built from: the Pillar IV (Ledger) MACD entries, the Pillar V Amendments log (vechelon_pillar_5_amendments.md), the Vechelon GitHub commit history (neilstryjski-git/vechelon — 328 commits as of May 12), and personal journal notes. Reverse-chronological, real. Will grow as the build does.