Case study 04 / Product design & development
Rydi
Sometimes you just need a ride.
Broken Compass is better when players encounter each other.
Not every encounter needs to be planned. You shouldn't need an event, a storyline or an introduction to give two people a reason to interact. Sometimes one person needs to get across town and someone else has a car.
That was the idea behind Rydi.
A resident requests a ride. Nearby players who have made themselves available to drive get the request. One of them accepts, and two people who may never have met now have a reason to spend a few minutes together.
Maybe it's just a ride.
Maybe it turns into something else.
But I didn't want the social goal to make transportation unreliable. If nobody accepts within 60 seconds, Rydi calls a city taxi.
Because sometimes you just need a ride.

A simple experience / 01
The experience sounded simple.
Request a ride. Someone accepts. Get in. Get there. Done.
Great.
What happens when two drivers accept at nearly the same time?
What does a driver need to know before accepting? When is the ride actually over? What happens if the rider gets out of the car? What if someone disconnects? When should money move?
The button was easy.
Defining the ride was the product.
- 01Request ride
- 02Available player drivers
- 03Accepted?
- YesPlayer ride
- No, after 60 secondsCity taxi


Identity without a detour / 02
The next action should survive onboarding.
One character can ride under one display name and drive under another, but both identities remain attached to the same authoritative Qbox citizen ID.
The first time someone requests a ride, Rydi asks what to call them. Save the name and the original request continues automatically. The first time someone goes available to drive, Rydi asks for a driver name and selfie. Save the profile and the availability action continues.
Returning people bypass onboarding. Profiles persist between sessions.
Availability does not. After reconnecting, a driver has to intentionally say they are available again. A saved identity is not a current promise to drive.

Information follows the relationship / 03
People need different information at different moments.
Before accepting, a driver needs enough information to decide whether to take the ride. After accepting, the exact pickup becomes relevant. The destination belongs later in the lifecycle. Rydi reveals information as the driver's relationship to the ride changes.
- Before acceptingEnough context to decide
- After acceptingExact pickup location
- During the rideDestination and completion controls
First accept wins / 04
An offer can reach many drivers. A ride belongs to one.
Multiple eligible drivers can receive the same request. When someone accepts, the server rechecks eligibility and assigns the ride atomically. The first valid driver wins. The actionable offer disappears for everyone else.
Later, multiple real drivers tested the race instead of leaving it as a tidy diagram.
Server-authoritative assignment
A platform boundary / 05
The first symptom looked like a Rydi bug.
The original idea was to send ride offers as phone text messages. That was the wrong interaction model. Rydi is an application. Its requests should arrive as Rydi notifications, and tapping one should open Rydi.
Rydi was correctly registered as an external application, but parts of NPWD 3.15.1 only searched the built-in application registry. The failed assumption lived at the platform boundary.
The shared lookup changed so built-in and external applications could use the same notification infrastructure. That fixed the assumption instead of surrounding each symptom with another Rydi-specific patch.
What counts as finished? / 06
A waypoint is easy to measure. That does not make it the ride.
Live Rydi testing showed that reaching a location is not necessarily the same thing as the humans being finished. GPS-based auto-completion came out.
Explicit human intent became primary. Observable human behavior became the fallback. The exit is debounced so briefly stepping out and returning does not end the trip, and the server verifies the actual ride vehicle before binding it.
Testing multiplayer / 07
I didn't have enough humans. So I made myself some fake people.
Synthetic drivers A, B and C could receive offers, accept, decline, disconnect and maintain enough available, busy and current-ride state to exercise the system. That made multiple offers, first-accept-wins, timeouts, stale acceptance and disconnect cleanup testable with one real person.
A second temporary system generated debug rider requests while a real driver was available. That let me experience the entire driver workflow from notification through completion.
The scaffolding existed to answer specific questions. When multiplayer testing became available, the debug ride generator was disabled.
Test the hypothesis / 08
The taxis looked strange. Rydi was the obvious suspect.
Rydi had recently changed taxi integration, so odd ambient vehicle behavior looked related. I compared the taxi resource before and after integration.
Rydi had changed dispatch, per-taxi blips, completion signaling and cleanup. It had not changed NPC or taxi creation, driver assignment, pickup AI, driving style, boarding, destination AI or network ownership.
Rydi had not changed how the taxis drove.
So I didn't fix it.
The investigation did expose a real cleanup problem caused by repeated replicated state updates. That was corrected separately. Observation produced a hypothesis. The evidence determined the intervention.
Payment follows completion / 09
First define when the ride ends. Then move the money.
In Rydi, payment waited until the lifecycle was stable enough to know when a ride had actually finished. Settlement happens after authoritative completion, never at request time.
Fare / v0.5
$10through 30 miles
+$1each additional mile or partial mile
- 30 miles
- $10
- 30.1 miles
- $11
- 42 miles
- $22
- Check the rider's balance.
- Debit the rider first.
- Credit the driver only after a successful debit.
- Attempt a refund if driver credit fails.
- Do not intentionally make the rider balance negative.
- Do not trap people in an active ride after failed payment.
- Do not double-charge simultaneous completion requests.
The goal was inexpensive local transportation for Broken Compass, not imitation of real-world rideshare pricing.
Under the simple experience / 10
One ride. Six main states. Several honest exits.
The interface stays simple because the system underneath it knows which transitions are valid.
- None
- Pending
- Accepted
- Arrived
- In progress
- Completed
PendingNo player accepts in 60 secondsFallback taxi
Any active stateCancellation or disconnectCleanup and recovery
AI-assisted development / 11
AI was part of the investigation system, not the author of the decisions.
I defined the experience, observed actual behavior, identified contradictions, decided what needed investigation, designed tests, questioned assumptions and set scope.
Codi worked as an implementation and repository collaborator. It helped trace the notification failure into NPWD's external-app assumptions, compare citra-taxi before and after integration, and build test scaffolding for questions that could not yet be tested with real players.
Version boundary / v0.5
At some point, you have to stop adding things.
v0.5 was scoped to proving the core transportation service:
- Profiles
- Availability
- Ride requests
- Notifications
- First-accept-wins
- Pickup
- Arrival
- Ride start
- Completion
- Payment
- NPC fallback
- Cleanup and recovery
Deferred to v0.6
Ride history · Ratings · Additional UI polish
Ride history, ratings and additional polish could wait.
First I wanted to know whether the ride worked.
All Work