S3 (a1): affiliate + partner plumbing

Zero out-of-pocket; everything config-flip-ready but DISABLED/organic.

- BOOK IT deep links: web/src/lib/bookLinks.js + affiliateConfig.js
  (all books enabled:false, Impact/Partnerize param shapes documented,
  empty params skipped). Wired into StatStrip BookItTeaser (real anchor
  now) + scan hand-off links. Every book anchor renders
  rel="sponsored noopener noreferrer" (BOOK_LINK_REL).
- Best-price marker: slateAdapter.detectBestBook (only when >=2 books
  post the SAME line and prices differ — absent beats wrong) + subtle
  signal-green dot in StatStrip. Slate.groupByGame threads the grouped
  per-book rows (books[]) onto PropRowProp instead of discarding them.
- Partner refs: ?ref=CODE -> vyndr_ref cookie (90d, first-touch,
  PartnerRefCapture in layout) -> signup metadata partner_ref ->
  internal GET /api/partners/report/:code (requireInternalAuth; honest
  zeros + note until the TODO migration in docs/PARTNERS.md adds
  user_profiles.partner_ref — NOT run). Stripe promo-code convention:
  partner code == promotion code, verbatim.
- Tests: +41 (2398 -> 2439, 209 suites); bookItTeaser + vyndrCoreScreens
  invariants updated to the new (stronger) rel contract. Web build exit 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kev
2026-07-11 14:28:18 -04:00
parent e4d2e79f95
commit 0996320bd1
23 changed files with 1151 additions and 36 deletions
+3
View File
@@ -9,6 +9,7 @@ import Footer from '@/components/Footer';
import AuthGate from '@/components/AuthGate';
import HashRedirect from '@/components/vyndr/HashRedirect';
import GlobalHosts from '@/components/vyndr/GlobalHosts';
import PartnerRefCapture from '@/components/vyndr/PartnerRefCapture';
// Session 50 — the Parlay Lab supersedes the legacy ParlayTray.
import ParlayPanel from '@/components/vyndr/ParlayPanel';
import BottomTabBar from '@/components/BottomTabBar';
@@ -156,6 +157,8 @@ export default async function RootLayout({ children }: { children: React.ReactNo
<SentryInit />
{/* Session 38 — prefs apply + paywall/checkout/prefs globals (§9/§10/§12) */}
<GlobalHosts />
{/* A1 S3 — first-visit ?ref=CODE → vyndr_ref cookie (90d, first-touch) */}
<PartnerRefCapture />
</ParlayProvider>
</ExplainModeProvider>
</AuthProvider>