Summary
test/claude-sdk-oauth-binding-lifecycle-security.test.ts > forgets process binding and invalidates/deletes durable state when selecting a non-Claude provider fails inside the Test (coding-agent 1/3) shard while passing in isolation. It reproduces on main itself, so it blocks unrelated PRs whose only red check is that shard.
Evidence
Failure on a main run (no PR involved) — run 35055501202, head fef17495, job Test (coding-agent 1/3):
FAIL test/claude-sdk-oauth-binding-lifecycle-security.test.ts > Claude SDK OAuth binding lifecycle security > forgets process binding and invalidates/deletes durable state when selecting a non-Claude provider
AssertionError: expected { …(11) } to be undefined
❯ test/claude-sdk-oauth-binding-lifecycle-security.test.ts:200:33
200| expect(getBinding(sessionId)).toBeUndefined();
Test Files 1 failed | 468 passed | 2 skipped (471)
Tests 1 failed | 3934 passed | 6 skipped (3941)
The received value is a live binding record (sdkSessionId, systemPromptHash, toolsetHash, …) for session id model-select-non-claude, i.e. the model_select handler did not forget the process binding.
Contrast runs I made locally on the same file:
| Scope |
Result |
the file alone, at main-derived base 30b5ea8024 |
Test Files 1 passed, Tests 2 passed |
| the file alone, at a feature head |
Test Files 1 passed, Tests 2 passed |
the whole test/claude-sdk-oauth-*.test.ts cluster (61 files) |
Test Files 61 passed, Tests 493 passed | 3 skipped |
shard 1/3 in CI |
fails as above |
So the interference comes from a file outside the claude-sdk-oauth cluster that shares this module's process-global state. The suite keeps the binding registry in module scope: the test registers one with registerSessionRegistry(extension.api) and clears it in afterEach via resetSessionRegistryBoundary() — a leftover registration from an earlier file in the shard would leave getBinding and the handler reading different boundaries.
Expected
The test passes in every shard composition, because the binding registry boundary cannot leak across test files. The fix is isolation at the seam (establish the boundary per test rather than trusting the previous file to have cleared it), not a retry, a skip, or a loosened assertion.
Acceptance criteria
- The exact shard composition that fails today (
vitest run --shard=1/3) passes.
- The cause is named in the PR: which state leaks and which file leaves it.
test/claude-sdk-oauth-binding-lifecycle-security.test.ts still fails if the model_select handler stops forgetting the binding (the assertion keeps its teeth).
Related
Found while verifying #1726/#1729 (#1746), whose only red check was this shard.
Summary
test/claude-sdk-oauth-binding-lifecycle-security.test.ts > forgets process binding and invalidates/deletes durable state when selecting a non-Claude providerfails inside theTest (coding-agent 1/3)shard while passing in isolation. It reproduces onmainitself, so it blocks unrelated PRs whose only red check is that shard.Evidence
Failure on a
mainrun (no PR involved) — run35055501202, headfef17495, jobTest (coding-agent 1/3):The received value is a live binding record (
sdkSessionId,systemPromptHash,toolsetHash, …) for session idmodel-select-non-claude, i.e. themodel_selecthandler did not forget the process binding.Contrast runs I made locally on the same file:
main-derived base30b5ea8024Test Files 1 passed,Tests 2 passedTest Files 1 passed,Tests 2 passedtest/claude-sdk-oauth-*.test.tscluster (61 files)Test Files 61 passed,Tests 493 passed | 3 skipped1/3in CISo the interference comes from a file outside the
claude-sdk-oauthcluster that shares this module's process-global state. The suite keeps the binding registry in module scope: the test registers one withregisterSessionRegistry(extension.api)and clears it inafterEachviaresetSessionRegistryBoundary()— a leftover registration from an earlier file in the shard would leavegetBindingand the handler reading different boundaries.Expected
The test passes in every shard composition, because the binding registry boundary cannot leak across test files. The fix is isolation at the seam (establish the boundary per test rather than trusting the previous file to have cleared it), not a retry, a skip, or a loosened assertion.
Acceptance criteria
vitest run --shard=1/3) passes.test/claude-sdk-oauth-binding-lifecycle-security.test.tsstill fails if themodel_selecthandler stops forgetting the binding (the assertion keeps its teeth).Related
Found while verifying #1726/#1729 (#1746), whose only red check was this shard.