fix: match routing observations across family key spaces and surface unpaired ones
The observed-routing-order line never appeared. Its lookup indexed the
observations record with a pool's family name, but the two sides come from
different key spaces: observation keys are getModelFamily()'s output, which
lowercases the model id ("fable"), while a pool row's family is the
scope.model.display_name recorded in the weekly_scoped limit ("Fable").
The direct index therefore always missed and fell through to the "No recent
traffic for this family" fallback — a plausible-looking message rather than
a visible failure, so the row lied even under heavy traffic.
Model families that never carry a weekly_scoped limit compound this: opus
and sonnet are recorded and served by the API but have no pool row at all,
so their order had nowhere to render.
Lookups now go through findRoutingObservation, which compares both sides
trimmed and lowercased, and selectUnpairedObservations lists every observed
family without a pool row in a compact block under the pool rows (hidden
entirely when empty). Both share one normalizeFamilyKey helper so the two
comparisons cannot drift apart. Verified against the live key shapes:
observations ['sonnet','opus','fable'] vs pool families ['Fable']. M
Matthias Breddin committed
9eecb188e10873853b1ecbb74e00b9408a081bb8
Parent: 0367cbd