From 56f63c4be5a895f377b945d647ffc4e03bf53b43 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 03:40:42 +0000 Subject: [PATCH 1/2] =?UTF-8?q?test(cli):=20=E6=8A=8A=20login=20NDJSON=20e?= =?UTF-8?q?2e=20=E7=9A=84=E6=94=BE=E8=A1=8C=E6=88=AA=E6=AD=A2=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E9=94=9A=E5=AE=9A=E5=88=B0=20device=20code=20?= =?UTF-8?q?=E7=AD=BE=E5=8F=91=E6=97=B6=E5=88=BB=20(#6872)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017uFVNMmTxLpmfQYiuKM1Yx --- .../cli/test/login-json-ndjson.e2e.test.ts | 63 ++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/packages/cli/test/login-json-ndjson.e2e.test.ts b/packages/cli/test/login-json-ndjson.e2e.test.ts index c232a7ab62..e5011bfa95 100644 --- a/packages/cli/test/login-json-ndjson.e2e.test.ts +++ b/packages/cli/test/login-json-ndjson.e2e.test.ts @@ -80,6 +80,31 @@ const AUTH_DOCS = resolve(REPO_ROOT, 'content/docs/permissions/authentication.md * anyway. Only reached when the record never arrives early — i.e. when the * contract is broken — and exists so that failure is an assertion rather than a * suite that hangs until the runner kills it. + * + * ## The clock starts at device-code issuance, not at spawn (#6872, shape from #6855) + * + * This budget is armed when the endpoint hands the CLI its device code, because + * that is the first instant at which the contract is even measurable: from + * there the CLI holds the verification URL and owes it to stdout. Everything + * before it — `script(1)`, the `tsx` transform of the whole oclif command tree, + * module loading — is process startup, about which #6531 says nothing. + * + * Armed at spawn instead, this budget policed startup rather than the contract. + * Measured on this file (5 runs, probe replicating {@link runDeviceLogin}), of + * the latency from spawn to the record being readable: + * + * | segment | measured | + * |-------------------------------------------------------|--------------| + * | spawn → device-code request (startup) | 6844–8841 ms | + * | device-code response → record readable (the contract) | 13–39 ms | + * + * So ~99.7% of the old budget was spent on work the contract does not govern, + * leaving startup needing only a ~2.3x slowdown to exhaust 20 s — ordinary on a + * box running four concurrent worktree builds, or on a merge-queue runner. Its + * cloud sibling, on the identical harness, duly ejected two unrelated PRs + * (#6847 spec-only, #6835 docs-only) before #6855 re-anchored it. Anchored + * here, the budget covers a ~20 ms window with ~500x headroom, and the number + * itself is unchanged: this is a re-anchoring, NOT a widened timeout. */ const RELEASE_DEADLINE_MS = 20_000; @@ -107,6 +132,14 @@ function startDeviceEndpoint(outcome: 'token' | 'access_denied') { let released = false; let authorizedAt: number | null = null; + // Resolved the moment the CLI has been handed its device code — the instant + // the emission contract starts running, and so the anchor for the release + // deadline. Definite-assignment: the Promise executor runs synchronously. + let markDeviceCodeIssued!: () => void; + const deviceCodeIssued = new Promise((res) => { + markDeviceCodeIssued = res; + }); + const server: Server = createServer((req, res) => { req.resume(); req.on('end', () => { @@ -117,6 +150,7 @@ function startDeviceEndpoint(outcome: 'token' | 'access_denied') { const { pathname } = new URL(req.url ?? '/', 'http://placeholder'); if (pathname === '/api/v1/auth/device/code') { + markDeviceCodeIssued(); return send(200, { device_code: 'DEV-CODE-6531', user_code: 'WXYZ-6531', @@ -147,6 +181,7 @@ function startDeviceEndpoint(outcome: 'token' | 'access_denied') { return { server, release: () => { released = true; }, + deviceCodeIssued, authorizedAt: () => authorizedAt, listen: () => new Promise((res) => { @@ -202,12 +237,20 @@ async function runDeviceLogin(outcome: 'token' | 'access_denied'): Promise { - if (urlSeenAt === null) { - releasedByDeadline = true; - endpoint.release(); - } - }, RELEASE_DEADLINE_MS); + // Armed on device-code issuance rather than here, so the budget covers the + // window the contract governs and not the child's startup — see + // {@link RELEASE_DEADLINE_MS} for the measurements behind that (#6872). + // Stays unarmed if the CLI never reaches the device flow at all; that run + // ends when the child exits, and the assertions below name the absence. + let deadline: ReturnType | undefined; + void endpoint.deviceCodeIssued.then(() => { + deadline = setTimeout(() => { + if (urlSeenAt === null) { + releasedByDeadline = true; + endpoint.release(); + } + }, RELEASE_DEADLINE_MS); + }); const shell = [ `'${TSX}' '${CLI}' login --json --no-browser`, @@ -289,7 +332,13 @@ describe('os login --json — the declared NDJSON stream (#6531)', () => { it('hands over the verification URL BEFORE authorization — the reason this route was chosen', () => { // The endpoint only authorized because the watcher had already read the // record off stdout, so these two facts are the run's own history. - expect(ok.releasedByDeadline, 'the device record never reached stdout early').toBe(false); + expect( + ok.releasedByDeadline, + `the device record did not reach stdout within ${RELEASE_DEADLINE_MS}ms of the CLI ` + + 'receiving its device code — the buffered-emit regression this route was chosen to ' + + 'prevent. This window excludes process startup (#6872), so a slow runner is not a ' + + 'cause: at the moment it opens the CLI already holds the verification URL.', + ).toBe(false); expect(ok.urlSeenAt).not.toBeNull(); expect(ok.authorizedAt).not.toBeNull(); expect(ok.urlSeenAt!).toBeLessThanOrEqual(ok.authorizedAt!); From bf959d5edb552acf0f5b9034c7cbfc7a240fd5b5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 04:26:25 +0000 Subject: [PATCH 2/2] =?UTF-8?q?test(cli):=20=E7=94=A8=20n=3D10=20=E5=AE=9E?= =?UTF-8?q?=E6=B5=8B=E5=8C=BA=E9=97=B4=E6=A0=A1=E5=87=86=E9=94=9A=E5=AE=9A?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017uFVNMmTxLpmfQYiuKM1Yx --- .../cli/test/login-json-ndjson.e2e.test.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/cli/test/login-json-ndjson.e2e.test.ts b/packages/cli/test/login-json-ndjson.e2e.test.ts index e5011bfa95..9538e77eb9 100644 --- a/packages/cli/test/login-json-ndjson.e2e.test.ts +++ b/packages/cli/test/login-json-ndjson.e2e.test.ts @@ -90,21 +90,25 @@ const AUTH_DOCS = resolve(REPO_ROOT, 'content/docs/permissions/authentication.md * module loading — is process startup, about which #6531 says nothing. * * Armed at spawn instead, this budget policed startup rather than the contract. - * Measured on this file (5 runs, probe replicating {@link runDeviceLogin}), of + * Measured on this file (10 runs, probe replicating {@link runDeviceLogin}), of * the latency from spawn to the record being readable: * * | segment | measured | * |-------------------------------------------------------|--------------| - * | spawn → device-code request (startup) | 6844–8841 ms | + * | spawn → device-code request (startup) | 6844–9587 ms | * | device-code response → record readable (the contract) | 13–39 ms | * * So ~99.7% of the old budget was spent on work the contract does not govern, - * leaving startup needing only a ~2.3x slowdown to exhaust 20 s — ordinary on a - * box running four concurrent worktree builds, or on a merge-queue runner. Its - * cloud sibling, on the identical harness, duly ejected two unrelated PRs - * (#6847 spec-only, #6835 docs-only) before #6855 re-anchored it. Anchored - * here, the budget covers a ~20 ms window with ~500x headroom, and the number - * itself is unchanged: this is a re-anchoring, NOT a widened timeout. + * leaving startup needing only a ~2.1x slowdown to exhaust 20 s. The spread in + * that row is itself the argument: the same probe measured 6844 ms on a quiet + * box and 9587 ms once other worktrees started building, with nothing about + * the contract having changed in between. The cloud sibling, on this identical + * harness, duly ejected two unrelated PRs from the merge queue (#6847 + * spec-only, #6835 docs-only) before #6855 re-anchored it. + * + * Anchored here, the budget covers a ~20 ms window with ~500x headroom, and + * the number itself is unchanged: this is a re-anchoring, NOT a widened + * timeout. */ const RELEASE_DEADLINE_MS = 20_000;