STATE: Session 67 — price layer gated at two doors, read card wired

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCNgGSt5qvcLxaeQqa7Zpj
This commit is contained in:
Kev
2026-07-20 19:59:33 -04:00
parent 4e2f488341
commit 0264486bf9
+94 -1
View File
@@ -1,5 +1,5 @@
# VYNDR — STATE OF THE WORLD
### As of `8cf9cbf` (main, DEPLOYED + fingerprinted live), 2026-07-20. This file opens every future session. **Start with the CURRENT STATUS + OPEN ITEMS block below.**
### As of `4e2f488` (main, DEPLOYED + fingerprinted live), 2026-07-20. This file opens every future session. **Start with the CURRENT STATUS + OPEN ITEMS block below.**
---
@@ -131,6 +131,99 @@ exist locally; harmless, the data restores completely.
`specs/snapshot-retention.md` (Phase 2 report + schema) ·
`specs/audit-data/grade-collapse.md` · `specs/audit-data/gate-simulation.md`.
---
# 🟢 SESSION 67 — PRICE LAYER GATED + WIRED (2026-07-20)
## ⚠️ THE PREMISE WAS WRONG TWICE — read before trusting a "wire the fields" order
1. **There is no `/api/slate`.** The board reads `GET /api/snapshot/:sport`.
2. **The snapshot already carried the price fields.** `book_odds`, `fair_odds`,
`model_odds`, `p_win`, `ev_pct`, `value`, `takeable`, `fair_prob`,
`devig_method` were all live. Nothing needed adding.
3. **`fairLine` does not exist** (zero hits repo-wide). The Option-A
"derive from fairLine" instruction had no referent — `fair_odds` is already
a real de-vigged American price.
## Phase 0.5 gates — all PASS, verified numerically
- **Is it a price?** Yes. `fair_odds` = American odds from
`impliedProbToAmerican` inside `devigTwoWay`; `fair_prob` is the probability.
Both distinct from `line` (the stat threshold).
- **Numeric match?** **8/8 EXACT.** Recomputed `fair_odds`+`fair_prob`
independently from stored raw over/under prices; every value matched to the
integer and to 3dp.
- **Poison-independent on quarantine rows?** **YES, proven on the quarantined
cohort itself.** `devigTwoWay(over, under)` takes market prices only — no
model term is reachable — and the 8 rows recomputed above are all
`wrong_opponent_grade`. The poison is in the GRADE, not the price. So
**quarantine suppresses the MODEL leg only; the fair leg stands.**
## 🔴 THE REAL FINDING — the model price was PUBLIC
`GET /api/snapshot/:sport` is unauthenticated and was serving `model_odds`,
`p_win`, `ev_pct`, `value`, `takeable` to anonymous callers on **25/25 live
rows**. The Session-66 gate on `/api/analyze` was bypassed entirely.
**`/api/hero-prop` was a SECOND door** — it reads Redis directly via
`heroPropService`, so the first fix didn't cover it.
**`src/utils/snapshotGating.js` strips FIVE fields, not one** —
`model_odds` is the price, **`p_win` IS the price in another base**, and
**`ev_pct` is INVERTIBLE** (ev = f(p_win, book_odds); book_odds is public, so
ev hands the price over). `value`/`takeable` bound it. Market legs
(`book_odds`, `fair_odds`, `fair_prob`, `overround`, `devig_method`) stay on
**every** tier — **the fair leg is never the paywall**.
`src/utils/requestTier.js` resolves tier from a bearer token when present and
**FAILS CLOSED to `free`** on every error path, so a resolution failure can
only withhold, never leak. Responses now vary by entitlement → `/:sport` and
`/hero-prop` emit `private` Cache-Control for authenticated callers and both
Next proxies forward the bearer token.
## Read card — why it was empty, and the fix
A manual scan carries **no market** (`{player, stat, line, direction}`), so the
engine has no over/under to de-vig — `book_odds`/`fair_odds` were legitimately
absent. `lookupSnapshotPrices` in the Next `/api/scan` route recovers them from
the pre-graded snapshot (the same cache-only read it already did for locked
odds + team). Join is EXACT on **player + stat + line + side** (`fair_odds` is
side-specific) and returns nothing unless book AND fair are both present — a
user-chosen line the board never graded borrows no other line's price.
## 🔴 LIVE INDUCTION CAUGHT A SECOND CLASS OF BUG
With the leg stripped, `LiveHeroProp` forwarded everything to `PriceTriplet`
**except `model_price_locked`**`deriveValueState` fell to the
missing-model-price branch and the landing page told every anonymous visitor
**"MODEL READ WITHHELD"** — quarantine's copy, which says our own data is
untrustworthy. Nothing was poisoned; it was the paywall.
**A paywall must never wear poison's face.** Unit tests and markup review both
passed this; only rendering the deployed page caught it.
## Live fingerprint (anonymous, cache-busted)
- Snapshot: model fields `[]`, market fields intact, `model_price_locked: true`
- Read card `/api/scan`: `book -150 · fair -126 · model null · locked true`
- Landing hero renders: **BOOK 153 · ◆FAIR 129 (amber) · MODEL [LOCK BAR]**,
verdict **"MODEL PRICE ON ANALYST"** + UNLOCK CTA
- `/dashboard`, `/ledger`, `/parlay` — 200, no triplet, unchanged. No consumer
reads `ev_pct`/`model_odds`/`p_win`, so the strip broke nothing.
## 📊 FAIR-LEG ABSENCE: 0.0%
636 graded rows: 636 with book, 636 with fair, **0 one-sided**. The hero number
is not a sometimes-number on current data.
## ⚠️ PRODUCT CALL PARKED FOR KEV
The landing hero is anonymous, so it now shows the LOCK instead of the full
triplet it showed this morning (`VALUE +21.1% VS FAIR`). That follows the
stated free-tier rule exactly, but trades a strong shop-window moment for gate
consistency. **Reversal is one line** — add `model_price: true` to the free
tier in `src/config/tiers.js`, or special-case the hero route.
## Still open
- **Quarantine flag is NOT available at snapshot build.** No service writes
`quarantine_reason` (manual SQL only); it's a retrospective judgement, so a
fresh row can never carry it. The component handles the state if it ever
arrives; wiring an always-null field was refused.
- **Slate row** still held — `lib/marketBreadth.js` computes LINES only.
- **Ledger / public profile** still out — no fair-odds column.
- Screenshots unavailable again (browser capture tool errored all session);
substituted computed-style readback per state.
---
# 🟢 SESSION 66 — PRICE-LAYER TOKENS + THE PRICE TRIPLET (2026-07-20)