6c97f59546
PART A -- WNBA TRUTH CORRECTION (no behaviour change).
WNBA does not "abstain" and is not "anti-predictive". The -0.12 that
produced those words was NBA-template machinery run on WNBA data -- WNBA
has never had its own archetypes, variables, conditions or calibration,
which is precisely the "sport stubbed in on another sport's template"
CLAUDE.md forbids. That is an UNBUILT MODEL'S EXPECTED FAILURE, not a
verdict on the sport; reading it as a verdict would quietly retire a sport
we never actually attempted. Its own build is QUEUED, after MLB.
The guard CODE is unchanged -- FORECAST_RANKED_SPORTS = {'mlb'} and the
inheritance test are correct live safety either way. Only the meaning is
corrected, and generalised into the doctrine-as-a-gate: a sport ranks on
p_win ONLY once its OWN model is built and shown to predict (calibration
AND resolution on its own holdout). Others are held out as NOT-BUILT,
never as failed. Re-labelled across gradeRanking, snapshot route, tests,
MASTER-PLAN and the challenger report.
PART B -- THE FLIP, gated on a full-slate re-run.
The re-run found something better than a bigger sample. An induced
snapshot graded 7 props: gradeAndCacheSlate runs with DEFAULT_LIMIT = 25
and ~72% of those refuse for insufficient_data, while 546 props are
gradeable. So 8 props IS the board, structurally -- not a small sample of
it. Logged as its own finding; the cap is a separate order.
For a statistically meaningful delta I used 11 real historical boards
(n=328, board sizes 14-57): 79.9% of rows move, mean 5.16 places per
board, TOP READ CHANGES ON 9 OF 11 BOARDS. The re-ordering holds at real
board size. Query committed.
FLIPPED:
- rankGrades drops its edge key (safe for every sport: removes a
non-predictive tiebreak without putting p_win in front).
- selectTopGrades leads on forecast_rank, edge key removed.
- flattenToEdgeBoard sorts on forecastRank, not edge -- this board had
edge as its PRIMARY key, so the whole mobile board was ordered by a
quantity measured not to predict.
- forecast_rank threaded onto strip props.
Sports whose model is not built supply no forecast_rank, so their boards
fall through to the unchanged grade chain -- the fallback is the guard.
ROLLBACK ARMED: boards sort by forecast_rank WHEN PRESENT, so
FORECAST_RANK=0 reverts every surface on the next response -- no deploy,
no client release.
Edge is still computed, stored, carried and displayed as a labelled
diagnostic. Retired from ranking, not deleted.
Eight superseded tests updated to strictly stronger INVERSE properties --
they now fail if edge is ever re-introduced as a ranking key, which the
originals could not detect.
Gates: 4,045 tests / 323 suites green; next build exit 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc
604 lines
34 KiB
Markdown
604 lines
34 KiB
Markdown
# VYNDR — MASTER PLAN
|
||
**Single source of truth. Sessions EXECUTE against this and UPDATE it in place.**
|
||
Created 2026-07-31 by consolidation. Last updated 2026-08-01.
|
||
|
||
---
|
||
|
||
## 🔷 PRODUCT IDENTITY — the thing being built
|
||
|
||
**VYNDR IS A PREDICTIVE MODEL.** It projects what a player will **DO** and picks
|
||
accurately — reading and pulling the market apart. Student of the game, and an
|
||
aggregator.
|
||
|
||
**Market edge is a BYPRODUCT of a good prediction, NEVER the success criterion.**
|
||
|
||
> **SUCCESS = the forecast is honest about its own confidence AND still ranks.**
|
||
> Calibration *and* resolution. **No edge/CLV term belongs in a pass/fail gate** —
|
||
> they are diagnostics we report, not thresholds a model must clear to ship.
|
||
|
||
**PER-SPORT DOCTRINE** (Rashad Phillips, *Basketball Position Metric*, 2022 —
|
||
classify players by **what they do**, not position labels): each sport is its OWN
|
||
model, with its own variables, archetypes, conditions, calibration and honest
|
||
ceiling. Shared across sports: **only the Bayesian inference math.**
|
||
|
||
**TRUTH LAW:** no fabricated data anywhere · honest-absent over invented · label
|
||
limitations in-band ("market consensus, **not sharp**") · **provisional results
|
||
stay provisional until re-run** · documented ≠ verified.
|
||
|
||
---
|
||
|
||
## ▶ NEXT EXECUTABLE ORDER
|
||
|
||
**DONE 2026-08-01** — the MLB re-run landed, and it dissolved rather than answered
|
||
its own question: **calibration is ruler-independent**, so MLB isotonic was never
|
||
gated on the ruler. See `specs/mlb-recalibration-vs-consensus-ruler.md`.
|
||
|
||
**The next order follows from what that measurement found, not from the plan:**
|
||
|
||
> **`p_win` predicts outcomes (r = +0.26). `p_win − fair_prob` does not
|
||
> (r = −0.01 to −0.02, either ruler).** Subtracting the market destroys the
|
||
> signal. So the build is: **make the served product rank on `p_win`, not on
|
||
> edge** — and retire market-relative edge from every ranking, gate and display
|
||
> where it still sits.
|
||
|
||
Two things gate on accrual instead of on code, and cannot be rushed:
|
||
- The **exchange-inclusive ruler** is untestable until v2-era captures accrue.
|
||
- The **edge verdict** may change under that ruler — or may not; today it is
|
||
unproven either way, and 'unproven' is the honest label.
|
||
|
||
**DONE 2026-08-01** — the challenger is built and measured
|
||
(`specs/rank-on-pwin-challenger.md`): `rankByForecast` (p_win-first, **no edge
|
||
term**), delta recorded (MLB **87.5% of props move, top read changes**), edge
|
||
retired from decisions and from display quality-signalling, `forecast_rank`
|
||
stamped additively, **MLB-only by an enforced `FORECAST_RANKED_SPORTS` guard**.
|
||
|
||
**THE FLIP IS THE NEXT ORDER** — one decision, three edits. **Re-run the delta on
|
||
a full MLB slate first**; it measured on 8 graded props.
|
||
|
||
**Open, cheap, unrelated to the model:** the 🔴 pinnacle question (logged in
|
||
`BLOCKERS.md`), and the display layer still shows nothing of the widened
|
||
multi-book data.
|
||
|
||
---
|
||
|
||
## 📍 STATE AS OF 2026-08-01 (Order Zero, measured on prod with the real key)
|
||
|
||
| finding | number | what it means |
|
||
|---|---|---|
|
||
| **MLB slate invisible to us** | **64.8%** | our own allow-list, not the feed — now widened for DISPLAY |
|
||
| **books/prop, MLB** | 3.61 feed → 0.57 after filter | the filter cost, quantified |
|
||
| **books/prop, WNBA** | **4.21** feed → 1.20 | **WNBA is BETTER covered than MLB** |
|
||
| **consensus ruler** | **MARKET, not SHARP — ⏳ PENDING-RECOVERY, not permanent** | `matchbook`/`polymarket` = 0%, but **`pinnacle` ran until 07-30** (103,940 captures) and stopped. **Do not enshrine as permanent** until PropLine answers — see `BLOCKERS.md` |
|
||
| **ruler delta** (consensus − incumbent) | MLB mean +1.50 pts, median 0, **17% of props move ≥5 pts** | rulers genuinely differ; "better" is unproven |
|
||
| **MLB isotonic `p_win`** | **DECIDED** — reliability **0.0846**, resolution **0.190**, holdout **n=125** | **PROVISIONAL label RETRACTED 2026-08-01.** Calibration is **ruler-independent** (`estimateProbability` never sees a price; the fit is p_win-vs-outcome). Replicated on a fresh later window, both metrics improved |
|
||
| **edge vs the ruler** | corr(edge, outcome) **−0.010** (v1) → **−0.022** (v2), n=200 · corr(**p_win**, outcome) **+0.26** | **Subtracting the market DESTROYS the signal.** The consensus ruler does not rescue edge: *differs ≠ better* |
|
||
| **exchange-inclusive ruler** | **UNTESTABLE on existing data** | exchange quotes were never stored (discarded until 2026-08-01). Becomes testable only as v2-era captures accrue |
|
||
| **WNBA** | **MODEL NOT BUILT YET** — held out, *not* failed | **CORRECTED 2026-08-01.** The −0.12 was **NBA-template machinery run on WNBA data**. WNBA has never had its own archetypes/variables/conditions — the "sport stubbed in on another sport's template" CLAUDE.md forbids. That is an **unbuilt model's expected failure, not a verdict on the sport.** Its own build is QUEUED, after MLB |
|
||
| 🔴 **pinnacle feed** | **0 captures since 2026-07-31** (103,940 in the prior 10 days) | a live regression; **we had a sharp anchor and lost it.** Not caused by our changes |
|
||
| **soccer** | **settles** — ~15 competitions, 30d | "grades into a void" is a **$19/mo Pro-tier** problem, not a data problem |
|
||
| **CLV + results feeds** | `/odds/closing` + `/movement` **redacted**; `/results` **403 `required_tier: hobby`**; `/exports/resolved-props` **403 `required_tier: pro`** | **verified on our keys** — plain tier exclusion, not a key or plan fault. **$9/mo** buys CLV + steam + results; **$19/mo** adds the 90-day settlement export |
|
||
| **books SERVED** | 5 → **13**; props rendered **546 → 2,780** (5.1×); mean **4.22** books/prop | **AGGREGATOR widening is LIVE.** Of 2,234 newly-visible props, **31.2% carry a real non-DFS price**; **68.8% are DFS-only** — shown, tagged, never a market |
|
||
| **model input** | **byte-identical** — 546 gradeable props, `v1_first_book` | `MODEL_BOOKS` gate in `dedupeProps` + `indexOddsProps`. Lifts only on the re-run |
|
||
| **accrual clock** | **sequential, post-completion** | see §11. Pre-completion data does not count and is never pooled |
|
||
|
||
**The honest framing:** widening books is an **AGGREGATOR** win. **It does not fix
|
||
the model.** Do not let the free-side win read as model progress.
|
||
|
||
> **HOW TO USE:** this supersedes ad-hoc re-derivation. Before any order, read the
|
||
> phase you're in. After any order, tick the item and add one line. **Do not
|
||
> re-audit anything marked KNOWN** — that redundancy is what this document exists
|
||
> to kill.
|
||
|
||
---
|
||
|
||
## 0. VERIFICATION LEDGER (what was re-checked in this pass)
|
||
|
||
**NOTHING was re-verified. No query was run.** Everything required is already
|
||
captured in 22 artifacts produced this session plus the canonical board. Per the
|
||
order's own clause — *"If everything needed is already in the artifacts, say so and
|
||
skip verification"* — this is that case.
|
||
|
||
**Taken as KNOWN (source in brackets):**
|
||
- Model architecture, all layers [`model-architecture-recovery-map.md`]
|
||
- Grade↔outcome correlations, collapse cost [`full-output-grade-mapping.md`]
|
||
- p_win calibration + holdout verdicts [`grade-diagnostic-t0.md`, `pwin-recalibration-holdout.md`]
|
||
- Market-relative edge inversion [`grade-fix-part1-investigation.md`]
|
||
- Design implemented-vs-designed, 61 items [`design-vs-build-gap-audit.md`]
|
||
- Surface states, orphans, waves [`pre-audit-status-pull.md`, `incomplete-surface-triage.md`]
|
||
- Resolution pipeline true state [`resolution-and-clv-investigation.md`, `wave3-*.md`]
|
||
- Tier/monetization + founder mechanism [`tier-structure-pull.md`, `tier-redesign-spec.md`, `build2-review-zero-report.md`]
|
||
- Sport-boundary cost [`model-architecture-recovery-map.md` §Phase 2]
|
||
|
||
**Genuinely OPEN → carried as explicit unknowns (not verified because they need a
|
||
build or a decision, not a query):** sport order (Kev's call, §A2); board-reasoning
|
||
gating (a/b/c, §C2); CLV flag decision (§D3); the ~70 undefined team colours (§B).
|
||
|
||
---
|
||
|
||
## A. PER-SPORT MODELS — Phillips doctrine: each sport is its OWN model
|
||
|
||
### A1. MLB layer stack (the reference module), bottom-up
|
||
|
||
| # | layer | state | note |
|
||
|---|---|---|---|
|
||
| 1 | Data/feeds | **BUILT** | statsapi free+unlimited; statcast; park; weather; probables. Settles end-to-end. |
|
||
| 2 | Similarity (comparable instances) | **BUILT · NOT WIRED · NOT DEPLOYED** | `python/utils/similarity.py`; grade path skips to season/recent averages |
|
||
| 3 | Archetypes (batter + pitcher) | **BUILT · display only** | classifies + renders; **does NOT feed the grade** |
|
||
| 4 | Variable weights | **PARTIAL/ASSUMED** | engine1's flat ±1.0/±0.5 deltas are hand-set, never fitted |
|
||
| 5 | Conditions (park/weather/platoon/arsenal) | **BUILT · CHALLENGER ONLY** | rides `env_*`/`challenger_*`; "measured, never served" |
|
||
| 6 | Bayesian inference | **BUILT · NOT WIRED · NOT DEPLOYED** | `python/utils/bayesian.py`, 320 ln, genuinely sport-agnostic math |
|
||
| 7 | Calibration | **MEASURED, NOT APPLIED** | isotonic qualifies on holdout (rel .1038→.0939, res .139→.123, n=119) |
|
||
| 8 | Grade ladder | **BROKEN** | letter is a factor index, r≈0.005, **inverted** (B 52.4% < C 56.9%) |
|
||
|
||
**The through-line:** layers 2, 3, 5, 6 are built and *not connected*; layer 8 is
|
||
connected and *meaningless*. MLB's fix is connection, not construction.
|
||
|
||
### A2. Sport order (OPEN — Kev decides)
|
||
Proposed by readiness × clock: **1) MLB** (reference, only qualifying model) →
|
||
**2) CFB** (has a <30-day clock; soft-market thesis) → **3) NFL** → **4) NBA** →
|
||
**5) CBB** → **6) WNBA — first real build** (never had its own model) → **7) soccer** (quota-blocked).
|
||
Each gets the same 8-layer template. **No sport is abandoned — abstention is a
|
||
state, not a verdict.**
|
||
|
||
---
|
||
|
||
## B. DESIGN IMPLEMENTATION — 61 items catalogued
|
||
BUILT-TO-SPEC 20 · DRIFTED 7 · PARTIAL 16 · ABSENT 18. Ordered wire-in:
|
||
**D1-A done** (6 combat glyphs, boundary-blue completed, reaction primitives, READ-FAB).
|
||
**D1-finish done** (rationale/reveal/chips modules — *built, NOT mounted*).
|
||
Remaining: **D1-close** (mount — blocked on a ROW-GRAMMAR slot amendment + §C2) ·
|
||
**D1-B** (45 unwired glyphs + the 41-vs-74 archetype scope call) · S2 primitive set
|
||
(movement strip, crown, disagreement axis, SPLIT) · S3 article media · The Report
|
||
email + archive · Offseason artboards · **team colours: only 10 of ~80 defined —
|
||
the rest render honest-neutral until a real source exists.**
|
||
|
||
---
|
||
|
||
## C. SURFACES
|
||
**C1 — done:** Wave 1 wiring · `/compare` · `/record` · Build-1 gate.
|
||
**C2 — OPEN DECISION (blocks D1-close):** board reasoning is served ungated while
|
||
`tiers.js` declares `reasoning_visible:false`. Options (a) gate it, (b) accept as
|
||
free funnel, (c) leave unrendered.
|
||
**C3 — remaining:** `/record` **has no nav link** (the surface that justifies the
|
||
price) · `/notifications` · Offseason hub · `/system` · S3 media · `/soccer`
|
||
(quota) · share cards (blocked by D).
|
||
|
||
---
|
||
|
||
## D. RESOLUTION PIPELINE → USER OUTPUT
|
||
**KNOWN and load-bearing: settlement WORKS** (scheduler → `settleAllOutcomes` +
|
||
`settleAllLedgers`, 937+ settled, growing daily). **What is unreachable is the
|
||
user-output TAIL:** `/api/grading/resolve` has no caller, and its fanout holds
|
||
webPush/Telegram/Discord but **no share-card step and no recap**.
|
||
**D1** wire a trigger (or move the fanout into the settle pass) · **D2** share-card
|
||
generation + `/notifications` consent + result posts + recap · **D3 CLV flag
|
||
decision** — `clvCaptureReliable()` is *one env var*, and the pre-registered rule
|
||
stands: flip only if close_moved is a clear majority AND coverage is representative.
|
||
**🔴 Never wire `/api/grading/resolve` as a second settlement path — it double-counts.**
|
||
|
||
---
|
||
|
||
## E. SPORT BOUNDARY
|
||
Adding a sport is a **~10-file core edit** with four silent-failure modes
|
||
(MARKET_MAP → zero props; three stat whitelists → silent 400s; missing projection →
|
||
universal refusal; no settled feed → grades forever). **Collapse to a registry** so
|
||
a sport is a module. **Blocks all of A2 after MLB.**
|
||
|
||
---
|
||
|
||
## F. CHROME AUDIT — 11 items, 4 need a Desk session. **Runs when surfaces are stable, not before.**
|
||
|
||
---
|
||
|
||
# THE PHASES — 7 phases, ~18 orders
|
||
|
||
| phase | orders | contents | blocks |
|
||
|---|---|---|---|
|
||
| **1. MLB model truth** | 4 | promote isotonic p_win (MLB only — every other sport is NOT-BUILT, held out) · rebuild the ladder on calibrated p_win · re-adjudicate (ROI-by-grade, skew, proj-v1.1, C1 floor) · connect layers 2/3/5/6 | everything model-shaped |
|
||
| **2. Resolution tail** | 3 | trigger · share cards + notifications + posts + recap · CLV flag decision | share cards, social proof |
|
||
| **3. Surfaces + design lane** *(parallel with 1-2)* | 4 | C2 decision → D1-close mount · `/record` nav + remaining surfaces · D1-B glyphs/archetypes · S2 primitives | Chrome audit |
|
||
| **4. Sport boundary** | 2 | registry collapse · MLB re-expressed as the first module | all further sports |
|
||
| **5. Sport rollout** | 1 per sport | CFB → NFL → NBA → CBB → WNBA retry → soccer, each on the 8-layer template | — |
|
||
| **6. Monetization finish** | 2 | Stripe Phase-B live proof on first real signup · founder launch to the 3 existing users | — |
|
||
| **7. Chrome audit + hardening** | 2 | the 11-item visual sweep · credential rotation + migration-drift reconciliation | ship |
|
||
|
||
**Phases 1–4 and 6–7 = ~17 orders. Phase 5 = 1 order per sport (6 listed).**
|
||
**Total ≈ 23 orders to the end state**, of which **~11 are unblocked today**.
|
||
|
||
---
|
||
|
||
# DEFINITION OF DONE
|
||
|
||
**VYNDR is complete when:**
|
||
1. **MLB layers 1–8 are BUILT AND CONNECTED** — similarity, archetypes, fitted
|
||
weights, conditions and Bayesian all feed the grade; calibration applied; the
|
||
ladder monotone (A>B>C, no inversion) and proven on held-out data.
|
||
2. **Every listed sport is finished on the same 8-layer template**, or explicitly
|
||
held out with its reason recorded — **"not built yet"** where no sport-specific
|
||
model exists, and only "measured and failed" where one was genuinely built and
|
||
tested. Never silently absent, and never a verdict on a sport we never attempted.
|
||
3. **Design fully implemented** — all 61 catalogued items BUILT-TO-SPEC.
|
||
4. **Every surface built, reachable and honest** — no orphans, no live-but-not-honest
|
||
surface, no dead component.
|
||
5. **Resolution pipeline live end-to-end** — settle → share card / notification /
|
||
post / recap, firing on a real settlement.
|
||
6. **Sport boundary is a registry** — a new sport is a module, not a core edit.
|
||
7. **Chrome audit passed**, logged-out and entitled.
|
||
8. **The record is publishable on its own terms** — CLV either trustworthy-and-
|
||
representative or honestly absent; no claim outruns its evidence.
|
||
|
||
**The remaining work is finite and countable: ~23 orders across 7 phases.**
|
||
|
||
---
|
||
|
||
## STANDING LAWS (carried into every order)
|
||
Truth Law — absent beats wrong, no fabrication up or down · per-sport models, never
|
||
a global engine · lookahead guard (lock-time fields only) · overfitting guard (fit
|
||
one split, prove another) · never mint A's without new information · aggregate proof
|
||
is free, itemized judgment is paid · one canonical founder flag · atomicity by unique
|
||
index, never a count · cache-bust every post-deploy check · verify-after-write.
|
||
|
||
|
||
---
|
||
|
||
# 9. WHAT'S ACTUALLY MISSING FOR THIS TO WORK AS A PRODUCT
|
||
|
||
*The phases above say what is UNBUILT. This says what is missing for VYNDR to
|
||
genuinely do what it claims. Some of it is not a build, and one of it is not
|
||
fixable by us at all. Written plainly because a plan that only counts code is the
|
||
comfortable version.*
|
||
|
||
## 9.1 🔴 THE CENTRAL ONE: there is no demonstrated edge yet
|
||
|
||
Every edge measurement this session came back **null, negative, or unproven**:
|
||
|
||
| measurement | result |
|
||
|---|---|
|
||
| served grade → outcome | **r ≈ 0.005**, and **inverted** (B 52.4% < C 56.9%) |
|
||
| p_win − fair_prob (3 formulations) | **negative in all three, both sports, both splits** |
|
||
| p_win alone, MLB, holdout | +0.165, **p ≈ 0.07 — not significant** |
|
||
| p_win alone, WNBA | **negative — but this measured an NBA-template model on WNBA data, so it is not a WNBA result at all** |
|
||
| CLV / beat-close | **null by guard** — instrument not trustworthy |
|
||
| ROI by grade | likely an artifact of a meaningless letter |
|
||
|
||
**The product's core claim — "our read is better than the market" — is not
|
||
currently supported by our own data.** Everything else in this plan is
|
||
scaffolding around that. Building all 23 orders and *not* closing this leaves a
|
||
beautifully-built product that doesn't do the one thing it sells.
|
||
|
||
**What closes it:** not code. **Sample and honest iteration.** The instrument
|
||
fields are ~10 days old (442 rows). At ~90 decided MLB rows/week, that is ~6–10
|
||
weeks *of accrual*.
|
||
|
||
> **CORRECTED 2026-07-31 — see §11.** The "6–10 weeks out" above quietly assumed
|
||
> the clock is **already running**. It is not. Those 442 rows measure a model
|
||
> with a bent single-book ruler and six disconnected layers — **a model that
|
||
> will not exist once Phase 1 lands.** They do not count toward the verdict and
|
||
> **must not be pooled** with post-completion rows.
|
||
>
|
||
> The clock starts at a **verified** "running as intended" gate, per sport.
|
||
> **6–10 weeks is the accrual duration, not the distance to the answer.** The
|
||
> distance to the answer is *build time + verification + 6–10 weeks.*
|
||
>
|
||
> Independently forced by arithmetic, not just discipline: the §10.1 ruler fix
|
||
> changes the denominator, so pre-fix and post-fix edge/CLV numbers are not the
|
||
> same measurement.
|
||
|
||
## 9.2 The projection — the actual engine — is thin and unvalidated
|
||
The grade's only real inputs today are **l5/l20 averages, an opponent rank, rest
|
||
and usage**. Similarity, archetypes, park/weather/platoon and the Bayesian layer
|
||
are all built and **not connected**. So VYNDR is currently a recent-form average
|
||
wearing an intelligence system's clothes. Phase 1 connects them — **but connecting
|
||
them is a hypothesis, not a guarantee.** They must each prove out on held-out data
|
||
or be left disconnected honestly.
|
||
|
||
## 9.3 A one-sport product marketed as multi-sport
|
||
MLB is the only model that has been BUILT and passed. WNBA's model does not exist
|
||
yet (what was measured was NBA-template machinery on WNBA data). NBA and soccer
|
||
**don't even settle** — they grade into a void. Until Phase 5, the honest framing
|
||
is *"an MLB product with other sports in development."* The site should not imply
|
||
otherwise.
|
||
|
||
## 9.4 No customers, therefore no feedback loop
|
||
**3 users, 0 paid.** The founder mechanism is built and race-proven, `/record`
|
||
exists, the gate works — and **none of it has met a real user.** Nothing here is
|
||
validated by usage: not the price, not the tier line, not whether the locked-shell
|
||
tease converts, not whether anyone wants this. **The first 10 real users will
|
||
teach more than the next 10 build orders.**
|
||
|
||
## 9.5 No distribution — the biggest non-code gap
|
||
There is no acquisition path at all. The newsletter send is unscheduled, share
|
||
cards are unbuilt (blocked on the resolution tail), social proof has no fuel
|
||
(needs a real record), partner/affiliate links are all `enabled:false`. **A product
|
||
nobody sees cannot be validated regardless of how good the model gets.** This
|
||
appears in no phase above and belongs on the board as its own track.
|
||
|
||
## 9.6 The read isn't actionable at the last mile
|
||
Push-to-book is a **teaser** — no affiliate is live, so a user who trusts a read
|
||
still leaves to place it manually. Bankroll guidance (Kelly) is Desk-gated. The
|
||
gap between *"here's a good read"* and *"I placed it"* is unclosed.
|
||
|
||
## 9.7 Operational fragility
|
||
Single-box, single Redis (persistence is a Coolify setting, not app-controlled),
|
||
one cron. Settlement silently covers 2 sports. **Three credentials remain flagged
|
||
for rotation, including a Stripe live key that transited a chat transcript.** No
|
||
staging environment — every verification this session ran against prod.
|
||
|
||
---
|
||
|
||
## THE HONEST SUMMARY
|
||
|
||
**Built well:** the truth infrastructure. Honest empty states, refusal paths,
|
||
n-gates, the append-only ledger, the settled/live gate, the atomic founder cap.
|
||
**This codebase does not lie about what it knows** — that is rare and it is real.
|
||
|
||
**Not yet true:** that the model beats the market. Not disproven either — *unmeasured
|
||
at adequate n*, on one sport, with a projection whose best layers aren't connected.
|
||
|
||
**So the finish line is not 23 orders.** It is 23 orders **plus a verdict from
|
||
accrued data that we cannot rush** — and the discipline to report that verdict
|
||
honestly if it says the edge isn't there. The plan above builds the machine. Only
|
||
time and honest measurement decide whether the machine is right.
|
||
|
||
|
||
---
|
||
|
||
# 10. TO BE A REAL AGGREGATOR *AND* A MODEL PEOPLE PAY FOR
|
||
|
||
*Kev's question: what makes this the top product, not just a finished one. The
|
||
answer that matters most: **the aggregator gap and the model gap are the SAME gap
|
||
in two places.** Fix the data breadth and both halves improve at once.*
|
||
|
||
## 10.1 The one finding that reframes everything
|
||
|
||
> **CORRECTED 2026-07-31 by Order Zero — `specs/order-zero-book-breadth-test.md`.**
|
||
> The original text below blamed a missing `regions`/`bookmakers` param. **That
|
||
> was wrong.** PropLine's OpenAPI contract states verbatim: `bookmakers` …
|
||
> **"Omitted = all books."** Omitting it is correct and always was.
|
||
>
|
||
> **The real cause is ours.** PropLine sends **18 books**; `oddsNormalizer`
|
||
> `ALLOWED_BOOKS` (11 entries) intersects them at **exactly 5** — which is
|
||
> precisely the "5 MLB books" the audit measured. We discard 13 of 18 ourselves,
|
||
> and 6 of our 11 allow-list entries don't exist at PropLine at all.
|
||
>
|
||
> Measured on real public data (MLB `pitcher_strikeouts`, 5 complete events):
|
||
> the feed carries **4.41 books/prop**; after our filter, **1.50** — and **12 of
|
||
> 34 props become invisible entirely** (zero allowed books).
|
||
>
|
||
> Also corrected: "73% single-book" is the **long tail** of deep/reliever props
|
||
> sole-posted by DraftKings or Bovada. **On the core props we actually grade,
|
||
> the market is 10–12 books wide.**
|
||
>
|
||
> And a real negative: **`pinnacle` appears on 0 of 40 MLB props.** The one
|
||
> sharp book in our allow-list contributes nothing here. The independent
|
||
> low-vig references that *are* present on 100% of core props are **exchanges**
|
||
> — `novig`, `smarkets`, `kalshi` (+ `matchbook`, `polymarket`). DFS pick'em
|
||
> (`prizepicks`/`underdog`/`sleeper`/`dabble`) also covers 100% but is **not a
|
||
> market price** and must never enter a consensus.
|
||
>
|
||
> **So this is not a test to run. It is a build we can do: split one allow-list
|
||
> into takeable / reference / excluded, and make `fair_prob_lock` a median
|
||
> consensus across reference books.** No param, no cost, no tier, no new source.
|
||
|
||
**Our "market" is often ONE book.** MLB props are **73% single-book** (2.18 audit).
|
||
And `proplineAdapter` sends only `{ apiKey, markets }` — **no `regions`, no
|
||
`bookmakers` param** (`:152`). We take PropLine's *default* response.
|
||
|
||
That single fact causes four separate problems we have been treating as unrelated:
|
||
1. **No line shopping** — the #1 free-tier hook in this category needs many books.
|
||
2. **`fair_prob_lock` is a de-vigged SINGLE SOFT BOOK**, not a consensus. That is
|
||
the ruler the model is judged against — a bent one. (Flagged as T1; never run.)
|
||
3. **CLV is weak** — you cannot measure "beat the close" against one book's close.
|
||
4. **No steam/disagreement detection** — needs ≥2 books to even exist.
|
||
|
||
*(Items 1–4 stand. Only the **cause** was wrong — and the fix is cheaper than
|
||
the original diagnosis implied.)*
|
||
|
||
### 10.1b We use 1 of PropLine's 29 endpoints
|
||
|
||
Reading the full spec surfaced ten unused endpoints that map directly onto §10.2
|
||
and §10.3 gaps — `/odds/closing` ("the canonical CLV helper"), `/odds/history`,
|
||
`/exports/odds-history`, `/movement` (steam across all 16 books), `/best-line`,
|
||
`/ev`, `/results` + `/exports/resolved-props` (**resolution across 33 sports**),
|
||
`/context` (**free**: probable pitchers, confirmed lineups, home-plate umpire,
|
||
first-pitch weather), `/markets/hit-rates`, `/players/{n}/trends`.
|
||
|
||
Two overturn standing beliefs: **"NBA/WNBA/soccer have no free settled feed"** may
|
||
be a **$19/mo** problem rather than a data problem; and we hand-built probable
|
||
pitchers / depth charts / lineup confirmation that `/context` serves free.
|
||
|
||
> **VERIFIED 2026-08-01 with the real key** — `specs/order-zero-consensus-ruler.md`.
|
||
> `/context` **WORKS, FREE** (umpire, roof, pitcher handedness, lineup
|
||
> confirmation — richer than what we hand-built). `/odds/closing` and
|
||
> `/movement` are **REDACTED** on our tier (full structure, **zero prices**) —
|
||
> my first pass wrongly called them "works" on a non-empty body. `/results` and
|
||
> `/exports/resolved-props` are **403**.
|
||
>
|
||
> **But the settlements exist to be bought:** `/markets/resolution-summary`
|
||
> shows **soccer graded across ~15 competitions in 30 days** (MLS 41k, Liga MX
|
||
> 15k, Brasileirão 12k, UCL/Europa/Conference…). **"Soccer grades into a void"
|
||
> is a $19/mo Pro-tier problem, not a data problem.** NBA is absent because it
|
||
> is July — seasonal, not a coverage gap, and not inferable either way.
|
||
>
|
||
> **WNBA is NOT thin at the feed** — 4.21 books/prop vs MLB's 3.61. It was
|
||
> allow-list-starved exactly as MLB was. This removes one candidate explanation
|
||
> for its −0.12 result. **And that result is not a WNBA verdict anyway** — it
|
||
> measured NBA-template machinery on WNBA data. WNBA is **NOT BUILT YET**, held
|
||
> out until it gets its own model.
|
||
>
|
||
> **No sharp anchor exists for props:** `pinnacle`, `matchbook` and `polymarket`
|
||
> all measured **0%** on both sports. The consensus ruler is therefore a MARKET
|
||
> consensus, not a SHARP one — stated as a permanent limitation, not a milestone.
|
||
|
||
## 10.2 What a real DATA AGGREGATOR has that we don't
|
||
|
||
| capability | ours | gap |
|
||
|---|---|---|
|
||
| **Book breadth** | 5 admitted of **18 sent**; 4.41→1.50 books/prop after our own filter | **not a feed gap — one allow-list (10.1).** Core props are already 10–12 books wide |
|
||
| **True consensus / no-vig line** | single-book de-vig | **unblocked now**: median across reference books (exchanges + pinnacle), n≥2 or labelled fallback |
|
||
| **Historical odds archive** | **STARTED** — `closing_captures` 844k rows, `lock_lines` (033) new, in-grade history capped at **24 points** | no full open→close series per prop. This is what makes CLV and backtesting real |
|
||
| **Market breadth** | 11 live markets | the category ships 50+ (alt lines, combos, innings, quarters) |
|
||
| **Alt-line ladders from books** | we *compute* a ladder; we don't *ingest* the books' | users shop rungs |
|
||
| **Injury / lineup wire** | partial (`depthChart`, confirmed-vs-projected) | no real-time news wire |
|
||
| **Player news** | `NewsWire` on Explore | not beat-level, not per-prop |
|
||
|
||
**None of this is model work. It's ingestion.** And it is the half competitors
|
||
compete on hardest, because it is visible to a free user in five seconds.
|
||
|
||
## 10.3 What a prediction model people PAY for has that we don't
|
||
|
||
1. **A distribution, not a point.** We project a point (l5/l20 average) and take
|
||
an empirical `P(over)`. Paid-tier models simulate a **full distribution per
|
||
stat** (negative-binomial / Poisson / MC). **We already have this** —
|
||
`projection/distribution.js` computes real survival probabilities and a rung
|
||
ladder — but it is **proj-v1.1, ledger-only, and it lost to the champion.** The
|
||
asset exists; it is unconnected and unproven.
|
||
2. **Opportunity modelled FIRST.** In props, playing time is the dominant driver —
|
||
plate appearances, snaps, minutes, batting-order slot. We carry `ab_per_game`
|
||
and minutes as *features*, not as a **projected opportunity** with its own
|
||
uncertainty. This is the single biggest modelling upgrade available.
|
||
3. **Per-stat models.** Hits, strikeouts and total bases have different shapes.
|
||
One additive factor index across all of them is why the ladder is meaningless.
|
||
4. **Matchup granularity that actually reaches the grade.** Arsenal, handedness,
|
||
park, weather, platoon — **all built, all challenger-only, none feed the grade.**
|
||
5. **Calibrated probabilities with honest intervals.** Measured (isotonic
|
||
qualifies on MLB) — **not applied.**
|
||
6. **A backtest harness on real historical odds.** Blocked by 10.2's archive gap:
|
||
you cannot backtest a price you never stored.
|
||
7. **CLV as the north-star metric**, published honestly. Instrument built,
|
||
guard-blocked, and weak until book breadth lands.
|
||
|
||
## 10.4 The uncomfortable pattern
|
||
|
||
**Almost every model capability above is ALREADY BUILT and DISCONNECTED**:
|
||
similarity, Bayesian, archetypes, park/weather/platoon, the distribution ladder,
|
||
calibration. VYNDR does not have a *building* problem. It has a **connection and
|
||
proof** problem — plus one genuine ingestion gap (book breadth) that starves both
|
||
halves at once.
|
||
|
||
That is good news: the expensive part is largely done. But it also means **no new
|
||
feature fixes this.** Connecting the layers and proving them on held-out data is
|
||
the work.
|
||
|
||
## 10.5 If I had to order it for "top product"
|
||
|
||
1. ~~Book breadth **test**~~ → **Book breadth FIX + consensus fair line** (10.1).
|
||
The test is done (Order Zero). It is now a build: split `ALLOWED_BOOKS` into
|
||
takeable / reference / excluded, make `fair_prob_lock` a median consensus.
|
||
Upgrades aggregator, model denominator and CLV together. **Gated on** exchange
|
||
prop-price validation + the WNBA measurement (needs the PropLine key).
|
||
2. **Opportunity projection** (10.3.2) — the biggest genuine modelling gain.
|
||
3. **Per-stat distributions** — connect `distribution.js`, prove per stat.
|
||
4. **Connect the built layers** (Phase 1) — each proven on held-out or left off.
|
||
5. **Full odds archive** — store every book's open→close; unlocks backtesting.
|
||
6. **Market breadth** — 11 → 50+ markets is mostly ingestion + the 4-layer wiring.
|
||
7. **Then** the sports rollout, on a template that is actually worth replicating.
|
||
|
||
**The ordering principle:** do not replicate a thin model across six sports. Get
|
||
MLB genuinely good first — a copied-six-times thin model is six times the
|
||
maintenance for the same absent edge.
|
||
|
||
## 10.6 The honest caveat on "top product"
|
||
|
||
The category's leaders are judged on one number: **do their picks beat the closing
|
||
line, at scale, published.** We cannot claim that yet — not because the product is
|
||
unfinished, but because **we have not measured it at adequate n on a market we can
|
||
trust.** Book breadth + the odds archive + accrued settlements are what make that
|
||
claim *possible*. Everything in §10 is in service of being able to make it — or of
|
||
being able to say honestly that we can't.
|
||
|
||
|
||
---
|
||
|
||
# 11. THE ACCRUAL CLOCK — SEQUENTIAL, POST-COMPLETION
|
||
|
||
*Kev's correction, 2026-07-31. Supersedes any "accrual runs in parallel with
|
||
building" framing anywhere in this document.*
|
||
|
||
## 11.1 The causality
|
||
|
||
You cannot meaningfully accrue until the product is **right and running as
|
||
intended** — model layers connected, ruler fixed (real consensus, not one soft
|
||
book), sports in, operating in the vision.
|
||
|
||
**Only then does the accrual clock start, and only then does time produce a
|
||
verdict. Building faster shortens the time TO clock-start. It never runs the
|
||
clock.**
|
||
|
||
The flawed assumption being removed: that today's half-connected model accrues
|
||
useful evidence while we build. It does not. That data measures a model that
|
||
**will not exist** after the layers are connected — a different model wearing the
|
||
same name.
|
||
|
||
## 11.2 Measurement rule (non-negotiable)
|
||
|
||
- Settled data accrued **before** completion does **NOT** count toward the edge
|
||
verdict.
|
||
- **NO POOLING across the completion boundary.** Pre-fix and post-fix are
|
||
different models — the same class of error as the model-version boundary, at
|
||
whole-model scale.
|
||
- The clock starts at **verified** "running as intended", not at "today".
|
||
|
||
## 11.3 Two clocks — stated separately so neither corner-cuts
|
||
|
||
**1. MLB-MODEL VERDICT clock.** Starts when MLB is genuinely complete: built
|
||
layers connected + honest consensus ruler + running as designed. Its accrued n
|
||
judges **MLB**.
|
||
|
||
**2. FULL-PRODUCT TRACK RECORD clock.** Starts when the vision is running:
|
||
sports in, aggregator built, operating as intended. Its accrued n judges **the
|
||
product claim**.
|
||
|
||
**Each subsequent sport gets its OWN clock**, starting when *that* sport's model
|
||
is complete — never when it is stubbed in. (Per-sport doctrine: a sport that
|
||
merely renders is not a sport that measures.)
|
||
|
||
## 11.4 "Complete" — defined honestly
|
||
|
||
A model/sport is **complete-enough-to-accrue** when its **built layers are
|
||
connected** and it runs against an **honest ruler** (real consensus), **operating
|
||
as designed** — *not* when every conceivable feature exists.
|
||
|
||
This definition is doing real work in both directions: it blocks the corner-cut
|
||
("close enough, start counting") **and** it blocks never-ship ("one more
|
||
feature"). The full-product claim additionally requires the vision's sports +
|
||
aggregator running.
|
||
|
||
## 11.5 The verification gate
|
||
|
||
**Between build and clock.** Before any accrual counts, verify — not assume —
|
||
that the product is running as intended:
|
||
|
||
- connected layers actually **fire** (present in the served payload, not merely
|
||
present in the repo)
|
||
- the ruler is a **real consensus** (`fair_prob_source: 'consensus_n'`, n≥2)
|
||
- the sport **settles correctly** (spot-checked against real box scores)
|
||
- surfaces are **honest** (no fabricated values; absent renders absent)
|
||
|
||
**This gate is verified, not assumed.** No accrual line item runs during the
|
||
build phases.
|
||
|
||
## 11.6 The 10-user track, reframed
|
||
|
||
Real users are onboarded to a **complete** product, so their usage teaches about
|
||
the real thing rather than a half-built one.
|
||
|
||
**We do NOT acquire users early to "start accrual."** That is the corner being
|
||
explicitly refused.
|
||
|
||
## 11.7 The finish line
|
||
|
||
```
|
||
build orders complete
|
||
↓
|
||
running-as-intended VERIFIED (§11.5 gate)
|
||
↓
|
||
clock starts (per sport, two clocks, §11.3)
|
||
↓
|
||
verdict reported honestly — including if it says the edge is not there
|
||
```
|
||
|
||
That last clause is the whole point. A clock you are willing to stop early is not
|
||
a measurement, and a verdict you are only willing to publish if it is favourable
|
||
is not a verdict.
|