From 7cf3892e761fb64763da872b1a0c62e74202f659 Mon Sep 17 00:00:00 2001 From: Kev Date: Fri, 31 Jul 2026 07:31:45 -0400 Subject: [PATCH] docs: corrected gate recorded + cache-busting verification lesson Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc --- outputs/VYNDR-COMPLETION-MATRIX.md | 22 ++++++++++++++++++++ specs/STATE.md | 32 ++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/outputs/VYNDR-COMPLETION-MATRIX.md b/outputs/VYNDR-COMPLETION-MATRIX.md index 3029d71..f5d6de8 100644 --- a/outputs/VYNDR-COMPLETION-MATRIX.md +++ b/outputs/VYNDR-COMPLETION-MATRIX.md @@ -742,3 +742,25 @@ grade, so nobody can tell which prop is the A. **Live anonymous fingerprint:** 25 grades → **5 settled free-full, 20 locked**, `live_locked {"count":20,"tiers":{"B":13,"C":7}}`, **judgment-field leak: NONE**. 319 suites / 3,971 tests green, web build exit 0. + +--- + +# BUILD 1 CORRECTED — itemized grades are PAID, live AND settled (2026-07-31) + +The resolution-flip shipped earlier the same day was **replaced**: freeing grades at settlement made +the free tier a one-day-delayed feed of the whole product. No per-grade flip now — every itemized +grade is Analyst+. + +**Free:** data aggregator + the **aggregate record that already existed** (`/api/accuracy` n=937, +byGrade, per-sport; `/api/ledger/accuracy`, both honoring the n≥20 hollow law with `pct:null`) + a +**capped, day-rotated 3-call sample** (resolved only) + the locked shell of tonight's reads +(`live_locked {count, tiers}`, aggregate-only, never joinable to a row). + +**Live anonymous, cache-busted:** 25 grades, **judgment leak NONE**, every row `locked`, tease +`{count:20, tiers:{B:13,C:7}}`, `free_sample` 3 of 5 settled — **exploit dead**. `outcome` is kept on +gated rows because a result is a fact, not a judgment. + +**Verification note:** the first post-deploy read was a CDN-cached pre-deploy body and falsely showed +a leak; `max-age=30` on this endpoint means post-deploy checks must bust the cache. + +319 suites / 3,970 tests green, web build exit 0. diff --git a/specs/STATE.md b/specs/STATE.md index b3fabdd..3461004 100644 --- a/specs/STATE.md +++ b/specs/STATE.md @@ -1040,6 +1040,38 @@ > (above). **Chrome audit:** free board (settled full + locked shells + unlock prompt) and the > entitled board (live full) both need a visual pass — the entitled half needs Kev's Desk session. +> ## 🔐 BUILD 1 **CORRECTED** 2026-07-31 — ITEMIZED GRADES ARE PAID (LIVE **AND** SETTLED) +> **Serving/gating change only** — `src/services/` untouched (diff empty). Pricing = Build 2, +> migration = Build 3. +> **WHY THE PRIOR GATE WAS WRONG (and why it was replaced within the hour):** freeing grades at +> resolution made the free tier a **ONE-DAY-DELAYED FEED OF THE WHOLE PRODUCT** — settlement is +> nightly, so a bettor watching one cycle behind got the entire method free. **There is now NO +> per-grade resolution flip:** an itemized grade, tonight's or last week's, is Analyst+. +> **FREE GETS (none of it itemizing the nightly slate):** (1) the full data aggregator, unchanged · +> (2) **the AGGREGATE record, which ALREADY EXISTED and is public** — `/api/accuracy` (sample **937**, +> byGrade tiers, per-sport mlb+wnba, `min_sample 20`) + `/api/ledger/accuracy`; **the honest-record +> laws are already honored there — A/D/F return `pct:null` under n≥20 rather than a fake percentage** +> · (3) a **CAPPED, day-rotated SAMPLE** of resolved calls (cap **3**, stable within a day, rotates +> across days, **only RESOLVED rows eligible** so a live read can never be sampled) · (4) the locked +> shell of tonight's reads. +> **EVERY itemized grade** for an unentitled tier now loses `grade`·`confidence`·`confidence_basis`· +> `reasoning`·`kill_conditions_triggered`·`projection`·`edge_pct`·`matchup_grade`·`form`·`alt_lines`· +> `kelly`, and is stamped `locked`. **Free-side DATA survives** so the board still reads as real — +> player · market · line · `book_odds` · **`fair_odds`** (never the paywall) · season/last10 · +> archetype — **and `outcome`, because a RESULT is a fact, not a judgment.** +> **✅ FINGERPRINT — LIVE, ANONYMOUS, CACHE-BUSTED:** 25 grades (5 settled) · **JUDGMENT LEAK: NONE ✓** +> · **every row locked: True** · tease `{"count":20,"tiers":{"B":13,"C":7}}` · **free_sample 3 of cap 3, +> all resolved** (`grade C · reasoning ✓ · outcome {"result":"hit","actual":8}`) · **3 of 5 settled → +> the exploit is dead** · shell keeps real data (Natasha Cloud · assists 6.5 · book −145 · **fair +> −123** · season 4.6). +> **⚠️ VERIFICATION NOTE WORTH KEEPING:** my first post-deploy read reported a full judgment leak and +> `locked: False` — **that was a CDN-CACHED body from the previous commit**, not the live gate. Three +> cache-busted fetches (`Cache-Control: no-cache` + cachebuster) returned NONE/True/3 consistently. +> **`Cache-Control: public, max-age=30` on this endpoint means any post-deploy check MUST bust the +> cache or it can read the old contract and produce a false alarm** — the mirror image of the +> silent-false harness bug, and the same lesson: a verification path can lie in either direction. +> **FLOOR: 319 suites / 3,970 tests green, web build exit 0.** + - **Redirect EXISTS + WIRED:** `closingCapture.buildCaptureRows`→`closing_captures` (append-only, provenance: captured_at/book/line_type/both-prices/missed_reason) via `intradayRefreshService:221` + internal endpoint; `ledgerService.attachClosingProb`→`closing_prob` (de-vigs both raw sides,