# 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.js` — `if (!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 treatment** — `MODEL_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.