Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,10 +602,10 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
const resolveServiceHomes = deps.resolveServiceHomes ?? currentServiceHomes;
let startupOwnershipHomes: ReturnType<typeof currentServiceHomes> | null = null;
let startupOwnershipStatePaths: readonly string[] | null = null;
// #2923: both synchronous startup ownership decisions keep their fresh,
// race-sensitive targeted task query. Only the expensive fallback listing is
// shared, and only while that targeted result stays byte-for-byte unchanged.
// Runtime ownership retries below intentionally omit this startup-local memo.
// #2923: retain a successful fallback listing only within the first startup
// ownership decision. A targeted query's bytes are not a Task Scheduler state
// generation, so the later race-sensitive decision must take a fresh listing.
// Runtime ownership retries below intentionally omit this startup-local memo too.
Comment on lines +605 to +608

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep startup within the service health window

On the documented localized Windows path where the targeted /xml response requires a full listing, this now runs two sequential listings before the listener binds. The measured host recorded in structure/04_transports-and-sidecars.md:22-34 takes 12.3 seconds per listing, so startup takes roughly 25 seconds, while confirmServiceServing gives ocx service install, start, and repair only 20 seconds (src/service.ts:655,687,708-720). Those commands will therefore report failure and exit 1 even though the service is still starting; the update path explicitly interprets that exit as reason to fall back to a direct start (src/service.ts:699-702). Preserve the fresh second ownership evidence, but adjust the corresponding service readiness budget or otherwise avoid exceeding it.

AGENTS.md reference: src/AGENTS.md:L10-L11

Useful? React with 👍 / 👎.

const startupWindowsTaskListingCache = createWindowsTaskListingCache();
try {
const homes = resolveServiceHomes();
Expand Down Expand Up @@ -797,7 +797,6 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
deps,
startupOwnershipHomes,
startupOwnershipStatePaths,
startupWindowsTaskListingCache,
);
const preparedNativeMainLifecycle = nativeOwnership.ownership !== "foreign"
&& startupOwnershipHomes !== null
Expand Down
15 changes: 11 additions & 4 deletions tests/codex-service-manager-probe-hardening.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ describe("Windows ownership probe hardening regressions", () => {
expect(result.kind).toBe("unknown");
});

test("one startup keeps two targeted queries but shares one unchanged full listing (#2923)", async () => {
test("a startup refreshes the full listing when a task appears after the second targeted snapshot", async () => {
const codexHome = join(home, "codex");
mkdirSync(codexHome, { recursive: true });
process.env.CODEX_HOME = codexHome;
Expand All @@ -227,15 +227,22 @@ describe("Windows ownership probe hardening regressions", () => {

let targetedQueries = 0;
let fullListings = 0;
let taskRegistered = false;
const runRaw: RawProbeRunner = (file, args) => {
if (!file.toLowerCase().endsWith("schtasks.exe")) return raw(1, "", "unexpected executable");
if (args.includes("/xml")) {
targetedQueries += 1;
// Model a localized targeted query that took its absent snapshot before
// a concurrent installer committed the task, then returned unchanged
// opaque bytes. Only the following fresh listing can observe the task.
if (targetedQueries === 2) taskRegistered = true;
return { status: 1, stdout: Buffer.alloc(0), stderr: GBK_TASK_NOT_FOUND, timedOut: false, spawnFailed: false };
}
if (args.includes("/fo")) {
fullListings += 1;
return raw(0, '"\\SomeOtherTask","N/A","Ready"\r\n');
return raw(0, taskRegistered
? '"\\opencodex-proxy","N/A","Ready"\r\n'
: '"\\SomeOtherTask","N/A","Ready"\r\n');
}
return raw(1, "", "unexpected query");
};
Expand All @@ -261,9 +268,9 @@ describe("Windows ownership probe hardening regressions", () => {
},
});
try {
expect(ownerships.slice(0, 2)).toEqual(["owned", "owned"]);
expect(ownerships.slice(0, 2)).toEqual(["owned", "unknown"]);
expect(targetedQueries).toBe(2);
expect(fullListings).toBe(1);
expect(fullListings).toBe(2);
} finally {
await server.stop(true);
}
Expand Down
Loading