---
type: plan
version: 1
status: documented 2026-09-15 from rian's thinking aloud; NO plan and NO decision by his instruction. The page-shape question reverses a settled decision (the /products address, 9 Sep with Mark and Adam) and must go back to them before anything is built.
supersedes: nothing; absorbs the deferred makeup-shades decision
---
# Variations, determinants and the line page

Rian, 15 Sep, after seeing the catalogue: *"I'm picturing a Product Line page where you can select
products and variations... maybe we treat all variations and product determinants all as variations
and all create new products. The barcode argument is what makes me lean that way. But then we have
some meta that helps us distinguish if the variation should be considered a product determinant or
variation for the purpose of presentation."*

This records **his proposal, the evidence for and against it, and the questions still open**. It
designs nothing.

## 1. The proposal, in his words and mine

1. **The line page becomes the primary page.** A "Coke Zero" page rather than a page each for Coke
   Zero 355ml, Coke Zero Cherry 355ml and Coke Zero Vanilla 355ml.
2. **Every determinant and every variation still creates its own product row.** Nothing is folded
   in the database. His reason is the barcode: each shade and each size carries its own GTIN, and
   GTIN is the identity that makes cross-shop matching a join.
3. **A new piece of metadata on the variation kind says how it behaves on the page:**
   - a **determinant** (quantity, concentration, vintage) changes the price, so the shopper picks
     it and the comparison table regenerates for that choice;
   - a **variation** (shade, flavour) does not meaningfully change the price, so it is *not*
     selectable at all. It is listed as information: these are the shades this shop carries.
4. **The reason it need not be selectable is that nobody is buying from us.** Rian: *"they're not
   purchasing anything, they're just viewing information."* A retailer needs a shade picker. A
   price comparison does not.

His test for which side a thing falls on is **does it change the price**.

## 2. What exists today

- `variation` is **part of the identity key** (`house | line | variation | quantity`). Every
  variation already splits products: shade, concentration and flavour alike. So point 2 of the
  proposal is already true, and needs no change.
- There is **no notion of a cosmetic variation.** Nothing in the schema or the code distinguishes a
  price-bearing variation from a presentational one. That is the genuinely new part.
- `VARIATION_KINDS` is a fixed tuple in `services/lines.py`: concentration, color, flavor, age,
  cask, edition. It is assigned by our own rules; a shop's wording never enters it.
- **There is no line page.** `product_lines.slug` exists and is described as "the address the line
  page will answer at", but no route, no API read and no component exist. The SPA has
  `/products/:productSlug` only.
- The address `/products/<name>-<id>` was **settled on 9 Sep** with Mark and Adam.
- Thin pages are already suppressed by a different mechanism, settled the same day: only products
  that can be compared, plus travel exclusives, are published.

## 3. The evidence

### 3.1 The thin-content argument does not hold, but a better one does

| | |
|---|---|
| Products that qualify to publish | 3,130 |
| Distinct lines behind them | 2,638 |
| Those lines holding exactly one published product | 2,364 |

Line pages would replace 3,130 pages with 2,638, a 16% reduction, and 90% of the survivors would
hold a single product and be exactly as thin. Across the whole catalogue 88% of lines hold one
product (12,129 of 13,706; mean 1.22).

**The argument that does hold is search intent.** Nobody searches "1 Million EDT 100ml duty free".
A line page can carry every size and concentration with the full cross-airport comparison on one
screen, which no single product page can. That case is independent of page counts.

### 3.2 Rian's price test, measured

**Shade passes.** One Clarins Joli Rouge Velvet page at Panama carries 18 shades. Across all 18
there are exactly two prices, 34 and 36 dollars.

**Concentration fails.** At Bogotá, 1 Million EDT 100ml is 109 dollars and 1 Million Elixir 100ml
is 130 dollars: same house, same line, same size, 19% apart. By rian's own rule concentration is a
determinant, not a variation, though the code files it under the same mechanism as shade.

**Vintage and ABV.** His readings are right: a distillation year changes the price and belongs with
the determinants; ABV is a property that neither splits nor selects, and is already its own column
outside the key. Note that "age" currently sits in the variation vocabulary *and* the line rules
treat a 12 and an 18 year old as different lines, so vintage is handled two ways at once.

### 3.3 Keeping shades as separate products does NOT cost comparability

This was the strongest argument against the proposal and the data refutes it. Comparability is the
share of products sold at more than one airport:

| Variation kind | Products | Comparable at 2+ airports | Share |
|---|---|---|---|
| (none) | 14,434 | 2,000 | 13.9% |
| concentration | 1,770 | 744 | 42.0% |
| color | 399 | 249 | 62.4% |
| flavor | 158 | 1 | 0.6% |

Shade-bearing products are the **most** comparable in the catalogue, because the same shades recur
across one operator's several airport shops. Fragmenting a lipstick into 18 products does not
strand it.

### 3.4 The flavour kind is misfiring, and it is confectionery

Flavour is the outlier above: one comparable product out of 158. The cause is not the model. The
kind is landing almost entirely on Paris confectionery gift boxes, where the flavour word is part
of a one-off product's own identity rather than a variation of a line: "15 Fine Chocolates Almond
Crispy" carries the flavour "almond praline", has no parsed quantity, and exists at CDG alone. The
single genuine case behaves perfectly: a Mint Chocolate cream at 1L compares across eight airports.

## 4. What the proposal gets right, and what it still owes

**Right, and cheap.** The page design needs no schema change. Products stay one-to-one with
barcodes, the identity key is untouched, and which selector splits a comparison becomes a rule
keyed on the variation kind. That is additive.

**The "not selectable" insight is the strongest part.** It removes a whole class of interface and a
whole class of wrong answer. A shade picker would imply we know which shades a shop has in stock
right now, which we do not and never will.

**What it owes:**
- **What the line page prints as the price of a non-selectable variation.** Eighteen shades at two
  prices cannot be one number without a rule. "From $34" repeats the multi-size-tile mistake the
  constitution already warns about, in reverse: a family's cheapest presented as the family's price.
- **Whether the determinant flag is per kind or per category.** Concentration is price-bearing in
  fragrance. Flavour looks cosmetic in a cream liqueur and identity-bearing in a chocolate box. A
  flat kind-to-behaviour map may be too blunt.
- **What happens to the six kinds not yet discussed.** Age, cask and edition have had no test
  applied to them.

## 4b. The grid question, and why it cannot be answered yet

Rian, later the same evening, on what a comparison grid should show when a line holds eighteen
shades: *"19 shades of the same product? 2 representive shades? 1?... if we have multiple products
in the same line with the same price, those get grouped into one comparable and a representative
product is selected... but then what if in one airport red and pink are one price and purple,
silver and gold are a different price? hmm, I dont know what to do."*

**The scenario cannot currently occur, because the shade is part of the line key.** Every shade
becomes its own line, so there is no line holding red, pink and purple to group. From the live
catalogue, these are three separate `product_lines` rows:

- `Blush Subtil 03 Sorbet De Corail`
- `Blush Subtil 041 Figue Espiègle`
- `Blush Subtil 1000 Berry Bisou`

This is deliberate and recorded: the Makeup line key keeps the shade tail until rian's deferred
makeup-shades decision, with the kind held as metadata beside it. So the grouping problem is
**blocked behind that decision**, not open in its own right.

It also explains the 62.4% comparability of colour products in §3.3, which is weaker evidence than
it first appears: a one-shade line matches itself across the three shops of one operator. Shades
barely spread in reality. Of 249 colour products carried at more than one airport, **two** reach a
third. One line at Panama lists eighteen shades of a Clarins lipstick while Paris lists one.

**What the answer should be once shades do group**, recorded so the reasoning is not redone:

- **Deduplicate the grid on identity, never on price.** Rian's price-grouping idea reshuffles
  whenever a price moves, a currency shifts, or one airport repriced half a shade range, and the
  groups are per-shop so they cannot be a cross-shop fact. One card per (line, determinants) with
  the cosmetic dimension collapsed is stable by construction.
- **A card still has to name a real product to stay honest.** A card priced as "the family" repeats
  the multi-size-tile failure the constitution forbids. The candidates are: price the specific
  variation carried at the most airports (like for like, a real comparison, but it can be absent at
  one shop), or print a range with the count ("most shades $36, two at $34"), which is honest but is
  not a comparison.
- **This is presentation, and reversible.** Nothing here touches the schema, the identity key or a
  barcode, which is why it is safe to settle it after the shade decision rather than before.

## 4c. Lines are over-split, and it is concentrated in beauty

Rian's reading, and it is correct: *"our product lines are going to collapse significantly... shades
are variations of a product line, not new product lines. I think we just haven't yet found the other
examples."*

**Measured.** Collapsing every line onto the first two words of its key, per brand, is a crude
proxy for stripping a shade tail, but it brackets the size of the problem:

| Family | Lines today | After a stem collapse | Reduction |
|---|---|---|---|
| beauty | 5,432 | 3,583 | 34.0% |
| confectionery | 377 | 327 | 13.3% |
| liquor | 7,896 | 7,224 | 8.5% |

Concrete cases, each of which is one line today split many ways:

| Brand | Would be one line | Lines today |
|---|---|---|
| CHANEL | Rouge Allure | 59 |
| Lancôme | Teint Idole | 45 |
| Shiseido | Synchro Skin | 43 |
| Clarins | Skin Illusion | 40 |
| Clarins | Joli Rouge | 38 |

**This corrects §3.1.** The finding there that 88% of lines hold exactly one product was read as a
reason the line page saves few pages. It is better read as the symptom rian names: those lines hold
one product *because the shade split them*. On corrected data the line page both saves more pages
and produces richer ones, which is the outcome the proposal wants. The 16% figure in §3.1 is a
floor measured on broken data, not the expected result.

**His prediction about new categories is supported in what we already hold:** confectionery sits
between beauty and liquor, so a food catalogue would inherit the same problem rather than escape it.

**The caveat that keeps this from being mechanical.** A common prefix is not a shade. Among the 40
"Skin Illusion" lines are `Skin Illusion Full Coverage Foundation` and `Skin Illusion Tinted
Moisturizer`, which are genuinely different products; among the "Joli Rouge" lines sits `Joli Rouge
Velvet`, a different lipstick from plain `Joli Rouge`. The rule to build is **strip the shade tail**,
not **collapse on a shared prefix**. Getting that wrong merges real products, which is the failure
the identity rules exist to prevent, so the collapse has to be driven by the shade the collector
actually published (the text after the separator, already read as `variation_kind = color`) rather
than by name similarity.

**Vocabulary, settled between us here:** a product is a variation of a line. A line is the real
product; each product under it is one size of one variation of that line.

## 5. Open questions

1. Does the line page replace the product page, or sit above it with product pages kept and
   canonicalised to the line?
2. If it replaces it, what is the address, and does Mark accept reversing the 9 Sep decision?
3. Where does the determinant flag live: a code constant keyed by kind, a column on
   `variation_aliases`, or a small table keyed by (vertical, kind)?
4. What price does a line page show, and what does it show for a variation nobody selects?
5. Does the comparable-products rule apply per product or per line? A line with one comparable
   product and nine single-shop products is a publishable page containing mostly uncomparable rows.
6. Is vintage a line distinction (as the line rules have it) or a determinant (as rian has it)?
7. **The makeup-shades decision now blocks the line page** (§4b): until a shade leaves the line
   key, a cosmetics line page has nothing to group and the grid question cannot be tested.

## 6. What must not break

- **One product, one barcode.** This is the whole reason the proposal keeps products separate.
- **A price is an observation**, and a family's cheapest is never presented as the family's price.
- **The comparable-or-exclusive publication rule** settled 9 Sep, and the brand-page floor of three
  products at two airports which reads the same data.
- **The settled address shape**, until Mark and Adam agree otherwise.

## 7. Where this is tracked

- `decide-the-line-page-and-the-variation-determinant-split` — the decision, links this document.
- `issue-the-flavor-variation-kind-is-landing-on-confectionery-gi` — §3.4.
- The makeup-shades decision this supersedes is on the running list as overtaken.
