Files
builtbykev 4ddfb84232 Session A (night2): AUTONOMY.md + SNAP_TTL 6h→24h P0 fix
The overnight cron gap (03→14 UTC) is 11h; a 6h snapshot TTL meant the
morning settle pass read an expired snapshot and the accuracy loop
silently settled nothing. Ledger (Postgres) settle was immune.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 00:18:01 -04:00

81 lines
4.4 KiB
Markdown
Raw Permalink 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.
# AUTONOMY — "Kev touches nothing for 7 days, what happens"
Session 60 (night2), traced on d10bb4c + tonight's fixes. Every step cites
its code. Verdict at the bottom.
## The daily loop (all times ET / UTC)
**10:00 AM ET (14:00 UTC) — cron slot 1** (`snapshotScheduler.js` HOURS_UTC
= 14,19,22,1,3; armed by `SNAPSHOT_CRON=1` in server.js):
1. **Settle pass runs FIRST** (`tick()` order: settleAllOutcomes →
settleAllLedgers → runAllSnapshots):
- `outcomeService.settleAllOutcomes` — settles yesterday's snapshot
grades vs real MLB game logs → `outcomes:{sport}:log` +
`accuracy:{sport}` + `accuracy:overall`.
**P0 FIXED TONIGHT:** `SNAP_TTL` was 6h but the overnight gap
(03:00→14:00 UTC) is 11h — the snapshot EXPIRED before this pass and
the accuracy loop settled nothing. Now 24h (`snapshotService.js`).
- `ledgerService.settleAllLedgers` — settles Supabase `ledger_entries`
(game_date < today ET): outcome + actual_value + CLV vs the captured
closing line. Reads Postgres, immune to Redis TTLs. Idempotent
(`.is('outcome', null)` guard) — re-runs are no-ops.
2. **Snapshot pass** (`snapshotService.runSnapshot` per sport):
- `getOdds` (PropLine free keys, odds-api backup) → date-pinned; a
morning with no lines yet = honest `skipped: no props` (NOT retried —
only thrown/null responses retry once).
- grades → normalize/dedupe → archetype+team resolve (free statsapi)
`snapshot:{sport}:latest` + `grades:{sport}` (6h TTL — outlives the
5h max intra-day gap) → ticker events.
- **Ledger write**: `recordPipelineGrades` upserts public rows
(user_id NULL, `ignoreDuplicates` — re-runs never dupe or overwrite
the original lock). `captureClosing` overwrites today's unsettled
rows' closing_line/odds with current feed values.
**3:00 PM / 6:00 PM / 9:00 PM / 11:00 PM ET — cron slots 25**: same tick.
Each run re-captures closing (the LAST write before a game starts is the
real close — after start the prop leaves the feed, so it freezes), computes
line deltas vs the previous snapshot (ticker MOVE), and upserts any newly
posted props as new locked rows.
**Overnight**: games finish; nothing runs until 10 AM, when the settle pass
closes yesterday's book. Every settled row gets outcome/actual/CLV exactly
once.
## Self-healing / watchdogs
- Missed-cron watchdog (per-minute, `isSnapshotOverdue`) → ntfy alert
(`vyndr-pipeline-kev2026`) once per missed slot. Snapshot success/empty/
failure also alert (`opsNotify`, never throws).
- `GET /api/internal/snapshot/status` (internal key) → cron_armed, last
snapshot, redis keys, `overdue` flag.
- Schedule/streaks/hotlist read caches self-heal on miss (cache-aside ESPN,
date-pinned since Session 57). Public snapshot/ledger reads are
cache-only and can never drain quota.
## What accumulates without touch (day 1 → day 7)
- Day 1: first locked rows; MODEL tab "RECORD BUILDING · N pending".
- Day 2 morning: first settles + CLV (closing captured day 1). AccuracyBadge
leaves "LEARNING" at 8 settled; ledger aggregates render % at n≥20.
- Day ~34 (MLB volume ~20-25 graded/day, all settleable): hit% + beat-close%
go live on /ledger MODEL, landing ModelRecord, player strips.
- Day 7: 30d-window aggregates, per-player records accruing. WNBA rows
accumulate PENDING (honest) until Phase 4.5 (ESPN box-score settle,
due ~Jul 24) — the one known credibility clock.
## Dependencies that must stay true (the only touch-points)
1. Coolify container running with `SNAPSHOT_CRON=1`, PropLine keys,
SUPABASE_URL + service key, REDIS_URL. (One-time, already set.)
2. Redis persistence is server-side config (Session 52) — snapshot keys
vanishing on restart = Coolify Redis setting, not code. The LEDGER is
Postgres and survives anything.
3. ntfy channel monitored casually — alerts are informational; the loop
needs no response.
## Verdict
**Zero-touch for 7 days: YES for the core record** (grade→lock→close→
settle→aggregate, all scheduled in-process, idempotent, alert-covered) —
after tonight's SNAP_TTL fix, which was the one broken link (morning
settles silently skipped). Known honest gaps: WNBA settles pending until
4.5; streaks/hot-list data production was EXTERNAL (tank01-prefetch via
n8n/cron, possibly unarmed) — Session B moves log production into the
snapshot pipeline so the aggregator feeds itself.