Skip to the story
← Enter the stadium

CREATIVE & PLAY / FANTASY FOOTBALL

I lost at fantasy.
So I built an AI manager.

One bad week. A wildly disproportionate response. And an integration that was, apparently, harder than “that can’t be that hard.”

WorkingFictional demo
UnfinishedESPN integration
PublicCode & handoff
The 84-second story · Open video

A completely reasonable response to losing.

I got blown out at fantasy football. Apparently my response was to build an AI-assisted manager instead of developing emotional resilience.

The idea: a local dashboard that could suggest waiver pickups and trades, explain the choices, ask me to approve an exact move, and learn from what happened. That can’t be that hard, right?

What actually works

The disconnected dashboard takes supplied projections and league rules, evaluates fictional pickup/drop options, and lets me review an exact proposal before approval. It shows pending and simulated outcomes, with history that survives a reload within the temporary demo session.

The separate comparison CLI compares legal lineups for six supplied pickup/drop alternatives against holding. It separates near-term and later value. The saved example is policy-sensitive: a management preference is needed, not a winner declared. This comparator is not the dashboard’s ranking engine.

These are deterministic evaluators. AI helped build the software; the demo does not establish a predictive advantage or a live fantasy recommendation.

The machine gets most of the way there.

Real ESPN roster and availability reads worked. On September 19, 2026, the browser experiment also entered the intended confirmation context. Preparation stopped before a usable approval card. Zero transaction sends occurred.

The two preparation checks that stopped it

The adapter expected roster.players to be a native JavaScript array. Saved client source made a legacy observable collection plausible. That is an unvalidated representation mismatch, not evidence that the drop player was missing.

A pending-transactions read returned HTTP 200 and parsed, but its required array check failed. The retained diagnostics could not distinguish an absent field from a non-array value. An empty fallback could not prove a complete, conflict-free result.

The video’s “non-JSON” explanation is not the established cause. The actual stopping point was those preparation checks. A real submission and attributable outcome remain unverified.

Things I learned the expensive way.

A convincing local demo and a working provider integration answer different questions. Passing simulations can’t close that gap.

Check the provider’s actual data shapes and current build before building more machinery around an assumption. Keep enough diagnostic detail to tell competing causes apart.

Compare a pickup with a legal lineup and with doing nothing. A promising name is not automatically an improvement.

The next useful milestone is one defensible real decision, with alternatives and limitations visible. My fantasy record has not been rescued by the number of tests.

Take the useful part.

The original code is public under MIT. You can run the fictional demo without an ESPN account, inspect the unfinished integration, or pick up the next useful question in the handoff.

Run the disconnected demo locally

Clone the repository, then run these commands. The export was verified on macOS with Python 3.14; Linux is expected but not separately verified, and Windows support is not established.

git clone https://github.com/griffmak/griffguides-fantasy-manager.git
cd griffguides-fantasy-manager
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python -B demo.py

Open the loopback URL printed by the launcher. Read the one-time token from the temporary file it names and enter it in the dashboard. Choose Synthetic demo → Refresh data → Evaluate. Approve a fictional waiver proposal, then simulate completion or rejection. Reload to see its history. Ctrl-C ends the session and deletes its temporary data.

Use demo.py; the older server.py uses persistent state. The separate comparison CLI and its fixture are documented in the README. This website does not host the fantasy app.

Maybe you can get further than I did. At minimum, you can admire how much work I created for myself.