adventure-table

M01 — 開發設計方針

Phase:M01 — Multi-Source Character Content Expansion
類型:M Phase(Modification / Maintenance Phase)
A~N 是截至 2026-09-06 已完成的 Character Content baseline;M01 現為長期保持 open 的 Character Content Expansion / Maintenance track。M01-O 已拍板為 XGE / TCE Feat Expansion。本文件定義 M01-O 與未來 M01-P+ 必須延續的工程契約,以及已完成 baseline 不可破壞的邊界。完成後必須為真以 實作規格.md 為準;測試證據以 測試指南.md 為準。

最後更新:2026-09-13


1. M01 lifecycle / Subphase 編號

已完成:

M01-A — Multi-Source Content Pack Foundation
M01-B — PHB Character Origins & Background Expansion
M01-C — SCAG / GoS Background Expansion
M01-D — VGM Race Expansion
M01-E — SCAG Half-Elf Variant & Grant Replacement
M01-F — VRGR Lineage & Dhampir
M01-G — TCE Artificer Core
M01-H — TCE Artificer Advanced Features & Infusions
M01-I — TCE Optional Class Features & Fighting Styles
M01-J — 2014 Class Subclass Expansion
M01-K — PHB Feat & Spell Catalog Expansion
M01-L — VGM & SCAG Remaining Race Expansion / Generic Race Mechanics
M01-M — MTF Planar Race Expansion & Tiefling Bloodline / Variant System
M01-N — Character Sheet HTML Export

這些 Subphase 的完整歷史實作細節與 closeout 證據留在 git history、M01-*_CLOSEOUT.md 與現有測試中;它們不是待辦。

已拍板、尚未實作:

M01-O — XGE & TCE Feat Expansion

下一個未使用字母是 M01-P。 不預留任何字母給 Full M01 Integration & Closeout。M01可以長期保持 open,同時 P2、P3… 正常往前;每個新 M01 Subphase自己設計、自己 closeout。

未來只有在使用者正式拍板某批內容/角色系統維護工作時,才把該 Subphase加進三份 M01文件。不要預先拆 P/Q/R。


2. 現有地基與不可破壞契約

M01-O+ 開工時一律以當下 main 真正 codebase再次核對 signatures;不能假設 2026-09-06 的 file layout 永遠不變。

目前 baseline的重要語意:

任何新 M01 都必須延續這些邊界,除非新的正式 Subphase規格明確、有證據地修改 contract。


3. Runtime content / reference boundary

正式 runtime source:

data/<pack>/...
data/rules/...
existing localization SSOT

docs/暫用規則資訊/ 只作 human / maintainer authoring reference。

固定流程:

reference
→ authoring / normalization
→ checked-in data/<pack>/ + localization
→ runtime Content Registry

禁止 production runtime parse / fetch / import Markdown。正式 package沒有 docs/ 時仍要工作。

新 pack必須:


4. 新 M01 Subphase 的最小設計原則

4.1 只做當批資料真的需要的 primitive

可擴充:choice、grant、replacement、level gate、lineage、optional feature、expanded option、retraining、subclass progression、feat prerequisite / acquisition、resource metadata、movement、closed conditional rule等。

禁止為 future books預造:

arbitrary expression language
user-authored scripts
generic Combat trigger DSL
generic effect interpreter
unbounded condition language

需要 P3/P4/P5/Rest/Adventure等 substrate的效果可以 structured + manual / deferred;不要從 M01偷跑未來 engine。

4.2 泛化現有 substrate,不做 source-specific第二套

新內容若與既有機制同類,優先重用/泛化:

禁止 if <race/class/book name>散落成新 engine,除非那真的是來源特有 anomaly correction且有明確理由。

4.3 Build / State ownership先定義再寫 code

每個新 rule shape先回答:

永久角色決定? → Build / immutable Version
目前可變狀態? → CharacterState
Builder未確認輸入? → Draft
純 content fact? → data/<pack>

不能用 UI方便性決定 persistence ownership。


5. P2+ 開始後的 downstream compatibility

M01 不再只活在 P0/P1世界。P2 之後,每個新的 M01-O+ 開工前必須先盤點:

本次修改的 shared contracts
→ 現在有哪些 downstream P Phase consumer
→ 哪些需要 regression / migration / DTO compatibility

例如未來修改:

如果 P2已建立 Party Roster / Seat / Session對 Character identity的引用,就必須一併驗證那些 reference 不被新 M01破壞。

不要求每個小型 content-only M01把所有後續 P Phase全跑一次。 只驗直接受影響的 consumer;範圍依 dependency graph決定。


6. M03 standalone 永久 boundary

M03 closeout後:

app.content.*
app.domain.character*
shared character persistence / builder logic

必須維持 standalone-safe,不得因 P2多人系統出現後反向 import:

Room
Campaign
Session
Seat
Party Roster
multiplayer-only service

新 M01若碰到 protected Character / Content module,至少 static review:

新 M01若碰:

Character JSON envelope
builder_provenance
Version payload
State payload
StableKey resolution
import landing / reconstruction

則要同步 review Web↔Standalone exchange。schema變更必須 backward-safe或明確版本化,不得只讓 web path通過。


7. Localization 永久 contract

M02規則持續生效:

不要再安排「先英文、以後 M02補」。M02已是基礎能力,不是未來補翻譯的排程箱。


8. Migration / persistence policy

新增 optional JSON field原則上使用 backward-safe default,不因 convenience bulk rewrite所有舊 immutable Build / State。

只有真正需要 relational schema change時才用 Alembic,例如:

新 M01若影響歷史資料讀取,要明確涵蓋:

old Build JSON
old State JSON
old Version History
old builder provenance
old Character JSON export where applicable

Build Edit / Correction產生 Version N+1,不 in-place rewrite Version N。


9. API / UI 共通邊界

M01不是 API redesign track。

優先延伸既有 endpoints / DTO;新 API只在既有介面無法合理表達時加入。Frontend保持:

不得為某本書建立平行 Builder、Content Library management、Book enable/disable workflow或 Marketplace,除非未來產品規格另外拍板。


10. Cumulative hardening 不是一次性 Full Closeout

A~N過程建立過很多跨 rule-shape hardening需求,例如:

未來若新 M01碰到其中一塊,應在該 Subphase順手收斂真正重複的 helper與新增 regression;不要把 technical debt全部推給一個不存在的 Full M01 closeout。

同樣地,未來 P Phase closeout若要跑 whole-character cumulative regression,可以引用 測試指南.md 的 cumulative baseline;不代表 M01被關閉。


11. Magic Items / future content

完整 Magic Items、generic Attunement、item charges/recharge、magic-item modifier automation等目前沒有自動成為 M01 scope。

若使用者之後拍板,做法是:

M01-P / Q / ...
→ 明確 scope
→ 三份 M01 文件同步
→ 實作 / 驗證 / closeout該 Subphase

Artificer既有少量 staged item identity / attunement metadata繼續相容,但不等於 Magic Item subsystem已完成。


12. Long-running M01 Anti-patterns

以下一律視為設計錯誤:

  1. 非 SRD資料偽裝成 srd5.1
  2. 用 display name當 foreign key或 canonical dedupe key。
  3. content_sources同時被當 allowlist與provenance。
  4. 掃描 data/所有資料夾自動 enable未完成 pack。
  5. Frontend hardcode D&D legality / progression。
  6. 新 rule shape為單一 race/class/book建立第二套 engine,已有 generic substrate卻不重用。
  7. permanent choice / live state ownership混亂。
  8. replacement只 hide UI、不移除 final Build semantics。
  9. Expanded access被誤寫成 Known / Prepared。
  10. Versioned choice直接 in-place修改歷史 Build。
  11. 需要 Combat / Roll / Rest的效果假裝已自動執行。
  12. runtime讀 docs/暫用規則資訊/
  13. 新 user-visible content只交一種 locale。
  14. P2+ 已存在後修改 shared Character contract卻只跑 P0/P1 regression。
  15. 為了多人功能讓 Character / Content core反向 import Room / Session / Seat / Campaign。
  16. 改 Character JSON / provenance / version payload卻不檢查 standalone import/export。
  17. 把未拍板 future content當成 M01 open 所以「順便」加入。
  18. 把應由新 Subphase明確定義/驗收的 major schema或technical debt推給一個不存在的 Full M01 closeout。

13. M01-N — Character Sheet HTML Export

13.1 為什麼在 client 端產生

角色卡上的名稱是前端向 presentation batch 解析出來的結果。若在 server 產生 HTML,等於把 M02 的 localized resolver 與整個角色卡版面在 Python 再實作一次,之後每次改角色卡都要維護兩份呈現邏輯。因此本 Subphase 不新增 server endpoint、不改 DTO、不動 Python。

附帶效果:輸出資料取自前端已持有的 sheet 資料,那份資料本來就經過 server 過濾,所以「只輸出看得到的東西」是結構上成立,不需要額外機制。P2 引入 Seat / Controller 後同樣成立。

13.2 渲染方式

createRoot 把輸出用的 React 樹掛在一個 detached 節點上,flushSync 後讀 innerHTML

不得為此把 react-dom/server 加進 client bundle——它目前只在測試用,加進去等於為一顆按鈕擴大所有使用者的下載量。

13.3 呈現層重用

角色卡的呈現元件必須被輸出重用,不另寫第二套版面。輸出模式與螢幕模式的差別只有三點:

三段全出,不受目前分頁影響
不輸出互動控制(按鈕、輸入、搜尋、分頁列)
依 scope 決定是否輸出 Current State 區段

scope 是輸出模式的參數,不是兩套元件。

13.4 Build only 的邊界

inventory 整段來自 CharacterState.inventory_state,角色卡從未呈現 Build 的 starting_equipment。因此角色配置的物品欄是整段不輸出,不是改用 starting equipment 呈現:後者對已升級的角色沒有參考價值,且會逼出一個沒有其他用途的 DTO 欄位。

AC 由 calculate_armor_class(build, state, registry) 得出、會讀目前裝備。角色配置仍輸出 AC並在文件內註明基準,理由是沒有 AC 的角色參考卡難用,而靜默輸出一個依賴隱藏資料的數字不誠實。

13.5 自足輸出

樣式以建置期 inline 匯入(Vite ?inline)取得字串後寫進輸出檔的 <style>,不在執行期抓 document.styleSheets——後者在不同建置與瀏覽器下不可靠。

輸出檔內不得出現 <script>、外部 hrefsrcfetch 或任何網路依賴。字型只用系統字型堆疊。

下載走 Blob + URL.createObjectURL + <a download>,用後釋放物件 URL。檔名需對角色名做檔案系統安全處理。

13.6 列印樣式

輸出檔內含 @media print 區塊,只在列印/另存 PDF 時生效,螢幕呈現不受影響。至少處理:淺色底、移除純裝飾的光暈與陰影、卡片 break-inside: avoid、法術網格降欄以符合 A4 寬度。

13.7 Localization

輸出當下的 locale決定整份文件語言,之後不再變動。新增的 UI文案(按鈕、範圍選項、文件內標題與 AC註記)依 M01永久 contract同一 Subphase交齊 zh-TW / en

13.8 入口

角色卡 header既有的 Character JSON匯出按鈕不動(M03已驗收交付)。新增獨立的 HTML匯出入口,由它提供範圍選擇。合併成單一匯出選單留待日後產品層另行拍板。

13.9 邊界


14. M01-O — XGE & TCE Feat Expansion

14.1 Materialization / inventory

Authoring reference 固定為:

docs/暫用規則資訊/專長_TCE_XGE.md

正式資料分別 materialize 到既有 data/xge/data/tce/,並納入各自 manifest / registry validation。不要新增 xge-tce 混合 pack,也不要因 reference 合併成同一份 Markdown就混掉 source provenance。

實作時新增 deterministic inventory verifier(名稱可依現有 scripts 慣例),至少驗:

XGE feat count = 15
TCE feat count = 15
M01-O total = 30
duplicate StableKey = 0
wrong pack provenance = 0

14.2 Feat schema:優先沿用 M01-K

先以 M01-K 現有 Feat data / acquisition schema做 gap analysis。以下既有能力必須重用,不另起 source-specific欄位:

只有 30 個 Feats確實無法表示的 rule shape才新增 optional typed field;新增欄位需 backward-safe default,舊 PHB Feat不需要 migration rewrite。

Spellcasting atom 擴到 subclass-granted Spellcasting。 現行 {"type": "spellcasting"} 的判定只看 class 層(class data 有 spellcasting 或 class index 落在施法職業集合),Eldritch Knight / Arcane Trickster 這類由 subclass level 3 feature 授予 Spellcasting 的 Build 會被誤拒。M01-O 必須把判定改為以 candidate Build 的最終 facts 為準:

class-level Spellcasting(含 Artificer)
Pact Magic(Warlock)
subclass-granted Spellcasting feature(依已選 subclass 與已達 subclass level 判定)

三者任一成立即通過。判定沿用 canonical feature / subclass StableKey,不用 display name 也不硬列 subclass 名稱清單;未達授予 level 的 Build(例如 Fighter 2 尚未選 Eldritch Knight)仍拒絕。此擴充是共用 atom 的修正,Elemental Adept / Spell Sniper / War Caster 等既有 PHB Feat 會一併受益,closeout 必須把它們的 prerequisite regression納入 M01-K 直接受影響 suite。

14.3 Ancestry / size prerequisite atoms

M01-O 新增的 racial feat prerequisite不得用 display name比對。prerequisite resolver需要能基於 canonical Build facts判斷:

race StableKey / canonical ancestry tag
subrace / variant lineage identity
size category
OR / AND composition

至少支援:

Variant Human 不是 Human 的 subrace。 Resolver不得依 subrace 關係推導它;shared content facts / ancestry normalization 必須明確讓 phb2014:race:variant-human 滿足 Human ancestry prerequisite,同時保留它自己的 StableKey identity。Half-Elf variant、Tiefling bloodline / variant等既有 replacement composition也不得因此失去其 canonical base ancestry資格。若現有 Build只保存 StableKey而沒有 ancestry tag,應在 shared content facts/resolver補最小 normalization,不在每個 Feat資料內硬列所有 variant StableKey。

14.4 Proficiency / Expertise / language choices

Prodigy / Skill Expert等選擇使用 generic nested choice:

Expertise candidate resolver看 candidate Build當下的最終 proficiency facts,並排除已經被同類 double-proficiency效果覆蓋的技能。若同一次 acquisition先選新 skill proficiency,再選 expertise,後一個 choice必須能看見前一個 choice產生的 candidate facts;不要靠前端自行拼候選清單。

Squat Nimbleness的 Acrobatics / Athletics使用同一 skill proficiency primitive;Chef / Poisoner工具熟練亦使用同一 tool proficiency grant。Artificer Initiate 必須從 canonical Artisan’s Tools pool 自選一種工具熟練,將被選工具 StableKey 保存成 acquisition nested choice;同一選擇同時產生「此工具可作為施展任何 INT-based spell 的法器」之 typed focus eligibility,不建立第二個互相可能不一致的工具選擇。 Fey Teleportation 的 Sylvan 使用既有 language grant,不只存在 description。

14.5 Canonical option-pool reuse

三個 TCE Feats不得複製 class option data:

Builder的 option identity仍保存原 canonical StableKey;Feat acquisition只保存 entitlement / selected refs / provenance。

14.6 Feat-linked retraining

需要最小通用的 acquisition-linked retraining policy,而不是用 feat名稱 hardcode:

never
on_any_level_up
on_asi_level_up

M01-O mapping:

Eldritch Adept       → on_any_level_up,替換 1 invocation
Fighting Initiate    → on_asi_level_up,替換該 Feat grant 的 style
Metamagic Adept      → on_asi_level_up,替換其中 1 個 metamagic option

Level Up產生 candidate Build / Version N+1;Version N原選擇不可被原地改寫。Build Edit不應被拿來無條件繞過 retraining gate;若產品既有 Build Edit可做 correction,必須沿用其「修正」語意而不是把它變成免費 retraining。

14.7 Feat-granted spells

固定法術與選擇法術都 reference canonical spell StableKey,不複製 spell definition。

需涵蓋:

這些是 Feat source的 spell access / free-cast grants,不得混寫成 class Known / Prepared。若 spell因其他 pack已有 canonical identity,直接 cross-pack ref;不要在 xge / tce 重複定義 spell。

14.8 Ability-choice dependency

Fey Touched / Shadow Touched / Telekinetic / Telepathic的 INT/WIS/CHA +1 選擇同時是該 Feat spellcasting ability。資料模型應讓 spell grant reference同一 acquisition choice結果,例如以 choice id / selected ability token關聯,而不是另外建立第二個可不一致的 casting-ability選擇。

14.9 Static mechanics reuse

可以由現有 shared substrate安全表示的 mechanic直接接線:

14.10 Metamagic Adept resource contribution

這 2 Sorcery Points有特殊 restriction:

contributes 2 to sorcery-point capability
allowed use = metamagic only
not allowed = flexible casting / other sorcery-point spends
recharge = long rest

若既有 resource model只有「一個無差別整數池」,不得直接把 capacity +2後遺失用途限制。優先補 generic source-scoped resource contribution,例如 contribution保留 source_ref + allowed_spend_tags;presentation可顯示總量,但未來 spend resolver必須能知道哪些點受限制。若目前沒有自動 spend substrate,Current State至少要能不失真地保存/顯示該 contribution,不假裝一般 Sorcery Points完全等價。

14.11 Automation classification

每個 M01-O Feat都要明確標示 implementation level,延續 M01-K分類,可視實際 schema沿用/細化:

full_structural
static_derived
structured_deferred_roll
structured_deferred_reaction
structured_deferred_combat
structured_deferred_rest
structured_deferred_inventory

例如:

分類是 machine-readable boundary;不能只把「尚未自動」寫在 Markdown後讓 runtime無法知道。

14.12 API / Builder / presentation

M01-O原則上不新增 endpoint。既有 Builder choices / validation / Review / Character Sheet DTO能承接時只擴 content與typed mechanics。

Frontend只渲染 server choices:

若現有 DTO無法呈現某個已正式存入 Build的 nested selection,才做最小 additive DTO欄位;不得順便 redesign Character Sheet。

14.13 Persistence / migration

預期以 content data + backward-safe JSON extension為主,不預設需要 Alembic migration。只有實際 gap analysis證明需要 relational shape時才新增 migration。

至少保證:

old PHB feat Build仍可讀
M01-O Build reload/restart不丟 acquisition choices
Version History保存 retraining前後版本
content_sources derive xge / tce
Character JSON round-trip保留 M01-O StableKey與必要 selections

若新增 optional Build/State field,舊資料缺欄位必須有 safe default。

14.14 Standalone / downstream compatibility

M01-O碰 Content Registry、Feat Builder與可能的 Build JSON optional fields,因此至少需要 M03 standalone compatibility review。新增 helper放 shared Character / Content層,不反向 import Room / Campaign / Session / Seat。

P2 Room Character Workspace是直接 UI consumer:至少確認在 Room內 Create / Level Up / Build Edit仍能取得新的 Feat choices。P3只要 Character identity / sheet payload contract未改,原則上做 static/focused compatibility即可;若最終實作改了 shared DTO/schema,再擴大到實際受影響的 P3 regression。