# VYNDR build handoff — 2026-08-08 Written from the repo, not from summary. Every claim below was grep- or run-verified at `71d3b7b`. Where something could not be verified from the repo it says UNKNOWN. ## Repo state ``` HEAD 71d3b7b78692c31ba0596ec88874986ff1b58116 gitea main 71d3b7b78692c31ba0596ec88874986ff1b58116 (in sync) tree clean — 0 modified/untracked tests 4,539 passing / 4 skipped web build exit 0 ``` **Push remote is `gitea`, not `origin`.** `origin` is GitHub and has no credential on this machine; every push must be `git push gitea main`. This cost twenty commits of false "no credentials" diagnosis — see `docs/GIT-PUSH-DIAGNOSIS.md`. --- ## BUILD STATE per surface ### DONE this arc — verified present | surface | file | lines | |---|---|---| | **E1 movement strip** | `web/src/components/vyndr/MovementStrip.tsx` | 113 | | **F9–F11 offseason hub shell** | `web/src/app/offseason/page.tsx` + `components/vyndr/OffseasonHub.tsx` | 21 + hub | | **E10 Report issue template** | `src/services/report/reportTemplate.js` | 169 | | **E12 /report archive** | `web/src/app/report/page.tsx` + `components/vyndr/ReportArchive.tsx` + `src/routes/report.js` | 17 + comp + route | | **Content engine** | `src/services/content/contentEngine.js` + 3 templates + `cardRenderer.js` | 172 + | | **Content studio API + preview** | `src/routes/contentStudio.js` + `web/src/app/studio/page.tsx` | 175 + 131 | | **Wave-D1 motion primitives** | `web/src/lib/motion.js` (E17/E18/E27/E28) | 100 | | **Honest served grade** | `src/services/model/servedGrade.js` | 142 | | **Archetype doctrine** | `specs/ARCHETYPE-TAXONOMY-DOCTRINE.md` | 53 | Also DONE and verified: **D1 glyph library** (39 of 83 wired — every glyph that maps to a real archetype; colours match the registry exactly), **A1 card token** (`--bg-1`, 32 consumers), **B1 boundary channel** (`--priced-out`, 4 consumers), **book comparison** (mounted via `GradeResultCard → scan/page`), **THE WIRE** (`vyndr/Ticker`). ### GATED — verified absent, with the gate named | surface | gate | verified | |---|---|---| | **F5 article media** | card-system reconciliation | `ArticleHero`/`StatCallout`/`PullQuote` = 0 files | | **E16/F8 share cards + crops** | card-system reconciliation *and* the resolution tail has no share-card generation step | `ShareCard.tsx` has **0 importers** | | **In-season hub IA** | the content formula (social chat) | no spec exists anywhere | | **E9 calibration curve** | model accrual — 0 eligible dates | `CalibrationCurve` = 0 files | | **E15 Price Triplet MODEL leg** | model — EV layer | renders honest `NO_MODEL` | | **E2 BookChip tiles / E6 push-to-book** | licensing / affiliate approval | every book `enabled:false` | | **E3 best-number crown** | measurement — measured flat | `CrownBadge` = 0 files | | **E13 Scanner S6** | another build order | — | **There are no ungated Wave-2 targets left.** The next move is a decision, not a build. --- ## THE TWO OPEN DECISIONS ### 1. Card-system reconciliation — unblocks F5 *and* E16/F8 together The content engine emits **1080×1350 SVG** cards (`src/services/content/cardRenderer.js`). The design package defines **five master sizes** with layouts and rasterised PNGs in `specs/design-reference/exports/`: settle 1080×1350, story 1080×1920, square 1080, X 1200×675, record 1080×1350, article OG 1200×630. **I built the renderer without checking whether a designed card system existed. It did.** They are a parallel invention — not wrong, but they must become one visual language before F5 (whose hero graphics are generated data visuals) or E16 can proceed. The decision: does the content engine conform to the E16 masters, or do the masters absorb the engine's generator? **Cheapest unblock on the board — it frees two gated items at once.** ### 2. In-season hub information architecture `Vyndr Offseason.dc.html` specifies an **offseason** hub, and its shell is built. Nothing specifies what that surface is **in-season**, or how content, articles, wire and the live slate share year-round navigation. Every component exists; the composition does not. The spec is explicit that **sport state lives in the sport tab** (`NFL · CAMP −5D`) and never in a separate offseason tab — so this is not a new route, it is a mode. Gated on the social chat's content formula, which determines the recurring slots. --- ## ACCRUAL CLOCK ``` champion marker: engine1@2026-08-07-fullwindow calibration_refit 0/10 WAITING (~2 weeks) hits_factor_lift 0/10 WAITING (~2 weeks) prior_verdict_reaudit 0/14 WAITING (3+ weeks) rbi_lineup_slot_gate 0/14 WAITING (3+ weeks) blocked: no settled rows yet carry the repaired champion marker ``` **FIRST TRIGGER: 10 eligible calibration dates → calibration re-fit.** Then, in order: hits factor lift → prior verdict re-audit → rbi lineup-slot gate. **Thresholds are ATTEMPT floors, not TRUST floors.** Reaching 10 dates means the re-fit *can be measured*, not that it is trustworthy. A 10-date map is thin, deploys PROVISIONAL with auto-demotion, and its interval will be wide. **No measurement on reconstructions of the retired forecast.** Anything requiring settled data waits. `src/services/model/reAuditEligibility.js` enforces it — eligibility is a version marker, counted in DATES not rows, and a mixed table counts only the repaired rows. --- ## STANDING DOCTRINE **Truth Law.** No fabricated data anywhere — including **designer sample data**. The design files' numbers (Nabers 1,120.5, Wembanyama +420→+330, Nº 128, DAY RECORD 9–4) are a *spec for what a live feed renders*, never content to paste. Tests assert none of it ships. `Number(null) === 0` is the classic breach; note its inverse also bites — **zero is a real fact** ("0 cleared B+" is our most honest post). **83-glyph four-sport taxonomy.** A glyph renders ONLY where its archetype is modeled and proven. 39 wired; **44 dormant slots** for WNBA/NBA/Soccer. Wiring them would mean inventing 44 archetypes to consume artwork — decoration presented as classification, forbidden. `DUAL THREAT` and `PAINT BOSS` are modeled archetypes with no mark: the mirror gap, flagged to design. **Repaired champion.** The forecaster read `res.last10` — **ten games** — as its season rate. Fixed to the full season log with recency weight 0.40 → 0.20. Resolution: hits 0.00251 → 0.00817. **Every factor verdict in the programme was measured against the broken baseline and may deserve re-audit** — direction unknown, not pre-priced. **Calibration is WITHDRAWN.** `CALIBRATION_DEPLOYED = []`. The maps were fitted on the retired forecast; refitting today would refit it again. Served `p_win` is repaired-champion raw. **Grade doctrine.** `A+`, `A`, `A-` are **unissuable** — structurally, not rarely. Ceiling is **B+ at 0.663 realized against a 0.6005 baseline**. Bands that cannot be separated from the baseline SAY so. The served letter derives from `p_win`; `engine1.grade` is preserved as `engine_grade` and read by no serving code. **Two guards, both in CI:** - `renderReachability.test.js` — a promised field must trace payload → adapter → component → **mounted**. Built-but-unread killed three surfaces before this. - `baseRateWindow.test.js` — no fixed N may stand in for a season. Six paths across two sports fell to that class. **Compose, don't fork.** The recurring failure this arc: building beside an existing thing instead of on it (cards vs E16, `MovementStrip` nearly vs `gradeShift`, `reportTemplate` nearly vs `newsletterService`). Check for the existing implementation first — twice it was already there. --- ## PARALLEL WORKSTREAM SEEDS ### A. Social strategy chat > VYNDR's content engine ships posts from real data (`src/services/content/`, > three templates, Truth-Law enforced — an unbacked token refuses to render). > Review `/studio` output and `specs/CONTENT-ENGINE.md`. Produce: the content > formula/calendar (what posts recur, on what cadence), and **the card-format > decision** — the engine's 1080×1350 renderer vs the five designed masters in > `specs/design-reference/exports/`. That decision unblocks F5 article media and > E16/F8 on the build side. Voice: `specs/VOICE.md` governs; the engine templates > encode a sharper register — decide whether that becomes the house voice. ### B. Multi-sport archetype build > VYNDR has archetype registries for MLB (15), NBA (15), Soccer (6), WNBA (5) — > but **only MLB batters is MODELED**; the rest have no proven factor model, so > their glyphs stay dormant per `specs/ARCHETYPE-TAXONOMY-DOCTRINE.md`. 44 of 83 > designed glyphs await their sport. Pick a sport, build its archetype > classifier from real features, and take it through the two-part gate > (`src/services/model/factorGate.js` — must MOVE the prediction and improve > out-of-sample Brier, cumulative-Bonferroni corrected). Prerequisite: that > sport needs settled outcomes. WNBA settles via ESPN box scores; NBA and Soccer > do not settle at all yet, which is the real first task there. --- ## Where to start in the next chat 1. Take one of the two open decisions (card system is cheapest — unblocks two). 2. Or run a parallel workstream seed above. 3. **Do not** start model work — it is date-blocked and will stay so for ~2 weeks. 4. Verify before building: this arc corrected its own board four times. `git grep` beats any summary, including this one.