Owner review 5, lane G: scoring on the honor system, and how to treat a retirement (2026-10-12)
Status: Draft · Date: 2026-10-12 · Lane G of owner review 5, point 4 (the flow half) and the mid-review question on retirement. Inputs: library §10 (Scoring) as built, review 4 lane D §3 to §4, review 3 lane C §3, the kit's scoring pieces in flat-v2-kit-controls.jsx, the shipped RPCs and tables, and sources fetched on 2026-10-12 (§0, §9). Audience: Korean club members aged 40 to 60. Nothing outside this file was changed.
Owner, review 5, point 4. "...to simplify the flow, I was thinking we can rely on an honor system for score entry. Any participant of the match can fill out the score (and we need to take care of racing scenarios properly so that we don't accidentally override written scores). And once a score is submitted, the participant can appeal for a change, and the host/admin has the authority to intervene and change the score manually. ... This way, we can remove many of the complicated scenarios and meta data and helper texts, and keep things simple. We should simply have a simple request for review or arbitrage flow." Mid-review: "As for retirement, what's the best practice?"
0. Evidence tags and limits
| Tag | Meaning |
|---|---|
| C | Read in our code, migrations or library today, with path:line. |
| P | A primary page of the organisation itself (its help centre, rulebook or regulations), fetched 2026-10-12. |
| S | A secondary page (a blog, a federation section's local rules, a forum). Single-source claims are marked as such. |
| X | Not reached. Said so rather than guessed. |
Not reached today: the current DUPR help-centre pages (only DUPR's 2024 blog post and a third-party guide were readable), the Playtomic player-help article on posting results and the one on editing results (HTTP 403 both times; the 24 h rule below is from the search snippet of the first, so S), the Tennis Australia league-manager page (403), the current ATP and WTA rulebook text (the 2022 PDF is compressed binary), the ITF World Tennis Number rules on retirements (nothing found), the USTA dynamic-rating treatment of retirements (not documented in anything found), Global Tennis Network, Tennis Round, TopTennisLeague and LTA Rally (no public rules found), and KATO or KATA ranking rules for 기권승 (nothing found). Where a claim needs one of these it says X. Nothing is inferred into a peer's behaviour.
1. At a glance
Is the honor system sound? Yes, with one change to how we think about it. Every peer that lets people enter their own scores protects itself with a counterparty step (confirm, deny, or a 24 to 72 hour window) or with a human arbiter. The owner's proposal replaces the counterparty step with an appeal after the fact, and the sensible reading is: entered means settled and rated; an appeal never holds anything; the host can change it. That is closer to Battlefy and USTA's auto-confirm than to DUPR, and it works because a club session has a named host in the room.
| Point | Recommendation (decided by evidence unless marked taste) |
|---|---|
| Who enters | Any of the match's players; the host or an official always can. First commit wins, checked against matches.score_revision (it already exists, C). A second writer sees the stored score and can only appeal (§3). |
| Appeal | Any player of the match, until session date + 7 days. It carries an optional proposed score and nothing else: no reason picker. It never holds the gate (§4). |
| Host review | Keep, change, or exclude. A host who plays in the match never judges it; with no other official the score stands (§4.3). |
| Ratings | Apply at entry after the existing 8 s undo; a host change replays through the existing recompute_all_ratings (§5). Taste question Q1: peers wait 24 to 72 hours, we do not. |
| State machine | 12 states become 7, 34 transitions 14, 19 races 5, 21 pushes 5, 14 policy keys 7, four sheets and a request row two sheets (§6). |
| Screens | ScoreEntry keeps the keypad and gains avatars; its helper lines go from two to zero; one component serves enter, appeal and host (§7). |
| Retirement (the owner's question) | It counts in the record as a win for the side that stayed. It is rated only if either side has won at least 4 games (UTR's published line), on the score as played, with no extrapolation. Half weight is our own house value, not UTR's (§8). |
2. How others do it (point 4, part 1)
2.1 The table
| Product | Who enters | Who confirms | Dispute / appeal | Arbiter | When rating applies | Tag |
|---|---|---|---|---|---|---|
| DUPR (recreational) | A player (the article does not say who) | "all players involved" must confirm | Any participant can reject; "the match is automatically removed". No window stated. A group chat of up to five is suggested | None named; support for other questions | Only once validated; "pending validation" matches "don't affect ratings" | P |
| DUPR (organised play) | The organiser, or "allow players in the pool/court to enter their own scores" | Not by the players | Organiser's own tools | Organiser | As partner-submitted matches | S |
| Playtomic | A player of the match | One opposing player, or automatic | Reject: the result "remains suspended" and is re-entered; Playtomic does not arbitrate and points players to the match chat | None (players agree among themselves) | Accepted at once if an opponent accepts, else automatically after 24 hours | S (snippet; page 403) |
| UTR (self-posted, casual only) | Any logged-in user | The opponent can confirm or deny by email | Denied: "Rejected", seen only by the poster. The poster may delete a score "at any time, whether pending or accepted" | UTR support can "add or edit" scores on a record | Confirmed scores affect ratings "the following day"; denied do not | P |
| LTA World Tennis Number (UK) | A player | The opponent | The opponent can correct within 72 hours; after that, auto-confirmed for Advantage members | Not stated | Not stated on the page | P |
| USTA League (TennisLink) | Either captain, within 48 hours (section rules) | The other captain has 48 hours | Dispute in writing to the league coordinator within 48 hours, or before the next match (section rules) | Local league coordinator | After the 48 h rule auto-confirms | S (section rules, Dayton and others) |
| Battlefy (esports) | A participant | The opponent, with a countdown | "Report incorrect" raises a dispute for admins; with no choice the score is auto-confirmed when the timer ends | Tournament admin, who enters the correct score by hand | Not applicable | P |
| Ultimate Tennis (ladder) | The winner reports | Players confirm between themselves first | Mid-match disputes go back to the last agreed score | Not stated | Not stated | S |
| Korean club practice | The 총무 or the host records on the 대진표 | Nobody (the record is the host's word) | Raised in person or in the club chat | The 총무 | When the host records it | X (no published source; lane D §3.1 found the KATO flow is 본부석 practice, not text) |
2.2 What the table says
- No peer lets one person enter a score that nobody can answer. Everything above has either a counterparty step (DUPR, Playtomic, UTR, LTA, USTA, Battlefy) or a host who is the only entrant (organised pickleball, the Korean practice). The owner's proposal sits between the two: anyone may enter, nobody confirms, anyone may appeal, the host decides.
- The closest working precedent is the timer-based auto-confirm. Battlefy, USTA (48 h), Playtomic (24 h) and LTA (72 h) all end up at "the score stands unless someone objects". Our proposal is the same outcome with the clock set to zero, because a club session has the host and four people in the same place. Said plainly so the owner can veto it: zero is our choice, not a peer's number.
- The arbiter is always a person who is not a party. Coordinator, tournament admin, support, or the organiser. Playtomic is the one that refuses to arbitrate, and its own help says players must settle it in chat. We have a host, so we arbitrate.
- Every peer keeps ratings away from an unanswered score (DUPR, UTR, Playtomic's 24 h). This is the real risk in our proposal and §5 deals with it.
- UTR lets the poster delete a score at any time. We do not copy that: a deletion after the next match has started would break the gate. Our 8 s undo and the host's exclude are the bounded equivalents.
3. Races with no confirmation step (point 4, part 2)
3.1 What the code gives us (C)
- One write path per match:
private.lock_match_score_write(match_id)takes the rating-replay barrier (shared), the departures lock and the session-roster lock before the row (supabase/migrations/00681_full_set_score_corrections.sql:85-96). - A version already exists:
matches.score_revision, bumped by trigger whenever the score, status, sets, format, outcome or completion time changes (00681:105-131). It is the version check; no new column. - An idempotency key already guards a replay (
public.submit_match_score,00641:691-720), with the rate limit placed after it so a retry is free. - What is missing:
private.submit_match_scoreauthorises only the session creator and club staff (00507:394-404), so a player cannot write today. Players go throughmatch_score_calls(one row per match,agreed_by/rejected_by,00149:15-26) andagree_to_call, which needs every participant. The honor system replaces that table's confirm path; it does not extend it. - The score also applies ratings inside the same call:
RETURN private.apply_match_ratings(...)(00507:529). So today a host's entry is already final and rated at once. The honor system generalises that to players.
3.2 The rule for each race (the five that remain)
All five use the same lock and the same refusal. The write carries expected_revision (the score_revision the writer last saw; 0 for a new score). If it differs, the server returns score_changed with the stored score and writes nothing.
| # | Race | Rule |
|---|---|---|
| Q1 | Two players enter at once | First commit wins. The second gets score_changed and its sheet turns into the appeal sheet, showing the stored score and keeping the digits it typed as the proposed score. The writer who lost does not retype; one tap on 검토 요청 sends it. Same sets typed twice: a no-op, Toast 이미 입력됐어요 and nothing to appeal. |
| Q2 | Entering while someone else is editing | There is no edit state to race with. Between the sheet opening and the send, any write bumps score_revision, so the send is refused as in Q1. Nothing is ever overwritten, including by the host. |
| Q3 | Entering after the host has entered | The host's entry is revision 1 like any other. A player who opens ScoreEntry after it lands sees the score, not an empty keypad (the door is gone, §7), and may only appeal. |
| Q4 | An appeal racing a host change | The appeal carries the revision it was written against. If the host changed the score first, the appeal gets score_changed, the sheet shows the new score under the Notice 방금 점수가 바뀌었어요, and the appellant may appeal again. If the appeal lands first, the host's change simply closes it (§4.2). Two appeals on the same revision merge into one open review; there is no second row. |
| Q5 | Offline entry, or an entry that arrives late | The client queues the write with its idempotency key and the revision it saw. On replay the server applies Q1 to Q4. A refused entry is never dropped silently: the match row shows 점수가 이미 있어요 and the unsent digits wait in the sheet as a proposed score for one tap. A write past the entry window gets window_closed and goes to the host. |
Two further guards, not races. The entrant's 8 s 되돌리기 withdraws the score only while nobody has appealed and no later match has started; the gate (R14 and R19 of the library) is unchanged: a start reads the settled state under the same lock.
Deleted races: R1 to R13, R15 to R18 of library §10 (13 rows disappear with the confirm step, the counter, the contest window and the timers). R14 and R19 (two hosts start, a start racing a write) are merged into the one remaining guard above. R15 to R18 concern held players, offline gate opening, corrections and substitutes; the gate rules for them are unchanged but they are properties of the gate, not of scoring, and stay in the gate's own spec.
4. The appeal (point 4, part 3)
4.1 The shape
| Question | Recommendation | Basis |
|---|---|---|
| Who may appeal | Any of the four players, including the entrant (a typo found after the 8 s undo). The host may also open a review on any match (no appeal needed) from the row. | Battlefy: the other side flags; USTA: either captain can dispute. In our model there is no "other side" to wait for. |
| Until when | Session date + appealDays (7). After that only the host or an official can change a score. It reuses the shipped correctionDays value, so no new number. | Library policy correctionDays = 7 (C, library §10 Result policy); USTA 48 h and LTA 72 h are for the confirm step we do not have. |
| With what | An optional proposed score, entered on the same keypad. No reason picker, no note. Leaving the digits unchanged sends "please check", which is the useful case when the player is unsure. | The three-reason Dispute sheet exists to route a dispute (wrong players, did not play); the host sees the roster and the scorecard and can tell. Playtomic and UTR collect no reason either. |
| Does an open appeal hold the gate | No. Entered is settled even with an appeal open. The match and its rating stand; the appeal is a flag on the row. | See 4.2. This reverses the S5 reading of review 4. |
| Who is told | The host and officials who are not playing in that match (one push, high). The appellant gets the decision. The entrant is told only that a review was asked, not by whom or with what (it avoids a quarrel in the court). | Library rule 6: tell each person once, in their role. |
4.2 Why an open appeal must not hold the gate
The owner wanted disputes settled "right now, as soon as the match is over" (review 4). That was a wish for speed, and the honor system already delivers it: the score is in and the next match can start the moment it is entered. Holding the gate on an appeal would hand any one player a way to freeze a session, and the only person who can lift it is a host who may be on another court. Evidence: no peer holds the next game on a dispute (lane D §3.1: "I found no peer that hard-gates the next match on the previous score"), and our own stop outcomes exist precisely so that a stuck match never freezes a session (library R18). The trade-off: a wrong score can feed one more match's pairing while the review is open. That is bounded by the host being told at once, and a host change replays ratings (§5). Taste, not evidence: if the owner wants the harder rule, the alternative is "an open appeal holds only the appellant's own next match", which still cannot freeze the others; it is one guard in deriveMatchStartGate.
An appeal not decided by appealStandDays (7) stands as entered, unlike today's S5 which becomes 기록 제외. In an honor system the entered score is the default, not a proposal, so silence cannot erase it.
4.3 How the host resolves it
| Action | Effect |
|---|---|
| 그대로 유지 | The review closes; the score and ratings stand. The appellant is told. |
| 이 점수로 바꾸기 | The proposed score replaces it (a new revision); ratings replay; all four are told. Shown only when a proposal differs. |
| 점수 고치기 | The host types another score on the keypad (the same ScoreEntry, prefilled); same effect as above. |
| 기록 제외 | The match becomes not counted (outcome non_competitive), the rating reverts by replay. Used when the match was not played. |
A host never judges a match they play in. Keep it: the host is a player there and the other three have no reason to trust them in their own cause (library rule 5). Who arbitrates when the only official is playing:
- Another official of the session or club (a co-host,
match_director, club admin or owner), including one who is not at the court; the review reaches them by the same push. That is the shipped authority list (00507:394-404: clubowner,admin,match_director, or the session creator). - No other official exists (a pickup session with one host who plays): the score stands and the appeal stays on the record. It does not freeze (library R18) and nobody is made judge in their own cause. The host sees the flag on the scorecard; the appellant sees 검토할 수 있는 운영자가 없어요 and the row stays tagged. This is the honest outcome; the alternative (a four-person vote) is the confirm step returning. Evidence: DUPR's own validation has no admin at all, and Playtomic declines to arbitrate; leaving a conflict of interest unresolved is how both end.
5. When ratings apply (point 4, part 4)
| Option | For | Against |
|---|---|---|
| A. At entry, after the 8 s undo | One clock, the same as the gate; matches the shipped host path (apply_match_ratings runs inside the call, 00507:529); the player sees the change as the match ends. | Every peer waits (DUPR for all players, Playtomic 24 h, UTR a day, USTA 48 h, LTA 72 h). A wrong score moves four ratings until it is changed. |
| B. At session end | One rating run per session, appeals mostly already settled. | Needs a trigger that does not exist for sessions the host forgets to close; the player waits hours for the number the app is built around; two clocks (gate and rating). |
| C. After an appeal window (24 h) | Closest to peers; fewest replays. | Contradicts "entered = settled"; the next match's pairing would use stale ratings. |
Recommendation: A. Evidence: (1) the shipped host path already rates at entry, so A removes a difference rather than adding one; (2) the replay exists and is already exclusive-locked: agree_to_correction takes lock_rating_replay(true), bumps score_revision and calls private.recompute_all_ratings() (00681:503-583); (3) peers wait because their entry has no host in the room, and the wrong-score risk they are guarding against (an unsupervised pair inflating a number) is exactly what the host-present club session lacks; (4) B and C each add a clock and a state, which is what the owner asked to remove. Trade-off, honestly: replays cost more than one-shot applies, and a visible rating that later moves can irritate. The cost is bounded by appealDays and the expected rate of appeals (low in a club), and the rating Toast already waits for the undo window.
One guard to add: a rating change from a host change after the session has ended replays only that match's players, and each gets one line in their rating history ("점수가 수정되어 다시 계산됐어요"), never a push per player beyond the decision push (§6.5).
Taste question Q1 (below): "Rate at entry, or wait 24 hours like every peer?" My answer is A; the owner owns the risk appetite.
6. The new state machine (point 4, part 5)
6.1 Rules
- Entered = settled = rated. A score is an assertion by someone who played; nothing waits on a second person.
- One writer per match, first commit wins (§3). No write overwrites a stored score except the host/official's explicit change, which carries the revision it replaces.
- An appeal asks, the host decides. Appeals never change a score and never hold the gate.
- Nobody judges their own match (§4.3).
- Tell each person once, in their role, and only when it needs them.
6.2 States (12 become 7)
| ID | State (ko · en) | matches.status / outcome | Rating | Tag | Entered by |
|---|---|---|---|---|---|
| H0 | 예정 · Scheduled | scheduled | none | none | the draw |
| H1 | 경기 중 · Playing | in_progress | none | none | 시작 (gated: each player's earlier matches are H3, H5 or a stop outcome) |
| H2 | 점수 입력 전 · Awaiting | awaiting_result | none | none | the host ends the round, the next round starts, or the rotation timer |
| H3 | 입력됨 · Scored | completed; outcome decisive, retired, walkover, defaulted | applied | none (stop outcomes keep their Tag) | any player enters (T-enter); a host or official enters or changes it |
| H4 | 검토 중 · Under review | as H3, plus one open review | stands | 검토 중 (warning weak) | a player appeals (A4) |
| H5 | 기록 제외 · Not counted | completed, outcome = non_competitive | none (reverted) | 기록 제외 | the host or an official; 30 days with no result |
| H6 | 중단 · Suspended | in_progress, outcome = suspended | not yet | 중단 (warning) | a stop with a score so far; it releases the gate for the others |
Deleted states: S3 확인 대기, S5c 분쟁 (countered), S5 분쟁 (disputed), S4h 호스트 확정, S4c 이의 접수, R 수정 요청 (overlay); S4 확정 merges into H3. The Tags 확인 대기, 분쟁, 호스트 확정 go with them. A host-set result is no longer a state, only the attribution line the scorecard already draws (edit-attribution-line.tsx).
6.3 Transitions (34 become 14)
| id | Event | Guard | Effect |
|---|---|---|---|
| A0 | H0 → H1: start | The gate is open for all four players (review 4 ruling, unchanged); a host or official; no override. | Held matches listed with the reason. |
| A1 | H1 → H2: the match ends | 라운드 종료, the next round, or the rotation timer. | P1 to the four. |
| A2 | H2 → H3: enter 점수 저장 | A player of the match, a host or an official; expected_revision = 0; valid sets; idempotency key; not past the entry window. | Revision 1; verified_by the entrant; ratings after the 8 s window; the gate opens; P2 to the other three; Toast 저장했어요 · 되돌리기. |
| A3 | H3 → H2: 되돌리기 (undo) | The entrant, within 8 s, no review open, no later match started. | The score is withdrawn before rating; the log keeps it. |
| A4 | H3 → H4: appeal 검토 요청 | A player of the match, within appealDays, expected_revision equals the stored one; an optional proposed score; one open review per match. | P3 to the host and the officials not playing; the row shows 검토 중; ratings and the gate are untouched. |
| A5 | H4 → H3: 그대로 유지 | A host or official who does not play. | Review closed; P4 to the appellant. |
| A6 | H3 or H4 → H3: change (이 점수로 바꾸기, 점수 고치기) | As A5, or the entrant's own host role; expected_revision carried. | Revision + 1; ratings replayed; P4 to the four. |
| A7 | H3 or H4 → H5: 기록 제외 | As A5. | Ratings reverted by replay; P4 to the four; the gate stays open. |
| A8 | H4 → H3: the appeal is withdrawn | The appellant. | Review closed; nobody is told. |
| A9 | H4 → H3: stands | appealStandDays (7) with no decision. | Review closed as kept; P4 to the appellant. |
| A10 | H1 or H2 → H3 (stop outcome): 경기를 끝까지 못 했나요? | A player of the match, a host or an official records 기권, 부전승, 취소됨; 실격 is host or official only; a winning side is required. | As A2; walkover and cancelled are not rated; 기권 is rated per §8. |
| A11 | H1 → H6: suspend 중단 | The host or an official, or a player. | The score so far and a resume plan; the gate opens for the others. |
| A12 | H6 → H1: resume, or H6 → 미완료 (7 days, or 여기서 끝낼게요) | The host or an official, at the next session or the planned day. | Continue from the stored score; 미완료 is not counted or rated. |
| A13 | H2 → H5: exclude (not played), or 30 days with no result | A host or official; the 30-day timer (noResultVoidDays). | P4 to the four; no rating. A reopen (H5 → H2) is the same host action. |
(A13 includes the shipped "reopen"; the table keeps 14 rows by counting A0 to A13.)
Unchanged from review 4 rulings: the sequential gate (each player's earlier matches must be H3, H5 or a recorded stop outcome) and the stop outcomes table and their numbers (library §10 Stop outcomes; §8 below corrects the retirement weight's attribution).
6.4 Who may do what
| Viewer | H2 | H3 | H4 | H5 |
|---|---|---|---|---|
| A player of the match | 점수 입력 | 검토 요청 (the entrant also 되돌리기 for 8 s) | read; withdraw their own | read |
| Host or official, not playing | 점수 입력, 기록 제외 | 점수 고치기, 기록 제외 | 그대로 유지, 바꾸기, 기록 제외 | 다시 열기 |
| Host who plays in the match | as a player | as a player | as a player (another official decides, else it stands) | as a player |
| Anyone else | read | read | read | read |
6.5 Pushes (21 become 5)
One push per change, to the people it concerns, with the body naming the round and court so it reads alone.
| P | When (to whom) | Title | Body |
|---|---|---|---|
| P1 | A1, to the four; repeated once after gateNudgeMinutes (3) only while a successor is held | 점수를 입력해 주세요 | 3라운드, 2번 코트, 경기가 끝났어요 |
| P2 | A2, to the other three players | 점수가 입력됐어요 | 3라운드, 2번 코트, 김민준 님이 6-4를 입력했어요 (the body names who, because the order of the numbers is the entrant's) |
| P3 | A4, to the host and the officials who do not play (high) | 점수 검토 요청이 왔어요 | 3라운드, 2번 코트 |
| P4 | A5 to A7, A9, A13, to the appellant and the four where the score changed or the match left the record | 점수 검토가 끝났어요 / 점수가 바뀌었어요 / 기록에서 제외됐어요 | 3라운드, 2번 코트, 6-4로 유지됐어요 / 6-3으로 바뀌었어요 |
| P5 | A2, A5 to A7, A10, A13, when a held match is released, to the host and the players whose match was held | 다음 경기를 시작할 수 있어요 | 3라운드, 2번 코트 |
Deleted pushes: N2 (second entry reminder), N3 and N4 and N20 (confirm asks and reminders), N5 (edited), N6 (cancelled), N7 (confirmed), N8 and N9 (declined, countered), N10 and N10b and N21 (dispute and escalation to the host, merged into P3 and the held-row on the board), N11 and N12 and N18 (host decided, excluded, expired, merged into P4), N13 and N14 (contest), N15 and N16 (correction), N17 (reopened). N1 becomes P1, N19 becomes P5.
6.6 Policy (14 keys become 7)
| Key | Value | Meaning |
|---|---|---|
entryWindowDays | 7 | players enter until the session date + 7, then the host or an official |
noResultVoidDays | 30 | no result 30 days after the session becomes 기록 제외 |
appealDays | 7 | players may appeal until the session date + 7 (was correctionDays) |
appealStandDays | 7 | an appeal not decided in 7 days stands as entered (was disputeVoidDays) |
gateNudgeMinutes | 3 | the one repeat of P1 while a successor is held |
suspendedResumeDays | 7 | a suspended match not resumed becomes 미완료 |
retiredMinGames | 4 | a retirement is rated only if either side has won this many games |
Deleted keys: autoConfirmHours, reminderHours, hostEscalationHours, entryReminderHours, contestHours, hostDisputeReminderHours, gateEscalationMinutes; correctionDays and disputeVoidDays are renamed above. The 8 s undo stays a constant of the Toast.
6.7 What is deleted from library §10, in one table
| Kept | Deleted |
|---|---|
| States H0 to H6 (7) | S3, S5c, S5, S4h, S4c, R and S4 as a separate state (6 gone, 12 to 7 with S0 counted) |
| Transitions A0 to A13 (14) | T3 to T10, T14 to T17, T21 to T28 (22 rows); T0, T1, T2 merge into A0 to A2; T11 to T13, T18 to T20, T29 to T33 fold into A5 to A13 |
| Races Q1 to Q5 (5) | R1 to R13 (the confirm, counter, contest and timer races); R14 to R19 move to the gate's own spec |
| Pushes P1 to P5 (5) | N2 to N18, N20, N21 (19) |
| Sheets: ScoreEntry (3 modes), Host review | Confirm sheet, Dispute sheet, Decide sheet, Result request row (the 확인할 점수 list), the Counter and Stale variants, the conflict variant of ScoreEntry |
| Timers: entry window, 30-day void, appeal window and stand, gate nudge, suspended expiry | 12 h reminder, 24 h escalation, 24 h counter expiry, 48 h contest, 7-day dispute void, daily digest |
| Helper lines: 0 on ScoreEntry | 상대 팀 한 명이 동의하면 확정돼요 · 확정되면 다음 경기를 시작할 수 있어요 · 저장하면 바로 확정되고 선수들에게 알려요 · 상대 팀 한 명이 동의하면 고쳐져요 · 입력한 사람이 먼저 확인해요 and the Dispute and Confirm helper tables |
7. The screens, minimal (point 4, part 6)
7.1 ScoreEntry: one component, three modes
Mode enter: any player, host or official. Mode appeal: the same Sheet, title 점수 검토 요청, cells prefilled with the stored score, button 검토 요청, no stop link. Mode host: same as enter for a host or official on a match with a stored score (title 점수 고치기). Keypad first stays, docked from the first frame (library §10 ScoreEntry; owner: "Score entry on the number pad is great").
| Control or line | Today (player, §10 ScoreEntry) | Proposed |
|---|---|---|
| Title row, × and the match name | 2 | 2 |
| Set cells | 2 per set | 2 per set, each team line led by its avatar pair (point 3 and 5 of the review; grammar owned by lane E/F) |
| Tiebreak cell | at 6-6 and 7-6 | same |
| Stop link 경기를 끝까지 못 했나요? | 1 | 1 (enter mode only) |
| Helper lines | 1 or 2 (확인 요청 fact, gate fact) | 0 |
| Button | 확인 요청 (player), 점수 저장 (host), 수정 요청 (correction) | 점수 저장 (everyone), 검토 요청 (appeal) |
| Variants | player, host, correction, one set, bo3/5, conflict, stop x3 (9) | enter, appeal, host, one set, bo3/5, stop x3 (8) |
The avatar rows are the Team lines grammar of review 4 lane E; ScoreEntry takes the same primitive so there is one match-row grammar (owner, point 4). Two things this removes: the conflict variant (Q1 turns into the appeal sheet) and the three button names. The gate fact moves to where it is felt: the held row on the 대진표 says why, once (it already does).
7.2 The match row and block once a score is in
- No confirm buttons anywhere: the Result request row, the 확인할 점수 list and the AnswerPair (동의 · 거절) are deleted.
- No door for a player on a scored match (the chevron law of review 4: no action means no door).
- The only mark is the Tag. A plain result shows none. An open review shows 검토 중. A stop outcome keeps its Tag (기권, 부전승, 중단). The old 확인 대기, 분쟁 and 호스트 확정 vanish.
- The appeal affordance lives in the Match state block of the scorecard, not on the row, as one text action 점수 검토 요청 › (the canon bars CTAs in cards). That is one entry point and no new element on any row.
- The host's door is the board's 할 일 Task Cell 검토 N경기 (the old 분쟁 cell, renamed), which opens the list of Match rows; a row opens the host review sheet.
7.3 One appeal sheet and one host review sheet
- Appeal sheet = ScoreEntry mode
appeal(above). Controls: ×, set cells, button 검토 요청. Helper lines 0. - Host review sheet (new, replaces Decide): title 점수 검토, the match as its sub; 입력된 점수 as Team lines (with avatars); when a player proposed one, {이름} 님이 말한 점수 under it. Footer pair: 그대로 유지 (tonal) · 이 점수로 바꾸기 (or 점수 고치기 with no proposal, which opens the keypad prefilled). One text action 기록 제외. Controls: ×, two buttons, one text action (4). Helper lines 0.
7.4 Counts against the current design
| Piece | Controls now | Controls proposed | Helper lines now | Helper lines proposed |
|---|---|---|---|---|
| ScoreEntry (player) | ×, 2 cells per set, stop link, button (4 plus cells) | same | 1 to 2 | 0 |
| Confirm sheet | ×, 2 buttons (variants add edit, cancel, a Cell) | deleted | 1 | 0 |
| Dispute sheet | ×, 3 radios, optional cells, button | deleted (the appeal is ScoreEntry) | 4 variants | 0 |
| Decide sheet | ×, 4 radios, optional cells, button | Host review: ×, 2 buttons, 1 text action | 1 | 0 |
| Result request row | 2 buttons plus a row | deleted | 0 | 0 |
| Match row, Tags | 5 result Tags (확인 대기, 분쟁, 호스트 확정, 확정, 기록 제외) | 1 added (검토 중), 기록 제외 kept | n/a | n/a |
| Total sheets | 4 sheets and 1 row | 2 sheets (ScoreEntry, Host review) | 7 helper variants | 0 |
8. Retirement: the plain answer
It counts as a win for the side that stayed, in the record. It is rated only if either side has won at least four games, on the score as played, with nothing added for the games not played. The weight (half) is our own house value, not UTR's, and the owner should know that.
8.1 What the sources say
| Source | Retirement | Walkover / default | Tag |
|---|---|---|---|
| UTR | A match is excluded when "the match starts but neither player wins at least four games (due to a withdrawal/retirement)". Incomplete matches "count when at least four games were won by either player". The rating is "based purely on scores"; no half weight is documented. | "It does not take into account walkovers, defaults, or double walkovers and abandoned matches"; but for a default, "the games that were completed before the default may still contribute". | P |
| ITF World Tennis Number | X: no rule found on retirements. | X | X |
| USTA NTRP / dynamic | Section regulations say RETIRED matches "shall count" for eligibility; how the dynamic rating treats a retired score is not documented in anything found. | Defaults count only in limited cases for eligibility (section rules). | S, X |
| DUPR | "Retired matches do count"; the score at retirement is recorded and "the remaining points needed to complete the game are typically awarded to the opponent" (extrapolation). Single source (a pickleball.com blog), not DUPR's own page. Forfeit counts if 7 or more points were scored; a withdrawal before play does nothing. | As left. | S (single) |
| ATP | A retirement is an official result with the win to the opponent; the retiring player keeps points already earned in the event (a 2023 rulebook table quoted in a journal supplement and a second article). The ATP rulebook PDF itself was not readable. | A walkover is "neither a match win nor a loss" for the record (a tennis blog, S); the ATP awards walkover winners points "as if the match had been played" (same blog). The WTA's treatment was not confirmed (X). | S |
| FIDE (Elo, chess) | Not a retirement rule, but the analogy: "any game where both players have made at least one move will be rated"; "games that go unplayed, whether through forfeiture or any other reason, are not counted". | Unplayed games are not rated. | P |
| KATO / KATA (Korea) | X: no published 기권승 point rule found. | X | X |
8.2 Correction to lane D
Lane D §4.5 and the library's stop-outcomes table say "half weight if either side has 4 or more games (UTR)". UTR is the source of the four-game line only. UTR documents no discount: it counts a qualifying retirement on the score as played. The half weight comes from the shipped incomplete branch of the rating engine (00590:238-242: actual 0.6/0.4, weight 0.5) and lane D itself labels it "a taste value, not evidence". The library table should read "rated, at the house weight, if either side has retiredMinGames".
8.3 Recommendation
- Win-loss: yes. The side that stayed is the winner (
winner_side), the retirer takes the loss. Backing: ATP (opponent credited), DUPR (opponent wins), FIDE (a game with moves is a game), USTA (retired matches count). The owner has already ruled that retirements count. - Rated: yes, but only if either side has won at least 4 games (
retiredMinGames). Backing: UTR is the one primary source with an exact line; FIDE has the same shape (a minimum played before it counts). Below it, the match is in the record and not rated. - Weight: keep the house half weight. The evidence does not give a number (UTR gives none; DUPR extrapolates, which we do not store the data for). The reason for a discount is ours: our rating is a win/loss Elo, and a retirement while the retiree was ahead (a 3-1 lead, the retirer wins on the scoreboard but loses the match) would reward or punish a full step for a result that stopped for injury. Half weight limits that swing. If the owner prefers UTR's literal rule, set the weight to 1.0 in the config; no code changes.
- No extrapolation of the unplayed games (UTR; DUPR's remainder rule needs sets and points we do not store).
- How it is written:
6-4 3-1 기권(6-4 3-1 ret.), the Tag 기권 on the retirer's side of the match, the winner emphasised per the existing rule, andwinner_siderequired on the record (the retirer is not necessarily behind). A walkover is written부전승with no numerals and never rated. - Honor system effect: any player of the match may record 기권 (the retirer usually says so himself); a false retirement is an appeal like any other. 실격 stays host-only because it is a ruling, not a statement.
9. Decisions
9.1 Decided by evidence
| # | Decision | Why |
|---|---|---|
| D1 | First commit wins on score_revision; a second writer becomes an appeal. | The version and lock already exist (C); no peer overwrites silently. |
| D2 | An open appeal never holds the gate. | No peer gates on a dispute; one player could freeze a session. |
| D3 | An undecided appeal stands as entered. | In an honor system the entered score is the default. |
| D4 | A host never judges a match they play in; with no other official the score stands. | Conflict of interest; DUPR and Playtomic have no arbiter either; R18 says never freeze. |
| D5 | Retirement counts in the record; rated from 4 games; no extrapolation; wording corrected for UTR. | UTR (P), ATP (S), DUPR (S), FIDE (P). |
9.2 The owner's taste (few, concrete)
| # | Question | My recommendation |
|---|---|---|
| Q1 | Rate at entry, or wait a day like every peer? | At entry. One clock, replay exists; the risk is a wrong score moving four ratings until the host fixes it. |
| Q2 | Should a retirement count at half weight (ours) or full weight on the score as played (UTR's rule)? | Half. It is cheap to change later (one config value). |
| Q3 | A pickup session with one host who plays: the score stands and an appeal stays on record. Acceptable? | Yes; the alternative is a vote, which is the confirm step again. |
10. What I could not verify
- Playtomic's 24 h auto-validation and its edit-result process: from a search snippet and a secondary search summary; the two help pages returned 403.
- DUPR's current dispute window and whether it moved to an "invalidate within 72 hours" model: one third-party source says it planned to; the 2024 DUPR blog says every player confirms. Treat DUPR as "all confirm" until its help centre is read.
- The Tennis Australia league-manager page, GTN, Tennis Round, TopTennisLeague and LTA Rally: nothing readable.
- ATP and WTA rulebook retirement and walkover text: from secondary quotations only.
- KATO or KATA ranking points for 기권승: not found; ask a 총무 or read the 대회 요강.
- USTA's treatment of a retired match inside the dynamic NTRP: not documented in what was found.
11. Sources (read 2026-10-12)
- DUPR, Ensuring Fair Play: The Importance of Match Validation on DUPR (P).
- Pickleball.com, Decoding forfeits, withdrawals and retires (S, single source for DUPR retirement).
- Pickleball.com, Players enter their own scores during events (S, search summary).
- Playtomic, How to publish your match results and How to edit incorrect match results (403; content from search summary, S).
- UTR, Post a Score, Self Posting, Effect of walkovers, retirements, defaults and incomplete matches, Matches that do not count, Can my rating be changed (P).
- LTA, How long do I have to dispute or confirm a result (P).
- USTA, Enter League Scores, USTA League Captains Guide, Dayton local league rules 2024, USTA SoCal League Rules (S, section rules; search summaries).
- Battlefy, Using score confirmation, as an organizer (P, search summaries).
- Ultimate Tennis, Scoring conflicts (S).
- FIDE, Rating Regulations, section 5.1 Unplayed Games (P).
- ATP retirement and walkover: tennis walkover explainer, 2023 ATP rulebook table in a journal supplement (S).
- Our code:
supabase/migrations/00149_match_score_calls.sql:15-26,00507_submit_match_score_sets.sql:394-404, 496, 529,00590_apply_match_ratings_decomposed.sql:238-242,00641_rate_limit_policy_registry.sql:691-720,00681_full_set_score_corrections.sql:85-131, 503-583. - Our design: library §10 (
docs/design/flat-v2-component-library-2026-10-09.md:2817), lane D of review 4 §3 to §4.5, lane C of review 3 §3.
Implementation
Order: the owner rules on Q1 to Q3; then the boards; then code. Wireframe-first applies (CLAUDE.md): nothing below is built before the board is marked.
Spec and library
- [ ]
docs/design/flat-v2-component-library-2026-10-09.md§10: replace Result states, Transitions, Races, Who may do what, Result policy and Pushes with §6 of this file; delete the Result request row, Confirm sheet and Dispute sheet entries; rewrite ScoreEntry (modesenter,appeal,host; helper lines 0; avatars) and the Host decision sheet (Host review); correct the retirement weight attribution (§8.2). - [ ]
docs/design/decision-log-autonomous-2026-09-17.md: log D1 to D5 and Q1 to Q3. - [ ]
docs/architecture/screen-blueprint.md: update the scorecard, the board 할 일 cell and the session detail block.
Kit and boards (docs/twomore_design_system/wireframes/)
- [ ]
flat-v2-kit-controls.jsx:FvkScoreEntry(addmode: appeal, removeconflict, helper lines, role-specific buttons);FvkScoreStop(unchanged apart from who may pick 실격);FvkMatchRow(drop the answer pair and the removed Tags); deleteFvkResultRequestRow,FvkConfirmSheet,FvkDisputeSheet; replaceFvkDecideSheetwithFvkReviewSheet. - [ ]
flat-v2-lib-social.jsx,flat-v2-detail.jsx: redraw the scoring frames from the new states H0 to H6. - [ ]
flat-v2-flows.jsxFlow 7: redraw as enter, appeal, host review; recount taps (retirement: 점수 입력, the stop link, a reason, 다음, a team, 다음, 점수 저장 stays 7). - [ ] Tag map: add
검토 중(warning weak); remove 확인 대기, 분쟁, 호스트 확정, 이의 접수.
Database (one migration, entity-change checklist: entity, mapper, adapter, migration, types)
- [ ] New
public.submit_match_scoreauthorisation: allow a participant of the match (the four player ids) as well as staff and the creator; addp_expected_revisionand the typed refusalscore_changed(HINT) returning the stored sets; keep the idempotency key and rate limit; apply ratings after the 8 s window. Drop and recreate per the 00502 postmortem (a changed arg list is DROP plus CREATE, not CREATE OR REPLACE).sfthe live function first. - [ ] New table
match_score_reviews(match_id, opened_by, proposed sets nullable, revision, opened_at, resolved_at, resolution); RLS read for the match's club members; writes only through SECURITY DEFINER RPCs withsearch_path = '', REVOKE FROM PUBLIC and explicit GRANT, in the same migration as the table's GRANT. RPCs:request_score_review,resolve_score_review(keep, change, exclude; refuses a host who plays in the match,not_allowed). - [ ] Retire
match_score_calls,call_match,agree_to_call,reject_call,propose_score_correction,agree_to_correctiononce the new path ships; keep the replay (private.recompute_all_ratings) and call it fromresolve_score_review. - [ ]
deriveMatchStartGate: accept H3, H5 and recorded stop outcomes; an open review does not hold. pgTAP canaries: second writer refused with the stored score; a stale-revision appeal refused; a host who plays cannot resolve. - [ ] Rating engine:
retiredMinGamesand the weight from config (MATCH_OUTCOME_POLICY), not literals; theincompletebranch (00590:238-242) reads them. - [ ] Signals: five types (P1 to P5); delete the confirm, counter, contest and correction signal types and their
signal-routes.tsentries; update the i18n registry and dead-key gate.
App code (packages/features/sessions/src/, packages/app/src/)
- [ ]
scorecard/score-edit-sheet.tsx,score-call-sheet.tsx,score-correction-sheet.tsx,multi-set-score-entry.tsx,match-card.tsxinline fields: replace with oneScoreEntry(three modes) on the keypad; add the avatar Team lines primitive from lane E/F. - [ ] New host review sheet; the board's 검토 N경기 Task Cell;
scorecard/live-tab/live-match-row.tsxanduse-live-tab-state.tsxlose the pending-call callout and agree control. - [ ]
packages/app/src/domain/session-attention.tsandpresentation/utils/session-closeout.ts(the 확인 필요 badge on Home): re-key to "open reviews" for hosts and to "no score yet" for players. - [ ] Ports, adapter, mapper, mutation hooks: delete
match-score-call.*anduse-call-match.ts; adduseSubmitMatchScore(withexpectedRevision),useRequestScoreReview,useResolveScoreReviewas module-scopecreateMutationHooks; offline queue replays with the idempotency key and handlesscore_changedas in Q5. - [ ] Tests on the test-kit: Q1 to Q5 as unit tests of the typed refusal; no hollow mocks.
- [ ] Docs:
docs/canon/match-generation.md(scoring pointer),docs/history/rebuild-log.md;yarn checkbefore reporting done.