Skip to content

fix(runner): prevent Windows process probes from blocking session lifecycle - #2035

Open
techotaku39 wants to merge 8 commits into
tiann:mainfrom
techotaku39:fix/windows-lifecycle-responsiveness
Open

techotaku39 wants to merge 8 commits into
tiann:mainfrom
techotaku39:fix/windows-lifecycle-responsiveness

Conversation

@techotaku39

@techotaku39 techotaku39 commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

On Windows, synchronous process checks can block Runner long enough for a fresh session's /session-started request to time out. With 16 tracked sessions, local control requests stalled for over 20 seconds.

  • Use one asynchronous, bounded process snapshot per heartbeat. Preserve bindings on failed or unverifiable queries, and reject observations made stale by a new startup webhook.
  • Batch cleanup registry checks and yield between tree probes. Verify process generations and preserve replacement bindings during concurrent termination.
  • Recheck bindings immediately before signalling in tracked and orphan stop paths. Protect newly registered shared roots while allowing verified descendants to be reaped after their original wrapper exits, including before the initial signal.
  • On a failed registry process snapshot, retain records for sibling protection while allowing unrelated identified targets to be stopped.
  • Keep SDK process-exit cleanup synchronous so Claude and tool processes receive their termination signal before the exit handler returns. Normal Runner termination retains asynchronous verification.

Startup and archive deadlines are unchanged.

Validation

Windows checks used Bun 1.4.0 and isolated task homes, databases, credentials and ports.

  • From cli/: bunx vitest run --config vitest.heartbeat.local.config.ts — 174 passed, including 38 new tests, native CIM coverage and startup-webhook regression coverage. The task-local config replaces automatic environment setup.
  • bun typecheck, bun run build, bun run --cwd cli build:exe:allinone — passed.
  • Invoke-HapiTaskPlaywright.ps1 -Name windows-lifecycle-pr -Suite Root terminal-wrap-fidelity.spec.ts — 2 passed.
  • bun run gen:fixtures produced no normalized content changes; bun run test:web then passed 3,349 tests. bun run test:shared passed 327 tests.
  • Packaged Full acceptance used native Codex with a local model fixture and 16 lightweight tracked processes. Fresh creates/replies during delayed snapshots, failed/empty/timed-out snapshots, stale snapshots, and tracked/untracked stop-session races passed. A sibling reported during pre-signal collection stayed alive.
  • Compatibility build with perf(cli): reduce Windows process probes during session shutdown #2046: 197 focused tests passed. Final Invoke-HapiTaskPlaywright.ps1 -Name heartbeat-combined-check -Suite Root --config playwright.heartbeat.local.config.ts — 6 passed, including real SDK-parent exit with no surviving child/tool process, the two stop races, an unrelated tracked process terminating despite a failed registry snapshot (final status remains conservative), heartbeat load, and archive/reopen/sibling replies/mobile rendering.
  • Earlier browser acceptance on the combined heartbeat build passed six actual Create-form submissions across desktop/mobile viewports, including replies at ages 76–179 seconds. 379 active-state observations found no inactive session or request error.

Acceptance harnesses are workspace-local. Mobile checks use emulation; model responses use a local fixture. Test environments were stopped afterward.

Remaining limitation

Standalone Windows archive acceptance can still exceed the Hub's 30-second RPC deadline; this also reproduces on the unchanged parent. The combined build with #2046 passed archive/reopen. One creation attempt concurrent with full-suite validation timed out; its identical serial rerun passed. This does not eliminate every startup or shutdown deadline failure under load.

Related Issues

Refs #1910.
Refs #1914.

Related PRs: #1940 and #2046.

AI Disclosure

Implementation and validation were assisted by OpenAI Codex, using GPT-6.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review mode: initial

Requirement — Pass

Keeping Windows Runner responsive during discovery cleanup and session archive is justified. Existing synchronous process probes multiply registry-scan costs and can delay lifecycle requests.

Evidence

  • PR description identifies session-started timeouts and archive RPC deadlines.
  • Base cli/src/runner/run.ts:1124 filters every registry record through runtimeMayBeAlive; cli/src/utils/process.ts:75 performs synchronous tasklist/CIM generation probes.

Approach — Pass

A fresh process snapshot removes per-record Windows probes. Yielding between tree probes allows queued control work, while generation-aware verification avoids treating reused PIDs as surviving session processes.

Evidence

  • cli/src/runner/run.ts:1124 reads registry records before obtaining a fresh snapshot and preserves conservative handling of scan failures.
  • cli/src/utils/process.ts:436 yields between probes; line 481 accepts a changed known generation as termination evidence while retaining unknown identities.

Code — Reviewed

Reviewed the entire seven-file merge-base diff and surrounding callers. No actionable defects were identified. Runtime behavior was assessed statically without executing PR code.

No reportable code issues found.

Testing

Not run (automation; PR code execution is prohibited).

  • Fixed-head CI: integration and windows-codex-mcp succeeded; test remained in progress when checked.
  • Added tests cover snapshot filtering, invalid listings, event-loop yielding, unknown generations, PID reuse, and diagnostic callback failures.
  • The PR reports 131 focused Windows tests and packaged lifecycle checks; these were not independently rerun.
  • git diff --check passed. No PR tests, builds, or scripts were executed.

HAPI Bot

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review mode: follow-up after new commits

Requirement — Pass

Keeping Windows Runner responsive during process checks is a justified lifecycle fix.

Evidence

  • Issue #1914 identifies synchronous Windows tasklist polling as an event-loop bottleneck.
  • The base heartbeat calls isProcessAlive for each tracked PID; cli/src/utils/process.ts:16 shells out synchronously on Windows.

Approach — Pass

A bounded asynchronous heartbeat snapshot addresses repeated blocking probes. Binding guards and conservative handling of failed queries are appropriate.

Evidence

  • cli/src/runner/orphanReap.ts:199 performs an asynchronous CIM query with a timeout.
  • cli/src/runner/run.ts:1771 captures bindings and rejects observations after binding or webhook-revision changes.
  • cli/src/codex/shared/registry.ts:35 supports batched generation checks while retaining unknown identities.

Code — Reviewed

Reviewed the entire 11-file merge-base diff and surrounding callers. One cleanup race remains when a terminated process's PID is reused.

  • [Minor] Preserve replacement bindings after generation-aware termination — cli/src/runner/run.ts:1418

Testing

Not run (automation; PR code execution is prohibited).

  • Fixed-head CI: integration and windows-codex-mcp succeeded; test remained in progress.
  • git diff --check passed. No PR code, tests, builds or scripts were executed.
  • Added tests cover responsiveness, failed snapshots, yielding and PID reuse, but do not cover replacement Runner bindings during stop cleanup. PR-reported Windows acceptance was not independently rerun.

HAPI Bot

Comment thread cli/src/runner/run.ts

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review mode: follow-up after new commits

Requirement — Pass

Keeping Windows Runner responsive during process checks is a justified session-lifecycle fix.

Evidence

  • Issue #1914 identifies synchronous Windows process polling as an event-loop bottleneck.
  • Base cli/src/utils/process.ts:16 invokes tasklist synchronously; the base heartbeat repeats liveness checks for tracked PIDs.

Approach — Pass

A bounded asynchronous heartbeat snapshot addresses repeated blocking probes. Conservative failure handling and binding revisions appropriately protect concurrent lifecycle updates.

Evidence

  • cli/src/runner/orphanReap.ts:199 performs an asynchronous CIM query with a timeout.
  • cli/src/runner/run.ts:1783 captures bindings and rejects observations after binding or webhook-revision changes.
  • cli/src/codex/shared/registry.ts:35 supports batched generation checks while retaining unknown identities.

Code — Reviewed

Reviewed the entire 13-file merge-base diff and surrounding callers. The tracked-session cleanup race is addressed, but the persisted-PID fallback remains vulnerable.

  • [Minor] Preserve replacement bindings in persisted-PID stop cleanup — cli/src/utils/process.ts:481

Testing

Not run (automation; PR code execution is prohibited).

  • Fixed-head CI: test, integration and windows-codex-mcp succeeded.
  • git diff --check passed. No PR code, tests, builds or scripts were executed.
  • Added tests cover snapshot failures, responsiveness, generation-aware shutdown and the binding helper, but do not exercise replacement bindings through the persisted-PID stop fallback.
  • PR-reported Windows acceptance results were not independently rerun.

HAPI Bot

Comment thread cli/src/utils/process.ts

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review mode: follow-up after new commits

Requirement — Pass

Keeping Windows Runner responsive during process checks is a justified session-lifecycle fix.

Evidence

  • Issue #1914 documents synchronous Windows process polling blocking the event loop.
  • Base cli/src/utils/process.ts:16 uses synchronous tasklist probes; the base heartbeat repeats process checks for tracked PIDs.

Approach — Pass

A bounded asynchronous heartbeat snapshot addresses repeated blocking probes. Conservative failure handling and binding guards appropriately protect concurrent lifecycle updates.

Evidence

  • cli/src/runner/orphanReap.ts:199 implements an asynchronous Windows query with a timeout.
  • cli/src/runner/run.ts:1790 captures bindings and webhook revisions before querying processes.
  • cli/src/runner/run.ts:1496 guards persisted-PID cleanup, addressing the previous review finding.

Code — Reviewed

Reviewed the entire 13-file merge-base diff and relevant callers. The previous cleanup finding is resolved, but the new pre-termination yields expose a shared-root termination race.

  • [Major] Recheck bindings after yielding and before signalling the wrapper — cli/src/utils/process.ts:439

Testing

Not run (automation; PR code execution is prohibited).

  • Fixed-head CI: integration and windows-codex-mcp succeeded; test remained in progress when inspected.
  • git diff --check passed. No PR code, tests, builds, or scripts were executed.
  • Added tests cover yielding and binding cleanup separately, but do not cover a sibling webhook invalidating the stop decision during pre-signal marker collection.
  • PR-reported Windows acceptance results were not independently rerun.

HAPI Bot

Comment thread cli/src/utils/process.ts

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review mode: follow-up after new commits

Requirement — Pass

Keeping Windows Runner responsive during process checks is a justified session-lifecycle fix.

Evidence

  • Issue #1914 documents synchronous Windows process polling blocking the event loop.
  • Base cli/src/utils/process.ts:16 performs synchronous tasklist probes; the base heartbeat repeats process checks for tracked PIDs.

Approach — Pass

A bounded asynchronous heartbeat snapshot addresses repeated blocking probes. Conservative failure handling and binding checks are appropriate, but termination guards need implementation corrections.

Evidence

  • cli/src/runner/orphanReap.ts:199 implements an asynchronous Windows query with a timeout.
  • cli/src/runner/run.ts:1790 captures bindings and webhook revisions before applying process observations.
  • cli/src/utils/process.ts:453 rechecks mapped-stop bindings before signalling.

Code — Reviewed

Reviewed the entire 13-file merge-base diff and relevant callers. The previous mapped-stop race is addressed, but two Windows lifecycle defects remain.

  • [Major] Allow expected wrapper exit while reaping surviving descendants — cli/src/utils/process.ts:464
  • [Major] Propagate the binding guard through orphan-sweep termination — cli/src/utils/process.ts:453

Testing

Not run (automation; PR code execution is prohibited).

  • Fixed-head CI: test, integration, and windows-codex-mcp succeeded.
  • git diff --check passed.
  • No PR code, tests, builds, or scripts were executed. PR-reported Windows acceptance results were not independently rerun.
  • Added tests do not cover guard invalidation by expected child-exit cleanup or sibling registration during orphan-sweep termination.

HAPI Bot

Comment thread cli/src/utils/process.ts Outdated
Comment thread cli/src/utils/process.ts Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review mode: follow-up after new commits

Requirement — Pass

Keeping Windows Runner responsive during process checks addresses a supported session-lifecycle problem.

Evidence

  • Issue #1914 identifies synchronous Windows process polling as event-loop blocking and recommends asynchronous or batched probes.
  • Base cli/src/utils/process.ts:16 uses synchronous tasklist probes; the base heartbeat repeats process checks for tracked PIDs.

Approach — Pass

A bounded asynchronous heartbeat snapshot addresses repeated blocking probes. Conservative failure handling and binding guards are appropriate.

Evidence

  • cli/src/runner/orphanReap.ts:199 implements an asynchronous Windows query with a timeout.
  • cli/src/runner/run.ts:1800 captures bindings and webhook revisions before applying observations.
  • cli/src/runner/run.ts:1201 and :1539 propagate binding guards through both stop-session orphan sweeps.

Code — Reviewed

Reviewed the entire 13-file merge-base diff and relevant callers. Both previous findings are addressed; one additional Windows shutdown regression remains.

  • [Major] Preserve synchronous termination for process-exit cleanup — cli/src/utils/process.ts:443

Testing

Not run (automation; PR code execution is prohibited).

  • Fixed-head CI: integration and windows-codex-mcp succeeded; test was still running at the last check.
  • The added tests do not cover synchronous process-exit cleanup. The Windows CI job selects only codexMcpProxy.test.ts, excluding the new native heartbeat test.
  • git diff --check passed. No PR code, tests, builds, or scripts were executed; PR-reported Windows acceptance results were not independently rerun.

HAPI Bot

Comment thread cli/src/utils/process.ts

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review mode: follow-up after new commits

Requirement — Pass

Keeping Windows Runner responsive during process checks addresses a supported session-lifecycle problem.

Evidence

  • Issue #1914 identifies synchronous Windows process polling as event-loop blocking and recommends asynchronous or batched probes.
  • Base cli/src/utils/process.ts:16 uses synchronous tasklist probes, which the base heartbeat repeats for tracked PIDs.

Approach — Pass

A bounded asynchronous heartbeat snapshot addresses repeated blocking probes. Binding guards, conservative observations, and separate synchronous exit cleanup are appropriate.

Evidence

  • cli/src/runner/orphanReap.ts:199 implements the asynchronous Windows snapshot with a timeout.
  • cli/src/runner/run.ts:1800 captures bindings and webhook revisions before applying observations.
  • cli/src/claude/sdk/query.ts:444 uses synchronous termination for process-exit cleanup.

Code — Reviewed

Reviewed all 16 changed files and relevant callers. The previous exit-cleanup finding is addressed; two stop-path regressions remain.

  • [Major] Continue descendant cleanup when the wrapper exits before signalling — cli/src/utils/process.ts:444
  • [Minor] Avoid blocking unrelated termination on a failed registry snapshot — cli/src/runner/run.ts:1162

Testing

Not run (automation; PR code execution is prohibited).

  • Fixed-head CI: test, integration, and windows-codex-mcp succeeded.
  • Windows CI selects codexMcpProxy.test.ts and does not run the new native heartbeat test.
  • The committed tests do not cover wrapper exit before initial signalling or failed registry snapshots during non-Codex termination.
  • git diff --check passed. No PR code, tests, builds, or scripts were executed; PR-reported acceptance results were not independently rerun.

HAPI Bot

Comment thread cli/src/utils/process.ts Outdated
Comment thread cli/src/runner/run.ts Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review mode: follow-up after new commits

Requirement — Pass

Keeping Windows Runner responsive during process checks addresses a supported session-lifecycle problem.

Evidence

  • Issue #1914 identifies synchronous tasklist polling as event-loop blocking and recommends asynchronous or batched probes.
  • Base cli/src/utils/process.ts:16 performs synchronous Windows liveness checks, repeated by the base heartbeat.

Approach — Pass

A bounded asynchronous heartbeat snapshot addresses repeated blocking probes. Conservative failure handling, binding guards, batched registry checks, and synchronous exit cleanup fit the lifecycle requirements.

Evidence

  • cli/src/runner/orphanReap.ts:199 implements the asynchronous snapshot with a 10-second timeout.
  • cli/src/runner/run.ts:1790 captures bindings and webhook revisions before applying observations.
  • cli/src/runner/stopRuntimes.ts:14 retains potentially live registry records after snapshot failure.
  • cli/src/claude/sdk/query.ts:441 preserves synchronous process-exit signalling.

Code — Reviewed

Reviewed all 18 changed files and relevant callers. Both previous findings are addressed: captured descendants can be cleaned after pre-signal wrapper exit, and failed registry snapshots no longer prevent unrelated termination. No further actionable defects were established. Runtime behavior was assessed statically.

No reportable code issues found.

Testing

Not run (automation; PR code execution is prohibited).

  • Fixed-head CI: integration and windows-codex-mcp succeeded; test remained in progress when checked.
  • Windows CI selects codexMcpProxy.test.ts and does not execute the new native Windows heartbeat test.
  • New regression tests cover pre-signal wrapper exit, replacement bindings, new siblings, and conservative registry snapshot failure handling.
  • git diff --check passed. No PR code, tests, builds, or scripts were executed; PR-reported acceptance results were not independently rerun.

HAPI Bot

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:cli CLI, runner, agent wrappers bug Something isn't working community-pr PR from non-collaborator contributor platform:windows Windows-specific

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants