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),即走既有驅逐收攤:停模擬、送 controlleave並退出回合,不再參與後續回合。
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 投票;判定不一致導致簽章不足時整場作廢為接受的安全失敗。