Wave 1: wire /intelligence, /slip, /parlay + /marketplace honesty pass

Wiring + one copy pass. No grade, ledger, model or scoring change (diff empty
across intelligence/, ledgerService, outcomeService, gradeSlateService).

REVIEW ZERO — each surface proven with real data BEFORE wiring:
  0.1 /intelligence vs /system are NOT duplicates. System.dc.html is a
      multi-surface artboard (TERMINAL + INTELLIGENCE + WIRE sections), not the
      design for a distinct /system route; its INTELLIGENCE section is already
      realised as the live app/intelligence/page.tsx. No /system page exists and
      none should be built as a second copy — the prod 404 is correct.
  0.2 /intelligence renders live and gates SERVER-side, not by blur: the proxy
      requires auth and limits by tier (desk 50 signals / non-desk 8), and
      returns 401 to an anonymous caller (verified live). No leak.
  0.3 /slip parses a real DraftKings slip end to end: 3/3 legs,
      needs_review false, Aaron Judge total_bases over 1.5 @ -115. Honest limit
      recorded: parsers are layout-rigid, an unsupported layout yields ZERO legs
      rather than wrong ones (never-guess), so real-world OCR hit-rate across
      layouts is CANNOT DETERMINE until user slips arrive.
  0.4 /parlay direct route hits the real correlation builder on the same
      ParlayContext the drawer uses.
  0.5 /marketplace advertised four unbuilt things but made NO performance or
      profit claim, and its capture was already real (/api/waitlist upserts to a
      waitlist table). The gap was tense, not fabrication.

WIRED: Nav MORE gains Intelligence, Slip Reader and Marketplace; Parlay Lab
re-pointed from the drawer hash to /parlay (the drawer is unaffected —
ParlayPanel stays mounted with its floating badge).

GATING: /intelligence added to GATED_ROUTES because its feed 401s signed-out, so
an ungated link would land visitors on a permanently empty page. /parlay stays
OPEN deliberately — it is the free parlay funnel and gating it would be a
monetization regression.

/marketplace honesty pass: every item body now opens "Not built yet." /
"Not written yet." / "Not produced yet." with what is planned; the subhead states
it is not a purchase, not a pre-order and not a promise of a ship date; the
playbook item carries "No profit claim, no promised return". The capture stays
real — no fake button. Unit-locked.

Floor: 315 suites / 3920 tests green (12 new), web build exit 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc
This commit is contained in:
Kev
2026-07-31 04:51:42 -04:00
parent 6328a5ce92
commit ff7f5d8d2d
6 changed files with 174 additions and 8 deletions
+60
View File
@@ -0,0 +1,60 @@
# SPEC — WAVE 1: WIRE THE WORKING SURFACES + /marketplace HONESTY (shipped)
2026-07-31. Wiring + one copy pass. No grade, ledger, model, or scoring change.
## REVIEW ZERO — each surface proven BEFORE wiring
- **0.1 `/intelligence` vs `/system` — NOT duplicates; nothing to resolve away.** `System.dc.html`
is a MULTI-surface artboard (it contains TERMINAL, INTELLIGENCE and WIRE sections), not the design
for a distinct `/system` route. Its INTELLIGENCE section is **already realised** as the live
`app/intelligence/page.tsx`. **No `/system` page exists and none should be built as a second copy**
the prod 404 is correct. Only `/intelligence` was wired. ✅
- **0.2 `/intelligence` renders live + gates correctly.** Real feed (`/api/intelligence/feed`), 160
lines, zero placeholders. **Entitlement is enforced SERVER-side, not by blur**: the proxy requires
auth and limits by tier — **desk 50 signals / non-desk 8** — and returns `401 Not signed in` to an
anonymous caller (**verified live in prod**). So making it nav-reachable leaks nothing. ✅
- **0.3 `/slip` functions END TO END on a real slip.** Parsed the real DraftKings fixture:
**3/3 legs, `needs_review: false`**, leg 1 = `Aaron Judge · total_bases · over 1.5 @ -115`. ✅
**Honest limit recorded:** the parsers are layout-rigid. A slip in an unsupported layout yields
**zero legs rather than wrong ones** — correct per the NEVER-GUESS contract, but it means real-world
OCR hit-rate across layouts is **CANNOT DETERMINE** until real user slips arrive.
- **0.4 `/parlay` direct route hits the working surface.** `app/parlay/page.tsx` is the real
correlation builder on the **same `ParlayContext`** the drawer uses — not a separate unbuilt page. ✅
- **0.5 `/marketplace`** advertises four unbuilt things (API access · custom alerts · "The Edge"
playbook · merch capsule). **No performance/edge/profit claim was present** (the worse category —
clean). The capture was already REAL: `fetch('/api/waitlist')``src/routes/waitlist.js` upserts to
a `waitlist` table. The gap was tense: bodies read as present-tense descriptions.
## PHASE 1 — WIRED (all three passed)
`Nav.tsx MORE`: **Intelligence**, **Slip Reader**, **Marketplace** added; **Parlay Lab re-pointed from
`#parlay` (drawer-only) to `/parlay`** (the real page). The drawer is unaffected — `ParlayPanel` stays
mounted in the layout with its floating badge, its natural in-context use from a prop row.
**Gating change (one line, principled):** `/intelligence` added to `GATED_ROUTES`. Its feed 401s
signed-out, so an ungated nav link would land visitors on a permanently empty page. `/slip` was already
gated (personal surface). **`/parlay` deliberately stays OPEN** — it is the free parlay funnel and
gating it would be a monetization regression (same rule as dashboard/scan).
## PHASE 2 — /marketplace HONESTY PASS
Every item body now opens **"Not built yet."** / "Not written yet." / "Not produced yet." followed by
what is *planned*. The subhead is explicit: *"None of the below is built yet… joining a list records
your interest, nothing more. It is not a purchase, not a pre-order, and not a promise of a ship date."*
The playbook item carries **"No profit claim, no promised return."** The capture stays REAL (no fake
button). Unit-locked: every body must contain a not-yet marker, and the page must carry no
guaranteed/win-rate/ROI/beat-the-book language.
## PHASE 3 — VERIFY
Grade, ledger, model, scoring **untouched** (diff empty across `intelligence/`, `ledgerService`,
`outcomeService`, `gradeSlateService`). **315 suites / 3,920 tests green** (12 new), **web build exit 0**.
**Chrome audit manifest — Wave 1 additions:**
| surface | state | check |
|---|---|---|
| `/intelligence` | **signed-out** → should redirect to `/login` (now gated) | no empty-feed page |
| `/intelligence` | **free** vs **ENTITLED (Desk)** | free sees 8 signals + lock overlay; **Desk sees 50***entitled path needs Kev* |
| `/slip` | **free (signed-in)** | upload/paste → editable legs; unsupported layout → empty, not wrong |
| `/parlay` | **logged-out** | direct nav entry loads the full lab; free 2-leg cap + payout blur |
| `/marketplace` | logged-out | "Not built yet" on every item; Join waitlist records and confirms |
## TAGS
VERIFIED: 0.10.5, all wiring, gating, copy. **CANNOT DETERMINE: real-world slip OCR hit-rate across
book layouts** (no user slips yet). BLOCKED: none.
+91
View File
@@ -0,0 +1,91 @@
/**
* WAVE 1 — surfaces wired only after being proven to work with real data
* (specs/wave1-wiring.md). Existence != works: the populated-path crash and the
* single-book default both came from surfaces that "existed" but had never run.
*/
const fs = require('fs');
const path = require('path');
const read = (p) => fs.readFileSync(path.join(__dirname, '../../', p), 'utf8');
const routes = require('../../web/src/lib/routes');
describe('the three proven surfaces are reachable from nav', () => {
const nav = read('web/src/components/Nav.tsx');
test('/intelligence, /slip and /marketplace have nav entries', () => {
expect(nav).toMatch(/href: '\/intelligence'/);
expect(nav).toMatch(/href: '\/slip'/);
expect(nav).toMatch(/href: '\/marketplace'/);
});
test('Parlay Lab points at the REAL page, not only the drawer hash', () => {
expect(nav).toMatch(/\{ label: 'Parlay Lab', href: '\/parlay' \}/);
expect(nav).not.toMatch(/href: '#parlay', action: 'parlay'/);
});
});
describe('gating is correct for the newly-public surfaces', () => {
test('/intelligence is GATED — its feed 401s signed-out, so an ungated link would land on an empty page', () => {
expect(routes.GATED_ROUTES).toContain('/intelligence');
expect(routes.isGatedRoute('/intelligence')).toBe(true);
});
test('/slip stays gated (personal surface)', () => {
expect(routes.GATED_ROUTES).toContain('/slip');
});
test('/parlay stays OPEN — it is the free parlay funnel, gating it is a monetization regression', () => {
expect(routes.OPEN_ROUTES).toContain('/parlay');
expect(routes.isGatedRoute('/parlay')).toBe(false);
});
test('no route is both gated and open', () => {
const overlap = routes.GATED_ROUTES.filter((r) => routes.OPEN_ROUTES.includes(r));
expect(overlap).toEqual([]);
});
});
describe('/slip actually parses a real slip end to end (the works-with-real-data proof)', () => {
const { parseSlipText } = require('../../src/services/slipReader');
const fixture = fs.readFileSync(
path.join(__dirname, '..', 'fixtures', 'slips', 'draftkings.txt'), 'utf8',
);
test('extracts every leg from the real DraftKings grammar', () => {
const out = parseSlipText(fixture);
expect(out.book).toBe('draftkings');
expect(out.legs).toHaveLength(3);
expect(out.needs_review).toBe(false);
expect(out.legs[0]).toMatchObject({ player: 'Aaron Judge', stat: 'total_bases', line: 1.5, side: 'over', odds: -115 });
});
test('an UNRECOGNISED layout yields NO legs rather than wrong ones (never-guess)', () => {
// A layout the parser does not support must degrade to empty, not fabricate.
const out = parseSlipText('DraftKings\nAaron Judge\nHome Runs\nOver 0.5 +285');
expect(out.legs).toHaveLength(0);
});
});
describe('/marketplace is honest interest-capture', () => {
const src = read('web/src/app/marketplace/page.tsx');
test('every item states it is NOT built yet', () => {
const bodies = src.match(/body: '[^']+'/g) || [];
expect(bodies.length).toBeGreaterThanOrEqual(4);
for (const b of bodies) expect(b).toMatch(/Not built yet|Not written yet|Not produced yet/);
});
test('the page says plainly it is not a purchase, pre-order, or ship-date promise', () => {
expect(src).toMatch(/None of the below is built yet/);
expect(src).toMatch(/not a pre-order/);
});
test('makes NO performance / edge / profit claim', () => {
expect(src).toMatch(/No profit claim, no promised return/);
expect(src).not.toMatch(/guaranteed|win rate|ROI of|beat the book/i);
});
test('the capture is REAL — it posts to the live waitlist endpoint', () => {
expect(src).toMatch(/fetch\('\/api\/waitlist'/);
expect(fs.existsSync(path.join(__dirname, '../../web/src/app/api/waitlist/route.ts'))).toBe(true);
});
});
+1 -1
View File
File diff suppressed because one or more lines are too long
+7 -5
View File
@@ -6,22 +6,22 @@ const ITEMS = [
{
list: 'api',
title: 'API access',
body: 'Programmatic access to the grading engine. Build your own dashboards, alerts, automations.',
body: 'Not built yet. Planned: programmatic access to the grading engine so you can build your own dashboards, alerts and automations.',
},
{
list: 'alerts',
title: 'Custom alerts',
body: 'Get pinged when specific players, stats, or matchups hit your threshold — by SMS, email, or webhook.',
body: 'Not built yet. Planned: get pinged when specific players, stats or matchups hit a threshold you set — by SMS, email or webhook.',
},
{
list: 'The Edge',
title: 'The Edge — playbook',
body: 'Long-form analysis of the model and how to deploy it in real bankroll management.',
body: 'Not written yet. Planned: long-form analysis of how the model works and how to think about bankroll. No profit claim, no promised return.',
},
{
list: 'merch',
title: 'Capsule drop',
body: 'First merch run. 200 units. Founder access gets first pull.',
body: 'Not produced yet. Planned: a first merch run, founder access gets first pull. Unit count not fixed.',
},
];
@@ -60,7 +60,9 @@ export default function MarketplacePage() {
The VYNDR Marketplace.
</h1>
<p style={{ color: 'var(--text-secondary)', fontSize: 16, maxWidth: 560, margin: '0 auto' }}>
API access, custom alerts, deep analysis, and limited drops. Reserve your spot these go to founder users first.
None of the below is built yet. This page exists so you can tell us which ones you&apos;d
actually use joining a list records your interest, nothing more. It is not a purchase,
not a pre-order, and not a promise of a ship date.
</p>
</header>
+10 -2
View File
@@ -24,8 +24,16 @@ const PRIMARY = [
];
const MORE = [
{ label: 'Explore', href: '/explore' },
// Session 60 (4.4) — discoverable Parlay Lab entry (opens the drawer).
{ label: 'Parlay Lab', href: '#parlay', action: 'parlay' as const },
// Wave 1 (2026-07-31) — the Parlay Lab entry now points at the REAL page
// (app/parlay/page.tsx, the full correlation builder) rather than only opening
// the drawer. The drawer is unaffected: ParlayPanel stays mounted in the layout
// with its floating badge, which is its natural in-context use from a prop row.
{ label: 'Parlay Lab', href: '/parlay' },
// Wave 1 — proven-before-wired. /intelligence renders a real, server-tier-limited
// feed; /slip parses a real slip end to end (3/3 legs on the DraftKings grammar).
{ label: 'Intelligence', href: '/intelligence' },
{ label: 'Slip Reader', href: '/slip' },
{ label: 'Marketplace', href: '/marketplace' },
// Compare is in development (no real two-player feed yet) — no nav entry until
// it renders real data. The route still resolves to an honest in-dev state.
{ label: 'Tracker', href: '/tracker' },
+5
View File
@@ -25,6 +25,11 @@ const GATED_ROUTES = [
'/notifications',
'/invite',
'/slip', // A1 S9 — slip reader (personal surface: reads the user's own bet slip)
// Wave 1 (2026-07-31) — /intelligence became nav-reachable. Its feed REQUIRES auth
// (`/api/intelligence/feed` returns 401 signed-out, verified live) and limits by tier
// SERVER-side (desk 50 signals / non-desk 8) — so it never blurred-over full data.
// Gated so a signed-out visitor lands on /login instead of a permanently empty feed.
'/intelligence',
];
/* Open — reachable without auth (marketing + the free funnel + auth itself). */