Docs / TeoScore / Seating Logistics & Strategy
Seating Logistics & Strategy (V18–V20)
Category 7 — can you get the seat you want, and (for groups) can you sit together?
V19 is live on fare-class availability; V18 and V20 are disabled
The scoring logic for all three variables is written and spec-conformant. V18 and V20 are switched off by product decision. V19 is live and moves ranking — here is exactly what it does and does not see:
- V19 scores fare-class availability only. Whether the cabin you asked for is still bookable (
details.seatCount, real provider data), per leg and averaged, ignoring specific seat attributes. See the V19 card below. - Its activation is your stated cabin, and that only became real on 2026-08-28. The search contract used to declare
cabinClassas.optional().default('economy'), so a request that omitted a cabin arrived carrying economy and nothing could tell “chose economy” from “didn't say”. The default is gone, the stated cabin is forwarded assearchPreferences.requestedCabin, and a search with no stated cabin drops V19 rather than scoring an assumption. - Coverage gap, award vs cash. Every producer of
details.seatCountis a seats.aero path;normalizeDuffelnever sets it. So V19 scores award options and drops on live cash ones — and because a dropped category renormalizes, that hands award options a small edge over otherwise identical cash ones. Closing it needs Duffel per-cabin availability, not a scoring change. - Seat maps: teofetch fetches them for cash offers, but coverage is inconsistent in practice. Verified live (2026-08-04): a real production search returned zero cash options with seat-map data populated. Award options have no seat map at all (seats.aero has no seat-level API). The seat-map ratio method is gated off anyway, because this design scores fare class only.
- V20 disabled 2026-08-04. It has no equivalent fallback — contiguous-seating detection fundamentally needs seat-level data, so it's switched off rather than left silently inert.
- Known gap: the search API accepts
searchPreferences.seatPreferencebut never threads it into scoring, so V19's richer seat-map method could not activate from a real request even if it were un-gated.
Remaining: get per-cabin availability onto the cash path so V19 stops being award-only, fix the seat-preference wiring gap, and decide whether to re-enable V18/V20.
How the category aggregates
V19×0.50 + V20×0.50 when both active (or whichever one is) — in practice just V19 today, since V20 is disabled. V18 is a separate cross-traveler aggregation layer, not part of this per-itinerary score.
Measures — for a 2+ traveler search, keeps one traveler's bad experience from being averaged away. Currently disabled, always falls back to a plain average.
Disabled 2026-08-04 (V18_DISABLED)
Explicit product decision, disabled alongside V16 and V17. Already inert before this disable — no group-booking flow calls this aggregation yet. Group_Final_Score now always equals Group_Average; the floor blend below is intact for re-enabling once group-booking flows exist.
Group_Final = Group_Average × 0.60 + Group_Floor × 0.40 (floor = worst individual score)
Data & accuracy — not a score of the itinerary — an aggregation rule (aggregateGroupScoresWithFloor) that blends the already-computed per-traveler TeoScores. Confidence is profile-based, dropping to default-persona if any companion lacks a TEO profile.
Gotchas — disabled; the 60/40 floor blend no longer applies. Solo searches were already unaffected (average === floor → your own score, no blend).
Measures — whether the cabin you picked is still bookable at all — open seats or sold out. Specific seat types are not considered.
Scoped to the launch design, 2026-08-24
V19 had two methods. The richer one scored a continuous ratio of preferred-open to total-open seats from a real seat map, and it outranked the 100/0 formula below. It is now gated off (V19_SEAT_MAP_METHOD_DISABLED) so V19 matches its launch design exactly: score fare-class availability, and ignore specific seat attributes. The ratio logic is intact and re-enabling it is one flag.
Worth being precise about why it was gated, because it was not dead code: the profile→scoring pipeline that feeds it is wired end to end (your saved seat preferences do reach scoring). It fired rarely because seat-map coverage is thin — a live check found 0 of 111 real cash options carried one, and award itineraries never do.
Activation became real, 2026-08-28
The spec activates V19 when “the traveler has specified a desired fare class/cabin”. That was unenforceable while the search contract declared cabinClass as .optional().default('economy'): a request that omitted a cabin arrived carrying economy, so a chosen economy and an unstated one were the same bytes. The default was removed, teo-api forwards the stated cabin to scoring as searchPreferences.requestedCabin, and V19 drops when no cabin was stated instead of scoring against an assumption.
Segment: seatCount > 0 → 100 · no reported count → segment skipped V19 = sum(Segment) / scored_segment_count fare-confirmed whenever any leg reports open seats no cabin stated in the search → drops · no leg reports a count → drops
Data & accuracy — Reads details.seatCount — real provider data, populated from seats.aero's per-cabin remaining-seats field. Scored per leg (each leg reads its own seatCount where a feed provides one, the option-level value otherwise) and averaged; no producer writes a per-leg count today, so the average is currently over identical values. Award-only in practice — normalizeDuffel never sets seatCount, so live cash options drop. A reported 0 means “this program publishes no count”, not “sold out” — checked live on 2026-08-28, every award program returns a real count of 1–9 except AAdvantage, which always returns 0. A cabin that could not be sold is never offered at all, so V19 abstains on a 0 rather than scoring it as unavailable.
Status — live. Enabled 2026-08-24, activation made real 2026-08-28 — Seating Logistics & Strategy carries real weight, so V19 moves ranking. It is the category's only live variable (V18 and V20 are retired), and when it drops the whole category is excluded from the weighted score rather than counted as a zero.
Measures — whether your party can actually sit together. Currently disabled, always drops.
Disabled 2026-08-04 (V20_DISABLED)
Explicit product decision, disabled alongside V21. Already effectively inert before this disable — V20 needs a real seat map, and a real production search returned zero cash options with one; award itineraries never have one at all. The cluster/proximity formula below is intact for re-enabling; it just isn't contributing to anything right now.
cluster mode: largest single row ≥ N → 100 · near-cluster ≥ N → 90 · else 100 × (best ÷ N) proximity mode (1-1-1 configs): row-spread 0→75 · 1→60 · 2→40 · 3→25 · 4+→10
Data & accuracy — cluster mode for most cabins; proximity mode for single-seat configs (75 ceiling, since true sitting-together isn't possible). Booked cabin only, averaged across segments. Cluster detection needs seat clusterId data in the seat map.
Status — disabled; always drops regardless of group size or seat-map availability. Weight-0 already, so no ranking impact either way.