LiquidRetail · Quote Quality Workstream · Shipped 2026‑08‑11
This is a review of the data and the gates that feed ads — not a creative review. No ads were re-rendered for this comparison. Rendering costs real money and wasn’t approved for this run, so nothing below shows a new ad image. What changed is what gets stored, trimmed, ranked, and blocked before a quote or a rating ever reaches a renderer.
A quote stored for Pelagic Gear’s brand‑reviews card ran off the end of a sentence. It wasn’t a rendering glitch — the stored text itself stopped mid-thought.
It passed the anti-fabrication check on the way in because it genuinely is a substring of the grounded narrative — nothing was invented. Cutting a string mid-sentence doesn’t fabricate anything; it just leaves you holding an unfinished thought, and nothing downstream was checking for that.
Selection, not repair. The fix doesn’t rewrite or complete the sentence — it selects a shorter prefix that already ends cleanly. The kept text above is a literal, character-for-character prefix of the original string. Nothing is added, nothing is reworded.
This section was meant to carry two screenshots of the live Pelagic Gear brand‑reviews card: before-pelagic-brand-reviews.png and after-pelagic-brand-reviews.png.
Checked before embedding: the two files are byte-for-byte identical (same SHA-256) and both show stock fashion product photography — not a UI capture of any reviews card, quote, or rating. Whatever got saved to those paths isn’t the before/after pair described. Rather than caption unrelated images as if they were the fix, they’re omitted here. The text diff above is the verified before/after; the screenshots need to be recaptured from the live app.
Vuori held 4.6★ / 15,545 reviews. After a refresh, the same brand held null / null. A null rating makes social_proof_led ineligible outright — there’s nothing to display.
The first diagnosis was “grounded-search drift” — the theory that the web-grounding step was returning inconsistent results across refreshes. That theory didn’t survive a timeline check: the deploy live at 09:05 already contained the new retrieval prompt, so every number-less run observed afterward was already running the new prompt, not the old one. The last fetch that actually returned numbers was at 07:45, before the new prompt went live. The drift theory was blaming the wrong side of the deploy.
The real cause: the grounded pass‑1 call was ending with finishReason: MAX_TOKENS, and nothing in the codebase checked finishReason — so a truncated narrative looked identical to a complete one downstream. The rating was requested last in the prompt, so the model spent its budget listing quotes and never got to write the numbers. It was compounded by a second gap: pass‑2 set thinkingBudget: 0, but pass‑1 never did, and hidden reasoning tokens bill against the same ceiling as the visible output.
| Prompt | finishReason | Narrative | Quotes | Rating |
|---|---|---|---|---|
| Numbers last, thinking on | MAX_TOKENS | 941 chars | 4 | none |
| Numbers first, thinking off | STOP | 3,026 chars | 12 | 4.58 / 15,626 |
Verified end to end through the shipped function:
Three consecutive live Vuori refreshes stored three different ratings, because the code kept whichever aggregate the narrative happened to mention first — an artifact of prose order, not a decision.
| Refresh | Stored | Would print |
|---|---|---|
| Local verification | 4.58★ / 15,626 · vuoriclothing.com | 4.6★ |
| 18:18 live | 3.8★ / 28 | nothing |
| 18:20 live | 2.5★ / 126 · Trustpilot | nothing |
Now, pass‑1 asks for every aggregate the grounding turns up, each with its source named, and is explicitly forbidden from pre-picking or averaging them. A separate picker ranks the candidates afterward and records which source won.
Read literally, “prefer the highest rating” would hand the ad to a 5.0★ from 3 reviews over a 4.6★ from 15,000. So the picker ranks on credible sample size first, then most reviews, then highest rating — a 50-review floor has to clear before rating is even compared.
Anything at or below 4.39★ prints no stars at all, regardless of which source won the pick. That’s why 3.8★ and 2.5★ were never going to show in the table above — the floor, not the picker, was already going to suppress them.
These were genuinely stored as ad-usable under the old bar. They’re dropped at intake now.
“All clothes, including the workout shorts, have a slim, tailored fit.”
Neutral product description — no praise, nothing a reviewer chose to say approvingly.
“They go on flash sale and/or 20% off.”
A promotional claim, not a sentiment — nobody chose to praise a discount.
“Great fit, and lightweight.”
Generic filler — true of almost any product, says nothing specific.
“This is a low-support option best suited for lighter activities.”
Contains “best,” so it passed a positive-word list — but the sentence argues against the product.
“These might be the softest sweatpants I’ve ever put on.”
This is the counter-example that drove a calibration change. Sensory praise like this has no evaluative word like “great” or “best” on the surface — it used to read as neutral. Sensory praise now counts as praise.
One more layer: the string that actually gets typeset is now judged on its own, separately from the quote it was pulled from. A parent quote can pass the bar while the fragment lifted out of it for layout does not:
All three fail the bar as standalone fragments even when their parent quote passes it. That mismatch — approving the sentence, then typesetting whatever snippet was cut from it without re-checking — is how a subjectless fragment became a testimonial.
Not dressing this up. Four things shipped today didn’t fix, in order of how much they matter.
Quote selection is still intent-blind. The funnel-stage/angle bias logic exists in code, but no caller supplies a stage or angle, and the layout artifact is cached — so every static ad for a given product reads the same stored quote regardless of who it’s targeting. A fix is proposed, not built.
STAGE_TERMS has no retention or conquest terms defined, even though quotes are already being tagged stage: 'retention' elsewhere in the pipeline.
Video still binds a ≤50-character snippet by design. That length rarely fits a complete sentence, so video is structurally more exposed to the fragment problem in Section 4 than static formats are.
The rating picker can select a brand’s self-reported aggregate over a lower third-party one. That’s an explicit owner decision, not an oversight — it’s now recorded per-brand via ratingSource so it’s visible which way each brand was set.