adventure-table

P3-C Closeout Checklist

P3-C — Roll, Check & PendingAction closeout scope。編號對應 實作規格 的「完成後必須為真」。

Verification evidence

分支 p3-c-roll-check-pending-action

Alembic heads
  0015_character_state_revision (character) (head)
  0019_p3c_check_command        (web)       (head)

本機指令
  apps/server  pytest                       1247 collected, exit 0, 無 fail
                                            (新增 8 條:5 條授權 / Group 在此輪執行,
                                              3 條 PostgreSQL 在無 P3_POSTGRES_URL 時 skip)
  apps/web     npm test -- --run            63 files / 285 passed
  apps/web     npm run build                tsc --noEmit + vite build 通過
  repo root    docker compose config        pass
  apps/web     npm run test:e2e:docker      114 tests:111 passed, 3 skipped (8.2m)
                                            第二輪 xge-less m03c-character-import.spec.ts:7 passed
  apps/server  P3_POSTGRES_URL=... pytest tests/test_p3c_postgres_rolls.py
                                            3 passed(連跑三次穩定;本機以獨立的
                                            adventure_table_p3c database 執行)

E2E 的 3 skipped 是既有的 intentional skip(m01j direct high-level create 與 m03c 兩條需要缺 pack 後端的案例),後者由同一次 test:e2e:docker 的第二輪實際執行並通過。

test_p3c_postgres_rolls.py 已加入 .github/workflows/p3-non-e2e.ymlpostgres-migrations job;該 job 是 CI 上唯一的真 PostgreSQL 證據來源,本機另以 docker compose 的 PostgreSQL 實跑過同一組測試。

E2E Journey C1 在 apps/web/e2e/p3c-roll-check-pending-action.spec.ts。它目前只在本機全套執行時被跑到p3-non-e2e.yml 不含 E2E job,p2-e2e.ymlworkflow_dispatch

關門過程中修正的問題

  1. bounded Resume cursor 讓 reload 掉歷史。 P3-C 原本把 eventStreamFromResume() 的 cursor 從 0 改成 Resume 回傳的 cursor,並刪掉 P3-B 的 backfill 測試,改以「不再回掃 bounded window 之前的歷史」為預期行為。Resume 視窗是 RECENT_EVENT_WINDOW = 50,因此超過 50 個 raw event 的 Session,reload 後看不到更早的訊息,牴觸 P3-B 第 16 條與 P3-A 第 6 條。修正後只有在 Resume 能自證覆蓋 after_seq == 0has_more == falsecursor >= last_event_seq 時才沿用該 cursor,否則退回 durable replay,P3-B 的 backfill 測試一併復原。
  2. Resume 競態會永久吃掉已畫出的訊息。 Session 頁在 mount 與每次 lifecycle mutation 都重抓完整 Resume,而 React 會重跑 mount effect,因此可能有多個 Resume 同時在飛;reload() 沒有 staleness guard,且整包取代 event stream,而 long-poll runner 擁有自己的單調 cursor、不會重送已送出的 page。晚回來的 Resume 因此會把剛畫出的 event 永久抹掉——P3-B Journey B2 的間歇性失敗(畫面停在 No in-session messages yet.)就是這條。修正後 reload() 只有最新一次可寫入,event stream 改為合併,sessionEventStream.test.ts 增加兩條回歸測試。
  3. 契約 4、9 與真 PostgreSQL 併發缺測試證據。 關門對照時發現:complete_formal() 的拒絕分支(非 current DM 第三方、跨 Seat、跨 Session、舊 access session)完全沒有測試觸發過,DM proxy「用 subject Character 規則計算」在 roll 層也沒有證據,Group Check 只有輸入格式驗證,且 P3-C 沒有任何 test_p3c_postgres_*.py,CI 的 PostgreSQL job 對本 Subphase 零覆蓋。補上 test_p3c_roll_group_and_authorization.py(5 條,真 repository)與 test_p3c_postgres_rolls.py(3 條,真 PostgreSQL),並把後者接進 CI job。

Boundary

app.domain.rooms.rollsapp.domain.rooms.pending_actionsapp.domain.rooms.table_character_stateapp.persistence.rooms.p3c_*app.api.rooms.p3c_* 全部含 .rooms. 區段,落在 test_m03_import_boundary.py 既有的 FORBIDDEN_MODULE_RE 內。test_m03d_schema_parity.pyFORBIDDEN_MULTIPLAYER_TABLES 已加入 roll_groupsroll_requestsroll_resultspending_actions,SQLite character track 不會被灌進這四張表。

新增的 roll / check capability 由 test_p3c_capability_contract.py::test_p3c_roll_check_capability_is_web_only 確認 standalone 側維持 false。

Character Current State 的寫入沒有回頭呼叫只吃 Human RoomAccessContext 的 P2 live_character_write_scope(),而是在同一 transaction 內鎖 session_participants 並重驗 subject binding;這是實作規格第 14 條要求的 actor-neutral 形狀,P3-D 會把兩條授權入口收斂成同一個 typed resolver。

已知限制

Handoff

P3-C 已完成並關門。下一步是 P3-D — AI Controller, Scoped Token & Handoff

P3-D 直接繼承以下 substrate,不得再造第二套: