Phase:P0 — Character Core + SRD / Rules Foundation
本文件是 P0 的實作契約。只處理 P0 真正需要的技術設計,不替 P1 以後預先設計系統;已知跨 Phase 承接只記 requirement,不提前定 schema / API。
最後更新:2026-08-29
三份 P0 文件使用完全一致的 Subphase 名稱與順序:
P0-A — Project Foundation
P0-B — Character-Relevant SRD Foundation
P0-C — Character Core & Persistence
P0-D — Character Rules & Backend API
P0-E — Character Sheet & State UI
P0-F — Full P0 Integration & Closeout
每個 Subphase 必須先完成自己的 implementation + tests + verification,再 commit 並進下一個。
沿用目前技術棧:
Frontend:
Backend:
Database:PostgreSQL。
Tests:pytest、Vitest、Playwright。
Local / Deployment baseline:Docker Compose。
P0 不需要 Redis、Message Queue、Microservices、Kubernetes、Vector DB。
P0 建議第一版:
adventure-table/
├─ apps/
│ ├─ web/
│ │ ├─ src/
│ │ │ ├─ api/
│ │ │ ├─ components/
│ │ │ ├─ features/
│ │ │ │ └─ character-sheet/
│ │ │ ├─ pages/
│ │ │ └─ main.tsx
│ │ └─ ...
│ └─ server/
│ ├─ app/
│ │ ├─ api/
│ │ ├─ domain/
│ │ │ ├─ character/
│ │ │ └─ rules/
│ │ ├─ content/
│ │ ├─ persistence/
│ │ ├─ services/
│ │ └─ main.py
│ ├─ migrations/
│ └─ tests/
├─ data/
│ └─ srd5.1/
├─ docs/
│ └─ P0/
│ ├─ 實作規格.md
│ ├─ 開發設計方針.md
│ └─ 測試指南.md
├─ docker-compose.yml
├─ AGENTS.md
├─ PROJECT_BRIEF.md
├─ 規格企劃.md
└─ 技術棧討論.md
P0-A 只建立真正需要的 scaffold。不要建立 Combat、Room、Adventure、AI Runtime 等空 module。
P0-A 至少建立:
PostgreSQL available
↓
Alembic baseline works
↓
FastAPI starts
↓
health/readiness responds
Vite dev server starts
↓
minimal React app shell renders
測試 runner 在這一階段就要可以執行,即使只有最小 smoke case。
P0 採:
Version-controlled normalized data files = P0 SRD reference content 的 Source of Truth。
P0 不需要為 static SRD reference data 再做一份可手動編輯的 DB truth。
資料放在:
data/srd5.1/
P0-B 建議至少包含:
data/srd5.1/
├─ manifest.json
├─ races.json
├─ classes.json
├─ subclasses.json
├─ backgrounds.json
├─ feats.json
├─ skills.json
├─ equipment.json
├─ spells.json
├─ conditions.json
├─ damage-types.json
├─ languages.json
└─ ... character-relevant rule/reference data
如果某類資料太大可拆多檔,不為形式一致硬塞單檔。
P0-B 不建立 monsters.json 或 equivalent Monster / Beast stat-block data。
只導入角色系統與 P1 Builder 會真正引用的 SRD 5.1 類別:
以下全部不屬於 P0-B:
以上明確延後到 P4-A。這裡只記承接要求,不預先定 P4 的 Monster Template schema、API 或 Combat representation。
每個 reference entry 必須有不依賴顯示名稱的 stable key,例如:
srd5.1:spell:fireball
srd5.1:class:fighter
srd5.1:equipment:chain-mail
Character 只保存 stable content key,不複製整份 SRD definition。
顯示名稱可改,stable key 不應因此改變。
各類 reference content 使用 Pydantic model 驗證。
共同欄位至少:
key
name
source
ruleset
其他欄位依資料類型定義。
不要設計一個萬能巨大 SRD schema。
Server 啟動時:
Read manifest
↓
Load P0-B data files
↓
Pydantic validation
↓
Cross-reference validation
↓
Build ContentRegistry
↓
Application ready
若 mandatory content schema / reference 錯誤:啟動失敗,不要靜默 skip。
P0 先讓 static data 由 ContentRegistry 查詢即可;未來若真的需要 DB indexing,再於需求出現時增加。
P0-C 正式建立 Character identity、immutable Build Version、mutable Current State 與 persistence。
characters概念欄位:
id
name
ruleset
current_version_id
created_at
updated_at
使用 UUID 作為 DB entity id;SRD reference 仍使用 stable content key。
character_versions保存 Build snapshot:
id
character_id
version_no
build_payload JSONB
created_at
原則:
build_payload 必須通過 CharacterBuild Pydantic schema。
P0 採 JSONB snapshot 的理由:
若 P1 後真的需要 relational query,再對真正需求 migration。
character_states概念欄位:
character_id
state_payload JSONB
updated_at
state_payload 通過 CharacterState schema,至少表示:
Build 與 State 不可共用同一 payload。
至少區分:
CharacterBuild
CharacterState
CharacterSheetDTO
CharacterSheetDTO 的完整 calculated read model 於 P0-D 建立;P0-C 先確保 Build / State DTO 有正式 schema。
至少能表示:
identity/basic profile
race_ref
class_progression[]
ability_scores
proficiencies
saving_throw_proficiencies
skill_choices
feature_refs[]
feat_refs[]
spell_access_entries[]
hp_progression[]
starting_equipment[]
roleplay_profile
numeric_overrides
不可只存:
fighter = 5
wizard = 5
至少保留 acquisition order,例如:
[
fighter,
fighter,
fighter,
fighter,
fighter,
wizard,
wizard,
wizard,
wizard,
wizard
]
之後可 derive total level、per-class level、starting class。
CharacterBuild.ability_scores = 永久 Build 選擇全部解析後、Numeric Override 前的 resolved Build Score。
標準 fixture:
STR = 16
DEX = 14
CON = 14
INT = 16
WIS = 10
CHA = 8
Human racial bonus 已包含,之後 Rules Engine 不得再因 race_ref 加一次。
Build spell access entry 至少:
entry_id
spell_key
source_type
source_key
access_type
entry_id 是 Build 內 logical stable id。
P0 Build access_type:
known
spellbook
always_prepared
granted
不得使用 prepared 作為 Build access type。
一般 Prepared Spells 存 CharacterState.prepared_spell_entry_ids[],指向目前 Build 可準備的 entry。
Always-prepared / Granted 不需要塞進每日 prepared selection 才生效。
P0 fixture 正式支援 Wizard spellbook + prepared state;Cleric / Druid 的完整準備模型等 P1 真正做 Builder 時再擴充,不先做大型 abstraction。
hp_progression[] 保存每個 Character Level 實際採用的 base HP gain,與 class_progression[] 對齊。
標準 fixture:
class_progression:
Fighter, Fighter, Fighter, Fighter, Fighter,
Wizard, Wizard, Wizard, Wizard, Wizard
hp_progression:
10, 6, 6, 6, 6,
4, 4, 4, 4, 4
這只代表 fixture 使用 fixed value;P1 若允許 rolled HP,保存該級實際採用的 base HP gain 即可。
Numeric override 屬 Build,允許的 key 由 P0-D rules layer 明確定義;不得允許任意 raw DB field override。
可包含:
ability:<ability-key>
ac
max_hp
skill_modifier:<skill-key>
spell_save_dc:<source-key>
至少:
current_hp
temporary_hp
Max HP 不存成另一份一般 authoritative state;由 P0-D 計算。
至少:
condition_ref
display/source note # optional
P0 保存與顯示;完整 condition gameplay automation 屬 P4。
至少:
prepared_spell_entry_ids[]
spell_slots_used / remaining
class-specific resource usage # fixture 真正需要時
Prepared selection 改變只更新 State,不建立 Character Version。
Build total 可由 class progression derive;State 只保存每種骰型 available 數量。
標準 fixture:
d10: available 5 / total 5
d6: available 5 / total 5
State 不複製 total 作另一份 truth。
整份 live Inventory 都住 Current State。
每個 live entry 至少:
entry_id
item_ref
quantity
equipped
關鍵規則:
starting_equipment[] 只記起始選擇 / 初始化來源。inventory_state。固定:Human Fighter 5 / Wizard 5,Character Level 10。
標準 fixture 無 Numeric Override,至少包含:
16 / 14 / 14 / 16 / 10 / 8。10, 6, 6, 6, 6, 4, 4, 4, 4, 4。Seed 不需要 production Builder API,例如可用 developer-only command:
python -m app.scripts.seed_p0_fighter_wizard
P0-C Alembic migration 至少建立:
characters
character_versions
character_states
Rules Engine 必須是純 Python domain logic,不依賴 FastAPI request、SQLAlchemy session 或 React。
建議:
app/domain/rules/
├─ abilities.py
├─ proficiency.py
├─ skills.py
├─ saves.py
├─ armor_class.py
├─ hit_points.py
├─ spellcasting.py
└─ character_sheet.py
計算流程:
CharacterBuild
+ CharacterState
+ ContentRegistry
↓
Resolve effective Ability Scores
↓
Rules Calculation
↓
Apply remaining Numeric Overrides
↓
CharacterSheetDTO
至少遵守:
hp_progression[] + effective CON modifier,再套 Max HP override。Max HP baseline:
sum(base_hp_gain)
+ effective CON modifier × total character level
↓
其他已支援 structured modifier(若有)
↓
Max HP Numeric Override(若有,最後)
GET /api/rules/content/{category}
GET /api/rules/content/{category}/{key}
可做基本 filter/search,但 P0 不做大型 search service。
GET /api/characters/{character_id}
GET /api/characters/{character_id}/sheet
PATCH /api/characters/{character_id}/state
PATCH /state 至少承載:
P0 不做 production POST /characters Builder workflow。
Backend 回 machine-readable error code + readable message;Frontend 不依英文 error string 判斷流程。
P0 不需完整 i18n framework。
從標準 fixture 建 test-derived variant:
ability:strength = 18
ac = 19
max_hp = 80
固定 expectation:
Fighter(非 Eldritch Knight)5 對 multiclass caster level 貢獻 0;Fighter5/Wizard5 slots 等同 Wizard5。
P0 fixture 只證明 representation / class order / source / access / prepared separation;不驗證多個 caster class combined progression,完整規則留 P1。
至少:
/characters/:characterId
Character Header
├─ Name
├─ Level
├─ HP
├─ AC
└─ Conditions
Tabs / Pages
├─ Attributes / Skills
│ └─ Hit Dice by die type
├─ Spells
└─ Inventory
Page 1:Ability、Skills、Saves、Passive Perception、Hit Dice、Features/Traits、Conditions、optional Roleplay。
Page 2:Build spell access(Known / Spellbook / Always-prepared / Granted)與 Current State Prepared selection 分開顯示;另顯示 Spell Save DC / Attack、slot/resource state。
Page 3:只讀 CharacterState.inventory_state 的 live Inventory,不從 Build Starting Equipment 重建。
Read:
React page
↓
TanStack Query
↓
GET /api/characters/{id}/sheet
↓
CharacterSheetDTO
Mutation:
UI action
↓
PATCH state
↓
Server validates + persists
↓
invalidate / refetch
↓
UI 顯示 authoritative result
不要用 optimistic mutation 假裝正式 HP / resource / inventory state 已成功;P0 採 server-confirmed update。
需要能驗證的 Current State 操作包括:
Prepared Spells 必須正確 display / persist / API validate,但 P0 不要求完整 Prepare-Spells Wizard。
Hit Dice 必須正確 display / persist,但 P0 不要求完整 Short Rest spend workflow。
Optional Roleplay 空白不得阻擋頁面。
P0-F 原則上只做 integration、regression、補驗收缺口與必要 bugfix,不再新增新的大型產品能力。
P0-F 最終 startup:
DB available
↓
Alembic schema ready
↓
ContentRegistry validate/load Character-Relevant SRD
↓
FastAPI ready
↓
React app can load fixture Character Sheet
SRD mandatory validation 失敗時 readiness 不得假裝正常。
P0-F 關門時必須確認:
PROJECT_BRIEF.md 仍明記 P4-A 承接 SRD Monster / Beast stat blocks。P4-A 的具體資料表示方式等 P4 開工時才設計。
本文件刻意不定義:
這些等對應 Phase 開工時再寫。