Files
orca/src/shared/child-process/process-tree-kill-gate.ts
T
Neil 9db1b4ce6e fix(crash-reporting): make the own-Chromium gate a real choke point
Round-3 review found the guard was not the choke point its own comments
claimed: six pid-addressed `taskkill /pid <pid> /t /f` families in main were
ungated and uninstrumented, so the stale-pid shape stayed producible and a
`selfInitiatedTreeKillCount: 0` could read as exculpatory when it was not.

- Gate the remaining main-process families: the git command-runner abort, the
  notebook-cell and automation-precheck timeouts.
- Turn the `src/shared` seam into the gate itself (`process-tree-kill-gate`), so
  the runProcess choke point, the codex app-server deadline kill and the
  ephemeral-VM recipe kill ask the same decision. Those three are compiled into
  the CLI/relay too and cannot import main; main installs the guard at preflight.
- Ratchet (`main-process-tree-kill-gate.test.ts`): a new pid-addressed taskkill
  in main that skips the gate fails, and the allowlist entries must still exist.
- Give pid-addressed kills eviction priority in the 32-entry ring: 32 routine
  `win-pty-job` teardowns from a window-close burst no longer evict the one
  entry that discriminates a self-kill from an external one.
- Correct the coverage doc, which described the uninstrumented Windows sites as
  POSIX `process.kill(-pid)` group kills and omitted the git and codex paths.
2026-09-03 04:07:42 -07:00

42 lines
1.5 KiB
TypeScript

/**
* Seam that lets the main process decide, and record, the tree-kills issued
* from code it does not own.
*
* Why a seam and not a direct call: `signalProcessTree` is the choke point every
* `runProcess` termination funnels through, and the codex app-server and
* ephemeral-VM kills are shared with the CLI — all of them live outside
* `src/main` and cannot import the own-Chromium guard or the crash breadcrumb
* store. Main registers the guard at startup; everywhere else this admits every
* kill and records nothing.
*/
/** Blast radius, not mechanism: `win-taskkill-tree` is addressed by pid and walks
* whatever tree that pid has *now*, so it can land on a recycled pid that is
* since one of Orca's own Chromium processes. A process group can only contain
* processes Orca itself put there. */
export type ProcessTreeKillScope = 'win-taskkill-tree' | 'posix-process-group'
export type ProcessTreeKill = {
pid: number
site: string
scope: ProcessTreeKillScope
}
/** False means the caller must not kill: main is currently accounting for that pid. */
type ProcessTreeKillGate = (kill: ProcessTreeKill) => boolean
let gate: ProcessTreeKillGate | null = null
export function setProcessTreeKillGate(next: ProcessTreeKillGate | null): void {
gate = next
}
export function admitProcessTreeKill(kill: ProcessTreeKill): boolean {
try {
return gate?.(kill) ?? true
} catch {
// Diagnostics must never turn a successful termination into a failed one.
return true
}
}