Run the live match in halves, and make the mid-half line-up a plan - #84
Merged
Conversation
Two things, both on the live screen. "Volgende opstelling" is gone: the button, the service call behind it and the line-up rewrite it did. What it was for was never clear at the touchline, and it was the only thing that made a quarter boundary a moment the clock had to be told about. The clock now runs in halves however the line-ups were planned — Game.NextPeriod() skips the quarter left behind inside a half already played, so the second half opens at Q3 and Q2 is never kicked off. A period with no timings costs GameMinutesReport nothing, so the half is credited to the line-up that actually played it plus the substitutions made during it. The "Changes at half-way" card stays, and is now purely a list to work through by tapping the players on the pitch above it. The order of match events could be wrong across half time. The scoreboard clock stops at the end of a half, but the minute written down counted straight on, so a goal two minutes into first-half stoppage was stored as 37 and sorted after a goal in the 36th minute of the second half — which happened a minute later. Substitutions were worse: their minute came off the raw elapsed time and ignored the scoreboard clock entirely. A minute is now a MatchMinute pair, 35 or 35+2, and comparing the two parts is chronological across the whole match. A goal stores both halves of it (new nullable-free AdditionalMinute column, no backfill — nothing left in an old row says whether a 37 was stoppage time or typed in by hand); a substitution derives its own from AtSeconds and the period it belongs to. The timeline, the result page's goal list and ScoreProgressionReport all sort on the pair. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AfrvWcNR8hMjw3ny6dMemX
The touchline screen dealt in periods: a quarters game's second line-up of a half showed up as a card beside the pitch, and the clock, the services and the model all still spoke of periods even though nothing stopped for one. A period row is now what it always was in practice — a planned line-up. The row that opens a half is the one the match is played, timed and recorded with; the row planned for the middle of a half is a plan, and nothing else. The model, the clock service and the live screen say so: CurrentOrLastHalf, LiveHalf, NextHalf, MidHalfPlan, EndHalfAsync, StartNextHalfAsync, and messages that talk about halves rather than periods. The changes due partway through a half move out of the screen and into a pop-up behind a "Changes (n)" button on the line-up card. Standing beside the live line-up, a plan reads as the state of play and invites being asked about before it is carried out; behind a button it is looked up, worked through by tapping the pitch, and dismissed. It is still admin-only, still stripped of the swaps play has overtaken, and still readable before kick-off. No schema change: what is stored is what was stored, and the minutes a season is built from are untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AfrvWcNR8hMjw3ny6dMemX
Only the two service summaries in architecture.md and patterns.md clashed: main reworded them for the goal write that now commits with its scoreline, this branch reworded them for halves. Both statements are true, so the merged lines say both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AfrvWcNR8hMjw3ny6dMemX
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.
Two commits that were pushed to this branch after PR #80 merged, so they had no pull request of their own. Opening one now.
The live match runs in halves. "Volgende opstelling" is gone — the button, the service call behind it and the line-up rewrite it did. It was the only thing that made a quarter boundary a moment the clock had to be told about, and what it did at the touchline was never clear. The clock now runs in halves however the line-ups were planned:
NextHalf()skips the quarter left behind inside a half already played, so the second half opens at Q3 and Q2 is never kicked off. A period with no timings costsGameMinutesReportnothing, so the half is credited to the line-up that actually played it plus the substitutions made during it.The model and the services now say "half" where they mean one —
CurrentOrLastHalf,LiveHalf,NextHalf,MidHalfPlan,EndHalfAsync,StartNextHalfAsync— because a period row turned out to be what it always was in practice: a planned line-up. The row that opens a half is the one the match is played, timed and recorded with; the row planned for the middle of a half is a plan and nothing else.The order of match events could be wrong across half time. The scoreboard clock stops at the end of a half, but the minute written down counted straight on, so a goal two minutes into first-half stoppage was stored as 37 and sorted after a goal in the 36th minute of the second half — which happened a minute later. Substitutions were worse: their minute came off the raw elapsed time and ignored the scoreboard clock entirely. A minute is now a
MatchMinutepair, 35 or 35+2, compared on both parts so the comparison is chronological across the whole match. A goal stores both halves of the pair; a substitution derives its own fromAtSecondsand the period it belongs to. The timeline, the result page's goal list andScoreProgressionReportall sort on the pair.The mid-half changes moved into a pop-up behind a "Changes (n)" button on the line-up card. Standing beside the live line-up, a plan reads as the state of play and invites being asked about before it has been carried out; behind a button it is looked up, worked through by tapping the pitch, and dismissed. Still admin-only, still stripped of the swaps play has overtaken, still readable before kick-off.
Migration —
20260813062317_AddGoalAdditionalMinuteadds one nullable-freeAdditionalMinutecolumn toGameGoals, with no backfill: nothing left in an old row says whether a stored37was stoppage time or a minute typed in by hand, so existing goals keep reading and sorting exactly as they do today. The second commit changes no schema.Anonymous visibility is unchanged. The live screen and the timeline stay public reads; the changes pop-up is behind the same admin check the card it replaced was.
Worth reading alongside #83, which was filed against this design: it argues the goal should carry
GamePeriodIdandAtSecondslike a substitution does, so the minute is derived rather than frozen andAdditionalMinutecan go again. That is a follow-up to this, not a reason to hold it — the bug fixed here is visible today.Checked:
dotnet build -c Releaseclean,dotnet test399/399 at the branch head. I also mergedmaininto the branch locally to see whether PR #81's changes toMatchGoalServiceandGameGoalcollide with the goal-minute work here — they merge clean and the suite stays green, but I did not push that merge, because this branch is still being written to. It is one commit behindmain, so it needs the "Update branch" button before the merge button will light up.🤖 Generated with Claude Code
https://claude.ai/code/session_013ys63zAujLcoaS7UP9F2wm
Generated by Claude Code