Skip to content

test: budget every sub-floor internal wait that gates on a child or server (Windows stack 5/5) - #3558

Merged
lidge-jun merged 4 commits into
devfrom
codex/win-5-wait-budgets
Sep 5, 2026
Merged

test: budget every sub-floor internal wait that gates on a child or server (Windows stack 5/5)#3558
lidge-jun merged 4 commits into
devfrom
codex/win-5-wait-budgets

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Summary

Windows suite stabilization, PR 5 of 5 (stacked on #3555). Budgets every internal wait in tests/ that gates on a spawned child or a live server, as a class.

Five dispatches of this stack on windows-latest failed on five different literal deadlines and zero assertions: 15 s and 20 s case budgets (#3550), then on run 33930757649 a 10 s waitFor default in codex-write-lock and a 30 s per-request abort in codex-composed-acceptance — both of which had passed on the four previous runs. A spawned Bun child boots in 8-19 s on that runner with 2× run-to-run variance, so each run samples a new wait sized below that floor. Fixing them one per 25-minute cycle cannot converge; the class had to be enumerated once.

devlog/_plan/260905_windows_suite_stabilization/080_run_variance_residuals.md inventories 58 literal deadlines under tests/ and classifies them by what they gate on. This PR changes class A (child-boot markers) and class B (server round-trips, worker lifecycle): helper defaults (waitFor, waitForPath, waitForPort, waitForLiveWorker, waitForIdle) and inline deadlines go through watchdogMs(), which keeps the local number and applies the CI/platform floor (45 s on Windows). The held-request in composed-acceptance gets SERVER_BUDGET_MS * 2 because it spans a startup plus a held gather, not one round-trip. Class C (deliberately short: 500 ms /healthz probes, refused-connection checks, in-process polls) is untouched; class D (ambiguous 2-5 s deadlines) is listed for a later pass.

No product change. Twelve test files.

Verification

  • bun run typecheck clean
  • bun test on the three codex-integration files run in isolation: 59 pass
  • A full local sweep of all twelve files was blocked by another session holding the repository's user test lock; the CI dispatch is the gate
  • CI run 33933578890 dispatched on this head; the acceptance bar is two consecutive all-green Windows runs

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Summary by CodeRabbit

  • Bug Fixes

    • Improved test reliability on slower Windows environments by applying consistent timing budgets to process startup, server responses, locks, and worker lifecycle checks.
    • Prevented storage policy tests from silently passing when cleanup jobs fail to return to an idle state.
    • Adjusted held-request timing to accommodate both internal processing and server response delays.
  • Documentation

    • Added planning documentation covering Windows test timing residuals and budget guidelines.

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 5, 2026 00:37
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Team

Run ID: 5e5bbd4b-384a-47ae-b514-577bafe6b8e9

📥 Commits

Reviewing files that changed from the base of the PR and between e6ed7a4 and 3201181.

📒 Files selected for processing (19)
  • devlog/_plan/260905_windows_suite_stabilization/080_run_variance_residuals.md
  • tests/codex-integration/codex-composed-acceptance.test.ts
  • tests/codex-integration/codex-history-lock.test.ts
  • tests/codex-integration/codex-history-worker.test.ts
  • tests/codex-integration/codex-inject-write-lock.test.ts
  • tests/codex-integration/codex-prompt-route.test.ts
  • tests/codex-integration/codex-prompt-text-probe.test.ts
  • tests/codex-integration/codex-retained-root-serialization.test.ts
  • tests/codex-integration/codex-shim.test.ts
  • tests/codex-integration/codex-write-lock.test.ts
  • tests/codex-integration/native-profile-manager.test.ts
  • tests/codex-integration/native-profile-startup.test.ts
  • tests/helpers/storage-policy-api.ts
  • tests/oauth/oauth-refresh-lock-multiprocess.test.ts
  • tests/server/server-background-lifecycle.test.ts
  • tests/storage/storage-mutation-race.test.ts
  • tests/storage/storage-policy-job-responsive.test.ts
  • tests/storage/storage-worker-lifecycle.test.ts
  • tests/storage/storage-worker-teardown-isolate.test.ts

📝 Walkthrough

Walkthrough

The changes replace Windows-sensitive literal timeouts with shared internal and enclosing test budgets across integration, server, storage, OAuth, and helper tests. The storage policy responsiveness test now asserts that the job reaches the expected idle state.

Changes

Windows timing budget stabilization

Layer / File(s) Summary
Timing residual analysis and correction plan
devlog/_plan/260905_windows_suite_stabilization/080_run_variance_residuals.md
The plan inventories literal deadlines, classifies timeout sites, records review blockers, and defines the corrected internal and enclosing budget assignments.
Integration test budget wiring
tests/codex-integration/*, tests/oauth/oauth-refresh-lock-multiprocess.test.ts
Child-marker waits and helper defaults use INTERNAL_DEADLINE_MS. Spawn-based test timeouts use SPAWN_BUDGET_MS. The held /api/sync request uses HELD_REQUEST_BUDGET_MS.
Storage and server wait defaults
tests/helpers/storage-policy-api.ts, tests/server/server-background-lifecycle.test.ts, tests/storage/*
Worker-backed wait helpers use INTERNAL_DEADLINE_MS. The responsive job test now fails when the expected idle state is not observed before the deadline.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: ingwannu

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/win-5-wait-budgets

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the chore Maintenance, CI, tests, refactors, or build changes (not a user-facing bug or feature). label Sep 5, 2026
@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 66 / 80

이 PR은 Windows 스위트 안정화 스택의 다섯 번째(제목 기준 5/5) 조각입니다. 베이스는 dev가 아니라 #3555 헤드(codex/win-4-quorum-observer)이고, 제품 src/는 한 줄도 안 건드립니다. 지금 dev HEAD는 4dde2db97(패키지 2.43.0, 최근 #3543 OpenAI usage 견적을 API 가격표로 맞춤)입니다. 앞에서 #3550이 케이스 예산(15s/20s)을 올렸고 #3555가 quorum-cache 관측을 atime에서 spy로 바꿨는데도, windows-latest에 스택을 다섯 번 돌리면 매번 다른 내부 대기가 먼저 터졌습니다. 이번엔 run 33930757649에서 codex-write-lockwaitFor 기본 10초와 codex-composed-acceptanceSERVER_BUDGET_MS(30초) Abort가 빨갰고, 둘 다 단언 실패가 아니라 타임아웃이었습니다.

왜 한 곳씩 고치면 안 되냐면, 그 러너에서 Bun 자식이 마커를 쓰기까지 대략 8–19초가 걸리고 실행마다 어느 대기가 그 바닥 아래로 걸리는지가 바뀌기 때문입니다. 25분짜리 디스패치를 돌릴 때마다 “다음 잔여”만 고치면 c-1(연속 두 번 전녹)에 영원히 못 닿습니다. 그래서 이 PR은 tests/ 아래 리터럴 마감 58곳을 읽어 A(자식 마커)/B(서버·워커)/C(일부러 짧은 것)/D(애매)로 나누고, A·B만 watchdogMs()로 올립니다. watchdogMs는 로컬 숫자는 그대로 두고 CI에서만 플랫폼 바닥(Windows 45초, 그 외 30초)을 씌우는 기존 헬퍼입니다(tests/helpers/ci-watchdog.ts). held /api/syncSERVER_BUDGET_MS * 2로 둔 이유도 문서에 분명합니다. 그 요청은 “한 번의 왕복”이 아니라 “스타트업 + 붙잡아 둔 gather + OFF 왕복”이라 형제 케이스가 47–58초에 통과하는 동안 30초 Abort만 먼저 울렸기 때문입니다.

손댄 파일은 통합·oauth·server·storage 테스트 12개와 계획서 devlog/_plan/260905_windows_suite_stabilization/080_run_variance_residuals.md입니다. 헬퍼 기본값(waitFor / waitForPath / waitForPort / waitForLiveWorker / waitForIdle)을 바꾸면 호출부 전부가 상속받도록 했고, C류(500ms /healthz 프로브, 거절 연결, 인프로세스 폴)는 그대로 두었습니다. 로컬로는 codex-integration 세 파일 격리 59통과 typecheck 통과를 적었고, 전체 스윕은 유저 테스트 락에 막혀 CI 디스패치가 게이트라고 했습니다. 제품 동작·릴리즈 노트와는 무관한 테스트 예산 클래스 수정입니다.

라인 459 - tests/codex-integration/codex-write-lock.test.ts의 “one OS user…” 케이스 바깥 예산이 여전히 30_000입니다. 같은 파일의 waitFor 기본은 이제 watchdogMs(10_000)이라 Windows CI에서는 45초 바닥이 됩니다. 내부 대기가 바깥 케이스보다 길어지면 Bun이 30초에 케이스를 먼저 죽이고, 올려 둔 바닥은 쓰이지 않습니다. run 5 실패는 10.67초라 30초면 당장 숨을 쉴 수는 있어도, 주석이 말한 “플랫폼 바닥 적용”과 숫자가 어긋납니다. 케이스 예산을 SPAWN_BUDGET_MSwatchdogMs(30_000) 이상으로 맞추는 편이 이 PR의 논리와 같습니다.

경로 codex-retained-root-serialization 자식 스크립트 장벽 8초 - 080 인벤토리 A표에 :514 8초(두 자식)를 MUST로 적었는데, 실제 변경은 waitForPath 12초/16초 호출만 감쌌습니다. 남은 8초는 자식 안 globalThis.fetch가 서로 마커를 기다릴 때 Date.now() + 8000으로 박혀 있습니다. 피어 부팅이 느리면 이 장벽이 먼저 풀릴 수 있어, “A는 전부 예산” 문장과 어긋납니다. 의도적으로 자식 간 조율로 남긴 건지, 다음 패스에 watchdogMs(8_000)으로 올릴지 한 줄로 밝혀 주세요.

라인 221 근처 - tests/server/server-background-lifecycle.test.ts에서 인벤토리가 같이 묶었던 5초 폴(Date.now() + 5_000으로 job idle 대기)은 그대로이고, waitForLiveStorageWorker 기본 10초만 watchdogMs로 올랐습니다. 5초 쪽이 정말 인프로세스-only면 C로 재분류해 080에 적어 두는 편이 후속 패스가 덜 헷갈립니다.

경로 080 계획 vs 구현 - 계획 B는 SPAWN_BUDGET_MS / SERVER_BUDGET_MS / isolationBudgetMs() 이름을 쓰라고 했는데, 대부분 사이트는 watchdogMs(로컬숫자)입니다. 로컬 속도를 지키는 선택으로는 맞고, held 요청만 SERVER_BUDGET_MS * 2(고정 60초)라 Windows 바닥(45초)과도 스케일이 다릅니다. 한 클래스 수정인데 이름이 두 갈래면 다음 사람이 “어느 상수를 따르나”로 다시 흔들릴 수 있습니다.

메인테이너의 판단이 필요한 지점

너의 추천
Windows CI(특히 방금 올린 디스패치)에서 연속 전녹 두 번을 게이트로 두고, 그 전에 write-lock 케이스 바깥 예산을 내부 watchdogMs 바닥 이상으로 맞추는 작은 후속을 넣으세요. #3555가 먼저 들어간 뒤에만 이 PR을 스택 merge하세요. 클래스 D는 전녹을 본 뒤에 별 이슈로 열어도 됩니다. 제품 회귀 위험은 없습니다.

이 댓글은 grok-bot이 작성했습니다

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes on exact head 3b431b41390c57d75c6571ff73626c124ada0531.

The class-based inventory is the right direction, but the new internal budgets are not yet effective at several call sites. watchdogMs(10_000/20_000) becomes 45 seconds on Windows CI, while the enclosing tests still have explicit 30-second case timeouts. Bun will terminate the test before the helper can use its new deadline. This occurs at least in:

  • server-background-lifecycle.test.ts (live-worker wait, case timeout 30s)
  • storage-policy-job-responsive.test.ts (server/worker poll, case timeout 30s)
  • storage-worker-lifecycle.test.ts (live/idle waits, case timeouts 30s)
  • storage-worker-teardown-isolate.test.ts (live-worker wait in 30s cases)

Move the enclosing cases onto named case budgets that exceed the largest internal watchdog with cleanup/assertion headroom, or keep the inner deadline below the explicit case ceiling. Add a source/meta regression so a future class expansion cannot create inner watchdog >= enclosing test timeout again.

There is also a vacuous wait in storage-policy-job-responsive.test.ts: the loop exits when the deadline expires without asserting that the job reached the expected idle state. Raising that deadline cannot prove the lifecycle completed. Track the terminal observation and fail explicitly if it was never reached.

Finally, this PR targets codex/win-4-quorum-observer, while #3555 is still changes-requested and not on dev. Keep this stacked until the lower PRs land, then retarget/rebase onto the resulting current dev and rerun the full exact-head matrix. The stated two-consecutive-green Windows acceptance bar should be applied only after the timeout hierarchy above is valid.

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed exact head c477a02a77684eb712cad955c6b0ce19b16d3fa6. The outer/inner timeout inversion from the previous head is fixed, and the storage-policy idle poll now fails closed. Two blockers remain.

  1. The PR body and devlog record normal windows-latest child readiness at 8–19 seconds, but every migrated child/server wait now uses INTERNAL_DEADLINE_MS = 15_000. That is already below an observed successful startup, so the stated two-consecutive-green goal still cannot be met reliably. Choose an internal child-ready bound with headroom over the measured 19-second tail while keeping each enclosing case comfortably larger; do not knowingly set the fix below the data that motivated it.

  2. tests/codex-integration/codex-retained-root-serialization.test.ts still has a vacuous two-child rendezvous. The embedded route script loops until both barrier markers exist, but after the deadline it unconditionally returns a model response. If the peer starts late or never reaches the seam, the intended concurrent serialization proof runs sequentially and may pass. After the loop, explicitly fail when both markers were not observed, and include this path in the ablation/source guard promised by the test-budget policy.

Please also refresh the PR description: it still says twelve files and watchdogMs() even though this head touches nineteen test/helper files and intentionally moved the affected internal waits to INTERNAL_DEADLINE_MS. Keep the PR stacked until the lower layers land and rerun the exact-head Windows acceptance after these corrections.

@lidge-jun
lidge-jun force-pushed the codex/win-4-quorum-observer branch from fd786be to 70d9041 Compare September 5, 2026 02:39
Base automatically changed from codex/win-4-quorum-observer to dev September 5, 2026 02:39
t added 4 commits September 5, 2026 11:39
… server (Windows class fix)

Five dispatches of this stack on windows-latest failed on five DIFFERENT
literal deadlines, none of them an assertion: 15 s and 20 s case budgets
(#3550), then a 10 s waitFor default in codex-write-lock and a 30 s per-request
abort in codex-composed-acceptance on run 33930757649. Each run samples one or
two new ones because a spawned Bun child boots in 8-19 s on that runner and the
suite has many waits sized below that from local timings.

Inventory in devlog 080: 58 literal deadlines under tests/, classified by what
they gate on. This commit changes the ones that gate on a spawned child or a
live server (class A/B): helper DEFAULTS (waitFor, waitForPath, waitForPort,
waitForLiveWorker, waitForIdle) and inline deadlines now go through
watchdogMs(), which keeps the local number and applies the CI/platform floor
(45 s on Windows). The held-request in composed-acceptance gets two server
budgets because it spans a startup plus a held gather, not one round-trip.
Deliberately-short bounds (500 ms /healthz probes, refused-connection checks,
in-process polls) are untouched; class D sites are left for the next pass.

12 test files; typecheck clean. Local verification: 59 pass across the three
codex-integration files run in isolation; a full local sweep was blocked by
another session holding the user test lock, so the CI dispatch is the gate.
…E_MS inside, SPAWN_BUDGET_MS outside

Review of 3b431b4 (FAIL, 6 blockers) caught a composition error: watchdogMs()
is 45 s on Windows CI, and I had put it on INTERNAL waits inside cases budgeted
at 15-30 s. On the platform the fix targets, Bun's per-test timeout would fire
before the wait's own diagnostic - a bare timeout instead of the marker name,
which is where this unit started. test-budget.ts:64 states the invariant:
an internal deadline stays a few times under its enclosing budget.

Corrected shape, two knobs moved together:
- every child/server-gated internal wait uses INTERNAL_DEADLINE_MS (15 s),
  the repository's named in-test bound, as windows-tray and cli-models already
  do; helper DEFAULTS changed so callers inherit
- every case whose body spawns a child gets SPAWN_BUDGET_MS (45 s): the four
  15 s and three 30 s spawn cases in native-profile-manager, all four
  codex-write-lock cases, history-lock, history-worker, oauth-refresh-lock

Other blockers folded:
- retained-root :515 (8 s two-child barrier) and :555 (20 s attempt loop) were
  in my own inventory and unchanged; now INTERNAL_DEADLINE_MS / SPAWN_BUDGET_MS
- storage-policy-job-responsive fell through on expiry with no assertion, so a
  job that never went idle still passed; expect(settled) added - a real
  vacuous test independent of Windows
- composed-acceptance: SERVER_BUDGET_MS * 2 was arithmetic on a wrong model
  (the held request and the OFF mutation overlap; startup precedes both).
  Named HELD_REQUEST_BUDGET_MS = SERVER_BUDGET_MS + INTERNAL_DEADLINE_MS with
  the actual shape in its comment
- ten more A/B candidates from the reviewer's re-grep read one by one: five
  budgeted (codex-shim, codex-prompt-route, codex-prompt-text-probe,
  helpers/storage-policy-api, storage-mutation-race), five left with a reason
  each in devlog 080 (in-process polls, an asserted-latency bound, a preflight
  whose failure is a skip)

Ablation (test-budget.ts rule 2), both run on macOS with the lock free:
- history-lock: holder's ready-marker write disabled -> 'timed out waiting for
  .../held' from the helper at 15.0 s, the enclosing 45 s case intact
- storage-policy-job-responsive: idle transition masked -> expect(settled)
  Expected true / Received false; before this commit the same mutation passed

17 test files + 1 helper; each file run individually on macOS: 331 pass /
0 fail; typecheck clean.
@lidge-jun
lidge-jun force-pushed the codex/win-5-wait-budgets branch from c477a02 to 3201181 Compare September 5, 2026 02:39
@lidge-jun
lidge-jun merged commit bf8bc44 into dev Sep 5, 2026
6 of 7 checks passed
@lidge-jun
lidge-jun deleted the codex/win-5-wait-budgets branch September 5, 2026 02:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore Maintenance, CI, tests, refactors, or build changes (not a user-facing bug or feature).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants