57 · Financial coherence audit (2026-05-18)
Senior PM / finance + UX review of all main public pages. Each finding lists the page, the offending claim, the missing context, the verdict, and the copy fix actually shipped in this PR.
Pages reviewed:
/(landing components insrc/components/landing/*)/performance/backtest/methodologie/how-it-works/recommendations/pricing/docs- Blog hero block
A · Findings (10)
A1 · LandingHero "Win rate T+90" badge has no sample or period
- Where:
src/components/landing/LandingHero.tsx:299-312 - Claim:
XX% of signals with score ≥ 65 are profitable at 90 days - Missing: n, time period, comparison (vs random ~50% on noisy 90d returns).
- Verdict: Headline-only number. Reader can't tell if it's a curated 196-row
subset or the full 23,788-row universe. The default snapshot fallback in
LandingTrackRecordis 53.3% (whole-universe OOS), butLandingHeroreads whichever number the caller passes — the live wiring uses the curated subset. - Fix: append "· n=196 filtered, 2021-2026, vs ~50% random" to the badge.
A2 · LandingTrackRecord chart title vs metrics mismatch
- Where:
src/components/landing/LandingTrackRecord.tsx:110-145 - Claim: chart is labelled "Win rate · T+90 · 24-month rolling" but the
fallback values are documented in code as "last 6 months BUY cohort"
(audit 51) and the sparkline points are literally faked (line 34:
// Sparkline data: fake historical win rate over 24 months). - Verdict: Misleading. Either the data is rolling-24m or it is the last 6-month cohort, it cannot be both. The fake sparkline is the most legally risky single line in the codebase: a marketing chart that invents historical values.
- Fix:
- Caption changed to "Win rate · T+90 · 6-month BUY cohort".
- Sparkline marked
aria-label="Illustrative shape — see /performance for audited history". - Code comment upgraded so future devs replace it with real data.
A3 · /recommendations BUY universe stat is fine but jargon-heavy
- Where:
src/app/recommendations/page.tsx:53-54 - Claim:
BUY universe: mean +0.78%, win rate 46.8% (n=23,788 cross-market) - Missing: nothing factually, but "cross-market" / "BUY universe" / "composite score v5.1" are pure jargon — a retail user has no idea whether 46.8% is good or bad.
- Verdict: Honest but unreadable. Same number on
/performanceis contextualised ("Sigma alpha comes from selection, not from buying the universe"). - Fix: Glossary tooltips wired on
win rate,composite score,cross-market.
A4 · "Sharpe 1.32" / "DSR 0.311" claims need consistent guard rails
- Where:
src/components/landing/LandingFeatures.tsx:191-192 - Claim:
Portfolio Sharpe DSR 0.311 (CI95 [-0.08, +2.79]) · cross-sectional 1.87 - Verdict: Excellent disclosure (CI straddles zero is shown), but the same paragraph mixes 3 different Sharpe definitions in one breath. A reader who doesn't know what DSR is will see "1.87" and stop reading.
- Fix: Glossary tooltips on
Sharpe,DSR,cross-sectional. The 1.87 figure already carries an asterisk inLandingSigma, propagate the same pattern here.
A5 · "Sharpe 1.0203" style false precision
- Where: searched. None found in shipping pages — STRATEGY_PROOF rounds
to 2 decimals everywhere via
sharpeDisclosure(). - Verdict: Clean. Keep using
.toFixed(2)for Sharpe everywhere.
A6 · Survivor-/look-ahead-bias disclosure
- Where:
src/lib/winning-strategy.ts:877-884disclosure.survivorshipBiasanddisclosure.pitBiasRiskare present and detailed. - Verdict: Disclosure exists but only on
/performance. Landing, blog hero and/recommendationsnever link to it. A user scrolling the landing sees Sharpe figures with zero bias warning. - Fix: Footer rail "Past-performance & bias notice" link added to landing
via
LandingFeaturesStrategy card. Already linked from blog index, kept.
A7 · Mixing whole universe with curated Sigma subset
- Where: across pages. STRATEGY_PROOF distinguishes
universeSize=23,788fromfilteredSubsetSize=196, and most components use the right one. Risk: a future contributor swapping them silently because the field names are similar. - Verdict: Architecture is correct, the risk is regression.
- Fix: Glossary terms
universeandfiltered-subsetboth seeded, used in tooltips so future copy authors can grab the canonical definitions rather than improvising.
A8 · "554k+ filings" headline number drift
- Where:
/how-it-works, landing hero, blog meta. - Claim: "around 554k filings" hard-coded in several copy strings; live DB
count via
decls.totalis closer to 591k (current count on prod, per audit 48). The hero uses the live count, the "what we do" cards still say 554k. - Verdict: Minor staleness. The live total is preferred.
- Fix:
/how-it-workscards refactored to read from the live total via the existinggetDeclarationStats()helper used by the hero.
A9 · "/fonctionnement" is a routing zombie
- Where:
src/app/api/docs/route.ts:53still links to/fonctionnement/, which 301s to/how-it-works. Foldersrc/app/fonctionnement/has only_components/and nopage.tsx. - Verdict: Inconsequential, but the 301 hop is wasteful and the dead folder is confusing. Out of scope for this PR — flagged in audit 56 backlog.
A10 · /pricing Pro tier mentions "Walk-forward backtest + Bailey-LdP DSR"
- Where:
src/app/pricing/page.tsx:82 - Claim: pure jargon as feature bullet.
- Verdict: A buyer who doesn't know DSR cannot evaluate the feature.
- Fix: Glossary tooltips on
walk-forwardandDSRso hovering a pricing bullet explains it.
B · Risks not fixed in this PR
- The "fake 24-month sparkline" data (A2 §2) is now caveated but not replaced with real rolling data. Replacing it requires a backfill job over RecoSnapshot history.
/recommendationspage table headers ("Score breakdown", "Wilson CI") are still untooltipped on small viewports — popovers don't fit on a phone. Mobile copy needs a second pass.- The "since 2015" framing on landing implies 11 years of OOS testing. Reality is in-sample 2015-2025 + walk-forward 2025-01 → 2026-05. The STRATEGY_PROOF disclosure says so, but the landing copy ("real performance since 2015") does not. Recommended fix: split the headline into "11 years backtested, 17 months walk-forward".
C · Glossary terms shipped
25 terms seeded — see prisma/seed-glossary.ts and the /admin/glossary
table. Each carries a slug, EN + FR label, ~80-word EN + FR definition,
optional formula, optional example, optional source list.
Terms: sharpe-ratio, deflated-sharpe-ratio, win-rate, max-drawdown, annualized-return, out-of-sample, walk-forward, cross-sectional, insider-buy-signal, composite-score, cluster-bonus, holding-period, filing-date, transaction-date, mar-article-19, sec-form-4, pdmr, beta-neutral, t-plus-90, alpha, regime, universe, filtered-subset, wilson-ci, sortino-ratio.
D · BO additions
/admin/glossary— list + inline edit per term. Server actions; no client state library./admin/landing-copy— JSON-backed Setting rows (landing.heroHeadline,landing.kpiLabels,landing.ctaCopy). Pure form, no preview UI in v1.