Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
c956f60
fix(bc-dlck): remove the decision-locked confirm on a withdrawal corr…
gitrgoliveira Oct 7, 2026
db2cb5b
fix(bc-otpl, bc-dsid): plain-text controls and a withdrawal prompt wi…
gitrgoliveira Oct 7, 2026
6b7d8ae
fix(bc-kosc): time each court's knockout after its pool phase
gitrgoliveira Oct 7, 2026
1820f5a
fix(bc-crpn): never hold a running match and an open correction together
gitrgoliveira Oct 7, 2026
f49bb52
fix(bc-htsd): the hantei side buttons pick, Finish commits
gitrgoliveira Oct 7, 2026
32d11b5
refactor(bc-crpn): name the later-matches list through scoreRowMatchName
gitrgoliveira Oct 7, 2026
30da2cb
test(bc-dlck): drop an unnecessary conversion flagged by unconvert
gitrgoliveira Oct 7, 2026
6c5f699
docs: recapture the screenshots this branch changes
gitrgoliveira Oct 7, 2026
d2f89e3
docs(bc-htsd): CLAUDE.md names the hantei pick, not submitHantei
gitrgoliveira Oct 7, 2026
2eb33de
refactor: simplify review cleanups for the six operator fixes
gitrgoliveira Oct 7, 2026
cb1a834
fix: address code-review findings on the six operator fixes
gitrgoliveira Oct 7, 2026
5232a32
fix(bc-kosc): move the knockout past a tie-break added after the draw
gitrgoliveira Oct 7, 2026
f5cab2b
refactor(bc-kosc): drop a nil-competition check no caller can reach
gitrgoliveira Oct 7, 2026
1972b66
fix(bc-kosc): a court move keeps that court's knockout after its pools
gitrgoliveira Oct 7, 2026
fcbded9
fix: name the side's colour on hantei buttons; address review findings
gitrgoliveira Oct 7, 2026
a07a157
fix(bc-htsd): hint says to cancel hantei before correcting; pin the t…
gitrgoliveira Oct 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions CLAUDE.md

Large diffs are not rendered by default.

Binary file modified docs/screenshots/mobile-encho-overtime.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/screenshots/mobile-pool-standings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/screenshots/mobile-score-editor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 7 additions & 2 deletions docs/user-guide/court-operators/recording-decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Not every bout is decided on points. The score editor records the kendo outcomes

Kiken covers two distinct situations with different consequences.

Pick the side that withdrew: no side is chosen for you, and **Record** stays unavailable until you pick one. Once you do, the button names it (for example **Record: AKA withdrew**) and a line beneath says who cannot fight again and who wins the match.

### Voluntary withdrawal (FIK Article 31)

A voluntary kiken is permanent. The competitor takes no further matches in this competition and cannot be reinstated.
Expand All @@ -20,6 +22,8 @@ An injury kiken can be reinstated later by the operator if the competitor recove

**Fusenpai** is the no-show decision: the competitor who did not appear forfeits the match.

Pick the side that did not show up: no side is chosen for you, and **Record** stays unavailable until you pick one. Once you do, the button names it (for example **Record: SHIRO did not show up**).

**Fusensho** is used in team matches when the opposing team fields a vacant position, awarding that bout to the other side. It is also the decision the app records for the match itself when you record the win for the opponent of an already-barred competitor; refer to [Matches after a competitor is barred](#matches-after-a-competitor-is-barred). When neither team has a fighter for a position, record that bout as a **Tie** instead.

A kiken, fusenpai, or fusensho awards the winner the match points without a technique, and each awarded point is recorded as a circle. This follows the FIK Regulations of Kendo Shiai and Shinpan (Article 32 and the score board appendix). The winner's score shows two circles, or a single circle during overtime, when only the one deciding point is awarded. Any point the withdrawing side had already scored stays valid and is kept on the sheet. In a team match, the sub-bouts already fought are preserved and still count toward the standings.
Expand All @@ -38,6 +42,8 @@ Encho is the extra period played when a match is level at the end of regulation

An individual knockout match cannot be finished level. While the score is tied, the **Finish** button reads **Needs a winner** instead: fight encho, then record a hantei if the competitors are still level after it.

To record a hantei, tap **Decide by hantei** and pick the winning side. Each button names the side's colour and competitor, for example **SHIRO (White) wins: Tanaka** and **AKA (Red) wins: Yamada**. Then finish the match as usual: **Finish** (on the court console, **Finish + Start Next**) and tap it again to confirm, or **Save correction** when you are correcting a result. Picking a side on its own records nothing, so a slip of the thumb between the two buttons can be put right before anything is saved.

In the score editor, open the **Overtime** control and tick **Encho started**. A counter appears so you can record how many overtime periods were fought, using the **+** and **-** buttons. The counter starts at 1 and has no upper limit. How many periods are fought, and how a match still level after them is finally settled, is the shimpan's call. That may be a judges' decision for an individual bout, or a daihyosen for a team encounter. Record what actually happened on court, however many periods that took.

![The score editor during a second overtime period: the Encho started box is ticked, the period counter reads two, and the header shows the overtime readout.](../../screenshots/mobile-encho-overtime.png)
Expand Down Expand Up @@ -101,7 +107,7 @@ If both competitors due to meet are already barred, neither can fight. In a pool

A kiken or fusenpai entered in error can always be fixed. Open the match in the score editor. It shows what is recorded, for example "Recorded: Kiken – Voluntary, Kyoto withdrew", and marks the side that withdrew with **Kiken** or **Fus.** beside its name. In a team match the result under the team names, at the top of the score sheet, names the recorded winner and the decision, and the bouts after the withdrawal are credited to the other team, even though nobody actually fought them. Pick the fix that matches what happened:

- **The wrong competitor or team was marked.** Record the withdrawal again for the other side. In an individual match, use the **Kiken – Voluntary**, **Kiken – Injury** or **Fusenpai** button in the editor's **Decision** row. In a team match, open **Withdrawal or no-show** and use the same buttons there. The side you first marked is eligible again, and the other side becomes the one that withdrew.
- **The wrong competitor or team was marked.** Record the withdrawal again for the other side. In an individual match, use the **Kiken – Voluntary**, **Kiken – Injury** or **Fusenpai** button in the editor's **Decision** row. In a team match, open **Withdrawal or no-show** and use the same buttons there. The side you first marked is eligible again, and the other side becomes the one that withdrew. Nothing else changes: a competitor with a **Fusenpai** recorded on another match stays withdrawn because of that match, and matches already closed with a fusensho because of the first withdrawal keep it, so correct each of them one by one.
- **Nobody withdrew, and the match was fought to a result.** Choose **Remove kiken**, **Remove fusenpai**, or **Remove fusensho**: the button names the decision that was recorded. The editor takes that decision off the score board: its circles go, the points the withdrawn competitor struck stay, and you enter the result as it was fought, or a draw in a pool or league. In a team match, every bout needs a result, as when you finish one. Then use **Save correction**, which asks for a short reason like any correction. The match stays finished the whole time, so the fix never needs the court and a match being fought there carries on undisturbed. **Undo** puts the recorded decision back before you save; once the correction is saved on the device and waiting to be sent, it can no longer be undone this way. In a knockout match whose bouts were tied, a representative bout decided it: use **Clear kiken and reopen** (or **Clear fusenpai and reopen**) instead, which puts the match back on the court to fight it.
- **Nobody withdrew, and there is more of the match to fight.** Choose **Clear kiken and reopen** or **Clear fusenpai and reopen**, whichever was recorded. It takes one tap and needs no reason; the editor says what it will do beside the button. A kiken or fusenpai gives the opponent the match win, so without it the match was never decided. The match goes back to in progress and the editor stays open on it. The withdrawn competitor or team can compete again. Score the rest of the match and finish it as usual.

Expand All @@ -118,7 +124,6 @@ Without removing the decision first, **Save correction** never removes a kiken o

A fix can change who goes through to later matches, so the app warns you first and you can cancel or go ahead:

- Recording the withdrawal for the other side warns you when either side has already started a later match.
- Clearing a decision in a knockout match, or removing one so that the other competitor wins, warns you when the next match has already been played. If you go ahead, that next match is reopened with its winner cleared and its points kept, so check them and take back any that no longer apply, and the editor names it once the reopen is done. A round after it that nobody has played yet stops showing its winner. If a withdrawal decided that next match, its withdrawn competitor is eligible again too.

In a knockout, a next match that is still in progress must be finished or sent back to the queue before a decision can be cleared, removed in favour of the other competitor, or recorded for the other side. Clearing and reopening also needs this match's court: if another match is running there, the app names it and offers to send it back to the queue, which keeps any score entered for it. Removing the decision does not need the court.
Expand Down
10 changes: 6 additions & 4 deletions docs/user-guide/court-operators/scoring-a-match.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,20 @@ In an engi match the flags are saved as you enter them once the match has starte

## Correct a completed result

Once a match is scored it moves to the **Completed** list on the court console, and each row carries a **Correct** button. You do not have to leave the console to fix a mistake. Correct opens the finished match back in the score editor, even while the next match is already running on the same court.
Once a match is scored it moves to the **Completed** list on the court console, and each row carries a **Correct** button. You do not have to leave the console to fix a mistake. Correct opens the finished match back in the score editor. It works while no match is running on the court. If one is, tapping **Correct** names that match and tells you to finish it or send it back to the queue first, then correct.

![The court console Completed list showing a finished match with its result and a Correct button.](../../screenshots/console-correct-completed.png)

What Correct offers depends on the format:

- For most matches, the editor reopens with the recorded scores ready to edit. Adjust the ippons, fouls, or winner, then use **Save correction**. A short reason is required so the change stays traceable. A judges' decision (hantei) given to the wrong competitor is corrected by tapping the other competitor's hantei button; the same reason is asked for first, and the new decision replaces the old one.
- For most matches, the editor reopens with the recorded scores ready to edit. Adjust the ippons, fouls, or winner, then use **Save correction**. A short reason is required so the change stays traceable. A judges' decision (hantei) given to the wrong competitor is corrected by picking the other competitor's hantei button and then **Save correction**; the new decision replaces the old one.
- For a kachinuki team encounter, the editor shows the recorded bouts with a **Reopen match** button. An encounter that ended with a withdrawal or a no-show shows **Clear kiken and reopen** or **Clear fusenpai and reopen** instead; refer to [Correcting a withdrawal recorded by mistake](recording-decisions.md#correcting-a-withdrawal-recorded-by-mistake). Reopen is immediate: one tap returns the encounter to running with its bout log intact, so you can carry on or fix a bout. No reason is needed, and none is asked for when you end the match again, whether you finish on a scored bout or record a withdrawal or a no-show. Correcting a mistake never costs you more than a tap.

Reopening puts the encounter back into play, and a court can only run one match at a time. If another match is already running on that court, the editor names it and offers two ways forward. Leave it running, or send it back to the queue and reopen in a single step with **Queue it and reopen**. The match sent back keeps any score already entered for it and carries on from there when it is started again.
While a correction is open, **Start match** does not start another match: it tells you to save the correction or leave it with **Back to court** first. **Save correction** finishes it, and **Back to court** cancels it without saving. If a match is started on the court from another screen while you correct, the correction stays open and the running match is listed above it until you save or go back to the court.

If the correction changes who won a knockout match, the later rounds update to follow the new winner. If the next match, or the bronze (3rd-place) match, has already been fought and scored, the app refuses the correction and names the match that is blocking it by its number and round, for example Match 3 (Final): the round is the heading of the bracket column you find it under. You can confirm anyway. Confirming applies the correction and reopens the named match where it is, with the new competitor in it: its winner is cleared, and its points and bouts are kept, so check them and take back any that no longer apply before you finish it again. The match was already played, so it stays where it is and nothing in the queue changes. A semifinal is the one case that names two matches at once, because it feeds both the final and the bronze match; confirming then reopens both. A round further on that nobody has played yet stops showing the winner of the reopened match. A round further on that has already been played is not changed yet: when you finish the match you have just reopened, the app asks you the same question again for that round, so you work forward one round at a time. A later slot that was only filled by a bye, and never fought, does not block a correction; it simply updates to follow the new winner. If the next match, or the bronze match, is being fought right now, the correction is refused and nothing is saved, for example "Match 3 (Final) is being fought now on Shiaijo A. Finish it or send it back to the queue, then save this correction again." Confirming cannot get past it. Sending the match back to the queue keeps its score. A later match that was sent back to the queue with points already scored takes the new competitor's name and keeps its points, so check them when you start it and take back any that no longer apply. When you are done, use **Back to court** to return to the live match.
Reopening puts the encounter back into play, and a court can only run one match at a time. On the court console nothing else is running while you correct, but when you correct from the Scores page another match may be running on that court. Then the editor names it and offers two ways forward. Leave it running, or send it back to the queue and reopen in a single step with **Queue it and reopen**. The match sent back keeps any score already entered for it and carries on from there when it is started again.

If the correction changes who won a knockout match, the later rounds update to follow the new winner. If the next match, or the bronze (3rd-place) match, has already been fought and scored, the app refuses the correction and names the match that is blocking it by its number and round, for example Match 3 (Final): the round is the heading of the bracket column you find it under. You can confirm anyway. Confirming applies the correction and reopens the named match where it is, with the new competitor in it: its winner is cleared, and its points and bouts are kept, so check them and take back any that no longer apply before you finish it again. The match was already played, so it stays where it is and nothing in the queue changes. A semifinal is the one case that names two matches at once, because it feeds both the final and the bronze match; confirming then reopens both. A round further on that nobody has played yet stops showing the winner of the reopened match. A round further on that has already been played is not changed yet: when you finish the match you have just reopened, the app asks you the same question again for that round, so you work forward one round at a time. A later slot that was only filled by a bye, and never fought, does not block a correction; it simply updates to follow the new winner. If the next match, or the bronze match, is being fought right now, the correction is refused and nothing is saved, for example "Match 3 (Final) is being fought now on Shiaijo A. Finish it or send it back to the queue, then save this correction again." Confirming cannot get past it. Sending the match back to the queue keeps its score. A later match that was sent back to the queue with points already scored takes the new competitor's name and keeps its points, so check them when you start it and take back any that no longer apply. When you are done, **Save correction** ends the correction, or use **Back to court** to cancel it without saving.

### Correct a pool result after the knockout has started

Expand Down
10 changes: 5 additions & 5 deletions internal/engine/bc_idfx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func TestRecordDecision_KikenResolvesLoserByID_NotFirstRegisteredNamesake(t *tes
}))

// decisionBy "aka" => SideA (Tanaka@A) is the withdrawing/losing side.
_, status, err := eng.RecordDecision(compID, "Pool A-0", "kiken", "aka", "injury", nil, false)
_, status, err := eng.RecordDecision(compID, "Pool A-0", "kiken", "aka", "injury", nil)
require.NoError(t, err)
require.NotNil(t, status, "a CompetitorStatus must be written for the kiken")
assert.Equal(t, tanakaA, status.PlayerID, "the ineligibility must land on Tanaka@A, the competitor who actually withdrew")
Expand Down Expand Up @@ -210,7 +210,7 @@ func TestRecordDecision_PartiallyStampedRow_StillRecordsIneligibility(t *testing
}))

// decisionBy "aka" => SideA (Tanaka) is the withdrawing/losing side.
_, status, err := eng.RecordDecision(compID, "Pool A-0", "kiken", "aka", "injury", nil, false)
_, status, err := eng.RecordDecision(compID, "Pool A-0", "kiken", "aka", "injury", nil)
require.NoError(t, err)
require.NotNil(t, status, "a CompetitorStatus must be written -- the losing SIDE is known even though her id field on this row is empty")
assert.Equal(t, tanakaID, status.PlayerID, "the ineligibility must land on Tanaka, resolved by name fallback for the known losing side")
Expand Down Expand Up @@ -251,7 +251,7 @@ func TestRecordDecisionTx_SameNamePairing_RescoreAsFoughtRestoresEligibility(t *
}))

// decisionBy "aka" => SideA (Tanaka@DojoA) withdraws.
_, status, err := eng.RecordDecision(compID, "Pool A-0", "kiken", "aka", "injury", nil, false)
_, status, err := eng.RecordDecision(compID, "Pool A-0", "kiken", "aka", "injury", nil)
require.NoError(t, err)
require.NotNil(t, status)
assert.Equal(t, tanakaAID, status.PlayerID)
Expand All @@ -266,7 +266,7 @@ func TestRecordDecisionTx_SameNamePairing_RescoreAsFoughtRestoresEligibility(t *
// for this match -- the stale Pool A-0 ineligibility entry must be
// restored by the record itself, not re-derived from the row's (still
// same-name, still id-less-on-WinnerID) sides.
_, restoredStatus, err := eng.RecordDecision(compID, "Pool A-0", "fought", "aka", "", nil, false)
_, restoredStatus, err := eng.RecordDecision(compID, "Pool A-0", "fought", "aka", "", nil)
require.NoError(t, err)
require.NotNil(t, restoredStatus, "the stale ineligibility for THIS match must be restored, not silently skipped as ambiguous")
assert.Equal(t, tanakaAID, restoredStatus.PlayerID)
Expand Down Expand Up @@ -572,7 +572,7 @@ func TestRecordDecisionTx_SameNamePairing_AttributesWinnerBySide(t *testing.T) {
}))

// decisionBy "aka" => SideA (Tokyo) withdraws; SideB (Osaka) survives.
result, status, err := eng.RecordDecision(compID, "Pool A-0", "kiken", "aka", "injury", &state.EnchoMetadata{PeriodCount: 1}, false)
result, status, err := eng.RecordDecision(compID, "Pool A-0", "kiken", "aka", "injury", &state.EnchoMetadata{PeriodCount: 1})
require.NoError(t, err)
require.NotNil(t, result)

Expand Down
Loading
Loading