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
This commit is contained in:
Kev
2026-07-31 06:40:43 -04:00
parent 43281bb885
commit 844ab96f21
+119
View File
@@ -0,0 +1,119 @@
# 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.