歷史記錄 · 2026-07-08¶
重大重構里程碑¶
2026-07-08 ─ 里程碑 18:拍 10f pages——18 routes 全量構圖+假 provider 通電+⭐SSG 36 路(測試 450/450、十二驗證全綠)¶
18 頁全落地(線框逐頁、17 元件覆蓋 18 routes——track/part 詳情共用):home(Hero+雙 CTA+精選列)/login(三入口卡+⭐建立流程=一次性助記詞 modal→抄寫確認→導車庫、PIN 錯誤走 errors.keyManager.key)/garage(大型展示占位+資訊卡=stat bars5+數值+On-chain pill+[車間][本機測試]+車位卡切換+新車)/garage-edit(三欄=8 類零件清單〔點選換件、tire×4/roller×2 成組〕+canvas ≥50%+屬性欄+[檢查]+底部固定 [測試][儲存]、1024 折單欄)/editor(§0.5 版面骨架=工具列+雙鈕右錨〔上鏈鈕帶費用報價〕+直向頁籤欄+右欄四子分頁+狀態列)/tracks·vehicles(篩選+排序+卡網格)/ugc-detail(評分五星+血緣 isBasedOn+takedown/pending pill+檢舉預檢+[選用]+⭐載入後動態重套 SEO)/race-config(人數/回合滑桿+逐回合場地·用車+無晶片+密碼→createRoom 導房)/room(⭐P2P 診斷逐人=TS/ping/direct·TURN/載入%/就緒+網路合計+聊天+[準備])/race(HUD=排名/圈/時間/NET/電量/三路溫度/worstFatigue/技能鈕 event·hold、⭐無 cooldown 無過熱預警紀律、假 session 合成幀通電、聊天收合)/result(排名表+TS Δ 紅綠+破壞彩蛋)/creator(信譽·作品·獲幣 formatMoney+代表作)/settings(10d 服務通電=音訊三滑桿/語言·主題·色弱/IPFS 貢獻+pinning 節點表/鍵位唯讀/隱私+分類重設+RELOAD_HINTS 提示)/about/help(三欄文件工具+搜尋)/dmca(notice/counter 表單→mailto 草稿)。基建:⭐fake-providers(十域 rich 假件=車位 CRUD 即時生效/UGC 24 卡/房間流 timer 合成載入進度/假 RaceSession 50ms 幀+HUD——契約 conformance 即 12 拍一行換真)+app 殼(navbar+餘額+mobile 底部導航 640 斷點+NavigationEnd hook=SEO applyToHead+route→BGM)+AppAudioService(routeToBgmSlot+轉場決策+⭐iOS 首擊解鎖 pointerdown once+音量 effect 即套;default 槽全 null=靜音待資產)+stats-display(VehicleStats→0–100 bar 顯示歸一)+o4-chat-panel(訊息流+輸入+100 則環)+i18n +157 keys→266×4 語系(nav/sort/stats/ugc/parts/pages. 全頁文案零硬字串;tracks 排序等 TS 層文案走 I18N.t)。⭐SSG 36 路 prerender 上線(@angular/platform-server+@angular/ssr 22.0.5 exact 使用者核可):main.server.ts(⭐BootstrapContext 透傳——缺之=NG0401 Missing Platform)+app.config.server(provideServerRendering 疊瀏覽器 config)+angular.json server/ssr:false/prerender.routesFile=ssg-routes.txt;⭐i18n/SEO 改 DI factory(urlPath 走 Location=base-href 剝除+prerender 每頁 URL 正確→每語系門牌出對應語系全內容、驗證=zh-TW/tracks「場地展示廳」·en/tracks「Tracks」·hreflang 五連·canonical 語系正確);⭐router 級 redirect 在 prerender 輸出「Redirecting」殘頁→語系前綴改掛同組真子路由(SSG 全內容)+瀏覽器端殼 snap-back(NavigationEnd 剝前綴 replaceUrl——canon「導向無前綴路由」runtime 行為保留);⭐server DOM 兩坑=querySelector 無命中回 undefined(非 null)+元素無 dataset/href 屬性 setter→seo applyToHead 全面 setAttribute 化+?? null 歸一。服務組裝(app.config):settings(IDB/Memory 依環境)→i18n(字典動態 import 四分支)→themes(fetch manifest+CSS 變數 DOM 套用+persist 走 settings display)→audio 啟動序 initializer。測試 +1=450/450(38 檔)(殼三 provider 掛載+routes 名錄 18 完整性)。staged:viewport 占位換 O4Viewport 真 3D+editor 核心接線+鍵位重綁(3D 拍)、檢舉/上鏈表單完整 UI+真 Api(鏈上拍)、真 RaceSession(race-runtime 拍)、about 版本+PIN gate(收口拍)、og 圖檔+27 雛形視覺細修(美術資產 pipeline)、⚠️initial bundle 953KB(警告閾 500/錯誤閾 1000——收口拍併 Lighthouse 調 budgets+懶化)。
2026-07-08 ─ 站 10 全站複審(10e/10x/10f+收尾):生產修 6、觀察 4——全結案(測試 450/450、十二驗證全綠)¶
生產修 6:①(中)檢舉 reason 字面 drift——詳情頁 'copyright' ≠ 域層 enum 'copyright-violation'(真 Api 接上即拒)→修字面+契約收緊(ReportDraft.reason/ArbitrationCaseView.reason:string→ReportReason,此類錯誤改由型別擋)②(中)車間儲存改名 bug——saveLoadout 硬帶 'Car' 覆蓋車位名→slotName 載入保留原名回寫(名稱編輯 UI 隨後續拍)③(中)⭐CSP 只注入根 index——prerender 後 dist 有 37 份 html、35 份門牌頁無 CSP→inject-csp 改遍歷 dist 全 html 注入(實測 37/37)④(細項)snap-back 丟 query/hash——/zh-TW/tracks?ref=x 剝前綴後 query 遺失→suffix 保留⑤(細項)已銷毀後註冊 onDestroy 會 throw——room/race 頁非同步接線(joinRoomChat then)在快速離頁時炸 unhandled rejection→destroyed 旗標守門(先旗標、已銷毀改即時 leave())⑥(機械)canon 程式架構 §13 語系前綴機制描述同步實作精確形(前綴掛同組路由+殼 snap-back——router 級 redirect 的 SSG 空殼坑已記)。觀察 4(備查不改):主題切換未 live-apply(ThemeService 無 DI token、現僅 default 單主題——theme picker 真接線隨主題資產首入);PIN 錯誤 i18n 參數缺口(Result<…,Error> 只載 key、{attemptsLeft} 無從插值——contracts 錯誤形擴充候補、隨真 identity Api 一併);詳情頁 title 短暫顯示 {name} 占位(殼先套基礎 meta、實體載入後重套——runtime 過渡可接受、門牌頁不受影響);GH Pages 部署需 --base-href /open-4wd/(既定收口拍 CI 事項)。掃描面:檢舉 reason 全站字面、DMCA 信箱(dmca@open4wd.org=dmca.md §DMCA agent 有據 ✓)、ui-kit 殘檔 0、inline template 僅剩 spec 宿主(測試夾具、合規)、fake timer 清理鏈、audio 轉場矩陣、生成物決定性。
2026-07-08 ─ 10f 收尾補刀(使用者指示):⭐元件檔案結構統一+bundle 預算調整(測試 450/450、十二驗證全綠)¶
⭐元件檔案結構統一(開源可讀性/降合併衝突面——canon 程式架構 §14 補則):全部 @Component「一元件一同名資料夾、template(.html)/樣式(.scss)/邏輯(.ts)分檔」——inline template/styles 全面退場。範圍=pages 17 頁+shared 2(chat-panel/ugc-card)+ui-kit 13 元件(⭐五合一 inputs.ts 拆為 input/select/toggle/slider/tab 五資料夾+O4Option 歸 ui-kit/types.ts;toast.ts 拆 toast/toast-service.ts+toast-container.;forms.ts 拆 forms/validation-message.ts+form-field.);app 殼原本已分檔(不動);服務/純邏輯模組不涉。轉換以一次性腳本執行(backtick 區塊抽出+相對 import 深一層改寫),barrel/routes lazy 路徑全數對齊。bundle 預算調整:initial 500kB/1MB→1MB/2MB(現值 953KB=fontsource 628KB+假 provider 期 main;三 D 拍後再檢視)、anyComponentStyle 4/8→6/12KB;⭐build 歸零警告(附帶修 NG8102×2=race-config template ?? 冗餘移 TS helper)。測試 450/450、36 路 prerender、十二驗證全綠。
2026-07-08 ─ 里程碑 17:拍 10x 介面契約凍結——AppDataProviders 十域 facade+viewport/race-session/bootstrap 契約(測試 449/449、十二驗證全綠)¶
src/pages/contracts/ 落地(總規劃 10x;18 頁的唯一資料入口、假 provider 與真實作同型別互換):AppDataProviders 十域=identity(三態 status/建身分回一次性助記詞/importMnemonic/PIN 解鎖——拒因走 errors.keyManager. i18n key)+garage(車位 CRUD+GarageLimits 🪙開位報價+statsOf=mechanics-catalog 同步純函數+partsCatalog 8 類公版+UGC 併列)+economy(balanceX100 bigint/月流水/贊助榜/上鏈費報價)+reputation(ReputationView 分/有效分/星+history)+ugc(browse 分頁查詢+UgcCard/UgcDetail〔similarityPending+takedown 三態+myRating〕+creatorProfile+lineageOf+rate/revoke〔useMatchId 背書〕)+matchmaking(quickMatch/createRoom/joinRoom+subscribeRoom→RoomView〔P2P 診斷逐人 ping/direct·TURN/載入%/就緒——線框「遊戲 UX 非除錯資訊」〕+submitLoadout carRotation+matchSummary 含 TS 變化與破壞數彩蛋)+chat(ChatRoomHandle=send/subscribe/block/exportEvidence——沿站 9 原語)+spectator(joinAsSpectator 五拒路→SpectateHandle 幀流與比賽渲染同型)+moderation(canFileReport 預檢/fileReport/myReports/仲裁收件匣+castVote)+settings(直用 10d SettingsService)。RaceSession 契約:start(RaceStartContext=match|local-test 雙模〔車庫/編輯器出口 A 同契約不上鏈〕)/feedInput(SkillInput=chip 槽按放、迷你四驅無方向盤)/onFrame(RenderFrame=simTick+车位姿+brokenPartIds)/onHudState(HudState=賽內機制 §2.4 逐項——rank/lap/電量/三路溫度/worstFatigue/技能鈕 event·hold/NET 燈;無過熱預警紀律入註)/onRoundEnd/onMatchEnd/abort+RaceSessionFactory。O4Viewport 契約:SceneDescriptor 四場景(vehicle-showcase/assembly overlay 三開關/track-preview/race=FrameSource 餵入——race-runtime 與 spectator 同型直插)+ViewportEvent(select/camera-mode 沿 spectator CameraMode)。bootstrap 組裝形:createOpen4wdApp(config)→{ledger, providers, raceSessionFactory, dispose}。DI=APP_DATA+RACE_SESSION_FACTORY token+provide 對(provideI18n 慣例)。穩定域型別直引不重宣告(VehicleLoadout/Room/MatchConfig/ChatMessage/ReportInfo/VehicleStats/CameraMode/Open4wdLedger);讀取 Promise、串流 subscribe→Unsubscribe、命令 Result。測試 +3=449/449(38 檔):十域最小 stub 全型別 conformance(=頁面拍假 provider 種子)+RaceSession 契約形+DI provide 對。canon 同步=程式架構.md pages/ 列補 contracts/ 說明。契約變更紀律*:後拍需擴充→先改 contracts(單一權威)再改實作與頁面。
2026-07-08 ─ 里程碑 16:拍 10e seo——MetadataService+JSON-LD+三產物生成器(測試 446/446、⭐十二驗證全綠)¶
src/seo/ 落地(canon 程式架構/seo.md 逐條):seo-routes.json=路由 SEO 名錄權威(18 routes;indexable 三態 static/ugc/none——static 8 頁進 sitemap+SSG、ugc=track/part/creator 待 UGC 索引、none=app 流程頁;og 圖固定路徑或 ogImageParam 動態欄)+matchSeoRoute(:param 段萬用+語系前綴剝除防禦——SSG 前綴形進站也能比對)。MetadataService(埠注入 SeoI18nPort=I18nService 結構子集、canon §2 介面形):getMetadata=i18n seo.* keys 出 title/description(動態頁 params 插值、creator 走 ICU 複數)+canonical/hreflang=語系前綴形收錄 URL(HREFLANG_MAP×route、x-default=en)+og/twitter 全欄;applyToHead=upsert 冪等(自然 selector 更新、換頁不累積、keywords/JSON-LD 缺時清場)+<html lang> 對齊。JSON-LD 三建構器(canon §3):首頁 @graph(VideoGame——MIT license/isAccessibleForFree/1–8 人取 PLAYERS_PER_RACE_MAX+WebSite SearchAction→/tracks 搜尋)、場地 Game(author Person+isBasedOn fork 血緣 URL)、創作者 Person(memberOf Open4WD Community)+JsonLdInjector(data-managed script 單例替換)。i18n +36 keys×4 語系(→109 keys):seo. 全 18 頁 title/description(canon §1 表 en 逐字、home description 沿用 app.description、home keywords)。scripts/generate-seo.mjs 三產物+⭐check:seo=第十二驗證:robots.txt(canon §5 形+Sitemap 絕對 URL 自 CANONICAL_BASE_URL 注入——cutover 換常數即換產物)、sitemap.xml(8 靜態頁×4 語系、每條 xhtml:link alternates 五連;>50k 拆 sitemapindex 四檔邏輯已備)、ssg-routes.txt(4 條未前綴殼+32 前綴收錄頁);UGC 索引 seo-ugc-index.json 缺席=僅靜態並記 log(無活鏈期)。index.html head 基線:description/author/theme-color/apple meta。⭐theme-color 漂移修(canon 三處):seo.md #1a1a2e+pwa-offline.md #1a1a2e/#0f0f1e 皆早期草稿值、與 10a 設計系統 default 主題脫節→統一「=default --color-bg-canvas(現值 #0d0f12)」單一權威+themes:validate 新守門(index.html theme-color ≡ default canvas);canon 另修 §4/§6(生成器名收斂 generate-seo.mjs、SSG 清單構成精確化)。staged:router events+resolver 接線(10f)、prerender builder(需 @angular/platform-server+@angular/ssr 依賴核可、頁面成形後)、og 圖檔(美術資產 pipeline)、seo-ugc-index.json(OrbitDB top-N 匯出、鏈上通電後)、sitemap 每日 GitHub Actions(拍 14 隨 CI)。測試 +7=446/446(37 檔)*:路由比對/首頁 metadata+@graph/動態插值+isBasedOn/ICU 複數+params 未備妥略過/applyToHead 冪等+清場/Injector 單例。
2026-07-08 ─ 里程碑 15:拍 10d settings——AppSettings 七分類+SettingsService(測試 439/439、十一驗證全綠)¶
src/settings/ 落地(canon 程式架構/settings.md 逐條;設計權威=遊戲機制 §9):types(AppSettings 六持久分類——about 純讀取不入模型、developer 第 8 項隨 UI;AudioSettings/DisplaySettings/NetworkSettings〔PinningNodeConfig+ipfsContribution+turnServer——credential PIN 加密隨 key-manager 接線〕/GameplaySettings〔KeyBinding 最小形、預設鍵位權威=賽內機制 §2.4.2〕/PrivacySettings+DEFAULT_SETTINGS canon §2 逐行+SETTINGS_RELOAD_HINTS(語言=reload、TURN=next-connection、其餘即時——canon §4 表資料化供 UI 提示);⭐AudioSettings 雙定義收斂=audio-system 本地版刪除、re-export settings 權威(依賴向=audio→settings 無環)。SettingsService(signal 讀點+埠注入 store):persist=統一 IndexedDB 'settings' store 每分類一列 {key,value,updatedAt}(分類級 LWW=未來多裝置同步合併單位);load=defaults 疊已存分類(缺分類=default、未知分類忽略=前向相容);update 寫入防呆=⭐音量 clamp 0–100+⭐官方 pinning 節點不可刪(清單無 official=自動補回預設官方節點——canon §5 防呆);resetCategory/resetAll(PIN 確認=UI 層 gate);Memory+IDB 雙實作(openUnifiedDb 每操作開關連線慣例)。測試 +5=439/439(36 檔):載入合併+未知分類忽略、clamp+persist+signal、官方節點防呆雙向、reset 兩級、提示表形狀。staged:設定頁 UI 七分類+深層連結(10f)、重設全部 PIN gate+TURN credential 加密(key-manager 接線)、about 版本 6 欄位(拍 14)、developer 項(UI 站)、多裝置 LWW 同步(未來)。
2026-07-08 ─ 里程碑 14:拍 10c i18n——四語系字典+I18nService+ICU 複數(測試 434/434、⭐十一驗證全綠)¶
兩裁決執行前置:①⭐THEME_RESERVED_TOKENS=8 tokens(狀態語意三色+觸控下限三尺寸+HUD 字級二)四處同步(layout.ts/themes-validate.mjs/程式參數表 §14/runtime 防禦測試=主題覆寫 --color-danger 被擋恆取 default);②⭐字型 self-host=@fontsource-variable/inter@5.2.8+@fontsource/noto-sans-{tc,sc,jp}@5.2.9(皆 exact、audit 恆 2 low)——angular.json styles 引入(Inter Variable+CJK 各 400/700)、--font-family-base 首位 'Inter Variable'、667 woff2 分片入 dist(unicode-range 惰性載入=CJK subset 免手動管線)、canon 前端技術策略 §16 補注(@fontsource 取代 glyphhanger 管線、OFL 隨包、授權彙整頁隨 /about)、mono=系統等寬。src/i18n/ 落地(canon 程式架構/i18n.md+語系清單.md 對映):四字典(zh-TW 撰寫源/zh-CN/en fallback/ja;73 keys 起始集=各站零文案 code 收斂——app 品牌副標四鍵(語系清單 §2 逐字)/common/dialog/race(含 ICU 觀戰者複數例)/garage/settings/errors.keyManager 五碼+errors.p2p 四碼/chat.reject 七碼/spectator.reject 五碼/themes.fallback 三碼+category/validation 七碼/⭐skills 8×{name,effect}——canon 分組表機械擴 skills・dialog・chat・spectator 組)+I18nService(埠注入:loadDict/storage/urlPath/browserLanguage+devMode——setLang flatten+persist+lazy fallback en/t()=缺 key→en→dev raw・prod [key]/插值缺參保留 {param}/detectLang=URL 前綴→persist→navigator〔繁體圈含港澳〕→en)+⭐ICU 複數子集自寫({name, plural, =N/類別/other、# 替換、巢狀插值}——Intl.PluralRules 原生分類+平衡括號解析、零第三方依賴;canon「intl-messageformat 類 lib」以此達成、select 型別用到再擴)+Intl 日期/數字/金額(bigint minor 整數拆分 ÷100 兩位小數、免 Number 精度疑慮)+i18n pipe(impure+I18N token+provideI18n)+angular.json 資產映射 src/i18n→/assets/i18n+⭐i18n:validate=驗證鏈第十一件(zh-TW 基準;en 缺=error〔fallback 鏈前提〕、他語缺=warn、多 key=warn、非字串值=error)。測試 +6=434/434(35 檔):service 全路(fallback/雙缺標記/缺參保留/detectLang 五例)、真字典煙測(觀戰者複數 0/3)、ICU 子集(=N 優先/en one/#/巢狀)、formatMoney 三例、pipe DI 渲染。staged:bootstrap 接線(detectLang→setLang→provideI18n)+設定頁切換 reload(10f)、SW 字典預快取(拍 14)、程式碼 key 使用面掃描(i18n:validate TODO)。
2026-07-08 ─ 里程碑 13:拍 10b themes——ThemeService+分類註冊表+themes:validate(測試 428/428、⭐十驗證全綠)¶
src/themes/ 落地(canon 程式架構/themes.md+主題系統.md 對映):categories.json(起始僅 basic、分類=資料非 enum、顯示名走 themes.category.<id> i18n)+retired-ids.json(空;退役 id 永不重用)+ThemeService(純 TS 埠注入=可測:loadManifest/setCssProperty/storage)——setTheme 降級 guard(查無=unknown-id/退役=retired→回 default+fallbackReason 供 UI 提示、降級不寫 persisted)/applyPersisted(⭐load-failed 不改 persisted=離線缺資產回線自癒、主題系統 §6)/applyTokens(default 合併覆寫+⭐保留 token 恆取 default=runtime 防禦、CI 另擋)/⭐token 鏡像(合併後全量 Map——Three.js 不讀 CSS 變數、o4-3d-stage preset 重算讀此)/resolveAsset(canon §3 逐行:保留 SFX 槽恆 default/null 靜音/路徑=主題 URL /assets/themes/<id>//缺鍵 fallback default;與 audio-system 純檢核版語意同源)。default manifest 補音訊槽名錄 18 槽全 null(名錄完備、檔案可缺=全網靜音待補檔)。⭐themes:validate=驗證鏈第十件(主題系統 §7 逐條:id 正則+長度+退役+資料夾一致/category 註冊表/name.en 必填/token 值禁 url(/keys ⊆ default/保留 token 覆蓋擋+保留 SFX 覆寫警/default 音訊槽完備/⭐程式碼引用集 ⊆ default(掃 src 的 var(--xxx) 字面、tokens/ 生成物排除)/路徑越界/引用檔存在+資料夾檔案全被引用(防垃圾)/SVG sanitize(script・事件屬性・外部引用)/單主題 ≤20MB;音訊解碼煙測=TODO 隨音訊資產首入)——⭐首跑即抓真貨:Angular 鷹架歡迎頁引用 --gray-400 等不存在 token→鷹架 app.html 353 行清為 <router-outlet /> 骨架(10f 本就要換)+app.spec 對齊。⭐工具坑=mjs 區塊註解內含 */ 字面(「script/on/」)提前關閉註解=SyntaxError。測試 +4-1=428/428(34 檔):setTheme 套用+persist/查無・load-failed 降級不改 persisted/保留 token 防禦(名單現空=行為以 default 值驗)/token 鏡像全量/resolveAsset 三態+保留 SFX+URL 前綴/註冊表形狀。staged:Angular DI+settings persist 接線(10f)、SW 快取分路+升版自癒(拍 14)、聽測煙測(資產首入);⏳THEME_RESERVED_TOKENS 候選提裁決*(見當輪回報)。
2026-07-08 ─ 里程碑 12:拍 10a styles+ui-kit——token 供應鏈+o4-* 基礎元件庫(測試 425/425、⭐九驗證全綠)¶
決策檔:D-20260708-10
⭐token 供應鏈端到端(前端技術策略 §5 單一權威架構照做):src/themes/default/theme.json=token 名錄+預設值權威(設計系統 §2–4+§7 motion 逐表轉錄、54 tokens)→scripts/generate-tokens.mjs(前綴分類生成七檔 src/styles/tokens/*.scss :root 基準、生成物禁手改、⭐入 .prettierignore〔prettier 折 font-family 長行=check 漂移坑〕)→--check=驗證鏈第九件 check:tokens(同 check-materials 哲學)→styles.scss 全域基底(reset+token+全站 focus ring+prefers-reduced-motion)。⭐兩層 token 架構定案(使用者提問引發):語意層+元件層 --o4-*(細粒度旋鈕、值可 var(--語意) 回退、必登錄 default manifest=validate 治理同 audio 槽位;首例 --o4-button-primary-bg)——主題自由度=token 面豐富度;拒任意主題 CSS 覆蓋(社群 PR 資料包=overlay 釣魚/gameplay 資訊完整性〔THEME_RESERVED_SFX 哲學〕/元件 DOM 非穩定 API)——大幅視覺分歧走 token 全面+資產槽+3D preset 同步三正路;順修硬編碼=primary/danger 文字色 #0d0f12→--color-text-on-accent。o4-* 基礎 13 元件+forms 層(全六態、OnPush+signals、零文案):button(五 variant+loading 鎖)/icon(路徑註冊表、currentColor、fail-soft、裝飾 aria-hidden/語意 role=img)/input・select・toggle(role=switch)・slider・tab(segmented、tablist)=model() 雙向/modal(僅高風險確認、backdrop=原生 button tabindex−1〔a11y 規則〕、Esc+dismissible 鎖、z-1000)/toast(service signal 佇列+TOAST_DURATION_MS 注入時鐘+action 鈕、z-100 不阻擋)/card・status-pill(色+形雙識別)・stat-gauge(scaleX 只動 transform+meter aria+clamp)/⭐3d-stage 殼(renderer 注入工廠+HUD 疊層 pointer-events 架構+ResizeObserver+dispose;three 依賴鏈 headless new Scene() 煙測)/O4FormField+validationMessageOf(Angular ValidationErrors→validation.* key+ICU 參數、領域自帶 key 透傳;⭐表單層禁重寫門檻——規則權威在領域層)。⭐Angular 首戰坑四則(記入本機開發筆記):NG1053=signal query 禁 ES private/zoneless TestBed=宿主狀態用 signal+whenStable/setInput 對 model 以 Object.is 對上次設值去重(內部 set 不算——重設同值被跳過、測試直接寫 model)/label-has-associated-control 對投影控件靜態不可見(原生包裹關聯 runtime 成立=註記豁免)。測試 +9=425/425(33 檔)。staged:遊戲化(energy-bar/rank-badge/coin-chip/countdown/hud-*…)與容器(shell/navbar/tabbar/page-bg/states)元件隨 10f 頁面構圖按需落;icon 註冊表→sprite 隨資產 pipeline;⏳@fontsource 四顆(字型 self-host)待使用者核;⏳THEME_RESERVED_TOKENS 名單隨 10b 提裁決。
2026-07-08 ─ 里程碑 11 前置:客戶端整合總規劃定案+3D 依賴鏈(測試 416/416、八驗證全綠)¶
⭐總規劃定案(使用者三裁決):①站 10 切法 10a–10f 核可 ②pages 目標=C(全通電、不留懸空)——實作分拍但規劃一次到位:10a–10e(tokens/ui-kit/themes/i18n/settings/seo)→10x 介面契約凍結(AppDataProviders 十域 facade/O4Viewport/bootstrap 組裝形/RaceSession——pages 寫向契約、後拍實作塞入)→10f 18 頁全量(假 provider)→11 viewport+資產載入→12 bootstrap+Api+上鏈編排+治理 reducer(⏳slot×config 懸掛在此收口)→13 race-runtime→14 versioning+pwa-offline 收口→首次本機測試閘;每拍照舊八驗證+複審、承諾連續不中插。⭐規劃過程抓到站位缺席:比賽主迴圈組裝(input→世界構建→simTick→rollback→渲染→HUD→結算交換)原站位清單無認領→補立拍 13。全 staged 債務(14 模組 barrel 註記全掃)銷帳對照入本機規劃筆記(未入 repo)。③3D 依賴本站裝(避免中途卡住)。美術資源盤點:參考雛形 27 張(desktop+mobile 成對、覆蓋近全 18 routes;garage 資訊卡=站 8 vehicle-stats 消費端、race HUD 與賽內機制 §2.4 一致)+設計系統/頁面線框/前端技術策略=tokens・版面・元件權威;雛形圖英文字樣=概念版面示意(禁字鐵則管出貨資產、UI 文字全 DOM+i18n)。
3D 依賴鏈落地:three@0.185.1+@types/three@0.185.0(devDep;three 不含 examples/jsm 型別)皆 exact;audit 恆 2 low。⭐解碼側零下載——draco 解碼器(gltf 變體)與 basis_transcoder 隨 three npm 包出貨→angular.json 資產映射 build 期自 node_modules 輸出 /vendor/{draco-gltf,basis}/(lockfile 完整性即 provenance、零漂移、不入 check:vendored);編碼側 vendored=draco_encoder.js(google/draco 1.5.7=與 npm draco3d 同版鐵則、4e 記案兌現)入 public/vendor/draco/+PROVENANCE.md+check:vendored 第 3 件(sha256 ce9e4e70…)。build 產出三路驗證全在(draco-gltf/draco_decoder.wasm、basis/basis_transcoder.wasm、draco/draco_encoder.js)。測試 416/416 不變、八驗證全綠。下一步=10a(tokens+o4-* 元件庫+字型 self-host+o4-viewport 殼)。
2026-07-08 ─ 里程碑 10:spectator+audio-system+chat-system——比賽體驗三件套(⭐站 9 收官;測試 416/416、八驗證全綠)¶
src/spectator/ 三檔(canon spectator.md+觀戰流程對映):messages(RoleInRoom/RoomMember/SpectatorMessage/RaceEvent/CameraMode 五模式型別+checkElimination=致命 part(chassis/battery/motor)任一 broken=唯一淘汰條件〔零件與場景 §12、LETHAL_PART_TYPES〕;SpectatorRaceState=顯示層最小車輛姿態——觀戰者不簽章不跑物理、計時 ephemeral 不入帳本)+session(SpectatorSession=receive+視覺插值純核心:10Hz snapshot→線性補幀、位置 lerp+⭐四元數 nlerp 短弧翻正、alpha=距上次/廣播間隔封頂 1、首幀直出、prev 缺席車 passthrough;時鐘外餵=可測)+broadcaster(SpectatorBroadcaster=注入 send 埠、tick 外部驅動、open-only、無觀戰者零成本+evaluateSpectatorJoin 進房閘三拒路=關閉/滿員/密碼、拒因零文案 code)。src/audio-system/ 三檔(canon audio-system.md 對映):slots(BGM 7+SFX 11 槽名錄=機制一次完整檔案隨主題累積+routeToBgmSlot §2.1 表全映射〔/race/:id/result 先於 /race/:id、未映射=null 沿用不轉場〕)+resolve(⭐三態解析=路徑/省略 fallback default/null 明確靜音+保留槽(THEME_RESERVED_SFX)主題供檔一律忽略恆 default〔gameplay 資訊音效不可被主題弱化〕+decideBgmTransition 五路〔同檔續播/異檔 crossfade/目標無檔淡出/補齊 fade-in・none〕+三路音量 0–100→gain clamp)+manifest-check(§6 CI 檢核純函式=default 必列全槽/theme keys ⊆ default/保留槽覆寫警告——隨主題站併入 themes:validate)。src/chat-system/ 六檔(canon chat-system.md 對映):messages(ChatMessage=SignedPayload 傳輸、roomId 在簽章涵蓋內防拼接;SystemMessage=本地生成不經網路)+validate(validateChatContent 空/500+ChatRateLimiter 滑動 1s 窗時鐘注入+acceptChatMessage 收訊端逐則守門六拒路=驗章(注入埠、bootstrap 接 security.verifyP2PMessage 四關)→sender=signer→roomId→per-sender 強制限流→封鎖清單靜音→內容;⭐canon 草圖機械修=限流於接受時記帳〔草圖只查不記=限流永不觸發;且被拒訊息不占窗〕)+filter(ProfanityFilter=words normalize 命中→原文大小寫不敏感遮蔽+patterns 正則+版本不符跳過;⭐canon 草圖機械修=words 進正則前逸出特殊字元〔含符號詞會炸〕;顯示時過濾傳送原文、可關)+text-blacklist.data(隨版資料檔初始空、與 dmca 表同治理 shape 各域自持)+recent-teammates(近 50 人 FIFO 淘汰、Memory/IndexedDB 雙實作〔db open-4wd/store recent-teammates〕、nickname 解析注入、跳過自己)+history(ChatHistory=保留原始 SignedPayload、messageId 去重、exportEvidence=騷擾檢舉可離線驗真證據、房間結束即清)。測試 +15=416/416(32 檔):淘汰判定、插值三態+session 事件分派、廣播 open-only+進房閘、路由映射全表、三態解析+保留槽、轉場五路、音量 clamp、manifest 檢核、聊天六拒路+接受記帳(第 6 則 rate-limited)、過濾(遮蔽/regex/版本跳過/開關/語系隔離)、隊友清單(累計/50 淘汰/跳過自己)、證據歷史(去重/匯出原件/清空)。staged:joinAsSpectator 連線編排+10Hz timer+SpectatorCamera rig(UI/render 站)、AudioService Web Audio 接線+iOS 首擊解鎖+世界音效第二批(UI 站)、DataChannel 'chat' 廣播接線+spectator chat 分離頻道+ChatApi(UI 站)、themes:validate 整合(站 10 themes)。
同日落地複審(中 2 細 1 修+觀察 4):①⭐IndexedDB 佈局三方漂移(中)——canon pwa-offline §5=統一 DB open-4wd 單一 upgrade 集中建全部 stores;現況三方各行其是:editor local-store 用 open4wd(名 drift、4e 遺留)+自建 2 stores、新寫 chat store 無版本 open+自建 1 store(若 db 已被他模組以版本 1 建立而缺本 store→無版本 open 不觸發 upgrade→transaction 直接 throw=經典版本競合地雷)→ 修=openUnifiedDb 統一工廠先行落地 src/pwa-offline/(站 11 §5 的實作雛形:單一 upgrade 建 7 stores、onblocked 顯式回報)+editor local-store 遷入(DB 名歸 canon open-4wd;專案未發布無資料遷移問題)+chat store 改走工廠+每操作開關連線(長持連線擋版本升級;同 editor try/finally 慣例)。②canon 內部矛盾修(中)——pwa-offline §5 列 encrypted-keys store 於統一 DB,但 key-manager.md §5 明定獨立 open4wd-keys(2b 已依之實作)→ 以 key-manager.md 為權威(鎖定生命週期與安全隔離)、§5 移除該行+補「金鑰不在統一 DB」注+「單一 upgrade 集中建/用畢即 close」規範化。③觀戰進房閘漏房態維度(細)——觀戰流程 §4 加入時機:賽前(waiting)不可、倒數/比賽中/結算可——evaluateSpectatorJoin 補 state 閘('race-not-started'/'room-closed' 兩 code)+五拒路測試。觀察(備查):acceptChatMessage 驗章先於限流=正確序(限流以驗證後 signer 記帳——先限流會讓偽造 signer 灌爆受害者窗=靜音攻擊;crypto 洪水殘餘由傳輸層 gossipsub score 承擔);ProfanityFilter 符號拆詞(normalized 命中但原文 regex 不中)=canon 草圖忠實限制;RecentTeammate.reputationSnapshot 未填(canon 草圖同、隨 UI 需求);ChatHistory 房內無上限(房間結束即清=canon 語意、長房記憶體由 UI 層裁)。routeToBgmSlot 對 §13 全 18 routes 覆核一致(含 /race-config 頂層)。測試 416/416 不變、八驗證全綠。
2026-07-08 ─ 里程碑 9:mechanics-catalog——技能目錄+8 Stats(⭐站 8 收官;測試 400/400、八驗證全綠)¶
薄站(設計使然):60 機制庫已全數收斂進既有 spec(遊戲機制.md §6——晶片 8 skill enum+武器三分支+場地 entity 三類;完整明細屬早期探索封存非現行規格)、效果邏輯早在 3c-2/3c-2b/3c-3 落地——本站=收斂後的宣告面兩件:skill-catalog.ts(8 descriptors 逐列轉錄 §1 表:Event 單發/Hold 持續模式+i18n 鍵 skills.<id> 零文案+isHoldSkill;vehicle-sim 為行為權威、本目錄=UI/表單層單一宣告源不重複效果邏輯)+vehicle-stats.ts(8 Stats 車輛資訊卡推導——§5 表逐列、⭐顯示層非共識〔不進物理/經濟;頁面線框資訊卡用〕:Mass=Σ mass_g/Drag・Lift=body aero(box 期無烘焙=null 不硬湊)/Stability=軸距/重心高 ×100(canon 無式=顯示啟發、幾何未知 null)/Accel・TopSpeed=actual_power×torque 分配(actual_power=min(auto_input_w, declared_output_w) 同算式表 §2)/Grip=均值(friction+rolling_resistance)(§5 字面轉錄、顯示指標非物理量)/Endurance=capacity_mah;builtin adapter=質量 volume×密度+auto_input_w/auto_capacity_mah 依 K_MOTOR_INPUT/K_BATTERY_CAPACITY 推導(建模參數表 §3 同式)、混 UGC ref=null〔UGC adapter 隨資產載入層以 GLB extras auto_ 補齊〕)。新機制提案判斷標準(§6 三問)=社群流程、不落 runtime(模組 TSDoc 記載)。測試 +3=400/400(29 檔):目錄逐列對 canon 表+byId+isHoldSkill、stats 手算值+null 容忍+grip 均值、真公版 loadout 全解析(11 件質量/K 係數推導/tire 材質係數)+混 UGC null。staged*:UGC 零件 stats adapter(隨載入層)、資訊卡 UI 與顯示歸一化(隨 UI 站)。
同日落地複審(中 1 細 1 修+觀察 3):①Event/Hold 分類雙副本無鎖定(中)——vehicle-sim 內部 isEventSkill(行為權威)與目錄 descriptors 是同一份分類的兩個獨立副本、內容現同但未來加技能會靜默漂移 → 修=isEventSkill 自 vehicle-sim export(physics-engine barrel 補)+目錄測試交叉鎖定(8 skill 逐一 isHoldSkill === !isEventSkill——兩副本自此互為 tripwire);②canon §5 補顯示式注(細)——遊戲機制 §5 表補 Stability 顯示式(軸距÷重心高 ×100)+Grip 均值註+「8 Stats=顯示層非共識、actual_power 同算式表 §2、缺項顯示—」段(防未來把顯示啟發誤讀為共識值)+src/mechanics-catalog/ 對應注;測試補 weapon 路(12 件)。觀察(備查):adapter 不驗 ref 與槽位 type 相符(malformed loadout 得無意義 stats 不炸——loadout 有效性歸 loadout 驗證層);?? 50/?? 0 缺參預設=防禦性(builtin 零件必有值);i18n 鍵 skills.<id> 名單隨 i18n 站對齊(同 validation. 慣例)。測試 +1=401/401(29 檔)*、八驗證全綠。
2026-07-08 ─ 里程碑 8c:matchmaking——TrueSkill 整數定點/配對/房間/loadout/結算簽章交換(⭐站 7 全刀落地;測試 395/395、八驗證全綠)¶
決策檔:D-20260708-05
src/matchmaking/ 七檔(canon matchmaking.md+ledger.md §5+比賽結算流程 §8+算式表 §20 對映):⭐TRUESKILL_VW_TABLE 離線生成(trueskill-vw-table.ts 1201 筆 ×2=v(t)=φ/Φ、w=v(v+t) 的 round(×1000);產表器 erf 兩分支〔Taylor |x|<2/Lentz 連分數 x≥2、雙精度 ~1e-15〕;invariants 測試獨立重寫浮點對照 bit-exact+v 單調不增+w∈[0,1000)——同仲裁權重表模式);trueskill-fixed.ts(isqrt 精確 floor/lookupVW=clamp ±6000·步長 10·截斷不插值/updateRatingsAfterMatch=成對近似全 trunc、Δμ 累加為獨立整數項=順序無關、⭐σ'² 以 BigInt 精確計算〔σ²×w̄×σ² 中間積 ~10¹⁹ 超 2^53〕/adjustRatingForFrequentDisconnects=僅 disconnectCount ≥5 托底 σ 6000);⭐canon 欠明釘死 2(算式表 §20 補注):①σ 縮減式的 cSq=該玩家 P−1 個配對 cSq 的整數平均(截斷)——canon 未定聚合方式、實作即權威(同 comboHash 慣例)②σ_min 普適下限 vs 托底矛盾修——原式 σ'=max(isqrt, σ_min) 普適套 6000 會使頻繁斷線托底永為 no-op、且成熟玩家 σ 無法低於新人初值 → 更新無普適下限(縮減項恆 <σ²+τ² 注入=自然正下界)、6000 僅頻繁斷線者(matchmaking.md §1/比賽進行流程 §12.3 兩源本就如此);appliers.ts(match-result reducer=rating per-match 更新+disconnectCounts 累積+達標者托底;只讀寫自域欄位=compose 序不敏感);ledger 連帶=trueSkillRatings 占位 {mu,sigma}→TrueSkillRatingX1000 {muX1000,sigmaX1000} 具體形+⭐disconnectCounts 規則索引補欄(頻繁斷線判定需 lifetime 計數、hot matchRecords 會滾出——canon ledger.md state 補欄+codec wire、decode 容忍舊塊);messages.ts(MatchRequest/Offer/Accept=SignedPayload、matchmaking.md §2 欄位權威+computeTolerance=5+⌊t/10⌋×5 封頂 30+findMatchCandidate=封鎖/版本預篩/模式/規則/⭐窗口於 displayRating÷1000 整數分域);room.ts(Room/MatchConfig/RoundConfig+validateMatchConfig 域檢+handleHostLeft〔⭐state union 補 'closed'——canon sketch 回 closed 但 union 未列、機械修〕+房號 34 字元 ×6 撞號重生+blocklist 三執行點純函式);loadout.ts(validateLoadoutSubmission 六道=簽章/matchId/成員/loadout.peerId 一致/車數=回合數/local: 拒/黑名單拒+結構檢核與 B 軸版本=注入埠〔隨組裝檢核/versioning 模組〕+loadoutHashOf=重連防偷換);⭐settlement-exchange.ts(buildMatchResultCandidate=deriveTotalRanking+computeMatchSettlement+自簽〔⭐timestamp=deps.now()——canon 草圖 consensusNow(getAllEvents) 為示意、全量掃描不實際、±30s live 閘容忍與 appendEvent 同語意〕;reviewAndSignMatchResult=⭐簽章資格收緊非 forfeit 完賽者〔canon sketch 只查 ranking.includes、gate ① 同規則——ledger.md §5 補注〕+proposer 驗簽+deriveStateAt catch-up+verifySettlement+⭐總名次重算防偽+rounds/loadouts 本機觀測=注入埠;requiredSignatures=⌊N/2⌋+1、N=非 forfeit 完賽數;takeover=hash 對齊+collectedSignatures 逐簽驗證(任一不合法整包忽略)+⭐PeerId byte-order 最小裁定);⭐settlement-gate.ts(gate ③ 接線——validateIncoming 同步介面 × deriveStateAt 非同步 → SettlementRecomputeGate=同步快取+有界暖身(3 次、防垃圾 base DoS):未備=回 'base-not-synced' 丟 live 不阻決定性〔事件仍在 log、refold 依 §8.6 條 4「歷史同步不重驗」照收〕、備妥後同 base 同步驗算 match/mismatch;buildLiveValidator=match-result 分派三道〔presentCount/eligibleSigners 自事件推導=非 forfeit 完賽者〕、快照資訊注入埠〔未注入=整場無快照保守語意〕、其餘型別走標準單簽)。測試 +17=395/395(28 檔):⭐P=2 等分黃金值(29205/20795、σ 7193)bit-exact+重跑決定性、P=3 全序+σ 全縮、查表 t=0=798/637+clamp+截斷、isqrt 精確、σ 托底門檻、reducer 五連斷線→count 5+σ=6000、窗口曲線、候選逐道篩選、房間域檢+遞補+房號+blocklist、loadout 通過+六拒路、結算 build→review 往返(quorum 2)+四拒路(外人/forfeit/名次竄改/settlement 竄改/base 未同步)+takeover 裁定、gate 暖身→match/mismatch+validator 分派、VW 表 1201 筆浮點對照。staged:Matchmaker 廣播輪詢+gossip topic、SETTLEMENT timer/takeover 等候窗編排、快照監看(snapshotInfo)、blocklist IndexedDB store、loadout 結構檢核(車輛組裝 §2 隨組裝模組)、B 軸版本檢核(隨 versioning)、MatchmakingApi/UI 隨 UI 站。
同日落地複審(高 1 中 1 細 2 修+觀察 5):①⭐⭐foldGuard 多簽無條件放行=偽造結算直達全網 state(高)——log AC 為 write:['']、任一 peer 可把舊時戳(±30s 外)的偽造 match-result 直接 append:live 驗證整段被繞過(歷史條目只走 foldGuard)、foldGuard 對 MULTISIG_TYPES 一律 true(6b-2 staged 註記「量化細則隨業務模組」的欠帳)→ economy/matchmaking/reputation reducer 全套用=假獎金假 rating 假信譽決定性進所有 peer 的 state;即使時戳在窗內被 live 拒,條目仍在 log、任何 refold 照收=拒收只是延遲。修=gate ① 的 timeless 形入 fold:verifyMatchResultSignatureSet(eligible/quorum 全由事件自身推導=非 forfeit 完賽者、簽章集對 digest 全驗+⌊N/2⌋+1、畸形 fail-closed)→ foldGuard match-result 分支恆驗;時效性條款(新鮮度/重算)維持 live 專屬——canon 比賽結算流程 §8.6 條 4 精確化(「歷史同步不重驗」限時效性條款、簽章集 fold 恆驗——「已在鏈上」不構成多簽信任錨);其餘多簽型別維持放行(race-snapshot/desync 無 state reducer、checkpoint 另有三法)。測試 ×2=單元(真 Ed25519:足額過/缺 quorum/外人/forfeit 簽者/畸形)+fold e2e(舊時戳偽造不折入・足額真簽折入 hot);storage spec 的 match-result fixture 改真簽足額(守門生效後空簽 fixture 全滅的反向驗證)。②buildLiveValidator 多簽分派錯(中、自傷)——非 match-result 的多簽型別(race-snapshot/desync/ledger-checkpoint)被路由到標準單簽驗證:多簽事件 BaseEvent.signature=空簽章 → 合法 race-snapshot(新鮮度錨供給源)全數 live 誤拒 → 修=race-snapshot 走 validateRaceSnapshot(≥1 簽)、desync/checkpoint 放行(同 defaultLiveValidator)。③validateTakeover 補 reusedBaseLogHeadCid 與 candidate 內嵌 base 一致性檢查(細——「沿用原 base 不重算」的完整性)。④matchmaking.md §3 sketch 窗口比較補「÷1000 整數分域」註(細——算式表 §20 已定、sketch 尺度未標)。觀察(備查):「在場人數」vs「完賽人數」用語並存=語意重合(forfeit=不在場;quorum 分母實作=非 forfeit 完賽者、流程 §8 表為權威);lookupVW 非步長倍數 t 的桶向=t 空間 floor(決定性、實作即權威);LoadoutSubmission.signature 只簽 loadout 不含 matchId(canon 草圖原樣——跨場重放由傳輸層 SignedPayload sender=signer+提交者本人語意承接);P=1 ranking 事件 rating 原樣寫入(無實害);SettlementRecomputeGate 有界暖身燒完後恆 defer=保守方向(事件仍經 refold 落地、fold 簽章守門把關)。測試 +2=397/397(28 檔)*、八驗證全綠。
2026-07-08 ─ 里程碑 8b:moderation——檢舉/純仲裁計票/三振黑名單(測試 376/376、八驗證全綠)¶
決策檔:D-20260708-06
src/moderation/ 六檔(canon moderation.md+檢舉與仲裁流程對映;常數權威=程式參數表 §5):events.ts(Report/ReportRevoke/ArbitrationVote/ArbitrationResult 四事件;⭐fold 純函數性補欄 ×2(同 UgcRateRevoke 前例)=①ReportRevokeEvent 補 target(fold 以 (reporter,target) 定位、reportEventId 留審計)②ArbitrationResultEvent 補案錨摘要(caseKind/reporter/target/targetKind/reason——fold 不可查 ledger 原事件;moderation.md §5.6 收件第 1 條語意擴為「摘要與案錨事件逐位一致」));rules.ts(canFileReport 四道擋點=收件與 fold 守門共用:①對象終局(已下架/已全域黑名單;DMCA 清單不擋=平行路線)②同對象序列化+similarityPending 拒(防 pile-on k 案 k× 扣分)③檢舉者開放案上限 3 ④reason×targetKind 白名單+cid 可解析作者+同對象重複告檢查;canRevoke=抽籤點(+24h 檢查點)前;canReportAgain=撤回/流局/過期可再告;isReportExpired 90 天純 derive);panel.ts(isQualifiedArbiter 六門檻=兩造本人排除+全域黑名單排除+保護後分數 ≥200+場數 ≥10+年資 ≥30 天+與兩造 24h 雙向共賽排除〔cid 案先解析作者再過濾〕;⭐決定性抽選 drawPanel=sha256("${caseId}@${checkpointTimestamp} ${peerId}") hex 字典序取前 7〔canon 只定「檢查點雜湊為 seed」、序法實作即權威(同 comboHash 慣例);excluded 參數承接重抽排除缺席〕、池 <7 回 null=流局);tally.ts(filterTallyVotes=面板過濾+冒名排除(arbiter≠peerId)+每人 log 序最後一筆;hasQuorum=有效票 ≥4〔棄權不計分母〕;tallyArbitration=weight 查 arbitrationVoteWeightX100(保護後分數@抽籤點快照、呼叫端餵)、⭐通過判定 passW×100 > denomW×60 整數交叉相乘、passRatioX100 floor);appliers.ts(三 reducer:report=冒名+四道擋點守門 no-op、reportEventId=${reporter}@${timestamp} 合成引用〔fold 無 entry hash〕;report-revoke=(reporter,target) 定位、已結案/已撤 no-op〔撤回窗由收件層以檢查點驗、fold 信任鏈上既成事實〕;arbitration-result=同案首筆冪等→檢舉案 pass=檢舉者 +10+責任人罰則分流(違反版權 −100/cid 案 −20 ugc-blacklisted/peer 案 −20 griefing)+⭐三振純 derive(convictionsByOffender ≥3→peerBlacklist 永久)、reject=準確率記帳+惡意檢舉者三條件 AND(樣本 ≥10+準確率 <30% 整數交叉相乘+30 天無前科→−30+lastMaliciousReporterAt 寫入)/上傳期案(無 reporter、雙向不進準確率)pass=uploadPendingConvictions push+30 天窗寫入時修剪〔不扣信譽、下架另走 BlacklistEvent〕、reject=clearSimilarityPending 解除經濟隔離;⭐arbitration-vote 不註冊=票不入 state——tally 由 ArbitrationResultEvent 收件驗證層 replay 票序列重算〔§5.6 全網驗算取代多簽〕;reducer 不寫事件=fold 不得 appendEvent、BlacklistEvent 任一 peer 依 §5.6 另寫);index.ts。連帶收斂:①⭐BlacklistEvent drift 修——7c 實作 type 'blacklist'→canon moderation.md §1 'blacklist-cid'+補 triggeredAt 欄(anti-piracy appliers/spec 同步);②ledger 補兩規則索引欄(convictionsByOffender/uploadPendingConvictions——canon §5.5 三振與安全港 30 天窗使用但 state 未宣告、機械補欄+state-codec wire、decode 容忍舊塊缺欄);③ReportInfo/ArbitrationResultRecord 占位→具體形收斂(reason 收窄 union 在 moderation 層;interface 無 index signature 寫不進 Record 占位——同 7d ReputationDelta 慣例)。測試 +9=376/376(27 檔):四道擋點各路+撤回窗、資格六門檻(兩造/年資/場數)、⭐決定性抽選(同 seed 同面板/seed 變重骰/排除缺席/池<7 null)、tally(面板過濾+冒名+改票最後一筆+quorum+⭐60% 整數邊界 3:2 不過·4:2 過)、reducer 全流(pass=+10/−100/三振第 3 次觸發+同案首筆冪等、reject=準確率+惡意檢舉者 −30、上傳期案=安全港計數+pending 解除)。staged:仲裁進程編排(抽籤點/期滿檢查點監看、ArbitrationResultEvent 寫入與 §5.6 五條收件驗證、arbitration-vote 收件驗證、BlacklistEvent 寫入)隨站點 bootstrap;⏳流局(no-quorum)落點議題(複審議)——canon「流局不寫事件、outcome 純 derive」×「ReportInfo.outcome 入 state 需事件觸發 fold」結構矛盾:現實作 outcome 僅由 ArbitrationResultEvent 寫入(pass/reject)、流局案在 state 中維持 open→同對象序列化擋再告直到 90 天過期(canon 語意=流局可立即再告);上傳期案流局的 similarityPending 解除同無事件可觸發;ModerationApi/檢舉 UI 隨 UI 站。
同日落地複審(中 2 細 2 修+觀察 4+裁決 2 提請):①⭐fold 案錨守門缺失(中)——applyArbitrationResult 全信事件自帶案錨摘要:偽造摘要(reason 亂填誤導罰則分流/指向不存在的案)或換 reportEventId 對同案重播=fold 層雙重扣分/重複計三振・安全港(收件五條會擋,但 fold=最後共識線——同 7a 負額守門原則)→ 修=ResultEvent 補 anchoredAt(案錨事件 timestamp、收件條 1 摘要一致性含此欄)+檢舉案六重比對(開放案存在/未撤回/無結果/reportedAt=anchoredAt/reason/targetKind 逐位、不符 no-op)+上傳期案三重守門(ugcRecords 存在+uploadedAt=anchoredAt+similarityPending 審查中)+同 cid 上傳期案結果去重(掃 arbitrationResults 摘要——安全港不可換 id 重複計);ReportInfo 補 targetKind 供比對。②canon 未同步 8b 補欄(中)——7d/8a 前例皆回寫、8b 漏:moderation.md §1(RevokeEvent.target+ResultEvent 案錨摘要含 anchoredAt)/§2(ReportInfo.targetKind+convictionsByOffender・uploadPendingConvictions 兩規則索引欄+arbitrationResults 值→ArbitrationResultRecord 摘要〔state 不揹簽章;caseKind+target=上傳期案去重依據〕)/§5.6 條 1 擴寫(摘要逐位一致+fold 對 state 案錄守門)+ledger.md DerivedState 摘要副本同步。③未知 reason 放行(細)——isValidReasonTarget 對 runtime 亂填 reason fallthrough 視同 other → 收斂 default false(收件與 fold 共用道④)。④BlacklistEvent.triggeredBy string→EventId(細)。觀察(備查):三振僅計檢舉案定罪=§5.5「被檢舉」語意(上傳期案有自己的 30 天安全港閘、灰區撞衫不應永 ban);ReportInfo.reportEventId(合成 ${reporter}@${ts})與 ResultEvent.reportEventId(真 EventId)雙軌=id 僅作 fold 冪等鍵、定位靠案錨摘要(收件層驗 id→案錨綁定);pendingReports 只增不剪=canon §3 設計(讀點 derivePendingReports 過濾、隨 ModerationApi);details 500 字/evidence 上限=收件 shape 層(隨五條收件)。⏳裁決 2 提請:A)流局 no-quorum 落點(前段已識別)——建議=期滿檢查點 proposer 寫 no-quorum 結果事件(canon 變更:result union 擴 'no-quorum'、驗算=期滿+有效票<4+重抽輪盡;similarityPending 解除是經濟共識值、read-point derive 會把檢查點時間線+票集合拖進結算路徑=侵入大)。B)≥90 申訴案案錨不在鏈上(新識別)——§5.7 申訴案的 UgcUploadEvent 收件已拒=不在 ledger:§5.6 條 1「指向存在的上傳事件」結構上無法通過、fold 也解析不到上傳者(申訴判抄的安全港計數落空);申訴機器需 canon 定案錨上鏈方式(隨仲裁進程編排刀專輪)。測試 +1=377/377(27 檔)、八驗證全綠。
同日裁決 A 執行(使用者核可「依照建議」):canon 翻案「流局不寫事件」→ 流局寫 result: 'no-quorum' 結果事件(任一 peer 可寫+全網驗算;§5.6 no-quorum 分支=重算確認「期滿有效票 <4 且重抽輪已盡、或該輪合格池 <7」、池 <7 → arbiters: [];BlacklistEvent 不得指向 no-quorum;設計理由入 canon=outcome 入 state 需事件觸發 fold+經濟共識值〔similarityPending 解除〕的變化必須由事件觸發、讀點 derive 會把檢查點時間線+票集合拖進 economy 驗算路徑)——moderation.md §1(result union)/§2(ArbitrationResultRecord)/§5.2/§5.3/§5.4/§5.6+檢舉與仲裁流程 §1/§6.3+信譽系統 §8.2+anti-piracy.md §5.1 全同步(grep「流局不寫/純 derive」活文件 0 殘留)。實作=events/derived-state result union 擴 'no-quorum'+report 案流局分支(零 delta 零準確率、只落 outcome——canReportAgain 的 no-quorum 放行分支自死碼活化、同對象可立即再告)+上傳期案 reject/no-quorum 皆 clearSimilarityPending(流局不計安全港);案錨守門對 no-quorum 事件同等生效。測試 +1=378/378(27 檔)、八驗證全綠。裁決 B=使用者改提「正主檢舉下架盜版 → 正主再上傳」路線、評估回覆(可行且優於原選項 1——7c 防鹽化分層已承接 ≥90 對判抄品降級 pending、再上傳案錨在鏈上、申訴案型可整個移除;待使用者確認後收斂 §5.7+全 corpus 申訴殘引)。
同日裁決 B 執行(使用者核可「依照這個方向」):≥90 拒收申訴案型整個移除、正主救濟收斂為組合既有機器(①標準檢舉命中對象→仲裁下架+盜版者 −100 →②再上傳——對判抄下架品的 ≥90 命中依防鹽化分層降級 pending→走上傳期案、判非抄/流局→經濟開始)——結構洞(案錨不在鏈上)非修復而是消滅、上傳期案自此單一案型(案錨恆在鏈上)。canon 九檔同步:moderation.md §5.7(標題去「/ ≥90 申訴」+新「正主救濟路徑」段——含逐位元相同 mesh 不可復刻上鏈的 content-addressing 本質限制、DMCA 命中維持硬擋走反通知)/anti-piracy.md §4 sketch(enter-dispute 選項→report-match 引導檢舉)+§5 表格(誤判申訴列→正主救濟列)+§5.1(DMCA 硬擋去「可申訴」→反通知指引)/版權.md §5.3 表+§8 步驟 4/UGC上傳流程 §2+檢核表/編輯器操作 §5.3 表(拒收列補「檢舉此對象」入口)/信譽系統 §5.1 去申訴括註/程式流程/anti-piracy 尾注順修 drift(灰區「判抄→拒收」→「判抄→下架」——原句誤用申訴案語意)/玩家整體旅程痛點表順修(「過保護期可申訴」=無對應機制的懸空語→純仲裁定罪門檻+惡意檢舉者反制)。grep「申訴/enter-dispute/成功才上鏈」活文件 0 殘留(僅剩「無獨立申訴案型」否定句);程式碼零功能變更(7c「≥90 對判抄品=至多 pending 可收」與 8b 案錨守門全相容)。測試 378/378 不變。
2026-07-08 ─ 里程碑 8a:站 7 首刀——reputation 純 derive(測試 367/367、八驗證全綠)¶
src/reputation/ 四檔(canon reputation.md+算式表 §16/§17 對映):score.ts(ReputationReason 10 種+REPUTATION_DELTAS 數值表轉錄〔信譽系統 §2.2/§2.3 權威〕、clampReputation [0,1000]、⭐讀點契約 getEffectiveScore=scores 存 raw+新手 7 天下限 400 只作用於讀值〔仲裁扣分無新手豁免——豁免的是讀值非判罰〕、reputationToStars);weight-table.ts(⭐離線一次性生成 round(100×log10(rep+10)) 1001 項整數表簽入——runtime 禁浮點 log10〔tally 全網重算、跨硬體 libm 差翻轉 60% 邊界〕;測試以浮點對照驗生成物 bit-exact〔測試環境可用浮點驗常數〕;rep 200→232/500→271/1000→300 對齊算式表 §17 範例);appliers.ts(match-result reducer:斷線 −5 不論 finisherCount〔void 正為全體斷線而設、不經日額〕/fc≥3 場完賽 +1+matchCount 同計數/⭐連勝=最近 5 場含本場僅 fc≥3 場全冠 +5〔void 不入窗不破連勝〕/24h 日額 10 只蓋 match-finish+win-streak〔recentMatchGains 專用索引、發放時寫入+修剪、history 100 筆僅 UI 不得作依據〕/⭐UGC 使用門檻每 100 次 +5〔ugcMilestonesAwarded 索引防重複〕;⭐compose 序敏感=reputation 必鏈 economy 之後〔吃本場已入 hot 的 recentMatches 與已更新 usage——連勝含本場+milestone 檢查依賴〕;applyReputationDelta=供 moderation 仲裁 delta 收斂);index.ts。連帶收斂:ledger ReputationDelta 占位→具體形(reason 收窄 union 在 reputation 層)+補 ugcMilestonesAwarded 欄(⭐canon reputation.md §5.2 使用但 §2 未宣告的規則索引——機械補欄、state-codec wire 同步〔decode 容忍舊塊缺欄 ?? {}〕);ugc-rating 的 effectiveScoreOf 最小版收斂為 import reputation.getEffectiveScore(讀點單一入口達成)。測試 +5=367/367(26 檔):clamp/新手保護讀值三態/星級分段、⭐權重表 1001 項全量浮點對照+算式表範例點、斷線罰不受 void 限、void 場不加分、完賽 +1+matchCount、連勝第 5 場觸發 510、日額 10 硬頂(12 場 B=510)、usage 里程碑過百 +5+防重複(99+3=102→一次、105 不重發)——全走 compose 全域鏈(economy→reputation)。staged:仲裁類 delta(§5.3 惡意檢舉者三條件/§5.4 檢舉成立罰則)與三振黑名單隨 8b moderation(事件形歸屬);ReputationApi 查詢面隨 UI 站。
同日落地複審(細 2 補+觀察 3):①canon §6.1/§6.2 明列本模組的兩個連動 API 未落——補 arbitrationVoteWeightX100(clamp [0,1000] 查表包裝、8b tally 用)+shouldFilterByReputation(配對過濾、吃保護後分數、8c 用)+clamp 邊界與新手讀值過濾測試。觀察(備查):winner 連勝檢查加了 forfeit 防禦(canon 草圖無——ranking[0] 結構上幾乎不可能 forfeit、防禦性);matchCount 不受日額影響(「同一計數」讀為場次資格定義而非發放成功——日額限信譽發放、非場次統計);連勝窗排序平手(同 finishedAt)依 Map 插入序=fold 序=log 序、V8 stable sort ✓ 決定性成立;cause=matchId(fold 無 entry hash、同 7d 慣例)。測試 367/367 不變、八驗證全綠。
2026-07-08 ─ 里程碑 7e:dmca client 側+similarity 公式定案(⭐站 6 全站收官;測試 362/362、八驗證全綠)¶
決策檔:D-20260708-09
兩項裁決執行(使用者「依照建議」):①slot amount×config 驗證=選項 A——維持懸掛至 ConfigUpdateEvent reducer 落地(epoch 化 config 查詢、對事件時點當期 config 驗 amount ≥ vehicle_slot_minor;現狀守門=正數+餘額+防跳級、playtest 前實害趨零);②computeFeatureSimilarity v1=選項 A 落地——⭐算式表新 §21(五維相對差〔vertex/volume/surface/AABB 三軸 max/重心三軸絕對值域 max〕ppm 整數法→similarityX100=max(0,100−floor(max(五維 ppm)/10⁴));取 max 哲學=最不像的維度決定不相似度、多維各改 8% 的稀釋式洗白仍 sim 92 照拒;pcaAxes/isMirror v1 不進式=標準化已對齊/翻正;⭐版本護欄=綁 fingerprintVersion、升級舊作品鎖原版跨版放行=公式演化不成硬分叉;舊 §21 常數權威來源→§22〔活文件零引用、重編號安全〕)+anti-piracy.md §3 補公式權威引用+src/anti-piracy/mesh-fingerprint.ts 落 computeFeatureSimilarity 預設(SimilarityScorer port 保留)+測試 5 路(全同 100/單維 10%→90/多維 8% max→92/重心負軸/近全異 floor→1)。
src/dmca/ 六檔(canon dmca.md client 側;⭐DMCA 全維運層化——後端 API/collection/Agent 認證屬 open-4wd-pinning):types.ts(DMCANotice/CounterNotice 表單 schema+DMCANoticeStatus 七態+TakedownEntry+BlacklistEntry);takedown-list.data.ts+blacklist.data.ts(隨版出貨資料檔、初始空;⭐canon 路徑調整=.json→.data.ts〔型別安全+免 loader、PR review 語意同〕);takedown.ts(buildTakedownSet=removedAt 濾除〔清單可逆〕、isDmcaTakenDown、counterNoticePathFor=/dmca?counter=<noticeId>〔⭐公開下架標示即「通知創作者」實體——匿名 PeerId 無 email 可寄〕;⭐不設配對閘=絕版品模型、⭐不進共識〔收件驗證不得吃本清單〕、不做遠端拉清單);blacklist-check.ts(checkBlacklist 三 matchMode×大小寫×四語系×五欄位;pattern 信任源=PR review);index.ts。測試 +3=362/362(25 檔):下架清單可逆+反通知路徑、黑名單三模式/語系過濾/多欄位/空值跳過、similarity v1 五路。staged:/dmca 表單頁+詳情頁下架標示(UI 站)、canUseUgc 併查(moderation 刀)、後端 API/dmca-notices collection/Repeat Infringer 拒服務(pinning repo)、Agent 註冊 runbook(部署期)。⭐站 6(ugc-fork+anti-piracy+dmca+economy+ugc-rating)全站收官;裁決懸掛餘 1=slot amount×config(隨治理 reducer)。
同日落地複審(canon drift 1 修+觀察 3):①dmca.md §4 sketch input 鍵名 drift(細)——sketch input 寫 {partName; description; tags} 但迴圈以 input[field](field∈appliesTo 枚舉=partDescription/partTags)索引:鍵名不一致=description/tags 永遠 undefined 掃不到 → canon 修為 appliesTo 枚舉名(實作本已按唯一自洽讀法收斂)。②§21→§22 重編號殘引掃描(照鐵則全 corpus、非截斷):活文件僅 anti-piracy.md §3 新引用=正確指向新 §21、「常數權威來源」無外部引用、歷史記錄照鐵則不動——乾淨。觀察(備查):exact/substring 的 caseSensitive 旗標實作為全三模式生效(canon sketch 僅 regex 吃旗標=簡化寫法、欄位語意本應全模式);suggestion 暫為中文字面(canon sketch 原樣)——UI 站接 i18n 時轉 code 映射(同 P2pRejectReason 模式);DMCA 下架品若殘留 loadout 使用、royalty 鏈上照鑄=模型自洽(清單不進共識=金額共識不能吃 client 清單;法遵面 pinning 已 unpin→GLB 載不到→usage 自然消失)。測試 362/362 不變、八驗證全綠。
2026-07-08 ─ 里程碑 7d:ugc-rating——整數 Bayesian 評分/里程碑/RatingApi(測試 356/356、八驗證全綠)¶
src/ugc-rating/ 三檔(canon ugc-rating.md+算式表 §18/§19 對映):rating-math.ts(ratingWeightX100 三階梯 50/100/150、implicitRatingX100 四段查表〔0→300/1–9→360/10–99→420/≥100 封頂 480——log10 跨硬體不一致故查表〕、computeRatingX100 整數 Bayesian〔零票隱式/<5 票 (n·mean+5·300)/(n+5) 拉中性/≥5 加權平均〕、里程碑常數 10 票·450·+20);appliers.ts(rate reducer=⭐覆蓋先扣舊加新〔weightedSumX10000/totalWeightX100 bigint〕+weight 按評分者讀點保護後信譽〔effectiveScoreOf 最小版=raw+新手 7 天下限 400、待 reputation 模組收斂共用〕+守門〔冒名 rater≠peerId/builtin/黑名單/退役/similarityPending 不可評分=防審查期刷里程碑〕;revoke reducer=⭐以 (rater,cid) 定位現行投票+扣票重算;⭐高評分里程碑=≥10 票且 ≥450→作者 +20〔clamp 0–1000〕+history 前 100+awardedCids per-CID 去重、revoke 也檢查〔撤低分票可令 rating 升越門檻〕;ratingApiFor=economy settlement 注入真實作〔有 stat 用算值/無 stat 隱式查表〕——解掉 7a RatingApi 占位);index.ts。連帶:ledger UgcRatingStat/ExplicitVote/CreatorRatingMilestone 占位收斂具體形+state-codec wire(explicitVotes Map↔Record、weightedSum bigint 歸一、awardedCids Set↔排序陣列)。⭐canon 草圖純函數性修 2(ugc-rating.md §2/§6.1):①applyRatingEvent 草圖用 ledger.getEvent(rateEventId) 查原事件=fold 不可查外部——UgcRateRevokeEvent 補 cid 欄(fold 以 (rater,cid) 定位;rateEventId 留審計引用;canon §2 已補注);②里程碑 causeEventId 用 event.id=fold 拿不到 entry hash——以 cid@timestamp 合成(history 僅 UI 非規則)。測試 +7=356/356(24 檔):權重三階梯邊界、隱式四段、Bayesian 三態(333 例=1 票 5 星拉中性)、覆蓋扣舊加新、冒名/審查中/builtin 守門、revoke 定位+未投+冒名、⭐里程碑(10×5 星觸發 520/去重不重發/4×2 星+10×5 星=414 未觸發→撤 3 低分票=472 觸發)、RatingApi 雙路。staged:submitRating/revokeRating builder(24h 覆蓋防抖+useMatchId 背書查詢、隨 UI 站)、useMatchId 收件層驗證、getRatingDetail 等查詢 API。
同日落地複審(中 2 細 1 修):①score 範圍守門(中)——score: 1|2|3|4|5 僅 TS 型別、runtime 不擋:惡意 client 寫 score=100 直接灌爆 weightedSum(rating 可超 500 上限)→ 補 Number.isInteger && 1–5 否則 no-op;②⭐UgcMaintenanceEvent reducer 遺漏補實(中)——canon ledger.md §7 規則全齊卻被歸入「隨 ugc 生命週期刀」staged,而 7d「retired 不可評分」守門正依賴其上游 → 補 economy appliers ugc-maintenance:retire 限血緣創作者(否則任何人付一筆 burn 即可下架他人熱門 UGC)/renew·unretire 任何人可付(利他續命)/⭐lastUsedAt 不更新(續租只買 grace 窗)/守門=冒名+正數+record 缺席+餘額(非上鏈消費不可入負);③ratings 域 state-codec roundtrip 測試補(細——explicitVotes Map/bigint 計票/awardedCids Set 三形還原驗證)。測試 +2=358/358、八驗證全綠。乾淨面:權重讀點含 fold 順序正確性(common fold 先設 registeredAt→reducer 讀到新手下限 ✓)、覆蓋語意冪等、Bayesian 整數除法無零除結構、里程碑 clamp 0–1000。deriveRetireStatus(自動退役判定:no-use/低分/creator-gone 三候選+grace)仍隨 ugc 生命週期刀(需 lastEventTimestampOf 索引)。
2026-07-08 ─ 里程碑 7c:anti-piracy——相似度分類/宣告收件驗證/安全港/下架 reducer(測試 349/349、八驗證全綠)¶
決策檔:D-20260708-07
src/anti-piracy/ 六檔(canon anti-piracy.md 對映;門檻權威=版權.md §5.3):mesh-fingerprint.ts(MeshFingerprint{primary sha256, features, lsh?}+MeshFeatures 型別+isInFeatureBucket=vertexCount/volume ±10% 快篩〔bigint 整數比〕;⏳SimilarityScorer=注入 port——computeFeatureSimilarity 為共識值但 canon 未定公式〔版權 §5.3 只定門檻〕、不得擅自發明〔定案後改式=硬分叉〕→X100 整數計分介面、公式裁決項);similarity.ts(classifySimilarity:exact 優先→≥90 拒收/70–90 pending/<70 過〔全 X100 整數比、門檻常數=版權 §5.3 權威〕;⭐防鹽化分層=仲裁判抄下架品喪失硬擋力〔≥90 全為 blacklisted→連同灰區命中降級 pending;鏈式 70% 變體不可把封鎖區延伸〕、DMCA 清單維持硬擋〔站外 IP、清單隨 client 版不入共識分層〕、⭐混合命中取最嚴=任一 ≥90 活躍/DMCA→拒收且只回硬擋 matches);receive-validation.ts(validateSimilarityDeclaration:⭐驗宣告真實性不驗完備性〔驗完備會 split;隱匿由事後檢舉承接=安全港誘因自我執行〕;暫停期灰區宣告一律拒/逐筆重算注入 callback〔'glb-unavailable'→defer 排隊有界重試=settlement base 模式〕/≥90 對活躍/DMCA=應拒收等級宣告無效/≥90 對判抄品=至多 pending 可收);safe-harbor.ts(isPendingChannelSuspended=rolling 30 天判抄達 3=pending 通道暫停〔斷鹽鏈、<70% 原創上傳不受影響、不動信譽〕+countPendingByUploader+canDeclarePendingUpload=同時待審 <3——常數皆 Protocol.ledger 既有);appliers.ts(BlacklistEvent{cid,triggeredBy} 域 reducer:入黑名單+⭐解除 similarityPending〔下架即審結〕+同 cid 冪等;clearSimilarityPending helper=判非抄/流局解除〔審查期不補發〕供 moderation 案結果 reducer 收斂;BlacklistEvent 收件驗證五條隨 moderation §5.6);index.ts。測試 +7=349/349(23 檔):分桶快篩邊界、分類四態+防鹽化三場景(全 blacklisted 降級/混合取最嚴/DMCA 硬擋優先)、宣告驗證五路(空宣告/灰區過/≥90 活躍拒/≥90 判抄品可收/GLB defer/暫停期拒)、安全港(三振/過窗釋放/上限)、blacklist reducer(下架+解除+冪等)與 clear 判非抄路。⏳裁決項:computeFeatureSimilarity 公式(特徵向量→0–100 整數相似度;共識值、需 canon 定案後全 client 同式——建議形留待討論)。staged:canonicalize 八步驟+SHA-256(Rust/WASM+Sanitize Worker、隨編輯器上傳刀)、findByPrimaryHash/queryByFeatureBucket(儲存層索引)、判非抄/流局解除接線與 BlacklistEvent 收件驗證(隨 moderation 刀)、handleUpload UX 編排(隨 UI 站)。
同日落地複審(高 1 修+觀察 3;similarity 公式處置使用者核可):①⭐收件驗證吃 DMCA 清單=決定性違規(高)——canon §5.1 明文「DMCA 清單隨 client 版出貨、不可入共識分層(收件驗證只能吃 on-chain 狀態)」,原實作 DeclarationRecompute 回 dmcaListed 並納入拒收判定:不同 client 版清單不同→同宣告跨 client 收拒不一=分叉面 → 修=收件驗證只看 active(非仲裁判抄下架、on-chain 可決定);DMCA 硬擋只留 UX/分類層(classifySimilarity 的 dmcaListed 保留=本地上傳決策用)。觀察(備查):拒收結果只回硬擋 matches(判抄品命中不列——可 fork/申訴對象更準);pending 暫停期判定用收件端本地鐘=live-gate 性質(同 ±30s 首播閘、fold 層不重驗);分桶 ±10% 以 max 為基準(對稱、快篩非共識)。使用者核可:computeFeatureSimilarity 公式維持注入 port、專門一輪討論後定案進 canon(不擋進度)。測試 349/349 不變、八驗證全綠。
2026-07-08 ─ 里程碑 7b:ugc-fork——兩階段 Fork 偵測/物理指紋/衍生血緣(測試 341/341、八驗證全綠)¶
決策檔:D-20260708-08
src/ugc-fork/ 六檔(canon ugc-fork.md 全對映):fingerprint.ts(五族指紋型別=Rigid〔chassis/body/weapon;com/inertia 三軸〕/Rolling/Functional〔motor·battery+sidecar〕/Chip〔skillSlots〕/Track——全 bigint 整數量化 mg/μm/μm²/μm³;relDiff=ppm 整數法;⭐rigid 取最大分量防多維小改洗訊號、weaponBranchHash 不同〔含一有一無〕=強訊號 1.0;functional/chip 回雙值〔OR 邏輯 classify 層裁、sidecar 一有一無=1〕;kind 不符=防禦性全異);config.ts(ForkDetectionConfig 真結構+FORK_DETECTION_DEFAULTS=§4 權威值〔stage1 0.10、rigid .08/.15、rolling .05/.10、functional_mesh .08/.15、sidecar .10/.20、chip_mesh .05/.15、chip_skill .05/.25、track .10/.20〕;FINGERPRINT_VERSION=1 常數=derive_logic 與門檻治理分離);events.ts(UGCMetadata 完整形+UgcUploadEvent{cid,metadata,similarityMatches}+UgcForkEvent{childCid,parentCid}+AllowForkChangeEvent);classify.ts(computeGeometryDiff=(頂點差+AABB 三軸最大差)/2、classifyByDiff 單值/雙值 OR、evaluateForkDecision 候選集注入純函數+⭐跨 fingerprintVersion 一律放行〔無共同基準寧放過不誤殺〕);appliers.ts(ugc 域 reducer:ugc-upload=record 建檔+⭐血緣 derive〔權威=metadata.parentCid;一層 [p]/二層封頂 [p,gp];builtin/幽靈 parent/禁 fork=不建血緣但 record 照建=防蹭分潤〕+similarityMatches 非空→similarityPending 隔離;同 CID 重複上鏈首筆有效;allow-fork-change=僅原作者+⭐false→true 單向);index.ts。連帶:①ledger 補 composeApplierRegistries(多域 registry 組合:同事件型別各域 reducer 依引數序鏈接——economy 燒費→ugc 記錄同吃 ugc-upload;⭐鏈接序=derive 決定性一部分、隨 client 發版);②economy 事件形收斂——費用面 UgcUploadEvent/UgcForkEvent 占位刪除、appliers 改吃完整事件(uploadTypeOf(metadata))、config.forkDetection 占位→真型別+GENESIS 填 §4 預設;③⭐canon 矛盾修(economy.md §1/§2)——ugc-fork.md §5 fork 流程寫 upload+fork 兩事件、economy.md §2 對兩型都收費=雙重收費矛盾→釘死「收費限 UgcUploadEvent(原創/fork 都寫、單次 50/500)、UgcForkEvent=免費血緣宣告」(§1 表拆兩行+§2 switch 移 ugc-fork case;經濟系統上鏈費單次語意為據)。測試 +12=341/341(22 檔):relDiff ppm 三態、rigid 最大分量+weapon 雜湊三態、functional sidecar 三態、chip 對稱差集 0.5 例、track、kind 防禦、幾何快篩、classify 單值/雙值全分支、evaluateForkDecision 三路(命中/幾何跳過/跨版放行)、reducer 全守門(血緣三層封頂/builtin/幽靈/禁 fork/重複 CID/審查隔離/allowFork 單向+非作者)、⭐compose 雙域鏈接(燒費+建檔同事件、獨有鍵保留)。staged:uploadAsFork 編排(assetStorage+指紋計算屬編輯器管線)、AABB 候選查詢(儲存層)、ForkLineageApi 查詢面、similarityPending 解除(隨 7c anti-piracy 仲裁 reducer)。
同日落地複審(中 2 修+觀察 2):①⭐checkpoint 往返 fingerprint bigint 變型(中)——state-codec 對 ugcRecords.metadata(嵌套 unknown 結構)無解碼歸一:dag-cbor 把安全範圍 bigint 解回 number → 從 derived_state 塊還原的指紋餵 relDiff(bigint 運算)直接 throw、AABB 候選比對全癱 → 補 量化後綴規約修復器(_mg/_um/_um2/_um3/_mm/_mm2 結尾欄位=bigint、含陣列如 com_um——決定性、免跨層型別依賴;ledger 不能 import ugc-fork 型別=層次單向)+roundtrip 測試(含負值軸);②⭐parent 資格漏擋(中)——版權.md §4「parent 不得為已下架(仲裁黑名單)或 similarity-pending 待審」(fork child 實體含 parent mesh=侵權物再散佈起點、新血緣讓被定罪者從新作永續領 20%)——deriveLineage 原只擋 builtin/幽靈/禁 fork → 補 blacklist+similarityPending 兩擋(不建血緣、record 照建;⭐與 §8「parent 黑名單→分潤照舊」不衝突=時點不同:後者指既有 fork 的分潤不受 parent 後續狀態影響、前者是新 fork 當下的資格);DMCA 清單不進帳本→共識側不驗(builder 擋、維運層模型)。觀察(備查):chip skillDiff 對稱差集=skill 名集合(kineticRatio 重配不觸發→傾向判 fork=保守防洗白;canon「對稱差集」未明定元素形、若 playtest 認定 ratio 重配應算原創屆時擴為 (skill,ratio) 對);UGCMetadata 在 ledger 層維持占位 Record(具體形收斂受層次限制——ledger←ugc-fork 會成循環、cast 留在 ugc-fork applier 邊界);rigidDiff 不含 frontalArea_um2=canon 公式原樣(存而不比)。測試 +1=342/342、八驗證全綠。——economy 邏輯核心(config/公式/結算/域 reducer;測試 327/327、八驗證全綠)
src/economy/ 七檔(canon economy.md 全對映+算式表 §12–§15+程式參數表 §17):config.ts(EconomyConfig 全群組型別=§17 單一權威轉錄+GENESIS 預設——已知值照 canon〔min_player 3/combo 24h·5/per-player 20/上鏈費 50·500·0/車位 1000/revShare 70·20·10/hot_quarters 2〕、⭐待 playtest 值全初估逐一註記〔soft_cap 1M/prize base 50+multipliers [100,50,30,14]=算式表 §13 範例反推 bit 相容/royalty base 50+dedup 24h/sponsor min 100/lifecycle 六門檻〕+validateEconomyConfig=revShare 總和必 100);formulas.ts(⭐全 bigint 整除截斷抹零:monthFactorX100=softCap×100/(softCap+monthMinted)〔>99× floor→0=月硬頂〕、computeMatchPrize=base×fc×mult×factor/10⁴〔尾檔攤平、<3 人 void〕、nicheMultiplierX100=clamp(ratingX100−250,50,200)、computeRoyaltyForUgc=base×(ratingX100×20)×niche×factor/10⁸〔90 minor 維度驗算過〕、computeRoyaltyShares 70/20/10〔⭐缺層+整除殘留歸當前作者=原創實拿 100%、主作者缺席回空〕);events.ts(UgcUpload/UgcFork 費用面+SlotPurchase{payer,amount,newSlotIndex}+UgcSponsorBurn{payer,targetCid,amount};完整 upload schema 權威隨 ugc-fork/anti-piracy 收斂);state-helpers.ts(checkMonthRollover=UTC 月初純函數不寫事件/⭐computeComboHash=完賽者 PeerId 字典序排序+'\n' 連接+sha256 hex〔共識值、實作即權威〕/combo·prized 窗 append 即淘汰〔與檢查點 GC 語意等價〕/recordUgcUsage〔lastUsedAt 單調〕/shouldMintRoyalty=per(cid,player) 跨場 dedup/updateSponsorshipBoard=uniqueSponsors 防重計+top100 決定性修剪〔金額降序·cid 字典序〕/incrementSlot·UploadCount/extractUgcAuthors〔血緣作者=record.author、跨版本映射隨 versioning〕);settlement.ts(computeMatchSettlement=economy.md §4 逐行:rollover→factor→forfeit 集→finishers→comboHash→mintEligible=≥3 完賽 && K=5 窗未滿→prizes〔per-player 24h 上限達標者 0、名次保留他人不動〕→royalties〔黑名單/退役/similarityPending 三重過濾→per(cid,finisher) dedup→shares〕;verifySettlement=逐項比對五類欄任一不符拒;RatingApi 注入介面〔真實作隨 ugc-rating〕);appliers.ts(buildEconomyAppliers(config):⭐共識側守門一律「無效=no-op」〔apply 序決定性、雙花由 log 序裁〕——ugc-upload/fork=applyUploadBurn〔⭐首次上鏈負資產一生一次、債務封頂於該次費用〕、slot-purchase=餘額+newSlotIndex=derive+1 防跳級、ugc-sponsor-burn=餘額守門+榜、match-result=rollover→prizes/royalties 入帳+totalMinted/monthMinted+combo 窗〔僅合格場佔窗〕+prized 窗+usage 統計〔mintEligible 閘+similarityPending 跳過+僅完賽者〕+hot MatchRecord——⭐覆蓋 ledgerCoreAppliers 同鍵版〔registry spread 序〕、自含 matchRecordFromEvent〔自 ledger 匯出〕);index.ts barrel+staged 註記。連帶:ledger SponsorshipStat 占位收斂為具體形(totalAmountMinor/sponsorCount/uniqueSponsors/lastSponsoredAt)+state-codec 補 wire 轉換(Set→排序陣列+bigint 歸一)。測試 +20=327/327(21 檔):公式全對算式表數字表(月軟曲線 100/50/16/0、8 人賽 400/200/120/56+尾檔攤平+高峰減半、niche 五點、royalty 90/15、shares 原創 100%+殘留 71/20/10+單層 80/20+缺主作者空)、config 不變式、月跨界、comboHash 集合語意、K=5 滿窗拒+過窗復鑄、usage 亂序單調+去重窗、贊助榜 top100 修剪、結算五場景(標準 3 完賽 150/75/45+royalty×3 歸作者、void、K=5、per-player cap 名次保留、三重過濾+dedup)、verifySettlement 篡改拒、appliers 四路守門(首次負資產一生一次 no-op、跳級、餘額、入帳全欄+月重置)。接線點(站點 bootstrap):ledger options=registry 疊 buildEconomyAppliers、getSignerSet=()=>new Set(config.governanceSigners)、hotMatchQuarters=config;收件 gate ③=deriveStateAt(baseLogHeadCid)→verifySettlement(結算流程編排隨站 7 matchmaking)。staged:EconomyApi 查詢面+formatCurrency+消耗 builder 隨 UI 站;ConfigUpdateEvent 治理 reducer 隨治理事件流程;deriveRetireStatus 已有 config.ugc_lifecycle 可接(隨 ugc 生命週期刀)。
同日落地複審(高 1 中 1 細 1 修+裁決項 1):①⭐amount 正數守門(高)——slot-purchase/ugc-sponsor-burn 的 amount 為事件自報值、原實作僅驗「餘額 ≥ amount」:負額事件使 subBalance 反向加錢=自鑄漏洞(0 額 sponsor 亦可洗榜)→ 共識側補 amount ≤ 0(sponsor:< 1 minor)=no-op(canon 草圖同漏、守門原則「無效=no-op」涵蓋;上鏈費不受影響——費率取自 config 非事件);②⭐royalty 陣列順序決定性(中)——ugcsUsed 為 Set 迭代序、依 loadouts Record 鍵插入序:提案端本地構造的鍵序 vs 收件端 dag-cbor 解碼(canonical 排序鍵)可能不同 → royalties 陣列序不同 → verifySettlement 逐 index 比對誤拒(false negative 拒簽)→ ugcsUsed 過濾後補字典序 .sort()=settlement 產出 canonical 序(測試=同內容反轉鍵序兩 ctx → settlement 深等);③sponsor 下限錯置(細)——GENESIS sponsorship.min_amount_minor 誤標 100 初估待 playtest,canon 經濟系統消耗表為定值 ≥1 minor → 改 1、註記 canon 定值(builder UX 下限、共識側正數守門在 applier)。⏳裁決項(未動):slot-purchase 的 amount 自報且共識側未對 config 費率驗證(canon economy.md §2 草圖亦無此檢)——正數守門後殘餘風險=1 minor 買車位;對 apply 當下 config 驗 amount ≥ vehicle_slot_minor 有 replay 語意問題(治理調價後歷史事件對新 config 失效),正解需 epoch 化 config 查詢(ConfigUpdateEvent reducer 落地後按事件時點取當期 config 驗),屆時一併裁。測試 +2=329/329、八驗證全綠。後續使用者裁決(同日):車位費用確認保留(2026-06-01 定案=移除上限、費用 1000 扁平不變;使用者原疑「已移除費用」查證為記憶誤差);⭐單位 drift 修——玩家整體旅程「燒 50 幣/500 幣/1000 幣」三處實為 minor units 值(權威=程式參數表 §17;1 幣=100 minor)→ 全改 minor units 表述+車位行補權威連結(全 corpus 掃過:其餘檔〔UGC上傳流程/衍生流程/economy.md/編輯器操作〕本已用 minor、僅旅程檔兩行中招)。乾淨面:公式與算式表逐條 bit 相容、月跨界錨單調、comboHash 集合語意、首次負資產債務封頂 ≤ 該次費用、prized/combo 窗 append 即淘汰無界防護、registry 覆蓋語意(economy match-result 自含 hot 插入)。
2026-07-08 ─ 里程碑 6b-2:ledger 儲存接線——OrbitDB/helia+LedgerApi(站 5 全站收官;測試 307/307、八驗證全綠)¶
決策檔:D-20260708-03
src/ledger/ 新七件+interfaces 對齊(canon §10–§11 對映;依賴=既裝 4 顆零新增):event-codec.ts(log 值→LedgerEvent 解碼層:BaseEvent 四欄 shape 閘〔6b-1 複審承諾的 malformed 守門〕+⭐bigint 歸一——dag-cbor 對安全範圍 bigint 編一般 CBOR 整數、解碼回 number=金額欄位過 log 往返靜默變型(探測實證 5n→5),已知貨幣欄位〔burnAmount/結算 amounts〕解碼即還原 bigint;簽章 digest 不受影響=同 bytes);state-codec.ts(DerivedState 決定性序列化:Map→plain Record〔dag-cbor canonical 鍵排序=插入序無關〕、Set→字典序排序陣列、解碼側全欄位 Map/Set 重建+bigint 歸一;cidOfBytes=CIDv1(dag-cbor,sha2-256) 字串 brand;cold partition/signer set 塊 codec 同檔);checkpoint-store.ts(檢查點+簽章永留獨立庫=canon §10、不走 LRU;LevelStorage 預設〔瀏覽器 browser-level=IDB〕、storage 可注入;previous_checkpoint_cid 鏈狀 history);orbit-log.ts(OrbitDB 適配:entryStorage=ComposedStorage(LRU 10000, IPFSBlockStorage pin:false)=canon LRU primary/IPFS fallback、heads 走預設 LRU1000+Level;AC=IPFSAccessController write:['']=開放帳本〔有效性由收件驗證+apply 守門管、OrbitDB identity 屬傳輸層與 PeerId 解耦〕;⭐排序契約探測實證=all() 舊→新、iterator() 新→舊→entriesAfter 反轉供 fold;head()=clock 最大+hash 字典序決定性;BlockAccess=內容定址塊埠〔put=cidOfBytes+blockstore、get 帶 AbortSignal timeout、pin 冪等〕);ipfs-node.ts(helia 工廠:⭐helia 7 mixin 架構——主入口 createHelia=withBitswap(withLibp2p(withHTTP(...))) 且 @helia/libp2p 預設組態靜態拉 @libp2p/webrtc→node-datachannel、Node 載入即炸→helia 一律動態 import〔同 webrtc 前例〕;組合=createHeliaLight〔本地塊儲存+pins〕+掛入 peer-discovery 的 libp2p〔單節點單 libp2p〕;blockstore/datastore 預設 IDB origin storage);ledger-api.ts(Open4wdLedger=canon §11 LedgerApi:⭐驗證雙層設計——fold 守門=決定性 timeless 檢查〔單簽事件驗 BaseEvent.signature;多簽量化細則隨業務模組=鏈上可決定〕→所有 peer 無論何時同步 derive 收斂一致、關掉「live 拒收→歷史復活」分叉面;±30s 首播閘=live 專屬〔倒填/未來時戳擋鑄入端、歷史=既成事實〕、live 拒收記 session 集 refold 跳過;appendEvent=自動 stamp+簽〔canon Omit 形〕、appendPreSigned=多簽事件原樣上鏈不得重 stamp;檢查點三法=propose〔節流+cold 分桶 buildCheckpointState+塊 put/pin+30min expiry〕→signCheckpoint〔驗提案簽+treasury set 比對+節流+deriveStateAt 重算逐位 cid 比對〕→finalize〔validateCheckpointSignatures+永留庫+宣告事件+refold〕;deriveStateAt=純函數 10s deadline 超時 null;getMatchRecord=hot→cold 由新到舊 lazy;ledgerCoreAppliers=match-result→hot MatchRecord〔matchId 覆寫=refold 冪等〕;亂序條目→檢查點 refold 保 log 序);events.ts 補 LedgerCheckpointEvent(宣告事件、pinning service 訂閱面);interfaces/ledger.ts 對齊 canon §11(appendEvent Omit 形、CheckpointProposal canon 五欄、補 getAllEvents/deriveStateAt/getLatestLogHead/sync 兩法;DerivedState 占位改 object 上界);orbitdb-core.d.ts(@orbitdb/core 4 無官方型別→最小面自聲明、比照 vendored 型別策略)。測試 +14=307/307(20 檔):狀態序列化決定性(插入序無關同 CID/bafyrei 形/巢狀 Map/cold+signer set)、事件解碼(shape 閘+bigint 歸一)、檢查點庫鏈狀 history、Open4wdLedger 全流程(真簽 append 過標準收件、live 假簽拒收 session 集 refold 不復活、歷史 timeless fold+假簽跳過、registeredAt 倒填無效、⭐檢查點 E2E=雙實例 propose→reviewer 獨立重算簽→竄改拒簽→finalize→refold hot 裁剪+cold lazy 查回+節流、deriveStateAt 中途 head 不動 cache/未知 head null)、⭐真 OrbitDB 整合(stub ipfs+全 MemoryStorage+sync:false:排序契約+完整 E2E 含 OrbitDB 往返 bigint 歸一驗證)。⭐新坑二則(已記入本機開發筆記):①OrbitDB 內部(uint8arrays 無 process.browser guard)在 vitest jsdom 產 node-realm Buffer→noble 對 populateGlobal 的 jsdom Uint8Array instanceof 必炸、呼叫端無從重包→@vitest-environment node 檔級根治(本檔零 DOM 需求);②cborg useBuffer 含 !process.browser guard→新增 src/test-setup.ts(angular.json unit-test builder setupFiles 掛點)設 process.browser=true 關 Buffer 快路徑=我方 dag-cbor 路徑在 jsdom 全免疫。staged*:validateIncoming 疊結算三道/治理 signer set/getEconomyConfig 隨站 6;真多節點複製驗證隨本機測試期。
同日複審結案+bitswap 依賴執行(使用者核可):依賴=@helia/libp2p@1.0.5+@helia/bitswap@4.0.5 exact 直宣告(樹內同版零新上游、audit 恆既有 2 low)→ ipfs-node 升級 withBitswap(withLibp2p(createHeliaLight, 自有 libp2p))=canon 塊級網路補抓(LRU miss 跨 peer 抓塊/新玩家同步 derived state 塊)到位;⚠️mixin 生命週期實查=withLibp2p 對已啟動節點冪等 start、但 helia.stop() 會連帶 stop 傳入的 libp2p——關閉順序歸 app 層(TSDoc 記)。修 3(中 2 細 1):①⭐fold 收斂語意(中)——原 live 拒收記 session 集且 refold 沿用=「live 拒的 timestamp 超窗事件、晚同步 peer 會收」的跨 peer derive 分叉面;改為 fold 層統一決定性守門(shape/單簽 timeless 驗章/⭐未來時戳排除——未來戳放行會把 consensus clock 推去未來、癱瘓全部 time-based derive;各誠實 peer 於各自 fold 時刻同判定=時間推進下收斂、近未來戳=檢查點重算兩造短暫不一致→拒簽重試 fail-closed)、live 拒收只擋即時 fold/通知、session 拒收集移除(業務級量化拒收=站 6 apply 守門);②⭐狀態變更競態(中)——亂序條目觸發的 refold 原為 fire-and-forget、與後續 ingest 非同步交錯=lost update → ingest/refold 全走單一佇列序列化(#enqueue、錯誤不斷鏈)+狀態讀取端(getDerivedState/getMatchRecord/getMatchFromPartition/getRecentMatchesForPlayer/proposeCheckpoint 基底)drain 佇列後讀;③open() 重複開啟守門(細)。canon 檢核補:reviewAndSign「log_head 須為當前 head 祖先」=deriveStateAt 隱含達成(unknown head→replay 失敗→null→拒簽)、註解明文+測試落地(合法簽章掛未知 log_head 的提案→拒簽)。測試斷言 +7=307/307 不變(未來時戳即時+refold 雙排除+clock 未被未來戳污染、double open 拒、bad log_head 拒簽;「refold 不復活」語意改由決定性守門承擔、斷言全數維持)、八驗證全綠。觀察(備查):LRU_HEADS_LIMIT=1000 恰為 OrbitDB 預設(未顯式覆寫、canon 數值已滿足);readEvents(未知 checkpoint CID)→回全量(安全超集);event-codec normalizeAmounts 就地變異 log 條目值(冪等、僅 number→bigint 良性);Identities(ipfs) 身分塊走預設 pin:true。
2026-07-08 ─ 里程碑 6b-1:站 5 後半——ledger 邏輯核心(純邏輯零接線;測試 293/293、八驗證全綠)¶
決策檔:D-20260708-04
src/ledger/ 八檔(canon 程式架構/ledger.md §1–§7 對映;OrbitDB/helia 接線留 6b-2):events.ts(MatchResultEvent〔rounds/loadouts/economySettlement/signatures〕、RaceSnapshotEvent 最小 schema〔matchId+roundIndex+frame+checksum+signatures——結算 base 新鮮度時間錨〕、DesyncEvent、UgcMaintenanceEvent〔retire/renew/unretire〕、LedgerCheckpoint(+Proposal)、結算 pubsub 四訊息;⭐可簽章欄位一律 plain Record〔dag-cbor 不編 JS Map;canon 的 ReadonlyMap=記憶體視圖〕、多簽事件 BaseEvent.signature=空簽章、SignerSignature{signer,sig} 通用簽章對);serialize.ts(serializeForSigning=dag-cbor canonical 剔 signature/signatures+⭐realm 重包進 noble;signingDigest=sha256 32B;⭐assertPlainSignable 深度守門——dag-cbor 對 Map 是靜默編碼〔decode 回 plain object=往返不對稱〕、任意深度 Map/Set 一律拋);derive-utils.ts(consensusNow=max timestamp 空回 0、partitionKeyOf=UTC quarter、currentHotPartitions 跨年回數、collectAllUgcUsedInMatch〔builtin:/local: 過濾+Set 去重=同 UGC 跨回合 royalty 一次、重用 loadoutRefs〕、⭐deriveTotalRanking=算式表 §20.1 精確轉錄〔線性積分 P−k+1→總時間 DNF 計滿→PeerId 字典序〕);derived-state.ts(七域+hot/cold 骨架=canon §2 全欄位〔站 6/7 業務子型別以占位 Record 標注權威〕、emptyDerivedState、⭐applyEvent 共通 fold=clock 單調+registeredAt 首見即定 max(head,自報)〔倒填無效〕+套用計數——對未註冊型別也生效〔前向相容共通欄位跨版本一致〕、域 reducer 走 ApplierRegistry 注入);receive-validation.ts(首播限定:標準單簽=±30s+PeerId 自證驗章;⭐match-result 三道=①⌊N/2⌋+1 不同 signer ∈ 非 forfeit 完賽者〔重複簽不灌票〕②base 新鮮度=含本場最後快照+lag≤600s、整場無快照僅收 mintEligible=false、base 未同步判定不能→defer ③settlement 重算注入 callback match/mismatch/base-not-synced→accept/reject/defer〔未注入=跳過、economy 站 6 必接〕;race-snapshot=±30s+≥1 有效簽不驗 quorum;verdict 三態 accept/reject(reason)/defer);fork-resolution.ts(最長鏈→lexicographic-checksum 平手字典序最小勝);checkpoint.ts(治理 quorum=floor(2N/3)+1、signer set 下限 3、24h 本地鐘節流、bucketMatchesForCold 純分桶〔finishedAt quarter;分桶只在 proposer 階段〕、validateCheckpointSignatures〔裸簽章×signer set 試配、每 signer 一票、quorum 欄自洽防自報低門檻〕);index.ts barrel+staged 註記。測試 +20=293/293(19 檔:canonical 鍵序不變/簽章欄剔除/undefined·深層 Map/Set 拒編/quarter 切分跨年/UGC 蒐集去重/總名次三層 tiebreak 各路/fold 倒填·首筆定終身·registry dispatch/⭐真 Ed25519 全路收件=篡改·冒名·超窗·缺簽·重複簽·非完賽者·無快照鑄幣·stale base·defer·重算三態/fork 平手/checkpoint quorum 表+節流邊界+分桶+finalize 驗簽六路)、八驗證全綠。staged:6b-2=OrbitDB/helia 接線(ComposedStorage LRU 10000、checkpoint 獨立 LevelStorage 永留、access controller 掛收件驗證、syncFromCheckpoint、LedgerApi、DerivedState 決定性 Map→Record 序列化);deriveRetireStatus 隨 EconomyConfig.ugc_lifecycle;desync/ugc-maintenance 收件細則隨各業務模組。
同日落地複審(中 1 細 3+canon 草圖 drift 2 處修):①⭐match-result 收件 gate ① 寬容→嚴格(中)——原實作「跳過非法簽章項、數有效者達 quorum 即收」,canon 字面為「每個 signer ∈ 非 forfeit 完賽者、簽章對整事件有效」:夾雜任一非 eligible signer(→signer-not-eligible)或任一無效簽章(→invalid-signature)=整包拒收即使其餘已滿 quorum——事件是 proposer 定稿的固定工件、不該夾垃圾簽章,且寬容/嚴格實作差異=同事件跨 client 收拒不一的帳本分叉面;篡改事件 reason 隨之由 insufficient-quorum → invalid-signature(更精確);race-snapshot 維持寬鬆存在性檢查(canon「≥1 有效簽章即收」=掃到一枚有效即可、在場人數收件端不可知——與 match-result 分屬兩層,TSDoc 已記)。②DesyncEvent.signatures 誤用 SignerSignature[]→改回 canon 裸 Signature[](細)——canon §9 明定裸簽章、驗證以 participatingPeers 試配(同 checkpoint 模式);RaceSnapshotEvent 維持 {signer,sig}=canon 未定形+收件端無參與者集合可試配、裸簽不可驗(結構性必要、非偏離)。③validateCheckpointSignatures quorum 欄自洽 <→!== 精確等值(細)——quorum=floor(2N/3)+1 是 canonical 形、自報偏高門檻同屬畸形。④collectAllUgcUsedInMatch 回傳 ReadonlySet<string>→ReadonlySet<CID>(細)——canon 簽名;PartRef=CID|BuiltinId 扣除兩前綴後殘餘即 CID。⑤canon 草圖 drift:ledger.md §5 build/review 草圖 candidate.signer→candidate.peerId(BaseEvent 契約欄位名;×2 處)+程式流程/matchmaking.md 流程圖同步 1 處;grep 全 corpus 0 殘留。觀察(備查):收件端不重算 ranking↔rounds=canon 設計(三道明定;ranking 防偽屬簽章者 pre-sign §5 ②、共謀房本可整包偽造 rounds——若要收件端也驗屬 canon 變更需裁決);deriveTotalRanking 前置條件=rounds 非空且 ranking 含全員(canon 同款假設、malformed shape 由 6b-2 解碼層 shape 驗證守);佔位子型別(SponsorshipStat 等)未出 barrel=刻意留站 6/7 收斂。測試不變 293/293(嚴格模式斷言 +2 併入既有案)、八驗證全綠。
2026-07-08 ─ 里程碑 6a:站 5 前半——network-sync Rollback Netcode(零新依賴;測試 271/271、八驗證全綠)¶
src/network-sync/ 七檔(canon network-sync.md 全對映;rollback checksum ≠ 賽內快照):types.ts(NetworkMessage 五型:input/checksum 高頻 Race Sign Key 簽+nonce=frame、sync-request/sync-response/leave 低頻走 SignedPayload);input-buffer.ts(缺輸入=重複上一輸入、真輸入到達回報預測正確性——預測錯誤=rollback 觸發源);state-buffer.ts(快照環、容量 STATE_BUFFER_MAX_FRAMES=12 刻意大於回滾視窗);checksum.ts=ChecksumExchanger(每 120 frame 多數決;逾窗 30 frame 缺席不阻塞;⭐mismatch 不 rollback〔決定性重播治不了真分歧〕僅累積 strike、連續 3 次升級 persistent-desync;已決輪票倉即清防長局積累);wire.ts(DataChannel 二進位:Input=[type:1][frame:4LE][len:1][events×3B][sig:64]、Checksum=[type:1][frame:4LE][hash:32][sig:64];SKILL_WIRE_ORDER=SkillId 宣告序穩定鍵;⭐簽章 digest=sha256(type‖frame‖events/hash‖raceId)——raceId 入雜湊=跨場挪用無效、nonce=frame 單調防重放;簽驗屬傳輸層、本檔只出 digest 與編解碼);reconnect.ts(15s 視窗 500ms 輪詢、恢復即 requestSync、逾時=異常斷線交上層;時鐘/睡眠注入);rollback-engine.ts(主迴圈:本地輸入寫入+廣播→drain 遲到輸入→預測錯誤回滾重播→推進→週期 checksum;⭐回滾目標在窗外=直接升級 persistent-desync〔onPersistentDesync 注入〕、比賽照常推進;OutboundMessage=未簽兩型〔簽章傳輸層補〕;resolveChecksum 以本地留存 hash 對輪次多數決)。測試 +12=271/271(18 檔:預測語意全序/快照修剪/checksum 五態+strike 歸零+容差缺席+兩人平手/wire roundtrip+digest 敏感性+畸形拒收/重連雙路/⭐端到端=遲到輸入回滾重播含真輸入·視窗外 desync·checksum 節奏·多數決入帳)、八驗證全綠。站 5 剩餘=ledger(OrbitDB/helia 依賴屆時裁決;DerivedState 純邏輯層可先行)。
同日落地複審(中 1 細 2 修):①InputBuffer 長局無界成長(中)——輸入表保留整場每 frame(60Hz×8 人=數十萬條目;canon 草圖同漏、StateBuffer 有修剪它沒有)→ 補 prune(beforeFrame)+engine 每 frame 修剪至 STATE_BUFFER×2 之前(回滾永遠只回窗內);②checksum 票倉可被惡意成員灌表(細)——setChecksum 收任意 frame、未廣播輪永不 resolve 清票 → engine 收票閘=對齊廣播間隔+不過度超前(≤currentFrame+interval);③encodeInput 事件數 >255 靜默截斷長度 byte(細)→ throw。乾淨面=快照時序(推進前 push=回滾語意自洽、重播尾快照與當前 push 冪等)/未來 frame 輸入不誤觸回滾/wire digest 材料與 canon 公式逐項一致/分層零反向(引擎經 interfaces Pick 消費物理、不 import physics-engine)。觀察=SKILL_WIRE_ORDER 與 part-rules SKILL_IDS 皆為 SkillId 宣告序投影(雙處、僅可尾插——未來可上移 protocol 常數);checksum 一輪一決(tie/決後票倉即清、late vote 不再翻案)=刻意語意。測試 +2=273/273、八驗證全綠。
2026-07-08 ─ 里程碑 5b:libp2p 依賴進場+節點接線(站 4 全站收官;測試 259/259、八驗證全綠)¶
⭐依賴裁決(使用者定 A 案「現在進」+授權查證):npm registry 查證關鍵發現=gossipsub 於 libp2p v3 過渡期移交上游(@libp2p/gossipsub 16.0.4、js-libp2p monorepo 內與核心同步出版;ChainSafe 14.x 舊家錨 interface-2 不用)→ interface-3 世代全套成立;helia 7/OrbitDB 4 前瞻相容確認(皆 interface-3 側、站 5 免整組重釘;OrbitDB multiformats ^13 vs 我們 14=嵌套各用各的、asCID 跨版互認、站 5 再確認升版)。安裝 15 顆 exact pin(核可 14+@libp2p/ping 3.1.8——kad-dht 16 型別硬性要求的兄弟服務、同 monorepo):libp2p 3.3.5/@libp2p/gossipsub 16.0.4/noise 17/yamux 8/kad-dht 16.3.4/webrtc 6.0.26/websockets 10.1.16/circuit-relay-v2 4.2.8/identify 4.1.9/bootstrap 12.0.26/crypto 5.1.21/peer-id 6.0.12/multiaddr 13.0.3/multiformats 14.0.4;audit=僅既有 2 low(@angular/build 開發鏈);⭐node-datachannel 拒建(@libp2p/webrtc 的 Node 端原生 WebRTC、瀏覽器走原生 RTCPeerConnection——照 sharp 前例 allowBuilds: false);pnpm 自動記錄 minimumReleaseAgeExclude(家族當日出版)。接線五檔(API 全數 .d.ts 內省釘死、不猜介面):identity.ts(generateKeyPairFromSeed('Ed25519', seed) 確定性橋接 key-manager 金鑰=同鑰雙表示;型別經 Libp2pOptions['privateKey'] 萃取——@libp2p/interface 非直接依賴不得直 import);node-config.ts(canon §1 全參數落地:connectionEncrypters 新名/allowPublishToZeroTopicPeers 改名/createPeerScoreParams+createTopicScoreParams 建 rooms topic score 含 behaviourPenalty/appSpecificScore 掛點接 AppPeerScore/Open4wdServices 型別字面量向 kad-dht 證明 ping 兄弟服務存在;⭐@libp2p/webrtc 動態載入——其 Node 端入口於模組載入即 require 已拒建的 node-datachannel、vitest bundle 直接炸,動態化後測試不執行該路徑、瀏覽器 runtime 走 browser 條件;buildNodeOptions 傳輸注入化、測試止於組態形狀);dht.ts(TrackProviderHelper:provide/findProviders 達 limit 收束+DHT_QUERY_TIMEOUT_MS AbortSignal;findPeerAddresses);relay.ts(reserveRelaySlot 首成即止);gossipsub-signaling.ts(fallback 第三級 provider:SignalMessage 以 SignedPayload 封包走 matchmaking topic 廣播+收端 target 過濾;房間裁決通道與房號探測誠實不支援=上層依 fallback 序決定可用面;canon peer-discovery.md §2 補 signal 封包行)。⭐⭐debug 戰役(測試 1 紅→四輪探針系統化收斂):症狀=gossip roundtrip 的 signPayload 拋「expected Uint8Array, got type=object」;收斂鏈=stack 釘 buildSignedMessage→spec 端 encode 全形狀正常→km 端同函式「靜態參數過、真實參數炸」不對稱→DIAG 傾印=ctor=Buffer、instanceof Uint8Array=false——根因=cborg 的 Buffer 快路徑 × jsdom realm:encode 對特定尺寸輸出 Node Buffer,其在 vitest jsdom realm 下非本 realm Uint8Array 的 instance,noble isBytes 的 constructor.name === 'Uint8Array' 檢查拒收(Buffer 名為 'Buffer')→ 修=buildSignedMessage 對 encode 輸出零拷貝重包本 realm 視圖(一行、全 SignedPayload 簽驗路徑受益;瀏覽器 runtime 無 Buffer 全域、不觸發此路徑)。測試 +19(身分橋接確定性/組態形狀與空 bootstrap/DHT limit 與逾時訊號/relay 首成即止與全滅/gossip roundtrip·target 過濾·誠實回錯 等)=259/259(17 檔)、八驗證全綠。站 4(peer-discovery+signaling-service)全站收官;staged=TURN token 取得(隨 open-4wd-turn wire)、GossipSub provider 與真 libp2p 節點的整合驗證(隨本機測試期)。
同日落地複審(同類邊界加固 2+canon 草圖同步):①cborg→noble 邊界全面 realm 化——buildSignedMessage 修過後掃全 corpus dagCbor.encode/decode 消費端:TopicSubscriber.publish 的 encode 輸出補零拷貝重包(gossipsub 內部雜湊同屬位元組驗證邊界、便宜保險);verifyMessage 三輸入(signature/message/publicKey)過 asRealmBytes 正規化再進 noble(簽章/公鑰可能來自 dag-cbor 解出的 Buffer 視圖——同類故障在驗證端會以「靜默 invalid-signature」呈現、比拋錯更難抓);physics 的 encode 只進 set()/subarray()(無 noble 邊界)=確認乾淨。②canon peer-discovery.md §1 草圖對齊實際 API:connectionEncrypters(複數新名)/allowPublishToZeroTopicPeers/IPColocationFactorWeight(IP 大寫)/services 補 ping: ping()(kad-dht 硬性要求)/gossipsub 來源註(@libp2p/gossipsub 上游新家)/scoreParams 改官方建構器寫法(直接給部分欄位會缺欄)。乾淨面:釘版全 exact(15 筆家族零 ^~);relay 的 multiaddr() 解析在 try 內(畸形位址走 fallback 不拋);gossip provider 重連語意(disconnect 清 handler、subscribed 冪等);dht 的 CID 前提已文檔化(builtin: 非 CID、僅鏈上資產);分層零反向(gossip provider 居 peer-discovery、signaling-service 不回吃)。觀察:createPeerScoreParams 的參數域驗證屬 gossipsub 建構期(runtime)——真節點整合驗證已列 staged;minimumReleaseAgeExclude 21 筆=pnpm 對「當日出版」家族的自動記錄、升版時同步更新。測試 259/259*、八驗證全綠。
2026-07-08 ─ 里程碑 5a:站 4 邏輯核心——peer-discovery+signaling-service(零新依賴;測試 252/252 全綠)¶
決策檔:D-20260708-01
站位策略比照站 3:先落依賴無關的邏輯核心(純函式/注入式、jsdom 可測),libp2p 節點接線(createNode/DHT/Circuit Relay/GossipSub signaling fallback)=5b、待依賴裁決。peer-discovery/ 六檔:topics.ts(僅 rooms/matchmaking 兩 topic+RoomAnnounce/RoomClose payload;配對訊息 schema 權威=matchmaking、本層不重定義欄位);rate-limiter.ts(每 peerId:topic 每 60s 30 則、注入時鐘、固定窗);room-discovery.ts=RoomDiscoveryTable(announce 寫入/close 刪除/ttl sweep/去重清單/訂閱快照;⭐歸屬完整性=公告者必須是房主本人、同房號後續公告與關房只認原房主——防冒名公告/撞號搶佔/惡意關房);topic-subscriber.ts=簽章收發 glue(發送簽章封裝、接收 verifyP2PMessage 四關+限流〔以驗簽後 signer 計〕+畸形丟棄;⭐wire 編碼定案=dag-cbor——canon 草圖的 JSON 無法無損往返 SignedPayload 的 Uint8Array 欄位、且 dag-cbor 為專案既有 canonical 序列化,peer-discovery.md §4 已同步;pubsub 面比照 libp2p services.pubsub、5b 直接餵真實例);peer-score.ts=AppPeerScore(黑名單 −1000/信譽 ≥800 加 50/<300 扣 50;Reputation/Moderation 唯讀視圖注入、隨 ledger 落地餵入);bootstrap.ts(社群清單執行期 raw 抓取+失敗 fallback 內建;⭐內建預設=空清單、官方節點部署時隨版填入——staged)。signaling-service/ 六檔:wire.ts=SignalingWire 13 型完整 union(interfaces 占位就此收斂;server 本體屬部署 repo open-4wd-signaling);ws-provider.ts=WebSocketSignalingProvider(Worker/Star 兩級共用同協議僅端點異:connect=開大廳+register+等 room-state、握手 3 型帶 target 轉發、sendToRoom=register 後送訊等房主裁決回覆〔room-state=ok/error=err〕、⭐checkRoomExists=register 進通道看快照有無其他成員〔房間由成員存在性定義〕、match 佇列 POST 回 handle=peerId;cancel=佇列 TTL 300s 自然過期+本地放棄——server 取消路由隨 matchmaking 收斂;WebSocket/fetch 全注入);provider-chain.ts=connectWithFallback(priority 序 × RETRY_MAX × RETRY_BACKOFF_MS、delay 注入);rtc-config.ts=buildRtcConfiguration(官方 coturn STUN〔未部署期空缺〕→公網保底→玩家自填 TURN 三級序);handshake.ts=performHandshake(程式流程圖序列:offer→answer→trickle ICE 至 connected、逾時/failed 收斂 err 並 close;⭐早到 ICE candidate 緩衝至 remoteDescription 設定後補入——WebRTC 規格順序要求;RTCPeerConnection 工廠注入、DataChannel 建立屬 network-sync)。staged(5b):libp2p 依賴家族裁決→createNode 組態/TrackProviderHelper(DHT provide/findProviders)/findPeer/Circuit Relay v2 slot 保留/GossipSub signaling fallback provider;TURN token 取得(隨 open-4wd-turn wire 格式)。測試 +16=252/252(17 檔:新增 peer-discovery.spec+signaling.spec——限流窗口/房間表歸屬完整性與 ttl/簽發驗簽 roundtrip 與 untrusted·畸形·限流丟棄/PeerScore 四檔/bootstrap 解析與 fallback/register 流程與逾時/wire 轉發雙向/join 裁決雙路/房號探測/配對佇列/fallback 鏈順序/RTC 三級序/握手雙端全序+候選緩衝+逾時)、八驗證全綠。
同日落地複審(中 2 細項 4 修):①⭐房間表長 ttl 滯留(中)——ttl 為公告者自報無上限,ttl=1e15+多房號灌表(限流下每分鐘 30 筆)表無界成長 → sweep 時 ttl 夾生效上限 ROOM_TTL_MAX_MS(重公告間隔 10 倍=300s;房主本就每 30s 重公告)+回歸測試;②⭐fetch 裸傳 Illegal invocation(中)——bootstrap 預設參數 = fetch 把 window.fetch 拆下裸呼叫、瀏覽器拋 TypeError → 預設包 lambda;③重複 connect 洩漏舊大廳 socket → connect 開頭先關+測試;④waitForReply 不監聽 socket 關閉(server 半途斷線等滿整個逾時)→ onclose 立即收斂 err;⑤限流分工註記入 TSDoc(app 層=驗簽後以 authenticated signer 計、防單一身分洗版;驗簽前的傳輸層洪泛 CPU 面=gossipsub 自身 peer score/behaviourPenalty 承擔——5b 接線期勿誤解為重複防線);⑥interfaces 占位註解補指向(SignalingWire 已收斂、占位留存=避免 interfaces 反向依賴模組)。乾淨面:NonceSet 跨 topic 共用=天然防跨 topic 重放;sweepExpired 用簽章時戳(驗簽 ±30s 錨定、不可倒填);checkRoomExists 短暫互見=良性(雙方同時探測撞號=雙方都重生、正確收斂);握手 settle 後 unsubscribe=connected 後遲到 trickle ICE 不再入(已有可用配對、標準行為);wire=JSON 僅限 signaling server 通道(無二進位欄位)、gossip 通道=dag-cbor 分工明確。觀察備查:connect 的 room-state 與 handler 接管之間存在微任務窗(極早到的握手訊息落舊 handler 被忽略——握手有逾時重試語意兜底);provider 無 match-found 監聽面(interfaces 未定、輪詢/推播隨 matchmaking 收斂);leave 型别 payload 空物件(server 只認 type)。測試 +2=254/254、八驗證全綠。
2026-07-08 ─ ⭐全專案深讀複審(4 路平行深讀→逐條驗證;生產修 20+測試 +28=229/229、八驗證全綠)¶
衛生層覆核(下一條)被使用者質疑「你真的有 review 嗎?」→ 承認該輪僅 grep 掃蕩零程式碼重讀,改發 4 路平行深讀(①跨模組契約一致性②editor 全家邏輯/邊界/資源③站 0–2 舊碼以站 3 七缺陷模式回訪④測試品質與斷言強度);agent findings 不直接採信、逐條對 canon 與程式碼求證後分級執行。
高(4 修):⭐簽章長度畸形打斷 P2P 驗證迴圈(站 1)——ed25519.verify 對非 64B 簽章直接 throw(agent 以實裝 @noble/curves 2.2.0 實測證實)、verifyMessage→verifySignedPayload→verifyP2PMessage 全鏈無防護:白名單內成員一筆簽章長度亂寫的封包即令宣告回 Result 的驗證函式拋出(dag-cbor byte-string 長度無限制;既有測試只竄改 payload 保留合法長度簽章故未抓到)→ verifyMessage try/catch 收斂 false+TSDoc+雙層測試(0/3/100B → invalid-signature、不拋)。⭐libktx embind 建構期洩漏(4e)——createInfo/texture/params 建構在 try 外,new ktxTexture 拋出時 createInfo 永不 delete(批次編碼累積 WASM heap 洩漏)→ 全數移入 try、null-init+?.delete() finally+假模組建構拋出測試(斷言 createInfo 仍被釋放)。⭐IndexedDbLocalAssetStore 錯誤路徑不關連線(4e)——close() 全在成功路徑、quota/abort reject 即殘留 IDBDatabase 連線(本 store 專存大 GLB、quota 正是高發失敗模式)→ 四方法 try/finally close+open 補 onblocked(版本升級他分頁持舊連線=open 永久懸置→顯式回報)+fake-IDB 測試(雙 store fallback/list 過濾/remove/正常與錯誤路徑 closeCount==openCount)。⭐worldRotationQuat 鏡射輸出非法四元數(4e 複審引入)——Shepperd 法僅對 proper rotation 有效、負縮放(det<0)時輸出 norm≈0.707 的非單位值直接烘進 auto_empties(鏡射複製=對稱建模常見手法)→ det<0 翻正第三基底軸右手化(X/Y 軸向保留、Z 取反、輸出恆單位)+TSDoc+三軸 180°(Shepperd trace≤0 三分支首度覆蓋)與鏡射案測試。
中(6 修):key-manager 儲存層 reject 穿透 Result 契約(站 1)——unlockProfile/deleteProfile/changePin/pin-guard tryUnlock 的 store 操作裸奔(quota/私密模式=unhandled rejection、Memory 測試替身永不 reject 故測不到)→ KeyManagerError 增列 storage-error(不計暴力鎖次數)+四處收斂+failing-store 測試。引擎平行 GLB 解析縱深(站 2)——collectMergedMesh/collectTrackMesh 索引不驗上界(越界讀→undefined→NaN→質量 NaN→fatigue >= 1 恆假=broken 永不觸發;場地直餵 Rapier trimesh cooker)且 sanitize 前提只寫實作檔頭 → 索引上界檢查(既有 try/catch 收斂 err)+volume/area/AABB 非有限值守門+interfaces/physics.ts 介面級前提文檔(meshBytes=已過 sanitize、引擎僅縱深檢查)+畸形索引測試。validator NaN 掃蕩(4c 全家)——域比較 x<low||x>high 對 NaN 恆假=靜默放行(JSON 1e400→Infinity→運算 NaN 可達):weather 溫度/風速/風向/patch 六欄、gravity 強度、kinematic 週期/轉角/open_duration、entity 磁源/conveyor、checkpoint scale 上下限、containment 兩助手(改「正向證明在內」)、mount AABB 距離/互距/hinge 軸長、part 磁源、stage1 extents 全改反向形式 !(x>=low&&x<=high);amplitude_mm 補分量 typeof(字串 "10"×"10"=100 隱性轉型騙過長度計算)→ NaN/字串測試群。checkTextureBudget 提前 return(4c-2)——首張超尺寸即棄整個函式(其餘影像與張數預算全跳)違反 Stage 2 全量呈現 → 逐張報+預算照驗+6×8192 測試(oversize×6+budget 同報);sniffImageSize 移入 try(截斷表頭可拋)+imageHeaderBytes 實作對齊 TSDoc(bufferView 越界 catch→null)。pointToTriangleDistanceSquared 退化除零(4c-2)——零面積三角形落入面內插值分支=1/0 → 分母>0 守門+最近頂點回退;補 Ericson 7 區域全覆蓋單元測試(先前 1/7)+退化雙案(三點重合/共線)。bake 漏烘 auto_launch_ammo(4e;agent「彈材質 N 對 1」線索對 canon 求證出真缺口)——建模參數表 §4.3 明文 [{node_name,material,volume_cm3,local_transform}]、車輛組裝滿彈引用 → bakeWeapon 補子彈掃描(材質=子節點 extras、體積=wave A per-node、local_transform=對 launcher 局部 4×4;localMatrix 公用 export)+雙彈測試。
細項(修):deterministicCbrt 入 physics-engine barrel(決定性紀律唯一合法立方根被鎖檔內);barrel 補齊(physics-engine PartGeometry;editor FindingExtra/GltfMaterialRaw/GltfPrimitiveRaw/GltfMeshRaw/localMatrix;security GltfBuffer/GltfBufferView/GltfAccessor/GltfPrimitive/GltfMesh);chain 編號後綴改純十進位正整數 regex(Number() 收 0x10/1e1、前導零撞號)+鑑別測試;PART_SIDE_MOUNT_NAMES 註解精確化(canon §6 對照:part 端=單一通用名、「前綴匹配」=裝配期對 chassis 端語意——程式本來就對、agent 觀察結案);claimedSegments 註解對齊實作(同 actuator 重複列舉也算雙重宣告)+同 actuator 案測試;PartInstance.materials mesh_node=TSDoc 註明 extras wire 格式鏡像(刻意);decodeRgbaInWorker bitmap.close() try/finally。
測試品質工程(第④路全單):⭐checkpoint 測試 fixture 整組超限(AABB y 高 1210→上限 121、全 fixture y=150:「合法」對照組實際觸發 2 筆 reject、z 分支被 y 軸 break 短路從未執行、全靠 toContain 掩蓋)→ fixture 重設合法域+逐碼 nodeName 計數+總數=5 斷言;⭐驅動體 17 測試同名鏈節複製(藏 16 筆未斷言 double-claim)→ 17 相異連號+not.toContain 交叉;oneOnFixed 沿用孤兒 fixture(藏 2 筆 route-orphan-empty)→ 獨立 fixture toEqual 單碼;溫度超域案缺 patch(混 patch-missing)→ 帶合法 patch toEqual 單碼;rename 重名 fixture 缺 4 mount(藏 4 筆 mount-missing)→ 補齊 toEqual 單碼;roller 型別首測;stage1 degenerate-aabb 首度觸發(零厚度+NaN);findings 工具(hasReject/countByLevel/info)首測;decimateNodeBudgets 直測(比例分攤/保底 1/不超編);VISUAL_LAYERS 全內容斷言(原僅驗長度);⭐compress 貼圖分支首度 e2e(手工組 texture GLB+注入解碼+真 libktx 實壓→輸出 mimeType='image/ktx2'+magic+嗅探尺寸);FixtureNode.box 註解修真話(單三角形、非封閉 box)。201→229/229(+28)、八驗證全綠。
agent 誤報駁回(脈絡缺失類):checkpoint 球形 vs trigger box=box 近似期已知簡化(3c-3 已記);weather patch/wind 引擎無欄位=站 2 staged 台帳 D 類;RoutePoint.forward=canon 已釘「取自 empty rotation −Z」、convert 隨真 GLB 期(台帳 B 類);dag-cbor −0/undefined/鍵序=agent 實測乾淨;原型鏈哨兵/*/ 註解/識別比較回訪零命中;session/undo/viz-prefs、kmeans/CIEDE2000、wave-b 投影核心、submit 逐行覆核乾淨。
裁決項(新驗證規則提案、未實作):①鏡射 empty validator reject——quat 已防禦右手化,但鏡射下 empty 方向語意本質不可表示(forward/hinge 軸會差一個反射),建議 reject「請在建模軟體 apply scale」;canon 無明文、需裁決。②chain 歸屬清單跨鏈/未成鏈宣告檢核——deriveAutoChain 靜默忽略非錨鏈清單項(TSDoc 原承諾「validator 另報」與實作不符、已修為真話);補檢核需 part-rules 引 scanChains+既有 fixture 全面加父子結構、中等工作量。③收件端 GLB 反放大縱深(同一小 accessor 數十萬次 primitive 參照=CPU/記憶體放大;sanitize 上限先行、引擎前提文檔已補)=觀察。其餘觀察:deviationPct 於 expectedMm=0 時 Infinity→JSON null(canonical 特徵表不會填 0);未被三角形引用頂點計入 AABB/投影(業界慣例;橫向與 compress prune 缺席一併備查);IndexedDB onversionchange 自關(DB_VERSION 升版才相關)。
同日裁決執行(使用者核可兩項全做):①empty-mirrored-transform reject 落地——checkMirroredEmpties 共用規則(validatePart+validateTrack 皆接、part-rules 定義 export):語意 empty(classifyEmptyName ≠ 'other' 且無 mesh)處於鏡射世界變換(isMirroredTransform=3×3 基底行列式 < 0、glb-scene 公用 export)=逐節點拒收;組織節點與純 mesh 鏡射不列管(幾何鏡射合法)、雙軸鏡射(det > 0=180° 旋轉)天然放行;canon=建模參數表 §5 新「鏡射禁令」段(apply scale 指引)。②chain 歸屬↔實體鏈對應檢核落地——checkActuators 引 scanChains(chain-scan 對 part-rules 僅 type-import、無執行期循環):清單項存在於 graph 但不屬任何同父 Chain_Segment_* 兄弟鏈群(含 root 裸放——掃不成鏈、烘不出 auto_chain 的既有靜默洞)=weapon-chain-unscanned-claim 逐項拒收;單一 actuator 清單映射 ≥2 條實體鏈=weapon-chain-cross-chain 拒收(auto_chain 一 actuator 一串、跨鏈無法表示);canon=§3.7 chain 欄補歸屬約束。既有 chain 測試 fixture 全面父子化(chainedWeaponGraph helper)、雙 double-claim 測試順勢升格 toEqual 嚴格斷言;新測試 5=鏡射三態(拒收/雙軸合法/mesh 鏡射合法)+父層傳遞+場地 RoutePoint 鏡射+未成鏈與合法成鏈對照+跨鏈。測試 234/234、八驗證全綠。
同日裁決執行複審(高 1 中 1 修、checkActuators 重構收斂):scanChains 進 validator 後重讀整段抓出兩缺陷——①⭐驅動體上限可低報繞過(高):drivenBodies += chain.length 數清單長度非歸屬鏈實際節段數,「選一串=整串歸屬」下手工 GLB 對 17 節鏈只列 1 名=計數 1 過 16 上限、auto_chain 卻烘整串(4c 期無 scan 可用的既有缺陷、本輪才修得了)→ 計數改歸屬鏈實際 segments.length(無可解析鏈 fallback 清單長度——該清單已被 unscanned/not-found 拒收);②跨 actuator 異名共同歸屬同一鏈不被抓(中):double-claim 只比名字,actuator1 列 seg1、actuator2 列 seg2(同鏈)=各擁整串的排他衝突靜默通過 → 新增鏈層歸屬排他(chainClaimedBy:同鏈第二個歸屬者=weapon-chain-double-claim,同名跨 actuator 亦由此涵蓋);重構=掃描前置一次、清單內重複改 per-actuator set、重複列舉不再重複計入驅動體;行為註記=未成鏈節段的跨 actuator 同名重複,從 double-claim 改由 weapon-chain-unscanned-claim ×2 拒收(拒收不漏、碼更準確)。測試 +2(只列一節=實際鏈長 17 觸上限+異名同鏈雙歸屬)=236/236、八驗證全綠。乾淨面=isMirroredTransform 行列式展開式驗算、corpus「鏡射/負縮放」零措辭衝突(其餘命中皆美術提示詞語境)、雙軸鏡射放行與組織節點排除設計確認;觀察=無 mesh 的 Chain_Segment_* 節點(畸形建模)也落在鏡射禁令內——比 canon 列表嚴一格、訊息仍正確導向 apply scale、無害。
2026-07-08 ─ ⭐站 3 收官・全專案覆核(站 0–3 大段落完成盤點;測試 201/201、八驗證全綠)¶
覆核面全數通過:①repo 衛生=git 164 檔全 staged、零 untracked 漏網、vendored 二進位未被 .gitignore 吃掉(check-ignore 驗證);②決定性紀律全域回掃=**/locale 家族(localeCompare/toLocale/Intl)非 spec 零殘留、Date.now 六處全非共識(簽章時戳/nonce TTL/本地 log/本機存檔時戳)、Math.random 零、TODO/FIXME 零、console 僅 main.ts bootstrap 錯誤;③分層=低層模組(physics-engine/security/material-params/builtin-assets/system-constants/interfaces)零反向 import editor;④供應鏈=依賴全 exact pin(零 ^~)、audit 2 low 皆既有開發鏈、CSP wasm-unsafe-eval 已在(Rapier 期鋪路、libktx/draco 共用)、check:vendored 綠、sharp build script 明確拒跑;⑤測試分布=15 spec 檔覆蓋全部已實作模組;⑥canon 未來式殘留(「隨編輯器補入」類)零;chip slot 推導三處(常數定義/編輯器 derive/builtin 不變式)=常數單一權威下的已知可接受重複。
⭐staged 債務台帳(下一段開工的單一參照;權威=本條):
- A. editor 家(隨 viewport/UI=站 10 主收斂):finalize 總 orchestrator(wave A→編輯→wave B→驗證→壓縮→雙出口串接、compress·bake 順序+extras 保留斷言)/Worker 接線(wave-a.worker
new Worker、finalize Worker、⭐draco 官方 web 版draco_encoder.jsvendor+入 check-vendored〔npm 僅 nodejs 包裝〕、libktx importScripts)/viewport 全家(o4-3d-stage workshop、gizmo、SceneEdits 解算)/貼圖 UV 取樣色彩(OffscreenCanvas)/cluster→實體 sub-mesh 切割落 GLB。 - B. 真 GLB 期烘焙家族:零件 canonical AABB 縮放對齊+canonical 尺寸表(builtin-assets 供給 → 接 computeFeatureDeviations)/Axis mesh 主軸推斷本體(auto_inferred、PCA)/auto_route_polyline_3d/auto_collider_tris(collider 標記慣例待定)/auto_exposure/auto_density_warning/cell_fracture 碎片路線。
- C. 站 4–6 接線:出口 B 上鏈全鏈(站 5 ledger:CID/IPFS/簽章/燒幣;站 6 anti-piracy:fingerprint/文字黑名單/fork parent 狀態)/收件端組裝(stage1+validatePart/Track+requireCompression=true)/三 tag 表單驗證(出口 B 表單層)/SANITIZE_WORKER_VERSION·MAX_MEMORY_MB 接線(2c 觀察)。
- D. 站 2 引擎殘留(隨輪胎摩擦模型+entity 系統):天氣摩擦 patch/風力(aero 烘焙→引擎接線、drag_coefs 鍵名對照)/entity 三類+destructible Voronoi/grip modifier(sticky·grip_loss)/彈體破壞。
- E. 橫向備查:i18n
validation.*key 名單(站 10 語系清單)/ESLint 別名繞過=tripwire 級/submitLocal 跨 kind 覆寫 edge(IndexedDB 雙實作未測項已於次日深讀複審以 fake-IDB 補測結案)。
現況:站 0–3 全完(workspace/system-constants/interfaces/key-manager/security/physics-engine/material-params/builtin-assets/editor 九模組實裝)、測試 201/201(15 檔)、驗證鏈八件套。下一步=站 4:peer-discovery+signaling-service。
2026-07-08 ─ 里程碑 4e:wave B 烘焙+壓縮管線+送出雙出口(測試 200/200 全綠;⭐站 3 editor 收官)¶
⭐依賴裁決(使用者定 A 案「一次到位」、逐步查證後修訂):npm×4=draco3d@1.5.7(Google 官方、幾何壓縮 WASM)+@gltf-transform/{core,extensions,functions}@4.4.1(標準 glTF 讀寫層、extension writer 互通性驗證過;exact pin、audit 僅 2 low=既有 @angular/build 開發鏈);vendored×1=Khronos KTX-Software v4.4.2 Web libktx.{js,wasm}(public/vendor/libktx/+PROVENANCE.md〔版本/來源 URL/SHA-256/Apache-2.0〕+check:vendored 雜湊守門腳本入 package.json+CI——vendored 工件不走 lockfile/audit、以來源雜湊釘死替代);⭐社群 ktx2-encoder 查證後除名降備援(v0.x 單一維護者包裝層 vs 格式制定者官方全套——供應鏈最弱環不該是社群包裝;使用者追問推翻初判、正確);查證過程攔下外部資料兩處幻覺出處(不存在的 repo 連結/three.js 只有 transcoder 無 encoder)。⭐pnpm 11 坑=未裁決 build script 使全部 pnpm run 失敗 → pnpm-workspace.yaml allowBuilds: sharp: false(ndarray-pixels 的 Node 原生模組、僅瀏覽器路徑用不到——明確拒跑)。wave B 烘焙(pipeline/wave-b.ts;同輸入同輸出=僅 +−×÷√):共用柵格投影核心(128²、方向基底=軸向與 30° 解析常數、重心插值取深度、y/v 相異子格偏移同 voxel 慣例)→ ①六向接觸面積(auto_contact_area_mm2;接觸=首擊面位於該向最外緣 ±1mm 容差內——±方向有別、非單純輪廓);②aero 8 向(auto_aero:阻力係數=輪廓/包圍框截面、±30° 俯仰斜向、壓力中心=迎風面首擊點面積加權平均、lift_factor=上下可見表面不對稱啟發〔弧頂=升力負值〕——初版啟發、真風洞 pass 隨 playtest 校準);③thermal_limit weakest 合成;④canonical 特徵容差(±15% 且 >±2mm 拒收、≤15% 記 auto_feature_deviations 軟警告;尺寸表由 builtin-assets 供給=呼叫端組 measurements);⑤Voronoi 碎片計畫(種子沿最長軸均佈質心線=無隨機、voxel 柱區間最近種子歸屬、method: voxel_fallback、loss=近似誤差;extras 只烘 count/method/loss 摘要)。壓縮步(pipeline/compress.ts+libktx-encoder.ts):glTF-Transform WebIO+draco() transform(encoder module 注入)+KTX2 貼圖替換(KHR_texture_basisu、影像解碼注入〔Worker=createImageBitmap+OffscreenCanvas〕);libktx adapter API 面經執行期內省釘死(textureCreateInfo→ktxTexture(ALLOC_STORAGE)→setImageFromMemory→compressBasis(basisParams)→writeToMemory;threadCount=1〔無 COOP/COEP=無 SAB〕;預設 ETC1S、UASTC 可選)。⭐真編碼 e2e 抓到既有 bug:實壓輸出證實 KTX2 magic 第 7 byte=0xBB——security/glb.ts sniffImageSize 的 KTX2 分支自 2c 起誤寫 0xAB(嗅探永不命中 KTX2、貼圖預算檢核會靜默跳過 ktx2)→ 雙修(glb.ts+KTX2_MAGIC 常數);draco 實壓輸出經 validator requireCompression 反向驗證轉綠。extras 烘焙(pipeline/bake-extras.ts):draft+wave A/B → snake_case Root Extras(零件全欄位+auto_ 家族+weapon 區塊含 auto_chain/auto_empties 位置快取;場地 track/weather/physics 區塊+auto_track_aabb_mm/auto_visual_tris/auto_player_max)→ writeRootExtras 落預設 scene extras+canonical 重打包(open4wd_version 由出口 B 上鏈時寫、本機品無 marker)。送出雙出口(pipeline/submit.ts+local-store.ts):出口 A=submitLocal → local:<uuid>(crypto.randomUUID)入 LocalAssetStore(Memory 測試/IndexedDB runtime=db open4wd、store local-vehicles/local-tracks、可覆寫重存);出口 B=submitToChain 誠實 stub 回錯(燒幣/CID/IPFS/ledger 簽章隨站 5、反複製隨站 6)。draco3d ambient 型別宣告(src/types/draco3d.d.ts);Node 內建模組於 spec 以動態運算式載入(免 @types/node 新依賴)。staged:finalize 總 orchestrator+Worker 接線(隨 viewport/UI);三 tag 表單驗證(出口 B);collider 衍生。測試 +10=200/200*(15 檔):wave B box 解析解全套(六向 ±3%/aero 係數≈1+lift≈0/thermal/容差三級/碎片決定性)+⭐libktx 實壓(ETC1S+UASTC magic+嗅探尺寸)+⭐draco 實壓(validator 轉綠)+extras snake_case 對映與回讀+出口 A 存取覆寫+出口 B stub。八驗證全綠(七件套+check:vendored)。
同日落地複審(細項 2 修、staged 群明錄、觀察 5 備查):①⭐auto_empties 缺 rot——canon §4.3 明文 {pos, rot} 快取、初版只烘 pos → 補 worldRotationQuat(世界矩陣基底列正規化去 scale+Shepperd 法轉 quaternion;glb-scene 公用 export、viewport 可複用)+單元測試(含縮放節點還原旋轉);②⭐draco 瀏覽器路徑陷阱記案——draco3d npm 只含 nodejs 版包裝(draco_encoder_nodejs.js 走 require/fs),測試路徑已證可用,但 finalize Worker 的 importScripts 需官方 web 版 draco_encoder.js(wasm 同檔可用)→ 隨 Worker 接線 vendor(來源=google/draco release、雜湊入 check-vendored)——現在記案防接線期踩坑。乾淨面覆核:wave B 對 UGC機制 §4.1 五項全覆蓋;auto_feature_deviations 恆寫入(空陣列)合 §4 required 慣例;投影重心插值權重驗算(e1→a、e2→b、e0→c 正確);斜向基底正交性驗算(d·v=0);draco 與 libktx 實壓輸出經 validator 雙向互證;extras undefined 欄位由 JSON 序列化自然剔除。staged 群明錄(真 GLB 期烘焙家族):auto_route_polyline_3d(box 期 TrackConfig 宣告直供引擎)/auto_collider_tris(collider 標記慣例未定)/auto_exposure(曝露圖)/auto_density_warning/auto_inferred(⭐Axis mesh 主軸推斷本體未實作——validator 只 warn、推斷=真 mesh PCA 期)。觀察備查:aero extras drag_coefs 鍵名('+X'…'-Z+Y30')=引擎接線時的對照鍵(canon 未釘字面、勿雙處發明);compress 與 bake 的順序及 extras 保留=finalize orchestrator 職責(gltf-transform 往返保 extras、未含 extras 實測——orchestrator 落地時補斷言);submitLocal 跨 assetKind 覆寫在 IndexedDB 雙 store 下會殘留舊件(UI 不變 kind、低風險 edge);IndexedDB 實作未測(jsdom 無、ProfileStore 先例);預設值填補=session/orchestrator 職責、bake 不自填(權責分離正確)。測試 200→201/201、八驗證全綠。
2026-07-08 ─ 里程碑 4d:session(A2 無草稿+undo)+chain-scan(auto_chain 推導;測試 189/189 全綠)¶
session/ 三檔:①undo-stack.ts=快照式泛型撤銷堆疊(push/undo/redo/reset;撤銷後新編輯丟棄 redo 分支=業界慣例;全 session 深度無上限——單步快照=draft+覆寫層小物件、記憶體壓力低,未來納大型陣列狀態再改差分制)。②editor-session.ts=記憶體-only 狀態容器(A2 無草稿:離開/關閉即棄、零持久化);撤銷單位=EditState{draft, sceneEdits}——SceneEdits 覆寫層=場景編輯的資料契約(translations/rotations/scales/renames/addedEmpties/removedEmpties,key=base graph node 名):base graph 不可變、viewport 與 finalize 對照覆寫層解算最終 transform(序列化落 GLB 屬送出管線);apply(mutate)=深拷貝快照後就地改(structuredClone、mutator 不汙染舊快照);resetTo=切換編輯對象整疊清空(前一對象即棄)。③viz-prefs.ts=視覺輔助層開關(編輯器唯一持久化例外):10 層 VISUAL_LAYERS 名錄(spline/走廊/forward 箭頭/Mount marker/RoutePoint 編號/Checkpoint box/KillZone box/Respawn 箭頭/Entity gizmo/cluster 著色)、預設全開、localStorage key open4wd.editor.visualLayers;storage 可注入、壞 JSON/缺鍵補預設/未知鍵忽略/寫入失敗靜默。chain-scan/:scanChains=同父 Chain_Segment_* 兄弟群掃描——⭐鏈序=父節點 children 陣列相對順序(非名稱後綴)、多父分串;相鄰節推 joint;型別推導啟發(canon 明文實作層裁量)=細長比:鏈節幾何 AABB 最長軸 ≥ 2× 次長軸=板/片狀 → revolute(繞窄軸單軸擺)、近等軸球狀節 → spherical、無幾何=revolute;deriveAutoChain=依 actuator 歸屬產 AutoChainEntry{actuatorIndex, segmentCount, joints}(送出時序列化為 snake_case auto_chain)——整串歸屬語意(清單點名任一節=該串全部)、鏈序取掃描結果、joint 鏈錨定 pivot(首 joint=pivot→首節);名稱皆不在鏈串=略過(validator 另報)。測試 +12=189/189(14 檔):undo 全序+分支丟棄+reset;session 深拷貝隔離(mutator 不汙染舊快照)+resetTo 清空;viz 預設全開 10 層+roundtrip+缺鍵補預設未知鍵忽略+壞 JSON/拋出 storage 不炸;⭐children 順序鏈序(2,1,3 fixture)+細長/球狀啟發+多父分串+pivot 錨定整串歸屬+略過案。七驗證全綠。
同日落地複審(細項 4 修、觀察 3 備查):①⭐鏈段歸屬衝突未擋(4c 缺口、本複審撈出)——同一 Chain_Segment 出現在兩個 actuator 的歸屬清單=引擎雙重驅動衝突(「選一串=歸屬本 actuator」的排他語意)→ part-rules 補 weapon-chain-double-claim(跨 actuator+同清單內重複皆拒——4c-1 複審「清單重複名雙計=保守」觀察就此轉正)+回歸測試;②localStorage 屬性存取本身可拋(隱私模式/停用 cookie 下 SecurityError 在「取屬性」即發生、非僅 getItem)→ globalThisStorage 包 try/catch;③SceneEdits 解算語意補文檔——「新增後再刪=自 addedEmpties 移除、removedEmpties 僅指 base 節點;rename 僅對 base、added 直接改 entry;transform 覆寫鍵恆為 base 名(rename 不換 key)」——覆寫層消歧義規則原缺、viewport/finalize 實作前定案;④undo-stack TSDoc 補呼叫契約(push 存參考、快照由呼叫端供給——EditorSession.apply 深拷貝已代辦)。乾淨面覆核:viz 10 層對編輯器操作 §1.6 清單逐一對照 ✓;A2 語意(零持久化/切換即棄/唯一例外 viz)✓;structuredClone 對 draft 內 Map(materialOverrides)/TypedArray 支援 ✓;apply 中 mutator 拋出=clone 即棄、stack 不變(fail-safe)✓;joint 型別取「連入節(to)」形狀=決定性且語意合理。觀察備查:auto_chain joints type 序列化字面值('revolute'/'spherical')隨 4e 序列化定案;歸屬清單跨多串=首串為準(4e finalize 可加 warning);「清空頁籤」=apply() 的域操作、UI 層組合(模組提供原語即可)。測試 189→190/190、七驗證全綠。