跳轉到

D-20261030-04|後續回合屏障停滯改記 forfeit

背景與驅動力

D-20260802-02 為每個回合建立 race-ready 屏障:RACE_READY_STALL_TIMEOUT_MS(30 秒)內沒有任何嚴格進度即取消,不產生 settlement、forfeit、斷線或信譽後果,房間回 waiting。這個語意原本是為首回合 GO 前設計的。

issue 001123 修正後(2026-10-29,未另立決策),首回合 GO 後的 peer-left 只把離場者移出等待集合(peerLeftPolicy: 'drop'),續賽交給 partition guard 的嚴格多數規則。但 src/race-runtime/race-ready.ts 的停滯逾時在每個回合仍回傳 cancelled,src/race-runtime/race-session.ts 收到後走 emitPreRaceCancelled 與 abortInternal:整場中止,已完成的回合也不結算。典型情境是某成員仍在線,但卡在下一回合的資產載入(issue 001125)。canon 也沒有定義後續回合剩餘人數低於結算門檻時如何處置。

2026-10-30 維護者裁決:後續回合 stall 逾時改為把未到者移出本回合並記 forfeit,適用 D-20261029-02 整場壓縮名次,其餘成員照常開賽。草稿審閱時進一步裁定:剩餘非 forfeit 成員低於結算門檻時整場作廢,與 partition-void 結果相同,不做部分結算;簽章者資格與門檻不變。

考慮過的選項

  • 後續回合停滯者記 forfeit、其餘照常開賽;低於門檻整場作廢(採納)。一名卡住的成員不再讓全場已完成的回合作廢;人數不足時與現行分割作廢一致。
  • 低於門檻時提早結束、只結算已完成回合:簽章者只能是非 forfeit 完賽者、門檻是 ⌊N/2⌋+1,此時必然湊不到簽章;要成立須改簽章者資格或門檻,且收件驗證要求事件回合數等於開賽宣告,也須一併放寬(否決)。
  • 維持取消整場:首回合後的取消會丟掉已完成回合,且任一成員卡住即可讓整場無結算(否決)。
  • 延長後續回合的 stall 窗:只延後同一結果(否決)。

決定

1. 適用範圍

只適用於首回合 GO 之後的回合(race-session.ts 以 matchHasGone 區分,與 001123 的 peerLeftPolicy: 'drop' 同一界線)。首回合 GO 前的屏障語意不變:停滯或 peer-left 即取消,無任何後果。

2. 停滯逾時的處置

  • 後續回合的 RaceReadyCoordinator 停滯逾時時,不再回傳 cancelled,改為把尚未達 presentation-ready 的等待成員(missingPeers)移出本回合,其餘成員視為到齊、照常進入倒數與 GO。
  • 被移出者比照 GO 後網路斷線處理:寫入結算的 disconnects(forfeit),整場依 D-20261029-02 從每個回合名次壓出、不得分、總名次排在末位;信譽與斷線計次依 賽內機制.md 第 4 節既有規則。與網路斷線相同,不通知模擬端。
  • 被移出者本機若也判定自己停滯(自身未達 presentation-ready),即走既有驅逐收攤:停模擬、送 control leave 並退出回合,不再參與後續回合。

3. 結算門檻與整場作廢

  • 結算門檻指 MatchResultEvent 多簽門檻:⌊N/2⌋+1,N 一律為 original active roster(ranking ∪ disconnects 去重),signer 只能是非 forfeit 完賽者(流程/比賽結算.md LEDGER-R-095)。GO 後的 partition guard(src/race-runtime/race-partition-guard.ts)使用同一個數字:original active roster 的 floor(N/2)+1。
  • 停滯移除後,若非 forfeit 的剩餘成員數 < ⌊N/2⌋+1,整場作廢:不開始下一回合、不執行 settlement、不產生 MatchResultEvent,結果與 partition-void 相同。不做部分結算。
  • 簽章者資格、門檻與事件定義都不變;src/ledger/receive-validation.ts 要求 gridProof.context.roundCount === rounds.length 的檢查因此不需更動。
  • 未低於門檻時照常續賽,各回合既有作廢與經濟規則(finisherCount < 3 的整場經濟 void、缺回合錨證書即不具鑄幣資格等)照 流程/比賽結算.md 處理。

4. 各端判定不一致時的 fail-safe

停滯判定使用各 peer 的本機單調時鐘,各端收到 ready 進度的時點不同,邊界情況下可能一端判定成員停滯、另一端判定已到齊,兩端的 disconnects 因而不同。此時結算候選核對會拒簽,若湊不到 ⌊N/2⌋+1 簽章,整場依「多簽未達門檻」作廢、無結算。這是接受的 fail-safe:判定不一致不會產生錯誤的結算,只會讓該場無結算。

5. 被移出者之後送出的回合訊息

被移出者若自己沒有判定停滯,可能在其他 peer 逾時後才達 presentation-ready,並開始送出本回合輸入。網路斷線者送不出訊息,停滯移除才會出現這種情況。各 peer 對本回合已記為 forfeit 的成員,一律丟棄其本回合的輸入與 checksum 投票,比照網路斷線者不再影響模擬。各端判定不一致而導致模擬分歧或簽章不足時,依第 4 項整場作廢。

後果與影響

  • 一名在線但卡住的成員只讓自己 forfeit,不再讓整場已完成的回合作廢;首回合 GO 前的行為不變。
  • 剩餘人數不足或各端停滯判定不一致時整場無結算,與分割作廢、多簽未達門檻的既有結果一致;不新增部分結算路徑。
  • 被移出的車留在模擬世界、以斷線車既有規則模擬;它在本回合從未送出輸入,所有 peer 對它都沒有真值、預測基準一致。
  • 待實作時處理的檔案:
  • main:src/race-runtime/race-ready.ts(後續回合停滯結果型別與移出邏輯)、src/race-runtime/race-session.ts(停滯結果處置、寫入 disconnects、門檻判定與整場作廢)、src/race-runtime/race-ready.spec.ts 與 race-session 的回合別測試。帳本收件驗證與 pinning 不需更動。
  • specs:流程/比賽進行.md 第 1.7 節「取消語意」與第 12 節、流程/比賽結算.md、賽內機制.md 第 4 節、程式參數/protocol.md RACE_READY_STALL_TIMEOUT_MS 說明(現寫「逾時取消且不產生結算或懲罰」)。
  • 與既有決策的關係:
  • amends D-20260802-02:「無嚴格進度 30 秒即取消」與「取消不產生 forfeit 等後果」限縮為首回合 GO 前;後續回合改依本決策。D-20260802-02 的 H1 之後加註一段說明指回本決策,原四節不改寫。
  • 套用 D-20261029-02,不修訂其內容。
  • 當日歷史記錄需補條目並回引本決策。

定案紀錄

2026-10-30 使用者審閱後同意採納:首回合 GO 之後的回合停滯逾時,未到者記 forfeit、其餘照常開賽;剩餘非 forfeit 成員低於 ⌊N/2⌋+1 時整場作廢,不做部分結算;各 peer 丟棄已記 forfeit 成員的本回合輸入與 checksum 投票;判定不一致導致簽章不足時整場作廢為接受的安全失敗。