本檔記錄已確認、但決定暫不處理的問題。每一項都要寫清楚:症狀、根因(或已排除的假設)、影響範圍、暫時處置、以及重啟處理的條件。
已修好的問題不留在本檔,移除即可;歷史從 git log 追。
最後更新:2026-09-09
狀態:未處理,待 diagnose
發現:2026-09-04,M03-D 驗證期間跑全套 E2E;2026-09-06 P2-A 驗證期間確認影響範圍不只 P1-D
範圍收窄:2026-09-07,P2-D 驗證期間。本編號原本涵蓋兩種表現面,其中「即時摘要沒刷新」已找到根因並修復(3439cbe),該半已依本檔開頭的規則移除。剩下的「revision 不推進」仍未確認,沿用本編號以免既有 closeout 的引用失效。
位置:
apps/web/e2e/m01e-half-elf-variants.spec.ts:60(waitForDraftRevision),M01-E Wood Elf descent Fleet of Foot persists 35 ft walking speedapps/web/e2e/m01m-mtf-tiefling.spec.ts:77(waitForDraftRevision),M01-M Zariel bloodline replaces the standard Tiefling packages
疑似同族但證據不足:apps/web/e2e/m01j-subclass-expansion.spec.ts:394,2026-09-06 全套失敗但沒留下 error-context。
疑似同族但簽章不同:apps/web/e2e/m01i-optional-features.spec.ts:412,M01-I existing Fighter levels up with Martial Versatility and preserves Version History。2026-09-14 U01-A 關門第一次完整 run(isolated server-e2e / adventure_table_e2e)失敗,簽章是 expect(page).toHaveURL(/\/character-builder\/…/) 5000ms 逾時、頁面停在 Room /characters,即 Level Up 點擊後 Draft 建立/導向超過 5 秒;當下 E2E DB 已累積 24 隻角色。同 wrapper 單跑 5 / 5 過(該案 13.3s),第二次完整 run 通過。它不是 waitForDraftRevision,只是「單次 5 秒等待真的等滿」的同一類;歸入本編號前仍需 diagnose 確認是否同根因。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.ts 的 timeout: 60_000 與 m01k: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),唯一能滿足它的方式就是版號真的前進。
未確認。 已知的觀察與已排除的假設:
apps/web,且 e2e-docker.mjs 只重建 web service。5907a80 全套即有 m01m 以本簽章失敗,而 P2-A 對這些 spec 的 diff 只有 Room bootstrap 的 import 與導覽。character-builder.spec.ts 曾是唯一只以 expect(getByText('Saved on server')).toBeVisible() 當存檔完成條件的 Builder spec,該文字分不出這次與上一次存檔,斷言立刻通過而後續斷言撞上尚未重算的摘要。已於 3439cbe 改用版號等待,3 跑 3 敗變 5 跑 5 過。m01e / m01m 從一開始就用版號等待,因此該修正對它們是 no-op,兩者是不同的問題。rooms 累積到 331 筆時 character-builder 連三次全敗;清空殘留後回到偶發水準。該次觀察屬已修的那一半,對本簽章是否同樣成立未驗證。character-builder 單跑,本簽章一次都沒出現。因此無法確認目前是否仍然存在。e2e-docker.mjs 的第二輪(拿掉 xge 的 M03-C import 子集)完全不執行。不改測試碼、不標 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/ 可以避免同類遺漏再次發生,但那是獨立的重構,不屬於本編號的修復範圍。
狀態:暫停執行(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:直接高等級創角 == 逐級升等)已由後端整合測試覆蓋且穩定通過:
apps/server/tests/test_m01j_level_up_choice_guard.py該檔直接打真實 API 走完「開 Level Up 草稿 → 升到 3 級 → 選符文騎士 → 存入兩個符文 → Confirm」,並驗證邊界(不可抽掉先前選項、不可靜默清空、歷史等級選項仍凍結)。
該測試標記 test.fixme(),後續不執行。標記處註明本條編號。
要恢復這條測試,需要先把填值方式從「掃描 + 選第一個」改成明確驅動——例如比照 fillExactSpellBuckets,用 n / m 計數器判斷完成條件,並對每個子職業選項指名選取而非取第一個可選項。在那之前不要只是重跑。
狀態:已有可靠替代做法;Windows dev server 本身的問題屬上游未修 發現:2026-09-02 根因定案:2026-09-02(先前記載過三個錯誤根因,見「作廢記錄」) 影響:本機 Windows 開發環境;Linux 容器與 CI 未觀察到
在 Windows 上讓 Playwright 託管 vite(playwright.config.ts 的 webServer)跑整套,會在隨機時間點崩掉,出現 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 拒絕。
三項直接量測:
SIGTERM / SIGINT / SIGHUP / SIGBREAK / exit / beforeExit / uncaughtException / unhandledRejection / stdin end / close 全裝,外加每秒從 event loop 內部寫檔的 heartbeat)重現兩次:log 裡除了啟動兩行只有 heartbeat,沒有任何 handler 觸發過。heartbeat 停止的時刻就是 vite 停止服務的時刻。DEBUG=pw:webserver 全程只有 WebServer available(開頭)與 Terminating the WebServer(整套跑完之後),中間 3.4 分鐘、53 個 refused,Playwright 一次都沒動手。Windows 會靜默限制 listen backlog,佇列滿時 client 收到的是 WSAECONNREFUSED(Microsoft 文件),所以「連線被拒」代表的是 accept 停擺,不是 server 消失。
上游有同樣症狀且未修的回報:
preview。以同碼異 OS 對照排除:同一份 apps/web/src、同一批 spec、同一個 backend、同一個 Windows Chromium,只把 vite process 從 Windows 換到 Linux 容器(docker compose 的 web 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;此路徑尚未在本機驗證過)。
本條先前記載過三個錯誤根因,都已被上述量測推翻,不要再引用:
DEBUG=pw:webserver 推翻。