版本規範¶
本檔角色:版本系統設計、升版策略、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 §11 的
BUILTIN_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 不影響配對)。
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 後再以鎖定包重驗:
- 同房全員(2–8 人)版本一致
- ledger 上
economy_config_version與本地一致 - 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_version 與 builtin_assets_version 嵌入
CACHE_NAME(open4wd-${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 應用內按鈕¶
設定頁 → 「強制更新」按鈕:
- unregister 所有 service worker
- clear caches
- 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–§13 是 A 軸(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;空
descriptors/breakingWalls 是上述 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,故一個欄位即可。typeenum = 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.mdclient 常數(公版走builtin:前綴、不在鏈上,天然不計入門檻)。 - 數「不同創作者」而非 CID 數量 → 防 griefing(一人自建自升湊門檻、強行作廢別人舊資產)+ 更符合「生態真的移動了」。
三條件:
- 它是 DerivedState(折疊 ledger 而得),按 §20 pin 進房快照。
- 單調 ratchet(只升不降):升級件數只增不減 → 天然單調 → 舊計算路徑才敢回收。
- 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(上鏈費只燒在
UgcUploadEvent;UgcForkEvent是免費血緣宣告,後繼事件無費用掛點)。防濫用 = 收件驗證六條(舊 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、successorEdges/lineageNodeByCid與 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,需回頭重訂「舊路徑保留政策」(重播舊賽需要對應舊路徑)。