fix(renderer): keep process cadence timing browser-safe

This commit is contained in:
Merge Sim
2026-09-05 02:19:14 -07:00
parent b27c54b422
commit c939764f7d
5 changed files with 21 additions and 8 deletions
@@ -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.
@@ -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.
@@ -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.
+3 -5
View File
@@ -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<T> = { 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()
}
@@ -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