Repository navigation
fix(bc-dsid, bc-otpl, bc-htsd, bc-dlck, bc-kosc, bc-crpn): operator review fixes from PR #442 - #462
Merged
gitrgoliveira merged 16 commits intoOct 8, 2026
Conversation
…ection Recording a withdrawal on the other side after a later match had started showed a generic "Proceed anyway?" confirm that named neither the match nor what confirming did. Under the 2026-09-27 ruling a withdrawal correction changes only that match, and the matches it can reach in a knockout already carry their own refusal or confirm, so the confirm guarded nothing. It is removed with its engine lock (ErrDecisionLocked, hasDownstreamMatchStarted), the force flag on the decision write, and the client's retry. Knockout behaviour is unchanged (operator decision 2026-10-07): a running later match is still refused, a played one is still named and confirmed before it is reopened. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…th no default side
bc-otpl: tapping the word "Overtime" on the overtime pill opened the
glossary instead of the encho counter, because a glossary term (itself a
control that stops the tap) sat inside the button. The pill and the
"Encho started" label now hold plain text with the glossary chip beside
them, and the rule (a term is never inside a button or label) is stated
once above TermAS.
bc-dsid: the withdrawal and no-show prompt preselected Shiro and
recorded on a bare Record, so a hasty tap withdrew the wrong competitor.
No side is preselected now; Record stays off until a side is picked and
then names it ("Record: AKA withdrew"), and a line says what recording
does: "<name> cannot fight again in this competition. <other> wins this
match." (operator decision 2026-10-07). The side options are 44px pills
under a coarse pointer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In a pools + knockout competition every knockout match took the day's start time, so Finish + Start Next ran a resolved knockout match in the middle of the remaining pool bouts and the printed schedule showed impossible times. The preview bracket now reads the stored pool times (poolPhaseEndByCourt) and starts each court's knockout after that court's last pool bout, as the estimator already does. A knockout-only competition keeps the day start. No stored times are rewritten. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the court console, Correct opened a correction whatever was running, and Start match started a match whatever correction was open, so the running match vanished from the page and a later Start could send it back to the queue without a word. Per the 2026-09-27 ruling, Correct is now refused while a bout is live on the court and Start is refused while a correction is open; each refusal shows a notice at the tapped control and a toast naming the other match. A landed Save correction ends the correction. A match started on the court from another screen leaves the correction open and is listed in the "Another bout is running" alert (operator decision 2026-10-07). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Once hantei was armed in the individual score editor, SHIRO wins and AKA wins sat 6px apart and each committed the verdict on one tap, advancing the bracket and starting the next match, so a neighbour tap recorded the opposite winner. Per the operator decision of 2026-10-07 the buttons now only pick a side, as the team editor's representative-bout hantei already does, and Finish (two taps) or Enter commits the picked side. A verdict recorded on another device is adopted as the pick. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The clear-withdrawal later-matches list composed the same label and pairing inline that the court console's refusal notices now take from scoreRowMatchName; it reads that owner too. Output unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mobile-score-editor and mobile-encho-overtime show the overtime pill and the "Encho started" label as plain text with the glossary chip beside them (bc-otpl). mobile-pool-standings changes because its fixture scores the Scores list in order: knockout matches no longer interleave with the remaining pool bouts (bc-kosc), so each pool bout takes a different scripted result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- DecisionPrompt reads the side word from sideWord and sits in the existing .radio-group row; the new pill-row rule is gone (bc-dsid). - The court-console refusal notice is derived from the match that blocked the tap, so the clearing effect and the two hand-built toast strings go; the Correct refusal builds on courtBusyMessage (bc-crpn). - The decision submit drops the opts parameter only the removed force retry used, and a stale doc comment names one confirmation (bc-dlck). - The bracket slot assigner computes the opening time once and takes the pool-phase map directly (bc-kosc). - Three tests that only checked their own copy of a ternary are removed; the hantei Finish test now taps through the bounce guard (bc-htsd). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- bc-kosc: a knockout court that holds no pool bout starts after the latest pool end on any court, not at the day start; the doc says the pool end is read once, when the preview is built. - bc-htsd: leaving a running match with a hantei side picked asks before dropping it; a recorded hantei on an untied scoreline cannot be finished (the server refuses it). - bc-crpn: the court console asks writeKeepsEditorOpen before ending a correction. - scoreRowMatchName takes the missing-side word from sideWord. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A tie-break or representative bout added once a pool is finished is appended after its court's last pool bout, which is where that court's knockout was timed, so the two ran at the same time. pushKnockoutPastPools moves a court's scheduled knockout matches after the new pool end, keeping their order and gaps and skipping lunch, in the same transaction as the injection. A court whose knockout already starts after its pools, or where a knockout match has started, is left as it is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pool bout moved onto another court keeps its time, and on that court it can now run into the knockout. UpdateMatchCourt moves that court's knockout past it in the same transaction, and the handler announces a schedule change so every screen picks up the new times. A time set by hand still moves nothing: it is the operator ordering the court, and the queue's up/down is two such edits. Pushing there would undo moving a knockout match ahead of a remaining pool bout. The draw passes the pool matches generatePools saved straight to the preview bracket instead of reading them back; the quarantine rebuild reads them from disk itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The hantei winner buttons (individual editor and team representative bout) read "SHIRO (White) wins" / "AKA (Red) wins", with the competitor in the individual editor, so an operator who does not know the words can tell them apart. side_cell.jsx's sideWithColour owns the label; the decision side picker and the team summary band read it too. - A Save correction answered after another correction was opened no longer closes that one (endCorrectionOf, functional update). - A court move is saved even when tournament.md cannot be read; the knockout push is skipped and logged instead of aborting the move. - pushKnockoutPastPools moves past lunch only a match it moves, and leaves a knockout row whose time does not parse alone. - An armed hantei says why Finish is off, and a recorded hantei on an untied scoreline (legacy) keeps its Cancel reachable. - The Start refusal during a correction names the console's buttons. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eam labels Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gitrgoliveira
added a commit
that referenced
this pull request
Oct 8, 2026
…-crpn are plain tests Main merged #462 (bc-dsid, bc-otpl, bc-htsd, bc-dlck, bc-kosc, bc-crpn). The journeys now walk what an operator meets on that build. - bc-crpn: Correct is refused at the court console while a bout is live. J1, J2, J4 and J5 record the refusal (the row notice and the toast), then take the route the notice names: send the live bout back to the queue, correct, start it again. J5's court-busy panel is reached from the Scores tab, where a reopen can still meet a busy court, with its V1 and V4 variants kept. The bc-crpn test asserts the new refusal: Start match while a correction is open names the correction and starts nothing. - bc-dsid: the withdrawal and no-show prompt picks no side and Record is off until one is picked. The hasty variants record that, then the operator picks; fixtures/individual.mjs recordDecision now takes the side. - bc-htsd: the individual hantei buttons pick the side and Finish records it. - bc-dlck: the re-recorded kiken asserts no "Proceed anyway?" confirm shows. - bc-otpl and bc-kosc pass as written and are plain tests. - J5's M1 points won read 7-3 and then 6-3: bout 5's double tap scores one M since bc-dtip (#450). The old 8-3 and 7-3 counted the bounce's second M. - A banned phrase about the outcome of a withdrawal is gone from three audit labels, a J3 step title and the bc-kfup test title. One fixme left, on an open bead: bc-dhrp, which still fails at its own assertion. make e2e: 32 passed, 1 skipped, EXIT 0 (kachinuki re-run after the points fix: 11 passed). make go/test: EXIT 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
15 tasks done
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.
Summary
Six findings from the PR #442 operator review, fixed in one PR as the operator asked. Each fix follows the bead's plan, re-checked against current main, with the operator decisions of 2026-10-07.
TermAS, and a render test with the realTermenforces it.ErrDecisionLocked,hasDownstreamMatchStarted), theforceflag on the decision write, and the client's retry. Knockout behaviour is unchanged (operator decision: R1):Why
Each bead's DESIGN section holds the root cause and the plan. All six plans were re-verified against main
0d91b765before any code was written. The corrections found were:correctionPromptstate, not a ref;decision_lockedon replay;Files changed
CLAUDE.mdhasDownstreamMatchStarted; the hantei pick replacessubmitHanteiin the change-groups and result-slot notes; the leave rule names the hantei side pickdocs/screenshots/mobile-encho-overtime.pngdocs/screenshots/mobile-pool-standings.pngdocs/screenshots/mobile-score-editor.pngdocs/user-guide/court-operators/recording-decisions.mddocs/user-guide/court-operators/scoring-a-match.mdinternal/engine/bc_idfx_test.goforceargument (bc-dlck)internal/engine/bracket.gobuildBracketFromDrawtakes the pool matches; the preview bracket times each court's knockout after its pool phase (bc-kosc);generatePoolPreviewBrackettakes the pool matches instead of reading them backinternal/engine/competition.gogeneratePoolssaved to the preview bracket (bc-kosc)internal/engine/daihyosen.gosavePoolMatchesAndKnockoutTimes: a representative bout added after the draw moves the knockout past it (bc-kosc); the helper moved toscheduler_slots.gointernal/engine/decision_no_cascade_test.gointernal/engine/decision_stamp_test.goforceargument (bc-dlck)internal/engine/default_win_bout_credit_test.goforceargument (bc-dlck)internal/engine/default_win_bout_padding_unreadable_test.goforceargument (bc-dlck)internal/engine/downstream_correction_guard_test.gointernal/engine/downstream_running_refusal_test.got103Forceloop collapses to one case; the refusal stays (bc-dlck)internal/engine/eligibility.goforcefromRecordDecision*and deletehasDownstreamMatchStarted(bc-dlck)internal/engine/eligibility_test.gohasDownstreamMatchStartedtests (bc-dlck)internal/engine/errors.goErrDecisionLocked(bc-dlck)internal/engine/kachinuki_reopen_withdrawal_test.goforceargument (bc-dlck)internal/engine/kachinuki_test.goforce(bc-dlck);buildBracketFromDrawarity (bc-kosc)internal/engine/knockout_test.gobuildBracketFromDrawarity (bc-kosc)internal/engine/legacy_template_v1_test.gobuildBracketFromDrawarity (bc-kosc)internal/engine/load_error_propagation_test.gohasDownstreamMatchStartedrow (bc-dlck)internal/engine/match_merge_phase3_test.gointernal/engine/match_merge_review_test.goforceargument (bc-dlck)internal/engine/match_merge_test.goforce(bc-dlck)internal/engine/mixed_schedule_test.goTestPushKnockoutPastPools(bc-kosc); a court move moves the knockout; a hand-set time and the queue's up on a knockout match move nothing; an unreadable tournament keeps the court move; the push leaves an operator's later time inside lunch and an unparseable time aloneinternal/engine/pool_requalify_test.goforce(bc-dlck);buildBracketFromDrawarity (bc-kosc)internal/engine/pools.gogeneratePoolsreturns the pool matches it saved (bc-kosc)internal/engine/pools_extra_qualifiers_order_test.gogeneratePoolsreturns its matches (bc-kosc)internal/engine/quarantine.gointernal/engine/reopen_bye_unwind_test.goforceargument (bc-dlck)internal/engine/reopen_fusensho_status_test.goforceargument (bc-dlck)internal/engine/reopen_scheduled_court_test.goforceargument (bc-dlck)internal/engine/reopen_withdrawal_test.goforceargument (bc-dlck)internal/engine/restore_touches_no_eligibility_test.goforceargument (bc-dlck)internal/engine/schedule.gointernal/engine/scheduler_slots.gopoolPhaseEndByCourt;assignBracketMatchSlotstakes a per-court start (bc-kosc); a court with no pool bout waits for the latest pool end; newpushKnockoutPastPools(bc-kosc);moveKnockoutPastPoolsandsavePoolMatchesAndKnockoutTimeslive here; the push moves past lunch only a match it moves and skips a row whose time does not parseinternal/engine/scheduler_slots_test.goTestAssignBracketSlots_StartAfterPoolPhaseandTestPoolPhaseEndByCourt(bc-kosc); a court with no pool bout waits for the latest pool endinternal/engine/scoring.goUpdateMatchCourtmoves the new court's knockout past a pool bout moved onto it;UpdateMatchTimemoves nothing (bc-kosc); a tournament that cannot be read skips the knockout push (logged) rather than refusing the court moveinternal/engine/scoring_test.goforceargument (bc-dlck)internal/engine/scoring_tx.gohadPriorLoser;refuseDecisionReachingRunningMatchstays (bc-dlck)internal/engine/scoring_tx_internal_test.gohasDownstreamMatchStartedtests (bc-dlck)internal/engine/scoring_tx_test.goWithdrawalCorrectionChangesNoOtherMatch; lock subtest rewritten (bc-dlck);buildBracketFromDrawarity (bc-kosc)internal/engine/tiebreaker.gointernal/engine/winnerid_invariant_test.goforceargument (bc-dlck)internal/engine/withdrawal_cleared_by_correction_test.goforceargument (bc-dlck)internal/engine/withdrawal_removed_restores_test.goforceargument (bc-dlck)internal/engine/withdrawal_ruling_kept_test.goforceargument (bc-dlck)internal/mobileapp/decision_no_confirm_test.goforce:truestill binds and applies (bc-dlck)internal/mobileapp/deps.goScoringEnginemethods loseforce(bc-dlck)internal/mobileapp/deps_test.gointernal/mobileapp/downstream_running_handler_test.godecision_lockedordering (bc-dlck)internal/mobileapp/handlers_decision.goDecisionRequest.Forceand thedecision_locked409 (bc-dlck)internal/mobileapp/handlers_match.gointernal/mobileapp/ineligible_sides_annotation_test.goforceargument (bc-dlck)internal/mobileapp/reopen_downstream_labels_test.goforceargument (bc-dlck)scripts/screenshots/recipes/editors.mjsscripts/screenshots/recipes/public.mjsspecs/openapi.yamldecision_lockedordering note (bc-dlck)web-mobile/css/styles.cssweb-mobile/js/__tests__/admin_scoring_modal.test.jsxforceemitted and no retry (bc-dlck); the submit flows move to a real-React render file and the default-side pin becomes a no-side refusal (bc-dsid)web-mobile/js/__tests__/api_client_downstream_knockout.test.jsxweb-mobile/js/__tests__/pool_ids.test.jsxscoreRowMatchNamecases (bc-crpn)web-mobile/js/__tests__/render/admin_scoring_modal.render.test.jsxweb-mobile/js/__tests__/render/admin_shiaijo.render.test.jsxweb-mobile/js/__tests__/render/correction_save_is_not_chained.render.test.jsxweb-mobile/js/__tests__/render/decision_prompt_side.render.test.jsxweb-mobile/js/__tests__/render/hantei_single_tap.render.test.jsxweb-mobile/js/__tests__/render/individual_editor_hantei_correction.render.test.jsxweb-mobile/js/__tests__/render/individual_editor_live_hantei.render.test.jsxweb-mobile/js/__tests__/render/kachinuki_reopen.render.test.jsxweb-mobile/js/__tests__/render/remove_withdrawal_correction.render.test.jsxweb-mobile/js/__tests__/render/term_not_inside_control.render.test.jsxTerm: tapping the word "Overtime" opens the counter; no term inside a button or label in EnchoControl or DecisionPrompt (bc-otpl)web-mobile/js/__tests__/sync_queue.test.jsxdecision_lockedreplay case (bc-dlck)web-mobile/js/__tests__/write_result_court_console_refusals.test.jsxweb-mobile/js/admin_scoring_individual.jsxsubmitHanteiremoved (bc-htsd);defaultSide(bc-dsid) andentityLabel(bc-dlck) props removed; leaving a running match with a hantei side picked asks first; Finish needs the tied scoreline the hantei row shows; the buttons name colour and competitor throughsideWithColour; the row stays while armed and says why Finish is offweb-mobile/js/admin_scoring_shared.jsxwithdrawalConsequence(bc-dsid); EnchoControl plain text and the term rule (bc-otpl); T103 retry removed (bc-dlck); later-matches list named throughscoreRowMatchName(bc-crpn); side labels throughsideWithColourweb-mobile/js/admin_scoring_team.jsxdefaultSide(bc-dsid) andentityLabel(bc-dlck) removed; daihyosen comment points at the term rule (bc-otpl); hantei buttons and summary band labels throughsideWithColour(bc-htsd)web-mobile/js/admin_shiaijo.jsxliveBout,isStartedSnapshot, Correct and Start gates, derived refusal notices, Save/onClose end the correction, the also-running alert during a correction (bc-crpn); ending a correction askswriteKeepsEditorOpen, and ends only the correction the write was for (endCorrectionOf)web-mobile/js/api_client.jsxdecision_lockedforce retry (bc-dlck)web-mobile/js/pool_ids.jsxscoreRowMatchName(bc-crpn); the missing-side word comes fromsideWordweb-mobile/js/side_cell.jsxsideWithColour: "SHIRO (White)" / "AKA (Red)" for a control that picks a side (bc-htsd)web-mobile/js/write_result.jsxstartWhileCorrectingMessage(names Save correction and Back to court) andcorrectWhileRunningMessage; comment (bc-dlck)Screenshots
Taken in Chromium at 1180x820 with a coarse pointer (CDP touch emulation,
(pointer: coarse)asserted true).bc-dsid: Kiken - Voluntary after picking Aka. Nothing was picked before this tap. The button names the side and the consequence line shows.

bc-htsd: hantei armed, Shiro (White) picked. Each button names its colour and competitor. Nothing has been recorded yet; Finish commits it. Before a side is picked, the row says "Pick the hantei winner, then finish."

bc-crpn: Correct tapped while League table · Match 2 runs. A notice appears under Correct, and the same text as a toast. (After the simplify pass, this sentence is built on the court-busy refusal, so it now ends "...back to the queue first. Then correct this match." The screenshot shows the earlier single sentence.)

bc-crpn and bc-otpl: Start match tapped while a correction is open. The Up next card shows a notice. The overtime pill reads plain "Overtime" with "?" beside it.

bc-dlck: the kiken re-recorded on the other side. No confirm dialog appeared. The running match is unchanged.

bc-kosc: Shiaijo D schedule. The semifinals are at 10:00 and 10:05, after the last pool bout at 09:55.

bc-kosc: a tie-break added after the draw. Pool A ended in a three-way draw, recorded from the court console; its tie-break bouts were added at 09:30, 09:35 and 09:40. The knockout moved to 09:45, 09:50 and 09:55.

bc-htsd: leaving with a hantei side picked. On a running final, Decide by hantei, AKA wins, then Close asks before dropping the pick. Cancel kept the editor open with AKA still picked.

bc-kosc: a pool bout moved onto another court. Pool B's last bout, hand-set to 09:40 on Shiaijo A (which moved nothing), was moved to Shiaijo B with the court picker. Shiaijo B's knockout moved to 09:45, 09:50 and 09:55; Shiaijo A's kept its times.

Test plan
make go/testpasses (lint + security scan + tests). Exit 0, run on the commit before the screenshot and CLAUDE.md-only commits. Unit JS: 333 files / 6489 tests. Coverage: engine 90.9%, mobileapp 90.1%. Run again on1972b66f: exit 0, engine 90.9%, mobileapp 90.1%; render suite 1657 tests andmake js/check-importsOK on5232a323(no JS since). Each new test fails with only its fix reverted.make js/check-importspasses. Each new test was shown to fail with only its fix reverted, except these:middle_closed_setpins the centre. The winner buttons read "SHIRO (White) wins: Kb4" / "AKA (Red) wins: Kb3", and the row says to pick a winner until one is picked; checked at 1180×820 and in iPad portrait (820×1180), where long names wrap inside the row with no sideways scroll.cb1a8345,5232a323): a tie-break added from the court console moved the knockout past it (bracket tab); a hantei side picked on a running match asks before Close, and Cancel keeps the pick. 0 console warnings or errors.1972b66f: a pool bout moved to Shiaijo B with the court picker moved B's knockout to 09:45 onwards, A's unchanged; a hand-set time moved nothing; 0 console warnings or errors.docs/(make docs/buildpasses;check_links.pyandcheck_prose.pyOK).make docs/media SINCE=main): 3 changed, reviewed and copied in.mobile-pool-standingswas checked against a capture from a main-equivalent tree, where it is unchanged; the difference comes from bc-kosc's ordering, as described in the Files table. Videos were not re-copied because none of these changes appear in them.For PR #442 (the e2e harness), when it rebases onto this
recordDecisionfixture call: Record is disabled with no side picked (bc-dsid).Closes bc-dsid
Closes bc-otpl
Closes bc-htsd
Closes bc-dlck
Closes bc-kosc
Closes bc-crpn
🤖 Generated with Claude Code