跳轉到

版本規範

本檔角色:版本系統設計、升版策略、Service Worker 升級。 實作見 程式架構/versioning.md

1. 六欄位版本

interface ClientVersionInfo {
  client_version: string; // X.Y.Z 三段非負整數
  rapier_version: string; // 物理引擎版本鎖死
  protocol_version: string; // P2P 訊息協議版本(semver 字串,如 "1.0.0")
  derive_logic_version: number; // DerivedState 推導邏輯版本
  builtin_assets_version: number; // 公版資產物理參數版本
  economy_config_version: number; // 經濟治理版本(鏈上)
  build_timestamp: number; // 診斷用
  commit_hash: string; // 診斷用
}

1.1 版本字串格式

client_version 必為 X.Y.Z 三段非負整數

  • 無預發布標記、無 build metadata
  • 理由:開源 PWA,所有 push 到 master 視為正式發布,無 alpha / beta / rc
CLIENT_VERSION_REGEX = /^\d+\.\d+\.\d+$/

CI build 時驗證;不符合拒絕 deploy。

2. 版本欄位職責

欄位 升版者 升版時機
client_version Client 發版 任何 build 都 bump(patch / minor / major 依變更類型)
rapier_version Client 發版 跟著 Rapier npm package 鎖檔變動 → 連帶 major
protocol_version Client 發版 DataChannel / PubSub 訊息協議破壞性變動 → 連帶 major
derive_logic_version Client 發版 推導演算法(TrueSkill / Bayesian / fork / niche / monthCurve)變動 → 連帶 major;規則修正(含經濟漏洞修復)的生效切點 / 追溯政策見 經濟系統.md §2.1
builtin_assets_version Client 發版 公版資產物理參數變動 → 連帶 major
economy_config_version Governance signer set(鏈上) 治理透過 ConfigUpdateEvent 更新;與 client 解耦

重點economy_config_version 在 runtime 可變,與 client 發版週期解耦。其餘 build 時固定。

builtin_assets_version 的目前開發基線以 network.md §11BUILTIN_ASSETS_VERSION_CURRENT 為唯一數值權威。公版資產必須包含 current PhysicsManifest 的 projectile LaunchExit、純視覺 fragment descriptor、逐實際 collider 六向接觸面積、逐 tire collider region wear capacity、定點能量帳、能量域熱、六軸 aero、typed weather exposure/track entity topology 與 part collider proxies,並一次 rebake 27 個公版資產。專案尚未公開,因此其他未發布候選不建立 public compatibility,也沒有舊資產 fallback; 首次公開再凍結當時 bytes/provenance(D-20260811-02)。 內部 candidates 不形成 compatibility era;主 repo 成功公開時才進入 live。此後的不相容介面依本檔既有版本與遷移規則處理,生命週期切點與首次發布鏈以 專案生命週期 為準。

3. 升版分類

變更類別 bump 擋配對 範例
Bug fix(不影響 deterministic / protocol) patch 修正 UI 排版、input 防呆
純資源變更(i18n 字典、主題資源、UI 文字、靜態圖、文檔) patch 新增 UI 字串的日文翻譯、新增主題/分類(主題系統.md)、修錯字、替換 hero 圖
新功能(不影響 deterministic / protocol) minor 新增 settings 頁面、聊天表情
清單資料(驗證 / 過濾用清單隨 client 出貨) minor 材質廢止 DEPRECATED_MATERIAL_IDS材質表.md §11)、文字黑名單庫資料(版權.md §7);yank 清單除外 → major(影響降版目標 = 共識輸入,§22
破壞 deterministic 或 protocol major 升 Rapier、改物理常數、變更 DataChannel 結構、改鑄幣公式

公版資產參數變動(影響 deterministic)歸 major

hard/soft CCD 開關、CCD substeps、solver iterations、固定步長等 Rapier world/body 設定皆屬 physics-version-pinned 共識輸出;任一值變更歸 major 並擋跨版配對。pre-launch 階段直接更新目前 開發基線,不為未公開 intermediate 值保留 fallback、migration 或雙版本執行路徑。

純資源變更也要 bump patch 的理由:可被偵測 / 通知(i18n 字典變化等)。

4. Conventional Commits + path-based 自動化

feat: ...        → minor
fix: ...         → patch
docs / chore / style ... → patch
BREAKING CHANGE 註記 → major

Path-based bump(依改動路徑):

路徑 觸發
src/material-params/ major(材質參數變動 → derive_logic + builtin_assets)
src/builtin-assets/ major(公版零件 / 場地物理參數builtin_assets_version + client major;純美術不 bump,見 程式架構/builtin-assets.md §4
src/asset-schema/(B 軸遷移描述子 / 破壞牆 / yank 清單;路徑名以實作為準) major(發版軸掛鉤,§16 / §22
src/system-constants/protocol/ major
src/system-constants/network/ minor
src/system-constants/ui/ patch
src/i18n/ patch
src/themes/ patch
public/assets/themes/ patch
public/community/*.json(社群 provider registry build snapshot) minor——同源部署快照隨 client build 出貨;runtime 優先同源 registry,失敗時使用最後成功快取/build snapshot(部署資訊.md §5
docs/ / README.md patch

5. 相容性檢查(checkCompatibility)

兩位玩家在配對前比對:六欄位任一不同 → 拒絕配對client_version 比 major 段,其餘全等比對;minor / patch 不影響配對)。

實作見 程式架構/versioning.md §2

5.1 為何 economy_config 也擋配對

即使 client 版本相同,若 ledger 上的 EconomyConfig 已被治理升級而某 peer 還沒 catch up,開賽前的 fork 分類、玩家可見費率/報價與房間輸入判定可能不同,所以強制 economy_config_version 一致才能配對。MatchResult 的 payout 不使用這份房間快照;canonical fold 會按事件位置的 epoch state 唯一計算。公式本身改版仍屬 derive_logic major(§2)。

6. 版本廣播與收集

  • 連線握手時交換 ClientVersionInfo
  • 收集同房全員(2–8 人)的所有 peer 版本
  • 收齊性檢查(防靜默放行):任一 peer 版本收集逾時 / 未回應 → 整體失敗,不得以少於全員的版本集靜默開賽
  • 不相容 → 拒絕 race / 提示升版

收集協議實作(廣播 / 重試 / 逾時)見 程式架構/versioning.md §1

7. 開賽前驗證

全員 Ready 後、倒數前以同一 ReadySet 驗證;進入 preloading 後再以鎖定包重驗:

  1. 同房全員(2–8 人)版本一致
  2. ledger 上 economy_config_version 與本地一致
  3. builtin-assets CID 比對通過

ReadyDeclaration 帶完整 ClientVersionInfo,每端必須驗證全體聲明與實際 admitted manifests;任一失敗不得簽 ReadySet ack。倒數前形成的 LockedStartPackage 保存已確認的版本、loadout 與起跑證明,preloading 不得改用車庫的新資料。任一重驗失敗 → 拒絕開賽,UI 提示「請刷新瀏覽器或升級」。

8. Service Worker 升版

Service Worker 實作見 程式架構/versioning.md §5

8.1 Build 整合

service-worker.js 在 build 時把 client_versionbuiltin_assets_version 嵌入 CACHE_NAMEopen4wd-${CLIENT_VERSION}-assets-${BUILTIN_ASSETS_VERSION}),任一公開版本改變都會 建立新快取。activate 僅刪除 open4wd- namespace 下的非 active cache,不得清除同 origin 其他應用。

Build 注入細節見 程式架構/versioning.md §7

8.2 啟動畫面提示

啟動橫幅顯示:

  • 「發現新版本 v1.2.4,2 分鐘後自動套用」
  • 「版本不相容,請立即更新」(major bump)

套用時機 guard:自動套用(skipWaiting + reload)只在 idle(非比賽中、非房間內、非 Stage 2 編輯中)觸發;比賽 / 房間中發現新版 → 顯示提示但延後到離開後的下一個 idle 時點才套用——比賽中 reload = 異常斷線(信譽 −5 + forfeit,信譽系統.md §2.3),升版不得製造斷線。§10 的 24h 強制升版同受此 guard。Stage 2 編輯中同屬非 idle——編輯器不做持久化草稿(編輯內容僅存 session 記憶體),強制 reload = 工作遺失,故套用延後至離開編輯器。

8.3 配對版本不符提示

配對失敗時:

您的版本:v1.2.3
對手版本:v1.3.0
請點此立即更新

8.4 設定頁版本資訊

設定頁顯示完整 ClientVersionInfo:

  • client_version
  • rapier_version
  • protocol / derive_logic / builtin_assets / economy_config
  • build_timestamp / commit_hash

9. 強制重整流程

9.1 應用內按鈕

設定頁 → 「強制更新」按鈕:

  1. unregister 所有 service worker
  2. clear caches
  3. reload

9.2 /reset/ 逃生口

當 service worker 卡死、應用無法啟動:

  • 直接訪問 /reset/
  • 純 static HTML,不依賴 SW
  • 提供「清除所有快取 + 重新載入」按鈕

10. 升版檢查 API

升版檢查 API 實作見 程式架構/versioning.md §4

async function checkForUpdate(): Promise<UpdateInfo | null> {
  // 定期呼叫,預設 1 小時(UPDATE_CHECK_INTERVAL_MS)
  // 從 GitHub Pages 拉 /version.json + 比對 commit_hash(network-only;勿與 PWA manifest.json 混淆)
}

UPDATE_FORCE_GRACE_PERIOD_MS: 86_400_000(24 小時軟性升級寬限)後強制升版——強制執行點同受 §8.2 套用時機 guard(比賽 / 房間 / Stage 2 編輯中不 reload、延後至下一個 idle 時點)。

11. 風險與緩解

風險 緩解
Service Worker 快取卡死 /reset/ 逃生口
版本檢查失敗(GitHub Pages 暫時 down) 軟性 fallback:使用本地版本 + 警告
不同 peer 升版速度不一 24 小時軟性升級寬限 + major bump 強制
economy_config_version 跨 client / 鏈差異 開賽前再驗一次(最新從 ledger 讀)

12. 跨模組對接

模組 升版觸發
Rapier (physics-engine/) rapier_version → major
network-sync/ (DataChannel) protocol_version → major
經濟鑄幣公式(算式表.md derive_logic_version → major
ugc-rating/ Bayesian derive_logic_version → major
material-params/ 數值 builtin_assets_version + derive_logic_version 雙 bump → major(§4 / §13
B 軸 schema(遷移描述子 / 破壞牆 / yank 清單) client major§16 / §22 發版軸掛鉤)
system-constants/economy-config/ 預設 spec 預設不改,治理 ConfigUpdateEvent 修改 economy_config_version

13. 不變式(CI 強制)

client_version 符合 X.Y.Z regex
rapier_version 必須等於 package.json 內 Rapier 版本
任何 src/material-params/ 變更 → builtin_assets_version + derive_logic_version 雙 bump
任何 src/system-constants/protocol/ **或 `src/system-constants/network/sync.ts` / `src/network-sync/` wire、admission、投票語意**變更 → protocol_version + major bump
B 軸:遷移描述子每道破壞牆恰一份、含向上 migration + 向下 mapping(§22)
B 軸:yank 清單變更 → 必須同版存在非 yanked 替代版(§22)
B 軸:schema / 破壞牆 / yank 清單變更 → major bump(§16)

B 軸:UGC 資產 schema 版本

§1§13A 軸(client build 版本)。以下 §14 起是 B 軸(UGC 資產 GLB extras 符合哪版 schema)。兩軸獨立記錄、但 B 必須對接 A。

目前開發期狀態:尚未發布資產,所有 type 只支援 §15 定義的單一 current marker 與 current PhysicsManifest;其完整形狀包含 projectile LaunchExit、純視覺 fragment descriptor、逐實際 intact collider 六向接觸面積、逐 tire collider region wear capacity、定點能量帳、能量域熱、六軸 aero、typed weather exposure/track entity topology 與 deterministic part collider proxies。production migration catalog 為空,其他 marker 一律拒載。§16–§26 僅保留未來正式發布後的版本治理設計,不代表目前存在相容層。

public/assets/asset-migrations.json 是每個 release 隨附的機器可讀 projection,供外部資產 authoring/相容性檢查工具取得 current versions、migration descriptor 與 breaking wall;空 descriptorsbreakingWalls 是上述 current 開發期基線的有效輸出。主 client 不以 HTTP 讀回這份 公開檔,而是直接使用同一 src/asset-schema/descriptor-metadata.json 權威的 TypeScript binding; build 的 stale check 保證公開 projection 沒有和 runtime binding 分岔。

14. 兩軸關係

是什麼 由誰升 控制什麼
A. client_version 等 6 欄位 client build 版本 client 發版 checkCompatibility 配對閘門(同場全員必相同)
B. open4wd_version(per-type) 單顆 GLB extras 符合哪版 schema schema 演進(CI 偵測 + 開發者宣告遷移) 資產的生命週期、降版本運算、可用性

核心保證:A 軸已確保同場全員 client_version major 相同(其餘五欄全等,§5,而映射函式 / 破壞牆 / yank 清單全隨 major 出貨(§16 發版軸掛鉤)→ 同 rapier / derive_logic / 同一套「保留映射函式」→ 對任何支援的資產 schema 版本解讀一致。B 軸的決定性建立在 A 軸之上:只要同場、所有資產落在 client 的支援範圍內,運算就一致。

B 軸 schema 版本 ≠ builtin_assets_version:前者是「GLB extras 格式」,後者是「公版資產內容」。兩者常一起 bump 但分開記。

15. 識別 marker(地基)

// Root Extras(所有 open4wd GLB 通用)
{
  "open4wd_version": 1, // pre-launch 唯一支援的 per-type schema marker
  "type": "tire", // 8 零件 + "track"
}
  • open4wd_version:版本號是從 1 起單調增加的整數 per-type 命名空間;例如 tire 的 2 與 body 的 2 是各自獨立序列。一顆 GLB 只有一個 type,故一個欄位即可。
  • type enum = 8 零件 type + track(UI 上傳清單含 track)。
  • 場地識別:場地與零件統一,用 open4wd_version + type: "track",無場地專屬識別欄位。
  • loader 四分支(載入 Open4WD canonical 資產時)
  • open4wd_version → 拒絕載入(非 open4wd 資產)
  • open4wd_version !== 1 → 開發期直接拒絕載入(目前沒有舊/新版相容路徑)
  • type === "track" → 場地解析路徑
  • 其餘 → 零件解析路徑
  • Stage 1 外部上傳解析不走此分支:無 marker 視為一般 glTF,依創作者明示來源單位轉為公尺並做 provisional fit;帶當前 marker 的本機重開/fork 則直接沿用其精確 type 與 baked facts。可編輯匯出由 canonical 成品產生但會移除 marker、manifest 與衍生快取,再次匯入一律回到一般外部來源分支。open4wd_version 由本機儲存與上鏈共用 finalizer 寫入。
  • 版本號本身不編碼破壞性:它是正整數有序標籤,bump 一律 +1;某次 bump 是否為破壞牆,由遷移描述子 / 破壞牆表記錄(§18 / §22),不由數字判讀。

16. bump 原則

變更性質 schema bump 對舊版的效果
破壞性(增刪欄位、語意改、零件 ↔ chassis 介面配對〔convert〕演算法改、與物理運算式連動的欄位、無安全預設的新必填) +1 舊版進入棄用生命週期(§17
非破壞(純數值 / 位置:算法調值、mount 位置、auto_* re-convert) +1 不棄用,僅「建議升級」提示
  • 呼應公版規則:動 collider/mass 才 bump、純美術不 bump。
  • 「必須手動編輯」不是獨立的棄用觸發,而是破壞性變更中無法自動升級的子集 → 見 §17 升級分類 ②。
  • 發版軸掛鉤:任何 schema 版本 bump(破壞或非破壞)= 新映射函式 / 新破壞牆隨 client major 發版——映射變動影響 deterministic,本就是 §3 分類的必然結論;A 軸 major 閘由此保證同房全員持同一套牆 / 映射 / 支援範圍(§14 核心保證的前提)。minor 出貨會使同 major 不同 minor 的 peer 算出不同降版目標。

17. 資產生命週期

活躍 ─(非破壞變更)─────> 仍活躍,只顯示「建議升級」提示,照常可用
     └(破壞 / 必須手動編輯)─> 棄用:仍可用,但場內含棄用件 → 該 type 全場「降版本運算」(§18)
                            └(動態 min 抬過此版, §19)─> unusable:組裝/開房 picker 不出現、不可直接下場

三種升級分類(資產往新版怎麼移動,與棄用觸發正交):

分類 條件 升級操作
① 自動升級 有可推導的安全預設 client 自動補欄位,使用者只跑上鏈步驟
② 需手動編輯 無安全預設的新必填 開 Stage 2 補正後才能升級
③ 無法升級 語意徹底破壞 不能升、停在棄用直到 unusable 後退場
  • 遷移 = 產生新 CID(保 immutable 鐵則,舊 CID 永不被改)。
  • 版本後繼升級上鏈不耗幣§23)。

18. 降版本運算(核心機制)

當一場比賽出現棄用資產(§17,破壞性或必須手動編輯)→ 該 type 全場降版本運算

  • client 用該資產 schema version 對應的「extras → 物理輸入映射」函式重算該 type 所有資產。
  • 跑在當前引擎上(不需對應版本的 Rapier WASM)。降版本重現的是目標 schema 的映射,不是該版本發行時的引擎物理;決定性只要求同場全員一致,不要求與先前賽事 byte-identical。
  • 與 Rapier 無關:部件/場地的破壞性升版幾乎必然落在「公式 / 建模解析」層(derive_logic / builtin_assets / 材質參數 / 解析)。Rapier 換版屬 A 軸硬閘門(跨版根本不混場),降版本問題不在那條路上出現。保留的是輕量 TS 映射函式。

不變式(關鍵,禁止違反):B 軸降版本只替換單顆資產的 per-asset「extras→ 數值」映射;跨資產的聚合 / 交互(碰撞、stats 合成)一律走 derive_logic = A 軸、全場一致。因此跨 type 混版本(如降版 tire + 當前 weapon)不會錯亂 —— 耦合點是 Rapier 與 derive_logic,兩者均勻。

降版目標(per-type) = 全場該 type 持有資產中「最低非破壞相容類的最高版」(略過 yanked 版,§22)。

進入 live 後的例子:A 輪胎 v1、B 輪胎 v2、C 輪胎 v3;1→2 非破壞、2→3 破壞 → {1,2} 同一相容類、3 在破壞牆之上 → 統一降到 2(C 的 v3 向下映射到 v2)。

雙向遷移(每個破壞牆都必須提供)

破壞類型 向下映射
加必填欄位(additive) 舊公式不讀新欄位 → 丟棄,trivial
刪 / 改語意(subtractive / semantic) 新版無舊欄位 → 開發者在遷移描述子提供合成預設
② 人工有損升級 人改的值機器推不回 → 需 pre-edit 快照烘進 CID(§22

①/additive 用保留函式即算即得、不必存快照;只有 ② 人工有損升級才存 pre-edit 快照。

降版目標決定性:loadout 開賽前互換 + 破壞牆表 / yank 清單一致(client 隨 major 出貨,§16 / §22)+ 棄用・min 狀態一致(ledger 衍生、對 §20 pinned 檢查點算)→ 全員算出同一目標。

19. 動態最低支援版(dynamic min-supported)

per-type 最低支援版不是靜態 client 內建表,而是鏈上衍生的動態值

  • min 推導 predicate:對每道破壞牆 V(per-type)——「從 < V 升級到 ≥ V」的不同創作者數 ≥ 門檻 → min 抬到 V;取滿足的最高 V;單調不降。生態升夠 → min 自動抬升 → 舊版同時變 unusable。
  • 門檻常數 DEPRECATED_TO_UNUSABLE_THRESHOLD(per-type、預設 3),放 protocol.md client 常數(公版走 builtin: 前綴、不在鏈上,天然不計入門檻)。
  • 數「不同創作者」而非 CID 數量 → 防 griefing(一人自建自升湊門檻、強行作廢別人舊資產)+ 更符合「生態真的移動了」。

三條件

  1. 它是 DerivedState(折疊 ledger 而得),按 §20 pin 進房快照。
  2. 單調 ratchet(只升不降):升級件數只增不減 → 天然單調 → 舊計算路徑才敢回收。
  3. client 實體保有所有「可能還可用版本」的映射路徑;僅在動態 min 越過某版後才回收該版路徑 → 靜態地板 ≤ 動態閘門 → 有效 min = 動態閘門,保留路徑負擔有界

20. 共識:pinned 帳本檢查點

「升級件數 / min / 降版目標 / 資產是否 usable」皆 DerivedState,對同一段 ledger log prefix 決定性。沿用 ledger 既有「pinned 帳本檢查點 + 開賽前重驗」模式(帳本檢查點機制見 程式架構/ledger-checkpoint.md §3;freshness 政策與 §7 economy_config 不同——economy 是開賽前讀最新、驗全員相等,B 軸是 pin 後不追新):

  • 開房當下 pin 一個帳本檢查點(= host 已同步的最新 LedgerCheckpointEvent,非任意 log head——兩者是不同物件);整房生命週期所有版本閘門都對此檢查點算。
  • 後加入的 peer 必須 sync 到至少此 帳本檢查點 才算有效加入。
  • 後來鏈上新增的升級事件不會中途把人踢出房(以 pin 的快照為準)。
  • ReadySet 驗證先對同一份實際 loadout manifests 與 pinned 帳本檢查點判定;RoomService 在 create/join 與每次 Ready 都以本機已驗證 checkpoint 重跑 isPinnedCheckpointFresh,preloading 的版本檢查再要求 loadout 精確等於 LockedStartPackage
  • host 用較舊 帳本檢查點 不是漏洞(降版本本身安全且決定性),頂多讓較舊內容進房、別人看提示自行退(§21§24);staleness 上限 = ROOM_PINNED_CHECKPOINT_MAX_AGE_HOURS(72,protocol.md §5),超限或本機查無該 checkpoint 皆拒絕、host 須先 sync(防惡意 host pin 遠古檢查點讓 unusable 資產復活)。

21. 比賽提示

提示 觸發 計算意義
提示一:降版本運算 場內有棄用資產 → 該 type 全場降版本 真的改變物理。強提示
提示二:數據可能有差異 場內混了非破壞的較舊版 → 它們對新版才有的欄位用預設值 不降版、不崩,僅老資產吃預設 → 表現略不同。軟提示

另有第三種、非 B 軸的開賽前提示:場內含廢止材質的絕版品(玩家零件或場地)時顯示「絕版材質揭露」軟提示——性質是平衡揭露(材質物理未變、不降版、不擋配對),與本節兩種「降版 / schema 差異」提示正交。權威見 材質表.md §11.3

兩提示在開賽前顯示。開賽前退出無懲罰(本就無罰),故「自行退出」是降版本場的真實出口。降版本場照常計排名 + 經濟(棄用件存在 ↔ 該 type 生態小 ↔ 多半不只一人用、非狙擊;不公平可退)。

22. 遷移流程

  • 遷移描述子(每個破壞牆一份,CI 強制存在且自洽):宣告分類(①/②/③)+ 向上 migration + 向下 mapping。CI 自動偵測 schema 欄位增刪/型別變(結構與否),但分類與遷移碼由開發者撰寫(自動偵測 + 人工宣告)。
  • 鏈式遷移:資產落後多版 → migration 可組合(v1→v2→v3→v4 依序);向下映射同樣組合(v4→…→v1)。鏈式 ② 人工步驟併成一次編輯流程,不逼使用者分多次開 Stage 2。
  • schema 定於「上鏈提交時」對齊當前 client 驗證(非編輯器開啟時):編輯器掛多久無關,Stage 3 提交那刻若內容低於當前 schema → 先 shim 正規化成 current 才上鏈。A 軸 24h 強制更新寬限托底;升版套用受 §8.2 idle guard——Stage 2 編輯中屬非 idle、不強制 reload(編輯器不做持久化草稿,編輯內容僅存 session 記憶體)。
  • 版本 yank(壞版專用):給「壞掉」而非「只是舊」的版本。yanked 版不能當新建目標、不能當降版目標(免重現 bug)、停在該版的資產被強提示遷到最近「好版本」、過渡期降版到最近的非 yanked 鄰版續玩。yank(人工標記壞版)與年齡棄用(走生態替代)分開。這是整套唯一會強推遷移的情況。 yank 清單 = client 隨附資料、隨 major 出貨、不上鏈(yank 改變降版目標 = 共識輸入,見 §16 發版軸掛鉤)、不進 DerivedState(讀取層 overlay,程式架構/ledger-checkpoint.md §2;同廢止材質的 client 清單模式)。yank × 動態 min:可用性與降版目標一律在非 yanked 版本集合上計算;min 落在 yanked 版 → 過渡期有效地板 = min 之下最近的非 yanked 版(此例外優先於 unusable 判定——防「min 指向壞版 → 全 type 無版可用」死角)。CI 強制:yank 清單變更必須同版存在非 yanked 替代版(修好的新版或既有好版;yank 發版本就是修 bug 的 major,§13)。

23. 版本後繼血緣 + 經濟免費

  • 兩種邊
  • fork 邊(衍生作品、可能他人做)→ 70/20/10 分潤、血緣樹新增層。
  • 版本後繼邊(同創作者 schema 升級)→ 不新增分潤層、不算衍生
  • ledger 新增 AssetVersionUpgradeEvent(新 CID → 舊 CID 後繼邊)。
  • 評分 / 使用次數 / fork 樹位置三項全繼承:aggregate 在「血緣節點」層、不綁單一 CID → 升級自然延續,fork 樹位置不動。血緣節點收斂與 CID→ 節點映射的推導規則見 資料系統.md §4.1
  • 升級通道邊界僅限跨破壞牆、恆免費——舊 CID 處於棄用 / unusable(版本低於該 type 最新破壞牆;牆隨 major 出貨 = 純 client 靜態判定)才可寫後繼邊(① 自動 + ② 需手動編輯 免費;③ 不適用;被 schema 變更逼著遷移、不該收費)。不綁動態 min(若只有 unusable 才免費,棄用期升級要錢、而 min 又要「不同創作者升級 ≥ 門檻」才動,ratchet 得靠人付費點火)。非破壞舊版無升級通道——本就活躍可用、mesh 又鎖死不可改,「付費升級」無物可買;要改內容走一般付費上傳 / fork(上鏈費只燒在 UgcUploadEventUgcForkEvent 是免費血緣宣告,後繼事件無費用掛點)。防濫用 = 收件驗證六條(舊 CID 低於最新破壞牆 / 同創作者 / 同 type / 版本單調後繼 / 舊 CID 非仲裁黑名單 / mesh 幾何指紋一致、僅 extras 差異——schema 遷移只動 extras,改 mesh = 新上傳 / fork,付費且不繼承;程式架構/ledger-admission.md §1)。

24. 組裝 / 賽前驗證對接

  • 組裝釘具體零件 CID(immutable)。組裝記錄格式版本歸 A 軸,非 B。
  • 進房對動態 min 重驗(以 §20 pinned 帳本檢查點 為準):
  • 釘住 CID 版本 < 動態 min(unusable)→ 該組裝進房擋下,要求換件 / 遷移後重組才能上場。
  • 釘住 CID 為棄用(可降版)→ 照常進房、走降版本運算。
  • unusable 資產的可達性:unusable = 從「組裝/開房 picker」移除、不可直接下場;但其公開資產頁 / 血緣瀏覽仍在(公開 ledger、content-addressed、immutable)→ 別人仍可 fork(fork 經編輯器補成當前 schema → 可用,分潤流回原血緣節點)。
  • 三種救援路徑(持有人對待棄用/unusable 資產):(a) 上傳者不想維護 → 放著(棄用窗內可降版用)(b) 變 unusable 後上傳者又想用 → 自己升級(免費保血緣)(c) 別人想用 → fork。
  • preRaceVersionCheck 擴充:對 pinned 帳本檢查點 驗 loadout 所有資產 ≥ 動態 per-type min、算降版目標、發 §21 兩種提示。

目前支援範圍:降版目標、動態 min、loadout 閘與 preRaceVersionCheck 均由現行契約支援。版本後繼具備 descriptor contract、fold guard、atomic reducer、successorEdgeslineageNodeByCid 與 node-keyed royalty/評分/使用量;production descriptor catalog 為空,因此 asset-version-upgrade 由 admission 拒絕。加入第一道真實破壞牆時必須同批啟用完整 persistence/live-validation 路徑,不得只打開事件 allowlist。

25. 決定性論證

所有 migration 上 / 下函式皆純函式、client-shipped、version-keyed。同場 client_version major 相同(§5;映射隨 major 出貨,§16)→ 同一套保留映射函式 + 同 Rapier + 同 derive_logic → 對任何支援版本(含降版本目標)解讀一致。B 軸在支援範圍內不破壞 determinism。

26. 暫不實作:replay

replay(賽事重播 / 事後審計)牽涉因素多,暫不納入,待主體討論完成後再評估。少了 replay 反而簡化:舊計算路徑的保留壽命只由「現役比賽是否還需降版到該版」決定§19 條件 3)。已知相依:未來若加入 replay,需回頭重訂「舊路徑保留政策」(重播舊賽需要對應舊路徑)。