From dbc14164853fdf54c360ae7d8b00aaa313d2fdfe Mon Sep 17 00:00:00 2001 From: Kev Date: Fri, 31 Jul 2026 07:07:13 -0400 Subject: [PATCH] docs: tier redesign spec recorded (gate discriminator exists; counter is display-only; base is 3 users) Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc --- outputs/VYNDR-COMPLETION-MATRIX.md | 18 ++++++++++++++ specs/STATE.md | 40 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/outputs/VYNDR-COMPLETION-MATRIX.md b/outputs/VYNDR-COMPLETION-MATRIX.md index b882763..32dffa9 100644 --- a/outputs/VYNDR-COMPLETION-MATRIX.md +++ b/outputs/VYNDR-COMPLETION-MATRIX.md @@ -706,3 +706,21 @@ Backend untouched. Three pure modules, unit-locked: 318 suites / 3,961 tests green, web build exit 0. **Mounting the modules into the live row components is a follow-up**; the visuals go to the Chrome audit. + +--- + +# TIER REDESIGN SPEC — 2026-07-31 → `specs/tier-redesign-spec.md` + +Option 2 (settled-free / live-paid) designed, not built. **FREE** = full data aggregator + the +**complete settled record** (letter, reasoning, edge, outcome — browsable) as the proof hook. +**ANALYST $14.99→$24.99** = tonight's live grades + reasoning + edge. **DESK $44.99→$59.99** = + +alt ladder, Kelly, portfolio, engine2. + +**The gate already has its discriminator:** `/api/snapshot` merges per-grade `outcome` (live WNBA: +5 of 25 settled), so `outcome != null ⇒ free`, `null ⇒ paid`, with no new pipeline. Filter whole +grades server-side, never infer resolution from time, fail closed to LIVE. + +**Two findings that reshape the plan:** (a) founder pricing is gated by **code + expiry, not seat +count** — the real counter (`/api/founders/count`, live 0/100) only displays and is cached 300s, so +making it a transactional gate is a genuine build; (b) the user base is **3 free, 0 paid**, so the +migration is a courtesy note, not a mass event — effort belongs on the settled-record proof surface. diff --git a/specs/STATE.md b/specs/STATE.md index 9820fb6..0c8165b 100644 --- a/specs/STATE.md +++ b/specs/STATE.md @@ -967,6 +967,46 @@ > unlimited scans, and Desk capabilities. **The one gate that is declared but not enforced is board > reasoning** — which is exactly the decision the D1-close rationale mount is waiting on. +> ## 🧾 TIER REDESIGN SPEC 2026-07-31 (report-first) → **`specs/tier-redesign-spec.md`** +> Option 2 (settled-free / live-paid) designed + sequenced. **Nothing built; no tier, price, gate or +> Stripe object changed.** +> **0.2 — THE LADDER IS HALF-EXPRESSIBLE TODAY, AND THE GATE IS THE WRONG THING.** `PRICE_MAP` +> already separates founder from standing objects (`STRIPE_PRICE_ANALYST[_FOUNDER]`, +> `STRIPE_PRICE_DESK[_FOUNDER]`), so **lifetime grandfathering is NATIVE** — a sub created against a +> founder price stays on it. **🔴 But founder access is gated by CODE + EXPIRY, not seat count** +> (`FOUNDER2026/VYNDR/BETONBLK/EARLYBIRD`, expiry 2026-12-31) — **anyone with a code gets founder +> pricing at ANY seat number.** A real Stripe-derived counter exists (`/api/founders/count`, **live +> `{claimed:0, total:100}`**) but only DISPLAYS, and is **cached 300s** — a cached read cannot enforce +> "slot 100 and 101 differ permanently." **Making the counter the GATE, uncached and transactional at +> checkout-session creation (plus retiring the code bypass), is a real build.** +> **0.3 — THE PAID→FREE FLIP POINT ALREADY EXISTS ON THE SERVED PAYLOAD.** Settlement writes +> `ledger_entries.outcome` + `settled_at`, and `/api/snapshot` already merges per-grade results — +> **live WNBA: 25 grades, 5 carrying `outcome:{result:'hit',actual:1}`.** So the discriminator +> (`outcome != null`) is present on the exact object to be gated. **No new pipeline needed.** +> **🔴 0.4 — THE MIGRATION IS NOT WHAT THE ORDER ASSUMES: `users` holds 3 users, ALL free, created +> Jun 12-19, and ZERO paid.** There is no warm mass base. The "founder launch to existing users" is a +> **courtesy note to 3 people**, and **the launch's real audience is people who have not signed up +> yet** — so effort belongs on the settled-record proof surface, not on migration machinery. +> **THE TABLE:** FREE = full data aggregator **+ the COMPLETE settled record** (letter · reasoning · +> edge · outcome, browsable/filterable) — a complete product, not a demo, and the proof hook that +> sells. ANALYST **$14.99→$24.99** = tonight's LIVE grades + reasoning + edge, unlimited. DESK +> **$44.99→$59.99** = + alt ladder · Kelly · portfolio · engine2. +> **REASONING + GRADE + EDGE ARE ONE PAID UNIT WHILE LIVE** (the reasoning IS the grade's argument; +> splitting them sells a claim without its defence) **and all three go free together at resolution** — +> which also converts today's unenforced board-reasoning leak into a **deliberate rule**. +> **THE GATE:** `outcome == null` ⇒ LIVE ⇒ Analyst+ · `outcome != null` ⇒ SETTLED ⇒ everyone. +> **Filter whole grades server-side (not field-strips)** so a live grade cannot leak partially · +> **never infer resolution from time or game status, only from a written outcome** (a game can be +> final before the settle pass runs) · **fail closed to LIVE** so a settle failure withholds rather +> than exposes · `void`/`unrecoverable` are terminal ⇒ free. +> **BUILD ORDER:** (1) the settled/live gate — depends on nothing new · (2) the free settled-record +> surface — **flagged as arguably shipping WITH (1), since gating live grades without it leaves free +> users with no graded content at all** · (3) Stripe ladder + transactional counter + grandfather rule +> + retire the code gate · (4) the founder note to the 3 · (5) pricing visuals (**already designed** in +> the package — no new design needed). +> **CANNOT DETERMINE: whether the four Stripe price objects exist in the dashboard** (env not readable +> here) — flagged as a prerequisite for build 3. + - **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,