Forward model_price_locked to the hero — a paywall was wearing poison's copy

Live induction on the deployed landing page caught this; markup review and the
unit suite both passed it. With the model leg stripped for anonymous visitors,
LiveHeroProp forwarded book/fair/model/ev/quarantine to PriceTriplet but NOT
model_price_locked, so deriveValueState fell through to the missing-model-price
branch and the card rendered "MODEL READ WITHHELD" — the quarantine state,
whose copy says we suppressed our own price because a leg is poisoned. Nothing
was poisoned. The real reason was the paywall, and the two must never share a
face: one says our data is untrustworthy, the other says you don't have this
tier.

Now forwarded, with a test asserting both the separation in deriveValueState
and the forwarding at the call site.

Tests 3559 passed / 291 suites, web build exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCNgGSt5qvcLxaeQqa7Zpj
This commit is contained in:
Kev
2026-07-20 19:56:21 -04:00
parent aaa41134d4
commit 4e2f488341
3 changed files with 31 additions and 1 deletions
+23
View File
@@ -261,6 +261,29 @@ describe('PriceTriplet component', () => {
});
});
// ── 6b. CONSUMERS MUST FORWARD THE LOCK FLAG ─────────────────────────────
describe('a gated model leg never wears quarantine copy', () => {
it('deriveValueState separates GATED from WITHHELD', () => {
const gated = { book_odds: -153, fair_odds: -129, model_odds: null, ev_pct: null, model_price_locked: true };
const withheld = { book_odds: -153, fair_odds: -129, model_odds: null, ev_pct: null };
expect(vs.deriveValueState(gated)).toBe(vs.STATES.NO_VERDICT_LOCKED);
expect(vs.deriveValueState(withheld)).toBe(vs.STATES.QUARANTINE);
});
it('LiveHeroProp forwards model_price_locked (live bug: it did not)', () => {
// Without this the landing hero rendered "MODEL READ WITHHELD" — poison's
// copy — to every anonymous visitor, when the real reason was the paywall.
const hero = read('web/src/components/LiveHeroProp.tsx');
expect(hero).toMatch(/model_price_locked: data\.model_price_locked/);
});
it('the grade adapter forwards it too', () => {
const { buildPriceTriplet } = require('../../web/src/lib/gradeAdapter');
const t = buildPriceTriplet({ book_odds: -153, fair_odds: -129, model_price_locked: true }).priceTriplet;
expect(t.model_price_locked).toBe(true);
});
});
// ── 7. THE SERVER GATE — fair is never the paywall ───────────────────────
describe('free-tier gate strips the model price at the wire', () => {
const { applyTierGating } = require('../../src/utils/tierGating');