# Order Zero — CONSENSUS RULER + ENDPOINT VERIFICATION **Date:** 2026-08-01 · Phase 1 measured against the **live prod feed with the real key**; Phase 2 built **challenger-first — the live ruler is byte-identical.** **Gates:** tests 4,022 passed / 322 suites · `next build` **exit 0** · keyed verification run on prod · challenger delta recorded. --- ## PHASE 1 — KEYED VERIFICATION Run via `GET /api/internal/propline-verify` (internal-key gated, read-only, no cache/ledger/grade writes). Breadth reuses `proplineAdapter.fetchRaw` — the exact live request — so this measures what the pipeline actually receives. ### 1.1 WNBA is NOT thin at the feed. It was starved by our own allow-list. MLB 3,138 props / 20 events · WNBA 160 props / 4 events. **Never pooled.** | | MLB | WNBA | |---|---:|---:| | books/prop **in the feed** | **3.61** | **4.21** | | books/prop **after `ALLOWED_BOOKS`** | **0.57** | **1.20** | | props **INVISIBLE** (zero admitted books) | **2,033 (64.8%)** | 46 (28.8%) | **WNBA has BETTER feed breadth than MLB (4.21 vs 3.61).** The answer to the order's question is unambiguous: **WNBA was allow-list-starved, exactly as MLB was — not thin at source.** That does **not** by itself explain WNBA's anti-predictive result, and I am not going to claim it does. It removes one candidate explanation ("no market to measure against") and leaves the ruler bend as a live hypothesis to re-test after the fix. **Nothing here promotes WNBA out of abstention.** The headline number for MLB is not the mean — it is that **we cannot see 64.8% of the MLB slate at all.** ### 1.2 Exchange reality — PARTIALLY confirmed, and two candidates are dead Reference-book presence, as a share of props: | book | MLB | WNBA | | |---|---:|---:|---| | `smarkets` | 27.1% | **0%** | exchange | | `novig` | 22.2% | **46.3%** | exchange | | `kalshi` | 14.9% | 8.8% | prediction market | | `bovada` | 9.2% | 16.3% | offshore, PropLine's own no-vig fallback | | **`pinnacle`** | **0%** | **0%** | the classic sharp anchor | | **`matchbook`** | **0%** | **0%** | exchange | | **`polymarket`** | **0%** | **0%** | prediction market | **Confirmed:** exchanges do carry real props — `smarkets` 873 quotes, `novig` 730, `kalshi` 1,369 on MLB. The hypothesis was not empty. **Three candidates are dead for player props:** `pinnacle`, `matchbook`, `polymarket` returned **zero** on both sports. Pinnacle carries 144k markets globally — just not these. **We have no sharp anchor for props.** Twice measured, both sports. ### 1.3 The coverage curve — the number that decides the ruler Strict eligibility: **≥2 reference books posting BOTH sides at the SAME line.** One-sided quotes cannot be de-vigged; two books at different lines are not the same market. | reference policy | MLB n≥2 | WNBA n≥2 | |---|---:|---:| | `exchange_only` | 14.3% | 5.6% | | `exchange_plus_sharp` (+pinnacle, bovada) | 14.3% | 5.6% | | **`exchange_plus_us`** (+DK/FD/MGM/BR) | **28.1%** | **46.3%** | | `takeable_only` | 12.6% | 30.6% | Adding pinnacle and matchbook/polymarket changes **nothing** — they are 0%. **Of props we can actually GRADE** (those with an admitted book), consensus is available on **80.5% of MLB** (923/1,146) and **54.9% of WNBA** (79/144). That is the operative number: the 15% figure is diluted by props we cannot see at all. ### 1.4 DFS is the trap, quantified `prizepicks` covers **82.3%** of MLB props and `sleeper` **68.1%** of WNBA — the highest coverage in the entire feed. Admitting DFS "for breadth" would have looked like the single biggest improvement available and would have bent the ruler in a new direction. Fixed-payout, deliberately shaded, flat juice. **Permanently excluded from all pricing.** ### 1.5 Endpoint verification — works / partial / no, FOR OUR TIER | endpoint | verdict | evidence | |---|---|---| | `/v1/sports` | ✅ **WORKS** | 54 sports, all active | | `/v1/markets/resolution-summary` | ✅ **WORKS** | 2,085,840 props graded in 30d | | `/events/{id}/context` (MLB) | ✅ **WORKS, FREE** | venue, `roof_type`, `is_indoor`, both probable pitchers **+ handedness**, `lineup_confirmed`, **home-plate umpire**, weather | | `/events/{id}/context` (WNBA) | ⚠️ **PARTIAL** | 404 — MLB-only today, as documented | | `/events/{id}/odds/closing` | ⚠️ **PARTIAL — REDACTED** | 200 with **2,102 outcomes and 0 prices** + `upgrade_url` | | `/events/{id}/movement` | ⚠️ **PARTIAL — REDACTED** | 2,102 outcomes, **0 prices**, `steam: []` | | `/events/{id}/results` | ❌ **NO** | 403 | | `/exports/resolved-props` | ❌ **NO** | 403 on **all four** sport keys | **A correction to my own first pass.** I initially classified `/odds/closing` and `/movement` as **WORKS** because the bodies were non-empty. They are **structure-only redactions** — full shape, zero prices, plus an `upgrade_url`. `detectRedaction` now counts actual prices and downgrades works→partial, and a test locks it. **A non-empty body is not proof of access.** ### 1.6 The $19/mo question — answered, with one honest gap `/exports/resolved-props` is 403 for us, so we cannot **pull** settlements. But `/markets/resolution-summary` proves they **exist to be bought** (30 days): | sport | props graded | events | |---|---:|---:| | `baseball_mlb` | 1,798,850 | 363 | | `basketball_wnba` | 152,960 | 72 | | **soccer_mls** | **41,444** | 35 | | **soccer_liga_mx** | 15,037 | 21 | | soccer_brasileirao / world_cup / UCL / Europa / Conference / … | ~60,000 combined | ~340 | | `mma_ufc` | 2,738 | 39 | | tennis, golf, rugby | ~19,000 | ~1,150 | **Soccer settles.** ~15 competitions graded in the last 30 days. Our standing "soccer grades into a void" is a **Pro-tier ($19/mo) problem, not a data availability problem.** **NBA is absent — and that is seasonal, not a coverage gap.** It is July; `/sports` lists `basketball_nba` as active. **I cannot confirm NBA settlement until the season starts, and I am not going to infer it.** --- ## PHASE 2 — THE CONSENSUS RULER (challenger-first, NOT live) ### 2.1 The three-way split — `src/config/bookRoles.js` One allow-list was answering two different questions: *"can we show this?"* and *"can we price against this?"* Conflating them is what bent the ruler. - **TAKEABLE** — a user can actually bet here. Best price, line shopping. - **REFERENCE** — may price the ruler; never surfaced as a place to bet. Selected policy: `exchange_plus_us`. - **EXCLUDED** — DFS pick'em + offshore/intl. **Permanently** barred from pricing. **Deliberate deviation, stated plainly.** The order said *remove the 6 phantom entries.* **I kept them.** They returned zero PropLine quotes, but PropLine is not our only provider — the odds-api backup path may carry Caesars/bet365/etc. A book that never appears is never matched, so keeping it costs nothing; deleting it risks silently dropping real books on the backup with no upside. Recorded in `PHANTOM_ON_PROPLINE` rather than enacted as a deletion. **Reverse this if you'd rather.** **The honest limitation, recorded in the config itself:** including soft US books makes this a **MARKET** consensus, not a **SHARP** one. It is a large improvement on one arbitrary book. It is **not** the sharp closing line the category's leaders are measured against — and §1.2 says we cannot build that one, because no sharp book prices these props. ### 2.2 The rule — `src/services/consensusRuler.js` 1. Group by the **SAME LINE**. Different lines are never averaged. 2. **TWO-SIDED REFERENCE quotes only.** A one-sided price cannot be de-vigged. 3. **MEDIAN** of per-book de-vigged `fair_prob` — median, so one stale exchange cannot drag the ruler. 4. **n≥2**, or fall back to single-book **and label it** (`source: 'single_book'`, stamped v1). **Never silently mixed** — a column holding both is two rulers wearing one name. 5. No reference quote at all → `null`, `source: 'none'`. Never fabricated. ### 2.3 The challenger delta — and a correction I caught mid-run **My first delta run was wrong and I stopped it.** I modelled the incumbent as first-row-wins over the **raw** feed and it reported that an EXCLUDED book was "the market" on **69% of MLB prop-lines, prizepicks alone at 47%.** **That is false.** `normalizeProps` applies `ALLOWED_BOOKS` **before** `dedupeProps` runs, so DFS books never reach the incumbent. The allow-list, for all the coverage it costs, **does** keep DFS out of the ruler. Overstating the incumbent's badness would have been as dishonest as understating it — and more persuasive. Corrected, tested, and re-run. **Corrected results** (`allowedBooks` respected — incumbent is 100% admitted books, no DFS): | | MLB | WNBA | |---|---:|---:| | prop-line groups | 6,422 | 853 | | gradeable (has an admitted book) | 1,146 (17.8%) | 144 (16.9%) | | consensus available | 995 | 87 | | comparable (both rulers) | 923 | 79 | | incumbent books | betmgm 586 · dk 538 · fd 16 · br 6 | dk 90 · fd 54 | **Delta (consensus − incumbent, probability points, over side):** | | MLB | WNBA | |---|---:|---:| | mean | **+1.50** | −0.29 | | median | 0 | 0 | | p10 / p90 | −1.6 / **+10.0** | −2.0 / +1.6 | | median abs / p90 abs | 0.5 / **11.95** | 0.55 / 2.5 | | disagree ≥2 pts | **24.8%** | 20.3% | | disagree ≥5 pts | **17.0%** | 2.5% | **Read it honestly: the median prop does not move at all.** The change is concentrated in a **right-skewed minority** — on MLB, **17% of comparable props move by 5+ probability points**, and the skew is one-directional (p90 +10.0 vs p10 −1.6): the incumbent systematically prices the OVER *below* the exchange-inclusive consensus. WNBA is nearly symmetric and much tighter. **What this does and does not establish.** It establishes the two rulers genuinely differ, on a minority of props, in a consistent direction. It does **not** establish that the new ruler is *better* — that requires re-running the edge and CLV measurements against it, which is the next order. ### 2.4 `ruler_version` stamped at the boundary — **APPLIED** `ledger_entries.ruler_version` (migration recorded as `migrations/025_ledger_ruler_version.sql`; applied to prod). **1,384 existing rows backfilled to `v1_first_book`** — a statement of fact, not an assumption: every row to date was produced by the first-book rule. 629 of them carry a `fair_prob_lock`. New rows stamp `CURRENT_RULER_VERSION` via `ledgerService`. **NEVER pool edge or CLV across differing `ruler_version` values.** The denominator changed; the numbers are not the same measurement. This is the same no-pooling boundary as `model_version`, and it is **arithmetically forced**. --- ## PHASE 3 — CONSEQUENCE **MLB isotonic `p_win` remains PROVISIONAL.** It was calibrated against the `v1_first_book` ruler. **It does not promote until re-run against the consensus ruler**, after this delta is reviewed. Recorded in MASTER-PLAN. --- ## WHAT IS AND IS NOT LIVE **LIVE:** the internal verification endpoint; `bookRoles.js` and `consensusRuler.js` as libraries; the `ruler_version` column and stamp. **NOT LIVE — deliberately:** `ALLOWED_BOOKS` is **unchanged**, so the served slate is byte-identical. `CURRENT_RULER_VERSION` is still `v1_first_book`. No live path calls `consensusRuler`. No grade, cache or slate changed. --- ## THE DECISION THIS TEES UP Widening `ALLOWED_BOOKS` to the reference set would make **64.8% of the MLB slate visible** for the first time — that is the single largest product change available, and it is a config edit. But it is **not** free: those props would grade against a market we have never measured our model on, and every edge number reset to `v2`. **That is the next order's decision, not this one's** — and it should be taken after the ruler re-run, not before it. ## OPEN 1. **Ruler policy sign-off** — `exchange_plus_us` selected off the curve. Confirm. 2. **Pro tier ($19/mo)** — buys settlement for **soccer** (proven), CLV via `/odds/closing`, steam via `/movement`, and `/results`. Genuinely cheap relative to what it unblocks. 3. **The 6 phantom books** — kept, against the order's wording, with reasoning above. Reverse if you disagree. 4. **`/context` is free and richer than what we hand-built** — umpire, roof, pitcher handedness, lineup confirmation. Wiring it is a later order.