Skip to content

test(compaction): stub the in-handler pre-route prime so Windows teardown can remove CODEX_HOME - #7091

Merged
lidge-jun merged 1 commit into
devfrom
fix/windows-compaction-ebusy
Oct 14, 2026
Merged

lidge-jun merged 1 commit into
devfrom
fix/windows-compaction-ebusy

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Summary

Windows CI on dev (run 38507644375, windows 4/9) still failed tests/responses/responses-compaction-routing.test.ts after #7088, with EBUSY: resource busy or locked, rm ...ocx-compact-connect-probe-*. The failing case spent its entire 15 s removal retry budget.

The cause is the pre-route quota prime that resolveCodexAuthContext starts and does not await inside handleResponsesCompact. #7088 stubbed only the second, explicit resolveCodexAuthContext call. The prime reaches primeMain → withNativeMainSharedClaim → openClaimDatabase, which opens .opencodex-native-main.claim.sqlite under the case's CODEX_HOME and then awaits hardenStableLockFile. On win32 that call is async ACL hardening. Teardown removes the directory with removeTestTempTree, whose retries sleep with Bun.sleepSync. Because that blocks the event loop, the hardening can never finish and close the file, so every retry hits the Windows sharing violation (EBUSY). The prime is single-flight across cases, so the failure moves to whichever case is tearing down while the prime runs.

The fix is test-only. The native Codex pool compaction block now stubs primeCodexPoolQuotas on src/codex/auth-api, the module the lazy import("./auth-api") resolves through, in a beforeEach/afterEach pair. None of the three cases asserts on priming. This is not a product leak, because in a running proxy the prime finishes and releaseClaim closes the file. It only becomes a problem when a synchronous teardown starves it.

Verification

  • Reproduced on macOS by temporarily delaying the POSIX hardenStableLockFile by 500 ms and checking lsof at teardown (instrumentation reverted): before the fix, .opencodex-native-main.claim.sqlite was still open after the spend lease dropped. After the fix, nothing under the case directory is open, and the spy records the intercepted pre-route primes (3 in the Spark case, 1 in each Reset-derived 429 in an OpenAI combo blocks same-account fallback models #590 case).
  • bun test tests/responses/responses-compaction-routing.test.ts: 124 pass / 0 fail, three consecutive runs.
  • bun run typecheck: clean.
  • bun test tests/ci-workflows/file-size-ratchet.test.ts: 9 pass (file at 2667 lines, cap 2776).
  • bun run privacy:scan: passed.
  • I did not run the full bun run test. The change is confined to one test file's setup, so the focused file plus the ratchet covers it. Windows itself is left to the next cross-platform workflow_dispatch, because PRs run only the Ubuntu subset.

Checklist

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

Summary by CodeRabbit

  • Tests
    • Updated native Codex pool compaction tests to avoid performing quota priming during test runs.

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner October 14, 2026 04:04
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-14T04:06:36.203273Z ea52716 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

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 Oct 14, 2026
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 5397d7e8-cd2f-4438-84c2-d7f8e0fe21fe
📥 Commits

Reviewing files that changed from the base of the PR and between 3dda89f and ea52716.

📒 Files selected for processing: 1
  • tests/responses/responses-compaction-routing.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.

📝 Walkthrough

The native Codex pool compaction tests now stub primeCodexPoolQuotas as a resolved no-op and restore the stub after each test.

Changes: Codex pool compaction tests

Layer / File(s) Summary
Quota priming test setup
tests/responses/responses-compaction-routing.test.ts
The tests import the Codex auth API and stub primeCodexPoolQuotas during native Codex pool compaction tests. Teardown restores the stub and clears its reference.

Priority: ⬇️ Low

Estimated code review effort: 1 (Trivial) | ~5 minutes

Change: Other

Merge Risk: ⚪ Minimal · up to ea527

This test-only change stubs quota priming during native-pool tests and restores it afterward. The source supports the intended teardown protection, with no actionable merge risk remaining.

Pre-merge checks: Passed 5
Passed Passed checks: 5 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly describes the test-only change: stubbing the in-handler primeCodexPoolQuotas call to prevent Windows CODEX_HOME teardown failures caused by EBUSY.
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 1…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@lidge-jun
lidge-jun merged commit ba865da into dev Oct 14, 2026
33 checks passed
@lidge-jun
lidge-jun deleted the fix/windows-compaction-ebusy branch October 14, 2026 04:16
lidge-jun added a commit that referenced this pull request Oct 14, 2026
* fix(test-teardown): settle un-awaited background work before removing a tree

The pre-route pool quota prime runs without being awaited and holds the
native-main claim database open across ACL hardening and a WHAM read.
Teardown retried removal with Bun.sleepSync, which blocks the event loop
the prime needs to reach its finally, so on Windows every retry failed
EBUSY until the 15 s budget ran out (#7088 and #7091 stubbed the prime
per test).

Track fire-and-forget work in src/lib/background-work, register the pool
prime and its lazy-import callers, settle it in a global afterEach, and
add removeTreeAfterBackgroundWork (settle, reap icacls, then retry with
waits that yield). The compaction fixture uses it, and the per-test prime
stubs from #7088 and #7091 are reverted so the real prime runs again.

* fix(responses): serialize snapshot follow-up scheduling with shutdown

* fix(config): track the config-dir ACL harden as background work

icacls holds the config directory open while it runs; a config write or
removal that lands on it before the child exits fails with a sharing
violation on Windows. Registering the flight lets the per-case drain
settle it, and the V2 refusal test now prints the CLI errors when the
handler refuses before the native toggle.

* fix(cli): reject occupied auxiliary ports before private startup work

* fix(tray): retain timed-out probes until asynchronous reaping

Remove WaitForExit from startup-health timeout ticks and retain process ownership until exit and both readers settle. Run lifecycle fixtures with the production Bun engine instead of a nested PowerShell cold start, which failed readiness before maintenance in Windows run 38503986851.

* fix(config): preserve Windows ACL memo across repeated reads

* test: bound child exit and pipe waits in slow-spawn cases

* fix(ci): share one transpiler cache per shard; widen the DPAPI PowerShell bound

Run 2 and 3 of the Windows proof failed on first-spawn timeouts (writeJournal
child 40s, launcher attest 15s) and a 10s DPAPI PowerShell call. Each CI batch
is its own bun test process, and with BUN_RUNTIME_TRANSPILER_CACHE_PATH unset
the preload gives every batch an empty private cache, so the first child each
batch spawns re-transpiles its whole module graph. The batch runner now creates
one private cache per shard and exports it. The DPAPI helper's PowerShell
bound moves from 10s to 20s: it is async and off the request path, and a cold
PowerShell loading System.Security exceeded 10s on a loaded runner.

* test(desktop-egress): give each proxy variable one spelling on Windows

Windows environment names are case-insensitive, so the child env that set
ALL_PROXY to the fixture proxy and all_proxy to "" defined one variable
twice. Env-block ordering decided the winner; adding the shard transpiler
cache variable flipped it, and the -all cases reached the real chatgpt.com
(403). Clear every spelling and set only the intended route.

* fix(test-teardown): never remove or exit under live tracked work; address review findings

- removeTreeAfterBackgroundWork keeps the tree and names pending work instead of
  deleting under a live writer.
- Final sandbox cleanup bounds background-work settlement (15 s) and leaves the
  root for stale recovery with the labels when work never settles.
- spawnTestChild rejects when the child never started (ENOENT) instead of
  resolving an empty result.
- Auxiliary preflight rejects loopback and hub management configured on one port.
- Tray shutdown keeps a probe tracked until it has actually exited and waits for
  its pipe readers before disposal.
- The free-socket preflight test binds inside try so a lost race still stops the
  first server.

---------

Co-authored-by: jun <jun@juns-Mac-Studio.local>
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.

1 participant