diff --git a/specs/MASTER-PLAN.md b/specs/MASTER-PLAN.md index 4871479..8f8dd8e 100644 --- a/specs/MASTER-PLAN.md +++ b/specs/MASTER-PLAN.md @@ -29,37 +29,31 @@ 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`. +**BUILD THE MATCHUP (PLATOON) AXIS on `challengerProjection` (arch-v1).** +Sourcing verified 2026-08-01 by VALUE, not endpoint presence: -**The next order follows from what that measurement found, not from the plan:** +| input | coverage | source | +|---|---:|---| +| opposing probable starter | **29/30 team-sides** | schedule `hydrate=probablePitcher` | +| pitcher hand (L/R) | **1342/1342** | `pitchHand.code` | +| batter hand (L/R/S) | **1342/1342** | `batSide.code` (L 416 / R 848 / S 78) | -> **`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. +**One shared dependency unlocks it:** `/sports/1/players` — a list we already +fetch and cache — carries `currentTeam.id`, `batSide` **and** `pitchHand`. +**SOURCE-LINEUPS-first is NOT needed.** Archetype-level reach on the opposing +starter is also available (the SP resolves to a player id, so the existing +classifier applies) — noted, not built. -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. +*Blocked on nothing.* -**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**. +### Accruing in parallel — do NOT re-run early +- **`opportunity_drift`** — live, 142 rows/slate, orthogonal (r ≈ 0 vs every + projection input). Verdict n-blocked; `scripts/opportunity-axis-holdout.sql`. +- **`environment`** — resolver fixed and verified (105/120), **first ledger write + is tomorrow's slate** (same-day rows dedupe by design and cannot backfill). -**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. - ---- +Both promote only on their own axis-filtered holdout, and only if **reliability +AND resolution** improve. ## 📍 STATE AS OF 2026-08-01 (Order Zero, measured on prod with the real key) @@ -78,6 +72,10 @@ multi-book data. | **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 | +| ✅ **p_win ranking / edge retired** | DONE — boards rank on `forecast_rank`, edge is diagnostic-only, rollback armed (`FORECAST_RANK=0`) | `specs/rank-on-pwin-challenger.md` | +| ✅ **calibration** | **DECIDED** — ruler-independent; MLB isotonic **DECIDED**, not provisional | `specs/mlb-recalibration-vs-consensus-ruler.md` | +| ✅ **grade cap** | DONE — 25 → 500; board **7 → 365+** graded props | `specs/grade-cap-and-refusal-diagnosis.md` | +| ✅ **S59 join invariant** | **ARMED** — root cause was `searchPlayer` returning `team: null` (`currentTeam` has no `name`); fail-safe: drops only on a positive not-in-game | `tests/unit/slateJoinInvariant.test.js` | | **arch-v1 condition axes** | **env + matchup fired on 0/634 prod rows** — `team` was null on 416/416, so the venue join had no key. **Environment FIXED** (joins on the game; resolves 105/120). **Matchup still dead** — needs opposing SP + both hands, three separate absences | `specs/arch-v1-axis-audit.md` | | **opportunity_drift** | built, orthogonal (r≈0), live as a challenger on 142 rows/slate | verdict n-blocked; `scripts/opportunity-axis-holdout.sql` | | **opportunity layer** | **NOT BUILT** — `ab_per_game` is display-only; engine1 has no usage factor; MLB batting order unavailable in every wired source | Step 0 stopped before wiring. Recommended instead: an `opportunity_drift` axis on the EXISTING `challengerProjection` (arch-v1). See `specs/connect-opportunity-step0.md` | @@ -207,7 +205,29 @@ a sport is a module. **Blocks all of A2 after MLB.** | **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**. + +> **RECONCILED 2026-08-01.** Phase 1 (MLB model truth) is the ACTIVE phase and is +> further along than the table above implies — but not because orders were +> ticked off as planned. Most of it turned out to be **connection and repair, not +> construction**: the ladder question dissolved (calibration is ruler- +> independent), the cap was discarding 95.7% of the slate, and two condition axes +> were wired but firing on zero rows. +> +> **Closed:** p_win ranking + edge retirement · calibration DECIDED · MLB isotonic +> DECIDED · grade cap 25→500 · book widening (display) · S59 invariant armed · +> environment axis repaired. +> +> **Open in Phase 1:** matchup axis (next order) · the two accruing verdicts +> (`opportunity_drift`, `environment`) — **time, not code** · connect the still- +> dormant layers (similarity, Bayesian, distribution ladder). +> +> **Carried, not in Phase 1:** WNBA is **NOT BUILT**, not failed (its −0.12 was +> NBA-template machinery on WNBA data) · the consensus ruler is **MARKET, not +> SHARP**, and that is **PENDING-RECOVERY** until PropLine answers why Pinnacle +> MLB prop coverage stopped on 2026-07-31 (`BLOCKERS.md`) — do not enshrine it as +> permanent. +> +> **Remaining to the end state: ≈ 19 orders**, of which **~9 are unblocked today**. ---