From e46c88e3649e4c7ab542c9004c44758592225d04 Mon Sep 17 00:00:00 2001 From: Kev Date: Mon, 20 Jul 2026 02:06:43 -0400 Subject: [PATCH] STATE: retention clock proven ticking via induced cron entrypoint --- specs/STATE.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/specs/STATE.md b/specs/STATE.md index 19945d2..c753ab1 100644 --- a/specs/STATE.md +++ b/specs/STATE.md @@ -133,6 +133,41 @@ exist locally; harmless, the data restores completely. --- +## 🟢 RETENTION CLOCK TICKS ON ITS OWN (2026-07-20 06:05 UTC) +Induced the REAL cron entrypoint rather than waiting for 14:00 UTC. +- **Cron capture needed NO wiring — verified in code.** `snapshotScheduler`'s + tick calls `runAll` = `snapshotService.runAllSnapshots` → `runSnapshot` per + sport → `gradeAndCacheSlate` → `onGraded` → retention. **The scheduled and + manual paths are the same function.** Nothing had been captured only because + no slot had fired since retention deployed (~02:55; slots 14/19/22/1/3). +- **PROVEN by induction:** `POST /api/internal/snapshot/all` (= `runAllSnapshots`, + the exact function the tick calls) wrote cycles at **06:03:23 mlb** and + **06:05:09 wnba**, 50 rows each, `cycle_hour_utc=6`, features 50/50. + *(The HTTP request gateway-timed-out at ~2 min; the job completed server-side — + verified in the DB, not from the response.)* +- **`archetype` FIXED — 50/50 on MLB** (was 0/100). Retention still COLLECTS at + grade time (only moment features exist) but now PERSISTS after enrichment via + `retentionService.mergeEnrichment`, which fills ONLY archetype/team/opponent + and never rewrites grade-time features or model output (test-locked). +- **🔶 `team`/`opponent` STILL NULL — upstream, not the merge.** The ENRICHED + grades themselves carry `team:null` (verified live: 0/25 with team, 25/25 with + archetype). `resolvePlayerStats` doesn't return `stats.team` for MLB, and + enrichment never sets `opponent` at all. Fixing that is an upstream change, + deliberately not made under this order. +- **WNBA archetype is null** — honest absence: WNBA stats don't resolve (Python + service offline), so there is nothing to classify from. +- **ZERO-WRITE ALARM ARMED** — `opsWatch.retentionZeroWriteAlarm` pages at + missed-snapshot severity when a slot GRADED props but retention wrote fewer + rows than the slate. `runSnapshot` now returns `retentionRows` so the tick can + evaluate it. Retention is best-effort by design, so a broken write is silent by + construction; this is the counterweight. Verified quiet on a healthy run + (gradeCount 25 → retentionRows 50). +- **INTRADAY DEFERRED (Phase 4).** `intradayRefreshService` calls + `analyzeViaEngine1` DIRECTLY — it never goes through `gradeAndCacheSlate`, so + there is no `onGraded` hook to reuse. Capturing it needs its own ctx + + snapshot_id + persist path and would ~5× row volume. Not small; not done. +- Outcome stamping still NOT implemented (depends on the settlement fix). + ## 🟢 RETENTION LIVE — model_snapshots capturing (2026-07-20, migration 025 applied) Phase 2 priority zero. **History now compounds from tonight.** Spec: `specs/snapshot-retention.md`.