Skip to content

Commit 8b9347a

Browse files
committed
docs(plan): record the last three, which had three different causes
Executes candidate 5 from the parallel research unit and writes down what each remaining failure actually was. The server-auth loop tests were mine to catch and I missed two of three: the same file held a five-harness and a six-harness version of the case I fixed last round. WS-REBIND-01 was never a budget overrun. It died at 748ms against its own hardcoded 1s deadline while the test budget was 5s, and an internal deadline shorter than the budget fails faster than a timeout -- which is exactly why it read as random rather than slow. executeArchivedCleanup is not ours. It runs in 7.16ms locally and blew a 20s budget on CI, three orders of magnitude apart, with no sleep or spawn anywhere in it. Also records what I could not prove: the harness never resets the process-global WebSocket registry, and I could not reproduce a failure from that locally even after leaking a socket deliberately. It is in as a defensive reset and labelled that way, because writing it up as a found-and-fixed leak would put the next reader on false footing.
1 parent e78fff1 commit 8b9347a

1 file changed

Lines changed: 65 additions & 0 deletions

File tree

devlog/_plan/260731_pr_issue_triage_round/050_windows_ci_flake_rca.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,68 @@ macOS의 claude 핑 테스트도 같은 계열이다. 90ms 침묵에 25ms 핑
192192

193193
두 문서를 합치지 않고 나란히 둔다. 이 문서는 **이 라운드에서 무엇을 고쳤는지**
194194
기록이고, 그 문서는 **모드 분류와 개선 후보**의 조사다. 목적이 다르다.
195+
196+
## 마지막 라운드 — 남은 세 건과 정책화
197+
198+
`000_research.md`의 개선 후보 5번(예산 정책화)을 실행했다. 그 전에 남은 실패 세 건을
199+
각각 확인했는데, **셋 다 원인이 달랐다.**
200+
201+
### 1. server-auth 루프 테스트 — 내가 한 번 놓쳤다
202+
203+
이전 라운드에서 하니스 4개짜리 하나를 고쳤는데, 같은 파일에 **5개짜리와 6개짜리가
204+
더 있었다.** `startPoolRetryHarness()`는 호출마다 `TEST_DIR`를 지우고 다시 만들고,
205+
서버를 띄우고, 전역 `fetch`를 리다이렉트한다. 그걸 다섯 번 하면 5초 안에 안 끝난다.
206+
207+
둘 다 body 커서를 쓰는 단일 하니스로 바꿨다. 각 케이스는 여전히 자기 원본 400과
208+
`acct-pool-a` 단일 dispatch를 증명한다.
209+
210+
### 2. WS-REBIND-01 — 예산 초과가 아니었다
211+
212+
748ms에 죽었다. 5초 예산에 748ms면 타임아웃이 아니다. 테스트 안에 **자체 1초 데드라인**
213+
있었다:
214+
215+
```
216+
setTimeout(() => reject(new Error("websocket retry timed out")), 1_000);
217+
```
218+
219+
실제 서버를 멈췄다 다시 띄우고, WebSocket을 열고, 계정 간 2홉 재시도를 기다리는 데
220+
1초다. 부하 걸린 러너에서는 낙관적이다.
221+
222+
**내부 데드라인이 테스트 예산보다 짧으면 타임아웃보다 빨리 죽어서 로직 에러처럼 보인다.**
223+
이게 이 건이 유독 무작위처럼 보였던 이유다. 헬퍼에 `INTERNAL_DEADLINE_MS`로 이름을 붙였다.
224+
225+
덤으로 하니스가 **WebSocket 레지스트리를 안 지운다**는 것도 찾았다. 프로세스 전역이고
226+
teardown에서 살아남는데, WS-REBIND-01은 계정별 정확한 소켓 개수를 단언한다. 다섯 가지
227+
상태를 지우면서 이것만 빠져 있었다.
228+
229+
다만 **로컬에서 재현은 못 했다.** 리셋을 빼고 돌려도, 소켓을 하나 흘려놓고 돌려도
230+
통과했다. 그래서 커밋에 "방어적 조치이고 재현된 수정이 아니다"라고 적었다. 재현 못 한 걸
231+
고쳤다고 쓰면 다음 사람이 잘못된 근거 위에서 판단한다.
232+
233+
### 3. executeArchivedCleanup — 우리 문제가 아니다
234+
235+
**이미 20초를 받고 25052ms에 죽었다.** 20초를 넘기는 테스트는 예산 문제가 아니라는
236+
신호라 먼저 재봤다.
237+
238+
```
239+
(pass) executeArchivedCleanup > permanent cleanup removes logs, goals, and memory rows … [7.16ms]
240+
```
241+
242+
로컬 **7.16ms**, CI **25052ms**. 3500배 차이다. sleep도 spawn도 없는 동기 SQLite
243+
작업이다. 테스트가 느린 게 아니라 러너가 그 순간 그만큼 느렸다는 뜻이다.
244+
245+
숫자를 다시 올리는 대신 `STORE_BUDGET_MS`로 묶었다. 이 파일에만 흩어진 리터럴이
246+
22개였다.
247+
248+
## 정책 — `tests/helpers/test-budget.ts`
249+
250+
예산을 올린 커밋이 네 개가 되도록 매번 숫자를 따로 정하고 있었다. 스위트 전체에
251+
timeout 리터럴이 52개다. 정당한 예산과 침묵당한 플레이크를 구분할 방법이 없다.
252+
253+
헬퍼는 **숫자가 아니라 규칙**을 적는다. 둘 다 참일 때만 예산을 올릴 수 있다:
254+
255+
1. 그 기다림이 테스트가 단언하는 것에 본질적이다.
256+
2. **ablation이 여전히 실패한다.**
257+
258+
1번만 있고 2번이 없으면 무력한 테스트를 고정시키는 것이다. `020`에서 지적한 바로 그
259+
실패 모드다.

0 commit comments

Comments
 (0)