This was not a thing to propose — it exists. functions/bets/odds-fetch.js
is a live Pages Function wired to The Odds API, with a 15-minute KV cache, per-month call
accounting against the free tier, and a usage dashboard at
/bets/api-usage/.
Tested today: it returns
503 api-key-missing. It has never had a key. One environment variable is the
whole distance between "no automatic lines" and "automatic lines".
Lines: gen-bet-lines.js reads ESPN's public game data (DraftKings, one book) for every NFL game in the next 8 days and feeds the hub. No key. The Odds API above would still add more books and history.
Finals: nfl-finals-to-ledger.js closes ledger rows when ESPN marks a game final. A logged pick is auto-graded; a game with no line becomes MISSED. It never invents a line.
VERIFIED 2026-09-13 20:21 ET: the first heartbeat commit (5d3b1974, github-actions) shows the job runs on GitHub, reaches ESPN (espn_ok: true), and can read the phone queue. The paragraph below is kept as the diagnosis that led to the heartbeat.
(Before verification) The game-night GitHub job (.github/workflows/nfl-finals.yml) was UNVERIFIED. It had not produced a single commit. The likely cause is a blind spot, not a crash: it only committed when it closed a game, and CC had already closed each one locally, so any run that did happen left no trace. The Actions run history is behind a GitHub login this machine does not have, so CC could not check. Since 2026-09-13 every CI run writes handoff-blocks/ci/nfl-finals-heartbeat.json (when it ran, whether ESPN answered) and commits it at least every 2 hours (6 until 2026-09-14, which let a whole game night pass unseen), or immediately if ESPN refused. That settles "does it run, and can GitHub's servers reach ESPN" at the first run.
Manual fallback, always works: node handoff-blocks/scripts/nfl-finals-to-ledger.js (add --dry-run to preview). If a phone line is still queued it holds the game rather than marking it MISSED; run node handoff-blocks/scripts/process-queue.js first.
ODDS_API_KEY in Cloudflare Pages → Settings →
Environment variables — the same place as ADMIN_INVITE_SECRET.
Not in this repo: git history is permanent, and gated is not undoable.The budget already works out. The existing notes reckon roughly 12 calls per three hours of viewing one game, about 200 a month at typical use — comfortably inside 500. The cache is what makes that true, and it is already written.
| Task | Status |
|---|---|
| Pull final scores and player stats after a game | YES — published facts, done for last night |
| Grade the analysis against the result | YES — the backtest on the NE@SEA page |
| Track placed bets and maintain the ledger | YES, once Sam says what he staked |
| Build and merge page structure | YES |
| Verify schedules and matchups | YES — tonight's game was checked rather than assumed |
| Refresh a data feed on a run | YES — the ADP feed already works this way; odds would too, with a key |
| Task | Why not |
|---|---|
| Forward-looking analysis — the read on a matchup | Needs live research: injury reports, beat coverage, line movement in the hours before kickoff. CC runs when Sam runs it, not continuously |
| Watch a line move | Even with the API key, CC has no schedule. The relay waits for a session — there is no always-on worker |
| Decide a bet | That is Sam's, and it should stay Sam's |
The one position the page argued for lost by 21.5 points. Both positions it declined to take were right. The MLB backtest already found that STRONG bets underperformed MEDIUM ones; this is a second sport saying the same thing.
Which is an argument about where automation is worth having. Automating the confident half — picks and reads — would industrialise the part with no demonstrated edge. Automating the record-keeping half industrialises the part that has actually been missing, and the ledger holding one tracked bet since May is the evidence for which of those matters.
Seven of the fifteen older game pages are unfilled skeletons. The constraint has never been the ability to generate analysis. It has been grading it afterwards.