You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(web): one rating everywhere from the stored bayesian score #1545
Refs #1531 (phase P4 of the design Docs/Proposals/2026-10-31-criteria-stars-pros-cons.html; JOY approved D1-D10 with the recommendations on 2026-10-31).
Outcome
Every page and the API show the same product rating: master_products.rating_score at one decimal (D2, D3; design 2.4). The expert score is shown separately with its label; the leaderboard combined score only breaks ties.
Scope
Product page, cards, compare, versus, watchlist, leaderboard, /api/v1/rate/* and the OpenAPI spec read rating_score; display-truth tests follow.
Acceptance
iPhone 16 128GB shows one number on the product page, versus scoreboard and matrix, compare and cards.
Depends on: P2 applied. Database: reads the P2 columns only.
Size: S
Follow-ups from PR #1478 (merged 3648fb0), to do once rating_score exists
/api/v1/rate/versus still breaks ties with the composite and checks a tie at 2 decimals. The screen can show a tie while the API names a leader. Make it use the same one-decimal tie as the page, and keep the composite only as the tiebreak.
The home and compare battle cards, the leaderboard and deals still show composite or persona scores at 2 decimals. Under D3 the composite only breaks ties: show rating_score at 1 decimal, and label any persona score as such.
Small fixes:
The buyer score is rounded twice: 4.249 shows 4.3. Round once.
When neither model has a score, the summary prints "0.0" while the stars say "Chưa có đánh giá". Show "Chưa có đánh giá" in both.
Other callers of resolveCompareEffectiveUserRating: compare, watchlist, deep inspect, catalog toolbar. They follow automatically.
API:/api/v1/rate/* select lists add rating_score. Responses add rating_score_5, and the existing fields stay for one release. server/rate/openapi.ts documents it.
Ranking follows D3:
The leaderboard ranks by rating_score. The composite only breaks ties.
Battle cards, leaderboard rows and the podium show rating_score at 1 decimal.
A persona or criterion view keeps its own score, labelled with the persona or criterion. It is not shown as "the rating".
Versus API tie rule. It can land before the migration. is_tie and overall_winner use the score at one decimal, like the page. The composite and price become the tiebreak only. margin_10 is the gap between the scores as shown, and the OpenAPI description follows. Switching the API's base score to rating_score waits for the column.
Composite or persona scores at 2 decimals on battle cards, the leaderboard and deals. This waits for the column: showing a 1-decimal number that the list is not sorted by would make equal-looking scores rank differently.
Small fixes:
"0.0" when neither model has a score. It can land before: the summary and verdict say "Chưa có đánh giá" like the stars.
Double rounding. I propose no code change before the column. The double rounding goes away when the screen reads rating_score directly. But the column itself is stored at 2 decimals, so a Bayesian 4.249 is stored as 4.25 and shown as 4.3. To round only once, P2 would store numeric(4,3) (or wider) and the screen would round to 1 decimal. A question for the P2 author (PR docs(proposals): dos-me migration for criteria ratings, pros and cons (p2) #1549), not a blocker: the error is at most 0.05 and only near x.x5.
[Agent: PO] PR #1571 (one rating from rating_score) is merging. Left open for one follow-up: the catalog RPC rate.search_master_products still sorts rating_desc and filters p_min_rating on user_rating_weighted/average_rating, so the catalog 'highest rated' order, ?rating= and the leaderboard top-100 load can disagree with the shown rating. Fix = a DOS-Me migration switching both to rating_score (coordinate with DOS.Me PR #1497, which also edits that RPC). Assigned to the Core session after P3.
Live check after promote #1572 (rate.vn on main b5de9f6)
Read-only check: gentle GETs and headless Chromium, desktop at 1366 px and phone at 390 px, in light and dark.
One rating: Apple iPhone 16 128GB shows 4.8 everywhere. Its stored rating_score is 4.814. It appears as 4.8 on:
the product page (headline 4.8 (15));
its catalog card on /smartphones;
the versus page against the 16 Plus, in the scoreboard, the verdict "ngang điểm tổng hợp (4.8)" and the cards;
/compare;
the leaderboard row and the battle cards;
the API: rating_score_5 = 4.8 on products, products/{slug}, leaderboard and versus.
Leaderboard order: the shown ratings go down the list, from 4.8 to 4.7 to 4.5, with no order breaks in the API's 8 smartphones.
The top five all show 4.8. They are ordered by the composite, which equals the unrounded rating for products with no expert or criterion score.
The versus API gives is_tie: true for 16 vs 16 Plus (both 9.6), and the page says "Ngang điểm 1v1".
Light and dark, phone and desktop: every page returns 200 and the backgrounds follow the theme. The stars, the scoreboard and the label "Điểm tiêu chí từ người review hoặc chuyên gia" are readable in all four.
Nothing is broken, so no issue was filed.
Still open, as noted in #1571: the catalog RPC search_master_products sorts and filters on user_rating_weighted, not on rating_score. Fixing that needs a DOS-Me migration.
[Agent: PO] Rollout note: when the DOS-Me migration from PR #1574 (search_master_products on rating_score) is applied, ship a small web PR that updates the OpenAPI wording for min_rating and sort at the same time (deferred from PR #1575 because prod still filters on weighted, then expert, then average).
[Agent: PO] Done: one rating everywhere (PRs #1478, #1551, #1571, #1575), the catalog RPC on rating_score (DOS.Me #1533 dev, #1537 main, live on production), OpenAPI wording (#1591), mirrored in #1594; promoted in #1595.
Activity
JOY commentedon Oct 31, 2026
Implementation plan (Web)
I read the P2 migration in PR #1549 (
Docs/Proposals/2026-10-31-dos-me-criteria-ratings.sql):master_products.rating_scoreisnumeric(3,2): Bayesian, k = 3, mu = 4.2, NULL when there are no reviews.rating_weight_sumis also added.Every place that switches to
rating_score(after P2 is applied)There is one reader change, and the rest follows from it:
resolveCompareEffectiveUserRating(lib/rate-domain.ts) readsrating_scorefirst. Fallback touser_rating_weightedstays until the backfill is checked.scoreMasterProductForLeaderboarduserScore5(lib/leaderboard-engine.ts) reads the same helper.Direct readers of
user_rating_weightedoraverage_rating, which move to the helper:smartphones/[slug]/page.tsx):computeBayesianRatingSummaryreads the stored score, keeping the review list only for counts;smartphones/page.tsx,rate-api.ts(the card value and the sort onuser_rating_weighted).page.tsx,hero-products.ts,HeroSearchInspector.leaderboard/page.tsx.server/rate/deals.ts.resolveCompareEffectiveUserRating: compare, watchlist, deep inspect, catalog toolbar. They follow automatically./api/v1/rate/*select lists addrating_score. Responses addrating_score_5, and the existing fields stay for one release.server/rate/openapi.tsdocuments it.Ranking follows D3:
rating_score. The composite only breaks ties.rating_scoreat 1 decimal.display-truthtests follow.The three follow-ups from PR #1478
is_tieandoverall_winneruse the score at one decimal, like the page. The composite and price become the tiebreak only.margin_10is the gap between the scores as shown, and the OpenAPI description follows. Switching the API's base score torating_scorewaits for the column.rating_scoredirectly. But the column itself is stored at 2 decimals, so a Bayesian 4.249 is stored as 4.25 and shown as 4.3. To round only once, P2 would storenumeric(4,3)(or wider) and the screen would round to 1 decimal. A question for the P2 author (PR docs(proposals): dos-me migration for criteria ratings, pros and cons (p2) #1549), not a blocker: the error is at most 0.05 and only near x.x5.Order
fix(web): versus api ties at one decimal and unrated pairs say so (#1551
feat(web): one rating everywhere from the stored rating_score (#1571)
JOY commentedon Nov 1, 2026
[Agent: PO] PR #1571 (one rating from rating_score) is merging. Left open for one follow-up: the catalog RPC
rate.search_master_productsstill sortsrating_descand filtersp_min_ratingonuser_rating_weighted/average_rating, so the catalog 'highest rated' order,?rating=and the leaderboard top-100 load can disagree with the shown rating. Fix = a DOS-Me migration switching both torating_score(coordinate with DOS.Me PR #1497, which also edits that RPC). Assigned to the Core session after P3.JOY commentedon Nov 1, 2026
Live check after promote #1572 (rate.vn on main b5de9f6)
Read-only check: gentle GETs and headless Chromium, desktop at 1366 px and phone at 390 px, in light and dark.
One rating: Apple iPhone 16 128GB shows 4.8 everywhere. Its stored
rating_scoreis 4.814. It appears as 4.8 on:4.8 (15));/smartphones;/compare;rating_score_5= 4.8 onproducts,products/{slug},leaderboardandversus.Leaderboard order: the shown ratings go down the list, from 4.8 to 4.7 to 4.5, with no order breaks in the API's 8 smartphones.
is_tie: truefor 16 vs 16 Plus (both 9.6), and the page says "Ngang điểm 1v1".Light and dark, phone and desktop: every page returns 200 and the backgrounds follow the theme. The stars, the scoreboard and the label "Điểm tiêu chí từ người review hoặc chuyên gia" are readable in all four.
Nothing is broken, so no issue was filed.
Still open, as noted in #1571: the catalog RPC
search_master_productssorts and filters onuser_rating_weighted, not onrating_score. Fixing that needs a DOS-Me migration.docs(proposals): the search rating filter compares at one decimal (#1545
docs(proposals): dos-me search sorts and filters on the shown rating (#…
JOY commentedon Nov 1, 2026
[Agent: PO] Rollout note: when the DOS-Me migration from PR #1574 (search_master_products on rating_score) is applied, ship a small web PR that updates the OpenAPI wording for min_rating and sort at the same time (deferred from PR #1575 because prod still filters on weighted, then expert, then average).
fix(web): rating fallbacks follow the shown rating (#1575)
docs(web): openapi min rating and rating sort read the shown rating (#…
chore(db): mirror the search trigram and rating score migrations (#1594)
JOY commentedon Nov 3, 2026
[Agent: PO] Done: one rating everywhere (PRs #1478, #1551, #1571, #1575), the catalog RPC on rating_score (DOS.Me #1533 dev, #1537 main, live on production), OpenAPI wording (#1591), mirrored in #1594; promoted in #1595.