Repository navigation
ESPN: approval-gated roster edits (add/drop) - #176
Merged
Merged
Conversation
This was referenced Oct 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Approval-gated roster edits (add/drop) for the ESPN module — the module's second write, gated harder than lineup moves because it changes who is ON the roster. Server-side propose → approve, following the gate shape Resy established (and enforcing it in the backend, not just the agent layer):
POST /espn/roster/edits {add_player_id?, drop_player_id?}(a swap is both; add-only and drop-only work too). Validates against the live league: add target is a free agent in the league-scoped kona pool (waivers.py's fetch, reused), drop target is on the account's own roster, post-edit roster is legal under the league's slot counts with the lineup planner's IR accounting. Persists apendingedit (newespn_roster_editstable, migration 0014) with the validated player snapshot, whether the add target is on waivers, a human preview, and a 30-minute expiry. Submits nothing to ESPN.POST /espn/roster/edits/{id}/approveexecutes the pending, unexpired edit as ONE ESPN transaction with both moves. The edit is claimed atomically (pending → executing) before submitting, league state is re-validated at approve time (stale edits fail closed), and the outcome is recorded on the row:executed/waiver_pending(ESPN parked it as a waiver claim; bid 0, no FAAB in v1) /failedwith ESPN's error. Double-approve → 409, another account's edit → 404; never double-submits.GET /espn/roster/edits(list, optional?status=) andGET /espn/roster/edits/{id}: the audit trail, like lineup runs.MCP mirrors the routes (
propose_roster_edit,approve_roster_edit,get_roster_edits) as two separate tools — the pair is the gate. Tenant isolation is the module rule throughout: token → account → league/team, every query scoped byaccount_id.Nothing is wired into the scheduler or lineup automation; the waiver digest stays recommendations-only. Edits happen only on an explicit propose + approve.
Transaction format assumption
The fixtures don't cover ADD/DROP submissions, so the shape is derived from the
mTransactions2READ payload this module already handles (seelogic.get_transactionsand live league history): a swap is one transaction oftype: "FREEAGENT"— or"WAIVER"withbidAmount: 0when the add target is on waivers — whose items are{playerId, type: "ADD", fromTeamId: 0, toTeamId: <team>}and{playerId, type: "DROP", fromTeamId: <team>, toTeamId: 0}(team 0 = free-agent pool), in the same envelope the proven lineup write uses. The client stamps items itself (post_roster_transaction) so no caller can smuggle another move type; trades have no code path. First live exercise will be a supervised integration test after merge/deploy.Also
beli_schema_contract.sql+ contract version 13 → 14 (the runner requires version == latest migration). Two existing version asserts intest_espn_migration.pyupdated, plus new schema/contract coverage.docs/espn.md: roster-edits section + route table (and the stale "there never will be add/drop endpoints" line corrected).swap/swap-approve/swapsverbs and CONNECTOR.md documents the gate discipline.Tests
tests/test_espn_roster_edits.py(19 tests): propose validation failures (rostered add, unknown add, unowned drop, illegal roster size), add-only with an open slot, snapshot + preview, approve = single transaction with both moves, waiver-pending recorded, double-approve rejected, expiry rejected + marked, submit failure recorded, stale-at-approve fails closed, tenant isolation, client wire shape (FREEAGENT/WAIVER stamping), HTTP round-trip + error mapping.