From c939764f7d0a010033915ea4e9cc5102eca51d33 Mon Sep 17 00:00:00 2001 From: Merge Sim Date: Sat, 5 Sep 2026 02:19:14 -0700 Subject: [PATCH] fix(renderer): keep process cadence timing browser-safe --- ...gent-inspection-cadence-batching-benchmark.mjs | 2 +- .../agent-completion-poll-interval.test.ts | 15 ++++++++++++++- .../agent-completion-poll-interval.ts | 2 +- src/shared/process-table-snapshot-reader.ts | 8 +++----- src/shared/process-table-snapshot-timing.ts | 2 ++ 5 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 src/shared/process-table-snapshot-timing.ts diff --git a/config/scripts/agent-inspection-cadence-batching-benchmark.mjs b/config/scripts/agent-inspection-cadence-batching-benchmark.mjs index 128627676c4..5ececab3298 100644 --- a/config/scripts/agent-inspection-cadence-batching-benchmark.mjs +++ b/config/scripts/agent-inspection-cadence-batching-benchmark.mjs @@ -57,7 +57,7 @@ const { POLL_TIER_INTERVAL_MS } = await import( path.join(ROOT, 'src/renderer/src/components/terminal-pane/agent-completion-poll-cadence.ts') ) const { PROCESS_TABLE_SNAPSHOT_MAX_STALENESS_MS } = await import( - path.join(ROOT, 'src/shared/process-table-snapshot-reader.ts') + path.join(ROOT, 'src/shared/process-table-snapshot-timing.ts') ) // Pre-change: independent ±10% jitter per pane, re-rolled on every reschedule. diff --git a/src/renderer/src/components/terminal-pane/agent-completion-poll-interval.test.ts b/src/renderer/src/components/terminal-pane/agent-completion-poll-interval.test.ts index fffbb94de90..fb85fb0f212 100644 --- a/src/renderer/src/components/terminal-pane/agent-completion-poll-interval.test.ts +++ b/src/renderer/src/components/terminal-pane/agent-completion-poll-interval.test.ts @@ -1,5 +1,6 @@ +import { build } from 'esbuild' import { describe, expect, it } from 'vitest' -import { PROCESS_TABLE_SNAPSHOT_MAX_STALENESS_MS } from '../../../../shared/process-table-snapshot-reader' +import { PROCESS_TABLE_SNAPSHOT_MAX_STALENESS_MS } from '../../../../shared/process-table-snapshot-timing' import { POLL_TIER_INTERVAL_MS } from './agent-completion-poll-cadence' import { nextCadenceInspectionDelayMs } from './agent-completion-poll-interval' @@ -14,6 +15,18 @@ describe('nextCadenceInspectionDelayMs', () => { now }) + it('keeps its production import graph browser-only', async () => { + await expect( + build({ + bundle: true, + entryPoints: [`${import.meta.dirname}/agent-completion-poll-interval.ts`], + logLevel: 'silent', + platform: 'browser', + write: false + }) + ).resolves.toMatchObject({ errors: [] }) + }) + it('walks panes that scheduled at different moments onto one shared deadline', () => { // Why this matters: the inspection queue collapses shared-observation tasks enqueued in the // same tick onto one process-table capture, so a shared deadline is one `ps` for all panes. diff --git a/src/renderer/src/components/terminal-pane/agent-completion-poll-interval.ts b/src/renderer/src/components/terminal-pane/agent-completion-poll-interval.ts index a8058bc2ac5..79c1c6bd439 100644 --- a/src/renderer/src/components/terminal-pane/agent-completion-poll-interval.ts +++ b/src/renderer/src/components/terminal-pane/agent-completion-poll-interval.ts @@ -1,4 +1,4 @@ -import { PROCESS_TABLE_SNAPSHOT_MAX_STALENESS_MS } from '../../../../shared/process-table-snapshot-reader' +import { PROCESS_TABLE_SNAPSHOT_MAX_STALENESS_MS } from '../../../../shared/process-table-snapshot-timing' /** * Picks the delay until a pane's next cadence inspection. diff --git a/src/shared/process-table-snapshot-reader.ts b/src/shared/process-table-snapshot-reader.ts index 53496b359ee..764fb89f205 100644 --- a/src/shared/process-table-snapshot-reader.ts +++ b/src/shared/process-table-snapshot-reader.ts @@ -2,6 +2,7 @@ import { execFile as execFileCb } from 'node:child_process' import { promisify } from 'node:util' import { readLinuxProcessStartTimes } from './linux-process-start-times' import { withEvidenceBudget } from './process-table-evidence-budget' +import { PROCESS_TABLE_SNAPSHOT_MAX_STALENESS_MS } from './process-table-snapshot-timing' import { PS_ARGS, PS_MAX_BUFFER_BYTES, @@ -17,6 +18,7 @@ export { PROCESS_TABLE_EVIDENCE_BUDGET_MS, withEvidenceBudget } from './process-table-evidence-budget' +export { PROCESS_TABLE_SNAPSHOT_MAX_STALENESS_MS } from './process-table-snapshot-timing' const execFile = promisify(execFileCb) @@ -26,7 +28,7 @@ const execFile = promisify(execFileCb) // whole subsystem answered "unverifiable" about a table it could read. This keeps a wedged // `ps` bounded while staying out of reach of a host that is merely busy. export const PS_TIMEOUT_MS = 15_000 -const DEFAULT_SNAPSHOT_TTL_MS = 500 +const DEFAULT_SNAPSHOT_TTL_MS = PROCESS_TABLE_SNAPSHOT_MAX_STALENESS_MS type Snapshot = { value: T; capturedAtMs: number; completedAtMs: number } @@ -282,10 +284,6 @@ export async function getStrictProcessTableSnapshotWithAge(): Promise<{ return { rows: snapshot.value.strict(), capturedAgeMs: snapshot.capturedAgeMs } } -/** How much older than its own await a TTL-cached capture may be, on top of the capture's own - * duration. Reported ages carry both, so this alone is not the staleness bound. */ -export const PROCESS_TABLE_SNAPSHOT_MAX_STALENESS_MS = DEFAULT_SNAPSHOT_TTL_MS - export function resetProcessTableSnapshotForTests(): void { processTableReader.reset() } diff --git a/src/shared/process-table-snapshot-timing.ts b/src/shared/process-table-snapshot-timing.ts new file mode 100644 index 00000000000..eaa3621728c --- /dev/null +++ b/src/shared/process-table-snapshot-timing.ts @@ -0,0 +1,2 @@ +/** How long a completed shared process-table snapshot remains reusable, and the maximum cadence pull-forward. */ +export const PROCESS_TABLE_SNAPSHOT_MAX_STALENESS_MS = 500