BikeControl (SwiftControl)
Use Zwift Click, Zwift Play, Shimano Di2, or any Bluetooth controller with MyWhoosh, Rouvy, Zwift, and more. Free to try on iOS, Android, Windows, and macOS.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗This folder turns BFF (Ben's Fantasy Football) into a free website that keeps itself up to date all year, with nothing to edit from one season to the next. Two GitHub workflows do the work, no API keys needed:
| Workflow | File | When | What it does |
|---|---|---|---|
| Update Gridiron Fantasy | update.yml | every 3 hours (23 minutes past) | downloads the latest NFL data, rebuilds every projection, ranking, trend and prop line, saves and grades the weekly calls, collects news, writes and updates BFF Originals, optionally syncs your Sleeper league, checks the page, saves it |
| Live scores, news and lines | scores.yml | every 10 minutes | live scores and box scores, news from every source, prop lines moved by ESPN's and Sleeper's latest projections, the odds and news, parlay picks, BFF Originals (breaking news, previews, recaps); only saves when something changed |
Both share one queue, so they never save at the same moment, and each run starts from the newest saved data (not the version that was current when it was queued). Before anything is saved, check_page.py checks the
page (data readable, enough players, no script errors) and the live files; anything broken is put back to the
last good version, so the published site never breaks.
ff_kdst.py): Players has K and D/ST buttons with this week's ranks, the season's
points, the last three games and the rest-of-season projection; each has its own card (game log, road ahead, how it
is scored and projected). Leagues keep them: ESPN (kicker ids, D/ST ids), Sleeper (DEF team codes, kicker ids),
typed or copied rosters ("Bills D/ST", a kicker's name) and screenshots. Your lineup fills the league's K and D/ST
spots, and power rankings count them when the league starts them. Live points come from ESPN's box score (field
goals by distance from the scoring plays; sacks, takeaways, return touchdowns, safeties and points allowed), with the
official numbers once the data job posts them. Ask BFF answers "best kickers this week", "should I start the
Texans defense" or a kicker by name. They stay out of BFF's trade values, buy/sell calls, waiver targets and prop
lines, so nothing in the skill-player rankings changes. Scoring (Yahoo's default settings, which Sleeper's
follow): field goals 0-39 yards 3, 40-49 yards 4, 50+ yards 5, extra points 1; D/ST sack 1, interception 2, fumble
recovery 2, touchdown 6, safety 2, blocked kick 2, points allowed 0: 10, 1-6: 7, 7-13: 4, 14-20: 1, 21-27: 0,
28-34: -1, 35+: -4, where points allowed leave out touchdowns the other defense scores off this team's offense
(interception and fumble returns, blocked field goals), safeties and defensive two-point returns, as Yahoo counts
them. Projections (research/bt_kdst.py, fit on 2015-22 and tested on 2023-25 games they never saw): D/ST from the
opponent's Vegas implied points, the defense's sacks and takeaways so far, the opponent offense's sacks and giveaways
so far and home field (average miss 4.4 points, correlation 0.33 with what happened; the league average missed by 4.5
with 0.08); kickers from the team's implied points, his points per game so far and indoor games (3.6 and 0.16 against
3.7 and 0.04: a kicker's week is mostly luck, and the page says so). The D/ST counts match nflverse's own team stats
for 2023-25 (interceptions, safeties and blocked kicks exactly; sacks within 1%, since play-by-play also counts the
team sacks no player is credited with).game_build.py, Draft > Spin Draft game): spin a slot machine for a random NFL team and season since
1999, draft one of that team's real fantasy scorers into an open spot (QB, RB, RB, WR, WR, TE, FLEX), and build the
highest-scoring lineup in seven spins, with two re-spins. The end screen shows your total, how close you came to the
best lineup your spins allowed (every order tried), your best score, and a result to copy and share. Daily
challenge gives everyone the same spins that day; PPR, half PPR or standard. Points are each player's
regular-season total for that team that season from nflverse's weekly stats (a player traded midseason counts for
each team with the points he scored there), with the franchise named as it was then (St. Louis Rams to 2015, San
Diego Chargers to 2016, Oakland Raiders to 2019). Checked: every weekly row of every season re-adds to its points with
the stated rules, and famous seasons match their published totals (Cooper Kupp 2021: 439.5 PPR; Christian McCaffrey
2023: 391.3). game.json is built once and rebuilt only when a season finishes.live_update.py into live.json (key pbp), so the page shows it
even where ESPN can't be reached directly. A part ESPN hasn't sent yet is left out rather than guessed.teamId=) also picks your team. ESPN's service
answers this site for public leagues. A private league needs your ESPN sign-in, which browsers keep to ESPN's own
pages, so ESPN answers other sites as if you were signed out (a reader's private league failed this way in October
2026, although he was signed in). For those, Private league? under ESPN has three steps: open your league's data
on ESPN (BFF builds the link from your league ID), copy everything, paste it and Import. On a computer there is also a
one-click bookmark: drag "BFF: import from ESPN" to the bookmarks bar, open your league on ESPN and click it; it reads
the league on ESPN's page and opens BFF with it after the # in the address (that part never leaves the browser), and
finds your team from your ESPN sign-in. BFF asks before a bookmark replaces a different saved league. A private league
doesn't refresh on its own: bring it in again after trades (the power rankings say so and link to the steps).
Sleeper: type your username (pick a league if you have several; your team is found for you) or the league ID.
Yahoo, NFL.com, CBS and the rest: Yahoo's data needs an approved app login and does not answer websites (checked),
so these are read from what you copy: select all on a team's roster page (or a page with every roster), copy, paste,
"Read page", repeat per team, Import. The page finds every player (also "J. Allen" style names with a team or
position nearby, and names glued to team codes), splits a page with every roster into teams, and guesses each team's
name (the line above its record); every name can be fixed and missing players added before the import. Screenshots
work on phones: each one is cleaned up (enlarged, turned into black text on white) and read in the browser with the
open-source Tesseract.js (version 6.0.1, loaded from jsDelivr only when used, about 6 MB the first time); a test with
14-player rosters (light and dark mode, full and "J. Allen" names) found every player and the team name. The typed
format still works: one line per team, "Team name: Player, Player, ..."calls_update.py. Start and
sit calls keep updating until that player's game kicks off, then lock; buy and sell calls lock at the week's first
kickoff. After the games each call is graded with official stats: a must start hits when he finishes as a starter
(top 12 QB, 24 RB, 30 WR, 12 TE in PPR), a must sit hits when he finishes outside those spots, and buy lows and sell
highs are graded on the player's next 3 games. Buy and sell calls get the same common-sense checks as the
Trends page (see Common sense below). A season scorecard shows the hit rates next to honest yardsticks
(starting the best season averages; all usual starters). Weeks 2-5 of 2026 were rebuilt
(one time) from the model of the day using only pre-game information and are labeled "rebuilt"; everything from week 6
on is saved live.research/brain/), that sorts a question into 28 kinds (start/sit,
outlook, trade, trade value, keep or drop, waivers, rankings, league leaders, stats, injury status, news, team,
defense matchups, game predictions, props, parlays, touchdown scorers, lineup, roster help, weekly calls, how BFF
works, football rules and terms, greetings, thanks, small talk, venting, complaints, off-topic). On 94 hand-written
questions it never trained on it picked the right kind 96% of the time. The voice (research/brain/voice.py,
shipped inside brain.json) makes it talk like Ben: a short human opener that fits the answer (an easy start/sit
call, a coin flip, a trade that tilts your way or one to avoid, bad injury news), a follow-up on about half the
answers, never the same opener twice in a row, small talk ("how's it going?", a football joke), sympathy plus real
help when someone vents about their team, and owning a miss when someone says a projection was wrong (with the
accuracy record). The numbers, verdicts and reasons always come from BFF's own functions. Asked whether it is a
person, it says plainly that it is a bot Ben built. Names, teams, positions and numbers are recognized before it reads the question, so it learns how
people ask, not who. A football knowledge base (61 topics: scoring, overtime, the dynamic kickoff, penalties,
positions and coverages, the playoff format, the draft, rosters, injured reserve, the injury report, fantasy terms,
analytics terms like EPA and CPOE, betting terms, special teams, eligible receivers, fantasy scoring, records and 2026 dates) is searched by meaning. It remembers the
conversation ("what about Gibbs?", "is he playing?", "why?", "rest of season?"), and asks instead of guessing:
"Allen" gets "Which Allen do you mean?" with buttons, and an unclear question about one player gets the two most
likely readings as buttons. Answers use the site's own numbers: start/sit, trades, trade ideas for a team you type
in, lineup and waivers, stats and game logs, injury status, news and BFF Originals, rankings, league leaders, team
info and depth charts (team nicknames or cities: "the backup running back in Detroit"), standings and division races,
schedules and strength of schedule (next 4 weeks, rest of season, fantasy playoffs), defenses ranked against each position,
defenses to stream, handcuffs, trade targets by position, game predictions, parlays and props (21+), bios (height,
weight, college, draft), every Super Bowl back to Super Bowl I (by number, Roman numeral, year or team), and how BFF
works. It says plainly what it does not track (weather forecasts, award odds, coaching staffs) and recognizes when a
name is not a current player ("Jerry Rice" is not Jerry Jeudy).BFF's own model (version 5, ff_feat.py + ff_model.py): gradient-boosted decision trees trained on every QB, RB,
WR and TE game of the 13 completed seasons before this one (2013-2025 in 2026: about 72,000 player-games), retrained
every run. Two kinds of trees are averaged: one predicts total PPR points, the other predicts each scoring stat
(passing yards and touchdowns, interceptions, rushing yards and touchdowns, catches, receiving yards and touchdowns),
which are then added up with the scoring rules. Each is averaged over 3 random seeds, and each stops training when a
held-out slice stops improving, so it does not memorize noise.
What it reads (all known before kickoff): usage (expected points from targets and carries by depth and field position, snap, target, carry and air-yard shares, red-zone and goal-line chances), production (season to date, recent form fading by half every 2.5 games, the last two games, last season and the one before; rookies start from what players drafted in their range scored), quarterback stats (attempts, yards, touchdowns, EPA, completion % over expected, sacks, rushing), the team's pace, pass rate over expected, efficiency and scoring, the opponent's efficiency against the pass and run and the fantasy points it allows to the position, the Vegas spread, total and implied team total, home, dome, rest, division game, the depth chart, his own injury report and practice participation, and teammates ruled out. Weeks ahead without betting lines use each team's points scored and allowed so far instead.
Tested out of sample: every season from 2021 to 2026 was predicted by models trained only on earlier seasons
(research/bt_weekly_v5.py). On the players the FantasyPros experts ranked who played, the model alone ranked
players 0.622 in 2023-25 (experts 0.631) and 0.631 in 2026, tying the experts (0.631); the old model alone was
0.601. In the season it is tested on each run (2025 in 2026), against a season average shrunk toward last season:
ranking accuracy QB 0.548 (baseline 0.423), RB 0.754 (0.694), WR 0.674 (0.625), TE 0.647 (0.598).
The weekly stack of four forecasts (new): next week's projection stacks BFF's model with the FantasyPros weekly
expert consensus and ESPN's and Sleeper's public projections. Within each position, every forecast becomes a z-score
(spreads above that week's average) among the players the experts ranked: model 30%, experts 10%, Sleeper 30%,
ESPN 30%, re-weighted over the forecasts a player has. The k-th strongest player gets the k-th highest of the players'
average projected points (BFF, Sleeper, ESPN). The rule is in ff_stack.py. ff_produce.py fetches ESPN and Sleeper every run and falls back to
the numbers proj_compare.py saved; a site must project at least half of a position group to be used, and with
neither site the old tested mix (40% model, 60% experts) is used. The weights were chosen on 2020-22 only
(), so 2023-2026 are a clean test.
Numbers alone can be silly: a backup running back who piled up usage while the starter was hurt looks like a buy low right when the starter comes back. BFF checks for that and a few other things, and every check was tested on 2020-25.
ff_produce.py, research research/bt_fillin.py): a back or receiver who took a starter's snaps (40%+
of them, and 15+ points more than he played beside the starter) while that starter (a .55+ snap share for RBs, .70+ for WRs and TEs
in his best 2 of his last 3 games, or first on the depth chart) missed 1-6 games, and who ranks below the starter
(on the depth chart; without one, by targets plus carries). Once the starter was back, BFF's model over-projected such
fill-ins 73% of the time: they scored 79% (RB), 80% (WR) and 75% (TE) of their projection. So each of a fill-in's
weeks is scaled by that share, in proportion to the chance the starter plays that week (2023-25 check, fit on
2020-22 only: average miss 3.82 -> 3.44 points, bias +1.37 -> +0.13). The starter's own projection needed no change
(100-104% of it). A starter who has not played yet this season (suspension, exempt list) counts too: Josh Jacobs
coming back makes Pierre Strong a fill-in. Example, week 7 of 2026: Dameon Pierce while Saquon Barkley returns.research/bt_commonsense.py): no buy low on a fill-in whose starter is back or due back
within two games, on two games played, on a player whose snaps are falling (last two games under 75% of his season
share), or on a player whose usage is already top 6 at his position (that usage tends to come back down). With the
checks, buy-low calls were right 69% of the time (60% without; the calls they drop: 44%). A fill-in still scoring 8+
a game when his starter returns is a sell (68% of them scored less from then on, 1.7 fewer a game). Studs (top-6
usage) running hot stay off the sell list: they cooled off by 4.2 a game but still averaged 17.9. The Trends page shows
who was left off and why, and the player card, the dashboard, Ask BFF and the weekly calls use the same rules.0.98 x 0.985^weeks-away (the chance he is still
active) times the expected points above that level, allowing for weekly ups and downs (a bell curve with a spread of
half the replacement level), so fringe players keep some upside value. Byes, injured reserve and "out" this week come from the weekly projections.articles.py)Data-driven fantasy articles written for BFF and published on their own, mixed into the News tab (gold "BFF Original"
label, lead story while fresh, their own panel, a Home card, and on each mentioned player's card). Each opens in a
full-screen reader with linked players, a byline, the time, update notes and sources, and has its own link
(#news/a/<id>).
Written like an analyst, not a stat sheet: each player's game opens with the verdict ("stole the show", "was a letdown", "did his job"), then the line in plain words ("caught all four of his targets for 130 yards and a touchdown"), then what it means (the points against BFF's projection, a season high, a bell-cow workload or a committee, a true number-one target share). Scoring plays read as sentences ("a 43-yard field goal by Cairo Santos", "Caleb Williams' 24-yard touchdown pass to Luther Burden III"), and previews explain the Vegas line in words (a double-digit favorite, a low total that points to a grind, a shootout-level total) instead of listing it. Every number is still the data's own. A writer change rewrites the articles still in their window (writer version 7).
| Article (Eastern time) | When it publishes |
|---|---|
| Thursday outlook (records and form, Vegas line, each team's fantasy outlook, injuries, start 'em / sit 'em, best bets, BFF's pick) | Thursday 8 AM, updated until kickoff |
| Thursday recap (scoreboard, how it happened, a fantasy breakdown for each team, stock up and down, waiver wire, looking ahead) | Friday 7 AM |
| Injury report (ruled out or doubtful and who moves up, still on injured reserve, questionables with kickoff times, quarterback situations) | Friday 5 PM, until the first Sunday kickoff |
| Sunday outlook (things to know, start 'em / sit 'em, game-by-game capsules, best bets of the day) | Saturday 8 AM, updated until kickoff |
| Sunday recap (every score, the top performances, game by game, stock, injuries to watch, early waiver look, what is still to come) | once the afternoon games are final, before the night game |
| Sunday night recap | after the night game is final |
| Monday outlook and Monday recap | Monday 8 AM (updated until kickoff), and after the Monday game is final |
| Fantasy landscape (the week's MVPs, risers, fallers, injury fallout, waiver priorities, buy low / sell high, byes and best matchups ahead, BFF's calls scorecard) | Tuesday 10 AM, once the week is complete |
| Other game days (Saturday games late in the season, holidays, international games) | outlook by 8 AM that day (or 10 hours before the first kickoff, if that is earlier), recap 7 AM the next day |
| Breaking news (a relevant player ruled out or doubtful for his next game, out for the season (with the diagnosis when the headline names it), placed on or activated from injured reserve, traded or released) | on the next run after the news reaches BFF (every 10 minutes for headlines, every 3 hours for the official data) |
articles.py and save articles.json with the
page, so new articles appear on the live site within 10 minutes of their time with nothing for you to do. If the
writer ever fails, the rest of the run still saves, the 3-hour run is marked failed at the end (GitHub emails you)
and status.json shows "articles": "failure". (October 2026 fix: a game not started yet, which has no score in
live.json, crashed the writer, so no new article reached the live site for a day; the Thursday recap and the
stories behind it publish on the first run with the fix, and a test now covers it.)WV in articles.py), a recap or Tuesday landscape still in its
window is rewritten once with the fix (same link and publish time), and a "ruled out" story whose player is then
reported out for the season becomes that season-ending story in place instead of a second article.ANTHROPIC_API_KEY (an API key from console.anthropic.com,
billed to your Anthropic account) and Claude rewrites each draft into a polished article (credited "BFF AI Desk
(Claude)"). Claude gets only the draft and a fact sheet; if its version contains any number that is not in them, it
is thrown out and BFF's own text is published instead. Optional variables: (default
) and (the most Claude calls in 24 hours, default 30; after that BFF's own text is
used). A typical week needs about 40 calls; a Sunday preview sends about 12,000 characters (roughly 3,000 tokens)
and gets up to 4,000 tokens back, a breaking story about 2,500 characters (under 1,000 tokens). See current prices at
.Without this the site works exactly as before: everything a visitor links or stars is saved in their own browser. With it, the account button (top right) lets visitors create an account and get their leagues, team, favorites and settings back on any device. Creating one requires ticking a box agreeing to the Terms of Use and Privacy Policy (both open from the account panel and the footer). Accounts are stored in a free Google Firebase project that you own:
firestore.rules from this kit, and Publish. (These
rules let each signed-in person read, save and delete only their own data.)firebaseConfig snippet it shows.BFF_FIREBASE and paste the snippet. Optional variables: BFF_TERMS_STATE (the state whose law governs the terms;
California if not set) and BFF_CONTACT (an email address or link where people can reach you about their data).The Firebase web config is not a secret (it is meant to be in web pages); the Firestore rules are what protect the data. The free plan allows 50,000 reads and 20,000 writes a day, far more than a personal site needs. People can delete their account and its data from the account panel. On the claude.ai copy, the visitor's Claude sign-in is the account and the data is saved with the page itself, private to each person. The Terms of Use and Privacy Policy are a plain-language starting point, not legal advice: have a lawyer review them if the site grows, and note that no terms can stop every lawsuit; they limit liability, require individual arbitration and rule out class actions where the law allows.
fetch_data.py reads the NFL schedule to decide the season: in progress, about
to start, or (after the last regular-season game) the next one. The model always learns from the 13 latest completed
seasons and tests on the most recent, so each finished season joins on its own; nothing is tied to a year.health.py writes status.json (which steps worked, the season, the last
good build, and which forecasts the weekly stack used at each position: m+r+s+e = BFF's model, the experts, Sleeper
and ESPN; fallback = neither site answered, so the tested 40/60 mix ran). It changes at least once a day, which also keeps GitHub from pausing the scheduled jobs (GitHub pauses
them after 60 days without activity in a public repository).requirements.txt (the newest versions install if those
ever stop installing); each run checks the page before saving and keeps the last good version if anything fails;
missing or late data files are skipped and picked up on the next run; GitHub emails you if a run fails..cache); after that each run downloads only the last five
seasons, and those completed-season files are kept for a week in a GitHub cache. Every player's games are projected in
one batch (about 4 minutes a run). The Python libraries come from pip's cache.art/ (written by
assemble.py from app.css), so the page is about 600 KB smaller and the pictures stay in the browser's cache. The 10-minute job reads ESPN's game pages only when they can
have changed: live games every run, games not yet started every run in the last 4 hours before kickoff (inactives),
every half hour within two days (practice reports) and every 2 hours before that.ff_lines.py)site_stats.py), put through a rule fit
to real posted lines: receiving yards 0.916 x, receptions 0.975 x + 0.15, rushing yards 0.843 x + 0.112 x the player's
season average - 3.5, passing yards 0.404 x + 0.250 x his season average + 0.218 x the market x (Vegas implied team
total / 22 - 1) + 75.5 (rounded down to .5), and passing touchdowns 0.5 below a projection of 0.95, 2.5 from 2.3, else
1.5. Books post 0.5 passing TDs for low-scoring passers (Cam Ward, Daniel Jones, Malik Willis, J.J. McCarthy in week 7);
the old model-based TD lines said 1.5 for them. BFF's own model stays out of the line so the picks can test it.Ask BFF's smartest mode runs on BFF's AI server: a small program (ai/worker.js) on Cloudflare's free tier that runs
an open AI model with Cloudflare Workers AI. No Claude, no OpenAI account, no cost: Cloudflare's free plan includes
10,000 "Neurons" a day (reset at 00:00 UTC), roughly 50 to 90 questions a day with gpt-oss-120b. When they are used up
the server says so and Ask BFF gives classic answers until the reset; nothing is ever charged on the free plan.
workers.dev address).CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN with the two values.ai.json and rebuilds the page. Ask BFF's header then says "BFF AI".How it is kept safe and cheap: only your site's own pages may call the server (it reads the site address from the
repository, plus CNAME if you use your own domain), the server adds BFF's rules itself (football only, facts only;
the page can't change them), each address gets 12 requests a minute, messages are size-capped, and nothing is stored.
To answer more questions a day, swap MODEL and FALLBACK_MODEL in ai/wrangler.toml (Qwen3 30B fits about four
times as many) and run the workflow again. To switch the server off, delete ai.json (or the Worker in Cloudflare).
Chrome's built-in AI needs no setup at all: on desktop Chrome 148 and later, Chrome has its own AI model (Gemini
Nano, through the Prompt API). When it is already on the visitor's computer, Ask BFF uses it as soon as it opens; when
Chrome has not downloaded it yet, AI settings offer to turn it on (Chrome downloads it once for every site, about 3-4
GB). It runs on the visitor's computer, so nothing is sent anywhere. Order: BFF's AI server, then Chrome's built-in AI,
then the on-device AI below, then the classic Ask BFF.
On-device AI needs no setup: in Ask BFF's AI settings a visitor can turn it on; the browser downloads an open Qwen3
model once (about 1 GB on phones and smaller computers, about 2.5 GB on bigger ones) and runs it with WebGPU (recent
Chrome or Edge on a computer, Chrome on Android, Safari 26 and later). It is used when the server is not available.
Tested: tests/aiworker.mjs (the server with a stand-in for Cloudflare: origins, limits, the free allowance, the
fallback model, reply shapes) and tests/aitest.py (the page with a stand-in server and engine: the facts sent, the
number check, lookups, follow-ups, the league, the fallbacks, escaping, the device check). The real models answer only
on your site once it is switched on.
If Claude has access to your repository it pushes updates itself. To do it by hand, upload (replace) these files:
template.html, app.css, index.html, the art/ folder, brain.json, kb.json, articles.py, ff_build2.py,
ff_feat.py, ff_model.py, ff_produce.py, fetch_data.py, assemble.py, check_page.py, ff_lines.py,
parlay_update.py, calls_update.py, ff_news.py, news_update.py, live_update.py, proj_compare.py, ff_stack.py, site_stats.py,
ff_kdst.py, game_build.py, game.json, sync_sleeper.py, health.py, requirements.txt, firestore.rules, .gitignore, .nojekyll, README.md, the
research/ folder (the accuracy tests and Ask BFF's training; optional, never run by the workflows), the ai/ folder
(BFF's AI server), and the three workflow files in .github/workflows/ (update.yml, scores.yml, ai.yml). calls_backfill.py is retired and can be deleted. Leave
your *_history.json, lines.json, parlays.json, bet_inputs.json, articles.json, news.json, news_state.json,
live.json, site_stats.json and proj_compare.json alone; your workflows keep those up to date. Then run Update Gridiron Fantasy once (the first
run builds the model's 13-season feature tables, about 6 extra minutes).
Optional: add the ANTHROPIC_API_KEY secret for Claude-written articles (see BFF Originals).
gridiron-fantasy, choose Public, and create it..github
folder and .nojekyll file. Click Commit changes. Then Add file > Create new file, name it .nojekyll,
leave it empty, and commit (it tells GitHub to publish the files as they are)..github/workflows/update.yml, paste in the
contents of update.yml from this kit, and commit. Repeat for .github/workflows/scores.yml.
(Already set up? Replace both files with these new versions.)main and / (root), then Save. Your site will be at
https://YOUR-USERNAME.github.io/gridiron-fantasy/.SLEEPER_LEAGUE_ID and paste the number.research/bt_sites.pyHead to head on the same players (every player-game ESPN, Sleeper, the experts and BFF all projected; ESPN's and Sleeper's past projections fetched from their public APIs, ESPN re-scored to full PPR):
| 2023-25 (11,636 player-games) | Ranking accuracy | Better of two starters | Avg. miss (PPR) |
|---|---|---|---|
| FantasyPros experts | 0.565 | 62.1% | (ranks only) |
| ESPN | 0.565 | 62.3% | 4.75 |
| Sleeper | 0.561 | 62.1% | 4.86 |
| BFF model alone | 0.556 | 61.6% | 4.82 |
| BFF projection (stack) | 0.577 | 62.7% | 4.71 |
2026 weeks 1-6 (1,631): experts 0.552, ESPN 0.554 / 4.81, Sleeper 0.560 / 4.81, BFF 0.574 / 4.76. The stack beat every other source on all three measures in both periods and ranked better than each of them at every position; quarterbacks gained most (0.381 vs 0.369 in 2023-25, 0.390 vs 0.366 in 2026). Over every player the experts ranked (13,119 player-games in 2023-25): stack 0.641, previous 40/60 mix 0.635, experts 0.631, model alone 0.622; average miss 4.46 vs 4.53; 2026: 0.648 vs 0.636 vs 0.631. Better than the experts in all six seasons 2021-2026.
Why 10% better is out of reach (asked for, tested, not possible): the lead over the best other source is about 2% in ranking accuracy and under 1% in average miss. Weekly scoring is mostly noise no forecast can see. Hindsight check on 2023-25: ranking each player by his real average in all his other games that season, future games included, scores 0.531 with a 4.92-point miss, worse than ESPN's pre-game projection (0.561, 4.75). The sources agree with each other at 0.90-0.94, so their misses are mostly the same misses; weights picked on the test answers themselves reach only 0.578 (the stack: 0.577); ridge, gradient-boosted and per-position stacks did no better. Only perfect knowledge of each player's usage in that very game reaches 0.718. A 10% lead would need 0.622.
Live head-to-head with ESPN and Sleeper (proj_compare.py, every 10 minutes): before each game BFF saves ESPN's
and Sleeper's public projections next to its own, freezes them at kickoff, and after the games grades every source on
the same players with official stats (ESPN's projected stats are re-scored to PPR so the scoring matches). The Learn
page shows the running results; this replaces the old limitation that their past projections cannot be tested.
Later weeks and the rest of the season: each remaining game is projected with its own opponent and team totals; a player's rest-of-season level is their average (2023-25 trade-value backtest: better than the separate rest-of-season model the old version added). The page's Why? breakdown shows the matchup, Vegas, teammates and expert-mix effects as their own bars, and each card shows the expected stat line behind the projection.
Injuries: each player's status for his next game comes from ESPN's injury list for that game (live_update.py
saves it every 10 minutes until kickoff), else the official injury report, else what the practice reports imply
before Friday's game statuses. Measured on every 2021-2025 report against who really played (QB/RB/WR/TE with 3+
games): Out played 0.1% of the time, Doubtful 0.4% (both projected 0), Questionable 61% (full projection, tagged Q);
missed last week and not practicing 1.3% (projected 0, tagged as expected out), limited 40% (tagged Q). A report from
a game already played never tags anyone. Later weeks for a player out now use the measured return curve (39% the
next week, 69% after two, 85% after three, about 90% later, compared with healthy players) or ESPN's estimated
return date. When neither ESPN nor the official report lists a player for his next game yet, a timeline reported in
the news ("out at least three weeks", "out for the season", "to miss the remainder of the season") keeps him out until his team's game that many weeks after
the story (labeled "expected from news reports"); a headline about a return is never read as a new absence. The page
also lays ESPN's newest list over the data between runs.
Who is in the player pool: only players on an NFL team. Each run checks the newest weekly team rosters
(then the season roster, then the depth charts): a player who is not on a team's roster as active, reserve,
inactive, practice squad or exempt is a free agent and left out (October 2026: Tyreek Hill, who has no team, and four
others). A player on the commissioner's exempt list is projected 0 and shown as "not allowed to play", until a
news report says he has been reinstated or is on track to play (then he is listed Questionable for his return; the
reinstatement is kept in news_state.json after the story leaves the news feed, until the roster catches up; this
fixed Josh Jacobs showing as out a week after his Oct 16 reinstatement). Fullbacks are no longer
ranked as running backs.
No usage this season: a running back, receiver or tight end who has been on his team at least 3 weeks, never on a reserve list, without one fantasy stat and not a depth-chart starter is capped near what such players really score (measured on 2021-2025, 1,160 players: RB 1.23, WR 1.08, TE 0.58 PPR points the next week), until he gets touches.
Questionable and then inactive: a player listed Questionable who then did not play is treated as having missed the game. With no injury report for his next game yet, his chance of playing follows what really happened to such players (2021-2025, 558 cases: 52.5% played the next game and 62.5% the one after, against 29% and 46% after an Out).
Starting quarterbacks: only a team's expected starter is projected (the best quarterback on the depth chart who is not out). Backups are projected 0; when the starter is out, the fill-in gets that game and, later, the chance the starter is still out. When the starter missed his last game and his team has no injury report for the next one yet (Monday to Wednesday), he is projected for the measured chance he plays and the next quarterback for the rest (week 7 of 2026: Baker Mayfield about 39% of his projection, Jalon Daniels about 61% of his), so the team's quarterback total stays whole; once ESPN or the official report sets a status, the normal rule takes over on the page right away.
Games a player left hurt still count toward his rates: we tested leaving them out (2025 test season) and next-game and rest-of-season errors got slightly worse, because players coming back from those injuries really did score less.
Half PPR and standard: a player's catches are removed in proportion to how much the projection regressed his scoring (tested on 2023-2025: lower error than removing his full catches per game).
Custom scoring: each player carries his own stat mix per game (passing yards, touchdowns and two-pointers, interceptions, carries, rushing and receiving yards and touchdowns, catches, first downs, fumbles lost): this season's games, shrunk toward last season's mix (or his position's average) as if he had 3 more games of it. His PPR projection is converted by the ratio of his points under your rules to his PPR points on that mix (limited to 0.3x-3x). Example, week 6 of 2026: with TE premium (1.5 points per tight-end catch) Brock Bowers rose from 11th to 5th by BFF value; with 6-point passing touchdowns quarterback projections rose about 11%. In custom leagues the expert consensus (which is scored in PPR) counts 10 points less.
research/bt_value_v5.pyBFF_AI_MODELclaude-sonnet-5-5BFF_AI_MAX_DAYlive_update.pylive.jsoncontent-visibilityresearch/bt_props_more.py): pass attempts, completions, interceptions, rush
attempts, targets, anytime TD and 1st-half TD, from the same ESPN and Sleeper projections (site_stats.py now also
saves their pass attempts, carries and targets). Pass attempts sit at the projection (its whole number + .5);
completions are projected attempts times the passer's completion rate (this season, last season at half weight, pulled
toward the league's 64.5%); rush attempts 1.003 x - 0.259; targets 1.025 x (few books post targets, so the line sits
where 2025's overs hit as often as receptions'). Against week 7's real lines, each rule scored on players it never saw:
pass attempts 1.08 from the books' (the books 1.25 apart), completions 0.84 (0.61), rush attempts 0.95 (0.48).
Interceptions and touchdowns are chances of 1 or more with fair odds: interceptions from projected attempts times the
passer's rate pulled toward the league's 2.17% (3 points from the books' no-vig price on average, 26 passers);
touchdowns from projected carries and targets at each position's touchdown rate, the Vegas team total and the player's
own scoring history, fit on 2025 and checked on 2026 games it never saw (expected 23.9% of players to score, 22.4%
did); 1st-half TD takes the share of each position's touchdowns scored before halftime (about half). None get picks:
at the count lines no side hit 55% in both 2025 and 2026, and touchdown and interception prices are for comparing with
your book. The bets page filters by prop type and lists the week's top touchdown chances; player cards, the prop
checker and Ask BFF show them too; parlay leans stay on the tested yardage stats.site_stats.json is saved only when
a number changed.parlay_update.py builds the best 2-, 3- and 5-pick parlays from tested picks only (one leg per game, no
questionable players), locks them at the week's first kickoff, and grades them from official box scores.