A computed verdict can still be wrong

hop.flights' whole pitch is a verdict that's computed, not guessed. For any search with more than one passenger, it was computing that verdict from two numbers that didn't describe the same trip.

hop.flights answers one question: use points, or pay cash. The whole pitch of the verdict engine — the assembleSearch function in the search library — is that the answer is computed from a user's real loyalty balances and real cash prices, never generated. That's the moat: no hedge, no disclaimer, just a number. Which also means that when the number is wrong, it's wrong with the same flat confidence as when it's right.

01 Symptom

A solo search checks out. Add a second passenger to the same search and the verdict can flip outright — points to cash, or cash to points — not a rounding difference, the opposite recommendation, while the page still states it with total certainty. A DeepWiki audit run against the codebase in July surfaced it as the highest-priority open issue: live, in production, on the exact search pattern a family actually runs.

2 passengers, same flightPAY CASH

same flight, same fare class — the only input that changed is party size.

02 Root cause

Seats.aero returns award pricing per seat: miles and taxes for one passenger. Duffel returns cash pricing as a total, already summed across everyone on the itinerary. assembleSearch divides one against the other without checking which is which — the per-seat miles become the denominator, the whole-party cash total becomes part of the numerator. The Seats.aero client never reads the party-size parameter at all, so the number typed into the search box is invisible to the function computing the award side. The result inflates the cents-per-point figure (cpp) by roughly party size: two passengers runs the number roughly double, three roughly triple.

Seats.aero miles + taxes per seatDuffel cash total for whole partyassembleSearch joins themcpp computedverdict shown

03 Fix

The fix is arithmetic, not architecture: scale the award side by party size before it meets the cash side, so both numbers describe the same trip. It survived this long because the mock fixtures used in day-to-day development don't scale by party size either — the same flat numbers come back whether the search box says one passenger or four, so the bug never got a chance to surface in the normal dev loop. It only shows up against real data, on a real multi-passenger search. It's tracked in Linear now as one of four hard gates the project holds before billing turns on at all — alongside one payment lane tested end to end, alerts confirmed live, and written commercial terms with the seat-availability provider. None of the four ship until the verdict itself holds for more than one passenger.

A verdict that's computed instead of guessed is only a better product if the arithmetic actually checks out — otherwise it's just a guess that sounds more certain than it is.

All Thinking pieces