From c397b4c78879ae0c7024493dcab28a290e415b0d Mon Sep 17 00:00:00 2001 From: garrytan Date: Wed, 28 Oct 2026 14:02:01 +0000 Subject: [PATCH] fix(browse): scope test 5's PowerShell assertion to the owner; identify a win32 agent by generation when readers disagree Review of d4f0ea3c (specialist + outside voice, confirmed by Windows Free Tests 40699973746 shard 3): test 5's fake child pid cannot be opened, so its start time falls back to the PowerShell query; only the owner must skip it. Adversarial note: the Bun reader and the Node daemon's PowerShell reader can disagree for a process created in a DST-ambiguous hour; on win32 a live process carrying the record's --agent-gen is still the agent, so it is stopped, never treated as gone beside a second agent. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- browse/src/terminal-agent-control.ts | 6 +++--- browse/test/process-liveness-windows.test.ts | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/browse/src/terminal-agent-control.ts b/browse/src/terminal-agent-control.ts index bb96310e0..ac2eb78a4 100644 --- a/browse/src/terminal-agent-control.ts +++ b/browse/src/terminal-agent-control.ts @@ -368,7 +368,8 @@ function agentStatus(record: AgentRecord, ownerPid?: number): 'owned' | 'gone' | if (ownerPid !== undefined && (record.ownerPid !== ownerPid || record.ownerStartTime !== readAgentStartTime(ownerPid))) return 'unknown'; const actual = agentProcessInfo(record.pid); if (!actual.startTime) return isAgentRecordLive(record) ? 'unknown' : 'gone'; - if (actual.startTime !== record.startTime) return 'gone'; + const ownsGeneration = actual.commandLine.split(/\s+/).some(arg => arg.replace(/^['"]|['"]$/g, '') === `--agent-gen=${record.gen}`); + if (actual.startTime !== record.startTime && !(process.platform === 'win32' && ownsGeneration)) return 'gone'; try { let state: string | undefined; if (process.platform === 'linux') { @@ -380,8 +381,7 @@ function agentStatus(record: AgentRecord, ownerPid?: number): 'owned' | 'gone' | if (state === 'Z') return 'gone'; } catch {} if (!isAgentRecordLive(record)) return 'gone'; - return actual.commandLine.split(/\s+/).some(arg => arg.replace(/^['"]|['"]$/g, '') === `--agent-gen=${record.gen}`) - ? 'owned' : 'unknown'; + return ownsGeneration ? 'owned' : 'unknown'; } export function isOurAgent(record: AgentRecord, ownerPid?: number): boolean { diff --git a/browse/test/process-liveness-windows.test.ts b/browse/test/process-liveness-windows.test.ts index fff426b3e..252f5fd1f 100644 --- a/browse/test/process-liveness-windows.test.ts +++ b/browse/test/process-liveness-windows.test.ts @@ -151,7 +151,8 @@ describe('process liveness probe (Windows terminal-agent leak)', () => { if (process.platform === 'win32') { expect(captured.env.BROWSE_OWNER_START_TIME).toMatch(/^\/Date\(\d+\)\/$/); expect(captured.env.BROWSE_OWNER_START_TIME).toBe(readAgentStartTime(process.pid)); - expect(powershellCalls).toEqual([]); + expect(powershellCalls.filter(call => call.includes(`ProcessId = ${process.pid}'`))).toEqual([]); + expect(powershellCalls.every(call => call.includes("ProcessId = 2147483647'"))).toBe(true); } else { expect(captured.env.BROWSE_OWNER_START_TIME).toBe('fixture-owner-start'); } @@ -252,12 +253,22 @@ describe('win32 terminal-agent identity (GSTA-139)', () => { expect(calls.signals).toEqual([]); }); } - onWin32({ ffi: '/Date(1761600000124)/', powershell: { status: 0, creationDate: birth, commandLine: ownedCommand } }, calls => { + onWin32({ ffi: '/Date(1761600000124)/', powershell: { status: 0, creationDate: birth, commandLine: 'bun run unrelated.ts' } }, calls => { expect(isAgentRecordGone(record())).toBe(true); expect(calls.signals).toEqual([]); }); }); + test('a start time that disagrees across readers still identifies the live agent by its generation, so no second agent starts', () => { + const daemonRecorded = '/Date(1761596400123)/'; + onWin32({ ffi: birth, powershell: { status: 0, creationDate: daemonRecorded, commandLine: ownedCommand } }, calls => { + expect(isAgentRecordGone(record(daemonRecorded))).toBe(false); + expect(isOurAgent(record(daemonRecorded))).toBe(true); + expect(stopAgentByRecord(record(daemonRecorded), 0)).toBe(true); + expect(calls.signals).toEqual(['SIGTERM']); + }); + }); + test('an unreadable start time is unknown: never signalled and never treated as gone', () => { onWin32({ ffi: undefined, powershell: { status: null } }, calls => { expect(readAgentStartTime(pid)).toBe('');