Files
vyndr/specs/tier-structure-pull.md
builtbykev 844ab96f21 report: tier structure pull — the declared free-tier gate is unenforced on the board
Read-only. Nothing changed.

FREE TIER, EXACTLY:
  - Board /api/snapshot: NO count limit. The only gate is
    stripModelPrice(grades, tier) at routes/snapshot.js:106-107 — no slice, no
    volume branch. Live anonymous right now: MLB 5, WNBA 25 = the full board.
    The "3 scans/day" cap rations the SCAN path only.
  - Grade letter: fully visible on every tier (grade_visible: true). Anon also
    receives confidence, edge_pct and VYNDR's own projection.
  - Edge fields: correctly stripped. p_win/ev_pct/model_odds/value/takeable are
    ALL absent from the anonymous payload, with model_price_locked stamped so the
    card shows a lock teaser rather than an absent leg. This half works as designed.

THE HEADLINE — the two paths disagree on reasoning:
  - Scan REDACTS it: tierGating.js lockReasoning + lockKillConditions +
    tier_gated + upgrade hint, driven by free.reasoning_visible = false.
  - Board SERVES IT IN FULL: snapshotGating MODEL_FIELDS is
    [model_odds, p_win, ev_pct, value, takeable] — reasoning is not in the list.
    Verified live anonymously: full reasoning.summary plus a kill condition WITH
    its reason.

Intent: config/tiers.js declares free: { reasoning_visible: false } with the
comment "blurred — frontend renders tier-locked". One of the two paths does not
enforce the product's own declared line, so the evidence reads as oversight
rather than funnel — a funnel would be declared in config, not contradicted by
it. Flagged with the counterweight: board reasoning is good marketing and the
data layer is already free, so closing it is a monetization tightening (Kev's
call), not a fabrication fix.

FREE DATA IS A REAL AGGREGATOR, not just a limited graded view: schedule,
per-book lines, player stats, streaks, hot lists, team hubs, public record — all
public and uncapped (probed live).

PAID (config/tiers.js, checkout.js:4): analyst $14.99 / desk $44.99. Analyst is
unlimited reads; Desk differentiates on capability (alt ladder, Kelly, portfolio,
engine2). africa tier is defined but activation is blocked on a DB CHECK
constraint. api_access is false on every tier. book_odds/fair_odds deliberately
pass through on all tiers — the de-vigged fair number is the hook and is never
the paywall; only model_odds gates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc
2026-07-31 06:40:43 -04:00

6.4 KiB

VYNDR — TIER STRUCTURE PULL (report-only, 2026-07-31)

Real numbers from code + live anonymous prod probes. Nothing changed.


PHASE 1 — THE FREE TIER, EXACTLY

1. How many grades does free/anonymous see?

path limit enforced where
Board /api/snapshot/:sport NO LIMIT — the entire slate routes/snapshot.js:106-107 — the only gate is stripModelPrice(grades, tier); there is no count cap, no slice, no tier branch on volume
Scan /api/scan 3 per day (scans_per_day: 3) config/tiers.js free block; anonymous callers fall back to the free bucket, IP-keyed

Live, anonymous, right now: MLB 5 grades and WNBA 25 grades returned — the full board. So "5 free scans" rations the SCAN path only; the BOARD is unlimited and ungated on volume.

2. Does free see the GRADE LETTER? — YES, in full. grade_visible: true on every tier.

Anon payload carries grade: "B", confidence: 63, edge_pct: 10.8, and projection: 5.8 (VYNDR's own model number).

3. Does free get REASONING? — THE TWO PATHS DISAGREE. This is the headline.

path reasoning evidence
Scan REDACTED utils/tierGating.jsif (!canAccess(tier,'reasoning_visible'))lockReasoning() + lockKillConditions() + tier_gated: true + upgrade hint
Board SERVED IN FULL utils/snapshotGating.js MODEL_FIELDS = ['model_odds','p_win','ev_pct','value','takeable']reasoning is not in the list

Live anonymous board payload: reasoning.summary = "Natasha Cloud is averaging 5.8 assists over his last 5 games…" plus 1 kill condition WITH its reason. The scan path locks exactly this content; the board hands it over.

4. Edge fields — CORRECTLY STRIPPED. p_win, ev_pct, model_odds, value, takeable

are ALL absent from the anonymous payload, and model_price_locked: true is stamped so the card renders a lock teaser rather than an absent leg. This half of the gate works exactly as designed.


PHASE 2 — THE FREE DATA EXPERIENCE

Every one of these is public, no auth, 200 live: schedule/:sport · streaks/:sport · hotlist/:sport · stats/player/:name · books/:sport (per-book lines) · gamelines/:sport · team/:abbr · accuracy · ticker.

So there IS a real free data-aggregator surface, not merely a limited view of the graded product: schedules, per-book line shopping, player season/last-10 stats, archetypes, streaks, hot lists, team hubs and the public model record are all free and uncapped. The graded product sits on top of that as the paid layer — except that, per Phase 1.3, the board currently gives away the explanation layer too.


PHASE 3 — THE PAID TIERS (as coded)

config/tiers.js — four tiers (africa is defined but activation is blocked on a DB CHECK constraint, so it is not live):

capability free africa (inactive) analyst desk
scans/day 3 10
grade_visible
reasoning_visible
kill_conditions_detail (count only)
alerts
clv_badge
model_price (our own price)
alt_line_ladder
kelly_sizing
portfolio / engine2 (LLM)
stat_dashboard
api_access (nobody)

Prices as coded (web/src/lib/checkout.js:4): analyst $14.99 · desk $44.99. Analyst is unlimited reads by design — Desk differentiates on capability (alt ladder, Kelly, portfolio, engine2), not on rationing reads.

A deliberate, documented carve-out: book_odds / fair_odds pass through on every tier — "the de-vigged fair number is the free-tier hook, and the fair leg is NEVER the paywall; only our own price gates." Only model_odds is withheld.


PHASE 4 — THE HONEST PICTURE

8. Deliberate funnel, or oversight? — THE CONFIG SAYS OVERSIGHT.

config/tiers.js states the intent in one line: free: { reasoning_visible: false }, with the comment "blurred — frontend renders tier-locked." The scan path implements exactly that. The board path simply never got the same treatmentMODEL_FIELDS was written (Session 67) to close the model-price leak and lists only the five price-derived fields.

The evidence points one way: the product has declared that free must not see reasoning, and one of two paths does not enforce it. I would not call that a funnel decision — a funnel would be declared in config, not contradicted by it. (It is also not a new leak: it has been serving this way, and the earlier honesty passes did not catch it because they hunted fabricated content, not under-gated real content.)

Worth weighing before "fixing" it: the board reasoning is genuinely good marketing, and the line-shopping/stats layer is already free. Closing it is a monetization tightening, which is a different decision from closing a fabrication. That is Kev's call, not a bug fix.

9. WHAT IS GIVEN AWAY vs SOLD TODAY

FREE gets today PAID adds
Grades Whole board, uncapped + letter + confidence + edge_pct + VYNDR's projection — (nothing on volume)
Explanation Full reasoning + kill-condition reasons ON THE BOARD; redacted on scan Reasoning on the scan path; kill-condition detail
Prices book + fair (de-vigged) — deliberate hook model_odds (our price), CLV badge
Edge math none (p_win/ev_pct/value/takeable all stripped) the edge fields
Scans 3/day
Desk-only alt ladder, Kelly, portfolio, engine2
Raw data schedule, per-book lines, player stats, streaks, hot lists, team hubs, public record — all uncapped

One-line summary: free already receives the entire graded board, the model's projection, and — on the board only — the model's full explanation; paid buys the price/edge math, unlimited scans, and Desk capabilities. The gate that is declared but not enforced is board reasoning.

TAGS

VERIFIED: every number above (config file/line + live anonymous probes). CANNOT DETERMINE: /api/odds/:sport returned no status on probe (timeout/limiter) — not conclusive either way. BLOCKED: none. Nothing changed.