跳轉到

歷史記錄 · 2026-07-10

月份索引歷史記錄

重大重構里程碑

2026-07-10 ─ fold 信譽投毒閉合:loadout 簽章綁 matchId=參與證明(收件⓪-loadout+applier 閘;測試 707/707、十四步全綠)

決策檔:D-20260710-01

複審另案(fold 守門自推 quorum→回填時戳偽造 match-result 投毒信譽/斷線)經深追確認真實可利用(非已知取捨——H2 只補鑄幣側、reputation/disconnect 側無防線),使用者裁「先修」。不 commit/push

攻擊鏈(全程實碼確認):攻擊者零成本 keygen sybil 當 ranking(quorum 自過)、受害者列 disconnects、matchId 綁自造名單(⓪照過)、時戳回填 >30s 繞 live 三閘(ledger-api.ts #applyEntry#foldEntries 直墜 foldGuard),reputation/appliers 對 disconnects 無條件 −5matchmaking/appliers disconnectCount++→σ 托底;換 startedAt 即換 matchId=無限重放、可對任意 PeerId 網路級決定性投毒(信譽→0、5 筆即評分卡死;無黑名單路、可回復)。

根因DisconnectInfoloadouts 零簽章=事件無「被列 disconnect 者確在本場」的證明;sybil finisher 可為任意 disconnects 背書。修法(重用 loadout 簽章為參與證明):①loadout 簽章綁 matchId(loadoutSigningMessage(matchId, loadout)=跨場不可重放;sign+verify 兩側改;出簽移至 ledger/serialize.ts 避 ledger↔matchmaking 反向依賴);②exchangeLoadouts 收集全體簽章、隨結算入 MatchResultEvent.loadoutSignatures(新欄);③收件⓪-loadout(fold+live)逐筆驗簽章有效(缺/偽=loadout-proof-invalid 拒);④applier 只罰/只評有 loadout 者——reputation 斷線 −5 與 matchmaking disconnectCount/rating 皆閘於 loadout 存在(rating 位次相關=必閘;reputation 完賽 +1/里程碑不閘=被誤列 ranking 只會加分無害、避免破壞 void/里程碑語意)。攻擊者偽造不了受害者 matchId-bound 簽章=無法把其塞進 loadouts=投毒失效。

⭐行為變更(使用者裁決):absent(開賽後、交 loadout 前掉線=無密碼學承諾)者不再罰 −5/記斷線——更公平(沒承諾不罰、順手不再誤罰提交窗內網路抖動的誠實玩家)。

測試 +2(707/707):ledger.spec(⭐loadout 簽章缺/偽=fold+live 雙拒 loadout-proof-invalid;squat 測試補 loadoutSignatures)/reputation.spec(⭐disconnects 無 loadout=不扣、有 loadout=照扣)+全 fixtures 補欄(ledger/ledger-storage/economy/matchmaking/race-runtime/bootstrap/reputation spec,含 exchangeLoadouts/submissionOf 改簽 matchId-bound)。canon:ledger.md §1 live ⓪-loadout+§6.1 fold 守門列+MatchResultEvent/MatchContext schema/matchmaking.md §5 簽章綁 matchId+參與證明/比賽進行.md §1.3·§1.4。schema 變更屬 v1 紀元內(無 genesis 帳本、無遷移)。下一步不變=author 24+3 GLB→首次本機測試(不 commit/push)

2026-07-10 ─ 首測前 staged 清帳碼複審→全修(四路 agent+自驗;高 2 中 9 細項群;測試 705/705・50 檔、十四步驗證全綠)

決策檔:D-20260710-05

staged 清帳(房間開賽編排+room-runtime)落地後、首測前,四路獨立複審(共識安全/資源生命週期/整合正確性/前端 i18n)+自驗 matchId 綁定端到端,findings 分級高 2/中 9/細項群,使用者裁「全修」(逐項確認);⭐fold 信譽投毒列另案(非本輪碼、既有共識層)。不 commit/push

高 2:⭐高-A 賽中離場不呼 noteMatchOver(三路收斂)——noteMatchOver 是清 activeMatch+房 preloading→waiting 唯一路徑但只接在 onMatchEnd/onSessionEnded;abort(返回鍵/賽中出口/start 失敗)不發終局=房卡 preloading(回房被導頁效果彈回死賽頁乒乓)+activeMatch 殘留使此後 UGC 取塊全被誤 pin race-lock(GC 回收不了);修=race-page destroy/start-err 補呼 noteMatchOver+room-page 導頁前確認本節點確有此場 activeMatch+leaveRoom 清殘態。⭐高-B race-lock 跨重啟殘留——raceLockedCids 純記憶體、pin 分類寫 IDB;賽中關分頁→啟動 Set 空=該批 CID 以 race-lock 永凍(pin 只升不降、evict 跳過非 lru);修=UgcCacheLruService.releaseAllRaceLocks() 啟動掃全表降 lru(開機必無比賽)+releaseRaceLock 改走之(連帶關 loadout/mine 未接線時的誤降險)。

中 9中-C quickMatch 硬化(offer 綁定=非發起方只認撮合對象 offer〔擋劫持/假房號 DoS/initiator 窗拆房〕+settle 守門〔逾時/取消不建房、建成才 settle 則拆〕+cancelQuickMatch 契約貫通〔dispose/另起操作/race-config 離頁停 poller〕);⭐中-D 入房密碼=繼任重掛不送密碼(seeded 免密碼、假繼任者無從擷取)+挑戰-應答協議(join-request 不帶密碼→host 發 join-challenge{nonce}→joiner 回 join-auth{proof=sha256(nonce‖password‖joinerPeerId)};綁身分擋中繼、明文永不上行);中-E host 聊天中繼前 per-成員限流canSend 5/s、擋亂簽洪流被放大 1→N);中-F app dispose 收 RoomService+dialer 持久 WS(先於 libp2p 停);中-G #active 重入守門#generation 世代 token=併發建/入房後起者 supersede、前者提交前自拆半成品);中-H 錯誤訊息統一 i18n key(joinPresence 透傳/validateMatchConfig reason→errors.p2p.offlineerrors.room.badConfig);⭐中-I 成員端 match-start 校驗(host 不可信:matchId 綁 match-start 名單自洽+名單須等於成員先前觀察值、host 偷改 roster=忽略不進 preloading);中-J host 撥號半開洪流防護canAcceptDial=半開 link 也計入 maxPlayers 上限);中-K close 收半開 link+wire handler #closed 守門(收攤後到達訊息不被 admit 進死房)。

細項群:decodeRoomWire 淺層 shape 驗(上行 displayName ≤64/數值欄有限、擋放大)/MatchmakerPoller #known 容量帽 256(防 Sybil 灌爆)/room-page 單人房不呈現[開賽](免按下必吃 notEnoughPlayers)/room-page .error 樣式+role=alert/matchEntryPending 文案中性化(四語系)/dialerPromise 失敗只重置「自己那顆」(防覆滅較新 in-flight=孤兒 WS)/room-page 導頁 latch 失敗解閂。留置:room-link 內部診斷中文 Error(dial/accept 失敗全被吞、永不外露 UI=內部除錯用、非違規)。另案(非本輪碼):fold 守門 verifyMatchResultSignatureSet 自 event 自推 quorum→回填時戳偽造 match-result 對任意受害者無上限扣信譽/灌斷線(economy 側月硬頂已擋鑄幣、reputation/disconnect 側似未擋;純 derive 信譽投毒面)=待威脅模型評估。

測試 +18(705/705):room-runtime.spec(challenge-response 正確 proof/⭐proof 綁 PeerId 擋中繼/聊天限流 5/s/⭐canAcceptDial 半開計數+close 收半開+拒 close 後 admit/dispose 收 dialer/⭐成員端 roster 偷改忽略/quickMatch cancel/timeout/⭐offer 綁定拒第三方)+pwa-offline(releaseAllRaceLocks 全降+冪等)+race-page(destroy/start-err noteMatchOver)+room-page(導頁一致性閘+單人房不呈現)。canon:room-runtime.md §2 wire(挑戰-應答)/§4 host(半開洪流/限流/close 守門)/§6 service(重入/成員校驗/cancel/dispose)/§6.1 繼任免密/§7 offer 綁定回寫。下一步不變=author 24+3 GLB→首次本機測試(不 commit/push)

2026-07-10 ─ 首測前 staged 清帳:房間開賽編排+star 拓撲等待房+quickMatch 接線+race-lock 賽後 GC+M1 去重集定案(測試 691/691・50 檔、十四步驗證全綠)

決策檔:D-20260710-03D-20260710-04

承第二輪複審高-1 裁決(repo 除公版 GLB 外須可直上 public git、後補 GLB 不動程式)——把「隨房間開賽流程接線」的所有 staged 項一次做掉。不 commit/push(首測前鐵則)。

⭐房間開賽編排(高-1 生成端落地)+star 拓撲等待房(新 src/room-runtime/:real-providers 的 matchmaking/chat 域原為 OFFLINE 樁——補齊等待房傳輸層。因 RaceMesh 固定名單(進出破壞 all-or-nothing)+signaling wire 窄(server 不擴充),採 star 拓撲:成員各一條 WebRTC link 連房主、房主權威 Room+中繼(聊天/ready/room-state/match-start);presence 走 signaling 房域註冊(openRoomPresence=房間存在性標記+peer-joined/left)、SDP 握手經此中繼;賽內 full-mesh 仍於 session start 全新建(race 路未動、對齊 canon「mesh 於開賽確認後成形」)。模組:room-wire(dag-cbor union、room-state 剝 password)/room-linkdialRoomLink initiator+acceptRoomLink late-bound responder 首 offer 定對端)/host-roomHostRoom join 裁決·ready·chat 中繼·ping RTT·startMatch=三閘→deriveMatchId(members,startedAt,rules)→match-start 廣播+回呼、MemberRoom 路由+pong+onHostLost)/room-service(createRoom/joinRoom/subscribeRoom/setReady/submitLoadout〔stash key match-start 轉正〕/startMatch/activeMatch/noteMatchOver/quickMatch/繼任 #onHostLost seededMembers 免密碼補收)。頁面:room-page 房主偵測(hostPeerId===我)+[開始比賽]委派+導頁效果(state=preloading+currentMatchId→/race/:matchId#navigated 閂)/race-page match 進場改讀 activeMatch()(不符路由=matchEntryPending)+onMatchEnd/onSessionEnded 呼 noteMatchOver()。bootstrap:RoomServiceFacade 委派、makeBrowserRoomService(dialer 惰性建、RoomView 轉接)、create-app/app-shell 接線。i18n ×4 補 errors.room.*(8)+errors.matchmaking.timeoutpages.room.startMatchawaitingHost(廢 enterRacematchEntryPending 改義)。

⭐quickMatch gossip 接線room-service.quickMatch 早完備、僅缺 bootstrap DI 的 matchTopic=結構性永不可用非部署閘):makeGossipMatchTopicTOPIC_MATCHMAKINGpayload.type 多工 match-request/match-offer、單一 TopicSubscriber 訂閱+自維 handler 集=真 unsubscribe、開放信任集〔配對找陌生人:簽章/時戳/nonce/限流為封套、版本/封鎖/matchId 綁 roster 為賽級閘〕);pubsub=libp2p services.pubsub(app-shell 型別 only import 抹除、BrowserRoomServiceInput.pubsub 注入)。

⭐race-lock 賽後 GC(實現 pwa-offline.md §6 既有 spec、非新規):app-shell lruPinroomService.activeMatch() 決定 pin 類(賽中=race-lock、否則 lru)+記本場 CID;noteMatchOver facade 包裹=releaseRaceLock(逐筆 reclassify→lru+RACE_LOCK_GC_DELAY_MS=5s 延遲 maybeRunGc)。設定頁清快取入口=確認前輪已接(APP_MAINTENANCE.clearUgcCache+toast、無需補)。

⭐M1 settledMatchIds 定案(前輪 staged→本輪處置):完整性=冪等閘正確性前提——不同於顯示用 recentMatches(可逐出),此集任一 matchId 被逐出=該場重複結算窗重開(雙記分);故永不逐出、隨場數線性成長(~46B/筆)=刻意接受,任何未來 bounding 須維持「全集同步命中」(緊湊持久索引),不得採 hot 窗逐出。derived-state doc 改寫定調。

測試 +28(691/691・50 檔):room-runtime.spec(wire roundtrip/HostRoom join·拒因·繼任免密·ready·chat·ping·開賽三閘+成功·賽終·drop/MemberRoom 路由·requestJoin·pong·onHostLost/RoomService 兩節點 startMatch→activeMatch→noteMatchOver=記憶體 link 對接)15/room-deps.spec(GossipMatchTopic 跨端投遞·多工·unsubscribe=真簽章 FakePubSub)2/race-page +4(match 進場·不符·noteMatchOver×2)/room-page 5(房主偵測·非房主·startMatch 委派·導頁·waiting 不導)。race-lock 接線屬 launch 路(autoLaunch:false 邊界外、本機測試閘覆蓋,同 lruPin/LRU 既有慣例)。canon 回寫:新 程式架構/room-runtime.md(star 拓撲/wire/link/host·member/繼任/quickMatch/race-lock 對接)/matchmaking.md §6(stale sendToRoom 模型改指 room-runtime、留 handleHostLeft domain)·§10(room-runtime 對接列)/程式架構.md(目錄樹+模組表列 room-runtime);pwa-offline.md/ledger.md 既有 spec 已涵蓋〔賽後 GC §6·§154、fold ⓪ §350〕無需改。驗證:十四步真 exit 0(format/format:check/lint/check:types/check:vendored/test 691/691/build+inject-csp 38+inject-precache 101〔初始包 1.09MB<1.25 預算、room-runtime 入 lazy chunk〕/check:invariants 3/check:materials/check:tokens/themes:validate/i18n:validate 4×348/check:seo/check:pwa)。下一步=使用者 author 24+3 GLB→首次本機測試(不 commit/push)

2026-07-10 ─ 首測前第二輪複審(針對首輪全修碼再複審)→全修(高 2 中 11 細項;⭐高-1 同日全落地;測試 665/665/47 檔、十四步驗證全綠)

決策檔:D-20260710-02

首輪五路全修後、首次本機測試前,針對「該批全修碼」再完整複審一輪(四路 agents+自驗四縫合點),findings 分級 2 高/11 中/13 細項+觀察,使用者裁決「全修」;其餘 22 項全修。不 commit/push(首測前鐵則)。

高 2:⭐高-2 版本 auth 缺 from 綁定VersionWire.onMessage 未帶認證來源=惡意 peer 可冒名頂替他人版本靜默放行;修=onMessage(handler: (msg, from: PeerId))serveVersionRequestscollectVersionsFromAllPeersmessage.sender === from 恆驗〔比照 roster-confirm/settlement 兄弟共識〕、race-session versionWire 轉接透傳 from);⭐高-1 結算搶佔verifyMatchResultSignatureSet 的 eligible 由自陳 event.ranking 推導=單人 ranking→quorum 1→自簽通過,可搶佔任意 matchId 先行 settle、毒化持久去重集使真結果被冪等閘吞)——初裁 staged(隨 match-start),同日使用者再裁「repo 除公版 GLB 外須可直上 public git、後補 GLB 不動程式」→ 驗證側全落地(開源 P2P 攻擊面=協議非 UI,共識層不留 TODO):deriveMatchId(roster, startedAt, matchRules)=canonical {type:'match-id', roster 去重字典序, startedAt, matchRules} 之 sha-256 hex(src/ledger)+三處同式——收件⓪(matchid-roster-mismatch 拒收)/foldGuard ⓪(timeless 恆驗、先於簽章集)/race-session 開賽自洽閘(不符拒開、不建連);RaceStartContext +startedAt(結算事件 startedAt 改由開賽時戳供給——同房連場 id 區異、原 room.createdAt 撞 id 之窗順帶關);fixtures 全面改 derive(ledger/ledger-storage/matchmaking/race-runtime 四 spec)+it.todo 轉真測試(搶佔拒/自名單自簽僅能佔己場 id/推導決定性/⓪ 專屬拒〔足額真簽仍拒〕)+自洽閘新測試。生成端一行(房間開賽編排屆時呼 deriveMatchId)隨房間流程拍、契約已由三處驗證與 canon 鎖死。

中 11 全修:M3 靜默死亡(RaceSession 契約 +onSessionEnded(reason)=結算失敗/catch/desync 驅逐三異常終局各發一次、race-page 訂閱顯示+導頁)/M4 CID 命中不記帳(asset-source hit 路補 onCidFetched touch)/M5 雙資產源(race-deps 改收 app-shell 既建單一 UnifiedAssetSource)/M6 無 in-flight 合流(fetchGlbMap<ref,Promise> 合流、並發同 ref 只抓一次)/M7 extras 無反應性(跨層 EXTRAS_VERSION signal 橋=bootstrap 暖 extras 後通知 garage/garage-edit computed 重算)/M8 offline-banner 卡住(clearTimeout 移入轉場分支、online→online 重複事件不動作)/M9 技能 release 遺失(keyup 讀 event.code 不過 typing 守門、window:blur 全放手、僅按下才守門)/M10 unlockWithPin 參數丟棄(I18nError 攜 ICU 參數、login FlowError 透傳 pipe)/M11 settings 強制更新無守門(o4RequireOnline 掛 forceUpdate);M2 legacy 播種決定性(state-codec decode 移除 settledMatchIds 自 hot 窗 keys 播種——fresh project 無 legacy 檢查點、encode 端恆為 recentMatches 超集=decode 只讀權威欄、缺欄空集);M1 settled 無界成長=staged document(隨 match-start/matchId 綁 roster 同拍冷分桶;每筆 ~46B 成長極緩、決定性不受影響)。

細項群全修:negative-cache 永久化(extras 僅成功入快取=失敗自癒)/extras 無界(EXTRAS_CACHE_LIMIT=256 LRU)/iOS iPad 檢測(Mac+maxTouchPoints)/dispose 歸零/evictLru meta.delete catch/fake interval guard/settings +catch/minimap colorForPeer 穩定定色(缺席車跳號不致全體點色位移)/ugc-detail role="group"/login 建立卡 createError/⭐firstSeenAt 綁版本(寬限錨改 {version, ts} 形=連續換版〔B→C〕重新起算 24h、sw-register localStorage JSON 序列化、+釘測試)。細項 local-test 多餘重建=documented-accepted(race-page trackless→tracked 一次性整場重建;onTrack 契約僅在「有場地」時發射非空 ref、空舞台〔假件期/無場地賽〕永不發射=頁面無從區分「等待」與「本就無場地」,硬載入閘會抹掉合法空舞台;成本微=賽前一次非逐幀,留原樣)。觀察落檔:rollback 'window' origin 下游確認無脆弱依賴(onPersistentDesync origin 為封閉 union 'window' | 'checksum'、兩路皆處理;window 路 escalate(frame, {}) 空 hashes 存檔即 DesyncEvent 調查記錄、handleDesyncEviction origin-agnostic+desyncEvicted 冪等閘、無下游要求 hashes 非空)。

跨層修:⭐i18n 參數面 widen 至 Readonly<Record<string, unknown>>(pipe/t()formatIcu——formatIcu 本以 String()Number() 強制、全 caller 為子集不破;解 I18nError.params 攜 keyManager 錯誤參數〔attemptsLeft: number | null〕的型別摩擦)。canon 回寫:matchmaking.md 新 §4.1「matchId 生成契約(綁 roster)」=roster=開賽 room.members(缺席/中離落 disconnects、驗證端以 sorted(ranking ∪ disconnects) 重組)+sha-256 hex 格式+§4 currentMatchId 欄註/ledger.md §1 收件⓪+§6.1 fold 守門 ⓪ 條款/比賽結算流程 §8.6 步 0/程式架構.md bootstrap 行;derived-state settledMatchIds doc=線性成長帳(~46B/筆)與冷分桶前瞻(與 matchId 契約脫鉤——契約已落地)。驗證:十四步全綠(format/format:check/lint/check:types/check:vendored/test 665/665・47 檔〔todo 清零〕/build+inject-csp 38+inject-precache 96/check:invariants 3/check:materials/check:tokens/themes:validate/i18n:validate/check:seo/check:pwa 真 exit 0)。下一步=使用者 author 24+3 GLB→首次本機測試(不 commit/push)

2026-07-10 ─ 首測前全專案五路複審→全修(高 4 中 16 細項全數;測試 660/660・47 檔、十四步驗證全綠、SSG 產物決定性驗證)

決策檔:D-20260710-03

拍 14 收口後、首次本機測試前,五路複審(共識安全/資源生命週期/接線整合/canon 對齊/前端測試品質)+自驗 local-test 端到端走查,findings 合併去重分級(高 4/中 16/細項群/觀察維持現狀),使用者裁決「全修」:

高 4:⭐SSG 語系汙染(唯一產物級缺陷——模組層 settings 單例於 prerender worker 跨 route 共享、i18n persist 洩漏=無前綴頁語系隨渲染序漂〔實測根頁抽到 ja〕;修=server 端 i18n storage no-op+browserLanguage 恆 en〔Node ≥21 有全域 navigator=build 機系統語系、跨機器不決定〕→ 無前綴 prerender 恆 en);⭐match-result 去重僅 hot 窗(檢查點 cold 分桶逐出+economy no-op〔界限拒/月硬頂〕不寫 hot 兩路繞過=複製 append 重套 TrueSkill/信譽累積型 reducer;修=state.match.settledMatchIds 持久去重集——永不逐出、隨檢查點序列化〔排序穩定〕、舊檢查點缺欄以 hot keys 播種、閘後無條件回寫);⭐rollback input 無過舊幀下界(快照窗外舊幀 input→回滾不可能→window 路立即升級 persistent-desync=觀測方自我驅逐計 forfeit——單一慢 peer〔窗僅 12 幀=0.2s〕遲到輸入可連鎖清場、亦可惡意構造單訊息踢人;修=frame < currentFrame − STATE_BUFFER_MAX_FRAMES 直接丟棄、落後端分歧交 checksum strike 緩衝);⭐abort×connectTransport 窗(cleanups 已 splice 後才 push=連線永不回收+幽靈參賽者〔有簽章永無輸入〕;修=pushCleanup〔abort 後即得即收〕+每 stage await 後重查 aborted+abortRound 出口〔loop.stop 後 round promise 不懸掛〕)。

中 16 全修:viewport 資產埠雙重斷(VIEWPORT_ASSETS 恆 NULL+builtin:/local: 被當 CID)→ 統一資產源 bootstrap/asset-source.ts(builtin:→隨版檔兩層目錄/local:→出口 A 庫/CID→塊+LRU 16+記帳鉤;race 物理與 viewport 顯示同路由、app.config DI 供給);race 場景場地真渲染(RaceSession.onTrack 契約+descriptor trackRef+mm→m 縮放);builtinExtras 接線(BuiltinExtrasCache launch 背景暖=garage stats 有值);出口 A local: refs 比賽路通;比賽頁鍵盤技能 1–4(canon 鍵位定案落地:keydown/keyup+auto-repeat 防抖+輸入焦點守門);race-config/ugc 評分 err 顯示(拒因 i18n key 上 UI);runMatchRounds try/catch+buildRound abort 引擎回收+countdown timers 注入化;keyAnnounces per-peer 容量上限;UGC 快取 LRU 操作序列化+evict 逐筆重讀(race-lock 升類不被過期快照覆滅)+iOS 0.5/0.4+verifyConnectivity 真 ping 注入+設定頁清快取入口;A 軸開賽前版本閘接進 startMatch(版本訊息入 RaceControlMessage、myVersionInfo 六欄自報、收齊+相容 fail-closed 先於 loadout 交換、收集值進 MatchResultEvent.clientVersions);結算頁 TS 變化真值(session summary 經 router state 夾帶、直開 fallback provider);CI 補 check:materialscheck:pwa;SW precache 全量化(inject-precache build 後掃 dist 注入 hash 檔名 JS/CSS=首次安裝後可離線開 app;install 改逐資產 add、cache.put 限 200+catch);字典模型收斂(/assets/i18n/ 死資產刪除——字典隨 bundle chunk、precache 涵蓋);設定頁維護區(六欄位版本資訊+強制更新+清快取+reset 連結;EconomyData.configEpoch 新讀點)+about 版本顯示;o4RequireOnline capture 相位重寫(掛元件宿主也擋得住內層 button)+掛載配對三鈕/評分列+.offline-disabled 全域樣式+aria-disabled;小地圖自我標記(HudState.myPeerId+外環)+DPR backing store+Space 鍵。

細項群:countdown/離線橫幅常駐 live region 容器化;o4-button ariaLabel 轉發;progress accessible name;DNF/NET i18n 化;升版 idle 正則納 garage/edit;寬限錨 localStorage 持久(重載不歸零);version.json 不可達不誤判 major;login 匯入錯誤分卡顯示+import-failed i18n key+importMnemonic 尊重 displayName(key-manager recoverFromMnemonic +nickname 參數);handleDesyncEviction 上鏈失敗不阻斷驅逐主線;SettlementExchange reviewing 旗標 try/catch 復位;race-deps networkFor 先收舊回合+disposed 拒建;mesh close 旗標終止重連輪詢;app-shell LRU 移出 unwind try(切真後失敗不回退假態);IDB onblocked 屬既有低機率備查;過期「隨 X 拍」註解群清零+i18n stageNote 死鍵刪除;bootstrap 測試鏈序真斷言(milestone 讀同事件 economy 更新=因果證明)。觀察維持現狀(使用者裁決範圍外):fold/live 結算驗證不對稱=canon 明示 timeless 取捨、base-href 子路徑部署=部署前收口項、launch 失敗維持假態=設計行為。

canon 回寫:pwa-offline.md §2(precache 雙層模型+分路表現況+逐資產 add+put 語意)/§3(ping /version.json)/§4(capture 相位+aria-disabled)/§6(LRU 併發互斥註);versioning.md §5(BYPASS +/reset.js);i18n.md §2 loadDict(動態 import)/§5(detectLang 四段序+server persist no-op)/§6(字典隨 chunk);語系清單.md §6/§7;themes.md §4(default manifest 隨 bundle)。新增測試 20(660/660・47 檔):settledMatchIds 三測+codec roundtrip/rollback 下界/countdown 序+abort/connectTransport abort/版本閘 fail-closed/統一資產源+extras 快取/LRU 併發互斥/race-page 元件(countdown 0=GO・dwell・色階・鍵盤)/minimapDots 純函數/離線橫幅 timer/require-online 攔截。

2026-07-10 ─ 拍 14 收口:A 軸 client 版本+Service Worker 升版+PWA/離線全套(測試 640/640・45 檔、十四步驗證全綠——check:pwa 新增為第十四驗證)

裁決執行(DesyncEvent 措辭淡化四處活 copy 同步=被踢者自簽自報;2–15s catch-up sync 列 其他.md §3 playtest 專輪)後開拍 14:

A 軸 client 版本落地src/versioning/client-version.ts):六欄位 ClientVersionInfo 收斂形(後三欄 number;economy_config_version=鏈上 epoch 讀點注入)+checkCompatibility(client 比 major、其餘全等)+開賽前收集(serveVersionRequestspreRaceClientVersionCheck 收齊性把關防靜默放行;埠形就緒、房間訊息接線隨 room 開賽流程)+UpdateChecker(SW waiting 偵測、24h 軟性寬限、套用前 idle guard=比賽/房間/編輯中延後)+forceReload。新常數 RAPIER_VERSION_CURRENTDERIVE_LOGIC_VERSION_CURRENTBUILTIN_ASSETS_VERSION_CURRENT

Service Worker+逃生口public/service-worker.js(快取分路對齊 pwa-offline.md §2:導航 network-first//api//version.json/reset.* network-only bypass//help/*.md SWR/其餘 cache-first;跨源不經 SW;install 不 skipWaiting、SKIP_WAITING 訊息制;CACHE_NAME 版本隔離)+/reset.htmlreset.js(純 static、外部 script=CSP script-src 'self' 相容——inline 會被 build 注入的 CSP 擋)。

generate:pwa(第十四驗證 check:pwa):manifest×4(description=i18n app.tagline+平台字尾;theme-color 同步 index.html 單一權威鏈)/version.json(本機決定性形=build_timestamp 0・commit_hash 'local'、CI deploy 覆寫)/SW CACHE_VERSION 行注入/icons×3(node 標準庫手寫 PNG、純幾何四圓=圖檔禁字鐵則合規)。

pwa-offline 模組OnlineStatusService(事件流+主動 ping /version.json——靜態部署無 /api/health)/sw-register(dev 不註冊;升版循環=minor 通知後 2 分鐘自動套用、major 通知待 UI 觸發、寬限後轉強制仍過 guard)/persist+quota 三級警示/UgcCacheLruService(pin 五類只升不降、race-lock 硬規、lru 最舊先 evict 至目標線、離線不 GC、idbUgcCacheMeta 每操作開關連線)。

接線:app.config(SW 註冊+升版 toast+persist+quota 警示)/app 殼 OfflineBanner(離線橫幅+恢復提示)/o4RequireOnline directive(離線灰化+攔截)/MetadataService 依語系 upsert manifest link/app-shell launch 後建 LRU+啟動 quota 檢查(HeliaCachePort=browser-ports 暴露 pins+gc)。i18n 10 keys×4。

build 衛生:budgets initial 1MB→1.25MB(現值 1.07MB——key-manager 家族〔noble/argon2/bip39/cborg/multiformats〕經 app-shell 靜態鏈進 initial=拍 13「登入前 identity 真」設計使然、非本拍新增;stats.json 歸因驗證)+libp2p transitive CJS 17 顆列 allowedCommonJsDependencies(server prerender 圖掃到、runtime 不進 server)。

staged(隨房間開賽流程):賽後 GC 觸發(settling→closed 後 5s)、race-lock pin 分類接線、設定頁「清空快取」入口。Lighthouse@lhci/cli 0.15.1(exact、同日使用者核可)+.lighthouserc.jsonpnpm run lighthouse——首跑 EXIT=0(a11y/best-practices/SEO 全過 error 門檻、performance 0.63–0.76=warn 級 headless 本機參考值);產物 .lighthouseci/ 入 .gitignore。同日文檔複查:權威檔歷史語氣清零(其他.md 移除裁決日期標記——歷程只進本檔鐵則)。

2026-07-10 ─ 拍 13 race-runtime 同拍深讀複審→全修(三路 agents 合併 高6中9細4;測試 630/630・44 檔、十三步驗證全綠)

決策檔:D-20260710-06

拍 13 主體七批收官後,使用者裁決「全修後再進拍 14」。三路複審(資源生命週期/共識正確性/接線與 canon 對齊)合併去重、四批執行:

共識核心:⭐結算收簽加「提案者身分閘」(candidate.peerId=本機觀測非 forfeit 完賽者 byte-min)+「本機觀測錨」(rounds/loadouts 與自身賽內經歷、賽前交換提交比對)——堵「非主搶發內部自洽偽 candidate 收割多簽」;⭐定稿回報改「鏈上確認唯一出口」(廢樂觀定稿——主死於 append 前誤報成功)+提案週期重送+SettlementCancelMessage(決定性拒簽者過多=主動終局、transient 不計);⭐提案 base=deriveStateAt(baseLogHeadCid)(鏡像滯後=與背書端分歧全員拒簽)+回合終局快照 await 落地再取 head;快照多簽=在場(present)選舉與 quorum 分母+30s 重試;⭐新「名單交叉核對」共識輪(rosterHash 互驗 fail-closed——loadout 交換各端獨立定案的名單剪刀差會開賽即決定性發散;canon 比賽進行流程新 §1.6);desync 偵測接線(resolveChecksum 容差窗後裁定→persistent-desync=自報 DesyncEventleave('desync') 自我驅逐);input 同幀鎖首發(equivocation 不覆蓋);⭐收件 snapshotInfo 供給落地(RaceSnapshotWatcher=末筆時戳+base 前綴掃描暖答;無之=有鑄幣結算全網拒收)+ledger defer 重審佇列(原 staged 債);⭐fold 端 match-result 全鏈冪等閘(economy 原有守門、但 TrueSkill/信譽累積型 reducer 會被重複條目雙重套用——雙主並行 append 因此轉良性)。

連線與生命週期:WebRTC disconnected 改 15s 重連窗(暫態自癒不再即刻 forfeit;state 級 catch-up sync 留待同步協定——2–15s 斷線現走 desync 自我驅逐、語意同異常斷線);回合鑰交換只等在場者(中途離場不再殺整場餘回合);mesh 部分失敗全體收攤/通道懸空拒絕/逐回合傳輸訂閱回收/signaling 斷線;app-shell launch 失敗回收節點(同鑰雙節點防)+dispose×launch 競態;引擎掛載前歸戶(abort 落 await 間可收攤)。

接線與 HUD:B 軸資產版本閘接上 loadout 驗證(v1 紀元實質放行、治理 ratchet 後生效);整車質量 50–500g 聚合檢核入世界構建(烘焙值加總、雙端同拒);未評分 UGC royalty 改隱式查表(原 ?? 250 定值兩處);builtin GLB URL 對齊兩層目錄;賽內機制 §2.4 HUD 補全=worst-fatigue 同分裁決序+色階 widget 去數字+dwell 1.5s+3-2-1-GO 倒數+場地小地圖(M 鍵/點擊收合、直向預設收合)。

canon 回寫:比賽結算流程 §8.1(cancel 訊息)/§8.3(deriveStateAt、重送、取消判定、⭐定稿=鏈上確認)/§8.4(身分閘、觀測錨、拒簽 hash 快取)/§8.5(雙主 append=fold 冪等閘吸收);比賽進行流程新 §1.6。留裁決:①比賽進行流程 §6「DesyncEvent 全參與者簽章」與實作(被踢者自簽自報)的措辭收斂;②2–15s 斷線的 state 級 catch-up sync 協定設計。