Files
vyndr/specs/STATE.md
T
2026-07-12 13:44:36 -04:00

108 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# VYNDR — STATE OF THE WORLD
### As of `ec5ee3d` (main, 2026-07-12). This file opens every future session. Update it when a train ships.
## WHERE THINGS STAND
**Tests: 2757 / 229 suites, all green. Web build exit 0.** Baseline at the A1
board's start was 2398. Governing docs: `specs/VYNDR-NORTH-STAR.md` (v1.1) +
`specs/VOICE.md` (v1.1) + `specs/ROW-GRAMMAR.md` — read them before building
anything.
**The record is live and writing itself.** Day one (Jul 11): 24→25 MLB rows,
real locks (line/odds/book), closing capture every snapshot + every 20-min
intraday refresh, settlement each morning slot with signed CLV. No percentage
renders anywhere under n≥20 — enforced in `ledgerService.getModelAggregate`,
the single gate. WNBA/soccer rows pend honestly until Phase 4.5.
## THE THREE A1 TRAINS (all merged to main, all pushed)
| Train | Range | Carried |
|---|---|---|
| #1 | `4d2b27d → 219167e` | S0 verify · S1 promise audit (`PROMISE-AUDIT.md`: alt ladder + quarter-Kelly BUILT, analyst unlimited, "40+" claims made honest) · S2 compliance (/responsible-gambling rebuilt, /terms /privacy drafts, /methodology, 5 seed articles in `content/articles/`) · S3 affiliate (BOOK IT deep links organic-until-config-flip, best-price dots, ?ref= partner attribution) · S4 media engine (`mediaEngine` + executable VOICE lint, `/desk` arsenal + DATA BRIEF, Ghost DRAFTS-only publisher) · S5 viability (statsapi lineups → CONFIRMED/NOT-IN kills grades visibly; ESPN injury wire; Yesterday/Today/Tomorrow nav + Settle panel) · S7 newsletter (Listmonk capture + THE VYNDR REPORT assembly, env-gated) · S8 ops (settle alarm, 3-slot failure pager, quota/disk/mem, 9AM pulse — test alert delivered to the real ntfy channel) · S9 slip reader (tesseract.js OCR + per-book parsers, /slip page; synthetic-image acceptance passed) · S10 public profiles (/u/{handle}, private-by-default, no-existence-leak) · migrations 021/022 |
| #2 | `219167e → 14dc9cf` | S6 display (ROW-GRAMMAR.md + slot-order fixes, line sparklines from intraday history, last-10 ●●○ dots, CLV distribution on MODEL tab, ⌘K SearchModal + mobile search, landing LCP fixes) |
| #3 | `14dc9cf → ec5ee3d` | S11 live tracking (statsapi/ESPN live box lines → ON PACE / NEEDS N / HIT ✓ in the outcome slot, live games float up, "TRACKING — READ LOCKED PRE-GAME"; real-live acceptance: Harper 3/1.5 TB ▼8th → HIT ✓). Grades never change in-game. |
**Deploy status:** ALL LIVE as of `17fb981` (Jul 12 17:43 UTC). Fingerprints
confirmed in prod: `/api/live/mlb` serves JSON with `hasLive:true` (S11 live
tracking working — a real game was tracked at confirm time), `/api/health`
healthy, `/api/snapshot/summary``expected_interval_s:1200` (env var now set).
**P0 postmortem (`17fb981`):** the API silently served a 14h-old image
(`4d2b27d`) from Jul 11 ~20:00 to Jul 12 17:43. Root cause: the Dockerfile
never copied `content/`, and `mediaEngine.js` read `content/stark-lines.json`
with an unguarded module-load `readFileSync` → ENOENT at require time crashed
`app.js`'s eager route chain (app → routes/desk → deskService → mediaEngine)
→ boot failed → Coolify healthcheck rolled back. Every merge from S6 onward
was merged-but-not-running for 14h. Fixed: Dockerfile COPYs content/;
mediaEngine treats the file as optional garnish (loadStark try/catch → {});
`src/preflight.js` now prints `[preflight] OK|DEGRADED` at boot so a missing
file/env is legible instead of masked by a rollback. LESSON: any module-load
file read is a boot-crash risk — keep them lazy + try/catch, and the
Dockerfile must copy every dir the runtime reads.
## LIVE INFRA MAP (verified in prod)
- **VYNDR Web** — Next.js :3000, vyndr.app. **VYNDR API** — Express :3001,
api.vyndr.app (`/api/internal/*` behind `VYNDR_INTERNAL_KEY`).
- **Scheduler (in-process, API app, `SNAPSHOT_CRON=1`)** — snapshot slots
14,19,22,1,3 UTC; settle pass (outcomes + ledger, idempotent) runs FIRST
each slot; 20-min intraday odds refresh during slate hours
(`INTRADAY_REFRESH=0` kills); desk-ready ping after the day's first slot;
daily pulse 13:00 UTC; missed-cron + 3-slot-failure + quota pagers → ntfy
`vyndr-pipeline-kev2026`. Boot logs: `[snapshotScheduler] armed` +
`[settlement] armed`.
- **Data**: Redis (mastermind-cache) for all pipeline caches (SNAP_TTL 24h —
do NOT shorten; the 11h overnight gap killed morning settles once).
**Supabase = the ledger** (`ledger_entries` + `public_profiles` +
`user_profiles.partner_ref`; RLS everywhere, service-role-only writes;
migrations 001022 applied, files in `supabase/migrations/`).
- **Deploys**: gitea `git.builtbykev.com/builtbykev/vyndr` → Coolify webhook
(HMAC secret fixed Jul 11; auto-deploy proven). GitHub `origin` remote is
dead-PAT hygiene debt only.
- **Free feeds** (zero out-of-pocket law): PropLine ×3 keys (9k/day capacity;
intraday uses ≤144/day), odds-api backup (quota-gated), statsapi.mlb.com,
ESPN site API. tesseract.js OCR self-hosted (traineddata caches on first
call — needs outbound network once).
## ENV VARS STILL PENDING ON KEV'S SIDE (Coolify, API app)
1. `SNAPSHOT_EXPECTED_INTERVAL=1200`**confirmed absent** (fresh restart
still served the 18000 default). SYNC badge tracks intraday cadence once set.
2. `DESK_OWNERS=kevdevelops@gmail.com` — /desk 403s everyone until set.
3. `LISTMONK_URL` / `LISTMONK_USER` / `LISTMONK_TOKEN` / `LISTMONK_LIST_ID`
(all four together) — newsletter no-ops politely without them; box-side
setup in `docs/NEWSLETTER.md`.
4. Optional: `GHOST_URL` + `GHOST_ADMIN_API_KEY` (drafts), `PULSE_HOUR_UTC`.
**Entity placeholders** (S2, blocked on Kev's values): `[ENTITY NAME]`,
`[STATE OF FORMATION]`, `[ARBITRATION VENUE]`, `[CONTACT EMAIL]` in
`web/src/app/terms/page.tsx` (6×) + `web/src/app/privacy/page.tsx` (5×).
A test keeps unverified entity names out until replaced.
## HONEST OPEN ITEMS
- **Phase 4.5 — WNBA settlement via ESPN box scores. DUE ~Jul 24** (hard
date). Until then WNBA/soccer/NBA ledger rows pend forever; accuracy = MLB only.
- **The record needs time**: no percentage anywhere before n≥20 settles, by
design. First percentages expected ~day 34 of MLB volume.
- **Slip reader**: acceptance was a clean synthetic image; needs one real
phone screenshot (DK) to validate OCR robustness.
- **Landing LCP**: S6's fixes are static-analysis-provable but unmeasured —
run PageSpeed mobile vs the 3.8s baseline; next suspect is the
LiveHeroProp post-fetch swap.
- **Partner report endpoint** returns real numbers only for signups AFTER
migration 021 (metadata path live; OAuth signups carry no ref — known gap
in `docs/PARTNERS.md`).
- **NBA Oct / NFL Sep readiness** + soccer end-to-end: `specs/vyndr-roadmap.md`.
- **Killed permanently** (founder ruling): AI chat assistant, light mode.
## OPERATING LAWS (never relax)
Zero out-of-pocket · absent beats wrong, refusal beats hollow · n≥20 before
any percentage · nothing auto-posts anywhere, ever · grades never change
in-game (revisions are public via `revised_from_grade`) · one meaning per
color, data in mono · no exclamation points, anywhere · the pipeline is the
only source of numbers, including in marketing (`mediaEngine` lint enforces).
— STATE as of ec5ee3d · the record is writing —