# Stream F: awards (rewritten 2026-09-05 Sat, against build plan v3 §4 #2, §7 and the dated tasks on /plan)
> **Status 10 Sep:** F1 landed (the strategy, decision card 5); F2's picker and rebuild-by-natural-key landed, the pin waits on `decide-overrides-before-r`. The body below is the brief as asked on 5 Sep.
**Runs:** Mon 14 (F1), Tue 15 (F2). A card-corner "top award" exists today; this replaces its
selection with a stated strategy and a tested picker.

## Goal
Decide, then implement, which award a product shows when it has many, in a way Adam can
explain to brands: every retained award still appears in the structured data, older awards from
the same competition are suppressed on the page, and one award is featured by a picker that
weighs recency, level and the visitor's airports. A human can pin a different one.

## Tasks, in order (ids are `/plan` tasks; record each with `main/scripts/plan-set.py`)
1. **F1 Strategy document for Adam (Mon 14).** One page, plain language, no code: the rules
   above; how ties break; how a pin overrides the picker; the aging idea from rian (awards older
   than a year fade on listings to encourage re-entry) stated as a proposal with an on/off
   decision for Adam. Publish it where Adam reads (`/todo` as a question item with the document
   attached, or a `/discuss` entry) and record the ask on the running list (`--blocks F2`).
2. **F2 Picker + rebuild by natural key (Tue 15).** `awards_import.py --rebuild` upserts by the
   natural key `(product_id, competition_slug, year, medal)`; ids may churn, keys do not. The
   picker: most recent award per competition retained; feature = highest level among the most
   recent, ties by recency, then by airport affinity with the visitor's selected airports (JFK
   selected → a New York medal); deterministic without a visitor. A pin is an `overrides` row
   (`entity_type='product', field='award_pin'`) keyed by the natural key, never `awards.id`; if
   the key no longer matches, fall back to the picker and write a `reverifications` row (both
   tables land with A8; if they have not, the pin is a `blocked` note, not a hand-rolled column).
   Tests for every ordering rule and the fallback. Structured data keeps emitting all retained
   awards (Stream B owns `seo.py`; hand it the list through the existing award fields).

## Owns
`app/services/awards_import.py`, a new `app/services/award_picker.py`, `featured.py`,
`web/src/components/Medal.tsx`, their tests.

## Must not touch
Page routes and `seo.py` (Stream B), styles (Stream C), collectors (Stream A), migrations
(the tables the pin needs come from A8).

## Rules
- Award matching stays conservative (brand and expression must agree; ambiguity means no
  medal); never loosen it to raise coverage.
- Medal artwork in `web/public/medals`, keyed `<competition>-<tier>.png`; bump
  `MEDAL_ART_VERSION` in `Medal.tsx` on any file change; the text badge remains the fallback.
- Everything else per `OVERNIGHT-RULES.md`: never deploy, own files only, commit prefixed `F:`,
  `main/check.sh` green, handoff ≤25 lines, `/plan` updated, decisions on the running list.
