adventure-table

已知問題

本檔記錄已確認、但決定暫不處理的問題。每一項都要寫清楚:症狀、根因(或已排除的假設)、影響範圍、暫時處置、以及重啟處理的條件。

已修好的問題不留在本檔,移除即可;歷史從 git log 追。

最後更新:2026-09-09


KI-P1D-001 — Builder Draft revision 偶發不推進

狀態:未處理,待 diagnose 發現:2026-09-04,M03-D 驗證期間跑全套 E2E;2026-09-06 P2-A 驗證期間確認影響範圍不只 P1-D 範圍收窄:2026-09-07,P2-D 驗證期間。本編號原本涵蓋兩種表現面,其中「即時摘要沒刷新」已找到根因並修復(3439cbe),該半已依本檔開頭的規則移除。剩下的「revision 不推進」仍未確認,沿用本編號以免既有 closeout 的引用失效。

位置

2026-09-09 曾誤把兩支 M01-K 失敗歸入本編號,已查明並排除。 那兩支的簽章是 Test timeout of 30000ms exceeded(整支測試用完 Playwright 預設的單測預算),不是本編號的 Timeout 5000ms exceeded while waiting on the predicate(單次 poll 等滿仍未推進)。兩者的錯誤輸出裡都會出現 expect(...).toBeGreaterThan 加一個沒有前進的 revision,但意義相反:前者只表示截止當下還沒觀察到下一版。實測把 test timeout 拉到 180 秒後,m01k:392 3/3 通過、每次 32.5–33.8 秒(run 34298326870),確認是測試耗時略超過 30 秒上限,與 Draft autosave 無關。修法是 playwright.config.tstimeout: 60_000m01k:342 的角色名稱唯一化,不屬於本編號。

歸類本編號前必須先分辨這兩種逾時。 只看 waitForDraftRevision 出現在 stack 上並不足夠。

症狀

選項已送出,.builder-save-state 顯示的 revision 停在前一個值,等滿 5 秒逾時:

expect(received).toBeGreaterThan(expected)
Expected: > 2
Received:   2
Timeout 5000ms exceeded while waiting on the predicate

這是真的沒推進,不是等待條件寫錯。受影響的 spec 都用 expect.poll(() => currentDraftRevision(page)).toBeGreaterThan(previousRevision),唯一能滿足它的方式就是版號真的前進。

根因

未確認。 已知的觀察與已排除的假設:

影響範圍

暫時處置

不改測試碼、不標 test.fixme()、不提高 timeout。

屬於本編號的簽章只有一種:waitForDraftRevision 逾時。若該次全套 E2E 的失敗全部屬於本編號,視為「除本編號外通過」,據以放行該 Subphase 關門;只要出現任何其他簽章就不得放行。放行紀錄必須寫明失敗的 spec、行號與簽章。

跑全套前先讓 e2e-global-setup.mjs 清乾淨資料殘留,可壓低發生機率。

重啟條件

本編號目前沒有重現案例,重啟的第一步是先把重現率拉高,否則沒有可用的 feedback loop:連續重跑受影響的兩支 spec、堆高 rooms / drafts 殘留、或在存檔路徑注入延遲以拉寬競態窗口。

重跑時請用 --repeat-each 搭配 --reporter=list,前者拉高重現率,後者印出每次的耗時——2026-09-09 的 M01-K 誤判正是因為沒有耗時資料,把「時間用完」讀成「revision 不推進」。Windows 本機不要走 Playwright 託管 vite 的路徑,見 KI-ENV-001。

有重現之後追 Draft autosave:一次選項送出後,.builder-save-state 的 revision 為何可能不推進。修好之前不要只是提高該處 timeout——那會把競態藏起來而不是修掉。

一併看 KI-M01J-001:它記錄的「某些控制項第一次點擊後草稿 revision 不會在逾時內遞增」與本編號同族,修好本項後應重新評估該項是否還需要 test.fixme()

順帶記錄的結構問題

十二支 Builder spec 各自複製了一份 currentDraftRevision / waitForDraftRevision / chooseOption,而且至少有兩種變體(toHaveText('Draft revision N+1')expect.poll(...).toBeGreaterThan())。character-builder 會落單漏掉版號等待,正是因為沒有共用入口。把這些收進 e2e/support/ 可以避免同類遺漏再次發生,但那是獨立的重構,不屬於本編號的修復範圍。


KI-M01J-001 — M01-J 直創/逐級升等等價 E2E 不穩定

狀態:暫停執行(test.fixme()發現:2026-09-02,M01-J 批二(G6/G7 E2E) 位置apps/web/e2e/m01j-subclass-expansion.spec.ts,測試名稱 M01-J direct high-level create matches sequential level up

症狀

同一條測試,同樣的程式碼,跑起來時好時壞。失敗時停在 Review:

invalid_subclass_choice_count
  path: draft_payload.choice_selections.m01-j:tce:rune-knight:rune-carver
  message: Rune Knight requires exactly 2 selections; got 0.

符文雕刻(Rune Carver)在 Level Up 路徑上沒有被自動填入。

根因

這是測試 harness 的問題,不是產品缺陷。

該 spec 用一套通用的「掃描所有空的下拉選單、選第一個可選項」邏輯來填必填項。這套邏輯在 Create 路徑穩定,在 Level Up 路徑不穩定:某些控制項第一次點擊後,草稿 revision 不會在逾時內遞增,掃描迴圈因而放棄該控制項並繼續,留下未填的必填選項。加入重試後仍未穩定。

同一份契約(J.8:直接高等級創角 == 逐級升等)已由後端整合測試覆蓋且穩定通過:

該檔直接打真實 API 走完「開 Level Up 草稿 → 升到 3 級 → 選符文騎士 → 存入兩個符文 → Confirm」,並驗證邊界(不可抽掉先前選項、不可靜默清空、歷史等級選項仍凍結)。

影響範圍

暫時處置

該測試標記 test.fixme()後續不執行。標記處註明本條編號。

重啟條件

要恢復這條測試,需要先把填值方式從「掃描 + 選第一個」改成明確驅動——例如比照 fillExactSpellBuckets,用 n / m 計數器判斷完成條件,並對每個子職業選項指名選取而非取第一個可選項。在那之前不要只是重跑。


KI-ENV-001 — Windows 上的 vite dev server 會在跑測試途中停止接受連線

狀態:已有可靠替代做法;Windows dev server 本身的問題屬上游未修 發現:2026-09-02 根因定案:2026-09-02(先前記載過三個錯誤根因,見「作廢記錄」) 影響:本機 Windows 開發環境;Linux 容器與 CI 未觀察到

症狀

在 Windows 上讓 Playwright 託管 vite(playwright.config.tswebServer)跑整套,會在隨機時間點崩掉,出現 40~56 個失敗,其中絕大多數是:

net::ERR_CONNECTION_REFUSED at http://127.0.0.1:4173

第一個失敗通常是草稿 revision 逾時,之後全部是連線被拒。崩潰的時間點與測試編號都會漂移(觀察到 2 秒、22 秒、3.4 分鐘)。

根因

Windows 上的 vite dev server 會停止處理事件迴圈,連線因而被 Windows 的 listen backlog 拒絕。

三項直接量測:

  1. process 沒有結束。 以 instrumented server(SIGTERM / SIGINT / SIGHUP / SIGBREAK / exit / beforeExit / uncaughtException / unhandledRejection / stdin end / close 全裝,外加每秒從 event loop 內部寫檔的 heartbeat)重現兩次:log 裡除了啟動兩行只有 heartbeat,沒有任何 handler 觸發過。heartbeat 停止的時刻就是 vite 停止服務的時刻。
  2. Playwright 沒有殺它。 DEBUG=pw:webserver 全程只有 WebServer available(開頭)與 Terminating the WebServer(整套跑完之後),中間 3.4 分鐘、53 個 refused,Playwright 一次都沒動手。
  3. 崩潰當下 socket 仍在 LISTEN、process 仍存在、handle 數正常,但 HTTP 已不回應。

Windows 會靜默限制 listen backlog,佇列滿時 client 收到的是 WSAECONNREFUSEDMicrosoft 文件),所以「連線被拒」代表的是 accept 停擺,不是 server 消失。

上游有同樣症狀且未修的回報:

這不是本專案的程式碼

同碼異 OS 對照排除:同一份 apps/web/src、同一批 spec、同一個 backend、同一個 Windows Chromium,只把 vite process 從 Windows 換到 Linux 容器(docker composeweb service,5173),整套一次跑連續四輪全綠、零 refused、每輪 4.2 分鐘。Windows 上同樣的東西 0/4。

另外已檢查:apps/web/src 沒有任何 import.meta.env、沒有輪詢 / refetchInterval / EventSource;e2e spec 只有一處 newPage() 且有 close(),全套共 52 次 goto,無 context 洩漏;失敗輪次的 vite 輸出沒有 dep re-optimization、沒有 reload、沒有任何 [vite] 錯誤。

已量測排除的假設

假設 量測結果
本專案 frontend / spec 的缺陷 同碼在 Linux 容器 4/4 全綠,Windows 0/4
Playwright webServer 生命週期 DEBUG=pw:webserver 證明中途未終止子行程
vite stdin EOF → SIGTERM 對 webServer 設 CI=true 無效,且 stdin handler 從未觸發
vite CLI 快捷鍵(q + Enter) Playwright 給的是 pipe,stdin.isTTY=undefined,快捷鍵未綁定
vite process 資源耗盡 死前 handle / thread / memory / 連線數全部持平
單一 vite process 存活時間門檻 曾在第 2 秒就崩,也曾撐滿 4.7 分鐘
整機 TCP 連線數過高 兩個半套各衝到約 4700 / 5010 條仍全綠
TCP 埠耗盡 峰值 rport=4173 TimeWait 679、rport=8000 TimeWait 117,遠低於 Windows 動態埠上限 16384
記憶體不足 全程 18 GB free
Chromium 行程洩漏 跑完殘留 0
後端掛掉或變慢 每次崩潰後 8000 /ready 仍為 200
某些測試本身壞掉 拆兩批與 Docker 路徑皆為 71 passed / 1 skipped
M01-J 造成的回歸 git diff 7f3873d..HEAD -- apps/web 只有兩個 e2e spec 檔,apps/web/src 零改動

關於 M01-J:M01-J 期間才第一次觀察到,但 M01-J 沒有動任何 frontend 程式碼。最後一次整套一次跑成功的紀錄是 M02-H closeout 的 37 passed;其後套件長到 72,跑得夠久才踩到這個 Windows dev server 缺陷。M01-J 是壓垮的最後一根稻草,不是來源。

處置

整套 E2E 走容器裡的 dev server。 先清空 character_states / character_versions / characters / character_build_drafts 並重新 seed fixture,然後:

cd apps/web && npm run test:e2e:docker

該 script(apps/web/scripts/e2e-docker.mjs)會先 docker compose up -d --build web,等 5173 可用,再以 PLAYWRIGHT_BASE_URL=http://127.0.0.1:5173 執行 Playwright。額外參數用 -- 傳遞,例如 npm run test:e2e:docker -- --reporter=line

--build 不是可選的。 web service 沒有掛 bind mount,原始碼是 build 進 image 的;略過重建會靜默測到上一版 frontend。script 把重建綁在一起就是為了堵這個坑。

備援做法(已驗證):讓 Playwright 託管、但拆兩批執行,每批之間重新 reset。批一為 app-shell / character-builder / character-sheet / m01c / m01d / m01e / m01f / m01g / m01i(35 tests);批二為 m01j / m02a / m02b / m02f / m02g / m02h-bilingual / m02h-localization / p1f / p1g / p1h(37 tests)。

不要用單一次 Windows 託管的整套指令結果宣稱全綠,也不要寄望重跑會過。

附帶觀察

p1f-character-creation / p1g-level-up 曾在整套最後段間歇失敗,症狀是 Review 沒有出現「No blocking issues」或 Confirm & Create Character 停在 disabled(docs/M02/M02-H_CLOSEOUT.md 也記過類似現象並歸因多 worker)。此症狀在 Windows dev server 路徑出現 2/2,在 Docker 路徑 0/4。目前判斷它是同一個 dev server 問題的下游症狀,不另立條目;若在 Docker 路徑再度出現,才需要獨立追查。

重啟條件

Windows dev server 本身的缺陷屬上游未修,本專案不打算追。若要恢復在 Windows 上一次跑整套,需要上游修好 #11644,或改成 build + vite preview(需補 preview.proxy,目前 vite.config.ts 只有 server.proxy;此路徑尚未在本機驗證過)。

作廢記錄

本條先前記載過三個錯誤根因,都已被上述量測推翻,不要再引用:

  1. 「整機 TCP 連線爬到 2700 把 vite 灌爆」——兩個半套各衝到 4700 / 5010 仍全綠。
  2. 「TIME_WAIT 僅 7,據此排除埠耗盡」——量測範圍抓錯,實際是幾百;埠耗盡的結論仍成立但理由不同。
  3. 「單一 vite process 撐不過約 3 分鐘」與「Playwright webServer 生命週期收掉子行程」——前者被 4.7 分鐘存活推翻,後者被 DEBUG=pw:webserver 推翻。