Repository navigation
fix(bc-rfsw,bc-offl,bc-rboq,bc-flst,bc-kheb,bc-mrgc,bc-hlck): merge match writes by change, held results visible, correction refusals, kachinuki encho - #453
Merged
gitrgoliveira merged 48 commits intoOct 5, 2026
Conversation
…ued bout shows "Not sent yet" Co-Authored-By: Claude Code <noreply@anthropic.com>
… the bounce Co-Authored-By: Claude Code <noreply@anthropic.com>
…they land Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
…er match Co-Authored-By: Claude Code <noreply@anthropic.com>
…ir stored legacy marks on load Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
…i encho inside recordDecisionTx Co-Authored-By: Claude Code <noreply@anthropic.com>
A stale /decision replay is reported superseded instead of refused for a running later match; the decision pre-check shares the score door's winner-changed test and refusal tail. A typed fighter name committed by tapping an ippon button no longer swallows that tap. Running and played refusals list three or more matches "A, B and C" as the SPA does. The kachinuki encho repair keeps its marker unset when a match file cannot be read, and a new kachinuki competition starts with the marker set; the decision write fails rather than silently miscounting when the competition cannot be loaded. Co-Authored-By: Claude Code <noreply@anthropic.com>
A reopen blocked by a running downstream knockout match showed "...then reopen again." in the SPA while the server's message said "...then reopen this match again." Both now say the latter. The sentence is pinned by one table both languages load (internal/engine/testdata/downstream_running_messages.json), covering the save and reopen doors for one match and three, with and without a court, in place of copies of the same literals in each language's tests, which is how the two drifted. Stale comments naming the old wording are updated. Co-Authored-By: Claude Code <noreply@anthropic.com>
Operator ruling 2026-10-03: an operator can fix any mistake, but the fix must leave the match resolved. A withdrawal or default win recorded by mistake on a match fought to a result was only removable by Clear withdrawal and reopen, which puts the match back to running, needs a free court and leaves it unresolved until finished again. The score editors' recorded-withdrawal box now also offers Remove withdrawal (Remove default win). It takes the ruling off the board: the default-win circles go, the withdrawer's struck points stay, and the operator enters the real result, a win or a pool/league draw. Save correction sends it with the request-only clearWithdrawal, which makes engine.KeepsWithdrawalRuling replace the ruling instead of keeping it: the match stays completed, never takes the court, and the competitor the withdrawal barred is eligible again. A team match needs a result on every bout, as on any finish; a knockout winner change goes through the usual downstream checks. Undo puts the recorded ruling back before saving. Clear withdrawal and reopen stays for a match with fighting left. Kachinuki is not offered Remove withdrawal: a finished kachinuki encounter has no Save correction. Co-Authored-By: Claude Code <noreply@anthropic.com>
After Remove withdrawal the board and the box say the withdrawal is going, but the header still showed "Kiken" beside the withdrawn competitor, two answers on one screen. WithdrawalMarkedName takes the editor's pending removal and drops the mark until Undo or the save. Also pins a hantei decided after Remove: its own write (submitHantei) carries clearWithdrawal like Save correction does. Co-Authored-By: Claude Code <noreply@anthropic.com>
- useWithdrawalRemoval (admin_scoring_shared.jsx, beside useMatchReopen) owns Remove withdrawal for both editors: the state, one reset rule (a change of match id, decision or side ends a pending removal), the RecordedWithdrawal prop, the clearWithdrawal patch block and one rulingShown value the board, the summary and the name mark read. The team editor states the kachinuki exclusion once. On a reset the individual board re-seeds; the team editor keeps its bout edits. - WithdrawalMarkedName takes rulingShown, so a surface that forgets it shows no mark rather than a stale one. - The team editor's knockout tie block and its button label read one input: a removal whose bouts tie in a knockout reads "Needs a winner". - RecordedWithdrawal works out one eligibility outcome for both buttons' copy and renders one consequence paragraph; a fusensho whose competitor is already eligible now says so after Remove too. - The individual editor's completed writes share one tail. - The shared refusal table gains the 3rd-place cases; the JS copies go. - Tests reuse collectEvents and one subtest-name helper. Co-Authored-By: Claude Code <noreply@anthropic.com>
…onfirm names the match Operator request 2026-10-03 (PR #453). Two gaps on the court console when a match is moved to another shiaijo: - The move confirm named one competitor ("Ito Rin leaves Shiaijo A"), which reads as a person moving. It now names the match, both competitors, through confirmMatchLabel, the naming Send back to queue already used. - On the receiving court, the moved match was visible only through the competition picker while that court's own competition still had matches waiting. The court now names the other competition's waiting matches in a quiet neutral line ("1 Individual match also waiting on this court. Switch"), keeps the amber "Switch to" for when its own competition has none left, and still switches only on a tap (one operator per court). Checked in the browser with the receiving court open in a second tab: the line and the picker appear there about 0.5 s after the move, with no reload. Co-Authored-By: Claude Code <noreply@anthropic.com>
Court console (admin_shiaijo.jsx): - The console no longer switches competition by itself. The default only chooses the first competition shown; it is then held as if picked, so a bout finishing (or a match moved onto the court) cannot swap it. Only the picker and the banner change it (one operator per court, ruling 2026-09-26). The amber "Switch to" is now reached through that flow, not by picking the finished competition by hand in the test. - Every other competition waiting on the court is named, each line with its own switch, and each line's accessible name carries its text. Editors: - Undo of a removal puts back the recorded hantei verdict too, so a hantei armed on the tie the removal made no longer leaves every control disabled once the circles return. - A tied knockout team match after Remove no longer offers a representative bout the server would refuse (the stored match still holds the withdrawal): the panel and the block's title point to Clear withdrawal and reopen, which puts the match back on the court. - A correction held offline reads as saved on the device and offers no Undo, which could not recall it. Docs and the ClearsWithdrawal comment follow. Co-Authored-By: Claude Code <noreply@anthropic.com>
- The engi recorder writes the stamps the merge decided, so a finish that moved a newer invalid recount out leaves its flags at the finish's own time, and both arrival orders reach the same winner and stamps. - Points that would clear a kiken or fusenpai but leave a knockout tied do not clear it: the decision stands and the points and overtime are held together under its reason, never "needs a winner". - Overtime enters that rule only when it actually changes, so an echoed (E) is a no-op again instead of a false "newer change" answer, and a hold records a reason only when it held something. - heldDecision travels only with its heldReason (one owner). - The team editor's two hints name the real button for the recorded decision and no longer promise the court; "again" is said only of a fusensho; the queue alert compares decisions by their word; two docs sentences match each button state; the decision-hold message says "change", not "scoring"; the OpenAPI representative-bout answers list their held fields; the glossary no longer says a side wins by default. - The three prose gates share one pattern pair, catch text wrapped across lines or split across literals, and pass identifiers. Co-Authored-By: Claude Code <noreply@anthropic.com>
- Operator ruling 2026-10-04: for an engi match the newest count decides, in either arrival order. A finish whose result applies over a newer stored valid count closes the match with that count's winner; the finish's own count is kept in the history as held. A stale finish over a completed match stays held whole, now pinned by its own test. - specs/openapi.yaml named the result of a withdrawal wrongly; it now names the decisions, and a gate reads the spec as well. - The three prose gates share one pattern pair that also covers the past and participle forms, the other side of the same wording, text wrapped across a line, and HTML or emphasis split text in the docs, while passing identifiers and attribute runs. - The kiken glossary entries say the withdrawer's points stand rather than a fixed score; two docs sentences name the fusenpai; the recording-decisions page describes each button state truthfully, including a competitor barred by a separate match. Co-Authored-By: Claude Code <noreply@anthropic.com>
- A refused engi pool correction leaves no history entry: the entry is written after the requalification check, as every other door does. - An engi write answers with the stamp the merge settled on, so the next tap's floor matches the stored match. - The prose gates share three patterns and one case table read by the Go and JS tests: a phrase split across a line on either side is caught, the noun form is caught, and ordinary settings and connection prose passes. A git-tracked scan reads the person-read files outside docs/, and the wording it found in the tournament guide and the tracked spec inputs now names the decision. - The reopen description in the spec, the recording-decisions page and CLAUDE.md match the code: the still-barred check applies to every kiken, fusenpai and fusensho, a reinstatement lifts it, and clearing the match that recorded a bar moves it onto a chained fusenpai. Co-Authored-By: Claude Code <noreply@anthropic.com>
A docs review of this PR found the history-reason lists in the data model and the OpenAPI spec named four of the six reasons the server records, leaving out the late finish kept with its scoreline and a kiken, fusenpai or fusensho that already closed the match. The scoring guide quoted the short note where the editor shows the full banner, and cut one note before its last sentence. The override and decision 409s named runningMatches without describing that body. A table cell pointed at "the next row". All now match the code. Co-Authored-By: Claude Code <noreply@anthropic.com>
The armed state of a two-tap button (.btn--confirm) set a navy fill but no text colour, so kachinuki's End match, a plain .btn, showed "Tap again" in dark ink on navy. The rule now owns its text colour. Release builds embedded no Preact: web-mobile/vendor is gitignored and only the Makefile's vendor-frontend target fetches it, which goreleaser never ran, so every release archive and package served a blank UI. The release hooks now run that target, and the package smoke test starts the app and fetches its UI assets, so a missing one fails the release. A crash test pins that a match write and its history line land together through the transaction log, whichever file fails to apply. The reopen's OpenAPI text said a force-reopened match loses its bouts and that a bye is refused; it keeps its points and the bye is unwound. The RecordedWithdrawal comment described a reason asked before a reopen, which was removed on 2026-09-25. The kachinuki demo video is recaptured with the readable armed button. Co-Authored-By: Claude Code <noreply@anthropic.com>
A browser check found the comment's premise wrong for the team editor: it disables the name box while a pick's lineup write is out, which drops focus, so the next tap reopens the list through onFocus. The onClick path is for a box that kept focus (after Escape, or in the lineup panel, which keeps the box enabled). Co-Authored-By: Claude Code <noreply@anthropic.com>
Operator ruling 2026-10-05: after an operator discards a held result, the two-tap commit goes back to its first tap, so a discarded result is not one stray tap from being sent again. Each editor has one function for "this device holds no write for the match", used by its own Discard held result and by the edge a discard elsewhere (the top bar's list) produces; it disarms Finish, Finish + Start Next, End match and the engi save. A held overtime change read "Kept in history: overtime, overtime"; it now reads "overtime, started" or "overtime, not started". Co-Authored-By: Claude Code <noreply@anthropic.com>
gitrgoliveira
added a commit
that referenced
this pull request
Oct 7, 2026
…nl's Main now carries #447, #450 and #453. Their beads' fixmes were run unskipped on the merged build: - Plain tests now, passing as written: bc-kfup, bc-htcr, bc-plcl (individual), bc-fsnp, bc-dhas (team), bc-emsl, bc-kheb, bc-sync (kachinuki). - bc-offl: the court header reads "Offline: 1 result not sent" while the Finish is queued (captured offline); the test looked for "Not saved yet". - bc-sbq: #447 made Send back to queue keep the score, and the confirm now says "Any score entered is kept". The test asserts that, then starts the bout again and finds its point still on the board. - bc-kpnl's two tests are removed: the panel they wait for was taken out by the 2026-09-26 ruling (recording a withdrawal changes only that match). Follow-on edits: - The two 800 ms waits that kept bc-dhas from erasing the daihyosen row are gone (J7 and bc-dhrp); bc-dhas's own test proves the fix. - bc-fsnp waits for the sync pill to read Synced instead of 1.5 s: since bc-sync the pill reads Syncing from the first tap. - J5 final plays on from the first unfought bout: bc-kheb, which runs before it in this serial file, records the final's bout 1. Five fixmes remain, all on open beads: bc-otpl, bc-kosc, bc-crpn, bc-lprf, bc-dhrp. make e2e: 28 passed, 5 skipped, EXIT 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gitrgoliveira
added a commit
that referenced
this pull request
Oct 11, 2026
…, shared-court journeys Replan revision 2 of PR #442 (operator decisions 2026-10-11). Harness review against main (review only, then fixed): - finishMatch/finishTeam judge the server's answer to the completing write (finishLanded): a refused or unanswered Finish fails instead of reading as landed. - ensureRunning returns null only when the court has nothing startable and throws when a tapped Start ran nothing; its callers assert, not branch. - Forms are found by label and role, pills read by aria-pressed, the lineup panel by its dialog role; stale comments about the app corrected. - Same-side ippon taps are paced (tapOnSide); slotMarks reads locked slots. - One owner each for the court-console and team-sheet selectors; dead exports removed; README layout, tap-guard and Finish notes corrected. Console policy: a console error or uncaught page error in any page of any context fails the test unless it is on CONSOLE_ALLOWLIST (fixtures/test.mjs), each entry narrow and with its reason (deliberate offline steps; 409 refusals the app shows as refused); warnings are recorded. New journeys (seeded through the UI by fixtures/shared-court.mjs): - shared-shiaijo.spec.mjs (bc-shcr): two competitions on one shiaijo, walked as the clumsy operator; asserts one bout at a time, no competition switch by itself, the advance staying in the scored competition, Send back keeping the score. - moved-match.spec.mjs (bc-mvsj): a waiting match moved to another court seen live on that court's console (two devices), with #453's decisions asserted. Suspected defects they found are recorded as audit rows, pending the operator's rulings. make e2e: 38 passed. make go/test: EXIT 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
Seven beads, plus operator requests made during review.
Merge changes, never drop (bc-mrgc). Operator ruling 2026-10-03: "Nothing should be dropped. All events must be ordered." A match write now applies only the groups it changes (points, result, overtime, engi flags, representative players, each bout row), each ordered by when the change was made, never by when it arrived. Whatever does not apply is kept in a per-match history, written in the same transaction as the match and shown in every score editor under History. The rules, each an operator ruling:
The offline queue keeps every write: one entry per write, each sent on its own, nothing dropped for its age. A write that keeps failing holds back no other, and a write the server keeps refusing can be discarded on its own.
No write stamped before the match it was made against (bc-hlck). A device stamps a write no earlier than the match it saw, so a correction made just after a write from a device whose clock runs ahead is applied rather than kept in the history.
Correction refused while a later match is being fought (bc-rfsw). A knockout correction that would change a side of a running later match is refused with a 409 naming that match and its shiaijo, instead of re-seating a bout in progress.
Held results are visible (bc-offl, bc-rboq). The top bar reads "Offline: N result(s) not sent", the sync pill "Reconnecting…", and a toast confirms "1 finished result sent." Every queued notice reads "Not sent yet: saved on this device, and sent when the connection returns." The top-bar count opens a list of everything held on the device, with Discard for a write the server keeps refusing. Kachinuki Record bout returns its result, so a bout held offline shows "Not sent yet".
Fighter list (bc-flst). The list commits on click, swallows a bounced second tap, and reopens when its name box takes focus.
Kachinuki overtime on the bout (bc-kheb). A kachinuki encounter carries no match-level (E): the mark lives on the bout row. Writes strip it, and stored marks are repaired once on load.
Remove kiken, fusenpai or fusensho. Operator ruling 2026-10-03: "an operator can fix a mistake, but the fix must leave the match resolved". A decision recorded by mistake on a match fought to a result is removed in one save: Remove takes it off the board, the operator enters the real result, and Save correction sends it. The match stays finished, never needs the court, and the barred competitor can fight again. Clear and reopen stays beside it for a match with fighting left. Kachinuki is not offered Remove: a finished encounter has no Save correction.
The decision is named. Operator ruling 2026-10-04: every finished match has a result, a scoreline or a registered decision. Every label, notice, message and docs page names the recorded decision ("Record fusensho for ", "Remove kiken", "Not applied: this match was closed with a fusensho"). Three prose gates keep the old term out of Go strings, web-mobile strings and the docs.
Shared courts (operator request 2026-10-03). The court-move confirm names both competitors. A court whose competition still has matches names another competition's waiting matches in a quiet line ("1 Individual match also waiting on this court."), and the console switches only on a tap.
Gaps closed (operator request 2026-10-04): a court console back online refetches its queue; each pool bout has one number on every surface, its place in the playing order; a write the disk refused after its transaction log committed is retried by the device and logged as a disk error; the server refuses to start on a data folder it cannot write, naming each folder; the pdfcpu 0.16.0 bump on main (every call takes a context) is fixed.
Two defects found while closing the review's gaps. Release archives and packages embedded no Preact, so the app's UI rendered blank: the published v2.1.0 answers 404 for all four of its vendor scripts. The release hooks now fetch them, and the package smoke test starts the app and fetches its UI assets, so a missing one fails the release. And the armed state of a two-tap button had no text colour of its own, so kachinuki's armed End match read dark on navy; it is white now.
Why
web-mobile/vendoris gitignored and only the Makefile'svendor-frontendtarget fetches it; goreleaser never ran that target, and its smoke test only ranbracket-creator version. Docker images and the Homebrew formula build through other paths and were not affected..btn--confirmset a navy fill and relied on.btn--primaryfor white text; End match is a plain.btn.Upgrade and rollback
Upgrading in the middle of a tournament needs no action:
pool-matches.csvgains aGroupStampscolumn, appended last, andbracket.jsonagroupStampsfield. A match without them reads every group as stamped at itsModifiedAt, which is how the previous release ordered writes.history/folder, created when a match is first written.config.mdrecords it (kachinuki_encounter_encho_cleared).Rolling back to the previous release: it reads these files, ignoring the extra column, the extra field and
history/. Its next save of a match file drops the stamps, so those matches fall back to one stamp per match. Its writes are not added to the history, and its Discard draw leaveshistory/in place, so a draw discarded and regenerated under it shows the earlier draw's history for the reused match ids after upgrading again.Checked in a browser against v2.1.0 on the same data: a finish this build's page held offline was sent to v2.1.0 and saved; after a reload as the v2.1.0 client, a finish this build had queued (the new entry format, with
changed) was read, sent and saved, and the queue emptied. v2.1.0 rewrotepool-matches.csvwithout the stamps column and added no history line. The published v2.1.0 binary serves no UI (the release defect above), so the old client was v2.1.0 built from its tag.Files changed
.github/workflows/release.yaml/dist/app.js,/dist/glossary_data.jsand the four/vendor/*.jsfiles.goreleaser.yamlmake vendor-frontend, so archives and packages embed PreactCLAUDE.mddocs/architecture/data-model.mddocs/architecture/infrastructure-architecture.mddocs/architecture/network-architecture.mddocs/check_prose.pydocs/screenshots/kachinuki-reopen.png,kachinuki-correct-bout.png,kachinuki-knockout-tie-encho.png,kachinuki-scoring-buttons.png,mobile-encho-overtime.png,mobile-score-editor.pngkachinuki-reopenalso no longer clips its footer)docs/screenshots/mobile-draw-preview.png,mobile-pool-standings.pngdocs/videos/kachinuki-demo.webmdocs/user-guide/court-operators/recording-decisions.mddocs/user-guide/court-operators/scoring-a-match.mddocs/user-guide/install/install.mddocs/user-guide/organisers/team-tournaments.mdrunning_a_kendo_tournament.md,specs/003-tournament-gap-closure/inputs/gaps_spec_brief.md,gaps_tournament_spec.md,gaps_webui.mdspecs/openapi.yamlchanged,heldGroups,displacedGroups,heldReason,heldDecision,clearWithdrawal; the history route and every reason it records; the new 409 shapes, with therunningMatchesbody on every door that returns itinternal/domain/decision.goDecisionWord: the one Go owner of a decision's wordinternal/domain/glossary.go,web-mobile/js/glossary_data.js(generated)internal/engine/bracket_result.gobracketMatchCourtAndSchedulefor a replayed decision's answerinternal/engine/competition.gointernal/engine/daihyosen.gointernal/engine/eligibility.golookupExistingResultIn: one read answers the prior and which file holds itinternal/engine/engi.gointernal/engine/engine.gointernal/engine/errors.gosentenceListlists "A, B and C" as the SPA doesinternal/engine/kachinuki.gorunningDownstreamRefusal; encounter overtime stripped; the advance re-checked under the lock, re-read on a stale check, stamped no older than the match, and written with its history entry in one transaction (KachinukiAdvancehands back its stamps)internal/engine/league_tiebreak.gointernal/engine/match_history.gointernal/engine/match_merge.gointernal/engine/pools.gonumberPoolMatchesInPlayingOrder: a pool's ids follow the playing orderinternal/engine/scoring.goKeepsWithdrawalRulingdoes not keep a ruling the write clears; both write branches merge; the hand-set winner and the requeue write and append history in one transactioninternal/engine/scoring_tx.gorecordDecisionTxinternal/engine/swiss.go,tiebreaker.gointernal/engine/testdata/downstream_running_messages.jsoninternal/mobileapp/deps.goMaybeAdvanceKachinukireturns*engine.KachinukiAdvanceinternal/mobileapp/errors.goreasonHuman; a committed but unapplied write logged as a disk errorinternal/mobileapp/handlers_branding.go,handlers_sponsors.gostateinternal/mobileapp/handlers_competition.gointernal/mobileapp/handlers_daihyosen.gointernal/mobileapp/handlers_decision.gointernal/mobileapp/handlers_match.go/scorereadschangedandclearWithdrawal;withHeldGroups, the one owner of the held answer keys; the running revision guard holds;messageForHeldReason; the Record-bout answer carries the advance's stampsinternal/mobileapp/handlers_match_history.goGET .../matches/:mid/historyinternal/mobileapp/middleware.go,server.gointernal/mobileapp/testdata/default_win_cases.jsoninternal/pdf/generate.go,pagenum.go,pdfops.gointernal/state/bracket.gogroupStampson bracket matches;MatchByIDinternal/state/legacy_upgrade.gointernal/state/match_groups.gointernal/state/match_history.gointernal/state/models.goGroupStamps; the request-only fields;ClearKachinukiEncounterEnchoand its markerinternal/state/pools.goGroupStampsCSV column; request-only fields never cachedinternal/state/store.go,writable.gocheckDataWritable,ErrDataNotWritable), run before the transaction log replayinternal/state/testdata/match_groups.jsonTestMatchGroups_SharedTableandmatch_groups_table.test.jsxinternal/state/transactions.goStoreTxinternal/**/*_test.go(55 files)match_history_crash_test.go), lost-update windows, the refusals, the repair, the prose gatesweb-mobile/check-helpers.mjs,check-imports.mjs,check-write-result.mjsadmin_shell.jsxmay import fromwrite_result.jsxweb-mobile/css/styles.css.btn--confirmowns its text colourweb-mobile/js/__tests__/**(44 files)web-mobile/js/admin.jsxweb-mobile/js/admin_schedule_score_editor.jsxweb-mobile/js/admin_scoring_autosave.jsxweb-mobile/js/admin_scoring_engi.jsx,admin_scoring_individual.jsxclearWithdrawal; a discarded held write disarms Finish and the engi save (dropHeldWrite)web-mobile/js/admin_scoring_shared.jsxRecordedWithdrawalwith Remove and Undo;useWithdrawalRemoval; the held-write hooks and notice; a decision carries the stamp it was recorded against; comments onRecordedWithdrawaland the fighter list's focus brought up to dateweb-mobile/js/admin_scoring_team.jsxdropHeldWrite)web-mobile/js/admin_shell.jsxweb-mobile/js/admin_shiaijo.jsxweb-mobile/js/api_client.jsx_stampAfterSeenon every stamped writeweb-mobile/js/api_serializers.jsxclearWithdrawalweb-mobile/js/app.jsxweb-mobile/js/barred_match_notice.jsx,ineligible_match.jsxweb-mobile/js/match_groups.jsxweb-mobile/js/match_history_view.jsxweb-mobile/js/match_scoreboard.jsx,viewer_standings.jsxweb-mobile/js/viewer_utils.jsxweb-mobile/js/write_result.jsxdecisionWordScreenshots
bc-rfsw: correction refused, running final named
bc-offl: finished offline, the top bar shows the held result
bc-offl: the reconnecting pill while a write is held
bc-offl: the sent toast, once the held result lands and the court moves on
bc-rboq: a kachinuki bout held offline shows Not sent yet
bc-flst: the fighter list, and the list reopened by a focused name box
bc-flst: a typed name committed by tapping an ippon button scores it (bout 1); a double tap on a list option picks once (bout 2, Fusensho untouched)
bc-kheb: a tied bout fought on in encho and decided: (E) on the bout row, none on the encounter
The armed End match is readable (it was dark text on navy)
Remove: the recorded kiken offers both fixes, Clear kiken and reopen and Remove kiken (Aoki withdrew; court A is scoring Mori vs Sato meanwhile)
Removed: the circles gone, the real result entered (Sato K, Aoki M K), a reason asked as for any correction
Saved: the match reads K vs MK, Aoki's next match can start, Mori vs Sato was never interrupted
While the removal is pending, the name carries no Kiken mark (Undo brings it back)
Team match: removed, bouts 2 and 3 become rows to fill (Save refuses until they have a result)
Team match saved: Kyoto wins 2-1 on bouts and can compete again
Buttons name the recorded decision
Shared court: the move confirm names the match
Shared court: court B, open in another tab, names the moved match about 0.3 s after the move, with no reload, and stays on Teams
Shared court: one tap switches court B to Individual, the moved match is Up next
Merge: a kiken made offline before a point recorded on another device. The point stands, the match keeps running, the kiken is kept in the history and nobody is barred
Merge: a late point that would leave a finished knockout tied. The match keeps its finish, the point is kept in the history, and the operator is told it needs a winner (the Scores tab's editor, which stays open; on the court console the editor closes when the match finishes and the same message comes as an alert)
Merge: a court offline ticked overtime after another device recorded a kiken. Back online, the overtime is held and the court is told which decision closed the match and where to change it; the history records "a kiken closed this match" (the Scores tab's editor, as above)
Kachinuki Record bout, then the next tap: the winner stays on against Red's next fighter, and the first point on the new bout saves cleanly ("Synced", no held note)
Gaps: a court console back online moved on by itself (Match 1 had been started on another device)
Gaps: the held-writes list from the top bar, with Discard for a write the server keeps refusing
Gaps: the editor's banner once the server keeps refusing a held result
Gaps: the first bout to be played is Match 1 on the court console and in the scores list
Gaps: a result finished while the browser's storage is full
Review history
Every finding was checked against the code before anything changed. Each fix comes with a test that fails without it, except where noted.
displacedGroups). A write that does not name the result keeps a hantei mark. The queue holds nothing behind a failing write. Request-only fields never reach the cached copy. CodeQL's allocation alert fixed.runningMatchesbody on every 409 that returns it; a table cross-reference by name; this body rebuilt with the upgrade and rollback notes and every changed file. (Docs only, no test.)match_history_crash_test.go): a real transaction whose file apply fails on the match file or on the history file, a crash before the apply and one between the two files, each replayed on restart with both landing, and an abort that leaves neither; it fails whenAppendMatchHistorybypasses the transaction log. The OpenAPI reopen text said a force-reopened match loses its bouts and that a bye is refused (it keeps its points and the bye is unwound). Two stale code comments. The four untested items below were checked in a browser. Found on the way: the release packaging defect and the armed-button colour (the colour has a CSS test that fails without the fix; the release fix was checked with a goreleaser snapshot build, whose binary served all six UI assets, against the v2.1.0 release, where the new smoke step stops at Preact's 404). All PR screenshots were recaptured on this build.dropHeldWriteserves its own Discard held result and the edge a discard from the top bar produces. A render test covers both doors in all three editors and fails without the disarm; in the browser, after the server refused a finish 11 times and the operator discarded it, the button read "Finish + Start Next →" and the queue was empty. The held overtime line reads "Kept in history: overtime, started" rather than "overtime, overtime".Dismissed with evidence: "re-parses the same files" (config, pool and bracket loads are served by the version and mtime cache); "the kept-in-history note can leak across matches" (every host keys the editor on its match).
Notes for review
{queued: true}until the flush sends it; a finish that carries none of its own scoreline cannot be replayed under a newer tying change and is held; the history file is rewritten whole on each write to its match; two writes floored by the same seen stamp share it; a correction held whole when a court still scoring moved the result stamp first; a write the disk refused after its log committed is answered 500 and retried, and a Discard does not stop it applying at restart.match_history_crash_test.go; queued writes across a rollback in a browser (refer to Upgrade and rollback); the lineup panel's Save lineup with the fighter list in a browser (a click commits, a double tap places one fighter, a focused box reopens the list, and the saved lineup carries each member's id); and the Record-bout floor with the page's server time set 2.5 s behind, where the next tap's own estimate was older than the appended bout and it went out at the bout's stamp plus 1 ms and applied.config.mdonce per kachinuki competition on first startup. On a tied level bout X outranks (E), so only "Undo encho" shows the overtime until a point lands.runningDownstreamRefusalmay go if bc-dlck removes the T103 block. bc-offl's e2e fixme flip lives on PR test(bc-e2eh, mp-yqxn.2): e2e harness and a clumsy-operator review of knockout and mixed #442.76ffb044,37d4187e,0ec2e3ea,47d739da,8b90315c) and two that quote it to say it was removed (aa81d5c7,53f54164). Rewording them needs a force-push; a squash merge leaves none of them.Test plan
make go/testpasses (lint, security scan, tests), re-run after the gap fixesmobile-app --folderon a scratch folder)docs/(make docs/buildand the link check pass)make docs/screenshots SINCE=main: the six editor shots gaining the History line and the two standings shots naming the decisions; the other 23 unchanged. Remove changes no docs screenshot: none shows the recorded-decision box. After the gap fixes,make docs/media SINCE=mainagain: all 31 screenshots unchanged (two needed a second run after a seeding timeout), andkachinuki-demo.webmrecaptured because it shows the armed End matchCloses bc-rfsw
Closes bc-offl
Closes bc-rboq
Closes bc-flst
Closes bc-kheb
Closes bc-mrgc
Closes bc-hlck
🤖 Generated with Claude Code