You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug][CI]: Windows batch leg skips its nested live-lock regression under OCX_TEST_NO_QUEUE=1 #4991
PR #4876 correctly stops the Windows batch runner from queueing against its own previous batch by setting OCX_TEST_NO_QUEUE=1 on the Test in fresh-process batches step. However, the only Windows-only regression that proves nested Bun processes inherit and enforce the live test-run lock is guarded by the opposite condition:
test.if(process.platform==="win32"&&process.env[TEST_RUN_NO_QUEUE_ENV]!=="1")("nested Windows Bun tests inherit the acquired live lock and refuse an incomplete capability",// ...)
On current dev (6304a94c03), that test is therefore skipped in the exact Windows step whose lock behavior it is meant to protect.
Current evidence
.github/workflows/ci.yml sets OCX_TEST_NO_QUEUE: "1" for the Windows fresh-process batch step.
tests/ci-workflows/test-runner.test.ts skips the nested live-lock test whenever that variable is 1.
The same test is Windows-only, so Linux/macOS shards cannot cover it.
The ordinary no-queue unit test only proves that resolveWrappedTestRunLockPath() returns undefined; it does not prove the nested-process capability/incomplete-capability behavior exercised by the skipped integration.
Impact
A regression in nested Windows lock inheritance, membership creation, preload/home guard propagation, or incomplete-capability refusal can pass the main Windows suite silently. This is a test-coverage hole, not a claim that production service behavior is currently broken.
Required correction
Keep the outer batch step's no-queue behavior from #4876, but run the nested lock regression in a controlled child environment that explicitly supplies an acquired test lock and removes OCX_TEST_NO_QUEUE only for that fixture. The test should prove:
the child inherits the same lock path, run id, and token;
the child member receipt exists;
preload and the live-home guard are armed;
an incomplete inherited capability is refused; and
the parent owner receipt is unchanged.
The corrected test must execute on hosted Windows CI and must not reintroduce queueing between the outer six-file batches.
Regression origin
Introduced by the interaction between #4851's fresh-process batching and #4876's step-wide no-queue environment. #4876 remains directionally correct; this issue tracks restoring the coverage it unintentionally disabled.
이 이슈는 Windows fresh-process 배치 스텝이 OCX_TEST_NO_QUEUE=1을 켜서 자기 배치끼리 줄 서지 않게 한 #4876과, nested live-lock 회귀가 그 env가 1이 아닐 때만 돌아가게 둔 #4851 계열 가드가 맞부딪혀 커버리지가 빠졌다는 보고서입니다. tip(e80e571f63, package 2.59.0)에서 확인했습니다.
.github/workflows/ci.yml Windows “Test in fresh-process batches” 스텝은 주석대로 preload의 Windows user lock이 배치 N+1을 스트래글러 뒤에 세우지 않도록 OCX_TEST_NO_QUEUE: "1"을 넣습니다. 같은 tip의 tests/ci-workflows/test-runner.test.ts는 test.if(process.platform === "win32" && process.env[TEST_RUN_NO_QUEUE_ENV] !== "1")로 “nested Windows Bun tests inherit the acquired live lock and refuse an incomplete capability”를 가드합니다. 따라서 호스티드 Windows 배치 잡에서는 이 테스트가 항상 skip입니다. Linux/macOS 샤드는 win32 가드 때문에 대체 커버가 안 됩니다. 같은 파일의 no-queue 단위 테스트는 resolveWrappedTestRunLockPath()가 undefined인지만 보고, nested inherit·member receipt·preload/home guard·incomplete capability 거부는 증명하지 않습니다.
요구되는 다섯 단언은 tip 테스트 본문에 이미 있습니다. child가 같은 lock path/run id/token을 상속하고, members 영수증이 생기고, preload·live-home guard가 켜지며, token을 뺀 incomplete env는 거부되고, parent owner.json은 변하지 않는 것입니다. 고침은 이 단언을 유지한 채 가드만 “바깥 NO_QUEUE=1이어도 child fixture는 lock을 명시 주입”으로 바꾸는 일입니다. 바깥 여섯 파일 배치가 다시 서로 줄 서게 만들면 #4876 회귀입니다.
영향은 테스트 구멍이지, 지금 프로덕션 서비스가 깨졌다는 주장은 아닙니다. 다만 nested Windows lock inheritance·membership·guard 전파·incomplete refusal이 깨져도 메인 Windows 스위트가 조용히 초록일 수 있습니다. #4876 방향(배치 간 큐 제거)은 유지해야 하고, 고침은 바깥 스텝 no-queue는 남긴 채 픽스처 child에서만 획득된 lock을 주입하고 OCX_TEST_NO_QUEUE를 제거해 다섯 가지 단언을 다시 돌리는 형태가 맞습니다. types/config 스플릿과 무관합니다.
점수는 높게 잡습니다. tip에서 상호 배타 조건이 코드로 확인되고, Windows-only라 다른 OS가 못 메꿉니다. 운영 장애 신고는 아니지만 CI 회귀 망원경 손실이라 우선순위가 있습니다.
.github/workflows/ci.yml (Windows Test in fresh-process batches) - tip L864 근처 OCX_TEST_NO_QUEUE=1. #4876 의도 유지 대상.
tests/ci-workflows/test-runner.test.ts (~L1038) - win32 && NO_QUEUE!==1 가드로 nested live-lock 회귀. 배치 스텝과 동시에 참이 될 수 없음.
경로/심볼 TEST_RUN_NO_QUEUE_ENV / resolveWrappedTestRunLockPath - no-queue 단위 테스트는 path undefined만 증명.
경로/심볼 #4851/#4876 - 배치화와 no-queue의 상호작용. #4876을 되돌리지 말 것.
메인테이너의 판단이 필요한 지점
nested 회귀를 같은 파일의 조건부 child env로 살릴지, Windows 전용 작은 잡/스텝으로 분리할지.
skip을 명시적 test.todo/문서화로 남길지(비추천) 바로 픽스처 수정 PR을 받을지.
너의 추천
이슈 맞고 tip에서도 재현됩니다. 바깥 배치 no-queue는 유지한 채 nested 픽스처만 통제된 child env로 고치는 PR을 받으세요. 호스티드 Windows에서 실제로 실행되는 것까지 CI로 증명되게 하세요. 라벨은 바꾸지 마세요.
The outer no-queue setting from #4876 is unchanged. Instead of restoring the lane's lock, the case now brings its own holder: a controller running as a plain bun <file> child under an isolated mkdtemp root, with the no-queue opt-out removed only from the environment copy handed to it. The outer environment, the real home, and any pre-existing owner are never touched, and the controller's cwd sits outside the repository so Bun loads no preload into the holder itself — which would otherwise take the same lock and then wait on itself.
It resolves the user-scoped lock through resolveWrappedTestRunLockPath and takes it with acquireTestRunLock for its own run id, rather than writing an owner file by hand. The healthy nested child returns booleans only: path match, run match, token match, member receipt, preload executed, home guard armed, with OCX_TEST_PRELOAD_PID and OCX_TEST_HOME_GUARD stripped from every child environment so those two receipts have to come from the child's own preload. Three children are rejected before they execute — no token, a foreign token, a foreign lock path — each checked for a non-zero exit, the matching refusal on stderr, and the absence of a receipt on stdout. A fifth scenario plants a live foreign owner and proves the acquire path waits it out and gives up rather than reclaiming it, with an elapsed floor so an immediate refusal carrying the right message cannot pass.
The completion bar for this issue was that the case be recorded as executed rather than skipped on a hosted Windows lane with no-queue actually set. On windows-latest shard 6/9 of run 35380618954 at head 499ca44f27:
(pass) bun test user lock > a nested Windows Bun test inherits the live lock its controller
holds and refuses an incomplete capability [4035.04ms]
All nine Windows shards are green at that head, and no budget was widened — the controller is handed a deadline below the caller's spawnSync timeout and bounds each child by what is left of it.
Two limits are recorded rather than hidden. On the no-queue lane the controller creates and ACL-hardens the real per-user lock directory, because that path is what resolveInheritedTestRunLock validates by host digest and basename; a relocated lock would be rejected by the very check under test. And reclaimStaleLock has a pre-existing check-then-rename race between two contenders; this change adds one contender on a lane that has no other, and closing that race means editing the lock implementation, which is outside this fix.
Activity
lidge-jun commentedon Sep 18, 2026
리뷰 · 우선순위 72 / 80
이 이슈는 Windows fresh-process 배치 스텝이
OCX_TEST_NO_QUEUE=1을 켜서 자기 배치끼리 줄 서지 않게 한 #4876과, nested live-lock 회귀가 그 env가 1이 아닐 때만 돌아가게 둔 #4851 계열 가드가 맞부딪혀 커버리지가 빠졌다는 보고서입니다. tip(e80e571f63, package 2.59.0)에서 확인했습니다..github/workflows/ci.ymlWindows “Test in fresh-process batches” 스텝은 주석대로 preload의 Windows user lock이 배치 N+1을 스트래글러 뒤에 세우지 않도록OCX_TEST_NO_QUEUE: "1"을 넣습니다. 같은 tip의tests/ci-workflows/test-runner.test.ts는test.if(process.platform === "win32" && process.env[TEST_RUN_NO_QUEUE_ENV] !== "1")로 “nested Windows Bun tests inherit the acquired live lock and refuse an incomplete capability”를 가드합니다. 따라서 호스티드 Windows 배치 잡에서는 이 테스트가 항상 skip입니다. Linux/macOS 샤드는 win32 가드 때문에 대체 커버가 안 됩니다. 같은 파일의 no-queue 단위 테스트는resolveWrappedTestRunLockPath()가 undefined인지만 보고, nested inherit·member receipt·preload/home guard·incomplete capability 거부는 증명하지 않습니다.요구되는 다섯 단언은 tip 테스트 본문에 이미 있습니다. child가 같은 lock path/run id/token을 상속하고, members 영수증이 생기고, preload·live-home guard가 켜지며, token을 뺀 incomplete env는 거부되고, parent owner.json은 변하지 않는 것입니다. 고침은 이 단언을 유지한 채 가드만 “바깥 NO_QUEUE=1이어도 child fixture는 lock을 명시 주입”으로 바꾸는 일입니다. 바깥 여섯 파일 배치가 다시 서로 줄 서게 만들면 #4876 회귀입니다.
영향은 테스트 구멍이지, 지금 프로덕션 서비스가 깨졌다는 주장은 아닙니다. 다만 nested Windows lock inheritance·membership·guard 전파·incomplete refusal이 깨져도 메인 Windows 스위트가 조용히 초록일 수 있습니다. #4876 방향(배치 간 큐 제거)은 유지해야 하고, 고침은 바깥 스텝 no-queue는 남긴 채 픽스처 child에서만 획득된 lock을 주입하고
OCX_TEST_NO_QUEUE를 제거해 다섯 가지 단언을 다시 돌리는 형태가 맞습니다. types/config 스플릿과 무관합니다.점수는 높게 잡습니다. tip에서 상호 배타 조건이 코드로 확인되고, Windows-only라 다른 OS가 못 메꿉니다. 운영 장애 신고는 아니지만 CI 회귀 망원경 손실이라 우선순위가 있습니다.
.github/workflows/ci.yml (Windows Test in fresh-process batches) - tip L864 근처 OCX_TEST_NO_QUEUE=1. #4876 의도 유지 대상.
tests/ci-workflows/test-runner.test.ts (~L1038) - win32 && NO_QUEUE!==1 가드로 nested live-lock 회귀. 배치 스텝과 동시에 참이 될 수 없음.
경로/심볼 TEST_RUN_NO_QUEUE_ENV / resolveWrappedTestRunLockPath - no-queue 단위 테스트는 path undefined만 증명.
경로/심볼 #4851/#4876 - 배치화와 no-queue의 상호작용. #4876을 되돌리지 말 것.
메인테이너의 판단이 필요한 지점
test.todo/문서화로 남길지(비추천) 바로 픽스처 수정 PR을 받을지.너의 추천
이슈 맞고 tip에서도 재현됩니다. 바깥 배치 no-queue는 유지한 채 nested 픽스처만 통제된 child env로 고치는 PR을 받으세요. 호스티드 Windows에서 실제로 실행되는 것까지 CI로 증명되게 하세요. 라벨은 바꾸지 마세요.
이 댓글은 grok-bot이 작성했습니다
test(ci): give the Windows nested live-lock case its own lock owner (#…
lidge-jun commentedon Sep 19, 2026
Fixed on
devby #5078.The outer no-queue setting from #4876 is unchanged. Instead of restoring the lane's lock, the case now brings its own holder: a controller running as a plain
bun <file>child under an isolatedmkdtemproot, with the no-queue opt-out removed only from the environment copy handed to it. The outer environment, the real home, and any pre-existing owner are never touched, and the controller's cwd sits outside the repository so Bun loads no preload into the holder itself — which would otherwise take the same lock and then wait on itself.It resolves the user-scoped lock through
resolveWrappedTestRunLockPathand takes it withacquireTestRunLockfor its own run id, rather than writing an owner file by hand. The healthy nested child returns booleans only: path match, run match, token match, member receipt, preload executed, home guard armed, withOCX_TEST_PRELOAD_PIDandOCX_TEST_HOME_GUARDstripped from every child environment so those two receipts have to come from the child's own preload. Three children are rejected before they execute — no token, a foreign token, a foreign lock path — each checked for a non-zero exit, the matching refusal on stderr, and the absence of a receipt on stdout. A fifth scenario plants a live foreign owner and proves the acquire path waits it out and gives up rather than reclaiming it, with an elapsed floor so an immediate refusal carrying the right message cannot pass.The completion bar for this issue was that the case be recorded as executed rather than skipped on a hosted Windows lane with no-queue actually set. On
windows-latestshard 6/9 of run35380618954at head499ca44f27:All nine Windows shards are green at that head, and no budget was widened — the controller is handed a deadline below the caller's
spawnSynctimeout and bounds each child by what is left of it.Two limits are recorded rather than hidden. On the no-queue lane the controller creates and ACL-hardens the real per-user lock directory, because that path is what
resolveInheritedTestRunLockvalidates by host digest and basename; a relocated lock would be rejected by the very check under test. AndreclaimStaleLockhas a pre-existing check-then-rename race between two contenders; this change adds one contender on a lane that has no other, and closing that race means editing the lock implementation, which is outside this fix.No local suite was run.