Skip to content

Multi-cabin search: --cabin economy,business (work-7ecx)#17

Merged
ak2k merged 1 commit into
mainfrom
worktree-work-7ecx-multi-cabin
May 18, 2026
Merged

Multi-cabin search: --cabin economy,business (work-7ecx)#17
ak2k merged 1 commit into
mainfrom
worktree-work-7ecx-multi-cabin

Conversation

@ak2k

@ak2k ak2k commented May 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • Extends --cabin to accept a CSV (economy,business); fans out N parallel single-cabin queries on both Matrix and gflight; client-side joins itineraries on (flight-number, date) per slice; renders one $ column per cabin with for non-overlap. New --sort <cabin> picks the sort column (default: first cabin).
  • Domain stays single-cabin — multi-cabin is purely orchestration, so wire.py / links.py / fli_bridge.py don't change. Single-cabin path is preserved bit-for-bit; only len(cabins) > 1 takes the new orchestrator.
  • PP overlay: when --cabin is multi and --provider-opt pp.cabins= isn't set, PP cabins are auto-derived from --cabin with a Business → +First rule (award seekers treat biz/first as a paired premium tier; First availability is rare enough that surfacing it costs nothing).

Demo

Cash side, gflight backend (flight search JFK LHR --dep 2026-08-15 --return 2026-08-22 --cabin economy,business -n 3 --cash-only):

                Google Flights · Y+J (sorted by Y) (USD)
┏━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━┓
┃ # ┃ carriers ┃ outbound               ┃ return                ┃    Y $ ┃ J $ ┃
┡━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━┩
│ 1 │ ?        │ JFK→LHR VS10           │ LHR→JFK DL2           │ 794.00 │   — │
│ 2 │ ?        │ JFK→LHR VS10           │ LHR→JFK VS137         │ 794.00 │   — │
│ 3 │ ?        │ JFK→LHR VS10           │ LHR→JFK VS25          │ 794.00 │   — │
└───┴──────────┴────────────────────────┴───────────────────────┴────────┴─────┘

is expected when each cabin's top-N covers different carriers (JFK-LHR: cheapest econ is VS, cheapest biz is FI — distinct top-N sets). Spec acknowledges this; help text suggests bumping -n for broader overlap. Internal N-bump is filed as follow-up work-r9ht.

PP overlay (with --providers pp) auto-shows Economy / Business / First columns — Business → +First rule:

Leg: outbound JFK→LHR 2026-08-15
┏━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━┓
┃ flight ┃     price ┃    Economy ┃   Business ┃ First ┃ ¢/mi (Y) ┃
┡━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━┩
│ VS10   │ USD794.00 │      36.0k │     100.0k │     — │     1.8¢ │
└────────┴───────────┴────────────┴────────────┴───────┴──────────┘

User can override with --provider-opt pp.cabins=Economy to scope PP independently.

Implementation

  • New src/flight_cli/_multi_cabin.py (~110 lines): itinerary_key, MultiCabinRow, merge, parse_price.
  • cli.py additions: _resolve_cabin_list, --sort option, _derive_pp_cabins, _run_matrix_multi / _run_gflight_multi (parallel fan-out via anyio.create_task_group), _render_multi_cabin_search, _run_matrix_path_multi / _run_gflight_path_multi top-level entries.
  • Per-cabin query failures are soft: log a warning and render that cabin's column as rather than failing the whole search.
  • Matrix deep-link emits for the sort cabin (only cabin Matrix's URL encodes one of).

Test plan

  • make check — ruff + ruff format + basedpyright strict + pytest, all green
  • 33 new unit tests in tests/test_multi_cabin.py (CSV parsing, key construction, merge with full/partial/no overlap, sort-by-missing sinks, top_n truncation, PP cabin derivation rules)
  • Single-cabin regression smoke: render bit-identical to main
  • Multi-cabin gflight smoke: --cabin economy,business -n 5 --cash-only
  • --sort flip: --sort business reorders rows and updates title
  • Multi-cabin matrix smoke: --cabin economy,business --routing "BA"
  • Multi-cabin + PP smoke: shows auto-derived Y/J/F PP columns
  • PP cabin override smoke: --provider-opt pp.cabins=Economy scopes PP without affecting cash
  • Bad --sort cabin: clean exit 2 with message

Deferred (follow-up bd items)

  • work-r9ht — internal N-bump for broader join overlap (spec's "Future option").
  • work-338g — unified cash+PP super-table render (PP stays per-leg today; rolling per-leg awards up to per-itinerary columns is its own design problem).

Extends `--cabin` to accept a CSV; fans out N parallel single-cabin
queries (one per cabin) on both Matrix and gflight; client-side joins
itineraries on per-slice (flight-number, date); renders one row per
itinerary with one $ column per cabin. '—' marks cabins where the
itinerary fell outside that cabin's top-N. New `--sort <cabin>` picks
the ordering column (default: first cabin in --cabin).

Domain stays single-cabin — multi-cabin lives in the orchestration
layer, so wire/links/fli_bridge don't need to change. The single-cabin
code path is preserved bit-for-bit; only multi-cabin invocations
(`len(cabins) > 1`) take the new orchestrator.

PP overlay: when --cabin is multi and the user hasn't explicitly set
`--provider-opt pp.cabins=`, the PP cabin set is auto-derived from
--cabin. Business in --cabin implies First gets added to the PP query
— award seekers treat business/first as a paired premium tier, and
First availability is rare enough that surfacing it costs nothing
while filling a real research gap. The reverse promotion isn't
applied: asking for First is an explicit choice we don't second-guess.

Caveats noted in two follow-up bd items:
  - work-r9ht: internal N-bump to widen cabin overlap in the join.
  - work-338g: unified cash+PP super-table render (PP stays per-leg today).

Tests: 33 new (CSV parsing, key/merge/sort, PP cabin derivation).
Full gate green: 311 passing.
@ak2k ak2k merged commit 23b7375 into main May 18, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant