Tripshepherd - Native Guide App
One tour-day flow that holds four kinds of tour, across 22 cities
Tripshepherd runs tours in 22 cities with a roster of about 240 guides. I wrote the requirements, flows and code prototypes behind the native app they work from, and built the app's design system in Kotlin.
- Year
- 2026
- Role
- Product Designer
- Team
- Engineering, Operations

Overview
Tripshepherd is a tour operator: 149,045 bookings across 22 operating cities, a roster of about 240 guides, and a fleet of 74 vehicles. Guides work from a native app on their phone, on the road, between pickups, often with one hand.
I was the product designer on that app. I wrote the behavioural requirements, drew the flows, built the code prototypes engineering implemented from, and wrote the app's design-system module in Kotlin.
What I made
- Behavioural specs, 10,899 lines, with acceptance criteria per screen
- 36
- Deployable React prototypes, 14,793 lines, every state a screen can be in
- 9
- Kotlin files: the app's Compose design system and Host Mode screens
- 84
Why a guide app is harder than it looks
A tour is not a calendar event. A guide can be rostered for a shift with no tour on it, so scheduled and assigned are different states and both have to be designed. A guide asking for three days off can strand tours already assigned to them, and every one of those has to be handed to someone else before the request can even submit. A guide picking up an open shift has to be certified to run that specific tour.
Most of the work was turning rules like these into states a guide could read while driving.
The tour day
The tentpole flow is the working day: five phases from opening a tour to closing it out. Pre-tour, pickups, tour in progress, drop-offs, complete. Each phase is its own route with its own guard, so a guide cannot reach drop-offs with unresolved pickups, and cannot end a tour with stops still open.



One flow, four kinds of tour
Driving, walking, shuttle leg and juggle are four different shapes of day. Rather than four implementations, they share the same five phases through phase-aware props: the pickup carousel, the route card, the skip-reason modal and the list control all take the phase rather than being duplicated per type. A walking tour sets no drop-offs and the flow adapts around it.
A shuttle booking defaults to three legs but any of them can be removed, so the leg set is treated as a list of one to three rather than a fixed shape. A shuttle driver is a guide holding a shuttle certification, not a separate user type with a separate pool. Certification is a layer, not a role.
Three operational rules that changed the design
- Coverage eligibility is a gate, not a list. Recipients are filtered by certification and conflict up front, so a guide is never offered a shift they cannot legally run. The gate is asymmetric: it applies to whoever claims, not to whoever gives away.
- One waiver per booking, signed by the lead guest for the whole party. That dropped per-companion signer records from the backend and propagated to every surface that collects them.
- The post-completion stats screen was removed. The stats still log to operations. The guide just doesn't need them at the moment they finish a tour.
Iterating in the open
Schedule v2 was worked out across a single day. A week grid was rejected in favour of a month calendar, then refined down to the final layout. The city filter went through three treatments before it landed on the canonical one.









Parity with the native app
The web app was rebuilt with the native app as the source of truth for every API contract, payload and business rule. That made parity measurable rather than aspirational: 75% at the formal measurement, 49.5 of 66 wired screens scored full, partial or missing, and around 81% after the alignment sweep.


A reversal, from one guide
The app opened on an AI-first command centre. In the June first-run study, a working guide testing it on a live revenue tour read it as a threat.
I know neither myself nor other guides will want a company app doing anything with my personal accounts. That is a breach of privacy.
The synthesis recorded it as disconfirming rather than softening it. It contradicted an assumption that AI-forward framing reads as modern and valuable. To the person who would actually use it, it read as surveillance.
How it was checked
Sixteen screens across four iPhone viewports, 64 assertions per run: no horizontal overflow, sticky header on screen, bottom action inside the viewport, zero console errors. The script exits non-zero on failure, so a layout regression stops the build rather than being spotted later.
Not every decision was to build. Three of the four rulings in the July readiness audit were decisions not to: ship the waiver copy knowingly as an operations dependency, ship without availability and delete the dead intake chain, keep dispatch chat hidden to match native. Each was recorded with what was accepted, not just what was fixed.