歷史記錄 · 2026-10-29¶
重大重構里程碑¶
2026-10-29 ─ 回合終局一致性(issue 001117、001115)¶
改版前誠實 peer 的回合終局會分歧,原因有二。主動離場只有 control leave 通知,主執行緒以 quit-peers
整組替換模擬端的離場集合,生效幀取決於牆鐘(issue 001117)。失能滑行窗的起點與靜止連續幀數是消費端逐幀
累積的 RoundEndState,不在快照與 state hash 內,catch-up reset(F) 後恢復端從 F+1 重新起算(issue 001115;
重現 F=1080 時 live 1120、恢復端 1201)。2026-10-28 使用者裁決 001117 採丙案、001115 採甲案。
主動離場改為已簽輸入流的 leave 事件(input wire event type 2,[2,0,0]),必須單獨成幀,encode、decode
與 rollback 受理三處檢查,缺包預測不延續。leave 依一般輸入排程在取樣幀+D 的確定幀生效,引擎寫入該車
quitAtFrame;之後輸入忽略、進度凍結、衝線不算完賽,車體留在場上。使用者主動 abort 且回合已 GO 未終局時,
主執行緒送 leave-round 並等 leave-flushed(上限約 600 ms,由 D、STATE_BUFFER_MAX_FRAMES 與
FIXED_FRAMERATE_HZ 推得)後才送 control leave。ICE 斷線、重連逾時與 desync/持續落後自我驅逐只寫入
結算 disconnects,不影響存活判定與名次分區。跨回合以 round-end.quitPeers 累加、下一回合
RoundBuildRequest.quitAtStart 在 frame 0 標記。
RaceProgress 新增已雜湊的 quitAtFrame、lastMovingFrame、leftLiveAtFrame(StepOutput.frame 語意,
null 以 −1 編入 hash,SAVED_STATE_VERSION 維持 1);roundEndReason 改為只看當幀已確定視圖的純函式,
RoundEndState 與 quit-peers 刪除、不留相容層,HUD quit 停用原因改讀 progress.quitAtFrame。
001115 回歸三情境在 F∈{960, 1020, 1080} 時恢復端終局幀與 live 相同(1120/1084/1109),真引擎存檔載入續跑
與觀戰 checkpoint 起播亦同;determinism golden 與 conformance 向量不變。斷線車仍計入回合名次(issue 001122)、
下一回合 race-ready 仍等待已離場者(issue 001123)另案處理;pinning vendor 同步另取同意。
決策檔:D-20261029-01
2026-10-29 ─ 多回合離場不再取消後續回合(issue 001123)¶
改版前每回合以開賽時固定的成員建立 RaceReadyCoordinator,任一成員 peer-left 即取消;mesh 的
peer-left 只通知一次、不重播。多回合比賽中有人流內離場或斷線後,下一回合屏障會等待已離開的 peer,
不是 stall 逾時就是收到遲到的 peer-left,兩者都導向整場賽前取消、不結算,001117 的 quitAtStart
也因此走不到。
首回合 GO 後的後續回合,等待集合先扣除 disconnects 與前幾回合流內離場者;屏障期間才到的
peer-left 只移出等待集合、不取消,續賽交給 partition guard 嚴格多數規則。首回合 GO 前維持賽前取消,
stall 逾時每回合照舊取消。quitAtStart 仍只取自 round-end.quitPeers,網路斷線者不放入。
流程/比賽進行.md §1.7 與 賽內機制.md §3.1 同步改寫為現況。
2026-10-29 ─ forfeit 整場壓縮名次(issue 001122)¶
改版前 deriveTotalRanking 只吃 rounds、不讀 disconnects。斷線車在模擬中照常以預測輸入行駛,仍可能完賽並拿回合積分,
總名次可能排在完賽者之前,TrueSkill 與連勝(讀 ranking[0])都受影響;斷線車夾在 A、B 之間完賽時,兩人的回合積分差
被多拉開 1 分,相對名次與獎金名次可能改變。D-20261029-01 已定斷線車不應得分,但總名次推導未跟上(issue 001122)。
2026-10-29 使用者裁決採選項 A(整場 forfeit 壓縮),否決只移末位不壓縮(B)與只壓斷線後回合(C,disconnectedAt 為牆鐘)。
deriveTotalRanking(rounds, disconnects) 以 disconnects 中屬開賽名單者為 forfeit(同一玩家取最早 disconnectedAt)。
每回合先移除 forfeit 者,其餘玩家依壓縮後名次 k 得 P − k + 1,P 仍為開賽人數;forfeit 者整場 0 分、總完成時間
只計非 forfeit 者。總名次=非 forfeit 者(積分 → 總完成時間 → PeerId),其後 forfeit 者(disconnectedAt 早到晚 → PeerId)。
TrueSkill、連勝與獎金名次讀同一份 ranking;模擬層 RoundResult.ranking、回合共識錨、state hash 與 sync wire 不變。
依 pre_launch 直接改寫 ranking 語意、不留相容層;conformance 向量與 determinism golden 不受影響,pinning vendored
ledger/derive-utils.ts 需經 vendor:sync 同步。算式表.md §20.1、賽內機制.md §3.2
LEDGER-R-084 與 流程/比賽結算.md 已為現況。
決策檔:D-20261029-02
2026-10-29 ─ catch-up 分 tick 追趕與落後端快進(issue 001104、001111、001107、001108、001118、001114)¶
改版前 catch-up 有六個相連缺口:幀時鐘偏移無法回收、持續吃掉 (D+R)×T 的容忍(issue 001104);守門以「快照幀
不新於本地即不採用」拒絕、第一位 voter 的 sync-response 未交付時只能等逾時(issue 001111);provider 灌高
front 可造成誠實 peer tied 或殭屍車(issue 001107);載入、最多 7504 筆日誌驗簽與重播擠在同一個 tick
(issue 001108);noble 同步驗簽佔主執行緒(issue 001118);觀戰端在來源缺號或參賽者離場停送後凍結
(issue 001114)。2026-10-28 使用者對六張 issue 裁決「依照建議」,2026-10-29 對 C 批計畫 11 個裁決點回覆
「照建議」(裁決 3 重新裁決為「P0+保證 1–6」)。
落後端以嚴格多數遠端的 quorumLead 觸發快進,(R, 24] 幀內每 tick 最多追加 2 步,只改本機步進、不動 wire;
不實作本機超前暫停(實測接上暫停後 1..8 抖動只剩 5/12)。catch-up 只載入 F 並發 reset(F),之後由
RaceLoop 依 10 ms 牆鐘預算分 tick 追趕,追趕期間不排程本機輸入。守門改為追前緣目標 ≤ 本機才拒絕,允許倒退
載入並加上已投輪不重投、舊票不自判、錨與觀戰 checkpoint 丟棄 F 之後舊值等保護;responder 對未交付的
sync-response 有限重送。front 以排除 provider 的嚴格多數參考幀夾限在 [參考幀 − 4, 參考幀+D+R],追前緣目標
上限為參考幀+D+R。驗簽改為快取跳過、本機輸入比對、4 ms 分片,並採用 WebCrypto Ed25519+noble 複核(接受集合
與 noble 相同)。新增 provisional 回歸閘 catch-up-worst 與單 tick 追趕斷言;觀戰端加無進度看門狗,已 quit
的車免收封包。catch-up 後 1..6 抖動 12 seed 全員一致,1..8 列為已知限制(issue 001127)。sync/spectator wire、
determinism golden 與 conformance 向量不變;pinning vendor 同步另取同意。
決策檔:D-20261029-03
2026-10-29 ─ catch-up 追趕期限、驗簽心跳與背景恢復(issue 001104、001108、001118)¶
D-20261029-03 升格後的最終審查發現三個跨機制缺陷。單步成本超過一個固定步的慢裝置每個 tick 都達
MAX_SUBSTEPS、追趕差距恆定,追趕永不結束,而追趕期間暫停 lag 判定,該車變成無法操作,issue 001040 的
慢端自我退出失效;追趕中切到背景超過約 4.3 秒,傳輸層每 peer 256 則已驗佇列滿後丟棄 live input,恢復後
必然分歧並被驅逐;退回 noble 驗簽時(Firefox 驗 7504 筆約 434 步、Chromium 4× 降頻約 1050 步)每位 voter
都在驗簽途中被 300 步逾時取消,catch-up 無法完成。2026-10-29 使用者裁決另立新決策 amends D-20261029-03,
追趕期限改為看進展。
追趕差距每縮小一次即重新起算期限,連續 PERSISTENT_LAG_DEMOTE_FRAMES(300)個正常 step 沒有縮小才以
'lag' 自我退出(lag-evicted),不新增常數。追趕中背景恢復隱藏不超過 CHASE_SUSPENSION_MAX_FRAMES
(24 幀)時延長目標與進展基準,超過則結束追趕、改由自動 catch-up 重新對齊。傳輸層在日誌分片驗簽推進時送
內部心跳 sync-response-verifying,引擎據此順延逾時,另設自到達起 SYNC_LOG_VERIFY_MAX_STEPS(676 步,
由新 local-policy 常數 SYNC_LOG_VERIFY_ENTRY_MAX_MS=1.5 ms 推得)總上限;「誤判只損效能」只在單筆
≤ 1.5 ms 時成立。另外追趕中不回 probe、catch-up 進行中回到前景即取消並重新發起、本機排程幀嚴格遞增且被日誌
取代的舊排程不公告、快進追加步計入 step 時基。sync/spectator wire、determinism golden 與 conformance
向量不變;pinning vendor 同步另取同意。
決策檔:D-20261029-04
2026-10-29 ─ builtin 交付 schema 重新編號為 1(issue 001062)¶
D-20261022-01 為表達「場地已交付、零件延後」,把 public/assets/builtin-delivery.json 改成按 tracks/parts 分類型,
並命名為 schema 2。專案仍在 pre_launch,依專案生命週期與 D-20260814-24,Open4WD 自有 schema 與 manifest 的 current
baseline 一律為 1;舊的全域單一狀態形狀從未公開發布,沒有相容對象。2026-10-28 複驗退回 issue 001062,
指出該 manifest 的版本值不在 authority 清單內,CI 因此沒有擋下。2026-10-29 使用者審閱後同意採納。
按類型交付的現行形狀定為 schemaVersion: 1,「schema 2」只是未公開的候選名稱;D-20261022-01 的功能內容全部保留。
不留相容層:舊全域形狀以 exact-key 檢查拒收,未知版本同樣拒收。schemaVersion 納入 main 的
prelaunch-version-authorities(受檢 authority 24 → 26 項),specs 的 current authority 清單同步宣告 schema 1 交付權威。
現況規格見 程式架構/builtin-assets.md。
決策檔:D-20261029-05