# Stream K10: the review area rian can work in, and the price block he can use

(written 2026-09-17 Thu from rian's step E walk-through of staging; runs beside K9, while rian
reviews)

**Runs:** now, in its own worktree and branch off master. **Must not disturb the review:** never
deploy, never touch staging's or production's database, never run a collection, never edit
`.app.env` or `.env`, never write in `import/proposals/`. Rehearse on `dfp-devdb` (127.0.0.1:5433)
from `backups/dfp-2026-09-17-post-chain.dump`. Rian merges and deploys when he is ready.

```
cd /srv/apps/dutyfreeprofessor && git worktree add .claude/worktrees/review-area -b claude/review-area master
```

## Why

Rian opened `/review` and the product line page on staging for the first time on 17 Sep. The sheet
worked as designed and still did not answer the questions he had. His words, kept because they are
the brief:

> "I'd like to see some tabs. One where a human can see how decisions are being made. What are the
> programmatic stop words, for example, and what general guidelines is AI watching for when
> presenting rules for folding or splitting. I'd like to see the folding/splitting review in another
> tab. It seems like it focuses on brand folding, but what about product line decisions? I hope we
> haven't missed the product line decisions because that's where I think a lot of the folding will
> take place... eg the folding shades or misspellings, etc. And then there is option folding
> /splitting too I think? And I'd like to see the publish candidates in a 3rd tab."

And on the product line page, with no bottle and no airports chosen:

> "it says 'choose your quantity to see its price' but there is no ability to choose it right there
> in that section... if you choose the quantity, now it shows you a list of airports you can select.
> But if you click it, that takes you to the airport page. What I'm looking for is a price at that
> airport displayed on that page. So if the user has no airports selected, we assume they are not
> looking for comparisons but rather just looking for pricing for the different variants at
> different airports. And even if they do have airports selected, we should have some sort of option
> to browse the prices at different airports (not up front, but still accessible without deselecting
> their airports)."

Fixed already, in 0.50.1, so build on it rather than around it: a brand's sheet answered 500 when its
rows came from a rule (the words a rule matched sat where character offsets belong, now `matched` on
the evidence), and every rule row read as stale (a rule cites a name, not one listing).

## What already exists

- `/review` (`web/src/pages/ReviewPage.tsx`, `components/review/ReviewPanels.tsx`), the sheet list,
  a brand's sheet, the Index candidates panel, `routers/review.py`, `services/proposals.py`.
- Passes on staging today: `rule:<list>:6` for fourteen lists (938 proposals: drink_words 188,
  format_words 257, cask_words 154, lists_together 159, brand_trailers 91, connectors 37,
  region_words 14, brand_partial 12, shade_shapes 10, pack_words 8, audience 4, noise 3, age_words 1)
  and `arrival:6`. Their generators are `services/proposal_rules.py`; the boundary they may not cross
  is `main/docs/REVIEW-PROCESS.md` sections 2 and 4.
- `services/publish.py`'s candidate rules and `app.cli index list|approve`.
- K9 (running now, branch `claude/brand-split`) adds the brand split, the reject-offers-a-split path
  and the per-vertical scoping of the trailer list, and its K9.6 shows the word lists. **Coordinate:
  K10 owns the review area's shell and tabs; K9.6 fills the lists panel inside it.** Agree the
  component's name and props in K10.1 and say so in both handoffs.

## Goal

A review area rian can sit in for a day: three tabs, every kind of folding visible (not brands
alone), and a product line page whose price block lets a shopper choose, and browse prices by
airport without giving up their own airports.

## Tasks, in order (ids on /plan; commit prefix `K10:`)

1. **K10.1 The shell: three tabs.** `/review` becomes **How decisions are made**, **Folding and
   splitting**, **Publish candidates**, the address carrying the tab (`?tab=`), the sheet reachable
   from the second. Keep every existing approval action working; this is a re-arrangement, not a
   rewrite. Name the lists panel's component here and tell K9.
2. **K10.2 How decisions are made.** Read-only, and the answer to "how do I know what the machine is
   doing": the certain boundary from `REVIEW-PROCESS.md` section 2 in a person's words; every word
   list with its words, the vertical it applies to and how many rows it folded (K9.6 fills this);
   the spot-check thresholds from section 4; the grouping defaults from section 2 (one product line
   per age, shades leave the line, and the rest); and what an AI pass is asked to do and may never
   do. Each block links to the proposals it produced. The doc stays the source: read it, never
   restate it in code.
3. **K10.3 Folding and splitting, for every level.** Rian's real worry: the sheet reads as brand
   work. Show, side by side and filterable by level: **brands** (the trailer folds and brand pair
   suggestions), **product lines** (the 1,323 line pair suggestions from `suggest`, the
   `lists_together` and `brand_partial` rules, misspellings), **options and wordings** (shade shapes,
   skin-type tails, the attribute wording aliases) and **variants** (the 15 variant pairs, the
   arrivals). Each row says what it would join or split, what evidence it read, and what it changes.
   The counts per level are the first thing on the tab, so a person sees where the work is.
4. **K10.4 Publish candidates.** The Index candidates panel becomes its own tab: the 2,081 waiting
   (364 brands, 1,701 product lines, 16 places), filterable, with what made each a candidate and one
   approve per page. Rian approves; nothing is automatic.
5. **K10.5 The price block on a product line page.** With nothing chosen, the block that asks for a
   quantity offers the choice in the block itself, not only in the selector above it. With a bottle
   chosen and no airports, list that bottle's shops with their prices and dates ON the page (an
   airport name is not a link away from the page). With airports chosen, keep today's comparison and
   add a way to see every other shop's price without clearing the choice (a disclosure, not a second
   page). The rules that do not move: a price always carries its date, "From $X" never appears, and
   a family's cheapest is never shown as the family's price.
6. **K10.6 Tests and the rehearsal.** The sheet's three tabs render from real staging-shaped data on
   a copy; a rule row shows its matched words and is not stale (pinned in 0.50.1, keep it); the price
   block's four states have a test each (nothing chosen, a bottle chosen with no airports, airports
   chosen, a bottle at a shop the shopper did not choose). `main/check.sh` green; five-line handoff.

## Must not touch

Staging and production, `.app.env`, `.env`, `import/proposals/`, the identity rules and the certain
boundary (K10.2 displays the boundary, never changes it), `services/proposal_rules.py`'s generators
beyond reading them, and K9's files (`normalize.py`'s trailer list, the split service).

## Acceptance

- Three tabs, the address carries the tab, every existing approval still works.
- A person can see, without reading code: the certain boundary, every word list and its words, the
  spot-check thresholds, the grouping defaults, and what an AI pass may not do.
- The folding tab shows brands, product lines, options and variants with counts, and rian can tell
  at a glance where the work is.
- The publish tab lists the candidates with their reason and approves one page at a time.
- The price block answers rian's four states; every price carries its date; no "From $X".
- `main/check.sh` prints "checks passed"; nothing deployed; staging untouched.
