From f6f21fb82c88c3636e43326a9f4a80e2ffbdc434 Mon Sep 17 00:00:00 2001 From: t Date: Sun, 6 Sep 2026 21:41:35 +0900 Subject: [PATCH 1/8] fix(test): forward the complete bare Windows run lock capability --- tests/ci-workflows/test-runner.test.ts | 59 +++++++++++++++++++++++++- tests/preload.ts | 18 ++++++-- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/tests/ci-workflows/test-runner.test.ts b/tests/ci-workflows/test-runner.test.ts index 798c7e67d86..84b0dfc928d 100644 --- a/tests/ci-workflows/test-runner.test.ts +++ b/tests/ci-workflows/test-runner.test.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from "bun:test"; -import { existsSync, mkdtempSync, statSync, writeFileSync } from "node:fs"; +import { spawnSync } from "node:child_process"; +import { existsSync, mkdtempSync, readFileSync, statSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { basename, dirname, isAbsolute, join, posix, win32 } from "node:path"; import { @@ -19,6 +20,7 @@ import { resolveDefaultTestRunLockPath, resolveInheritedTestRunLock, resolveWrappedTestRunLockPath, + TEST_RUN_ID_ENV, TEST_RUN_LOCK_PATH_ENV, TEST_RUN_LOCK_TOKEN_ENV, TEST_RUN_NO_QUEUE_ENV, @@ -30,6 +32,7 @@ import { windowsIdentityPowerShellSpawnOptionsForTests, } from "../../src/codex/user-identity"; import { removeTreeWithRetry } from "../helpers/remove-tree"; +import { INTERNAL_DEADLINE_MS, SPAWN_BUDGET_MS } from "../helpers/test-budget"; function runGit(cwd: string, ...args: string[]): string { @@ -656,6 +659,60 @@ describe("bun test user lock", () => { expect(resolveCalls).toBe(0); }); + 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", + () => { + const root = mkdtempSync(join(tmpdir(), "opencodex-nested-test-")); + try { + const lockPath = process.env[TEST_RUN_LOCK_PATH_ENV]; + expect(Boolean(lockPath && process.env[TEST_RUN_LOCK_TOKEN_ENV] && process.env[TEST_RUN_ID_ENV])).toBe(true); + const ownerBefore = readFileSync(join(lockPath!, "owner.json"), "utf8"); + const fixture = join(root, "nested.test.ts"); + writeFileSync(fixture, ` + import { test } from "bun:test"; + import { readFileSync, existsSync } from "node:fs"; + import { join } from "node:path"; + test("nested lock receipt", () => { + const path = process.env.OCX_TEST_RUN_LOCK_PATH; + const owner = JSON.parse(readFileSync(join(path, "owner.json"), "utf8")); + console.log(JSON.stringify({ nestedLockReceipt: { + samePath: path === ${JSON.stringify(lockPath)}, + sameRun: owner.runId === ${JSON.stringify(process.env[TEST_RUN_ID_ENV])}, + sameToken: owner.token === process.env.OCX_TEST_RUN_LOCK_TOKEN, + member: existsSync(join(path, "members", process.pid + "-" + owner.token)), + preloadRan: process.env.OCX_TEST_PRELOAD_PID === String(process.pid), + guardArmed: process.env.OCX_TEST_HOME_GUARD === "1", + } })); + }); + `); + const args = ["test", "--preload", repoPath("tests/preload.ts"), fixture]; + const child = spawnSync(process.execPath, args, { + cwd: root, env: { ...process.env }, encoding: "utf8", timeout: INTERNAL_DEADLINE_MS, + }); + // Keep process diagnostics bounded and never render the owner token or child output. + expect(child.status).toBe(0); + const marker = child.stdout.split("\n").find(line => line.startsWith('{"nestedLockReceipt":')); + expect(marker ? JSON.parse(marker).nestedLockReceipt : null).toEqual({ + samePath: true, sameRun: true, sameToken: true, member: true, preloadRan: true, guardArmed: true, + }); + expect(readFileSync(join(lockPath!, "owner.json"), "utf8") === ownerBefore).toBe(true); + + const incomplete = { ...process.env }; + delete incomplete[TEST_RUN_LOCK_TOKEN_ENV]; + const refused = spawnSync(process.execPath, args, { + cwd: root, env: incomplete, encoding: "utf8", timeout: INTERNAL_DEADLINE_MS, + }); + expect(refused.status).toBe(1); + expect(refused.stderr.includes("capability is incomplete")).toBe(true); + expect(refused.stdout.includes('{"nestedLockReceipt":')).toBe(false); + expect(readFileSync(join(lockPath!, "owner.json"), "utf8") === ownerBefore).toBe(true); + } finally { + removeTreeWithRetry(root); + } + }, + { timeout: SPAWN_BUDGET_MS }, + ); + test("falls back from an unsafe XDG root to a validated mode-0700 UID directory", () => { if (process.platform === "win32" || typeof process.getuid !== "function") return; const root = mkdtempSync(join(tmpdir(), "opencodex-runtime-fallback-")); diff --git a/tests/preload.ts b/tests/preload.ts index ad64ff63842..01717a0f52c 100644 --- a/tests/preload.ts +++ b/tests/preload.ts @@ -17,7 +17,10 @@ import { acquireTestRunLock, resolveBareTestRunIdentity, resolveInheritedTestRunLock, + resolveWrappedTestRunLockPath, TEST_RUN_ID_ENV, + TEST_RUN_LOCK_PATH_ENV, + TEST_RUN_LOCK_TOKEN_ENV, } from "../scripts/test-run-lock"; import { rmSync } from "node:fs"; @@ -77,17 +80,26 @@ const inheritedLock = resolveInheritedTestRunLock({ env: process.env, }); process.env[TEST_RUN_ID_ENV] = runId; -await acquireTestRunLock({ +// A bare Windows run also parents nested Bun tests. Resolve its validated path +// once, then pass the complete capability to descendants just as the wrapper does. +const lockPath = inheritedLock?.lockPath + ?? (process.platform === "win32" ? resolveWrappedTestRunLockPath() : undefined); +const runLock = await acquireTestRunLock({ runId, ownerPid: bareIdentity.ownerPid, - lockPath: inheritedLock?.lockPath, - validatedRuntimePath: inheritedLock !== undefined, + lockPath, + validatedRuntimePath: lockPath !== undefined, joinExistingOwnerToken: inheritedLock?.ownerToken, onWait: owner => console.warn( `[test] bare Bun worker ${process.pid} is waiting for test run${owner ? ` pid ${owner.pid}` : ""} to release the user lock.`, ), }); +if (process.platform === "win32" && lockPath && runLock.owner) { + process.env[TEST_RUN_LOCK_PATH_ENV] = lockPath; + process.env[TEST_RUN_LOCK_TOKEN_ENV] = runLock.owner.token; +} + // Clean up only the root this preload created. The `bun run test` wrapper owns its own. process.on("exit", () => { try { rmSync(isolated.root, { recursive: true, force: true }); } catch { /* best effort at exit */ } From b186033e3d6d3dbcf4b2047530ca00834fe9c013 Mon Sep 17 00:00:00 2001 From: t Date: Sun, 6 Sep 2026 21:43:14 +0900 Subject: [PATCH 2/8] perf(cursor): avoid full-store scans for fresh blob admission --- src/adapters/cursor/native-exec.ts | 74 +++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/src/adapters/cursor/native-exec.ts b/src/adapters/cursor/native-exec.ts index 938dc17b9f7..abbdc1b8030 100644 --- a/src/adapters/cursor/native-exec.ts +++ b/src/adapters/cursor/native-exec.ts @@ -140,6 +140,10 @@ let blobOldestEvictableAt: number | null = null; let rejectedEntryTooLarge = 0; let rejectedPinnedSaturation = 0; let blobExpiryAccountingTimer: ReturnType | undefined; +/** Earliest unpinned storedAt+ttl; skip the write-time TTL walk while this is in the future. */ +let blobNextUnpinnedExpiryAt: number | null = null; +/** Earliest unpinned remote expiry strictly in the future; drives the single reclassify timer. */ +let blobNextRemoteExpiryAt: number | null = null; function isExpired(entry: CursorBlobEntry, now: number): boolean { return now - entry.storedAt >= blobLimits.ttlMs; @@ -157,6 +161,8 @@ function recomputeBlobClassAccounting(): void { let pinnedBytes = 0; let evictableBytes = 0; let oldestAt: number | null = null; + let nextUnpinnedExpiry = Number.POSITIVE_INFINITY; + let nextRemoteExpiry = Number.POSITIVE_INFINITY; for (const [k, entry] of blobs) { const requestPinned = entry.requestPins.size > 0; const provenancePinned = entry.provenance === "remote-setBlobArgs" && !isExpired(entry, now); @@ -169,11 +175,20 @@ function recomputeBlobClassAccounting(): void { evictableBytes += entry.sizeBytes + k.length; oldestAt = oldestAt === null ? entry.storedAt : Math.min(oldestAt, entry.storedAt); } + if (!requestPinned) { + const expiresAt = entry.storedAt + blobLimits.ttlMs; + nextUnpinnedExpiry = Math.min(nextUnpinnedExpiry, expiresAt); + if (entry.provenance === "remote-setBlobArgs" && expiresAt > now) { + nextRemoteExpiry = Math.min(nextRemoteExpiry, expiresAt); + } + } } blobLocalBytes = localBytes; blobPinnedBytes = pinnedBytes; blobEvictableBytes = evictableBytes; blobOldestEvictableAt = oldestAt; + blobNextUnpinnedExpiryAt = Number.isFinite(nextUnpinnedExpiry) ? nextUnpinnedExpiry : null; + blobNextRemoteExpiryAt = Number.isFinite(nextRemoteExpiry) ? nextRemoteExpiry : null; scheduleBlobExpiryAccounting(now); } @@ -185,13 +200,8 @@ function reconcileBlobClassAccountingAndEnforce(): void { function scheduleBlobExpiryAccounting(now: number): void { if (blobExpiryAccountingTimer) clearTimeout(blobExpiryAccountingTimer); blobExpiryAccountingTimer = undefined; - let nextExpiry = Number.POSITIVE_INFINITY; - for (const entry of blobs.values()) { - if (entry.provenance !== "remote-setBlobArgs" || entry.requestPins.size > 0) continue; - const expiresAt = entry.storedAt + blobLimits.ttlMs; - if (expiresAt > now) nextExpiry = Math.min(nextExpiry, expiresAt); - } - if (!Number.isFinite(nextExpiry)) return; + const nextExpiry = blobNextRemoteExpiryAt; + if (nextExpiry === null || nextExpiry <= now || !Number.isFinite(nextExpiry)) return; blobExpiryAccountingTimer = setTimeout(() => { blobExpiryAccountingTimer = undefined; reconcileBlobClassAccountingAndEnforce(); @@ -199,6 +209,41 @@ function scheduleBlobExpiryAccounting(now: number): void { blobExpiryAccountingTimer.unref?.(); } +/** + * O(1) class/timer update for a newly admitted key when no other row changed. + * Full-map recompute stays on replacement, eviction, pin changes, and TTL fire — + * the 4096-entry ceiling fill must not walk the store on every remote admit. + */ +function accountAdmittedBlob(k: string, entry: CursorBlobEntry, now: number): void { + const requestPinned = entry.requestPins.size > 0; + const expired = isExpired(entry, now); + const provenancePinned = entry.provenance === "remote-setBlobArgs" && !expired; + const logicalBytes = entry.sizeBytes + k.length; + if (entry.provenance === "local-regenerated") blobLocalBytes += entry.sizeBytes; + if (requestPinned || provenancePinned) blobPinnedBytes += logicalBytes; + if (!requestPinned && (entry.provenance === "local-regenerated" || expired)) { + blobEvictableBytes += logicalBytes; + blobOldestEvictableAt = blobOldestEvictableAt === null ? entry.storedAt : Math.min(blobOldestEvictableAt, entry.storedAt); + } + if (requestPinned) return; + const expiresAt = entry.storedAt + blobLimits.ttlMs; + blobNextUnpinnedExpiryAt = blobNextUnpinnedExpiryAt === null + ? expiresAt + : Math.min(blobNextUnpinnedExpiryAt, expiresAt); + if (entry.provenance !== "remote-setBlobArgs" || expiresAt <= now) return; + const previousRemoteExpiry = blobNextRemoteExpiryAt; + blobNextRemoteExpiryAt = previousRemoteExpiry === null + ? expiresAt + : Math.min(previousRemoteExpiry, expiresAt); + if ( + !blobExpiryAccountingTimer + || previousRemoteExpiry === null + || expiresAt < previousRemoteExpiry + ) { + scheduleBlobExpiryAccounting(now); + } +} + function deleteBlob(k: string, recompute = true): number { const entry = blobs.get(k); if (!entry) return 0; @@ -246,9 +291,11 @@ function setBlob( } const removals = new Set(); - for (const [candidateKey, entry] of blobs) { - if (candidateKey === k && sameData) continue; - if (entry.requestPins.size === 0 && isExpired(entry, now)) removals.add(candidateKey); + if (blobNextUnpinnedExpiryAt !== null && now >= blobNextUnpinnedExpiryAt) { + for (const [candidateKey, entry] of blobs) { + if (candidateKey === k && sameData) continue; + if (entry.requestPins.size === 0 && isExpired(entry, now)) removals.add(candidateKey); + } } const existingRemovedByTtl = existing !== undefined && removals.has(k); @@ -326,7 +373,12 @@ function setBlob( blobBytes += entry.sizeBytes; blobKeyBytes += k.length; for (const scope of entry.requestPins) blobRequestScopes.get(scope)?.keys.add(k); - reconcileBlobClassAccountingAndEnforce(); + if (removals.size > 0 || existing !== undefined) { + reconcileBlobClassAccountingAndEnforce(); + } else { + accountAdmittedBlob(k, entry, now); + enforceAppOwnedMemoryBudget(); + } return { admitted: true, replaced: existing !== undefined }; } From 69f9e07c4fa7b80bcda9e4ba28e3c64f42187828 Mon Sep 17 00:00:00 2001 From: t Date: Sun, 6 Sep 2026 21:47:27 +0900 Subject: [PATCH 3/8] test(cursor): cover incremental blob accounting and released-pin expiry --- tests/providers/cursor/cursor-blob.test.ts | 81 ++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/tests/providers/cursor/cursor-blob.test.ts b/tests/providers/cursor/cursor-blob.test.ts index b173345d1d4..7df7693e18a 100644 --- a/tests/providers/cursor/cursor-blob.test.ts +++ b/tests/providers/cursor/cursor-blob.test.ts @@ -1702,6 +1702,87 @@ describe("Cursor bounded blob store", () => { resetCursorBlobStateForTests(); expect(cursorBlobMetrics()).toMatchObject({ count: 0, totalBytes: 0, localBytes: 0, pinnedBytes: 0 }); }); + + test("fresh blob inserts accumulate class bytes across remote and local entries", () => { + const originalNow = Date.now; + let now = 1_000; + Date.now = () => now; + try { + setCursorBlobLimitsForTests({ ttlMs: 50, maxEntryBytes: 8, maxTotalBytes: 64 }); + const remoteId = sha256(bytes("rem")); + setBlobReply(remoteId, bytes("rem")); + expect(cursorBlobMetrics()).toMatchObject({ + count: 1, + totalBytes: 3, + keyBytes: 66, + localBytes: 0, + pinnedBytes: 3 + 66, + oldestAt: null, + }); + expect(cursorBlobRetainedStoreSnapshot()).toMatchObject({ + count: 1, + bytes: 3 + 66, + evictableBytes: 0, + pinnedBytes: 3 + 66, + oldestAt: null, + }); + now = 1_001; + const localId = storeCursorBlob(bytes("loc")); + expect(cursorBlobMetrics()).toMatchObject({ + count: 2, + totalBytes: 6, + keyBytes: 132, + localBytes: 3, + pinnedBytes: 3 + 66, + oldestAt: 1_001, + }); + expect(cursorBlobRetainedStoreSnapshot()).toMatchObject({ + count: 2, + bytes: 6 + 132, + evictableBytes: 3 + 66, + pinnedBytes: 3 + 66, + oldestAt: 1_001, + }); + expectBlobHit(remoteId, bytes("rem")); + expectBlobHit(localId, bytes("loc")); + expect(cursorBlobStoreDebugSnapshotForTests().map(row => row.provenance).sort()).toEqual([ + "local-regenerated", + "remote-setBlobArgs", + ]); + } finally { + Date.now = originalNow; + } + }); + + test("releasing an expired pin still TTL-purges that row on the next write", () => { + const originalNow = Date.now; + let now = 100; + Date.now = () => now; + try { + setCursorBlobLimitsForTests({ ttlMs: 10, maxEntryBytes: 8, maxTotalBytes: 64, maxEntries: 8 }); + const scope = createCursorBlobRequestScope(); + const pinnedId = sha256(bytes("pin")); + setBlobReply(pinnedId, bytes("pin"), 1, scope); + sealCursorBlobRequestScope(scope); + now = 105; + const liveId = sha256(bytes("live")); + setBlobReply(liveId, bytes("live")); + now = 111; + releaseCursorBlobRequestScope(scope); + const laterId = sha256(bytes("new")); + setBlobReply(laterId, bytes("new")); + // Observe before getBlob can lazily delete an expired entry itself. + expect(cursorBlobMetrics()).toMatchObject({ count: 2, totalBytes: 7, keyBytes: 132 }); + expect(cursorBlobRetainedStoreSnapshot()).toMatchObject({ + count: 2, bytes: 7 + 132, pinnedBytes: 7 + 132, evictableBytes: 0, + }); + expectBlobMiss(pinnedId); + expectBlobHit(liveId, bytes("live")); + expectBlobHit(laterId, bytes("new")); + } finally { + Date.now = originalNow; + } + }); }); describe("Cursor blob ID key channel bounds", () => { From 0f658a0b4a0e5223a7c1415bd09a544975baa433 Mon Sep 17 00:00:00 2001 From: t Date: Sun, 6 Sep 2026 22:12:22 +0900 Subject: [PATCH 4/8] docs: lock the 2.44.0 release promotion and verification roadmap --- .../_plan/260906_release_244_publish/000_plan.md | 14 ++++++++++++++ .../260906_release_244_publish/010_roadmap.md | 6 ++++++ .../260906_release_244_publish/020_integrate.md | 7 +++++++ .../260906_release_244_publish/030_dev_bump.md | 5 +++++ .../260906_release_244_publish/040_preview.md | 4 ++++ .../_plan/260906_release_244_publish/050_stable.md | 4 ++++ 6 files changed, 40 insertions(+) create mode 100644 devlog/_plan/260906_release_244_publish/000_plan.md create mode 100644 devlog/_plan/260906_release_244_publish/010_roadmap.md create mode 100644 devlog/_plan/260906_release_244_publish/020_integrate.md create mode 100644 devlog/_plan/260906_release_244_publish/030_dev_bump.md create mode 100644 devlog/_plan/260906_release_244_publish/040_preview.md create mode 100644 devlog/_plan/260906_release_244_publish/050_stable.md diff --git a/devlog/_plan/260906_release_244_publish/000_plan.md b/devlog/_plan/260906_release_244_publish/000_plan.md new file mode 100644 index 00000000000..30b77876bad --- /dev/null +++ b/devlog/_plan/260906_release_244_publish/000_plan.md @@ -0,0 +1,14 @@ +# 2.44.0 release train + +Loop: satisfy-spec / C4. Trigger: maintainer explicitly requested main + preview merges and deployment after PR3771 CI passes, with cxc-loop. Goal: verified npm preview and stable2.44.0 plus release docs. Non-goals: local suites/typecheck/build, liveKiro, unrelated features, native stacks, rebase, direct protected-ref pushes, credential/settings changes. Verifier: GitHub exact-SHA jobs and actual Test steps, release workflow immutable SHA guards, npm metadata/digests and source-bound docs deploy. Stop: all five units done with receipts; never count pending/skipped as pass. Memory artifact: this numbered unit + .tmp/release-244 + bound goalplan. Outcomes: DONE only with published channel proof; failed gates remain unresolved; ambiguous publish requires registry inspection. Escalation: missing actual account authority or outstanding maintainer objection, unplanned security defect, or exhausted evidence-driven attempts. Existing GitHub/npm OIDC access only; user supplied no numeric token/time budget. Operational review checkpoint: six hours or five failed evidence-driven attempts per release surface; do not call that success. Leaves have read-only audit scope; main reclaims failed dispatches. + +## Dependency order +1. Roadmap docs only (010). +2. Exact regression candidate integration and freeze (020). +3. dev pre-move2.45.0 (030). +4. Independent preview promotion, dry run and publication (040). +5. Independent stable promotion, dry run, publication and docs proof (050). + +Fresh baseline: main06ec553630fa2ee51a96b5cbf694089021249194/latest2.43.0; preview53c784c2a635b061799e4f7542432a921f548bf9/2.43.0-preview.20260906; devbd1cda99c162e3b4b41b14f6ad5ca2cf6f1a1f03. Candidate69f9e07c4fa7b80bcda9e4ba28e3c64f42187828 includes that dev. Service34034184142 all3pass; manualCI34034178072 stillrunning. #3763 deferred documentation remains user-withdrawn; #3644 field report remains unresolved, with no runtime-fix claim. This train does not silently reinstate either task. + +Authority: MAINTAINERS.md and scripts/release.ts / .github/workflows/release.yml. Local release helper is not invoked because it runs local suites and can push. Existing workflows perform build/audit/pack/publication on hosted runners. No new runtime field/enum or enforcement is added. GitHub required checks/immutable workflow guards are enforcement; manual admin integration remains bypassable owner authority and is documented accurately. SoT sync: no architecture/CLI contract changes; public release notes generated by the existing changelog builder. Existing dashboard evidence from prior verification is reused with exact source provenance, never claimed as a new render. diff --git a/devlog/_plan/260906_release_244_publish/010_roadmap.md b/devlog/_plan/260906_release_244_publish/010_roadmap.md new file mode 100644 index 00000000000..4b176d7ba31 --- /dev/null +++ b/devlog/_plan/260906_release_244_publish/010_roadmap.md @@ -0,0 +1,6 @@ +# Roadmap lock +Dependencies: none; consumes previous verification-only conclusion, whose no-release limit is superseded by the new explicit maintainer request. +NEW local numbered000/010/020/030/040/050 documents and goalplan. No product delta or remote publication in this cycle. Before: no release-authorized active plan. After: dependency-ordered audited plans with all final-head/registry criteria. Review complete docs and live workflow inputs; source audit commands are read-only. Check `git diff --check`, required doc existence and nonempty criteria; these verify documents, not product runtime. Commit these records on codex/release-244-publish-07c0 only. Keep this commit out of PR3771's already-running head. D records independent audit and next020. Existing no-local-suite constraint still applies. + +## Locked CI event contract after independent audit +Windows1/6 through6/6 and macOScontrol are RC/#3771 validation gates. For version-only independentpreview/main promotions, require each finalSHA's successful push-event Cross-platformCI and sameSHAServiceLifecycle; do not launch laneall on a releasebranch while its pushrun is active, because branch-ref cancel-in-progress can cancel the requiredpushrun. If any runtime/source drift from frozenRC appears, stoppromotion and returnto RCvalidation. An extra manualrun, if actuallyneeded, starts only afterpushCI completion. This is the predeclared gate mapping, not a waiver of a failedtest. diff --git a/devlog/_plan/260906_release_244_publish/020_integrate.md b/devlog/_plan/260906_release_244_publish/020_integrate.md new file mode 100644 index 00000000000..3bae9b53511 --- /dev/null +++ b/devlog/_plan/260906_release_244_publish/020_integrate.md @@ -0,0 +1,7 @@ +# Regression integration and immutable RC +Dependencies: roadmap. No new code is planned: PR3771 already contains tests/preload.ts, tests/ci-workflows/test-runner.test.ts, src/adapters/cursor/native-exec.ts and tests/providers/cursor/cursor-blob.test.ts. Before: open draft69f9e07c4. After: reviewed green PR squashed into dev and recorded integrated RC with package2.44.0. +Read `gh pr view 3771 --json headRefOid,baseRefName,state,statusCheckRollup,reviews` and GraphQL reviewThreads plus live maintainer permission. Read manualCI34034178072 and lifecycle34034184142, asserting head equality and actual Windows1..6 Test/macOScontrol execution. Original25 Windows cases must pass; Cursor4096/4097 original bound and added expiry/accounting tests must pass. Trigger scenarios are original failures and missingcapability/expired-pin cases already encoded in regression tests. If red, inspect failing job logs and only repair the scoped cause in an amended unit; no retry-as-fix. +When all required exacthead checks/reviews pass, record maintainer integration evidence in PR body, mark ready and `gh pr merge 3771 --admin --squash --match-head-commit `. Validate dev base immediately before merge. Fetchdev, verify merge commit ancestry, and record RC as the integrateddev commit. Any later devcommit is not silently added to RC. No directdevpush. Host CI verifier APIs already ran successfully (exit0) and read this exact head, while final conclusions remain pending. + +## Fresh release blocker discovered during roadmap audit +At69f9e07c4 manualCI34034178072 Windows3/6 job101489123778 fails tests/claude-integration/claude-desktop-remote-hub.test.ts stored-profile=true on its30second RemoteDesktop apply deadline; falsecase passes in61.7seconds total. macOScontrol is nowgreen. Extend this unit to diagnose/fix the newly observed Windows release-validation failure using explicit hypotheses and hosted baseline/candidate evidence, preserving original behavior assertions and isolation. Candidate file scope is that fixture and its direct CLI apply dependency only if diagnosis establishes runtimecause. No change justified by merely increasingbudget/retrying. Update this plan with exactdiff and independentreview before implementation. #3771 cannotmerge until allgates pass. diff --git a/devlog/_plan/260906_release_244_publish/030_dev_bump.md b/devlog/_plan/260906_release_244_publish/030_dev_bump.md new file mode 100644 index 00000000000..275e8961c70 --- /dev/null +++ b/devlog/_plan/260906_release_244_publish/030_dev_bump.md @@ -0,0 +1,5 @@ +# Development version pre-move +Dependencies: integrated frozen RC. MODIFY package.json only in a PR based on currentdev: version2.44.0 ->2.45.0, or NOOP only when freshdev already strictly outranks intendedstable2.44.0. No runtime/code/lock dependency change. Keep frozenRC at2.44.0. +Inspect the defaultmain version of dev-version-bump.yml. If workflow_dispatch exists, dispatch frommain with intended-version=2.44.0 mode=pre-move and use generatedPR. If older main has onlyworkflow_call, create the established one-file versionPR via scripts/bump-dev-version.ts (--help/CLI inspected first) or exact JSON version rewrite. No local build/tests. Push scopedbranch --no-verify; require hosted exacthead CI and reviewed version-onlydiff, then authorizedadminPR merge todev. Verify origin/dev:package.json and ancestry fresh. Releaseworkflow assert-ahead independently enforces pre-move. A version collision is a blocker, not an automatic unreviewed versionchoice. Capture sourceSHA/version/PR/run proof in this unit. + +Current defaultmain workflow_dispatch was fetched and confirmed (exit0) on2026-09-06; the manual workflow path is selected. diff --git a/devlog/_plan/260906_release_244_publish/040_preview.md b/devlog/_plan/260906_release_244_publish/040_preview.md new file mode 100644 index 00000000000..a2c773e8be2 --- /dev/null +++ b/devlog/_plan/260906_release_244_publish/040_preview.md @@ -0,0 +1,4 @@ +# Preview promotion and publication +Dependencies: frozenRC plus verified dev-ahead. NEW ordinary promotionbranch from freshpreview; MERGE immutableRC with normalmerge (no rebase/force); resolve only reviewed branch/version conflicts. MODIFY package.json fromRC2.44.0 to2.44.0-preview.YYYYMMDD (execution-day UTCdate, choose next suffix only if existing version requires it after explicit freshregistry inspection). All other source tree entries must equalRC; assert `git diff --exit-code RC HEAD -- . :(exclude)package.json` after resolving history. No2.45.0dev pre-move in releasecandidate. +Open template-complete preview promotionPR; include actualprior dashboard screenshot and exact source evidence because release delta includes dashboard changes. Target exemption is release promotion, not a featurePR. Inspect required checks and maintainer objections; user explicitly authorizes this promotion. Merge using allowedPR method/admin as authorized with exacthead guard; neverdirectpreviewpush. Fetch finalpreviewSHA, proveRCancestor and packageversion. +For finalpreviewSHA require successful ci.yml eventpush and service-lifecycle.yml sameSHA (manual lifecycle if auto absent); Windows6 and macOScontrol are already required on the unchangedRC; do not claim push'sskippedWindows tested. Never dispatch manualCI on preview/main while the requiredpushrun is active (sharedref concurrency cancels it). Runtime drift returns toRCvalidation beforepromotion. RC all-lane evidence is separate from final push gate; source-only-equivalent versiondelta is documented. Dry-run release.yml frompreview with version, tag=preview, expected-sha full40 and dry-run=true. Waitsuccess, inspect dry-run buildpack, then sameSHA dry-run=false. Neveroverlap publish workflows; existing release concurrencyserializes. Read back npm exactversion metadata+dist-tag+gitHead+dist.integrity+attestations, verify tarball digest andprovenance against releaseSHA; verify vversiontag and prerelease. No localinstall/suite/build. If publish response ambiguous inspectregistry/tagfirst, never duplicatepublish. Oldpublishedversion remainsrollback installtarget; changing dist-tag/rollback is onlydone if actuallyneeded andauthorized, not as a test. diff --git a/devlog/_plan/260906_release_244_publish/050_stable.md b/devlog/_plan/260906_release_244_publish/050_stable.md new file mode 100644 index 00000000000..fb87ff18062 --- /dev/null +++ b/devlog/_plan/260906_release_244_publish/050_stable.md @@ -0,0 +1,4 @@ +# Stable promotion, publication, and closure +Dependencies: previewpublished proof, immutableRC and devahead. NEW ordinary main promotionbranch from freshmain; merge the SAME RC independently, preservingmainhistory. Finaltree equalsRC and package2.44.0; do not merge previewversion ordev2.45.0. Resolve explicit conflicts and verify RC ancestry and tree identity. PromotionPR uses full template, release exemption, previous dashboard screenshot/evidence and author attribution preserved by originalhistory. Honor outstandingmaintainer objections and exacthead checks; allowedadminmerge remains explicitowner action, not independentapproval. +After mainPRmerge fetchfinalmainSHA and require sameSHA successful push-event CI plus lifecycle3. Run hosted release.yml dry-run=true then false frommain, version=2.44.0 tag=latest expected-sha40; serialize afterpreviewpublication. Verify npm latest/version/gitHead/integrity/provenance, tagv2.44.0 and GitHub release target. Read deploy-docs.yml triggers and default branchsource; wait successful Pages deployment for finalmainSHA or dispatch the existingworkflow if required bypathfilters, then inspect site response/sourceproof. If workflowredafter acceptednpm publish, inspectactualregistry/tag/release state before recovery; create only missingmetadata using existing validatedchangelog, never republishsameversion. +Finalcheck rereads dev/main/preview refs and versions, verifiesboth tags/artifactdigests and publishedinstall-smoke evidence from releaseCI. Record GUI/runtime field limitations accurately (Kiroquota absent, Windowsquota fieldissue3644 not newlyvalidated). Update boundgoalplan/ledger and pauseheartbeat3771-ci-dev onlyafter allcriteria met. Keep cleanup scoped: no worktreedeletion, no massbranchcleanup, no localdaemon/installmutation. D reports both publishedversions and proofURLs. Any incompletegate remainsopen. From 7fb9683db31638b7a693a2aa8d73c1c1bfe4dd7e Mon Sep 17 00:00:00 2001 From: t Date: Sun, 6 Sep 2026 22:25:17 +0900 Subject: [PATCH 5/8] docs: record polling-only release tracking and Windows diagnostic scope --- devlog/_plan/260906_release_244_publish/000_plan.md | 4 +++- devlog/_plan/260906_release_244_publish/020_integrate.md | 6 ++++++ devlog/_plan/260906_release_244_publish/050_stable.md | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/devlog/_plan/260906_release_244_publish/000_plan.md b/devlog/_plan/260906_release_244_publish/000_plan.md index 30b77876bad..02192a924f1 100644 --- a/devlog/_plan/260906_release_244_publish/000_plan.md +++ b/devlog/_plan/260906_release_244_publish/000_plan.md @@ -9,6 +9,8 @@ Loop: satisfy-spec / C4. Trigger: maintainer explicitly requested main + preview 4. Independent preview promotion, dry run and publication (040). 5. Independent stable promotion, dry run, publication and docs proof (050). -Fresh baseline: main06ec553630fa2ee51a96b5cbf694089021249194/latest2.43.0; preview53c784c2a635b061799e4f7542432a921f548bf9/2.43.0-preview.20260906; devbd1cda99c162e3b4b41b14f6ad5ca2cf6f1a1f03. Candidate69f9e07c4fa7b80bcda9e4ba28e3c64f42187828 includes that dev. Service34034184142 all3pass; manualCI34034178072 stillrunning. #3763 deferred documentation remains user-withdrawn; #3644 field report remains unresolved, with no runtime-fix claim. This train does not silently reinstate either task. +Fresh baseline: main06ec553630fa2ee51a96b5cbf694089021249194/latest2.43.0; preview53c784c2a635b061799e4f7542432a921f548bf9/2.43.0-preview.20260906; devbd1cda99c162e3b4b41b14f6ad5ca2cf6f1a1f03. Candidate69f9e07c4fa7b80bcda9e4ba28e3c64f42187828 includes that dev. Service34034184142 all3pass; manualCI34034178072 completed with one Windows3/6 cold-restart apply deadlinefailure; originalWindows25 and macOScontrol20404/0 nowpass. #3763 deferred documentation remains user-withdrawn; #3644 field report remains unresolved, with no runtime-fix claim. This train does not silently reinstate either task. Authority: MAINTAINERS.md and scripts/release.ts / .github/workflows/release.yml. Local release helper is not invoked because it runs local suites and can push. Existing workflows perform build/audit/pack/publication on hosted runners. No new runtime field/enum or enforcement is added. GitHub required checks/immutable workflow guards are enforcement; manual admin integration remains bypassable owner authority and is documented accurately. SoT sync: no architecture/CLI contract changes; public release notes generated by the existing changelog builder. Existing dashboard evidence from prior verification is reused with exact source provenance, never claimed as a new render. + +Latest user steering: no heartbeat automation; track all CI and release workflows by direct bounded polling in this task. Automation3771-ci-dev is absent (delete returnednot_found). No replacement automation is authorized. diff --git a/devlog/_plan/260906_release_244_publish/020_integrate.md b/devlog/_plan/260906_release_244_publish/020_integrate.md index 3bae9b53511..82e96f34913 100644 --- a/devlog/_plan/260906_release_244_publish/020_integrate.md +++ b/devlog/_plan/260906_release_244_publish/020_integrate.md @@ -5,3 +5,9 @@ When all required exacthead checks/reviews pass, record maintainer integration e ## Fresh release blocker discovered during roadmap audit At69f9e07c4 manualCI34034178072 Windows3/6 job101489123778 fails tests/claude-integration/claude-desktop-remote-hub.test.ts stored-profile=true on its30second RemoteDesktop apply deadline; falsecase passes in61.7seconds total. macOScontrol is nowgreen. Extend this unit to diagnose/fix the newly observed Windows release-validation failure using explicit hypotheses and hosted baseline/candidate evidence, preserving original behavior assertions and isolation. Candidate file scope is that fixture and its direct CLI apply dependency only if diagnosis establishes runtimecause. No change justified by merely increasingbudget/retrying. Update this plan with exactdiff and independentreview before implementation. #3771 cannotmerge until allgates pass. + +## Diagnostic diff before repair +PreviousD locked releaseorder and gateeventcontract. H1 loopbackdownloadfails5secondbound; H2 Windowschildnativework (PowerShell/icacls) occupies30seconddeadline; H3 commandfails to reachprocess.exit. Falsifiers: fetchstart/endtiming, nativeprocesscategory+duration, processexitmarker and alive-at-deadline. Per-process timers afterprocess.exit cannotexplainfailure. +FirstB diagnostic only: temporarycontents:read Windowsworkflow on separatecodex/diagnose-desktop-apply-07c0 rooted at69f9; pinnedcheckout/setup, existinginstall/build. MODIFY tests/fixtures/claude-desktop-network-guard.ts onrunner to wrapBun.spawnSync/fetch/process.exit with fixedevent/category/millisecond receipts to a fixtureownedfile; preserveoriginalnetworkpermit. MODIFY failingtest onrunner only to print atmost512fixedrecords in a finallyblock aroundunchanged30000msdeadline. No rawargs/URLs/tokens/stdout/stderr or productionpatch. Exact instrumentation and workflow are .tmp/release-244/ci-repair/instrument-desktop-apply.py anddesktop-apply-ci.yml. BaselineWindowstrue/falsepassed82.1/79.5seconds total; candidate69.3failure/61.7pass doesnotisolateapplycost. Reviewdiagnosticthenexecute; use findings to amend exactrepairdiff and re-audit before production/testpatch. + +Diagnostic audit synthesis: reviewerconfirmedpermissions/pins/networkguard/30sdeadline andprivacyscope. Added asynchronousBun.spawn completiontraces (not justspawnSync) tocover asyncACL. Removedredundantglobal--timeout60000 fromisolateddiagnostic (eachcasealready240000 andcleanup90000). JSONLsplit findingrebuffed withgeneratedTS static inspection: Pythonwrites oneescaped\\n intoTSstring, whose runtimevalueisnewline; replacingitwithphysicalnewlinewouldbreakTS. No repositorytests/typecheck/buildran; onlyfilegenerationintoscratchwasinspected. diff --git a/devlog/_plan/260906_release_244_publish/050_stable.md b/devlog/_plan/260906_release_244_publish/050_stable.md index fb87ff18062..797a7496f0b 100644 --- a/devlog/_plan/260906_release_244_publish/050_stable.md +++ b/devlog/_plan/260906_release_244_publish/050_stable.md @@ -1,4 +1,4 @@ # Stable promotion, publication, and closure Dependencies: previewpublished proof, immutableRC and devahead. NEW ordinary main promotionbranch from freshmain; merge the SAME RC independently, preservingmainhistory. Finaltree equalsRC and package2.44.0; do not merge previewversion ordev2.45.0. Resolve explicit conflicts and verify RC ancestry and tree identity. PromotionPR uses full template, release exemption, previous dashboard screenshot/evidence and author attribution preserved by originalhistory. Honor outstandingmaintainer objections and exacthead checks; allowedadminmerge remains explicitowner action, not independentapproval. After mainPRmerge fetchfinalmainSHA and require sameSHA successful push-event CI plus lifecycle3. Run hosted release.yml dry-run=true then false frommain, version=2.44.0 tag=latest expected-sha40; serialize afterpreviewpublication. Verify npm latest/version/gitHead/integrity/provenance, tagv2.44.0 and GitHub release target. Read deploy-docs.yml triggers and default branchsource; wait successful Pages deployment for finalmainSHA or dispatch the existingworkflow if required bypathfilters, then inspect site response/sourceproof. If workflowredafter acceptednpm publish, inspectactualregistry/tag/release state before recovery; create only missingmetadata using existing validatedchangelog, never republishsameversion. -Finalcheck rereads dev/main/preview refs and versions, verifiesboth tags/artifactdigests and publishedinstall-smoke evidence from releaseCI. Record GUI/runtime field limitations accurately (Kiroquota absent, Windowsquota fieldissue3644 not newlyvalidated). Update boundgoalplan/ledger and pauseheartbeat3771-ci-dev onlyafter allcriteria met. Keep cleanup scoped: no worktreedeletion, no massbranchcleanup, no localdaemon/installmutation. D reports both publishedversions and proofURLs. Any incompletegate remainsopen. +Finalcheck rereads dev/main/preview refs and versions, verifiesboth tags/artifactdigests and publishedinstall-smoke evidence from releaseCI. Record GUI/runtime field limitations accurately (Kiroquota absent, Windowsquota fieldissue3644 not newlyvalidated). Update boundgoalplan/ledger and verify no heartbeat automation remains and complete directpoll tracking after allcriteria met. Keep cleanup scoped: no worktreedeletion, no massbranchcleanup, no localdaemon/installmutation. D reports both publishedversions and proofURLs. Any incompletegate remainsopen. From 9d624987cac669fe75276e081a129067e75058c0 Mon Sep 17 00:00:00 2001 From: t Date: Sun, 6 Sep 2026 22:40:46 +0900 Subject: [PATCH 6/8] test(desktop): give CLI apply its subprocess budget after measured lookup cost --- .../_plan/260906_release_244_publish/020_integrate.md | 10 ++++++++++ .../claude-desktop-remote-hub.test.ts | 5 ++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/devlog/_plan/260906_release_244_publish/020_integrate.md b/devlog/_plan/260906_release_244_publish/020_integrate.md index 82e96f34913..405c478e557 100644 --- a/devlog/_plan/260906_release_244_publish/020_integrate.md +++ b/devlog/_plan/260906_release_244_publish/020_integrate.md @@ -11,3 +11,13 @@ PreviousD locked releaseorder and gateeventcontract. H1 loopbackdownloadfails5se FirstB diagnostic only: temporarycontents:read Windowsworkflow on separatecodex/diagnose-desktop-apply-07c0 rooted at69f9; pinnedcheckout/setup, existinginstall/build. MODIFY tests/fixtures/claude-desktop-network-guard.ts onrunner to wrapBun.spawnSync/fetch/process.exit with fixedevent/category/millisecond receipts to a fixtureownedfile; preserveoriginalnetworkpermit. MODIFY failingtest onrunner only to print atmost512fixedrecords in a finallyblock aroundunchanged30000msdeadline. No rawargs/URLs/tokens/stdout/stderr or productionpatch. Exact instrumentation and workflow are .tmp/release-244/ci-repair/instrument-desktop-apply.py anddesktop-apply-ci.yml. BaselineWindowstrue/falsepassed82.1/79.5seconds total; candidate69.3failure/61.7pass doesnotisolateapplycost. Reviewdiagnosticthenexecute; use findings to amend exactrepairdiff and re-audit before production/testpatch. Diagnostic audit synthesis: reviewerconfirmedpermissions/pins/networkguard/30sdeadline andprivacyscope. Added asynchronousBun.spawn completiontraces (not justspawnSync) tocover asyncACL. Removedredundantglobal--timeout60000 fromisolateddiagnostic (eachcasealready240000 andcleanup90000). JSONLsplit findingrebuffed withgeneratedTS static inspection: Pythonwrites oneescaped\\n intoTSstring, whose runtimevalueisnewline; replacingitwithphysicalnewlinewouldbreakTS. No repositorytests/typecheck/buildran; onlyfilegenerationintoscratchwasinspected. + +## Measured cause and proposed one-file harness repair +Diagnostic34035944642 at9358fad1: apply25,929ms exit0; nativeknownfolderPowerShell22,811ms, SID197ms, hubdownload10ms, ACLsteps16-19ms each, registry24/16ms, process.exit0. H1downloadstall and H3exit/lingeringhandle rejected in this trace. H2 refined to actualWindowsknownfolder lookup cost, notACL. src/codex/user-identity.ts explicitly gives this lookup30seconds; an end-to-end30secondapply test budget can expire before a validnear-budget lookup plus requiredCLIwork finishes. Producttimeout remains30seconds. +Proposed MODIFY tests/claude-integration/claude-desktop-remote-hub.test.ts ONLY: importexistingSPAWN_BUDGET_MS from ../helpers/test-budget; replace apply.exited30_000 withSPAWN_BUDGET_MS(45_000). Explain measuredlookupcost in comment. Preserveoveralltest240s, cleanup90s, networkguard and allmodel/profile/restartassertions. No productguard/ACL/identity changes. Beforepermanentedit, hostedcontrolledslowlookup: nativePowerShell command pads its realexecution to28s inside the unchangedproduct30sbound, only in diagnosticclient. Original30sbudget mustfaildeadline;45s candidate mustpassoriginalassertions. Then removeinjectedslowlookup and mutate productionapplysnapshotchosenalias20260211->validbutwrong20260911; the45s test mustfailonmodelassertion (notdeadline), satisfyingtests/helpers/test-budget.ts requiredablation. Followwith uninstrumentedexactheadall-laneCI andlifecycle. No retry-as-fix and no change to4096/4097or original25cases. + +Independent Hilbert review: VERDICT PASS. The30s wholeCLI deadline is narrower than the product's30s lookup plus measuredrequiredwork; existing45sSPAWN_BUDGET remainsbelow240stest. Acceptance remainsconditional on controlledvalid28slookup red/green and wrongalias modelassertion ablation. Diagnostic34036362222 at29fa76f07 isrunning these onhostedWindows with unchangedproductguards. No permanenttestbudgetedit yet. Primaryoperator explicitly requested directpoll; noheartbeat exists. + +## Hosted proof and permanent delta +34036362222 (29fa76f07): controllednativeknownfolderlookup28,226ms completedwithinproduct30s; oldwholeCLIdeadlinefailed30,004ms withchildstillalive. Canonical45s case exited0 at31,381ms andall22originalassertionspassed. Itsablationstep didnotexecute because diagnosticPythondefaultCP1252 couldnotreadKoreanUTF8source; notaproductfailure. +34036626846 (247651739): UTF8-correct standaloneablationran with45sbudget; validbutwrong20260911aliasmadeoriginalwritten.inferenceModels.toEqual(snapshot.models) fail in57,052ms testcase, withoutapplydeadlinefailure. This proves thelongerbudgetdoesnotmaskthemodelidentityregression. Permanentdelta nowonlyimportsSPAWN_BUDGET_MS andusesitforapply.child.exited; allproducttimeouts/guards/assertions unchanged. Fulluninstrumentedlatest-headCI/lifecyclewillrunagain beforeintegration. Diagnosticworkflows andfaultinjections remainoffPR. diff --git a/tests/claude-integration/claude-desktop-remote-hub.test.ts b/tests/claude-integration/claude-desktop-remote-hub.test.ts index a8a229fe0c9..ed2c5ebf812 100644 --- a/tests/claude-integration/claude-desktop-remote-hub.test.ts +++ b/tests/claude-integration/claude-desktop-remote-hub.test.ts @@ -7,6 +7,7 @@ import type { OcxConfig } from "../../src/types"; import type { Desktop3pModelEntry } from "../../src/claude/desktop-3p"; import { repoPath, fixturePath } from "../helpers/repo-root"; import { removeTreeWithRetry } from "../helpers/remove-tree"; +import { SPAWN_BUDGET_MS } from "../helpers/test-budget"; const DATA_KEY = "test-key"; const cliPath = repoPath("src/cli/index.ts"); @@ -209,7 +210,9 @@ for (const storedProfile of [true, false]) { else expect(chosenEntry!.name).toMatch(/^claude-opus-4-8-[a-z][a-z0-9]{2}$/); const apply = spawnOwned(client, ["claude", "desktop", "apply", "--static"]); - const appliedCode = await within(apply.child.exited, 30_000, "Remote Desktop apply deadline"); + // The Windows known-folder lookup alone may validly use its 30s budget + // (22.8s in hosted tracing); leave room for the rest of this real CLI apply. + const appliedCode = await within(apply.child.exited, SPAWN_BUDGET_MS, "Remote Desktop apply deadline"); const appliedOutput = await within(Promise.all([apply.stdout, apply.stderr]), 5_000, "Apply output drain deadline"); if (appliedCode !== 0) throw new Error("Remote Desktop apply failed: " + appliedOutput[1]); expect(appliedOutput.join("\n")).not.toContain(DATA_KEY); From 921828c6720e775da9127edad60e91be68256ae6 Mon Sep 17 00:00:00 2001 From: t Date: Sun, 6 Sep 2026 22:53:10 +0900 Subject: [PATCH 7/8] docs: clarify release scope and exact verification commands --- devlog/_plan/260906_release_244_publish/020_integrate.md | 2 +- devlog/_plan/260906_release_244_publish/040_preview.md | 2 +- devlog/_plan/260906_release_244_publish/050_stable.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/devlog/_plan/260906_release_244_publish/020_integrate.md b/devlog/_plan/260906_release_244_publish/020_integrate.md index 405c478e557..3ba16b62adb 100644 --- a/devlog/_plan/260906_release_244_publish/020_integrate.md +++ b/devlog/_plan/260906_release_244_publish/020_integrate.md @@ -1,5 +1,5 @@ # Regression integration and immutable RC -Dependencies: roadmap. No new code is planned: PR3771 already contains tests/preload.ts, tests/ci-workflows/test-runner.test.ts, src/adapters/cursor/native-exec.ts and tests/providers/cursor/cursor-blob.test.ts. Before: open draft69f9e07c4. After: reviewed green PR squashed into dev and recorded integrated RC with package2.44.0. +Dependencies: roadmap. Initial pre-diagnostic scope: no new production code was planned; the later test-only SPAWN_BUDGET_MS change for apply.child.exited is recorded below. PR3771 already contains tests/preload.ts, tests/ci-workflows/test-runner.test.ts, src/adapters/cursor/native-exec.ts and tests/providers/cursor/cursor-blob.test.ts. Before: open draft69f9e07c4. After: reviewed green PR squashed into dev and recorded integrated RC with package2.44.0. Read `gh pr view 3771 --json headRefOid,baseRefName,state,statusCheckRollup,reviews` and GraphQL reviewThreads plus live maintainer permission. Read manualCI34034178072 and lifecycle34034184142, asserting head equality and actual Windows1..6 Test/macOScontrol execution. Original25 Windows cases must pass; Cursor4096/4097 original bound and added expiry/accounting tests must pass. Trigger scenarios are original failures and missingcapability/expired-pin cases already encoded in regression tests. If red, inspect failing job logs and only repair the scoped cause in an amended unit; no retry-as-fix. When all required exacthead checks/reviews pass, record maintainer integration evidence in PR body, mark ready and `gh pr merge 3771 --admin --squash --match-head-commit `. Validate dev base immediately before merge. Fetchdev, verify merge commit ancestry, and record RC as the integrateddev commit. Any later devcommit is not silently added to RC. No directdevpush. Host CI verifier APIs already ran successfully (exit0) and read this exact head, while final conclusions remain pending. diff --git a/devlog/_plan/260906_release_244_publish/040_preview.md b/devlog/_plan/260906_release_244_publish/040_preview.md index a2c773e8be2..4f861e6178a 100644 --- a/devlog/_plan/260906_release_244_publish/040_preview.md +++ b/devlog/_plan/260906_release_244_publish/040_preview.md @@ -1,4 +1,4 @@ # Preview promotion and publication -Dependencies: frozenRC plus verified dev-ahead. NEW ordinary promotionbranch from freshpreview; MERGE immutableRC with normalmerge (no rebase/force); resolve only reviewed branch/version conflicts. MODIFY package.json fromRC2.44.0 to2.44.0-preview.YYYYMMDD (execution-day UTCdate, choose next suffix only if existing version requires it after explicit freshregistry inspection). All other source tree entries must equalRC; assert `git diff --exit-code RC HEAD -- . :(exclude)package.json` after resolving history. No2.45.0dev pre-move in releasecandidate. +Dependencies: frozenRC plus verified dev-ahead. NEW ordinary promotionbranch from freshpreview; MERGE immutableRC with normalmerge (no rebase/force); resolve only reviewed branch/version conflicts. MODIFY package.json fromRC2.44.0 to2.44.0-preview.YYYYMMDD (execution-day UTCdate, choose next suffix only if existing version requires it after explicit freshregistry inspection). All other source tree entries must equalRC; assert `git diff --exit-code RC HEAD -- . ':(exclude)package.json'` after resolving history. No2.45.0dev pre-move in releasecandidate. Open template-complete preview promotionPR; include actualprior dashboard screenshot and exact source evidence because release delta includes dashboard changes. Target exemption is release promotion, not a featurePR. Inspect required checks and maintainer objections; user explicitly authorizes this promotion. Merge using allowedPR method/admin as authorized with exacthead guard; neverdirectpreviewpush. Fetch finalpreviewSHA, proveRCancestor and packageversion. For finalpreviewSHA require successful ci.yml eventpush and service-lifecycle.yml sameSHA (manual lifecycle if auto absent); Windows6 and macOScontrol are already required on the unchangedRC; do not claim push'sskippedWindows tested. Never dispatch manualCI on preview/main while the requiredpushrun is active (sharedref concurrency cancels it). Runtime drift returns toRCvalidation beforepromotion. RC all-lane evidence is separate from final push gate; source-only-equivalent versiondelta is documented. Dry-run release.yml frompreview with version, tag=preview, expected-sha full40 and dry-run=true. Waitsuccess, inspect dry-run buildpack, then sameSHA dry-run=false. Neveroverlap publish workflows; existing release concurrencyserializes. Read back npm exactversion metadata+dist-tag+gitHead+dist.integrity+attestations, verify tarball digest andprovenance against releaseSHA; verify vversiontag and prerelease. No localinstall/suite/build. If publish response ambiguous inspectregistry/tagfirst, never duplicatepublish. Oldpublishedversion remainsrollback installtarget; changing dist-tag/rollback is onlydone if actuallyneeded andauthorized, not as a test. diff --git a/devlog/_plan/260906_release_244_publish/050_stable.md b/devlog/_plan/260906_release_244_publish/050_stable.md index 797a7496f0b..2d1e2fc68e2 100644 --- a/devlog/_plan/260906_release_244_publish/050_stable.md +++ b/devlog/_plan/260906_release_244_publish/050_stable.md @@ -1,4 +1,4 @@ # Stable promotion, publication, and closure Dependencies: previewpublished proof, immutableRC and devahead. NEW ordinary main promotionbranch from freshmain; merge the SAME RC independently, preservingmainhistory. Finaltree equalsRC and package2.44.0; do not merge previewversion ordev2.45.0. Resolve explicit conflicts and verify RC ancestry and tree identity. PromotionPR uses full template, release exemption, previous dashboard screenshot/evidence and author attribution preserved by originalhistory. Honor outstandingmaintainer objections and exacthead checks; allowedadminmerge remains explicitowner action, not independentapproval. -After mainPRmerge fetchfinalmainSHA and require sameSHA successful push-event CI plus lifecycle3. Run hosted release.yml dry-run=true then false frommain, version=2.44.0 tag=latest expected-sha40; serialize afterpreviewpublication. Verify npm latest/version/gitHead/integrity/provenance, tagv2.44.0 and GitHub release target. Read deploy-docs.yml triggers and default branchsource; wait successful Pages deployment for finalmainSHA or dispatch the existingworkflow if required bypathfilters, then inspect site response/sourceproof. If workflowredafter acceptednpm publish, inspectactualregistry/tag/release state before recovery; create only missingmetadata using existing validatedchangelog, never republishsameversion. +After mainPRmerge fetchfinalmainSHA and require sameSHA successful push-event CI plus a successful `service-lifecycle.yml` run for the same finalmainSHA, covering all three jobs. Run hosted release.yml dry-run=true then false frommain, version=2.44.0 tag=latest `expected-sha=`; serialize afterpreviewpublication. Verify npm latest/version/gitHead/integrity/provenance, tagv2.44.0 and GitHub release target. Read deploy-docs.yml triggers and default branchsource; wait successful Pages deployment for finalmainSHA or dispatch the existingworkflow if required bypathfilters, then inspect site response/sourceproof. If workflowredafter acceptednpm publish, inspectactualregistry/tag/release state before recovery; create only missingmetadata using existing validatedchangelog, never republishsameversion. Finalcheck rereads dev/main/preview refs and versions, verifiesboth tags/artifactdigests and publishedinstall-smoke evidence from releaseCI. Record GUI/runtime field limitations accurately (Kiroquota absent, Windowsquota fieldissue3644 not newlyvalidated). Update boundgoalplan/ledger and verify no heartbeat automation remains and complete directpoll tracking after allcriteria met. Keep cleanup scoped: no worktreedeletion, no massbranchcleanup, no localdaemon/installmutation. D reports both publishedversions and proofURLs. Any incompletegate remainsopen. From 176b7eb6609daf8874304357a7f388781695939a Mon Sep 17 00:00:00 2001 From: t Date: Mon, 7 Sep 2026 00:33:38 +0900 Subject: [PATCH 8/8] test(windows): isolate a prepared PowerShell cache and finish full shards --- .github/workflows/ci.yml | 6 +++- .../020_integrate.md | 20 +++++++++++++ .../260906_release_244_publish/040_preview.md | 2 +- tests/ci-workflows/ci-workflows.test.ts | 2 +- .../claude-desktop-remote-hub.test.ts | 28 +++++++++++++++++-- 5 files changed, 53 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b319437d14..8c15cb696ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -751,7 +751,11 @@ jobs: # the same truncation as above. The bound is kept; the work per shard is cut instead. # Six shards put each leg at roughly two-thirds of the four-shard wall time, back # inside the margin 25 was chosen to provide. - timeout-minutes: 25 + # Shard 1 of run 34036848646 then reached that wall with 2736 passing tests + # and no test failures. The matched tests were 25% slower than the prior + # complete run; about one minute of tests remained. Keep every test deadline + # and all six shards, but leave the whole batch and cleanup a 30-minute bound. + timeout-minutes: 30 strategy: fail-fast: false matrix: diff --git a/devlog/_plan/260906_release_244_publish/020_integrate.md b/devlog/_plan/260906_release_244_publish/020_integrate.md index 3ba16b62adb..4fdff56abdf 100644 --- a/devlog/_plan/260906_release_244_publish/020_integrate.md +++ b/devlog/_plan/260906_release_244_publish/020_integrate.md @@ -21,3 +21,23 @@ Independent Hilbert review: VERDICT PASS. The30s wholeCLI deadline is narrower t ## Hosted proof and permanent delta 34036362222 (29fa76f07): controllednativeknownfolderlookup28,226ms completedwithinproduct30s; oldwholeCLIdeadlinefailed30,004ms withchildstillalive. Canonical45s case exited0 at31,381ms andall22originalassertionspassed. Itsablationstep didnotexecute because diagnosticPythondefaultCP1252 couldnotreadKoreanUTF8source; notaproductfailure. 34036626846 (247651739): UTF8-correct standaloneablationran with45sbudget; validbutwrong20260911aliasmadeoriginalwritten.inferenceModels.toEqual(snapshot.models) fail in57,052ms testcase, withoutapplydeadlinefailure. This proves thelongerbudgetdoesnotmaskthemodelidentityregression. Permanentdelta nowonlyimportsSPAWN_BUDGET_MS andusesitforapply.child.exited; allproducttimeouts/guards/assertions unchanged. Fulluninstrumentedlatest-headCI/lifecyclewillrunagain beforeintegration. Diagnosticworkflows andfaultinjections remainoffPR. + +## Replan after remaining product refusal +C at9d624987c: Windows3/6 run34036848646 job101496387805 returnedclient_lifecycle_lock_failed in storedprofiletrue (72.4scase), whilefalsepassed102.8s. Thisisnot45sdeadline; nofurtherbudgetraise. Cdidnotpass, resettoPwithsameunfinishedunit. H1 identitylookuprefusal/timeout (knownfoldertrace22.8s); H2 ACL/filesystempreparation failure; H3 SQLite/namespace safetyrefusal. Classify error.cause codes usingfixedallowlist andidentity-timeout/namespacebooleans inthrowawaydiagnostic, plusnativeexitcode/timedOutflags. Threefreshsamplesmeasurefailurecondition ratherthanretryingforgreen; noobservedfailuremeansunresolved, notsuccess. Scriptsinstrument-lifecycle-cause.py/lifecycle-cause-ci.yml stay.tmp anddiagnosticrefonly, basedon921docsheadwhichpreserves9druntime. FutureFFIlookupoptionsare read-onlyresearch untilcauseconfirmed andsecurityreviewed. Priorinternalattemptcountisareviewcheckpoint, notuser-requestedterminationbudget; do not abandonthereleasegoalorclaimcompletion. + +Three-sample diagnostic34038264294 didnotreproduceclientrefusal (allpass; apply25.8-26.3s, nofailureflags). ItdoesnotclearfullCI. Nextboundedprobe keepsfullWindows3/6context alongsideisolatedcase, addingonlyfixedPowerShellphase timings aroundAdd-Type andSHGetKnownFolderPath. This separatescompiler/startupcost fromnativeAPIlatency beforeconsideringanyin-processFFIcall; a slowOSAPIwouldmake synchronousFFIanunsafeperformancefix. Exactscriptinstrument-known-folder-stages.py andworkflowknown-folder-stages-ci.yml arediagnosticonly. No budgetincrease orproductionlookupedit authorizedbyemptyflags. + +Newfixture hypothesis fromworkingcomparison: tests/codex-integration/codex-user-identity.test.ts givesrealchildprocessesownedTEMP/TMP andexistingLOCALAPPDATA within10schild/20stestbounds; Desktopfixtureallowlist omitsTEMP/TMP andpointsAPPDATA/LOCALAPPDATA atuncreateddirectories. Isolate missing/temp-only/profile-only/both withunchangedproductionlookupandallassertions. This canexplaincompilerlatencywithoutaproductrewrite; fixture-onlyrepairpreferredifmeasured. Diagnosticfixture-environment-ci.yml usesfourfixedmodes, freshWindowsjobs andprivateownedfolders. No nativeAPIcodechange. + +Fixtureenv experiment34039649747 rejectsTEMP/AppData-onlyrootcause: missing/temp/profile/both allretain22-27scompilationcost; no failureflags. Separatestageprobe establishesAdd-Type17.8s versusnativeAPI16ms, andfullshardclientcasespassed with16.7scompiler (hygiene failuresareexpectedbecauseitsdiagnosticCIyamlreplacesnormalworkflow). Thisdoesnotidentifytheoldgenericrefusal, butitproves a removablecompilerhotspot threatening the existing30schildbudget. + +Proposed experimentalruntime delta, notyetappliedtoproduct: replaceonlythefixedknown-folderAdd-Typebinding with public.NETFramework Reflection.Emit metadata in the same trustedPowerShellchild. Keep30stimeout, outputUTF16/base64, successfulcache, SIDquery, GUID, DEFAULT_PATH0x400/null-currenttoken, canonicalization andACLchecks. Noin-processFFI/unboundednativecall. DefinePInvokeMethod signatureGuid&,UInt32,IntPtr,IntPtr& ->Int32, Winapi,Unicode; PreserveSig required; outparameter metadata; FreeCoTaskMemfinallyevenfailure. DLLpathcomesfrom.NETSystemDirectory, notenv. Hostedprototype comparesunchangedlegacyC#referencepath, envshadowpath, negativeGUIDHRESULT, andfocusedidentity/Desktoptests beforepermanentpatch. Exactemit-known-folder.py andknown-folder-conformance.ts in.tmp. PrimaryMicrosoftDefinePInvokeMethod/AppDomain/PreserveSigdocs were opened; generatedpublicAPIexampleconfirms thismetadataapproach. Nativegenericfailure remainsunclassifieduntilfullgate; donotclaimitfixedfromprototypealone. + +Prototype security review (Locke): PASS forprototype-onlydispatch; originalreference savedbeforeedit, 9-argentrypoint/PreserveSig/finally-free,30sproductionbound,45slegacyoracleonly, no rawpathlogging. Run34040992290 atad982feb53 uses thisprobe andexistingfocusedtests. No productionlookupedit yet. + +Windows1 whole-job bound: cancelledrun completed2736passingtests versus2981in priorcompletedrun. Matchedtests took1244s vs996s (25%slower); remaining245tests took49.4s previously, about61.7s atobservedratio. Therewere no reportedtestfailuresbeforejobcancellation. Proposedci.yml platform-windows timeout25->30minutes gives finitebatch/cleanupmargin whilepreserving allsixshards, everytestcommand, per-testdeadlines and crash-onlyretry. Updateexistingci-workflows.test.ts budgetexpectation25->30. This isnot a code-failurewaiver and must stillcompletealltests; securityreviewmustconfirmno trigger/permission/runner/pin changes. + +Prototype34040992290 passedsamepath/shadow/HRESULTconformance (legacy3579ms, emitted402ms, shadow382ms, productionbound30000), and9focusedtests. Howeverminimal-environmentDesktopcasesstilltook82.7/76.9s. Thereforedo notlandtheproductionrewrite: itdoesnotremovefixture-pathlatency. Nextdiagnostic34041357794 isolatesPSModulePath, executablePATH/PATHEXT, andWindowsinfrastructureenv asfourstaticmodeswithoriginalproductionlookup. Theseareonlyrunner-localdiagnosticchanges; fullPATHinheritanceisnotauthorizedasapermanentfixturefixbecauseitwouldwidenexecutablevisibility. + +Confirmedfixturecause: keybisect34041973010 andsingle-variable34042207026 isolatePSModuleAnalysisCachePath. Inheritedpreparedcache: apply3731ms/AddType215ms; missing27309/22840ms, NUL23473/20008ms, emptyowned25964/22720ms. Thusfreshcacheanalysis—notjustC#binding—is thecost. Owned-copy34042446427 preservesoriginalcacheisolation andpassesoriginalscenario in12.9s withapply4632ms/AddType190ms. No productionlookuprewrite willland. +Permanentfixturechange: Windows-only ownedmodule-analysis-cache path seededbycopy from anabsolute, regular, non-symlinkparentcachewhenavailable; no blanketenv/PATHinheritance. Missing/staleseedorchangedsizefallsbacktocoldownedcache; otherIOfailuresremainfailureswithoutloggingprivatepaths. Alloriginalmodel/credential/restartassertionsandguardsremain. KeplerindependentreviewPASS forownedseedingandracehandling; Windows30minwholejobbound separatelyPASS. Fulllatest-headCIstillrequired. Allinterpreter/FFIexperimentsremainonlyondiagnosticrefs. diff --git a/devlog/_plan/260906_release_244_publish/040_preview.md b/devlog/_plan/260906_release_244_publish/040_preview.md index 4f861e6178a..1f45fb3ee53 100644 --- a/devlog/_plan/260906_release_244_publish/040_preview.md +++ b/devlog/_plan/260906_release_244_publish/040_preview.md @@ -1,4 +1,4 @@ # Preview promotion and publication -Dependencies: frozenRC plus verified dev-ahead. NEW ordinary promotionbranch from freshpreview; MERGE immutableRC with normalmerge (no rebase/force); resolve only reviewed branch/version conflicts. MODIFY package.json fromRC2.44.0 to2.44.0-preview.YYYYMMDD (execution-day UTCdate, choose next suffix only if existing version requires it after explicit freshregistry inspection). All other source tree entries must equalRC; assert `git diff --exit-code RC HEAD -- . ':(exclude)package.json'` after resolving history. No2.45.0dev pre-move in releasecandidate. +Dependencies: frozenRC plus verified dev-ahead. NEW ordinary promotionbranch from freshpreview; MERGE immutableRC with normalmerge (no rebase/force); resolve only reviewed branch/version conflicts. MODIFY package.json fromRC2.44.0 to2.44.0-preview.YYYYMMDD (execution-day date in the maintainer timezone (Asia/Seoul), choose next suffix only if existing version requires it after explicit freshregistry inspection). All other source tree entries must equalRC; assert `git diff --exit-code RC HEAD -- . ':(exclude)package.json'` after resolving history. No2.45.0dev pre-move in releasecandidate. Open template-complete preview promotionPR; include actualprior dashboard screenshot and exact source evidence because release delta includes dashboard changes. Target exemption is release promotion, not a featurePR. Inspect required checks and maintainer objections; user explicitly authorizes this promotion. Merge using allowedPR method/admin as authorized with exacthead guard; neverdirectpreviewpush. Fetch finalpreviewSHA, proveRCancestor and packageversion. For finalpreviewSHA require successful ci.yml eventpush and service-lifecycle.yml sameSHA (manual lifecycle if auto absent); Windows6 and macOScontrol are already required on the unchangedRC; do not claim push'sskippedWindows tested. Never dispatch manualCI on preview/main while the requiredpushrun is active (sharedref concurrency cancels it). Runtime drift returns toRCvalidation beforepromotion. RC all-lane evidence is separate from final push gate; source-only-equivalent versiondelta is documented. Dry-run release.yml frompreview with version, tag=preview, expected-sha full40 and dry-run=true. Waitsuccess, inspect dry-run buildpack, then sameSHA dry-run=false. Neveroverlap publish workflows; existing release concurrencyserializes. Read back npm exactversion metadata+dist-tag+gitHead+dist.integrity+attestations, verify tarball digest andprovenance against releaseSHA; verify vversiontag and prerelease. No localinstall/suite/build. If publish response ambiguous inspectregistry/tagfirst, never duplicatepublish. Oldpublishedversion remainsrollback installtarget; changing dist-tag/rollback is onlydone if actuallyneeded andauthorized, not as a test. diff --git a/tests/ci-workflows/ci-workflows.test.ts b/tests/ci-workflows/ci-workflows.test.ts index 5b2fa348146..ec2c919c456 100644 --- a/tests/ci-workflows/ci-workflows.test.ts +++ b/tests/ci-workflows/ci-workflows.test.ts @@ -118,7 +118,7 @@ describe("GitHub Actions hardening", () => { expect(ci.jobs?.["macos-control"]?.["timeout-minutes"]).toBe(30); // Higher than the Linux shards on purpose: at 15 the Windows leg cancelled a // shard mid-suite, which reports as neither pass nor fail (#2152). - expect(ci.jobs?.["platform-windows"]?.["timeout-minutes"]).toBe(25); + expect(ci.jobs?.["platform-windows"]?.["timeout-minutes"]).toBe(30); expect(ci.jobs?.["keyring-smoke"]?.["timeout-minutes"]).toBe(8); // Same lesson as the Windows shards above, one job later: at 8 the Windows leg // spent ~7 minutes installing dependencies and was cancelled at the wall before diff --git a/tests/claude-integration/claude-desktop-remote-hub.test.ts b/tests/claude-integration/claude-desktop-remote-hub.test.ts index ed2c5ebf812..e126d6cf2e4 100644 --- a/tests/claude-integration/claude-desktop-remote-hub.test.ts +++ b/tests/claude-integration/claude-desktop-remote-hub.test.ts @@ -1,7 +1,7 @@ import { afterEach, expect, test } from "bun:test"; import { createHash } from "node:crypto"; -import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs"; -import { delimiter, dirname, join } from "node:path"; +import { copyFileSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { delimiter, dirname, isAbsolute, join } from "node:path"; import { tmpdir } from "node:os"; import type { OcxConfig } from "../../src/types"; import type { Desktop3pModelEntry } from "../../src/claude/desktop-3p"; @@ -44,6 +44,30 @@ function fixture(side: string, allowedOrigins: string[]) { OCX_TEST_DENIED_REQUESTS: paths.denied, }; mkdirSync(env.XDG_RUNTIME_DIR!, { recursive: true }); + if (process.platform === "win32") { + // A fresh profile otherwise rebuilds PowerShell's command-analysis cache + // in every CLI child. Seed an owned copy; background updates must never + // write the runner's or developer's original cache. + const cache = join(root, "module-analysis-cache"); + env.PSModuleAnalysisCachePath = cache; + const source = Object.entries(process.env).find(([key]) => + key.toLowerCase() === "psmoduleanalysiscachepath")?.[1]; + if (source && isAbsolute(source)) { + try { + const before = lstatSync(source); + if (before.isFile() && !before.isSymbolicLink()) { + copyFileSync(source, cache); + if (lstatSync(cache).size !== before.size) rmSync(cache, { force: true }); + } + } catch (error) { + const code = (error as NodeJS.ErrnoException).code; + if (code !== "ENOENT" && code !== "ESTALE") { + throw new Error("Desktop fixture could not read or copy the PowerShell module cache"); + } + rmSync(cache, { force: true }); + } + } + } return { ...paths, env }; } type Fixture = ReturnType;