Honesty pass: remove every live fabrication (REMOVE/HIDE only, no feature cut)
Six live untruths corrected — no grade/snapshot/scorer/pipeline touched: 1. /compare — hardcoded Jokic A+/Wembanyama A + fake VERDICT replaced with an honest in-development state; removed from Nav + BottomTabBar (route still resolves, never the sample). Real two-player build is later. 2. Pricing — founder Desk $34.99→$44.99 (matches lib/checkout.js), Analyst $14.99; removed the struck $19.99/$44.99 "regular" numbers and DeskShowcase's stale $34.99. First-100 counter is real (ClaimMeter → Stripe countFounderSeats); no fake "first 50" desk claim added (no such counter exists). 3. FAQ "NexaPay" → Stripe (verified: live checkout is Next→Express→checkout.stripe.com). 4. FAQ + Features "Brier/CLV published from day one" removed (not surfaced yet) — returns when real. Backend Brier compute untouched. 5. MobileEdgeBoard removed from the Slate — its edge% feed was a miscalibrated placeholder (masked >40% as "—"); phones now show the real game cards. 6. Price triplet — never-computed model/EV now derives NO_MODEL (honest absent, MODEL "—" / "NOT PRICED", no verdict) instead of QUARANTINE's false "we suppressed our price / a leg is poisoned" copy. Fixes grade card + LiveHeroProp. Full suite 3833 green, web build exit 0. Tests updated to the new honest contracts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VsztNChZ7vEvSR61AuMhD1
This commit is contained in:
@@ -36,13 +36,16 @@ describe('Pricing — Desk is the hero, real prices, single primary CTA', () =>
|
||||
expect(tierBlock(pricing, 'desk')).toBe('true');
|
||||
});
|
||||
|
||||
test('real prices only — Desk $34.99 founder / $44.99 regular, Analyst $14.99/$19.99, Free 5 reads', () => {
|
||||
expect(pricing).toMatch(/id: 'desk'[\s\S]*?price: '\$34\.99'/);
|
||||
expect(pricing).toMatch(/id: 'desk'[\s\S]*?originalPrice: '\$44\.99'/);
|
||||
test('real founder prices only — Desk $44.99, Analyst $14.99, no struck/unwired "regular" number, Free 5 reads', () => {
|
||||
// Honesty pass: founder Desk = $44.99 (matches lib/checkout.js); Analyst = $14.99.
|
||||
// The struck "regular" numbers ($19.99 / $34.99) were removed — the post-founder
|
||||
// price is not wired, and we do not promise a figure that isn't.
|
||||
expect(pricing).toMatch(/id: 'desk'[\s\S]*?price: '\$44\.99'/);
|
||||
expect(pricing).toMatch(/id: 'analyst'[\s\S]*?price: '\$14\.99'/);
|
||||
expect(pricing).toMatch(/id: 'analyst'[\s\S]*?originalPrice: '\$19\.99'/);
|
||||
expect(pricing).toContain('5 reads to try the model');
|
||||
// no fabricated legacy price
|
||||
// no stale/fabricated founder numbers anywhere in the grid
|
||||
expect(pricing).not.toContain('$34.99');
|
||||
expect(pricing).not.toContain("originalPrice: '$19.99'");
|
||||
expect(pricing).not.toContain("originalPrice: '$24.99'");
|
||||
expect(pricing).not.toContain("originalPrice: '$49.99'");
|
||||
});
|
||||
@@ -55,8 +58,8 @@ describe('Pricing — Desk is the hero, real prices, single primary CTA', () =>
|
||||
expect(showcase).toContain('Every grade, every alt line, live.');
|
||||
expect(showcase).not.toContain('$1M');
|
||||
expect(showcase).not.toContain('1M terminal');
|
||||
expect(showcase).toContain('$44.99'); // the conversion hook figure
|
||||
expect(showcase).toContain('$34.99'); // the founder price
|
||||
expect(showcase).toContain('$44.99'); // the founder price (first 100)
|
||||
expect(showcase).not.toContain('$34.99'); // stale founder price — removed
|
||||
// real feature visuals fill the right half (kills the dead half, #8)
|
||||
expect(showcase).toContain('ALT LINE LADDER');
|
||||
expect(showcase).toContain('QUARTER-KELLY');
|
||||
|
||||
@@ -157,9 +157,13 @@ describe('deriveValueState — the single verdict function', () => {
|
||||
expect(vs.deriveValueState({})).toBe(vs.STATES.REFUSAL);
|
||||
});
|
||||
|
||||
it('a missing model price or EV withholds the verdict — never asserts NO EDGE', () => {
|
||||
expect(vs.deriveValueState({ ...base, model_odds: null })).toBe(vs.STATES.QUARANTINE);
|
||||
expect(vs.deriveValueState({ ...base, ev_pct: null })).toBe(vs.STATES.QUARANTINE);
|
||||
it('a missing model price or EV → NO_MODEL (absent leg), never NO EDGE and never QUARANTINE', () => {
|
||||
// Honesty pass: never-computed is NOT a deliberate quarantine, so it must
|
||||
// not wear QUARANTINE's "we suppressed / a leg is poisoned" copy.
|
||||
expect(vs.deriveValueState({ ...base, model_odds: null })).toBe(vs.STATES.NO_MODEL);
|
||||
expect(vs.deriveValueState({ ...base, ev_pct: null })).toBe(vs.STATES.NO_MODEL);
|
||||
// A real quarantine_reason still quarantines (distinct from never-computed).
|
||||
expect(vs.deriveValueState({ ...base, model_odds: null, quarantine_reason: 'x' })).toBe(vs.STATES.QUARANTINE);
|
||||
});
|
||||
|
||||
it('a LOCKED model leg is neither quarantine nor refusal', () => {
|
||||
@@ -174,7 +178,7 @@ describe('valueStateColor — green appears on exactly one state', () => {
|
||||
expect(vs.valueStateColor(vs.STATES.VALUE)).toBe('var(--g-a)');
|
||||
const others = [
|
||||
vs.STATES.PRICED_OUT, vs.STATES.NO_EDGE, vs.STATES.QUARANTINE,
|
||||
vs.STATES.REFUSAL, vs.STATES.NO_VERDICT_LOCKED,
|
||||
vs.STATES.REFUSAL, vs.STATES.NO_VERDICT_LOCKED, vs.STATES.NO_MODEL,
|
||||
];
|
||||
for (const s of others) expect(vs.valueStateColor(s)).not.toBe('var(--g-a)');
|
||||
});
|
||||
@@ -263,11 +267,13 @@ 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', () => {
|
||||
it('deriveValueState separates GATED (locked) from NEVER-COMPUTED — neither is QUARANTINE', () => {
|
||||
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 };
|
||||
const neverComputed = { 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);
|
||||
// Honesty pass: a never-computed model leg is NO_MODEL (absent), NOT quarantine
|
||||
// — it must not claim we "suppressed" a price we never produced.
|
||||
expect(vs.deriveValueState(neverComputed)).toBe(vs.STATES.NO_MODEL);
|
||||
});
|
||||
|
||||
it('LiveHeroProp forwards model_price_locked (live bug: it did not)', () => {
|
||||
|
||||
@@ -43,9 +43,11 @@ describe('Phase F — BottomTabBar (5-tab spec)', () => {
|
||||
describe('Phase F — More bottom sheet', () => {
|
||||
const src = read('components/BottomTabBar.tsx');
|
||||
it('lists the secondary routes', () => {
|
||||
['/compare', '/tracker', '/blog', '/invite', '/pricing', '/account', '/help', '/about', '/responsible-gambling'].forEach((href) =>
|
||||
// Compare removed from nav (honesty pass — in development, no real feed).
|
||||
['/tracker', '/blog', '/invite', '/pricing', '/account', '/help', '/about', '/responsible-gambling'].forEach((href) =>
|
||||
expect(src).toContain(href),
|
||||
);
|
||||
expect(src).not.toContain('/compare');
|
||||
});
|
||||
it('slides up with a dismissible backdrop', () => {
|
||||
expect(src).toContain('sheet-up');
|
||||
|
||||
@@ -182,23 +182,14 @@ describe('M1 — mobile foundation is wired (structural, not visual)', () => {
|
||||
describe('M4 — mobile structural rules locked (source, not visual)', () => {
|
||||
const css = read('app/globals.css');
|
||||
|
||||
it('the flat EDGE BOARD is the mobile board; game cards are desktop-only', () => {
|
||||
// Design screen 01: <768px shows the flat edge-ranked board, ≥768px keeps
|
||||
// the game-grouped cards.
|
||||
expect(css).toMatch(/\.edge-board-mobile\s*\{\s*display:\s*none/);
|
||||
expect(css).toMatch(/@media\s*\(max-width:\s*767px\)[^]*?\.edge-board-mobile\s*\{\s*display:\s*block/);
|
||||
expect(css).toMatch(/\.edge-board-desktop\s*\{\s*display:\s*none\s*!important/);
|
||||
it('the fabricated mobile edge board is REMOVED — phones show the real game cards', () => {
|
||||
// Honesty pass: MobileEdgeBoard's edge% feed was a miscalibrated placeholder
|
||||
// (it masked any value >40% to "—"). It renders nowhere until a real edge
|
||||
// feed exists. The Slate no longer imports or renders it; mobile now shows
|
||||
// the same real game cards as desktop.
|
||||
const slate = read('components/Slate.tsx');
|
||||
expect(slate).toMatch(/MobileEdgeBoard/);
|
||||
expect(slate).toMatch(/flattenToEdgeBoard/);
|
||||
});
|
||||
|
||||
it('the mobile edge board renders matchup chips + tier grade + one hero edge%', () => {
|
||||
const b = read('components/vyndr/MobileEdgeBoard.tsx');
|
||||
expect(b).toMatch(/TeamChip/); // Rev 3 matchup chips
|
||||
expect(b).toMatch(/GradeBadge/); // tier-colored grade
|
||||
expect(b).toMatch(/var\(--miss\)/); // negative edge = red by sign
|
||||
expect(b).toMatch(/rampOpacity/); // Design's ranked opacity ramp
|
||||
expect(slate).not.toMatch(/MobileEdgeBoard/);
|
||||
expect(slate).not.toMatch(/flattenToEdgeBoard/);
|
||||
});
|
||||
|
||||
it('document still never scrolls sideways on phones (overflow contained)', () => {
|
||||
@@ -290,12 +281,14 @@ describe('P1-7 — edge board guards the broken edge placeholder', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ── P1-8 lock — Compare verdict makes no unverifiable "tonight" claim ────────
|
||||
describe('P1-8 — Compare verdict is time-honest (form, not a phantom game)', () => {
|
||||
// ── P1-8 lock — Compare ships NO fabrication (honest in-development state) ────
|
||||
describe('P1-8 — Compare carries no hardcoded grades or phantom verdict', () => {
|
||||
const compare = read('app/compare/page.tsx');
|
||||
it('the sample verdict never claims "tonight" (NBA has 0 games in July)', () => {
|
||||
it('no sample players, no fake verdict, no "tonight" claim — honest in-dev', () => {
|
||||
expect(compare).not.toMatch(/edge tonight/i);
|
||||
expect(compare).toMatch(/on current form/i);
|
||||
expect(compare).not.toMatch(/Joki|Wembanyama/);
|
||||
expect(compare).not.toContain('VYNDR VERDICT');
|
||||
expect(compare).toMatch(/IN DEVELOPMENT/i);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -84,10 +84,11 @@ describe('Phase E.3 — stubs are now real pages', () => {
|
||||
it.each(pages)('/%s is no longer a RouteStub', (p) => {
|
||||
expect(read(`app/${p}/page.tsx`)).not.toContain('RouteStub');
|
||||
});
|
||||
it('compare renders two player inputs + a verdict', () => {
|
||||
it('compare shows an honest in-development state — no sample grades, no fake verdict', () => {
|
||||
const src = read('app/compare/page.tsx');
|
||||
expect((src.match(/TerminalInput/g) || []).length).toBeGreaterThanOrEqual(2);
|
||||
expect(src).toContain('VYNDR VERDICT');
|
||||
expect(src).toMatch(/IN DEVELOPMENT/i);
|
||||
expect(src).not.toContain('VYNDR VERDICT');
|
||||
expect(src).not.toMatch(/Joki|Wembanyama/); // no hardcoded sample players
|
||||
});
|
||||
it('invite renders the 3-friends referral messaging', () => {
|
||||
expect(read('app/invite/page.tsx')).toContain('3 friends');
|
||||
|
||||
Reference in New Issue
Block a user