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:
@@ -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.
|
||||
Reference in New Issue
Block a user