- Provisional rows were being treated as finished by
updateProbabilitiesAfterResult, whose finishedMap filtered on
finalPosition alone. Entry floors made that fire for the whole seeded
field: on the first match result, AFL seeds 1-6 would each be pinned
to 100% at their floor position and dropped from the ICM recalc,
zeroing the championship odds of six teams that had not played. Filter
partial rows out of finishedMap so they stay in the unfinished set.
Finalized 0-position eliminations still finalize as before.
- generate-bracket recalculated standings only inside
markEliminatedAndAnnounce, which no-ops when nothing was eliminated.
A season whose participants exactly equal the bracket field would
never surface the floors in teamStandings.totalPoints. Recalculate
explicitly in that case (skipDiscord: seeding is not a result).
- applyBracketEntryFloors upserted unconditionally, so regenerating a
bracket mid-tournament could downgrade a team already sitting on a
better placement. Read existing placements first and only write when
the floor improves on what a participant already has; position 0 is
eliminated, not a placement, so it never blocks a floor.
- Relaxing the reprocess guard to matches.length made the season-wide
deleteParticipantResultsBySportsSeasonId reachable with zero completed
matches, wiping other events' placements with no replay able to
rebuild them. Skip the wipe when there is nothing to replay; entry
floors and elimination marking are additive and need no wipe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkhdcbUCvramxJdVdoBsKd
An AFL top-4 seed has the double chance from the moment the bracket is
drawn: lose the Qualifying Final, lose the Semi-Final, and you still
finish in the 5th-6th tier. Nothing was awarding that. Seeds 1-4 sat on
0 fantasy points until their first game resolved, which understated
every roster holding them.
Add an `entryFloor` field to BracketRound for floors a seeding locks in
before anyone plays, plus `applyBracketEntryFloors` to bank them, wired
into both bracket generation and reprocess-bracket. For afl_10 that is 5
for the Qualifying Finals (seeds 1-4) and 7 for the Elimination Finals
(seeds 5-6). Every write is provisional, so a real result supersedes it,
and upsertParticipantResult's never-un-finalize guard leaves finalized
rows alone.
Two related floors were also wrong, both from the generic
"winning into a scoring round means top-8" default in
nonScoringWinnerFloorFor:
- Qualifying Finals winners banked 5 when the bye to a Preliminary
Final guarantees the 3rd-4th tier. progressive-floor-scoring.test.ts
already asserted 3 here, but via an isScoring=true call the runtime
never makes.
- Wildcard winners banked 5 when winning only buys an Elimination
Final, whose losers are the 7th-8th tier — an over-award of a full
tier until that game was played.
Both are now explicit nonScoringWinnerFloor values on the template.
reprocess-bracket now applies entry floors after wiping results and
before replaying matches, and no longer refuses a bracket with no
completed matches, so setting a bracket and reprocessing awards the
guaranteed points. It stays silent on Discord as before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkhdcbUCvramxJdVdoBsKd