v4.0.0
Driver accept, decline and rematch.
- A match is now an offer: trips carry
offered_atandaccepted_at, drivers carryoffers,accepts,declines(Alembic0004; existing matched trips are backfilled as accepted). POST /rides/{id}/accept?driver_id=takes the trip; only after acceptance do the driver's pings move it through en_route, arrived, in_trip and completed.POST /rides/{id}/decline?driver_id=puts the trip back torequestedwith the driver appended todeclined_by, then releases the DynamoDB claim with a conditional update (status = busy AND trip_id = this trip) so a driver already claimed elsewhere is untouched. The next sweep rematches the trip to the nearest driver not indeclined_by.- Offers nobody answers within
DISPATCH_OFFER_TIMEOUT_S(15 s) are expired at the start of each dispatcher sweep (FOR UPDATE SKIP LOCKED) and handled as declines with the eventoffer_timeout. GET /drivers/{id}/acceptancereports offers, accepts, declines and the acceptance rate;GET /dispatch/statsaddsoffers_declinedandoffers_timed_out.- Simulated drivers accept offers (and decline a configurable share, 10% in
make demo); simulated riders wait for an accepted match. The demo summary reports accepted, declined, timed out and rematched rides. - Tests: acceptance gating pings, decline releasing the claim and rematching without the decliner, a trip waiting when its only driver declined, offer timeout in the sweep, the conditional release, migration 0004 up and down. 85 tests total.