adventure-table

P3-B Closeout Checklist

P3-B — Exploration, Chat & Actions closeout scope。編號對應 實作規格 的「完成後必須為真」。

Verification evidence

分支 p3-b-exploration-chat-actions

Alembic heads
  0015_character_state_revision (character) (head)
  0017_p3b_exploration_stage   (web)       (head)
  branchpoint 0008_m03c_import_records -> 0009 (character) / 0010 (web)

本機指令
  apps/server  pytest                       1158 passed, 20 skipped (394.66s)
  apps/web     npm test -- --run            55 files / 255 passed
  apps/web     npm run build                pass(既有 chunk-size 警告)
  repo root    docker compose config        pass
  apps/web     npm run test:e2e:docker      113 tests:110 passed, 3 skipped (8.0m)
                                            第二輪 xge-less m03c-character-import.spec.ts:7 passed

GitHub Actions
  P3 Non-E2E  run 34326610019  @ 77bf7c6  綠
    backend / frontend / postgres-migrations / windows-standalone 四個 job 全綠

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

postgres-migrations job 明列並實際執行 test_p3b_postgres_stage.py(五條:index 加 atomic stage event、stale DM rollback、message/cursor idempotent replay、stale actor rollback、projection failure rollback),以及既有的 test_p3a_postgres_events.pytest_p2a_postgres_migration.pytest_p2b_postgres_workspace.pytest_p2e_postgres_sessions.pytest_p2f_postgres_seat_selection.pytest_postgres_roster_lock_order.pytest_m03c_migration.py;這些檔案在本機沒有 P3_POSTGRES_URL / P2_POSTGRES_URL 時會 skip,唯一的真 PostgreSQL 證據來自該 job。

P3 Non-E2E 的最後一次綠燈在 77bf7c6,之後的 commit 只改動 E2E spec、backend 測試、RoomSessionPage 的 banner 呈現與一條 CSS。其中 backend 與前端單元測試面已由本機全套覆蓋,E2E 面由上表的全套執行覆蓋。

關門過程中修正的問題

  1. 兩條 real-backend journey 從未綠過。 B1 與 B2 都死在 Playwright strict mode,不是桌面行為出錯。getByLabel 比對的是外層 label 的文字,而 composer 兩個 select 的 label 文字包含全部 option 標籤;Character 又同時命中可調整版面時加入的 aria-label="Table Characters" 區塊。改用 role 加 exact accessible name。reload 後的 Stage 斷言是同一類問題的另一面:DM 的 Stage 編輯器會從已存的 Stage 回填,於是同一段文字同時出現在畫布段落與 textarea,該行改為 scope 到 .session-stage__canvas
  2. gate 4 與 gate 21 缺精確證據。 原本只證明 Player 被拒與 access session 被 revoke 的 stale DM 被拒,沒有「持 Room DM Key、但不是本場固定 DM Controller」這個 actor。補了 test_dm_key_holder_who_is_not_the_session_dm_cannot_change_the_stagetest_dm_key_holder_who_is_not_the_session_dm_cannot_proxy_or_narrate,後者同時證明該 actor 用自己的 Seat 發言仍然成功,所以拒絕的是桌上權限而不是可達性。
  3. P3-A 留下的兩項 banner 呈現取捨。 致命錯誤原本同時渲染 generic error banner 與 disconnected banner,敘述同一件事;重新連線中的暫時狀態沿用 error-banner 告警樣式。現在 onFatal 不再另設 generic error banner,reconnecting 改用新的 .notice-bannerrole="status",fatal 維持 error-bannerrole="alert"RoomSessionPage.test.ts 加上對應斷言。

Boundary

app.domain.rooms.explorationapp.persistence.rooms.exploration*app.api.rooms.exploration 全部含 .rooms. 區段,落在 test_m03_import_boundary.py 既有的 FORBIDDEN_MODULE_RE 內,standalone 匯入圖不會碰到它們。test_m03d_schema_parity.pyFORBIDDEN_MULTIPLAYER_TABLES 已加入 session_messagesroom_stage_imagessession_stages,SQLite character track 不會被灌進這三張表。

P3-B 沒有新增 capability flag:Stage 與 Chat 都掛在 P3-A 已有的 table_runtime 之下,standalone 該旗標本來就是 false,沒有出現需要單獨開關的新 web-only 能力。

已知限制

Handoff

P3-B 已完成並關門。下一步是 P3-C — Roll, Check & PendingAction

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